@raketa-cloud/admin 1.10.6 → 1.10.8

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.
@@ -1160,92 +1160,78 @@
1160
1160
  padding-right: 0px;
1161
1161
  }
1162
1162
  }
1163
- @media (prefers-color-scheme: dark) {
1164
- :where(.dark\:divide-neutral-700 > :not(:last-child)) {
1165
- border-color: var(--color-neutral-700);
1166
- }
1167
- .dark\:border-neutral-500 {
1168
- border-color: var(--color-neutral-500);
1169
- }
1170
- .dark\:border-neutral-700 {
1171
- border-color: var(--color-neutral-700);
1172
- }
1173
- .dark\:bg-blue-900\/50 {
1174
- background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 50%, transparent);
1175
- }
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);
1176
1174
  @supports (color: color-mix(in lab, red, red)) {
1177
- .dark\:bg-blue-900\/50 {
1178
- background-color: color-mix(in oklab, var(--color-blue-900) 50%, transparent);
1179
- }
1180
- }
1181
- .dark\:bg-emerald-900\/50 {
1182
- 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);
1183
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);
1184
1180
  @supports (color: color-mix(in lab, red, red)) {
1185
- .dark\:bg-emerald-900\/50 {
1186
- background-color: color-mix(in oklab, var(--color-emerald-900) 50%, transparent);
1187
- }
1188
- }
1189
- .dark\:bg-neutral-800 {
1190
- background-color: var(--color-neutral-800);
1191
- }
1192
- .dark\:bg-orange-900\/50 {
1193
- 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);
1194
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);
1195
1189
  @supports (color: color-mix(in lab, red, red)) {
1196
- .dark\:bg-orange-900\/50 {
1197
- background-color: color-mix(in oklab, var(--color-orange-900) 50%, transparent);
1198
- }
1199
- }
1200
- .dark\:bg-orange-950\/40 {
1201
- 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);
1202
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);
1203
1195
  @supports (color: color-mix(in lab, red, red)) {
1204
- .dark\:bg-orange-950\/40 {
1205
- background-color: color-mix(in oklab, var(--color-orange-950) 40%, transparent);
1206
- }
1207
- }
1208
- .dark\:bg-raketa-primary\/15 {
1209
- background-color: color-mix(in srgb, rgb(var(--primary)) 15%, transparent);
1196
+ background-color: color-mix(in oklab, var(--color-orange-950) 40%, transparent);
1210
1197
  }
1198
+ }
1199
+ .dark\:bg-raketa-primary\/15:where(.dark, .dark *) {
1200
+ background-color: color-mix(in srgb, rgb(var(--primary)) 15%, transparent);
1211
1201
  @supports (color: color-mix(in lab, red, red)) {
1212
- .dark\:bg-raketa-primary\/15 {
1213
- background-color: color-mix(in oklab, var(--color-raketa-primary) 15%, transparent);
1214
- }
1215
- }
1216
- .dark\:bg-red-900\/50 {
1217
- 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);
1218
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);
1219
1207
  @supports (color: color-mix(in lab, red, red)) {
1220
- .dark\:bg-red-900\/50 {
1221
- background-color: color-mix(in oklab, var(--color-red-900) 50%, transparent);
1222
- }
1223
- }
1224
- .dark\:text-blue-300 {
1225
- color: var(--color-blue-300);
1226
- }
1227
- .dark\:text-emerald-300 {
1228
- color: var(--color-emerald-300);
1229
- }
1230
- .dark\:text-orange-300 {
1231
- color: var(--color-orange-300);
1208
+ background-color: color-mix(in oklab, var(--color-red-900) 50%, transparent);
1232
1209
  }
1233
- .dark\:text-red-300 {
1234
- color: var(--color-red-300);
1235
- }
1236
- .dark\:invert {
1237
- --tw-invert: invert(100%);
1238
- 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,);
1239
- }
1240
- @media (hover: hover) {
1241
- .dark\:hover\:bg-neutral-700:hover {
1242
- background-color: var(--color-neutral-700);
1243
- }
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);
1244
1230
  }
