portal-mojo 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 (369) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -0
  3. package/docs/DetailView.md +173 -0
  4. package/docs/ModelTable.md +128 -0
  5. package/docs/README.md +98 -0
  6. package/docs/admin-assistant.md +72 -0
  7. package/docs/admin-bouncer.md +120 -0
  8. package/docs/admin-cloudwatch.md +38 -0
  9. package/docs/admin-credentials.md +116 -0
  10. package/docs/admin-dashboard.md +15 -0
  11. package/docs/admin-devices-geoip.md +284 -0
  12. package/docs/admin-dns-certificates.md +122 -0
  13. package/docs/admin-dns-records.md +65 -0
  14. package/docs/admin-dns-registrar.md +33 -0
  15. package/docs/admin-dns.md +80 -0
  16. package/docs/admin-identity-users.md +103 -0
  17. package/docs/admin-incidents.md +71 -0
  18. package/docs/admin-jobs.md +426 -0
  19. package/docs/admin-members.md +112 -0
  20. package/docs/admin-messaging.md +56 -0
  21. package/docs/admin-metrics-explorer.md +63 -0
  22. package/docs/admin-monitoring.md +99 -0
  23. package/docs/admin-network-security.md +393 -0
  24. package/docs/admin-phonehub.md +27 -0
  25. package/docs/admin-push.md +36 -0
  26. package/docs/admin-rules.md +87 -0
  27. package/docs/admin-security-tickets.md +40 -0
  28. package/docs/admin-settings.md +78 -0
  29. package/docs/admin-shortlinks.md +32 -0
  30. package/docs/admin-storage.md +141 -0
  31. package/docs/auth-pages.md +212 -0
  32. package/docs/auth.md +58 -0
  33. package/docs/calendar.md +122 -0
  34. package/docs/charts.md +303 -0
  35. package/docs/client.md +206 -0
  36. package/docs/collection-multiselect.md +117 -0
  37. package/docs/collection-select.md +132 -0
  38. package/docs/combobox.md +105 -0
  39. package/docs/dataview.md +226 -0
  40. package/docs/datepicker.md +151 -0
  41. package/docs/daterange.md +170 -0
  42. package/docs/datetimepicker.md +249 -0
  43. package/docs/defineModel.md +72 -0
  44. package/docs/detail-primitives.md +174 -0
  45. package/docs/feedback.md +85 -0
  46. package/docs/file-upload-ux.md +146 -0
  47. package/docs/forms.md +435 -0
  48. package/docs/grouping-and-fmt.md +98 -0
  49. package/docs/idioms.md +112 -0
  50. package/docs/image-editor.md +101 -0
  51. package/docs/loading.md +242 -0
  52. package/docs/location-address.md +76 -0
  53. package/docs/markdown.md +203 -0
  54. package/docs/menus-and-access.md +89 -0
  55. package/docs/multiselect-dropdown.md +153 -0
  56. package/docs/params.md +73 -0
  57. package/docs/popover.md +130 -0
  58. package/docs/realtime.md +61 -0
  59. package/docs/record-feed.md +238 -0
  60. package/docs/right-panel.md +42 -0
  61. package/docs/tabs-and-form-wizard.md +60 -0
  62. package/docs/taginput.md +120 -0
  63. package/docs/timepicker.md +173 -0
  64. package/docs/timezone-select.md +147 -0
  65. package/docs/worldmap.md +302 -0
  66. package/package.json +55 -0
  67. package/src/admin/assistant/AssistantFeed.tsx +29 -0
  68. package/src/admin/assistant/AssistantPanel.tsx +65 -0
  69. package/src/admin/assistant/api.ts +17 -0
  70. package/src/admin/assistant/data.ts +63 -0
  71. package/src/admin/assistant/index.ts +21 -0
  72. package/src/admin/assistant/launchers.tsx +33 -0
  73. package/src/admin/assistant/pages.tsx +78 -0
  74. package/src/admin/assistant/streaming.ts +238 -0
  75. package/src/admin/assistant/types.ts +30 -0
  76. package/src/admin/bouncer/devices.tsx +230 -0
  77. package/src/admin/bouncer/index.ts +34 -0
  78. package/src/admin/bouncer/models.ts +199 -0
  79. package/src/admin/bouncer/signals.tsx +182 -0
  80. package/src/admin/bouncer/signatures.tsx +220 -0
  81. package/src/admin/cloudwatch/CloudWatchChart.tsx +61 -0
  82. package/src/admin/cloudwatch/CloudWatchDashboardPage.tsx +52 -0
  83. package/src/admin/cloudwatch/CloudWatchResourceDetail.tsx +33 -0
  84. package/src/admin/cloudwatch/data.ts +163 -0
  85. package/src/admin/cloudwatch/index.ts +16 -0
  86. package/src/admin/credentials/group-api-keys.tsx +433 -0
  87. package/src/admin/credentials/index.ts +4 -0
  88. package/src/admin/credentials/models.ts +244 -0
  89. package/src/admin/credentials/secret-dialog.tsx +77 -0
  90. package/src/admin/credentials/webhook-subscriptions.tsx +423 -0
  91. package/src/admin/dashboard/AdminDashboardPage.tsx +101 -0
  92. package/src/admin/dashboard/data.ts +60 -0
  93. package/src/admin/dashboard/index.ts +2 -0
  94. package/src/admin/dns/CertificateDetail.tsx +115 -0
  95. package/src/admin/dns/CertificateLifecyclePoller.tsx +141 -0
  96. package/src/admin/dns/CertificateRequestDialog.tsx +103 -0
  97. package/src/admin/dns/CertificatesPage.tsx +85 -0
  98. package/src/admin/dns/DnsRecordEditor.tsx +110 -0
  99. package/src/admin/dns/DnsRecordsPage.tsx +36 -0
  100. package/src/admin/dns/DnsRecordsPanel.tsx +71 -0
  101. package/src/admin/dns/DomainCertificatesSection.tsx +40 -0
  102. package/src/admin/dns/DomainDetail.tsx +41 -0
  103. package/src/admin/dns/DomainOnboardingDialog.tsx +27 -0
  104. package/src/admin/dns/DomainPurchaseDetail.tsx +11 -0
  105. package/src/admin/dns/DomainPurchaseWizard.tsx +112 -0
  106. package/src/admin/dns/DomainPurchasesPage.tsx +16 -0
  107. package/src/admin/dns/DomainsPage.tsx +27 -0
  108. package/src/admin/dns/ProviderCredentialsPage.tsx +304 -0
  109. package/src/admin/dns/RegistrantContactPage.tsx +42 -0
  110. package/src/admin/dns/api.ts +515 -0
  111. package/src/admin/dns/certificate-data.ts +221 -0
  112. package/src/admin/dns/data.ts +32 -0
  113. package/src/admin/dns/dns-data.ts +266 -0
  114. package/src/admin/dns/dns-integration.ts +24 -0
  115. package/src/admin/dns/domain-links.ts +37 -0
  116. package/src/admin/dns/index.ts +86 -0
  117. package/src/admin/dns/models.ts +418 -0
  118. package/src/admin/dns/purchase-data.ts +67 -0
  119. package/src/admin/dns/registrant-data.ts +52 -0
  120. package/src/admin/identity/index.ts +5 -0
  121. package/src/admin/identity/members/GroupMembersPanel.tsx +146 -0
  122. package/src/admin/identity/members/MemberDetail.tsx +226 -0
  123. package/src/admin/identity/members/MembersPage.tsx +144 -0
  124. package/src/admin/identity/members/index.ts +24 -0
  125. package/src/admin/identity/members/member-flows.tsx +132 -0
  126. package/src/admin/identity/members/models.ts +204 -0
  127. package/src/admin/identity/users/UserDetail.tsx +257 -0
  128. package/src/admin/identity/users/UsersPage.tsx +163 -0
  129. package/src/admin/identity/users/index.ts +27 -0
  130. package/src/admin/identity/users/models.ts +232 -0
  131. package/src/admin/identity/users/sections/ApiKeysSection.tsx +119 -0
  132. package/src/admin/identity/users/sections/AuditSection.tsx +190 -0
  133. package/src/admin/identity/users/sections/DevicesSection.tsx +140 -0
  134. package/src/admin/identity/users/sections/GroupsSection.tsx +64 -0
  135. package/src/admin/identity/users/sections/LoginsSection.tsx +106 -0
  136. package/src/admin/identity/users/sections/MetadataPanel.tsx +36 -0
  137. package/src/admin/identity/users/sections/NotificationsSection.tsx +117 -0
  138. package/src/admin/identity/users/sections/OAuthSection.tsx +72 -0
  139. package/src/admin/identity/users/sections/OverviewSection.tsx +109 -0
  140. package/src/admin/identity/users/sections/PermissionsSection.tsx +35 -0
  141. package/src/admin/identity/users/sections/PersonalSection.tsx +73 -0
  142. package/src/admin/identity/users/sections/ProfileSection.tsx +175 -0
  143. package/src/admin/identity/users/sections/SecuritySection.tsx +123 -0
  144. package/src/admin/identity/users/sections/actions.tsx +752 -0
  145. package/src/admin/identity/users/sections/permission-catalog.ts +138 -0
  146. package/src/admin/identity/users/sections/queries.ts +74 -0
  147. package/src/admin/identity/users/sections/shared.tsx +299 -0
  148. package/src/admin/incidents/EventDetail.tsx +43 -0
  149. package/src/admin/incidents/EventsPage.tsx +52 -0
  150. package/src/admin/incidents/IncidentDetail.tsx +68 -0
  151. package/src/admin/incidents/IncidentsPage.tsx +102 -0
  152. package/src/admin/incidents/forensics.tsx +73 -0
  153. package/src/admin/incidents/index.ts +16 -0
  154. package/src/admin/incidents/models.ts +113 -0
  155. package/src/admin/incidents/sanitize.ts +71 -0
  156. package/src/admin/index.ts +383 -0
  157. package/src/admin/jobs/JobDashboardPage.tsx +111 -0
  158. package/src/admin/jobs/JobDetail.tsx +721 -0
  159. package/src/admin/jobs/JobRunnersPage.tsx +216 -0
  160. package/src/admin/jobs/JobsTablePage.tsx +122 -0
  161. package/src/admin/jobs/RunnerDetail.tsx +576 -0
  162. package/src/admin/jobs/ScheduledTaskDetail.tsx +421 -0
  163. package/src/admin/jobs/ScheduledTasksPage.tsx +167 -0
  164. package/src/admin/jobs/columns.tsx +297 -0
  165. package/src/admin/jobs/control.ts +488 -0
  166. package/src/admin/jobs/index.ts +85 -0
  167. package/src/admin/jobs/models.ts +599 -0
  168. package/src/admin/jobs/queries.ts +272 -0
  169. package/src/admin/jobs/scheduled-task-form.tsx +493 -0
  170. package/src/admin/jobs/sections/JobChannelsPanel.tsx +142 -0
  171. package/src/admin/jobs/sections/JobOperationsSection.tsx +423 -0
  172. package/src/admin/jobs/sections/JobRunnersStrip.tsx +82 -0
  173. package/src/admin/jobs/sections/JobThroughputSection.tsx +118 -0
  174. package/src/admin/messaging/EmailDomainWizard.tsx +24 -0
  175. package/src/admin/messaging/EmailDomainsPage.tsx +17 -0
  176. package/src/admin/messaging/EmailTemplatesPage.tsx +13 -0
  177. package/src/admin/messaging/MailboxesPage.tsx +15 -0
  178. package/src/admin/messaging/PublicMessagesPage.tsx +13 -0
  179. package/src/admin/messaging/SandboxedEmailPreview.tsx +23 -0
  180. package/src/admin/messaging/SentMessagesPage.tsx +11 -0
  181. package/src/admin/messaging/api.ts +17 -0
  182. package/src/admin/messaging/data.ts +10 -0
  183. package/src/admin/messaging/index.ts +23 -0
  184. package/src/admin/messaging/models.ts +76 -0
  185. package/src/admin/messaging/push/PushPage.tsx +44 -0
  186. package/src/admin/messaging/push/api.ts +17 -0
  187. package/src/admin/messaging/push/index.ts +9 -0
  188. package/src/admin/messaging/push/models.ts +43 -0
  189. package/src/admin/messaging/sanitize.ts +44 -0
  190. package/src/admin/monitoring/LogInspector.tsx +160 -0
  191. package/src/admin/monitoring/LogsPage.tsx +135 -0
  192. package/src/admin/monitoring/MetricsExplorerPage.tsx +360 -0
  193. package/src/admin/monitoring/MetricsPermissionsPage.tsx +166 -0
  194. package/src/admin/monitoring/MetricsSourcePicker.tsx +136 -0
  195. package/src/admin/monitoring/index.ts +55 -0
  196. package/src/admin/monitoring/metrics-explorer-client.ts +229 -0
  197. package/src/admin/monitoring/metrics-explorer-data.ts +158 -0
  198. package/src/admin/monitoring/models.ts +155 -0
  199. package/src/admin/network/BlockedIPsPage.tsx +194 -0
  200. package/src/admin/network/BlockedIpDetail.tsx +262 -0
  201. package/src/admin/network/FirewallLogPage.tsx +115 -0
  202. package/src/admin/network/IPSetDetail.tsx +280 -0
  203. package/src/admin/network/IPSetEditor.tsx +191 -0
  204. package/src/admin/network/IPSetsPage.tsx +177 -0
  205. package/src/admin/network/geofence/BlocksTab.tsx +289 -0
  206. package/src/admin/network/geofence/ExemptionsTab.tsx +349 -0
  207. package/src/admin/network/geofence/GeofencingPage.tsx +119 -0
  208. package/src/admin/network/geofence/PostureHeader.tsx +184 -0
  209. package/src/admin/network/geofence/RuleEditor.tsx +176 -0
  210. package/src/admin/network/geofence/RulesTab.tsx +254 -0
  211. package/src/admin/network/geofence/SimulatorTab.tsx +281 -0
  212. package/src/admin/network/geofence/geofence-data.ts +674 -0
  213. package/src/admin/network/index.ts +68 -0
  214. package/src/admin/network/models.ts +607 -0
  215. package/src/admin/phonehub/PhoneHubPage.tsx +57 -0
  216. package/src/admin/phonehub/api.ts +47 -0
  217. package/src/admin/phonehub/data.ts +6 -0
  218. package/src/admin/phonehub/index.ts +10 -0
  219. package/src/admin/phonehub/models.ts +68 -0
  220. package/src/admin/rules/HandlerChainBuilder.tsx +51 -0
  221. package/src/admin/rules/RuleSetDetailPage.tsx +33 -0
  222. package/src/admin/rules/RuleSetsPage.tsx +33 -0
  223. package/src/admin/rules/editors.tsx +54 -0
  224. package/src/admin/rules/handler-dsl.ts +213 -0
  225. package/src/admin/rules/index.ts +13 -0
  226. package/src/admin/rules/models.ts +112 -0
  227. package/src/admin/security/devices/LoginEventDetail.tsx +123 -0
  228. package/src/admin/security/devices/LoginLocationMap.tsx +321 -0
  229. package/src/admin/security/devices/LoginLocationsPage.tsx +162 -0
  230. package/src/admin/security/devices/UserDeviceDetail.tsx +546 -0
  231. package/src/admin/security/devices/UserDeviceLocationDetail.tsx +272 -0
  232. package/src/admin/security/devices/UserDevicesPage.tsx +84 -0
  233. package/src/admin/security/devices/index.ts +40 -0
  234. package/src/admin/security/devices/models.ts +470 -0
  235. package/src/admin/security/geoip/GeoIpCachePage.tsx +135 -0
  236. package/src/admin/security/geoip/GeoIpDossier.tsx +804 -0
  237. package/src/admin/security/geoip/geoip-forms.ts +102 -0
  238. package/src/admin/security/geoip/index.ts +27 -0
  239. package/src/admin/security/geoip/models.ts +397 -0
  240. package/src/admin/security/index.ts +31 -0
  241. package/src/admin/security/models.ts +137 -0
  242. package/src/admin/security/tickets.tsx +562 -0
  243. package/src/admin/security-permissions.ts +5 -0
  244. package/src/admin/settings/SettingDetail.tsx +57 -0
  245. package/src/admin/settings/SettingEditor.tsx +157 -0
  246. package/src/admin/settings/SettingsPage.tsx +72 -0
  247. package/src/admin/settings/index.ts +19 -0
  248. package/src/admin/settings/model.ts +143 -0
  249. package/src/admin/shortlinks/ShortlinkDetail.tsx +55 -0
  250. package/src/admin/shortlinks/ShortlinkHistoryPage.tsx +18 -0
  251. package/src/admin/shortlinks/ShortlinksPage.tsx +59 -0
  252. package/src/admin/shortlinks/index.ts +18 -0
  253. package/src/admin/shortlinks/models.ts +246 -0
  254. package/src/admin/storage/BackendsPage.tsx +43 -0
  255. package/src/admin/storage/BucketDetail.tsx +77 -0
  256. package/src/admin/storage/BucketsPage.tsx +64 -0
  257. package/src/admin/storage/FileManagerDetail.tsx +36 -0
  258. package/src/admin/storage/FilePreview.tsx +54 -0
  259. package/src/admin/storage/FileUploadSurface.tsx +250 -0
  260. package/src/admin/storage/FileView.tsx +109 -0
  261. package/src/admin/storage/FilesPage.tsx +53 -0
  262. package/src/admin/storage/file-renditions.ts +99 -0
  263. package/src/admin/storage/index.ts +31 -0
  264. package/src/admin/storage/models.ts +585 -0
  265. package/src/admin/storage/storage-dialogs.tsx +80 -0
  266. package/src/charts/CircularProgress.tsx +281 -0
  267. package/src/charts/KPIStrip.tsx +162 -0
  268. package/src/charts/KPITile.tsx +125 -0
  269. package/src/charts/MetricsChart.tsx +459 -0
  270. package/src/charts/MetricsMiniWidget.tsx +588 -0
  271. package/src/charts/MiniChart.tsx +283 -0
  272. package/src/charts/PieChart.tsx +354 -0
  273. package/src/charts/SeriesChart.tsx +378 -0
  274. package/src/charts/chart-dialogs.tsx +149 -0
  275. package/src/charts/exportChart.ts +144 -0
  276. package/src/charts/index.ts +28 -0
  277. package/src/charts/pie-math.ts +127 -0
  278. package/src/charts/stats.ts +265 -0
  279. package/src/charts/worldmap/WorldMap.tsx +649 -0
  280. package/src/charts/worldmap/countryCentroids.ts +304 -0
  281. package/src/charts/worldmap/geo.ts +293 -0
  282. package/src/charts/worldmap/use-world-land.ts +47 -0
  283. package/src/charts/worldmap/world-land.ts +18 -0
  284. package/src/charts/worldmap/worldmap-data.ts +285 -0
  285. package/src/client/auth.ts +737 -0
  286. package/src/client/client.ts +268 -0
  287. package/src/client/duid.ts +31 -0
  288. package/src/client/errors.ts +33 -0
  289. package/src/client/group-context.ts +28 -0
  290. package/src/client/group.tsx +126 -0
  291. package/src/client/hooks.ts +50 -0
  292. package/src/client/index.ts +37 -0
  293. package/src/client/jwt.ts +75 -0
  294. package/src/client/location.ts +285 -0
  295. package/src/client/lookups.ts +41 -0
  296. package/src/client/markdown-fallback.tsx +115 -0
  297. package/src/client/markdown-parse.ts +386 -0
  298. package/src/client/markdown-sanitize.ts +217 -0
  299. package/src/client/markdown.ts +122 -0
  300. package/src/client/me.ts +188 -0
  301. package/src/client/mock.ts +9984 -0
  302. package/src/client/model.ts +227 -0
  303. package/src/client/params.ts +292 -0
  304. package/src/client/realtime-mock.ts +221 -0
  305. package/src/client/realtime.ts +561 -0
  306. package/src/client/record-feed.ts +329 -0
  307. package/src/client/safe-export.ts +71 -0
  308. package/src/client/types.ts +278 -0
  309. package/src/client/upload.ts +421 -0
  310. package/src/ui/AddressField.tsx +273 -0
  311. package/src/ui/AttachmentQueue.tsx +92 -0
  312. package/src/ui/CollectionMultiSelect.tsx +622 -0
  313. package/src/ui/CollectionSelect.tsx +517 -0
  314. package/src/ui/ComboBox.tsx +336 -0
  315. package/src/ui/DetailView.tsx +438 -0
  316. package/src/ui/FileDrop.tsx +194 -0
  317. package/src/ui/FileField.tsx +369 -0
  318. package/src/ui/FilterBar.tsx +269 -0
  319. package/src/ui/FormFields.tsx +77 -0
  320. package/src/ui/FormView.tsx +360 -0
  321. package/src/ui/FormWizard.tsx +336 -0
  322. package/src/ui/GroupSwitcher.tsx +202 -0
  323. package/src/ui/Guarded.tsx +16 -0
  324. package/src/ui/MarkdownView.tsx +89 -0
  325. package/src/ui/ModelTable.tsx +848 -0
  326. package/src/ui/MultiSelectDropdown.tsx +390 -0
  327. package/src/ui/Popover.tsx +204 -0
  328. package/src/ui/RecordFeed.tsx +627 -0
  329. package/src/ui/RequiresGroup.tsx +32 -0
  330. package/src/ui/RightPanel.tsx +107 -0
  331. package/src/ui/SidebarNav.tsx +330 -0
  332. package/src/ui/Tabs.tsx +161 -0
  333. package/src/ui/TagInput.tsx +381 -0
  334. package/src/ui/ThemeProvider.tsx +52 -0
  335. package/src/ui/UploadQueue.tsx +603 -0
  336. package/src/ui/armed-button.tsx +91 -0
  337. package/src/ui/date/Calendar.tsx +498 -0
  338. package/src/ui/date/DatePicker.tsx +289 -0
  339. package/src/ui/date/DateRangePicker.tsx +280 -0
  340. package/src/ui/date/DateTimePicker.tsx +587 -0
  341. package/src/ui/date/PresetRail.tsx +165 -0
  342. package/src/ui/date/TimePicker.tsx +624 -0
  343. package/src/ui/date/TimezoneSelect.tsx +267 -0
  344. package/src/ui/date/fns.ts +679 -0
  345. package/src/ui/detail/DataView.tsx +692 -0
  346. package/src/ui/detail/FlowStrip.tsx +80 -0
  347. package/src/ui/detail/KnownFieldsCard.tsx +121 -0
  348. package/src/ui/detail/MetadataSection.tsx +219 -0
  349. package/src/ui/detail/StackTraceView.tsx +150 -0
  350. package/src/ui/detail/StatusPanel.tsx +73 -0
  351. package/src/ui/detail/Timeline.tsx +52 -0
  352. package/src/ui/detail/index.ts +12 -0
  353. package/src/ui/field-registry.tsx +428 -0
  354. package/src/ui/field-wire.ts +322 -0
  355. package/src/ui/form-autosave.ts +583 -0
  356. package/src/ui/format.ts +444 -0
  357. package/src/ui/grouping.ts +150 -0
  358. package/src/ui/image-editor/ImageEditor.tsx +484 -0
  359. package/src/ui/image-editor/canvas.ts +254 -0
  360. package/src/ui/image-editor/index.ts +3 -0
  361. package/src/ui/image-editor/math.ts +416 -0
  362. package/src/ui/index.ts +58 -0
  363. package/src/ui/loading.tsx +604 -0
  364. package/src/ui/menu-registry.ts +213 -0
  365. package/src/ui/modal.tsx +297 -0
  366. package/src/ui/password.tsx +302 -0
  367. package/src/ui/schema-form-core.tsx +315 -0
  368. package/src/ui/toast.tsx +219 -0
  369. package/src/ui/ui.tsx +45 -0
