@freighttech/terminal-tracking 0.7.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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/dist/generated/entities/terminal_config/index.js +53 -0
  4. package/dist/generated/entities/terminal_config/index.js.map +7 -0
  5. package/dist/generated/entities/terminal_event/index.js +59 -0
  6. package/dist/generated/entities/terminal_event/index.js.map +7 -0
  7. package/dist/generated/entities/terminal_tracking_job/index.js +37 -0
  8. package/dist/generated/entities/terminal_tracking_job/index.js.map +7 -0
  9. package/dist/generated/entities/terminal_vessel_visit/index.js +41 -0
  10. package/dist/generated/entities/terminal_vessel_visit/index.js.map +7 -0
  11. package/dist/generated/entities.ids.generated.js +26 -0
  12. package/dist/generated/entities.ids.generated.js.map +7 -0
  13. package/dist/index.js +20 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/modules/terminal_tracking/acl.js +14 -0
  16. package/dist/modules/terminal_tracking/acl.js.map +7 -0
  17. package/dist/modules/terminal_tracking/api/openapi.js +27 -0
  18. package/dist/modules/terminal_tracking/api/openapi.js.map +7 -0
  19. package/dist/modules/terminal_tracking/api/terminal-configs/route.js +140 -0
  20. package/dist/modules/terminal_tracking/api/terminal-configs/route.js.map +7 -0
  21. package/dist/modules/terminal_tracking/api/terminal-configs/test/route.js +58 -0
  22. package/dist/modules/terminal_tracking/api/terminal-configs/test/route.js.map +7 -0
  23. package/dist/modules/terminal_tracking/api/tracking-jobs/details/route.js +109 -0
  24. package/dist/modules/terminal_tracking/api/tracking-jobs/details/route.js.map +7 -0
  25. package/dist/modules/terminal_tracking/api/tracking-jobs/poll/route.js +58 -0
  26. package/dist/modules/terminal_tracking/api/tracking-jobs/poll/route.js.map +7 -0
  27. package/dist/modules/terminal_tracking/api/tracking-jobs/route.js +189 -0
  28. package/dist/modules/terminal_tracking/api/tracking-jobs/route.js.map +7 -0
  29. package/dist/modules/terminal_tracking/api/utils.js +12 -0
  30. package/dist/modules/terminal_tracking/api/utils.js.map +7 -0
  31. package/dist/modules/terminal_tracking/api/vessel-visits/route.js +83 -0
  32. package/dist/modules/terminal_tracking/api/vessel-visits/route.js.map +7 -0
  33. package/dist/modules/terminal_tracking/backend/terminal-configs/page.js +669 -0
  34. package/dist/modules/terminal_tracking/backend/terminal-configs/page.js.map +7 -0
  35. package/dist/modules/terminal_tracking/backend/terminal-configs/page.meta.js +19 -0
  36. package/dist/modules/terminal_tracking/backend/terminal-configs/page.meta.js.map +7 -0
  37. package/dist/modules/terminal_tracking/backend/terminal-containers/page.js +275 -0
  38. package/dist/modules/terminal_tracking/backend/terminal-containers/page.js.map +7 -0
  39. package/dist/modules/terminal_tracking/backend/terminal-containers/page.meta.js +19 -0
  40. package/dist/modules/terminal_tracking/backend/terminal-containers/page.meta.js.map +7 -0
  41. package/dist/modules/terminal_tracking/ce.js +35 -0
  42. package/dist/modules/terminal_tracking/ce.js.map +7 -0
  43. package/dist/modules/terminal_tracking/commands/index.js +4 -0
  44. package/dist/modules/terminal_tracking/commands/index.js.map +7 -0
  45. package/dist/modules/terminal_tracking/commands/terminal-configs.js +107 -0
  46. package/dist/modules/terminal_tracking/commands/terminal-configs.js.map +7 -0
  47. package/dist/modules/terminal_tracking/commands/terminal-tracking.js +27 -0
  48. package/dist/modules/terminal_tracking/commands/terminal-tracking.js.map +7 -0
  49. package/dist/modules/terminal_tracking/commands/tracking-jobs.js +56 -0
  50. package/dist/modules/terminal_tracking/commands/tracking-jobs.js.map +7 -0
  51. package/dist/modules/terminal_tracking/components/ContainerDetailsDrawer.js +371 -0
  52. package/dist/modules/terminal_tracking/components/ContainerDetailsDrawer.js.map +7 -0
  53. package/dist/modules/terminal_tracking/data/entities.js +337 -0
  54. package/dist/modules/terminal_tracking/data/entities.js.map +7 -0
  55. package/dist/modules/terminal_tracking/data/validators.js +98 -0
  56. package/dist/modules/terminal_tracking/data/validators.js.map +7 -0
  57. package/dist/modules/terminal_tracking/di.js +39 -0
  58. package/dist/modules/terminal_tracking/di.js.map +7 -0
  59. package/dist/modules/terminal_tracking/encryption.js +14 -0
  60. package/dist/modules/terminal_tracking/encryption.js.map +7 -0
  61. package/dist/modules/terminal_tracking/events.js +45 -0
  62. package/dist/modules/terminal_tracking/events.js.map +7 -0
  63. package/dist/modules/terminal_tracking/i18n/de.json +175 -0
  64. package/dist/modules/terminal_tracking/i18n/en.json +178 -0
  65. package/dist/modules/terminal_tracking/i18n/es.json +175 -0
  66. package/dist/modules/terminal_tracking/i18n/pl.json +206 -0
  67. package/dist/modules/terminal_tracking/index.js +15 -0
  68. package/dist/modules/terminal_tracking/index.js.map +7 -0
  69. package/dist/modules/terminal_tracking/lib/adapters/bct/adapter.js +164 -0
  70. package/dist/modules/terminal_tracking/lib/adapters/bct/adapter.js.map +7 -0
  71. package/dist/modules/terminal_tracking/lib/adapters/bct/auth/basic.js +23 -0
  72. package/dist/modules/terminal_tracking/lib/adapters/bct/auth/basic.js.map +7 -0
  73. package/dist/modules/terminal_tracking/lib/adapters/bct/incos-semantics.js +146 -0
  74. package/dist/modules/terminal_tracking/lib/adapters/bct/incos-semantics.js.map +7 -0
  75. package/dist/modules/terminal_tracking/lib/adapters/bct/types.js +1 -0
  76. package/dist/modules/terminal_tracking/lib/adapters/bct/types.js.map +7 -0
  77. package/dist/modules/terminal_tracking/lib/adapters/gct/adapter.js +106 -0
  78. package/dist/modules/terminal_tracking/lib/adapters/gct/adapter.js.map +7 -0
  79. package/dist/modules/terminal_tracking/lib/adapters/gct/auth/token.js +120 -0
  80. package/dist/modules/terminal_tracking/lib/adapters/gct/auth/token.js.map +7 -0
  81. package/dist/modules/terminal_tracking/lib/adapters/gct/gct-semantics.js +77 -0
  82. package/dist/modules/terminal_tracking/lib/adapters/gct/gct-semantics.js.map +7 -0
  83. package/dist/modules/terminal_tracking/lib/adapters/gct/types.js +1 -0
  84. package/dist/modules/terminal_tracking/lib/adapters/gct/types.js.map +7 -0
  85. package/dist/modules/terminal_tracking/lib/adapters/index.js +12 -0
  86. package/dist/modules/terminal_tracking/lib/adapters/index.js.map +7 -0
  87. package/dist/modules/terminal_tracking/lib/adapters/n4/adapter.js +121 -0
  88. package/dist/modules/terminal_tracking/lib/adapters/n4/adapter.js.map +7 -0
  89. package/dist/modules/terminal_tracking/lib/adapters/n4/auth/ropc.js +81 -0
  90. package/dist/modules/terminal_tracking/lib/adapters/n4/auth/ropc.js.map +7 -0
  91. package/dist/modules/terminal_tracking/lib/adapters/n4/http.js +66 -0
  92. package/dist/modules/terminal_tracking/lib/adapters/n4/http.js.map +7 -0
  93. package/dist/modules/terminal_tracking/lib/adapters/n4/n4-semantics.js +95 -0
  94. package/dist/modules/terminal_tracking/lib/adapters/n4/n4-semantics.js.map +7 -0
  95. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.js +70 -0
  96. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.js.map +7 -0
  97. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.js +44 -0
  98. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.js.map +7 -0
  99. package/dist/modules/terminal_tracking/lib/adapters/n4/proxy.js +15 -0
  100. package/dist/modules/terminal_tracking/lib/adapters/n4/proxy.js.map +7 -0
  101. package/dist/modules/terminal_tracking/lib/adapters/n4/types.js +1 -0
  102. package/dist/modules/terminal_tracking/lib/adapters/n4/types.js.map +7 -0
  103. package/dist/modules/terminal_tracking/lib/adapters/zoned-time.js +27 -0
  104. package/dist/modules/terminal_tracking/lib/adapters/zoned-time.js.map +7 -0
  105. package/dist/modules/terminal_tracking/lib/availability.js +40 -0
  106. package/dist/modules/terminal_tracking/lib/availability.js.map +7 -0
  107. package/dist/modules/terminal_tracking/lib/ensure-schedule.js +51 -0
  108. package/dist/modules/terminal_tracking/lib/ensure-schedule.js.map +7 -0
  109. package/dist/modules/terminal_tracking/lib/holds.js +240 -0
  110. package/dist/modules/terminal_tracking/lib/holds.js.map +7 -0
  111. package/dist/modules/terminal_tracking/lib/logger.js +19 -0
  112. package/dist/modules/terminal_tracking/lib/logger.js.map +7 -0
  113. package/dist/modules/terminal_tracking/lib/rate-limiter.js +30 -0
  114. package/dist/modules/terminal_tracking/lib/rate-limiter.js.map +7 -0
  115. package/dist/modules/terminal_tracking/lib/stops.js +48 -0
  116. package/dist/modules/terminal_tracking/lib/stops.js.map +7 -0
  117. package/dist/modules/terminal_tracking/lib/terminal-adapter.js +1 -0
  118. package/dist/modules/terminal_tracking/lib/terminal-adapter.js.map +7 -0
  119. package/dist/modules/terminal_tracking/lib/totp.js +41 -0
  120. package/dist/modules/terminal_tracking/lib/totp.js.map +7 -0
  121. package/dist/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.js +101 -0
  122. package/dist/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.js.map +7 -0
  123. package/dist/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.js +39 -0
  124. package/dist/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.js.map +7 -0
  125. package/dist/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.js +13 -0
  126. package/dist/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.js.map +7 -0
  127. package/dist/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.js +15 -0
  128. package/dist/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.js.map +7 -0
  129. package/dist/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.js +13 -0
  130. package/dist/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.js.map +7 -0
  131. package/dist/modules/terminal_tracking/services/terminalMatcherService.js +61 -0
  132. package/dist/modules/terminal_tracking/services/terminalMatcherService.js.map +7 -0
  133. package/dist/modules/terminal_tracking/services/terminalRegistry.js +24 -0
  134. package/dist/modules/terminal_tracking/services/terminalRegistry.js.map +7 -0
  135. package/dist/modules/terminal_tracking/services/terminalTrackingService.js +687 -0
  136. package/dist/modules/terminal_tracking/services/terminalTrackingService.js.map +7 -0
  137. package/dist/modules/terminal_tracking/setup.js +23 -0
  138. package/dist/modules/terminal_tracking/setup.js.map +7 -0
  139. package/dist/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.js +22 -0
  140. package/dist/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.js.map +7 -0
  141. package/generated/entities/terminal_config/index.ts +36 -0
  142. package/generated/entities/terminal_event/index.ts +39 -0
  143. package/generated/entities/terminal_tracking_job/index.ts +28 -0
  144. package/generated/entities/terminal_vessel_visit/index.ts +30 -0
  145. package/generated/entities.ids.generated.ts +52 -0
  146. package/package.json +80 -0
  147. package/src/index.ts +44 -0
  148. package/src/modules/terminal_tracking/__integration__/TC-TRACK-301-gct-config-test-connection.spec.ts +66 -0
  149. package/src/modules/terminal_tracking/__integration__/TC-TRACK-317-bct-config-test-connection.spec.ts +68 -0
  150. package/src/modules/terminal_tracking/__integration__/helpers.ts +148 -0
  151. package/src/modules/terminal_tracking/__integration__/meta.ts +4 -0
  152. package/src/modules/terminal_tracking/acl.ts +10 -0
  153. package/src/modules/terminal_tracking/api/openapi.ts +28 -0
  154. package/src/modules/terminal_tracking/api/terminal-configs/route.ts +140 -0
  155. package/src/modules/terminal_tracking/api/terminal-configs/test/route.ts +64 -0
  156. package/src/modules/terminal_tracking/api/tracking-jobs/details/route.ts +116 -0
  157. package/src/modules/terminal_tracking/api/tracking-jobs/poll/route.ts +64 -0
  158. package/src/modules/terminal_tracking/api/tracking-jobs/route.ts +243 -0
  159. package/src/modules/terminal_tracking/api/utils.ts +10 -0
  160. package/src/modules/terminal_tracking/api/vessel-visits/route.ts +95 -0
  161. package/src/modules/terminal_tracking/backend/terminal-configs/page.meta.ts +16 -0
  162. package/src/modules/terminal_tracking/backend/terminal-configs/page.tsx +728 -0
  163. package/src/modules/terminal_tracking/backend/terminal-containers/page.meta.ts +16 -0
  164. package/src/modules/terminal_tracking/backend/terminal-containers/page.tsx +328 -0
  165. package/src/modules/terminal_tracking/ce.ts +31 -0
  166. package/src/modules/terminal_tracking/commands/index.ts +3 -0
  167. package/src/modules/terminal_tracking/commands/terminal-configs.ts +132 -0
  168. package/src/modules/terminal_tracking/commands/terminal-tracking.ts +34 -0
  169. package/src/modules/terminal_tracking/commands/tracking-jobs.ts +69 -0
  170. package/src/modules/terminal_tracking/components/ContainerDetailsDrawer.tsx +540 -0
  171. package/src/modules/terminal_tracking/data/entities.ts +386 -0
  172. package/src/modules/terminal_tracking/data/validators.ts +113 -0
  173. package/src/modules/terminal_tracking/di.ts +46 -0
  174. package/src/modules/terminal_tracking/encryption.ts +12 -0
  175. package/src/modules/terminal_tracking/events.ts +48 -0
  176. package/src/modules/terminal_tracking/i18n/de.json +175 -0
  177. package/src/modules/terminal_tracking/i18n/en.json +178 -0
  178. package/src/modules/terminal_tracking/i18n/es.json +175 -0
  179. package/src/modules/terminal_tracking/i18n/pl.json +206 -0
  180. package/src/modules/terminal_tracking/index.ts +13 -0
  181. package/src/modules/terminal_tracking/lib/__tests__/availability.test.ts +136 -0
  182. package/src/modules/terminal_tracking/lib/__tests__/ensure-schedule.test.ts +67 -0
  183. package/src/modules/terminal_tracking/lib/__tests__/holds.test.ts +73 -0
  184. package/src/modules/terminal_tracking/lib/__tests__/stops.test.ts +80 -0
  185. package/src/modules/terminal_tracking/lib/__tests__/totp.test.ts +38 -0
  186. package/src/modules/terminal_tracking/lib/adapters/bct/__tests__/adapter.test.ts +196 -0
  187. package/src/modules/terminal_tracking/lib/adapters/bct/__tests__/incos-semantics.test.ts +303 -0
  188. package/src/modules/terminal_tracking/lib/adapters/bct/adapter.ts +219 -0
  189. package/src/modules/terminal_tracking/lib/adapters/bct/auth/basic.ts +34 -0
  190. package/src/modules/terminal_tracking/lib/adapters/bct/incos-semantics.ts +291 -0
  191. package/src/modules/terminal_tracking/lib/adapters/bct/types.ts +113 -0
  192. package/src/modules/terminal_tracking/lib/adapters/gct/__tests__/adapter.test.ts +238 -0
  193. package/src/modules/terminal_tracking/lib/adapters/gct/__tests__/gct-semantics.test.ts +133 -0
  194. package/src/modules/terminal_tracking/lib/adapters/gct/adapter.ts +152 -0
  195. package/src/modules/terminal_tracking/lib/adapters/gct/auth/token.ts +219 -0
  196. package/src/modules/terminal_tracking/lib/adapters/gct/gct-semantics.ts +136 -0
  197. package/src/modules/terminal_tracking/lib/adapters/gct/types.ts +58 -0
  198. package/src/modules/terminal_tracking/lib/adapters/index.ts +16 -0
  199. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/adapter-proxy.test.ts +69 -0
  200. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/data-table.test.ts +179 -0
  201. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/http.test.ts +98 -0
  202. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/n4-semantics.test.ts +140 -0
  203. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/proxy.test.ts +19 -0
  204. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/vessel-data-table.test.ts +67 -0
  205. package/src/modules/terminal_tracking/lib/adapters/n4/adapter.ts +163 -0
  206. package/src/modules/terminal_tracking/lib/adapters/n4/auth/ropc.ts +104 -0
  207. package/src/modules/terminal_tracking/lib/adapters/n4/http.ts +127 -0
  208. package/src/modules/terminal_tracking/lib/adapters/n4/n4-semantics.ts +156 -0
  209. package/src/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.ts +102 -0
  210. package/src/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.ts +60 -0
  211. package/src/modules/terminal_tracking/lib/adapters/n4/proxy.ts +24 -0
  212. package/src/modules/terminal_tracking/lib/adapters/n4/types.ts +56 -0
  213. package/src/modules/terminal_tracking/lib/adapters/zoned-time.ts +65 -0
  214. package/src/modules/terminal_tracking/lib/availability.ts +119 -0
  215. package/src/modules/terminal_tracking/lib/ensure-schedule.ts +83 -0
  216. package/src/modules/terminal_tracking/lib/holds.ts +344 -0
  217. package/src/modules/terminal_tracking/lib/logger.ts +21 -0
  218. package/src/modules/terminal_tracking/lib/rate-limiter.ts +52 -0
  219. package/src/modules/terminal_tracking/lib/stops.ts +124 -0
  220. package/src/modules/terminal_tracking/lib/terminal-adapter.ts +136 -0
  221. package/src/modules/terminal_tracking/lib/totp.ts +63 -0
  222. package/src/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.ts +104 -0
  223. package/src/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.ts +40 -0
  224. package/src/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.ts +11 -0
  225. package/src/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.ts +13 -0
  226. package/src/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.ts +16 -0
  227. package/src/modules/terminal_tracking/services/__tests__/availabilityEmit.test.ts +277 -0
  228. package/src/modules/terminal_tracking/services/__tests__/createJob.test.ts +59 -0
  229. package/src/modules/terminal_tracking/services/__tests__/pollAllBatch.test.ts +147 -0
  230. package/src/modules/terminal_tracking/services/__tests__/terminalMatcher.test.ts +70 -0
  231. package/src/modules/terminal_tracking/services/__tests__/vesselResolution.test.ts +453 -0
  232. package/src/modules/terminal_tracking/services/terminalMatcherService.ts +97 -0
  233. package/src/modules/terminal_tracking/services/terminalRegistry.ts +30 -0
  234. package/src/modules/terminal_tracking/services/terminalTrackingService.ts +912 -0
  235. package/src/modules/terminal_tracking/setup.ts +24 -0
  236. package/src/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.ts +32 -0
