@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,161 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { useGlobalKillSwitches } from '../../client/lib/use-global-kill-switches.js';
5
+ import { isActiveStatus } from './BulkTranslate.types.js';
6
+ import { BulkTranslatePreflightModal } from './BulkTranslatePreflightModal.js';
7
+ import { useBulkTranslateActive } from './useBulkTranslateActive.js';
8
+ const SKELETON_STYLE = {
9
+ width: '120px',
10
+ height: '30px',
11
+ background: 'var(--theme-elevation-100)',
12
+ borderRadius: '4px',
13
+ // Visual hint that this is a placeholder, not interactive. No
14
+ // animation library — keyframe lives in the global Payload theme
15
+ // (`--theme-pulse-*`) and the simple opacity transition below is a
16
+ // reasonable cross-theme fallback.
17
+ opacity: 0.7
18
+ };
19
+ const CHIP_STYLE = {
20
+ display: 'inline-flex',
21
+ alignItems: 'center',
22
+ gap: '0.5rem',
23
+ padding: '0.4rem 0.75rem',
24
+ borderRadius: '4px',
25
+ background: 'var(--theme-warning-100, #fef3c7)',
26
+ border: '1px solid var(--theme-warning-500, #d97706)',
27
+ color: 'var(--theme-warning-500, #d97706)',
28
+ fontSize: '0.8125rem',
29
+ fontWeight: 500,
30
+ textDecoration: 'none'
31
+ };
32
+ const BUTTON_STYLE = {
33
+ display: 'inline-flex',
34
+ alignItems: 'center',
35
+ gap: '0.5rem',
36
+ padding: '0.4rem 0.85rem',
37
+ background: 'transparent',
38
+ border: '1px solid var(--theme-elevation-300)',
39
+ borderRadius: '4px',
40
+ color: 'var(--theme-elevation-1000)',
41
+ fontSize: '0.8125rem',
42
+ fontWeight: 500,
43
+ cursor: 'pointer'
44
+ };
45
+ /**
46
+ * Pure derivation — exported for unit tests so the tristate logic can
47
+ * be exercised without React.
48
+ *
49
+ * 1.2.8: also surfaces attribution when the active batch was started
50
+ * by a different user. The label switches from a generic
51
+ * "Bulk run in progress" to "Bulk run by alice@…" so editors can
52
+ * tell at a glance whose run is occupying the queue without
53
+ * navigating into the monitor card.
54
+ */ export function deriveTriggerState(args) {
55
+ const { loading, data, monitorAnchorId, currentUserId } = args;
56
+ // Initial-load skeleton (closes N8 / F-DA-LOADING-FLICKER window).
57
+ if (loading || data === null) {
58
+ return {
59
+ kind: 'loading'
60
+ };
61
+ }
62
+ const batch = data.batch;
63
+ if (batch && isActiveStatus(batch.status)) {
64
+ const completed = batch.completedUnits ?? 0;
65
+ const total = batch.totalUnits ?? 0;
66
+ const ownedByViewer = currentUserId != null && batch.triggeredByUserId != null && String(batch.triggeredByUserId) === currentUserId;
67
+ let label;
68
+ if (ownedByViewer) {
69
+ label = `Your bulk run — ${completed}/${total} complete`;
70
+ } else if (batch.triggeredByEmail) {
71
+ label = `Bulk run by ${batch.triggeredByEmail} — ${completed}/${total} complete`;
72
+ } else {
73
+ label = `Bulk run in progress — ${completed}/${total} complete`;
74
+ }
75
+ return {
76
+ kind: 'active-run',
77
+ label,
78
+ href: monitorAnchorId ? `#${monitorAnchorId}` : '#'
79
+ };
80
+ }
81
+ return {
82
+ kind: 'idle'
83
+ };
84
+ }
85
+ export const BulkTranslateTrigger = ({ basePath, monitorAnchorId = 'bulk-translate-monitor', currentUserId = null })=>{
86
+ const { data, loading, refetch } = useBulkTranslateActive(basePath);
87
+ const [modalOpen, setModalOpen] = useState(false);
88
+ // Plugin-wide bulk-translate kill switch (v1.2.8). Top-level for
89
+ // rules-of-hooks; rendering decision below.
90
+ const killSwitches = useGlobalKillSwitches();
91
+ const state = deriveTriggerState({
92
+ loading,
93
+ data,
94
+ monitorAnchorId,
95
+ currentUserId
96
+ });
97
+ if (state.kind === 'loading') {
98
+ return /*#__PURE__*/ _jsx("div", {
99
+ "aria-busy": "true",
100
+ "aria-label": "Loading bulk translate status",
101
+ "data-testid": "bulk-translate-trigger-skeleton",
102
+ role: "status",
103
+ style: SKELETON_STYLE
104
+ });
105
+ }
106
+ if (state.kind === 'active-run') {
107
+ return /*#__PURE__*/ _jsxs("a", {
108
+ "data-testid": "bulk-translate-trigger-active",
109
+ href: state.href,
110
+ style: CHIP_STYLE,
111
+ children: [
112
+ /*#__PURE__*/ _jsx("span", {
113
+ "aria-hidden": "true",
114
+ children: "●"
115
+ }),
116
+ state.label
117
+ ]
118
+ });
119
+ }
120
+ // Kill switch off — show paused affordance so admins can see the
121
+ // feature isn't broken, it's intentionally disabled. Active-run takes
122
+ // precedence above so an in-flight run is still visible even when
123
+ // new enqueues are blocked.
124
+ if (killSwitches && !killSwitches.bulkEnabled) {
125
+ return /*#__PURE__*/ _jsx("button", {
126
+ "aria-disabled": "true",
127
+ "data-testid": "bulk-translate-trigger-paused",
128
+ disabled: true,
129
+ style: {
130
+ ...BUTTON_STYLE,
131
+ opacity: 0.55,
132
+ cursor: 'not-allowed'
133
+ },
134
+ title: "Bulk Translate is paused site-wide. An admin can re-enable it in Translation Settings.",
135
+ type: "button",
136
+ children: "Bulk Translate (paused)"
137
+ });
138
+ }
139
+ return /*#__PURE__*/ _jsxs(_Fragment, {
140
+ children: [
141
+ /*#__PURE__*/ _jsx("button", {
142
+ "data-testid": "bulk-translate-trigger-idle",
143
+ onClick: ()=>setModalOpen(true),
144
+ style: BUTTON_STYLE,
145
+ type: "button",
146
+ children: "Bulk Translate"
147
+ }),
148
+ modalOpen && /*#__PURE__*/ _jsx(BulkTranslatePreflightModal, {
149
+ basePath: basePath,
150
+ onClose: ()=>{
151
+ setModalOpen(false);
152
+ // Refresh active-batch state so the trigger flips to
153
+ // "active-run" immediately after a successful enqueue
154
+ // (mirrors F-UX-15 expectation that the UI never shows a
155
+ // stale "idle" trigger post-enqueue).
156
+ refetch();
157
+ }
158
+ })
159
+ ]
160
+ });
161
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Editor-facing "Your recent bulk runs" panel (1.2.8).
3
+ *
4
+ * Replaces the admin `UsageTable` on the Translation Hub Overview for
5
+ * editor-role viewers. Sourced from `bulk_translate_batches` via the
6
+ * `/api/translation-hub/bulk-translate` list endpoint (opened to
7
+ * editor+admin in 1.2.8) instead of `translation_usage` (the per-doc
8
+ * audit collection, which has no `triggeredByUserId` column and so
9
+ * can't support the attribution chip the editor experience needs).
10
+ *
11
+ * Layout: compact table of the editor's last ~5 bulk runs with an
12
+ * attribution chip on every row plus a CTA linking to the full
13
+ * BulkRunsHub for deeper history. Cost / token / model columns are
14
+ * absent — the field-level access mask on the batches collection
15
+ * strips those for non-admin viewers, and surfacing them to editors
16
+ * would reintroduce the cost-anxiety problem the UX critique flagged.
17
+ *
18
+ * Owned vs others' runs: per Decision #31-revised the table shows
19
+ * BOTH the current editor's runs and other editors' runs. The action
20
+ * column is only present on rows the editor owns. Rows owned by
21
+ * other editors render with an attribution chip and no action menu —
22
+ * editors coordinate by reading the chip, not by acting on each
23
+ * other's runs.
24
+ */
25
+ import type React from 'react';
26
+ interface Props {
27
+ basePath: string;
28
+ /** Stringified user id. Drives the attribution chip + action gating. */
29
+ currentUserId: string | null;
30
+ currentUserEmail: string | null;
31
+ }
32
+ export declare const EditorRecentRunsPanel: React.FC<Props>;
33
+ export {};
@@ -0,0 +1,290 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { readResponseError } from '../shared/fetch-error-body.js';
5
+ // Active polling cadence matches the rest of the Hub — 5s while a
6
+ // batch is in flight, 60s otherwise.
7
+ const POLL_INTERVAL_MS = 5_000;
8
+ const IDLE_POLL_INTERVAL_MS = 60_000;
9
+ const ROW_LIMIT = 8;
10
+ const SECTION_STYLE = {
11
+ background: 'var(--theme-elevation-50)',
12
+ border: '1px solid var(--theme-elevation-150)',
13
+ borderRadius: '6px',
14
+ padding: '1rem'
15
+ };
16
+ const HEADER_ROW_STYLE = {
17
+ display: 'grid',
18
+ gridTemplateColumns: '1.4fr 1fr 0.9fr 0.7fr 0.7fr',
19
+ gap: '0.75rem',
20
+ padding: '0.5rem 0',
21
+ fontSize: '0.75rem',
22
+ fontWeight: 600,
23
+ textTransform: 'uppercase',
24
+ letterSpacing: '0.05em',
25
+ color: 'var(--theme-elevation-500)'
26
+ };
27
+ const ROW_STYLE = {
28
+ display: 'grid',
29
+ gridTemplateColumns: '1.4fr 1fr 0.9fr 0.7fr 0.7fr',
30
+ gap: '0.75rem',
31
+ padding: '0.5rem 0',
32
+ alignItems: 'center',
33
+ fontSize: '0.875rem',
34
+ borderTop: '1px solid var(--theme-elevation-100)',
35
+ color: 'var(--theme-elevation-800)'
36
+ };
37
+ const STATUS_COLORS = {
38
+ queued: 'var(--theme-elevation-500)',
39
+ running: 'var(--theme-warning-500, #d97706)',
40
+ cancelling: 'var(--theme-warning-500, #d97706)',
41
+ success: 'var(--theme-success-500, #16a34a)',
42
+ partial: 'var(--theme-warning-500, #d97706)',
43
+ failed: 'var(--theme-error-500, #b91c1c)',
44
+ cancelled: 'var(--theme-elevation-500)',
45
+ reverted: 'var(--theme-elevation-500)'
46
+ };
47
+ function relTime(iso) {
48
+ if (!iso) return '—';
49
+ const diff = Date.now() - new Date(iso).getTime();
50
+ if (!Number.isFinite(diff)) return '—';
51
+ const s = Math.floor(diff / 1000);
52
+ if (s < 60) return `${s}s ago`;
53
+ if (s < 3600) return `${Math.floor(s / 60)}m ago`;
54
+ if (s < 86_400) return `${Math.floor(s / 3600)}h ago`;
55
+ return `${Math.floor(s / 86_400)}d ago`;
56
+ }
57
+ // Active = anything that isn't terminal. Drives the poll cadence
58
+ // (5s while live, 60s while idle).
59
+ const TERMINAL_STATUSES = new Set([
60
+ 'success',
61
+ 'partial',
62
+ 'failed',
63
+ 'cancelled',
64
+ 'reverted'
65
+ ]);
66
+ export const EditorRecentRunsPanel = ({ basePath, currentUserId, currentUserEmail })=>{
67
+ void currentUserEmail; // reserved for future "by you" chip styling
68
+ const [rows, setRows] = useState(null);
69
+ const [error, setError] = useState(null);
70
+ useEffect(()=>{
71
+ let cancelled = false;
72
+ let timer;
73
+ let hasActiveBatch = false;
74
+ async function fetchRows() {
75
+ const url = `${basePath}/api/translation-hub/bulk-translate?limit=${ROW_LIMIT}&sort=-enqueuedAt`;
76
+ const res = await fetch(url, {
77
+ credentials: 'include'
78
+ });
79
+ if (!res.ok) {
80
+ throw new Error(await readResponseError(res));
81
+ }
82
+ const json = await res.json();
83
+ return json.batches ?? [];
84
+ }
85
+ async function poll() {
86
+ try {
87
+ const next = await fetchRows();
88
+ if (cancelled) return;
89
+ setRows(next);
90
+ setError(null);
91
+ hasActiveBatch = next.some((r)=>!TERMINAL_STATUSES.has(r.status));
92
+ } catch (e) {
93
+ if (cancelled) return;
94
+ setError(e instanceof Error ? e.message : String(e));
95
+ } finally{
96
+ if (!cancelled && document.visibilityState === 'visible') {
97
+ const nextDelay = hasActiveBatch ? POLL_INTERVAL_MS : IDLE_POLL_INTERVAL_MS;
98
+ timer = setTimeout(poll, nextDelay);
99
+ }
100
+ }
101
+ }
102
+ function onVisibility() {
103
+ if (document.visibilityState === 'visible' && !cancelled) {
104
+ if (timer) clearTimeout(timer);
105
+ poll();
106
+ }
107
+ }
108
+ document.addEventListener('visibilitychange', onVisibility);
109
+ poll();
110
+ return ()=>{
111
+ cancelled = true;
112
+ if (timer) clearTimeout(timer);
113
+ document.removeEventListener('visibilitychange', onVisibility);
114
+ };
115
+ }, [
116
+ basePath
117
+ ]);
118
+ const visibleRows = useMemo(()=>rows ?? [], [
119
+ rows
120
+ ]);
121
+ return /*#__PURE__*/ _jsxs("section", {
122
+ style: SECTION_STYLE,
123
+ children: [
124
+ /*#__PURE__*/ _jsxs("header", {
125
+ style: {
126
+ display: 'flex',
127
+ alignItems: 'baseline',
128
+ justifyContent: 'space-between',
129
+ marginBottom: '0.75rem'
130
+ },
131
+ children: [
132
+ /*#__PURE__*/ _jsx("h2", {
133
+ style: {
134
+ margin: 0,
135
+ fontSize: '1rem',
136
+ color: 'var(--theme-elevation-1000)'
137
+ },
138
+ children: "Recent bulk runs"
139
+ }),
140
+ /*#__PURE__*/ _jsx("a", {
141
+ href: `${basePath}/admin/translation-runs`,
142
+ style: {
143
+ fontSize: '0.75rem',
144
+ color: 'var(--theme-elevation-500)',
145
+ textDecoration: 'none'
146
+ },
147
+ children: "View all →"
148
+ })
149
+ ]
150
+ }),
151
+ error && /*#__PURE__*/ _jsx("p", {
152
+ style: {
153
+ color: 'var(--theme-error-500, #b91c1c)',
154
+ fontSize: '0.875rem',
155
+ margin: '0 0 0.5rem'
156
+ },
157
+ children: error
158
+ }),
159
+ rows === null && /*#__PURE__*/ _jsx("p", {
160
+ style: {
161
+ margin: 0,
162
+ color: 'var(--theme-elevation-500)',
163
+ fontSize: '0.875rem'
164
+ },
165
+ children: "Loading…"
166
+ }),
167
+ rows !== null && visibleRows.length === 0 && /*#__PURE__*/ _jsx("p", {
168
+ style: {
169
+ margin: 0,
170
+ color: 'var(--theme-elevation-500)',
171
+ fontSize: '0.875rem'
172
+ },
173
+ children: "No bulk translation runs yet. Use the Bulk Translate button above to start one."
174
+ }),
175
+ visibleRows.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
176
+ children: [
177
+ /*#__PURE__*/ _jsxs("div", {
178
+ style: HEADER_ROW_STYLE,
179
+ children: [
180
+ /*#__PURE__*/ _jsx("span", {
181
+ children: "Run"
182
+ }),
183
+ /*#__PURE__*/ _jsx("span", {
184
+ children: "Triggered by"
185
+ }),
186
+ /*#__PURE__*/ _jsx("span", {
187
+ children: "Status"
188
+ }),
189
+ /*#__PURE__*/ _jsx("span", {
190
+ children: "Progress"
191
+ }),
192
+ /*#__PURE__*/ _jsx("span", {
193
+ style: {
194
+ textAlign: 'right'
195
+ },
196
+ children: "When"
197
+ })
198
+ ]
199
+ }),
200
+ visibleRows.map((r)=>{
201
+ const total = r.totalUnits ?? 0;
202
+ const done = r.completedUnits ?? 0;
203
+ const failed = r.failedUnits ?? 0;
204
+ const skipped = r.skippedUnits ?? 0;
205
+ const owned = currentUserId != null && r.triggeredByUserId != null && String(r.triggeredByUserId) === currentUserId;
206
+ const triggeredBy = r.triggeredByEmail ?? (r.triggeredByUserId != null ? `user #${r.triggeredByUserId}` : '—');
207
+ return /*#__PURE__*/ _jsxs("div", {
208
+ style: ROW_STYLE,
209
+ children: [
210
+ /*#__PURE__*/ _jsxs("span", {
211
+ style: {
212
+ overflow: 'hidden',
213
+ textOverflow: 'ellipsis'
214
+ },
215
+ title: `Batch ${r.id} · mode ${r.mode}`,
216
+ children: [
217
+ /*#__PURE__*/ _jsxs("code", {
218
+ style: {
219
+ fontSize: '0.75rem'
220
+ },
221
+ children: [
222
+ "#",
223
+ String(r.id)
224
+ ]
225
+ }),
226
+ ' ',
227
+ /*#__PURE__*/ _jsx("span", {
228
+ style: {
229
+ color: 'var(--theme-elevation-500)'
230
+ },
231
+ children: r.mode
232
+ })
233
+ ]
234
+ }),
235
+ /*#__PURE__*/ _jsxs("span", {
236
+ style: {
237
+ display: 'inline-flex',
238
+ alignItems: 'center',
239
+ gap: '0.4rem',
240
+ overflow: 'hidden',
241
+ textOverflow: 'ellipsis'
242
+ },
243
+ children: [
244
+ triggeredBy,
245
+ owned && /*#__PURE__*/ _jsx("span", {
246
+ style: {
247
+ fontSize: '0.65rem',
248
+ padding: '0.05rem 0.4rem',
249
+ borderRadius: '999px',
250
+ background: 'var(--theme-success-100, #dcfce7)',
251
+ color: 'var(--theme-success-800, #166534)',
252
+ fontWeight: 500
253
+ },
254
+ children: "you"
255
+ })
256
+ ]
257
+ }),
258
+ /*#__PURE__*/ _jsxs("span", {
259
+ style: {
260
+ color: STATUS_COLORS[r.status] ?? 'var(--theme-elevation-800)',
261
+ fontWeight: 500
262
+ },
263
+ children: [
264
+ r.status,
265
+ failed > 0 ? ` (${failed} failed)` : '',
266
+ skipped > 0 ? ` (${skipped} skipped)` : ''
267
+ ]
268
+ }),
269
+ /*#__PURE__*/ _jsxs("span", {
270
+ children: [
271
+ done,
272
+ "/",
273
+ total
274
+ ]
275
+ }),
276
+ /*#__PURE__*/ _jsx("span", {
277
+ style: {
278
+ textAlign: 'right',
279
+ color: 'var(--theme-elevation-500)'
280
+ },
281
+ children: relTime(r.completedAt ?? r.startedAt ?? r.enqueuedAt)
282
+ })
283
+ ]
284
+ }, String(r.id));
285
+ })
286
+ ]
287
+ })
288
+ ]
289
+ });
290
+ };
@@ -0,0 +1,74 @@
1
+ import type React from 'react';
2
+ export interface TranslationSettings {
3
+ activeProvider?: string | null;
4
+ enabledTargetLocales?: string[] | null;
5
+ globalAutoTranslateEnabled?: boolean | null;
6
+ globalManualTranslateEnabled?: boolean | null;
7
+ globalBulkTranslateEnabled?: boolean | null;
8
+ perCollection?: Array<{
9
+ slug: string;
10
+ enabled?: boolean | null;
11
+ autoOnPublish?: boolean | null;
12
+ targetLocalesOverride?: string[] | null;
13
+ excludedFieldPaths?: string[] | null;
14
+ translateSlug?: boolean | null;
15
+ }> | null;
16
+ }
17
+ export interface OpenRouterSettings {
18
+ model?: string | null;
19
+ }
20
+ /**
21
+ * Locale list passed from the server (`req.payload.config.localization`).
22
+ * Source of truth for locale toggles in the Hub — replaces the prior
23
+ * hardcoded `['es', 'fr']` in SettingsRail that would silently drift the
24
+ * moment a consumer added or removed a locale in payload.config.
25
+ */
26
+ export interface LocaleConfig {
27
+ defaultLocale: string;
28
+ locales: Array<{
29
+ code: string;
30
+ label: string;
31
+ }>;
32
+ }
33
+ export interface JobRow {
34
+ collection: string;
35
+ completed_locales?: string[] | null;
36
+ created_at: string;
37
+ document_id: string;
38
+ failed_locales?: Array<{
39
+ locale: string;
40
+ error?: string;
41
+ }> | null;
42
+ id: number;
43
+ job_id?: string;
44
+ started_at?: string | null;
45
+ status: 'running' | 'completed' | 'failed';
46
+ total_locales: number;
47
+ }
48
+ interface TranslationHubClientProps {
49
+ locales: LocaleConfig;
50
+ /**
51
+ * 1.2.8: viewer role. Drives which tabs render, which KPI cards
52
+ * appear, and whether the Recent translations table scopes by
53
+ * ownership. `admin` keeps the pre-1.2.8 surface; `editor` hides
54
+ * Configuration / Audit / Advanced tabs and reframes Overview around
55
+ * "your runs" (still shows other editors' runs as read-only with
56
+ * attribution per Decision #31-revised).
57
+ */
58
+ role: 'admin' | 'editor';
59
+ /**
60
+ * 1.2.8: current user id (stringified, matches the persisted
61
+ * `triggeredByUserId` column shape). Used by the UsageTable to
62
+ * decide which rows are owned by the current viewer so the action
63
+ * menu is shown for own rows and hidden for others.
64
+ */
65
+ currentUserId: string | null;
66
+ /**
67
+ * 1.2.8: current user email. Mirrored into modal copy ("…this
68
+ * conflict, you tried to start a new run while…") so the editor
69
+ * sees their own email in the chrome.
70
+ */
71
+ currentUserEmail: string | null;
72
+ }
73
+ export declare const TranslationHubClient: React.FC<TranslationHubClientProps>;
74
+ export {};