@@ -0,0 +1,297 @@
1
+ // Admin jobs — the jobs table's segments, per-segment column sets, filters and
2
+ // its safe client export. Port of web-mojo sections/JobTableSection.js (its
3
+ // per-status `COLUMNS` map, `ALL_FILTERS` and the cancel batch action),
4
+ // re-wired to the fields django-mojo actually serves.
5
+ //
6
+ // Corrections carried from reading the backend rather than the source:
7
+ // · web-mojo's `pending` column set rendered a `priority` badge. jobs.Job
8
+ // has NO priority field in any graph — the column always read `undefined`
9
+ // and rendered `0`. Replaced with the queue facts that exist: how long the
10
+ // job has waited and when it expires.
11
+ // · its `running` set labelled `created` "Started". `started_at` is a real
12
+ // field and is what "started" means for a running job.
13
+ // · the `all` set hand-rolled epoch-vs-ms coercion inside a formatter; `fmt`
14
+ // already sniffs every django-mojo datetime shape.
15
+ // · every cell was a trusted-HTML template string. These are ReactNode
16
+ // renders — the escaping contract cannot be broken.
17
+ //
18
+ // The SEGMENTS are the other half of the port: web-mojo shipped one page per
19
+ // status (JobsTablePage, plus dashboard sections passing `status`), which meant
20
+ // five tables and five column configs to keep in step. Here one table carries
21
+ // preset param bundles and swaps its column set with the segment, because the
22
+ // segment IS the params — `status` (+ `run_at__isnull` to split Queued from
23
+ // Scheduled) and nothing else.
24
+ import { createSafeExporter } from '../../client';
25
+ import { Badge, fmt, type Column, type Preset } from '../../ui';
26
+ import {
27
+ isOverdueJob,
28
+ jobStatusIcon,
29
+ jobStatusTone,
30
+ JobModel,
31
+ type JobRow,
32
+ } from './models';
33
+
34
+ // ── Segments ──────────────────────────────────────────────────────────
35
+
36
+ export type JobSegmentKey = 'running' | 'queued' | 'scheduled' | 'failed' | 'all';
37
+
38
+ /**
39
+ * Queued vs Scheduled splits on `run_at__isnull`, NOT on comparing a datetime
40
+ * on the wire: `__isnull` is an explicitly supported lookup that needs no date
41
+ * coercion, and a pending job whose `run_at` has passed stays in Scheduled
42
+ * flagged "overdue" — which is its true state, not a queue position.
43
+ */
44
+ export const JOB_SEGMENTS: (Preset & { key: JobSegmentKey })[] = [
45
+ { key: 'running', label: 'Running', params: { status: 'running' } },
46
+ { key: 'queued', label: 'Queued', params: { status: 'pending', run_at__isnull: 'true' } },
47
+ { key: 'scheduled', label: 'Scheduled', params: { status: 'pending', run_at__isnull: 'false' } },
48
+ { key: 'failed', label: 'Failed', params: { status: 'failed' } },
49
+ { key: 'all', label: 'All', params: {} },
50
+ ];
51
+
52
+ /**
53
+ * Which column set the current params ask for.
54
+ *
55
+ * Deliberately more forgiving than `presetActive`: the chip highlight requires
56
+ * an EXACT filter match (adding a channel filter un-highlights the chip), but
57
+ * the columns should keep following the status the operator is looking at.
58
+ * "Running jobs on the email channel" still wants the Running columns.
59
+ */
60
+ export function jobSegmentOf(status: string | null, runAtIsNull: string | null): JobSegmentKey {
61
+ if (status === 'running') return 'running';
62
+ if (status === 'failed') return 'failed';
63
+ if (status === 'pending') return runAtIsNull === 'false' ? 'scheduled' : 'queued';
64
+ return 'all';
65
+ }
66
+
67
+ // ── Shared cells ──────────────────────────────────────────────────────
68
+
69
+ /** "runs in 2h" / "3m ago" — `fmt.relative` only speaks the past tense. */
70
+ export function relativeWhen(value: number | null | undefined, fallback = '—'): string {
71
+ if (value == null) return fallback;
72
+ const diffSec = Math.round((value * 1000 - Date.now()) / 1000);
73
+ const ago = diffSec <= 0;
74
+ const magnitude = Math.abs(diffSec);
75
+ const text = magnitude < 60 ? `${magnitude}s`
76
+ : magnitude < 3600 ? `${Math.floor(magnitude / 60)}m`
77
+ : magnitude < 86400 ? `${Math.floor(magnitude / 3600)}h`
78
+ : `${Math.floor(magnitude / 86400)}d`;
79
+ return ago ? `${text} ago` : `in ${text}`;
80
+ }
81
+
82
+ /** Job identity: the id kept recognizable at both ends, channel + func under it. */
83
+ export function JobIdentity({ job }: { job: JobRow }) {
84
+ return (
85
+ <>
86
+ <div className="jobs-cell-id"><code>{fmt.truncateMiddle(job.id, 12)}</code></div>
87
+ <div className="jobs-cell-sub">
88
+ {job.channel} · {job.func ? fmt.truncateMiddle(job.func, 34, '…') : 'no function'}
89
+ </div>
90
+ </>
91
+ );
92
+ }
93
+
94
+ export function JobStatusBadge({ status }: { status: string }) {
95
+ return (
96
+ <Badge tone={jobStatusTone(status)}>
97
+ <i className={`bi ${jobStatusIcon(status)}`} /> {status.toUpperCase()}
98
+ </Badge>
99
+ );
100
+ }
101
+
102
+ function RunnerCell({ runnerId }: { runnerId: string | null }) {
103
+ return runnerId
104
+ ? <code className="jobs-id">{fmt.truncateMiddle(runnerId, 18)}</code>
105
+ : <span className="dim">—</span>;
106
+ }
107
+
108
+ function AttemptCell({ job }: { job: JobRow }) {
109
+ return <span className="jobs-nums">{job.attempt} / {job.max_retries || '∞'}</span>;
110
+ }
111
+
112
+ // ── Column sets ───────────────────────────────────────────────────────
113
+
114
+ const IDENTITY: Column<JobRow> = {
115
+ key: 'id', label: 'Job', sortable: false, hideable: false,
116
+ render: (row) => <JobIdentity job={row} />,
117
+ };
118
+
119
+ const STATUS: Column<JobRow> = {
120
+ key: 'status', label: 'Status', sortable: true,
121
+ render: (row) => <JobStatusBadge status={row.status} />,
122
+ };
123
+
124
+ const CHANNEL: Column<JobRow> = {
125
+ key: 'channel', label: 'Channel', sortable: true,
126
+ render: (row) => <Badge tone="muted">{row.channel}</Badge>,
127
+ };
128
+
129
+ const RUNNER: Column<JobRow> = {
130
+ key: 'runner_id', label: 'Runner', sortable: true,
131
+ render: (row) => <RunnerCell runnerId={row.runner_id} />,
132
+ };
133
+
134
+ const ATTEMPT: Column<JobRow> = {
135
+ key: 'attempt', label: 'Attempt', sortable: true, align: 'center',
136
+ render: (row) => <AttemptCell job={row} />,
137
+ };
138
+
139
+ const CREATED: Column<JobRow> = {
140
+ key: 'created', label: 'Created', sortable: true,
141
+ render: (row) => <span title={fmt.datetime(row.created)}>{fmt.relative(row.created)}</span>,
142
+ };
143
+
144
+ /** `duration_ms` is a serializer EXTRA, not a column the server can sort on. */
145
+ const DURATION: Column<JobRow> = {
146
+ key: 'duration_ms', label: 'Duration', sortable: false, align: 'end',
147
+ render: (row) => (row.duration_ms > 0 ? fmt.duration(row.duration_ms) : <span className="dim">—</span>),
148
+ };
149
+
150
+ const COLUMNS: Record<JobSegmentKey, Column<JobRow>[]> = {
151
+ running: [
152
+ IDENTITY,
153
+ RUNNER,
154
+ STATUS,
155
+ {
156
+ key: 'started_at', label: 'Started', sortable: true,
157
+ render: (row) => (row.started_at == null
158
+ ? <span className="dim">not yet claimed</span>
159
+ : <span title={fmt.datetime(row.started_at)}>{fmt.relative(row.started_at)}</span>),
160
+ },
161
+ ATTEMPT,
162
+ {
163
+ key: 'cancel_requested', label: 'Cancel', sortable: false, align: 'center',
164
+ render: (row) => (row.cancel_requested
165
+ ? <Badge tone="warning">requested</Badge>
166
+ : <span className="dim">—</span>),
167
+ },
168
+ ],
169
+
170
+ // web-mojo's `pending` set led with a `priority` badge. There is no such
171
+ // field — what a queued job actually has is an age and an expiry.
172
+ queued: [
173
+ IDENTITY,
174
+ CHANNEL,
175
+ {
176
+ key: 'created', label: 'Waiting', sortable: true,
177
+ render: (row) => <span title={fmt.datetime(row.created)}>{fmt.relative(row.created)}</span>,
178
+ },
179
+ {
180
+ key: 'expires_at', label: 'Expires', sortable: true,
181
+ render: (row) => (row.expires_at == null
182
+ ? <span className="dim">never</span>
183
+ : <span title={fmt.datetime(row.expires_at)}>{relativeWhen(row.expires_at)}</span>),
184
+ },
185
+ ATTEMPT,
186
+ ],
187
+
188
+ scheduled: [
189
+ IDENTITY,
190
+ {
191
+ key: 'run_at', label: 'Runs at', sortable: true,
192
+ render: (row) => (
193
+ <div className="jobs-cell-stack">
194
+ <span>{fmt.datetime(row.run_at)}</span>
195
+ <span className="jobs-cell-sub">{relativeWhen(row.run_at)}</span>
196
+ </div>
197
+ ),
198
+ },
199
+ {
200
+ // A pending job whose run_at has passed is not "late by a little" —
201
+ // nothing has picked it up, which is the point of showing it.
202
+ key: 'overdue', label: 'State', sortable: false, align: 'center',
203
+ render: (row) => (isOverdueJob(row)
204
+ ? <Badge tone="warning"><i className="bi bi-exclamation-triangle" /> OVERDUE</Badge>
205
+ : <Badge tone="info">WAITING</Badge>),
206
+ },
207
+ CHANNEL,
208
+ CREATED,
209
+ ],
210
+
211
+ failed: [
212
+ IDENTITY,
213
+ {
214
+ key: 'last_error', label: 'Error', sortable: false,
215
+ render: (row) => {
216
+ // Only the FIRST line: a traceback in a table cell is noise,
217
+ // and the full text is one click away in the detail modal.
218
+ const firstLine = (row.last_error || '').split('\n')[0] ?? '';
219
+ return (
220
+ <div className="jobs-cell-error" title={row.last_error || undefined}>
221
+ {firstLine ? fmt.truncate(firstLine, 90, '…') : 'Unknown error'}
222
+ </div>
223
+ );
224
+ },
225
+ },
226
+ ATTEMPT,
227
+ {
228
+ key: 'finished_at', label: 'Failed', sortable: true,
229
+ render: (row) => <span title={fmt.datetime(row.finished_at ?? row.modified)}>{fmt.relative(row.finished_at ?? row.modified)}</span>,
230
+ },
231
+ RUNNER,
232
+ ],
233
+
234
+ all: [
235
+ IDENTITY,
236
+ CHANNEL,
237
+ STATUS,
238
+ {
239
+ key: 'run_at', label: 'Scheduled', sortable: true,
240
+ render: (row) => (row.run_at == null
241
+ ? <span className="dim">—</span>
242
+ : (
243
+ <span className={isOverdueJob(row) ? 'jobs-text-warning' : undefined} title={fmt.datetime(row.run_at)}>
244
+ <i className={`bi ${isOverdueJob(row) ? 'bi-clock-history' : 'bi-clock'}`} /> {relativeWhen(row.run_at)}
245
+ </span>
246
+ )),
247
+ },
248
+ CREATED,
249
+ {
250
+ key: 'finished_at', label: 'Finished', sortable: true,
251
+ render: (row) => (row.finished_at == null
252
+ ? <span className="dim">—</span>
253
+ : <span title={fmt.datetime(row.finished_at)}>{fmt.relative(row.finished_at)}</span>),
254
+ },
255
+ DURATION,
256
+ ],
257
+ };
258
+
259
+ export function jobColumns(segment: JobSegmentKey): Column<JobRow>[] {
260
+ return COLUMNS[segment] ?? COLUMNS.all;
261
+ }
262
+
263
+ // ── Safe client export ────────────────────────────────────────────────
264
+
265
+ /**
266
+ * Job payloads carry whatever the caller published (recipients, file paths,
267
+ * credentials someone put in a dict), metadata is equally open-ended, and
268
+ * `last_error` is a raw exception string. None of them belong in a file that
269
+ * leaves the browser, so the export is a CLIENT projection over the same
270
+ * filtered set rather than the server's `download_format`, which would ship
271
+ * every graph field.
272
+ */
273
+ export function sanitizeJobRowForExport(row: JobRow): JobRow {
274
+ return { ...row, payload: {}, metadata: {}, last_error: '' };
275
+ }
276
+
277
+ export const JOB_EXPORTER = createSafeExporter<JobRow>({
278
+ endpoint: JobModel.endpoint,
279
+ filename: 'jobs',
280
+ sanitizeRow: sanitizeJobRowForExport,
281
+ fields: [
282
+ { key: 'id' },
283
+ { key: 'channel' },
284
+ { key: 'func' },
285
+ { key: 'status' },
286
+ { key: 'runner_id' },
287
+ { key: 'attempt' },
288
+ { key: 'max_retries' },
289
+ { key: 'cancel_requested' },
290
+ { key: 'created' },
291
+ { key: 'run_at' },
292
+ { key: 'started_at' },
293
+ { key: 'finished_at' },
294
+ { key: 'expires_at' },
295
+ { key: 'duration_ms' },
296
+ ],
297
+ });