@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.
Files changed (301) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +714 -0
  3. package/dist/alerts-collection.d.ts +21 -0
  4. package/dist/alerts-collection.js +159 -0
  5. package/dist/api.d.ts +4 -0
  6. package/dist/api.js +918 -0
  7. package/dist/bulk-translate-batches-collection.d.ts +29 -0
  8. package/dist/bulk-translate-batches-collection.js +404 -0
  9. package/dist/bulk-translate-units-collection.d.ts +35 -0
  10. package/dist/bulk-translate-units-collection.js +310 -0
  11. package/dist/client/estimated-cost-cell.d.ts +6 -0
  12. package/dist/client/estimated-cost-cell.js +12 -0
  13. package/dist/client/excluded-fields-field.d.ts +45 -0
  14. package/dist/client/excluded-fields-field.js +553 -0
  15. package/dist/client/field-translate-button.d.ts +6 -0
  16. package/dist/client/field-translate-button.js +199 -0
  17. package/dist/client/index.d.ts +6 -0
  18. package/dist/client/index.js +6 -0
  19. package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
  20. package/dist/client/lib/use-global-kill-switches.js +58 -0
  21. package/dist/client/translate-button.d.ts +2 -0
  22. package/dist/client/translate-button.js +228 -0
  23. package/dist/client/translate-modal.d.ts +16 -0
  24. package/dist/client/translate-modal.js +549 -0
  25. package/dist/client/translation-progress.d.ts +10 -0
  26. package/dist/client/translation-progress.js +297 -0
  27. package/dist/components/TranslationNavGroup.d.ts +45 -0
  28. package/dist/components/TranslationNavGroup.js +104 -0
  29. package/dist/defaults.d.ts +11 -0
  30. package/dist/defaults.js +16 -0
  31. package/dist/endpoints/client-config.d.ts +44 -0
  32. package/dist/endpoints/client-config.js +145 -0
  33. package/dist/endpoints/estimate.d.ts +5 -0
  34. package/dist/endpoints/estimate.js +237 -0
  35. package/dist/endpoints/progress.d.ts +2 -0
  36. package/dist/endpoints/progress.js +314 -0
  37. package/dist/endpoints/translate.d.ts +11 -0
  38. package/dist/endpoints/translate.js +376 -0
  39. package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
  40. package/dist/endpoints/translation-hub/_helpers.js +297 -0
  41. package/dist/endpoints/translation-hub/active.d.ts +21 -0
  42. package/dist/endpoints/translation-hub/active.js +220 -0
  43. package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
  44. package/dist/endpoints/translation-hub/cancel.js +233 -0
  45. package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
  46. package/dist/endpoints/translation-hub/enqueue.js +529 -0
  47. package/dist/endpoints/translation-hub/failures.d.ts +12 -0
  48. package/dist/endpoints/translation-hub/failures.js +67 -0
  49. package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
  50. package/dist/endpoints/translation-hub/force-reset.js +144 -0
  51. package/dist/endpoints/translation-hub/index.d.ts +21 -0
  52. package/dist/endpoints/translation-hub/index.js +20 -0
  53. package/dist/endpoints/translation-hub/list.d.ts +40 -0
  54. package/dist/endpoints/translation-hub/list.js +182 -0
  55. package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
  56. package/dist/endpoints/translation-hub/preflight.js +141 -0
  57. package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
  58. package/dist/endpoints/translation-hub/retry-failed.js +235 -0
  59. package/dist/endpoints/translation-hub/revert.d.ts +88 -0
  60. package/dist/endpoints/translation-hub/revert.js +405 -0
  61. package/dist/endpoints/translation-hub/status.d.ts +45 -0
  62. package/dist/endpoints/translation-hub/status.js +391 -0
  63. package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
  64. package/dist/endpoints/translation-hub/usage-summary.js +481 -0
  65. package/dist/exports/client.d.ts +6 -0
  66. package/dist/exports/client.js +6 -0
  67. package/dist/exports/components.d.ts +6 -0
  68. package/dist/exports/components.js +5 -0
  69. package/dist/exports/index.d.ts +8 -0
  70. package/dist/exports/index.js +7 -0
  71. package/dist/exports/providers.d.ts +9 -0
  72. package/dist/exports/providers.js +5 -0
  73. package/dist/exports/views-client.d.ts +23 -0
  74. package/dist/exports/views-client.js +22 -0
  75. package/dist/exports/views.d.ts +30 -0
  76. package/dist/exports/views.js +29 -0
  77. package/dist/hooks/after-change-global.d.ts +4 -0
  78. package/dist/hooks/after-change-global.js +109 -0
  79. package/dist/hooks/after-change.d.ts +16 -0
  80. package/dist/hooks/after-change.js +205 -0
  81. package/dist/hooks/after-delete.d.ts +30 -0
  82. package/dist/hooks/after-delete.js +95 -0
  83. package/dist/index.d.ts +5 -0
  84. package/dist/index.js +5 -0
  85. package/dist/jobs-collection.d.ts +17 -0
  86. package/dist/jobs-collection.js +139 -0
  87. package/dist/lexical/classifier.d.ts +3 -0
  88. package/dist/lexical/classifier.js +108 -0
  89. package/dist/lexical/deserializer.d.ts +4 -0
  90. package/dist/lexical/deserializer.js +263 -0
  91. package/dist/lexical/placeholder-integrity.d.ts +6 -0
  92. package/dist/lexical/placeholder-integrity.js +21 -0
  93. package/dist/lexical/placeholders.d.ts +21 -0
  94. package/dist/lexical/placeholders.js +117 -0
  95. package/dist/lexical/serializer.d.ts +21 -0
  96. package/dist/lexical/serializer.js +233 -0
  97. package/dist/lexical/types.d.ts +32 -0
  98. package/dist/lexical/types.js +1 -0
  99. package/dist/lib/auth-diagnostics.d.ts +14 -0
  100. package/dist/lib/auth-diagnostics.js +19 -0
  101. package/dist/lib/batch-counts.d.ts +58 -0
  102. package/dist/lib/batch-counts.js +105 -0
  103. package/dist/lib/bulk-translate-migrations.d.ts +92 -0
  104. package/dist/lib/bulk-translate-migrations.js +153 -0
  105. package/dist/lib/coalescing-queue.d.ts +38 -0
  106. package/dist/lib/coalescing-queue.js +69 -0
  107. package/dist/lib/content-extractor.d.ts +16 -0
  108. package/dist/lib/content-extractor.js +410 -0
  109. package/dist/lib/content-hash.d.ts +1 -0
  110. package/dist/lib/content-hash.js +19 -0
  111. package/dist/lib/content-patcher.d.ts +15 -0
  112. package/dist/lib/content-patcher.js +293 -0
  113. package/dist/lib/cost-guards.d.ts +2 -0
  114. package/dist/lib/cost-guards.js +18 -0
  115. package/dist/lib/daily-spend-cap.d.ts +58 -0
  116. package/dist/lib/daily-spend-cap.js +233 -0
  117. package/dist/lib/effective-locales.d.ts +181 -0
  118. package/dist/lib/effective-locales.js +302 -0
  119. package/dist/lib/error-messages.d.ts +245 -0
  120. package/dist/lib/error-messages.js +626 -0
  121. package/dist/lib/events.d.ts +39 -0
  122. package/dist/lib/events.js +146 -0
  123. package/dist/lib/exclude-fields.d.ts +3 -0
  124. package/dist/lib/exclude-fields.js +64 -0
  125. package/dist/lib/field-breadcrumb.d.ts +31 -0
  126. package/dist/lib/field-breadcrumb.js +227 -0
  127. package/dist/lib/field-diff.d.ts +1 -0
  128. package/dist/lib/field-diff.js +25 -0
  129. package/dist/lib/field-empty.d.ts +2 -0
  130. package/dist/lib/field-empty.js +68 -0
  131. package/dist/lib/field-resolver.d.ts +3 -0
  132. package/dist/lib/field-resolver.js +164 -0
  133. package/dist/lib/group-soft-skips.d.ts +39 -0
  134. package/dist/lib/group-soft-skips.js +45 -0
  135. package/dist/lib/locale-merge.d.ts +44 -0
  136. package/dist/lib/locale-merge.js +357 -0
  137. package/dist/lib/locale-row-check.d.ts +30 -0
  138. package/dist/lib/locale-row-check.js +64 -0
  139. package/dist/lib/logger.d.ts +74 -0
  140. package/dist/lib/logger.js +97 -0
  141. package/dist/lib/manual-edit-guard.d.ts +128 -0
  142. package/dist/lib/manual-edit-guard.js +393 -0
  143. package/dist/lib/output-validation.d.ts +48 -0
  144. package/dist/lib/output-validation.js +148 -0
  145. package/dist/lib/payload-read.d.ts +16 -0
  146. package/dist/lib/payload-read.js +51 -0
  147. package/dist/lib/per-doc-claim.d.ts +90 -0
  148. package/dist/lib/per-doc-claim.js +140 -0
  149. package/dist/lib/per-doc-lock.d.ts +94 -0
  150. package/dist/lib/per-doc-lock.js +119 -0
  151. package/dist/lib/persist-usage.d.ts +91 -0
  152. package/dist/lib/persist-usage.js +116 -0
  153. package/dist/lib/progress-store.d.ts +103 -0
  154. package/dist/lib/progress-store.js +314 -0
  155. package/dist/lib/rate-limiter.d.ts +3 -0
  156. package/dist/lib/rate-limiter.js +53 -0
  157. package/dist/lib/snapshot-select.d.ts +43 -0
  158. package/dist/lib/snapshot-select.js +108 -0
  159. package/dist/lib/translate-prompt.d.ts +31 -0
  160. package/dist/lib/translate-prompt.js +66 -0
  161. package/dist/lib/translation-token-bucket.d.ts +57 -0
  162. package/dist/lib/translation-token-bucket.js +365 -0
  163. package/dist/lib/truncate-source-value.d.ts +1 -0
  164. package/dist/lib/truncate-source-value.js +27 -0
  165. package/dist/manual-edit-collection.d.ts +22 -0
  166. package/dist/manual-edit-collection.js +124 -0
  167. package/dist/plugin.d.ts +3 -0
  168. package/dist/plugin.js +934 -0
  169. package/dist/providers/ai-sdk-adapter.d.ts +35 -0
  170. package/dist/providers/ai-sdk-adapter.js +100 -0
  171. package/dist/providers/anthropic.d.ts +31 -0
  172. package/dist/providers/anthropic.js +66 -0
  173. package/dist/providers/custom.d.ts +36 -0
  174. package/dist/providers/custom.js +24 -0
  175. package/dist/providers/gemini.d.ts +20 -0
  176. package/dist/providers/gemini.js +48 -0
  177. package/dist/providers/mock.d.ts +2 -0
  178. package/dist/providers/mock.js +29 -0
  179. package/dist/providers/openai.d.ts +28 -0
  180. package/dist/providers/openai.js +69 -0
  181. package/dist/settings-global.d.ts +74 -0
  182. package/dist/settings-global.js +216 -0
  183. package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
  184. package/dist/tasks/bulk-translate-coordinator.js +708 -0
  185. package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
  186. package/dist/tasks/bulk-translate-doc-task.js +1000 -0
  187. package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
  188. package/dist/tasks/bulk-translate-janitor.js +311 -0
  189. package/dist/tasks/translate-job-task.d.ts +51 -0
  190. package/dist/tasks/translate-job-task.js +154 -0
  191. package/dist/translate.d.ts +113 -0
  192. package/dist/translate.js +911 -0
  193. package/dist/translation-daily-spend-collection.d.ts +24 -0
  194. package/dist/translation-daily-spend-collection.js +133 -0
  195. package/dist/translation-rate-limits-collection.d.ts +30 -0
  196. package/dist/translation-rate-limits-collection.js +144 -0
  197. package/dist/types.d.ts +672 -0
  198. package/dist/types.js +1 -0
  199. package/dist/usage-collection.d.ts +14 -0
  200. package/dist/usage-collection.js +377 -0
  201. package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
  202. package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
  203. package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
  204. package/dist/views/BulkRunsHub/BucketRow.js +982 -0
  205. package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
  206. package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
  207. package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
  208. package/dist/views/BulkRunsHub/EmptyState.js +64 -0
  209. package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
  210. package/dist/views/BulkRunsHub/FilterBar.js +284 -0
  211. package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
  212. package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
  213. package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
  214. package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
  215. package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
  216. package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
  217. package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
  218. package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
  219. package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
  220. package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
  221. package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
  222. package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
  223. package/dist/views/BulkRunsHub/index.d.ts +17 -0
  224. package/dist/views/BulkRunsHub/index.js +80 -0
  225. package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
  226. package/dist/views/BulkRunsHub/urlFilters.js +50 -0
  227. package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
  228. package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
  229. package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
  230. package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
  231. package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
  232. package/dist/views/TranslationHub/ActiveJobs.js +320 -0
  233. package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
  234. package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
  235. package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
  236. package/dist/views/TranslationHub/AlertBanner.js +568 -0
  237. package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
  238. package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
  239. package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
  240. package/dist/views/TranslationHub/AuditPanel.js +1367 -0
  241. package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
  242. package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
  243. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
  244. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
  245. package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
  246. package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
  247. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
  248. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
  249. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
  250. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
  251. package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
  252. package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
  253. package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
  254. package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
  255. package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
  256. package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
  257. package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
  258. package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
  259. package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
  260. package/dist/views/TranslationHub/Hub.client.js +357 -0
  261. package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
  262. package/dist/views/TranslationHub/ModelCombobox.js +415 -0
  263. package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
  264. package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
  265. package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
  266. package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
  267. package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
  268. package/dist/views/TranslationHub/SettingsRail.js +382 -0
  269. package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
  270. package/dist/views/TranslationHub/StatusStrip.js +451 -0
  271. package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
  272. package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
  273. package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
  274. package/dist/views/TranslationHub/UsageTable.js +1240 -0
  275. package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
  276. package/dist/views/TranslationHub/alertGrouping.js +99 -0
  277. package/dist/views/TranslationHub/index.d.ts +20 -0
  278. package/dist/views/TranslationHub/index.js +109 -0
  279. package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
  280. package/dist/views/TranslationHub/tabNavigation.js +74 -0
  281. package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
  282. package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
  283. package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
  284. package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
  285. package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
  286. package/dist/views/TranslationHub/useFocusTrap.js +81 -0
  287. package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
  288. package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
  289. package/dist/views/shared/EditorError.d.ts +97 -0
  290. package/dist/views/shared/EditorError.js +205 -0
  291. package/dist/views/shared/ModelCell.d.ts +18 -0
  292. package/dist/views/shared/ModelCell.js +31 -0
  293. package/dist/views/shared/docHref.d.ts +16 -0
  294. package/dist/views/shared/docHref.js +26 -0
  295. package/dist/views/shared/fetch-error-body.d.ts +25 -0
  296. package/dist/views/shared/fetch-error-body.js +42 -0
  297. package/dist/views/shared/filterPillStyle.d.ts +35 -0
  298. package/dist/views/shared/filterPillStyle.js +40 -0
  299. package/dist/views/shared/format.d.ts +75 -0
  300. package/dist/views/shared/format.js +131 -0
  301. package/package.json +141 -0
