@burdenoff/microfe-theglobalfuel 2026.705.5 → 2026.709.2

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 (241) hide show
  1. package/README.md +3 -0
  2. package/dist/dev/main.d.ts +2 -0
  3. package/dist/dev/main.d.ts.map +1 -0
  4. package/dist/generated/global-operations.d.ts +2 -0
  5. package/dist/generated/global-operations.d.ts.map +1 -0
  6. package/dist/generated/global-types.d.ts +18199 -0
  7. package/dist/generated/global-types.d.ts.map +1 -0
  8. package/dist/generated/wspace-operations.d.ts +2 -0
  9. package/dist/generated/wspace-operations.d.ts.map +1 -0
  10. package/dist/generated/wspace-types.d.ts +43046 -0
  11. package/dist/generated/wspace-types.d.ts.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +3 -1
  15. package/dist/theglobalfuel/TheGlobalFuelRoot.d.ts +5 -0
  16. package/dist/theglobalfuel/TheGlobalFuelRoot.d.ts.map +1 -0
  17. package/dist/theglobalfuel/TheGlobalFuelRoot.js +106 -104
  18. package/dist/theglobalfuel/TheGlobalFuelRoot.js.map +1 -1
  19. package/dist/theglobalfuel/TheGlobalFuelRoutes.d.ts +4 -0
  20. package/dist/theglobalfuel/TheGlobalFuelRoutes.d.ts.map +1 -0
  21. package/dist/theglobalfuel/TheGlobalFuelRoutes.js +60 -38
  22. package/dist/theglobalfuel/TheGlobalFuelRoutes.js.map +1 -1
  23. package/dist/theglobalfuel/components/DataTable.d.ts +30 -0
  24. package/dist/theglobalfuel/components/DataTable.d.ts.map +1 -0
  25. package/dist/theglobalfuel/components/DataTable.js.map +1 -1
  26. package/dist/theglobalfuel/components/EmptyState.d.ts +18 -0
  27. package/dist/theglobalfuel/components/EmptyState.d.ts.map +1 -0
  28. package/dist/theglobalfuel/components/EvidenceUpload.d.ts +14 -0
  29. package/dist/theglobalfuel/components/EvidenceUpload.d.ts.map +1 -0
  30. package/dist/theglobalfuel/components/FileLink.d.ts +15 -0
  31. package/dist/theglobalfuel/components/FileLink.d.ts.map +1 -0
  32. package/dist/theglobalfuel/components/FilterTabs.d.ts +18 -0
  33. package/dist/theglobalfuel/components/FilterTabs.d.ts.map +1 -0
  34. package/dist/theglobalfuel/components/MetricsCard.d.ts +23 -0
  35. package/dist/theglobalfuel/components/MetricsCard.d.ts.map +1 -0
  36. package/dist/theglobalfuel/components/MiniBar.d.ts +16 -0
  37. package/dist/theglobalfuel/components/MiniBar.d.ts.map +1 -0
  38. package/dist/theglobalfuel/components/PageLayout.d.ts +18 -0
  39. package/dist/theglobalfuel/components/PageLayout.d.ts.map +1 -0
  40. package/dist/theglobalfuel/components/PageLayout.js +2 -1
  41. package/dist/theglobalfuel/components/PageLayout.js.map +1 -1
  42. package/dist/theglobalfuel/components/Pagination.d.ts +22 -0
  43. package/dist/theglobalfuel/components/Pagination.d.ts.map +1 -0
  44. package/dist/theglobalfuel/components/StateViews.d.ts +24 -0
  45. package/dist/theglobalfuel/components/StateViews.d.ts.map +1 -0
  46. package/dist/theglobalfuel/components/StatusBadge.d.ts +9 -0
  47. package/dist/theglobalfuel/components/StatusBadge.d.ts.map +1 -0
  48. package/dist/theglobalfuel/components/Tabs.d.ts +17 -0
  49. package/dist/theglobalfuel/components/Tabs.d.ts.map +1 -0
  50. package/dist/theglobalfuel/components/TheDiscussion.d.ts +18 -0
  51. package/dist/theglobalfuel/components/TheDiscussion.d.ts.map +1 -0
  52. package/dist/theglobalfuel/components/TheTags.d.ts +17 -0
  53. package/dist/theglobalfuel/components/TheTags.d.ts.map +1 -0
  54. package/dist/theglobalfuel/components/WorldMap.d.ts +18 -0
  55. package/dist/theglobalfuel/components/WorldMap.d.ts.map +1 -0
  56. package/dist/theglobalfuel/components/WorldMap.js.map +1 -1
  57. package/dist/theglobalfuel/components/index.d.ts +21 -0
  58. package/dist/theglobalfuel/components/index.d.ts.map +1 -0
  59. package/dist/theglobalfuel/components/viz/AllocationBar.d.ts +21 -0
  60. package/dist/theglobalfuel/components/viz/AllocationBar.d.ts.map +1 -0
  61. package/dist/theglobalfuel/components/viz/AreaChart.d.ts +18 -0
  62. package/dist/theglobalfuel/components/viz/AreaChart.d.ts.map +1 -0
  63. package/dist/theglobalfuel/components/viz/AvatarStack.d.ts +13 -0
  64. package/dist/theglobalfuel/components/viz/AvatarStack.d.ts.map +1 -0
  65. package/dist/theglobalfuel/components/viz/BarChart.d.ts +17 -0
  66. package/dist/theglobalfuel/components/viz/BarChart.d.ts.map +1 -0
  67. package/dist/theglobalfuel/components/viz/CertificateCard.d.ts +14 -0
  68. package/dist/theglobalfuel/components/viz/CertificateCard.d.ts.map +1 -0
  69. package/dist/theglobalfuel/components/viz/DonutChart.d.ts +20 -0
  70. package/dist/theglobalfuel/components/viz/DonutChart.d.ts.map +1 -0
  71. package/dist/theglobalfuel/components/viz/HBarFunnel.d.ts +17 -0
  72. package/dist/theglobalfuel/components/viz/HBarFunnel.d.ts.map +1 -0
  73. package/dist/theglobalfuel/components/viz/Heatmap.d.ts +26 -0
  74. package/dist/theglobalfuel/components/viz/Heatmap.d.ts.map +1 -0
  75. package/dist/theglobalfuel/components/viz/KanbanBoard.d.ts +17 -0
  76. package/dist/theglobalfuel/components/viz/KanbanBoard.d.ts.map +1 -0
  77. package/dist/theglobalfuel/components/viz/LineChart.d.ts +20 -0
  78. package/dist/theglobalfuel/components/viz/LineChart.d.ts.map +1 -0
  79. package/dist/theglobalfuel/components/viz/OrgTree.d.ts +14 -0
  80. package/dist/theglobalfuel/components/viz/OrgTree.d.ts.map +1 -0
  81. package/dist/theglobalfuel/components/viz/PathSteps.d.ts +17 -0
  82. package/dist/theglobalfuel/components/viz/PathSteps.d.ts.map +1 -0
  83. package/dist/theglobalfuel/components/viz/ProgressRing.d.ts +20 -0
  84. package/dist/theglobalfuel/components/viz/ProgressRing.d.ts.map +1 -0
  85. package/dist/theglobalfuel/components/viz/RatingStars.d.ts +14 -0
  86. package/dist/theglobalfuel/components/viz/RatingStars.d.ts.map +1 -0
  87. package/dist/theglobalfuel/components/viz/SegmentedTabs.d.ts +26 -0
  88. package/dist/theglobalfuel/components/viz/SegmentedTabs.d.ts.map +1 -0
  89. package/dist/theglobalfuel/components/viz/SkillBar.d.ts +17 -0
  90. package/dist/theglobalfuel/components/viz/SkillBar.d.ts.map +1 -0
  91. package/dist/theglobalfuel/components/viz/Sparkline.d.ts +16 -0
  92. package/dist/theglobalfuel/components/viz/Sparkline.d.ts.map +1 -0
  93. package/dist/theglobalfuel/components/viz/StatCard.d.ts +22 -0
  94. package/dist/theglobalfuel/components/viz/StatCard.d.ts.map +1 -0
  95. package/dist/theglobalfuel/components/viz/StepProgress.d.ts +17 -0
  96. package/dist/theglobalfuel/components/viz/StepProgress.d.ts.map +1 -0
  97. package/dist/theglobalfuel/components/viz/Timeline.d.ts +21 -0
  98. package/dist/theglobalfuel/components/viz/Timeline.d.ts.map +1 -0
  99. package/dist/theglobalfuel/components/viz/index.d.ts +35 -0
  100. package/dist/theglobalfuel/components/viz/index.d.ts.map +1 -0
  101. package/dist/theglobalfuel/components/viz/lib/Avatar.d.ts +15 -0
  102. package/dist/theglobalfuel/components/viz/lib/Avatar.d.ts.map +1 -0
  103. package/dist/theglobalfuel/components/viz/lib/CartesianFrame.d.ts +44 -0
  104. package/dist/theglobalfuel/components/viz/lib/CartesianFrame.d.ts.map +1 -0
  105. package/dist/theglobalfuel/components/viz/lib/ResponsiveSvg.d.ts +27 -0
  106. package/dist/theglobalfuel/components/viz/lib/ResponsiveSvg.d.ts.map +1 -0
  107. package/dist/theglobalfuel/components/viz/lib/format.d.ts +10 -0
  108. package/dist/theglobalfuel/components/viz/lib/format.d.ts.map +1 -0
  109. package/dist/theglobalfuel/components/viz/lib/geometry.d.ts +41 -0
  110. package/dist/theglobalfuel/components/viz/lib/geometry.d.ts.map +1 -0
  111. package/dist/theglobalfuel/components/viz/lib/hooks.d.ts +18 -0
  112. package/dist/theglobalfuel/components/viz/lib/hooks.d.ts.map +1 -0
  113. package/dist/theglobalfuel/components/viz/lib/palette.d.ts +31 -0
  114. package/dist/theglobalfuel/components/viz/lib/palette.d.ts.map +1 -0
  115. package/dist/theglobalfuel/components/viz/lib/types.d.ts +27 -0
  116. package/dist/theglobalfuel/components/viz/lib/types.d.ts.map +1 -0
  117. package/dist/theglobalfuel/context/TheGlobalFuelContext.d.ts +16 -0
  118. package/dist/theglobalfuel/context/TheGlobalFuelContext.d.ts.map +1 -0
  119. package/dist/theglobalfuel/context/index.d.ts +6 -0
  120. package/dist/theglobalfuel/context/index.d.ts.map +1 -0
  121. package/dist/theglobalfuel/hooks/index.d.ts +15 -0
  122. package/dist/theglobalfuel/hooks/index.d.ts.map +1 -0
  123. package/dist/theglobalfuel/hooks/index.js +4 -0
  124. package/dist/theglobalfuel/hooks/useCursorPager.d.ts +26 -0
  125. package/dist/theglobalfuel/hooks/useCursorPager.d.ts.map +1 -0
  126. package/dist/theglobalfuel/hooks/useNotify.d.ts +23 -0
  127. package/dist/theglobalfuel/hooks/useNotify.d.ts.map +1 -0
  128. package/dist/theglobalfuel/hooks/useTheAnalytics.d.ts +38 -0
  129. package/dist/theglobalfuel/hooks/useTheAnalytics.d.ts.map +1 -0
  130. package/dist/theglobalfuel/hooks/useTheAnalytics.js +46 -0
  131. package/dist/theglobalfuel/hooks/useTheAnalytics.js.map +1 -0
  132. package/dist/theglobalfuel/hooks/useTheComments.d.ts +36 -0
  133. package/dist/theglobalfuel/hooks/useTheComments.d.ts.map +1 -0
  134. package/dist/theglobalfuel/hooks/useTheFiles.d.ts +47 -0
  135. package/dist/theglobalfuel/hooks/useTheFiles.d.ts.map +1 -0
  136. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts +79 -0
  137. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts.map +1 -0
  138. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js +2 -9
  139. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js.map +1 -1
  140. package/dist/theglobalfuel/hooks/useTheNativeControls.d.ts +20 -0
  141. package/dist/theglobalfuel/hooks/useTheNativeControls.d.ts.map +1 -0
  142. package/dist/theglobalfuel/hooks/useTheNativeControls.js +22 -0
  143. package/dist/theglobalfuel/hooks/useTheNativeControls.js.map +1 -0
  144. package/dist/theglobalfuel/hooks/useThePageView.d.ts +10 -0
  145. package/dist/theglobalfuel/hooks/useThePageView.d.ts.map +1 -0
  146. package/dist/theglobalfuel/hooks/useThePageView.js +13 -0
  147. package/dist/theglobalfuel/hooks/useThePageView.js.map +1 -0
  148. package/dist/theglobalfuel/hooks/useTheTags.d.ts +44 -0
  149. package/dist/theglobalfuel/hooks/useTheTags.d.ts.map +1 -0
  150. package/dist/theglobalfuel/hooks/useTheTranslation.d.ts +4 -0
  151. package/dist/theglobalfuel/hooks/useTheTranslation.d.ts.map +1 -0
  152. package/dist/theglobalfuel/hooks/useTheTranslation.js +12 -0
  153. package/dist/theglobalfuel/hooks/useTheTranslation.js.map +1 -0
  154. package/dist/theglobalfuel/index.d.ts +11 -0
  155. package/dist/theglobalfuel/index.d.ts.map +1 -0
  156. package/dist/theglobalfuel/index.js +2 -0
  157. package/dist/theglobalfuel/pages/assets/AssetDetailPage.d.ts +13 -0
  158. package/dist/theglobalfuel/pages/assets/AssetDetailPage.d.ts.map +1 -0
  159. package/dist/theglobalfuel/pages/assets/AssetDetailPage.js +395 -353
  160. package/dist/theglobalfuel/pages/assets/AssetDetailPage.js.map +1 -1
  161. package/dist/theglobalfuel/pages/assets/AssetsPage.d.ts +10 -0
  162. package/dist/theglobalfuel/pages/assets/AssetsPage.d.ts.map +1 -0
  163. package/dist/theglobalfuel/pages/assets/AssetsPage.js +152 -146
  164. package/dist/theglobalfuel/pages/assets/AssetsPage.js.map +1 -1
  165. package/dist/theglobalfuel/pages/compliance/CompliancePage.d.ts +11 -0
  166. package/dist/theglobalfuel/pages/compliance/CompliancePage.d.ts.map +1 -0
  167. package/dist/theglobalfuel/pages/compliance/CompliancePage.js +233 -229
  168. package/dist/theglobalfuel/pages/compliance/CompliancePage.js.map +1 -1
  169. package/dist/theglobalfuel/pages/contracts/ContractDetailPage.d.ts +9 -0
  170. package/dist/theglobalfuel/pages/contracts/ContractDetailPage.d.ts.map +1 -0
  171. package/dist/theglobalfuel/pages/contracts/ContractDetailPage.js +410 -389
  172. package/dist/theglobalfuel/pages/contracts/ContractDetailPage.js.map +1 -1
  173. package/dist/theglobalfuel/pages/contracts/ContractsPage.d.ts +8 -0
  174. package/dist/theglobalfuel/pages/contracts/ContractsPage.d.ts.map +1 -0
  175. package/dist/theglobalfuel/pages/contracts/ContractsPage.js +237 -231
  176. package/dist/theglobalfuel/pages/contracts/ContractsPage.js.map +1 -1
  177. package/dist/theglobalfuel/pages/dashboard/DashboardPage.d.ts +17 -0
  178. package/dist/theglobalfuel/pages/dashboard/DashboardPage.d.ts.map +1 -0
  179. package/dist/theglobalfuel/pages/dashboard/DashboardPage.js +255 -250
  180. package/dist/theglobalfuel/pages/dashboard/DashboardPage.js.map +1 -1
  181. package/dist/theglobalfuel/pages/emissions/EmissionsPage.d.ts +13 -0
  182. package/dist/theglobalfuel/pages/emissions/EmissionsPage.d.ts.map +1 -0
  183. package/dist/theglobalfuel/pages/emissions/EmissionsPage.js +154 -150
  184. package/dist/theglobalfuel/pages/emissions/EmissionsPage.js.map +1 -1
  185. package/dist/theglobalfuel/pages/inventory/InventoryPage.d.ts +11 -0
  186. package/dist/theglobalfuel/pages/inventory/InventoryPage.d.ts.map +1 -0
  187. package/dist/theglobalfuel/pages/inventory/InventoryPage.js +151 -147
  188. package/dist/theglobalfuel/pages/inventory/InventoryPage.js.map +1 -1
  189. package/dist/theglobalfuel/pages/production/ProductionPage.d.ts +11 -0
  190. package/dist/theglobalfuel/pages/production/ProductionPage.d.ts.map +1 -0
  191. package/dist/theglobalfuel/pages/production/ProductionPage.js +175 -171
  192. package/dist/theglobalfuel/pages/production/ProductionPage.js.map +1 -1
  193. package/dist/theglobalfuel/pages/provenance/ProvenancePage.d.ts +23 -0
  194. package/dist/theglobalfuel/pages/provenance/ProvenancePage.d.ts.map +1 -0
  195. package/dist/theglobalfuel/pages/provenance/ProvenancePage.js +153 -146
  196. package/dist/theglobalfuel/pages/provenance/ProvenancePage.js.map +1 -1
  197. package/dist/theglobalfuel/pages/reserves/ReservesPage.d.ts +10 -0
  198. package/dist/theglobalfuel/pages/reserves/ReservesPage.d.ts.map +1 -0
  199. package/dist/theglobalfuel/pages/reserves/ReservesPage.js +168 -164
  200. package/dist/theglobalfuel/pages/reserves/ReservesPage.js.map +1 -1
  201. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.d.ts +11 -0
  202. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.d.ts.map +1 -0
  203. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js +179 -175
  204. package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js.map +1 -1
  205. package/dist/theglobalfuel/services/assistantApi.d.ts +25 -0
  206. package/dist/theglobalfuel/services/assistantApi.d.ts.map +1 -0
  207. package/dist/theglobalfuel/services/assistantApi.js +225 -0
  208. package/dist/theglobalfuel/services/assistantApi.js.map +1 -0
  209. package/dist/theglobalfuel/services/createSandboxAssistantTransport.d.ts +38 -0
  210. package/dist/theglobalfuel/services/createSandboxAssistantTransport.d.ts.map +1 -0
  211. package/dist/theglobalfuel/services/createSandboxAssistantTransport.js +120 -0
  212. package/dist/theglobalfuel/services/createSandboxAssistantTransport.js.map +1 -0
  213. package/dist/theglobalfuel/services/index.d.ts +7 -0
  214. package/dist/theglobalfuel/services/index.d.ts.map +1 -0
  215. package/dist/theglobalfuel/types/assistant.d.ts +65 -0
  216. package/dist/theglobalfuel/types/assistant.d.ts.map +1 -0
  217. package/dist/theglobalfuel/types/index.d.ts +376 -0
  218. package/dist/theglobalfuel/types/index.d.ts.map +1 -0
  219. package/dist/theglobalfuel/utils/cn.d.ts +4 -0
  220. package/dist/theglobalfuel/utils/cn.d.ts.map +1 -0
  221. package/dist/theglobalfuel/utils/formatters.d.ts +44 -0
  222. package/dist/theglobalfuel/utils/formatters.d.ts.map +1 -0
  223. package/dist/theglobalfuel/utils/i18n.d.ts +5 -0
  224. package/dist/theglobalfuel/utils/i18n.d.ts.map +1 -0
  225. package/dist/theglobalfuel/utils/i18n.js +15 -0
  226. package/dist/theglobalfuel/utils/i18n.js.map +1 -0
  227. package/dist/theglobalfuel/utils/index.d.ts +10 -0
  228. package/dist/theglobalfuel/utils/index.d.ts.map +1 -0
  229. package/dist/theglobalfuel/utils/navigation.d.ts +14 -0
  230. package/dist/theglobalfuel/utils/navigation.d.ts.map +1 -0
  231. package/dist/theglobalfuel/utils/pageContext.d.ts +26 -0
  232. package/dist/theglobalfuel/utils/pageContext.d.ts.map +1 -0
  233. package/dist/theglobalfuel/utils/pageContext.js +65 -0
  234. package/dist/theglobalfuel/utils/pageContext.js.map +1 -0
  235. package/dist/theglobalfuel/utils/transitions.d.ts +17 -0
  236. package/dist/theglobalfuel/utils/transitions.d.ts.map +1 -0
  237. package/package.json +5 -1
  238. package/dist/node_modules/clsx/dist/clsx.js +0 -18
  239. package/dist/node_modules/clsx/dist/clsx.js.map +0 -1
  240. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +0 -1716
  241. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ShipmentsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/shipments/ShipmentsPage.tsx"],"sourcesContent":["/**\n * Shipments — schedule and track cargo movements against offtake contracts.\n * @module theglobalfuel/pages/shipments\n *\n * Reads `theListShipments` (a cursor connection — `{ items, pageInfo }`, filtered\n * by contract + status) and `theListContracts` (to resolve contract references +\n * populate the picker); writes `theScheduleShipment`, `theDeliverShipment` and\n * lifecycle transitions via `theTransitionShipment`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Ship, CalendarClock, Truck, PackageCheck, ArrowRight, MapPin } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { FileLink } from '../../components/FileLink';\nimport { EvidenceUpload } from '../../components/EvidenceUpload';\nimport { Pagination } from '../../components/Pagination';\nimport { AllocationBar, PathSteps, type PathStep, type Segment } from '../../components';\nimport {\n useNotify,\n useTheShipments,\n useTheContracts,\n useTheScheduleShipment,\n useTheDeliverShipment,\n useTheTransitionShipment,\n useCursorPager,\n type UploadedTheFile,\n} from '../../hooks';\nimport { formatEnumLabel, formatDate, formatVolume } from '../../utils/formatters';\nimport {\n toBackendDate,\n SHIPMENT_TRANSITIONS,\n SHIPMENT_DELIVERABLE_FROM,\n nextStatuses,\n} from '../../utils';\nimport type {\n TheShipment,\n TheShipmentStatus,\n TheTransportMode,\n TheUnit,\n TheOfftakeContract,\n} from '../../types';\n\nconst STATUS_FILTERS: Array<{ value: TheShipmentStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'LOADING', label: 'Loading' },\n { value: 'IN_TRANSIT', label: 'In transit' },\n { value: 'DELIVERED', label: 'Delivered' },\n { value: 'CANCELLED', label: 'Cancelled' },\n];\n\nconst TRANSPORT_MODES: TheTransportMode[] = [\n 'PIPELINE',\n 'TANKER',\n 'VESSEL_BULK',\n 'RAIL',\n 'TRUCK',\n 'GRID',\n];\n\nconst UNITS: TheUnit[] = ['BARRELS', 'CUBIC_METERS', 'TONNES', 'KILOGRAMS', 'MWH', 'GJ', 'MCF'];\n\nconst PAGE_SIZE = 25;\n\n/** Lifecycle order for the status-distribution bar + legend. */\nconst STATUS_ORDER: TheShipmentStatus[] = [\n 'SCHEDULED',\n 'LOADING',\n 'IN_TRANSIT',\n 'DELIVERED',\n 'CANCELLED',\n];\n\nconst STATUS_LABEL: Record<TheShipmentStatus, string> = {\n SCHEDULED: 'Scheduled',\n LOADING: 'Loading',\n IN_TRANSIT: 'In transit',\n DELIVERED: 'Delivered',\n CANCELLED: 'Cancelled',\n};\n\n/**\n * Tokenized AllocationBar color per shipment status — aligned to the StatusBadge\n * palette (neutral → info → warning → success → danger). All are semantic CSS\n * variables so the bar themes automatically.\n */\nconst STATUS_SEGMENT_COLOR: Record<TheShipmentStatus, string> = {\n SCHEDULED: 'var(--color-text-muted)',\n LOADING: 'var(--color-accent-blue)',\n IN_TRANSIT: 'var(--color-status-warning-text)',\n DELIVERED: 'var(--color-status-success-text)',\n CANCELLED: 'var(--color-status-error-text)',\n};\n\n/**\n * Build an origin → mode → destination route flow for a shipment, with node\n * states derived from its lifecycle status. Origin/destination are free-text\n * strings — never plotted on a map. Only call with shipments that carry both.\n */\nconst routeSteps = (s: TheShipment): PathStep[] => {\n const cancelled = s.status === 'CANCELLED';\n const delivered = s.status === 'DELIVERED';\n const inTransit = s.status === 'IN_TRANSIT';\n const departed = delivered || inTransit;\n const midMeta = cancelled\n ? 'Cancelled'\n : delivered\n ? 'Delivered'\n : inTransit\n ? 'In transit'\n : formatEnumLabel(s.status);\n return [\n {\n label: (s.origin ?? '').trim(),\n meta: 'Origin',\n state: cancelled ? 'todo' : departed ? 'done' : 'active',\n },\n {\n label: formatEnumLabel(s.mode),\n meta: midMeta,\n state: cancelled ? 'locked' : delivered ? 'done' : inTransit ? 'active' : 'todo',\n },\n {\n label: (s.destination ?? '').trim(),\n meta: 'Destination',\n state: cancelled ? 'locked' : delivered ? 'done' : 'todo',\n },\n ];\n};\n\ninterface ScheduleFormState {\n contractId: string;\n reference: string;\n mode: TheTransportMode;\n carrier: string;\n origin: string;\n destination: string;\n volume: string;\n unit: TheUnit;\n scheduledAt: string;\n notes: string;\n}\n\nconst EMPTY_FORM: ScheduleFormState = {\n contractId: '',\n reference: '',\n mode: 'TANKER',\n carrier: '',\n origin: '',\n destination: '',\n volume: '',\n unit: 'BARRELS',\n scheduledAt: '',\n notes: '',\n};\n\nexport function ShipmentsPage() {\n const [status, setStatus] = useState<TheShipmentStatus | 'ALL'>('ALL');\n const [contractId, setContractId] = useState<string | 'ALL'>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<ScheduleFormState>(EMPTY_FORM);\n const [doc, setDoc] = useState<UploadedTheFile | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n // Track the shipment whose transition is in flight so only that row's Select\n // disables (transitionShipment.isPending is shared across all rows).\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n // The shipment whose route flow is traced in the logistics panel (falls back\n // to the first routeable shipment when unset or no longer present).\n const [tracedId, setTracedId] = useState<string | null>(null);\n\n const notify = useNotify();\n\n const scopedContractId = contractId === 'ALL' ? undefined : contractId;\n\n // Table query — respects both the contract scope and the status filter, and is\n // cursor-paginated to PAGE_SIZE.\n const { data, isLoading, isError, refetch } = useTheShipments(\n scopedContractId,\n status === 'ALL' ? undefined : status,\n { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE }\n );\n // Metrics query — same contract scope but ignores the status filter so the\n // header counts stay stable as the table is narrowed. A broad limit keeps the\n // KPI status mix representative (the table itself is paginated to PAGE_SIZE).\n const metricsQuery = useTheShipments(scopedContractId, undefined, { limit: 200 });\n\n // Contracts drive the picker + let us resolve a shipment's contract reference.\n const contractsQuery = useTheContracts({ pagination: { limit: 100 } });\n const contractItems = useMemo(\n () => contractsQuery.data?.items ?? [],\n [contractsQuery.data]\n );\n const contractMap = useMemo(() => {\n const map = new Map<string, TheOfftakeContract>();\n for (const c of contractItems) map.set(c.id, c);\n return map;\n }, [contractItems]);\n\n const scheduleShipment = useTheScheduleShipment();\n const deliverShipment = useTheDeliverShipment();\n const transitionShipment = useTheTransitionShipment();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n const metricsRows = useMemo(() => metricsQuery.data?.items ?? [], [metricsQuery.data]);\n\n // ── Header metrics (derived from the contract-scoped, status-agnostic list) ──\n const scheduledCount = metricsRows.filter((s) => s.status === 'SCHEDULED').length;\n const inTransitCount = metricsRows.filter((s) => s.status === 'IN_TRANSIT').length;\n const deliveredCount = metricsRows.filter((s) => s.status === 'DELIVERED').length;\n\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const s of metricsRows) counts[s.status] = (counts[s.status] ?? 0) + 1;\n return counts;\n }, [metricsRows]);\n\n const filterOptions: FilterOption<TheShipmentStatus | 'ALL'>[] = STATUS_FILTERS.map((opt) => ({\n value: opt.value,\n label: opt.label,\n count: opt.value === 'ALL' ? metricsRows.length : (statusCounts[opt.value] ?? 0),\n }));\n\n // ── Logistics viz (derived from the broad, contract-scoped metrics list) ──\n // Status distribution → AllocationBar segments (lifecycle-ordered, tokenized).\n const statusSegments = useMemo<Segment[]>(\n () =>\n STATUS_ORDER.filter((st) => (statusCounts[st] ?? 0) > 0).map((st) => ({\n label: STATUS_LABEL[st],\n value: statusCounts[st] ?? 0,\n color: STATUS_SEGMENT_COLOR[st],\n })),\n [statusCounts]\n );\n\n // Shipments that carry both an origin and a destination can be route-traced.\n const routeableShipments = useMemo(\n () => metricsRows.filter((s) => (s.origin ?? '').trim() && (s.destination ?? '').trim()),\n [metricsRows]\n );\n\n // Distinct origin → destination lanes across the routeable set.\n const distinctLanes = useMemo(() => {\n const lanes = new Set<string>();\n for (const s of routeableShipments) {\n lanes.add(`${(s.origin ?? '').trim()} → ${(s.destination ?? '').trim()}`);\n }\n return lanes.size;\n }, [routeableShipments]);\n\n const tracedShipment = useMemo(\n () => routeableShipments.find((s) => s.id === tracedId) ?? routeableShipments[0] ?? null,\n [routeableShipments, tracedId]\n );\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setDoc(null);\n setFormError(null);\n };\n\n const handleSchedule = async () => {\n if (!form.contractId) {\n setFormError('Select the contract this shipment fulfils.');\n return;\n }\n if (!form.reference.trim()) {\n setFormError('A shipment reference is required.');\n return;\n }\n const volume = Number(form.volume);\n if (!form.volume || Number.isNaN(volume) || volume <= 0) {\n setFormError('Enter a volume greater than zero.');\n notify.error('Enter a volume greater than zero.');\n return;\n }\n setFormError(null);\n try {\n await scheduleShipment.mutateAsync({\n contractId: form.contractId,\n reference: form.reference.trim(),\n mode: form.mode,\n carrier: form.carrier.trim() || null,\n origin: form.origin.trim() || null,\n destination: form.destination.trim() || null,\n volume,\n unit: form.unit,\n scheduledAt: toBackendDate(form.scheduledAt) ?? null,\n documentFileId: doc?.fileId ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Shipment scheduled');\n setDialogOpen(false);\n resetForm();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to schedule shipment.');\n notify.fromError(err);\n }\n };\n\n const columns: Column<TheShipment>[] = [\n {\n key: 'reference',\n header: 'Shipment',\n render: (s) => (\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{s.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary md:hidden\">{formatEnumLabel(s.mode)}</p>\n </div>\n ),\n },\n {\n key: 'contract',\n header: 'Contract',\n hideOnMobile: true,\n render: (s) => {\n const contract = contractMap.get(s.contractId);\n return contract ? (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{contract.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{contract.counterparty}</p>\n </div>\n ) : (\n <span className=\"font-mono text-xs text-text-tertiary\">{s.contractId.slice(0, 8)}</span>\n );\n },\n },\n {\n key: 'mode',\n header: 'Mode',\n hideOnMobile: true,\n render: (s) => <span className=\"text-text-secondary\">{formatEnumLabel(s.mode)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'route',\n header: 'Route',\n hideOnMobile: true,\n render: (s) => {\n const origin = s.origin?.trim();\n const destination = s.destination?.trim();\n if (!origin && !destination) return <span className=\"text-text-tertiary\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <span className=\"truncate\">{origin ?? '—'}</span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary\" />\n <span className=\"truncate\">{destination ?? '—'}</span>\n </span>\n );\n },\n },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n sortValue: (s) => s.volume,\n render: (s) => (\n <span className=\"tabular-nums text-text-primary\">{formatVolume(s.volume, s.unit)}</span>\n ),\n },\n {\n key: 'scheduledAt',\n header: 'Scheduled',\n align: 'right',\n hideOnMobile: true,\n sortValue: (s) => s.scheduledAt ?? '',\n render: (s) => <span className=\"text-text-tertiary\">{formatDate(s.scheduledAt)}</span>,\n },\n {\n key: 'document',\n header: 'Document',\n hideOnMobile: true,\n render: (s) =>\n s.document?.id ? (\n <FileLink fileId={s.document.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (s) => {\n // Lifecycle transitions reachable via theTransitionShipment (SCHEDULED →\n // LOADING → IN_TRANSIT → CANCELLED). DELIVERED is NOT offered here — it is\n // reached through \"Mark delivered\", which additionally rolls volume into\n // the contract. Terminal states (DELIVERED / CANCELLED) expose neither.\n const nexts = nextStatuses(SHIPMENT_TRANSITIONS, s.status);\n const canDeliver = SHIPMENT_DELIVERABLE_FROM.includes(s.status);\n const rowBusy = transitioningId === s.id;\n if (nexts.length === 0 && !canDeliver) {\n return <span className=\"text-text-tertiary\">—</span>;\n }\n return (\n <div className=\"flex items-center justify-end gap-2\">\n {nexts.length > 0 && (\n <Select\n value={s.status}\n disabled={rowBusy}\n onValueChange={(v) => {\n if (v === s.status) return;\n setTransitioningId(s.id);\n transitionShipment.mutate(\n { id: s.id, status: v as TheShipmentStatus },\n {\n onSuccess: () => notify.success('Shipment updated'),\n onError: (e) => notify.fromError(e),\n onSettled: () => setTransitioningId(null),\n }\n );\n }}\n >\n <SelectTrigger\n className=\"h-8 w-36\"\n aria-label={`Change status for shipment ${s.reference}`}\n >\n <SelectValue placeholder={formatEnumLabel(s.status)} />\n </SelectTrigger>\n <SelectContent>\n {nexts.map((st) => (\n <SelectItem key={st} value={st}>\n {formatEnumLabel(st)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {canDeliver && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={deliverShipment.isPending || rowBusy}\n onClick={() =>\n deliverShipment.mutate(s.id, {\n onSuccess: () => notify.success('Shipment delivered'),\n onError: (e) => notify.fromError(e),\n })\n }\n >\n <PackageCheck className=\"mr-1.5 h-4 w-4\" /> Mark delivered\n </Button>\n )}\n </div>\n );\n },\n },\n ];\n\n const isFiltered = status !== 'ALL' || contractId !== 'ALL';\n\n return (\n <PageLayout\n title=\"Shipments\"\n description=\"Cargo movements delivering offtake contracts — pipelines, tankers, vessels, rail & grid.\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Schedule shipment\n </Button>\n }\n >\n {/* Header metrics — status mix within the current contract scope. */}\n <div className=\"mb-4 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Ship className=\"h-5 w-5\" />}\n value={metricsRows.length}\n label=\"Total shipments\"\n />\n <MetricsCard\n icon={<CalendarClock className=\"h-5 w-5\" />}\n value={scheduledCount}\n label=\"Scheduled\"\n />\n <MetricsCard\n accent=\"warning\"\n icon={<Truck className=\"h-5 w-5\" />}\n value={inTransitCount}\n label=\"In transit\"\n />\n <MetricsCard\n accent=\"success\"\n icon={<PackageCheck className=\"h-5 w-5\" />}\n value={deliveredCount}\n label=\"Delivered\"\n />\n </div>\n\n {/* Logistics — status distribution + per-shipment route flow (broad, contract-scoped). */}\n <div className=\"mb-6 grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <div className=\"flex flex-col gap-4 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Status distribution</h3>\n <Ship className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n {statusSegments.length > 0 ? (\n <AllocationBar\n segments={statusSegments}\n height={14}\n label=\"By status\"\n rightLabel={`${metricsRows.length} shipment${metricsRows.length === 1 ? '' : 's'}`}\n />\n ) : (\n <p className=\"py-6 text-sm text-text-tertiary\">\n No shipments yet — schedule one to see the status mix.\n </p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-3 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Route flow</h3>\n <span className=\"inline-flex items-center gap-1.5 text-xs text-text-tertiary\">\n <MapPin className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {distinctLanes} lane{distinctLanes === 1 ? '' : 's'}\n </span>\n </div>\n {tracedShipment ? (\n <>\n <Select value={tracedShipment.id} onValueChange={setTracedId}>\n <SelectTrigger aria-label=\"Trace a shipment route\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {routeableShipments.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.reference} · {(s.origin ?? '').trim()} → {(s.destination ?? '').trim()}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <PathSteps steps={routeSteps(tracedShipment)} />\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs text-text-tertiary\">\n <StatusBadge status={tracedShipment.status} />\n <span>{formatEnumLabel(tracedShipment.mode)}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatVolume(tracedShipment.volume, tracedShipment.unit)}\n </span>\n {tracedShipment.scheduledAt ? (\n <span className=\"tabular-nums\">Sched {formatDate(tracedShipment.scheduledAt)}</span>\n ) : null}\n </div>\n </>\n ) : (\n <p className=\"py-6 text-sm text-text-tertiary\">\n Add an origin and destination to a shipment to trace its route.\n </p>\n )}\n </div>\n </div>\n\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={filterOptions}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={contractId}\n onValueChange={(v) => {\n setContractId(v as string | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-56\" aria-label=\"Filter by contract\">\n <SelectValue placeholder=\"All contracts\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All contracts</SelectItem>\n {contractItems.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.reference} · {c.counterparty}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading shipments…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load shipments.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Ship className=\"h-10 w-10\" />}\n title={isFiltered ? 'No matching shipments' : 'No shipments yet'}\n description={\n isFiltered\n ? 'No shipments match these filters. Try a different status or contract.'\n : 'Schedule your first shipment to start tracking cargo against your offtake contracts.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Schedule shipment\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n defaultSort={{ key: 'scheduledAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n {/* Schedule shipment dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule shipment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Book a cargo movement against an offtake contract and track it to delivery.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleSchedule();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipContract\">Contract *</Label>\n <Select\n value={form.contractId}\n onValueChange={(v) => {\n // Default the unit to the contract's unit for a sensible start.\n const contract = contractMap.get(v);\n setForm((prev) => ({\n ...prev,\n contractId: v,\n unit: contract?.unit ?? prev.unit,\n }));\n }}\n >\n <SelectTrigger id=\"shipContract\">\n <SelectValue placeholder=\"Select a contract\" />\n </SelectTrigger>\n <SelectContent>\n {contractItems.length === 0 ? (\n <SelectItem value=\"none\" disabled>\n No contracts available\n </SelectItem>\n ) : (\n contractItems.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.reference} · {c.counterparty}\n </SelectItem>\n ))\n )}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipRef\">Reference *</Label>\n <Input\n id=\"shipRef\"\n value={form.reference}\n onChange={(e) => setForm({ ...form, reference: e.target.value })}\n placeholder=\"SHP-2026-0142\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipMode\">Mode</Label>\n <Select\n value={form.mode}\n onValueChange={(v) => setForm({ ...form, mode: v as TheTransportMode })}\n >\n <SelectTrigger id=\"shipMode\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSPORT_MODES.map((m) => (\n <SelectItem key={m} value={m}>\n {formatEnumLabel(m)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipOrigin\">Origin</Label>\n <Input\n id=\"shipOrigin\"\n value={form.origin}\n onChange={(e) => setForm({ ...form, origin: e.target.value })}\n placeholder=\"Ras Tanura Terminal\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipDest\">Destination</Label>\n <Input\n id=\"shipDest\"\n value={form.destination}\n onChange={(e) => setForm({ ...form, destination: e.target.value })}\n placeholder=\"Rotterdam\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipVolume\">Volume *</Label>\n <Input\n id=\"shipVolume\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.volume}\n onChange={(e) => setForm({ ...form, volume: e.target.value })}\n placeholder=\"500000\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipUnit\">Unit</Label>\n <Select\n value={form.unit}\n onValueChange={(v) => setForm({ ...form, unit: v as TheUnit })}\n >\n <SelectTrigger id=\"shipUnit\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {UNITS.map((u) => (\n <SelectItem key={u} value={u}>\n {formatEnumLabel(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipScheduled\">Scheduled date</Label>\n <Input\n id=\"shipScheduled\"\n type=\"date\"\n value={form.scheduledAt}\n onChange={(e) => setForm({ ...form, scheduledAt: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipCarrier\">Carrier</Label>\n <Input\n id=\"shipCarrier\"\n value={form.carrier}\n onChange={(e) => setForm({ ...form, carrier: e.target.value })}\n placeholder=\"Frontline Ltd.\"\n />\n </div>\n\n <EvidenceUpload\n id=\"shipDocument\"\n label=\"Bill of lading (optional)\"\n value={doc}\n onChange={setDoc}\n hint=\"Attach the bill of lading or shipping document (PDF or image).\"\n />\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipNotes\">Notes</Label>\n <Textarea\n id=\"shipNotes\"\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context — laycan window, inspector, terms.\"\n rows={2}\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setDialogOpen(false);\n resetForm();\n }}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={scheduleShipment.isPending}>\n {scheduleShipment.isPending ? 'Scheduling…' : 'Schedule shipment'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShipmentsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/shipments/ShipmentsPage.tsx"],"sourcesContent":["/**\n * Shipments — schedule and track cargo movements against offtake contracts.\n * @module theglobalfuel/pages/shipments\n *\n * Reads `theListShipments` (a cursor connection — `{ items, pageInfo }`, filtered\n * by contract + status) and `theListContracts` (to resolve contract references +\n * populate the picker); writes `theScheduleShipment`, `theDeliverShipment` and\n * lifecycle transitions via `theTransitionShipment`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Ship, CalendarClock, Truck, PackageCheck, ArrowRight, MapPin } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { FileLink } from '../../components/FileLink';\nimport { EvidenceUpload } from '../../components/EvidenceUpload';\nimport { Pagination } from '../../components/Pagination';\nimport { AllocationBar, PathSteps, type PathStep, type Segment } from '../../components';\nimport {\n useNotify,\n useTheShipments,\n useTheContracts,\n useTheScheduleShipment,\n useTheDeliverShipment,\n useTheTransitionShipment,\n useCursorPager,\n type UploadedTheFile,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport { formatEnumLabel, formatDate, formatVolume } from '../../utils/formatters';\nimport {\n toBackendDate,\n SHIPMENT_TRANSITIONS,\n SHIPMENT_DELIVERABLE_FROM,\n nextStatuses,\n} from '../../utils';\nimport type {\n TheShipment,\n TheShipmentStatus,\n TheTransportMode,\n TheUnit,\n TheOfftakeContract,\n} from '../../types';\n\nconst STATUS_FILTERS: Array<{ value: TheShipmentStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'LOADING', label: 'Loading' },\n { value: 'IN_TRANSIT', label: 'In transit' },\n { value: 'DELIVERED', label: 'Delivered' },\n { value: 'CANCELLED', label: 'Cancelled' },\n];\n\nconst TRANSPORT_MODES: TheTransportMode[] = [\n 'PIPELINE',\n 'TANKER',\n 'VESSEL_BULK',\n 'RAIL',\n 'TRUCK',\n 'GRID',\n];\n\nconst UNITS: TheUnit[] = ['BARRELS', 'CUBIC_METERS', 'TONNES', 'KILOGRAMS', 'MWH', 'GJ', 'MCF'];\n\nconst PAGE_SIZE = 25;\n\n/** Lifecycle order for the status-distribution bar + legend. */\nconst STATUS_ORDER: TheShipmentStatus[] = [\n 'SCHEDULED',\n 'LOADING',\n 'IN_TRANSIT',\n 'DELIVERED',\n 'CANCELLED',\n];\n\nconst STATUS_LABEL: Record<TheShipmentStatus, string> = {\n SCHEDULED: 'Scheduled',\n LOADING: 'Loading',\n IN_TRANSIT: 'In transit',\n DELIVERED: 'Delivered',\n CANCELLED: 'Cancelled',\n};\n\n/**\n * Tokenized AllocationBar color per shipment status — aligned to the StatusBadge\n * palette (neutral → info → warning → success → danger). All are semantic CSS\n * variables so the bar themes automatically.\n */\nconst STATUS_SEGMENT_COLOR: Record<TheShipmentStatus, string> = {\n SCHEDULED: 'var(--color-text-muted)',\n LOADING: 'var(--color-accent-blue)',\n IN_TRANSIT: 'var(--color-status-warning-text)',\n DELIVERED: 'var(--color-status-success-text)',\n CANCELLED: 'var(--color-status-error-text)',\n};\n\n/**\n * Build an origin → mode → destination route flow for a shipment, with node\n * states derived from its lifecycle status. Origin/destination are free-text\n * strings — never plotted on a map. Only call with shipments that carry both.\n */\nconst routeSteps = (s: TheShipment): PathStep[] => {\n const cancelled = s.status === 'CANCELLED';\n const delivered = s.status === 'DELIVERED';\n const inTransit = s.status === 'IN_TRANSIT';\n const departed = delivered || inTransit;\n const midMeta = cancelled\n ? 'Cancelled'\n : delivered\n ? 'Delivered'\n : inTransit\n ? 'In transit'\n : formatEnumLabel(s.status);\n return [\n {\n label: (s.origin ?? '').trim(),\n meta: 'Origin',\n state: cancelled ? 'todo' : departed ? 'done' : 'active',\n },\n {\n label: formatEnumLabel(s.mode),\n meta: midMeta,\n state: cancelled ? 'locked' : delivered ? 'done' : inTransit ? 'active' : 'todo',\n },\n {\n label: (s.destination ?? '').trim(),\n meta: 'Destination',\n state: cancelled ? 'locked' : delivered ? 'done' : 'todo',\n },\n ];\n};\n\ninterface ScheduleFormState {\n contractId: string;\n reference: string;\n mode: TheTransportMode;\n carrier: string;\n origin: string;\n destination: string;\n volume: string;\n unit: TheUnit;\n scheduledAt: string;\n notes: string;\n}\n\nconst EMPTY_FORM: ScheduleFormState = {\n contractId: '',\n reference: '',\n mode: 'TANKER',\n carrier: '',\n origin: '',\n destination: '',\n volume: '',\n unit: 'BARRELS',\n scheduledAt: '',\n notes: '',\n};\n\nexport function ShipmentsPage() {\n useThePageView('shipments');\n const t = useTheTranslation();\n const [status, setStatus] = useState<TheShipmentStatus | 'ALL'>('ALL');\n const [contractId, setContractId] = useState<string | 'ALL'>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<ScheduleFormState>(EMPTY_FORM);\n const [doc, setDoc] = useState<UploadedTheFile | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n // Track the shipment whose transition is in flight so only that row's Select\n // disables (transitionShipment.isPending is shared across all rows).\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n // The shipment whose route flow is traced in the logistics panel (falls back\n // to the first routeable shipment when unset or no longer present).\n const [tracedId, setTracedId] = useState<string | null>(null);\n\n const notify = useNotify();\n\n const scopedContractId = contractId === 'ALL' ? undefined : contractId;\n\n // Table query — respects both the contract scope and the status filter, and is\n // cursor-paginated to PAGE_SIZE.\n const { data, isLoading, isError, refetch } = useTheShipments(\n scopedContractId,\n status === 'ALL' ? undefined : status,\n { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE }\n );\n // Metrics query — same contract scope but ignores the status filter so the\n // header counts stay stable as the table is narrowed. A broad limit keeps the\n // KPI status mix representative (the table itself is paginated to PAGE_SIZE).\n const metricsQuery = useTheShipments(scopedContractId, undefined, { limit: 200 });\n\n // Contracts drive the picker + let us resolve a shipment's contract reference.\n const contractsQuery = useTheContracts({ pagination: { limit: 100 } });\n const contractItems = useMemo(() => contractsQuery.data?.items ?? [], [contractsQuery.data]);\n const contractMap = useMemo(() => {\n const map = new Map<string, TheOfftakeContract>();\n for (const c of contractItems) map.set(c.id, c);\n return map;\n }, [contractItems]);\n\n const scheduleShipment = useTheScheduleShipment();\n const deliverShipment = useTheDeliverShipment();\n const transitionShipment = useTheTransitionShipment();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n const metricsRows = useMemo(() => metricsQuery.data?.items ?? [], [metricsQuery.data]);\n\n // ── Header metrics (derived from the contract-scoped, status-agnostic list) ──\n const scheduledCount = metricsRows.filter((s) => s.status === 'SCHEDULED').length;\n const inTransitCount = metricsRows.filter((s) => s.status === 'IN_TRANSIT').length;\n const deliveredCount = metricsRows.filter((s) => s.status === 'DELIVERED').length;\n\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const s of metricsRows) counts[s.status] = (counts[s.status] ?? 0) + 1;\n return counts;\n }, [metricsRows]);\n\n const filterOptions: FilterOption<TheShipmentStatus | 'ALL'>[] = STATUS_FILTERS.map((opt) => ({\n value: opt.value,\n label: opt.label,\n count: opt.value === 'ALL' ? metricsRows.length : (statusCounts[opt.value] ?? 0),\n }));\n\n // ── Logistics viz (derived from the broad, contract-scoped metrics list) ──\n // Status distribution → AllocationBar segments (lifecycle-ordered, tokenized).\n const statusSegments = useMemo<Segment[]>(\n () =>\n STATUS_ORDER.filter((st) => (statusCounts[st] ?? 0) > 0).map((st) => ({\n label: STATUS_LABEL[st],\n value: statusCounts[st] ?? 0,\n color: STATUS_SEGMENT_COLOR[st],\n })),\n [statusCounts]\n );\n\n // Shipments that carry both an origin and a destination can be route-traced.\n const routeableShipments = useMemo(\n () => metricsRows.filter((s) => (s.origin ?? '').trim() && (s.destination ?? '').trim()),\n [metricsRows]\n );\n\n // Distinct origin → destination lanes across the routeable set.\n const distinctLanes = useMemo(() => {\n const lanes = new Set<string>();\n for (const s of routeableShipments) {\n lanes.add(`${(s.origin ?? '').trim()} → ${(s.destination ?? '').trim()}`);\n }\n return lanes.size;\n }, [routeableShipments]);\n\n const tracedShipment = useMemo(\n () => routeableShipments.find((s) => s.id === tracedId) ?? routeableShipments[0] ?? null,\n [routeableShipments, tracedId]\n );\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setDoc(null);\n setFormError(null);\n };\n\n const handleSchedule = async () => {\n if (!form.contractId) {\n setFormError('Select the contract this shipment fulfils.');\n return;\n }\n if (!form.reference.trim()) {\n setFormError('A shipment reference is required.');\n return;\n }\n const volume = Number(form.volume);\n if (!form.volume || Number.isNaN(volume) || volume <= 0) {\n setFormError('Enter a volume greater than zero.');\n notify.error('Enter a volume greater than zero.');\n return;\n }\n setFormError(null);\n try {\n await scheduleShipment.mutateAsync({\n contractId: form.contractId,\n reference: form.reference.trim(),\n mode: form.mode,\n carrier: form.carrier.trim() || null,\n origin: form.origin.trim() || null,\n destination: form.destination.trim() || null,\n volume,\n unit: form.unit,\n scheduledAt: toBackendDate(form.scheduledAt) ?? null,\n documentFileId: doc?.fileId ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Shipment scheduled');\n setDialogOpen(false);\n resetForm();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to schedule shipment.');\n notify.fromError(err);\n }\n };\n\n const columns: Column<TheShipment>[] = [\n {\n key: 'reference',\n header: 'Shipment',\n render: (s) => (\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{s.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary md:hidden\">{formatEnumLabel(s.mode)}</p>\n </div>\n ),\n },\n {\n key: 'contract',\n header: 'Contract',\n hideOnMobile: true,\n render: (s) => {\n const contract = contractMap.get(s.contractId);\n return contract ? (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{contract.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{contract.counterparty}</p>\n </div>\n ) : (\n <span className=\"font-mono text-xs text-text-tertiary\">{s.contractId.slice(0, 8)}</span>\n );\n },\n },\n {\n key: 'mode',\n header: 'Mode',\n hideOnMobile: true,\n render: (s) => <span className=\"text-text-secondary\">{formatEnumLabel(s.mode)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'route',\n header: 'Route',\n hideOnMobile: true,\n render: (s) => {\n const origin = s.origin?.trim();\n const destination = s.destination?.trim();\n if (!origin && !destination) return <span className=\"text-text-tertiary\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <span className=\"truncate\">{origin ?? '—'}</span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary\" />\n <span className=\"truncate\">{destination ?? '—'}</span>\n </span>\n );\n },\n },\n {\n key: 'volume',\n header: 'Volume',\n align: 'right',\n sortValue: (s) => s.volume,\n render: (s) => (\n <span className=\"tabular-nums text-text-primary\">{formatVolume(s.volume, s.unit)}</span>\n ),\n },\n {\n key: 'scheduledAt',\n header: 'Scheduled',\n align: 'right',\n hideOnMobile: true,\n sortValue: (s) => s.scheduledAt ?? '',\n render: (s) => <span className=\"text-text-tertiary\">{formatDate(s.scheduledAt)}</span>,\n },\n {\n key: 'document',\n header: 'Document',\n hideOnMobile: true,\n render: (s) =>\n s.document?.id ? (\n <FileLink fileId={s.document.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (s) => {\n // Lifecycle transitions reachable via theTransitionShipment (SCHEDULED →\n // LOADING → IN_TRANSIT → CANCELLED). DELIVERED is NOT offered here — it is\n // reached through \"Mark delivered\", which additionally rolls volume into\n // the contract. Terminal states (DELIVERED / CANCELLED) expose neither.\n const nexts = nextStatuses(SHIPMENT_TRANSITIONS, s.status);\n const canDeliver = SHIPMENT_DELIVERABLE_FROM.includes(s.status);\n const rowBusy = transitioningId === s.id;\n if (nexts.length === 0 && !canDeliver) {\n return <span className=\"text-text-tertiary\">—</span>;\n }\n return (\n <div className=\"flex items-center justify-end gap-2\">\n {nexts.length > 0 && (\n <Select\n value={s.status}\n disabled={rowBusy}\n onValueChange={(v) => {\n if (v === s.status) return;\n setTransitioningId(s.id);\n transitionShipment.mutate(\n { id: s.id, status: v as TheShipmentStatus },\n {\n onSuccess: () => notify.success('Shipment updated'),\n onError: (e) => notify.fromError(e),\n onSettled: () => setTransitioningId(null),\n }\n );\n }}\n >\n <SelectTrigger\n className=\"h-8 w-36\"\n aria-label={`Change status for shipment ${s.reference}`}\n >\n <SelectValue placeholder={formatEnumLabel(s.status)} />\n </SelectTrigger>\n <SelectContent>\n {nexts.map((st) => (\n <SelectItem key={st} value={st}>\n {formatEnumLabel(st)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {canDeliver && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={deliverShipment.isPending || rowBusy}\n onClick={() =>\n deliverShipment.mutate(s.id, {\n onSuccess: () => notify.success('Shipment delivered'),\n onError: (e) => notify.fromError(e),\n })\n }\n >\n <PackageCheck className=\"mr-1.5 h-4 w-4\" /> Mark delivered\n </Button>\n )}\n </div>\n );\n },\n },\n ];\n\n const isFiltered = status !== 'ALL' || contractId !== 'ALL';\n\n return (\n <PageLayout\n title={t('theglobalfuel.pages.shipments.title', 'Shipments')}\n description={t(\n 'theglobalfuel.pages.shipments.description',\n 'Cargo movements delivering offtake contracts — pipelines, tankers, vessels, rail & grid.'\n )}\n dataTour=\"theglobalfuel-shipments-page\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Schedule shipment\n </Button>\n }\n >\n {/* Header metrics — status mix within the current contract scope. */}\n <div className=\"mb-4 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Ship className=\"h-5 w-5\" />}\n value={metricsRows.length}\n label=\"Total shipments\"\n />\n <MetricsCard\n icon={<CalendarClock className=\"h-5 w-5\" />}\n value={scheduledCount}\n label=\"Scheduled\"\n />\n <MetricsCard\n accent=\"warning\"\n icon={<Truck className=\"h-5 w-5\" />}\n value={inTransitCount}\n label=\"In transit\"\n />\n <MetricsCard\n accent=\"success\"\n icon={<PackageCheck className=\"h-5 w-5\" />}\n value={deliveredCount}\n label=\"Delivered\"\n />\n </div>\n\n {/* Logistics — status distribution + per-shipment route flow (broad, contract-scoped). */}\n <div className=\"mb-6 grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <div className=\"flex flex-col gap-4 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Status distribution</h3>\n <Ship className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n {statusSegments.length > 0 ? (\n <AllocationBar\n segments={statusSegments}\n height={14}\n label=\"By status\"\n rightLabel={`${metricsRows.length} shipment${metricsRows.length === 1 ? '' : 's'}`}\n />\n ) : (\n <p className=\"py-6 text-sm text-text-tertiary\">\n No shipments yet — schedule one to see the status mix.\n </p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-3 rounded-[var(--radius-card)] border border-border-subtle bg-bg-surface p-5 shadow-elevation-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-secondary\">Route flow</h3>\n <span className=\"inline-flex items-center gap-1.5 text-xs text-text-tertiary\">\n <MapPin className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {distinctLanes} lane{distinctLanes === 1 ? '' : 's'}\n </span>\n </div>\n {tracedShipment ? (\n <>\n <Select value={tracedShipment.id} onValueChange={setTracedId}>\n <SelectTrigger aria-label=\"Trace a shipment route\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {routeableShipments.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.reference} · {(s.origin ?? '').trim()} → {(s.destination ?? '').trim()}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <PathSteps steps={routeSteps(tracedShipment)} />\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs text-text-tertiary\">\n <StatusBadge status={tracedShipment.status} />\n <span>{formatEnumLabel(tracedShipment.mode)}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatVolume(tracedShipment.volume, tracedShipment.unit)}\n </span>\n {tracedShipment.scheduledAt ? (\n <span className=\"tabular-nums\">\n Sched {formatDate(tracedShipment.scheduledAt)}\n </span>\n ) : null}\n </div>\n </>\n ) : (\n <p className=\"py-6 text-sm text-text-tertiary\">\n Add an origin and destination to a shipment to trace its route.\n </p>\n )}\n </div>\n </div>\n\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={filterOptions}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={contractId}\n onValueChange={(v) => {\n setContractId(v as string | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-56\" aria-label=\"Filter by contract\">\n <SelectValue placeholder=\"All contracts\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All contracts</SelectItem>\n {contractItems.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.reference} · {c.counterparty}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading shipments…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load shipments.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Ship className=\"h-10 w-10\" />}\n title={isFiltered ? 'No matching shipments' : 'No shipments yet'}\n description={\n isFiltered\n ? 'No shipments match these filters. Try a different status or contract.'\n : 'Schedule your first shipment to start tracking cargo against your offtake contracts.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Schedule shipment\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n defaultSort={{ key: 'scheduledAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n {/* Schedule shipment dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule shipment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Book a cargo movement against an offtake contract and track it to delivery.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleSchedule();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipContract\">Contract *</Label>\n <Select\n value={form.contractId}\n onValueChange={(v) => {\n // Default the unit to the contract's unit for a sensible start.\n const contract = contractMap.get(v);\n setForm((prev) => ({\n ...prev,\n contractId: v,\n unit: contract?.unit ?? prev.unit,\n }));\n }}\n >\n <SelectTrigger id=\"shipContract\">\n <SelectValue placeholder=\"Select a contract\" />\n </SelectTrigger>\n <SelectContent>\n {contractItems.length === 0 ? (\n <SelectItem value=\"none\" disabled>\n No contracts available\n </SelectItem>\n ) : (\n contractItems.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.reference} · {c.counterparty}\n </SelectItem>\n ))\n )}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipRef\">Reference *</Label>\n <Input\n id=\"shipRef\"\n value={form.reference}\n onChange={(e) => setForm({ ...form, reference: e.target.value })}\n placeholder=\"SHP-2026-0142\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipMode\">Mode</Label>\n <Select\n value={form.mode}\n onValueChange={(v) => setForm({ ...form, mode: v as TheTransportMode })}\n >\n <SelectTrigger id=\"shipMode\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSPORT_MODES.map((m) => (\n <SelectItem key={m} value={m}>\n {formatEnumLabel(m)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipOrigin\">Origin</Label>\n <Input\n id=\"shipOrigin\"\n value={form.origin}\n onChange={(e) => setForm({ ...form, origin: e.target.value })}\n placeholder=\"Ras Tanura Terminal\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipDest\">Destination</Label>\n <Input\n id=\"shipDest\"\n value={form.destination}\n onChange={(e) => setForm({ ...form, destination: e.target.value })}\n placeholder=\"Rotterdam\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipVolume\">Volume *</Label>\n <Input\n id=\"shipVolume\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.volume}\n onChange={(e) => setForm({ ...form, volume: e.target.value })}\n placeholder=\"500000\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipUnit\">Unit</Label>\n <Select\n value={form.unit}\n onValueChange={(v) => setForm({ ...form, unit: v as TheUnit })}\n >\n <SelectTrigger id=\"shipUnit\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {UNITS.map((u) => (\n <SelectItem key={u} value={u}>\n {formatEnumLabel(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipScheduled\">Scheduled date</Label>\n <Input\n id=\"shipScheduled\"\n type=\"date\"\n value={form.scheduledAt}\n onChange={(e) => setForm({ ...form, scheduledAt: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipCarrier\">Carrier</Label>\n <Input\n id=\"shipCarrier\"\n value={form.carrier}\n onChange={(e) => setForm({ ...form, carrier: e.target.value })}\n placeholder=\"Frontline Ltd.\"\n />\n </div>\n\n <EvidenceUpload\n id=\"shipDocument\"\n label=\"Bill of lading (optional)\"\n value={doc}\n onChange={setDoc}\n hint=\"Attach the bill of lading or shipping document (PDF or image).\"\n />\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"shipNotes\">Notes</Label>\n <Textarea\n id=\"shipNotes\"\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context — laycan window, inspector, terms.\"\n rows={2}\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setDialogOpen(false);\n resetForm();\n }}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={scheduleShipment.isPending}>\n {scheduleShipment.isPending ? 'Scheduling…' : 'Schedule shipment'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { PageContext } from '../utils/pageContext';
2
+ import { AssistantArtifact, AssistantMode, AssistantPromptResult, AssistantRawMessage, AssistantSandboxAuthContext, AssistantSessionInfo, DownloadedAssistantArtifact } from '../types/assistant';
3
+
4
+ export type { AssistantArtifact, AssistantRawMessage, AssistantSandboxAuthContext, AssistantSessionInfo, DownloadedAssistantArtifact, } from '../types/assistant';
5
+ export declare function findExistingSandbox(workspaceId: string, mode: AssistantMode, authContext?: AssistantSandboxAuthContext): Promise<string | null>;
6
+ export declare function createAssistantSandbox(mode: AssistantMode, workspaceId: string, authContext?: AssistantSandboxAuthContext): Promise<string>;
7
+ export declare function waitForSandboxReady(sandboxId: string, workspaceId: string, authContext?: AssistantSandboxAuthContext, maxWaitMs?: number, onProgress?: (message: string) => void): Promise<void>;
8
+ export declare function waitForAssistantServiceReady(sandboxId: string, workspaceId: string, authContext?: AssistantSandboxAuthContext, maxWaitMs?: number, onProgress?: (message: string) => void): Promise<void>;
9
+ export declare function createAssistantSession(sandboxId: string, workspaceId: string, mode: AssistantMode, authContext?: AssistantSandboxAuthContext): Promise<{
10
+ sessionId: string;
11
+ }>;
12
+ export declare function getAssistantSession(sandboxId: string, workspaceId: string, sessionId: string, authContext?: AssistantSandboxAuthContext): Promise<AssistantSessionInfo>;
13
+ export declare function sendAssistantPrompt(sandboxId: string, workspaceId: string, sessionId: string, prompt: string, mode: AssistantMode, pageContext?: PageContext, authContext?: AssistantSandboxAuthContext): Promise<AssistantPromptResult>;
14
+ export declare function sendAssistantPromptAsync(sandboxId: string, workspaceId: string, sessionId: string, prompt: string, mode: AssistantMode, pageContext?: PageContext, authContext?: AssistantSandboxAuthContext): Promise<void>;
15
+ export declare function getAssistantMessages(sandboxId: string, workspaceId: string, sessionId: string, authContext?: AssistantSandboxAuthContext, limit?: number): Promise<AssistantRawMessage[]>;
16
+ export declare function checkAssistantAiCredits(workspaceId: string, authContext?: AssistantSandboxAuthContext): Promise<boolean>;
17
+ export declare function reportAssistantAiTokenUsage(workspaceId: string, sessionId: string, messageId: string, model: string | undefined, tokens: {
18
+ input: number;
19
+ output: number;
20
+ }, authContext?: AssistantSandboxAuthContext): Promise<void>;
21
+ export declare function getAssistantArtifacts(sandboxId: string, workspaceId: string, sessionId: string, authContext?: AssistantSandboxAuthContext): Promise<AssistantArtifact[]>;
22
+ export declare function downloadAssistantArtifact(sandboxId: string, workspaceId: string, sessionId: string, artifactPath: string, authContext?: AssistantSandboxAuthContext): Promise<DownloadedAssistantArtifact>;
23
+ export declare function sendHeartbeat(sandboxId: string, workspaceId: string, sessionId: string, authContext?: AssistantSandboxAuthContext): Promise<void>;
24
+ export declare function extendAssistantSandboxTTL(sandboxId: string, workspaceId: string, additionalSeconds?: number, authContext?: AssistantSandboxAuthContext): Promise<void>;
25
+ //# sourceMappingURL=assistantApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistantApi.d.ts","sourceRoot":"","sources":["../../../src/theglobalfuel/services/assistantApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,225 @@
1
+ import { graphqlFetch as e } from "@burdenoff/fe-libs/shared/graphql";
2
+ //#region src/theglobalfuel/services/assistantApi.ts
3
+ var t = "burdenoffregistry.azurecr.io", n = "alpha", r = "alpha-proxy-auth-v1", i = "alpha-proxy-auth-v1", a = "alpha-delegated-auth-v11", o = "sandbox-app-client-headers-v1";
4
+ function s(e) {
5
+ switch (e) {
6
+ case "general": return r;
7
+ case "vibe-plugins": return i;
8
+ case "assistant":
9
+ case "api-calls": return a;
10
+ default: return n;
11
+ }
12
+ }
13
+ function c(e) {
14
+ return `${t}/wspace/ai-assistant-${e === "assistant" ? "api-calls" : e}:${s(e)}`;
15
+ }
16
+ function l(e) {
17
+ switch (e) {
18
+ case "general": return {
19
+ cpuRequest: "100m",
20
+ cpuLimit: "500m",
21
+ memoryRequest: "256Mi",
22
+ memoryLimit: "1Gi"
23
+ };
24
+ case "vibe-plugins": return {
25
+ cpuRequest: "150m",
26
+ cpuLimit: "750m",
27
+ memoryRequest: "384Mi",
28
+ memoryLimit: "1536Mi"
29
+ };
30
+ case "assistant":
31
+ case "api-calls": return {
32
+ cpuRequest: "150m",
33
+ cpuLimit: "750m",
34
+ memoryRequest: "384Mi",
35
+ memoryLimit: "1536Mi"
36
+ };
37
+ default: return {
38
+ cpuRequest: "150m",
39
+ cpuLimit: "750m",
40
+ memoryRequest: "384Mi",
41
+ memoryLimit: "1536Mi"
42
+ };
43
+ }
44
+ }
45
+ function u(e) {
46
+ let t = e.metadata?.assistantMode;
47
+ if (t === "assistant" || t === "general" || t === "api-calls" || t === "vibe-plugins") return t;
48
+ let n = e.name ?? "";
49
+ return n.startsWith("ai-assistant-vibe-plugins-") ? "vibe-plugins" : n.startsWith("ai-assistant-api-calls-") ? "api-calls" : n.startsWith("ai-assistant-general-") ? "general" : n.startsWith("ai-assistant-assistant-") ? "assistant" : null;
50
+ }
51
+ function d(e, t) {
52
+ return {
53
+ workspaceId: e,
54
+ ...t?.organizationId ? { custom: { organizationId: t.organizationId } } : {}
55
+ };
56
+ }
57
+ function f(e, t) {
58
+ let n = {};
59
+ return e && (n["x-workspace-id"] = e), t?.userId && (n["x-actor-id"] = t.userId, n["x-actor-type"] = "user"), t?.organizationId && (n["x-org-id"] = t.organizationId), Object.keys(n).length > 0 ? n : void 0;
60
+ }
61
+ async function p(t) {
62
+ let n = f(t.workspaceId, t.authContext), r = await e({
63
+ gateway: "workspace",
64
+ query: t.query,
65
+ variables: t.variables,
66
+ workspaceToken: !0,
67
+ workspaceId: t.workspaceId,
68
+ organizationId: t.authContext?.organizationId ?? void 0,
69
+ authToken: t.authContext?.accessToken ?? void 0,
70
+ ...n ? { extraHeaders: n } : {}
71
+ });
72
+ if (r.errors?.length) {
73
+ let e = r.errors.map((e) => e.message).join(", "), t = Error(e || "Sandbox GraphQL error");
74
+ throw t.result = r, t;
75
+ }
76
+ if (!r.data) throw Error("Sandbox GraphQL returned no data");
77
+ return r.data;
78
+ }
79
+ async function m(e, t, n) {
80
+ return ((await p({
81
+ query: "query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }",
82
+ variables: { context: d(e, n) },
83
+ workspaceId: e,
84
+ authContext: n
85
+ })).getActiveSandboxes ?? []).find((e) => (e.status === "RUNNING" || e.status === "PROVISIONING" || e.status === "PENDING") && e.template === "AI_BUILDER" && u(e) === t && (!n?.userId || e.createdBy === n.userId) && e.metadata?.authStrategy === o && e.metadata?.imageTag === s(t))?.id ?? null;
86
+ }
87
+ async function h(e, t, n) {
88
+ let r = l(e), i = [
89
+ {
90
+ name: "AI_ASSISTANT_MODE",
91
+ value: e
92
+ },
93
+ {
94
+ name: "HOSTNAME",
95
+ value: "0.0.0.0"
96
+ },
97
+ {
98
+ name: "WORKSPACE_ID",
99
+ value: t
100
+ },
101
+ {
102
+ name: "AI_ASSISTANT_PRODUCT",
103
+ value: "theglobalfuel"
104
+ }
105
+ ], a = await p({
106
+ query: "mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) { id }\n }",
107
+ variables: { input: {
108
+ name: `ai-assistant-${e}-${Date.now()}`,
109
+ description: `AI Assistant sandbox (${e} mode)`,
110
+ template: "AI_BUILDER",
111
+ mode: "DEPLOYMENT",
112
+ containers: [{
113
+ name: "ai-assistant",
114
+ image: c(e),
115
+ ports: [{
116
+ containerPort: 8080,
117
+ name: "http"
118
+ }],
119
+ env: i
120
+ }],
121
+ resources: {
122
+ cpuRequest: r.cpuRequest,
123
+ cpuLimit: r.cpuLimit,
124
+ memoryRequest: r.memoryRequest,
125
+ memoryLimit: r.memoryLimit
126
+ },
127
+ ttlSeconds: 600,
128
+ metadata: {
129
+ assistantMode: e,
130
+ assistantOwnerUserId: n?.userId ?? null,
131
+ assistantOrganizationId: n?.organizationId ?? null,
132
+ authStrategy: o,
133
+ imageTag: s(e)
134
+ },
135
+ context: d(t, n)
136
+ } },
137
+ workspaceId: t,
138
+ authContext: n
139
+ });
140
+ if (!a?.createSandbox?.id) throw Error("createSandbox returned empty response");
141
+ return a.createSandbox.id;
142
+ }
143
+ async function g(e, t, n, r = 3e5, i) {
144
+ let a = Date.now();
145
+ for (; Date.now() - a < r;) {
146
+ let r = await p({
147
+ query: "query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) { status errorMessage }\n }",
148
+ variables: {
149
+ id: e,
150
+ context: d(t, n)
151
+ },
152
+ workspaceId: t,
153
+ authContext: n
154
+ }), o = r?.getSandbox?.status, s = r?.getSandbox?.errorMessage;
155
+ if (o === "RUNNING") return;
156
+ if (o === "FAILED" || o === "STOPPED" || o === "EXPIRED") throw Error(s ? `Sandbox ${o.toLowerCase()}: ${s}` : `Sandbox ${o.toLowerCase()}`);
157
+ let c = Math.max(1, Math.round((Date.now() - a) / 1e3));
158
+ i?.(`Starting assistant environment... (${c}s)`), await new Promise((e) => setTimeout(e, 2e3));
159
+ }
160
+ throw Error("Sandbox startup timed out");
161
+ }
162
+ async function _(e, t, n, r, i, a) {
163
+ let o = (await p({
164
+ query: "mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) { status body }\n }",
165
+ variables: { input: {
166
+ sandboxId: e,
167
+ path: n,
168
+ method: r,
169
+ body: i,
170
+ context: d(t, a)
171
+ } },
172
+ workspaceId: t,
173
+ authContext: a
174
+ })).proxySandboxRequest;
175
+ if (!o || o.status >= 400) {
176
+ let e = o?.body;
177
+ throw Error(e?.error ?? `Proxy error: ${o?.status ?? "unknown"}`);
178
+ }
179
+ return o.body;
180
+ }
181
+ async function v(e, t, n, r = 9e4, i) {
182
+ let a = Date.now();
183
+ for (; Date.now() - a < r;) {
184
+ try {
185
+ await _(e, t, "/health", "GET", void 0, n);
186
+ return;
187
+ } catch (e) {
188
+ let t = e instanceof Error ? e.message : String(e);
189
+ if (!/proxy error: 502|proxy error: 503|proxy error: 404|service not available/i.test(t)) throw e;
190
+ }
191
+ let r = Math.max(1, Math.round((Date.now() - a) / 1e3));
192
+ i?.(`Connecting to assistant... (${r}s)`), await new Promise((e) => setTimeout(e, 2e3));
193
+ }
194
+ throw Error("Assistant service did not become healthy before timeout");
195
+ }
196
+ async function y(e, t, n, r) {
197
+ return { sessionId: (await _(e, t, "/sessions", "POST", { mode: n }, r)).session.id };
198
+ }
199
+ async function b(e, t, n, r, i, a, o) {
200
+ let s = {
201
+ mode: i,
202
+ prompt: r,
203
+ pageContext: a ?? void 0
204
+ };
205
+ await _(e, t, `/sessions/${n}/prompt-async`, "POST", s, o);
206
+ }
207
+ async function x(e, t, n, r, i = 50) {
208
+ return (await _(e, t, `/sessions/${n}/messages?limit=${i}`, "GET", void 0, r)).messages ?? [];
209
+ }
210
+ async function S(e, t, n = 600, r) {
211
+ await p({
212
+ query: "mutation ExtendSandboxTTL($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) { id }\n }",
213
+ variables: {
214
+ id: e,
215
+ additionalSeconds: n,
216
+ context: d(t, r)
217
+ },
218
+ workspaceId: t,
219
+ authContext: r
220
+ });
221
+ }
222
+ //#endregion
223
+ export { h as createAssistantSandbox, y as createAssistantSession, S as extendAssistantSandboxTTL, m as findExistingSandbox, x as getAssistantMessages, b as sendAssistantPromptAsync, v as waitForAssistantServiceReady, g as waitForSandboxReady };
224
+
225
+ //# sourceMappingURL=assistantApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistantApi.js","names":[],"sources":["../../../src/theglobalfuel/services/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API service for TheGlobalFuel.\n * @module theglobalfuel/services\n *\n * All requests go through the wspace-public-gateway (GraphQL Federation):\n * Frontend → wspace-public-gateway → wspace-sandbox-svc → ai-assistant container\n *\n * Uses inline GraphQL queries/mutations via fe-libs `graphqlFetch` (the same\n * transport pattern the rest of this MFE uses).\n */\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { PageContext } from '../utils/pageContext';\nimport type {\n AssistantArtifact,\n AssistantMode,\n AssistantPromptResult,\n AssistantRawMessage,\n AssistantSandboxAuthContext,\n AssistantSessionInfo,\n DownloadedAssistantArtifact,\n} from '../types/assistant';\n\nexport type {\n AssistantArtifact,\n AssistantRawMessage,\n AssistantSandboxAuthContext,\n AssistantSessionInfo,\n DownloadedAssistantArtifact,\n} from '../types/assistant';\n\n// ── Image registry ─────────────────────────────────────────────────\n\nconst ACR_REGISTRY = 'burdenoffregistry.azurecr.io';\nconst DEFAULT_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha';\nconst GENERAL_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst VIBE_PLUGINS_IMAGE_TAG =\n import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst API_CALLS_IMAGE_TAG =\n import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-delegated-auth-v11';\nconst ASSISTANT_API_AUTH_STRATEGY = 'sandbox-app-client-headers-v1';\n\nfunction getImageTagForMode(mode: AssistantMode): string {\n switch (mode) {\n case 'general':\n return GENERAL_IMAGE_TAG;\n case 'vibe-plugins':\n return VIBE_PLUGINS_IMAGE_TAG;\n case 'assistant':\n case 'api-calls':\n return API_CALLS_IMAGE_TAG;\n default:\n return DEFAULT_IMAGE_TAG;\n }\n}\n\nfunction getImageForMode(mode: AssistantMode): string {\n const imageMode = mode === 'assistant' ? 'api-calls' : mode;\n return `${ACR_REGISTRY}/wspace/ai-assistant-${imageMode}:${getImageTagForMode(mode)}`;\n}\n\nfunction getResourcesForMode(mode: AssistantMode): {\n cpuRequest: string;\n cpuLimit: string;\n memoryRequest: string;\n memoryLimit: string;\n} {\n switch (mode) {\n case 'general':\n return {\n cpuRequest: '100m',\n cpuLimit: '500m',\n memoryRequest: '256Mi',\n memoryLimit: '1Gi',\n };\n case 'vibe-plugins':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n case 'assistant':\n case 'api-calls':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n default:\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n }\n}\n\nfunction resolveSandboxAssistantMode(sandbox: {\n metadata: Record<string, unknown> | null;\n name?: string | null;\n}): AssistantMode | null {\n const metadataMode = sandbox.metadata?.assistantMode;\n if (\n metadataMode === 'assistant' ||\n metadataMode === 'general' ||\n metadataMode === 'api-calls' ||\n metadataMode === 'vibe-plugins'\n ) {\n return metadataMode;\n }\n\n const name = sandbox.name ?? '';\n if (name.startsWith('ai-assistant-vibe-plugins-')) return 'vibe-plugins';\n if (name.startsWith('ai-assistant-api-calls-')) return 'api-calls';\n if (name.startsWith('ai-assistant-general-')) return 'general';\n if (name.startsWith('ai-assistant-assistant-')) return 'assistant';\n return null;\n}\n\n// ── Types ───────────────────────────────────────────────────────────\n\ninterface ProxyResponse {\n proxySandboxRequest: {\n status: number;\n body: Record<string, unknown>;\n };\n}\n\n// ── Request helpers ─────────────────────────────────────────────────\n\nfunction buildSandboxContextInput(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, unknown> {\n return {\n workspaceId,\n ...(authContext?.organizationId\n ? {\n custom: {\n organizationId: authContext.organizationId,\n },\n }\n : {}),\n };\n}\n\nfunction buildExtraHeaders(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, string> | undefined {\n const headers: Record<string, string> = {};\n if (workspaceId) headers['x-workspace-id'] = workspaceId;\n if (authContext?.userId) {\n headers['x-actor-id'] = authContext.userId;\n headers['x-actor-type'] = 'user';\n }\n if (authContext?.organizationId) headers['x-org-id'] = authContext.organizationId;\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\nasync function executeSandboxGraphQL<TData>(params: {\n query: string;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n const extraHeaders = buildExtraHeaders(params.workspaceId, params.authContext);\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query: params.query,\n variables: params.variables,\n workspaceToken: true,\n workspaceId: params.workspaceId,\n organizationId: params.authContext?.organizationId ?? undefined,\n authToken: params.authContext?.accessToken ?? undefined,\n ...(extraHeaders ? { extraHeaders } : {}),\n });\n\n if (response.errors?.length) {\n const messages = response.errors.map((error: { message: string }) => error.message).join(', ');\n const errorWithStatus = new Error(messages || 'Sandbox GraphQL error') as Error & {\n statusCode?: number;\n result?: unknown;\n };\n errorWithStatus.result = response;\n throw errorWithStatus;\n }\n\n if (!response.data) {\n throw new Error('Sandbox GraphQL returned no data');\n }\n\n return response.data;\n}\n\n// ── Find existing sandbox for a mode ───────────────────────────────\n\nexport async function findExistingSandbox(\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<string | null> {\n const data = await executeSandboxGraphQL<{\n getActiveSandboxes: Array<{\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n }>;\n }>({\n query: `query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }`,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return (\n sandboxes.find(\n (s) =>\n (s.status === 'RUNNING' || s.status === 'PROVISIONING' || s.status === 'PENDING') &&\n s.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(s) === mode &&\n (!authContext?.userId || s.createdBy === authContext.userId) &&\n s.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n s.metadata?.imageTag === getImageTagForMode(mode)\n )?.id ?? null\n );\n}\n\n// ── Sandbox lifecycle ──────────────────────────────\n\nexport async function createAssistantSandbox(\n mode: AssistantMode,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<string> {\n const resources = getResourcesForMode(mode);\n const env = [\n { name: 'AI_ASSISTANT_MODE', value: mode },\n { name: 'HOSTNAME', value: '0.0.0.0' },\n { name: 'WORKSPACE_ID', value: workspaceId },\n { name: 'AI_ASSISTANT_PRODUCT', value: 'theglobalfuel' },\n ];\n\n const data = await executeSandboxGraphQL<{ createSandbox: { id: string } }>({\n query: `mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) { id }\n }`,\n variables: {\n input: {\n name: `ai-assistant-${mode}-${Date.now()}`,\n description: `AI Assistant sandbox (${mode} mode)`,\n template: 'AI_BUILDER',\n mode: 'DEPLOYMENT',\n containers: [\n {\n name: 'ai-assistant',\n image: getImageForMode(mode),\n ports: [{ containerPort: 8080, name: 'http' }],\n env,\n },\n ],\n resources: {\n cpuRequest: resources.cpuRequest,\n cpuLimit: resources.cpuLimit,\n memoryRequest: resources.memoryRequest,\n memoryLimit: resources.memoryLimit,\n },\n ttlSeconds: 600,\n metadata: {\n assistantMode: mode,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode),\n },\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n if (!data?.createSandbox?.id) {\n throw new Error('createSandbox returned empty response');\n }\n return data.createSandbox.id;\n}\n\nexport async function waitForSandboxReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 300_000,\n onProgress?: (message: string) => void\n): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < maxWaitMs) {\n const data = await executeSandboxGraphQL<{\n getSandbox: { status?: string; errorMessage?: string } | null;\n }>({\n query: `query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) { status errorMessage }\n }`,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const status = data?.getSandbox?.status;\n const errorMessage = data?.getSandbox?.errorMessage as string | undefined;\n if (status === 'RUNNING') return;\n if (status === 'FAILED' || status === 'STOPPED' || status === 'EXPIRED') {\n throw new Error(\n errorMessage\n ? `Sandbox ${status.toLowerCase()}: ${errorMessage}`\n : `Sandbox ${status.toLowerCase()}`\n );\n }\n const elapsedSeconds = Math.max(1, Math.round((Date.now() - start) / 1000));\n onProgress?.(`Starting assistant environment... (${elapsedSeconds}s)`);\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error('Sandbox startup timed out');\n}\n\n// ── Proxy calls to ai-assistant (via gateway → sandbox-svc) ───────\n\nasync function proxyToSandbox(\n sandboxId: string,\n workspaceId: string,\n path: string,\n method: string,\n body?: unknown,\n authContext?: AssistantSandboxAuthContext\n): Promise<Record<string, unknown>> {\n const result = await executeSandboxGraphQL<ProxyResponse>({\n query: `mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) { status body }\n }`,\n variables: {\n input: {\n sandboxId,\n path,\n method,\n body,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n const proxyResult = result.proxySandboxRequest;\n if (!proxyResult || proxyResult.status >= 400) {\n const errBody = proxyResult?.body as Record<string, unknown> | undefined;\n throw new Error(\n (errBody?.error as string) ?? `Proxy error: ${proxyResult?.status ?? 'unknown'}`\n );\n }\n\n return proxyResult.body;\n}\n\nexport async function waitForAssistantServiceReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 90_000,\n onProgress?: (message: string) => void\n): Promise<void> {\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < maxWaitMs) {\n try {\n await proxyToSandbox(sandboxId, workspaceId, '/health', 'GET', undefined, authContext);\n return;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (\n !/proxy error: 502|proxy error: 503|proxy error: 404|service not available/i.test(message)\n ) {\n throw error;\n }\n }\n\n const elapsedSeconds = Math.max(1, Math.round((Date.now() - startedAt) / 1000));\n onProgress?.(`Connecting to assistant... (${elapsedSeconds}s)`);\n\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n throw new Error('Assistant service did not become healthy before timeout');\n}\n\n// ── Session management ─────────────────────────────────────────────\n\nexport async function createAssistantSession(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<{ sessionId: string }> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n '/sessions',\n 'POST',\n { mode },\n authContext\n );\n const session = body.session as Record<string, unknown>;\n return { sessionId: session.id as string };\n}\n\nexport async function getAssistantSession(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantSessionInfo> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}`,\n 'GET',\n undefined,\n authContext\n );\n const session = body.session as Record<string, unknown> | undefined;\n\n return {\n id: (session?.id as string | undefined) ?? sessionId,\n mode: (body.mode as string | undefined) ?? 'unknown',\n };\n}\n\n// ── Send prompt ────────────────────────────────────────────────────\n\nexport async function sendAssistantPrompt(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantPromptResult> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext\n ? {\n currentUrl: pageContext.currentUrl,\n currentPage: pageContext.currentPage,\n entityType: pageContext.entityType,\n entityId: pageContext.entityId,\n platform: pageContext.platform,\n version: pageContext.version,\n viewportSize: pageContext.viewportSize,\n }\n : undefined,\n };\n\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/prompt`,\n 'POST',\n requestBody,\n authContext\n );\n\n const message = body.message as Record<string, unknown> | undefined;\n const tokens = message?.tokens as { input: number; output: number } | undefined;\n\n return { response: body.text as string, tokens };\n}\n\nexport async function sendAssistantPromptAsync(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext ?? undefined,\n };\n\n await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/prompt-async`,\n 'POST',\n requestBody,\n authContext\n );\n}\n\nexport async function getAssistantMessages(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext,\n limit = 50\n): Promise<AssistantRawMessage[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/messages?limit=${limit}`,\n 'GET',\n undefined,\n authContext\n );\n\n return (body.messages as AssistantRawMessage[] | undefined) ?? [];\n}\n\n// ── AI credits (per-token metering) ────────────────────────────────\n\nexport async function checkAssistantAiCredits(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<boolean> {\n const data = await executeSandboxGraphQL<{ checkAiCredits: boolean }>({\n query: `query CheckAiCredits { checkAiCredits }`,\n variables: {},\n workspaceId,\n authContext,\n });\n return data.checkAiCredits;\n}\n\nexport async function reportAssistantAiTokenUsage(\n workspaceId: string,\n sessionId: string,\n messageId: string,\n model: string | undefined,\n tokens: { input: number; output: number },\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const inputTokens = Math.max(0, Math.round(tokens.input ?? 0));\n const outputTokens = Math.max(0, Math.round(tokens.output ?? 0));\n if (inputTokens + outputTokens === 0) return;\n if (!model) return;\n\n await executeSandboxGraphQL<{ reportAiTokenUsage: boolean }>({\n query: `mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }`,\n variables: {\n input: { sessionId, messageId, model, inputTokens, outputTokens },\n },\n workspaceId,\n authContext,\n });\n}\n\nexport async function getAssistantArtifacts(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantArtifact[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/artifacts`,\n 'GET',\n undefined,\n authContext\n );\n\n return ((body.artifacts as AssistantArtifact[] | undefined) ?? []).filter(\n (artifact) => !artifact.name.startsWith('.')\n );\n}\n\nexport async function downloadAssistantArtifact(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n artifactPath: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<DownloadedAssistantArtifact> {\n const encodedPath = encodeURIComponent(artifactPath);\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/artifacts/download?path=${encodedPath}`,\n 'GET',\n undefined,\n authContext\n );\n\n const artifact = body.artifact as Record<string, unknown> | undefined;\n if (!artifact) {\n throw new Error('Artifact download returned an empty response');\n }\n\n return {\n filename: artifact.filename as string,\n mimeType: artifact.mimeType as string,\n contentBase64: artifact.contentBase64 as string,\n };\n}\n\n// ── Heartbeat / TTL ───────────────────────────────────────────────────────\n\nexport async function sendHeartbeat(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/heartbeat`,\n 'POST',\n undefined,\n authContext\n );\n}\n\nexport async function extendAssistantSandboxTTL(\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await executeSandboxGraphQL<{ extendSandboxTTL: { id: string } }>({\n query: `mutation ExtendSandboxTTL($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) { id }\n }`,\n variables: {\n id: sandboxId,\n additionalSeconds,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n workspaceId,\n authContext,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * TheGlobalFuel adapter for the shared fe-libs AssistantWidget.
3
+ * @module theglobalfuel/services
4
+ *
5
+ * The floating widget (fe-libs, Layer 1) is backend-agnostic — it calls an
6
+ * injected `AssistantTransport`. This hook builds a transport that reuses the
7
+ * sandbox `api-calls` agent: it provisions/reuses a sandbox, opens a session,
8
+ * dispatches the prompt async, then polls for the streamed answer.
9
+ */
10
+ /**
11
+ * Locally-defined mirror of the fe-libs `AssistantTransport` contract.
12
+ *
13
+ * We intentionally do NOT import the type from `@burdenoff/fe-libs` here:
14
+ * microfe's tsconfig maps `@burdenoff/fe-libs/*` to fe-libs *source*, so
15
+ * vite-plugin-dts would rewrite a cross-package type used in this hook's
16
+ * public signature to a broken source-relative path in the emitted `.d.ts`.
17
+ * Structural typing makes this shape assignable to fe-libs' `AssistantTransport`
18
+ * at the call site (theglobalfuel-app's AppShell), which is where compatibility
19
+ * is enforced — drift there fails the app type-check.
20
+ */
21
+ interface AssistantSendArgs {
22
+ prompt: string;
23
+ onProgress: (partialText: string) => void;
24
+ signal: AbortSignal;
25
+ }
26
+ export interface AssistantTransport {
27
+ sendPrompt: (args: AssistantSendArgs) => Promise<{
28
+ text: string;
29
+ }>;
30
+ }
31
+ /**
32
+ * Returns a memoized `AssistantTransport` wired to the TheGlobalFuel sandbox
33
+ * `api-calls` agent. The sandbox + session are cached in refs so follow-up
34
+ * turns reuse the warm environment for the lifetime of the host shell.
35
+ */
36
+ export declare function useSandboxAssistantTransport(): AssistantTransport;
37
+ export {};
38
+ //# sourceMappingURL=createSandboxAssistantTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createSandboxAssistantTransport.d.ts","sourceRoot":"","sources":["../../../src/theglobalfuel/services/createSandboxAssistantTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH;;;;;;;;;;GAUG;AACH,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAuHD;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,kBAAkB,CAuHjE"}