@raketa-cloud/admin 0.0.69 → 0.0.71

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/handlers/articles/TranslationsHandler.js +129 -44
  3. package/src/handlers/events/TranslationsHandler.js +132 -49
  4. package/src/tests/handlers/articles/TranslationHandler.test/create.test.js +291 -0
  5. package/src/tests/handlers/articles/TranslationHandler.test/create_form.test.js +71 -0
  6. package/src/tests/handlers/articles/TranslationHandler.test/index.test.js +91 -0
  7. package/src/tests/handlers/events/TranslationHandler.test/create.test.js +297 -0
  8. package/src/tests/handlers/events/TranslationHandler.test/create_form.test.js +71 -0
  9. package/src/tests/handlers/events/TranslationHandler.test/index.test.js +91 -0
  10. package/src/tests/handlers/pages/TranslationHandler.test/create.test.js +1 -1
  11. package/src/views/_auth/login.ejs +72 -82
  12. package/src/views/articles/_shared/_history_toolbar.ejs +2 -2
  13. package/src/views/articles/show.ejs +15 -4
  14. package/src/views/articles/translations/_translation_toolbar.ejs +18 -0
  15. package/src/views/articles/translations/create_form.ejs +68 -4
  16. package/src/views/articles/translations/index.ejs +39 -0
  17. package/src/views/events/_shared/_history_toolbar.ejs +2 -2
  18. package/src/views/events/show.ejs +15 -4
  19. package/src/views/events/translations/_translation_toolbar.ejs +18 -0
  20. package/src/views/events/translations/create_form.ejs +68 -4
  21. package/src/views/events/translations/index.ejs +39 -0
  22. package/src/views/pages/_shared/_history_toolbar.ejs +2 -2
  23. package/src/views/pages/show.ejs +2 -4
  24. package/src/views/pages/translations/_translation_toolbar.ejs +18 -0
  25. package/src/views/pages/translations/index.ejs +2 -19
  26. package/src/views/articles/translations/_form.ejs +0 -31
  27. package/src/views/events/translations/_form.ejs +0 -33
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raketa-cloud/admin",
3
- "version": "0.0.69",
3
+ "version": "0.0.71",
4
4
  "main": "src/index.js",
