@justanarthur/payload-plugin-translator 3.0.3 → 3.1.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/rsc.js ADDED
@@ -0,0 +1,483 @@
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
+ import { ReviewStyles } from "@justanarthur/payload-plugin-translator/client";
16
+
17
+ // src/review/view/Detail.tsx
18
+ import { ReviewActions } from "@justanarthur/payload-plugin-translator/client";
19
+
20
+ // src/review/view/href.ts
21
+ import { formatAdminURL } from "payload/shared";
22
+ var reviewHref = (adminRoute, params) => {
23
+ const query = new URLSearchParams(Object.entries(params).filter((entry) => entry[1] !== undefined && entry[1] !== "").map(([key, value]) => [key, String(value)])).toString();
24
+ return `${formatAdminURL({ adminRoute, path: REVIEW_VIEW_PATH })}${query ? `?${query}` : ""}`;
25
+ };
26
+ var editHref = (adminRoute, { collectionSlug, globalSlug, id }, locale) => `${formatAdminURL({
27
+ adminRoute,
28
+ path: globalSlug ? `/globals/${globalSlug}` : `/collections/${collectionSlug}/${id}`
29
+ })}?locale=${locale}`;
30
+
31
+ // src/review/view/Detail.tsx
32
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
33
+ var STATE_LABEL = {
34
+ identical: "same as source",
35
+ missing: "missing",
36
+ ok: "translated",
37
+ placeholders: "placeholders differ"
38
+ };
39
+ var preview = (text) => text.length > 600 ? `${text.slice(0, 597)}…` : text;
40
+ var readablePath = (path) => path.replace(/\[(\d+)\]/g, (_, index) => ` ${Number(index) + 1}`).replace(/\(([^)]+)\)/g, " · $1").replace(/#(\d+)$/, (_, index) => ` (part ${Number(index) + 1})`).split(".").join(" › ");
41
+ var Detail = ({ adminRoute, review, onlyIssues, tab }) => {
42
+ const rows = onlyIssues ? review.fields.filter((field) => field.state !== "ok") : review.fields;
43
+ const { summary } = review;
44
+ return /* @__PURE__ */ jsxs(Fragment, {
45
+ children: [
46
+ /* @__PURE__ */ jsxs("div", {
47
+ className: "translator-review__header",
48
+ children: [
49
+ /* @__PURE__ */ jsxs("div", {
50
+ children: [
51
+ /* @__PURE__ */ jsxs("a", {
52
+ href: reviewHref(adminRoute, { tab }),
53
+ children: [
54
+ "← All ",
55
+ tab === "globals" ? "globals" : tab
56
+ ]
57
+ }),
58
+ /* @__PURE__ */ jsxs("h2", {
59
+ children: [
60
+ review.label,
61
+ " · ",
62
+ review.defaultLocale,
63
+ " → ",
64
+ review.locale
65
+ ]
66
+ })
67
+ ]
68
+ }),
69
+ /* @__PURE__ */ jsx(ReviewActions, {
70
+ entity: review.entity,
71
+ locale: review.locale,
72
+ reviewed: review.reviewed
73
+ })
74
+ ]
75
+ }),
76
+ /* @__PURE__ */ jsxs("div", {
77
+ className: "translator-review__meta",
78
+ children: [
79
+ /* @__PURE__ */ jsxs("span", {
80
+ children: [
81
+ summary.ok,
82
+ "/",
83
+ summary.total,
84
+ " translated (",
85
+ summary.coverage,
86
+ "%)"
87
+ ]
88
+ }),
89
+ summary.missing > 0 && /* @__PURE__ */ jsxs("span", {
90
+ children: [
91
+ summary.missing,
92
+ " missing"
93
+ ]
94
+ }),
95
+ summary.identical > 0 && /* @__PURE__ */ jsxs("span", {
96
+ children: [
97
+ summary.identical,
98
+ " same as ",
99
+ review.defaultLocale
100
+ ]
101
+ }),
102
+ summary.placeholders > 0 && /* @__PURE__ */ jsxs("span", {
103
+ children: [
104
+ summary.placeholders,
105
+ " placeholder mismatches"
106
+ ]
107
+ }),
108
+ summary.slugMissing && /* @__PURE__ */ jsxs("span", {
109
+ className: "translator-review__error",
110
+ children: [
111
+ "no slug, the page 404s in ",
112
+ review.locale
113
+ ]
114
+ }),
115
+ review.stale && /* @__PURE__ */ jsx("span", {
116
+ children: "source changed since the last translation"
117
+ }),
118
+ review.translatedAt && /* @__PURE__ */ jsxs("span", {
119
+ children: [
120
+ "translated ",
121
+ new Date(review.translatedAt).toLocaleString()
122
+ ]
123
+ }),
124
+ review.reviewedAt && /* @__PURE__ */ jsxs("span", {
125
+ children: [
126
+ "reviewed ",
127
+ new Date(review.reviewedAt).toLocaleString(),
128
+ " by ",
129
+ review.reviewedBy
130
+ ]
131
+ }),
132
+ review.lastJob && /* @__PURE__ */ jsxs("span", {
133
+ className: review.lastJob.error ? "translator-review__error" : undefined,
134
+ children: [
135
+ "last auto-translate job #",
136
+ review.lastJob.id,
137
+ ": ",
138
+ review.lastJob.error ? `failed — ${preview(review.lastJob.error)}` : review.lastJob.completedAt ? "completed" : review.lastJob.processing ? "running" : "queued"
139
+ ]
140
+ }),
141
+ /* @__PURE__ */ jsxs("a", {
142
+ href: editHref(adminRoute, review, review.locale),
143
+ children: [
144
+ "Edit in ",
145
+ review.locale
146
+ ]
147
+ }),
148
+ /* @__PURE__ */ jsx("a", {
149
+ href: reviewHref(adminRoute, { tab, entity: review.entity, locale: review.locale, issues: onlyIssues ? undefined : 1 }),
150
+ children: onlyIssues ? "Show all fields" : "Show only problems"
151
+ })
152
+ ]
153
+ }),
154
+ /* @__PURE__ */ jsx("div", {
155
+ className: "translator-review__scroll",
156
+ children: /* @__PURE__ */ jsxs("table", {
157
+ children: [
158
+ /* @__PURE__ */ jsx("thead", {
159
+ children: /* @__PURE__ */ jsxs("tr", {
160
+ children: [
161
+ /* @__PURE__ */ jsx("th", {
162
+ children: "Field"
163
+ }),
164
+ /* @__PURE__ */ jsx("th", {
165
+ children: "State"
166
+ }),
167
+ /* @__PURE__ */ jsx("th", {
168
+ children: review.defaultLocale
169
+ }),
170
+ /* @__PURE__ */ jsx("th", {
171
+ children: review.locale
172
+ })
173
+ ]
174
+ })
175
+ }),
176
+ /* @__PURE__ */ jsxs("tbody", {
177
+ children: [
178
+ rows.map((field) => /* @__PURE__ */ jsxs("tr", {
179
+ children: [
180
+ /* @__PURE__ */ jsx("td", {
181
+ className: "translator-review__path",
182
+ title: field.path,
183
+ children: readablePath(field.path)
184
+ }),
185
+ /* @__PURE__ */ jsx("td", {
186
+ children: /* @__PURE__ */ jsx("span", {
187
+ className: `translator-review__state translator-review__state--${field.state}`,
188
+ children: STATE_LABEL[field.state]
189
+ })
190
+ }),
191
+ /* @__PURE__ */ jsx("td", {
192
+ className: "translator-review__text",
193
+ children: preview(field.sourceText)
194
+ }),
195
+ /* @__PURE__ */ jsx("td", {
196
+ className: `translator-review__text${field.targetText.trim() ? "" : " translator-review__text--empty"}`,
197
+ children: field.targetText.trim() ? preview(field.targetText) : "empty"
198
+ })
199
+ ]
200
+ }, field.path)),
201
+ rows.length === 0 && /* @__PURE__ */ jsx("tr", {
202
+ children: /* @__PURE__ */ jsx("td", {
203
+ colSpan: 4,
204
+ children: "Every field is translated."
205
+ })
206
+ })
207
+ ]
208
+ })
209
+ ]
210
+ })
211
+ })
212
+ ]
213
+ });
214
+ };
215
+
216
+ // src/review/view/Overview.tsx
217
+ import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "react/jsx-runtime";
218
+ var cellTone = ({ summary, stale }) => [
219
+ "translator-review__cell",
220
+ summary.slugMissing || summary.coverage === 0 ? "translator-review__cell--missing" : summary.coverage === 100 ? "translator-review__cell--ok" : "translator-review__cell--partial",
221
+ stale ? "translator-review__cell--stale" : ""
222
+ ].filter(Boolean).join(" ");
223
+ var hasIssue = ({ summary, stale, reviewed }) => summary.coverage < 100 || summary.slugMissing || stale || !reviewed;
224
+ var Overview = ({ adminRoute, defaultLocale, entities, locales, onlyIssues, tab }) => {
225
+ const rows = onlyIssues ? entities.filter((entity) => Object.values(entity.locales).some((each) => each.summary.coverage < 100 || each.summary.slugMissing || each.stale)) : entities;
226
+ const totals = Object.fromEntries(locales.map((locale) => {
227
+ const reviews = entities.map((entity) => entity.locales[locale]);
228
+ const fields = reviews.reduce((sum, each) => sum + each.summary.total, 0);
229
+ const ok = reviews.reduce((sum, each) => sum + each.summary.ok, 0);
230
+ return [locale, fields ? Math.floor(ok / fields * 100) : 100];
231
+ }));
232
+ return /* @__PURE__ */ jsxs2(Fragment2, {
233
+ children: [
234
+ /* @__PURE__ */ jsx2("div", {
235
+ className: "translator-review__scroll",
236
+ children: /* @__PURE__ */ jsxs2("table", {
237
+ children: [
238
+ /* @__PURE__ */ jsx2("thead", {
239
+ children: /* @__PURE__ */ jsxs2("tr", {
240
+ children: [
241
+ /* @__PURE__ */ jsx2("th", {
242
+ children: "Document"
243
+ }),
244
+ locales.map((locale) => /* @__PURE__ */ jsxs2("th", {
245
+ children: [
246
+ locale,
247
+ " ",
248
+ /* @__PURE__ */ jsxs2("small", {
249
+ children: [
250
+ totals[locale],
251
+ "%"
252
+ ]
253
+ })
254
+ ]
255
+ }, locale))
256
+ ]
257
+ })
258
+ }),
259
+ /* @__PURE__ */ jsxs2("tbody", {
260
+ children: [
261
+ rows.map((entity) => /* @__PURE__ */ jsxs2("tr", {
262
+ children: [
263
+ /* @__PURE__ */ jsxs2("td", {
264
+ className: "translator-review__doc",
265
+ children: [
266
+ /* @__PURE__ */ jsx2("a", {
267
+ href: editHref(adminRoute, entity, defaultLocale),
268
+ children: entity.label
269
+ }),
270
+ /* @__PURE__ */ jsx2("small", {
271
+ children: entity.key
272
+ })
273
+ ]
274
+ }),
275
+ locales.map((locale) => {
276
+ const review = entity.locales[locale];
277
+ const title = [
278
+ `${review.summary.ok}/${review.summary.total} fields translated`,
279
+ review.summary.missing && `${review.summary.missing} missing`,
280
+ review.summary.identical && `${review.summary.identical} same as ${defaultLocale}`,
281
+ review.summary.placeholders && `${review.summary.placeholders} broken placeholders`,
282
+ review.summary.slugMissing && "no slug: page 404s",
283
+ review.stale && `source changed since translation`,
284
+ review.reviewed && "reviewed"
285
+ ].filter(Boolean).join(" · ");
286
+ return /* @__PURE__ */ jsx2("td", {
287
+ children: /* @__PURE__ */ jsxs2("a", {
288
+ className: cellTone(review),
289
+ href: reviewHref(adminRoute, { tab, entity: entity.key, locale, issues: hasIssue(review) ? 1 : undefined }),
290
+ title,
291
+ children: [
292
+ review.summary.slugMissing ? "404" : `${review.summary.coverage}%`,
293
+ review.reviewed ? " ✓" : "",
294
+ review.stale ? " ↻" : ""
295
+ ]
296
+ })
297
+ }, locale);
298
+ })
299
+ ]
300
+ }, entity.key)),
301
+ rows.length === 0 && /* @__PURE__ */ jsx2("tr", {
302
+ children: /* @__PURE__ */ jsx2("td", {
303
+ colSpan: locales.length + 1,
304
+ children: "Nothing to review here."
305
+ })
306
+ })
307
+ ]
308
+ })
309
+ ]
310
+ })
311
+ }),
312
+ /* @__PURE__ */ jsxs2("div", {
313
+ className: "translator-review__legend",
314
+ children: [
315
+ /* @__PURE__ */ jsx2("span", {
316
+ children: "% share of fields translated"
317
+ }),
318
+ /* @__PURE__ */ jsx2("span", {
319
+ children: "404 no slug in that locale"
320
+ }),
321
+ /* @__PURE__ */ jsx2("span", {
322
+ children: "↻ source changed since the last translation"
323
+ }),
324
+ /* @__PURE__ */ jsx2("span", {
325
+ children: "✓ reviewed against the current source"
326
+ })
327
+ ]
328
+ })
329
+ ]
330
+ });
331
+ };
332
+
333
+ // src/review/view/TranslationsView.tsx
334
+ import { jsx as jsx3, jsxs as jsxs3, Fragment as Fragment3 } from "react/jsx-runtime";
335
+ var param = (value) => Array.isArray(value) ? value[0] : value;
336
+ var TranslationsView = async ({ initPageResult, params, searchParams }) => {
337
+ const { req, permissions, visibleEntities, locale } = initPageResult;
338
+ const { payload } = req;
339
+ const adminRoute = payload.config.routes.admin;
340
+ const pluginConfig = payload.config.custom?.translator;
341
+ const collections = (pluginConfig?.collections ?? []).filter((slug) => permissions?.collections?.[slug]?.read);
342
+ const globals = (pluginConfig?.globals ?? []).filter((slug) => permissions?.globals?.[slug]?.read);
343
+ const tabs = [...collections, ...globals.length ? ["globals"] : []];
344
+ const tab = tabs.includes(param(searchParams?.tab) ?? "") ? param(searchParams?.tab) : tabs[0];
345
+ const entity = param(searchParams?.entity);
346
+ const targetLocale = param(searchParams?.locale);
347
+ const onlyIssues = param(searchParams?.issues) === "1";
348
+ const page = Math.max(1, Number(param(searchParams?.page)) || 1);
349
+ const { defaultLocale, targetLocales } = readLocales(req);
350
+ const content = async () => {
351
+ if (!req.user)
352
+ return /* @__PURE__ */ jsx3("p", {
353
+ children: "Log in to review translations."
354
+ });
355
+ if (!tab)
356
+ return /* @__PURE__ */ jsx3("p", {
357
+ children: "No translated collections or globals are readable for your user."
358
+ });
359
+ if (entity && targetLocale && targetLocales.includes(targetLocale)) {
360
+ try {
361
+ const review = await loadEntityLocaleReview({ req, entity, locale: targetLocale });
362
+ return /* @__PURE__ */ jsx3(Detail, {
363
+ adminRoute,
364
+ onlyIssues,
365
+ review,
366
+ tab
367
+ });
368
+ } catch (error) {
369
+ return /* @__PURE__ */ jsxs3("p", {
370
+ className: "translator-review__error",
371
+ children: [
372
+ "Could not load ",
373
+ entity,
374
+ ": ",
375
+ String(error)
376
+ ]
377
+ });
378
+ }
379
+ }
380
+ if (tab === "globals") {
381
+ const { entities, locales } = await loadGlobalsReview({ req, globalSlugs: globals });
382
+ return /* @__PURE__ */ jsx3(Overview, {
383
+ adminRoute,
384
+ defaultLocale,
385
+ entities,
386
+ locales,
387
+ onlyIssues,
388
+ tab
389
+ });
390
+ }
391
+ const review = await loadCollectionReview({ req, collectionSlug: tab, page });
392
+ return /* @__PURE__ */ jsxs3(Fragment3, {
393
+ children: [
394
+ /* @__PURE__ */ jsx3(Overview, {
395
+ adminRoute,
396
+ defaultLocale,
397
+ entities: review.entities,
398
+ locales: review.locales,
399
+ onlyIssues,
400
+ tab
401
+ }),
402
+ review.totalPages > 1 && /* @__PURE__ */ jsxs3("div", {
403
+ className: "translator-review__pager",
404
+ children: [
405
+ page > 1 && /* @__PURE__ */ jsx3("a", {
406
+ href: reviewHref(adminRoute, { tab, page: page - 1, issues: onlyIssues ? 1 : undefined }),
407
+ children: "← Newer"
408
+ }),
409
+ /* @__PURE__ */ jsxs3("span", {
410
+ children: [
411
+ "Page ",
412
+ page,
413
+ " of ",
414
+ review.totalPages,
415
+ " · ",
416
+ review.totalDocs,
417
+ " documents"
418
+ ]
419
+ }),
420
+ page < review.totalPages && /* @__PURE__ */ jsx3("a", {
421
+ href: reviewHref(adminRoute, { tab, page: page + 1, issues: onlyIssues ? 1 : undefined }),
422
+ children: "Older →"
423
+ })
424
+ ]
425
+ })
426
+ ]
427
+ });
428
+ };
429
+ const adminLocale = req.locale;
430
+ const body = await content();
431
+ req.locale = adminLocale;
432
+ return /* @__PURE__ */ jsxs3(DefaultTemplate, {
433
+ i18n: req.i18n,
434
+ locale,
435
+ params,
436
+ payload,
437
+ permissions,
438
+ searchParams,
439
+ user: req.user || undefined,
440
+ visibleEntities,
441
+ children: [
442
+ /* @__PURE__ */ jsx3(ReviewStyles, {}),
443
+ /* @__PURE__ */ jsxs3(Gutter, {
444
+ className: "translator-review",
445
+ children: [
446
+ !entity && /* @__PURE__ */ jsxs3(Fragment3, {
447
+ children: [
448
+ /* @__PURE__ */ jsxs3("div", {
449
+ className: "translator-review__header",
450
+ children: [
451
+ /* @__PURE__ */ jsx3("h1", {
452
+ children: "Translations"
453
+ }),
454
+ /* @__PURE__ */ jsx3("a", {
455
+ href: reviewHref(adminRoute, { tab, issues: onlyIssues ? undefined : 1 }),
456
+ children: onlyIssues ? "Show everything" : "Show only incomplete"
457
+ })
458
+ ]
459
+ }),
460
+ /* @__PURE__ */ jsx3("nav", {
461
+ className: "translator-review__tabs",
462
+ children: tabs.map((each) => /* @__PURE__ */ jsx3("a", {
463
+ "aria-current": each === tab,
464
+ className: "translator-review__tab",
465
+ href: reviewHref(adminRoute, { tab: each, issues: onlyIssues ? 1 : undefined }),
466
+ children: each
467
+ }, each))
468
+ })
469
+ ]
470
+ }),
471
+ body
472
+ ]
473
+ })
474
+ ]
475
+ });
476
+ };
477
+
478
+ // src/exports/rsc.ts
479
+ var rsc_default = TranslationsView;
480
+ export {
481
+ TranslationsView,
482
+ rsc_default as default
483
+ };