dev-mailbox.html 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Vendure Development Inbox</title>
  6. <style>
  7. body {
  8. display: flex;
  9. flex-direction: column;
  10. height: 100vh;
  11. margin: 0;
  12. font-family: Helvetica, Arial, sans-serif;
  13. }
  14. .top-bar {
  15. padding: 12px;
  16. display: flex;
  17. align-items: center;
  18. background-color: #2a2929;
  19. color: #efefef;
  20. height: 60px;
  21. }
  22. .heading {
  23. margin: 0;
  24. font-size: 22px;
  25. }
  26. button#refresh {
  27. margin-left: 12px;
  28. border-radius: 3px;
  29. display: flex;
  30. align-items: center;
  31. }
  32. button#refresh .label {
  33. margin-left: 6px;
  34. }
  35. .generate-controls {
  36. flex: 1;
  37. display: flex;
  38. justify-content: flex-end;
  39. }
  40. input,
  41. select,
  42. button {
  43. padding: 6px;
  44. border-radius: 3px;
  45. border: 1px solid #0b384b;
  46. margin-left: 3px;
  47. }
  48. button {
  49. text-transform: uppercase;
  50. font-size: 12px;
  51. transition: background-color 0.2s;
  52. padding: 0 12px;
  53. height: 32px;
  54. }
  55. button:hover {
  56. background-color: #efefef;
  57. }
  58. #language-code {
  59. width: 32px;
  60. }
  61. .content {
  62. display: flex;
  63. flex: 1;
  64. height: calc(100% - 60px);
  65. }
  66. .list {
  67. width: 40vw;
  68. min-width: 300px;
  69. overflow: auto;
  70. }
  71. .row {
  72. border-bottom: 1px dashed #ddd;
  73. padding: 12px 6px;
  74. cursor: pointer;
  75. transition: background-color 0.2s;
  76. }
  77. .row.selected {
  78. background-color: #d4e1e7;
  79. }
  80. .row:not(.selected):hover {
  81. background-color: #efefef;
  82. }
  83. .meta {
  84. display: flex;
  85. justify-content: space-between;
  86. color: #666;
  87. }
  88. .detail {
  89. flex: 1;
  90. border: 1px solid #999;
  91. display: flex;
  92. flex-direction: column;
  93. }
  94. .detail iframe {
  95. height: 100%;
  96. border: 1px solid #eee;
  97. overflow: auto;
  98. }
  99. .metadata {
  100. padding: 6px;
  101. color: #333;
  102. background-color: white;
  103. z-index: 1;
  104. box-shadow: 0px 5px 8px -7px rgba(0, 0, 0, 0.49);
  105. }
  106. </style>
  107. </head>
  108. <body>
  109. <div class="top-bar">
  110. <h1 class="heading">Vendure Dev Mailbox</h1>
  111. <div class="generate-controls">
  112. <select id="type-selector"></select>
  113. <input id="language-code" value="en" type="text" />
  114. <button id="generate-test">Generate test</button>
  115. </div>
  116. </div>
  117. <div class="content">
  118. <div class="list"></div>
  119. <div class="detail"></div>
  120. </div>
  121. <script>
  122. let selectedId = '';
  123. const normalizePath = (endpoint) => {
  124. const pathname = location.pathname;
  125. return pathname.endsWith('/') ? `${pathname}${endpoint}` : `${pathname}/${endpoint}`;
  126. };
  127. refreshInbox();
  128. setInterval(refreshInbox, 5000);
  129. const typeSelect = document.querySelector('#type-selector');
  130. fetch(normalizePath('types'))
  131. .then((res) => res.json())
  132. .then((res) => {
  133. res.forEach((type) => {
  134. const option = document.createElement('option');
  135. option.value = type;
  136. option.text = type;
  137. typeSelect.appendChild(option);
  138. });
  139. });
  140. const languageCodeInput = document.querySelector('#language-code');
  141. const generateTestButton = document.querySelector('#generate-test');
  142. generateTestButton.addEventListener('click', (e) => {
  143. fetch(normalizePath(`generate/${typeSelect.value}/${languageCodeInput.value}`))
  144. .then(() => new Promise((resolve) => setTimeout(resolve, 500)))
  145. .then(() => refreshInbox());
  146. });
  147. const list = document.querySelector('.list');
  148. function refreshInbox() {
  149. fetch(normalizePath('list'))
  150. .then((res) => res.json())
  151. .then((res) => renderList(res));
  152. }
  153. function renderList(items) {
  154. const list = document.querySelector('.list');
  155. list.innerHTML = '';
  156. const rows = items.forEach((item) => {
  157. const row = document.createElement('div');
  158. row.classList.add('row');
  159. row.dataset.id = item.fileName;
  160. row.innerHTML = `
  161. <div class="meta">
  162. <div class="date">${item.date}</div>
  163. <div class="recipient">${item.recipient}</div>
  164. </div>
  165. <div class="subject">${item.subject}</div>`;
  166. row.addEventListener('click', (e) => {
  167. selectedId = item.fileName;
  168. fetch(normalizePath('item/' + item.fileName))
  169. .then((res) => res.json())
  170. .then((res) => renderEmail(res))
  171. .then(() => highlightSelectedRow());
  172. });
  173. list.appendChild(row);
  174. });
  175. highlightSelectedRow();
  176. }
  177. function highlightSelectedRow() {
  178. document.querySelectorAll('.list .row').forEach((row) => {
  179. row.classList.remove('selected');
  180. if (row.dataset.id === selectedId) {
  181. row.classList.add('selected');
  182. }
  183. });
  184. }
  185. function renderEmail(email) {
  186. const content = `
  187. <div class="metadata">
  188. <table>
  189. <tr>
  190. <td>Recipient:</td>
  191. <td>${email.recipient}</td>
  192. </tr>
  193. <tr>
  194. <td>Subject:</td>
  195. <td>${email.subject}</td>
  196. </tr>
  197. <tr>
  198. <td>Date:</td>
  199. <td>${new Date().toLocaleString()}</td>
  200. </tr>
  201. </table>
  202. </div>
  203. <iframe srcdoc="${email.body.replace(/"/g, '&quot;')}"></iframe>
  204. `;
  205. document.querySelector('.detail').innerHTML = content;
  206. }
  207. </script>
  208. </body>
  209. </html>