@burdenoff/microfe-movethewheels 2026.930.5 → 2026.1010.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 (375) hide show
  1. package/dist/MoveTheWheelsRoot.js +177 -102
  2. package/dist/MoveTheWheelsRoutes.js +55 -11
  3. package/dist/_virtual/_rolldown/runtime.js +1 -1
  4. package/dist/components/ContainerEvidencePicker.js +143 -0
  5. package/dist/components/FreeTimeAgreementForm.js +496 -0
  6. package/dist/components/ReturnInstructionForm.js +108 -0
  7. package/dist/components/ReuseControls.js +91 -0
  8. package/dist/components/ReusePermissionForm.js +114 -0
  9. package/dist/components/ReuseProposalForm.js +455 -0
  10. package/dist/components/ReuseSourceFields.js +78 -0
  11. package/dist/components/consolidation/ConsolidationHistory.js +244 -0
  12. package/dist/components/consolidation/ConsolidationLotEditor.js +133 -0
  13. package/dist/components/consolidation/ConsolidationManifestWorkbench.js +143 -0
  14. package/dist/components/consolidation/ConsolidationPlanForm.js +172 -0
  15. package/dist/components/consolidation/ConsolidationPlanWorkbench.js +321 -0
  16. package/dist/components/consolidation/ConsolidationReportForm.js +94 -0
  17. package/dist/components/consolidation/ConsolidationReportsWorkbench.js +195 -0
  18. package/dist/components/consolidation/ConsolidationSummary.js +76 -0
  19. package/dist/components/consolidation/OriginalConsolidationHistory.js +112 -0
  20. package/dist/components/freight-cost/FreightDraftForm.js +213 -0
  21. package/dist/components/freight-cost/FreightFields.js +70 -0
  22. package/dist/components/freight-cost/FreightFxEditor.js +90 -0
  23. package/dist/components/freight-cost/FreightHistory.js +164 -0
  24. package/dist/components/freight-cost/FreightLineEditor.js +191 -0
  25. package/dist/components/freight-cost/FreightScopeEditor.js +72 -0
  26. package/dist/components/freight-cost/FreightTotals.js +66 -0
  27. package/dist/components/logistics-procedure/ProcedureDecisionForm.js +197 -0
  28. package/dist/components/logistics-procedure/ProcedureFields.js +31 -0
  29. package/dist/components/logistics-procedure/ProcedureHistory.js +177 -0
  30. package/dist/components/logistics-procedure/ProcedurePolicyForm.js +227 -0
  31. package/dist/components/logistics-procedure/ProcedurePolicyWorkbench.js +155 -0
  32. package/dist/components/logistics-procedure/ProcedureReviewWorkbench.js +221 -0
  33. package/dist/components/logistics-procedure/ProcedureRunWorkbench.js +190 -0
  34. package/dist/components/logistics-procedure/ProcedureSubjectPicker.js +130 -0
  35. package/dist/components/pending-pick/PendingPickDraft.js +84 -0
  36. package/dist/components/pending-pick/PendingPickHistory.js +113 -0
  37. package/dist/components/pending-pick/PickCapture.js +166 -0
  38. package/dist/components/pending-pick/PickLineSelection.js +111 -0
  39. package/dist/components/stock-binding/StockAssessmentDraft.js +48 -0
  40. package/dist/components/stock-binding/StockAssessmentHistory.js +61 -0
  41. package/dist/components/stock-binding/StockAssessmentSummary.js +199 -0
  42. package/dist/components/stock-binding/StockFactForm.js +232 -0
  43. package/dist/components/stock-observation/StockIssueForm.js +148 -0
  44. package/dist/components/stock-observation/StockIssueHistory.js +116 -0
  45. package/dist/components/stock-observation/StockIssueReadPanel.js +70 -0
  46. package/dist/components/trip-utilization/TripReportForm.js +118 -0
  47. package/dist/components/trip-utilization/TripReportHistory.js +54 -0
  48. package/dist/components/trip-utilization/TripReportSummary.js +216 -0
  49. package/dist/components/trip-utilization/TripSegmentFields.js +166 -0
  50. package/dist/generated/consolidation-operations.d.ts +1060 -0
  51. package/dist/generated/container-condition-operations.d.ts +868 -0
  52. package/dist/generated/container-operations.d.ts +356 -0
  53. package/dist/generated/empty-reuse-operations.d.ts +1578 -0
  54. package/dist/generated/free-time-operations.d.ts +938 -0
  55. package/dist/generated/freight-cost-operations.d.ts +716 -0
  56. package/dist/generated/logistics-procedure-operations.d.ts +938 -0
  57. package/dist/generated/pending-pick-operations.d.ts +1969 -0
  58. package/dist/generated/stock-binding-operations.d.ts +930 -0
  59. package/dist/generated/stock-observation-operations.d.ts +476 -0
  60. package/dist/generated/trip-utilization-operations.d.ts +651 -0
  61. package/dist/generated/wspace-operations.d.ts +143 -50
  62. package/dist/hooks/useConsolidationAction.js +119 -0
  63. package/dist/hooks/useContainerCondition.js +105 -0
  64. package/dist/hooks/useContainerLifecycle.js +192 -0
  65. package/dist/hooks/useEmptyReuseAction.js +45 -0
  66. package/dist/hooks/useFreightRecoveryAction.js +117 -0
  67. package/dist/hooks/useMultiModalShipments.js +1 -1
  68. package/dist/hooks/useProcedureAction.js +119 -0
  69. package/dist/hooks/useProcedureSelection.js +53 -0
  70. package/dist/hooks/useReuseRecoveryAction.js +116 -0
  71. package/dist/movethewheels/components/ContainerEvidencePicker.d.ts +10 -0
  72. package/dist/movethewheels/components/FreeTimeAgreementForm.d.ts +14 -0
  73. package/dist/movethewheels/components/ReturnInstructionForm.d.ts +8 -0
  74. package/dist/movethewheels/components/ReuseControls.d.ts +42 -0
  75. package/dist/movethewheels/components/ReusePermissionForm.d.ts +9 -0
  76. package/dist/movethewheels/components/ReuseProposalForm.d.ts +11 -0
  77. package/dist/movethewheels/components/ReuseSourceFields.d.ts +9 -0
  78. package/dist/movethewheels/components/consolidation/ConsolidationHistory.d.ts +13 -0
  79. package/dist/movethewheels/components/consolidation/ConsolidationLotEditor.d.ts +6 -0
  80. package/dist/movethewheels/components/consolidation/ConsolidationManifestWorkbench.d.ts +8 -0
  81. package/dist/movethewheels/components/consolidation/ConsolidationPlanForm.d.ts +8 -0
  82. package/dist/movethewheels/components/consolidation/ConsolidationPlanWorkbench.d.ts +5 -0
  83. package/dist/movethewheels/components/consolidation/ConsolidationReportForm.d.ts +9 -0
  84. package/dist/movethewheels/components/consolidation/ConsolidationReportsWorkbench.d.ts +7 -0
  85. package/dist/movethewheels/components/consolidation/ConsolidationSummary.d.ts +6 -0
  86. package/dist/movethewheels/components/consolidation/OriginalConsolidationHistory.d.ts +8 -0
  87. package/dist/movethewheels/components/freight-cost/FreightDraftForm.d.ts +8 -0
  88. package/dist/movethewheels/components/freight-cost/FreightFields.d.ts +23 -0
  89. package/dist/movethewheels/components/freight-cost/FreightFxEditor.d.ts +6 -0
  90. package/dist/movethewheels/components/freight-cost/FreightHistory.d.ts +5 -0
  91. package/dist/movethewheels/components/freight-cost/FreightLineEditor.d.ts +8 -0
  92. package/dist/movethewheels/components/freight-cost/FreightScopeEditor.d.ts +6 -0
  93. package/dist/movethewheels/components/freight-cost/FreightTotals.d.ts +6 -0
  94. package/dist/movethewheels/components/logistics-procedure/ProcedureDecisionForm.d.ts +20 -0
  95. package/dist/movethewheels/components/logistics-procedure/ProcedureFields.d.ts +17 -0
  96. package/dist/movethewheels/components/logistics-procedure/ProcedureHistory.d.ts +7 -0
  97. package/dist/movethewheels/components/logistics-procedure/ProcedurePolicyForm.d.ts +10 -0
  98. package/dist/movethewheels/components/logistics-procedure/ProcedurePolicyWorkbench.d.ts +5 -0
  99. package/dist/movethewheels/components/logistics-procedure/ProcedureReviewWorkbench.d.ts +5 -0
  100. package/dist/movethewheels/components/logistics-procedure/ProcedureRunWorkbench.d.ts +6 -0
  101. package/dist/movethewheels/components/logistics-procedure/ProcedureSubjectPicker.d.ts +7 -0
  102. package/dist/movethewheels/components/pending-pick/PendingPickDraft.d.ts +16 -0
  103. package/dist/movethewheels/components/pending-pick/PendingPickHistory.d.ts +5 -0
  104. package/dist/movethewheels/components/pending-pick/PickCapture.d.ts +6 -0
  105. package/dist/movethewheels/components/pending-pick/PickLineSelection.d.ts +17 -0
  106. package/dist/movethewheels/components/stock-binding/StockAssessmentDraft.d.ts +13 -0
  107. package/dist/movethewheels/components/stock-binding/StockAssessmentHistory.d.ts +18 -0
  108. package/dist/movethewheels/components/stock-binding/StockAssessmentSummary.d.ts +9 -0
  109. package/dist/movethewheels/components/stock-binding/StockFactForm.d.ts +6 -0
  110. package/dist/movethewheels/components/stock-observation/StockIssueForm.d.ts +7 -0
  111. package/dist/movethewheels/components/stock-observation/StockIssueHistory.d.ts +7 -0
  112. package/dist/movethewheels/components/stock-observation/StockIssueReadPanel.d.ts +5 -0
  113. package/dist/movethewheels/components/trip-utilization/TripReportForm.d.ts +6 -0
  114. package/dist/movethewheels/components/trip-utilization/TripReportHistory.d.ts +14 -0
  115. package/dist/movethewheels/components/trip-utilization/TripReportSummary.d.ts +5 -0
  116. package/dist/movethewheels/components/trip-utilization/TripSegmentFields.d.ts +9 -0
  117. package/dist/movethewheels/hooks/useConsolidationAction.d.ts +19 -0
  118. package/dist/movethewheels/hooks/useContainerCondition.d.ts +21 -0
  119. package/dist/movethewheels/hooks/useContainerLifecycle.d.ts +40 -0
  120. package/dist/movethewheels/hooks/useEmptyReuseAction.d.ts +14 -0
  121. package/dist/movethewheels/hooks/useFreightRecoveryAction.d.ts +23 -0
  122. package/dist/movethewheels/hooks/useProcedureAction.d.ts +18 -0
  123. package/dist/movethewheels/hooks/useProcedureSelection.d.ts +51 -0
  124. package/dist/movethewheels/hooks/useReuseRecoveryAction.d.ts +18 -0
  125. package/dist/movethewheels/pages/ConsolidationPage.d.ts +1 -0
  126. package/dist/movethewheels/pages/ContainerConditionPage.d.ts +1 -0
  127. package/dist/movethewheels/pages/ContainersPage.d.ts +2 -0
  128. package/dist/movethewheels/pages/EmptyReusePage.d.ts +1 -0
  129. package/dist/movethewheels/pages/FreeTimePage.d.ts +1 -0
  130. package/dist/movethewheels/pages/FreightEstimatePage.d.ts +1 -0
  131. package/dist/movethewheels/pages/LogisticsProceduresPage.d.ts +1 -0
  132. package/dist/movethewheels/pages/PendingPickPage.d.ts +1 -0
  133. package/dist/movethewheels/pages/StockBindingPage.d.ts +1 -0
  134. package/dist/movethewheels/pages/StockObservationsPage.d.ts +1 -0
  135. package/dist/movethewheels/pages/TripUtilizationPage.d.ts +1 -0
  136. package/dist/movethewheels/utils/consolidationHistoryTarget.d.ts +12 -0
  137. package/dist/movethewheels/utils/consolidationInput.d.ts +15 -0
  138. package/dist/movethewheels/utils/consolidationLot.d.ts +3 -0
  139. package/dist/movethewheels/utils/consolidationPins.d.ts +20 -0
  140. package/dist/movethewheels/utils/consolidationRecovery.d.ts +5 -0
  141. package/dist/movethewheels/utils/consolidationReportHead.d.ts +5 -0
  142. package/dist/movethewheels/utils/freeTimeDate.d.ts +1 -0
  143. package/dist/movethewheels/utils/freightAcknowledgement.d.ts +2 -0
  144. package/dist/movethewheels/utils/freightDraft.d.ts +36 -0
  145. package/dist/movethewheels/utils/freightRecovery.d.ts +5 -0
  146. package/dist/movethewheels/utils/graphqlClient.d.ts +2 -1
  147. package/dist/movethewheels/utils/pendingPickDraft.d.ts +8 -0
  148. package/dist/movethewheels/utils/procedureInput.d.ts +18 -0
  149. package/dist/movethewheels/utils/procedureRecovery.d.ts +5 -0
  150. package/dist/movethewheels/utils/reuseAttempt.d.ts +7 -0
  151. package/dist/movethewheels/utils/reuseForm.d.ts +6 -0
  152. package/dist/movethewheels/utils/reuseRecovery.d.ts +26 -0
  153. package/dist/movethewheels/utils/stockBindingForm.d.ts +23 -0
  154. package/dist/movethewheels/utils/stockBindingLabels.d.ts +10 -0
  155. package/dist/movethewheels/utils/stockObservationForm.d.ts +5 -0
  156. package/dist/movethewheels/utils/tripDraftContext.d.ts +25 -0
  157. package/dist/movethewheels/utils/tripUtilizationForm.d.ts +6 -0
  158. package/dist/node_modules/@react-leaflet/core/lib/component.js +5 -5
  159. package/dist/node_modules/fast-equals/dist/es/index.js +87 -74
  160. package/dist/node_modules/leaflet/dist/leaflet-src.js +8 -5
  161. package/dist/node_modules/lodash/_Stack.js +2 -1
  162. package/dist/node_modules/lucide-react/dist/esm/icons/clipboard-list.js +35 -0
  163. package/dist/node_modules/lucide-react/dist/esm/icons/repeat-2.js +22 -0
  164. package/dist/node_modules/react-leaflet/lib/MapContainer.js +3 -3
  165. package/dist/node_modules/react-smooth/es6/Animate.js +2 -2
  166. package/dist/node_modules/recharts/es6/cartesian/Area.js +13 -13
  167. package/dist/node_modules/recharts/es6/cartesian/Bar.js +9 -9
  168. package/dist/node_modules/recharts/es6/cartesian/Brush.js +12 -12
  169. package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js +7 -7
  170. package/dist/node_modules/recharts/es6/cartesian/CartesianGrid.js +13 -13
  171. package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js +4 -4
  172. package/dist/node_modules/recharts/es6/cartesian/Line.js +10 -10
  173. package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js +3 -3
  174. package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js +3 -3
  175. package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js +4 -4
  176. package/dist/node_modules/recharts/es6/cartesian/XAxis.js +3 -3
  177. package/dist/node_modules/recharts/es6/cartesian/YAxis.js +3 -3
  178. package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js +1 -1
  179. package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js +22 -22
  180. package/dist/node_modules/recharts/es6/component/Cursor.js +1 -1
  181. package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js +11 -11
  182. package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js +6 -6
  183. package/dist/node_modules/recharts/es6/component/Label.js +14 -14
  184. package/dist/node_modules/recharts/es6/component/LabelList.js +6 -6
  185. package/dist/node_modules/recharts/es6/component/Legend.js +5 -5
  186. package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js +3 -3
  187. package/dist/node_modules/recharts/es6/component/Text.js +2 -2
  188. package/dist/node_modules/recharts/es6/component/Tooltip.js +3 -3
  189. package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js +2 -2
  190. package/dist/node_modules/recharts/es6/container/Layer.js +2 -2
  191. package/dist/node_modules/recharts/es6/container/Surface.js +2 -2
  192. package/dist/node_modules/recharts/es6/context/chartLayoutContext.js +2 -2
  193. package/dist/node_modules/recharts/es6/polar/Pie.js +13 -13
  194. package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js +8 -8
  195. package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js +6 -6
  196. package/dist/node_modules/recharts/es6/shape/Cross.js +1 -1
  197. package/dist/node_modules/recharts/es6/shape/Curve.js +1 -1
  198. package/dist/node_modules/recharts/es6/shape/Dot.js +1 -1
  199. package/dist/node_modules/recharts/es6/shape/Polygon.js +4 -4
  200. package/dist/node_modules/recharts/es6/shape/Rectangle.js +4 -4
  201. package/dist/node_modules/recharts/es6/shape/Sector.js +1 -1
  202. package/dist/node_modules/recharts/es6/shape/Symbols.js +1 -1
  203. package/dist/node_modules/recharts/es6/shape/Trapezoid.js +4 -4
  204. package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js +9 -9
  205. package/dist/node_modules/recharts/es6/util/BarUtils.js +1 -1
  206. package/dist/node_modules/recharts/es6/util/CartesianUtils.js +1 -1
  207. package/dist/node_modules/recharts/es6/util/PolarUtils.js +1 -1
  208. package/dist/node_modules/recharts/es6/util/ReactUtils.js +2 -2
  209. package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js +1 -1
  210. package/dist/node_modules/recharts/es6/util/types.js +1 -1
  211. package/dist/node_modules/{react-is → recharts/node_modules/react-is}/cjs/react-is.development.js +2 -2
  212. package/dist/node_modules/{react-is → recharts/node_modules/react-is}/cjs/react-is.production.min.js +2 -2
  213. package/dist/node_modules/{react-is → recharts/node_modules/react-is}/index.js +2 -2
  214. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +97 -18
  215. package/dist/node_modules/zustand/esm/middleware.js +2 -2
  216. package/dist/pages/ConsolidationPage.js +76 -0
  217. package/dist/pages/ContainerConditionPage.js +972 -0
  218. package/dist/pages/ContainersPage.js +522 -0
  219. package/dist/pages/EmptyReusePage.js +777 -0
  220. package/dist/pages/FreeTimePage.js +657 -0
  221. package/dist/pages/FreightEstimatePage.js +284 -0
  222. package/dist/pages/LogisticsProceduresPage.js +100 -0
  223. package/dist/pages/PendingPickPage.js +155 -0
  224. package/dist/pages/StockBindingPage.js +236 -0
  225. package/dist/pages/StockObservationsPage.js +168 -0
  226. package/dist/pages/TripUtilizationPage.js +227 -0
  227. package/dist/src/generated/consolidation-operations.js +505 -0
  228. package/dist/src/generated/container-condition-operations.js +208 -0
  229. package/dist/src/generated/container-operations.js +101 -0
  230. package/dist/src/generated/empty-reuse-operations.js +749 -0
  231. package/dist/src/generated/free-time-operations.js +235 -0
  232. package/dist/src/generated/freight-cost-operations.js +325 -0
  233. package/dist/src/generated/logistics-procedure-operations.js +362 -0
  234. package/dist/src/generated/pending-pick-operations.js +1128 -0
  235. package/dist/src/generated/stock-binding-operations.js +175 -0
  236. package/dist/src/generated/stock-observation-operations.js +93 -0
  237. package/dist/src/generated/trip-utilization-operations.js +140 -0
  238. package/dist/src/generated/wspace-operations.js +708 -0
  239. package/dist/utils/consolidationHistoryTarget.js +27 -0
  240. package/dist/utils/consolidationInput.js +21 -0
  241. package/dist/utils/consolidationLot.js +29 -0
  242. package/dist/utils/consolidationPins.js +13 -0
  243. package/dist/utils/consolidationRecovery.js +40 -0
  244. package/dist/utils/consolidationReportHead.js +18 -0
  245. package/dist/utils/freeTimeDate.js +26 -0
  246. package/dist/utils/freightAcknowledgement.js +8 -0
  247. package/dist/utils/freightDraft.js +125 -0
  248. package/dist/utils/freightRecovery.js +36 -0
  249. package/dist/utils/graphqlClient.js +28 -28
  250. package/dist/utils/pendingPickDraft.js +46 -0
  251. package/dist/utils/procedureInput.js +43 -0
  252. package/dist/utils/procedureRecovery.js +42 -0
  253. package/dist/utils/reuseAttempt.js +20 -0
  254. package/dist/utils/reuseForm.js +25 -0
  255. package/dist/utils/reuseRecovery.js +71 -0
  256. package/dist/utils/stockBindingForm.js +142 -0
  257. package/dist/utils/stockBindingLabels.js +23 -0
  258. package/dist/utils/stockObservationForm.js +55 -0
  259. package/dist/utils/tripDraftContext.js +19 -0
  260. package/dist/utils/tripUtilizationForm.js +60 -0
  261. package/package.json +20 -5
  262. package/src/generated/consolidation-operations.ts +1138 -0
  263. package/src/generated/container-condition-operations.ts +771 -0
  264. package/src/generated/container-operations.ts +376 -0
  265. package/src/generated/empty-reuse-operations.ts +1648 -0
  266. package/src/generated/free-time-operations.ts +814 -0
  267. package/src/generated/freight-cost-operations.ts +753 -0
  268. package/src/generated/logistics-procedure-operations.ts +1047 -0
  269. package/src/generated/pending-pick-operations.ts +1508 -0
  270. package/src/generated/stock-binding-operations.ts +631 -0
  271. package/src/generated/stock-observation-operations.ts +373 -0
  272. package/src/generated/trip-utilization-operations.ts +500 -0
  273. package/src/generated/wspace-operations.ts +116 -0
  274. package/src/movethewheels/MoveTheWheelsRoot.tsx +79 -0
  275. package/src/movethewheels/MoveTheWheelsRoutes.tsx +22 -0
  276. package/src/movethewheels/components/ContainerEvidencePicker.tsx +146 -0
  277. package/src/movethewheels/components/FreeTimeAgreementForm.tsx +483 -0
  278. package/src/movethewheels/components/ReturnInstructionForm.tsx +102 -0
  279. package/src/movethewheels/components/ReuseControls.tsx +138 -0
  280. package/src/movethewheels/components/ReusePermissionForm.tsx +123 -0
  281. package/src/movethewheels/components/ReuseProposalForm.tsx +484 -0
  282. package/src/movethewheels/components/ReuseSourceFields.tsx +80 -0
  283. package/src/movethewheels/components/consolidation/ConsolidationHistory.tsx +159 -0
  284. package/src/movethewheels/components/consolidation/ConsolidationLotEditor.tsx +144 -0
  285. package/src/movethewheels/components/consolidation/ConsolidationManifestWorkbench.tsx +168 -0
  286. package/src/movethewheels/components/consolidation/ConsolidationPlanForm.tsx +185 -0
  287. package/src/movethewheels/components/consolidation/ConsolidationPlanWorkbench.tsx +395 -0
  288. package/src/movethewheels/components/consolidation/ConsolidationReportForm.tsx +124 -0
  289. package/src/movethewheels/components/consolidation/ConsolidationReportsWorkbench.tsx +233 -0
  290. package/src/movethewheels/components/consolidation/ConsolidationSummary.tsx +84 -0
  291. package/src/movethewheels/components/consolidation/OriginalConsolidationHistory.tsx +133 -0
  292. package/src/movethewheels/components/freight-cost/FreightDraftForm.tsx +224 -0
  293. package/src/movethewheels/components/freight-cost/FreightFields.tsx +91 -0
  294. package/src/movethewheels/components/freight-cost/FreightFxEditor.tsx +98 -0
  295. package/src/movethewheels/components/freight-cost/FreightHistory.tsx +91 -0
  296. package/src/movethewheels/components/freight-cost/FreightLineEditor.tsx +192 -0
  297. package/src/movethewheels/components/freight-cost/FreightScopeEditor.tsx +64 -0
  298. package/src/movethewheels/components/freight-cost/FreightTotals.tsx +68 -0
  299. package/src/movethewheels/components/logistics-procedure/ProcedureDecisionForm.tsx +204 -0
  300. package/src/movethewheels/components/logistics-procedure/ProcedureFields.tsx +43 -0
  301. package/src/movethewheels/components/logistics-procedure/ProcedureHistory.tsx +102 -0
  302. package/src/movethewheels/components/logistics-procedure/ProcedurePolicyForm.tsx +226 -0
  303. package/src/movethewheels/components/logistics-procedure/ProcedurePolicyWorkbench.tsx +174 -0
  304. package/src/movethewheels/components/logistics-procedure/ProcedureReviewWorkbench.tsx +278 -0
  305. package/src/movethewheels/components/logistics-procedure/ProcedureRunWorkbench.tsx +292 -0
  306. package/src/movethewheels/components/logistics-procedure/ProcedureSubjectPicker.tsx +111 -0
  307. package/src/movethewheels/components/pending-pick/PendingPickDraft.tsx +82 -0
  308. package/src/movethewheels/components/pending-pick/PendingPickHistory.tsx +93 -0
  309. package/src/movethewheels/components/pending-pick/PickCapture.tsx +70 -0
  310. package/src/movethewheels/components/pending-pick/PickLineSelection.tsx +130 -0
  311. package/src/movethewheels/components/stock-binding/StockAssessmentDraft.tsx +52 -0
  312. package/src/movethewheels/components/stock-binding/StockAssessmentHistory.tsx +56 -0
  313. package/src/movethewheels/components/stock-binding/StockAssessmentSummary.tsx +108 -0
  314. package/src/movethewheels/components/stock-binding/StockFactForm.tsx +186 -0
  315. package/src/movethewheels/components/stock-observation/StockIssueForm.tsx +103 -0
  316. package/src/movethewheels/components/stock-observation/StockIssueHistory.tsx +96 -0
  317. package/src/movethewheels/components/stock-observation/StockIssueReadPanel.tsx +72 -0
  318. package/src/movethewheels/components/trip-utilization/TripReportForm.tsx +116 -0
  319. package/src/movethewheels/components/trip-utilization/TripReportHistory.tsx +51 -0
  320. package/src/movethewheels/components/trip-utilization/TripReportSummary.tsx +127 -0
  321. package/src/movethewheels/components/trip-utilization/TripSegmentFields.tsx +118 -0
  322. package/src/movethewheels/hooks/useConsolidationAction.ts +196 -0
  323. package/src/movethewheels/hooks/useContainerCondition.ts +129 -0
  324. package/src/movethewheels/hooks/useContainerLifecycle.ts +291 -0
  325. package/src/movethewheels/hooks/useEmptyReuseAction.ts +72 -0
  326. package/src/movethewheels/hooks/useFreightRecoveryAction.ts +201 -0
  327. package/src/movethewheels/hooks/useMultiModalShipments.ts +2 -1
  328. package/src/movethewheels/hooks/useProcedureAction.ts +196 -0
  329. package/src/movethewheels/hooks/useProcedureSelection.ts +75 -0
  330. package/src/movethewheels/hooks/useReuseRecoveryAction.ts +192 -0
  331. package/src/movethewheels/pages/ConsolidationPage.tsx +70 -0
  332. package/src/movethewheels/pages/ContainerConditionPage.tsx +959 -0
  333. package/src/movethewheels/pages/ContainersPage.tsx +490 -0
  334. package/src/movethewheels/pages/EmptyReusePage.tsx +823 -0
  335. package/src/movethewheels/pages/FreeTimePage.tsx +720 -0
  336. package/src/movethewheels/pages/FreightEstimatePage.tsx +353 -0
  337. package/src/movethewheels/pages/LogisticsProceduresPage.tsx +86 -0
  338. package/src/movethewheels/pages/PendingPickPage.tsx +226 -0
  339. package/src/movethewheels/pages/StockBindingPage.tsx +299 -0
  340. package/src/movethewheels/pages/StockObservationsPage.tsx +181 -0
  341. package/src/movethewheels/pages/TripUtilizationPage.tsx +315 -0
  342. package/src/movethewheels/utils/consolidationHistoryTarget.ts +39 -0
  343. package/src/movethewheels/utils/consolidationInput.ts +70 -0
  344. package/src/movethewheels/utils/consolidationLot.ts +24 -0
  345. package/src/movethewheels/utils/consolidationPins.ts +52 -0
  346. package/src/movethewheels/utils/consolidationRecovery.ts +88 -0
  347. package/src/movethewheels/utils/consolidationReportHead.ts +29 -0
  348. package/src/movethewheels/utils/freeTimeDate.ts +31 -0
  349. package/src/movethewheels/utils/freightAcknowledgement.ts +31 -0
  350. package/src/movethewheels/utils/freightDraft.ts +170 -0
  351. package/src/movethewheels/utils/freightRecovery.ts +79 -0
  352. package/src/movethewheels/utils/graphqlClient.ts +14 -5
  353. package/src/movethewheels/utils/pendingPickDraft.ts +61 -0
  354. package/src/movethewheels/utils/procedureInput.ts +98 -0
  355. package/src/movethewheels/utils/procedureRecovery.ts +92 -0
  356. package/src/movethewheels/utils/reuseAttempt.ts +29 -0
  357. package/src/movethewheels/utils/reuseForm.ts +30 -0
  358. package/src/movethewheels/utils/reuseRecovery.ts +151 -0
  359. package/src/movethewheels/utils/stockBindingForm.ts +209 -0
  360. package/src/movethewheels/utils/stockBindingLabels.ts +41 -0
  361. package/src/movethewheels/utils/stockObservationForm.ts +73 -0
  362. package/src/movethewheels/utils/tripDraftContext.ts +61 -0
  363. package/src/movethewheels/utils/tripUtilizationForm.ts +105 -0
  364. package/src/operations/condition/container-condition.graphql +41 -0
  365. package/src/operations/consolidation/consolidation.graphql +490 -0
  366. package/src/operations/container/container-lifecycle.graphql +80 -0
  367. package/src/operations/empty-reuse/empty-reuse.graphql +737 -0
  368. package/src/operations/free-time/free-time.graphql +47 -0
  369. package/src/operations/freight-cost/freight.graphql +6 -0
  370. package/src/operations/logistics-procedure/procedure.graphql +15 -0
  371. package/src/operations/pending-pick/pending-pick.graphql +1122 -0
  372. package/src/operations/stock-binding/stock-binding.graphql +160 -0
  373. package/src/operations/stock-observation/stock-observation.graphql +20 -0
  374. package/src/operations/trip-utilization/trip-utilization.graphql +126 -0
  375. package/src/operations/wspace/container-evidence.graphql +6 -0
