@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,377 @@
1
+ import { DEFAULT_USAGE_COLLECTION_SLUG } from './defaults.js';
2
+ /**
3
+ * Factory for the auto-registered `translation-usage` collection.
4
+ *
5
+ * The plugin pushes this collection onto `config.collections` when
6
+ * `usageTracking.enabled` is true. Every translation job — succeeded or
7
+ * failed — writes one row from inside `lib/persist-usage.ts`. Cost data
8
+ * is treated as admin-only by default: `admin.hidden` keeps the entry
9
+ * out of the editor nav, and `access.read` blocks API reads even via
10
+ * direct URL.
11
+ *
12
+ * Mirrors the audit-log plugin's `createAuditLogsCollection` pattern.
13
+ */ export function createUsageCollection(readAccess, collectionSlug) {
14
+ const slug = collectionSlug ?? DEFAULT_USAGE_COLLECTION_SLUG;
15
+ const defaultAdminRead = ({ req })=>req?.user?.roles?.includes('admin') ?? false;
16
+ return {
17
+ slug,
18
+ // System rows are never edited in the admin document view, so
19
+ // Payload's document-locking buys nothing here — and its lock check
20
+ // costs a second pool connection inside every update transaction
21
+ // (core's checkDocumentLockStatus runs a find without `req`). Under
22
+ // concurrent updates (e.g. dismiss-all on alerts) that exhausted the
23
+ // pool and deadlocked a consumer in prod on 2026-06-10.
24
+ lockDocuments: false,
25
+ labels: {
26
+ singular: 'Translation Usage',
27
+ plural: 'Translation Usage'
28
+ },
29
+ admin: {
30
+ useAsTitle: 'jobId',
31
+ group: 'System',
32
+ // Hide from non-admins entirely so editors don't see cost data in the
33
+ // nav. The collection is also blocked at the API layer via access.read.
34
+ hidden: ({ user })=>!user?.roles?.includes('admin'),
35
+ description: 'One row per translation job (collection or global). Populated automatically by the plugin after each translate run — succeeded, failed, or partial. Empty until the first translate fires; rows are read-only and never created from the UI.',
36
+ defaultColumns: [
37
+ 'createdAt',
38
+ 'kind',
39
+ 'slug',
40
+ 'documentId',
41
+ 'status',
42
+ 'estimatedCostUsd',
43
+ 'model'
44
+ ]
45
+ },
46
+ access: {
47
+ read: readAccess ?? defaultAdminRead,
48
+ create: ()=>false,
49
+ update: ()=>false,
50
+ delete: ()=>false
51
+ },
52
+ graphQL: false,
53
+ timestamps: true,
54
+ fields: [
55
+ {
56
+ name: 'kind',
57
+ type: 'select',
58
+ required: true,
59
+ index: true,
60
+ options: [
61
+ {
62
+ label: 'Collection',
63
+ value: 'collection'
64
+ },
65
+ {
66
+ label: 'Global',
67
+ value: 'global'
68
+ }
69
+ ],
70
+ admin: {
71
+ readOnly: true
72
+ }
73
+ },
74
+ {
75
+ name: 'jobId',
76
+ type: 'text',
77
+ required: true,
78
+ index: true,
79
+ admin: {
80
+ readOnly: true
81
+ }
82
+ },
83
+ {
84
+ name: 'slug',
85
+ type: 'text',
86
+ required: true,
87
+ index: true,
88
+ admin: {
89
+ readOnly: true,
90
+ description: 'Collection slug or global slug.'
91
+ }
92
+ },
93
+ {
94
+ name: 'documentId',
95
+ type: 'text',
96
+ index: true,
97
+ admin: {
98
+ readOnly: true,
99
+ description: 'For globals this equals the slug.'
100
+ }
101
+ },
102
+ {
103
+ name: 'status',
104
+ type: 'select',
105
+ required: true,
106
+ index: true,
107
+ options: [
108
+ {
109
+ label: 'Succeeded',
110
+ value: 'succeeded'
111
+ },
112
+ {
113
+ label: 'Failed',
114
+ value: 'failed'
115
+ }
116
+ ],
117
+ admin: {
118
+ readOnly: true
119
+ }
120
+ },
121
+ {
122
+ name: 'sourceLocale',
123
+ type: 'text',
124
+ required: true,
125
+ admin: {
126
+ readOnly: true
127
+ }
128
+ },
129
+ {
130
+ name: 'targetLocales',
131
+ type: 'array',
132
+ admin: {
133
+ readOnly: true,
134
+ description: 'One row per target locale. `status` and `error` capture which locales failed and why; the top-level `error` field is now just an aggregate count.'
135
+ },
136
+ fields: [
137
+ {
138
+ name: 'locale',
139
+ type: 'text',
140
+ required: true
141
+ },
142
+ {
143
+ name: 'status',
144
+ type: 'select',
145
+ options: [
146
+ {
147
+ label: 'Succeeded',
148
+ value: 'succeeded'
149
+ },
150
+ {
151
+ label: 'Failed',
152
+ value: 'failed'
153
+ }
154
+ ],
155
+ admin: {
156
+ readOnly: true
157
+ }
158
+ },
159
+ {
160
+ name: 'error',
161
+ type: 'text',
162
+ admin: {
163
+ readOnly: true,
164
+ description: 'Verbatim provider error message for the first failed field in this locale. Empty when locale succeeded.'
165
+ }
166
+ },
167
+ {
168
+ name: 'errorCode',
169
+ type: 'text',
170
+ admin: {
171
+ readOnly: true,
172
+ description: 'Editor-facing code (1.2.5+) that the Translation Hub uses to render a friendly message. Empty on legacy rows; UI falls back to a generic message.'
173
+ }
174
+ },
175
+ {
176
+ name: 'failedFields',
177
+ type: 'array',
178
+ admin: {
179
+ readOnly: true
180
+ },
181
+ fields: [
182
+ {
183
+ name: 'path',
184
+ type: 'text',
185
+ required: true
186
+ }
187
+ ]
188
+ }
189
+ ]
190
+ },
191
+ {
192
+ name: 'succeededCount',
193
+ type: 'number',
194
+ required: true,
195
+ admin: {
196
+ readOnly: true,
197
+ description: 'Total field × locale results marked success. Includes hash-skipped fields. Use fieldsTranslated / fieldsHashSkipped for the breakdown.'
198
+ }
199
+ },
200
+ {
201
+ name: 'failedCount',
202
+ type: 'number',
203
+ required: true,
204
+ admin: {
205
+ readOnly: true,
206
+ description: 'Counts field × locale results, not distinct locales.'
207
+ }
208
+ },
209
+ {
210
+ // Field-locale entries that actually hit the LLM. Excludes
211
+ // hash-skipped entries (source unchanged since last translate).
212
+ // `succeededCount = fieldsTranslated + fieldsHashSkipped`.
213
+ name: 'fieldsTranslated',
214
+ type: 'number',
215
+ admin: {
216
+ readOnly: true,
217
+ description: 'Field × locale entries that actually called the LLM this run.'
218
+ }
219
+ },
220
+ {
221
+ // Field-locale entries where the source content hash matched the
222
+ // last-translated source hash → no LLM call (cost-saving skip).
223
+ // Detected via `characterCount === 0 && durationMs === 0` in
224
+ // FieldLocaleResult coming back from translate-for-locale.
225
+ name: 'fieldsHashSkipped',
226
+ type: 'number',
227
+ admin: {
228
+ readOnly: true,
229
+ description: 'Field × locale entries skipped because the source content was unchanged since the last translation — no LLM call.'
230
+ }
231
+ },
232
+ {
233
+ // Field-locale entries the manual-edit guard kept out of the
234
+ // write because the target locale value diverged from the
235
+ // plugin\'s lastWrittenHash (assumed manual edit).
236
+ name: 'fieldsPreserved',
237
+ type: 'number',
238
+ admin: {
239
+ readOnly: true,
240
+ description: 'Field × locale entries preserved because the target locale value was manually edited since the last plugin write.'
241
+ }
242
+ },
243
+ {
244
+ // Field-locale entries where the LLM echoed source verbatim or
245
+ // returned output that failed validation. The source value is
246
+ // kept; the field is not written.
247
+ name: 'fieldsSoftSkipped',
248
+ type: 'number',
249
+ admin: {
250
+ readOnly: true,
251
+ description: 'Field × locale entries soft-skipped (LLM echoed source or returned invalid output).'
252
+ }
253
+ },
254
+ {
255
+ // Detail rows for soft-skipped fields. Useful for editors to
256
+ // know WHY a field stayed in source rather than seeing a single
257
+ // aggregate number.
258
+ name: 'softSkippedFields',
259
+ type: 'array',
260
+ admin: {
261
+ readOnly: true
262
+ },
263
+ fields: [
264
+ {
265
+ name: 'path',
266
+ type: 'text',
267
+ required: true
268
+ },
269
+ {
270
+ name: 'locale',
271
+ type: 'text',
272
+ required: true
273
+ },
274
+ {
275
+ name: 'reason',
276
+ type: 'text'
277
+ },
278
+ {
279
+ name: 'reasonCode',
280
+ type: 'text',
281
+ admin: {
282
+ readOnly: true,
283
+ description: 'Editor-facing code (1.2.5+) that the Translation Hub uses to render a friendly message for this soft-skipped field. Empty on legacy rows.'
284
+ }
285
+ },
286
+ {
287
+ // The actual source-locale text the AI declined to translate.
288
+ // Lets editors see WHAT was kept (e.g. "Wheel Of", "VIP") without
289
+ // opening the doc — they can usually decide "yes that's a brand,
290
+ // leave it" or "no, that needed translation" from the value alone.
291
+ // Empty on legacy rows persisted before 1.2.8.
292
+ name: 'sourceValue',
293
+ type: 'text',
294
+ admin: {
295
+ readOnly: true,
296
+ description: "Source-locale value the AI kept as-is. Surfaced in the Hub so editors don't need to open the doc to decide whether the skip was correct."
297
+ }
298
+ }
299
+ ]
300
+ },
301
+ {
302
+ // Detail rows for fields the manual-edit guard preserved. Lets
303
+ // editors see WHICH fields are being protected (without this,
304
+ // they only see a count and can't audit whether the right fields
305
+ // are guarded). No `reason` — the reason is always "target
306
+ // value diverges from plugin's lastWrittenHash."
307
+ name: 'preservedFields',
308
+ type: 'array',
309
+ admin: {
310
+ readOnly: true
311
+ },
312
+ fields: [
313
+ {
314
+ name: 'path',
315
+ type: 'text',
316
+ required: true
317
+ },
318
+ {
319
+ name: 'locale',
320
+ type: 'text',
321
+ required: true
322
+ }
323
+ ]
324
+ },
325
+ {
326
+ name: 'inputTokens',
327
+ type: 'number',
328
+ required: true,
329
+ admin: {
330
+ readOnly: true
331
+ }
332
+ },
333
+ {
334
+ name: 'outputTokens',
335
+ type: 'number',
336
+ required: true,
337
+ admin: {
338
+ readOnly: true
339
+ }
340
+ },
341
+ {
342
+ name: 'estimatedCostUsd',
343
+ type: 'number',
344
+ admin: {
345
+ readOnly: true,
346
+ components: {
347
+ // Sub-dollar costs (gpt-4o-mini, claude-haiku) round to 0 with the
348
+ // default Number cell. Format as a small-precision currency so a
349
+ // 10c translation doesn't read as "$0".
350
+ Cell: '@purposeinplay/payload-ai-translate/client#EstimatedCostCell'
351
+ }
352
+ }
353
+ },
354
+ {
355
+ name: 'model',
356
+ type: 'text',
357
+ admin: {
358
+ readOnly: true
359
+ }
360
+ },
361
+ {
362
+ name: 'durationMs',
363
+ type: 'number',
364
+ admin: {
365
+ readOnly: true
366
+ }
367
+ },
368
+ {
369
+ name: 'error',
370
+ type: 'text',
371
+ admin: {
372
+ readOnly: true
373
+ }
374
+ }
375
+ ]
376
+ };
377
+ }
@@ -0,0 +1,32 @@
1
+ import type React from 'react';
2
+ import type { BulkTranslateBatchSummary } from '../TranslationHub/BulkTranslate.types.js';
3
+ /**
4
+ * Renders the batch progress cell as either an empty-state label or a
5
+ * "X / Y" fraction. Extracted as a pure helper so BR-4 ("0 / 0" shown
6
+ * for empty batches reads ambiguous) can be pinned with a unit test
7
+ * — see `__tests__/BatchRow.helpers.test.ts`.
8
+ *
9
+ * - totalUnits === 0 → `{ kind: 'empty' }` and the cell renders the
10
+ * italic "No units enqueued" copy (typically a cancelled-before-
11
+ * start run or an empty-scope selection).
12
+ * - totalUnits > 0 → `{ kind: 'fraction', completed, total }`.
13
+ */
14
+ export type BatchProgressDescriptor = {
15
+ kind: 'empty';
16
+ } | {
17
+ kind: 'fraction';
18
+ completed: number;
19
+ total: number;
20
+ percent: number;
21
+ };
22
+ export declare function describeBatchProgress(completedUnits: number, totalUnits: number): BatchProgressDescriptor;
23
+ interface BatchRowProps {
24
+ basePath: string;
25
+ batch: BulkTranslateBatchSummary;
26
+ isExpanded: boolean;
27
+ onToggle: () => void;
28
+ onAfterAction: () => void;
29
+ colCount: number;
30
+ }
31
+ export declare const BatchRow: React.FC<BatchRowProps>;
32
+ export {};