@raketa-cloud/admin 0.0.64 → 0.0.66
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.
- package/package.json +1 -1
- package/src/handlers/_shared/SettingsHandler.js +36 -0
- package/src/index.js +1 -2
- package/src/routeSettings.js +8 -4
- package/src/tests/handlers/_shared/SettingsHandler.test/edit_form.test.js +36 -0
- package/src/tests/handlers/_shared/SettingsHandler.test/update.test.js +114 -0
- package/src/views/articles/index.ejs +27 -1
- package/src/views/articles/show.ejs +2 -2
- package/src/views/events/index.ejs +27 -1
- package/src/views/events/show.ejs +2 -2
- package/src/views/layouts/application.ejs +40 -7
- package/src/views/pages/index.ejs +22 -1
- package/src/views/pages/show.ejs +2 -2
- package/src/views/redirects/index.ejs +2 -2
- package/src/views/redirects/show.ejs +3 -3
- package/src/views/settings/layout/edit_form.ejs +39 -3
- package/src/views/settings/meta/edit_form.ejs +5 -5
- package/src/handlers/settings/LayoutHandler.js +0 -51
- package/src/handlers/settings/MetaHandler.js +0 -52
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const edit_form = async (ctx) => {
|
|
2
|
+
const data = await ctx.client.settings.find(ctx.locale);
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
assigns: {
|
|
6
|
+
errors: {},
|
|
7
|
+
resource: data,
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const update = async (ctx) => {
|
|
13
|
+
const params = ctx.resourceParams.resource;
|
|
14
|
+
const current = await ctx.client.settings.find(ctx.locale);
|
|
15
|
+
const updated = ctx.client.settings.deepMerge(current, params);
|
|
16
|
+
|
|
17
|
+
const { errors } = await ctx.client.settings.update(ctx.locale, updated);
|
|
18
|
+
|
|
19
|
+
if (errors) {
|
|
20
|
+
return {
|
|
21
|
+
assigns: {
|
|
22
|
+
resource: { ...params },
|
|
23
|
+
errors: { resource: ctx.client.settings.formatErrors(errors) },
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.resource}/edit`,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
edit_form,
|
|
35
|
+
update,
|
|
36
|
+
};
|
package/src/index.js
CHANGED
|
@@ -49,8 +49,7 @@ export { default as PageTranslationsHandler } from "./handlers/pages/Translation
|
|
|
49
49
|
export { default as PageURLsHandler } from "./handlers/pages/URLsHandler";
|
|
50
50
|
export { default as PagesHandler } from "./handlers/pages/PagesHandler";
|
|
51
51
|
|
|
52
|
-
export { default as
|
|
53
|
-
export { default as SettingsMetaHandler } from "./handlers/settings/MetaHandler";
|
|
52
|
+
export { default as SettingsHandler } from "./handlers/_shared/SettingsHandler";
|
|
54
53
|
|
|
55
54
|
export { default as ArticleRevisionsHandler } from "./handlers/article_revisions/ArticleRevisionsHandler";
|
|
56
55
|
export { default as ArticleRevisionsCodeHandler } from "./handlers/article_revisions/CodeHandler";
|
package/src/routeSettings.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import routeMember from "./routeMember";
|
|
2
|
-
import
|
|
3
|
-
import MetaHandler from "./handlers/settings/MetaHandler";
|
|
2
|
+
import SettingsHandler from "./handlers/_shared/SettingsHandler";
|
|
4
3
|
|
|
5
4
|
function routeSettings(adapter) {
|
|
6
|
-
routeMember(adapter, "settings/meta",
|
|
7
|
-
|
|
5
|
+
routeMember(adapter, "settings/meta", SettingsHandler, {
|
|
6
|
+
resource: "settings/meta",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
routeMember(adapter, "settings/layout", SettingsHandler, {
|
|
10
|
+
resource: "settings/layout",
|
|
11
|
+
});
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export default routeSettings;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import SettingsHandler from "../../../../handlers/_shared/SettingsHandler";
|
|
2
|
+
import Settings from "../../../../cloud/Settings";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../../support/factory";
|
|
5
|
+
|
|
6
|
+
function buildCtx() {
|
|
7
|
+
return {
|
|
8
|
+
client: {
|
|
9
|
+
settings: new Settings(() => ({}), "token"),
|
|
10
|
+
},
|
|
11
|
+
locale: "en",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("Settings edit", () => {
|
|
16
|
+
test("edit_form", async () => {
|
|
17
|
+
const settings = Factory.build("settings");
|
|
18
|
+
|
|
19
|
+
const ctx = buildCtx();
|
|
20
|
+
|
|
21
|
+
const mockFind = jest
|
|
22
|
+
.spyOn(ctx.client.settings, "find")
|
|
23
|
+
.mockImplementation(() => settings.value);
|
|
24
|
+
|
|
25
|
+
const result = await SettingsHandler.edit_form(ctx);
|
|
26
|
+
|
|
27
|
+
expect(mockFind).toHaveBeenCalledWith("en");
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
assigns: {
|
|
31
|
+
resource: settings.value,
|
|
32
|
+
errors: {},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import SettingsHandler from "../../../../handlers/_shared/SettingsHandler";
|
|
2
|
+
import Settings from "../../../../cloud/Settings";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../../support/factory";
|
|
5
|
+
|
|
6
|
+
function buildCtx(params) {
|
|
7
|
+
return {
|
|
8
|
+
client: {
|
|
9
|
+
settings: new Settings(() => ({}), "token"),
|
|
10
|
+
},
|
|
11
|
+
locale: "en",
|
|
12
|
+
resourceParams: {
|
|
13
|
+
resource: params,
|
|
14
|
+
},
|
|
15
|
+
locale: "en",
|
|
16
|
+
resourceConfig: {
|
|
17
|
+
resource: "settings/meta",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("Settings update", () => {
|
|
23
|
+
test("update succesfully", async () => {
|
|
24
|
+
const settings = Factory.build("settings", {
|
|
25
|
+
value: {
|
|
26
|
+
meta: {
|
|
27
|
+
title: "Not A title!",
|
|
28
|
+
description: "No, no!",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const ctx = buildCtx({
|
|
34
|
+
meta: {
|
|
35
|
+
title: "A title",
|
|
36
|
+
description: "A description",
|
|
37
|
+
image: JSON.stringify({ url: "https://image-url.com" }),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const mockFind = jest
|
|
42
|
+
.spyOn(ctx.client.settings, "find")
|
|
43
|
+
.mockImplementation(() => settings.value);
|
|
44
|
+
|
|
45
|
+
const mockUpdate = jest
|
|
46
|
+
.spyOn(ctx.client.settings, "update")
|
|
47
|
+
.mockImplementation(() => ({ setting: {}, errors: null }));
|
|
48
|
+
|
|
49
|
+
const result = await SettingsHandler.update(ctx);
|
|
50
|
+
|
|
51
|
+
expect(mockFind).toHaveBeenCalledWith("en");
|
|
52
|
+
expect(mockUpdate).toHaveBeenCalledWith("en", {
|
|
53
|
+
meta: {
|
|
54
|
+
title: "A title",
|
|
55
|
+
description: "A description",
|
|
56
|
+
image: { url: "https://image-url.com" },
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({
|
|
61
|
+
redirect: "/en/settings/meta/edit",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("update failed", async () => {
|
|
66
|
+
const settings = Factory.build("settings", {
|
|
67
|
+
value: {
|
|
68
|
+
meta: {
|
|
69
|
+
title: "Not A title!",
|
|
70
|
+
description: "No, no!",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const updateParams = {
|
|
76
|
+
meta: {
|
|
77
|
+
title: "A title",
|
|
78
|
+
description: "A description",
|
|
79
|
+
image: JSON.stringify({ url: "https://image-url.com" }),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const ctx = buildCtx(updateParams);
|
|
84
|
+
|
|
85
|
+
const mockFind = jest
|
|
86
|
+
.spyOn(ctx.client.settings, "find")
|
|
87
|
+
.mockImplementation(() => settings.value);
|
|
88
|
+
|
|
89
|
+
const mockUpdate = jest
|
|
90
|
+
.spyOn(ctx.client.settings, "update")
|
|
91
|
+
.mockImplementation(() => ({
|
|
92
|
+
setting: null,
|
|
93
|
+
errors: [{ key: "generalError", message: "some error" }],
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
const result = await SettingsHandler.update(ctx);
|
|
97
|
+
|
|
98
|
+
expect(mockFind).toHaveBeenCalledWith("en");
|
|
99
|
+
expect(mockUpdate).toHaveBeenCalledWith("en", {
|
|
100
|
+
meta: {
|
|
101
|
+
title: "A title",
|
|
102
|
+
description: "A description",
|
|
103
|
+
image: { url: "https://image-url.com" },
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
assigns: {
|
|
109
|
+
resource: updateParams,
|
|
110
|
+
errors: { resource: { generalError: "some error" } },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
<tr>
|
|
21
21
|
<th><%- sort_button("Title", "title", _context) %></th>
|
|
22
22
|
<th>Visibility</th>
|
|
23
|
+
<th><%- sort_button("Publish at", "publish_at", _context) %></th>
|
|
24
|
+
<th><%- sort_button("Last change", "updated_at", _context) %></th>
|
|
23
25
|
<th>Translations</th>
|
|
24
26
|
</tr>
|
|
25
27
|
</thead>
|
|
@@ -75,6 +77,30 @@
|
|
|
75
77
|
</div>
|
|
76
78
|
</td>
|
|
77
79
|
|
|
80
|
+
<td>
|
|
81
|
+
<time data-pretty-datetime datetime="<%= item.publishAt %>"><%= item.publishAt %></time>
|
|
82
|
+
</td>
|
|
83
|
+
|
|
84
|
+
<td>
|
|
85
|
+
<div class="body-3 text-raketa-foreground/50">
|
|
86
|
+
<div class="flex items-center gap-1 mb-0.5">
|
|
87
|
+
<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">
|
|
88
|
+
<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" />
|
|
89
|
+
</svg>
|
|
90
|
+
<time data-pretty-datetime datetime="<%= item.updatedAt %>"><%= item.updatedAt %></time>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="flex items-center gap-1" title="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
94
|
+
<% if (item.changes[0]?.operatorMeta?.avatar) { %>
|
|
95
|
+
<img src="<%= item.changes[0]?.operatorMeta?.avatar %>" class="size-4 rounded-full" alt="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
96
|
+
<% } else { %>
|
|
97
|
+
<div class="size-4 rounded-full"></div>
|
|
98
|
+
<% } %>
|
|
99
|
+
<%= item.changes[0]?.operatorMeta?.name %>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</td>
|
|
103
|
+
|
|
78
104
|
<td>
|
|
79
105
|
<div class="flex flex-wrap items-center gap-1">
|
|
80
106
|
<% Object.keys(_context.locales).forEach(key => { %>
|
|
@@ -142,4 +168,4 @@
|
|
|
142
168
|
searchToggle.addEventListener('click', () => {
|
|
143
169
|
sidebar.classList.toggle('hidden');
|
|
144
170
|
});
|
|
145
|
-
</script>
|
|
171
|
+
</script>
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
<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" />
|
|
78
78
|
</svg>
|
|
79
79
|
|
|
80
|
-
<%=
|
|
80
|
+
<time data-pretty-datetime datetime="<%= revision.insertedAt %>"><%= revision.insertedAt %></time>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
83
83
|
</td>
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
<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" />
|
|
134
134
|
</svg>
|
|
135
135
|
|
|
136
|
-
<%=
|
|
136
|
+
<time data-pretty-datetime datetime="<%= change.insertedAt %>"><%= change.insertedAt %></time>
|
|
137
137
|
</div>
|
|
138
138
|
</div>
|
|
139
139
|
</td>
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
<tr>
|
|
21
21
|
<th><%- sort_button("Title", "title", _context) %></th>
|
|
22
22
|
<th>Visibility</th>
|
|
23
|
+
<th><%- sort_button("Publish at", "publish_at", _context) %></th>
|
|
24
|
+
<th><%- sort_button("Last change", "updated_at", _context) %></th>
|
|
23
25
|
<th>Translations</th>
|
|
24
26
|
</tr>
|
|
25
27
|
</thead>
|
|
@@ -75,6 +77,30 @@
|
|
|
75
77
|
</div>
|
|
76
78
|
</td>
|
|
77
79
|
|
|
80
|
+
<td>
|
|
81
|
+
<time data-pretty-datetime datetime="<%= item.publishAt %>"><%= item.publishAt %></time>
|
|
82
|
+
</td>
|
|
83
|
+
|
|
84
|
+
<td>
|
|
85
|
+
<div class="body-3 text-raketa-foreground/50">
|
|
86
|
+
<div class="flex items-center gap-1 mb-0.5">
|
|
87
|
+
<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">
|
|
88
|
+
<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" />
|
|
89
|
+
</svg>
|
|
90
|
+
<time data-pretty-datetime datetime="<%= item.updatedAt %>"><%= item.updatedAt %></time>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="flex items-center gap-1" title="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
94
|
+
<% if (item.changes[0]?.operatorMeta?.avatar) { %>
|
|
95
|
+
<img src="<%= item.changes[0]?.operatorMeta?.avatar %>" class="size-4 rounded-full" alt="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
96
|
+
<% } else { %>
|
|
97
|
+
<div class="size-4 rounded-full"></div>
|
|
98
|
+
<% } %>
|
|
99
|
+
<%= item.changes[0]?.operatorMeta?.name %>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</td>
|
|
103
|
+
|
|
78
104
|
<td>
|
|
79
105
|
<div class="flex flex-wrap items-center gap-1">
|
|
80
106
|
<% Object.keys(_context.locales).forEach(key => { %>
|
|
@@ -142,4 +168,4 @@
|
|
|
142
168
|
searchToggle.addEventListener('click', () => {
|
|
143
169
|
sidebar.classList.toggle('hidden');
|
|
144
170
|
});
|
|
145
|
-
</script>
|
|
171
|
+
</script>
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
<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" />
|
|
78
78
|
</svg>
|
|
79
79
|
|
|
80
|
-
<%=
|
|
80
|
+
<time data-pretty-datetime datetime="<%= revision.insertedAt %>"><%= revision.insertedAt %></time>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
83
83
|
</td>
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
<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" />
|
|
134
134
|
</svg>
|
|
135
135
|
|
|
136
|
-
<%=
|
|
136
|
+
<time data-pretty-datetime datetime="<%= change.insertedAt %>"><%= change.insertedAt %></time>
|
|
137
137
|
</div>
|
|
138
138
|
</div>
|
|
139
139
|
</td>
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
}
|
|
42
42
|
</style>
|
|
43
43
|
|
|
44
|
+
<script src="https://storage.googleapis.com/cdn-raketa-cloud/js/raketa-menu-builder-webcomponent.umd.js"></script>
|
|
45
|
+
|
|
46
|
+
<script src="https://storage.googleapis.com/cdn-raketa-cloud/js/raketa-list-input-webcomponent.umd.js"></script>
|
|
47
|
+
|
|
44
48
|
<body class="relative max-h-screen overflow-hidden">
|
|
45
49
|
<%- include('header.ejs') %>
|
|
46
50
|
|
|
@@ -143,10 +147,22 @@
|
|
|
143
147
|
|
|
144
148
|
parent.querySelectorAll('[data-auto="1"]').forEach(node => node.remove())
|
|
145
149
|
|
|
150
|
+
// Add user's localtime
|
|
151
|
+
const strTime = fakeUTCDate.toLocaleString("en-GB", {});
|
|
152
|
+
|
|
153
|
+
const hint = document.createElement("div");
|
|
154
|
+
|
|
155
|
+
hint.classList.add('hint')
|
|
156
|
+
hint.innerText = `In your local time: ${strTime}`;
|
|
157
|
+
hint.dataset.auto = "1";
|
|
158
|
+
|
|
159
|
+
parent.appendChild(hint)
|
|
160
|
+
|
|
146
161
|
TIMEZONES.forEach(timeZone => {
|
|
147
|
-
const strTime = fakeUTCDate.toLocaleString("en-
|
|
162
|
+
const strTime = fakeUTCDate.toLocaleString("en-GB", {
|
|
148
163
|
timeZone: `${timeZone.value}`
|
|
149
164
|
});
|
|
165
|
+
|
|
150
166
|
const hint = document.createElement("div");
|
|
151
167
|
|
|
152
168
|
hint.classList.add('hint')
|
|
@@ -156,12 +172,6 @@
|
|
|
156
172
|
parent.appendChild(hint)
|
|
157
173
|
})
|
|
158
174
|
}
|
|
159
|
-
|
|
160
|
-
document.querySelectorAll('input[type="datetime-local"]').forEach(input => {
|
|
161
|
-
enhanceDateTimeInputs(input);
|
|
162
|
-
|
|
163
|
-
input.addEventListener("change", (event) => enhanceDateTimeInputs(event.target))
|
|
164
|
-
})
|
|
165
175
|
</script>
|
|
166
176
|
|
|
167
177
|
<script>
|
|
@@ -183,6 +193,29 @@
|
|
|
183
193
|
})
|
|
184
194
|
})
|
|
185
195
|
</script>
|
|
196
|
+
|
|
197
|
+
<script>
|
|
198
|
+
window.addEventListener("load", (_event) => {
|
|
199
|
+
|
|
200
|
+
// Setup datetime inputs
|
|
201
|
+
document.querySelectorAll('input[type="datetime-local"]').forEach(input => {
|
|
202
|
+
enhanceDateTimeInputs(input);
|
|
203
|
+
|
|
204
|
+
input.addEventListener("change", (event) => enhanceDateTimeInputs(event.target))
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
// Setup time presentation
|
|
209
|
+
document.querySelectorAll('time[data-pretty-datetime]').forEach(item => {
|
|
210
|
+
const dateStr = item.getAttribute("datetime");
|
|
211
|
+
|
|
212
|
+
item.innerText = new Date(dateStr).toLocaleString('en-GB', {
|
|
213
|
+
dateStyle: "short",
|
|
214
|
+
timeStyle: "short"
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
</script>
|
|
186
219
|
</body>
|
|
187
220
|
|
|
188
221
|
</html>
|
|
@@ -20,6 +20,7 @@
|
|
|
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>
|
|
23
24
|
<th>Translations</th>
|
|
24
25
|
</tr>
|
|
25
26
|
</thead>
|
|
@@ -75,6 +76,26 @@
|
|
|
75
76
|
</div>
|
|
76
77
|
</td>
|
|
77
78
|
|
|
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
|
+
<time data-pretty-datetime datetime="<%= item.updatedAt %>"><%= item.updatedAt %></time>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="flex items-center gap-1" title="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
89
|
+
<% if (item.changes[0]?.operatorMeta?.avatar) { %>
|
|
90
|
+
<img src="<%= item.changes[0]?.operatorMeta?.avatar %>" class="size-4 rounded-full" alt="<%= item.changes[0]?.operatorMeta?.name %>">
|
|
91
|
+
<% } else { %>
|
|
92
|
+
<div class="size-4 rounded-full"></div>
|
|
93
|
+
<% } %>
|
|
94
|
+
<%= item.changes[0]?.operatorMeta?.name %>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</td>
|
|
98
|
+
|
|
78
99
|
<td>
|
|
79
100
|
<div class="flex flex-wrap items-center gap-1">
|
|
80
101
|
<% Object.keys(_context.locales).forEach(key => { %>
|
|
@@ -142,4 +163,4 @@
|
|
|
142
163
|
searchToggle.addEventListener('click', () => {
|
|
143
164
|
sidebar.classList.toggle('hidden');
|
|
144
165
|
});
|
|
145
|
-
</script>
|
|
166
|
+
</script>
|
package/src/views/pages/show.ejs
CHANGED
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
<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" />
|
|
92
92
|
</svg>
|
|
93
93
|
|
|
94
|
-
<%=
|
|
94
|
+
<time data-pretty-datetime datetime="<%= revision.insertedAt %>"><%= revision.insertedAt %></time>
|
|
95
95
|
</div>
|
|
96
96
|
</div>
|
|
97
97
|
</td>
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
<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" />
|
|
148
148
|
</svg>
|
|
149
149
|
|
|
150
|
-
<%=
|
|
150
|
+
<time data-pretty-datetime datetime="<%= change.insertedAt %>"><%= change.insertedAt %></time>
|
|
151
151
|
</div>
|
|
152
152
|
</div>
|
|
153
153
|
</td>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
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
55
|
</svg>
|
|
56
56
|
|
|
57
|
-
<%=
|
|
57
|
+
<time data-pretty-datetime datetime="<%= item.updatedAt %>"><%= item.updatedAt %></time>
|
|
58
58
|
</div>
|
|
59
59
|
</td>
|
|
60
60
|
<td class="actions">
|
|
@@ -72,4 +72,4 @@
|
|
|
72
72
|
</table>
|
|
73
73
|
|
|
74
74
|
<%- pagination(items.pagination, _context) %>
|
|
75
|
-
</div>
|
|
75
|
+
</div>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
</tr>
|
|
30
30
|
<tr>
|
|
31
31
|
<th class="w-1 whitespace-nowrap">Updated at</th>
|
|
32
|
-
<
|
|
32
|
+
<time data-pretty-datetime datetime="<%= item.updatedAt %>"><%= item.updatedAt %></time>
|
|
33
33
|
</tr>
|
|
34
34
|
</tbody>
|
|
35
35
|
</table>
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
<td><%= item.version %></td>
|
|
53
53
|
<td><%= item.title %></td>
|
|
54
54
|
<td><%= item.operatorMeta.name %></td>
|
|
55
|
-
<td
|
|
55
|
+
<td><time data-pretty-datetime datetime="<%= item.insertedAt %>"><%= item.insertedAt %></time></td>
|
|
56
56
|
</tr>
|
|
57
57
|
<% }) %>
|
|
58
58
|
</tbody>
|
|
59
59
|
</table>
|
|
60
|
-
</div>
|
|
60
|
+
</div>
|
|
@@ -5,11 +5,47 @@
|
|
|
5
5
|
|
|
6
6
|
<%- render_partial('../_nav.ejs', { active: 'layout' }) %>
|
|
7
7
|
|
|
8
|
+
<% const WIDGETS = {
|
|
9
|
+
HTML: {
|
|
10
|
+
defaults: {
|
|
11
|
+
code: "<div>HTML</div>"
|
|
12
|
+
},
|
|
13
|
+
admin: {
|
|
14
|
+
code: { type: "textarea" }
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
Image: {
|
|
18
|
+
primaryField: "image",
|
|
19
|
+
defaults: {
|
|
20
|
+
image: "https://placeholder.raketa.cloud/images/1920x1080"
|
|
21
|
+
},
|
|
22
|
+
admin: {
|
|
23
|
+
image: { type: "text" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
Title: {
|
|
27
|
+
primaryField: "title",
|
|
28
|
+
defaults: {
|
|
29
|
+
title: "Title"
|
|
30
|
+
},
|
|
31
|
+
admin: {
|
|
32
|
+
title: { type: "text" }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}; %>
|
|
36
|
+
|
|
8
37
|
<div>
|
|
9
|
-
<% form_for({
|
|
38
|
+
<% form_for({ resource }, errors, (f) => { %>
|
|
10
39
|
<form action="<%= `/${_context.locale}/settings/layout` %>" method="post" class="form">
|
|
11
|
-
|
|
12
|
-
|
|
40
|
+
<div class="mb-4">
|
|
41
|
+
<label>Header</label>
|
|
42
|
+
<raketa-menu-builder-input id="header-menu" name="resource[menus][header]" value="<%= JSON.stringify(resource.menus?.header || []) %>" widgets="<%= JSON.stringify(WIDGETS) %>"></raketa-menu-builder-input>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="mb-4">
|
|
46
|
+
<label>Footer</label>
|
|
47
|
+
<raketa-menu-builder-input id="header-menu" name="resource[menus][footer]" value="<%= JSON.stringify(resource.menus?.footer || []) %>" widgets="<%= JSON.stringify(WIDGETS) %>"></raketa-menu-builder-input>
|
|
48
|
+
</div>
|
|
13
49
|
|
|
14
50
|
<div class="form-actions">
|
|
15
51
|
<div class="btn-toolbar">
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<%- render_partial('../_nav.ejs', { active: 'meta' }) %>
|
|
7
7
|
|
|
8
8
|
<div class="grid gap-4 md:grid-cols-3">
|
|
9
|
-
<% form_for({
|
|
9
|
+
<% form_for({ resource }, errors, (f) => { %>
|
|
10
10
|
<form action="<%= `/${_context.locale}/settings/meta` %>" method="post" class="form">
|
|
11
|
-
<%- f.field('
|
|
12
|
-
<%- f.field('
|
|
11
|
+
<%- f.field('resource.meta.title', { label: "Meta title", hint: "Used for both meta and OpenGraph" }) %>
|
|
12
|
+
<%- f.field('resource.meta.description', { as: "textarea", label: "Meta description", hint: "Used for both meta and OpenGraph" }) %>
|
|
13
13
|
|
|
14
|
-
<raketa-image-picker name="
|
|
14
|
+
<raketa-image-picker name="resource[image]" value="<%= JSON.stringify(resource.meta?.image || {}) %>" label="OpenGraph image"></raketa-image-picker>
|
|
15
15
|
|
|
16
16
|
<div class="form-actions">
|
|
17
17
|
<div class="btn-toolbar">
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
</form>
|
|
22
22
|
<% }) %>
|
|
23
23
|
</div>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const edit_form = async (ctx) => {
|
|
2
|
-
const data = await ctx.client.settings.find(ctx.locale);
|
|
3
|
-
|
|
4
|
-
const layout_settings = {
|
|
5
|
-
header: JSON.stringify(data.menus?.header),
|
|
6
|
-
footer: JSON.stringify(data.menus?.footer),
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
assigns: {
|
|
11
|
-
errors: {},
|
|
12
|
-
layout_settings,
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const update = async (ctx) => {
|
|
18
|
-
const params = ctx.resourceParams.layout_settings;
|
|
19
|
-
|
|
20
|
-
const currentSettings = await ctx.client.settings.find(ctx.locale);
|
|
21
|
-
|
|
22
|
-
if (!currentSettings.menus) {
|
|
23
|
-
currentSettings.menus = {};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
currentSettings.menus.header = JSON.parse(params.header);
|
|
27
|
-
currentSettings.menus.footer = JSON.parse(params.footer);
|
|
28
|
-
|
|
29
|
-
const { errors } = await ctx.client.settings.update(
|
|
30
|
-
ctx.locale,
|
|
31
|
-
currentSettings
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
if (errors) {
|
|
35
|
-
return {
|
|
36
|
-
assigns: {
|
|
37
|
-
setting: { ...params },
|
|
38
|
-
errors: { setting: ctx.client.settings.formatErrors(errors) },
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
redirect: `/${ctx.locale}/settings/layout/edit`,
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export default {
|
|
49
|
-
edit_form,
|
|
50
|
-
update,
|
|
51
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const edit_form = async (ctx) => {
|
|
2
|
-
const data = await ctx.client.settings.find(ctx.locale);
|
|
3
|
-
const meta_settings = {
|
|
4
|
-
...data.meta,
|
|
5
|
-
image: data.meta?.image || {},
|
|
6
|
-
};
|
|
7
|
-
const errors = {};
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
assigns: {
|
|
11
|
-
errors,
|
|
12
|
-
meta_settings,
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const update = async (ctx) => {
|
|
18
|
-
const params = ctx.resourceParams.meta_settings;
|
|
19
|
-
|
|
20
|
-
const currentSettings = await ctx.client.settings.find(ctx.locale);
|
|
21
|
-
|
|
22
|
-
if (!currentSettings.meta) {
|
|
23
|
-
currentSettings.meta = {};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
currentSettings.meta.title = params.title;
|
|
27
|
-
currentSettings.meta.description = params.description;
|
|
28
|
-
currentSettings.meta.image = JSON.parse(params.image);
|
|
29
|
-
|
|
30
|
-
const { errors } = await ctx.client.settings.update(
|
|
31
|
-
ctx.locale,
|
|
32
|
-
currentSettings
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
if (errors) {
|
|
36
|
-
return {
|
|
37
|
-
assigns: {
|
|
38
|
-
setting: { ...params },
|
|
39
|
-
errors: { setting: ctx.client.settings.formatErrors(errors) },
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
redirect: `/${ctx.locale}/settings/meta/edit`,
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export default {
|
|
50
|
-
edit_form,
|
|
51
|
-
update,
|
|
52
|
-
};
|