@raketa-cloud/admin 1.1.0 → 1.3.0
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/dist/application.css +40 -45
- package/package.json +1 -1
- package/src/HonoAdapter.js +3 -4
- package/src/handlers/article_revisions/ArticleRevisionsHandler.js +8 -0
- package/src/handlers/articles/ArticlesHandler.js +12 -1
- package/src/handlers/event_revisions/EventRevisionsHandler.js +8 -0
- package/src/handlers/events/EventsHandler.js +13 -2
- package/src/handlers/page_revisions/PageRevisionsHandler.js +8 -0
- package/src/handlers/pages/PagesHandler.js +12 -1
- package/src/tests/handlers/article_revisions/ArticleRevisionsHandler.test/create.test.js +43 -35
- package/src/tests/handlers/articles/ArticlesHandler.test/show.test.js +12 -1
- package/src/tests/handlers/event_revisions/EventRevisionsHandler.test/create.test.js +41 -33
- package/src/tests/handlers/events/EventsHandler.test/show.test.js +12 -1
- package/src/tests/handlers/page_revisions/PageRevisionsHandler.test/create.test.js +41 -27
- package/src/tests/handlers/pages/PagesHandler.test/show.test.js +12 -1
- package/src/views/articles/_shared/_history_toolbar.ejs +2 -2
- package/src/views/articles/_shared/_toolbar.ejs +38 -26
- package/src/views/articles/_shared/history_table.ejs +6 -0
- package/src/views/events/_shared/_history_toolbar.ejs +2 -2
- package/src/views/events/_shared/_toolbar.ejs +38 -26
- package/src/views/events/_shared/history_table.ejs +6 -0
- package/src/views/layouts/application.ejs +18 -5
- package/src/views/pages/_shared/_history_toolbar.ejs +2 -2
- package/src/views/pages/_shared/_toolbar.ejs +38 -26
- package/src/views/pages/_shared/history_table.ejs +6 -0
package/dist/application.css
CHANGED
|
@@ -2134,12 +2134,8 @@ select {
|
|
|
2134
2134
|
right: -1rem;
|
|
2135
2135
|
}
|
|
2136
2136
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
.bottom-12 {
|
|
2142
|
-
bottom: 3rem;
|
|
2137
|
+
.bottom-20 {
|
|
2138
|
+
bottom: 5rem;
|
|
2143
2139
|
}
|
|
2144
2140
|
|
|
2145
2141
|
.end-1 {
|
|
@@ -2154,6 +2150,10 @@ select {
|
|
|
2154
2150
|
left: 2rem;
|
|
2155
2151
|
}
|
|
2156
2152
|
|
|
2153
|
+
.left-\[64px\] {
|
|
2154
|
+
left: 64px;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
2157
|
.right-0 {
|
|
2158
2158
|
right: 0px;
|
|
2159
2159
|
}
|
|
@@ -2190,6 +2190,10 @@ select {
|
|
|
2190
2190
|
z-index: 20;
|
|
2191
2191
|
}
|
|
2192
2192
|
|
|
2193
|
+
.z-50 {
|
|
2194
|
+
z-index: 50;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2193
2197
|
.m-1 {
|
|
2194
2198
|
margin: 0.25rem;
|
|
2195
2199
|
}
|
|
@@ -2341,6 +2345,10 @@ select {
|
|
|
2341
2345
|
display: none;
|
|
2342
2346
|
}
|
|
2343
2347
|
|
|
2348
|
+
.aspect-square {
|
|
2349
|
+
aspect-ratio: 1 / 1;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2344
2352
|
.size-14 {
|
|
2345
2353
|
width: 3.5rem;
|
|
2346
2354
|
height: 3.5rem;
|
|
@@ -2366,10 +2374,6 @@ select {
|
|
|
2366
2374
|
height: 2rem;
|
|
2367
2375
|
}
|
|
2368
2376
|
|
|
2369
|
-
.h-0 {
|
|
2370
|
-
height: 0px;
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
2377
|
.h-1 {
|
|
2374
2378
|
height: 0.25rem;
|
|
2375
2379
|
}
|
|
@@ -2407,10 +2411,6 @@ select {
|
|
|
2407
2411
|
max-height: 100vh;
|
|
2408
2412
|
}
|
|
2409
2413
|
|
|
2410
|
-
.w-0 {
|
|
2411
|
-
width: 0px;
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
2414
|
.w-1 {
|
|
2415
2415
|
width: 0.25rem;
|
|
2416
2416
|
}
|
|
@@ -2575,22 +2575,14 @@ select {
|
|
|
2575
2575
|
border-bottom-width: 2px;
|
|
2576
2576
|
}
|
|
2577
2577
|
|
|
2578
|
-
.border-
|
|
2579
|
-
border-
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
.border-l-\[10px\] {
|
|
2583
|
-
border-left-width: 10px;
|
|
2578
|
+
.border-l {
|
|
2579
|
+
border-left-width: 1px;
|
|
2584
2580
|
}
|
|
2585
2581
|
|
|
2586
2582
|
.border-t {
|
|
2587
2583
|
border-top-width: 1px;
|
|
2588
2584
|
}
|
|
2589
2585
|
|
|
2590
|
-
.border-t-\[28px\] {
|
|
2591
|
-
border-top-width: 28px;
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
2586
|
.border-mono-25 {
|
|
2595
2587
|
--tw-border-opacity: 1;
|
|
2596
2588
|
border-color: rgb(234 234 234 / var(--tw-border-opacity));
|
|
@@ -2615,17 +2607,9 @@ select {
|
|
|
2615
2607
|
border-color: transparent;
|
|
2616
2608
|
}
|
|
2617
2609
|
|
|
2618
|
-
.
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
.border-l-raketa-primary {
|
|
2623
|
-
--tw-border-opacity: 1;
|
|
2624
|
-
border-left-color: rgba(var(--primary), var(--tw-border-opacity));
|
|
2625
|
-
}
|
|
2626
|
-
|
|
2627
|
-
.border-t-transparent {
|
|
2628
|
-
border-top-color: transparent;
|
|
2610
|
+
.bg-\[\#F7F7FC\] {
|
|
2611
|
+
--tw-bg-opacity: 1;
|
|
2612
|
+
background-color: rgb(247 247 252 / var(--tw-bg-opacity));
|
|
2629
2613
|
}
|
|
2630
2614
|
|
|
2631
2615
|
.bg-black {
|
|
@@ -2713,6 +2697,11 @@ select {
|
|
|
2713
2697
|
padding-bottom: 0.5rem;
|
|
2714
2698
|
}
|
|
2715
2699
|
|
|
2700
|
+
.py-3 {
|
|
2701
|
+
padding-top: 0.75rem;
|
|
2702
|
+
padding-bottom: 0.75rem;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2716
2705
|
.py-4 {
|
|
2717
2706
|
padding-top: 1rem;
|
|
2718
2707
|
padding-bottom: 1rem;
|
|
@@ -2850,10 +2839,6 @@ select {
|
|
|
2850
2839
|
color: rgb(255 255 255 / var(--tw-text-opacity));
|
|
2851
2840
|
}
|
|
2852
2841
|
|
|
2853
|
-
.opacity-0 {
|
|
2854
|
-
opacity: 0;
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
2842
|
.opacity-10 {
|
|
2858
2843
|
opacity: 0.1;
|
|
2859
2844
|
}
|
|
@@ -3707,18 +3692,32 @@ body {
|
|
|
3707
3692
|
opacity: 1;
|
|
3708
3693
|
}
|
|
3709
3694
|
|
|
3695
|
+
.group\/item:hover .group-hover\/item\:block {
|
|
3696
|
+
display: block;
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3710
3699
|
.group:hover .group-hover\:block {
|
|
3711
3700
|
display: block;
|
|
3712
3701
|
}
|
|
3713
3702
|
|
|
3714
|
-
.group\/item:hover .group-hover\/item\:
|
|
3715
|
-
opacity: 1;
|
|
3703
|
+
.group\/item:hover .group-hover\/item\:bg-raketa-primary {
|
|
3704
|
+
--tw-bg-opacity: 1;
|
|
3705
|
+
background-color: rgba(var(--primary), var(--tw-bg-opacity));
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
.group\/item:hover .group-hover\/item\:text-white {
|
|
3709
|
+
--tw-text-opacity: 1;
|
|
3710
|
+
color: rgb(255 255 255 / var(--tw-text-opacity));
|
|
3716
3711
|
}
|
|
3717
3712
|
|
|
3718
3713
|
.group.expanded .group-\[\.expanded\]\:block {
|
|
3719
3714
|
display: block;
|
|
3720
3715
|
}
|
|
3721
3716
|
|
|
3717
|
+
.group.expanded .group-\[\.expanded\]\:hidden {
|
|
3718
|
+
display: none;
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3722
3721
|
.group.expanded .group-\[\.expanded\]\:w-60 {
|
|
3723
3722
|
width: 15rem;
|
|
3724
3723
|
}
|
|
@@ -3742,10 +3741,6 @@ body {
|
|
|
3742
3741
|
opacity: 0.5;
|
|
3743
3742
|
}
|
|
3744
3743
|
|
|
3745
|
-
.group\/item.is-active .group-\[\.is-active\]\/item\:opacity-100 {
|
|
3746
|
-
opacity: 1;
|
|
3747
|
-
}
|
|
3748
|
-
|
|
3749
3744
|
@media (min-width: 768px) {
|
|
3750
3745
|
.md\:grid-cols-3 {
|
|
3751
3746
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
package/package.json
CHANGED
package/src/HonoAdapter.js
CHANGED
|
@@ -36,10 +36,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
36
36
|
this.globalConfig = config;
|
|
37
37
|
|
|
38
38
|
// TODO: Pass in a path to redirect to after login?
|
|
39
|
-
setupAuthentication(
|
|
40
|
-
router,
|
|
41
|
-
config
|
|
42
|
-
);
|
|
39
|
+
setupAuthentication(router, config);
|
|
43
40
|
|
|
44
41
|
router.get("/", (c) => {
|
|
45
42
|
return c.redirect("/en");
|
|
@@ -101,6 +98,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
101
98
|
ctx.resourceConfig = resourceConfig;
|
|
102
99
|
ctx.locales = this.globalConfig.i18n.locales;
|
|
103
100
|
ctx.defaultLocale = this.globalConfig.i18n.defaultLocale;
|
|
101
|
+
ctx.maxOpenRevisions = this.globalConfig?.maxOpenRevisions || 3;
|
|
104
102
|
ctx.locale = currentLocale;
|
|
105
103
|
ctx.remoteConfig = { ...this.globalConfig.remoteConfig };
|
|
106
104
|
ctx.logger = Logger;
|
|
@@ -110,6 +108,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
110
108
|
locales: ctx.locales,
|
|
111
109
|
locale: ctx.locale,
|
|
112
110
|
defaultLocale: ctx.defaultLocale,
|
|
111
|
+
maxOpenRevisions: ctx.maxOpenRevisions,
|
|
113
112
|
path: ctx.req.path,
|
|
114
113
|
query: ctx.query || {},
|
|
115
114
|
layout: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const article = await ctx.client.articles.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (article.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.articleTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, articleErrors } = await ctx.client.articleRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -26,7 +26,18 @@ const show = async (ctx) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const revisions = await ctx.client.articleRevisions.all(id, ctx.locale);
|
|
29
|
-
const changes = await ctx.client.articleChanges.list(
|
|
29
|
+
const changes = await ctx.client.articleChanges.list(
|
|
30
|
+
{
|
|
31
|
+
"f:change_type": [
|
|
32
|
+
"revision_publish",
|
|
33
|
+
"revision_lock",
|
|
34
|
+
"revision_unlock",
|
|
35
|
+
"update",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
id,
|
|
39
|
+
ctx.locale
|
|
40
|
+
);
|
|
30
41
|
|
|
31
42
|
return {
|
|
32
43
|
assigns: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const event = await ctx.client.events.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (event.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.eventTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, eventErrors } = await ctx.client.eventRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toDateInput, toDateTimeInput } from "../../utils";
|
|
2
2
|
|
|
3
3
|
const index = async (ctx) => {
|
|
4
4
|
const items = await ctx.client.events.list(
|
|
@@ -26,7 +26,18 @@ const show = async (ctx) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const revisions = await ctx.client.eventRevisions.all(id, ctx.locale);
|
|
29
|
-
const changes = await ctx.client.eventChanges.list(
|
|
29
|
+
const changes = await ctx.client.eventChanges.list(
|
|
30
|
+
{
|
|
31
|
+
"f:change_type": [
|
|
32
|
+
"revision_publish",
|
|
33
|
+
"revision_lock",
|
|
34
|
+
"revision_unlock",
|
|
35
|
+
"update",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
id,
|
|
39
|
+
ctx.locale
|
|
40
|
+
);
|
|
30
41
|
|
|
31
42
|
return {
|
|
32
43
|
assigns: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const page = await ctx.client.pages.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (page.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.pageTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, pageErrors } = await ctx.client.pageRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -25,7 +25,18 @@ const show = async (ctx) => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const revisions = await ctx.client.pageRevisions.all(id, ctx.locale);
|
|
28
|
-
const changes = await ctx.client.pageChanges.list(
|
|
28
|
+
const changes = await ctx.client.pageChanges.list(
|
|
29
|
+
{
|
|
30
|
+
"f:change_type": [
|
|
31
|
+
"revision_publish",
|
|
32
|
+
"revision_lock",
|
|
33
|
+
"revision_unlock",
|
|
34
|
+
"update",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
id,
|
|
38
|
+
ctx.locale
|
|
39
|
+
);
|
|
29
40
|
|
|
30
41
|
return {
|
|
31
42
|
assigns: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import ArticleRevisionsHandler from "../../../../handlers/article_revisions/ArticleRevisionsHandler";
|
|
2
1
|
import ArticleRevisions from "../../../../cloud/ArticleRevisions";
|
|
3
|
-
|
|
2
|
+
import Articles from "../../../../cloud/Articles";
|
|
3
|
+
import ArticleRevisionsHandler from "../../../../handlers/article_revisions/ArticleRevisionsHandler";
|
|
4
4
|
import Factory from "../../../support/factory";
|
|
5
5
|
|
|
6
6
|
function buildCtx(articleID) {
|
|
7
7
|
return {
|
|
8
8
|
client: {
|
|
9
9
|
articleRevisions: new ArticleRevisions(() => ({}), "token"),
|
|
10
|
+
articles: new Articles(() => ({}), "token"),
|
|
10
11
|
},
|
|
11
12
|
params: { parent_id: articleID },
|
|
12
13
|
locale: "en",
|
|
@@ -14,66 +15,73 @@ function buildCtx(articleID) {
|
|
|
14
15
|
articleType: "article",
|
|
15
16
|
articleTypePlural: "articles",
|
|
16
17
|
},
|
|
18
|
+
maxOpenRevisions: 1,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
describe("Article revisions", () => {
|
|
21
23
|
test("create successful", async () => {
|
|
24
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
22
25
|
const revision = Factory.build("article_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
26
|
+
const ctx = buildCtx(article.id);
|
|
27
|
+
|
|
28
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
jest
|
|
26
31
|
.spyOn(ctx.client.articleRevisions, "create")
|
|
27
|
-
.
|
|
28
|
-
revision,
|
|
29
|
-
errors: null,
|
|
30
|
-
articleErrors: null,
|
|
31
|
-
}));
|
|
32
|
+
.mockResolvedValue({ revision, errors: null, articleErrors: null });
|
|
32
33
|
|
|
33
34
|
const result = await ArticleRevisionsHandler.create(ctx);
|
|
34
35
|
|
|
35
|
-
expect(
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
redirect: `/en/articles/${article.id}`,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("create failed when there is an opened revision for the article", async () => {
|
|
42
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 2 });
|
|
43
|
+
const ctx = buildCtx(article.id);
|
|
44
|
+
|
|
45
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
46
|
+
|
|
47
|
+
const result = await ArticleRevisionsHandler.create(ctx);
|
|
36
48
|
|
|
37
49
|
expect(result).toEqual({
|
|
38
|
-
redirect: `/en/articles/${
|
|
50
|
+
redirect: `/en/articles/${article.id}?revisions_count_error=1`,
|
|
39
51
|
});
|
|
40
52
|
});
|
|
41
53
|
|
|
42
54
|
test("create failed with errors", async () => {
|
|
43
|
-
const
|
|
55
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
44
56
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
57
|
+
const ctx = buildCtx(article.id);
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
.spyOn(ctx.client.articleRevisions, "create")
|
|
48
|
-
.mockImplementation(() => ({
|
|
49
|
-
revision: null,
|
|
50
|
-
errors,
|
|
51
|
-
articleErrors: null,
|
|
52
|
-
}));
|
|
59
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
jest
|
|
62
|
+
.spyOn(ctx.client.articleRevisions, "create")
|
|
63
|
+
.mockResolvedValue({ revision: null, errors, articleErrors: null });
|
|
57
64
|
|
|
58
|
-
expect(
|
|
65
|
+
await expect(ArticleRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
66
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
67
|
+
);
|
|
59
68
|
});
|
|
60
69
|
|
|
61
70
|
test("create failed with article errors", async () => {
|
|
62
|
-
const
|
|
71
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
63
72
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
73
|
+
const ctx = buildCtx(article.id);
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
.spyOn(ctx.client.articleRevisions, "create")
|
|
67
|
-
.mockImplementation(() => ({
|
|
68
|
-
revision: null,
|
|
69
|
-
errors: null,
|
|
70
|
-
articleErrors: errors,
|
|
71
|
-
}));
|
|
75
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
jest.spyOn(ctx.client.articleRevisions, "create").mockResolvedValue({
|
|
78
|
+
revision: null,
|
|
79
|
+
errors: null,
|
|
80
|
+
articleErrors: errors,
|
|
81
|
+
});
|
|
76
82
|
|
|
77
|
-
expect(
|
|
83
|
+
await expect(ArticleRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
84
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
85
|
+
);
|
|
78
86
|
});
|
|
79
87
|
});
|
|
@@ -72,7 +72,18 @@ describe("Articles Handler", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(mockedArticleFind).toHaveBeenCalledWith(article.id, "en");
|
|
74
74
|
expect(mockedArticleRevisions).toHaveBeenCalledWith(article.id, "en");
|
|
75
|
-
expect(mockedArticleChanges).toHaveBeenCalledWith(
|
|
75
|
+
expect(mockedArticleChanges).toHaveBeenCalledWith(
|
|
76
|
+
{
|
|
77
|
+
"f:change_type": [
|
|
78
|
+
"revision_publish",
|
|
79
|
+
"revision_lock",
|
|
80
|
+
"revision_unlock",
|
|
81
|
+
"update",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
article.id,
|
|
85
|
+
"en"
|
|
86
|
+
);
|
|
76
87
|
|
|
77
88
|
expect(result).toEqual({
|
|
78
89
|
assigns: {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import Events from "../../../../cloud/Events";
|
|
1
2
|
import EventRevisionsHandler from "../../../../handlers/event_revisions/EventRevisionsHandler";
|
|
3
|
+
|
|
2
4
|
import EventRevisions from "../../../../cloud/EventRevisions";
|
|
3
5
|
|
|
4
6
|
import Factory from "../../../support/factory";
|
|
@@ -7,6 +9,7 @@ function buildCtx(eventID) {
|
|
|
7
9
|
return {
|
|
8
10
|
client: {
|
|
9
11
|
eventRevisions: new EventRevisions(() => ({}), "token"),
|
|
12
|
+
events: new Events(() => ({}), "token"),
|
|
10
13
|
},
|
|
11
14
|
params: { parent_id: eventID },
|
|
12
15
|
locale: "en",
|
|
@@ -14,66 +17,71 @@ function buildCtx(eventID) {
|
|
|
14
17
|
eventType: "event",
|
|
15
18
|
eventTypePlural: "events",
|
|
16
19
|
},
|
|
20
|
+
maxOpenRevisions: 1,
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
describe("Event revisions", () => {
|
|
21
25
|
test("create successful", async () => {
|
|
26
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
22
27
|
const revision = Factory.build("event_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
28
|
+
const ctx = buildCtx(event.id);
|
|
29
|
+
|
|
30
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
jest
|
|
26
33
|
.spyOn(ctx.client.eventRevisions, "create")
|
|
27
|
-
.
|
|
28
|
-
revision,
|
|
29
|
-
errors: null,
|
|
30
|
-
eventErrors: null,
|
|
31
|
-
}));
|
|
34
|
+
.mockResolvedValue({ revision, errors: null, eventErrors: null });
|
|
32
35
|
|
|
33
36
|
const result = await EventRevisionsHandler.create(ctx);
|
|
34
37
|
|
|
35
|
-
expect(
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
redirect: `/en/events/${event.id}`,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("create failed when there is an opened revision for the event", async () => {
|
|
44
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 2 });
|
|
45
|
+
const ctx = buildCtx(event.id);
|
|
46
|
+
|
|
47
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
48
|
+
|
|
49
|
+
const result = await EventRevisionsHandler.create(ctx);
|
|
36
50
|
|
|
37
51
|
expect(result).toEqual({
|
|
38
|
-
redirect: `/en/events/${
|
|
52
|
+
redirect: `/en/events/${event.id}?revisions_count_error=1`,
|
|
39
53
|
});
|
|
40
54
|
});
|
|
41
55
|
|
|
42
56
|
test("create failed with errors", async () => {
|
|
43
|
-
const
|
|
57
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
44
58
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
59
|
+
const ctx = buildCtx(event.id);
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
.spyOn(ctx.client.eventRevisions, "create")
|
|
48
|
-
.mockImplementation(() => ({
|
|
49
|
-
revision: null,
|
|
50
|
-
errors,
|
|
51
|
-
eventErrors: null,
|
|
52
|
-
}));
|
|
61
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
jest
|
|
64
|
+
.spyOn(ctx.client.eventRevisions, "create")
|
|
65
|
+
.mockResolvedValue({ revision: null, errors, eventErrors: null });
|
|
57
66
|
|
|
58
|
-
expect(
|
|
67
|
+
await expect(EventRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
68
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
69
|
+
);
|
|
59
70
|
});
|
|
60
71
|
|
|
61
72
|
test("create failed with event errors", async () => {
|
|
62
|
-
const
|
|
73
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
63
74
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
75
|
+
const ctx = buildCtx(event.id);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
.spyOn(ctx.client.eventRevisions, "create")
|
|
67
|
-
.mockImplementation(() => ({
|
|
68
|
-
revision: null,
|
|
69
|
-
errors: null,
|
|
70
|
-
eventErrors: errors,
|
|
71
|
-
}));
|
|
77
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
jest
|
|
80
|
+
.spyOn(ctx.client.eventRevisions, "create")
|
|
81
|
+
.mockResolvedValue({ revision: null, errors: null, eventErrors: errors });
|
|
76
82
|
|
|
77
|
-
expect(
|
|
83
|
+
await expect(EventRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
84
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
85
|
+
);
|
|
78
86
|
});
|
|
79
87
|
});
|
|
@@ -72,7 +72,18 @@ describe("Events Handler", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(mockedeventFind).toHaveBeenCalledWith(event.id, "en");
|
|
74
74
|
expect(mockedEventRevisions).toHaveBeenCalledWith(event.id, "en");
|
|
75
|
-
expect(mockedEventChanges).toHaveBeenCalledWith(
|
|
75
|
+
expect(mockedEventChanges).toHaveBeenCalledWith(
|
|
76
|
+
{
|
|
77
|
+
"f:change_type": [
|
|
78
|
+
"revision_publish",
|
|
79
|
+
"revision_lock",
|
|
80
|
+
"revision_unlock",
|
|
81
|
+
"update",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
event.id,
|
|
85
|
+
"en"
|
|
86
|
+
);
|
|
76
87
|
|
|
77
88
|
expect(result).toEqual({
|
|
78
89
|
assigns: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import PageRevisionsHandler from "../../../../handlers/page_revisions/PageRevisionsHandler";
|
|
2
1
|
import PageRevisions from "../../../../cloud/PageRevisions";
|
|
3
|
-
|
|
2
|
+
import Pages from "../../../../cloud/Pages";
|
|
3
|
+
import PageRevisionsHandler from "../../../../handlers/page_revisions/PageRevisionsHandler";
|
|
4
4
|
import Factory from "../../../support/factory";
|
|
5
5
|
|
|
6
6
|
function buildCtx(pageID) {
|
|
7
7
|
return {
|
|
8
8
|
client: {
|
|
9
9
|
pageRevisions: new PageRevisions(() => ({}), "token"),
|
|
10
|
+
pages: new Pages(() => ({}), "token"),
|
|
10
11
|
},
|
|
11
12
|
params: { parent_id: pageID },
|
|
12
13
|
locale: "en",
|
|
@@ -14,58 +15,71 @@ function buildCtx(pageID) {
|
|
|
14
15
|
pageType: "page",
|
|
15
16
|
pageTypePlural: "pages",
|
|
16
17
|
},
|
|
18
|
+
maxOpenRevisions: 1,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
describe("Page revisions", () => {
|
|
21
23
|
test("create successful", async () => {
|
|
24
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
22
25
|
const revision = Factory.build("page_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
26
|
+
const ctx = buildCtx(page.id);
|
|
27
|
+
|
|
28
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
jest
|
|
26
31
|
.spyOn(ctx.client.pageRevisions, "create")
|
|
27
|
-
.
|
|
32
|
+
.mockResolvedValue({ revision, errors: null, pageErrors: null });
|
|
28
33
|
|
|
29
34
|
const result = await PageRevisionsHandler.create(ctx);
|
|
30
35
|
|
|
31
|
-
expect(
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
redirect: `/en/pages/${page.id}`,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("create failed when there is an opened revision for the page", async () => {
|
|
42
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 2 });
|
|
43
|
+
const ctx = buildCtx(page.id);
|
|
44
|
+
|
|
45
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
46
|
+
|
|
47
|
+
const result = await PageRevisionsHandler.create(ctx);
|
|
32
48
|
|
|
33
49
|
expect(result).toEqual({
|
|
34
|
-
redirect: `/en/pages/${
|
|
50
|
+
redirect: `/en/pages/${page.id}?revisions_count_error=1`,
|
|
35
51
|
});
|
|
36
52
|
});
|
|
37
53
|
|
|
38
54
|
test("create failed with errors", async () => {
|
|
39
|
-
const
|
|
55
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
40
56
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
57
|
+
const ctx = buildCtx(page.id);
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
.spyOn(ctx.client.pageRevisions, "create")
|
|
44
|
-
.mockImplementation(() => ({ revision: null, errors, pageErrors: null }));
|
|
59
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
jest
|
|
62
|
+
.spyOn(ctx.client.pageRevisions, "create")
|
|
63
|
+
.mockResolvedValue({ revision: null, errors, pageErrors: null });
|
|
49
64
|
|
|
50
|
-
expect(
|
|
65
|
+
await expect(PageRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
66
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
67
|
+
);
|
|
51
68
|
});
|
|
52
69
|
|
|
53
70
|
test("create failed with page errors", async () => {
|
|
54
|
-
const
|
|
71
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
55
72
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
73
|
+
const ctx = buildCtx(page.id);
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
.spyOn(ctx.client.pageRevisions, "create")
|
|
59
|
-
.mockImplementation(() => ({
|
|
60
|
-
revision: null,
|
|
61
|
-
errors: null,
|
|
62
|
-
pageErrors: errors,
|
|
63
|
-
}));
|
|
75
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
jest
|
|
78
|
+
.spyOn(ctx.client.pageRevisions, "create")
|
|
79
|
+
.mockResolvedValue({ revision: null, errors: null, pageErrors: errors });
|
|
68
80
|
|
|
69
|
-
expect(
|
|
81
|
+
await expect(PageRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
82
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
83
|
+
);
|
|
70
84
|
});
|
|
71
85
|
});
|
|
@@ -70,7 +70,18 @@ describe("Pages Handler", () => {
|
|
|
70
70
|
|
|
71
71
|
expect(mockedPageFind).toHaveBeenCalledWith(page.id, "en");
|
|
72
72
|
expect(mockedPageRevisions).toHaveBeenCalledWith(page.id, "en");
|
|
73
|
-
expect(mockedPageChanges).toHaveBeenCalledWith(
|
|
73
|
+
expect(mockedPageChanges).toHaveBeenCalledWith(
|
|
74
|
+
{
|
|
75
|
+
"f:change_type": [
|
|
76
|
+
"revision_publish",
|
|
77
|
+
"revision_lock",
|
|
78
|
+
"revision_unlock",
|
|
79
|
+
"update",
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
page.id,
|
|
83
|
+
"en"
|
|
84
|
+
);
|
|
74
85
|
|
|
75
86
|
expect(result).toEqual({
|
|
76
87
|
assigns: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/${_context.resourceConfig.articleType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
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="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" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<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" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.articleType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<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" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
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="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" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.articleType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="mb-4 history-table-wrapper">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/${_context.resourceConfig.eventType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
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="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" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<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" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.eventType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<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" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
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="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" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.eventType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="history-table-wrapper mb-4">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
|
|
56
56
|
<div id="sidebar" class="flex h-[calc(100vh-56px)] overflow-y-auto relative group">
|
|
57
57
|
<div class="flex w-16 group-[.expanded]:w-60 h-[calc(100vh-56px)] flex-col sticky left-0 top-0 transition-all duration-150 ease-in-out justify-between group/sidebar bg-raketa-primary/5 dark:bg-raketa-primary/15">
|
|
58
|
-
<button id="expand" type="button" class="absolute z-20 flex items-center justify-center rotate-180 transition-all ease-in-out duration-300 group-[.expanded]:rotate-0 rounded-full bottom-
|
|
58
|
+
<button id="expand" type="button" class="absolute z-20 flex items-center justify-center rotate-180 transition-all ease-in-out duration-300 group-[.expanded]:rotate-0 rounded-full bottom-20 size-8 -right-4 bg-raketa-primary text-white">
|
|
59
59
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
|
|
60
60
|
<path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" />
|
|
61
61
|
</svg>
|
|
@@ -65,7 +65,21 @@
|
|
|
65
65
|
<% _context.layout.sidebar.forEach((menu, idx) => { %>
|
|
66
66
|
<% if (menu.submenus) { %>
|
|
67
67
|
|
|
68
|
-
<div class="group/item">
|
|
68
|
+
<div class="relative group-[.expanded]:hidden group/item">
|
|
69
|
+
<div class="aspect-square w-full flex items-center justify-center group-hover/item:bg-raketa-primary">
|
|
70
|
+
<div class="size-6 opacity-100 group-[.expanded]:opacity-50 text-raketa-primary group-hover/item:text-white">
|
|
71
|
+
<%- atob(menu.icon) %>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="absolute z-50 top-0 left-[64px] w-[250px] border-l bg-[#F7F7FC] shadow-lg hidden group-hover/item:block">
|
|
76
|
+
<% menu.submenus.forEach(submenu => { %>
|
|
77
|
+
<a href="<%= `/${_context.locale}${submenu.link}` %>" class="block py-3 px-4 body-2 text-raketa-foreground/50 hover:text-raketa-foreground transition-all ease-in-out duration-300"><%= submenu.label %></a>
|
|
78
|
+
<% }) %>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="hidden group-[.expanded]:block group/item">
|
|
69
83
|
<details class="unstyled-details">
|
|
70
84
|
<summary class="relative flex items-center gap-3 px-5 py-4 text-raketa-primary cursor-pointer transition-all duration-300 ease-in-out">
|
|
71
85
|
<div class="size-6 opacity-100 group-[.expanded]:opacity-50">
|
|
@@ -90,13 +104,12 @@
|
|
|
90
104
|
<% } else { %>
|
|
91
105
|
|
|
92
106
|
<div class="group/item">
|
|
93
|
-
<a href="<%= `/${_context.locale}${menu.link}` %>" class="relative flex items-center gap-3 px-5 py-4 text-raketa-primary hover:bg-raketa-primary hover:text-white group-[.is-active]/item:bg-raketa-primary group-[.is-active]/item:text-white">
|
|
107
|
+
<a href="<%= `/${_context.locale}${menu.link}` %>" title="<%= menu.label %>" class="relative flex items-center gap-3 px-5 py-4 text-raketa-primary hover:bg-raketa-primary hover:text-white group-[.is-active]/item:bg-raketa-primary group-[.is-active]/item:text-white">
|
|
94
108
|
<div class="size-6 opacity-100 group-[.expanded]:opacity-50">
|
|
95
109
|
<%- atob(menu.icon) %>
|
|
96
110
|
</div>
|
|
97
111
|
|
|
98
112
|
<span class="group-[.expanded]:block hidden"><%= menu.label %></span>
|
|
99
|
-
<div class="absolute group-hover/item:opacity-100 group-[.is-active]/item:opacity-100 opacity-0 top-0 z-10 -right-[10px] w-0 h-0 border-t-[28px] border-t-transparent border-b-[28px] border-b-transparent border-l-[10px] border-l-raketa-primary"></div>
|
|
100
113
|
</a>
|
|
101
114
|
</div>
|
|
102
115
|
|
|
@@ -287,4 +300,4 @@
|
|
|
287
300
|
</script>
|
|
288
301
|
</body>
|
|
289
302
|
|
|
290
|
-
</html>
|
|
303
|
+
</html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
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="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" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<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" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.pageType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<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" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
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="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" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.pageType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="history-table-wrapper mb-4">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|