5
5
  "scripts": {
6
6
  "dev": "npx tailwindcss -i ./src/styles/application.css -o ../../apps/admin/static/application.css --watch",
@@ -1,25 +1,73 @@
1
- import { toDateTimeInput } from "../../utils";
1
+ const index = async (ctx) => {
2
+ const { parent_id } = ctx.params;
3
+ const { translations } = ctx.query;
4
+ let translatedArticles = [];
5
+
6
+ if (translations) {
7
+ const translationPairs = translations.split(";").map((pair) => {
8
+ const [locale, id] = pair.split(":");
9
+ return { locale, id };
10
+ });
11
+
12
+ translatedArticles = await Promise.all(
13
+ translationPairs.map(async ({ locale, id }) => {
14
+ const article = await ctx.client.articles.find(id, locale);
15
+
16
+ if (!article) {
17
+ throw new Error(
18
+ `Cannot find translated article with id ${id} and locale ${locale}`
19
+ );
20
+ }
21
+
22
+ const revisions = await ctx.client.articleRevisions.all(id, locale);
23
+ const openRevision = revisions.list[0];
24
+
25
+ return {
26
+ ...article,
27
+ translationLocale: locale,
28
+ revisionId: openRevision?.id,
29
+ };
30
+ })
31
+ );
32
+ }
33
+
34
+ const items = await ctx.client.articles.list(
35
+ ctx.query,
36
+ ctx.locale,
37
+ ctx.resourceConfig.articleType
38
+ );
39
+
40
+ const originalArticle = await ctx.client.articles.find(parent_id, ctx.locale);
41
+
42
+ return {
43
+ assigns: {
44
+ items,
45
+ visibilityOptions: ctx.client.articles.visibilityOptions(),
46
+ translations: translatedArticles,
47
+ parent_id,
48
+ articleTitle: originalArticle.title,
49
+ },
50
+ };
51
+ };
2
52
 
3
53
  const create_form = async (ctx) => {
4
- const { target_locale } = ctx.query;
5
- const target_locate_tags = await ctx.client.tags.all(target_locale);
54
+ const { target_locale = "" } = ctx.query;
6
55
 
7
56
  const { parent_id } = ctx.params;
8
57
  const blueprint = await ctx.client.articles.find(parent_id, ctx.locale);
58
+
59
+ if (!blueprint) {
60
+ throw new Error(`Cannot find article with id ${parent_id}`);
61
+ }
62
+
9
63
  const resource = structuredClone(blueprint);
10
64
 
11
65
  return {
12
66
  assigns: {
13
- resource: {
14
- ...resource,
15
- tags: [],
16
- publishAt: toDateTimeInput(blueprint.publishAt),
17
- },
67
+ resource,
18
68
  blueprint,
19
69
  targetLocale: target_locale,
20
- tags: target_locate_tags.list,
21
70
  errors: {},
22
- visibilityOptions: ctx.client.articles.visibilityOptions(),
23
71
  _translations: [],
24
72
  },
25
73
  };
@@ -27,54 +75,91 @@ const create_form = async (ctx) => {
27
75
 
28
76
  const create = async (ctx) => {
29
77
  const { parent_id } = ctx.params;
78
+ const originalLocale = ctx.locale;
30
79
  const blueprint = await ctx.client.articles.find(parent_id, ctx.locale);
31
80
 
32
- const { target_locale } = ctx.resourceParams;
33
- const params = { ...ctx.resourceParams.resource };
34
-
35
- const resource = ctx.client.articles.deepMerge(
36
- {
37
- settings: {
38
- ...blueprint.settings,
39
- },
40
- widgets: blueprint.widgets,
41
- categories: blueprint.categories.map((category) => category.id),
42
- tags: [],
43
- },
44
- ctx.client.articles.formatDates(params)
45
- );
81
+ const formLanguages = Array.isArray(ctx.resourceParams.resource.languages)
82
+ ? ctx.resourceParams.resource.languages
83
+ : [ctx.resourceParams.resource.languages].filter(Boolean);
46
84
 
47
- const { errors } = await ctx.client.articles.translate(
48
- parent_id,
49
- target_locale,
50
- resource
85
+ const existingTranslations = Object.keys(blueprint.translations || {});
86
+ const selectedLocales = formLanguages.filter(
87
+ (locale) =>
88
+ locale !== originalLocale && !existingTranslations.includes(locale)
51
89
  );
52
90
 
53
- if (errors) {
54
- const tags = await ctx.client.tags.all(target_locale);
91
+ const params = {
92
+ settings: blueprint.settings,
93
+ widgets: blueprint.widgets,
94
+ categories: blueprint.categories.map((category) => category.id),
95
+ publishAt: blueprint.publishAt,
96
+ title: blueprint.title,
97
+ slug: blueprint.slug,
98
+ tags: [],
99
+ };
100
+
101
+ const translationIds = [];
102
+
103
+ for (const locale of selectedLocales) {
104
+ const resource = ctx.client.articles.deepMerge(params, {
105
+ visibility: "private",
106
+ });
107
+
108
+ const { errors, article } = await ctx.client.articles.translate(
109
+ parent_id,
110
+ locale,
111
+ resource
112
+ );
113
+
114
+ if (errors) {
115
+ return {
116
+ assigns: {
117
+ resource: {
118
+ ...resource,
119
+ translations: blueprint.translations || {},
120
+ languages: formLanguages,
121
+ },
122
+ blueprint,
123
+ targetLocale: locale,
124
+ errors: { resource: ctx.client.articles.formatErrors(errors) },
125
+ },
126
+ };
127
+ }
128
+
129
+ if (article) {
130
+ translationIds.push(`${locale}:${article.id}`);
131
+ }
132
+
133
+ const { errors: revisionErrors } = await ctx.client.articleRevisions.create(
134
+ article.id,
135
+ locale
136
+ );
55
137
 
56
- return {
57
- assigns: {
58
- resource: {
59
- ...resource,
60
- publishAt: toDateTimeInput(resource.publishAt),
138
+ if (revisionErrors) {
139
+ return {
140
+ assigns: {
141
+ resource: {
142
+ ...resource,
143
+ translations: blueprint.translations || {},
144
+ languages: formLanguages,
145
+ },
146
+ blueprint,
147
+ targetLocale: locale,
148
+ errors: {
149
+ resource: ctx.client.articles.formatErrors(revisionErrors),
150
+ },
61
151
  },
62
- blueprint,
63
- targetLocale: target_locale,
64
- tags: tags.list,
65
- errors: { resource: ctx.client.pages.formatErrors(errors) },
66
- visibilityOptions: ctx.client.articles.visibilityOptions(),
67
- _translations: [],
68
- },
69
- };
152
+ };
153
+ }
70
154
  }
71
155
 
72
156
  return {
73
- redirect: `/${ctx.locale}/${ctx.resourceConfig.articleTypePlural}/${parent_id}`,
157
+ redirect: `/${originalLocale}/${ctx.resourceConfig.articleTypePlural}/${parent_id}/translations?translations=${translationIds.join(";")}`,
74
158
  };
75
159
  };
76
160
 
77
161
  export default {
162
+ index,
78
163
  create_form,
79
164
  create,
80
165
  };
@@ -1,29 +1,73 @@
1
- import { toDateTimeInput } from "../../utils";
1
+ const index = async (ctx) => {
2
+ const { parent_id } = ctx.params;
3
+ const { translations } = ctx.query;
4
+ let translatedEvents = [];
5
+
6
+ if (translations) {
7
+ const translationPairs = translations.split(";").map((pair) => {
8
+ const [locale, id] = pair.split(":");
9
+ return { locale, id };
10
+ });
11
+
12
+ translatedEvents = await Promise.all(
13
+ translationPairs.map(async ({ locale, id }) => {
14
+ const event = await ctx.client.events.find(id, locale);
15
+
16
+ if (!event) {
17
+ throw new Error(
18
+ `Cannot find translated event with id ${id} and locale ${locale}`
19
+ );
20
+ }
21
+
22
+ const revisions = await ctx.client.eventRevisions.all(id, locale);
23
+ const openRevision = revisions.list[0];
24
+
25
+ return {
26
+ ...event,
27
+ translationLocale: locale,
28
+ revisionId: openRevision?.id,
29
+ };
30
+ })
31
+ );
32
+ }
33
+
34
+ const items = await ctx.client.events.list(
35
+ ctx.query,
36
+ ctx.locale,
37
+ ctx.resourceConfig.eventType
38
+ );
39
+
40
+ const originalEvent = await ctx.client.events.find(parent_id, ctx.locale);
41
+
42
+ return {
43
+ assigns: {
44
+ items,
45
+ visibilityOptions: ctx.client.events.visibilityOptions(),
46
+ translations: translatedEvents,
47
+ parent_id,
48
+ eventTitle: originalEvent.title,
49
+ },
50
+ };
51
+ };
2
52
 
3
53
  const create_form = async (ctx) => {
4
- // const tags = await ctx.client.tags.all(ctx.locale);
5
- const { target_locale } = ctx.query;
6
- const target_locate_tags = await ctx.client.tags.all(target_locale);
54
+ const { target_locale = "" } = ctx.query;
7
55
 
8
56
  const { parent_id } = ctx.params;
9
57
  const blueprint = await ctx.client.events.find(parent_id, ctx.locale);
10
58
 
59
+ if (!blueprint) {
60
+ throw new Error(`Cannot find event with id ${parent_id}`);
61
+ }
62
+
11
63
  const resource = structuredClone(blueprint);
12
64
 
13
65
  return {
14
66
  assigns: {
15
- resource: {
16
- ...resource,
17
- tags: [],
18
- publishAt: toDateTimeInput(blueprint.publishAt),
19
- startsAt: toDateTimeInput(blueprint.startsAt),
20
- endsAt: toDateTimeInput(blueprint.endsAt),
21
- },
67
+ resource,
22
68
  blueprint,
23
69
  targetLocale: target_locale,
24
- tags: target_locate_tags.list,
25
70
  errors: {},
26
- visibilityOptions: ctx.client.events.visibilityOptions(),
27
71
  _translations: [],
28
72
  },
29
73
  };
@@ -31,54 +75,93 @@ const create_form = async (ctx) => {
31
75
 
32
76
  const create = async (ctx) => {
33
77
  const { parent_id } = ctx.params;
78
+ const originalLocale = ctx.locale;
34
79
  const blueprint = await ctx.client.events.find(parent_id, ctx.locale);
35
80
 
36
- const { target_locale } = ctx.resourceParams;
37
- const params = { ...ctx.resourceParams.resource };
38
-
39
- const resource = ctx.client.events.deepMerge(
40
- {
41
- settings: {
42
- ...blueprint.settings,
43
- },
44
- widgets: blueprint.widgets,
45
- categories: blueprint.categories.map((category) => category.id),
46
- tags: [],
47
- },
48
- ctx.client.events.formatDates(params)
49
- );
81
+ const formLanguages = Array.isArray(ctx.resourceParams.resource.languages)
82
+ ? ctx.resourceParams.resource.languages
83
+ : [ctx.resourceParams.resource.languages].filter(Boolean);
50
84
 
51
- const { errors } = await ctx.client.events.translate(
52
- parent_id,
53
- target_locale,
54
- resource
85
+ const existingTranslations = Object.keys(blueprint.translations || {});
86
+ const selectedLocales = formLanguages.filter(
87
+ (locale) =>
88
+ locale !== originalLocale && !existingTranslations.includes(locale)
55
89
  );
56
90
 
57
- if (errors) {
58
- const tags = await ctx.client.tags.all(target_locale);
59
- return {
60
- assigns: {
61
- resource: {
62
- ...resource,
63
- publishAt: toDateTimeInput(resource.publishAt),
64
- startsAt: toDateTimeInput(resource.startsAt),
65
- endsAt: toDateTimeInput(resource.endsAt),
91
+ const params = {
92
+ settings: blueprint.settings,
93
+ widgets: blueprint.widgets,
94
+ categories: blueprint.categories.map((category) => category.id),
95
+ publishAt: blueprint.publishAt,
96
+ startsAt: blueprint.startsAt,
97
+ endsAt: blueprint.endsAt,
98
+ title: blueprint.title,
99
+ slug: blueprint.slug,
100
+ tags: [],
101
+ };
102
+
103
+ const translationIds = [];
104
+
105
+ for (const locale of selectedLocales) {
106
+ const resource = ctx.client.events.deepMerge(params, {
107
+ visibility: "private",
108
+ });
109
+
110
+ const { errors, event } = await ctx.client.events.translate(
111
+ parent_id,
112
+ locale,
113
+ resource
114
+ );
115
+
116
+ if (errors) {
117
+ return {
118
+ assigns: {
119
+ resource: {
120
+ ...resource,
121
+ translations: blueprint.translations || {},
122
+ languages: formLanguages,
123
+ },
124
+ blueprint,
125
+ targetLocale: locale,
126
+ errors: { resource: ctx.client.events.formatErrors(errors) },
127
+ },
128
+ };
129
+ }
130
+
131
+ if (event) {
132
+ translationIds.push(`${locale}:${event.id}`);
133
+ }
134
+
135
+ const { errors: revisionErrors } = await ctx.client.eventRevisions.create(
136
+ event.id,
137
+ locale
138
+ );
139
+
140
+ if (revisionErrors) {
141
+ return {
142
+ assigns: {
143
+ resource: {
144
+ ...resource,
145
+ translations: blueprint.translations || {},
146
+ languages: formLanguages,
147
+ },
148
+ blueprint,
149
+ targetLocale: locale,
150
+ errors: {
151
+ resource: ctx.client.events.formatErrors(revisionErrors),
152
+ },
66
153
  },
67
- blueprint,
68
- targetLocale: target_locale,
69
- tags: tags.list,
70
- errors: { resource: ctx.client.pages.formatErrors(errors) },
71
- visibilityOptions: ctx.client.events.visibilityOptions(),
72
- _translations: [],
73
- },
74
- };
154
+ };
155
+ }
75
156
  }
157
+
76
158
  return {
77
- redirect: `/${ctx.locale}/${ctx.resourceConfig.eventTypePlural}/${parent_id}`,
159
+ redirect: `/${originalLocale}/${ctx.resourceConfig.eventTypePlural}/${parent_id}/translations?translations=${translationIds.join(";")}`,
78
160
  };
79
161
  };
80
162
 
81
163
  export default {
164
+ index,
82
165
  create_form,
83
166
  create,
84
167
  };