@@ -0,0 +1,196 @@
1
+ import { useRef, useState } from 'react';
2
+ import { print, type DocumentNode } from 'graphql';
3
+ import { useConditionApi } from './useContainerCondition';
4
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
5
+ import {
6
+ SaveConsolidationPlanDocument,
7
+ RecordConsolidationObservationDocument,
8
+ SaveConsolidationManifestDocument,
9
+ } from '../../generated/consolidation-operations';
10
+ import { originalConsolidationHistoryTarget } from '../utils/consolidationHistoryTarget';
11
+ import { consolidationAcknowledgement } from '../utils/consolidationInput';
12
+ import {
13
+ reuseRecoveryScope,
14
+ reuseRecoveryContextKey,
15
+ sameReuseRecoveryScope,
16
+ type ReuseRecovery,
17
+ } from '../utils/reuseRecovery';
18
+ import {
19
+ decodeConsolidationRecovery,
20
+ encodeConsolidationRecovery,
21
+ CONSOLIDATION_RECOVERY_STORAGE,
22
+ } from '../utils/consolidationRecovery';
23
+ const documents = [
24
+ SaveConsolidationPlanDocument,
25
+ RecordConsolidationObservationDocument,
26
+ SaveConsolidationManifestDocument,
27
+ ];
28
+ function loadRecovery(): { attempt: ReuseRecovery | null; blocked: boolean } {
29
+ try {
30
+ const raw = sessionStorage.getItem(CONSOLIDATION_RECOVERY_STORAGE);
31
+ const attempt = decodeConsolidationRecovery(raw);
32
+ return { attempt, blocked: raw !== null && attempt === null };
33
+ } catch {
34
+ return { attempt: null, blocked: true };
35
+ }
36
+ }
37
+ /** F08 retains one unresolved request across entity/context remounts; it never sends automatically. */
38
+ export function useConsolidationAction() {
39
+ const request = useConditionApi('SINGLE_ATTEMPT');
40
+ const context = useMoveTheWheels();
41
+ const scope = reuseRecoveryScope(
42
+ context.currentUser?.id,
43
+ context.workspaceId,
44
+ context.tenantId,
45
+ context.apiGatewayUrl,
46
+ context.workspaceToken
47
+ );
48
+ const [initial] = useState(loadRecovery);
49
+ const [pending, setPending] = useState<ReuseRecovery | null>(initial.attempt);
50
+ const [blocked, setBlocked] = useState(initial.blocked);
51
+ const pendingRef = useRef(pending);
52
+ const running = useRef(false);
53
+ const [busy, setBusy] = useState(false);
54
+ const contextKey = reuseRecoveryContextKey(scope);
55
+ const [feedback, setFeedback] = useState<{
56
+ scopeKey: string;
57
+ error: string | null;
58
+ notice: string | null;
59
+ }>({ scopeKey: contextKey, error: null, notice: null });
60
+ // Async callbacks retain their submitting context; a new context masks their feedback.
61
+ const setError = (error: string | null) =>
62
+ setFeedback({ scopeKey: contextKey, error, notice: null });
63
+ const setNotice = (notice: string | null) =>
64
+ setFeedback({ scopeKey: contextKey, error: null, notice });
65
+ const [revision, setRevision] = useState(0);
66
+ const retain = (attempt: ReuseRecovery | null, completed?: ReuseRecovery) => {
67
+ if (attempt)
68
+ sessionStorage.setItem(CONSOLIDATION_RECOVERY_STORAGE, encodeConsolidationRecovery(attempt));
69
+ else {
70
+ const saved = decodeConsolidationRecovery(
71
+ sessionStorage.getItem(CONSOLIDATION_RECOVERY_STORAGE)
72
+ );
73
+ // An older in-flight reply must not erase a subsequently retained attempt after a remount.
74
+ if (
75
+ !completed ||
76
+ (saved?.key === completed.key && sameReuseRecoveryScope(saved.scope, completed.scope))
77
+ )
78
+ sessionStorage.removeItem(CONSOLIDATION_RECOVERY_STORAGE);
79
+ }
80
+ pendingRef.current = attempt;
81
+ setPending(attempt);
82
+ setBlocked(false);
83
+ };
84
+ const send = async (attempt: ReuseRecovery) => {
85
+ if (running.current) return null;
86
+ if (!sameReuseRecoveryScope(attempt.scope, scope)) {
87
+ setError(
88
+ 'Return to the original account, organization, workspace and project before retrying.'
89
+ );
90
+ return null;
91
+ }
92
+ const document = documents.find((item) => print(item) === attempt.operation);
93
+ if (!document) {
94
+ setError('The saved operation changed. Check history before abandoning this attempt.');
95
+ return null;
96
+ }
97
+ running.current = true;
98
+ setBusy(true);
99
+ setError(null);
100
+ setNotice(null);
101
+ try {
102
+ const result = await request<Record<string, unknown>>(document, {
103
+ input: { ...attempt.input, idempotencyKey: attempt.key },
104
+ });
105
+ if (!consolidationAcknowledgement(result, attempt.input, attempt.scope.actorId))
106
+ throw new Error(
107
+ 'The save reply could not be confirmed. Keep the original request and check history.'
108
+ );
109
+ // Clearing storage may fail after a known reply. Retain the same key rather than making a new intent.
110
+ retain(null, attempt);
111
+ setRevision((value) => value + 1);
112
+ setNotice(attempt.success);
113
+ return result;
114
+ } catch (failure) {
115
+ setError(
116
+ failure instanceof Error
117
+ ? failure.message
118
+ : 'Unable to save. Check history before retrying.'
119
+ );
120
+ return null;
121
+ } finally {
122
+ running.current = false;
123
+ setBusy(false);
124
+ }
125
+ };
126
+ const run = async (document: DocumentNode, input: Record<string, unknown>, success: string) => {
127
+ if (running.current || pendingRef.current || blocked) {
128
+ setError('Resolve the original request before starting another.');
129
+ return null;
130
+ }
131
+ if (!scope) {
132
+ setError('Wait for the current account and workspace session.');
133
+ return null;
134
+ }
135
+ const operation = print(document);
136
+ if (!documents.some((item) => print(item) === operation)) {
137
+ setError('Unsupported consolidation operation.');
138
+ return null;
139
+ }
140
+ try {
141
+ if (sessionStorage.getItem(CONSOLIDATION_RECOVERY_STORAGE) !== null) {
142
+ setError(
143
+ 'A saved request is unresolved. Reload this page and check history before starting another.'
144
+ );
145
+ return null;
146
+ }
147
+ } catch {
148
+ setError('This browser cannot retain the request safely. Nothing was sent.');
149
+ return null;
150
+ }
151
+ const attempt: ReuseRecovery = {
152
+ version: 1,
153
+ scope,
154
+ operation,
155
+ input: structuredClone(input),
156
+ key: crypto.randomUUID(),
157
+ success,
158
+ };
159
+ try {
160
+ retain(attempt);
161
+ } catch {
162
+ setError('This browser cannot retain the request safely. Nothing was sent.');
163
+ return null;
164
+ }
165
+ return send(attempt);
166
+ };
167
+ const retryOriginal = async () => {
168
+ if (pendingRef.current) await send(pendingRef.current);
169
+ };
170
+ const discardAttempt = () => {
171
+ if (running.current) return;
172
+ try {
173
+ retain(null);
174
+ setError(null);
175
+ setNotice(null);
176
+ } catch {
177
+ setError('Unable to clear the saved attempt. Nothing was sent.');
178
+ }
179
+ };
180
+ return {
181
+ run,
182
+ retryOriginal,
183
+ discardAttempt,
184
+ refresh: () => setRevision((value) => value + 1),
185
+ busy,
186
+ error: feedback.scopeKey === contextKey ? feedback.error : null,
187
+ notice: feedback.scopeKey === contextKey ? feedback.notice : null,
188
+ revision,
189
+ uncertain: pending !== null || blocked,
190
+ recoveryInvalid: blocked,
191
+ canRetry: pending !== null && sameReuseRecoveryScope(pending.scope, scope),
192
+ originalContext: pending?.scope ?? null,
193
+ originalHistoryTarget: originalConsolidationHistoryTarget(pending, scope),
194
+ contextKey,
195
+ };
196
+ }
@@ -0,0 +1,129 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { print, type DocumentNode } from 'graphql';
3
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
4
+ import { executeGraphQL, type GraphQLAttemptPolicy } from '../utils/graphqlClient';
5
+
6
+ export function useConditionApi(attemptPolicy: GraphQLAttemptPolicy = 'RETRY_TRANSIENT') {
7
+ const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
8
+ useMoveTheWheels();
9
+ const options = useMemo(
10
+ () => ({
11
+ apiGatewayUrl,
12
+ authToken,
13
+ workspaceToken,
14
+ workspaceId,
15
+ tenantId,
16
+ actorId: currentUser?.id,
17
+ }),
18
+ [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
19
+ );
20
+ return useCallback(
21
+ <T>(document: DocumentNode, variables: Record<string, unknown>) =>
22
+ executeGraphQL<T>(options, print(document), variables, attemptPolicy),
23
+ [options, attemptPolicy]
24
+ );
25
+ }
26
+
27
+ /** Bounded pages, cancellation of stale responses, and explicit access failures. */
28
+ export function useConditionQuery<T>(
29
+ document: DocumentNode,
30
+ variables: Record<string, unknown> | null,
31
+ revision = 0
32
+ ) {
33
+ const request = useConditionApi();
34
+ const variablesKey = JSON.stringify(variables);
35
+ const [state, setState] = useState<{
36
+ request: typeof request;
37
+ variablesKey: string;
38
+ revision: number;
39
+ data: T | null;
40
+ error: string | null;
41
+ loading: boolean;
42
+ } | null>(null);
43
+ useEffect(() => {
44
+ let active = true;
45
+ const scope = { request, variablesKey, revision };
46
+ setState({ ...scope, data: null, error: null, loading: variablesKey !== 'null' });
47
+ if (variablesKey === 'null') return;
48
+ const parsed: Record<string, unknown> = JSON.parse(variablesKey);
49
+ void request<T>(document, parsed)
50
+ .then((data) => {
51
+ if (active) setState({ ...scope, data, error: null, loading: false });
52
+ })
53
+ .catch((failure: unknown) => {
54
+ if (active)
55
+ setState({
56
+ ...scope,
57
+ data: null,
58
+ error: failure instanceof Error ? failure.message : 'Unable to load these records.',
59
+ loading: false,
60
+ });
61
+ });
62
+ return () => {
63
+ active = false;
64
+ };
65
+ }, [document, variablesKey, request, revision]);
66
+ // Mask during render, before effects run after a context/access or revision change.
67
+ const current =
68
+ state?.request === request &&
69
+ state.variablesKey === variablesKey &&
70
+ state.revision === revision;
71
+ return {
72
+ data: current ? state.data : null,
73
+ error: current ? state.error : null,
74
+ loading: current ? state.loading : variablesKey !== 'null',
75
+ };
76
+ }
77
+
78
+ /** Preserve a request's idempotency key through transport retries until success. */
79
+ export function useConditionAction() {
80
+ const request = useConditionApi();
81
+ const pending = useRef(new Map<string, { key: string; input: Record<string, unknown> }>());
82
+ const [hasPendingAttempt, setHasPendingAttempt] = useState(false);
83
+ const [busy, setBusy] = useState(false);
84
+ const [error, setError] = useState<string | null>(null);
85
+ const [notice, setNotice] = useState<string | null>(null);
86
+ const [revision, setRevision] = useState(0);
87
+ const run = async <T>(
88
+ document: DocumentNode,
89
+ input: Record<string, unknown>,
90
+ success: string
91
+ ) => {
92
+ // A refreshed server revision must not turn an uncertain committed attempt into a new mutation.
93
+ const intent = Object.fromEntries(
94
+ Object.entries(input).filter(([name]) => name !== 'expectedVersion')
95
+ );
96
+ const fingerprint = JSON.stringify({ operation: print(document), input: intent });
97
+ const attempt = pending.current.get(fingerprint) ?? { key: crypto.randomUUID(), input };
98
+ pending.current.set(fingerprint, attempt);
99
+ setBusy(true);
100
+ setError(null);
101
+ setNotice(null);
102
+ try {
103
+ const result = await request<T>(document, {
104
+ input: { ...attempt.input, idempotencyKey: attempt.key },
105
+ });
106
+ pending.current.delete(fingerprint);
107
+ setHasPendingAttempt(pending.current.size > 0);
108
+ setRevision((value) => value + 1);
109
+ setNotice(success);
110
+ return result;
111
+ } catch (failure) {
112
+ setHasPendingAttempt(true);
113
+ setError(failure instanceof Error ? failure.message : 'Unable to save this record.');
114
+ return null;
115
+ } finally {
116
+ setBusy(false);
117
+ }
118
+ };
119
+ const resetFeedback = () => {
120
+ setError(null);
121
+ setNotice(null);
122
+ };
123
+ const discardAttempt = () => {
124
+ pending.current.clear();
125
+ setHasPendingAttempt(false);
126
+ resetFeedback();
127
+ };
128
+ return { run, busy, error, notice, revision, resetFeedback, hasPendingAttempt, discardAttempt };
129
+ }
@@ -0,0 +1,291 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { print } from 'graphql';
3
+ import {
4
+ ContainerRegistryDocument,
5
+ ContainerCyclesDocument,
6
+ ContainerEventsDocument,
7
+ RegisterContainerDocument,
8
+ OpenContainerCycleDocument,
9
+ RecordContainerEventDocument,
10
+ CloseContainerCycleDocument,
11
+ type ContainerEquipmentFieldsFragment,
12
+ type ContainerCycleFieldsFragment,
13
+ type ContainerEventFieldsFragment,
14
+ type ContainerRegistryQuery,
15
+ type ContainerCyclesQuery,
16
+ type ContainerEventsQuery,
17
+ type RegisterContainerMutation,
18
+ type OpenContainerCycleMutation,
19
+ type RecordContainerEventMutation,
20
+ type CloseContainerCycleMutation,
21
+ type RegisterMtwContainerInput,
22
+ type OpenMtwContainerCycleInput,
23
+ type RecordMtwContainerEventInput,
24
+ } from '../../generated/container-operations';
25
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
26
+ import { executeGraphQL } from '../utils/graphqlClient';
27
+
28
+ export const EQUIPMENT_TYPES = [
29
+ 'DRY_20',
30
+ 'DRY_40',
31
+ 'HIGH_CUBE_40',
32
+ 'REEFER_20',
33
+ 'REEFER_40',
34
+ 'OPEN_TOP',
35
+ 'FLAT_RACK',
36
+ 'TANK',
37
+ 'OTHER',
38
+ ] as const;
39
+ export const CONTAINER_EVENT_TYPES = [
40
+ 'GATE_OUT',
41
+ 'LOADED',
42
+ 'DISCHARGED',
43
+ 'AVAILABLE_FOR_PICKUP',
44
+ 'GATE_IN',
45
+ 'DELIVERED',
46
+ 'EMPTY_READY',
47
+ 'RETURN_REQUESTED',
48
+ 'EMPTY_RETURNED',
49
+ 'RETURN_ACCEPTED',
50
+ 'POSITION_UPDATE',
51
+ ] as const;
52
+ export type EquipmentType = (typeof EQUIPMENT_TYPES)[number];
53
+ export type ContainerEventType = (typeof CONTAINER_EVENT_TYPES)[number];
54
+ export type ContainerEventKind = 'ACTUAL' | 'PLANNED' | 'ESTIMATED';
55
+
56
+ export type ContainerRecord = ContainerEquipmentFieldsFragment;
57
+ export type ContainerCycle = ContainerCycleFieldsFragment;
58
+ export type ContainerEvent = ContainerEventFieldsFragment;
59
+
60
+ const PAGE_SIZE = 25;
61
+
62
+ function describeError(error: unknown): string {
63
+ return error instanceof Error ? error.message : 'The operation could not be completed.';
64
+ }
65
+
66
+ /** Source-labelled physical equipment; cargo shipment completion does not close a cycle. */
67
+ export function useContainerLifecycle() {
68
+ const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
69
+ useMoveTheWheels();
70
+ const options = useMemo(
71
+ () => ({
72
+ apiGatewayUrl,
73
+ authToken,
74
+ workspaceToken,
75
+ workspaceId,
76
+ tenantId,
77
+ actorId: currentUser?.id,
78
+ }),
79
+ [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
80
+ );
81
+ const [containers, setContainers] = useState<ContainerRecord[]>([]);
82
+ const [cycles, setCycles] = useState<ContainerCycle[]>([]);
83
+ const [events, setEvents] = useState<ContainerEvent[]>([]);
84
+ const [hasMore, setHasMore] = useState(false);
85
+ const [hasMoreCycles, setHasMoreCycles] = useState(false);
86
+ const [hasMoreEvents, setHasMoreEvents] = useState(false);
87
+ const [containerId, setContainerId] = useState('');
88
+ const [cycleId, setCycleId] = useState('');
89
+ const [page, setPage] = useState(0);
90
+ const [cyclePage, setCyclePage] = useState(0);
91
+ const [eventPage, setEventPage] = useState(0);
92
+ const [revision, setRevision] = useState(0);
93
+ const [loading, setLoading] = useState(false);
94
+ const [cyclesLoading, setCyclesLoading] = useState(false);
95
+ const [eventsLoading, setEventsLoading] = useState(false);
96
+ const [error, setError] = useState<string | null>(null);
97
+ const [cycleError, setCycleError] = useState<string | null>(null);
98
+ const [eventError, setEventError] = useState<string | null>(null);
99
+
100
+ useEffect(() => {
101
+ let cancelled = false;
102
+ setLoading(true);
103
+ setError(null);
104
+ setContainers([]);
105
+ setHasMore(false);
106
+ void executeGraphQL<ContainerRegistryQuery>(options, print(ContainerRegistryDocument), {
107
+ pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },
108
+ })
109
+ .then((data) => {
110
+ if (!cancelled) {
111
+ setContainers(data.mtwContainerPage.items);
112
+ setHasMore(data.mtwContainerPage.hasMore);
113
+ }
114
+ })
115
+ .catch((failure: unknown) => {
116
+ if (!cancelled) setError(describeError(failure));
117
+ })
118
+ .finally(() => {
119
+ if (!cancelled) setLoading(false);
120
+ });
121
+ return () => {
122
+ cancelled = true;
123
+ };
124
+ }, [options, page, revision]);
125
+
126
+ // Clear selections whenever the security/workspace context changes.
127
+ useEffect(() => {
128
+ setContainerId('');
129
+ setCycleId('');
130
+ setPage(0);
131
+ setCyclePage(0);
132
+ setEventPage(0);
133
+ }, [options]);
134
+
135
+ useEffect(() => {
136
+ let cancelled = false;
137
+ setCycles([]);
138
+ setHasMoreCycles(false);
139
+ setCycleError(null);
140
+ if (!containerId) {
141
+ setCyclesLoading(false);
142
+ return;
143
+ }
144
+ setCyclesLoading(true);
145
+ void executeGraphQL<ContainerCyclesQuery>(options, print(ContainerCyclesDocument), {
146
+ containerId,
147
+ pagination: { limit: PAGE_SIZE, offset: cyclePage * PAGE_SIZE },
148
+ })
149
+ .then((data) => {
150
+ if (!cancelled) {
151
+ setCycles(data.mtwContainerCyclePage.items);
152
+ setHasMoreCycles(data.mtwContainerCyclePage.hasMore);
153
+ }
154
+ })
155
+ .catch((failure: unknown) => {
156
+ if (!cancelled) setCycleError(describeError(failure));
157
+ })
158
+ .finally(() => {
159
+ if (!cancelled) setCyclesLoading(false);
160
+ });
161
+ return () => {
162
+ cancelled = true;
163
+ };
164
+ }, [options, containerId, cyclePage, revision]);
165
+
166
+ useEffect(() => {
167
+ let cancelled = false;
168
+ setEvents([]);
169
+ setHasMoreEvents(false);
170
+ setEventError(null);
171
+ if (!cycleId) {
172
+ setEventsLoading(false);
173
+ return;
174
+ }
175
+ setEventsLoading(true);
176
+ void executeGraphQL<ContainerEventsQuery>(options, print(ContainerEventsDocument), {
177
+ cycleId,
178
+ pagination: { limit: PAGE_SIZE, offset: eventPage * PAGE_SIZE },
179
+ })
180
+ .then((data) => {
181
+ if (!cancelled) {
182
+ setEvents(data.mtwContainerEventPage.items);
183
+ setHasMoreEvents(data.mtwContainerEventPage.hasMore);
184
+ }
185
+ })
186
+ .catch((failure: unknown) => {
187
+ if (!cancelled) setEventError(describeError(failure));
188
+ })
189
+ .finally(() => {
190
+ if (!cancelled) setEventsLoading(false);
191
+ });
192
+ return () => {
193
+ cancelled = true;
194
+ };
195
+ }, [options, cycleId, eventPage, revision]);
196
+
197
+ const refresh = useCallback(() => setRevision((value) => value + 1), []);
198
+ // Keep the same key after an uncertain response so a manual retry replays the
199
+ // committed operation. Changed payloads and confirmed successes start new work.
200
+ const pendingMutations = useRef(new Map<string, { fingerprint: string; key: string }>());
201
+ const retryKey = (operation: string, payload: unknown) => {
202
+ const fingerprint = JSON.stringify(payload);
203
+ const pending = pendingMutations.current.get(operation);
204
+ if (pending?.fingerprint === fingerprint) return pending.key;
205
+ const key = crypto.randomUUID();
206
+ pendingMutations.current.set(operation, { fingerprint, key });
207
+ return key;
208
+ };
209
+ const selectContainer = (id: string) => {
210
+ setContainerId(id);
211
+ setCycleId('');
212
+ setCyclePage(0);
213
+ setEventPage(0);
214
+ };
215
+ const selectCycle = (id: string) => {
216
+ setCycleId(id);
217
+ setEventPage(0);
218
+ };
219
+ const register = async (input: Omit<RegisterMtwContainerInput, 'idempotencyKey'>) => {
220
+ const data = await executeGraphQL<RegisterContainerMutation>(
221
+ options,
222
+ print(RegisterContainerDocument),
223
+ { input: { ...input, idempotencyKey: retryKey('register', input) } }
224
+ );
225
+ pendingMutations.current.delete('register');
226
+ setPage(0);
227
+ refresh();
228
+ selectContainer(data.registerMtwContainer.id);
229
+ };
230
+ const openCycle = async (input: Omit<OpenMtwContainerCycleInput, 'idempotencyKey'>) => {
231
+ const data = await executeGraphQL<OpenContainerCycleMutation>(
232
+ options,
233
+ print(OpenContainerCycleDocument),
234
+ { input: { ...input, idempotencyKey: retryKey('open', input) } }
235
+ );
236
+ pendingMutations.current.delete('open');
237
+ setCyclePage(0);
238
+ refresh();
239
+ selectCycle(data.openMtwContainerCycle.id);
240
+ };
241
+ const recordEvent = async (input: Omit<RecordMtwContainerEventInput, 'idempotencyKey'>) => {
242
+ await executeGraphQL<RecordContainerEventMutation>(
243
+ options,
244
+ print(RecordContainerEventDocument),
245
+ { input: { ...input, idempotencyKey: retryKey('record', input) } }
246
+ );
247
+ pendingMutations.current.delete('record');
248
+ setEventPage(0);
249
+ refresh();
250
+ };
251
+ const closeCycle = async (cycle: ContainerCycle) => {
252
+ const input = { cycleId: cycle.id, expectedVersion: cycle.version };
253
+ await executeGraphQL<CloseContainerCycleMutation>(options, print(CloseContainerCycleDocument), {
254
+ input: {
255
+ ...input,
256
+ idempotencyKey: retryKey('close', input),
257
+ },
258
+ });
259
+ pendingMutations.current.delete('close');
260
+ refresh();
261
+ };
262
+ return {
263
+ containers,
264
+ cycles,
265
+ events,
266
+ containerId,
267
+ cycleId,
268
+ loading,
269
+ cyclesLoading,
270
+ eventsLoading,
271
+ error,
272
+ cycleError,
273
+ eventError,
274
+ page,
275
+ setPage,
276
+ cyclePage,
277
+ setCyclePage,
278
+ eventPage,
279
+ setEventPage,
280
+ hasMore,
281
+ hasMoreCycles,
282
+ hasMoreEvents,
283
+ selectContainer,
284
+ selectCycle,
285
+ refresh,
286
+ register,
287
+ openCycle,
288
+ recordEvent,
289
+ closeCycle,
290
+ };
291
+ }
@@ -0,0 +1,72 @@
1
+ import { useRef, useState } from 'react';
2
+ import { print, type DocumentNode } from 'graphql';
3
+ import { useConditionApi } from './useContainerCondition';
4
+ import { reuseAttempt } from '../utils/reuseAttempt';
5
+
6
+ /** An uncertain retry keeps its original body and key, even after a read refresh. */
7
+ export function useEmptyReuseAction() {
8
+ const request = useConditionApi();
9
+ const pending = useRef<{
10
+ fingerprint: string;
11
+ input: Record<string, unknown>;
12
+ key: string;
13
+ document: DocumentNode;
14
+ success: string;
15
+ } | null>(null);
16
+ const running = useRef(false);
17
+ const [busy, setBusy] = useState(false);
18
+ const [error, setError] = useState<string | null>(null);
19
+ const [notice, setNotice] = useState<string | null>(null);
20
+ const [uncertain, setUncertain] = useState(false);
21
+ const [revision, setRevision] = useState(0);
22
+ const run = async <T>(
23
+ document: DocumentNode,
24
+ input: Record<string, unknown>,
25
+ success: string
26
+ ) => {
27
+ if (running.current) return null;
28
+ const selected = reuseAttempt(pending.current, print(document), input, () =>
29
+ crypto.randomUUID()
30
+ );
31
+ const attempt =
32
+ pending.current === selected ? pending.current : { ...selected, document, success };
33
+ pending.current = attempt;
34
+ running.current = true;
35
+ setBusy(true);
36
+ setError(null);
37
+ setNotice(null);
38
+ try {
39
+ const result = await request<T>(document, {
40
+ input: { ...attempt.input, idempotencyKey: attempt.key },
41
+ });
42
+ pending.current = null;
43
+ setUncertain(false);
44
+ setRevision((value) => value + 1);
45
+ setNotice(success);
46
+ return result;
47
+ } catch (failure) {
48
+ setUncertain(true);
49
+ setError(
50
+ failure instanceof Error
51
+ ? failure.message
52
+ : 'Unable to save. Refresh history before retrying.'
53
+ );
54
+ return null;
55
+ } finally {
56
+ running.current = false;
57
+ setBusy(false);
58
+ }
59
+ };
60
+ const retryOriginal = async () => {
61
+ const attempt = pending.current;
62
+ if (attempt) await run(attempt.document, attempt.input, attempt.success);
63
+ };
64
+ const refresh = () => setRevision((value) => value + 1);
65
+ const discardAttempt = () => {
66
+ pending.current = null;
67
+ setUncertain(false);
68
+ setError(null);
69
+ setNotice(null);
70
+ };
71
+ return { run, retryOriginal, busy, error, notice, uncertain, revision, refresh, discardAttempt };
72
+ }