@purposeinplay/payload-ai-translate 0.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/LICENSE +21 -0
- package/README.md +714 -0
- package/dist/alerts-collection.d.ts +21 -0
- package/dist/alerts-collection.js +159 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.js +918 -0
- package/dist/bulk-translate-batches-collection.d.ts +29 -0
- package/dist/bulk-translate-batches-collection.js +404 -0
- package/dist/bulk-translate-units-collection.d.ts +35 -0
- package/dist/bulk-translate-units-collection.js +310 -0
- package/dist/client/estimated-cost-cell.d.ts +6 -0
- package/dist/client/estimated-cost-cell.js +12 -0
- package/dist/client/excluded-fields-field.d.ts +45 -0
- package/dist/client/excluded-fields-field.js +553 -0
- package/dist/client/field-translate-button.d.ts +6 -0
- package/dist/client/field-translate-button.js +199 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +6 -0
- package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
- package/dist/client/lib/use-global-kill-switches.js +58 -0
- package/dist/client/translate-button.d.ts +2 -0
- package/dist/client/translate-button.js +228 -0
- package/dist/client/translate-modal.d.ts +16 -0
- package/dist/client/translate-modal.js +549 -0
- package/dist/client/translation-progress.d.ts +10 -0
- package/dist/client/translation-progress.js +297 -0
- package/dist/components/TranslationNavGroup.d.ts +45 -0
- package/dist/components/TranslationNavGroup.js +104 -0
- package/dist/defaults.d.ts +11 -0
- package/dist/defaults.js +16 -0
- package/dist/endpoints/client-config.d.ts +44 -0
- package/dist/endpoints/client-config.js +145 -0
- package/dist/endpoints/estimate.d.ts +5 -0
- package/dist/endpoints/estimate.js +237 -0
- package/dist/endpoints/progress.d.ts +2 -0
- package/dist/endpoints/progress.js +314 -0
- package/dist/endpoints/translate.d.ts +11 -0
- package/dist/endpoints/translate.js +376 -0
- package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
- package/dist/endpoints/translation-hub/_helpers.js +297 -0
- package/dist/endpoints/translation-hub/active.d.ts +21 -0
- package/dist/endpoints/translation-hub/active.js +220 -0
- package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
- package/dist/endpoints/translation-hub/cancel.js +233 -0
- package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
- package/dist/endpoints/translation-hub/enqueue.js +529 -0
- package/dist/endpoints/translation-hub/failures.d.ts +12 -0
- package/dist/endpoints/translation-hub/failures.js +67 -0
- package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
- package/dist/endpoints/translation-hub/force-reset.js +144 -0
- package/dist/endpoints/translation-hub/index.d.ts +21 -0
- package/dist/endpoints/translation-hub/index.js +20 -0
- package/dist/endpoints/translation-hub/list.d.ts +40 -0
- package/dist/endpoints/translation-hub/list.js +182 -0
- package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
- package/dist/endpoints/translation-hub/preflight.js +141 -0
- package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
- package/dist/endpoints/translation-hub/retry-failed.js +235 -0
- package/dist/endpoints/translation-hub/revert.d.ts +88 -0
- package/dist/endpoints/translation-hub/revert.js +405 -0
- package/dist/endpoints/translation-hub/status.d.ts +45 -0
- package/dist/endpoints/translation-hub/status.js +391 -0
- package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
- package/dist/endpoints/translation-hub/usage-summary.js +481 -0
- package/dist/exports/client.d.ts +6 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/components.d.ts +6 -0
- package/dist/exports/components.js +5 -0
- package/dist/exports/index.d.ts +8 -0
- package/dist/exports/index.js +7 -0
- package/dist/exports/providers.d.ts +9 -0
- package/dist/exports/providers.js +5 -0
- package/dist/exports/views-client.d.ts +23 -0
- package/dist/exports/views-client.js +22 -0
- package/dist/exports/views.d.ts +30 -0
- package/dist/exports/views.js +29 -0
- package/dist/hooks/after-change-global.d.ts +4 -0
- package/dist/hooks/after-change-global.js +109 -0
- package/dist/hooks/after-change.d.ts +16 -0
- package/dist/hooks/after-change.js +205 -0
- package/dist/hooks/after-delete.d.ts +30 -0
- package/dist/hooks/after-delete.js +95 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/jobs-collection.d.ts +17 -0
- package/dist/jobs-collection.js +139 -0
- package/dist/lexical/classifier.d.ts +3 -0
- package/dist/lexical/classifier.js +108 -0
- package/dist/lexical/deserializer.d.ts +4 -0
- package/dist/lexical/deserializer.js +263 -0
- package/dist/lexical/placeholder-integrity.d.ts +6 -0
- package/dist/lexical/placeholder-integrity.js +21 -0
- package/dist/lexical/placeholders.d.ts +21 -0
- package/dist/lexical/placeholders.js +117 -0
- package/dist/lexical/serializer.d.ts +21 -0
- package/dist/lexical/serializer.js +233 -0
- package/dist/lexical/types.d.ts +32 -0
- package/dist/lexical/types.js +1 -0
- package/dist/lib/auth-diagnostics.d.ts +14 -0
- package/dist/lib/auth-diagnostics.js +19 -0
- package/dist/lib/batch-counts.d.ts +58 -0
- package/dist/lib/batch-counts.js +105 -0
- package/dist/lib/bulk-translate-migrations.d.ts +92 -0
- package/dist/lib/bulk-translate-migrations.js +153 -0
- package/dist/lib/coalescing-queue.d.ts +38 -0
- package/dist/lib/coalescing-queue.js +69 -0
- package/dist/lib/content-extractor.d.ts +16 -0
- package/dist/lib/content-extractor.js +410 -0
- package/dist/lib/content-hash.d.ts +1 -0
- package/dist/lib/content-hash.js +19 -0
- package/dist/lib/content-patcher.d.ts +15 -0
- package/dist/lib/content-patcher.js +293 -0
- package/dist/lib/cost-guards.d.ts +2 -0
- package/dist/lib/cost-guards.js +18 -0
- package/dist/lib/daily-spend-cap.d.ts +58 -0
- package/dist/lib/daily-spend-cap.js +233 -0
- package/dist/lib/effective-locales.d.ts +181 -0
- package/dist/lib/effective-locales.js +302 -0
- package/dist/lib/error-messages.d.ts +245 -0
- package/dist/lib/error-messages.js +626 -0
- package/dist/lib/events.d.ts +39 -0
- package/dist/lib/events.js +146 -0
- package/dist/lib/exclude-fields.d.ts +3 -0
- package/dist/lib/exclude-fields.js +64 -0
- package/dist/lib/field-breadcrumb.d.ts +31 -0
- package/dist/lib/field-breadcrumb.js +227 -0
- package/dist/lib/field-diff.d.ts +1 -0
- package/dist/lib/field-diff.js +25 -0
- package/dist/lib/field-empty.d.ts +2 -0
- package/dist/lib/field-empty.js +68 -0
- package/dist/lib/field-resolver.d.ts +3 -0
- package/dist/lib/field-resolver.js +164 -0
- package/dist/lib/group-soft-skips.d.ts +39 -0
- package/dist/lib/group-soft-skips.js +45 -0
- package/dist/lib/locale-merge.d.ts +44 -0
- package/dist/lib/locale-merge.js +357 -0
- package/dist/lib/locale-row-check.d.ts +30 -0
- package/dist/lib/locale-row-check.js +64 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +97 -0
- package/dist/lib/manual-edit-guard.d.ts +128 -0
- package/dist/lib/manual-edit-guard.js +393 -0
- package/dist/lib/output-validation.d.ts +48 -0
- package/dist/lib/output-validation.js +148 -0
- package/dist/lib/payload-read.d.ts +16 -0
- package/dist/lib/payload-read.js +51 -0
- package/dist/lib/per-doc-claim.d.ts +90 -0
- package/dist/lib/per-doc-claim.js +140 -0
- package/dist/lib/per-doc-lock.d.ts +94 -0
- package/dist/lib/per-doc-lock.js +119 -0
- package/dist/lib/persist-usage.d.ts +91 -0
- package/dist/lib/persist-usage.js +116 -0
- package/dist/lib/progress-store.d.ts +103 -0
- package/dist/lib/progress-store.js +314 -0
- package/dist/lib/rate-limiter.d.ts +3 -0
- package/dist/lib/rate-limiter.js +53 -0
- package/dist/lib/snapshot-select.d.ts +43 -0
- package/dist/lib/snapshot-select.js +108 -0
- package/dist/lib/translate-prompt.d.ts +31 -0
- package/dist/lib/translate-prompt.js +66 -0
- package/dist/lib/translation-token-bucket.d.ts +57 -0
- package/dist/lib/translation-token-bucket.js +365 -0
- package/dist/lib/truncate-source-value.d.ts +1 -0
- package/dist/lib/truncate-source-value.js +27 -0
- package/dist/manual-edit-collection.d.ts +22 -0
- package/dist/manual-edit-collection.js +124 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +934 -0
- package/dist/providers/ai-sdk-adapter.d.ts +35 -0
- package/dist/providers/ai-sdk-adapter.js +100 -0
- package/dist/providers/anthropic.d.ts +31 -0
- package/dist/providers/anthropic.js +66 -0
- package/dist/providers/custom.d.ts +36 -0
- package/dist/providers/custom.js +24 -0
- package/dist/providers/gemini.d.ts +20 -0
- package/dist/providers/gemini.js +48 -0
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.js +29 -0
- package/dist/providers/openai.d.ts +28 -0
- package/dist/providers/openai.js +69 -0
- package/dist/settings-global.d.ts +74 -0
- package/dist/settings-global.js +216 -0
- package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
- package/dist/tasks/bulk-translate-coordinator.js +708 -0
- package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
- package/dist/tasks/bulk-translate-doc-task.js +1000 -0
- package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
- package/dist/tasks/bulk-translate-janitor.js +311 -0
- package/dist/tasks/translate-job-task.d.ts +51 -0
- package/dist/tasks/translate-job-task.js +154 -0
- package/dist/translate.d.ts +113 -0
- package/dist/translate.js +911 -0
- package/dist/translation-daily-spend-collection.d.ts +24 -0
- package/dist/translation-daily-spend-collection.js +133 -0
- package/dist/translation-rate-limits-collection.d.ts +30 -0
- package/dist/translation-rate-limits-collection.js +144 -0
- package/dist/types.d.ts +672 -0
- package/dist/types.js +1 -0
- package/dist/usage-collection.d.ts +14 -0
- package/dist/usage-collection.js +377 -0
- package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
- package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
- package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
- package/dist/views/BulkRunsHub/BucketRow.js +982 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
- package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
- package/dist/views/BulkRunsHub/EmptyState.js +64 -0
- package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
- package/dist/views/BulkRunsHub/FilterBar.js +284 -0
- package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
- package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
- package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
- package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
- package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
- package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
- package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
- package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
- package/dist/views/BulkRunsHub/index.d.ts +17 -0
- package/dist/views/BulkRunsHub/index.js +80 -0
- package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
- package/dist/views/BulkRunsHub/urlFilters.js +50 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
- package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
- package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
- package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
- package/dist/views/TranslationHub/ActiveJobs.js +320 -0
- package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
- package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
- package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
- package/dist/views/TranslationHub/AlertBanner.js +568 -0
- package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
- package/dist/views/TranslationHub/AuditPanel.js +1367 -0
- package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
- package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
- package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
- package/dist/views/TranslationHub/Hub.client.js +357 -0
- package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
- package/dist/views/TranslationHub/ModelCombobox.js +415 -0
- package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
- package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
- package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
- package/dist/views/TranslationHub/SettingsRail.js +382 -0
- package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
- package/dist/views/TranslationHub/StatusStrip.js +451 -0
- package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
- package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
- package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
- package/dist/views/TranslationHub/UsageTable.js +1240 -0
- package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
- package/dist/views/TranslationHub/alertGrouping.js +99 -0
- package/dist/views/TranslationHub/index.d.ts +20 -0
- package/dist/views/TranslationHub/index.js +109 -0
- package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
- package/dist/views/TranslationHub/tabNavigation.js +74 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
- package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
- package/dist/views/TranslationHub/useFocusTrap.js +81 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
- package/dist/views/shared/EditorError.d.ts +97 -0
- package/dist/views/shared/EditorError.js +205 -0
- package/dist/views/shared/ModelCell.d.ts +18 -0
- package/dist/views/shared/ModelCell.js +31 -0
- package/dist/views/shared/docHref.d.ts +16 -0
- package/dist/views/shared/docHref.js +26 -0
- package/dist/views/shared/fetch-error-body.d.ts +25 -0
- package/dist/views/shared/fetch-error-body.js +42 -0
- package/dist/views/shared/filterPillStyle.d.ts +35 -0
- package/dist/views/shared/filterPillStyle.js +40 -0
- package/dist/views/shared/format.d.ts +75 -0
- package/dist/views/shared/format.js +131 -0
- package/package.json +141 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the bulk-translate UI surfaces.
|
|
3
|
+
*
|
|
4
|
+
* These mirror the response shapes documented in
|
|
5
|
+
* `Plan-2026-05-27-bulk-translate.md` for the
|
|
6
|
+
* `/api/translation-hub/bulk-translate*` endpoints. The endpoints are
|
|
7
|
+
* implemented in parallel; until they land each consumer should treat a
|
|
8
|
+
* 404 as "no active batch" / "preflight unavailable".
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Terminal vs non-terminal status helpers — kept as exported constants
|
|
12
|
+
* so the UI logic can be tested in isolation without re-deriving the
|
|
13
|
+
* lifecycle anywhere.
|
|
14
|
+
*/
|
|
15
|
+
export declare const BULK_TRANSLATE_TERMINAL_STATUSES: readonly ["success", "failed", "partial", "reverted", "cancelled"];
|
|
16
|
+
export declare const BULK_TRANSLATE_ACTIVE_STATUSES: readonly ["queued", "running", "cancelling"];
|
|
17
|
+
export type BulkTranslateBatchStatus = (typeof BULK_TRANSLATE_ACTIVE_STATUSES)[number] | (typeof BULK_TRANSLATE_TERMINAL_STATUSES)[number];
|
|
18
|
+
export declare function isTerminalStatus(s: BulkTranslateBatchStatus | undefined | null): boolean;
|
|
19
|
+
export declare function isActiveStatus(s: BulkTranslateBatchStatus | undefined | null): boolean;
|
|
20
|
+
/** Per-collection progress row used by the in-flight monitor. */
|
|
21
|
+
export interface BulkTranslateCollectionProgress {
|
|
22
|
+
slug: string;
|
|
23
|
+
label: string;
|
|
24
|
+
total: number;
|
|
25
|
+
completed: number;
|
|
26
|
+
failed: number;
|
|
27
|
+
}
|
|
28
|
+
/** Response of GET /api/translation-hub/bulk-translate/active. */
|
|
29
|
+
export interface BulkTranslateActiveResponse {
|
|
30
|
+
/** Null when no active or recently-terminal batch exists. */
|
|
31
|
+
batch: BulkTranslateBatchSummary | null;
|
|
32
|
+
}
|
|
33
|
+
export interface BulkTranslateBatchSummary {
|
|
34
|
+
id: string;
|
|
35
|
+
status: BulkTranslateBatchStatus;
|
|
36
|
+
mode: 'changed' | 'force' | 'canary';
|
|
37
|
+
totalUnits: number;
|
|
38
|
+
completedUnits: number;
|
|
39
|
+
failedUnits: number;
|
|
40
|
+
estimatedCostUsd?: number;
|
|
41
|
+
actualCostUsd?: number;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
startedAt?: string | null;
|
|
44
|
+
completedAt?: string | null;
|
|
45
|
+
/** P50 ETA in seconds — backend computes from running batch metrics. */
|
|
46
|
+
etaSeconds?: number | null;
|
|
47
|
+
collections: BulkTranslateCollectionProgress[];
|
|
48
|
+
/** Set on the batch row 24h after completion to gate the Revert action. */
|
|
49
|
+
revertExpiresAt?: string | null;
|
|
50
|
+
revertedAt?: string | null;
|
|
51
|
+
triggeredByEmail?: string | null;
|
|
52
|
+
/** 1.2.8: stringified user id — drives the editor ownership chip. */
|
|
53
|
+
triggeredByUserId?: string | null;
|
|
54
|
+
}
|
|
55
|
+
/** Response of GET /api/translation-hub/bulk-translate/preflight. */
|
|
56
|
+
export interface BulkTranslatePreflightResponse {
|
|
57
|
+
scope: {
|
|
58
|
+
collections: string[];
|
|
59
|
+
globals: string[];
|
|
60
|
+
locales: string[];
|
|
61
|
+
sourceLocale: string;
|
|
62
|
+
};
|
|
63
|
+
documents: {
|
|
64
|
+
/** Total documents that WOULD be enqueued (after diff-skip if mode=changed). */
|
|
65
|
+
total: number;
|
|
66
|
+
/** Per-collection breakdown (label + counts). */
|
|
67
|
+
perCollection: Array<{
|
|
68
|
+
slug: string;
|
|
69
|
+
label: string;
|
|
70
|
+
changed: number;
|
|
71
|
+
total: number;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Best-effort cost estimate. May be `undefined` during the cold-cache
|
|
76
|
+
* window — UI shows skeleton + 10s retry CTA in that state.
|
|
77
|
+
*/
|
|
78
|
+
estimatedCostUsd?: number;
|
|
79
|
+
/** USD cap state at the moment preflight ran. */
|
|
80
|
+
dailySpend: {
|
|
81
|
+
spentUsd: number;
|
|
82
|
+
capUsd: number;
|
|
83
|
+
/** ISO string of next reset (midnight UTC). */
|
|
84
|
+
resetsAt: string;
|
|
85
|
+
};
|
|
86
|
+
/** Whether the consumer's plugin config has `bulk.requireTotp: true`. */
|
|
87
|
+
requireTotp: boolean;
|
|
88
|
+
/** Whether the requesting user has TOTP enrolled (for fallback UX). */
|
|
89
|
+
totpEnrolled: boolean;
|
|
90
|
+
}
|
|
91
|
+
/** Response of GET /api/translation-hub/bulk-translate/:id/failures. */
|
|
92
|
+
export interface BulkTranslateFailureRow {
|
|
93
|
+
unitId: string;
|
|
94
|
+
collection: string;
|
|
95
|
+
documentId: string;
|
|
96
|
+
documentTitle?: string | null;
|
|
97
|
+
locale: string;
|
|
98
|
+
failureCode?: string | null;
|
|
99
|
+
failureMessage: string;
|
|
100
|
+
attempts: number;
|
|
101
|
+
}
|
|
102
|
+
/** Response of GET /api/translation-hub/bulk-translate (list endpoint). */
|
|
103
|
+
export interface BulkTranslateListResponse {
|
|
104
|
+
batches: BulkTranslateBatchSummary[];
|
|
105
|
+
nextCursor: string | null;
|
|
106
|
+
total: number;
|
|
107
|
+
}
|
|
108
|
+
/** Per-unit row returned by GET /api/translation-hub/bulk-translate/:id/status */
|
|
109
|
+
export interface BatchJobSummary {
|
|
110
|
+
unitId: string;
|
|
111
|
+
collection: string;
|
|
112
|
+
documentId: string;
|
|
113
|
+
locale: string;
|
|
114
|
+
/** Unit-level status — distinct from batch-level status. */
|
|
115
|
+
status: 'pending' | 'running' | 'success' | 'failed' | 'skipped' | 'reverted';
|
|
116
|
+
attempts: number;
|
|
117
|
+
failureCode: string | null;
|
|
118
|
+
failureMessage: string | null;
|
|
119
|
+
costUsd: number;
|
|
120
|
+
startedAt: string | null;
|
|
121
|
+
completedAt: string | null;
|
|
122
|
+
/**
|
|
123
|
+
* AI call time in milliseconds — excludes throttle wait. UI renders
|
|
124
|
+
* this as the per-unit "Duration" column. `null` on legacy rows.
|
|
125
|
+
*/
|
|
126
|
+
processingDurationMs: number | null;
|
|
127
|
+
}
|
|
128
|
+
/** Unit-level count breakdown in the status drill-down response. */
|
|
129
|
+
export interface BatchUnitCounts {
|
|
130
|
+
total: number;
|
|
131
|
+
pending: number;
|
|
132
|
+
running: number;
|
|
133
|
+
completed: number;
|
|
134
|
+
failed: number;
|
|
135
|
+
skipped: number;
|
|
136
|
+
reverted: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Per-collection aggregate counts. Keyed by collection / global slug.
|
|
140
|
+
* Driven by the tree-grouped DrillDown — bucket headers render from
|
|
141
|
+
* these totals, not from the paginated `jobs` page (which would only
|
|
142
|
+
* reflect "how many of the loaded units are in this bucket").
|
|
143
|
+
*/
|
|
144
|
+
export interface BatchUnitCountsByCollection {
|
|
145
|
+
[collection: string]: {
|
|
146
|
+
isGlobal: boolean;
|
|
147
|
+
total: number;
|
|
148
|
+
pending: number;
|
|
149
|
+
running: number;
|
|
150
|
+
succeeded: number;
|
|
151
|
+
failed: number;
|
|
152
|
+
skipped: number;
|
|
153
|
+
reverted: number;
|
|
154
|
+
/**
|
|
155
|
+
* v1.2.7: filter-aware doc counts per bucket. Keyed by the UI filter
|
|
156
|
+
* chip name (`all`, `pending`, `running`, `completed`, `failed`,
|
|
157
|
+
* `skipped`, `reverted`). Each value is the number of DISTINCT
|
|
158
|
+
* documents in this bucket that have at least one unit in the
|
|
159
|
+
* matching state. A doc with one failed + one succeeded locale shows
|
|
160
|
+
* in BOTH `failed` and `completed`. `all === totalDocs`. Drives the
|
|
161
|
+
* bucket-header count when an active filter pill is set.
|
|
162
|
+
*/
|
|
163
|
+
docCountsByStatus: {
|
|
164
|
+
all: number;
|
|
165
|
+
pending: number;
|
|
166
|
+
running: number;
|
|
167
|
+
completed: number;
|
|
168
|
+
failed: number;
|
|
169
|
+
skipped: number;
|
|
170
|
+
reverted: number;
|
|
171
|
+
};
|
|
172
|
+
/** Distinct documentIds in this bucket (count of unique docs). */
|
|
173
|
+
totalDocs: number;
|
|
174
|
+
/**
|
|
175
|
+
* Wall-clock from this bucket's first unit start to its last unit
|
|
176
|
+
* completion (editor-perceived "time we spent translating this
|
|
177
|
+
* collection"). Null when the bucket has no completed units yet.
|
|
178
|
+
*/
|
|
179
|
+
collectionSpanMs: number | null;
|
|
180
|
+
/**
|
|
181
|
+
* Sum of `processingDurationMs` across this bucket — pure AI
|
|
182
|
+
* compute time. Null when no v1.2.5+ units have populated the
|
|
183
|
+
* column yet.
|
|
184
|
+
*/
|
|
185
|
+
aiActiveMs: number | null;
|
|
186
|
+
/**
|
|
187
|
+
* `collectionSpanMs − aiActiveMs`, clamped ≥0. Surfaced as a
|
|
188
|
+
* paired "AI active / Queue wait" sub-line on the bucket header
|
|
189
|
+
* per UX recommendation — distinguishes "the AI was slow" from
|
|
190
|
+
* "we were rate-limited." Null when either source is null.
|
|
191
|
+
*/
|
|
192
|
+
queueWaitMs: number | null;
|
|
193
|
+
distinctFailureCodes: number;
|
|
194
|
+
topFailureCodes: Array<{
|
|
195
|
+
code: string;
|
|
196
|
+
count: number;
|
|
197
|
+
}>;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** Response of GET /api/translation-hub/bulk-translate/:id/status */
|
|
201
|
+
export interface BulkTranslateStatusResponse {
|
|
202
|
+
data: {
|
|
203
|
+
batchId: string;
|
|
204
|
+
status: string;
|
|
205
|
+
mode: string;
|
|
206
|
+
canaryLimit: number | null;
|
|
207
|
+
estimatedCostUsd: number | null;
|
|
208
|
+
actualCostUsd: number;
|
|
209
|
+
counts: BatchUnitCounts;
|
|
210
|
+
/** 1.3.0+: per-collection totals. Empty object on old responses. */
|
|
211
|
+
countsByCollection?: BatchUnitCountsByCollection;
|
|
212
|
+
jobs: BatchJobSummary[];
|
|
213
|
+
nextCursor: string | null;
|
|
214
|
+
snapshot: {
|
|
215
|
+
providerKey: string | null;
|
|
216
|
+
modelId: string | null;
|
|
217
|
+
sourceLocale: string | null;
|
|
218
|
+
};
|
|
219
|
+
timestamps: {
|
|
220
|
+
enqueuedAt: string | null;
|
|
221
|
+
startedAt: string | null;
|
|
222
|
+
completedAt: string | null;
|
|
223
|
+
cancelledAt: string | null;
|
|
224
|
+
revertedAt: string | null;
|
|
225
|
+
};
|
|
226
|
+
triggeredByUserId: string | null;
|
|
227
|
+
triggeredByEmail: string | null;
|
|
228
|
+
failures: Record<string, unknown> | null;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/** Filter state managed by the FilterBar. */
|
|
232
|
+
export interface BulkRunsFilterState {
|
|
233
|
+
/** Comma-separated status values or aliases (active/terminal). */
|
|
234
|
+
status: string;
|
|
235
|
+
mode: string;
|
|
236
|
+
triggeredBy: string;
|
|
237
|
+
since: string;
|
|
238
|
+
until: string;
|
|
239
|
+
hasFailures: boolean;
|
|
240
|
+
}
|
|
241
|
+
/** Time-range choice for the SummaryStrip. */
|
|
242
|
+
export type BulkRunsTimeRange = '7d' | '30d' | 'all';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the bulk-translate UI surfaces.
|
|
3
|
+
*
|
|
4
|
+
* These mirror the response shapes documented in
|
|
5
|
+
* `Plan-2026-05-27-bulk-translate.md` for the
|
|
6
|
+
* `/api/translation-hub/bulk-translate*` endpoints. The endpoints are
|
|
7
|
+
* implemented in parallel; until they land each consumer should treat a
|
|
8
|
+
* 404 as "no active batch" / "preflight unavailable".
|
|
9
|
+
*/ /**
|
|
10
|
+
* Terminal vs non-terminal status helpers — kept as exported constants
|
|
11
|
+
* so the UI logic can be tested in isolation without re-deriving the
|
|
12
|
+
* lifecycle anywhere.
|
|
13
|
+
*/ export const BULK_TRANSLATE_TERMINAL_STATUSES = [
|
|
14
|
+
'success',
|
|
15
|
+
'failed',
|
|
16
|
+
'partial',
|
|
17
|
+
'reverted',
|
|
18
|
+
'cancelled'
|
|
19
|
+
];
|
|
20
|
+
export const BULK_TRANSLATE_ACTIVE_STATUSES = [
|
|
21
|
+
'queued',
|
|
22
|
+
'running',
|
|
23
|
+
'cancelling'
|
|
24
|
+
];
|
|
25
|
+
export function isTerminalStatus(s) {
|
|
26
|
+
if (!s) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return BULK_TRANSLATE_TERMINAL_STATUSES.includes(s);
|
|
30
|
+
}
|
|
31
|
+
export function isActiveStatus(s) {
|
|
32
|
+
if (!s) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return BULK_TRANSLATE_ACTIVE_STATUSES.includes(s);
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { BulkTranslateCollectionProgress } from './BulkTranslate.types.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
basePath: string;
|
|
5
|
+
batchId: string;
|
|
6
|
+
/**
|
|
7
|
+
* Parent batch status. Threaded into `categorizeFailure` so a row
|
|
8
|
+
* that failed while the batch was being cancelled (NEW-12) gets the
|
|
9
|
+
* `cancelled-mid-flight` copy instead of the generic config-error
|
|
10
|
+
* misclassification.
|
|
11
|
+
*/
|
|
12
|
+
batchStatus: string;
|
|
13
|
+
collection: BulkTranslateCollectionProgress;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
}
|
|
16
|
+
/** Truncate an error message for the row display. Exported for tests. */
|
|
17
|
+
export declare function truncateError(msg: string, maxLen?: number): string;
|
|
18
|
+
export declare const BulkTranslateFailureDrawer: React.FC<Props>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { categorizeFailure } from '../../lib/error-messages.js';
|
|
5
|
+
import { docHref } from '../shared/docHref.js';
|
|
6
|
+
import { EditorErrorChip } from '../shared/EditorError.js';
|
|
7
|
+
import { readResponseError } from '../shared/fetch-error-body.js';
|
|
8
|
+
import { useFocusTrap } from './useFocusTrap.js';
|
|
9
|
+
const OVERLAY_STYLE = {
|
|
10
|
+
position: 'fixed',
|
|
11
|
+
top: 0,
|
|
12
|
+
right: 0,
|
|
13
|
+
bottom: 0,
|
|
14
|
+
width: '100%',
|
|
15
|
+
maxWidth: '480px',
|
|
16
|
+
background: 'var(--theme-elevation-0)',
|
|
17
|
+
borderLeft: '1px solid var(--theme-elevation-150)',
|
|
18
|
+
boxShadow: '-10px 0 30px rgba(0,0,0,0.15)',
|
|
19
|
+
zIndex: 200,
|
|
20
|
+
display: 'flex',
|
|
21
|
+
flexDirection: 'column'
|
|
22
|
+
};
|
|
23
|
+
const HEADER_STYLE = {
|
|
24
|
+
padding: '1rem 1.5rem',
|
|
25
|
+
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
26
|
+
display: 'flex',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
justifyContent: 'space-between'
|
|
29
|
+
};
|
|
30
|
+
const ROW_STYLE = {
|
|
31
|
+
display: 'grid',
|
|
32
|
+
gridTemplateColumns: '1fr 60px 40px',
|
|
33
|
+
gap: '0.75rem',
|
|
34
|
+
padding: '0.6rem 1.5rem',
|
|
35
|
+
borderBottom: '1px solid var(--theme-elevation-100)',
|
|
36
|
+
alignItems: 'start',
|
|
37
|
+
fontSize: '0.8125rem'
|
|
38
|
+
};
|
|
39
|
+
/** Truncate an error message for the row display. Exported for tests. */ export function truncateError(msg, maxLen = 80) {
|
|
40
|
+
if (msg.length <= maxLen) {
|
|
41
|
+
return msg;
|
|
42
|
+
}
|
|
43
|
+
return `${msg.slice(0, maxLen - 1)}…`;
|
|
44
|
+
}
|
|
45
|
+
export const BulkTranslateFailureDrawer = ({ basePath, batchId, batchStatus, collection, onClose })=>{
|
|
46
|
+
const drawerRef = useRef(null);
|
|
47
|
+
const [rows, setRows] = useState(null);
|
|
48
|
+
const [error, setError] = useState(null);
|
|
49
|
+
const [retryingAll, setRetryingAll] = useState(false);
|
|
50
|
+
const [retryingUnit, setRetryingUnit] = useState(null);
|
|
51
|
+
useFocusTrap(drawerRef, {
|
|
52
|
+
enabled: true,
|
|
53
|
+
onEscape: onClose
|
|
54
|
+
});
|
|
55
|
+
useEffect(()=>{
|
|
56
|
+
let cancelled = false;
|
|
57
|
+
(async ()=>{
|
|
58
|
+
try {
|
|
59
|
+
const params = new URLSearchParams({
|
|
60
|
+
collection: collection.slug
|
|
61
|
+
});
|
|
62
|
+
const res = await fetch(`${basePath}/api/translation-hub/bulk-translate/${batchId}/failures?${params}`, {
|
|
63
|
+
credentials: 'include'
|
|
64
|
+
});
|
|
65
|
+
if (cancelled) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
throw new Error(await readResponseError(res));
|
|
70
|
+
}
|
|
71
|
+
const json = await res.json();
|
|
72
|
+
setRows(json.rows ?? []);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
if (!cancelled) {
|
|
75
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
return ()=>{
|
|
80
|
+
cancelled = true;
|
|
81
|
+
};
|
|
82
|
+
}, [
|
|
83
|
+
basePath,
|
|
84
|
+
batchId,
|
|
85
|
+
collection.slug
|
|
86
|
+
]);
|
|
87
|
+
async function onRetryUnit(unitId) {
|
|
88
|
+
setRetryingUnit(unitId);
|
|
89
|
+
try {
|
|
90
|
+
const res = await fetch(`${basePath}/api/translation-hub/bulk-translate/${batchId}/retry`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
credentials: 'include',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json'
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
unitIds: [
|
|
98
|
+
unitId
|
|
99
|
+
]
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
throw new Error(await readResponseError(res));
|
|
104
|
+
}
|
|
105
|
+
// Optimistic remove — server will re-add via poll if it lands in
|
|
106
|
+
// failed again.
|
|
107
|
+
setRows((prev)=>(prev ?? []).filter((r)=>r.unitId !== unitId));
|
|
108
|
+
} catch (e) {
|
|
109
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
110
|
+
} finally{
|
|
111
|
+
setRetryingUnit(null);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function onRetryAll() {
|
|
115
|
+
setRetryingAll(true);
|
|
116
|
+
setError(null);
|
|
117
|
+
try {
|
|
118
|
+
const res = await fetch(`${basePath}/api/translation-hub/bulk-translate/${batchId}/retry`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
credentials: 'include',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json'
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
collection: collection.slug
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
throw new Error(await readResponseError(res));
|
|
130
|
+
}
|
|
131
|
+
setRows([]);
|
|
132
|
+
} catch (e) {
|
|
133
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
134
|
+
} finally{
|
|
135
|
+
setRetryingAll(false);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
139
|
+
"aria-label": `Failed translations for ${collection.label}`,
|
|
140
|
+
"data-testid": "bulk-translate-failure-drawer",
|
|
141
|
+
ref: drawerRef,
|
|
142
|
+
role: "dialog",
|
|
143
|
+
style: OVERLAY_STYLE,
|
|
144
|
+
children: [
|
|
145
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
146
|
+
style: HEADER_STYLE,
|
|
147
|
+
children: [
|
|
148
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ _jsxs("h3", {
|
|
151
|
+
style: {
|
|
152
|
+
margin: 0,
|
|
153
|
+
fontSize: '1rem',
|
|
154
|
+
color: 'var(--theme-elevation-1000)'
|
|
155
|
+
},
|
|
156
|
+
children: [
|
|
157
|
+
collection.label,
|
|
158
|
+
" — failed translations"
|
|
159
|
+
]
|
|
160
|
+
}),
|
|
161
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
162
|
+
style: {
|
|
163
|
+
margin: '0.15rem 0 0',
|
|
164
|
+
fontSize: '0.75rem',
|
|
165
|
+
color: 'var(--theme-elevation-500)'
|
|
166
|
+
},
|
|
167
|
+
children: [
|
|
168
|
+
collection.failed,
|
|
169
|
+
" failed · ",
|
|
170
|
+
collection.total,
|
|
171
|
+
" total"
|
|
172
|
+
]
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
}),
|
|
176
|
+
/*#__PURE__*/ _jsx("button", {
|
|
177
|
+
"aria-label": "Close failure drawer",
|
|
178
|
+
onClick: onClose,
|
|
179
|
+
style: {
|
|
180
|
+
background: 'transparent',
|
|
181
|
+
border: 'none',
|
|
182
|
+
color: 'var(--theme-elevation-700)',
|
|
183
|
+
cursor: 'pointer',
|
|
184
|
+
fontSize: '1.25rem',
|
|
185
|
+
lineHeight: 1
|
|
186
|
+
},
|
|
187
|
+
type: "button",
|
|
188
|
+
children: "×"
|
|
189
|
+
})
|
|
190
|
+
]
|
|
191
|
+
}),
|
|
192
|
+
/*#__PURE__*/ _jsx("div", {
|
|
193
|
+
style: {
|
|
194
|
+
padding: '0.75rem 1.5rem',
|
|
195
|
+
borderBottom: '1px solid var(--theme-elevation-150)'
|
|
196
|
+
},
|
|
197
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
198
|
+
"data-testid": "bulk-translate-retry-all",
|
|
199
|
+
disabled: retryingAll || rows !== null && rows.length === 0,
|
|
200
|
+
onClick: onRetryAll,
|
|
201
|
+
style: {
|
|
202
|
+
background: 'transparent',
|
|
203
|
+
border: 'none',
|
|
204
|
+
color: 'var(--theme-success-500, #16a34a)',
|
|
205
|
+
textDecoration: 'underline',
|
|
206
|
+
cursor: retryingAll || rows !== null && rows.length === 0 ? 'not-allowed' : 'pointer',
|
|
207
|
+
fontSize: '0.8125rem',
|
|
208
|
+
padding: 0
|
|
209
|
+
},
|
|
210
|
+
type: "button",
|
|
211
|
+
children: retryingAll ? 'Retrying…' : `Retry all failed in ${collection.label}`
|
|
212
|
+
})
|
|
213
|
+
}),
|
|
214
|
+
error ? /*#__PURE__*/ _jsx("p", {
|
|
215
|
+
style: {
|
|
216
|
+
margin: '0.75rem 1.5rem',
|
|
217
|
+
color: 'var(--theme-error-500, #b91c1c)',
|
|
218
|
+
fontSize: '0.8125rem'
|
|
219
|
+
},
|
|
220
|
+
children: error
|
|
221
|
+
}) : null,
|
|
222
|
+
/*#__PURE__*/ _jsx("div", {
|
|
223
|
+
style: {
|
|
224
|
+
flex: 1,
|
|
225
|
+
overflowY: 'auto'
|
|
226
|
+
},
|
|
227
|
+
children: rows === null ? /*#__PURE__*/ _jsx("p", {
|
|
228
|
+
style: {
|
|
229
|
+
padding: '1rem 1.5rem',
|
|
230
|
+
color: 'var(--theme-elevation-500)',
|
|
231
|
+
fontSize: '0.8125rem',
|
|
232
|
+
margin: 0
|
|
233
|
+
},
|
|
234
|
+
children: "Loading failures…"
|
|
235
|
+
}) : rows.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
236
|
+
style: {
|
|
237
|
+
padding: '1rem 1.5rem',
|
|
238
|
+
color: 'var(--theme-elevation-500)',
|
|
239
|
+
fontSize: '0.8125rem',
|
|
240
|
+
margin: 0
|
|
241
|
+
},
|
|
242
|
+
children: "No failures."
|
|
243
|
+
}) : rows.map((row)=>{
|
|
244
|
+
const rowDocHref = docHref(basePath, collection.slug, row.documentId, row.locale);
|
|
245
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
246
|
+
style: ROW_STYLE,
|
|
247
|
+
children: [
|
|
248
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
249
|
+
children: [
|
|
250
|
+
/*#__PURE__*/ _jsx("div", {
|
|
251
|
+
style: {
|
|
252
|
+
color: 'var(--theme-elevation-1000)'
|
|
253
|
+
},
|
|
254
|
+
children: /*#__PURE__*/ _jsx("a", {
|
|
255
|
+
href: rowDocHref,
|
|
256
|
+
rel: "noopener noreferrer",
|
|
257
|
+
style: {
|
|
258
|
+
color: 'var(--theme-success-500, #16a34a)',
|
|
259
|
+
textDecoration: 'none'
|
|
260
|
+
},
|
|
261
|
+
target: "_blank",
|
|
262
|
+
title: `Open ${row.documentTitle ?? row.documentId} in ${row.locale}`,
|
|
263
|
+
children: row.documentTitle ?? row.documentId
|
|
264
|
+
})
|
|
265
|
+
}),
|
|
266
|
+
/*#__PURE__*/ _jsx("div", {
|
|
267
|
+
style: {
|
|
268
|
+
color: 'var(--theme-elevation-500)',
|
|
269
|
+
fontSize: '0.75rem',
|
|
270
|
+
marginTop: '0.15rem'
|
|
271
|
+
},
|
|
272
|
+
title: row.failureMessage,
|
|
273
|
+
children: /*#__PURE__*/ _jsx(EditorErrorChip, {
|
|
274
|
+
code: // NEW-12 (v1.2.6): unified path through
|
|
275
|
+
// `categorizeFailure` so the chip respects the
|
|
276
|
+
// parent batch's cancel state — a row that hit
|
|
277
|
+
// a real error while the batch was being
|
|
278
|
+
// cancelled renders as "Stopped mid-run", not
|
|
279
|
+
// "Translation system is not set up".
|
|
280
|
+
categorizeFailure({
|
|
281
|
+
failureCode: row.failureCode,
|
|
282
|
+
failureMessage: row.failureMessage,
|
|
283
|
+
batchStatus,
|
|
284
|
+
attemptCount: row.attempts
|
|
285
|
+
}).editorCode,
|
|
286
|
+
context: {
|
|
287
|
+
locale: row.locale
|
|
288
|
+
},
|
|
289
|
+
title: row.failureMessage
|
|
290
|
+
})
|
|
291
|
+
})
|
|
292
|
+
]
|
|
293
|
+
}),
|
|
294
|
+
/*#__PURE__*/ _jsx("span", {
|
|
295
|
+
style: {
|
|
296
|
+
display: 'inline-block',
|
|
297
|
+
padding: '0.15rem 0.4rem',
|
|
298
|
+
background: 'var(--theme-elevation-100)',
|
|
299
|
+
borderRadius: '4px',
|
|
300
|
+
fontSize: '0.7rem',
|
|
301
|
+
color: 'var(--theme-elevation-700)',
|
|
302
|
+
alignSelf: 'start',
|
|
303
|
+
fontFamily: 'monospace'
|
|
304
|
+
},
|
|
305
|
+
children: row.locale
|
|
306
|
+
}),
|
|
307
|
+
/*#__PURE__*/ _jsx("button", {
|
|
308
|
+
"aria-label": `Retry ${row.documentTitle ?? row.documentId} ${row.locale}`,
|
|
309
|
+
"data-testid": `bulk-translate-retry-${row.unitId}`,
|
|
310
|
+
disabled: retryingUnit === row.unitId,
|
|
311
|
+
onClick: ()=>onRetryUnit(row.unitId),
|
|
312
|
+
style: {
|
|
313
|
+
background: 'transparent',
|
|
314
|
+
border: '1px solid var(--theme-elevation-300)',
|
|
315
|
+
borderRadius: '4px',
|
|
316
|
+
color: 'var(--theme-elevation-700)',
|
|
317
|
+
cursor: retryingUnit === row.unitId ? 'not-allowed' : 'pointer',
|
|
318
|
+
padding: '0.2rem 0.4rem',
|
|
319
|
+
fontSize: '0.75rem',
|
|
320
|
+
alignSelf: 'start'
|
|
321
|
+
},
|
|
322
|
+
type: "button",
|
|
323
|
+
title: "Retry",
|
|
324
|
+
children: "↺"
|
|
325
|
+
})
|
|
326
|
+
]
|
|
327
|
+
}, row.unitId);
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
]
|
|
331
|
+
});
|
|
332
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { BulkTranslateBatchSummary } from './BulkTranslate.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* In-flight monitor surface. Driven by the parent's poll loop — the
|
|
5
|
+
* parent (`Hub.client.tsx`) owns the `useBulkTranslateActive` hook
|
|
6
|
+
* and passes the latest `batch` snapshot down. This keeps the
|
|
7
|
+
* trigger and the monitor on the SAME data source (matters for
|
|
8
|
+
* multi-admin sync, F-UX-05).
|
|
9
|
+
*/
|
|
10
|
+
interface Props {
|
|
11
|
+
basePath: string;
|
|
12
|
+
batch: BulkTranslateBatchSummary;
|
|
13
|
+
/** True after 3 consecutive failed polls. Surface offline banner. */
|
|
14
|
+
isOffline: boolean;
|
|
15
|
+
/** Refetch trigger for the offline-banner "retry now" button. */
|
|
16
|
+
onRetry: () => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Format an ETA seconds value to "~Nm" / "~Nh Mm". Exported for tests.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatEta(seconds: number | null | undefined): string;
|
|
22
|
+
/**
|
|
23
|
+
* Cancellation interim copy (F-UX-04). Returns null when batch isn't
|
|
24
|
+
* cancelling so the UI can early-return.
|
|
25
|
+
*/
|
|
26
|
+
export declare function deriveCancellationLabel(batch: Pick<BulkTranslateBatchSummary, 'status' | 'totalUnits' | 'completedUnits' | 'failedUnits'>): string | null;
|
|
27
|
+
export declare const BulkTranslateMonitor: React.FC<Props>;
|
|
28
|
+
export {};
|