@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,22 @@
1
+ import type React from 'react';
2
+ import type { BulkRunsTimeRange, BulkTranslateBatchSummary } from '../TranslationHub/BulkTranslate.types.js';
3
+ interface Props {
4
+ /** All currently loaded batches (full dataset, not just the page slice). */
5
+ batches: BulkTranslateBatchSummary[];
6
+ /** Total count from the API (`total` field in list response). */
7
+ total: number;
8
+ timeRange: BulkRunsTimeRange;
9
+ onTimeRangeChange: (r: BulkRunsTimeRange) => void;
10
+ /**
11
+ * ROUND5-3 (v1.2.6): when the filter bar has explicit `since` /
12
+ * `until` dates that don't match a preset, the preset pills must
13
+ * NOT advertise themselves as the active window — the table is
14
+ * filtered by the custom range, the KPIs are scoped to the preset,
15
+ * and showing "Last 7 days" pressed contradicts both surfaces.
16
+ * When true, all preset pills go inactive and a read-only "Custom"
17
+ * pill lights up to communicate the actual state.
18
+ */
19
+ hasCustomDateFilter?: boolean;
20
+ }
21
+ export declare const SummaryStrip: React.FC<Props>;
22
+ export {};
@@ -0,0 +1,249 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { filterPillColors } from '../shared/filterPillStyle.js';
4
+ import { formatCost } from '../shared/format.js';
5
+ import { isActiveStatus } from '../TranslationHub/BulkTranslate.types.js';
6
+ // ---------------------------------------------------------------------------
7
+ // Shared styles — mirroring StatusStrip.tsx CARD_STYLE exactly.
8
+ // ---------------------------------------------------------------------------
9
+ const CARD_STYLE = {
10
+ flex: 1,
11
+ padding: '1rem 1.25rem',
12
+ background: 'var(--theme-elevation-50)',
13
+ border: '1px solid var(--theme-elevation-150)',
14
+ borderRadius: '6px'
15
+ };
16
+ const LABEL_STYLE = {
17
+ margin: 0,
18
+ fontSize: '0.75rem',
19
+ fontWeight: 600,
20
+ textTransform: 'uppercase',
21
+ letterSpacing: '0.05em',
22
+ color: 'var(--theme-elevation-500)'
23
+ };
24
+ const VALUE_STYLE = {
25
+ margin: '0.25rem 0 0',
26
+ fontSize: '1.5rem',
27
+ fontWeight: 600,
28
+ color: 'var(--theme-elevation-1000)'
29
+ };
30
+ const SUBVALUE_STYLE = {
31
+ margin: '0.25rem 0 0',
32
+ fontSize: '0.75rem',
33
+ color: 'var(--theme-elevation-500)'
34
+ };
35
+ // ---------------------------------------------------------------------------
36
+ // Time-range toggle
37
+ // ---------------------------------------------------------------------------
38
+ const TIME_RANGE_LABEL = {
39
+ '7d': 'Last 7 days',
40
+ '30d': 'Last 30 days',
41
+ all: 'All time'
42
+ };
43
+ const TIME_RANGE_MS = {
44
+ '7d': 7 * 24 * 60 * 60 * 1000,
45
+ '30d': 30 * 24 * 60 * 60 * 1000,
46
+ all: null
47
+ };
48
+ // ---------------------------------------------------------------------------
49
+ // Helpers
50
+ // ---------------------------------------------------------------------------
51
+ function fmtNum(n) {
52
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
53
+ if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;
54
+ return n.toString();
55
+ }
56
+ function relTime(iso) {
57
+ if (!iso) return '—';
58
+ const diff = Date.now() - new Date(iso).getTime();
59
+ const s = Math.floor(diff / 1000);
60
+ if (s < 60) return `${s}s ago`;
61
+ if (s < 3600) return `${Math.floor(s / 60)}m ago`;
62
+ if (s < 86_400) return `${Math.floor(s / 3600)}h ago`;
63
+ return `${Math.floor(s / 86_400)}d ago`;
64
+ }
65
+ function filterByRange(batches, range) {
66
+ const ms = TIME_RANGE_MS[range];
67
+ if (ms === null) return batches;
68
+ const cutoff = Date.now() - ms;
69
+ return batches.filter((b)=>new Date(b.createdAt).getTime() >= cutoff);
70
+ }
71
+ export const SummaryStrip = ({ batches, total, timeRange, onTimeRangeChange, hasCustomDateFilter = false })=>{
72
+ const activeNow = batches.filter((b)=>isActiveStatus(b.status)).length;
73
+ const inWindow = filterByRange(batches, timeRange);
74
+ const windowCost = inWindow.reduce((sum, b)=>sum + (b.actualCostUsd ?? b.estimatedCostUsd ?? 0), 0);
75
+ const windowUnits = inWindow.reduce((sum, b)=>sum + b.totalUnits, 0);
76
+ // Last completed run — pick the most recent completedAt across all loaded
77
+ // batches (not just the window, so it's always meaningful).
78
+ const lastCompletedAt = batches.reduce((latest, b)=>{
79
+ if (!b.completedAt) return latest;
80
+ if (latest === null) return b.completedAt;
81
+ return b.completedAt > latest ? b.completedAt : latest;
82
+ }, null);
83
+ return /*#__PURE__*/ _jsxs("div", {
84
+ style: {
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ gap: '0.75rem'
88
+ },
89
+ children: [
90
+ /*#__PURE__*/ _jsxs("div", {
91
+ style: {
92
+ display: 'flex',
93
+ alignItems: 'center',
94
+ justifyContent: 'flex-end',
95
+ gap: '0.25rem'
96
+ },
97
+ children: [
98
+ Object.keys(TIME_RANGE_LABEL).map((t)=>{
99
+ // ROUND5-3 (v1.2.6): when a custom date range is in effect
100
+ // via the filter bar, no preset is the "active" window —
101
+ // de-activate every preset pill so the row stops claiming a
102
+ // window that contradicts the table.
103
+ const isActive = !hasCustomDateFilter && timeRange === t;
104
+ const colors = filterPillColors(isActive);
105
+ return /*#__PURE__*/ _jsx("button", {
106
+ "aria-pressed": isActive,
107
+ type: "button",
108
+ onClick: ()=>onTimeRangeChange(t),
109
+ style: {
110
+ padding: '0.3rem 0.65rem',
111
+ fontSize: '0.75rem',
112
+ background: colors.background,
113
+ border: colors.border,
114
+ borderRadius: '4px',
115
+ color: colors.color,
116
+ cursor: 'pointer',
117
+ fontWeight: isActive ? 600 : 400
118
+ },
119
+ children: TIME_RANGE_LABEL[t]
120
+ }, t);
121
+ }),
122
+ hasCustomDateFilter && (()=>{
123
+ const colors = filterPillColors(true);
124
+ return /*#__PURE__*/ _jsx("span", {
125
+ "aria-pressed": "true",
126
+ role: "status",
127
+ style: {
128
+ padding: '0.3rem 0.65rem',
129
+ fontSize: '0.75rem',
130
+ background: colors.background,
131
+ border: colors.border,
132
+ borderRadius: '4px',
133
+ color: colors.color,
134
+ fontWeight: 600,
135
+ cursor: 'default'
136
+ },
137
+ title: "Custom date range active via the filter bar below. Clear the Since / Until fields to return to a preset window.",
138
+ children: "Custom"
139
+ });
140
+ })()
141
+ ]
142
+ }),
143
+ /*#__PURE__*/ _jsxs("div", {
144
+ style: {
145
+ display: 'flex',
146
+ gap: '1rem'
147
+ },
148
+ children: [
149
+ /*#__PURE__*/ _jsxs("div", {
150
+ style: CARD_STYLE,
151
+ children: [
152
+ /*#__PURE__*/ _jsx("p", {
153
+ style: LABEL_STYLE,
154
+ children: "Active now"
155
+ }),
156
+ /*#__PURE__*/ _jsx("p", {
157
+ style: VALUE_STYLE,
158
+ children: activeNow
159
+ }),
160
+ /*#__PURE__*/ _jsx("p", {
161
+ style: SUBVALUE_STYLE,
162
+ children: activeNow === 0 ? 'No runs in progress' : 'Run(s) in progress'
163
+ })
164
+ ]
165
+ }),
166
+ /*#__PURE__*/ _jsxs("div", {
167
+ style: CARD_STYLE,
168
+ children: [
169
+ /*#__PURE__*/ _jsx("p", {
170
+ style: LABEL_STYLE,
171
+ children: "Total batches"
172
+ }),
173
+ /*#__PURE__*/ _jsx("p", {
174
+ style: VALUE_STYLE,
175
+ children: fmtNum(total)
176
+ }),
177
+ /*#__PURE__*/ _jsx("p", {
178
+ style: SUBVALUE_STYLE,
179
+ children: "All time"
180
+ })
181
+ ]
182
+ }),
183
+ /*#__PURE__*/ _jsxs("div", {
184
+ style: CARD_STYLE,
185
+ children: [
186
+ /*#__PURE__*/ _jsxs("p", {
187
+ style: LABEL_STYLE,
188
+ children: [
189
+ TIME_RANGE_LABEL[timeRange],
190
+ " cost"
191
+ ]
192
+ }),
193
+ /*#__PURE__*/ _jsx("p", {
194
+ style: VALUE_STYLE,
195
+ children: formatCost(windowCost)
196
+ }),
197
+ /*#__PURE__*/ _jsx("p", {
198
+ style: SUBVALUE_STYLE,
199
+ children: "Actual spend (loaded batches)"
200
+ })
201
+ ]
202
+ }),
203
+ /*#__PURE__*/ _jsxs("div", {
204
+ style: CARD_STYLE,
205
+ children: [
206
+ /*#__PURE__*/ _jsxs("p", {
207
+ style: LABEL_STYLE,
208
+ children: [
209
+ TIME_RANGE_LABEL[timeRange],
210
+ " units"
211
+ ]
212
+ }),
213
+ /*#__PURE__*/ _jsx("p", {
214
+ style: VALUE_STYLE,
215
+ children: fmtNum(windowUnits)
216
+ }),
217
+ /*#__PURE__*/ _jsx("p", {
218
+ style: SUBVALUE_STYLE,
219
+ children: "Translation units processed"
220
+ })
221
+ ]
222
+ }),
223
+ /*#__PURE__*/ _jsxs("div", {
224
+ style: CARD_STYLE,
225
+ children: [
226
+ /*#__PURE__*/ _jsx("p", {
227
+ style: LABEL_STYLE,
228
+ children: "Last run"
229
+ }),
230
+ /*#__PURE__*/ _jsx("p", {
231
+ style: VALUE_STYLE,
232
+ title: lastCompletedAt ?? undefined,
233
+ children: lastCompletedAt ? relTime(lastCompletedAt) : '—'
234
+ }),
235
+ /*#__PURE__*/ _jsx("p", {
236
+ style: SUBVALUE_STYLE,
237
+ children: lastCompletedAt ? new Date(lastCompletedAt).toLocaleDateString(undefined, {
238
+ month: 'short',
239
+ day: 'numeric',
240
+ year: 'numeric'
241
+ }) : 'No completed runs yet'
242
+ })
243
+ ]
244
+ })
245
+ ]
246
+ })
247
+ ]
248
+ });
249
+ };
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Group a flat list of `BatchJobSummary` units into a 2-level tree:
3
+ *
4
+ * Bucket (collection or global)
5
+ * └── Document
6
+ * └── Locales (rendered inline as chips, not as separate rows)
7
+ *
8
+ * Editor mental model is collection-shaped, not unit-shaped — direct quote:
9
+ * "let's say 50 docs from one collection fail but 150 are completed maybe
10
+ * I want to trigger retry for those 50 easily." The flat list doesn't
11
+ * surface that grouping; this helper does.
12
+ *
13
+ * Heuristic for distinguishing globals from collections: the worker stores
14
+ * `documentId === collection` for globals (the slug doubles as the id),
15
+ * whereas collections have a numeric / UUID document id. Confirmed against
16
+ * `api.ts:translateGlobal` which returns
17
+ * `{ documentId: options.global, collection: options.global, ... }`.
18
+ */
19
+ import type { BatchJobSummary, BatchUnitCountsByCollection } from '../TranslationHub/BulkTranslate.types.js';
20
+ export type DocGroup = {
21
+ collection: string;
22
+ documentId: string;
23
+ /** One entry per locale for this (collection, docId). */
24
+ jobs: BatchJobSummary[];
25
+ /** How many of this doc's locales finished as `failed`. */
26
+ failedLocaleCount: number;
27
+ /** How many of this doc's locales finished as `success`. */
28
+ succeededLocaleCount: number;
29
+ /**
30
+ * Per-doc wallclock span: `max(completedAt) − min(startedAt)` across
31
+ * this doc's locales. Primary duration metric at the doc level —
32
+ * answers "from when we started translating this doc to when the
33
+ * last locale finished." Includes any throttle queue wait the doc's
34
+ * locales sat through. Null when timestamps aren't available
35
+ * (in-flight unit).
36
+ */
37
+ docSpanMs: number | null;
38
+ /**
39
+ * Sum of `processingDurationMs` across this doc's locales — total AI
40
+ * compute time for the doc. Null on legacy units that pre-date the
41
+ * `processingDurationMs` column.
42
+ */
43
+ docAiActiveMs: number | null;
44
+ /**
45
+ * `docSpanMs − docAiActiveMs`, clamped ≥0. Pairs with `docAiActiveMs`
46
+ * as the secondary "AI active / queue wait" sub-line. Null when
47
+ * either source is null.
48
+ */
49
+ docQueueWaitMs: number | null;
50
+ /** Total cost across all locales for this doc, in USD. */
51
+ totalCostUsd: number;
52
+ /** Max attempts across this doc's locales. */
53
+ maxAttempts: number;
54
+ };
55
+ export type Bucket = {
56
+ /** Collection slug, or global slug. Used as the bucket key. */
57
+ collection: string;
58
+ /** `true` when this is a global (single row), `false` for collections. */
59
+ isGlobal: boolean;
60
+ /** Docs in this bucket, keyed by `documentId`. Ordered insertion. */
61
+ docs: Map<string, DocGroup>;
62
+ /**
63
+ * Total units across the ENTIRE bucket (from the backend's
64
+ * `countsByCollection` aggregate). Not just the paginated loaded
65
+ * subset. The doc rows in `docs` may be a subset of this — render
66
+ * `docs.size` of `totalDocs` to communicate the gap.
67
+ */
68
+ totalUnits: number;
69
+ succeededCount: number;
70
+ failedCount: number;
71
+ pendingCount: number;
72
+ runningCount: number;
73
+ skippedCount: number;
74
+ revertedCount: number;
75
+ /** Distinct documents in this bucket (from backend, not paginated). */
76
+ totalDocs: number;
77
+ /** Docs actually loaded into the `docs` map. ≤ totalDocs when paginated. */
78
+ loadedDocs: number;
79
+ /** Top-2 failure codes by frequency, for the bucket header summary. */
80
+ topFailureCodes: Array<{
81
+ code: string;
82
+ count: number;
83
+ }>;
84
+ /** Distinct failure code count — used to decide "show breakdown" CTA. */
85
+ distinctFailureCodes: number;
86
+ /** UX-spec per-collection duration aggregates. See `BatchUnitCountsByCollection`. */
87
+ collectionSpanMs: number | null;
88
+ aiActiveMs: number | null;
89
+ queueWaitMs: number | null;
90
+ /**
91
+ * v1.2.7: filter-aware doc counts per bucket. Keyed by the UI filter
92
+ * chip name (`all`, `pending`, `running`, `completed`, `failed`,
93
+ * `skipped`, `reverted`). Drives the bucket-header count + the
94
+ * filter-aware "hide bucket when 0" rule. Falls back to a derived
95
+ * shape from the scalar counts when the backend response is pre-1.2.7.
96
+ */
97
+ docCountsByStatus: {
98
+ all: number;
99
+ pending: number;
100
+ running: number;
101
+ completed: number;
102
+ failed: number;
103
+ skipped: number;
104
+ reverted: number;
105
+ };
106
+ };
107
+ /**
108
+ * Filter chip values shared with the BatchRow drill-down. Mirrors
109
+ * `UNIT_FILTER_CHIPS` there — duplicated here so the bucket helpers can
110
+ * type the active filter without importing the React module (which
111
+ * would create a cycle).
112
+ */
113
+ export type BucketStatusFilter = 'all' | 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'reverted';
114
+ /**
115
+ * Resolve the bucket header count for the active filter pill. Returns a
116
+ * UNIT-level count (the historical semantics — `succeededCount`,
117
+ * `failedCount` etc. on the bucket are unit-based). When the filter is
118
+ * `all`, returns `totalUnits`. Otherwise returns the matching scalar.
119
+ *
120
+ * Doc-level counts (`docCountsByStatus`) stay on the bucket for callers
121
+ * that need "how many distinct docs match this filter" — e.g. the
122
+ * `shouldBucketBeVisibleUnderFilter` rule below, which has to know
123
+ * whether any docs at all will render under the active filter.
124
+ */
125
+ export declare function getBucketCountForFilter(bucket: Pick<Bucket, 'totalUnits' | 'succeededCount' | 'failedCount' | 'pendingCount' | 'runningCount' | 'skippedCount' | 'revertedCount'>, filter: BucketStatusFilter): number;
126
+ /**
127
+ * Decide whether a bucket should render at all under the active filter.
128
+ * Hides buckets that have zero matching docs when a non-`all` filter is
129
+ * active — the previous behaviour rendered the bucket header with stale
130
+ * unfiltered counts, contradicting the global "No units match" message
131
+ * below. With this rule the entire bucket disappears, and the global
132
+ * empty state surfaces only when EVERY bucket is empty.
133
+ *
134
+ * Uses the DOC count rather than the unit count because the expanded
135
+ * panel renders one row per doc. A bucket with one doc whose only
136
+ * failed locale we'd be looking at is still worth showing — but only
137
+ * if at least one such doc exists.
138
+ */
139
+ export declare function shouldBucketBeVisibleUnderFilter(bucket: Pick<Bucket, 'docCountsByStatus' | 'totalDocs'>, filter: BucketStatusFilter): boolean;
140
+ /**
141
+ * Group the flat `jobs` array into buckets, using backend
142
+ * `countsByCollection` for the authoritative per-bucket totals.
143
+ *
144
+ * Two passes:
145
+ * 1. Seed every bucket from `countsByCollection` (the FULL totals
146
+ * across the batch — not just the paginated `jobs` page). This
147
+ * ensures bucket headers show correct counts even when the
148
+ * `jobs` page only contains a subset.
149
+ * 2. Walk `jobs` and attach the loaded docs to their buckets.
150
+ * `bucket.loadedDocs < bucket.totalDocs` when the page hasn't
151
+ * loaded every doc in the bucket — UI surfaces this gap.
152
+ *
153
+ * Sort order:
154
+ * 1. Buckets with failures come first (descending failure count).
155
+ * 2. Then collections alphabetically.
156
+ * 3. Then globals alphabetically (any bucket with `isGlobal: true`).
157
+ *
158
+ * Within a bucket, docs are kept in their original insertion order from
159
+ * the `jobs` array — Payload's status endpoint already sorts by unit id
160
+ * (closely aligned with creation order), which is a reasonable default.
161
+ */
162
+ export declare function groupJobsIntoBuckets(jobs: BatchJobSummary[], countsByCollection?: BatchUnitCountsByCollection): Bucket[];
163
+ /**
164
+ * Whether a bucket should start expanded on initial render. Per UX
165
+ * recommendation: failed buckets expanded by default (problem is on
166
+ * screen without clicking), all-success buckets collapsed (don't
167
+ * clutter the triage view).
168
+ */
169
+ export declare function shouldBucketBeExpandedByDefault(bucket: Bucket): boolean;
170
+ /**
171
+ * Compact duration helper used by `fmtBucketAiActive` /
172
+ * `fmtBucketQueued`. Thin wrapper around the shared `formatDuration`
173
+ * helper — kept as a separate export so the bucket-grouping module
174
+ * doesn't need to import from `views/shared/format.ts` directly at
175
+ * every call site, and so the bucket-runtime tests keep their
176
+ * historical name. NEW-18 (v1.2.6): carries minutes → hours → days
177
+ * via the shared helper; the legacy implementation capped at minutes
178
+ * (`964m 6s`).
179
+ */
180
+ export declare function fmtBucketDurationShort(ms: number | null | undefined): string;
181
+ type BucketDurations = {
182
+ aiActiveMs: number | null;
183
+ collectionSpanMs: number | null;
184
+ queueWaitMs: number | null;
185
+ };
186
+ /**
187
+ * Render the bucket header "AI active" cell. When `aiActiveMs` exceeds
188
+ * `collectionSpanMs` (parallelism), present as `≈<span>` rather than a
189
+ * number that visually contradicts the total wallclock. The raw numbers
190
+ * are unchanged; the tooltip still shows both.
191
+ */
192
+ export declare function fmtBucketAiActive(bucket: Pick<BucketDurations, 'aiActiveMs' | 'collectionSpanMs'>): string;
193
+ /**
194
+ * Render the bucket header "Queued" cell. Uses the server-clamped queue
195
+ * wait but renders `0` as `'0s'` (instead of `'—'`) so editors can tell
196
+ * "no wait" from "data missing." When AI overran span, the queue wait
197
+ * is necessarily zero — surface that explicitly.
198
+ */
199
+ export declare function fmtBucketQueued(bucket: BucketDurations): string;
200
+ export {};