@justanarthur/payload-plugin-translator 3.0.3 → 3.1.1

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/rsc.js ADDED
@@ -0,0 +1,703 @@
1
+ import"./shared/chunk-9d433kmv.js";
2
+ import {
3
+ readLocales,
4
+ loadCollectionReview,
5
+ loadGlobalsReview,
6
+ loadEntityLocaleReview
7
+ } from "./shared/chunk-15n5q7wd.js";
8
+ import {
9
+ REVIEW_VIEW_PATH
10
+ } from "./shared/chunk-31qgv1zk.js";
11
+
12
+ // src/review/view/TranslationsView.tsx
13
+ import { DefaultTemplate } from "@payloadcms/next/templates";
14
+ import { Gutter } from "@payloadcms/ui";
15
+
16
+ // src/review/view/Detail.tsx
17
+ import { ReviewActions } from "@justanarthur/payload-plugin-translator/client";
18
+
19
+ // src/review/view/href.ts
20
+ import { formatAdminURL } from "payload/shared";
21
+ var reviewHref = (adminRoute, params) => {
22
+ const query = new URLSearchParams(Object.entries(params).filter((entry) => entry[1] !== undefined && entry[1] !== "").map(([key, value]) => [key, String(value)])).toString();
23
+ return `${formatAdminURL({ adminRoute, path: REVIEW_VIEW_PATH })}${query ? `?${query}` : ""}`;
24
+ };
25
+ var editHref = (adminRoute, { collectionSlug, globalSlug, id }, locale) => `${formatAdminURL({
26
+ adminRoute,
27
+ path: globalSlug ? `/globals/${globalSlug}` : `/collections/${collectionSlug}/${id}`
28
+ })}?locale=${locale}`;
29
+
30
+ // src/review/view/Detail.tsx
31
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
32
+ var STATE = {
33
+ identical: { label: "same as source", tone: "warn" },
34
+ missing: { label: "missing", tone: "bad" },
35
+ ok: { label: "translated", tone: "ok" },
36
+ placeholders: { label: "placeholders differ", tone: "warn" }
37
+ };
38
+ var preview = (text) => text.length > 600 ? `${text.slice(0, 597)}…` : text;
39
+ var readablePath = (path) => path.replace(/\[(\d+)\]/g, (_, index) => ` ${Number(index) + 1}`).replace(/\(([^)]+)\)/g, " · $1").replace(/#(\d+)$/, (_, index) => ` (part ${Number(index) + 1})`).split(".").join(" › ");
40
+ var Detail = ({ adminRoute, review, onlyIssues, tab, page }) => {
41
+ const rows = onlyIssues ? review.fields.filter((field) => field.state !== "ok") : review.fields;
42
+ const { summary } = review;
43
+ const tone = summary.slugMissing || summary.coverage < 50 ? "bad" : summary.coverage < 100 ? "warn" : "ok";
44
+ const counters = [
45
+ { label: "translated", value: `${summary.coverage}%`, note: `${summary.ok} of ${summary.total} fields`, bar: true },
46
+ { label: "missing", value: summary.missing, note: "empty in this locale" },
47
+ { label: "same as source", value: summary.identical, note: `identical to ${review.defaultLocale}` },
48
+ { label: "placeholders", value: summary.placeholders, note: "differ from source" }
49
+ ];
50
+ return /* @__PURE__ */ jsxs(Fragment, {
51
+ children: [
52
+ /* @__PURE__ */ jsxs("div", {
53
+ className: "tr__head",
54
+ children: [
55
+ /* @__PURE__ */ jsxs("div", {
56
+ children: [
57
+ /* @__PURE__ */ jsxs("a", {
58
+ className: "tr__back",
59
+ href: reviewHref(adminRoute, { tab, page }),
60
+ children: [
61
+ "← All ",
62
+ tab
63
+ ]
64
+ }),
65
+ /* @__PURE__ */ jsx("h1", {
66
+ children: review.label
67
+ }),
68
+ /* @__PURE__ */ jsxs("p", {
69
+ className: "tr__lead",
70
+ children: [
71
+ review.defaultLocale.toUpperCase(),
72
+ " → ",
73
+ review.locale.toUpperCase(),
74
+ " · ",
75
+ review.entity
76
+ ]
77
+ })
78
+ ]
79
+ }),
80
+ /* @__PURE__ */ jsx(ReviewActions, {
81
+ entity: review.entity,
82
+ locale: review.locale,
83
+ reviewed: review.reviewed
84
+ })
85
+ ]
86
+ }),
87
+ /* @__PURE__ */ jsx("div", {
88
+ className: "tr__stats",
89
+ children: counters.map((counter) => /* @__PURE__ */ jsxs("div", {
90
+ className: "tr__stat",
91
+ children: [
92
+ /* @__PURE__ */ jsx("span", {
93
+ className: "tr__stat-label",
94
+ children: counter.label
95
+ }),
96
+ /* @__PURE__ */ jsx("span", {
97
+ className: "tr__stat-value",
98
+ children: counter.value
99
+ }),
100
+ counter.bar && /* @__PURE__ */ jsx("div", {
101
+ className: "tr__bar",
102
+ style: { ["--tr-tone"]: `var(--tr-${tone})` },
103
+ children: /* @__PURE__ */ jsx("span", {
104
+ style: { width: `${summary.coverage}%` }
105
+ })
106
+ }),
107
+ /* @__PURE__ */ jsx("span", {
108
+ className: "tr__stat-note",
109
+ children: counter.note
110
+ })
111
+ ]
112
+ }, counter.label))
113
+ }),
114
+ /* @__PURE__ */ jsxs("div", {
115
+ className: "tr__meta",
116
+ children: [
117
+ summary.slugMissing && /* @__PURE__ */ jsxs("span", {
118
+ className: "tr__chip tr__chip--bad",
119
+ children: [
120
+ "no slug: the page 404s in ",
121
+ review.locale
122
+ ]
123
+ }),
124
+ review.stale && /* @__PURE__ */ jsx("span", {
125
+ className: "tr__chip",
126
+ children: "↻ source changed since the last translation"
127
+ }),
128
+ review.translatedAt && /* @__PURE__ */ jsxs("span", {
129
+ className: "tr__chip",
130
+ children: [
131
+ "translated ",
132
+ new Date(review.translatedAt).toLocaleString()
133
+ ]
134
+ }),
135
+ review.reviewedAt && /* @__PURE__ */ jsxs("span", {
136
+ className: "tr__chip",
137
+ children: [
138
+ "✓ reviewed ",
139
+ new Date(review.reviewedAt).toLocaleString(),
140
+ " by ",
141
+ review.reviewedBy
142
+ ]
143
+ }),
144
+ review.lastJob && /* @__PURE__ */ jsxs("span", {
145
+ className: `tr__chip${review.lastJob.error ? " tr__chip--bad" : ""}`,
146
+ title: review.lastJob.error,
147
+ children: [
148
+ "job #",
149
+ review.lastJob.id,
150
+ ": ",
151
+ review.lastJob.error ? `failed — ${preview(review.lastJob.error).slice(0, 80)}` : review.lastJob.completedAt ? "completed" : review.lastJob.processing ? "running" : "queued"
152
+ ]
153
+ }),
154
+ /* @__PURE__ */ jsxs("a", {
155
+ className: "tr__chip",
156
+ href: editHref(adminRoute, review, review.locale),
157
+ children: [
158
+ "Edit in ",
159
+ review.locale.toUpperCase(),
160
+ " ↗"
161
+ ]
162
+ })
163
+ ]
164
+ }),
165
+ /* @__PURE__ */ jsxs("div", {
166
+ className: "tr__toolbar",
167
+ children: [
168
+ /* @__PURE__ */ jsxs("span", {
169
+ className: "tr__filters",
170
+ children: [
171
+ rows.length,
172
+ " of ",
173
+ review.fields.length,
174
+ " fields"
175
+ ]
176
+ }),
177
+ /* @__PURE__ */ jsx("a", {
178
+ "aria-pressed": onlyIssues,
179
+ className: "tr__toggle",
180
+ href: reviewHref(adminRoute, { tab, page, entity: review.entity, locale: review.locale, issues: onlyIssues ? undefined : 1 }),
181
+ children: "Only problems"
182
+ })
183
+ ]
184
+ }),
185
+ /* @__PURE__ */ jsx("div", {
186
+ className: "tr__card",
187
+ children: /* @__PURE__ */ jsxs("table", {
188
+ children: [
189
+ /* @__PURE__ */ jsx("thead", {
190
+ children: /* @__PURE__ */ jsxs("tr", {
191
+ children: [
192
+ /* @__PURE__ */ jsx("th", {
193
+ children: "Field"
194
+ }),
195
+ /* @__PURE__ */ jsx("th", {
196
+ children: "State"
197
+ }),
198
+ /* @__PURE__ */ jsx("th", {
199
+ children: review.defaultLocale.toUpperCase()
200
+ }),
201
+ /* @__PURE__ */ jsx("th", {
202
+ children: review.locale.toUpperCase()
203
+ })
204
+ ]
205
+ })
206
+ }),
207
+ /* @__PURE__ */ jsxs("tbody", {
208
+ children: [
209
+ rows.map((field) => /* @__PURE__ */ jsxs("tr", {
210
+ children: [
211
+ /* @__PURE__ */ jsx("td", {
212
+ className: "tr__path",
213
+ title: field.path,
214
+ children: readablePath(field.path)
215
+ }),
216
+ /* @__PURE__ */ jsx("td", {
217
+ children: /* @__PURE__ */ jsx("span", {
218
+ className: "tr__pill",
219
+ "data-tone": STATE[field.state].tone,
220
+ children: STATE[field.state].label
221
+ })
222
+ }),
223
+ /* @__PURE__ */ jsx("td", {
224
+ className: "tr__text",
225
+ children: preview(field.sourceText)
226
+ }),
227
+ /* @__PURE__ */ jsx("td", {
228
+ className: `tr__text${field.targetText.trim() ? "" : " tr__text--empty"}`,
229
+ children: field.targetText.trim() ? preview(field.targetText) : "empty"
230
+ })
231
+ ]
232
+ }, field.path)),
233
+ rows.length === 0 && /* @__PURE__ */ jsx("tr", {
234
+ children: /* @__PURE__ */ jsx("td", {
235
+ className: "tr__empty",
236
+ colSpan: 4,
237
+ children: "Every field is translated."
238
+ })
239
+ })
240
+ ]
241
+ })
242
+ ]
243
+ })
244
+ })
245
+ ]
246
+ });
247
+ };
248
+
249
+ // src/review/view/Overview.tsx
250
+ import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "react/jsx-runtime";
251
+ var toneOf = ({ summary }) => summary.slugMissing || summary.coverage < 50 ? "bad" : summary.coverage < 100 ? "warn" : "ok";
252
+ var coverageTone = (coverage) => coverage < 50 ? "bad" : coverage < 100 ? "warn" : "ok";
253
+ var isIncomplete = ({ summary, stale }) => summary.coverage < 100 || summary.slugMissing || stale;
254
+ var Overview = ({ adminRoute, defaultLocale, entities, locales, onlyIssues, tab, page }) => {
255
+ const rows = onlyIssues ? entities.filter((entity) => Object.values(entity.locales).some(isIncomplete)) : entities;
256
+ const stats = locales.map((locale) => {
257
+ const reviews = entities.map((entity) => entity.locales[locale]);
258
+ const fields = reviews.reduce((sum, each) => sum + each.summary.total, 0);
259
+ const ok = reviews.reduce((sum, each) => sum + each.summary.ok, 0);
260
+ return {
261
+ locale,
262
+ coverage: fields ? Math.floor(ok / fields * 100) : 100,
263
+ incomplete: reviews.filter(isIncomplete).length
264
+ };
265
+ });
266
+ return /* @__PURE__ */ jsxs2(Fragment2, {
267
+ children: [
268
+ /* @__PURE__ */ jsx2("div", {
269
+ className: "tr__stats",
270
+ children: stats.map(({ locale, coverage, incomplete }) => /* @__PURE__ */ jsxs2("div", {
271
+ className: "tr__stat",
272
+ children: [
273
+ /* @__PURE__ */ jsx2("span", {
274
+ className: "tr__stat-label",
275
+ children: locale
276
+ }),
277
+ /* @__PURE__ */ jsxs2("span", {
278
+ className: "tr__stat-value",
279
+ children: [
280
+ coverage,
281
+ "%"
282
+ ]
283
+ }),
284
+ /* @__PURE__ */ jsx2("div", {
285
+ className: "tr__bar",
286
+ style: { ["--tr-tone"]: `var(--tr-${coverageTone(coverage)})` },
287
+ children: /* @__PURE__ */ jsx2("span", {
288
+ style: { width: `${coverage}%` }
289
+ })
290
+ }),
291
+ /* @__PURE__ */ jsx2("span", {
292
+ className: "tr__stat-note",
293
+ children: incomplete ? `${incomplete} incomplete` : "all complete"
294
+ })
295
+ ]
296
+ }, locale))
297
+ }),
298
+ /* @__PURE__ */ jsx2("div", {
299
+ className: "tr__card",
300
+ children: /* @__PURE__ */ jsxs2("table", {
301
+ children: [
302
+ /* @__PURE__ */ jsx2("thead", {
303
+ children: /* @__PURE__ */ jsxs2("tr", {
304
+ children: [
305
+ /* @__PURE__ */ jsx2("th", {
306
+ children: "Document"
307
+ }),
308
+ locales.map((locale) => /* @__PURE__ */ jsx2("th", {
309
+ className: "tr__locale-col",
310
+ children: locale.toUpperCase()
311
+ }, locale))
312
+ ]
313
+ })
314
+ }),
315
+ /* @__PURE__ */ jsxs2("tbody", {
316
+ children: [
317
+ rows.map((entity) => /* @__PURE__ */ jsxs2("tr", {
318
+ children: [
319
+ /* @__PURE__ */ jsxs2("td", {
320
+ className: "tr__doc",
321
+ children: [
322
+ /* @__PURE__ */ jsx2("a", {
323
+ href: editHref(adminRoute, entity, defaultLocale),
324
+ children: entity.label
325
+ }),
326
+ /* @__PURE__ */ jsx2("span", {
327
+ className: "tr__key",
328
+ children: entity.key
329
+ })
330
+ ]
331
+ }),
332
+ locales.map((locale) => {
333
+ const review = entity.locales[locale];
334
+ const { summary } = review;
335
+ const title = [
336
+ `${summary.ok}/${summary.total} fields translated`,
337
+ summary.missing && `${summary.missing} missing`,
338
+ summary.identical && `${summary.identical} same as ${defaultLocale}`,
339
+ summary.placeholders && `${summary.placeholders} broken placeholders`,
340
+ summary.slugMissing && "no slug: the page 404s",
341
+ review.stale && "source changed since translation",
342
+ review.reviewed && "reviewed"
343
+ ].filter(Boolean).join(" · ");
344
+ return /* @__PURE__ */ jsx2("td", {
345
+ className: "tr__locale-col",
346
+ children: /* @__PURE__ */ jsxs2("a", {
347
+ className: "tr__cell",
348
+ "data-tone": toneOf(review),
349
+ href: reviewHref(adminRoute, { tab, page, entity: entity.key, locale, issues: isIncomplete(review) ? 1 : undefined }),
350
+ title,
351
+ children: [
352
+ /* @__PURE__ */ jsxs2("span", {
353
+ children: [
354
+ summary.slugMissing ? "404" : `${summary.coverage}%`,
355
+ (review.stale || review.reviewed) && /* @__PURE__ */ jsxs2("span", {
356
+ className: "tr__marks",
357
+ children: [
358
+ " ",
359
+ [review.stale && "↻", review.reviewed && "✓"].filter(Boolean).join(" ")
360
+ ]
361
+ })
362
+ ]
363
+ }),
364
+ /* @__PURE__ */ jsx2("span", {
365
+ className: "tr__bar",
366
+ children: /* @__PURE__ */ jsx2("span", {
367
+ style: { width: `${summary.slugMissing ? 100 : summary.coverage}%` }
368
+ })
369
+ })
370
+ ]
371
+ })
372
+ }, locale);
373
+ })
374
+ ]
375
+ }, entity.key)),
376
+ rows.length === 0 && /* @__PURE__ */ jsx2("tr", {
377
+ children: /* @__PURE__ */ jsx2("td", {
378
+ className: "tr__empty",
379
+ colSpan: locales.length + 1,
380
+ children: "Everything here is fully translated."
381
+ })
382
+ })
383
+ ]
384
+ })
385
+ ]
386
+ })
387
+ }),
388
+ /* @__PURE__ */ jsxs2("div", {
389
+ className: "tr__legend",
390
+ children: [
391
+ /* @__PURE__ */ jsxs2("span", {
392
+ children: [
393
+ /* @__PURE__ */ jsx2("i", {
394
+ className: "tr__dot",
395
+ style: { ["--tr-tone"]: "var(--tr-ok)" }
396
+ }),
397
+ "fully translated"
398
+ ]
399
+ }),
400
+ /* @__PURE__ */ jsxs2("span", {
401
+ children: [
402
+ /* @__PURE__ */ jsx2("i", {
403
+ className: "tr__dot",
404
+ style: { ["--tr-tone"]: "var(--tr-warn)" }
405
+ }),
406
+ "partly translated"
407
+ ]
408
+ }),
409
+ /* @__PURE__ */ jsxs2("span", {
410
+ children: [
411
+ /* @__PURE__ */ jsx2("i", {
412
+ className: "tr__dot",
413
+ style: { ["--tr-tone"]: "var(--tr-bad)" }
414
+ }),
415
+ "under half, or 404 without a slug"
416
+ ]
417
+ }),
418
+ /* @__PURE__ */ jsx2("span", {
419
+ children: "↻ source changed since the last translation"
420
+ }),
421
+ /* @__PURE__ */ jsx2("span", {
422
+ children: "✓ reviewed"
423
+ })
424
+ ]
425
+ })
426
+ ]
427
+ });
428
+ };
429
+
430
+ // src/review/view/styles.ts
431
+ var reviewStyles = `
432
+ .tr { --tr-ok: var(--theme-success-500); --tr-ok-bg: var(--theme-success-100); --tr-warn: var(--theme-warning-500); --tr-warn-bg: var(--theme-warning-100); --tr-bad: var(--theme-error-500); --tr-bad-bg: var(--theme-error-100); --tr-line: var(--theme-elevation-100); --tr-muted: var(--theme-elevation-500); --tr-card: var(--theme-elevation-50); padding-block: calc(var(--base) * 2) calc(var(--base) * 4); }
433
+ .tr a { text-decoration: none; color: inherit; }
434
+ .tr__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--base); margin-bottom: calc(var(--base) * 1.5); }
435
+ .tr__head h1 { margin: 0 0 4px; }
436
+ .tr__lead { margin: 0; color: var(--tr-muted); max-width: 60ch; }
437
+ .tr__back { display: inline-flex; gap: 6px; color: var(--tr-muted); font-size: 13px; margin-bottom: 8px; }
438
+ .tr__back:hover { color: var(--theme-text); }
439
+
440
+ .tr__stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-bottom: calc(var(--base) * 1.5); }
441
+ .tr__stat { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; border: 1px solid var(--tr-line); border-radius: var(--style-radius-m); background: var(--tr-card); }
442
+ .tr__stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--tr-muted); }
443
+ .tr__stat-value { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
444
+ .tr__stat-note { font-size: 12px; color: var(--tr-muted); }
445
+
446
+ .tr__bar { height: 4px; border-radius: 999px; background: var(--theme-elevation-150); overflow: hidden; }
447
+ .tr__bar > span { display: block; height: 100%; border-radius: inherit; background: var(--tr-tone, var(--tr-ok)); }
448
+
449
+ .tr__toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
450
+ .tr__tabs { display: inline-flex; padding: 3px; gap: 2px; border-radius: 999px; background: var(--theme-elevation-100); }
451
+ .tr__tab { padding: 5px 14px; border-radius: 999px; font-size: 13px; color: var(--theme-elevation-700); text-transform: capitalize; }
452
+ .tr__tab:hover { color: var(--theme-text); }
453
+ .tr__tab[aria-current='true'] { background: var(--theme-elevation-0); color: var(--theme-text); box-shadow: 0 1px 2px rgba(0,0,0,.2); }
454
+ .tr__filters { display: inline-flex; align-items: center; gap: 12px; font-size: 13px; color: var(--tr-muted); }
455
+ .tr__toggle { display: inline-flex; align-items: center; gap: 8px; padding: 5px 12px; border: 1px solid var(--tr-line); border-radius: 999px; color: var(--theme-elevation-800); }
456
+ .tr__toggle::before { content: ''; width: 26px; height: 14px; border-radius: 999px; background: var(--theme-elevation-200) radial-gradient(circle at 7px 50%, var(--theme-elevation-0) 4px, transparent 4.5px); }
457
+ .tr__toggle[aria-pressed='true']::before { background: var(--tr-ok) radial-gradient(circle at 19px 50%, var(--theme-elevation-0) 4px, transparent 4.5px); }
458
+
459
+ .tr__card { border: 1px solid var(--tr-line); border-radius: var(--style-radius-m); overflow: auto; background: var(--theme-elevation-0); }
460
+ .tr table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
461
+ .tr th { position: sticky; top: 0; z-index: 1; background: var(--tr-card); text-align: left; font-weight: 500; color: var(--tr-muted); padding: 10px 12px; border-bottom: 1px solid var(--tr-line); white-space: nowrap; }
462
+ .tr th small { display: block; font-size: 11px; font-weight: 400; }
463
+ .tr td { padding: 6px 12px; border-bottom: 1px solid var(--tr-line); vertical-align: middle; }
464
+ .tr tbody tr:last-child td { border-bottom: 0; }
465
+ .tr tbody tr:hover td { background: var(--theme-elevation-50); }
466
+ .tr__locale-col { text-align: center !important; }
467
+ .tr__doc { min-width: 240px; }
468
+ .tr__doc a { font-weight: 500; }
469
+ .tr__doc a:hover { text-decoration: underline; }
470
+ .tr__key { display: block; margin-top: 2px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; color: var(--tr-muted); }
471
+
472
+ .tr__cell { display: flex; flex-direction: column; gap: 4px; min-width: 64px; padding: 6px 8px; border-radius: 6px; text-align: center; font-variant-numeric: tabular-nums; font-weight: 500; transition: background .12s; }
473
+ .tr__cell:hover { background: var(--theme-elevation-100); }
474
+ .tr__cell[data-tone='ok'] { --tr-tone: var(--tr-ok); color: var(--tr-ok); }
475
+ .tr__cell[data-tone='warn'] { --tr-tone: var(--tr-warn); color: var(--tr-warn); }
476
+ .tr__cell[data-tone='bad'] { --tr-tone: var(--tr-bad); color: var(--tr-bad); }
477
+ .tr__marks { font-size: 11px; color: var(--tr-muted); }
478
+
479
+ .tr__legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 12px; font-size: 12px; color: var(--tr-muted); }
480
+ .tr__legend span { display: inline-flex; align-items: center; gap: 6px; }
481
+ .tr__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--tr-tone); }
482
+
483
+ .tr__pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 12px; font-size: 13px; color: var(--tr-muted); }
484
+ .tr__pager nav { display: inline-flex; gap: 6px; }
485
+ .tr__pager a, .tr__pager span[aria-disabled] { padding: 5px 12px; border: 1px solid var(--tr-line); border-radius: 999px; color: var(--theme-elevation-800); }
486
+ .tr__pager span[aria-disabled] { opacity: .4; }
487
+ .tr__pager a:hover { background: var(--theme-elevation-100); }
488
+
489
+ .tr__pill { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 500; white-space: nowrap; background: var(--theme-elevation-100); }
490
+ .tr__pill::before { content: ''; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
491
+ .tr__pill[data-tone='ok'] { color: var(--tr-ok); background: var(--tr-ok-bg); }
492
+ .tr__pill[data-tone='warn'] { color: var(--tr-warn); background: var(--tr-warn-bg); }
493
+ .tr__pill[data-tone='bad'] { color: var(--tr-bad); background: var(--tr-bad-bg); }
494
+
495
+ .tr__meta { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
496
+ .tr__chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--tr-line); border-radius: 999px; font-size: 12px; color: var(--theme-elevation-800); }
497
+ .tr__chip--bad { color: var(--tr-bad); border-color: var(--tr-bad); }
498
+ a.tr__chip:hover { background: var(--theme-elevation-100); }
499
+
500
+ .tr__path { width: 22%; font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; color: var(--theme-elevation-700); word-break: break-word; }
501
+ .tr__text { width: 34%; white-space: pre-wrap; word-break: break-word; line-height: 1.45; }
502
+ .tr__text--empty { color: var(--tr-muted); font-style: italic; }
503
+ .tr__empty { padding: 32px; text-align: center; color: var(--tr-muted); }
504
+ .tr__error { color: var(--tr-bad); }
505
+
506
+ .translator-review__actions { display: flex; flex-wrap: wrap; gap: 8px; }
507
+ .translator-review__actions .btn { margin: 0; }
508
+ `;
509
+
510
+ // src/review/view/TranslationsView.tsx
511
+ import { jsx as jsx3, jsxs as jsxs3, Fragment as Fragment3 } from "react/jsx-runtime";
512
+ var PAGE_SIZE = 50;
513
+ var param = (value) => Array.isArray(value) ? value[0] : value;
514
+ var TranslationsView = async ({ initPageResult, params, searchParams }) => {
515
+ const { req, permissions, visibleEntities, locale } = initPageResult;
516
+ const { payload } = req;
517
+ const adminRoute = payload.config.routes.admin;
518
+ const pluginConfig = payload.config.custom?.translator;
519
+ const collections = (pluginConfig?.collections ?? []).filter((slug) => permissions?.collections?.[slug]?.read);
520
+ const globals = (pluginConfig?.globals ?? []).filter((slug) => permissions?.globals?.[slug]?.read);
521
+ const tabs = [...collections, ...globals.length ? ["globals"] : []];
522
+ const tab = tabs.includes(param(searchParams?.tab) ?? "") ? param(searchParams?.tab) : tabs[0];
523
+ const entity = param(searchParams?.entity);
524
+ const targetLocale = param(searchParams?.locale);
525
+ const onlyIssues = param(searchParams?.issues) === "1";
526
+ const page = Math.max(1, Number(param(searchParams?.page)) || 1);
527
+ const { defaultLocale, targetLocales } = readLocales(req);
528
+ const content = async () => {
529
+ if (!req.user)
530
+ return /* @__PURE__ */ jsx3("p", {
531
+ className: "tr__empty",
532
+ children: "Log in to review translations."
533
+ });
534
+ if (!tab)
535
+ return /* @__PURE__ */ jsx3("p", {
536
+ children: "No translated collections or globals are readable for your user."
537
+ });
538
+ if (entity && targetLocale && targetLocales.includes(targetLocale)) {
539
+ try {
540
+ const review = await loadEntityLocaleReview({ req, entity, locale: targetLocale });
541
+ return /* @__PURE__ */ jsx3(Detail, {
542
+ adminRoute,
543
+ onlyIssues,
544
+ page,
545
+ review,
546
+ tab
547
+ });
548
+ } catch (error) {
549
+ return /* @__PURE__ */ jsxs3("p", {
550
+ className: "tr__error",
551
+ children: [
552
+ "Could not load ",
553
+ entity,
554
+ ": ",
555
+ String(error)
556
+ ]
557
+ });
558
+ }
559
+ }
560
+ if (tab === "globals") {
561
+ const { entities, locales } = await loadGlobalsReview({ req, globalSlugs: globals });
562
+ return /* @__PURE__ */ jsx3(Overview, {
563
+ adminRoute,
564
+ defaultLocale,
565
+ entities,
566
+ locales,
567
+ onlyIssues,
568
+ tab
569
+ });
570
+ }
571
+ const review = await loadCollectionReview({ req, collectionSlug: tab, page, limit: PAGE_SIZE });
572
+ const first = review.totalDocs ? (review.page - 1) * PAGE_SIZE + 1 : 0;
573
+ const last = Math.min(review.page * PAGE_SIZE, review.totalDocs);
574
+ const pageHref = (target) => reviewHref(adminRoute, { tab, page: target, issues: onlyIssues ? 1 : undefined });
575
+ return /* @__PURE__ */ jsxs3(Fragment3, {
576
+ children: [
577
+ /* @__PURE__ */ jsx3(Overview, {
578
+ adminRoute,
579
+ defaultLocale,
580
+ entities: review.entities,
581
+ locales: review.locales,
582
+ onlyIssues,
583
+ page,
584
+ tab
585
+ }),
586
+ /* @__PURE__ */ jsxs3("div", {
587
+ className: "tr__pager",
588
+ children: [
589
+ /* @__PURE__ */ jsxs3("span", {
590
+ children: [
591
+ first,
592
+ "–",
593
+ last,
594
+ " of ",
595
+ review.totalDocs,
596
+ " documents",
597
+ onlyIssues ? " (complete ones hidden)" : ""
598
+ ]
599
+ }),
600
+ review.totalPages > 1 && /* @__PURE__ */ jsxs3("nav", {
601
+ children: [
602
+ review.page > 1 ? /* @__PURE__ */ jsx3("a", {
603
+ href: pageHref(review.page - 1),
604
+ children: "← Newer"
605
+ }) : /* @__PURE__ */ jsx3("span", {
606
+ "aria-disabled": true,
607
+ children: "← Newer"
608
+ }),
609
+ /* @__PURE__ */ jsxs3("span", {
610
+ children: [
611
+ review.page,
612
+ " / ",
613
+ review.totalPages
614
+ ]
615
+ }),
616
+ review.page < review.totalPages ? /* @__PURE__ */ jsx3("a", {
617
+ href: pageHref(review.page + 1),
618
+ children: "Older →"
619
+ }) : /* @__PURE__ */ jsx3("span", {
620
+ "aria-disabled": true,
621
+ children: "Older →"
622
+ })
623
+ ]
624
+ })
625
+ ]
626
+ })
627
+ ]
628
+ });
629
+ };
630
+ const adminLocale = req.locale;
631
+ const body = await content();
632
+ req.locale = adminLocale;
633
+ return /* @__PURE__ */ jsxs3(DefaultTemplate, {
634
+ i18n: req.i18n,
635
+ locale,
636
+ params,
637
+ payload,
638
+ permissions,
639
+ searchParams,
640
+ user: req.user || undefined,
641
+ visibleEntities,
642
+ children: [
643
+ /* @__PURE__ */ jsx3("style", {
644
+ dangerouslySetInnerHTML: { __html: reviewStyles }
645
+ }),
646
+ /* @__PURE__ */ jsxs3(Gutter, {
647
+ className: "tr",
648
+ children: [
649
+ !entity && /* @__PURE__ */ jsxs3(Fragment3, {
650
+ children: [
651
+ /* @__PURE__ */ jsx3("div", {
652
+ className: "tr__head",
653
+ children: /* @__PURE__ */ jsxs3("div", {
654
+ children: [
655
+ /* @__PURE__ */ jsx3("h1", {
656
+ children: "Translations"
657
+ }),
658
+ /* @__PURE__ */ jsxs3("p", {
659
+ className: "tr__lead",
660
+ children: [
661
+ "How much of each document is translated from ",
662
+ defaultLocale.toUpperCase(),
663
+ ". Open a cell to compare it field by field."
664
+ ]
665
+ })
666
+ ]
667
+ })
668
+ }),
669
+ /* @__PURE__ */ jsxs3("div", {
670
+ className: "tr__toolbar",
671
+ children: [
672
+ /* @__PURE__ */ jsx3("nav", {
673
+ className: "tr__tabs",
674
+ children: tabs.map((each) => /* @__PURE__ */ jsx3("a", {
675
+ "aria-current": each === tab,
676
+ className: "tr__tab",
677
+ href: reviewHref(adminRoute, { tab: each, issues: onlyIssues ? 1 : undefined }),
678
+ children: each
679
+ }, each))
680
+ }),
681
+ /* @__PURE__ */ jsx3("a", {
682
+ "aria-pressed": onlyIssues,
683
+ className: "tr__toggle",
684
+ href: reviewHref(adminRoute, { tab, issues: onlyIssues ? undefined : 1 }),
685
+ children: "Only incomplete"
686
+ })
687
+ ]
688
+ })
689
+ ]
690
+ }),
691
+ body
692
+ ]
693
+ })
694
+ ]
695
+ });
696
+ };
697
+
698
+ // src/exports/rsc.ts
699
+ var rsc_default = TranslationsView;
700
+ export {
701
+ TranslationsView,
702
+ rsc_default as default
703
+ };