@raketa-cloud/admin 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/package.json +3 -2
  2. package/src/Adapter.js +1 -1
  3. package/src/cloud/ChangePageURL.js +24 -0
  4. package/src/cloud/ClonesPage.js +14 -0
  5. package/src/cloud/DeletePage.js +35 -0
  6. package/src/cloud/PageChanges.js +15 -0
  7. package/src/cloud/PageRevisions.js +135 -0
  8. package/src/cloud/PageTranslations.js +22 -0
  9. package/src/cloud/Pages.js +63 -0
  10. package/src/cloud/RaketaClient.js +118 -0
  11. package/src/cloud/Redirects.js +81 -0
  12. package/src/cloud/RevertPageRevision.js +18 -0
  13. package/src/cloud/Settings.js +30 -0
  14. package/src/cloud/Tags.js +80 -0
  15. package/src/cloud/documents/articlesDocuments.js +608 -0
  16. package/src/cloud/documents/categoryDocuments.js +64 -0
  17. package/src/cloud/documents/common.js +84 -0
  18. package/src/cloud/documents/pagesDocuments.js +245 -0
  19. package/src/cloud/documents/redirectsDocuments.js +82 -0
  20. package/src/cloud/documents/settingsDocuments.js +23 -0
  21. package/src/cloud/documents/tagsDocuments.js +63 -0
  22. package/src/handlers/PageCloneHandler.js +17 -0
  23. package/src/handlers/PageCloneHandler.test/create.test.js +55 -0
  24. package/src/handlers/PageDeleteHandler.js +68 -0
  25. package/src/handlers/PageDeleteHandler.test/create.test.js +112 -0
  26. package/src/handlers/PageDeleteHandler.test/create_form.test.js +54 -0
  27. package/src/handlers/PageRevisionsCodesHandler.js +83 -0
  28. package/src/handlers/PageRevisionsCodesHandler.test/edit_form.test.js +56 -0
  29. package/src/handlers/PageRevisionsCodesHandler.test/update.test.js +137 -0
  30. package/src/handlers/PageRevisionsGeneralHandler.js +86 -0
  31. package/src/handlers/PageRevisionsGeneralHandler.test/edit_form.test.js +62 -0
  32. package/src/handlers/PageRevisionsGeneralHandler.test/update.test.js +147 -0
  33. package/src/handlers/PageRevisionsHandler.js +47 -0
  34. package/src/handlers/PageRevisionsHandler.test/create.test.js +67 -0
  35. package/src/handlers/PageRevisionsHandler.test/destroy.test.js +54 -0
  36. package/src/handlers/PageRevisionsLockHandler.js +54 -0
  37. package/src/handlers/PageRevisionsMetaHandler.js +83 -0
  38. package/src/handlers/PageRevisionsMetaHandler.test/edit_form.test.js +57 -0
  39. package/src/handlers/PageRevisionsMetaHandler.test/update.test.js +141 -0
  40. package/src/handlers/PageRevisionsTagsHandler.js +80 -0
  41. package/src/handlers/PageRevisionsTagsHandler.test/edit_form.test.js +69 -0
  42. package/src/handlers/PageRevisionsTagsHandler.test/update.test.js +141 -0
  43. package/src/handlers/PageRevisionsUnlockHandler.js +22 -0
  44. package/src/handlers/PageRevisionsVisualsHandler.js +77 -0
  45. package/src/handlers/PageRevisionsVisualsHandler.test/edit_form.test.js +55 -0
  46. package/src/handlers/PageRevisionsVisualsHandler.test/update.test.js +129 -0
  47. package/src/handlers/PageRevisionsWidgetsHandler.js +41 -0
  48. package/src/handlers/PageRevisionsWidgetsHandler.test/show.test.js +47 -0
  49. package/src/handlers/PageRevisionsWidgetsHandler.test/update.test.js +104 -0
  50. package/src/handlers/PageTranslationsHandler.js +91 -0
  51. package/src/handlers/PageURLsHandler.js +69 -0
  52. package/src/handlers/PageURLsHandler.test/create.test.js +157 -0
  53. package/src/handlers/PageURLsHandler.test/create_form.test.js +51 -0
  54. package/src/handlers/PagesHandler.js +114 -0
  55. package/src/handlers/PagesHandler.test/create.test.js +140 -0
  56. package/src/handlers/PagesHandler.test/create_form.test.js +41 -0
  57. package/src/handlers/PagesHandler.test/index.test.js +49 -0
  58. package/src/handlers/PagesHandler.test/show.test.js +103 -0
  59. package/src/handlers/PublishPageRevisionHandler.js +84 -0
  60. package/src/handlers/PublishPageRevisionHandler.test/create.test.js +136 -0
  61. package/src/handlers/PublishPageRevisionHandler.test/create_form.test.js +47 -0
  62. package/src/handlers/RedirectsHandler.js +124 -0
  63. package/src/handlers/RedirectsHandler.test/create.test.js +84 -0
  64. package/src/handlers/RedirectsHandler.test/create_form.test.js +29 -0
  65. package/src/handlers/RedirectsHandler.test/destroy.test.js +54 -0
  66. package/src/handlers/RedirectsHandler.test/edit_form.test.js +38 -0
  67. package/src/handlers/RedirectsHandler.test/index.test.js +42 -0
  68. package/src/handlers/RedirectsHandler.test/show.test.js +41 -0
  69. package/src/handlers/RedirectsHandler.test/update.test.js +89 -0
  70. package/src/handlers/RevertPageRevisionHandler.js +26 -0
  71. package/src/handlers/RevertPageRevisionHandler.test/create.test.js +80 -0
  72. package/src/handlers/SettingsGeneralHandler.js +13 -0
  73. package/src/handlers/SettingsLayoutHandler.js +51 -0
  74. package/src/handlers/SettingsMetaHandler.js +52 -0
  75. package/src/handlers/TagsHandler.js +113 -0
  76. package/src/helpers/FormBuilder.js +12 -4
  77. package/src/index.js +33 -0
  78. package/src/setupAuthentication.js +3 -0
  79. package/src/styles/application.css +55 -19
  80. package/src/views/layouts/application.ejs +30 -31
  81. package/src/views/layouts/header.ejs +6 -7
  82. package/src/views/page_deletes/create_form.ejs +55 -0
  83. package/src/views/page_lock/create_form.ejs +35 -0
  84. package/src/views/page_publish/_form.ejs +57 -0
  85. package/src/views/page_publish/create_form.ejs +11 -0
  86. package/src/views/page_revisions/_nav.ejs +40 -0
  87. package/src/views/page_revisions/codes/edit_form.ejs +39 -0
  88. package/src/views/page_revisions/general/edit_form.ejs +69 -0
  89. package/src/views/page_revisions/meta/edit_form.ejs +41 -0
  90. package/src/views/page_revisions/tags/edit_form.ejs +64 -0
  91. package/src/views/page_revisions/visuals/edit_form.ejs +42 -0
  92. package/src/views/page_translations/_form.ejs +76 -0
  93. package/src/views/page_translations/create_form.ejs +11 -0
  94. package/src/views/page_urls/create_form.ejs +31 -0
  95. package/src/views/pages/_form.ejs +84 -0
  96. package/src/views/pages/create_form.ejs +9 -0
  97. package/src/views/pages/edit_form.ejs +3 -0
  98. package/src/views/pages/index.ejs +161 -0
  99. package/src/views/pages/show.ejs +281 -0
  100. package/src/views/redirects/_form.ejs +40 -0
  101. package/src/views/redirects/create_form.ejs +9 -0
  102. package/src/views/redirects/edit_form.ejs +9 -0
  103. package/src/views/redirects/index.ejs +75 -1
  104. package/src/views/redirects/show.ejs +60 -0
  105. package/src/views/settings/_nav.ejs +28 -0
  106. package/src/views/settings/general/edit_form.ejs +26 -0
  107. package/src/views/settings/layout/edit_form.ejs +22 -0
  108. package/src/views/settings/meta/edit_form.ejs +38 -0
  109. package/src/views/tags/_form.ejs +39 -0
  110. package/src/views/tags/create_form.ejs +9 -0
  111. package/src/views/tags/edit_form.ejs +9 -0
  112. package/src/views/tags/index.ejs +43 -0
  113. package/test/factory.js +91 -0
  114. package/test/handlersHelper.js +9 -0
