@raketa-cloud/admin 0.0.59 → 0.0.64

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 (75) hide show
  1. package/package.json +1 -1
  2. package/src/cloud/Articles.js +3 -42
  3. package/src/cloud/Events.js +3 -29
  4. package/src/cloud/Pages.js +3 -29
  5. package/src/cloud/documents/articlesDocuments.js +11 -14
  6. package/src/cloud/documents/common.js +14 -0
  7. package/src/cloud/documents/eventsDocuments.js +13 -14
  8. package/src/cloud/documents/pagesDocuments.js +10 -14
  9. package/src/handlers/articles/DeletesHandler.js +1 -0
  10. package/src/handlers/articles/URLsHandler.js +1 -0
  11. package/src/handlers/events/DeletesHandler.js +1 -0
  12. package/src/handlers/events/URLsHandler.js +1 -0
  13. package/src/handlers/page_revisions/TagHandler.js +40 -0
  14. package/src/handlers/pages/DeletesHandler.js +1 -0
  15. package/src/handlers/pages/URLsHandler.js +1 -0
  16. package/src/helpers/FormBuilder.js +2 -2
  17. package/src/styles/application.css +4 -0
  18. package/src/tests/handlers/articles/DeletesHandler.test/create_form.test.js +2 -1
  19. package/src/tests/handlers/articles/URLsHandler.test/create_form.test.js +2 -1
  20. package/src/tests/handlers/events/DeletesHandler.test/create_form.test.js +2 -1
  21. package/src/tests/handlers/events/URLsHandler.test/create_form.test.js +2 -1
  22. package/src/tests/handlers/page_revisions/TagHandler.test/edit_form.test.js +38 -3
  23. package/src/tests/handlers/page_revisions/TagHandler.test/update.test.js +39 -3
  24. package/src/tests/handlers/pages/DeletesHandler.test/create.test.js +1 -1
  25. package/src/tests/handlers/pages/DeletesHandler.test/create_form.test.js +1 -0
  26. package/src/tests/handlers/pages/URLsHandler.test/create_form.test.js +1 -0
  27. package/src/views/article_revisions/codes/edit_form.ejs +5 -16
  28. package/src/views/article_revisions/general/_form.ejs +2 -2
  29. package/src/views/article_revisions/general/edit_form.ejs +5 -16
  30. package/src/views/article_revisions/locks/create_form.ejs +5 -3
  31. package/src/views/article_revisions/meta/edit_form.ejs +8 -19
  32. package/src/views/article_revisions/publishes/_form.ejs +19 -36
  33. package/src/views/article_revisions/tags/edit_form.ejs +5 -16
  34. package/src/views/article_revisions/visuals/edit_form.ejs +5 -16
  35. package/src/views/articles/_form.ejs +14 -19
  36. package/src/views/articles/deletes/create_form.ejs +18 -5
  37. package/src/views/articles/index.ejs +1 -19
  38. package/src/views/articles/show.ejs +1 -1
  39. package/src/views/articles/translations/_form.ejs +1 -15
  40. package/src/views/articles/urls/create_form.ejs +10 -5
  41. package/src/views/categories/_form.ejs +1 -15
  42. package/src/views/event_revisions/_nav.ejs +1 -1
  43. package/src/views/event_revisions/codes/edit_form.ejs +5 -16
  44. package/src/views/event_revisions/general/_form.ejs +2 -2
  45. package/src/views/event_revisions/general/edit_form.ejs +5 -16
  46. package/src/views/event_revisions/locks/create_form.ejs +5 -3
  47. package/src/views/event_revisions/meta/edit_form.ejs +8 -19
  48. package/src/views/event_revisions/publishes/_form.ejs +19 -36
  49. package/src/views/event_revisions/tags/edit_form.ejs +5 -16
  50. package/src/views/event_revisions/visuals/edit_form.ejs +5 -16
  51. package/src/views/events/_form.ejs +14 -19
  52. package/src/views/events/deletes/create_form.ejs +18 -5
  53. package/src/views/events/index.ejs +1 -19
  54. package/src/views/events/show.ejs +1 -1
  55. package/src/views/events/translations/_form.ejs +1 -15
  56. package/src/views/events/urls/create_form.ejs +10 -5
  57. package/src/views/page_revisions/_nav.ejs +1 -1
  58. package/src/views/page_revisions/codes/edit_form.ejs +4 -15
  59. package/src/views/page_revisions/general/_form.ejs +1 -1
  60. package/src/views/page_revisions/general/edit_form.ejs +4 -15
  61. package/src/views/page_revisions/locks/create_form.ejs +5 -3
  62. package/src/views/page_revisions/meta/edit_form.ejs +7 -18
  63. package/src/views/page_revisions/publishes/_form.ejs +19 -36
  64. package/src/views/page_revisions/tags/edit_form.ejs +9 -15
  65. package/src/views/page_revisions/visuals/edit_form.ejs +4 -15
  66. package/src/views/pages/_form.ejs +13 -18
  67. package/src/views/pages/deletes/create_form.ejs +17 -4
  68. package/src/views/pages/index.ejs +1 -19
  69. package/src/views/pages/show.ejs +3 -3
  70. package/src/views/pages/translations/create_form.ejs +13 -5
  71. package/src/views/pages/translations/index.ejs +6 -4
  72. package/src/views/pages/urls/create_form.ejs +9 -4
  73. package/src/views/redirects/_form.ejs +5 -19
  74. package/src/views/settings/meta/edit_form.ejs +4 -18
  75. package/src/views/tags/_form.ejs +2 -16
