@raketa-cloud/admin 1.10.5 → 1.10.7
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 +93 -87
- package/package.json +1 -1
- package/src/cloud/Resources.js +39 -0
- package/src/cloud/documents/contentTasksDocuments.js +1 -0
- package/src/cloud/documents/resourcesDocuments.js +12 -0
- package/src/handlers/resources/PartialTranslationsHandler.js +279 -0
- package/src/routeResourceModule.js +11 -0
- package/src/styles/application.css +4 -0
- package/src/tests/handlers/resources/PartialTranslationsHandler.test.js +279 -0
- package/src/tests/routeResourceModule.test.js +26 -0
- package/src/tests/views/due_date.test.js +121 -0
- package/src/tests/views/partial_translations.test.js +50 -0
- package/src/views/_shared/_content_tasks.ejs +14 -2
- package/src/views/resources/_shared/_due_date.ejs +71 -0
- package/src/views/resources/partial-translations/create_form.ejs +72 -0
- package/src/views/resources/translations/create_form.ejs +4 -82
package/dist/application.css
CHANGED
|
@@ -326,6 +326,9 @@
|
|
|
326
326
|
.z-50 {
|
|
327
327
|
z-index: 50;
|
|
328
328
|
}
|
|
329
|
+
.m-auto {
|
|
330
|
+
margin: auto;
|
|
331
|
+
}
|
|
329
332
|
.my-4 {
|
|
330
333
|
margin-block: calc(var(--spacing) * 4);
|
|
331
334
|
}
|
|
@@ -514,6 +517,9 @@
|
|
|
514
517
|
.flex-none {
|
|
515
518
|
flex: none;
|
|
516
519
|
}
|
|
520
|
+
.shrink-0 {
|
|
521
|
+
flex-shrink: 0;
|
|
522
|
+
}
|
|
517
523
|
.-translate-x-1\/2 {
|
|
518
524
|
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
|
519
525
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
@@ -569,6 +575,9 @@
|
|
|
569
575
|
.justify-end {
|
|
570
576
|
justify-content: flex-end;
|
|
571
577
|
}
|
|
578
|
+
.gap-0\.5 {
|
|
579
|
+
gap: calc(var(--spacing) * 0.5);
|
|
580
|
+
}
|
|
572
581
|
.gap-1 {
|
|
573
582
|
gap: var(--spacing);
|
|
574
583
|
}
|
|
@@ -587,6 +596,11 @@
|
|
|
587
596
|
.gap-8 {
|
|
588
597
|
gap: calc(var(--spacing) * 8);
|
|
589
598
|
}
|
|
599
|
+
:where(.space-y-1 > :not(:last-child)) {
|
|
600
|
+
--tw-space-y-reverse: 0;
|
|
601
|
+
margin-block-start: calc(var(--spacing) * var(--tw-space-y-reverse));
|
|
602
|
+
margin-block-end: calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)));
|
|
603
|
+
}
|
|
590
604
|
:where(.divide-y > :not(:last-child)) {
|
|
591
605
|
--tw-divide-y-reverse: 0;
|
|
592
606
|
border-bottom-style: var(--tw-border-style);
|
|
@@ -1146,92 +1160,78 @@
|
|
|
1146
1160
|
padding-right: 0px;
|
|
1147
1161
|
}
|
|
1148
1162
|
}
|
|
1149
|
-
|
|
1150
|
-
:
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 50%, transparent);
|
|
1161
|
-
}
|
|
1163
|
+
:where(.dark\:divide-neutral-700:where(.dark, .dark *) > :not(:last-child)) {
|
|
1164
|
+
border-color: var(--color-neutral-700);
|
|
1165
|
+
}
|
|
1166
|
+
.dark\:border-neutral-500:where(.dark, .dark *) {
|
|
1167
|
+
border-color: var(--color-neutral-500);
|
|
1168
|
+
}
|
|
1169
|
+
.dark\:border-neutral-700:where(.dark, .dark *) {
|
|
1170
|
+
border-color: var(--color-neutral-700);
|
|
1171
|
+
}
|
|
1172
|
+
.dark\:bg-blue-900\/50:where(.dark, .dark *) {
|
|
1173
|
+
background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 50%, transparent);
|
|
1162
1174
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1163
|
-
|
|
1164
|
-
background-color: color-mix(in oklab, var(--color-blue-900) 50%, transparent);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
.dark\:bg-emerald-900\/50 {
|
|
1168
|
-
background-color: color-mix(in srgb, oklch(37.8% 0.077 168.94) 50%, transparent);
|
|
1175
|
+
background-color: color-mix(in oklab, var(--color-blue-900) 50%, transparent);
|
|
1169
1176
|
}
|
|
1177
|
+
}
|
|
1178
|
+
.dark\:bg-emerald-900\/50:where(.dark, .dark *) {
|
|
1179
|
+
background-color: color-mix(in srgb, oklch(37.8% 0.077 168.94) 50%, transparent);
|
|
1170
1180
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1171
|
-
|
|
1172
|
-
background-color: color-mix(in oklab, var(--color-emerald-900) 50%, transparent);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
.dark\:bg-neutral-800 {
|
|
1176
|
-
background-color: var(--color-neutral-800);
|
|
1177
|
-
}
|
|
1178
|
-
.dark\:bg-orange-900\/50 {
|
|
1179
|
-
background-color: color-mix(in srgb, oklch(40.8% 0.123 38.172) 50%, transparent);
|
|
1181
|
+
background-color: color-mix(in oklab, var(--color-emerald-900) 50%, transparent);
|
|
1180
1182
|
}
|
|
1183
|
+
}
|
|
1184
|
+
.dark\:bg-neutral-800:where(.dark, .dark *) {
|
|
1185
|
+
background-color: var(--color-neutral-800);
|
|
1186
|
+
}
|
|
1187
|
+
.dark\:bg-orange-900\/50:where(.dark, .dark *) {
|
|
1188
|
+
background-color: color-mix(in srgb, oklch(40.8% 0.123 38.172) 50%, transparent);
|
|
1181
1189
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1182
|
-
|
|
1183
|
-
background-color: color-mix(in oklab, var(--color-orange-900) 50%, transparent);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
.dark\:bg-orange-950\/40 {
|
|
1187
|
-
background-color: color-mix(in srgb, oklch(26.6% 0.079 36.259) 40%, transparent);
|
|
1190
|
+
background-color: color-mix(in oklab, var(--color-orange-900) 50%, transparent);
|
|
1188
1191
|
}
|
|
1192
|
+
}
|
|
1193
|
+
.dark\:bg-orange-950\/40:where(.dark, .dark *) {
|
|
1194
|
+
background-color: color-mix(in srgb, oklch(26.6% 0.079 36.259) 40%, transparent);
|
|
1189
1195
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1190
|
-
|
|
1191
|
-
background-color: color-mix(in oklab, var(--color-orange-950) 40%, transparent);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
.dark\:bg-raketa-primary\/15 {
|
|
1195
|
-
background-color: color-mix(in srgb, rgb(var(--primary)) 15%, transparent);
|
|
1196
|
+
background-color: color-mix(in oklab, var(--color-orange-950) 40%, transparent);
|
|
1196
1197
|
}
|
|
1198
|
+
}
|
|
1199
|
+
.dark\:bg-raketa-primary\/15:where(.dark, .dark *) {
|
|
1200
|
+
background-color: color-mix(in srgb, rgb(var(--primary)) 15%, transparent);
|
|
1197
1201
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1198
|
-
|
|
1199
|
-
background-color: color-mix(in oklab, var(--color-raketa-primary) 15%, transparent);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
.dark\:bg-red-900\/50 {
|
|
1203
|
-
background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 50%, transparent);
|
|
1202
|
+
background-color: color-mix(in oklab, var(--color-raketa-primary) 15%, transparent);
|
|
1204
1203
|
}
|
|
1204
|
+
}
|
|
1205
|
+
.dark\:bg-red-900\/50:where(.dark, .dark *) {
|
|
1206
|
+
background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 50%, transparent);
|
|
1205
1207
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1206
|
-
|
|
1207
|
-
background-color: color-mix(in oklab, var(--color-red-900) 50%, transparent);
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
.dark\:text-blue-300 {
|
|
1211
|
-
color: var(--color-blue-300);
|
|
1212
|
-
}
|
|
1213
|
-
.dark\:text-emerald-300 {
|
|
1214
|
-
color: var(--color-emerald-300);
|
|
1215
|
-
}
|
|
1216
|
-
.dark\:text-orange-300 {
|
|
1217
|
-
color: var(--color-orange-300);
|
|
1208
|
+
background-color: color-mix(in oklab, var(--color-red-900) 50%, transparent);
|
|
1218
1209
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1210
|
+
}
|
|
1211
|
+
.dark\:text-blue-300:where(.dark, .dark *) {
|
|
1212
|
+
color: var(--color-blue-300);
|
|
1213
|
+
}
|
|
1214
|
+
.dark\:text-emerald-300:where(.dark, .dark *) {
|
|
1215
|
+
color: var(--color-emerald-300);
|
|
1216
|
+
}
|
|
1217
|
+
.dark\:text-orange-300:where(.dark, .dark *) {
|
|
1218
|
+
color: var(--color-orange-300);
|
|
1219
|
+
}
|
|
1220
|
+
.dark\:text-red-300:where(.dark, .dark *) {
|
|
1221
|
+
color: var(--color-red-300);
|
|
1222
|
+
}
|
|
1223
|
+
.dark\:invert:where(.dark, .dark *) {
|
|
1224
|
+
--tw-invert: invert(100%);
|
|
1225
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1226
|
+
}
|
|
1227
|
+
@media (hover: hover) {
|
|
1228
|
+
.dark\:hover\:bg-neutral-700:where(.dark, .dark *):hover {
|
|
1229
|
+
background-color: var(--color-neutral-700);
|
|
1230
1230
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1231
|
+
}
|
|
1232
|
+
@media (width >= 64rem) {
|
|
1233
|
+
:where(.dark\:lg\:divide-neutral-700:where(.dark, .dark *) > :not(:last-child)) {
|
|
1234
|
+
border-color: var(--color-neutral-700);
|
|
1235
1235
|
}
|
|
1236
1236
|
}
|
|
1237
1237
|
}
|
|
@@ -1692,7 +1692,7 @@ body {
|
|
|
1692
1692
|
line-height: var(--tw-leading, var(--text-sm--line-height));
|
|
1693
1693
|
--tw-leading: 1.5;
|
|
1694
1694
|
line-height: 1.5;
|
|
1695
|
-
|
|
1695
|
+
&:where(.dark, .dark *) {
|
|
1696
1696
|
background-color: color-mix(in srgb, rgb(var(--primary)) 20%, transparent);
|
|
1697
1697
|
@supports (color: color-mix(in lab, red, red)) {
|
|
1698
1698
|
background-color: color-mix(in oklab, var(--color-raketa-primary) 20%, transparent);
|
|
@@ -1825,7 +1825,7 @@ body {
|
|
|
1825
1825
|
color: var(--color-raketa-foreground);
|
|
1826
1826
|
}
|
|
1827
1827
|
}
|
|
1828
|
-
|
|
1828
|
+
&:where(.dark, .dark *) {
|
|
1829
1829
|
color: var(--color-red-700);
|
|
1830
1830
|
}
|
|
1831
1831
|
}
|
|
@@ -1903,18 +1903,18 @@ body {
|
|
|
1903
1903
|
padding-block: calc(var(--spacing) * 2);
|
|
1904
1904
|
--tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
|
|
1905
1905
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1906
|
-
|
|
1906
|
+
&:where(.dark, .dark *) {
|
|
1907
1907
|
border-color: var(--color-neutral-700);
|
|
1908
1908
|
}
|
|
1909
|
-
|
|
1909
|
+
&:where(.dark, .dark *) {
|
|
1910
1910
|
background-color: var(--color-neutral-800);
|
|
1911
1911
|
}
|
|
1912
|
-
|
|
1912
|
+
&:where(.dark, .dark *) {
|
|
1913
1913
|
&:focus {
|
|
1914
1914
|
border-color: var(--color-raketa-primary);
|
|
1915
1915
|
}
|
|
1916
1916
|
}
|
|
1917
|
-
|
|
1917
|
+
&:where(.dark, .dark *) {
|
|
1918
1918
|
&:focus {
|
|
1919
1919
|
--tw-ring-color: var(--color-raketa-primary);
|
|
1920
1920
|
}
|
|
@@ -1944,7 +1944,7 @@ body {
|
|
|
1944
1944
|
padding-block: var(--spacing);
|
|
1945
1945
|
--tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
|
|
1946
1946
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1947
|
-
|
|
1947
|
+
&:where(.dark, .dark *) {
|
|
1948
1948
|
border-color: var(--color-neutral-700);
|
|
1949
1949
|
}
|
|
1950
1950
|
}
|
|
@@ -2057,7 +2057,7 @@ body {
|
|
|
2057
2057
|
transition-duration: 100ms;
|
|
2058
2058
|
--tw-ease: var(--ease-in-out);
|
|
2059
2059
|
transition-timing-function: var(--ease-in-out);
|
|
2060
|
-
|
|
2060
|
+
&:where(.dark, .dark *) {
|
|
2061
2061
|
border-color: var(--color-neutral-700);
|
|
2062
2062
|
}
|
|
2063
2063
|
}
|
|
@@ -2069,7 +2069,7 @@ body {
|
|
|
2069
2069
|
border-bottom-color: color-mix(in oklab, var(--color-black) 25%, transparent);
|
|
2070
2070
|
}
|
|
2071
2071
|
color: var(--color-raketa-foreground);
|
|
2072
|
-
|
|
2072
|
+
&:where(.dark, .dark *) {
|
|
2073
2073
|
border-bottom-color: color-mix(in srgb, #fff 40%, transparent);
|
|
2074
2074
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2075
2075
|
border-bottom-color: color-mix(in oklab, var(--color-white) 40%, transparent);
|
|
@@ -2082,10 +2082,10 @@ body {
|
|
|
2082
2082
|
border-color: var(--color-neutral-300);
|
|
2083
2083
|
background-color: var(--color-neutral-50);
|
|
2084
2084
|
color: var(--color-raketa-foreground);
|
|
2085
|
-
|
|
2085
|
+
&:where(.dark, .dark *) {
|
|
2086
2086
|
border-color: var(--color-neutral-600);
|
|
2087
2087
|
}
|
|
2088
|
-
|
|
2088
|
+
&:where(.dark, .dark *) {
|
|
2089
2089
|
background-color: var(--color-neutral-800);
|
|
2090
2090
|
}
|
|
2091
2091
|
}
|
|
@@ -2189,10 +2189,10 @@ body {
|
|
|
2189
2189
|
border-left-width: 1px;
|
|
2190
2190
|
background-color: var(--color-slate-50);
|
|
2191
2191
|
padding: calc(var(--spacing) * 4);
|
|
2192
|
-
|
|
2192
|
+
&:where(.dark, .dark *) {
|
|
2193
2193
|
border-color: var(--color-neutral-700);
|
|
2194
2194
|
}
|
|
2195
|
-
|
|
2195
|
+
&:where(.dark, .dark *) {
|
|
2196
2196
|
background-color: color-mix(in srgb, oklch(26.9% 0 none) 70%, transparent);
|
|
2197
2197
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2198
2198
|
background-color: color-mix(in oklab, var(--color-neutral-800) 70%, transparent);
|
|
@@ -2215,7 +2215,7 @@ body {
|
|
|
2215
2215
|
height: calc(var(--spacing) * 6);
|
|
2216
2216
|
width: 1px;
|
|
2217
2217
|
background-color: var(--color-mono-50);
|
|
2218
|
-
|
|
2218
|
+
&:where(.dark, .dark *) {
|
|
2219
2219
|
background-color: var(--color-neutral-700);
|
|
2220
2220
|
}
|
|
2221
2221
|
}
|
|
@@ -2492,6 +2492,11 @@ body {
|
|
|
2492
2492
|
syntax: "*";
|
|
2493
2493
|
inherits: false;
|
|
2494
2494
|
}
|
|
2495
|
+
@property --tw-space-y-reverse {
|
|
2496
|
+
syntax: "*";
|
|
2497
|
+
inherits: false;
|
|
2498
|
+
initial-value: 0;
|
|
2499
|
+
}
|
|
2495
2500
|
@property --tw-divide-y-reverse {
|
|
2496
2501
|
syntax: "*";
|
|
2497
2502
|
inherits: false;
|
|
@@ -2724,6 +2729,7 @@ body {
|
|
|
2724
2729
|
--tw-rotate-z: initial;
|
|
2725
2730
|
--tw-skew-x: initial;
|
|
2726
2731
|
--tw-skew-y: initial;
|
|
2732
|
+
--tw-space-y-reverse: 0;
|
|
2727
2733
|
--tw-divide-y-reverse: 0;
|
|
2728
2734
|
--tw-border-style: solid;
|
|
2729
2735
|
--tw-gradient-position: initial;
|
package/package.json
CHANGED
package/src/cloud/Resources.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CreateResourceDocument,
|
|
8
8
|
DeleteResourceDocument,
|
|
9
9
|
MiniResourcesDocument,
|
|
10
|
+
MultiPartialTranslateResourceDocument,
|
|
10
11
|
MultiTranslateResourceDocument,
|
|
11
12
|
ResourceByIdDocument,
|
|
12
13
|
ResourceChangePreviewDocument,
|
|
@@ -38,6 +39,17 @@ export default class Resources extends RaketaClient {
|
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
previewUrl(resource, locale) {
|
|
43
|
+
return buildMagicPreviewAddress(
|
|
44
|
+
{
|
|
45
|
+
slug: resource.slug,
|
|
46
|
+
locale,
|
|
47
|
+
resourceType: resource.resourceType,
|
|
48
|
+
},
|
|
49
|
+
this.getConfig(),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
visibilityOptions() {
|
|
42
54
|
return [
|
|
43
55
|
["private", "Private"],
|
|
@@ -213,6 +225,33 @@ export default class Resources extends RaketaClient {
|
|
|
213
225
|
return { resources, errors, localeErrors };
|
|
214
226
|
}
|
|
215
227
|
|
|
228
|
+
async multiPartialTranslate(
|
|
229
|
+
sourceResourceId,
|
|
230
|
+
locales,
|
|
231
|
+
widgets,
|
|
232
|
+
workflow,
|
|
233
|
+
resource,
|
|
234
|
+
) {
|
|
235
|
+
const input = {
|
|
236
|
+
sourceResourceId,
|
|
237
|
+
translations: locales.map((locale) => ({
|
|
238
|
+
locale,
|
|
239
|
+
widgets: JSON.stringify(widgets),
|
|
240
|
+
})),
|
|
241
|
+
};
|
|
242
|
+
this.appendWorkflow(input, workflow, {
|
|
243
|
+
slug: resource.slug,
|
|
244
|
+
locale: resource.locale,
|
|
245
|
+
resourceType: resource.resourceType,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const {
|
|
249
|
+
multiPartialTranslateResource: { contentTask, errors },
|
|
250
|
+
} = await this.query(MultiPartialTranslateResourceDocument, { input });
|
|
251
|
+
|
|
252
|
+
return { contentTask, errors };
|
|
253
|
+
}
|
|
254
|
+
|
|
216
255
|
async translateBatch(
|
|
217
256
|
sourceResourceId,
|
|
218
257
|
locales,
|
|
@@ -199,6 +199,17 @@ const MultiTranslateResourceDocument = gql`
|
|
|
199
199
|
}
|
|
200
200
|
`;
|
|
201
201
|
|
|
202
|
+
const MultiPartialTranslateResourceDocument = gql`
|
|
203
|
+
mutation MultiPartialTranslateResource(
|
|
204
|
+
$input: MultiPartialTranslateResourceInput!
|
|
205
|
+
) {
|
|
206
|
+
multiPartialTranslateResource(input: $input) {
|
|
207
|
+
contentTask ${contentTaskFragment()}
|
|
208
|
+
errors ${errorsFragment()}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
|
|
202
213
|
const ResourceRevisionsDocument = gql`
|
|
203
214
|
query ResourceRevisions($input: IdTypeInput!) {
|
|
204
215
|
resourceRevisions(input: $input) {
|
|
@@ -351,6 +362,7 @@ export {
|
|
|
351
362
|
ResourceAndSiteDocument,
|
|
352
363
|
TranslateResourceDocument,
|
|
353
364
|
MultiTranslateResourceDocument,
|
|
365
|
+
MultiPartialTranslateResourceDocument,
|
|
354
366
|
ResourceRevisionDocument,
|
|
355
367
|
ResourceRevisionsDocument,
|
|
356
368
|
StartResourceRevisionDocument,
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const toArray = (value) =>
|
|
2
|
+
Array.isArray(value) ? value : [value].filter(Boolean);
|
|
3
|
+
|
|
4
|
+
const isActiveContentTask = (task) =>
|
|
5
|
+
task && !task.completedAt && !task.failedAt;
|
|
6
|
+
|
|
7
|
+
const hasActiveContentTask = (resource) =>
|
|
8
|
+
isActiveContentTask(resource.activeContentTask) ||
|
|
9
|
+
Object.values(resource.translations || {}).some((translation) =>
|
|
10
|
+
isActiveContentTask(translation.active_content_task),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const hasTranslations = (resource) =>
|
|
14
|
+
Object.values(resource.translations || {}).some(
|
|
15
|
+
(translation) => translation?.id,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const targetTranslationLocales = (resource, sourceLocale) =>
|
|
19
|
+
Object.entries(resource.translations || {})
|
|
20
|
+
.filter(
|
|
21
|
+
([locale, translation]) => locale !== sourceLocale && translation?.id,
|
|
22
|
+
)
|
|
23
|
+
.map(([locale]) => locale);
|
|
24
|
+
|
|
25
|
+
const selectedWidgetIds = (resource, query = {}) => {
|
|
26
|
+
const requested = Array.isArray(query.widget_ids)
|
|
27
|
+
? query.widget_ids
|
|
28
|
+
: String(query.widget_ids || "")
|
|
29
|
+
.split(",")
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
|
|
32
|
+
return [...new Set(requested)];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const partitionWidgetIds = (resource, widgetIds) => {
|
|
36
|
+
const resourceWidgetIds = new Set(
|
|
37
|
+
(resource.widgets || []).map((widget) => widget.widgetId).filter(Boolean),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
validWidgetIds: widgetIds.filter((widgetId) =>
|
|
42
|
+
resourceWidgetIds.has(widgetId),
|
|
43
|
+
),
|
|
44
|
+
unknownWidgetIds: widgetIds.filter(
|
|
45
|
+
(widgetId) => !resourceWidgetIds.has(widgetId),
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const formAssigns = (resource, query, previewUrl, values = {}, errors = {}) => {
|
|
51
|
+
const submittedWidgetIds = values.widgetIds ?? values.widget_ids;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
resource,
|
|
55
|
+
previewUrl,
|
|
56
|
+
partialTranslation: {
|
|
57
|
+
locales: [],
|
|
58
|
+
workflow: {},
|
|
59
|
+
...values,
|
|
60
|
+
widgetIds:
|
|
61
|
+
submittedWidgetIds === undefined
|
|
62
|
+
? selectedWidgetIds(resource, query)
|
|
63
|
+
: toArray(submittedWidgetIds),
|
|
64
|
+
},
|
|
65
|
+
errors,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const validatePartialTranslation = ({
|
|
70
|
+
resource,
|
|
71
|
+
locales,
|
|
72
|
+
widgetIds,
|
|
73
|
+
workflow,
|
|
74
|
+
unknownWidgetIds,
|
|
75
|
+
unknownLocales,
|
|
76
|
+
}) => {
|
|
77
|
+
const errors = [];
|
|
78
|
+
|
|
79
|
+
if (locales.length === 0) {
|
|
80
|
+
errors.push({
|
|
81
|
+
key: "locales",
|
|
82
|
+
message: "select at least one existing translation",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (widgetIds.length === 0) {
|
|
86
|
+
errors.push({ key: "widgetIds", message: "can't be blank" });
|
|
87
|
+
}
|
|
88
|
+
if (unknownLocales.length > 0) {
|
|
89
|
+
errors.push({
|
|
90
|
+
key: "locales",
|
|
91
|
+
message: `not found in the resource translations: ${unknownLocales.join(", ")}`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (!workflow.type) {
|
|
95
|
+
errors.push({ key: "workflow", message: "can't be blank" });
|
|
96
|
+
}
|
|
97
|
+
if (unknownWidgetIds.length > 0) {
|
|
98
|
+
errors.push({
|
|
99
|
+
key: "widgetIds",
|
|
100
|
+
message: `not found in the source resource: ${unknownWidgetIds.join(", ")}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (!hasTranslations(resource)) {
|
|
104
|
+
errors.push({
|
|
105
|
+
key: "generalError",
|
|
106
|
+
message:
|
|
107
|
+
"cannot start a partial translation without existing translations",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (hasActiveContentTask(resource)) {
|
|
111
|
+
errors.push({
|
|
112
|
+
key: "generalError",
|
|
113
|
+
message:
|
|
114
|
+
"cannot start a partial translation while a translation task is in progress",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return errors;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const create_form = async (ctx) => {
|
|
122
|
+
const { parent_id } = ctx.params;
|
|
123
|
+
const { resourceType } = ctx.resourceConfig;
|
|
124
|
+
const resource = await ctx.client.resources.find(
|
|
125
|
+
parent_id,
|
|
126
|
+
ctx.locale,
|
|
127
|
+
resourceType,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (!resource) {
|
|
131
|
+
throw new Error(`Cannot find ${resourceType} with id ${parent_id}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const widgetIds = selectedWidgetIds(resource, ctx.query);
|
|
135
|
+
const { validWidgetIds, unknownWidgetIds } = partitionWidgetIds(
|
|
136
|
+
resource,
|
|
137
|
+
widgetIds,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const previewUrl = ctx.client.resources.previewUrl(
|
|
141
|
+
{ ...resource, resourceType },
|
|
142
|
+
ctx.locale,
|
|
143
|
+
);
|
|
144
|
+
const validationErrors = [];
|
|
145
|
+
|
|
146
|
+
if (widgetIds.length === 0) {
|
|
147
|
+
validationErrors.push({ key: "widgetIds", message: "can't be blank" });
|
|
148
|
+
}
|
|
149
|
+
if (unknownWidgetIds.length > 0) {
|
|
150
|
+
validationErrors.push({
|
|
151
|
+
key: "widgetIds",
|
|
152
|
+
message: `not found in the source resource: ${unknownWidgetIds.join(", ")}`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (!hasTranslations(resource)) {
|
|
156
|
+
validationErrors.push({
|
|
157
|
+
key: "generalError",
|
|
158
|
+
message:
|
|
159
|
+
"cannot start a partial translation without existing translations",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (hasActiveContentTask(resource)) {
|
|
163
|
+
validationErrors.push({
|
|
164
|
+
key: "generalError",
|
|
165
|
+
message:
|
|
166
|
+
"cannot start a partial translation while a translation task is in progress",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (validationErrors.length > 0) {
|
|
171
|
+
return {
|
|
172
|
+
assigns: formAssigns(
|
|
173
|
+
resource,
|
|
174
|
+
ctx.query,
|
|
175
|
+
previewUrl,
|
|
176
|
+
{ widgetIds: validWidgetIds },
|
|
177
|
+
{
|
|
178
|
+
partialTranslation:
|
|
179
|
+
ctx.client.resources.formatErrors(validationErrors),
|
|
180
|
+
},
|
|
181
|
+
),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return { assigns: formAssigns(resource, ctx.query, previewUrl) };
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const create = async (ctx) => {
|
|
189
|
+
const { parent_id } = ctx.params;
|
|
190
|
+
const { resourceType, resourceTypePlural } = ctx.resourceConfig;
|
|
191
|
+
const resource = await ctx.client.resources.find(
|
|
192
|
+
parent_id,
|
|
193
|
+
ctx.locale,
|
|
194
|
+
resourceType,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (!resource) {
|
|
198
|
+
throw new Error(`Cannot find ${resourceType} with id ${parent_id}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const previewUrl = ctx.client.resources.previewUrl(
|
|
202
|
+
{ ...resource, resourceType },
|
|
203
|
+
ctx.locale,
|
|
204
|
+
);
|
|
205
|
+
const values = ctx.resourceParams?.partial_translation || {};
|
|
206
|
+
const locales = toArray(values.locales);
|
|
207
|
+
const widgetIds =
|
|
208
|
+
values.widget_ids === undefined
|
|
209
|
+
? selectedWidgetIds({ widgets: [] }, ctx.query)
|
|
210
|
+
: toArray(values.widget_ids);
|
|
211
|
+
const workflow = values.workflow || {};
|
|
212
|
+
const { validWidgetIds, unknownWidgetIds } = partitionWidgetIds(
|
|
213
|
+
resource,
|
|
214
|
+
widgetIds,
|
|
215
|
+
);
|
|
216
|
+
const validLocales = targetTranslationLocales(resource, ctx.locale);
|
|
217
|
+
const unknownLocales = locales.filter(
|
|
218
|
+
(locale) => !validLocales.includes(locale),
|
|
219
|
+
);
|
|
220
|
+
const selectedValidLocales = locales.filter((locale) =>
|
|
221
|
+
validLocales.includes(locale),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const validationErrors = validatePartialTranslation({
|
|
225
|
+
resource,
|
|
226
|
+
locales,
|
|
227
|
+
widgetIds,
|
|
228
|
+
workflow,
|
|
229
|
+
unknownWidgetIds,
|
|
230
|
+
unknownLocales,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (validationErrors.length > 0) {
|
|
234
|
+
return {
|
|
235
|
+
assigns: formAssigns(
|
|
236
|
+
resource,
|
|
237
|
+
ctx.query,
|
|
238
|
+
previewUrl,
|
|
239
|
+
{
|
|
240
|
+
...values,
|
|
241
|
+
locales: selectedValidLocales,
|
|
242
|
+
widgetIds: validWidgetIds,
|
|
243
|
+
workflow,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
partialTranslation:
|
|
247
|
+
ctx.client.resources.formatErrors(validationErrors),
|
|
248
|
+
},
|
|
249
|
+
),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const widgets = (resource.widgets || []).filter((widget) =>
|
|
254
|
+
validWidgetIds.includes(widget.widgetId),
|
|
255
|
+
);
|
|
256
|
+
const { errors } = await ctx.client.resources.multiPartialTranslate(
|
|
257
|
+
parent_id,
|
|
258
|
+
locales,
|
|
259
|
+
widgets,
|
|
260
|
+
workflow,
|
|
261
|
+
{ ...resource, locale: ctx.locale, resourceType },
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
if (errors) {
|
|
265
|
+
return {
|
|
266
|
+
assigns: formAssigns(
|
|
267
|
+
resource,
|
|
268
|
+
ctx.query,
|
|
269
|
+
previewUrl,
|
|
270
|
+
{ ...values, locales, widgetIds: validWidgetIds, workflow },
|
|
271
|
+
{ partialTranslation: ctx.client.resources.formatErrors(errors) },
|
|
272
|
+
),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return { redirect: `/${ctx.locale}/${resourceTypePlural}/${parent_id}` };
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export default { create_form, create };
|