@@ -0,0 +1,161 @@
1
+ <div class="page-wrapper">
2
+ <div class="page-title">
3
+ <h1 class="heading-1">Pages</h1>
4
+
5
+ <div class="actions-toolbar">
6
+ <a href="<%= `/${_context.locale}/pages/new` %>" class="btn-primary">
7
+ Create new
8
+ </a>
9
+
10
+ <button type="button" data-toggle-sidebar="1" class="p-2.5 rounded hover:bg-neutral-200 transition-all duration-300 ease-in-out dark:hover:bg-neutral-700">
11
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
12
+ <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
13
+ </svg>
14
+ </button>
15
+ </div>
16
+ </div>
17
+
18
+ <table class="data-grid">
19
+ <thead>
20
+ <tr>
21
+ <th><%- sort_button("Title", "title", _context) %></th>
22
+ <th>Visibility</th>
23
+ <th><%- sort_button("Last change", "updated_at", _context) %></th>
24
+ <th>Translations</th>
25
+ </tr>
26
+ </thead>
27
+
28
+ <tbody>
29
+ <% items.list.forEach((item, idx)=> { %>
30
+ <tr>
31
+ <td class="w-1/2">
32
+ <a href="<%= `/${_context.locale}/pages/${item.id}` %>" class="btn-text mr-1">
33
+ <%= item.title %>
34
+ </a>
35
+ <% if (item.revisionsCount > 0) { %>
36
+ <span class="bg-orange-100 chip text-orange-700 dark:bg-orange-900/50 dark:text-orange-300"><%= item.revisionsCount !== 1 ? `${item.revisionsCount} open revisions` : 'Open revision' %></span>
37
+ <% } %>
38
+
39
+ <div class="mt-1">
40
+ <a href="<%= `https://localhost:3000/${_context.locale}/${item.slug}` %>" class="body-3 text-raketa-foreground/50" target="_blank">
41
+ /<%= item.slug %>
42
+ </a>
43
+ </div>
44
+ </td>
45
+
46
+ <td>
47
+ <div class="flex itemx-center gap-1 whitespace-nowrap">
48
+ <span class="text-raketa-foreground/30" title="<%= visibilityOptions.find(opt => opt[0] === item.visibility)[1] %>">
49
+ <% if (item.visibility === 'public') { %>
50
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
51
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
52
+ </svg>
53
+
54
+ <% } %>
55
+ <% if (item.visibility === 'private') { %>
56
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
57
+ <path d="M8.5 4.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0ZM10.9 12.006c.11.542-.348.994-.9.994H2c-.553 0-1.01-.452-.902-.994a5.002 5.002 0 0 1 9.803 0ZM14.002 12h-1.59a2.556 2.556 0 0 0-.04-.29 6.476 6.476 0 0 0-1.167-2.603 3.002 3.002 0 0 1 3.633 1.911c.18.522-.283.982-.836.982ZM12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
58
+ </svg>
59
+ <% } %>
60
+ <% if (item.visibility === 'unlisted') { %>
61
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
62
+ <path fill-rule="evenodd" d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l10.5 10.5a.75.75 0 1 0 1.06-1.06l-1.322-1.323a7.012 7.012 0 0 0 2.16-3.11.87.87 0 0 0 0-.567A7.003 7.003 0 0 0 4.82 3.76l-1.54-1.54Zm3.196 3.195 1.135 1.136A1.502 1.502 0 0 1 9.45 8.389l1.136 1.135a3 3 0 0 0-4.109-4.109Z" clip-rule="evenodd" />
63
+ <path d="m7.812 10.994 1.816 1.816A7.003 7.003 0 0 1 1.38 8.28a.87.87 0 0 1 0-.566 6.985 6.985 0 0 1 1.113-2.039l2.513 2.513a3 3 0 0 0 2.806 2.806Z" />
64
+ </svg>
65
+ <% } %>
66
+ <% if (item.visibility === 'protected') { %>
67
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
68
+ <path fill-rule="evenodd" d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z" clip-rule="evenodd" />
69
+ </svg>
70
+ <% } %>
71
+ </span>
72
+
73
+ <%= visibilityOptions.find(opt => opt[0] === item.visibility)[1] %>
74
+ </div>
75
+ </td>
76
+
77
+ <td>
78
+ <div class="body-3 text-raketa-foreground/50">
79
+ <div class="flex items-center gap-1 mb-0.5">
80
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 text-raketa-foreground">
81
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
82
+ </svg>
83
+
84
+ Jan 20, 2024
85
+ </div>
86
+
87
+ <div class="flex items-center gap-1" title="Yana Dimitrova">
88
+ <img src="https://lh3.googleusercontent.com/a/ACg8ocLRrXdEsbxWg9AMWcgEY9Gfs75-xp7dYsnanB9-u-ZS3A=s96-c" class="size-4 rounded-full" alt="Yana Dimitrova">
89
+ Yana D.
90
+ </div>
91
+ </div>
92
+ </td>
93
+
94
+ <td>
95
+ <div class="flex flex-wrap items-center gap-1">
96
+ <% Object.keys(_context.locales).forEach(key => { %>
97
+ <% const { name, _code, flag} = _context.locales[key] %>
98
+ <% if (key === _context.locale) { %>
99
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="size-6 rounded-full border dark:border-neutral-700" />
100
+ <% } else if (item.translations[key]) { %>
101
+ <a href="<%= `/${key}/pages/${item.translations[key].id}` %>">
102
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="size-6 rounded-full border dark:border-neutral-700" />
103
+ </a>
104
+ <% } else { %>
105
+ <a href="<%= `/${_context.locale}/pages/${item.id}/page_translations/new?target_locale=${key}` %>">
106
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="<%= [" transition-all ease-in-out duration-300 size-6 rounded-full border hover:opacity-100 dark:border-neutral-700", key === _context.locale ? "opacity-100" : "opacity-10"].join(" ") %>" />
107
+ </a>
108
+ <% } %>
109
+ <% }); %>
110
+ </div>
111
+ </td>
112
+ </tr>
113
+ <% }); %>
114
+ </tbody>
115
+ </table>
116
+
117
+ <%- pagination(items.pagination, _context) %>
118
+ </div>
119
+
120
+ <div data-sidebar="1" class="search-sidebar <%= Object.keys(_context.query).some(k => k.startsWith('f:')) ? '' : 'hidden' %>">
121
+ <span class="heading-2">Search by</span>
122
+
123
+ <form action="<%= `/${_context.locale}/pages` %>" method="GET" class="form mt-4">
124
+ <input type="hidden" name="order_direction" value="<%= _context.query.order_direction %>" />
125
+ <input type="hidden" name="order_by" value="<%= _context.query.order_by %>" />
126
+
127
+ <div class="mb-4">
128
+ <label for="keyword" class="font-semibold hover:cursor-pointer body-3">Keyword</label>
129
+ <input type="text" name="f:keyword" value="<%= _context.query["f:keyword"] %>" />
130
+ </div>
131
+
132
+ <div class="mb-4">
133
+ <label for="" class="font-semibold hover:cursor-pointer body-3">Tags</label>
134
+ <div class="checkbox-group mb-4">
135
+ <% tags.forEach((item,idx)=> { %>
136
+ <div class="flex items-center gap-1">
137
+ <% if (Array.from(_context.query["f:tags"] || []).includes(item.id)) { %>
138
+ <input type="checkbox" name="f:tags" id='tag-<%= item.id %>' checked value="<%= item.id %>" class="hover:cursor-pointer">
139
+ <% } else { %>
140
+ <input type="checkbox" name="f:tags" id='tag-<%= item.id %>' value="<%= item.id %>" class="hover:cursor-pointer">
141
+ <% } %>
142
+ <label for='tag-<%= item.id %>' class="body-2 hover:cursor-pointer"><%= item.name %></label>
143
+ </div>
144
+ <% }); %>
145
+ </div>
146
+ </div>
147
+
148
+ <div class="btn-toolbar">
149
+ <button type="submit" class="btn-primary">Search</button>
150
+ <a href="<%= `/${_context.locale}/pages` %>" class="btn-text">Clear search</a>
151
+ </div>
152
+ </form>
153
+ </div>
154
+
155
+ <script>
156
+ const sidebar = document.querySelector("[data-sidebar]");
157
+ const searchToggle = document.querySelector('[data-toggle-sidebar]');
158
+ searchToggle.addEventListener('click', () => {
159
+ sidebar.classList.toggle('hidden');
160
+ });
161
+ </script>
@@ -0,0 +1,281 @@
1
+ <div class="page-wrapper">
2
+ <div class="page-title">
3
+ <div class="flex items-center gap-4">
4
+ <h1 class="heading-1"><%= page.title %></h1>
5
+ <div class="flex flex-wrap items-center gap-1">
6
+ <% Object.keys(_context.locales).forEach(key => { %>
7
+ <% const { name, _code, flag} = _context.locales[key] %>
8
+ <% if (key === _context.locale) { %>
9
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="size-6 rounded-full border dark:border-neutral-700" />
10
+ <% } else if (page.translations[key]) { %>
11
+ <a href="<%= `/${key}/pages/${page.translations[key].id}` %>">
12
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="size-6 rounded-full border dark:border-neutral-700" />
13
+ </a>
14
+ <% } else { %>
15
+ <a href="<%= `/${_context.locale}/pages/${page.id}/page_translations/new?target_locale=${key}` %>">
16
+ <img title="<%= name %>" src="<%= flag %>" alt="<%= name %>" class="<%= [" transition-all ease-in-out duration-300 size-6 rounded-full border hover:opacity-100 dark:border-neutral-700", key === _context.locale ? "opacity-100" : "opacity-10"].join(" ") %>" />
17
+ </a>
18
+ <% } %>
19
+ <% }); %>
20
+ </div>
21
+ </div>
22
+
23
+ <div class="actions-toolbar">
24
+ <form method="POST" action="<%= `/${_context.locale}/pages/${page.id}/page_revisions` %>" class="inline-block">
25
+ <button type="submit" class="btn-primary">
26
+ Start revision
27
+ </button>
28
+ </form>
29
+
30
+ <a href="<%= `/${_context.locale}/pages/${page.id}/page_urls/new` %>" class="btn-text has-icon">
31
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
32
+ <path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
33
+ </svg>
34
+
35
+ Change URL
36
+ </a>
37
+
38
+ <form action="<%= `/${_context.locale}/pages/${page.id}/clones` %>" method="post" class="inline-block">
39
+ <button type="submit" class="btn-text has-icon">
40
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
41
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
42
+ </svg>
43
+
44
+ Clone page
45
+ </button>
46
+ </form>
47
+
48
+ <a href="<%= `/${_context.locale}/pages/${page.id}/page_deletes/new` %>" class="btn-danger has-icon">
49
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
50
+ <path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
51
+ </svg>
52
+
53
+ Delete page
54
+ </a>
55
+ </div>
56
+ </div>
57
+
58
+ <% if (revisions.length) { %>
59
+ <div class="pb-8">
60
+ <h4 class="pb-4 heading-3 flex items-center gap-2">
61
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4 text-raketa-primary">
62
+ <path fill-rule="evenodd" d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z" clip-rule="evenodd" />
63
+ <path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
64
+ </svg>
65
+
66
+ Open revisions
67
+ </h4>
68
+
69
+ <table class="data-grid alt">
70
+ <thead>
71
+ <tr>
72
+ <th>Title</th>
73
+ <th>Editor</th>
74
+ <th>Last change</th>
75
+ <th>&nbsp;</th>
76
+ </tr>
77
+ </thead>
78
+
79
+ <tbody>
80
+ <% revisions.forEach((revision, idx)=> { %>
81
+ <tr class="border-b border-mono-25">
82
+ <td class="py-4 pl-2 w-full">
83
+ <div class="flex gap-1 items-center">
84
+ <% if (revision.meta && revision.meta.locked) { %>
85
+ <div title="<%= revision.meta.lockMessage %>">
86
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4">
87
+ <path fill-rule="evenodd" d="M10 1a4.5 4.5 0 0 0-4.5 4.5V9H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2h-.5V5.5A4.5 4.5 0 0 0 10 1Zm3 8V5.5a3 3 0 1 0-6 0V9h6Z" clip-rule="evenodd" />
88
+ </svg>
89
+ </div>
90
+ <% } %>
91
+
92
+ <a href="/remote-login?redirect_to=/admin/page-revisions/<%= revision.id %>/builder" target="_blank" class="btn-text has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
93
+ <%= revision.title %>
94
+ </a>
95
+ </div>
96
+ </td>
97
+
98
+ <td class="whitespace-nowrap">
99
+ <div class="body-3 text-raketa-foreground/50">
100
+ <div class="flex items-center gap-1" title="Yana Dimitrova">
101
+ <img src="<%= revision.meta.operatorMeta.avatar %>" class="size-4 rounded-full">
102
+ <%= revision.meta.operatorMeta.name %>
103
+ </div>
104
+ </div>
105
+ </td>
106
+
107
+ <td class="whitespace-nowrap">
108
+ <div class="body-3 text-raketa-foreground/50">
109
+ <div class="flex items-center gap-1">
110
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 text-raketa-foreground">
111
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
112
+ </svg>
113
+
114
+ <%= (new Date(revision.insertedAt)).toLocaleString() %>
115
+ </div>
116
+ </div>
117
+ </td>
118
+
119
+ <td class="whitespace-nowrap">
120
+ <div class="actions-toolbar justify-end">
121
+ <a href="/remote-login?redirect_to=/admin/page-revisions/<%= revision.id %>/builder" target="_blank" class="btn-text has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
122
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
123
+ <path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125" />
124
+ </svg>
125
+ Edit content
126
+ </a>
127
+
128
+ <a href="<%= `/${_context.locale}/page_revisions/${revision.id}/page_publish/new` %>" class="btn-text has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
129
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
130
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" />
131
+ </svg>
132
+
133
+ Publish
134
+ </a>
135
+
136
+ <details class="unstyled-details">
137
+ <summary class="cursor-pointer">
138
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
139
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z" />
140
+ </svg>
141
+ </summary>
142
+
143
+ <div class="absolute border bg-raketa-background p-4 right-4 z-20 mt-2 flex flex-col gap-4 shadow-md rounded dark:border-neutral-500">
144
+ <a href="<%= `/${_context.locale}/page_revisions/${revision.id}/general/edit` %>" class="btn-text has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
145
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
146
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 0 1 1.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.559.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.894.149c-.424.07-.764.383-.929.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 0 1-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.398.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 0 1-.12-1.45l.527-.737c.25-.35.272-.806.108-1.204-.165-.397-.506-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.108-1.204l-.526-.738a1.125 1.125 0 0 1 .12-1.45l.773-.773a1.125 1.125 0 0 1 1.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894Z" />
147
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
148
+ </svg>
149
+
150
+ Edit settings
151
+ </a>
152
+
153
+ <a href="/remote-login?redirect_to=/admin/page-revisions/<%= revision.id %>/preview" target="_blank" class="btn-text has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
154
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
155
+ <path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
156
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
157
+ </svg>
158
+
159
+ Preview
160
+ </a>
161
+
162
+ <% if (revision.meta && revision.meta.locked) { %>
163
+ <form action="<%= `/${_context.locale}/page_revisions/${revision.id}/page_unlock` %>" method="post" class="inline-flex gap-2 items-center">
164
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
165
+ <path stroke-linecap="round" stroke-linejoin="round" d="M13.5 10.5V6.75a4.5 4.5 0 1 1 9 0v3.75M3.75 21.75h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H3.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" />
166
+ </svg>
167
+ <%- f.submit('Unlock', { class: 'btn-text' }) %>
168
+ </form>
169
+ <% } else { %>
170
+ <a href="<%= `/${_context.locale}/page_revisions/${revision.id}/page_lock/new` %>" class="btn-text has-icon">
171
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
172
+ <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" />
173
+ </svg>
174
+ Lock
175
+ </a>
176
+ <% } %>
177
+
178
+ <button hx-delete="<%= `/${_context.locale}/page_revisions/${revision.id}` %>" hx-confirm="Are you sure?" type="button" class="btn-danger has-icon <%= revision.meta && revision.meta.locked ? 'cursor-not-allowed text-gray-400 pointer-events-none' : '' %>">
179
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
180
+ <path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
181
+ </svg>
182
+
183
+ Delete
184
+ </button>
185
+ </div>
186
+ </details>
187
+ </div>
188
+ </td>
189
+ </tr>
190
+ <% }); %>
191
+ </tbody>
192
+ </table>
193
+ </div>
194
+ <% } %>
195
+
196
+ <div>
197
+ <h4 class="pb-4 heading-3 items-center gap-2 flex">
198
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4 text-raketa-primary">
199
+ <path d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v.75c0 1.036.84 1.875 1.875 1.875h17.25c1.035 0 1.875-.84 1.875-1.875v-.75C22.5 3.839 21.66 3 20.625 3H3.375Z" />
200
+ <path fill-rule="evenodd" d="m3.087 9 .54 9.176A3 3 0 0 0 6.62 21h10.757a3 3 0 0 0 2.995-2.824L20.913 9H3.087Zm6.163 3.75A.75.75 0 0 1 10 12h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" />
201
+ </svg>
202
+
203
+ History of changes
204
+ </h4>
205
+
206
+ <table class="data-grid">
207
+ <thead>
208
+ <tr>
209
+ <th>Title</th>
210
+ <th>Date of change</th>
211
+ <th>Published by</th>
212
+ <th>&nbsp;</th>
213
+ </tr>
214
+ </thead>
215
+
216
+ <tbody>
217
+ <% const maxVersion = Math.max(...changes.map((ch) => ch.version)); %>
218
+ <% changes.forEach((change, idx)=> { %>
219
+ <tr class="border-b border-mono-25">
220
+ <td class="py-4 pl-2">
221
+ <div class="flex items-center gap-2">
222
+ <%= [change.title, change.description].filter(Boolean).join(": ") %>
223
+
224
+ <% if (change.source === "revert") { %>
225
+ <img src="/static/_assets/revert.svg" alt="Revert version" class="size-4">
226
+ <% } %>
227
+ </div>
228
+ </td>
229
+
230
+ <td class="whitespace-nowrap">
231
+ <div class="body-3 text-raketa-foreground/50">
232
+ <div class="flex items-center gap-1">
233
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 text-raketa-foreground">
234
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
235
+ </svg>
236
+
237
+ <%= (new Date(change.insertedAt)).toLocaleString() %>
238
+ </div>
239
+ </div>
240
+ </td>
241
+
242
+ <td class="whitespace-nowrap">
243
+ <div class="body-3 text-raketa-foreground/50">
244
+ <div class="flex items-center gap-1" title="Yana Dimitrova">
245
+ <img src="<%= change.operatorMeta.avatar %>" class="size-4 rounded-full">
246
+ <%= change.operatorMeta.name %>
247
+ </div>
248
+ </div>
249
+ </td>
250
+
251
+ <td>
252
+ <div class="actions-toolbar justify-end">
253
+ <% if (maxVersion !== change.version) { %>
254
+ <form method="POST" action="<%= `/${_context.locale}/pages/${page.id}/page_changes/reverts` %>" class="inline-block">
255
+ <input type="hidden" name="version" value=<%= change.version %>>
256
+ <button type="submit" class="btn-text has-icon">
257
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
258
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 15 3 9m0 0 6-6M3 9h12a6 6 0 0 1 0 12h-3" />
259
+ </svg>
260
+
261
+ Revert
262
+ </button>
263
+ </form>
264
+ <% } %>
265
+
266
+ <a href="#" class="btn-text has-icon">
267
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
268
+ <path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
269
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
270
+ </svg>
271
+
272
+ Preview
273
+ </a>
274
+ </div>
275
+ </td>
276
+ </tr>
277
+ <% }); %>
278
+ </tbody>
279
+ </table>
280
+ </div>
281
+ </div>
@@ -0,0 +1,40 @@
1
+ <% form_for({ redirect }, errors, (f) => { %>
2
+ <form action="<%= redirect?.id ? `/${_context.locale}/redirects/${redirect.id}` : `/${_context.locale}/redirects` %>" method="post" class="form">
3
+ <%- f.field('redirect.url') %>
4
+ <%- f.field('redirect.destination') %>
5
+ <%- f.field('redirect.httpResponseCode') %>
6
+ <%- f.field('redirect.note') %>
7
+
8
+ <div class="form-actions">
9
+ <div class="btn-toolbar">
10
+ <%- f.submit('Save', { class: 'btn-primary' }) %>
11
+
12
+ <a href="<%= `/${_context.locale}/redirects` %>" class="btn-secondary">Cancel</a>
13
+ </div>
14
+
15
+ <% if (redirect?.id) {%>
16
+ <button hx-delete="<%= `/${_context.locale}/redirects/${redirect.id}` %>" hx-confirm="Are you sure?" class="btn-danger has-icon">
17
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
18
+ <path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
19
+ </svg>
20
+
21
+ Delete
22
+ </button>
23
+ <% } %>
24
+ </div>
25
+ </form>
26
+ <% }) %>
27
+
28
+ <div>
29
+ <div class="help">
30
+ <div class="mb-2 flex items-center gap-1">
31
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
32
+ <path stroke-linecap="round" stroke-linejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
33
+ </svg>
34
+
35
+ <h3 class="heading-3">Contextual help</h3>
36
+ </div>
37
+
38
+ <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore nostrum fugit sint sunt distinctio ut cum, corrupti ad error, quo labore minima. Reprehenderit quasi maxime repudiandae voluptatum commodi saepe molestiae?</p>
39
+ </div>
40
+ </div>
@@ -0,0 +1,9 @@
1
+ <div class="page-wrapper">
2
+ <div class="page-title">
3
+ <h1 class="heading-1">Create new</h1>
4
+ </div>
5
+
6
+ <div class="grid gap-4 md:grid-cols-3">
7
+ <%- include('_form.ejs') %>
8
+ </div>
9
+ </div>
@@ -0,0 +1,9 @@
1
+ <div class="page-wrapper">
2
+ <div class="page-title">
3
+ <h1 class="heading-1">Edit</h1>
4
+ </div>
5
+
6
+ <div class="grid gap-4 md:grid-cols-3">
7
+ <%- include('_form.ejs') %>
8
+ </div>
9
+ </div>
@@ -1 +1,75 @@
1
- <h1>Hello from package</h1>
1
+ <div class="page-wrapper">
2
+ <div class="page-title">
3
+ <h1 class="heading-1">Redirects</h1>
4
+
5
+ <div class="actions-toolbar">
6
+ <form action="<%= `/${_context.locale}/redirects` %>" method="GET" class="inline-form">
7
+ <button type="submit">
8
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4">
9
+ <path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM2 9a7 7 0 1 1 12.452 4.391l3.328 3.329a.75.75 0 1 1-1.06 1.06l-3.329-3.328A7 7 0 0 1 2 9Z" clip-rule="evenodd" />
10
+ </svg>
11
+ </button>
12
+
13
+ <input type="search" placeholder="Search by keyword..." name="f:keyword" value="<%= _context.query["f:keyword"] %>" />
14
+
15
+ <input type="hidden" name="order_direction" value="<%= _context.query.order_direction %>" />
16
+ <input type="hidden" name="order_by" value="<%= _context.query.order_by %>" />
17
+
18
+ </form>
19
+
20
+ <a href="<%= `/${_context.locale}/redirects/new` %>" class="btn-primary">
21
+ Create new
22
+ </a>
23
+ </div>
24
+ </div>
25
+
26
+ <table class="data-grid">
27
+ <thead>
28
+ <th><%- sort_button("Original URL", "url", _context) %></th>
29
+ <th><%- sort_button("Destination", "destination", _context) %></th>
30
+ <th class="number"><%- sort_button("HTTP Code", "http_response_code", _context) %></th>
31
+ <th class="bool">Note</th>
32
+ <th><%- sort_button("Updated at", "updated_at", _context) %></th>
33
+ <th class="actions">&nbsp;</th>
34
+ </thead>
35
+
36
+ <tbody>
37
+ <% items.list.forEach((item, idx)=> { %>
38
+ <tr>
39
+ <td>
40
+ <a href="<%= `/${_context.locale}/redirects/${item.id}/edit` %>" class="btn-text"><%= item.url %></a>
41
+ </td>
42
+ <td><%= item.destination %></td>
43
+ <td class="number"><%= item.httpResponseCode %></td>
44
+ <td class="bool">
45
+ <% if (item.note) { %>
46
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 text-emerald-600">
47
+ <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
48
+ </svg>
49
+ <% } %>
50
+ </td>
51
+ <td>
52
+ <div class="with-icon">
53
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
54
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
55
+ </svg>
56
+
57
+ <%= new Date(item.updatedAt).toLocaleString('en-GB', { dateStyle: "short", timeStyle: "short" }) %>
58
+ </div>
59
+ </td>
60
+ <td class="actions">
61
+ <a href="<%= `/${_context.locale}/redirects/${item.id}` %>" class="btn-text has-icon">
62
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
63
+ <path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0-3.75-3.75M17.25 21 21 17.25" />
64
+ </svg>
65
+
66
+ History
67
+ </a>
68
+ </td>
69
+ </tr>
70
+ <% }); %>
71
+ </tbody>
72
+ </table>
73
+
74
+ <%- pagination(items.pagination, _context) %>
75
+ </div>