@@ -0,0 +1,140 @@
1
+ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
2
+ import { z } from "zod";
3
+ import { TerminalConfig } from "../../data/entities.js";
4
+ import {
5
+ terminalConfigListSchema,
6
+ terminalConfigCreateSchema,
7
+ terminalConfigUpdateSchema
8
+ } from "../../data/validators.js";
9
+ import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
10
+ import {
11
+ createTerminalTrackingCrudOpenApi,
12
+ createPagedListResponseSchema,
13
+ defaultOkResponseSchema
14
+ } from "../openapi.js";
15
+ import { withScopedPayload } from "../utils.js";
16
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
17
+ const rawBodySchema = z.object({}).passthrough();
18
+ const routeMetadata = {
19
+ GET: { requireAuth: true, requireFeatures: ["terminal_tracking.terminal_configs.view"] },
20
+ POST: { requireAuth: true, requireFeatures: ["terminal_tracking.terminal_configs.manage"] },
21
+ PUT: { requireAuth: true, requireFeatures: ["terminal_tracking.terminal_configs.manage"] },
22
+ DELETE: { requireAuth: true, requireFeatures: ["terminal_tracking.terminal_configs.manage"] }
23
+ };
24
+ const listFields = [
25
+ "id",
26
+ "terminalCode",
27
+ "adapterType",
28
+ "displayName",
29
+ "baseUrl",
30
+ "authType",
31
+ "unlocode",
32
+ "isActive",
33
+ "createdAt",
34
+ "updatedAt"
35
+ ];
36
+ const buildFilters = (query) => {
37
+ const filters = { deletedAt: null };
38
+ if (query.terminalCode) filters.terminalCode = query.terminalCode;
39
+ if (query.isActive !== void 0) {
40
+ const parsed = parseBooleanToken(query.isActive);
41
+ if (parsed !== null) filters.isActive = parsed;
42
+ }
43
+ return filters;
44
+ };
45
+ const crud = makeCrudRoute({
46
+ metadata: routeMetadata,
47
+ orm: {
48
+ entity: TerminalConfig,
49
+ idField: "id",
50
+ orgField: "organizationId",
51
+ tenantField: "tenantId",
52
+ softDeleteField: "deletedAt"
53
+ },
54
+ list: {
55
+ schema: terminalConfigListSchema,
56
+ fields: listFields,
57
+ sortFieldMap: {
58
+ id: "id",
59
+ terminalCode: "terminal_code",
60
+ isActive: "is_active",
61
+ createdAt: "created_at"
62
+ },
63
+ buildFilters: async (query) => buildFilters(query)
64
+ },
65
+ actions: {
66
+ create: {
67
+ commandId: "terminal_tracking.terminal_config.create",
68
+ schema: rawBodySchema,
69
+ mapInput: async ({ raw, ctx }) => {
70
+ const { translate } = await resolveTranslations();
71
+ const scoped = withScopedPayload(raw ?? {}, ctx, translate);
72
+ return terminalConfigCreateSchema.parse(scoped);
73
+ },
74
+ response: ({ result }) => ({ id: result?.id ?? null }),
75
+ status: 201
76
+ },
77
+ update: {
78
+ commandId: "terminal_tracking.terminal_config.update",
79
+ schema: rawBodySchema,
80
+ mapInput: async ({ raw, ctx }) => {
81
+ const { translate } = await resolveTranslations();
82
+ const scoped = withScopedPayload(raw ?? {}, ctx, translate);
83
+ return terminalConfigUpdateSchema.parse(scoped);
84
+ },
85
+ response: ({ result }) => ({ id: result?.id ?? null }),
86
+ status: 200
87
+ },
88
+ delete: {
89
+ commandId: "terminal_tracking.terminal_config.delete",
90
+ schema: rawBodySchema,
91
+ mapInput: async ({ raw, ctx }) => {
92
+ const { translate } = await resolveTranslations();
93
+ const body = raw?.body ?? raw ?? {};
94
+ const scoped = withScopedPayload(body, ctx, translate);
95
+ return { id: body.id, ...scoped };
96
+ },
97
+ response: ({ result }) => ({ id: result?.id ?? null }),
98
+ status: 200
99
+ }
100
+ }
101
+ });
102
+ const openApi = createTerminalTrackingCrudOpenApi({
103
+ resourceName: "TerminalConfig",
104
+ pluralName: "TerminalConfigs",
105
+ querySchema: terminalConfigListSchema,
106
+ listResponseSchema: createPagedListResponseSchema(
107
+ z.object({
108
+ id: z.string().uuid(),
109
+ terminalCode: z.string(),
110
+ adapterType: z.string(),
111
+ displayName: z.string(),
112
+ isActive: z.boolean()
113
+ })
114
+ ),
115
+ create: { schema: rawBodySchema, description: "Creates a new terminal configuration." },
116
+ update: {
117
+ schema: rawBodySchema,
118
+ responseSchema: defaultOkResponseSchema,
119
+ description: "Updates an existing terminal configuration."
120
+ },
121
+ del: {
122
+ schema: z.object({ id: z.string().uuid() }),
123
+ responseSchema: defaultOkResponseSchema,
124
+ description: "Deletes a terminal configuration."
125
+ }
126
+ });
127
+ const metadata = crud.metadata;
128
+ const GET = crud.GET;
129
+ const POST = crud.POST;
130
+ const PUT = crud.PUT;
131
+ const DELETE = crud.DELETE;
132
+ export {
133
+ DELETE,
134
+ GET,
135
+ POST,
136
+ PUT,
137
+ metadata,
138
+ openApi
139
+ };
140
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/terminal_tracking/api/terminal-configs/route.ts"],
4
+ "sourcesContent": ["import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { z } from 'zod'\nimport { TerminalConfig } from '../../data/entities'\nimport {\n terminalConfigListSchema,\n terminalConfigCreateSchema,\n terminalConfigUpdateSchema,\n} from '../../data/validators'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport {\n createTerminalTrackingCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { withScopedPayload } from '../utils'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nconst rawBodySchema = z.object({}).passthrough()\n\ntype ListQuery = z.infer<typeof terminalConfigListSchema>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['terminal_tracking.terminal_configs.view'] },\n POST: { requireAuth: true, requireFeatures: ['terminal_tracking.terminal_configs.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['terminal_tracking.terminal_configs.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['terminal_tracking.terminal_configs.manage'] },\n}\n\nconst listFields = [\n 'id',\n 'terminalCode',\n 'adapterType',\n 'displayName',\n 'baseUrl',\n 'authType',\n 'unlocode',\n 'isActive',\n 'createdAt',\n 'updatedAt',\n]\n\nconst buildFilters = (query: ListQuery): Record<string, unknown> => {\n const filters: Record<string, unknown> = { deletedAt: null }\n if (query.terminalCode) filters.terminalCode = query.terminalCode\n if (query.isActive !== undefined) {\n const parsed = parseBooleanToken(query.isActive)\n if (parsed !== null) filters.isActive = parsed\n }\n return filters\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: TerminalConfig,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: terminalConfigListSchema,\n fields: listFields,\n sortFieldMap: {\n id: 'id',\n terminalCode: 'terminal_code',\n isActive: 'is_active',\n createdAt: 'created_at',\n },\n buildFilters: async (query) => buildFilters(query),\n },\n actions: {\n create: {\n commandId: 'terminal_tracking.terminal_config.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return terminalConfigCreateSchema.parse(scoped)\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'terminal_tracking.terminal_config.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return terminalConfigUpdateSchema.parse(scoped)\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 200,\n },\n delete: {\n commandId: 'terminal_tracking.terminal_config.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const body = (raw as any)?.body ?? raw ?? {}\n const scoped = withScopedPayload(body, ctx, translate)\n return { id: body.id, ...scoped }\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 200,\n },\n },\n})\n\nexport const openApi = createTerminalTrackingCrudOpenApi({\n resourceName: 'TerminalConfig',\n pluralName: 'TerminalConfigs',\n querySchema: terminalConfigListSchema,\n listResponseSchema: createPagedListResponseSchema(\n z.object({\n id: z.string().uuid(),\n terminalCode: z.string(),\n adapterType: z.string(),\n displayName: z.string(),\n isActive: z.boolean(),\n }),\n ),\n create: { schema: rawBodySchema, description: 'Creates a new terminal configuration.' },\n update: {\n schema: rawBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates an existing terminal configuration.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a terminal configuration.',\n },\n})\n\nexport const metadata = crud.metadata\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAC9B,SAAS,SAAS;AAClB,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AAEpC,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAI/C,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,yCAAyC,EAAE;AAAA,EACvF,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,2CAA2C,EAAE;AAAA,EAC1F,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,2CAA2C,EAAE;AAAA,EACzF,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,2CAA2C,EAAE;AAC9F;AAEA,MAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,eAAe,CAAC,UAA8C;AAClE,QAAM,UAAmC,EAAE,WAAW,KAAK;AAC3D,MAAI,MAAM,aAAc,SAAQ,eAAe,MAAM;AACrD,MAAI,MAAM,aAAa,QAAW;AAChC,UAAM,SAAS,kBAAkB,MAAM,QAAQ;AAC/C,QAAI,WAAW,KAAM,SAAQ,WAAW;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,cAAc;AAAA,MACZ,IAAI;AAAA,MACJ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA,IACA,cAAc,OAAO,UAAU,aAAa,KAAK;AAAA,EACnD;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,2BAA2B,MAAM,MAAM;AAAA,MAChD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,QAAQ,MAAM,KAAK;AAAA,MACpD,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,2BAA2B,MAAM,MAAM;AAAA,MAChD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,QAAQ,MAAM,KAAK;AAAA,MACpD,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,OAAQ,KAAa,QAAQ,OAAO,CAAC;AAC3C,cAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AACrD,eAAO,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO;AAAA,MAClC;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,QAAQ,MAAM,KAAK;AAAA,MACpD,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,UAAU,kCAAkC;AAAA,EACvD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,IAClB,EAAE,OAAO;AAAA,MACP,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,MACpB,cAAc,EAAE,OAAO;AAAA,MACvB,aAAa,EAAE,OAAO;AAAA,MACtB,aAAa,EAAE,OAAO;AAAA,MACtB,UAAU,EAAE,QAAQ;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EACA,QAAQ,EAAE,QAAQ,eAAe,aAAa,wCAAwC;AAAA,EACtF,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;AAEM,MAAM,WAAW,KAAK;AACtB,MAAM,MAAM,KAAK;AACjB,MAAM,OAAO,KAAK;AAClB,MAAM,MAAM,KAAK;AACjB,MAAM,SAAS,KAAK;",
6
+ "names": []
7
+ }
@@ -0,0 +1,58 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ const metadata = {
7
+ POST: { requireAuth: true, requireFeatures: ["terminal_tracking.terminal_configs.manage"] }
8
+ };
9
+ const testSchema = z.object({ id: z.string().uuid() });
10
+ async function POST(request) {
11
+ const auth = await getAuthFromRequest(request);
12
+ if (!auth || !auth.orgId || !auth.tenantId) {
13
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
14
+ }
15
+ let body;
16
+ try {
17
+ body = await request.json();
18
+ } catch {
19
+ return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
20
+ }
21
+ const parsed = testSchema.safeParse(body);
22
+ if (!parsed.success) {
23
+ return NextResponse.json({ error: "Invalid request", details: parsed.error.flatten() }, { status: 400 });
24
+ }
25
+ const container = await createRequestContainer();
26
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request });
27
+ const commandBus = container.resolve("commandBus");
28
+ const selectedOrgId = scope?.selectedId ?? auth.orgId;
29
+ const tenantId = auth.tenantId;
30
+ try {
31
+ const { result } = await commandBus.execute("terminal_tracking.terminal_config.test", {
32
+ input: { id: parsed.data.id, tenantId, organizationId: selectedOrgId },
33
+ ctx: {
34
+ container,
35
+ auth,
36
+ organizationScope: scope,
37
+ selectedOrganizationId: selectedOrgId ?? null,
38
+ organizationIds: scope?.filterIds ?? (selectedOrgId ? [selectedOrgId] : null),
39
+ request
40
+ },
41
+ metadata: {
42
+ tenantId: tenantId ?? null,
43
+ organizationId: selectedOrgId ?? null,
44
+ resourceKind: "terminal_tracking.terminal_config",
45
+ resourceId: parsed.data.id
46
+ }
47
+ });
48
+ return NextResponse.json(result);
49
+ } catch (error) {
50
+ const message = error instanceof Error ? error.message : "Failed to test terminal config";
51
+ return NextResponse.json({ error: message }, { status: 400 });
52
+ }
53
+ }
54
+ export {
55
+ POST,
56
+ metadata
57
+ };
58
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/terminal_tracking/api/terminal-configs/test/route.ts"],
4
+ "sourcesContent": ["import { NextRequest, NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['terminal_tracking.terminal_configs.manage'] },\n}\n\nconst testSchema = z.object({ id: z.string().uuid() })\n\nexport async function POST(request: NextRequest) {\n const auth = await getAuthFromRequest(request)\n if (!auth || !auth.orgId || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: unknown\n try {\n body = await request.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = testSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid request', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const commandBus = container.resolve('commandBus') as CommandBus\n const selectedOrgId = scope?.selectedId ?? auth.orgId\n const tenantId = auth.tenantId\n\n try {\n const { result } = await commandBus.execute<\n { id: string; tenantId: string; organizationId: string },\n { success: boolean; message: string; latencyMs?: number }\n >('terminal_tracking.terminal_config.test', {\n input: { id: parsed.data.id, tenantId, organizationId: selectedOrgId },\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: selectedOrgId ?? null,\n organizationIds: scope?.filterIds ?? (selectedOrgId ? [selectedOrgId] : null),\n request,\n },\n metadata: {\n tenantId: tenantId ?? null,\n organizationId: selectedOrgId ?? null,\n resourceKind: 'terminal_tracking.terminal_config',\n resourceId: parsed.data.id,\n },\n })\n return NextResponse.json(result)\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to test terminal config'\n return NextResponse.json({ error: message }, { status: 400 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAsB,oBAAoB;AAC1C,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAG5C,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,2CAA2C,EAAE;AAC5F;AAEA,MAAM,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAErD,eAAsB,KAAK,SAAsB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAC7C,MAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,UAAU;AAC1C,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,EAC5B,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1E;AAEA,QAAM,SAAS,WAAW,UAAU,IAAI;AACxC,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,QAAQ,CAAC;AACnF,QAAM,aAAa,UAAU,QAAQ,YAAY;AACjD,QAAM,gBAAgB,OAAO,cAAc,KAAK;AAChD,QAAM,WAAW,KAAK;AAEtB,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,WAAW,QAGlC,0CAA0C;AAAA,MAC1C,OAAO,EAAE,IAAI,OAAO,KAAK,IAAI,UAAU,gBAAgB,cAAc;AAAA,MACrE,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,QACnB,wBAAwB,iBAAiB;AAAA,QACzC,iBAAiB,OAAO,cAAc,gBAAgB,CAAC,aAAa,IAAI;AAAA,QACxE;AAAA,MACF;AAAA,MACA,UAAU;AAAA,QACR,UAAU,YAAY;AAAA,QACtB,gBAAgB,iBAAiB;AAAA,QACjC,cAAc;AAAA,QACd,YAAY,OAAO,KAAK;AAAA,MAC1B;AAAA,IACF,CAAC;AACD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9D;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,109 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
5
+ import { TerminalTrackingJob, TerminalEvent, TerminalVesselVisit } from "../../../data/entities.js";
6
+ const metadata = {
7
+ GET: { requireAuth: true, requireFeatures: ["terminal_tracking.tracking_jobs.view"] }
8
+ };
9
+ async function GET(request) {
10
+ const auth = await getAuthFromRequest(request);
11
+ if (!auth || !auth.orgId || !auth.tenantId) {
12
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
13
+ }
14
+ const id = new URL(request.url).searchParams.get("id");
15
+ if (!id) {
16
+ return NextResponse.json({ error: "Missing id" }, { status: 400 });
17
+ }
18
+ const container = await createRequestContainer();
19
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request });
20
+ const em = container.resolve("em");
21
+ const orgIds = scope?.filterIds ?? [auth.orgId];
22
+ const job = await em.findOne(TerminalTrackingJob, {
23
+ id,
24
+ tenantId: auth.tenantId,
25
+ organizationId: { $in: orgIds },
26
+ deletedAt: null
27
+ });
28
+ if (!job) {
29
+ return NextResponse.json({ error: "Not found" }, { status: 404 });
30
+ }
31
+ const events = await em.find(
32
+ TerminalEvent,
33
+ { job: job.id, tenantId: auth.tenantId },
34
+ { orderBy: { eventDateTime: "asc" } }
35
+ );
36
+ const visitRefs = [
37
+ ...new Set(
38
+ events.flatMap((e) => [e.visitRefIn, e.visitRefOut]).filter((r) => !!r)
39
+ )
40
+ ];
41
+ const visits = visitRefs.length ? await em.find(TerminalVesselVisit, {
42
+ tenantId: auth.tenantId,
43
+ organizationId: { $in: orgIds },
44
+ terminalCode: job.terminalCode,
45
+ visitRef: { $in: visitRefs }
46
+ }) : [];
47
+ const vesselVisits = Object.fromEntries(
48
+ visits.map((v) => [
49
+ v.visitRef,
50
+ {
51
+ visitRef: v.visitRef,
52
+ vesselName: v.vesselName ?? null,
53
+ ibVoyage: v.ibVoyage ?? null,
54
+ obVoyage: v.obVoyage ?? null,
55
+ line: v.line ?? null,
56
+ phase: v.phase ?? null,
57
+ eta: v.eta ? v.eta.toISOString() : null,
58
+ etd: v.etd ? v.etd.toISOString() : null,
59
+ ata: v.ata ? v.ata.toISOString() : null,
60
+ atd: v.atd ? v.atd.toISOString() : null,
61
+ beginReceive: v.beginReceive ? v.beginReceive.toISOString() : null,
62
+ dryCutoff: v.dryCutoff ? v.dryCutoff.toISOString() : null
63
+ }
64
+ ])
65
+ );
66
+ return NextResponse.json({
67
+ job: {
68
+ id: job.id,
69
+ terminalCode: job.terminalCode,
70
+ containerNumber: job.containerNumber,
71
+ status: job.status,
72
+ lastPollAt: job.lastPollAt ? job.lastPollAt.toISOString() : null,
73
+ nextPollAt: job.nextPollAt ? job.nextPollAt.toISOString() : null,
74
+ retryCount: job.retryCount,
75
+ errorHistory: job.errorHistory ?? [],
76
+ createdAt: job.createdAt ? job.createdAt.toISOString() : null
77
+ },
78
+ events: events.map((e) => ({
79
+ id: e.id,
80
+ ufvGkey: e.ufvGkey,
81
+ sourceEventId: e.sourceEventId,
82
+ eventType: e.eventType,
83
+ eventCode: e.eventCode,
84
+ eventClassifierCode: e.eventClassifierCode ?? null,
85
+ eventDateTime: e.eventDateTime ? e.eventDateTime.toISOString() : null,
86
+ transitState: e.transitState ?? null,
87
+ visitState: e.visitState ?? null,
88
+ facilityCode: e.facilityCode ?? null,
89
+ facilityCodeListProvider: e.facilityCodeListProvider ?? null,
90
+ unlocode: e.unlocode ?? null,
91
+ visitRefIn: e.visitRefIn ?? null,
92
+ visitRefOut: e.visitRefOut ?? null,
93
+ vesselName: e.vesselName ?? null,
94
+ voyageNumber: e.voyageNumber ?? null,
95
+ modeOfTransport: e.modeOfTransport ?? null,
96
+ seals: e.seals ?? null,
97
+ vgmWeightKg: e.vgmWeightKg ?? null,
98
+ impediments: e.impediments ?? null,
99
+ loadedAt: e.loadedAt ? e.loadedAt.toISOString() : null,
100
+ rawData: e.rawData ?? null
101
+ })),
102
+ vesselVisits
103
+ });
104
+ }
105
+ export {
106
+ GET,
107
+ metadata
108
+ };
109
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/terminal_tracking/api/tracking-jobs/details/route.ts"],
4
+ "sourcesContent": ["import { NextRequest, NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { TerminalTrackingJob, TerminalEvent, TerminalVesselVisit } from '../../../data/entities'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['terminal_tracking.tracking_jobs.view'] },\n}\n\nexport async function GET(request: NextRequest) {\n const auth = await getAuthFromRequest(request)\n if (!auth || !auth.orgId || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const id = new URL(request.url).searchParams.get('id')\n if (!id) {\n return NextResponse.json({ error: 'Missing id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const em = container.resolve('em') as EntityManager\n const orgIds = scope?.filterIds ?? [auth.orgId]\n\n const job = await em.findOne(TerminalTrackingJob, {\n id,\n tenantId: auth.tenantId,\n organizationId: { $in: orgIds },\n deletedAt: null,\n })\n if (!job) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n const events = await em.find(\n TerminalEvent,\n { job: job.id, tenantId: auth.tenantId },\n { orderBy: { eventDateTime: 'asc' } },\n )\n\n // Resolved vessel visits for this container's legs, keyed by visit ref.\n const visitRefs = [\n ...new Set(\n events.flatMap((e) => [e.visitRefIn, e.visitRefOut]).filter((r): r is string => !!r),\n ),\n ]\n const visits = visitRefs.length\n ? await em.find(TerminalVesselVisit, {\n tenantId: auth.tenantId,\n organizationId: { $in: orgIds },\n terminalCode: job.terminalCode,\n visitRef: { $in: visitRefs },\n })\n : []\n const vesselVisits = Object.fromEntries(\n visits.map((v) => [\n v.visitRef,\n {\n visitRef: v.visitRef,\n vesselName: v.vesselName ?? null,\n ibVoyage: v.ibVoyage ?? null,\n obVoyage: v.obVoyage ?? null,\n line: v.line ?? null,\n phase: v.phase ?? null,\n eta: v.eta ? v.eta.toISOString() : null,\n etd: v.etd ? v.etd.toISOString() : null,\n ata: v.ata ? v.ata.toISOString() : null,\n atd: v.atd ? v.atd.toISOString() : null,\n beginReceive: v.beginReceive ? v.beginReceive.toISOString() : null,\n dryCutoff: v.dryCutoff ? v.dryCutoff.toISOString() : null,\n },\n ]),\n )\n\n return NextResponse.json({\n job: {\n id: job.id,\n terminalCode: job.terminalCode,\n containerNumber: job.containerNumber,\n status: job.status,\n lastPollAt: job.lastPollAt ? job.lastPollAt.toISOString() : null,\n nextPollAt: job.nextPollAt ? job.nextPollAt.toISOString() : null,\n retryCount: job.retryCount,\n errorHistory: job.errorHistory ?? [],\n createdAt: job.createdAt ? job.createdAt.toISOString() : null,\n },\n events: events.map((e) => ({\n id: e.id,\n ufvGkey: e.ufvGkey,\n sourceEventId: e.sourceEventId,\n eventType: e.eventType,\n eventCode: e.eventCode,\n eventClassifierCode: e.eventClassifierCode ?? null,\n eventDateTime: e.eventDateTime ? e.eventDateTime.toISOString() : null,\n transitState: e.transitState ?? null,\n visitState: e.visitState ?? null,\n facilityCode: e.facilityCode ?? null,\n facilityCodeListProvider: e.facilityCodeListProvider ?? null,\n unlocode: e.unlocode ?? null,\n visitRefIn: e.visitRefIn ?? null,\n visitRefOut: e.visitRefOut ?? null,\n vesselName: e.vesselName ?? null,\n voyageNumber: e.voyageNumber ?? null,\n modeOfTransport: e.modeOfTransport ?? null,\n seals: e.seals ?? null,\n vgmWeightKg: e.vgmWeightKg ?? null,\n impediments: e.impediments ?? null,\n loadedAt: e.loadedAt ? e.loadedAt.toISOString() : null,\n rawData: e.rawData ?? null,\n })),\n vesselVisits,\n })\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,58 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ const metadata = {
7
+ POST: { requireAuth: true, requireFeatures: ["terminal_tracking.tracking_jobs.manage"] }
8
+ };
9
+ const pollSchema = z.object({ id: z.string().uuid() });
10
+ async function POST(request) {
11
+ const auth = await getAuthFromRequest(request);
12
+ if (!auth || !auth.orgId || !auth.tenantId) {
13
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
14
+ }
15
+ let body;
16
+ try {
17
+ body = await request.json();
18
+ } catch {
19
+ return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
20
+ }
21
+ const parsed = pollSchema.safeParse(body);
22
+ if (!parsed.success) {
23
+ return NextResponse.json({ error: "Invalid request", details: parsed.error.flatten() }, { status: 400 });
24
+ }
25
+ const container = await createRequestContainer();
26
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request });
27
+ const commandBus = container.resolve("commandBus");
28
+ const selectedOrgId = scope?.selectedId ?? auth.orgId;
29
+ const tenantId = auth.tenantId;
30
+ try {
31
+ const { result } = await commandBus.execute("terminal_tracking.tracking.poll_single", {
32
+ input: { jobId: parsed.data.id, tenantId, organizationId: selectedOrgId },
33
+ ctx: {
34
+ container,
35
+ auth,
36
+ organizationScope: scope,
37
+ selectedOrganizationId: selectedOrgId ?? null,
38
+ organizationIds: scope?.filterIds ?? (selectedOrgId ? [selectedOrgId] : null),
39
+ request
40
+ },
41
+ metadata: {
42
+ tenantId: tenantId ?? null,
43
+ organizationId: selectedOrgId ?? null,
44
+ resourceKind: "terminal_tracking.tracking_job",
45
+ resourceId: parsed.data.id
46
+ }
47
+ });
48
+ return NextResponse.json(result);
49
+ } catch (error) {
50
+ const message = error instanceof Error ? error.message : "Failed to poll terminal tracking job";
51
+ return NextResponse.json({ error: message }, { status: 400 });
52
+ }
53
+ }
54
+ export {
55
+ POST,
56
+ metadata
57
+ };
58
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/terminal_tracking/api/tracking-jobs/poll/route.ts"],
4
+ "sourcesContent": ["import { NextRequest, NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['terminal_tracking.tracking_jobs.manage'] },\n}\n\nconst pollSchema = z.object({ id: z.string().uuid() })\n\nexport async function POST(request: NextRequest) {\n const auth = await getAuthFromRequest(request)\n if (!auth || !auth.orgId || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: unknown\n try {\n body = await request.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = pollSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid request', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const commandBus = container.resolve('commandBus') as CommandBus\n const selectedOrgId = scope?.selectedId ?? auth.orgId\n const tenantId = auth.tenantId\n\n try {\n const { result } = await commandBus.execute<\n { jobId: string; tenantId: string; organizationId: string },\n { newEvents: number; updatedEvents: number }\n >('terminal_tracking.tracking.poll_single', {\n input: { jobId: parsed.data.id, tenantId, organizationId: selectedOrgId },\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: selectedOrgId ?? null,\n organizationIds: scope?.filterIds ?? (selectedOrgId ? [selectedOrgId] : null),\n request,\n },\n metadata: {\n tenantId: tenantId ?? null,\n organizationId: selectedOrgId ?? null,\n resourceKind: 'terminal_tracking.tracking_job',\n resourceId: parsed.data.id,\n },\n })\n return NextResponse.json(result)\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to poll terminal tracking job'\n return NextResponse.json({ error: message }, { status: 400 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAsB,oBAAoB;AAC1C,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAG5C,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,wCAAwC,EAAE;AACzF;AAEA,MAAM,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAErD,eAAsB,KAAK,SAAsB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAC7C,MAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,UAAU;AAC1C,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,EAC5B,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1E;AAEA,QAAM,SAAS,WAAW,UAAU,IAAI;AACxC,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,QAAQ,CAAC;AACnF,QAAM,aAAa,UAAU,QAAQ,YAAY;AACjD,QAAM,gBAAgB,OAAO,cAAc,KAAK;AAChD,QAAM,WAAW,KAAK;AAEtB,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,WAAW,QAGlC,0CAA0C;AAAA,MAC1C,OAAO,EAAE,OAAO,OAAO,KAAK,IAAI,UAAU,gBAAgB,cAAc;AAAA,MACxE,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,QACnB,wBAAwB,iBAAiB;AAAA,QACzC,iBAAiB,OAAO,cAAc,gBAAgB,CAAC,aAAa,IAAI;AAAA,QACxE;AAAA,MACF;AAAA,MACA,UAAU;AAAA,QACR,UAAU,YAAY;AAAA,QACtB,gBAAgB,iBAAiB;AAAA,QACjC,cAAc;AAAA,QACd,YAAY,OAAO,KAAK;AAAA,MAC1B;AAAA,IACF,CAAC;AACD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9D;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,189 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
+ import { z } from "zod";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
+ import { TerminalTrackingJob, TerminalEvent } from "../../data/entities.js";
8
+ import { trackingJobListSchema, trackingJobCreateSchema } from "../../data/validators.js";
9
+ import {
10
+ createTerminalTrackingCrudOpenApi,
11
+ createPagedListResponseSchema,
12
+ defaultOkResponseSchema
13
+ } from "../openapi.js";
14
+ import { withScopedPayload } from "../utils.js";
15
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
16
+ import { parseDynamicTableFilters } from "@freighttech/ui/backend/dynamic-table/server";
17
+ const rawBodySchema = z.object({}).passthrough();
18
+ const routeMetadata = {
19
+ GET: { requireAuth: true, requireFeatures: ["terminal_tracking.tracking_jobs.view"] },
20
+ POST: { requireAuth: true, requireFeatures: ["terminal_tracking.tracking_jobs.manage"] },
21
+ DELETE: { requireAuth: true, requireFeatures: ["terminal_tracking.tracking_jobs.manage"] }
22
+ };
23
+ const FILTER_FIELD_MAP = {
24
+ terminalCode: "terminalCode",
25
+ containerNumber: "containerNumber",
26
+ status: "status"
27
+ };
28
+ const buildFilters = (query) => {
29
+ const filters = { deletedAt: null };
30
+ if (query.terminalCode) filters.terminalCode = query.terminalCode;
31
+ if (query.containerNumber) filters.containerNumber = query.containerNumber;
32
+ if (query.status) filters.status = query.status;
33
+ if (query.filters) {
34
+ try {
35
+ const dyn = JSON.parse(query.filters);
36
+ const parsed = parseDynamicTableFilters(dyn, FILTER_FIELD_MAP);
37
+ if (parsed.length) filters.$and = [...filters.$and ?? [], ...parsed];
38
+ } catch {
39
+ }
40
+ }
41
+ return filters;
42
+ };
43
+ const ARRIVAL_CODES = /* @__PURE__ */ new Set(["GTIN", "DISC"]);
44
+ async function enrichWithMilestones(payload, ctx) {
45
+ const items = Array.isArray(payload?.items) ? payload.items : [];
46
+ const tenantId = ctx.auth?.tenantId;
47
+ if (!items.length || !tenantId) return;
48
+ const jobIds = items.map((i) => i?.id).filter((x) => typeof x === "string");
49
+ if (!jobIds.length) return;
50
+ const em = ctx.container.resolve("em");
51
+ const where = { job: { $in: jobIds }, tenantId };
52
+ if (ctx.organizationIds && ctx.organizationIds.length) where.organizationId = { $in: ctx.organizationIds };
53
+ const events = await em.find(TerminalEvent, where, { orderBy: { eventDateTime: "asc" } });
54
+ const byJob = /* @__PURE__ */ new Map();
55
+ for (const e of events) {
56
+ const jobId = String(e.job?.id ?? "");
57
+ if (!jobId) continue;
58
+ const arr = byJob.get(jobId);
59
+ if (arr) arr.push(e);
60
+ else byJob.set(jobId, [e]);
61
+ }
62
+ payload.items = items.map((item) => {
63
+ const evs = byJob.get(String(item.id)) ?? [];
64
+ const arrival = evs.find((e) => ARRIVAL_CODES.has(e.eventCode));
65
+ const departure = [...evs].reverse().find((e) => e.eventCode === "DEPA");
66
+ const latest = evs.length ? evs[evs.length - 1] : null;
67
+ const plain = JSON.parse(JSON.stringify(item));
68
+ plain.gateInAt = arrival?.eventDateTime ? arrival.eventDateTime.toISOString() : null;
69
+ plain.gateOutAt = departure?.eventDateTime ? departure.eventDateTime.toISOString() : null;
70
+ plain.holds = latest?.impediments && latest.impediments.length ? latest.impediments : [];
71
+ return plain;
72
+ });
73
+ }
74
+ const SORT_FIELD_MAP = {
75
+ id: "id",
76
+ terminalCode: "terminalCode",
77
+ containerNumber: "containerNumber",
78
+ status: "status",
79
+ createdAt: "createdAt",
80
+ lastPollAt: "lastPollAt"
81
+ };
82
+ async function listTrackingJobs(request) {
83
+ const auth = await getAuthFromRequest(request);
84
+ if (!auth || !auth.orgId || !auth.tenantId) {
85
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
86
+ }
87
+ const parsed = trackingJobListSchema.safeParse(
88
+ Object.fromEntries(new URL(request.url).searchParams)
89
+ );
90
+ if (!parsed.success) {
91
+ return NextResponse.json({ error: "Invalid query", details: parsed.error.flatten() }, { status: 400 });
92
+ }
93
+ const page = parsed.data.page ?? 1;
94
+ const pageSize = parsed.data.pageSize ?? parsed.data.limit ?? 50;
95
+ const container = await createRequestContainer();
96
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request });
97
+ const em = container.resolve("em");
98
+ const orgIds = scope?.filterIds ?? [auth.orgId];
99
+ const where = {
100
+ ...buildFilters(parsed.data),
101
+ tenantId: auth.tenantId,
102
+ organizationId: { $in: orgIds }
103
+ };
104
+ const sortField = SORT_FIELD_MAP[parsed.data.sortField ?? ""] ?? "createdAt";
105
+ const sortDir = parsed.data.sortDir ?? "desc";
106
+ const [items, total] = await em.findAndCount(TerminalTrackingJob, where, {
107
+ orderBy: { [sortField]: sortDir },
108
+ limit: pageSize,
109
+ offset: (page - 1) * pageSize
110
+ });
111
+ const payload = {
112
+ items,
113
+ total,
114
+ page,
115
+ pageSize,
116
+ totalPages: Math.ceil(total / pageSize)
117
+ };
118
+ await enrichWithMilestones(payload, { container, auth, organizationIds: orgIds });
119
+ return NextResponse.json(payload);
120
+ }
121
+ const crud = makeCrudRoute({
122
+ metadata: routeMetadata,
123
+ orm: {
124
+ entity: TerminalTrackingJob,
125
+ idField: "id",
126
+ orgField: "organizationId",
127
+ tenantField: "tenantId",
128
+ softDeleteField: "deletedAt"
129
+ },
130
+ actions: {
131
+ create: {
132
+ commandId: "terminal_tracking.tracking_job.create",
133
+ schema: rawBodySchema,
134
+ mapInput: async ({ raw, ctx }) => {
135
+ const { translate } = await resolveTranslations();
136
+ const scoped = withScopedPayload(raw ?? {}, ctx, translate);
137
+ return trackingJobCreateSchema.parse(scoped);
138
+ },
139
+ response: ({ result }) => ({ id: result?.id ?? null, newEvents: result?.newEvents ?? 0 }),
140
+ status: 201
141
+ },
142
+ delete: {
143
+ commandId: "terminal_tracking.tracking_job.delete",
144
+ schema: rawBodySchema,
145
+ mapInput: async ({ raw, ctx }) => {
146
+ const { translate } = await resolveTranslations();
147
+ const body = raw?.body ?? raw ?? {};
148
+ const scoped = withScopedPayload(body, ctx, translate);
149
+ return { id: body.id, ...scoped };
150
+ },
151
+ response: ({ result }) => ({ id: result?.id ?? null }),
152
+ status: 200
153
+ }
154
+ }
155
+ });
156
+ const openApi = createTerminalTrackingCrudOpenApi({
157
+ resourceName: "TerminalTrackingJob",
158
+ pluralName: "TerminalTrackingJobs",
159
+ querySchema: trackingJobListSchema,
160
+ listResponseSchema: createPagedListResponseSchema(
161
+ z.object({
162
+ id: z.string().uuid(),
163
+ terminalCode: z.string(),
164
+ containerNumber: z.string(),
165
+ status: z.string(),
166
+ gateInAt: z.string().datetime().nullable(),
167
+ gateOutAt: z.string().datetime().nullable(),
168
+ holds: z.array(z.string())
169
+ })
170
+ ),
171
+ create: { schema: rawBodySchema, description: "Creates a terminal tracking job for a container." },
172
+ del: {
173
+ schema: z.object({ id: z.string().uuid() }),
174
+ responseSchema: defaultOkResponseSchema,
175
+ description: "Deletes a terminal tracking job."
176
+ }
177
+ });
178
+ const metadata = routeMetadata;
179
+ const GET = listTrackingJobs;
180
+ const POST = crud.POST;
181
+ const DELETE = crud.DELETE;
182
+ export {
183
+ DELETE,
184
+ GET,
185
+ POST,
186
+ metadata,
187
+ openApi
188
+ };
189
+ //# sourceMappingURL=route.js.map