1245
- @media (width >= 64rem) {
1246
- :where(.dark\:lg\:divide-neutral-700 > :not(:last-child)) {
1247
- border-color: var(--color-neutral-700);
1248
- }
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);
1249
1235
  }
1250
1236
  }
1251
1237
  }
@@ -1706,7 +1692,7 @@ body {
1706
1692
  line-height: var(--tw-leading, var(--text-sm--line-height));
1707
1693
  --tw-leading: 1.5;
1708
1694
  line-height: 1.5;
1709
- @media (prefers-color-scheme: dark) {
1695
+ &:where(.dark, .dark *) {
1710
1696
  background-color: color-mix(in srgb, rgb(var(--primary)) 20%, transparent);
1711
1697
  @supports (color: color-mix(in lab, red, red)) {
1712
1698
  background-color: color-mix(in oklab, var(--color-raketa-primary) 20%, transparent);
@@ -1839,7 +1825,7 @@ body {
1839
1825
  color: var(--color-raketa-foreground);
1840
1826
  }
1841
1827
  }
1842
- @media (prefers-color-scheme: dark) {
1828
+ &:where(.dark, .dark *) {
1843
1829
  color: var(--color-red-700);
1844
1830
  }
1845
1831
  }
@@ -1917,18 +1903,18 @@ body {
1917
1903
  padding-block: calc(var(--spacing) * 2);
1918
1904
  --tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
1919
1905
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1920
- @media (prefers-color-scheme: dark) {
1906
+ &:where(.dark, .dark *) {
1921
1907
  border-color: var(--color-neutral-700);
1922
1908
  }
1923
- @media (prefers-color-scheme: dark) {
1909
+ &:where(.dark, .dark *) {
1924
1910
  background-color: var(--color-neutral-800);
1925
1911
  }
1926
- @media (prefers-color-scheme: dark) {
1912
+ &:where(.dark, .dark *) {
1927
1913
  &:focus {
1928
1914
  border-color: var(--color-raketa-primary);
1929
1915
  }
1930
1916
  }
1931
- @media (prefers-color-scheme: dark) {
1917
+ &:where(.dark, .dark *) {
1932
1918
  &:focus {
1933
1919
  --tw-ring-color: var(--color-raketa-primary);
1934
1920
  }
@@ -1958,7 +1944,7 @@ body {
1958
1944
  padding-block: var(--spacing);
1959
1945
  --tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
1960
1946
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1961
- @media (prefers-color-scheme: dark) {
1947
+ &:where(.dark, .dark *) {
1962
1948
  border-color: var(--color-neutral-700);
1963
1949
  }
1964
1950
  }
@@ -2071,7 +2057,7 @@ body {
2071
2057
  transition-duration: 100ms;
2072
2058
  --tw-ease: var(--ease-in-out);
2073
2059
  transition-timing-function: var(--ease-in-out);
2074
- @media (prefers-color-scheme: dark) {
2060
+ &:where(.dark, .dark *) {
2075
2061
  border-color: var(--color-neutral-700);
2076
2062
  }
2077
2063
  }
@@ -2083,7 +2069,7 @@ body {
2083
2069
  border-bottom-color: color-mix(in oklab, var(--color-black) 25%, transparent);
2084
2070
  }
2085
2071
  color: var(--color-raketa-foreground);
2086
- @media (prefers-color-scheme: dark) {
2072
+ &:where(.dark, .dark *) {
2087
2073
  border-bottom-color: color-mix(in srgb, #fff 40%, transparent);
2088
2074
  @supports (color: color-mix(in lab, red, red)) {
2089
2075
  border-bottom-color: color-mix(in oklab, var(--color-white) 40%, transparent);
@@ -2096,10 +2082,10 @@ body {
2096
2082
  border-color: var(--color-neutral-300);
2097
2083
  background-color: var(--color-neutral-50);
2098
2084
  color: var(--color-raketa-foreground);
2099
- @media (prefers-color-scheme: dark) {
2085
+ &:where(.dark, .dark *) {
2100
2086
  border-color: var(--color-neutral-600);
2101
2087
  }
2102
- @media (prefers-color-scheme: dark) {
2088
+ &:where(.dark, .dark *) {
2103
2089
  background-color: var(--color-neutral-800);
2104
2090
  }
2105
2091
  }
@@ -2203,10 +2189,10 @@ body {
2203
2189
  border-left-width: 1px;
2204
2190
  background-color: var(--color-slate-50);
2205
2191
  padding: calc(var(--spacing) * 4);
2206
- @media (prefers-color-scheme: dark) {
2192
+ &:where(.dark, .dark *) {
2207
2193
  border-color: var(--color-neutral-700);
2208
2194
  }
2209
- @media (prefers-color-scheme: dark) {
2195
+ &:where(.dark, .dark *) {
2210
2196
  background-color: color-mix(in srgb, oklch(26.9% 0 none) 70%, transparent);
2211
2197
  @supports (color: color-mix(in lab, red, red)) {
2212
2198
  background-color: color-mix(in oklab, var(--color-neutral-800) 70%, transparent);
@@ -2229,7 +2215,7 @@ body {
2229
2215
  height: calc(var(--spacing) * 6);
2230
2216
  width: 1px;
2231
2217
  background-color: var(--color-mono-50);
2232
- @media (prefers-color-scheme: dark) {
2218
+ &:where(.dark, .dark *) {
2233
2219
  background-color: var(--color-neutral-700);
2234
2220
  }
2235
2221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raketa-cloud/admin",
3
- "version": "1.10.6",
3
+ "version": "1.10.8",
4
4
  "main": "src/index.js",
5
5
  "files": [
6
6
  "src/",
@@ -22,6 +22,39 @@ const targetTranslationLocales = (resource, sourceLocale) =>
22
22
  )
23
23
  .map(([locale]) => locale);
24
24
 
25
+ const localesAtOpenRevisionLimit = (resource, sourceLocale, maxOpenRevisions) =>
26
+ Object.entries(resource.translations || {})
27
+ .filter(
28
+ ([locale, translation]) =>
29
+ locale !== sourceLocale &&
30
+ translation?.id &&
31
+ translation.revisionsCount >= maxOpenRevisions,
32
+ )
33
+ .map(([locale]) => locale);
34
+
35
+ const withTranslationRevisionCounts = async (ctx, resource, resourceType) => {
36
+ const revisionCounts = await Promise.all(
37
+ Object.entries(resource.translations || {}).map(
38
+ async ([locale, translation]) => {
39
+ if (locale === ctx.locale || !translation?.id) {
40
+ return [locale, translation];
41
+ }
42
+
43
+ const target = await ctx.client.resources.find(
44
+ translation.id,
45
+ locale,
46
+ resourceType,
47
+ );
48
+ return [
49
+ locale,
50
+ { ...translation, revisionsCount: target.revisionsCount },
51
+ ];
52
+ },
53
+ ),
54
+ );
55
+
56
+ return { ...resource, translations: Object.fromEntries(revisionCounts) };
57
+ };
25
58
  const selectedWidgetIds = (resource, query = {}) => {
26
59
  const requested = Array.isArray(query.widget_ids)
27
60
  ? query.widget_ids
@@ -73,6 +106,7 @@ const validatePartialTranslation = ({
73
106
  workflow,
74
107
  unknownWidgetIds,
75
108
  unknownLocales,
109
+ localesAtRevisionLimit,
76
110
  }) => {
77
111
  const errors = [];
78
112
 
@@ -91,6 +125,12 @@ const validatePartialTranslation = ({
91
125
  message: `not found in the resource translations: ${unknownLocales.join(", ")}`,
92
126
  });
93
127
  }
128
+ if (localesAtRevisionLimit.length > 0) {
129
+ errors.push({
130
+ key: "locales",
131
+ message: `open revision limit reached for: ${localesAtRevisionLimit.join(", ")}`,
132
+ });
133
+ }
94
134
  if (!workflow.type) {
95
135
  errors.push({ key: "workflow", message: "can't be blank" });
96
136
  }
@@ -131,14 +171,19 @@ const create_form = async (ctx) => {
131
171
  throw new Error(`Cannot find ${resourceType} with id ${parent_id}`);
132
172
  }
133
173
 
134
- const widgetIds = selectedWidgetIds(resource, ctx.query);
174
+ const resourceWithTranslationRevisionCounts =
175
+ await withTranslationRevisionCounts(ctx, resource, resourceType);
176
+ const widgetIds = selectedWidgetIds(
177
+ resourceWithTranslationRevisionCounts,
178
+ ctx.query,
179
+ );
135
180
  const { validWidgetIds, unknownWidgetIds } = partitionWidgetIds(
136
- resource,
181
+ resourceWithTranslationRevisionCounts,
137
182
  widgetIds,
138
183
  );
139
184
 
140
185
  const previewUrl = ctx.client.resources.previewUrl(
141
- { ...resource, resourceType },
186
+ { ...resourceWithTranslationRevisionCounts, resourceType },
142
187
  ctx.locale,
143
188
  );
144
189
  const validationErrors = [];
@@ -170,7 +215,7 @@ const create_form = async (ctx) => {
170
215
  if (validationErrors.length > 0) {
171
216
  return {
172
217
  assigns: formAssigns(
173
- resource,
218
+ resourceWithTranslationRevisionCounts,
174
219
  ctx.query,
175
220
  previewUrl,
176
221
  { widgetIds: validWidgetIds },
@@ -182,7 +227,13 @@ const create_form = async (ctx) => {
182
227
  };
183
228
  }
184
229
 
185
- return { assigns: formAssigns(resource, ctx.query, previewUrl) };
230
+ return {
231
+ assigns: formAssigns(
232
+ resourceWithTranslationRevisionCounts,
233
+ ctx.query,
234
+ previewUrl,
235
+ ),
236
+ };
186
237
  };
187
238
 
188
239
  const create = async (ctx) => {
@@ -198,8 +249,10 @@ const create = async (ctx) => {
198
249
  throw new Error(`Cannot find ${resourceType} with id ${parent_id}`);
199
250
  }
200
251
 
252
+ const resourceWithTranslationRevisionCounts =
253
+ await withTranslationRevisionCounts(ctx, resource, resourceType);
201
254
  const previewUrl = ctx.client.resources.previewUrl(
202
- { ...resource, resourceType },
255
+ { ...resourceWithTranslationRevisionCounts, resourceType },
203
256
  ctx.locale,
204
257
  );
205
258
  const values = ctx.resourceParams?.partial_translation || {};
@@ -210,30 +263,42 @@ const create = async (ctx) => {
210
263
  : toArray(values.widget_ids);
211
264
  const workflow = values.workflow || {};
212
265
  const { validWidgetIds, unknownWidgetIds } = partitionWidgetIds(
213
- resource,
266
+ resourceWithTranslationRevisionCounts,
214
267
  widgetIds,
215
268
  );
216
- const validLocales = targetTranslationLocales(resource, ctx.locale);
269
+ const validLocales = targetTranslationLocales(
270
+ resourceWithTranslationRevisionCounts,
271
+ ctx.locale,
272
+ );
217
273
  const unknownLocales = locales.filter(
218
274
  (locale) => !validLocales.includes(locale),
219
275
  );
220
- const selectedValidLocales = locales.filter((locale) =>
221
- validLocales.includes(locale),
276
+ const localesAtRevisionLimit = localesAtOpenRevisionLimit(
277
+ resourceWithTranslationRevisionCounts,
278
+ ctx.locale,
279
+ ctx.maxOpenRevisions,
280
+ );
281
+ const selectedValidLocales = locales.filter(
282
+ (locale) =>
283
+ validLocales.includes(locale) && !localesAtRevisionLimit.includes(locale),
222
284
  );
223
285
 
224
286
  const validationErrors = validatePartialTranslation({
225
- resource,
287
+ resource: resourceWithTranslationRevisionCounts,
226
288
  locales,
227
289
  widgetIds,
228
290
  workflow,
229
291
  unknownWidgetIds,
230
292
  unknownLocales,
293
+ localesAtRevisionLimit: locales.filter((locale) =>
294
+ localesAtRevisionLimit.includes(locale),
295
+ ),
231
296
  });
232
297
 
233
298
  if (validationErrors.length > 0) {
234
299
  return {
235
300
  assigns: formAssigns(
236
- resource,
301
+ resourceWithTranslationRevisionCounts,
237
302
  ctx.query,
238
303
  previewUrl,
239
304
  {
@@ -250,21 +315,25 @@ const create = async (ctx) => {
250
315
  };
251
316
  }
252
317
 
253
- const widgets = (resource.widgets || []).filter((widget) =>
254
- validWidgetIds.includes(widget.widgetId),
318
+ const widgets = (resourceWithTranslationRevisionCounts.widgets || []).filter(
319
+ (widget) => validWidgetIds.includes(widget.widgetId),
255
320
  );
256
321
  const { errors } = await ctx.client.resources.multiPartialTranslate(
257
322
  parent_id,
258
323
  locales,
259
324
  widgets,
260
325
  workflow,
261
- { ...resource, locale: ctx.locale, resourceType },
326
+ {
327
+ ...resourceWithTranslationRevisionCounts,
328
+ locale: ctx.locale,
329
+ resourceType,
330
+ },
262
331
  );
263
332
 
264
333
  if (errors) {
265
334
  return {
266
335
  assigns: formAssigns(
267
- resource,
336
+ resourceWithTranslationRevisionCounts,
268
337
  ctx.query,
269
338
  previewUrl,
270
339
  { ...values, locales, widgetIds: validWidgetIds, workflow },
@@ -1,4 +1,8 @@
1
1
  @import "tailwindcss";
2
+
3
+ /* Keep dark utilities opt-in through a `.dark` ancestor, rather than the OS theme. */
4
+ @custom-variant dark (&:where(.dark, .dark *));
5
+
2
6
  @plugin "@tailwindcss/forms";
3
7
  @plugin "@tailwindcss/typography";
4
8
  @source "../**/*.{html,js,ejs}";
@@ -24,6 +24,7 @@ const buildCtx = (params = {}) => ({
24
24
  params: { parent_id: "42" },
25
25
  query: {},
26
26
  locale: "en",
27
+ maxOpenRevisions: 1,
27
28
  resourceConfig: { resourceType: "page", resourceTypePlural: "pages" },
28
29
  resourceParams: params,
29
30
  });
@@ -194,6 +195,30 @@ describe("PartialTranslationsHandler", () => {
194
195
  });
195
196
  });
196
197
 
198
+ test("does not submit to locales at the open revision limit", async () => {
199
+ const ctx = buildCtx({
200
+ partial_translation: {
201
+ locales: ["fr"],
202
+ widget_ids: ["hero-1"],
203
+ workflow: { type: "LOKALISE_TT_AI" },
204
+ },
205
+ });
206
+ ctx.client.resources.find
207
+ .mockResolvedValueOnce({
208
+ ...resource,
209
+ translations: { fr: { id: "43" } },
210
+ })
211
+ .mockResolvedValueOnce({ id: "43", revisionsCount: 1 });
212
+
213
+ const result = await PartialTranslationsHandler.create(ctx);
214
+
215
+ expect(ctx.client.resources.multiPartialTranslate).not.toHaveBeenCalled();
216
+ expect(result.assigns.partialTranslation.locales).toEqual([]);
217
+ expect(result.assigns.errors.partialTranslation).toEqual({
218
+ locales: "open revision limit reached for: fr",
219
+ });
220
+ });
221
+
197
222
  test("does not submit when a translation has an active content task", async () => {
198
223
  const ctx = buildCtx({
199
224
  partial_translation: {
@@ -10,6 +10,7 @@ function renderPartialTranslations(translations) {
10
10
  return ejs.render(template, {
11
11
  _context: {
12
12
  locale: "en",
13
+ maxOpenRevisions: 1,
13
14
  locales: { en: { name: "English" }, fr: { name: "French" } },
14
15
  resourceConfig: { resourceTypePlural: "pages" },
15
16
  },
@@ -47,4 +48,14 @@ describe("partial translations form", () => {
47
48
  expect(rendered).not.toContain('value="de"');
48
49
  expect(rendered).not.toContain("This resource has no translations.");
49
50
  });
51
+
52
+ test("disables translations at the open revision limit", () => {
53
+ const rendered = renderPartialTranslations({
54
+ fr: { id: "43", revisionsCount: 1 },
55
+ });
56
+
57
+ expect(rendered).toContain('value="fr"');
58
+ expect(rendered).toContain('value="fr" disabled');
59
+ expect(rendered).toContain("open revision limit reached");
60
+ });
50
61
  });
@@ -9,7 +9,7 @@
9
9
 
10
10
  <div class="flex gap-4 w-full pb-4">
11
11
  <div class="flex items-center justify-between mb-4">
12
- <div class="flex items-center border-b-2">
12
+ <div class="flex items-center border-b-2 border-neutral-200 dark:border-neutral-700">
13
13
  <div class=" <%= active === 'general' ? activeClass : inactiveClass %>">
14
14
  <a href="<%= `/${_context.locale}/${_context.resourceConfig.resourceType}_revisions/${resource.id}/general/edit` %>" class="text-raketa-primary font-semibold">
15
15
  General
@@ -11,10 +11,11 @@
11
11
  <div class="mb-4">
12
12
  <h6>Current translations</h6>
13
13
  <div class="checkbox-group !max-h-fit">
14
- <% targetTranslations.forEach(([locale]) => { %>
15
- <label>
16
- <input type="checkbox" name="partial_translation[locales][]" value="<%= locale %>" <%= partialTranslation.locales.includes(locale) ? 'checked' : '' %>>
14
+ <% targetTranslations.forEach(([locale, translation]) => { const atRevisionLimit = translation.revisionsCount >= _context.maxOpenRevisions; %>
15
+ <label class="<%= atRevisionLimit ? 'opacity-50' : '' %>">
16
+ <input type="checkbox" name="partial_translation[locales][]" value="<%= locale %>" <%= partialTranslation.locales.includes(locale) ? 'checked' : '' %> <%= atRevisionLimit ? 'disabled' : '' %>>
17
17
  <%= _context.locales[locale]?.name || locale %>
18
+ <% if (atRevisionLimit) { %><span class="text-sm text-raketa-foreground/50">(open revision limit reached)</span><% } %>
18
19
  </label>
19
20
  <% }) %>
20
21
  </div>
@@ -5,7 +5,7 @@
5
5
 
6
6
  <div class="flex w-full pb-4">
7
7
  <div class="flex items-center justify-between mb-4">
8
- <div class="flex items-center border-b-2">
8
+ <div class="flex items-center border-b-2 border-neutral-200 dark:border-neutral-700">
9
9
  <div class="<%= active === 'meta' ? activeClass : inactiveClass %>">
10
10
  <a href="<%= `/${_context.locale}/settings/meta/edit` %>" class="text-raketa-primary font-semibold">
11
11
  Meta and OpenGraph