1
0

dev-mailbox.html 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. const ESCAPE_ENTITIES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' };
  154. const escapeChar = char => ESCAPE_ENTITIES[char] || char;
  155. const escapeHTMLEntities = value => String(value).replace(/[<>"&]/g, escapeChar);
  156. function renderList(items) {
  157. const list = document.querySelector('.list');
  158. list.innerHTML = '';
  159. const rows = items.forEach((item) => {
  160. const row = document.createElement('div');
  161. row.classList.add('row');
  162. row.dataset.id = item.fileName;
  163. row.innerHTML = `
  164. <div class="meta">
  165. <div class="date">${item.date}</div>
  166. <div class="recipient">${escapeHTMLEntities(item.recipient)}</div>
  167. </div>
  168. <div class="subject">${item.subject}</div>`;
  169. row.addEventListener('click', (e) => {
  170. selectedId = item.fileName;
  171. fetch(normalizePath('item/' + item.fileName))
  172. .then((res) => res.json())
  173. .then((res) => renderEmail(res))
  174. .then(() => highlightSelectedRow());
  175. });
  176. list.appendChild(row);
  177. });
  178. highlightSelectedRow();
  179. }
  180. function highlightSelectedRow() {
  181. document.querySelectorAll('.list .row').forEach((row) => {
  182. row.classList.remove('selected');
  183. if (row.dataset.id === selectedId) {
  184. row.classList.add('selected');
  185. }
  186. });
  187. }
  188. function renderEmail(email) {
  189. const content = `
  190. <div class="metadata">
  191. <table>
  192. <tr>
  193. <td>Recipient:</td>
  194. <td>${escapeHTMLEntities(email.recipient)}</td>
  195. </tr>
  196. <tr>
  197. <td>Subject:</td>
  198. <td>${email.subject}</td>
  199. </tr>
  200. <tr>
  201. <td>Date:</td>
  202. <td>${new Date().toLocaleString()}</td>
  203. </tr>
  204. </table>
  205. </div>
  206. <iframe srcdoc="${email.body.replace(/"/g, '&quot;')}"></iframe>
  207. `;
  208. document.querySelector('.detail').innerHTML = content;
  209. }
  210. </script>
  211. </body>
  212. </html>