@freighttech/shipment-tracking 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +25 -0
  3. package/dist/generated/entities/bic_config/index.js +21 -0
  4. package/dist/generated/entities/bic_config/index.js.map +7 -0
  5. package/dist/generated/entities/carrier_config/index.js +27 -0
  6. package/dist/generated/entities/carrier_config/index.js.map +7 -0
  7. package/dist/generated/entities/location_override/index.js +33 -0
  8. package/dist/generated/entities/location_override/index.js.map +7 -0
  9. package/dist/generated/entities/shipment/index.js +73 -0
  10. package/dist/generated/entities/shipment/index.js.map +7 -0
  11. package/dist/generated/entities/ships_go_config/index.js +31 -0
  12. package/dist/generated/entities/ships_go_config/index.js.map +7 -0
  13. package/dist/generated/entities/tracking_event/index.js +101 -0
  14. package/dist/generated/entities/tracking_event/index.js.map +7 -0
  15. package/dist/generated/entities/tracking_job/index.js +47 -0
  16. package/dist/generated/entities/tracking_job/index.js.map +7 -0
  17. package/dist/generated/entities/webhook/index.js +25 -0
  18. package/dist/generated/entities/webhook/index.js.map +7 -0
  19. package/dist/generated/entities/webhook_delivery/index.js +25 -0
  20. package/dist/generated/entities/webhook_delivery/index.js.map +7 -0
  21. package/dist/generated/entities.ids.generated.js +21 -0
  22. package/dist/generated/entities.ids.generated.js.map +7 -0
  23. package/dist/index.js +41 -0
  24. package/dist/index.js.map +7 -0
  25. package/dist/modules/shipment_tracking/acl.js +22 -0
  26. package/dist/modules/shipment_tracking/acl.js.map +7 -0
  27. package/dist/modules/shipment_tracking/api/bic-configs/route.js +176 -0
  28. package/dist/modules/shipment_tracking/api/bic-configs/route.js.map +7 -0
  29. package/dist/modules/shipment_tracking/api/bic-configs/test/route.js +95 -0
  30. package/dist/modules/shipment_tracking/api/bic-configs/test/route.js.map +7 -0
  31. package/dist/modules/shipment_tracking/api/cargo-events/route.js +93 -0
  32. package/dist/modules/shipment_tracking/api/cargo-events/route.js.map +7 -0
  33. package/dist/modules/shipment_tracking/api/carrier-configs/route.js +139 -0
  34. package/dist/modules/shipment_tracking/api/carrier-configs/route.js.map +7 -0
  35. package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js +177 -0
  36. package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js.map +7 -0
  37. package/dist/modules/shipment_tracking/api/location-overrides/route.js +179 -0
  38. package/dist/modules/shipment_tracking/api/location-overrides/route.js.map +7 -0
  39. package/dist/modules/shipment_tracking/api/openapi.js +27 -0
  40. package/dist/modules/shipment_tracking/api/openapi.js.map +7 -0
  41. package/dist/modules/shipment_tracking/api/shipments/route.js +192 -0
  42. package/dist/modules/shipment_tracking/api/shipments/route.js.map +7 -0
  43. package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js +185 -0
  44. package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js.map +7 -0
  45. package/dist/modules/shipment_tracking/api/tracking-jobs/route.js +144 -0
  46. package/dist/modules/shipment_tracking/api/tracking-jobs/route.js.map +7 -0
  47. package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js +107 -0
  48. package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js.map +7 -0
  49. package/dist/modules/shipment_tracking/api/utils.js +12 -0
  50. package/dist/modules/shipment_tracking/api/utils.js.map +7 -0
  51. package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js +76 -0
  52. package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js.map +7 -0
  53. package/dist/modules/shipment_tracking/api/webhooks/route.js +135 -0
  54. package/dist/modules/shipment_tracking/api/webhooks/route.js.map +7 -0
  55. package/dist/modules/shipment_tracking/api/webhooks/test/route.js +71 -0
  56. package/dist/modules/shipment_tracking/api/webhooks/test/route.js.map +7 -0
  57. package/dist/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
  58. package/dist/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
  59. package/dist/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
  60. package/dist/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
  61. package/dist/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
  62. package/dist/modules/shipment_tracking/assets/carriers/msc.png +0 -0
  63. package/dist/modules/shipment_tracking/assets/carriers/one.png +0 -0
  64. package/dist/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
  65. package/dist/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
  66. package/dist/modules/shipment_tracking/assets/carriers/zim.png +0 -0
  67. package/dist/modules/shipment_tracking/assets/index.js +52 -0
  68. package/dist/modules/shipment_tracking/assets/index.js.map +7 -0
  69. package/dist/modules/shipment_tracking/backend/carrier-configs/page.js +621 -0
  70. package/dist/modules/shipment_tracking/backend/carrier-configs/page.js.map +7 -0
  71. package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js +19 -0
  72. package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js.map +7 -0
  73. package/dist/modules/shipment_tracking/backend/location-overrides/page.js +198 -0
  74. package/dist/modules/shipment_tracking/backend/location-overrides/page.js.map +7 -0
  75. package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js +19 -0
  76. package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js.map +7 -0
  77. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js +279 -0
  78. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js.map +7 -0
  79. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js +19 -0
  80. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js.map +7 -0
  81. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js +10 -0
  82. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js.map +7 -0
  83. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js +19 -0
  84. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js.map +7 -0
  85. package/dist/modules/shipment_tracking/backend/webhooks/page.js +198 -0
  86. package/dist/modules/shipment_tracking/backend/webhooks/page.js.map +7 -0
  87. package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js +19 -0
  88. package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js.map +7 -0
  89. package/dist/modules/shipment_tracking/ce.js +52 -0
  90. package/dist/modules/shipment_tracking/ce.js.map +7 -0
  91. package/dist/modules/shipment_tracking/cli.js +229 -0
  92. package/dist/modules/shipment_tracking/cli.js.map +7 -0
  93. package/dist/modules/shipment_tracking/commands/bic-configs.js +66 -0
  94. package/dist/modules/shipment_tracking/commands/bic-configs.js.map +7 -0
  95. package/dist/modules/shipment_tracking/commands/carrier-configs.js +65 -0
  96. package/dist/modules/shipment_tracking/commands/carrier-configs.js.map +7 -0
  97. package/dist/modules/shipment_tracking/commands/index.js +9 -0
  98. package/dist/modules/shipment_tracking/commands/index.js.map +7 -0
  99. package/dist/modules/shipment_tracking/commands/location-overrides.js +93 -0
  100. package/dist/modules/shipment_tracking/commands/location-overrides.js.map +7 -0
  101. package/dist/modules/shipment_tracking/commands/shipments.js +200 -0
  102. package/dist/modules/shipment_tracking/commands/shipments.js.map +7 -0
  103. package/dist/modules/shipment_tracking/commands/shipsgo-configs.js +75 -0
  104. package/dist/modules/shipment_tracking/commands/shipsgo-configs.js.map +7 -0
  105. package/dist/modules/shipment_tracking/commands/tracking-jobs.js +120 -0
  106. package/dist/modules/shipment_tracking/commands/tracking-jobs.js.map +7 -0
  107. package/dist/modules/shipment_tracking/commands/tracking.js +140 -0
  108. package/dist/modules/shipment_tracking/commands/tracking.js.map +7 -0
  109. package/dist/modules/shipment_tracking/commands/webhooks.js +115 -0
  110. package/dist/modules/shipment_tracking/commands/webhooks.js.map +7 -0
  111. package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js +168 -0
  112. package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js.map +7 -0
  113. package/dist/modules/shipment_tracking/components/FacilityLocationInput.js +202 -0
  114. package/dist/modules/shipment_tracking/components/FacilityLocationInput.js.map +7 -0
  115. package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js +501 -0
  116. package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js.map +7 -0
  117. package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js +818 -0
  118. package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js.map +7 -0
  119. package/dist/modules/shipment_tracking/components/ShipmentDrawer.js +404 -0
  120. package/dist/modules/shipment_tracking/components/ShipmentDrawer.js.map +7 -0
  121. package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js +200 -0
  122. package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js.map +7 -0
  123. package/dist/modules/shipment_tracking/components/TimestampCell.js +108 -0
  124. package/dist/modules/shipment_tracking/components/TimestampCell.js.map +7 -0
  125. package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js +343 -0
  126. package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js.map +7 -0
  127. package/dist/modules/shipment_tracking/components/VesselTrackingMap.js +496 -0
  128. package/dist/modules/shipment_tracking/components/VesselTrackingMap.js.map +7 -0
  129. package/dist/modules/shipment_tracking/components/WebhookDrawer.js +332 -0
  130. package/dist/modules/shipment_tracking/components/WebhookDrawer.js.map +7 -0
  131. package/dist/modules/shipment_tracking/data/entities.js +686 -0
  132. package/dist/modules/shipment_tracking/data/entities.js.map +7 -0
  133. package/dist/modules/shipment_tracking/data/validators.js +434 -0
  134. package/dist/modules/shipment_tracking/data/validators.js.map +7 -0
  135. package/dist/modules/shipment_tracking/di.js +60 -0
  136. package/dist/modules/shipment_tracking/di.js.map +7 -0
  137. package/dist/modules/shipment_tracking/encryption.js +12 -0
  138. package/dist/modules/shipment_tracking/encryption.js.map +7 -0
  139. package/dist/modules/shipment_tracking/events.js +87 -0
  140. package/dist/modules/shipment_tracking/events.js.map +7 -0
  141. package/dist/modules/shipment_tracking/hooks/useVesselTracking.js +137 -0
  142. package/dist/modules/shipment_tracking/hooks/useVesselTracking.js.map +7 -0
  143. package/dist/modules/shipment_tracking/i18n/de.json +327 -0
  144. package/dist/modules/shipment_tracking/i18n/en.json +327 -0
  145. package/dist/modules/shipment_tracking/i18n/es.json +327 -0
  146. package/dist/modules/shipment_tracking/i18n/pl.json +327 -0
  147. package/dist/modules/shipment_tracking/index.js +15 -0
  148. package/dist/modules/shipment_tracking/index.js.map +7 -0
  149. package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js +66 -0
  150. package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js.map +7 -0
  151. package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js +1 -0
  152. package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js.map +7 -0
  153. package/dist/modules/shipment_tracking/lib/adapters/cosco.js +236 -0
  154. package/dist/modules/shipment_tracking/lib/adapters/cosco.js.map +7 -0
  155. package/dist/modules/shipment_tracking/lib/adapters/evergreen.js +87 -0
  156. package/dist/modules/shipment_tracking/lib/adapters/evergreen.js.map +7 -0
  157. package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js +68 -0
  158. package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js.map +7 -0
  159. package/dist/modules/shipment_tracking/lib/adapters/index.js +20 -0
  160. package/dist/modules/shipment_tracking/lib/adapters/index.js.map +7 -0
  161. package/dist/modules/shipment_tracking/lib/adapters/maersk.js +84 -0
  162. package/dist/modules/shipment_tracking/lib/adapters/maersk.js.map +7 -0
  163. package/dist/modules/shipment_tracking/lib/adapters/msc.js +170 -0
  164. package/dist/modules/shipment_tracking/lib/adapters/msc.js.map +7 -0
  165. package/dist/modules/shipment_tracking/lib/adapters/zim.js +85 -0
  166. package/dist/modules/shipment_tracking/lib/adapters/zim.js.map +7 -0
  167. package/dist/modules/shipment_tracking/lib/audit-helpers.js +20 -0
  168. package/dist/modules/shipment_tracking/lib/audit-helpers.js.map +7 -0
  169. package/dist/modules/shipment_tracking/lib/auth/base64url.js +8 -0
  170. package/dist/modules/shipment_tracking/lib/auth/base64url.js.map +7 -0
  171. package/dist/modules/shipment_tracking/lib/auth/oauth-client.js +38 -0
  172. package/dist/modules/shipment_tracking/lib/auth/oauth-client.js.map +7 -0
  173. package/dist/modules/shipment_tracking/lib/background.js +15 -0
  174. package/dist/modules/shipment_tracking/lib/background.js.map +7 -0
  175. package/dist/modules/shipment_tracking/lib/bic-api-client.js +106 -0
  176. package/dist/modules/shipment_tracking/lib/bic-api-client.js.map +7 -0
  177. package/dist/modules/shipment_tracking/lib/carrier-adapter.js +17 -0
  178. package/dist/modules/shipment_tracking/lib/carrier-adapter.js.map +7 -0
  179. package/dist/modules/shipment_tracking/lib/current-vessel.js +149 -0
  180. package/dist/modules/shipment_tracking/lib/current-vessel.js.map +7 -0
  181. package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js +124 -0
  182. package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js.map +7 -0
  183. package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js +112 -0
  184. package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js.map +7 -0
  185. package/dist/modules/shipment_tracking/lib/dcsa-params.js +16 -0
  186. package/dist/modules/shipment_tracking/lib/dcsa-params.js.map +7 -0
  187. package/dist/modules/shipment_tracking/lib/empty-return.js +48 -0
  188. package/dist/modules/shipment_tracking/lib/empty-return.js.map +7 -0
  189. package/dist/modules/shipment_tracking/lib/location-overrides.js +85 -0
  190. package/dist/modules/shipment_tracking/lib/location-overrides.js.map +7 -0
  191. package/dist/modules/shipment_tracking/lib/location-types.js +59 -0
  192. package/dist/modules/shipment_tracking/lib/location-types.js.map +7 -0
  193. package/dist/modules/shipment_tracking/lib/logger.js +113 -0
  194. package/dist/modules/shipment_tracking/lib/logger.js.map +7 -0
  195. package/dist/modules/shipment_tracking/lib/poi-event-processor.js +183 -0
  196. package/dist/modules/shipment_tracking/lib/poi-event-processor.js.map +7 -0
  197. package/dist/modules/shipment_tracking/lib/poi-nats-config.js +52 -0
  198. package/dist/modules/shipment_tracking/lib/poi-nats-config.js.map +7 -0
  199. package/dist/modules/shipment_tracking/lib/poi-types.js +43 -0
  200. package/dist/modules/shipment_tracking/lib/poi-types.js.map +7 -0
  201. package/dist/modules/shipment_tracking/lib/rate-limiter.js +31 -0
  202. package/dist/modules/shipment_tracking/lib/rate-limiter.js.map +7 -0
  203. package/dist/modules/shipment_tracking/lib/route-extraction.js +187 -0
  204. package/dist/modules/shipment_tracking/lib/route-extraction.js.map +7 -0
  205. package/dist/modules/shipment_tracking/lib/route-inference.js +106 -0
  206. package/dist/modules/shipment_tracking/lib/route-inference.js.map +7 -0
  207. package/dist/modules/shipment_tracking/lib/schedule-generator.js +63 -0
  208. package/dist/modules/shipment_tracking/lib/schedule-generator.js.map +7 -0
  209. package/dist/modules/shipment_tracking/lib/shipsgo-client.js +143 -0
  210. package/dist/modules/shipment_tracking/lib/shipsgo-client.js.map +7 -0
  211. package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js +204 -0
  212. package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js.map +7 -0
  213. package/dist/modules/shipment_tracking/lib/status-machine.js +131 -0
  214. package/dist/modules/shipment_tracking/lib/status-machine.js.map +7 -0
  215. package/dist/modules/shipment_tracking/lib/time-extraction.js +41 -0
  216. package/dist/modules/shipment_tracking/lib/time-extraction.js.map +7 -0
  217. package/dist/modules/shipment_tracking/lib/timestamp-utils.js +100 -0
  218. package/dist/modules/shipment_tracking/lib/timestamp-utils.js.map +7 -0
  219. package/dist/modules/shipment_tracking/lib/vessel-api.js +111 -0
  220. package/dist/modules/shipment_tracking/lib/vessel-api.js.map +7 -0
  221. package/dist/modules/shipment_tracking/lib/vessel-cache.js +50 -0
  222. package/dist/modules/shipment_tracking/lib/vessel-cache.js.map +7 -0
  223. package/dist/modules/shipment_tracking/lib/vessel-extraction.js +13 -0
  224. package/dist/modules/shipment_tracking/lib/vessel-extraction.js.map +7 -0
  225. package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js +62 -0
  226. package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js.map +7 -0
  227. package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js +48 -0
  228. package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js.map +7 -0
  229. package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js +15 -0
  230. package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js.map +7 -0
  231. package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js +47 -0
  232. package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js.map +7 -0
  233. package/dist/modules/shipment_tracking/notifications.js +145 -0
  234. package/dist/modules/shipment_tracking/notifications.js.map +7 -0
  235. package/dist/modules/shipment_tracking/services/carrierRegistry.js +24 -0
  236. package/dist/modules/shipment_tracking/services/carrierRegistry.js.map +7 -0
  237. package/dist/modules/shipment_tracking/services/poiNatsService.js +397 -0
  238. package/dist/modules/shipment_tracking/services/poiNatsService.js.map +7 -0
  239. package/dist/modules/shipment_tracking/services/shipsGoProvider.js +163 -0
  240. package/dist/modules/shipment_tracking/services/shipsGoProvider.js.map +7 -0
  241. package/dist/modules/shipment_tracking/services/trackingService.js +1283 -0
  242. package/dist/modules/shipment_tracking/services/trackingService.js.map +7 -0
  243. package/dist/modules/shipment_tracking/services/webhookService.js +263 -0
  244. package/dist/modules/shipment_tracking/services/webhookService.js.map +7 -0
  245. package/dist/modules/shipment_tracking/setup.js +72 -0
  246. package/dist/modules/shipment_tracking/setup.js.map +7 -0
  247. package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js +60 -0
  248. package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js.map +7 -0
  249. package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js +58 -0
  250. package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js.map +7 -0
  251. package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js +82 -0
  252. package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js.map +7 -0
  253. package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js +53 -0
  254. package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js.map +7 -0
  255. package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js +28 -0
  256. package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js.map +7 -0
  257. package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js +67 -0
  258. package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js.map +7 -0
  259. package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js +30 -0
  260. package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js.map +7 -0
  261. package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js +19 -0
  262. package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js.map +7 -0
  263. package/dist/modules/shipment_tracking/tables/index.js +6 -0
  264. package/dist/modules/shipment_tracking/tables/index.js.map +7 -0
  265. package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js +284 -0
  266. package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js.map +7 -0
  267. package/dist/modules/shipment_tracking/tables/tracking_job/table.js +36 -0
  268. package/dist/modules/shipment_tracking/tables/tracking_job/table.js.map +7 -0
  269. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js +25 -0
  270. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js.map +7 -0
  271. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js +355 -0
  272. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js.map +7 -0
  273. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js +26 -0
  274. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js.map +7 -0
  275. package/dist/types/assets.d.js +1 -0
  276. package/dist/types/assets.d.js.map +7 -0
  277. package/generated/entities/bic_config/index.ts +20 -0
  278. package/generated/entities/carrier_config/index.ts +23 -0
  279. package/generated/entities/location_override/index.ts +26 -0
  280. package/generated/entities/shipment/index.ts +46 -0
  281. package/generated/entities/ships_go_config/index.ts +25 -0
  282. package/generated/entities/tracking_event/index.ts +60 -0
  283. package/generated/entities/tracking_job/index.ts +33 -0
  284. package/generated/entities/webhook/index.ts +22 -0
  285. package/generated/entities/webhook_delivery/index.ts +22 -0
  286. package/generated/entities.ids.generated.ts +47 -0
  287. package/package.json +81 -0
  288. package/src/index.ts +39 -0
  289. package/src/modules/shipment_tracking/__integration__/TC-TRACK-410-shipsgo-config-api.spec.ts +133 -0
  290. package/src/modules/shipment_tracking/__integration__/meta.ts +4 -0
  291. package/src/modules/shipment_tracking/__tests__/empty-return-stop.test.ts +231 -0
  292. package/src/modules/shipment_tracking/__tests__/fixtures/hapag-lloyd-transship-container.json +702 -0
  293. package/src/modules/shipment_tracking/__tests__/fixtures/index.ts +434 -0
  294. package/src/modules/shipment_tracking/__tests__/fixtures/maersk-multi-transship-completed.json +1518 -0
  295. package/src/modules/shipment_tracking/__tests__/fixtures/maersk-transship-intransit.json +558 -0
  296. package/src/modules/shipment_tracking/__tests__/fixtures/msc-direct.json +222 -0
  297. package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi-transship-completed.json +974 -0
  298. package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi.json +4846 -0
  299. package/src/modules/shipment_tracking/__tests__/fixtures/msc-transship.json +409 -0
  300. package/src/modules/shipment_tracking/__tests__/fixtures/poi-events.json +240 -0
  301. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic/index.ts +138 -0
  302. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-eta-updates.json +288 -0
  303. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-route-inference.json +201 -0
  304. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-voyage-progression.json +410 -0
  305. package/src/modules/shipment_tracking/__tests__/shipsgo-air-mapper.test.ts +157 -0
  306. package/src/modules/shipment_tracking/__tests__/shipsgo-client.test.ts +155 -0
  307. package/src/modules/shipment_tracking/__tests__/shipsgo-mapper.test.ts +201 -0
  308. package/src/modules/shipment_tracking/__tests__/shipsgo-poll.test.ts +357 -0
  309. package/src/modules/shipment_tracking/__tests__/shipsgo-provider.test.ts +364 -0
  310. package/src/modules/shipment_tracking/__tests__/trackingService.integration.test.ts +2248 -0
  311. package/src/modules/shipment_tracking/__tests__/webhookPayload.test.ts +151 -0
  312. package/src/modules/shipment_tracking/acl.ts +18 -0
  313. package/src/modules/shipment_tracking/api/bic-configs/route.ts +206 -0
  314. package/src/modules/shipment_tracking/api/bic-configs/test/route.ts +108 -0
  315. package/src/modules/shipment_tracking/api/cargo-events/route.ts +101 -0
  316. package/src/modules/shipment_tracking/api/carrier-configs/route.ts +142 -0
  317. package/src/modules/shipment_tracking/api/dashboard/ships-map/route.ts +246 -0
  318. package/src/modules/shipment_tracking/api/location-overrides/route.ts +185 -0
  319. package/src/modules/shipment_tracking/api/openapi.ts +28 -0
  320. package/src/modules/shipment_tracking/api/shipments/route.ts +197 -0
  321. package/src/modules/shipment_tracking/api/shipsgo-configs/route.ts +215 -0
  322. package/src/modules/shipment_tracking/api/tracking-jobs/route.ts +149 -0
  323. package/src/modules/shipment_tracking/api/tracking-jobs/sync/route.ts +116 -0
  324. package/src/modules/shipment_tracking/api/utils.ts +10 -0
  325. package/src/modules/shipment_tracking/api/webhooks/deliveries/route.ts +82 -0
  326. package/src/modules/shipment_tracking/api/webhooks/route.ts +138 -0
  327. package/src/modules/shipment_tracking/api/webhooks/test/route.ts +84 -0
  328. package/src/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
  329. package/src/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
  330. package/src/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
  331. package/src/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
  332. package/src/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
  333. package/src/modules/shipment_tracking/assets/carriers/msc.png +0 -0
  334. package/src/modules/shipment_tracking/assets/carriers/one.png +0 -0
  335. package/src/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
  336. package/src/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
  337. package/src/modules/shipment_tracking/assets/carriers/zim.png +0 -0
  338. package/src/modules/shipment_tracking/assets/index.ts +66 -0
  339. package/src/modules/shipment_tracking/backend/carrier-configs/page.meta.ts +16 -0
  340. package/src/modules/shipment_tracking/backend/carrier-configs/page.tsx +782 -0
  341. package/src/modules/shipment_tracking/backend/location-overrides/page.meta.ts +16 -0
  342. package/src/modules/shipment_tracking/backend/location-overrides/page.tsx +241 -0
  343. package/src/modules/shipment_tracking/backend/shipment-tracking/page.meta.ts +16 -0
  344. package/src/modules/shipment_tracking/backend/shipment-tracking/page.tsx +340 -0
  345. package/src/modules/shipment_tracking/backend/tracking-jobs/page.meta.ts +16 -0
  346. package/src/modules/shipment_tracking/backend/tracking-jobs/page.tsx +16 -0
  347. package/src/modules/shipment_tracking/backend/webhooks/page.meta.ts +16 -0
  348. package/src/modules/shipment_tracking/backend/webhooks/page.tsx +228 -0
  349. package/src/modules/shipment_tracking/ce.ts +48 -0
  350. package/src/modules/shipment_tracking/cli.ts +321 -0
  351. package/src/modules/shipment_tracking/commands/bic-configs.ts +94 -0
  352. package/src/modules/shipment_tracking/commands/carrier-configs.ts +89 -0
  353. package/src/modules/shipment_tracking/commands/index.ts +8 -0
  354. package/src/modules/shipment_tracking/commands/location-overrides.ts +119 -0
  355. package/src/modules/shipment_tracking/commands/shipments.ts +247 -0
  356. package/src/modules/shipment_tracking/commands/shipsgo-configs.ts +101 -0
  357. package/src/modules/shipment_tracking/commands/tracking-jobs.ts +158 -0
  358. package/src/modules/shipment_tracking/commands/tracking.ts +230 -0
  359. package/src/modules/shipment_tracking/commands/webhooks.ts +159 -0
  360. package/src/modules/shipment_tracking/components/CombinedTimestampCell.tsx +236 -0
  361. package/src/modules/shipment_tracking/components/FacilityLocationInput.tsx +235 -0
  362. package/src/modules/shipment_tracking/components/LocationOverrideDrawer.tsx +586 -0
  363. package/src/modules/shipment_tracking/components/ShipmentDetailsDrawer.tsx +1364 -0
  364. package/src/modules/shipment_tracking/components/ShipmentDrawer.tsx +498 -0
  365. package/src/modules/shipment_tracking/components/ShipsGoConfigSection.tsx +266 -0
  366. package/src/modules/shipment_tracking/components/TimestampCell.tsx +146 -0
  367. package/src/modules/shipment_tracking/components/TrackingJobDrawer.tsx +467 -0
  368. package/src/modules/shipment_tracking/components/VesselTrackingMap.tsx +761 -0
  369. package/src/modules/shipment_tracking/components/WebhookDrawer.tsx +434 -0
  370. package/src/modules/shipment_tracking/data/entities.ts +748 -0
  371. package/src/modules/shipment_tracking/data/validators.ts +523 -0
  372. package/src/modules/shipment_tracking/di.ts +73 -0
  373. package/src/modules/shipment_tracking/encryption.ts +17 -0
  374. package/src/modules/shipment_tracking/events.ts +122 -0
  375. package/src/modules/shipment_tracking/hooks/useVesselTracking.ts +297 -0
  376. package/src/modules/shipment_tracking/i18n/de.json +327 -0
  377. package/src/modules/shipment_tracking/i18n/en.json +327 -0
  378. package/src/modules/shipment_tracking/i18n/es.json +327 -0
  379. package/src/modules/shipment_tracking/i18n/pl.json +327 -0
  380. package/src/modules/shipment_tracking/index.ts +13 -0
  381. package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-mapping.test.ts +385 -0
  382. package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-parser.test.ts +398 -0
  383. package/src/modules/shipment_tracking/lib/__tests__/empty-return.test.ts +174 -0
  384. package/src/modules/shipment_tracking/lib/__tests__/poi-event-processor.test.ts +699 -0
  385. package/src/modules/shipment_tracking/lib/__tests__/route-inference.test.ts +402 -0
  386. package/src/modules/shipment_tracking/lib/__tests__/status-machine.test.ts +447 -0
  387. package/src/modules/shipment_tracking/lib/__tests__/time-extraction.test.ts +331 -0
  388. package/src/modules/shipment_tracking/lib/__tests__/timestamp-utils.test.ts +260 -0
  389. package/src/modules/shipment_tracking/lib/__tests__/vessel-api.test.ts +125 -0
  390. package/src/modules/shipment_tracking/lib/__tests__/vessel-extraction.test.ts +108 -0
  391. package/src/modules/shipment_tracking/lib/adapters/cma-cgm.ts +84 -0
  392. package/src/modules/shipment_tracking/lib/adapters/cosco-types.ts +145 -0
  393. package/src/modules/shipment_tracking/lib/adapters/cosco.ts +314 -0
  394. package/src/modules/shipment_tracking/lib/adapters/evergreen.ts +107 -0
  395. package/src/modules/shipment_tracking/lib/adapters/hapag-lloyd.ts +88 -0
  396. package/src/modules/shipment_tracking/lib/adapters/index.ts +18 -0
  397. package/src/modules/shipment_tracking/lib/adapters/maersk.ts +104 -0
  398. package/src/modules/shipment_tracking/lib/adapters/msc.ts +223 -0
  399. package/src/modules/shipment_tracking/lib/adapters/zim.ts +106 -0
  400. package/src/modules/shipment_tracking/lib/audit-helpers.ts +29 -0
  401. package/src/modules/shipment_tracking/lib/auth/base64url.ts +4 -0
  402. package/src/modules/shipment_tracking/lib/auth/oauth-client.ts +53 -0
  403. package/src/modules/shipment_tracking/lib/background.ts +24 -0
  404. package/src/modules/shipment_tracking/lib/bic-api-client.ts +199 -0
  405. package/src/modules/shipment_tracking/lib/carrier-adapter.ts +136 -0
  406. package/src/modules/shipment_tracking/lib/current-vessel.ts +274 -0
  407. package/src/modules/shipment_tracking/lib/dcsa-event-mapping.ts +202 -0
  408. package/src/modules/shipment_tracking/lib/dcsa-event-parser.ts +163 -0
  409. package/src/modules/shipment_tracking/lib/dcsa-params.ts +14 -0
  410. package/src/modules/shipment_tracking/lib/empty-return.ts +122 -0
  411. package/src/modules/shipment_tracking/lib/location-overrides.ts +150 -0
  412. package/src/modules/shipment_tracking/lib/location-types.ts +154 -0
  413. package/src/modules/shipment_tracking/lib/logger.ts +195 -0
  414. package/src/modules/shipment_tracking/lib/poi-event-processor.ts +302 -0
  415. package/src/modules/shipment_tracking/lib/poi-nats-config.ts +134 -0
  416. package/src/modules/shipment_tracking/lib/poi-types.ts +198 -0
  417. package/src/modules/shipment_tracking/lib/rate-limiter.ts +58 -0
  418. package/src/modules/shipment_tracking/lib/route-extraction.ts +439 -0
  419. package/src/modules/shipment_tracking/lib/route-inference.ts +238 -0
  420. package/src/modules/shipment_tracking/lib/schedule-generator.ts +92 -0
  421. package/src/modules/shipment_tracking/lib/shipsgo-client.ts +350 -0
  422. package/src/modules/shipment_tracking/lib/shipsgo-mapper.ts +296 -0
  423. package/src/modules/shipment_tracking/lib/status-machine.ts +230 -0
  424. package/src/modules/shipment_tracking/lib/time-extraction.ts +85 -0
  425. package/src/modules/shipment_tracking/lib/timestamp-utils.ts +244 -0
  426. package/src/modules/shipment_tracking/lib/vessel-api.ts +299 -0
  427. package/src/modules/shipment_tracking/lib/vessel-cache.ts +102 -0
  428. package/src/modules/shipment_tracking/lib/vessel-extraction.ts +39 -0
  429. package/src/modules/shipment_tracking/lib/webhook-dispatcher.ts +93 -0
  430. package/src/modules/shipment_tracking/migrations/.snapshot-open-mercato.json +2914 -0
  431. package/src/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.ts +60 -0
  432. package/src/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.ts +22 -0
  433. package/src/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.ts +65 -0
  434. package/src/modules/shipment_tracking/notifications.ts +151 -0
  435. package/src/modules/shipment_tracking/services/carrierRegistry.ts +29 -0
  436. package/src/modules/shipment_tracking/services/poiNatsService.ts +526 -0
  437. package/src/modules/shipment_tracking/services/shipsGoProvider.ts +231 -0
  438. package/src/modules/shipment_tracking/services/trackingService.ts +1777 -0
  439. package/src/modules/shipment_tracking/services/webhookService.ts +347 -0
  440. package/src/modules/shipment_tracking/setup.ts +99 -0
  441. package/src/modules/shipment_tracking/subscribers/__tests__/terminal-event-ingest.test.ts +141 -0
  442. package/src/modules/shipment_tracking/subscribers/cargo-event-created.ts +115 -0
  443. package/src/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.ts +115 -0
  444. package/src/modules/shipment_tracking/subscribers/poi-event-notification.ts +144 -0
  445. package/src/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.ts +94 -0
  446. package/src/modules/shipment_tracking/subscribers/shipment-status-changed.ts +41 -0
  447. package/src/modules/shipment_tracking/subscribers/terminal-event-ingest.ts +109 -0
  448. package/src/modules/shipment_tracking/subscribers/tracking-job-initial-poll.ts +53 -0
  449. package/src/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.ts +50 -0
  450. package/src/modules/shipment_tracking/tables/index.ts +4 -0
  451. package/src/modules/shipment_tracking/tables/tracking_job/table.client.tsx +337 -0
  452. package/src/modules/shipment_tracking/tables/tracking_job/table.ts +34 -0
  453. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/config.ts +32 -0
  454. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.tsx +448 -0
  455. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.ts +25 -0
  456. package/src/types/assets.d.ts +5 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/components/ShipmentDetailsDrawer.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useState, useMemo, useCallback } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport Image from 'next/image'\nimport {\n Ship,\n MapPin,\n Anchor,\n Calendar,\n Package,\n ChevronDown,\n ChevronUp,\n Clock,\n ArrowRight,\n ExternalLink,\n Navigation,\n Radio,\n Truck,\n Warehouse,\n} from 'lucide-react'\nimport {\n Sheet,\n SheetContent,\n} from '@freighttech/ui/primitives/sheet'\nimport { Button } from '@freighttech/ui/primitives/button'\nimport { Badge } from '@freighttech/ui/primitives/badge'\nimport { Spinner } from '@freighttech/ui/primitives/spinner'\nimport { apiCall } from '@freighttech/ui/backend/utils/apiCall'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport type { TimestampEntry } from './CombinedTimestampCell'\nimport { getCarrierLogo } from '../assets'\nimport { VesselTrackingMap } from './VesselTrackingMap'\nimport { getCurrentVessel, type CurrentVesselInfo } from '../lib/current-vessel'\nimport type { RouteStopEntry, CargoEventEntry } from '../lib/route-extraction'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface Coords {\n latitude: number\n longitude: number\n}\n\ninterface FacilityLocation {\n name: string\n unlocode: string | null\n countryCode: string | null\n facilityCode: string | null\n facilityCodeListProvider: 'BIC' | 'SMDG' | null\n facilityTypeCode: string | null\n address: string | null\n coords: Coords | null\n operatorName: string | null\n source: 'dcsa' | 'bic' | 'manual'\n}\n\ninterface TrackingEventData {\n id: string\n eventType: string\n eventCode: string\n eventClassifierCode: 'ACT' | 'PLN' | 'EST' | null\n eventDateTime: string\n description?: string | null\n locationName?: string | null\n locationUnlocode?: string | null\n // Localized region names for AIS POI events (e.g. open-water waypoints without a UN/LOCODE)\n regionNamePl?: string | null\n regionNameEn?: string | null\n vesselName?: string | null\n vesselImo?: string | null\n voyageNumber?: string | null\n equipmentReference?: string | null\n isTransshipmentMove?: boolean | null\n // Facility/terminal details\n facilityCode?: string | null\n facilityCodeListProvider?: 'BIC' | 'SMDG' | null\n facilityTypeCode?: string | null\n facilityAddress?: string | null\n latitude?: number | null\n longitude?: number | null\n}\n\ninterface SealInfo {\n number: string\n source?: string | null\n type?: string | null\n}\n\ninterface ShipmentDetailsData {\n id: string\n status: string\n carrierCode?: string | null\n containerNumber?: string | null\n bookingNumber?: string | null\n isoEquipmentCode?: string | null\n bolNumber?: string | null\n etdTimestamps?: TimestampEntry[] | null\n etaTimestamps?: TimestampEntry[] | null\n atdTimestamps?: TimestampEntry[] | null\n ataTimestamps?: TimestampEntry[] | null\n // Location data (JSONB)\n originLocation?: FacilityLocation | null\n destinationLocation?: FacilityLocation | null\n vesselName?: string | null\n vesselImo?: string | null\n voyageNumber?: string | null\n // Denormalized route and events (no longer need separate API call)\n routeStops?: RouteStop[] | null\n cargoEvents?: TrackingEventData[] | null\n // Aggregated seals from tracking events\n seals?: SealInfo[] | null\n // Kept for backward compatibility\n trackingJob?: { id: string } | null\n}\n\ninterface RouteStop {\n location: string\n unlocode?: string\n type: 'origin' | 'transshipment' | 'port_call' | 'destination'\n vesselName?: string\n vesselImo?: string | null\n ata?: string | null\n atd?: string | null\n eta?: string | null\n etd?: string | null\n // Facility/terminal details\n facilityCode?: string | null\n facilityCodeListProvider?: 'BIC' | 'SMDG' | null\n facilityTypeCode?: string | null\n facilityAddress?: string | null\n coords?: Coords | null\n}\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction getPrimaryTimestamp(timestamps: TimestampEntry[] | null | undefined): string | null {\n if (!timestamps || timestamps.length === 0) return null\n const latest = timestamps.reduce((best, entry) =>\n entry.updatedAt > best.updatedAt ? entry : best\n )\n return latest.value\n}\n\nfunction formatShortDate(dateString: string | null | undefined): string {\n if (!dateString) return '-'\n const date = new Date(dateString)\n if (Number.isNaN(date.getTime())) return '-'\n return date.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })\n}\n\nfunction formatDateOnly(dateString: string | null | undefined): string {\n if (!dateString) return '-'\n const date = new Date(dateString)\n if (Number.isNaN(date.getTime())) return '-'\n return date.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit' })\n}\n\n/**\n * Pick the human-friendly POI region name for the active locale, falling back across\n * locales (the producer only emits pl + en). Returns null when no region name is set,\n * so callers can fall back to locationName / POI code.\n */\nfunction pickRegionName(\n event: Pick<TrackingEventData, 'regionNamePl' | 'regionNameEn'>,\n locale: string\n): string | null {\n if (locale === 'pl') return event.regionNamePl || event.regionNameEn || null\n return event.regionNameEn || event.regionNamePl || null\n}\n\n/**\n * Calculate transit time in days between departure and arrival.\n * Uses ATD as start, and ATA (if arrived) or ETA (if in transit) as end.\n */\nfunction calculateTransitDays(\n atd: string | null | undefined,\n ata: string | null | undefined,\n eta: string | null | undefined\n): number | null {\n const startDate = atd ? new Date(atd) : null\n const endDate = ata ? new Date(ata) : (eta ? new Date(eta) : null)\n if (!startDate || !endDate) return null\n if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) return null\n const diffMs = endDate.getTime() - startDate.getTime()\n return Math.ceil(diffMs / (1000 * 60 * 60 * 24))\n}\n\n/**\n * Calculate delay in days by comparing the oldest timestamp to the newest.\n * This shows drift over time as estimates get updated.\n * Returns positive for delays (late), negative for ahead of schedule.\n */\nfunction calculateDelayFromHistory(timestamps: TimestampEntry[] | null | undefined): number | null {\n if (!timestamps || timestamps.length < 2) return null\n // Sort by updatedAt to get oldest (original estimate) and newest (latest update)\n const sorted = [...timestamps].sort((a, b) =>\n new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()\n )\n const oldest = new Date(sorted[0].value)\n const newest = new Date(sorted[sorted.length - 1].value)\n if (Number.isNaN(oldest.getTime()) || Number.isNaN(newest.getTime())) return null\n const diffMs = newest.getTime() - oldest.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n return diffDays !== 0 ? diffDays : null\n}\n\n/**\n * Calculate delay by comparing planned (ETD/ETA) to actual (ATD/ATA).\n * Returns positive for delays (late), negative for ahead of schedule.\n */\nfunction calculateDelayFromPlanned(\n planned: string | null | undefined,\n actual: string | null | undefined\n): number | null {\n if (!planned || !actual) return null\n const plannedDate = new Date(planned)\n const actualDate = new Date(actual)\n if (Number.isNaN(plannedDate.getTime()) || Number.isNaN(actualDate.getTime())) return null\n const diffMs = actualDate.getTime() - plannedDate.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n return diffDays !== 0 ? diffDays : null\n}\n\n// NOTE: getCarrierLogo() is now imported from '../assets' and returns StaticImageData\n// for use with next/image component\n\n/**\n * Build carrier tracking URL with optional reference number.\n * Some carriers support direct deep-linking to a specific shipment.\n */\nfunction getCarrierTrackingUrl(\n carrierCode: string | null | undefined,\n reference?: string | null\n): string | null {\n if (!carrierCode) return null\n const code = carrierCode.toLowerCase()\n const ref = reference?.trim()\n\n // Carriers that support direct deep-linking with reference in URL path\n if ((code === 'maeu' || code === 'maersk') && ref) {\n return `https://www.maersk.com/tracking/${encodeURIComponent(ref)}`\n }\n\n // Fallback to generic tracking pages\n const urls: Record<string, string> = {\n maeu: 'https://www.maersk.com/tracking',\n maersk: 'https://www.maersk.com/tracking',\n mscu: 'https://www.msc.com/track-a-shipment',\n msc: 'https://www.msc.com/track-a-shipment',\n cmdu: 'https://www.cma-cgm.com/ebusiness/tracking',\n 'cma-cgm': 'https://www.cma-cgm.com/ebusiness/tracking',\n hlcu: 'https://www.hapag-lloyd.com/en/online-business/track/track-by-container-solution.html',\n 'hapag-lloyd': 'https://www.hapag-lloyd.com/en/online-business/track/track-by-container-solution.html',\n cosu: 'https://elines.coscoshipping.com/ebusiness/cargoTracking',\n cosco: 'https://elines.coscoshipping.com/ebusiness/cargoTracking',\n eglv: 'https://www.evergreen-line.com/trkc/',\n evergreen: 'https://www.evergreen-line.com/trkc/',\n oolu: 'https://www.oocl.com/eng/ourservices/eservices/cargotracking',\n oocl: 'https://www.oocl.com/eng/ourservices/eservices/cargotracking',\n one: 'https://ecomm.one-line.com/ecom/CUP_HOM_3301.do',\n oney: 'https://ecomm.one-line.com/ecom/CUP_HOM_3301.do',\n ymlu: 'https://www.yangming.com/e-service/Track_Trace/track_trace_cargo_tracking.aspx',\n 'yang-ming': 'https://www.yangming.com/e-service/Track_Trace/track_trace_cargo_tracking.aspx',\n zimu: 'https://www.zim.com/tools/track-a-shipment',\n zim: 'https://www.zim.com/tools/track-a-shipment',\n }\n return urls[code] || null\n}\n\nfunction getStatusBadgeVariant(status: string): 'default' | 'secondary' | 'destructive' | 'outline' {\n switch (status) {\n case 'ARRIVED':\n case 'DELIVERED':\n return 'default'\n case 'IN_TRANSIT':\n case 'DEPARTED':\n return 'secondary'\n case 'PENDING':\n case 'BOOKED':\n return 'outline'\n default:\n return 'outline'\n }\n}\n\n// NOTE: extractRouteFromEvents logic has been moved to lib/route-extraction.ts\n// and is now executed server-side in TrackingService.deriveShipmentStateFromEvents()\n// The computed route is stored as `routeStops` JSONB on the shipment entity.\n\n/**\n * Determines facility location type based on facility code and vessel presence.\n *\n * Facility types (DCSA standard):\n * - POTE = Port Terminal (always port)\n * - INTE = Intermodal Terminal (port if vessel, inland if no vessel)\n * - DEPO = Depot (always inland)\n * - CLOC = Client Location (always inland)\n * - null/undefined = Unknown (use MapPin icon)\n *\n * Returns: 'port' | 'inland' | 'unknown'\n */\nfunction getFacilityLocationType(\n facilityTypeCode: string | null | undefined,\n vesselName?: string | null\n): 'port' | 'inland' | 'unknown' {\n // Port terminals are always at ports\n if (facilityTypeCode === 'POTE') {\n return 'port'\n }\n\n // Depots and client locations are always inland\n if (facilityTypeCode === 'DEPO' || facilityTypeCode === 'CLOC') {\n return 'inland'\n }\n\n // Intermodal terminals: port if vessel involved, inland otherwise\n if (facilityTypeCode === 'INTE') {\n return vesselName ? 'port' : 'inland'\n }\n\n // Unknown facility type - use MapPin\n return 'unknown'\n}\n\n/**\n * Get the appropriate icon for a tracking event based on event code, facility type, and vessel.\n *\n * Icon selection logic:\n * - Port arrivals: Anchor (vessel arriving at port)\n * - Port departures: Ship (vessel departing from port)\n * - Inland arrivals (depot/client/inland intermodal): Warehouse\n * - Inland departures (depot/client/inland intermodal): Truck\n * - Unknown facility arrivals/departures: MapPin (generic location)\n * - Load/Discharge: Package (cargo handling)\n * - Proximity events: Navigation (approaching)\n * - Waypoints: Radio (tracking point)\n */\nfunction getEventIcon(\n eventCode: string,\n facilityTypeCode?: string | null,\n vesselName?: string | null\n) {\n const locationType = getFacilityLocationType(facilityTypeCode, vesselName)\n\n // Gate events\n if (eventCode === 'GTIN') {\n if (locationType === 'port') return Anchor\n if (locationType === 'inland') return Warehouse\n return MapPin\n }\n if (eventCode === 'GTOT') {\n if (locationType === 'port') return Ship\n if (locationType === 'inland') return Truck\n return MapPin\n }\n\n switch (eventCode) {\n // Standard DCSA events - check facility type for arrivals/departures\n case 'ARRI':\n if (locationType === 'port') return Anchor\n if (locationType === 'inland') return Warehouse\n return MapPin\n case 'DEPA':\n if (locationType === 'port') return Ship\n if (locationType === 'inland') return Truck\n return MapPin\n case 'LOAD':\n case 'DISC':\n return Package\n // POI/AIS events (always at ports/terminals)\n case 'PARR': // Port Arrival\n case 'TARR': // Terminal Arrival\n return Anchor\n case 'PPRD': // Port Departure\n case 'TPRD': // Terminal Departure\n return Ship\n case 'PPRA': // Port Proximity Arrival\n case 'TPRA': // Terminal Proximity Arrival\n return Navigation\n case 'WAYR': // Waypoint Reached\n return Radio\n default:\n return Package\n }\n}\n\n/**\n * Hook to get translated event label.\n * Uses i18n translations from shipment_tracking.event_codes\n */\nfunction useEventLabel() {\n const t = useT()\n \n return useCallback((eventCode: string): string => {\n const key = `shipment_tracking.event_codes.${eventCode}`\n const translated = t(key, eventCode.toLowerCase())\n // If translation returns the key itself, fall back to lowercase code\n return translated === key ? eventCode.toLowerCase() : translated\n }, [t])\n}\n\n// \u2500\u2500\u2500 Sub-components \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype LucideIcon = typeof Ship\n\ninterface CollapsibleSectionProps {\n title: string\n icon: LucideIcon\n count?: number\n defaultOpen?: boolean\n children: React.ReactNode\n}\n\nfunction CollapsibleSection({\n title,\n icon: Icon,\n count,\n defaultOpen = false,\n children,\n}: CollapsibleSectionProps) {\n const [isOpen, setIsOpen] = useState(defaultOpen)\n\n return (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <button\n type=\"button\"\n className=\"w-full p-3 flex items-center justify-between text-left hover:bg-muted transition-colors\"\n onClick={() => setIsOpen(!isOpen)}\n >\n <div className=\"flex items-center space-x-2\">\n <Icon className=\"w-4 h-4\" />\n <span className=\"font-medium text-foreground\">{title}</span>\n {count !== undefined && (\n <span className=\"bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-2 py-0.5 rounded-full text-xs font-medium\">\n {count}\n </span>\n )}\n </div>\n {isOpen ? (\n <ChevronUp className=\"w-4 h-4 text-muted-foreground\" />\n ) : (\n <ChevronDown className=\"w-4 h-4 text-muted-foreground\" />\n )}\n </button>\n {isOpen && (\n <div className=\"border-t border-border\">\n {children}\n </div>\n )}\n </div>\n )\n}\n\ninterface ShipmentCardProps {\n shipment: ShipmentDetailsData\n}\n\nfunction ShipmentCard({ shipment }: ShipmentCardProps) {\n const t = useT()\n const carrierLogo = getCarrierLogo(shipment.carrierCode)\n // For deep-linking, prefer BOL > booking > container as reference\n const trackingReference = shipment.bolNumber || shipment.bookingNumber || shipment.containerNumber\n const trackingUrl = getCarrierTrackingUrl(shipment.carrierCode, trackingReference)\n const atdActual = getPrimaryTimestamp(shipment.atdTimestamps)\n const ataActual = getPrimaryTimestamp(shipment.ataTimestamps)\n const etd = getPrimaryTimestamp(shipment.etdTimestamps)\n const eta = getPrimaryTimestamp(shipment.etaTimestamps)\n const atd = atdActual || etd\n const ata = ataActual || eta\n\n // Calculate transit time\n const transitDays = calculateTransitDays(atdActual || etd, ataActual, eta)\n\n // Extract location names from JSONB fields\n const originName = shipment.originLocation?.name || shipment.originLocation?.unlocode || '-'\n const destinationName = shipment.destinationLocation?.name || shipment.destinationLocation?.unlocode || '-'\n const originFacilityCode = formatFacilityCode(\n shipment.originLocation?.facilityCode,\n shipment.originLocation?.facilityCodeListProvider\n )\n const destinationFacilityCode = formatFacilityCode(\n shipment.destinationLocation?.facilityCode,\n shipment.destinationLocation?.facilityCodeListProvider\n )\n\n return (\n <div className=\"bg-card border border-border rounded-lg p-4 relative\">\n <div className=\"space-y-3\">\n {/* Header with reference and carrier logo */}\n <div className=\"flex items-start justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {shipment.bolNumber || shipment.bookingNumber || shipment.containerNumber || shipment.id.slice(0, 8)}\n </h2>\n {shipment.containerNumber && (\n <p className=\"text-sm text-muted-foreground font-mono mt-1\">{shipment.containerNumber}</p>\n )}\n </div>\n </div>\n\n {/* Carrier logo */}\n {carrierLogo && (\n <div className=\"w-12 h-12 flex items-center justify-center absolute top-4 right-4\">\n <Image\n src={carrierLogo}\n alt={shipment.carrierCode || 'Carrier'}\n width={40}\n height={40}\n className=\"object-contain\"\n />\n </div>\n )}\n\n {/* Route: Origin -> Destination */}\n <div className=\"flex items-center gap-2 text-sm\">\n <div className=\"font-medium text-foreground\">\n <b>{originName}</b>\n {originFacilityCode && (\n <span className=\"text-xs text-blue-600 dark:text-blue-400 ml-1 font-mono\">[{originFacilityCode}]</span>\n )}\n {atd && <span className=\"text-muted-foreground\"> ({formatDateOnly(atd)})</span>}\n </div>\n <ArrowRight className=\"w-4 h-4 text-muted-foreground\" />\n <div className=\"font-medium text-foreground\">\n <b>{destinationName}</b>\n {destinationFacilityCode && (\n <span className=\"text-xs text-blue-600 dark:text-blue-400 ml-1 font-mono\">[{destinationFacilityCode}]</span>\n )}\n {ata && <span className=\"text-muted-foreground\"> ({formatDateOnly(ata)})</span>}\n </div>\n </div>\n\n {/* Vessel info */}\n {shipment.vesselName && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Ship className=\"w-4 h-4\" />\n <span className=\"font-medium\">{shipment.vesselName}</span>\n {shipment.vesselImo && (\n <>\n <span className=\"text-muted-foreground/70\">\u2022</span>\n <span className=\"font-mono text-xs\">IMO: {shipment.vesselImo}</span>\n </>\n )}\n </div>\n )}\n\n {/* Transit time */}\n {transitDays !== null && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Clock className=\"w-4 h-4\" />\n <span>\n {t('shipment_tracking.details.transitTime', 'Transit time')}: {transitDays} {t('shipment_tracking.details.days', 'days')}\n </span>\n </div>\n )}\n\n {/* Verify on carrier website */}\n {trackingUrl && (\n <a\n href={trackingUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-2 text-sm text-blue-600 hover:text-blue-700 font-medium\"\n >\n <span>{t('shipment_tracking.details.verifyOnCarrier', 'Verify on carrier website')}</span>\n <ExternalLink className=\"w-4 h-4\" />\n </a>\n )}\n </div>\n </div>\n )\n}\n\ninterface DestinationStatusCardProps {\n shipment: ShipmentDetailsData\n}\n\nfunction DestinationStatusCard({ shipment }: DestinationStatusCardProps) {\n const t = useT()\n const ata = getPrimaryTimestamp(shipment.ataTimestamps)\n const eta = getPrimaryTimestamp(shipment.etaTimestamps)\n const hasArrived = ata !== null\n\n // Determine status\n let statusLabel = t('shipment_tracking.details.status.enRoute', 'En Route')\n let statusColor = 'bg-yellow-600'\n if (shipment.status === 'ARRIVED' || hasArrived) {\n statusLabel = t('shipment_tracking.details.status.inPort', 'In Port')\n statusColor = 'bg-blue-600'\n } else if (shipment.status === 'DELIVERED') {\n statusLabel = t('shipment_tracking.details.status.delivered', 'Delivered')\n statusColor = 'bg-green-600'\n }\n\n // Extract location name from JSONB field\n const destLoc = shipment.destinationLocation\n const destinationName = destLoc?.name || destLoc?.unlocode || '-'\n const facilityCode = formatFacilityCode(destLoc?.facilityCode, destLoc?.facilityCodeListProvider)\n\n return (\n <div className=\"bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950/30 dark:to-indigo-950/30 border border-blue-200 dark:border-blue-800 rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-3\">\n <h3 className=\"font-semibold text-foreground flex items-center gap-2\">\n <MapPin className=\"w-5 h-5 text-blue-600 dark:text-blue-400\" />\n {t('shipment_tracking.details.destinationStatus', 'Destination Status')}\n </h3>\n <span className={`${statusColor} text-white px-3 py-1 rounded-full text-xs font-medium`}>\n {statusLabel}\n </span>\n </div>\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-sm\">\n <Anchor className=\"w-4 h-4 text-muted-foreground\" />\n <span className=\"font-medium text-foreground\">{destinationName}</span>\n {facilityCode && (\n <span className=\"text-xs text-blue-600 dark:text-blue-400 font-mono\">[{facilityCode}]</span>\n )}\n </div>\n {/* Terminal address */}\n {destLoc?.address && (\n <div className=\"text-xs text-muted-foreground ml-6 truncate\" title={destLoc.address}>\n {destLoc.address}\n </div>\n )}\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Clock className=\"w-3 h-3\" />\n <span>\n {hasArrived\n ? `ATA: ${formatShortDate(ata)}`\n : eta\n ? `ETA: ${formatShortDate(eta)}`\n : '-'}\n </span>\n </div>\n </div>\n </div>\n )\n}\n\ninterface RouteDetailsProps {\n stops: RouteStop[]\n}\n\n/**\n * Get colors based on stop type.\n * Origin = green, Destination = blue, Transshipment = yellow\n */\nfunction getStopColors(type: 'origin' | 'transshipment' | 'port_call' | 'destination') {\n switch (type) {\n case 'origin':\n return {\n bg: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 border-green-300 dark:border-green-700',\n badge: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400',\n }\n case 'destination':\n return {\n bg: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 border-blue-300 dark:border-blue-700',\n badge: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400',\n }\n case 'port_call':\n return {\n bg: 'bg-slate-100 dark:bg-slate-800/40 text-slate-700 dark:text-slate-300 border-slate-300 dark:border-slate-600',\n badge: 'bg-slate-100 dark:bg-slate-800/40 text-slate-700 dark:text-slate-300',\n }\n case 'transshipment':\n default:\n return {\n bg: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400 border-yellow-300 dark:border-yellow-700',\n badge: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400',\n }\n }\n}\n\n/**\n * Formats facility code with provider badge (e.g., \"DCT (SMDG)\")\n */\nfunction formatFacilityCode(code: string | null | undefined, provider: string | null | undefined): string | null {\n if (!code) return null\n if (provider) return `${code} (${provider})`\n return code\n}\n\n/**\n * Get the appropriate icon for a route stop based on facility type, vessel, and stop type.\n *\n * Priority order:\n * 1. Explicit facility types (DEPO, CLOC, POTE, INTE) - respect the data\n * 2. Unknown facility type + origin/destination - assume port for sea container tracking\n * 3. Unknown facility type + transshipment - use MapPin\n *\n * This ensures:\n * - Depots/client locations show Warehouse even if they're origin/destination\n * - Ports with missing facilityTypeCode (like NHAVA SHEVA) show Anchor\n * - Intermodal terminals use vessel presence as the deciding factor\n */\nfunction getRouteStopIcon(\n facilityTypeCode: string | null | undefined,\n vesselName?: string | null,\n stopType?: 'origin' | 'transshipment' | 'port_call' | 'destination'\n) {\n // Explicit depot/client locations are always inland (warehouse icon)\n // Respect the data when we know it's a depot or client location\n if (facilityTypeCode === 'DEPO' || facilityTypeCode === 'CLOC') {\n return Warehouse\n }\n\n // Port terminals are always ports\n if (facilityTypeCode === 'POTE') {\n return Anchor\n }\n\n // Intermodal terminals: use vessel presence as signal\n if (facilityTypeCode === 'INTE') {\n return vesselName ? Anchor : Warehouse\n }\n\n // Unknown facility type (null/undefined):\n // For sea container origin/destination, assume port when we have no other info\n // (this handles cases like NHAVA SHEVA where facilityTypeCode is missing)\n if (stopType === 'origin' || stopType === 'destination') {\n return Anchor\n }\n\n return MapPin // truly unknown transshipment or no stop type\n}\n\nfunction RouteDetails({ stops }: RouteDetailsProps) {\n const t = useT()\n\n if (stops.length === 0) {\n return null\n }\n\n // Segment status helpers:\n // - Completed (green): departed from current AND arrived at next\n // - In progress (blue): departed from current but NOT arrived at next\n // - Pending (gray): not departed yet\n const getSegmentStatus = (currentStop: RouteStop, nextStop: RouteStop | undefined): 'completed' | 'in-progress' | 'pending' => {\n if (!currentStop.atd) return 'pending'\n if (nextStop?.ata) return 'completed'\n return 'in-progress'\n }\n\n const getSegmentLineColor = (status: 'completed' | 'in-progress' | 'pending') => {\n switch (status) {\n case 'completed':\n return 'bg-green-500 dark:bg-green-400'\n case 'in-progress':\n return 'bg-blue-500 dark:bg-blue-400'\n default:\n return 'bg-border'\n }\n }\n\n return (\n <div className=\"bg-card border border-border rounded-lg p-4\">\n <h3 className=\"font-semibold text-foreground mb-4 flex items-center gap-2\">\n <MapPin className=\"w-4 h-4\" />\n {t('shipment_tracking.details.routeDetails', 'Route Details')}\n </h3>\n <div className=\"relative\">\n <div className=\"space-y-0\">\n {stops.map((stop, index) => {\n const colors = getStopColors(stop.type)\n const badgeText = stop.type === 'destination'\n ? t('shipment_tracking.details.destination', 'Destination')\n : stop.type === 'transshipment'\n ? t('shipment_tracking.details.transshipment', 'Transshipment')\n : stop.type === 'port_call'\n ? t('shipment_tracking.details.portCall', 'Port call')\n : t('shipment_tracking.details.origin', 'Origin')\n\n // Calculate delays by comparing planned vs actual\n const departureDelay = calculateDelayFromPlanned(stop.etd, stop.atd)\n const arrivalDelay = calculateDelayFromPlanned(stop.eta, stop.ata)\n\n // Format facility code with provider\n const facilityDisplay = formatFacilityCode(stop.facilityCode, stop.facilityCodeListProvider)\n\n // Determine segment status (line to next stop)\n const isLastStop = index === stops.length - 1\n const nextStop = !isLastStop ? stops[index + 1] : undefined\n const segmentStatus = getSegmentStatus(stop, nextStop)\n\n // Get contextual icon based on facility type and stop type\n const StopIcon = getRouteStopIcon(stop.facilityTypeCode, stop.vesselName, stop.type)\n\n return (\n <div key={`${stop.unlocode || stop.location}-${index}`} className=\"relative flex items-start gap-3 py-2\">\n {/* Connecting line to next stop - starts below current icon, extends to next icon center */}\n {/* Icon: mt-1(4px) + p-2(8px) + icon(16px) + p-2(8px) = 36px total, center at 20px from item top */}\n {/* Line starts at 36px (below icon), extends to bottom + 20px into next item */}\n {!isLastStop && (\n <div\n className={`absolute left-[15px] top-[36px] bottom-[-20px] w-0.5 ${getSegmentLineColor(segmentStatus)}`}\n />\n )}\n <div className={`relative z-10 mt-1 p-2 rounded-full ${colors.bg}`}>\n <StopIcon className=\"w-4 h-4\" />\n </div>\n <div className=\"flex-1 min-w-0 flex items-start justify-between gap-4\">\n <div className=\"flex-shrink-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <div className=\"font-semibold text-foreground\">{stop.location}</div>\n <div className={`px-2 py-1 rounded text-xs font-medium whitespace-nowrap ${colors.badge}`}>\n {badgeText}\n </div>\n </div>\n {/* UN/LOCODE and facility code */}\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground mb-1\">\n {stop.unlocode && <span className=\"font-mono\">{stop.unlocode}</span>}\n {facilityDisplay && (\n <>\n {stop.unlocode && <span>\u2022</span>}\n <span className=\"font-mono text-blue-600 dark:text-blue-400\" title={t('shipment_tracking.details.terminalCode', 'Terminal Code')}>\n {facilityDisplay}\n </span>\n </>\n )}\n </div>\n {/* Facility address */}\n {stop.facilityAddress && (\n <div className=\"text-xs text-muted-foreground mb-1 max-w-[200px] truncate\" title={stop.facilityAddress}>\n {stop.facilityAddress}\n </div>\n )}\n {/* Coordinates */}\n {stop.coords && (\n <div className=\"text-xs text-muted-foreground/70 mb-1 font-mono\">\n {stop.coords.latitude.toFixed(4)}, {stop.coords.longitude.toFixed(4)}\n </div>\n )}\n {stop.vesselName && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Ship className=\"w-3 h-3\" />\n <span>{stop.vesselName}</span>\n </div>\n )}\n </div>\n <div className=\"text-right text-sm space-y-2 flex-shrink-0\">\n {/* Departure timestamps */}\n {(stop.atd || stop.etd) && (\n <div>\n <div className=\"text-muted-foreground text-xs\">{stop.atd ? 'ATD:' : 'ETD:'}</div>\n <div className=\"font-medium text-foreground\">{formatShortDate(stop.atd || stop.etd)}</div>\n {departureDelay !== null && (\n <div className={`text-xs ${departureDelay > 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`}>\n {departureDelay > 0 ? '+' : ''}{departureDelay} {t('shipment_tracking.details.days', 'days')}\n </div>\n )}\n </div>\n )}\n {/* Arrival timestamps */}\n {(stop.ata || stop.eta) && (\n <div>\n <div className=\"text-muted-foreground text-xs\">{stop.ata ? 'ATA:' : 'ETA:'}</div>\n <div className=\"font-medium text-foreground\">{formatShortDate(stop.ata || stop.eta)}</div>\n {arrivalDelay !== null && (\n <div className={`text-xs ${arrivalDelay > 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`}>\n {arrivalDelay > 0 ? '+' : ''}{arrivalDelay} {t('shipment_tracking.details.days', 'days')}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n\ninterface JourneyTimelineProps {\n events: TrackingEventData[]\n}\n\nfunction JourneyTimeline({ events }: JourneyTimelineProps) {\n const t = useT()\n const locale = useLocale()\n const getEventLabel = useEventLabel()\n\n // Sort events by datetime ascending (chronological order - oldest first at top)\n // Use id as secondary sort key for stability when timestamps are equal\n // Hide timeline noise: AIS waypoints (WAYR) and \"approaching\" proximity events\n // (PPRA/TPRA). Actual arrivals/departures (PARR/TARR/PPRD/TPRD) and DCSA events stay.\n const sortedEvents = useMemo(\n () => events\n .filter((e) => !['WAYR', 'PPRA', 'TPRA'].includes(e.eventCode))\n .sort((a, b) => {\n const timeA = new Date(a.eventDateTime).getTime()\n const timeB = new Date(b.eventDateTime).getTime()\n if (timeA !== timeB) return timeA - timeB\n return a.id.localeCompare(b.id)\n }),\n [events]\n )\n\n // An event has \"already happened\" if the journey has progressed past it \u2014 i.e. there is an\n // ACT (actual) event at or after its position. We anchor on the LAST actual event in\n // chronological order: everything up to and including it is done (green), even planned\n // (PLN/EST) milestones that were superseded by a later actual event (e.g. a planned\n // departure followed by an actual arrival). Everything after it is still upcoming. See CHAME-185.\n const lastActualIndex = useMemo(() => {\n let last = -1\n sortedEvents.forEach((e, i) => {\n if (e.eventClassifierCode === 'ACT') last = i\n })\n return last\n }, [sortedEvents])\n\n // Segment (connector) status between event[index] and event[index + 1]:\n // - Completed (green): both ends have already happened\n // - In progress (blue): we've reached this event but not the next (the current leg)\n // - Pending (gray): this event hasn't happened yet\n const getSegmentStatus = (index: number): 'completed' | 'in-progress' | 'pending' => {\n if (index + 1 <= lastActualIndex) return 'completed'\n if (index <= lastActualIndex) return 'in-progress'\n return 'pending'\n }\n\n const getSegmentLineColor = (status: 'completed' | 'in-progress' | 'pending') => {\n switch (status) {\n case 'completed':\n return 'bg-green-500 dark:bg-green-400'\n case 'in-progress':\n return 'bg-blue-500 dark:bg-blue-400'\n default:\n return 'bg-border'\n }\n }\n\n return (\n <CollapsibleSection\n title={t('shipment_tracking.details.journeyTimeline', 'Journey Timeline')}\n icon={Calendar}\n count={sortedEvents.length}\n defaultOpen={false}\n >\n <div className=\"p-3\">\n <div className=\"relative\">\n <div className=\"space-y-4\">\n {sortedEvents.map((event, index) => {\n const Icon = getEventIcon(event.eventCode, event.facilityTypeCode, event.vesselName)\n const hasHappened = index <= lastActualIndex\n const borderColor = hasHappened ? 'border-green-300 dark:border-green-700' : 'border-blue-300 dark:border-blue-700'\n const bgColor = hasHappened\n ? 'bg-green-100 dark:bg-green-900/30 border-green-300 dark:border-green-700 text-green-800 dark:text-green-300'\n : 'bg-blue-100 dark:bg-blue-900/30 border-blue-300 dark:border-blue-700 text-blue-800 dark:text-blue-300'\n const badgeColor = hasHappened\n ? 'bg-green-200 dark:bg-green-800/50 text-green-800 dark:text-green-300'\n : 'bg-blue-200 dark:bg-blue-800/50 text-blue-800 dark:text-blue-300'\n\n const isLastEvent = index === sortedEvents.length - 1\n const segmentStatus = getSegmentStatus(index)\n\n return (\n <div key={event.id} className=\"relative flex items-start gap-4\">\n {/* Connecting line to next event - connects icon centers symmetrically */}\n {/* Icon: border-2(2px) + p-2(8px) + icon(20px) + p-2(8px) + border-2(2px) = 40px, center at 20px */}\n {/* space-y-4 = 16px gap between items. Line: from 20px below center to 20px above next center */}\n {/* Start: 20px (center) + 20px (half icon) = 40px. End: 16px gap + 20px into next = -36px */}\n {!isLastEvent && (\n <div\n className={`absolute left-[19px] top-[40px] bottom-[-36px] w-0.5 ${getSegmentLineColor(segmentStatus)}`}\n />\n )}\n <div className={`relative z-10 p-2 rounded-full border-2 bg-card ${borderColor}`}>\n <Icon className={`w-5 h-5 ${hasHappened ? 'text-green-600 dark:text-green-400' : 'text-blue-600 dark:text-blue-400'}`} />\n </div>\n <div className={`flex-1 border rounded-lg p-3 ${bgColor}`}>\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-semibold\">{getEventLabel(event.eventCode)}</span>\n <span className={`px-2 py-0.5 rounded text-xs font-medium ${badgeColor}`}>\n {event.eventClassifierCode || 'N/A'}\n </span>\n </div>\n <div className=\"text-sm font-medium mb-2\">\n {(() => {\n // Precise place name wins: carrier events and AIS port/terminal\n // events both populate locationName (e.g. \"Gda\u0144sk\", \"Qingdao QQCTN\").\n if (event.locationName) return event.locationName\n // Open-water AIS waypoints have no place name \u2014 show the localized\n // region plus the raw POI grid code muted beside it\n // (e.g. \"Morze P\u00F3\u0142nocne\" \u00B7 \"N053W001-03738\").\n const regionName = pickRegionName(event, locale)\n if (regionName) {\n return (\n <>\n {regionName}\n {event.locationUnlocode && (\n <span className=\"ml-1.5 font-normal opacity-60\">{event.locationUnlocode}</span>\n )}\n </>\n )\n }\n return event.locationUnlocode || '-'\n })()}\n </div>\n <div className=\"flex items-center gap-4 text-xs\">\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"w-3 h-3\" />\n {formatShortDate(event.eventDateTime)}\n </div>\n {event.vesselName && (\n <div className=\"flex items-center gap-1\">\n <Ship className=\"w-3 h-3\" />\n {event.vesselName}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n </CollapsibleSection>\n )\n}\n\ninterface BookingDetailsProps {\n shipment: ShipmentDetailsData\n}\n\nfunction BookingDetails({ shipment }: BookingDetailsProps) {\n const t = useT()\n\n return (\n <CollapsibleSection\n title={t('shipment_tracking.details.bookingDetails', 'Booking Details')}\n icon={Package}\n defaultOpen={false}\n >\n <div className=\"p-3\">\n <div className=\"bg-muted rounded-lg p-4 space-y-2\">\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.containerNumber', 'Container Number')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.containerNumber || 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.size', 'Size')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.isoEquipmentCode || 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.bookingNumber', 'Booking Number')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.bookingNumber || 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.bolNumber', 'BOL Number')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.bolNumber || 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.carrierCode', 'Carrier Code')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.carrierCode || 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.seals', 'Seals')}\n </span>\n <span className=\"font-mono text-sm font-semibold text-foreground\">\n {shipment.seals?.length\n ? shipment.seals.map(s => s.number).join(', ')\n : 'N/A'}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.shipments.fields.status', 'Status')}\n </span>\n <Badge variant={getStatusBadgeVariant(shipment.status)}>\n {shipment.status}\n </Badge>\n </div>\n </div>\n </div>\n </CollapsibleSection>\n )\n}\n\n// \u2500\u2500\u2500 Main Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ShipmentDetailsDrawerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n shipmentId: string | null\n}\n\nexport function ShipmentDetailsDrawer({\n open,\n onOpenChange,\n shipmentId,\n}: ShipmentDetailsDrawerProps) {\n const t = useT()\n\n // Fetch shipment data\n const { data: shipment, isLoading: isLoadingShipment } = useQuery({\n queryKey: ['shipment_tracking_shipment_details', shipmentId],\n queryFn: async () => {\n if (!shipmentId) return null\n const response = await apiCall<{ items: Record<string, unknown>[] }>(\n `/api/shipment_tracking/shipments?id=${shipmentId}`\n )\n if (!response.ok) throw new Error('Failed to load shipment')\n const items = response.result?.items ?? []\n const item = items.find((i) => i.id === shipmentId) ?? items[0]\n if (!item) return null\n\n // Normalize API response\n // Handle trackingJob which can be: { id: string }, string (just the ID), or null\n const trackingJobRaw = item.trackingJob ?? item.tracking_job ?? item.trackingJobId ?? item.tracking_job_id\n let trackingJobId: string | null = null\n if (trackingJobRaw) {\n if (typeof trackingJobRaw === 'string') {\n trackingJobId = trackingJobRaw\n } else if (typeof trackingJobRaw === 'object' && 'id' in trackingJobRaw) {\n trackingJobId = (trackingJobRaw as { id: string }).id\n }\n }\n\n // Parse denormalized route stops\n const routeStopsRaw = item.routeStops ?? item.route_stops\n const routeStops = Array.isArray(routeStopsRaw) ? routeStopsRaw.map((stop: Record<string, unknown>) => ({\n location: stop.location as string,\n unlocode: (stop.unlocode) as string | undefined,\n type: (stop.type ?? 'transshipment') as 'origin' | 'transshipment' | 'port_call' | 'destination',\n vesselName: (stop.vesselName ?? stop.vessel_name) as string | undefined,\n vesselImo: (stop.vesselImo ?? stop.vessel_imo) as string | null,\n ata: (stop.ata) as string | null,\n atd: (stop.atd) as string | null,\n eta: (stop.eta) as string | null,\n etd: (stop.etd) as string | null,\n // Facility/terminal details\n facilityCode: (stop.facilityCode ?? stop.facility_code) as string | null,\n facilityCodeListProvider: (stop.facilityCodeListProvider ?? stop.facility_code_list_provider) as 'BIC' | 'SMDG' | null,\n facilityTypeCode: (stop.facilityTypeCode ?? stop.facility_type_code) as string | null,\n facilityAddress: (stop.facilityAddress ?? stop.facility_address) as string | null,\n coords: stop.coords ? stop.coords as Coords : null,\n })) : null\n\n // Parse denormalized cargo events\n const cargoEventsRaw = item.cargoEvents ?? item.cargo_events\n const cargoEvents = Array.isArray(cargoEventsRaw) ? cargoEventsRaw.map((evt: Record<string, unknown>) => ({\n id: evt.id as string,\n eventType: (evt.eventType ?? evt.event_type ?? '') as string,\n eventCode: (evt.eventCode ?? evt.event_code ?? '') as string,\n eventClassifierCode: (evt.eventClassifierCode ?? evt.event_classifier_code) as 'ACT' | 'PLN' | 'EST' | null,\n eventDateTime: (evt.eventDateTime ?? evt.event_date_time) as string,\n description: (evt.description) as string | null,\n locationName: (evt.locationName ?? evt.location_name) as string | null,\n locationUnlocode: (evt.locationUnlocode ?? evt.location_unlocode) as string | null,\n regionNamePl: (evt.regionNamePl ?? evt.region_name_pl) as string | null,\n regionNameEn: (evt.regionNameEn ?? evt.region_name_en) as string | null,\n vesselName: (evt.vesselName ?? evt.vessel_name) as string | null,\n vesselImo: (evt.vesselImo ?? evt.vessel_imo) as string | null,\n voyageNumber: (evt.voyageNumber ?? evt.voyage_number) as string | null,\n isTransshipmentMove: (evt.isTransshipmentMove ?? evt.is_transshipment_move) as boolean | null,\n // Facility/terminal details\n facilityCode: (evt.facilityCode ?? evt.facility_code) as string | null,\n facilityCodeListProvider: (evt.facilityCodeListProvider ?? evt.facility_code_list_provider) as 'BIC' | 'SMDG' | null,\n facilityTypeCode: (evt.facilityTypeCode ?? evt.facility_type_code) as string | null,\n facilityAddress: (evt.facilityAddress ?? evt.facility_address) as string | null,\n latitude: (evt.latitude) as number | null,\n longitude: (evt.longitude) as number | null,\n })) : null\n\n // Parse rich location data\n const originLocationRaw = item.originLocation ?? item.origin_location\n const destinationLocationRaw = item.destinationLocation ?? item.destination_location\n\n return {\n id: item.id as string,\n status: (item.status ?? 'PENDING') as string,\n carrierCode: (item.carrierCode ?? item.carrier_code) as string | null,\n containerNumber: (item.containerNumber ?? item.container_number) as string | null,\n bookingNumber: (item.bookingNumber ?? item.booking_number) as string | null,\n isoEquipmentCode: (item.isoEquipmentCode ?? item.iso_equipment_code) as string | null,\n bolNumber: (item.bolNumber ?? item.bol_number) as string | null,\n etdTimestamps: (item.etdTimestamps ?? item.etd_timestamps) as TimestampEntry[] | null,\n etaTimestamps: (item.etaTimestamps ?? item.eta_timestamps) as TimestampEntry[] | null,\n atdTimestamps: (item.atdTimestamps ?? item.atd_timestamps) as TimestampEntry[] | null,\n ataTimestamps: (item.ataTimestamps ?? item.ata_timestamps) as TimestampEntry[] | null,\n originLocation: originLocationRaw as FacilityLocation | null,\n destinationLocation: destinationLocationRaw as FacilityLocation | null,\n vesselName: (item.vesselName ?? item.vessel_name) as string | null,\n vesselImo: (item.vesselImo ?? item.vessel_imo) as string | null,\n voyageNumber: (item.voyageNumber ?? item.voyage_number) as string | null,\n routeStops,\n cargoEvents,\n trackingJob: trackingJobId ? { id: trackingJobId } : null,\n } as ShipmentDetailsData\n },\n enabled: open && !!shipmentId,\n })\n\n // Use denormalized data directly from shipment - no separate events query needed!\n const routeStops = shipment?.routeStops ?? []\n const events = shipment?.cargoEvents ?? []\n\n // Derive current vessel from shipment progress\n const currentVessel = useMemo<CurrentVesselInfo>(() => {\n return getCurrentVessel(\n shipment?.routeStops as RouteStopEntry[] | null | undefined,\n shipment?.cargoEvents as CargoEventEntry[] | null | undefined,\n { vesselName: shipment?.vesselName, vesselImo: shipment?.vesselImo }\n )\n }, [shipment?.routeStops, shipment?.cargoEvents, shipment?.vesselName, shipment?.vesselImo])\n\n const isLoading = isLoadingShipment\n\n // Handle keyboard shortcuts\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n onOpenChange(false)\n }\n },\n [onOpenChange]\n )\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent\n className=\"flex flex-col p-0 w-full sm:max-w-xl md:max-w-2xl\"\n overlayClassName=\"backdrop-blur-none\"\n onKeyDown={handleKeyDown}\n >\n {/* Header */}\n <div className=\"flex justify-between px-4 py-6 border-b border-border\">\n <div className=\"flex flex-col gap-2\">\n <h1 className=\"text-xl font-semibold text-foreground\">\n {t('shipment_tracking.details.title', 'Shipment Details')}\n </h1>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-4\">\n {isLoading ? (\n <div className=\"flex flex-col items-center justify-center h-48 gap-2\">\n <Spinner className=\"h-6 w-6\" />\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.details.loading', 'Loading shipment details...')}\n </span>\n </div>\n ) : !shipment ? (\n <div className=\"flex flex-col items-center justify-center h-48 gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.details.notFound', 'Shipment not found')}\n </span>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Shipment Card */}\n <ShipmentCard shipment={shipment} />\n\n {/* Destination Status */}\n <DestinationStatusCard shipment={shipment} />\n\n {/* Route Details */}\n {routeStops.length > 0 && <RouteDetails stops={routeStops} />}\n\n {/* Vessel Tracking Map */}\n {currentVessel.status === 'delivered' ? (\n // Container delivered - show delivered message instead of map\n <div className=\"relative w-full h-32 border border-green-200 dark:border-green-800 rounded-lg overflow-hidden bg-green-50 dark:bg-green-950/30\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"text-center\">\n <Package className=\"w-8 h-8 text-green-500 dark:text-green-400 mx-auto mb-2\" />\n <p className=\"text-sm font-medium text-green-700 dark:text-green-300\">\n {t('shipment_tracking.map.delivered', 'Container delivered')}\n </p>\n {currentVessel.currentPort && (\n <p className=\"text-xs text-green-600 dark:text-green-400 mt-1\">\n {currentVessel.currentPort}\n </p>\n )}\n </div>\n </div>\n </div>\n ) : currentVessel.vesselImo ? (\n // Show map with current or planned vessel\n <VesselTrackingMap\n vesselImo={currentVessel.vesselImo}\n vesselName={currentVessel.vesselName}\n height=\"250px\"\n autoRefresh={true}\n showInfoOverlay={true}\n containerStatus={currentVessel.status}\n currentPort={currentVessel.currentPort}\n isPlannedVessel={currentVessel.isPlannedVessel}\n currentLeg={currentVessel.currentLeg}\n traceFrom={currentVessel.traceFrom}\n />\n ) : (\n // No vessel IMO available\n <div className=\"relative w-full h-48 border border-border rounded-lg overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"text-center\">\n <Ship className=\"w-8 h-8 text-muted-foreground mx-auto mb-2\" />\n <p className=\"text-sm text-muted-foreground\">\n {t('shipment_tracking.details.noVesselData', 'No vessel data available')}\n </p>\n </div>\n </div>\n {/* Vessel name overlay when no IMO */}\n {currentVessel.vesselName && (\n <div className=\"absolute top-3 left-3 bg-card/95 backdrop-blur-sm rounded-lg shadow-md px-3 py-2 border border-border\">\n <div className=\"flex items-center gap-2\">\n <Ship className=\"w-4 h-4 text-blue-500 dark:text-blue-400\" />\n <div>\n <h2 className=\"font-semibold text-sm text-foreground\">{currentVessel.vesselName}</h2>\n <p className=\"text-xs text-muted-foreground\">\n {t('shipment_tracking.details.noImoNumber', 'IMO number not available')}\n </p>\n </div>\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Journey Timeline */}\n {events.length > 0 && <JourneyTimeline events={events} />}\n\n {/* Booking Details */}\n <BookingDetails shipment={shipment} />\n </div>\n )}\n </div>\n </SheetContent>\n </Sheet>\n )\n}\n\nexport default ShipmentDetailsDrawer\n"],
5
+ "mappings": "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",
6
+ "names": ["routeStops"]
7
+ }
@@ -0,0 +1,404 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback, useEffect } from "react";
4
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import { Ship, Anchor, Calendar, Package } from "lucide-react";
6
+ import {
7
+ Sheet,
8
+ SheetContent,
9
+ SheetHeader,
10
+ SheetTitle,
11
+ SheetDescription
12
+ } from "@freighttech/ui/primitives/sheet";
13
+ import { Button } from "@freighttech/ui/primitives/button";
14
+ import { Input } from "@freighttech/ui/primitives/input";
15
+ import { Label } from "@freighttech/ui/primitives/label";
16
+ import { Spinner } from "@freighttech/ui/primitives/spinner";
17
+ import { apiCall } from "@freighttech/ui/backend/utils/apiCall";
18
+ import { flash } from "@freighttech/ui/backend/FlashMessages";
19
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
20
+ import { FacilityLocationInput } from "./FacilityLocationInput.js";
21
+ function getPrimaryTimestamp(timestamps) {
22
+ if (!timestamps || timestamps.length === 0) return null;
23
+ const latest = timestamps.reduce(
24
+ (best, entry) => entry.updatedAt > best.updatedAt ? entry : best
25
+ );
26
+ return latest.value;
27
+ }
28
+ const initialFormData = {
29
+ carrierCode: "",
30
+ containerNumber: "",
31
+ bookingNumber: "",
32
+ bolNumber: "",
33
+ etd: "",
34
+ eta: "",
35
+ originLocation: null,
36
+ destinationLocation: null,
37
+ vesselName: "",
38
+ vesselImo: ""
39
+ };
40
+ function formatDateForInput(dateString) {
41
+ if (!dateString) return "";
42
+ const date = new Date(dateString);
43
+ if (Number.isNaN(date.getTime())) return "";
44
+ return date.toISOString().split("T")[0];
45
+ }
46
+ function ShipmentDrawer({
47
+ open,
48
+ onOpenChange,
49
+ mode,
50
+ shipmentId,
51
+ onSaved
52
+ }) {
53
+ const t = useT();
54
+ const queryClient = useQueryClient();
55
+ const [isSubmitting, setIsSubmitting] = useState(false);
56
+ const [formData, setFormData] = useState(initialFormData);
57
+ const { data: shipmentData, isLoading: isLoadingShipment } = useQuery({
58
+ queryKey: ["shipment_tracking_shipment", shipmentId],
59
+ queryFn: async () => {
60
+ if (!shipmentId) return null;
61
+ const response = await apiCall(`/api/shipment_tracking/shipments?id=${shipmentId}`);
62
+ if (!response.ok) throw new Error("Failed to load shipment");
63
+ const items = response.result?.items ?? [];
64
+ const item = items.find((i) => i.id === shipmentId) ?? items[0];
65
+ if (!item) return null;
66
+ return {
67
+ id: item.id,
68
+ carrierCode: item.carrierCode ?? item.carrier_code,
69
+ containerNumber: item.containerNumber ?? item.container_number,
70
+ bookingNumber: item.bookingNumber ?? item.booking_number,
71
+ bolNumber: item.bolNumber ?? item.bol_number,
72
+ etdTimestamps: item.etdTimestamps ?? item.etd_timestamps,
73
+ etaTimestamps: item.etaTimestamps ?? item.eta_timestamps,
74
+ atdTimestamps: item.atdTimestamps ?? item.atd_timestamps,
75
+ ataTimestamps: item.ataTimestamps ?? item.ata_timestamps,
76
+ originLocation: item.originLocation ?? item.origin_location,
77
+ destinationLocation: item.destinationLocation ?? item.destination_location,
78
+ vesselName: item.vesselName ?? item.vessel_name,
79
+ vesselImo: item.vesselImo ?? item.vessel_imo
80
+ };
81
+ },
82
+ enabled: mode === "edit" && !!shipmentId && open
83
+ });
84
+ useEffect(() => {
85
+ if (!open) return;
86
+ if (mode === "edit") {
87
+ if (shipmentData) {
88
+ const primaryEtd = getPrimaryTimestamp(shipmentData.etdTimestamps);
89
+ const primaryEta = getPrimaryTimestamp(shipmentData.etaTimestamps);
90
+ setFormData({
91
+ carrierCode: shipmentData.carrierCode || "",
92
+ containerNumber: shipmentData.containerNumber || "",
93
+ bookingNumber: shipmentData.bookingNumber || "",
94
+ bolNumber: shipmentData.bolNumber || "",
95
+ etd: formatDateForInput(primaryEtd),
96
+ eta: formatDateForInput(primaryEta),
97
+ originLocation: shipmentData.originLocation || null,
98
+ destinationLocation: shipmentData.destinationLocation || null,
99
+ vesselName: shipmentData.vesselName || "",
100
+ vesselImo: shipmentData.vesselImo || ""
101
+ });
102
+ }
103
+ } else {
104
+ setFormData(initialFormData);
105
+ }
106
+ }, [open, mode, shipmentData]);
107
+ const handleSubmit = useCallback(
108
+ async (e) => {
109
+ e.preventDefault();
110
+ setIsSubmitting(true);
111
+ try {
112
+ if (!formData.containerNumber && !formData.bookingNumber && !formData.bolNumber) {
113
+ flash(t("shipment_tracking.errors.referenceRequired", "At least one of container number, booking number, or BOL number is required"), "error");
114
+ setIsSubmitting(false);
115
+ return;
116
+ }
117
+ const buildManualTimestamp = (dateStr) => {
118
+ if (!dateStr) return void 0;
119
+ const isoValue = (/* @__PURE__ */ new Date(dateStr + "T00:00:00Z")).toISOString();
120
+ return {
121
+ value: isoValue,
122
+ offset: "Z",
123
+ source: "manual"
124
+ };
125
+ };
126
+ const etdEntry = buildManualTimestamp(formData.etd);
127
+ const etaEntry = buildManualTimestamp(formData.eta);
128
+ const cleanLocation = (loc) => {
129
+ if (!loc) return null;
130
+ if (!loc.name && !loc.unlocode) return null;
131
+ return {
132
+ name: loc.name || loc.unlocode || "Unknown",
133
+ unlocode: loc.unlocode || null,
134
+ countryCode: loc.countryCode || (loc.unlocode?.slice(0, 2) ?? null),
135
+ facilityCode: loc.facilityCode || null,
136
+ facilityCodeListProvider: loc.facilityCodeListProvider || null,
137
+ facilityTypeCode: loc.facilityTypeCode || null,
138
+ address: loc.address || null,
139
+ coords: loc.coords || null,
140
+ operatorName: loc.operatorName || null,
141
+ source: "manual"
142
+ };
143
+ };
144
+ const payload = {
145
+ carrierCode: formData.carrierCode || void 0,
146
+ containerNumber: formData.containerNumber || void 0,
147
+ bookingNumber: formData.bookingNumber || void 0,
148
+ bolNumber: formData.bolNumber || void 0,
149
+ originLocation: cleanLocation(formData.originLocation),
150
+ destinationLocation: cleanLocation(formData.destinationLocation),
151
+ vesselName: formData.vesselName || void 0,
152
+ vesselImo: formData.vesselImo || void 0
153
+ };
154
+ if (mode === "edit") {
155
+ if (etdEntry) payload.addEtdTimestamp = etdEntry;
156
+ if (etaEntry) payload.addEtaTimestamp = etaEntry;
157
+ } else {
158
+ const now = (/* @__PURE__ */ new Date()).toISOString();
159
+ if (etdEntry) {
160
+ payload.etdTimestamps = [{ ...etdEntry, updatedAt: now }];
161
+ }
162
+ if (etaEntry) {
163
+ payload.etaTimestamps = [{ ...etaEntry, updatedAt: now }];
164
+ }
165
+ }
166
+ let response;
167
+ if (mode === "edit" && shipmentId) {
168
+ response = await apiCall(
169
+ `/api/shipment_tracking/shipments`,
170
+ {
171
+ method: "PUT",
172
+ headers: { "Content-Type": "application/json" },
173
+ body: JSON.stringify({ id: shipmentId, ...payload })
174
+ }
175
+ );
176
+ } else {
177
+ response = await apiCall(
178
+ "/api/shipment_tracking/shipments",
179
+ {
180
+ method: "POST",
181
+ headers: { "Content-Type": "application/json" },
182
+ body: JSON.stringify(payload)
183
+ }
184
+ );
185
+ }
186
+ if (response.ok && response.result?.id) {
187
+ flash(
188
+ mode === "edit" ? t("shipment_tracking.shipments.flash.updated", "Shipment updated successfully") : t("shipment_tracking.shipments.flash.created", "Shipment created successfully"),
189
+ "success"
190
+ );
191
+ queryClient.invalidateQueries({ queryKey: ["shipment_tracking_shipments"] });
192
+ queryClient.invalidateQueries({ queryKey: ["shipment_tracking_shipment", shipmentId] });
193
+ onSaved?.({ id: response.result.id });
194
+ onOpenChange(false);
195
+ } else {
196
+ flash(response.result?.error || t("shipment_tracking.shipments.flash.saveFailed", "Failed to save shipment"), "error");
197
+ }
198
+ } catch (error) {
199
+ flash(error instanceof Error ? error.message : t("shipment_tracking.shipments.flash.saveFailed", "Failed to save shipment"), "error");
200
+ } finally {
201
+ setIsSubmitting(false);
202
+ }
203
+ },
204
+ [formData, mode, shipmentId, queryClient, onSaved, onOpenChange, t]
205
+ );
206
+ const handleKeyDown = useCallback(
207
+ (e) => {
208
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
209
+ e.preventDefault();
210
+ handleSubmit(e);
211
+ }
212
+ if (e.key === "Escape") {
213
+ onOpenChange(false);
214
+ }
215
+ },
216
+ [handleSubmit, onOpenChange]
217
+ );
218
+ const title = mode === "edit" ? t("shipment_tracking.shipments.edit", "Edit Shipment") : t("shipment_tracking.shipments.create", "Create Shipment");
219
+ if (mode === "edit" && isLoadingShipment) {
220
+ return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsx(
221
+ SheetContent,
222
+ {
223
+ className: "flex flex-col p-0",
224
+ style: { width: "550px", maxWidth: "550px" },
225
+ overlayClassName: "backdrop-blur-none",
226
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-48 gap-2", children: [
227
+ /* @__PURE__ */ jsx(Spinner, { className: "h-6 w-6" }),
228
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: t("shipment_tracking.shipments.loading", "Loading shipment...") })
229
+ ] })
230
+ }
231
+ ) });
232
+ }
233
+ return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
234
+ SheetContent,
235
+ {
236
+ className: "flex flex-col p-0",
237
+ style: { width: "550px", maxWidth: "550px" },
238
+ overlayClassName: "backdrop-blur-none",
239
+ onKeyDown: handleKeyDown,
240
+ children: [
241
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 p-6 pb-4 border-b", children: /* @__PURE__ */ jsxs(SheetHeader, { children: [
242
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-2", children: [
243
+ /* @__PURE__ */ jsx(Ship, { className: "h-5 w-5" }),
244
+ title
245
+ ] }),
246
+ /* @__PURE__ */ jsx(SheetDescription, { children: t("shipment_tracking.shipments.drawer.description", "Enter shipment details for tracking. At least one reference number is required.") })
247
+ ] }) }),
248
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0", children: [
249
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-6 space-y-6", children: [
250
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
251
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
252
+ /* @__PURE__ */ jsx(Package, { className: "h-4 w-4" }),
253
+ t("shipment_tracking.shipments.drawer.referenceNumbers", "Reference Numbers")
254
+ ] }),
255
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4", children: [
256
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
257
+ /* @__PURE__ */ jsx(Label, { htmlFor: "containerNumber", children: t("shipment_tracking.shipments.fields.containerNumber", "Container Number") }),
258
+ /* @__PURE__ */ jsx(
259
+ Input,
260
+ {
261
+ id: "containerNumber",
262
+ placeholder: "ABCD1234567",
263
+ value: formData.containerNumber,
264
+ onChange: (e) => setFormData({ ...formData, containerNumber: e.target.value.toUpperCase() })
265
+ }
266
+ )
267
+ ] }),
268
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
269
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
270
+ /* @__PURE__ */ jsx(Label, { htmlFor: "bookingNumber", children: t("shipment_tracking.shipments.fields.bookingNumber", "Booking Number") }),
271
+ /* @__PURE__ */ jsx(
272
+ Input,
273
+ {
274
+ id: "bookingNumber",
275
+ placeholder: "BKG123456",
276
+ value: formData.bookingNumber,
277
+ onChange: (e) => setFormData({ ...formData, bookingNumber: e.target.value })
278
+ }
279
+ )
280
+ ] }),
281
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
282
+ /* @__PURE__ */ jsx(Label, { htmlFor: "bolNumber", children: t("shipment_tracking.shipments.fields.bolNumber", "BOL Number") }),
283
+ /* @__PURE__ */ jsx(
284
+ Input,
285
+ {
286
+ id: "bolNumber",
287
+ placeholder: "BOL123456",
288
+ value: formData.bolNumber,
289
+ onChange: (e) => setFormData({ ...formData, bolNumber: e.target.value })
290
+ }
291
+ )
292
+ ] })
293
+ ] })
294
+ ] })
295
+ ] }),
296
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
297
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
298
+ /* @__PURE__ */ jsx(Anchor, { className: "h-4 w-4" }),
299
+ t("shipment_tracking.shipments.drawer.carrierVessel", "Carrier & Vessel")
300
+ ] }),
301
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
302
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
303
+ /* @__PURE__ */ jsx(Label, { htmlFor: "carrierCode", children: t("shipment_tracking.shipments.fields.carrierCode", "Carrier Code") }),
304
+ /* @__PURE__ */ jsx(
305
+ Input,
306
+ {
307
+ id: "carrierCode",
308
+ placeholder: "MAEU",
309
+ value: formData.carrierCode,
310
+ onChange: (e) => setFormData({ ...formData, carrierCode: e.target.value.toUpperCase() })
311
+ }
312
+ )
313
+ ] }),
314
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
315
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vesselName", children: t("shipment_tracking.shipments.fields.vesselName", "Vessel Name") }),
316
+ /* @__PURE__ */ jsx(
317
+ Input,
318
+ {
319
+ id: "vesselName",
320
+ placeholder: "Ever Given",
321
+ value: formData.vesselName,
322
+ onChange: (e) => setFormData({ ...formData, vesselName: e.target.value })
323
+ }
324
+ )
325
+ ] })
326
+ ] }),
327
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
328
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vesselImo", children: t("shipment_tracking.shipments.fields.vesselImo", "Vessel IMO") }),
329
+ /* @__PURE__ */ jsx(
330
+ Input,
331
+ {
332
+ id: "vesselImo",
333
+ placeholder: "9811000",
334
+ value: formData.vesselImo,
335
+ onChange: (e) => setFormData({ ...formData, vesselImo: e.target.value })
336
+ }
337
+ )
338
+ ] })
339
+ ] }),
340
+ /* @__PURE__ */ jsx(
341
+ FacilityLocationInput,
342
+ {
343
+ label: t("shipment_tracking.shipments.drawer.origin", "Origin"),
344
+ value: formData.originLocation,
345
+ onChange: (loc) => setFormData({ ...formData, originLocation: loc })
346
+ }
347
+ ),
348
+ /* @__PURE__ */ jsx(
349
+ FacilityLocationInput,
350
+ {
351
+ label: t("shipment_tracking.shipments.drawer.destination", "Destination"),
352
+ value: formData.destinationLocation,
353
+ onChange: (loc) => setFormData({ ...formData, destinationLocation: loc })
354
+ }
355
+ ),
356
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
357
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
358
+ /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4" }),
359
+ t("shipment_tracking.shipments.drawer.schedule", "Schedule")
360
+ ] }),
361
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
362
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
363
+ /* @__PURE__ */ jsx(Label, { htmlFor: "etd", children: t("shipment_tracking.shipments.fields.etd", "ETD") }),
364
+ /* @__PURE__ */ jsx(
365
+ Input,
366
+ {
367
+ id: "etd",
368
+ type: "date",
369
+ value: formData.etd,
370
+ onChange: (e) => setFormData({ ...formData, etd: e.target.value })
371
+ }
372
+ )
373
+ ] }),
374
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
375
+ /* @__PURE__ */ jsx(Label, { htmlFor: "eta", children: t("shipment_tracking.shipments.fields.eta", "ETA") }),
376
+ /* @__PURE__ */ jsx(
377
+ Input,
378
+ {
379
+ id: "eta",
380
+ type: "date",
381
+ value: formData.eta,
382
+ onChange: (e) => setFormData({ ...formData, eta: e.target.value })
383
+ }
384
+ )
385
+ ] })
386
+ ] })
387
+ ] })
388
+ ] }),
389
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 border-t bg-background p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3", children: [
390
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), children: t("common.cancel", "Cancel") }),
391
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
392
+ /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }),
393
+ t("common.saving", "Saving...")
394
+ ] }) : mode === "edit" ? t("common.saveChanges", "Save Changes") : t("shipment_tracking.shipments.create", "Create Shipment") })
395
+ ] }) })
396
+ ] })
397
+ ]
398
+ }
399
+ ) });
400
+ }
401
+ export {
402
+ ShipmentDrawer
403
+ };
404
+ //# sourceMappingURL=ShipmentDrawer.js.map