@focus-reactive/payload-plugin-translator 0.7.2 → 0.8.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/README.md +18 -3
- package/dist/client/entities/translation/api/mutations/useDismissStaleness.d.ts +12 -0
- package/dist/client/entities/translation/api/mutations/useDismissStaleness.js +40 -0
- package/dist/client/entities/translation/api/mutations/useQueueDocumentTranslation.js +6 -0
- package/dist/client/entities/translation/api/mutations/useRunDocumentTranslation.js +6 -0
- package/dist/client/entities/translation/api/queries/useDocumentStaleness.d.ts +17 -0
- package/dist/client/entities/translation/api/queries/useDocumentStaleness.js +35 -0
- package/dist/client/entities/translation/api/queries/useDocumentTranslation.js +23 -1
- package/dist/client/entities/translation/index.d.ts +12 -5
- package/dist/client/entities/translation/index.js +10 -5
- package/dist/client/entities/translation/model/panelStatus.d.ts +60 -0
- package/dist/client/entities/translation/model/panelStatus.js +101 -0
- package/dist/client/entities/translation/model/statusRows.d.ts +42 -0
- package/dist/client/entities/translation/model/statusRows.js +79 -0
- package/dist/client/entities/translation/model/types.d.ts +11 -0
- package/dist/client/entities/translation/model/types.js +1 -1
- package/dist/client/entities/translation/ui/ActionButton/ActionButton.d.ts +14 -0
- package/dist/client/entities/translation/ui/ActionButton/ActionButton.js +28 -0
- package/dist/client/entities/translation/ui/ActionButton/index.d.ts +1 -0
- package/dist/client/entities/translation/ui/ActionButton/index.js +3 -0
- package/dist/client/entities/translation/ui/ActionButton/styles.module.scss +11 -0
- package/dist/client/entities/translation/ui/PanelStatusMarker/PanelStatusMarker.d.ts +12 -0
- package/dist/client/entities/translation/ui/PanelStatusMarker/PanelStatusMarker.js +26 -0
- package/dist/client/entities/translation/ui/PanelStatusMarker/index.d.ts +1 -0
- package/dist/client/entities/translation/ui/PanelStatusMarker/index.js +3 -0
- package/dist/client/entities/translation/ui/PanelStatusMarker/styles.module.scss +12 -0
- package/dist/client/entities/translation/ui/TranslationDirection/styles.module.scss +2 -2
- package/dist/client/entities/translation/ui/TranslationStatusList/StatusBadge.d.ts +10 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/StatusBadge.js +27 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/TranslationStatusList.d.ts +14 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/TranslationStatusList.js +194 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/index.d.ts +1 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/index.js +3 -0
- package/dist/client/entities/translation/ui/TranslationStatusList/styles.module.scss +72 -0
- package/dist/client/features/collection-translation-form/ui/CollectionTranslationForm.js +17 -21
- package/dist/client/features/collection-translation-form/ui/styles.module.scss +14 -14
- package/dist/client/features/collection-translation-popup/ui/CollectionTranslationPopup.d.ts +4 -2
- package/dist/client/features/collection-translation-popup/ui/CollectionTranslationPopup.js +9 -9
- package/dist/client/features/collection-translation-popup/ui/styles.module.scss +5 -3
- package/dist/client/features/collection-translation-progress/ui/CollectionTranslationProgress.d.ts +5 -1
- package/dist/client/features/collection-translation-progress/ui/CollectionTranslationProgress.js +100 -93
- package/dist/client/features/collection-translation-progress/ui/styles.module.scss +52 -3
- package/dist/client/features/open-document-translation-popup/ui/OpenDocumentTranslationPopup.d.ts +4 -1
- package/dist/client/features/open-document-translation-popup/ui/OpenDocumentTranslationPopup.js +16 -5
- package/dist/client/features/open-document-translation-popup/ui/styles.module.scss +5 -3
- package/dist/client/features/translate-document-form/ui/DocumentTranslationForm.js +5 -2
- package/dist/client/features/translate-document-form/ui/styles.module.scss +8 -2
- package/dist/client/shared/lib/assets/icons/AlertTriangleIcon.d.ts +1 -0
- package/dist/client/shared/lib/assets/icons/AlertTriangleIcon.js +14 -0
- package/dist/client/shared/lib/assets/icons/CheckIcon.d.ts +1 -0
- package/dist/client/shared/lib/assets/icons/CheckIcon.js +17 -0
- package/dist/client/shared/lib/assets/icons/CloseIcon.d.ts +1 -0
- package/dist/client/shared/lib/assets/icons/CloseIcon.js +16 -0
- package/dist/client/shared/ui/Button/Button.js +2 -1
- package/dist/client/shared/ui/ColorIndicator/ColorIndicator.d.ts +1 -1
- package/dist/client/shared/ui/ColorIndicator/styles.module.scss +4 -0
- package/dist/client/shared/ui/Label/Label.d.ts +2 -0
- package/dist/client/shared/ui/Label/Label.js +3 -2
- package/dist/client/shared/ui/Label/styles.module.scss +11 -0
- package/dist/client/shared/ui/Loading/Loading.d.ts +6 -2
- package/dist/client/shared/ui/Loading/Loading.js +4 -2
- package/dist/client/shared/ui/Loading/barCount.d.ts +3 -0
- package/dist/client/shared/ui/Loading/barCount.js +11 -0
- package/dist/client/shared/ui/Loading/styles.module.scss +20 -27
- package/dist/client/shared/ui/Popup/Popup.js +3 -0
- package/dist/client/shared/ui/Select/Select.js +1 -1
- package/dist/client/shared/ui/form/FormSelect/FormSelect.js +1 -0
- package/dist/client/widgets/bulk-translation-dashboard/ui/BulkTranslationDashboard.js +37 -17
- package/dist/client/widgets/bulk-translation-dashboard/ui/styles.module.scss +27 -0
- package/dist/client/widgets/translate-document/ui/TranslateDocument.js +53 -27
- package/dist/client/widgets/translate-document/ui/styles.module.scss +27 -0
- package/dist/composition/levels/useDocTranslationApi.d.ts +3 -2
- package/dist/composition/levels/useDocTranslationApi.js +5 -3
- package/dist/core/provenance/ProvenanceStore.interface.d.ts +18 -0
- package/dist/core/provenance/index.d.ts +1 -0
- package/dist/core/provenance/index.js +1 -1
- package/dist/core/provenance/staleness.d.ts +19 -0
- package/dist/core/provenance/staleness.js +21 -0
- package/dist/plugin.js +22 -74
- package/dist/server/features/createTranslationRoutes.d.ts +8 -9
- package/dist/server/features/createTranslationRoutes.js +14 -5
- package/dist/server/features/get-document-status/model.js +7 -2
- package/dist/server/features/staleness/dismissStaleness.handler.d.ts +8 -0
- package/dist/server/features/staleness/dismissStaleness.handler.js +31 -0
- package/dist/server/features/staleness/getDocumentStaleness.handler.d.ts +12 -0
- package/dist/server/features/staleness/getDocumentStaleness.handler.js +43 -0
- package/dist/server/features/staleness/index.d.ts +2 -0
- package/dist/server/features/staleness/index.js +3 -0
- package/dist/server/features/staleness/model.d.ts +40 -0
- package/dist/server/features/staleness/model.js +17 -0
- package/dist/server/features/staleness/route.d.ts +7 -0
- package/dist/server/features/staleness/route.js +21 -0
- package/dist/server/features/translate-document/handler.d.ts +6 -8
- package/dist/server/features/translate-document/handler.js +21 -33
- package/dist/server/features/translate-document/index.d.ts +1 -0
- package/dist/server/features/translate-document/index.js +1 -0
- package/dist/server/features/translate-document/wireTranslateRunner.d.ts +28 -0
- package/dist/server/features/translate-document/wireTranslateRunner.js +49 -0
- package/dist/server/modules/provenance/{provenanceCollection.d.ts → Provenance.collection.d.ts} +17 -9
- package/dist/server/modules/provenance/{provenanceCollection.js → Provenance.collection.js} +23 -9
- package/dist/server/modules/provenance/Provenance.service.d.ts +56 -0
- package/dist/server/modules/provenance/Provenance.service.js +124 -0
- package/dist/server/modules/provenance/Provenance.shapes.d.ts +26 -0
- package/dist/server/modules/provenance/Provenance.shapes.js +7 -0
- package/dist/server/modules/provenance/{PayloadProvenanceStore.d.ts → Provenance.store.d.ts} +2 -0
- package/dist/server/modules/provenance/{PayloadProvenanceStore.js → Provenance.store.js} +38 -15
- package/dist/server/modules/provenance/Provenance.wiring.d.ts +23 -0
- package/dist/server/modules/provenance/Provenance.wiring.js +43 -0
- package/dist/server/modules/provenance/{provenanceCleanupHook.d.ts → ProvenanceCleanup.hook.d.ts} +4 -3
- package/dist/server/modules/provenance/{provenanceCleanupHook.js → ProvenanceCleanup.hook.js} +1 -1
- package/dist/server/modules/provenance/index.d.ts +8 -4
- package/dist/server/modules/provenance/index.js +6 -4
- package/dist/server/modules/translation-levels/PluginConfigBuilder.d.ts +6 -11
- package/dist/server/modules/translation-levels/PluginConfigBuilder.js +2 -0
- package/dist/server/modules/translation-levels/index.d.ts +1 -1
- package/dist/server/modules/translation-levels/types.d.ts +20 -9
- package/dist/server/shared/http/index.d.ts +1 -0
- package/dist/server/shared/http/index.js +1 -0
- package/dist/server/shared/http/toClientErrorMessage.d.ts +15 -0
- package/dist/server/shared/http/toClientErrorMessage.js +26 -0
- package/dist/server/shared/index.d.ts +1 -1
- package/dist/server/shared/index.js +1 -1
- package/dist/server/shared/payload/sourceDocument.d.ts +8 -0
- package/dist/server/shared/payload/sourceDocument.js +15 -0
- package/dist/types/ConfigModifier.d.ts +10 -0
- package/dist/types/ConfigModifier.js +10 -0
- package/package.json +1 -1
- package/dist/client/entities/translation/ui/CompletedTranslationStatus/CompletedTranslationStatus.d.ts +0 -7
- package/dist/client/entities/translation/ui/CompletedTranslationStatus/CompletedTranslationStatus.js +0 -45
- package/dist/client/entities/translation/ui/CompletedTranslationStatus/index.d.ts +0 -1
- package/dist/client/entities/translation/ui/CompletedTranslationStatus/index.js +0 -3
- package/dist/client/entities/translation/ui/CompletedTranslationStatus/styles.module.scss +0 -3
- package/dist/client/entities/translation/ui/FailedTranslationStatus/FailedTranslationStatus.d.ts +0 -9
- package/dist/client/entities/translation/ui/FailedTranslationStatus/FailedTranslationStatus.js +0 -54
- package/dist/client/entities/translation/ui/FailedTranslationStatus/index.d.ts +0 -1
- package/dist/client/entities/translation/ui/FailedTranslationStatus/index.js +0 -3
- package/dist/client/entities/translation/ui/FailedTranslationStatus/styles.module.scss +0 -3
- package/dist/client/entities/translation/ui/PendingTranslationStatus/PendingTranslationStatus.d.ts +0 -11
- package/dist/client/entities/translation/ui/PendingTranslationStatus/PendingTranslationStatus.js +0 -83
- package/dist/client/entities/translation/ui/PendingTranslationStatus/index.d.ts +0 -1
- package/dist/client/entities/translation/ui/PendingTranslationStatus/index.js +0 -3
- package/dist/client/entities/translation/ui/RunningTranslationStatus/RunningTranslationStatus.d.ts +0 -10
- package/dist/client/entities/translation/ui/RunningTranslationStatus/RunningTranslationStatus.js +0 -66
- package/dist/client/entities/translation/ui/RunningTranslationStatus/index.d.ts +0 -1
- package/dist/client/entities/translation/ui/RunningTranslationStatus/index.js +0 -3
- package/dist/client/features/document-translation-progress-failed/index.d.ts +0 -1
- package/dist/client/features/document-translation-progress-failed/index.js +0 -3
- package/dist/client/features/document-translation-progress-failed/ui/DocumentTranslationProgressFailed.d.ts +0 -6
- package/dist/client/features/document-translation-progress-failed/ui/DocumentTranslationProgressFailed.js +0 -28
- package/dist/client/features/document-translation-progress-pending/index.d.ts +0 -1
- package/dist/client/features/document-translation-progress-pending/index.js +0 -3
- package/dist/client/features/document-translation-progress-pending/ui/DocumentTranslationProgressPending.d.ts +0 -6
- package/dist/client/features/document-translation-progress-pending/ui/DocumentTranslationProgressPending.js +0 -44
- package/dist/client/features/document-translation-progress-running/index.d.ts +0 -1
- package/dist/client/features/document-translation-progress-running/index.js +0 -3
- package/dist/client/features/document-translation-progress-running/ui/DocumentTranslationProgressRunning.d.ts +0 -6
- package/dist/client/features/document-translation-progress-running/ui/DocumentTranslationProgressRunning.js +0 -28
|
@@ -31,56 +31,49 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
&__bars {
|
|
34
|
-
display:
|
|
35
|
-
gap: 7px;
|
|
34
|
+
display: flex;
|
|
36
35
|
align-items: center;
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Gap tightens with size so a small control's bars don't read as scattered.
|
|
38
|
+
&_sm {
|
|
39
|
+
gap: 4px;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
&
|
|
43
|
-
|
|
42
|
+
&_md {
|
|
43
|
+
gap: 6px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&_lg {
|
|
47
|
+
gap: 7px;
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
&__bar {
|
|
48
52
|
background-color: currentColor;
|
|
53
|
+
width: 2px;
|
|
49
54
|
|
|
50
|
-
&_size-
|
|
51
|
-
height:
|
|
52
|
-
width: 2px;
|
|
55
|
+
&_size-sm {
|
|
56
|
+
height: 9px;
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
&_size-
|
|
56
|
-
height:
|
|
57
|
-
width: 2px;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
&:nth-child(1) {
|
|
61
|
-
transform: translateY(0);
|
|
62
|
-
animation: animate-bar--odd 1.25s infinite;
|
|
59
|
+
&_size-md {
|
|
60
|
+
height: 12px;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
animation: animate-bar--even 1.25s infinite;
|
|
63
|
+
&_size-lg {
|
|
64
|
+
height: 15px;
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
// Neighbouring bars swing in opposition, regardless of how many there are.
|
|
68
|
+
&:nth-child(odd) {
|
|
71
69
|
transform: translateY(0);
|
|
72
70
|
animation: animate-bar--odd 1.25s infinite;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
&:nth-child(
|
|
73
|
+
&:nth-child(even) {
|
|
76
74
|
transform: translateY(-2px);
|
|
77
75
|
animation: animate-bar--even 1.25s infinite;
|
|
78
76
|
}
|
|
79
|
-
|
|
80
|
-
&:nth-child(5) {
|
|
81
|
-
transform: translateY(0);
|
|
82
|
-
animation: animate-bar--odd 1.25s infinite;
|
|
83
|
-
}
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
&__text {
|
|
@@ -15,6 +15,9 @@ function Popup({ open, children, onOpenChange, $trigger, $align, $side, onOpenAu
|
|
|
15
15
|
sideOffset: 8,
|
|
16
16
|
side: $side,
|
|
17
17
|
align: $align,
|
|
18
|
+
// Keep a gap from the viewport edges so the popup never sticks to the screen border —
|
|
19
|
+
// Radix shifts it inward to preserve this padding.
|
|
20
|
+
collisionPadding: 12,
|
|
18
21
|
className: style.popover,
|
|
19
22
|
onOpenAutoFocus: onOpenAutoFocus,
|
|
20
23
|
children: children
|
|
@@ -3,13 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { toast, useLocale, useSelection } from "@payloadcms/ui";
|
|
4
4
|
import { SelectAllStatus } from "@payloadcms/ui/providers/Selection";
|
|
5
5
|
import { useEffect, useMemo } from "react";
|
|
6
|
-
import { TranslationsApi } from "../../../entities/translation";
|
|
6
|
+
import { deriveCollectionPanelStatus, TranslationsApi } from "../../../entities/translation";
|
|
7
7
|
import { CollectionTranslationForm, FORM_FIELDS } from "../../../features/collection-translation-form";
|
|
8
8
|
import { CollectionTranslationFormModel } from "../../../features/collection-translation-form/index.client";
|
|
9
9
|
import CollectionTranslationPopup from "../../../features/collection-translation-popup";
|
|
10
10
|
import { CollectionTranslationProgress } from "../../../features/collection-translation-progress";
|
|
11
11
|
import { handleFormError } from "../../../shared/lib/forms/handle-form-error";
|
|
12
12
|
import { useCollectionDashboardUrlParams } from "../../../shared/lib/payload/hooks/useCollectionDashboardUrlParams";
|
|
13
|
+
import styles from "./styles.module.scss";
|
|
13
14
|
export default function BulkTranslationDashboard({ hasDrafts }) {
|
|
14
15
|
const locale = useLocale();
|
|
15
16
|
const { collection } = useCollectionDashboardUrlParams();
|
|
@@ -38,7 +39,10 @@ export default function BulkTranslationDashboard({ hasDrafts }) {
|
|
|
38
39
|
});
|
|
39
40
|
const selectAll = documentsSelection.selectAll === SelectAllStatus.AllAvailable;
|
|
40
41
|
const selectedCount = selectAll ? documentsSelection.totalDocs : documentsSelection.count;
|
|
41
|
-
const
|
|
42
|
+
const status = collectionTranslationApi.data;
|
|
43
|
+
const panelStatus = deriveCollectionPanelStatus(status);
|
|
44
|
+
const hasAnyCount = !!(status && (status.completed.length || status.failed.length || status.running.length || status.pending.length));
|
|
45
|
+
const showStatus = collectionTranslationApi.isPending || hasAnyCount;
|
|
42
46
|
const handleSubmit = async (formData)=>{
|
|
43
47
|
try {
|
|
44
48
|
await queueTranslationApi.mutateAsync({
|
|
@@ -51,25 +55,41 @@ export default function BulkTranslationDashboard({ hasDrafts }) {
|
|
|
51
55
|
}
|
|
52
56
|
};
|
|
53
57
|
return /*#__PURE__*/ _jsxs(CollectionTranslationPopup, {
|
|
54
|
-
|
|
58
|
+
status: panelStatus,
|
|
55
59
|
selectedCount: selectedCount,
|
|
56
60
|
children: [
|
|
57
|
-
/*#__PURE__*/ _jsx("
|
|
58
|
-
|
|
61
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
62
|
+
className: styles.title,
|
|
63
|
+
children: "Bulk translation"
|
|
59
64
|
}),
|
|
60
|
-
/*#__PURE__*/
|
|
61
|
-
|
|
65
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
66
|
+
className: styles.section,
|
|
67
|
+
children: [
|
|
68
|
+
/*#__PURE__*/ _jsx("h5", {
|
|
69
|
+
className: styles.eyebrow,
|
|
70
|
+
children: "Translate"
|
|
71
|
+
}),
|
|
72
|
+
/*#__PURE__*/ _jsx(CollectionTranslationForm, {
|
|
73
|
+
form: form,
|
|
74
|
+
onSubmit: handleSubmit,
|
|
75
|
+
selectedCount: selectedCount,
|
|
76
|
+
hasDrafts: hasDrafts
|
|
77
|
+
})
|
|
78
|
+
]
|
|
62
79
|
}),
|
|
63
|
-
/*#__PURE__*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
showStatus && /*#__PURE__*/ _jsxs("section", {
|
|
81
|
+
className: `${styles.section} ${styles["section--status"]}`,
|
|
82
|
+
children: [
|
|
83
|
+
/*#__PURE__*/ _jsx("h5", {
|
|
84
|
+
className: styles.eyebrow,
|
|
85
|
+
children: "Status"
|
|
86
|
+
}),
|
|
87
|
+
/*#__PURE__*/ _jsx(CollectionTranslationProgress, {
|
|
88
|
+
collection: collection,
|
|
89
|
+
data: collectionTranslationApi.data,
|
|
90
|
+
isPending: collectionTranslationApi.isPending
|
|
91
|
+
})
|
|
92
|
+
]
|
|
73
93
|
})
|
|
74
94
|
]
|
|
75
95
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.title {
|
|
2
|
+
margin: 0;
|
|
3
|
+
font-size: 0.8125rem;
|
|
4
|
+
font-weight: 600;
|
|
5
|
+
color: var(--theme-text, #222);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.section {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// One eyebrow style shared by every section header, so the sections read as one system.
|
|
15
|
+
.eyebrow {
|
|
16
|
+
margin: 0;
|
|
17
|
+
font-size: 0.6875rem;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
letter-spacing: 0.04em;
|
|
21
|
+
color: var(--theme-elevation-500, #8c8c8c);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.section--status {
|
|
25
|
+
padding-top: 0.75rem;
|
|
26
|
+
border-top: 1px solid var(--theme-elevation-100, #e6e6e6);
|
|
27
|
+
}
|
|
@@ -2,20 +2,19 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { toast, useLocale } from "@payloadcms/ui";
|
|
4
4
|
import { useEffect, useMemo } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import { buildTranslationStatusRows, derivePanelStatus, TranslationsApi, TranslationStatusList } from "../../../entities/translation";
|
|
6
6
|
import { OpenDocumentTranslationPopup } from "../../../features/open-document-translation-popup";
|
|
7
7
|
import { DocumentTranslationForm, FORM_FIELDS } from "../../../features/translate-document-form";
|
|
8
8
|
import { DocumentTranslationFormModel } from "../../../features/translate-document-form/index.client";
|
|
9
9
|
import { handleFormError } from "../../../shared/lib/forms/handle-form-error";
|
|
10
10
|
import { useCollectionDocumentUrlParams } from "../../../shared/lib/payload/hooks/useCollectionDocumentUrlParams";
|
|
11
|
-
import
|
|
12
|
-
import { DocumentTranslationProgressRunning } from "../../../features/document-translation-progress-running";
|
|
13
|
-
import { DocumentTranslationProgressPending } from "../../../features/document-translation-progress-pending";
|
|
11
|
+
import styles from "./styles.module.scss";
|
|
14
12
|
const TranslateDocument = ({ hasDrafts })=>{
|
|
15
13
|
const locale = useLocale();
|
|
16
14
|
const params = useCollectionDocumentUrlParams();
|
|
17
15
|
const queueTranslationApi = TranslationsApi.useQueueDocumentTranslation();
|
|
18
16
|
const { data, error, isLoading } = TranslationsApi.useDocumentTranslation(params);
|
|
17
|
+
const { data: staleness } = TranslationsApi.useDocumentStaleness(params);
|
|
19
18
|
useEffect(()=>{
|
|
20
19
|
if (error) {
|
|
21
20
|
toast.error(error.message);
|
|
@@ -23,6 +22,24 @@ const TranslateDocument = ({ hasDrafts })=>{
|
|
|
23
22
|
}, [
|
|
24
23
|
error
|
|
25
24
|
]);
|
|
25
|
+
const staleLocales = useMemo(()=>(staleness?.locales ?? []).filter((l)=>l.is_stale).map((l)=>l.target_lng), [
|
|
26
|
+
staleness
|
|
27
|
+
]);
|
|
28
|
+
// The panel trigger shows one aggregate marker; the popup shows the full per-locale list.
|
|
29
|
+
const panelStatus = useMemo(()=>derivePanelStatus({
|
|
30
|
+
runStatus: data?.status,
|
|
31
|
+
staleLocales
|
|
32
|
+
}), [
|
|
33
|
+
data?.status,
|
|
34
|
+
staleLocales
|
|
35
|
+
]);
|
|
36
|
+
const statusRows = useMemo(()=>buildTranslationStatusRows({
|
|
37
|
+
staleness,
|
|
38
|
+
run: data
|
|
39
|
+
}), [
|
|
40
|
+
staleness,
|
|
41
|
+
data
|
|
42
|
+
]);
|
|
26
43
|
const initialValues = useMemo(()=>({
|
|
27
44
|
[FORM_FIELDS.SOURCE_LNG]: locale.code,
|
|
28
45
|
[FORM_FIELDS.HIDDEN_COLLECTION_SLUG]: params.collection,
|
|
@@ -48,38 +65,47 @@ const TranslateDocument = ({ hasDrafts })=>{
|
|
|
48
65
|
handleFormError(e, form);
|
|
49
66
|
}
|
|
50
67
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
// Vertical stack: title → Translate (form) → Status (unified per-locale list). No columns, no
|
|
69
|
+
// corner chip — one consistent system so the popup stays calm as the plugin grows.
|
|
70
|
+
return /*#__PURE__*/ _jsx(OpenDocumentTranslationPopup, {
|
|
71
|
+
isLoading: isLoading,
|
|
72
|
+
status: panelStatus,
|
|
73
|
+
children: ({ close })=>/*#__PURE__*/ _jsxs(_Fragment, {
|
|
74
|
+
children: [
|
|
75
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
76
|
+
className: styles.title,
|
|
77
|
+
children: "Document translation"
|
|
78
|
+
}),
|
|
79
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
80
|
+
className: styles.section,
|
|
56
81
|
children: [
|
|
57
|
-
/*#__PURE__*/ _jsx("
|
|
58
|
-
|
|
82
|
+
/*#__PURE__*/ _jsx("h5", {
|
|
83
|
+
className: styles.eyebrow,
|
|
84
|
+
children: "Translate"
|
|
59
85
|
}),
|
|
60
86
|
/*#__PURE__*/ _jsx(DocumentTranslationForm, {
|
|
61
87
|
form: form,
|
|
62
|
-
onSubmit: (
|
|
88
|
+
onSubmit: (formData)=>handleSubmit(formData, close),
|
|
63
89
|
hasDrafts: hasDrafts
|
|
64
90
|
})
|
|
65
91
|
]
|
|
92
|
+
}),
|
|
93
|
+
statusRows.length > 0 && /*#__PURE__*/ _jsxs("section", {
|
|
94
|
+
className: `${styles.section} ${styles["section--status"]}`,
|
|
95
|
+
children: [
|
|
96
|
+
/*#__PURE__*/ _jsx("h5", {
|
|
97
|
+
className: styles.eyebrow,
|
|
98
|
+
children: "Status"
|
|
99
|
+
}),
|
|
100
|
+
/*#__PURE__*/ _jsx(TranslationStatusList, {
|
|
101
|
+
rows: statusRows,
|
|
102
|
+
collection: params.collection,
|
|
103
|
+
id: params.id
|
|
104
|
+
})
|
|
105
|
+
]
|
|
66
106
|
})
|
|
67
|
-
|
|
68
|
-
data?.status === "completed" && /*#__PURE__*/ _jsx(CompletedTranslationStatus, {
|
|
69
|
-
sourceLocale: data.input.source_lng,
|
|
70
|
-
targetLocale: data.input.target_lng,
|
|
71
|
-
completed_at: data.completed_at
|
|
72
|
-
}),
|
|
73
|
-
data?.status === "failed" && /*#__PURE__*/ _jsx(DocumentTranslationProgressFailed, {
|
|
74
|
-
data: data
|
|
75
|
-
}),
|
|
76
|
-
data?.status === "running" && /*#__PURE__*/ _jsx(DocumentTranslationProgressRunning, {
|
|
77
|
-
data: data
|
|
78
|
-
}),
|
|
79
|
-
data?.status === "pending" && /*#__PURE__*/ _jsx(DocumentTranslationProgressPending, {
|
|
80
|
-
data: data
|
|
107
|
+
]
|
|
81
108
|
})
|
|
82
|
-
]
|
|
83
109
|
});
|
|
84
110
|
};
|
|
85
111
|
export default TranslateDocument;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.title {
|
|
2
|
+
margin: 0;
|
|
3
|
+
font-size: 0.8125rem;
|
|
4
|
+
font-weight: 600;
|
|
5
|
+
color: var(--theme-text, #222);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.section {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// One eyebrow style shared by every section header, so the sections read as one system.
|
|
15
|
+
.eyebrow {
|
|
16
|
+
margin: 0;
|
|
17
|
+
font-size: 0.6875rem;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
letter-spacing: 0.04em;
|
|
21
|
+
color: var(--theme-elevation-500, #8c8c8c);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.section--status {
|
|
25
|
+
padding-top: 0.75rem;
|
|
26
|
+
border-top: 1px solid var(--theme-elevation-100, #e6e6e6);
|
|
27
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { LevelContext } from "../../server/modules/translation-levels";
|
|
2
2
|
/**
|
|
3
|
-
* Contribute the runner-agnostic document-translation API — the shared
|
|
3
|
+
* Contribute the runner-agnostic document-translation API — the shared route
|
|
4
4
|
* bundle, bound to the level context's runner. Both `documentLevel` and
|
|
5
5
|
* `collectionLevel` call this; the plugin deduplicates the endpoints by method +
|
|
6
|
-
* path, so the bundle registers exactly once.
|
|
6
|
+
* path, so the bundle registers exactly once. The provenance service factory
|
|
7
|
+
* rides along for the staleness endpoints (its schema lives inside the service).
|
|
7
8
|
*/
|
|
8
9
|
export declare function useDocTranslationApi(ctx: LevelContext): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createTranslationRoutes } from "../../server/features/createTranslationRoutes";
|
|
2
2
|
/**
|
|
3
|
-
* Contribute the runner-agnostic document-translation API — the shared
|
|
3
|
+
* Contribute the runner-agnostic document-translation API — the shared route
|
|
4
4
|
* bundle, bound to the level context's runner. Both `documentLevel` and
|
|
5
5
|
* `collectionLevel` call this; the plugin deduplicates the endpoints by method +
|
|
6
|
-
* path, so the bundle registers exactly once.
|
|
6
|
+
* path, so the bundle registers exactly once. The provenance service factory
|
|
7
|
+
* rides along for the staleness endpoints (its schema lives inside the service).
|
|
7
8
|
*/ export function useDocTranslationApi(ctx) {
|
|
8
9
|
ctx.addEndpoints(createTranslationRoutes({
|
|
9
10
|
taskRunnerFactory: ctx.taskRunnerFactory,
|
|
@@ -11,7 +12,8 @@ import { createTranslationRoutes } from "../../server/features/createTranslation
|
|
|
11
12
|
availableCollections: new Set(ctx.collections.map((collection)=>collection.slug))
|
|
12
13
|
},
|
|
13
14
|
access: ctx.access,
|
|
14
|
-
basePath: ctx.basePath
|
|
15
|
+
basePath: ctx.basePath,
|
|
16
|
+
provenanceServiceFactory: ctx.provenanceServiceFactory
|
|
15
17
|
}));
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -88,6 +88,24 @@ export interface ProvenanceStore {
|
|
|
88
88
|
* @returns The stored record, or `null` if none exists.
|
|
89
89
|
*/
|
|
90
90
|
find(key: ProvenanceKey): Promise<TranslationProvenanceRecord | null>;
|
|
91
|
+
/**
|
|
92
|
+
* List every record for a document — one per translated target locale. Backs #50's per-locale
|
|
93
|
+
* staleness read for a single document panel.
|
|
94
|
+
*
|
|
95
|
+
* @param collectionSlug - Slug of the translated document's collection.
|
|
96
|
+
* @param documentId - Stringified id of the translated document.
|
|
97
|
+
* @returns All provenance receipts for the document (empty when none exist).
|
|
98
|
+
*/
|
|
99
|
+
findByDocument(collectionSlug: string, documentId: string): Promise<TranslationProvenanceRecord[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Acknowledge the current source drift for one locale without re-translating (#50's dismiss):
|
|
102
|
+
* persist `dismissedFingerprint` so the indicator hides until the source changes again. No-op if
|
|
103
|
+
* the key has no record.
|
|
104
|
+
*
|
|
105
|
+
* @param key - The `(collectionSlug, documentId, targetLocale)` identity to dismiss.
|
|
106
|
+
* @param dismissedFingerprint - The current source fingerprint being acknowledged.
|
|
107
|
+
*/
|
|
108
|
+
dismiss(key: ProvenanceKey, dismissedFingerprint: string): Promise<void>;
|
|
91
109
|
/**
|
|
92
110
|
* Delete every record for a document (all target locales). Used to cascade-clean when the source
|
|
93
111
|
* document is deleted.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TranslationProvenanceRecord } from "./ProvenanceStore.interface";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a translated locale is out of date relative to its source — the #50 rule, in one place.
|
|
4
|
+
*
|
|
5
|
+
* A record is stale when the current source fingerprint differs from the one the translation was
|
|
6
|
+
* derived from (`record.sourceFingerprint`) **and** the editor has not acknowledged that exact drift
|
|
7
|
+
* (`record.dismissedFingerprint`). Dismissing sets `dismissedFingerprint` to the current fingerprint,
|
|
8
|
+
* so the indicator hides until the source changes again (a new fingerprint no longer matches the
|
|
9
|
+
* dismissed one). `dismissedFingerprint` is `null` until the first dismiss, in which case the check
|
|
10
|
+
* reduces to a plain source-drift comparison.
|
|
11
|
+
*
|
|
12
|
+
* Pure and payload-free so the staleness contract is testable without a database; the server handler
|
|
13
|
+
* supplies `currentFingerprint` via {@link computeSourceFingerprint} on the live source document.
|
|
14
|
+
*
|
|
15
|
+
* @param record - The stored provenance receipt for one `(collection, document, targetLocale)`.
|
|
16
|
+
* @param currentFingerprint - `computeSourceFingerprint` of the source document right now.
|
|
17
|
+
* @returns `true` when the target locale is out of date and not dismissed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRecordStale(record: TranslationProvenanceRecord, currentFingerprint: string): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a translated locale is out of date relative to its source — the #50 rule, in one place.
|
|
3
|
+
*
|
|
4
|
+
* A record is stale when the current source fingerprint differs from the one the translation was
|
|
5
|
+
* derived from (`record.sourceFingerprint`) **and** the editor has not acknowledged that exact drift
|
|
6
|
+
* (`record.dismissedFingerprint`). Dismissing sets `dismissedFingerprint` to the current fingerprint,
|
|
7
|
+
* so the indicator hides until the source changes again (a new fingerprint no longer matches the
|
|
8
|
+
* dismissed one). `dismissedFingerprint` is `null` until the first dismiss, in which case the check
|
|
9
|
+
* reduces to a plain source-drift comparison.
|
|
10
|
+
*
|
|
11
|
+
* Pure and payload-free so the staleness contract is testable without a database; the server handler
|
|
12
|
+
* supplies `currentFingerprint` via {@link computeSourceFingerprint} on the live source document.
|
|
13
|
+
*
|
|
14
|
+
* @param record - The stored provenance receipt for one `(collection, document, targetLocale)`.
|
|
15
|
+
* @param currentFingerprint - `computeSourceFingerprint` of the source document right now.
|
|
16
|
+
* @returns `true` when the target locale is out of date and not dismissed.
|
|
17
|
+
*/ export function isRecordStale(record, currentFingerprint) {
|
|
18
|
+
return currentFingerprint !== record.sourceFingerprint && currentFingerprint !== record.dismissedFingerprint;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=staleness.js.map
|
package/dist/plugin.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import { CacheProviderExport } from "./client/app/cache/CacheProvider.export";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { LifecycleNotifier, taskFromHandlerInput, withQueuedNotification } from "./server/modules/lifecycle";
|
|
2
|
+
import { configureProvenance } from "./server/modules/provenance";
|
|
3
|
+
import { wireTranslateRunner } from "./server/features/translate-document";
|
|
5
4
|
import { documentLevel, collectionLevel } from "./composition/levels";
|
|
6
5
|
import { PluginConfigBuilder } from "./server/modules/translation-levels/PluginConfigBuilder";
|
|
7
6
|
import { normalizePath } from "./server/shared";
|
|
8
|
-
/**
|
|
9
|
-
* Resolve the opt-in `provenance` config to a sidecar slug, or `null` when disabled.
|
|
10
|
-
* `false`/omitted → off; `true` or `{}` → on with the default slug; `{ slug }` → on with that slug.
|
|
11
|
-
*/ function resolveProvenanceSlug(provenance) {
|
|
12
|
-
if (!provenance) return null;
|
|
13
|
-
if (provenance === true) return DEFAULT_PROVENANCE_SLUG;
|
|
14
|
-
// `||` (not `??`) so an empty/blank slug falls back to the default instead of silently disabling.
|
|
15
|
-
return provenance.slug || DEFAULT_PROVENANCE_SLUG;
|
|
16
|
-
}
|
|
17
7
|
/** @deprecated Use `translatorPlugin` function instead */ export class TranslateCollectionPlugin {
|
|
18
8
|
pluginConfig;
|
|
19
9
|
constructor(pluginConfig){
|
|
@@ -22,61 +12,30 @@ import { normalizePath } from "./server/shared";
|
|
|
22
12
|
init() {
|
|
23
13
|
return async (config)=>{
|
|
24
14
|
const { access, translationProvider, runner, collections, levels, provenance, lifecycle, basePath: rawBasePath = "/translate" } = this.pluginConfig;
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// to make the contract explicit and avoid reliance on JSON round-trip.
|
|
15
|
+
// Build schema map from deep-cloned collections.
|
|
16
|
+
// Deep clone is required because Payload mutates the original collection objects, removing
|
|
17
|
+
// `localized: true` from nested fields during sanitization. JSON round-trip (not
|
|
18
|
+
// structuredClone) because Lexical editor configs contain async functions structuredClone
|
|
19
|
+
// cannot handle.
|
|
20
|
+
// TODO: Consider introducing a FieldLike interface with only the properties used by the
|
|
21
|
+
// pipeline (name, type, localized, fields, blocks, tabs, custom) to make the contract explicit
|
|
22
|
+
// and avoid reliance on JSON round-trip.
|
|
34
23
|
const schemaMap = new Map(collections.map((col)=>[
|
|
35
24
|
col.slug,
|
|
36
25
|
JSON.parse(JSON.stringify(col.fields))
|
|
37
26
|
]));
|
|
38
27
|
const collectionSlugs = new Set(schemaMap.keys());
|
|
39
28
|
const basePath = normalizePath(rawBasePath);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
handler: async (payload, input)=>{
|
|
49
|
-
const notifier = new LifecycleNotifier(lifecycleCallbacks, payload.logger);
|
|
50
|
-
const task = taskFromHandlerInput(input);
|
|
51
|
-
try {
|
|
52
|
-
await translateHandler.handle(payload, {
|
|
53
|
-
collection: input.collection,
|
|
54
|
-
collectionId: input.collectionId,
|
|
55
|
-
sourceLng: input.sourceLng,
|
|
56
|
-
targetLng: input.targetLng,
|
|
57
|
-
strategy: input.strategy,
|
|
58
|
-
publishOnTranslation: input.publishOnTranslation
|
|
59
|
-
});
|
|
60
|
-
} catch (error) {
|
|
61
|
-
await notifier.failed(task, error);
|
|
62
|
-
throw error; // rethrow so the runner marks the job failed
|
|
63
|
-
}
|
|
64
|
-
await notifier.completed(task);
|
|
65
|
-
},
|
|
29
|
+
// Each concern owns its own config-time wiring and exposes it uniformly; init() just composes.
|
|
30
|
+
const provenanceModule = configureProvenance(provenance, schemaMap);
|
|
31
|
+
const { taskRunnerFactory, configModifier: runnerConfigModifier } = wireTranslateRunner({
|
|
32
|
+
translationProvider,
|
|
33
|
+
schemaMap,
|
|
34
|
+
provenanceServiceFactory: provenanceModule.serviceFactory,
|
|
35
|
+
runner,
|
|
36
|
+
lifecycle: lifecycle ?? {},
|
|
66
37
|
collections: Array.from(collectionSlugs)
|
|
67
|
-
};
|
|
68
|
-
const runnerConfigModifier = runner.configure(runnerContext);
|
|
69
|
-
// Bind the context once so routes receive a self-sufficient factory: the
|
|
70
|
-
// runner needs no mutable per-instance handler state and create() has no
|
|
71
|
-
// "configure() must run first" ordering coupling (translator plan, 0c).
|
|
72
|
-
// When an `onQueued` callback is set, decorate the runner so `enqueue` fires it.
|
|
73
|
-
const taskRunnerFactory = {
|
|
74
|
-
create: (payload)=>{
|
|
75
|
-
const taskRunner = runner.create(payload, runnerContext.handler);
|
|
76
|
-
if (!lifecycleCallbacks.onQueued) return taskRunner;
|
|
77
|
-
return withQueuedNotification(taskRunner, new LifecycleNotifier(lifecycleCallbacks, payload.logger));
|
|
78
|
-
}
|
|
79
|
-
};
|
|
38
|
+
});
|
|
80
39
|
const activeLevels = levels ?? [
|
|
81
40
|
documentLevel(),
|
|
82
41
|
collectionLevel()
|
|
@@ -87,23 +46,12 @@ import { normalizePath } from "./server/shared";
|
|
|
87
46
|
access,
|
|
88
47
|
taskRunnerFactory,
|
|
89
48
|
schemaMap,
|
|
90
|
-
translationProvider
|
|
49
|
+
translationProvider,
|
|
50
|
+
provenanceServiceFactory: provenanceModule.serviceFactory
|
|
91
51
|
});
|
|
92
52
|
for (const level of activeLevels)level.extend(builder);
|
|
93
53
|
builder.addConfigModifier(runnerConfigModifier);
|
|
94
|
-
|
|
95
|
-
builder.addConfigModifier((cfg)=>{
|
|
96
|
-
const alreadyAdded = cfg.collections?.some((collection)=>collection.slug === provenanceSlug && isProvenanceCollection(collection));
|
|
97
|
-
if (!alreadyAdded) {
|
|
98
|
-
cfg.collections = [
|
|
99
|
-
...cfg.collections ?? [],
|
|
100
|
-
makeProvenanceCollection(provenanceSlug)
|
|
101
|
-
];
|
|
102
|
-
}
|
|
103
|
-
injectProvenanceCleanup(cfg, collectionSlugs, provenanceStoreFactory, provenanceSlug);
|
|
104
|
-
return cfg;
|
|
105
|
-
});
|
|
106
|
-
}
|
|
54
|
+
builder.addConfigModifier(provenanceModule.configure(collectionSlugs));
|
|
107
55
|
builder.addAdminProvider(new CacheProviderExport(basePath));
|
|
108
56
|
// The single place the Payload config is mutated.
|
|
109
57
|
return builder.applyTo(config);
|