@@ -1,6 +1,7 @@
1
- import TagHandler from "../../../../handlers/page_revisions/TagHandler";
2
1
  import PageRevisions from "../../../../cloud/PageRevisions";
2
+ import Pages from "../../../../cloud/Pages";
3
3
  import Tags from "../../../../cloud/Tags";
4
+ import TagHandler from "../../../../handlers/page_revisions/TagHandler";
4
5
 
5
6
  import Factory from "../../../support/factory";
6
7
 
@@ -9,12 +10,20 @@ function buildCtx(revision, updateSettings) {
9
10
  client: {
10
11
  pageRevisions: new PageRevisions(() => ({}), "token"),
11
12
  tags: new Tags(() => ({}), "token"),
13
+ pages: new Pages(() => ({}), "token"),
12
14
  },
13
15
  params: { parent_id: revision.id },
14
16
  resourceParams: {
15
17
  resource: updateSettings,
16
18
  },
17
19
  locale: "en",
20
+ defaultLocale: "es",
21
+ locales: {
22
+ es: {
23
+ name: "Spanish",
24
+ code: "es",
25
+ },
26
+ },
18
27
  resourceConfig: {
19
28
  pageType: "page",
20
29
  pageTypePlural: "pages",
@@ -70,8 +79,22 @@ describe("Page revision tags settings", () => {
70
79
  Factory.build("tag", { id: 42 }),
71
80
  Factory.build("tag", { id: 69 }),
72
81
  ];
82
+ const spanishTags = [Factory.build("tag", { id: 100 })];
73
83
  const revision = Factory.build("page_revision", { tags: [tags[0]] });
74
84
 
85
+ const originalPage = Factory.build("page", {
86
+ id: "original-123",
87
+ translations: {
88
+ en: { id: "translated-123" },
89
+ es: { id: "es-translated-123" },
90
+ },
91
+ });
92
+
93
+ const spanishPage = Factory.build("page", {
94
+ id: "es-translated-123",
95
+ tags: spanishTags,
96
+ });
97
+
75
98
  const updateSettings = {
76
99
  tags: [tags.map((t) => t.id)],
77
100
  };
@@ -91,10 +114,19 @@ describe("Page revision tags settings", () => {
91
114
 
92
115
  const mockTags = jest
93
116
  .spyOn(ctx.client.tags, "all")
94
- .mockImplementation(() => ({
95
- list: tags,
117
+ .mockImplementation((locale) => ({
118
+ list: locale === "es" ? spanishTags : tags,
96
119
  }));
97
120
 
121
+ const mockPageFind = jest
122
+ .spyOn(ctx.client.pages, "find")
123
+ .mockImplementation((id, locale) => {
124
+ if (locale === "es") {
125
+ return spanishPage;
126
+ }
127
+ return originalPage;
128
+ });
129
+
98
130
  const result = await TagHandler.update(ctx);
99
131
 
100
132
  expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
@@ -108,6 +140,8 @@ describe("Page revision tags settings", () => {
108
140
  settings: revision.settings,
109
141
  });
110
142
  expect(mockTags).toHaveBeenCalledWith("en");
143
+ expect(mockPageFind).toHaveBeenCalledWith(revision.originalId, "en");
144
+ expect(mockPageFind).toHaveBeenCalledWith("es-translated-123", "es");
111
145
 
112
146
  expect(result).toEqual({
113
147
  assigns: {
@@ -120,6 +154,8 @@ describe("Page revision tags settings", () => {
120
154
  },
121
155
  tags: tags,
122
156
  _translations: [],
157
+ defaultLocaleTags: spanishTags,
158
+ defaultLocaleName: "Spanish",
123
159
  },
124
160
  });
125
161
  });
@@ -1,5 +1,5 @@
1
- import DeletesHandler from "../../../../handlers/pages/DeletesHandler";
2
1
  import Pages from "../../../../cloud/Pages";
2
+ import DeletesHandler from "../../../../handlers/pages/DeletesHandler";
3
3
  import Factory from "../../../support/factory";
4
4
  import { remoteConfig } from "../../../support/handlersHelper";
5
5
 
@@ -33,6 +33,7 @@ describe("Page delete", () => {
33
33
 
34
34
  expect(result).toEqual({
35
35
  assigns: {
36
+ page,
36
37
  resource: {
37
38
  id: page.id,
38
39
  redirect: "",
@@ -34,6 +34,7 @@ describe("Page change url", () => {
34
34
 
35
35
  expect(result).toEqual({
36
36
  assigns: {
37
+ page,
37
38
  resource: { slug: page.slug, id: page.id, redirect: null },
38
39
  errors: {},
39
40
  _translations: [],
@@ -1,6 +1,9 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Edit revision / Code customizations</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">Code customizations / </span>
5
+ <%= resource.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <%- render_partial('../_nav.ejs', { resource, active: 'codes' }) %>
@@ -32,19 +35,5 @@
32
35
  </form>
33
36
  <% }) %>
34
37
  </div>
35
-
36
- <div>
37
- <div class="help">
38
- <div class="flex items-center gap-1 mb-2">
39
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
40
- <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" />
41
- </svg>
42
-
43
- <h3 class="heading-3">Contextual help</h3>
44
- </div>
45
-
46
- <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>
47
- </div>
48
- </div>
49
38
  </div>
50
- </div>
39
+ </div>
@@ -1,7 +1,7 @@
1
1
  <% form_for({ resource }, errors, (f) => { %>
2
2
  <form action="<%= `/${_context.locale}/${_context.resourceConfig.articleType}_revisions/${resource.id}/general` %>" method="POST" class="form">
3
3
  <%- f.field('resource.title', { required: true }) %>
4
- <%- f.field('resource.slug', { label: "URL", required: true }) %>
4
+ <%- f.field('resource.slug', { label: "URL", required: true, hint: "Only lowercase letters, numbers and dash allowed. E.g: celebrate-friday-13" }) %>
5
5
  <%- f.field('resource.publishAt', { required: true, type: 'datetime-local', label: 'Publish at (UTC)' }) %>
6
6
  <%- f.field('resource.visibility', { as: 'select', required: true, options: visibilityOptions }) %>
7
7
 
@@ -14,4 +14,4 @@
14
14
  </div>
15
15
  </div>
16
16
  </form>
17
- <% }) %>
17
+ <% }) %>
@@ -1,6 +1,9 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Edit revision / General</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">General / </span>
5
+ <%= resource.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <%- render_partial('../_nav.ejs', { resource, active: 'general' }) %>
@@ -9,19 +12,5 @@
9
12
  <div>
10
13
  <%- render_partial('_form.ejs') %>
11
14
  </div>
12
-
13
- <div>
14
- <div class="help">
15
- <div class="flex items-center gap-1 mb-2">
16
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
17
- <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" />
18
- </svg>
19
-
20
- <h3 class="heading-3">Contextual help</h3>
21
- </div>
22
-
23
- <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>
24
- </div>
25
- </div>
26
15
  </div>
27
- </div>
16
+ </div>
@@ -25,11 +25,13 @@
25
25
  <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" />
26
26
  </svg>
27
27
 
28
- <h3 class="heading-3">Contextual help</h3>
28
+ <h3 class="heading-3">How to lock a revision?</h3>
29
29
  </div>
30
30
 
31
- <p>Locking a revision will prevent further changes to this version of the article. This action cannot be undone. Please provide a reason for locking this revision.</p>
31
+ <div class="markdown">
32
+ <p>You can lock a revision to prevent publishing or deletion by anyone, including yourself. When locking, provide a clear, informative message explaining the reason. Remember that while this feature is designed to facilitate collaboration, any user can still unlock the revision.</p>
33
+ </div>
32
34
  </div>
33
35
  </div>
34
36
  </div>
35
- </div>
37
+ </div>
@@ -1,6 +1,9 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Edit revision / Open graph & meta tags</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">OpenGraph & Meta tags / </span>
5
+ <%= resource.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <%- render_partial('../_nav.ejs', { resource, active: 'meta' }) %>
@@ -9,10 +12,10 @@
9
12
  <div>
10
13
  <% form_for({ resource }, errors, (f) => { %>
11
14
  <form action="<%= `/${_context.locale}/${_context.resourceConfig.articleType}_revisions/${resource.id}/meta` %>" method="POST" class="form">
12
- <%- f.field('resource.settings.meta.title', { required: true, label: "Meta title" }) %>
13
- <%- f.field('resource.settings.meta.description', { as: "textarea", required: true, label: "Meta description" }) %>
15
+ <%- f.field('resource.settings.meta.title', { required: true, label: "Meta title", hint: "Used for both meta and OpenGraph" }) %>
16
+ <%- f.field('resource.settings.meta.description', { as: "textarea", required: true, label: "Meta description", hint: "Used for both meta and OpenGraph" }) %>
14
17
 
15
- <raketa-image-picker name="resource[settings][meta][image]" value="<%= JSON.stringify(resource.settings?.meta?.image || {}) %>" label="Meta image"></raketa-image-picker>
18
+ <raketa-image-picker name="resource[settings][meta][image]" value="<%= JSON.stringify(resource.settings?.meta?.image || {}) %>" label="OpenGraph image"></raketa-image-picker>
16
19
 
17
20
  <div class="form-actions">
18
21
  <div class="btn-toolbar">
@@ -23,19 +26,5 @@
23
26
  </form>
24
27
  <% }) %>
25
28
  </div>
26
-
27
- <div>
28
- <div class="help">
29
- <div class="flex items-center gap-1 mb-2">
30
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
31
- <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" />
32
- </svg>
33
-
34
- <h3 class="heading-3">Contextual help</h3>
35
- </div>
36
-
37
- <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>
38
- </div>
39
- </div>
40
29
  </div>
41
- </div>
30
+ </div>
@@ -1,7 +1,11 @@
1
1
  <% form_for({ publishing }, errors, (f) => { %>
2
2
  <form action="<%= `/${_context.locale}/${_context.resourceConfig.articleType}_revisions/${publishing.id}/publishes` %>" method="post" class="form">
3
- <%- f.field('publishing.title'), { required: true } %>
4
- <%- f.field('publishing.description', { as: "textarea" }) %>
3
+ <% if (errors.slug) { %>
4
+ <div class="mb-4 text-red-500">A page with this URL already exists</div>
5
+ <% } %>
6
+
7
+ <%- f.field('publishing.title'), { required: true, hint: "Appears in the history of changes" } %>
8
+ <%- f.field('publishing.description', { as: "textarea", hint: "Provide optional details" }) %>
5
9
  <%- f.field('publishing.visibility', { as: 'select', required: true, options: visibilityOptions }) %>
6
10
  <%- f.field('publishing.username', { required: true }) %>
7
11
  <%- f.field('publishing.password', { required: true }) %>
@@ -16,42 +20,21 @@
16
20
  </form>
17
21
  <% }) %>
18
22
 
19
- <div>
20
- <div class="help">
21
- <div class="flex items-center gap-1 mb-2">
22
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
23
- <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" />
24
- </svg>
23
+ <script>
24
+ const visibilityEl = document.getElementById("f_publishing_visibility");
25
+ const usernameEl = document.getElementById("f_publishing_username").parentElement;
26
+ const passwordEl = document.getElementById("f_publishing_password").parentElement;
25
27
 
26
- <h3 class="heading-3">Contextual help</h3>
27
- </div>
28
+ function handleVisibilityChange(value) {
29
+ const isHidden = value !== "protected";
28
30
 
29
- <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>
30
- </div>
31
- </div>
31
+ usernameEl.classList.toggle('hidden', isHidden);
32
+ passwordEl.classList.toggle('hidden', isHidden);
32
33
 
33
- <script>
34
- function onVisibilityChange(event) {
35
- const newValue = event.target.value;
36
-
37
- if (newValue === "protected") {
38
- document.getElementById("f_username").parentElement.style.display = "block";
39
- document.getElementById("f_username").required = true;
40
- document.getElementById("f_password").parentElement.style.display = "block";
41
- document.getElementById("f_password").required = true;
42
- } else {
43
- document.getElementById("f_username").parentElement.style.display = "none";
44
- document.getElementById("f_username").required = false;
45
- document.getElementById("f_password").parentElement.style.display = "none";
46
- document.getElementById("f_password").required = false;
47
- }
34
+ usernameEl.querySelector('input').toggleAttribute('required', !isHidden);
35
+ passwordEl.querySelector('input').toggleAttribute('required', !isHidden);
48
36
  }
49
37
 
50
- onVisibilityChange({
51
- target: {
52
- value: '<%= publishing.visibility %>'
53
- }
54
- })
55
-
56
- document.getElementById("f_visibility").addEventListener("change", onVisibilityChange)
57
- </script>
38
+ visibilityEl.addEventListener("change", (e) => handleVisibilityChange(e.target.value));
39
+ handleVisibilityChange(visibilityEl.value);
40
+ </script>
@@ -1,6 +1,9 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Edit revision / Content tags</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">Tags / </span>
5
+ <%= resource.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <%- render_partial('../_nav.ejs', { resource, active: 'tags' }) %>
@@ -20,19 +23,5 @@
20
23
  </form>
21
24
  <% }) %>
22
25
  </div>
23
-
24
- <div>
25
- <div class="help">
26
- <div class="flex items-center gap-1 mb-2">
27
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
28
- <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" />
29
- </svg>
30
-
31
- <h3 class="heading-3">Contextual help</h3>
32
- </div>
33
-
34
- <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>
35
- </div>
36
- </div>
37
26
  </div>
38
- </div>
27
+ </div>
@@ -1,6 +1,9 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Edit revision / Visuals</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">Visuals / </span>
5
+ <%= resource.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <%- render_partial('../_nav.ejs', { resource, active: 'visuals' }) %>
@@ -20,19 +23,5 @@
20
23
  </form>
21
24
  <% }) %>
22
25
  </div>
23
-
24
- <div>
25
- <div class="help">
26
- <div class="flex items-center gap-1 mb-2">
27
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
28
- <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" />
29
- </svg>
30
-
31
- <h3 class="heading-3">Contextual help</h3>
32
- </div>
33
-
34
- <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>
35
- </div>
36
- </div>
37
26
  </div>
38
- </div>
27
+ </div>
@@ -1,17 +1,26 @@
1
1
  <% form_for({ resource }, errors, (f) => { %>
2
2
  <form action="<%= resource?.id ? `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}` : `/${_context.locale}/${_context.resourceConfig.articleTypePlural}` %>" method="post" class="form">
3
+ <h2 class="heading-2 mb-4">General</h2>
4
+
3
5
  <%- f.field('resource.title', { required: true }) %>
4
- <%- f.field('resource.slug', { label: "URL", required: true }) %>
6
+ <%- f.field('resource.slug', { label: "URL", required: true, hint: "Only lowercase letters, numbers and dash allowed. Ex: celebrate-friday-13" }) %>
5
7
  <%- f.field('resource.publishAt', { required: true, type: 'datetime-local', label: 'Publish at (UTC)' }) %>
6
8
  <%- f.field('resource.visibility', { as: 'select', required: true, options: visibilityOptions }) %>
7
9
 
8
10
  <%- include('../_shared/_credentials_input.ejs', { f, visibilityFieldId: "f_resource_visibility"}) %>
11
+
12
+ <h2 class="heading-2 my-4">Visuals</h2>
13
+
9
14
  <%- include('../_shared/_layout_input.ejs', { f }) %>
10
15
 
11
- <%- f.field('resource.settings.meta.title', { required: true, label: "Meta title" }) %>
12
- <%- f.field('resource.settings.meta.description', { as: "textarea", required: true, label: "Meta description" }) %>
16
+ <h2 class="heading-2 my-4">OpenGraph & Meta tags</h2>
13
17
 
14
- <raketa-image-picker name="resource[settings][meta][image]" value="<%= JSON.stringify(resource.settings?.meta?.image || {}) %>" label="Meta image"></raketa-image-picker>
18
+ <%- f.field('resource.settings.meta.title', { required: true, label: "Meta title", hint: "Used for both meta and OpenGraph" }) %>
19
+ <%- f.field('resource.settings.meta.description', { as: "textarea", required: true, label: "Meta description", hint: "Used for both meta and OpenGraph" }) %>
20
+
21
+ <raketa-image-picker name="resource[settings][meta][image]" value="<%= JSON.stringify(resource.settings?.meta?.image || {}) %>" label="OpenGraph image"></raketa-image-picker>
22
+
23
+ <h2 class="heading-2 my-4">Content tags</h2>
15
24
 
16
25
  <%- f.field('resource.tags', { as: 'checkbox_group', options: tags.map(tag => [tag.id, tag.name]) }) %>
17
26
 
@@ -33,18 +42,4 @@
33
42
  <% } %>
34
43
  </div>
35
44
  </form>
36
- <% }) %>
37
-
38
- <div>
39
- <div class="help">
40
- <div class="flex items-center gap-1 mb-2">
41
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
42
- <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" />
43
- </svg>
44
-
45
- <h3 class="heading-3">Contextual help</h3>
46
- </div>
47
-
48
- <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>
49
- </div>
50
- </div>
45
+ <% }) %>
@@ -1,13 +1,16 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.articleType)) %></h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">Delete / </span>
5
+ <%= article.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <div class="grid gap-4 md:grid-cols-3">
7
10
  <div>
8
11
  <% form_for({ resource }, errors, (f) => { %>
9
12
  <form action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/deletes` %>" method="POST" class="form">
10
- <%- f.field('resource.redirect', { as: 'select', required: true,options: [['301', 'Redirect to another page'], ['404', 'Return 404'],['410', 'Return 410']] }) %>
13
+ <%- f.field('resource.redirect', { as: 'select', required: true,options: [['301', '301: Redirect to another page'], ['404', '404: Return an error'],['410', '410: Inform Google to remove the content from its index']] }) %>
11
14
  <%- f.field('resource.destination', {label: 'Destination URL', required: true}) %>
12
15
 
13
16
  <div class="form-actions">
@@ -27,10 +30,20 @@
27
30
  <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" />
28
31
  </svg>
29
32
 
30
- <h3 class="heading-3">Contextual help</h3>
33
+ <h3 class="heading-3">How to safely retire a page?</h3>
31
34
  </div>
32
35
 
33
- <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>
36
+ <div class="markdown">
37
+ <p>Warning: Deleting a page can break existing links. To mitigate this, choose one of these options:</p>
38
+
39
+ <ol>
40
+ <li>Create a redirect to another page</li>
41
+ <li>Set a 410 response to signal Google to remove the page from its index</li>
42
+ <li>Use a 404 response only if absolutely certain</li>
43
+ </ol>
44
+
45
+ <p>In all cases, the current content will be deleted and forgotten like tears in rain — a moment lost in time. You might also consider changing the visibility of the page to private, which will preserve the content but hide it from end users.</p>
46
+ </div>
34
47
  </div>
35
48
  </div>
36
49
  </div>
@@ -52,4 +65,4 @@
52
65
  })
53
66
 
54
67
  document.getElementById("f_resource_redirect").addEventListener("change", onDestinationChange)
55
- </script>
68
+ </script>
@@ -20,7 +20,6 @@
20
20
  <tr>
21
21
  <th><%- sort_button("Title", "title", _context) %></th>
22
22
  <th>Visibility</th>
23
- <th><%- sort_button("Last change", "updated_at", _context) %></th>
24
23
  <th>Translations</th>
25
24
  </tr>
26
25
  </thead>
@@ -76,23 +75,6 @@
76
75
  </div>
77
76
  </td>
78
77
 
79
- <td>
80
- <div class="body-3 text-raketa-foreground/50">
81
- <div class="flex items-center gap-1 mb-0.5">
82
- <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">
83
- <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" />
84
- </svg>
85
-
86
- Jan 20, 2024
87
- </div>
88
-
89
- <div class="flex items-center gap-1" title="Yana Dimitrova">
90
- <img src="https://lh3.googleusercontent.com/a/ACg8ocLRrXdEsbxWg9AMWcgEY9Gfs75-xp7dYsnanB9-u-ZS3A=s96-c" class="size-4 rounded-full" alt="Yana Dimitrova">
91
- Yana D.
92
- </div>
93
- </div>
94
- </td>
95
-
96
78
  <td>
97
79
  <div class="flex flex-wrap items-center gap-1">
98
80
  <% Object.keys(_context.locales).forEach(key => { %>
@@ -160,4 +142,4 @@
160
142
  searchToggle.addEventListener('click', () => {
161
143
  sidebar.classList.toggle('hidden');
162
144
  });
163
- </script>
145
+ </script>
@@ -36,7 +36,7 @@
36
36
  <thead>
37
37
  <tr>
38
38
  <th>Title</th>
39
- <th>Editor</th>
39
+ <th>Created by</th>
40
40
  <th>Last change</th>
41
41
  <th>&nbsp;</th>
42
42
  </tr>
@@ -28,18 +28,4 @@
28
28
  </div>
29
29
  </div>
30
30
  </form>
31
- <% }) %>
32
-
33
- <div>
34
- <div class="help">
35
- <div class="flex items-center gap-1 mb-2">
36
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
37
- <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" />
38
- </svg>
39
-
40
- <h3 class="heading-3">Contextual help</h3>
41
- </div>
42
-
43
- <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>
44
- </div>
45
- </div>
31
+ <% }) %>
@@ -1,12 +1,15 @@
1
1
  <div class="page-wrapper">
2
2
  <div class="page-title">
3
- <h1 class="heading-1">Change URL</h1>
3
+ <h1 class="heading-1">
4
+ <span class="opacity-50">Change URL / </span>
5
+ <%= article.title %>
6
+ </h1>
4
7
  </div>
5
8
 
6
9
  <div class="grid gap-4 md:grid-cols-3">
7
10
  <% form_for({ resource }, errors, (f) => { %>
8
11
  <form action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/urls` %>" method="post" class="form">
9
- <%- f.field('resource.slug', { label: "URL", required: true }) %>
12
+ <%- f.field('resource.slug', { label: "URL", required: true, hint: "Only lowercase letters, numbers and dash allowed. Ex: celebrate/friday-13" }) %>
10
13
  <%- f.field('resource.redirect', { as: "checkbox", value: "1", label: "Create 301 redirect for the old URL" }) %>
11
14
 
12
15
  <%- f.submit('Save record', { class: 'btn-primary' }) %>
@@ -21,11 +24,13 @@
21
24
  <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" />
22
25
  </svg>
23
26
 
24
- <h3 class="heading-3">Contextual help</h3>
27
+ <h3 class="heading-3">Changing URL</h3>
25
28
  </div>
26
29
 
27
- <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>
30
+ <div class="markdown">
31
+ <p>Caution: Changing a page's URL may create broken links from existing references. If needed, use the redirect checkbox to automatically forward traffic from the old address to the new one, preserving link integrity.</p>
32
+ </div>
28
33
  </div>
29
34
  </div>
30
35
  </div>
31
- </div>
36
+ </div>
@@ -21,18 +21,4 @@
21
21
  <% } %>
22
22
  </div>
23
23
  </form>
24
- <% }) %>
25
-
26
- <div>
27
- <div class="help">
28
- <div class="flex items-center gap-1 mb-2">
29
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
30
- <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" />
31
- </svg>
32
-
33
- <h3 class="heading-3">Contextual help</h3>
34
- </div>
35
-
36
- <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>
37
- </div>
38
- </div>
24
+ <% }) %>
@@ -30,7 +30,7 @@
30
30
 
31
31
  <div class="<%= active === 'meta' ? activeClass : inactiveClass %>">
32
32
  <a href="<%= `/${_context.locale}/${_context.resourceConfig.eventType}_revisions/${resource.id}/meta/edit` %>" class="text-raketa-primary font-semibold">
33
- Open graph & meta tags
33
+ OpenGraph & Meta tags
34
34
  </a>
35
35
  </div>
36
36