@@ -0,0 +1,29 @@
1
+ import type React from 'react';
2
+ import type { BulkTranslateBatchSummary } from './BulkTranslate.types.js';
3
+ interface Props {
4
+ basePath: string;
5
+ batch: BulkTranslateBatchSummary;
6
+ onDismiss: () => void;
7
+ /** Refresh handler after a successful revert — refetch active state. */
8
+ onAfterRevert?: () => void;
9
+ }
10
+ type Variant = 'success' | 'partial' | 'all-failed' | 'reverted' | 'cancelled';
11
+ /**
12
+ * Derive the terminal-card variant from a batch's status + counts.
13
+ * Exported for unit tests so we don't have to thread a full
14
+ * BulkTranslateBatchSummary through every assertion.
15
+ */
16
+ export declare function deriveVariant(batch: Pick<BulkTranslateBatchSummary, 'status' | 'completedUnits' | 'failedUnits'>): Variant;
17
+ /**
18
+ * Returns true when the batch is still inside the 24-hour revert
19
+ * window. Exported for tests.
20
+ */
21
+ export declare function canRevert(batch: Pick<BulkTranslateBatchSummary, 'revertExpiresAt' | 'revertedAt'>, now?: Date): boolean;
22
+ export declare const BulkTranslateTerminalCard: React.FC<Props>;
23
+ export declare const RevertConfirmModal: React.FC<{
24
+ basePath: string;
25
+ batchId: string;
26
+ onClose: () => void;
27
+ onSuccess: () => void;
28
+ }>;
29
+ export {};
@@ -0,0 +1,445 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useRef, useState } from 'react';
4
+ import { readResponseError } from '../shared/fetch-error-body.js';
5
+ import { formatCost } from '../shared/format.js';
6
+ import { useFocusTrap } from './useFocusTrap.js';
7
+ /**
8
+ * Derive the terminal-card variant from a batch's status + counts.
9
+ * Exported for unit tests so we don't have to thread a full
10
+ * BulkTranslateBatchSummary through every assertion.
11
+ */ export function deriveVariant(batch) {
12
+ if (batch.status === 'reverted') {
13
+ return 'reverted';
14
+ }
15
+ if (batch.status === 'cancelled') {
16
+ return 'cancelled';
17
+ }
18
+ if (batch.status === 'failed' || batch.completedUnits === 0) {
19
+ return 'all-failed';
20
+ }
21
+ if (batch.failedUnits > 0) {
22
+ return 'partial';
23
+ }
24
+ return 'success';
25
+ }
26
+ /**
27
+ * Returns true when the batch is still inside the 24-hour revert
28
+ * window. Exported for tests.
29
+ */ export function canRevert(batch, now = new Date()) {
30
+ if (batch.revertedAt) {
31
+ return false;
32
+ }
33
+ if (!batch.revertExpiresAt) {
34
+ return false;
35
+ }
36
+ return new Date(batch.revertExpiresAt).getTime() > now.getTime();
37
+ }
38
+ const SECTION_STYLE = {
39
+ background: 'var(--theme-elevation-50)',
40
+ border: '1px solid var(--theme-elevation-150)',
41
+ borderRadius: '6px',
42
+ padding: '1rem'
43
+ };
44
+ const VARIANT_TONE = {
45
+ success: {
46
+ fg: 'var(--theme-success-500, #16a34a)',
47
+ icon: '✓',
48
+ label: 'Bulk run complete'
49
+ },
50
+ partial: {
51
+ fg: 'var(--theme-warning-500, #d97706)',
52
+ icon: '⚠',
53
+ label: 'Bulk run complete with failures'
54
+ },
55
+ 'all-failed': {
56
+ fg: 'var(--theme-error-500, #b91c1c)',
57
+ icon: '✗',
58
+ label: 'Bulk run failed'
59
+ },
60
+ reverted: {
61
+ fg: 'var(--theme-elevation-700)',
62
+ icon: '↶',
63
+ label: 'Bulk run reverted'
64
+ },
65
+ cancelled: {
66
+ fg: 'var(--theme-elevation-700)',
67
+ icon: '○',
68
+ label: 'Bulk run cancelled'
69
+ }
70
+ };
71
+ export const BulkTranslateTerminalCard = ({ basePath, batch, onDismiss, onAfterRevert })=>{
72
+ const variant = deriveVariant(batch);
73
+ const tone = VARIANT_TONE[variant];
74
+ const [revertOpen, setRevertOpen] = useState(false);
75
+ const showRevert = canRevert(batch);
76
+ return /*#__PURE__*/ _jsxs("section", {
77
+ "data-testid": `bulk-translate-terminal-${variant}`,
78
+ style: SECTION_STYLE,
79
+ children: [
80
+ /*#__PURE__*/ _jsxs("div", {
81
+ style: {
82
+ display: 'flex',
83
+ alignItems: 'flex-start',
84
+ justifyContent: 'space-between',
85
+ gap: '0.75rem'
86
+ },
87
+ children: [
88
+ /*#__PURE__*/ _jsxs("div", {
89
+ children: [
90
+ /*#__PURE__*/ _jsxs("h2", {
91
+ style: {
92
+ margin: 0,
93
+ fontSize: '1rem',
94
+ color: tone.fg,
95
+ display: 'flex',
96
+ alignItems: 'center',
97
+ gap: '0.4rem'
98
+ },
99
+ children: [
100
+ /*#__PURE__*/ _jsx("span", {
101
+ "aria-hidden": "true",
102
+ children: tone.icon
103
+ }),
104
+ " ",
105
+ tone.label
106
+ ]
107
+ }),
108
+ /*#__PURE__*/ _jsxs("p", {
109
+ style: {
110
+ margin: '0.4rem 0 0',
111
+ fontSize: '0.8125rem',
112
+ color: 'var(--theme-elevation-700)'
113
+ },
114
+ children: [
115
+ batch.completedUnits,
116
+ " succeeded · ",
117
+ batch.failedUnits,
118
+ " failed",
119
+ typeof batch.actualCostUsd === 'number' ? ` · ~${formatCost(batch.actualCostUsd)}` : ''
120
+ ]
121
+ })
122
+ ]
123
+ }),
124
+ /*#__PURE__*/ _jsx("button", {
125
+ "aria-label": "Dismiss bulk run summary",
126
+ onClick: onDismiss,
127
+ style: {
128
+ background: 'transparent',
129
+ border: 'none',
130
+ color: 'var(--theme-elevation-500)',
131
+ fontSize: '1.25rem',
132
+ lineHeight: 1,
133
+ cursor: 'pointer',
134
+ padding: 0
135
+ },
136
+ type: "button",
137
+ children: "×"
138
+ })
139
+ ]
140
+ }),
141
+ /*#__PURE__*/ _jsxs("div", {
142
+ style: {
143
+ marginTop: '0.75rem',
144
+ display: 'flex',
145
+ gap: '0.75rem',
146
+ flexWrap: 'wrap'
147
+ },
148
+ children: [
149
+ variant === 'partial' ? /*#__PURE__*/ _jsxs(_Fragment, {
150
+ children: [
151
+ /*#__PURE__*/ _jsx("a", {
152
+ href: "#bulk-translate-monitor",
153
+ style: {
154
+ color: 'var(--theme-elevation-700)',
155
+ fontSize: '0.8125rem'
156
+ },
157
+ children: "View failed jobs →"
158
+ }),
159
+ /*#__PURE__*/ _jsx(RetryFailedLink, {
160
+ basePath: basePath,
161
+ batchId: batch.id
162
+ })
163
+ ]
164
+ }) : null,
165
+ variant === 'all-failed' ? /*#__PURE__*/ _jsxs(_Fragment, {
166
+ children: [
167
+ /*#__PURE__*/ _jsx("a", {
168
+ href: "#bulk-translate-monitor",
169
+ style: {
170
+ color: 'var(--theme-error-500, #b91c1c)',
171
+ fontSize: '0.8125rem'
172
+ },
173
+ children: "Open first failure →"
174
+ }),
175
+ /*#__PURE__*/ _jsx(RetryFailedLink, {
176
+ basePath: basePath,
177
+ batchId: batch.id
178
+ })
179
+ ]
180
+ }) : null,
181
+ showRevert && variant !== 'reverted' ? /*#__PURE__*/ _jsx("button", {
182
+ "data-testid": "bulk-translate-revert-open",
183
+ onClick: ()=>setRevertOpen(true),
184
+ style: {
185
+ background: 'transparent',
186
+ border: '1px solid var(--theme-elevation-300)',
187
+ borderRadius: '4px',
188
+ padding: '0.25rem 0.6rem',
189
+ color: 'var(--theme-elevation-1000)',
190
+ fontSize: '0.8125rem',
191
+ cursor: 'pointer'
192
+ },
193
+ type: "button",
194
+ children: "Revert this batch"
195
+ }) : null
196
+ ]
197
+ }),
198
+ revertOpen ? /*#__PURE__*/ _jsx(RevertConfirmModal, {
199
+ basePath: basePath,
200
+ batchId: batch.id,
201
+ onClose: ()=>setRevertOpen(false),
202
+ onSuccess: ()=>{
203
+ setRevertOpen(false);
204
+ onAfterRevert?.();
205
+ }
206
+ }) : null
207
+ ]
208
+ });
209
+ };
210
+ const RetryFailedLink = ({ basePath, batchId })=>{
211
+ const [busy, setBusy] = useState(false);
212
+ const [err, setErr] = useState(null);
213
+ async function onClick() {
214
+ setBusy(true);
215
+ try {
216
+ const res = await fetch(`${basePath}/api/translation-hub/bulk-translate/${batchId}/retry`, {
217
+ method: 'POST',
218
+ credentials: 'include',
219
+ headers: {
220
+ 'Content-Type': 'application/json'
221
+ },
222
+ body: JSON.stringify({
223
+ allFailed: true
224
+ })
225
+ });
226
+ if (!res.ok) {
227
+ throw new Error(await readResponseError(res));
228
+ }
229
+ } catch (e) {
230
+ setErr(e instanceof Error ? e.message : String(e));
231
+ } finally{
232
+ setBusy(false);
233
+ }
234
+ }
235
+ return /*#__PURE__*/ _jsxs(_Fragment, {
236
+ children: [
237
+ /*#__PURE__*/ _jsx("button", {
238
+ "data-testid": "bulk-translate-retry-all-failed",
239
+ disabled: busy,
240
+ onClick: onClick,
241
+ style: {
242
+ background: 'transparent',
243
+ border: '1px solid var(--theme-elevation-300)',
244
+ borderRadius: '4px',
245
+ padding: '0.25rem 0.6rem',
246
+ color: 'var(--theme-elevation-1000)',
247
+ fontSize: '0.8125rem',
248
+ cursor: busy ? 'not-allowed' : 'pointer'
249
+ },
250
+ type: "button",
251
+ children: busy ? 'Retrying…' : 'Retry All Failed'
252
+ }),
253
+ err ? /*#__PURE__*/ _jsx("span", {
254
+ style: {
255
+ color: 'var(--theme-error-500, #b91c1c)',
256
+ fontSize: '0.75rem'
257
+ },
258
+ children: err
259
+ }) : null
260
+ ]
261
+ });
262
+ };
263
+ export const RevertConfirmModal = ({ basePath, batchId, onClose, onSuccess })=>{
264
+ const ref = useRef(null);
265
+ const [totp, setTotp] = useState('');
266
+ const [busy, setBusy] = useState(false);
267
+ const [err, setErr] = useState(null);
268
+ useFocusTrap(ref, {
269
+ enabled: true,
270
+ onEscape: onClose
271
+ });
272
+ async function onConfirm() {
273
+ setBusy(true);
274
+ setErr(null);
275
+ try {
276
+ const res = await fetch(`${basePath}/api/translation-hub/bulk-translate/${batchId}/revert`, {
277
+ method: 'POST',
278
+ credentials: 'include',
279
+ headers: {
280
+ 'Content-Type': 'application/json'
281
+ },
282
+ body: JSON.stringify({
283
+ totp
284
+ })
285
+ });
286
+ if (!res.ok) {
287
+ const body = await res.json().catch(()=>null);
288
+ throw new Error(body?.error ?? `HTTP ${res.status}`);
289
+ }
290
+ onSuccess();
291
+ } catch (e) {
292
+ setErr(e instanceof Error ? e.message : String(e));
293
+ } finally{
294
+ setBusy(false);
295
+ }
296
+ }
297
+ return /*#__PURE__*/ _jsx("div", {
298
+ onClick: (e)=>{
299
+ if (e.target === e.currentTarget) {
300
+ onClose();
301
+ }
302
+ },
303
+ style: {
304
+ position: 'fixed',
305
+ inset: 0,
306
+ background: 'rgba(0,0,0,0.45)',
307
+ display: 'flex',
308
+ alignItems: 'flex-start',
309
+ justifyContent: 'center',
310
+ paddingTop: '4rem',
311
+ zIndex: 210
312
+ },
313
+ children: /*#__PURE__*/ _jsxs("div", {
314
+ "aria-describedby": "bulk-translate-revert-desc",
315
+ "aria-labelledby": "bulk-translate-revert-title",
316
+ "aria-modal": "true",
317
+ ref: ref,
318
+ role: "dialog",
319
+ style: {
320
+ width: '100%',
321
+ maxWidth: '420px',
322
+ background: 'var(--theme-elevation-0)',
323
+ border: '1px solid var(--theme-elevation-150)',
324
+ borderRadius: '6px',
325
+ padding: '1.25rem'
326
+ },
327
+ children: [
328
+ /*#__PURE__*/ _jsx("h3", {
329
+ id: "bulk-translate-revert-title",
330
+ style: {
331
+ margin: 0,
332
+ fontSize: '1rem',
333
+ color: 'var(--theme-elevation-1000)'
334
+ },
335
+ children: "Revert this batch?"
336
+ }),
337
+ /*#__PURE__*/ _jsx("p", {
338
+ id: "bulk-translate-revert-desc",
339
+ style: {
340
+ margin: '0.5rem 0 0.75rem',
341
+ color: 'var(--theme-elevation-700)',
342
+ fontSize: '0.8125rem'
343
+ },
344
+ children: "Restores pre-run snapshots for every translated locale row in this batch. Newer manual edits will be overwritten."
345
+ }),
346
+ /*#__PURE__*/ _jsx("label", {
347
+ htmlFor: "bulk-translate-revert-totp",
348
+ style: {
349
+ display: 'block',
350
+ fontSize: '0.6875rem',
351
+ fontWeight: 600,
352
+ textTransform: 'uppercase',
353
+ letterSpacing: '0.05em',
354
+ color: 'var(--theme-elevation-500)',
355
+ margin: '0 0 0.35rem'
356
+ },
357
+ children: "Authentication code"
358
+ }),
359
+ /*#__PURE__*/ _jsx("input", {
360
+ "aria-describedby": "bulk-translate-revert-totp-hint",
361
+ autoComplete: "one-time-code",
362
+ id: "bulk-translate-revert-totp",
363
+ inputMode: "numeric",
364
+ maxLength: 6,
365
+ onChange: (e)=>setTotp(e.target.value.replace(/\D/g, '').slice(0, 6)),
366
+ pattern: "[0-9]{6}",
367
+ // NEW-22 (v1.2.6): "000000" looked like a submittable default;
368
+ // a label that names the *format* tells the editor what to
369
+ // enter without implying any value is acceptable.
370
+ placeholder: "6-digit code",
371
+ style: {
372
+ width: '100%',
373
+ padding: '0.4rem 0.6rem',
374
+ background: 'var(--theme-elevation-0)',
375
+ border: '1px solid var(--theme-elevation-300)',
376
+ borderRadius: '4px',
377
+ fontSize: '0.875rem',
378
+ fontFamily: 'inherit',
379
+ color: 'var(--theme-elevation-1000)'
380
+ },
381
+ type: "text",
382
+ value: totp
383
+ }),
384
+ /*#__PURE__*/ _jsx("p", {
385
+ id: "bulk-translate-revert-totp-hint",
386
+ style: {
387
+ margin: '0.35rem 0 0',
388
+ fontSize: '0.6875rem',
389
+ color: 'var(--theme-elevation-500)'
390
+ },
391
+ children: "Enter the 6-digit code from your authenticator app."
392
+ }),
393
+ err ? /*#__PURE__*/ _jsx("p", {
394
+ style: {
395
+ color: 'var(--theme-error-500, #b91c1c)',
396
+ fontSize: '0.8125rem',
397
+ marginTop: '0.5rem'
398
+ },
399
+ children: err
400
+ }) : null,
401
+ /*#__PURE__*/ _jsxs("div", {
402
+ style: {
403
+ display: 'flex',
404
+ justifyContent: 'flex-end',
405
+ gap: '0.5rem',
406
+ marginTop: '1rem'
407
+ },
408
+ children: [
409
+ /*#__PURE__*/ _jsx("button", {
410
+ onClick: onClose,
411
+ style: {
412
+ padding: '0.4rem 0.85rem',
413
+ background: 'transparent',
414
+ border: 'none',
415
+ color: 'var(--theme-elevation-700)',
416
+ cursor: 'pointer',
417
+ fontSize: '0.8125rem'
418
+ },
419
+ type: "button",
420
+ children: "Cancel"
421
+ }),
422
+ /*#__PURE__*/ _jsx("button", {
423
+ "data-testid": "bulk-translate-revert-confirm",
424
+ disabled: busy || totp.length !== 6,
425
+ onClick: onConfirm,
426
+ style: {
427
+ padding: '0.4rem 0.85rem',
428
+ background: 'var(--theme-error-500, #b91c1c)',
429
+ border: '1px solid var(--theme-error-500, #b91c1c)',
430
+ borderRadius: '4px',
431
+ color: '#ffffff',
432
+ cursor: busy || totp.length !== 6 ? 'not-allowed' : 'pointer',
433
+ opacity: busy || totp.length !== 6 ? 0.5 : 1,
434
+ fontSize: '0.8125rem',
435
+ fontWeight: 600
436
+ },
437
+ type: "button",
438
+ children: busy ? 'Reverting…' : 'Revert batch'
439
+ })
440
+ ]
441
+ })
442
+ ]
443
+ })
444
+ });
445
+ };
@@ -0,0 +1,66 @@
1
+ import type React from 'react';
2
+ /**
3
+ * Tristate trigger surfaced in the Overview tab header.
4
+ *
5
+ * Per F-DA-LOADING-FLICKER / N8, this component MUST render a skeleton
6
+ * (NOT the actual button) until the active-run state resolves. If we
7
+ * rendered the button straight away, a 100-500ms window exists where
8
+ * an admin can double-click during the active-batch lookup and bypass
9
+ * the "refuse second click" rule.
10
+ *
11
+ * Render states:
12
+ * - `loading` (data === null && loading): 90×30px elevation-100 block.
13
+ * - `active-run` (batch exists + status active): status chip linking
14
+ * to the monitor anchor below.
15
+ * - `idle` (no active batch): outline "Bulk Translate" button that
16
+ * opens the pre-flight modal.
17
+ */
18
+ interface Props {
19
+ basePath: string;
20
+ /** Mirrored from `Hub.client.tsx` — passed to the modal for fetches. */
21
+ monitorAnchorId?: string;
22
+ /**
23
+ * 1.2.8: current viewer's user id (stringified). When an active
24
+ * batch's `triggeredByUserId` doesn't match, the chip shows the
25
+ * owner's email — "Bulk run by alice@… — 3/12 complete" — so the
26
+ * editor knows whose run they're seeing and can decide whether to
27
+ * wait or ping. Omitted on legacy admin-only callsites; defaults to
28
+ * the pre-1.2.8 generic copy.
29
+ */
30
+ currentUserId?: string | null;
31
+ }
32
+ type DerivedState = {
33
+ kind: 'loading';
34
+ } | {
35
+ kind: 'active-run';
36
+ label: string;
37
+ href: string;
38
+ } | {
39
+ kind: 'idle';
40
+ };
41
+ /**
42
+ * Pure derivation — exported for unit tests so the tristate logic can
43
+ * be exercised without React.
44
+ *
45
+ * 1.2.8: also surfaces attribution when the active batch was started
46
+ * by a different user. The label switches from a generic
47
+ * "Bulk run in progress" to "Bulk run by alice@…" so editors can
48
+ * tell at a glance whose run is occupying the queue without
49
+ * navigating into the monitor card.
50
+ */
51
+ export declare function deriveTriggerState(args: {
52
+ loading: boolean;
53
+ data: {
54
+ batch: {
55
+ status: string;
56
+ completedUnits: number;
57
+ totalUnits: number;
58
+ triggeredByUserId?: string | number | null;
59
+ triggeredByEmail?: string | null;
60
+ } | null;
61
+ } | null;
62
+ monitorAnchorId?: string;
63
+ currentUserId?: string | null;
64
+ }): DerivedState;
65
+ export declare const BulkTranslateTrigger: React.FC<Props>;
66
+ export {};