@open-mercato/core 0.6.8-develop.6925.1.cefa200fa4 → 0.6.8-develop.6930.1.1e5976efc3

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 (402) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/eudr_due_diligence_statement/index.js +47 -0
  3. package/dist/generated/entities/eudr_due_diligence_statement/index.js.map +7 -0
  4. package/dist/generated/entities/eudr_evidence_submission/index.js +51 -0
  5. package/dist/generated/entities/eudr_evidence_submission/index.js.map +7 -0
  6. package/dist/generated/entities/eudr_mitigation_action/index.js +31 -0
  7. package/dist/generated/entities/eudr_mitigation_action/index.js.map +7 -0
  8. package/dist/generated/entities/eudr_plot/index.js +39 -0
  9. package/dist/generated/entities/eudr_plot/index.js.map +7 -0
  10. package/dist/generated/entities/eudr_product_mapping/index.js +31 -0
  11. package/dist/generated/entities/eudr_product_mapping/index.js.map +7 -0
  12. package/dist/generated/entities/eudr_risk_assessment/index.js +35 -0
  13. package/dist/generated/entities/eudr_risk_assessment/index.js.map +7 -0
  14. package/dist/generated/entities.ids.generated.js +9 -0
  15. package/dist/generated/entities.ids.generated.js.map +2 -2
  16. package/dist/generated/entity-fields-registry.js +120 -0
  17. package/dist/generated/entity-fields-registry.js.map +2 -2
  18. package/dist/modules/attachments/fields/attachment.js +5 -1
  19. package/dist/modules/attachments/fields/attachment.js.map +2 -2
  20. package/dist/modules/attachments/lib/crud.js +3 -1
  21. package/dist/modules/attachments/lib/crud.js.map +2 -2
  22. package/dist/modules/eudr/acl.js +43 -0
  23. package/dist/modules/eudr/acl.js.map +7 -0
  24. package/dist/modules/eudr/ai-agents.js +205 -0
  25. package/dist/modules/eudr/ai-agents.js.map +7 -0
  26. package/dist/modules/eudr/ai-tools/compliance-pack.js +495 -0
  27. package/dist/modules/eudr/ai-tools/compliance-pack.js.map +7 -0
  28. package/dist/modules/eudr/ai-tools/types.js +10 -0
  29. package/dist/modules/eudr/ai-tools/types.js.map +7 -0
  30. package/dist/modules/eudr/ai-tools.js +10 -0
  31. package/dist/modules/eudr/ai-tools.js.map +7 -0
  32. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +386 -0
  33. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +7 -0
  34. package/dist/modules/eudr/api/evidence-submissions/route.js +318 -0
  35. package/dist/modules/eudr/api/evidence-submissions/route.js.map +7 -0
  36. package/dist/modules/eudr/api/mitigation-actions/route.js +249 -0
  37. package/dist/modules/eudr/api/mitigation-actions/route.js.map +7 -0
  38. package/dist/modules/eudr/api/openapi.js +27 -0
  39. package/dist/modules/eudr/api/openapi.js.map +7 -0
  40. package/dist/modules/eudr/api/plots/import/route.js +294 -0
  41. package/dist/modules/eudr/api/plots/import/route.js.map +7 -0
  42. package/dist/modules/eudr/api/plots/route.js +279 -0
  43. package/dist/modules/eudr/api/plots/route.js.map +7 -0
  44. package/dist/modules/eudr/api/product-mappings/route.js +257 -0
  45. package/dist/modules/eudr/api/product-mappings/route.js.map +7 -0
  46. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +298 -0
  47. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +7 -0
  48. package/dist/modules/eudr/api/product-mappings/suggestions/route.js +132 -0
  49. package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +7 -0
  50. package/dist/modules/eudr/api/reports/annual/route.js +266 -0
  51. package/dist/modules/eudr/api/reports/annual/route.js.map +7 -0
  52. package/dist/modules/eudr/api/risk-assessments/route.js +300 -0
  53. package/dist/modules/eudr/api/risk-assessments/route.js.map +7 -0
  54. package/dist/modules/eudr/api/statements/[id]/export/route.js +631 -0
  55. package/dist/modules/eudr/api/statements/[id]/export/route.js.map +7 -0
  56. package/dist/modules/eudr/api/statements/[id]/readiness/route.js +111 -0
  57. package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +7 -0
  58. package/dist/modules/eudr/api/statements/route.js +314 -0
  59. package/dist/modules/eudr/api/statements/route.js.map +7 -0
  60. package/dist/modules/eudr/api/suppliers/compliance/route.js +173 -0
  61. package/dist/modules/eudr/api/suppliers/compliance/route.js.map +7 -0
  62. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js +468 -0
  63. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js.map +7 -0
  64. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js +16 -0
  65. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js.map +7 -0
  66. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js +351 -0
  67. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js.map +7 -0
  68. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js +17 -0
  69. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js.map +7 -0
  70. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +313 -0
  71. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +7 -0
  72. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js +16 -0
  73. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js.map +7 -0
  74. package/dist/modules/eudr/backend/eudr/page.js +360 -0
  75. package/dist/modules/eudr/backend/eudr/page.js.map +7 -0
  76. package/dist/modules/eudr/backend/eudr/page.meta.js +16 -0
  77. package/dist/modules/eudr/backend/eudr/page.meta.js.map +7 -0
  78. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js +346 -0
  79. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js.map +7 -0
  80. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js +16 -0
  81. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js.map +7 -0
  82. package/dist/modules/eudr/backend/eudr/plots/create/page.js +253 -0
  83. package/dist/modules/eudr/backend/eudr/plots/create/page.js.map +7 -0
  84. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js +17 -0
  85. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js.map +7 -0
  86. package/dist/modules/eudr/backend/eudr/plots/page.js +386 -0
  87. package/dist/modules/eudr/backend/eudr/plots/page.js.map +7 -0
  88. package/dist/modules/eudr/backend/eudr/plots/page.meta.js +16 -0
  89. package/dist/modules/eudr/backend/eudr/plots/page.meta.js.map +7 -0
  90. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js +253 -0
  91. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js.map +7 -0
  92. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js +16 -0
  93. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js.map +7 -0
  94. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js +174 -0
  95. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js.map +7 -0
  96. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js +17 -0
  97. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js.map +7 -0
  98. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +306 -0
  99. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +7 -0
  100. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js +16 -0
  101. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js.map +7 -0
  102. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js +335 -0
  103. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js.map +7 -0
  104. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js +16 -0
  105. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js +220 -0
  107. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js.map +7 -0
  108. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js +17 -0
  109. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js.map +7 -0
  110. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +324 -0
  111. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +7 -0
  112. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js +16 -0
  113. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js.map +7 -0
  114. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js +648 -0
  115. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js.map +7 -0
  116. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js +16 -0
  117. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js.map +7 -0
  118. package/dist/modules/eudr/backend/eudr/statements/create/page.js +432 -0
  119. package/dist/modules/eudr/backend/eudr/statements/create/page.js.map +7 -0
  120. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js +17 -0
  121. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js.map +7 -0
  122. package/dist/modules/eudr/backend/eudr/statements/page.js +309 -0
  123. package/dist/modules/eudr/backend/eudr/statements/page.js.map +7 -0
  124. package/dist/modules/eudr/backend/eudr/statements/page.meta.js +16 -0
  125. package/dist/modules/eudr/backend/eudr/statements/page.meta.js.map +7 -0
  126. package/dist/modules/eudr/ce.js +56 -0
  127. package/dist/modules/eudr/ce.js.map +7 -0
  128. package/dist/modules/eudr/commands/evidence-submissions.js +571 -0
  129. package/dist/modules/eudr/commands/evidence-submissions.js.map +7 -0
  130. package/dist/modules/eudr/commands/index.js +7 -0
  131. package/dist/modules/eudr/commands/index.js.map +7 -0
  132. package/dist/modules/eudr/commands/lifecycle-events.js +19 -0
  133. package/dist/modules/eudr/commands/lifecycle-events.js.map +7 -0
  134. package/dist/modules/eudr/commands/mitigation-actions.js +488 -0
  135. package/dist/modules/eudr/commands/mitigation-actions.js.map +7 -0
  136. package/dist/modules/eudr/commands/plots.js +517 -0
  137. package/dist/modules/eudr/commands/plots.js.map +7 -0
  138. package/dist/modules/eudr/commands/product-mappings.js +468 -0
  139. package/dist/modules/eudr/commands/product-mappings.js.map +7 -0
  140. package/dist/modules/eudr/commands/risk-assessments.js +599 -0
  141. package/dist/modules/eudr/commands/risk-assessments.js.map +7 -0
  142. package/dist/modules/eudr/commands/statements.js +747 -0
  143. package/dist/modules/eudr/commands/statements.js.map +7 -0
  144. package/dist/modules/eudr/components/CountrySelectField.js +64 -0
  145. package/dist/modules/eudr/components/CountrySelectField.js.map +7 -0
  146. package/dist/modules/eudr/components/GeometryInput.js +133 -0
  147. package/dist/modules/eudr/components/GeometryInput.js.map +7 -0
  148. package/dist/modules/eudr/components/MappingSuggestionsDialog.js +307 -0
  149. package/dist/modules/eudr/components/MappingSuggestionsDialog.js.map +7 -0
  150. package/dist/modules/eudr/components/MitigationActionsSection.js +345 -0
  151. package/dist/modules/eudr/components/MitigationActionsSection.js.map +7 -0
  152. package/dist/modules/eudr/components/OrderSelectField.js +88 -0
  153. package/dist/modules/eudr/components/OrderSelectField.js.map +7 -0
  154. package/dist/modules/eudr/components/PlotImportDialog.js +229 -0
  155. package/dist/modules/eudr/components/PlotImportDialog.js.map +7 -0
  156. package/dist/modules/eudr/components/PlotMapPreview.js +35 -0
  157. package/dist/modules/eudr/components/PlotMapPreview.js.map +7 -0
  158. package/dist/modules/eudr/components/PlotMapPreviewImpl.js +97 -0
  159. package/dist/modules/eudr/components/PlotMapPreviewImpl.js.map +7 -0
  160. package/dist/modules/eudr/components/PlotMultiSelectField.js +198 -0
  161. package/dist/modules/eudr/components/PlotMultiSelectField.js.map +7 -0
  162. package/dist/modules/eudr/components/RiskCriteriaField.js +128 -0
  163. package/dist/modules/eudr/components/RiskCriteriaField.js.map +7 -0
  164. package/dist/modules/eudr/components/StatementLifecycleBar.js +274 -0
  165. package/dist/modules/eudr/components/StatementLifecycleBar.js.map +7 -0
  166. package/dist/modules/eudr/components/StatementReadinessChecklist.js +56 -0
  167. package/dist/modules/eudr/components/StatementReadinessChecklist.js.map +7 -0
  168. package/dist/modules/eudr/components/StatementRiskSection.js +179 -0
  169. package/dist/modules/eudr/components/StatementRiskSection.js.map +7 -0
  170. package/dist/modules/eudr/components/crudErrorI18n.js +31 -0
  171. package/dist/modules/eudr/components/crudErrorI18n.js.map +7 -0
  172. package/dist/modules/eudr/components/formConfig.js +593 -0
  173. package/dist/modules/eudr/components/formConfig.js.map +7 -0
  174. package/dist/modules/eudr/components/plotForm.js +68 -0
  175. package/dist/modules/eudr/components/plotForm.js.map +7 -0
  176. package/dist/modules/eudr/data/enrichers.js +62 -0
  177. package/dist/modules/eudr/data/enrichers.js.map +7 -0
  178. package/dist/modules/eudr/data/entities.js +437 -0
  179. package/dist/modules/eudr/data/entities.js.map +7 -0
  180. package/dist/modules/eudr/data/validators.js +271 -0
  181. package/dist/modules/eudr/data/validators.js.map +7 -0
  182. package/dist/modules/eudr/encryption.js +33 -0
  183. package/dist/modules/eudr/encryption.js.map +7 -0
  184. package/dist/modules/eudr/events.js +38 -0
  185. package/dist/modules/eudr/events.js.map +7 -0
  186. package/dist/modules/eudr/index.js +15 -0
  187. package/dist/modules/eudr/index.js.map +7 -0
  188. package/dist/modules/eudr/lib/annual-report.js +104 -0
  189. package/dist/modules/eudr/lib/annual-report.js.map +7 -0
  190. package/dist/modules/eudr/lib/completeness.js +64 -0
  191. package/dist/modules/eudr/lib/completeness.js.map +7 -0
  192. package/dist/modules/eudr/lib/detail-read-scope.js +16 -0
  193. package/dist/modules/eudr/lib/detail-read-scope.js.map +7 -0
  194. package/dist/modules/eudr/lib/geometry.js +142 -0
  195. package/dist/modules/eudr/lib/geometry.js.map +7 -0
  196. package/dist/modules/eudr/lib/notifications.js +56 -0
  197. package/dist/modules/eudr/lib/notifications.js.map +7 -0
  198. package/dist/modules/eudr/lib/plural.js +26 -0
  199. package/dist/modules/eudr/lib/plural.js.map +7 -0
  200. package/dist/modules/eudr/lib/reference-data.js +240 -0
  201. package/dist/modules/eudr/lib/reference-data.js.map +7 -0
  202. package/dist/modules/eudr/lib/species.js +10 -0
  203. package/dist/modules/eudr/lib/species.js.map +7 -0
  204. package/dist/modules/eudr/lib/statement-lifecycle.js +84 -0
  205. package/dist/modules/eudr/lib/statement-lifecycle.js.map +7 -0
  206. package/dist/modules/eudr/lib/statement-seeding.js +49 -0
  207. package/dist/modules/eudr/lib/statement-seeding.js.map +7 -0
  208. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js +15 -0
  209. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js.map +7 -0
  210. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js +24 -0
  211. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js.map +7 -0
  212. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js +15 -0
  213. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js.map +7 -0
  214. package/dist/modules/eudr/notifications.js +58 -0
  215. package/dist/modules/eudr/notifications.js.map +7 -0
  216. package/dist/modules/eudr/search.js +186 -0
  217. package/dist/modules/eudr/search.js.map +7 -0
  218. package/dist/modules/eudr/setup.js +12 -0
  219. package/dist/modules/eudr/setup.js.map +7 -0
  220. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js +34 -0
  221. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js.map +7 -0
  222. package/dist/modules/eudr/subscribers/notify-risk-concluded.js +35 -0
  223. package/dist/modules/eudr/subscribers/notify-risk-concluded.js.map +7 -0
  224. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js +37 -0
  225. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js.map +7 -0
  226. package/dist/modules/eudr/subscribers/notify-statement-submitted.js +34 -0
  227. package/dist/modules/eudr/subscribers/notify-statement-submitted.js.map +7 -0
  228. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js +34 -0
  229. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js.map +7 -0
  230. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js +124 -0
  231. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js.map +7 -0
  232. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js +22 -0
  233. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js.map +7 -0
  234. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js +203 -0
  235. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js.map +7 -0
  236. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js +23 -0
  237. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js.map +7 -0
  238. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js +19 -0
  239. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js.map +7 -0
  240. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js +31 -0
  241. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js.map +7 -0
  242. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js +100 -0
  243. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js.map +7 -0
  244. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js +17 -0
  245. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js.map +7 -0
  246. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js +116 -0
  247. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js.map +7 -0
  248. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js +17 -0
  249. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js.map +7 -0
  250. package/dist/modules/eudr/widgets/injection-table.js +32 -0
  251. package/dist/modules/eudr/widgets/injection-table.js.map +7 -0
  252. package/dist/modules/notifications/lib/notificationService.js +67 -3
  253. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  254. package/dist/modules/query_index/lib/engine.js +121 -48
  255. package/dist/modules/query_index/lib/engine.js.map +2 -2
  256. package/dist/modules/sales/api/documents/factory.js +17 -0
  257. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  258. package/dist/modules/sales/commands/documents.js +32 -18
  259. package/dist/modules/sales/commands/documents.js.map +2 -2
  260. package/dist/modules/sales/data/entities.js +1 -0
  261. package/dist/modules/sales/data/entities.js.map +2 -2
  262. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js +17 -0
  263. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js.map +7 -0
  264. package/generated/entities/eudr_due_diligence_statement/index.ts +22 -0
  265. package/generated/entities/eudr_evidence_submission/index.ts +24 -0
  266. package/generated/entities/eudr_mitigation_action/index.ts +14 -0
  267. package/generated/entities/eudr_plot/index.ts +18 -0
  268. package/generated/entities/eudr_product_mapping/index.ts +14 -0
  269. package/generated/entities/eudr_risk_assessment/index.ts +16 -0
  270. package/generated/entities.ids.generated.ts +9 -0
  271. package/generated/entity-fields-registry.ts +120 -0
  272. package/package.json +7 -7
  273. package/src/modules/attachments/fields/attachment.tsx +5 -0
  274. package/src/modules/attachments/lib/crud.ts +2 -0
  275. package/src/modules/eudr/acl.ts +39 -0
  276. package/src/modules/eudr/ai-agents.ts +258 -0
  277. package/src/modules/eudr/ai-tools/compliance-pack.ts +618 -0
  278. package/src/modules/eudr/ai-tools/types.ts +44 -0
  279. package/src/modules/eudr/ai-tools.ts +15 -0
  280. package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +481 -0
  281. package/src/modules/eudr/api/evidence-submissions/route.ts +335 -0
  282. package/src/modules/eudr/api/mitigation-actions/route.ts +263 -0
  283. package/src/modules/eudr/api/openapi.ts +28 -0
  284. package/src/modules/eudr/api/plots/import/route.ts +369 -0
  285. package/src/modules/eudr/api/plots/route.ts +296 -0
  286. package/src/modules/eudr/api/product-mappings/route.ts +269 -0
  287. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +382 -0
  288. package/src/modules/eudr/api/product-mappings/suggestions/route.ts +169 -0
  289. package/src/modules/eudr/api/reports/annual/route.ts +457 -0
  290. package/src/modules/eudr/api/risk-assessments/route.ts +323 -0
  291. package/src/modules/eudr/api/statements/[id]/export/route.ts +713 -0
  292. package/src/modules/eudr/api/statements/[id]/readiness/route.ts +129 -0
  293. package/src/modules/eudr/api/statements/route.ts +329 -0
  294. package/src/modules/eudr/api/suppliers/compliance/route.ts +241 -0
  295. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.ts +12 -0
  296. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.tsx +545 -0
  297. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.ts +13 -0
  298. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.tsx +383 -0
  299. package/src/modules/eudr/backend/eudr/evidence-submissions/page.meta.ts +12 -0
  300. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +379 -0
  301. package/src/modules/eudr/backend/eudr/page.meta.ts +12 -0
  302. package/src/modules/eudr/backend/eudr/page.tsx +477 -0
  303. package/src/modules/eudr/backend/eudr/plots/[id]/page.meta.ts +12 -0
  304. package/src/modules/eudr/backend/eudr/plots/[id]/page.tsx +404 -0
  305. package/src/modules/eudr/backend/eudr/plots/create/page.meta.ts +13 -0
  306. package/src/modules/eudr/backend/eudr/plots/create/page.tsx +267 -0
  307. package/src/modules/eudr/backend/eudr/plots/page.meta.ts +12 -0
  308. package/src/modules/eudr/backend/eudr/plots/page.tsx +442 -0
  309. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.ts +12 -0
  310. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.tsx +311 -0
  311. package/src/modules/eudr/backend/eudr/product-mappings/create/page.meta.ts +13 -0
  312. package/src/modules/eudr/backend/eudr/product-mappings/create/page.tsx +190 -0
  313. package/src/modules/eudr/backend/eudr/product-mappings/page.meta.ts +12 -0
  314. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +344 -0
  315. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.ts +12 -0
  316. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.tsx +421 -0
  317. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.meta.ts +13 -0
  318. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.tsx +247 -0
  319. package/src/modules/eudr/backend/eudr/risk-assessments/page.meta.ts +12 -0
  320. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +383 -0
  321. package/src/modules/eudr/backend/eudr/statements/[id]/page.meta.ts +12 -0
  322. package/src/modules/eudr/backend/eudr/statements/[id]/page.tsx +777 -0
  323. package/src/modules/eudr/backend/eudr/statements/create/page.meta.ts +13 -0
  324. package/src/modules/eudr/backend/eudr/statements/create/page.tsx +511 -0
  325. package/src/modules/eudr/backend/eudr/statements/page.meta.ts +12 -0
  326. package/src/modules/eudr/backend/eudr/statements/page.tsx +372 -0
  327. package/src/modules/eudr/ce.ts +52 -0
  328. package/src/modules/eudr/commands/evidence-submissions.ts +699 -0
  329. package/src/modules/eudr/commands/index.ts +6 -0
  330. package/src/modules/eudr/commands/lifecycle-events.ts +28 -0
  331. package/src/modules/eudr/commands/mitigation-actions.ts +574 -0
  332. package/src/modules/eudr/commands/plots.ts +627 -0
  333. package/src/modules/eudr/commands/product-mappings.ts +556 -0
  334. package/src/modules/eudr/commands/risk-assessments.ts +728 -0
  335. package/src/modules/eudr/commands/statements.ts +933 -0
  336. package/src/modules/eudr/components/CountrySelectField.tsx +77 -0
  337. package/src/modules/eudr/components/GeometryInput.tsx +170 -0
  338. package/src/modules/eudr/components/MappingSuggestionsDialog.tsx +385 -0
  339. package/src/modules/eudr/components/MitigationActionsSection.tsx +417 -0
  340. package/src/modules/eudr/components/OrderSelectField.tsx +116 -0
  341. package/src/modules/eudr/components/PlotImportDialog.tsx +277 -0
  342. package/src/modules/eudr/components/PlotMapPreview.tsx +46 -0
  343. package/src/modules/eudr/components/PlotMapPreviewImpl.tsx +111 -0
  344. package/src/modules/eudr/components/PlotMultiSelectField.tsx +238 -0
  345. package/src/modules/eudr/components/RiskCriteriaField.tsx +173 -0
  346. package/src/modules/eudr/components/StatementLifecycleBar.tsx +363 -0
  347. package/src/modules/eudr/components/StatementReadinessChecklist.tsx +82 -0
  348. package/src/modules/eudr/components/StatementRiskSection.tsx +242 -0
  349. package/src/modules/eudr/components/crudErrorI18n.ts +36 -0
  350. package/src/modules/eudr/components/formConfig.tsx +717 -0
  351. package/src/modules/eudr/components/plotForm.ts +75 -0
  352. package/src/modules/eudr/data/enrichers.ts +82 -0
  353. package/src/modules/eudr/data/entities.ts +369 -0
  354. package/src/modules/eudr/data/validators.ts +357 -0
  355. package/src/modules/eudr/encryption.ts +31 -0
  356. package/src/modules/eudr/events.ts +38 -0
  357. package/src/modules/eudr/i18n/de.json +747 -0
  358. package/src/modules/eudr/i18n/en.json +747 -0
  359. package/src/modules/eudr/i18n/es.json +747 -0
  360. package/src/modules/eudr/i18n/ko.json +747 -0
  361. package/src/modules/eudr/i18n/pl.json +747 -0
  362. package/src/modules/eudr/index.ts +13 -0
  363. package/src/modules/eudr/lib/annual-report.ts +120 -0
  364. package/src/modules/eudr/lib/completeness.ts +90 -0
  365. package/src/modules/eudr/lib/detail-read-scope.ts +44 -0
  366. package/src/modules/eudr/lib/geometry.ts +191 -0
  367. package/src/modules/eudr/lib/notifications.ts +85 -0
  368. package/src/modules/eudr/lib/plural.ts +36 -0
  369. package/src/modules/eudr/lib/reference-data.ts +128 -0
  370. package/src/modules/eudr/lib/species.ts +12 -0
  371. package/src/modules/eudr/lib/statement-lifecycle.ts +109 -0
  372. package/src/modules/eudr/lib/statement-seeding.ts +86 -0
  373. package/src/modules/eudr/migrations/.snapshot-open-mercato.json +2046 -0
  374. package/src/modules/eudr/migrations/Migration20260706102002_eudr.ts +16 -0
  375. package/src/modules/eudr/migrations/Migration20260706152612_eudr.ts +30 -0
  376. package/src/modules/eudr/migrations/Migration20260722100000_eudr.ts +17 -0
  377. package/src/modules/eudr/notifications.ts +56 -0
  378. package/src/modules/eudr/search.ts +212 -0
  379. package/src/modules/eudr/setup.ts +10 -0
  380. package/src/modules/eudr/subscribers/notify-mitigation-completed.ts +56 -0
  381. package/src/modules/eudr/subscribers/notify-risk-concluded.ts +58 -0
  382. package/src/modules/eudr/subscribers/notify-statement-reference-issued.ts +59 -0
  383. package/src/modules/eudr/subscribers/notify-statement-submitted.ts +49 -0
  384. package/src/modules/eudr/subscribers/notify-statement-withdrawn.ts +49 -0
  385. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-created.ts +189 -0
  386. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.ts +31 -0
  387. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.client.tsx +271 -0
  388. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.ts +24 -0
  389. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.client.tsx +37 -0
  390. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.ts +30 -0
  391. package/src/modules/eudr/widgets/injection/order-compliance/widget.client.tsx +131 -0
  392. package/src/modules/eudr/widgets/injection/order-compliance/widget.ts +16 -0
  393. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.client.tsx +168 -0
  394. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.ts +16 -0
  395. package/src/modules/eudr/widgets/injection-table.ts +31 -0
  396. package/src/modules/notifications/lib/notificationService.ts +83 -4
  397. package/src/modules/query_index/lib/engine.ts +139 -51
  398. package/src/modules/sales/api/documents/factory.ts +29 -0
  399. package/src/modules/sales/commands/documents.ts +65 -18
  400. package/src/modules/sales/data/entities.ts +1 -0
  401. package/src/modules/sales/migrations/.snapshot-open-mercato.json +10 -0
  402. package/src/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.ts +59 -0
@@ -0,0 +1,717 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
6
+ import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
7
+ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
8
+ import { LookupSelect } from '@open-mercato/ui/backend/inputs'
9
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
10
+ import { Input } from '@open-mercato/ui/primitives/input'
11
+ import type { StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'
12
+ import { Plus, Trash2 } from 'lucide-react'
13
+ import {
14
+ EUDR_ACTIVITY_TYPES,
15
+ EUDR_ACTOR_ROLES,
16
+ EUDR_COMMODITIES,
17
+ EUDR_STATEMENT_STATUSES,
18
+ EUDR_SUBMISSION_STATUSES,
19
+ GEOJSON_TYPES,
20
+ type EudrActivityType,
21
+ type EudrActorRole,
22
+ type EudrCommodity,
23
+ type EudrStatementStatus,
24
+ type EudrSubmissionStatus,
25
+ } from '../data/validators'
26
+
27
+ export type Translator = (
28
+ key: string,
29
+ fallbackOrParams?: string | Record<string, string | number>,
30
+ params?: Record<string, string | number>,
31
+ ) => string
32
+
33
+ export type ProductSnapshot = {
34
+ name?: string
35
+ sku?: string
36
+ }
37
+
38
+ export type CompanySnapshot = {
39
+ displayName?: string
40
+ }
41
+
42
+ export type OrderSnapshot = {
43
+ orderNumber?: string | null
44
+ }
45
+
46
+ export type ReferencedStatementValue = {
47
+ referenceNumber: string
48
+ verificationNumber?: string
49
+ }
50
+
51
+ export type PickerOption<Snapshot extends Record<string, unknown>> = {
52
+ value: string
53
+ label: string
54
+ subtitle?: string | null
55
+ snapshot?: Snapshot | null
56
+ unavailable?: boolean
57
+ }
58
+
59
+ type PickerPayload = {
60
+ items?: unknown[]
61
+ }
62
+
63
+ type LookupSelectFieldProps<Snapshot extends Record<string, unknown>> = {
64
+ id: string
65
+ value?: string | null
66
+ onChange: (value: string | undefined) => void
67
+ onSnapshot?: (snapshot: Snapshot | null) => void
68
+ placeholder: string
69
+ emptyLabel?: string
70
+ loadError: string
71
+ disabled?: boolean
72
+ loadOptions: (search: string) => Promise<PickerOption<Snapshot>[]>
73
+ loadSelectedOption?: (id: string) => Promise<PickerOption<Snapshot> | null>
74
+ }
75
+
76
+ const GEOJSON_SIZE_LIMIT = 1_048_576
77
+ export const PICKER_PAGE_SIZE = 20
78
+
79
+ export function commodityOptions(translate: Translator): Array<{ value: EudrCommodity; label: string }> {
80
+ return EUDR_COMMODITIES.map((commodity) => ({
81
+ value: commodity,
82
+ label: translate(`eudr.commodity.${commodity}`),
83
+ }))
84
+ }
85
+
86
+ export function submissionStatusOptions(translate: Translator): Array<{ value: EudrSubmissionStatus; label: string }> {
87
+ return EUDR_SUBMISSION_STATUSES.map((status) => ({
88
+ value: status,
89
+ label: translate(`eudr.submissionStatus.${status}`),
90
+ }))
91
+ }
92
+
93
+ export function statementStatusOptions(translate: Translator): Array<{ value: EudrStatementStatus; label: string }> {
94
+ return EUDR_STATEMENT_STATUSES.map((status) => ({
95
+ value: status,
96
+ label: translate(`eudr.statementStatus.${status}`),
97
+ }))
98
+ }
99
+
100
+ export function activityTypeOptions(translate: Translator): Array<{ value: EudrActivityType; label: string }> {
101
+ return EUDR_ACTIVITY_TYPES.map((activityType) => ({
102
+ value: activityType,
103
+ label: translate(`eudr.activityType.${activityType}`),
104
+ }))
105
+ }
106
+
107
+ export function actorRoleOptions(translate: Translator): Array<{ value: EudrActorRole; label: string }> {
108
+ return EUDR_ACTOR_ROLES.map((actorRole) => ({
109
+ value: actorRole,
110
+ label: translate(`eudr.actorRole.${actorRole}`),
111
+ }))
112
+ }
113
+
114
+ export function statusBadgeVariant(
115
+ status: EudrSubmissionStatus | EudrStatementStatus | string | null | undefined,
116
+ ): StatusBadgeVariant {
117
+ if (status === 'submitted') return 'info'
118
+ if (status === 'verified' || status === 'available') return 'success'
119
+ if (status === 'rejected') return 'error'
120
+ if (status === 'withdrawn') return 'warning'
121
+ return 'neutral'
122
+ }
123
+
124
+ function isRecord(value: unknown): value is Record<string, unknown> {
125
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
126
+ }
127
+
128
+ function readString(record: Record<string, unknown>, keys: string[]): string | null {
129
+ for (const key of keys) {
130
+ const value = record[key]
131
+ if (typeof value !== 'string') continue
132
+ const trimmed = value.trim()
133
+ if (trimmed.length > 0) return trimmed
134
+ }
135
+ return null
136
+ }
137
+
138
+ function normalizeProductOption(raw: unknown): PickerOption<ProductSnapshot> | null {
139
+ if (!isRecord(raw)) return null
140
+ const id = readString(raw, ['id'])
141
+ if (!id) return null
142
+ const title = readString(raw, ['title'])
143
+ const sku = readString(raw, ['sku'])
144
+ if (!title && !sku) return null
145
+ const snapshot = {
146
+ ...(title ? { name: title } : {}),
147
+ ...(sku ? { sku } : {}),
148
+ }
149
+ return { value: id, label: title ?? sku ?? '', subtitle: title ? sku : null, snapshot }
150
+ }
151
+
152
+ function normalizeCompanyOption(raw: unknown): PickerOption<CompanySnapshot> | null {
153
+ if (!isRecord(raw)) return null
154
+ const id = readString(raw, ['id'])
155
+ if (!id) return null
156
+ const displayName = readString(raw, ['display_name', 'displayName', 'name'])
157
+ if (!displayName) return null
158
+ return {
159
+ value: id,
160
+ label: displayName,
161
+ snapshot: { displayName },
162
+ }
163
+ }
164
+
165
+ function normalizeMappingOption(raw: unknown, translate: Translator): PickerOption<Record<string, unknown>> | null {
166
+ if (!isRecord(raw)) return null
167
+ const id = readString(raw, ['id'])
168
+ if (!id) return null
169
+ const productSnapshot = isRecord(raw.productSnapshot) ? raw.productSnapshot : null
170
+ const productName = productSnapshot ? readString(productSnapshot, ['name']) : null
171
+ const productSku = productSnapshot ? readString(productSnapshot, ['sku']) : null
172
+ const commodity = readString(raw, ['commodity'])
173
+ const hsCode = readString(raw, ['hsCode'])
174
+ const label = productName ?? productSku ?? translate('eudr.common.recordUnavailable')
175
+ const subtitleSegments = [
176
+ productName ? productSku : null,
177
+ commodity ? translate(`eudr.commodity.${commodity}`) : null,
178
+ hsCode,
179
+ ].filter((segment): segment is string => typeof segment === 'string' && segment.length > 0)
180
+ return { value: id, label, subtitle: subtitleSegments.length ? subtitleSegments.join(' - ') : null }
181
+ }
182
+
183
+ function normalizeStatementOption(raw: unknown, translate: Translator): PickerOption<Record<string, unknown>> | null {
184
+ if (!isRecord(raw)) return null
185
+ const id = readString(raw, ['id'])
186
+ if (!id) return null
187
+ const title = readString(raw, ['title'])
188
+ const referenceNumber = readString(raw, ['referenceNumber'])
189
+ const label = title ?? referenceNumber ?? translate('eudr.common.recordUnavailable')
190
+ return { value: id, label, subtitle: title ? referenceNumber : null }
191
+ }
192
+
193
+ export function LookupSelectField<Snapshot extends Record<string, unknown>>({
194
+ id,
195
+ value,
196
+ onChange,
197
+ onSnapshot,
198
+ placeholder,
199
+ loadError,
200
+ disabled,
201
+ loadOptions,
202
+ loadSelectedOption,
203
+ }: LookupSelectFieldProps<Snapshot>) {
204
+ const translate = useT()
205
+ const selectedValue = typeof value === 'string' && value.length > 0 ? value : null
206
+ const [selectedOption, setSelectedOption] = React.useState<PickerOption<Snapshot> | null>(null)
207
+ const knownOptionsRef = React.useRef<Map<string, PickerOption<Snapshot>>>(new Map())
208
+
209
+ React.useEffect(() => {
210
+ if (!selectedValue) {
211
+ setSelectedOption(null)
212
+ return
213
+ }
214
+ if (selectedOption?.value === selectedValue) return
215
+ const known = knownOptionsRef.current.get(selectedValue)
216
+ if (known) {
217
+ setSelectedOption(known)
218
+ return
219
+ }
220
+ let cancelled = false
221
+ const markUnavailable = () => {
222
+ if (!cancelled) {
223
+ setSelectedOption({
224
+ value: selectedValue,
225
+ label: translate('eudr.common.recordUnavailable'),
226
+ unavailable: true,
227
+ })
228
+ }
229
+ }
230
+ if (!loadSelectedOption) {
231
+ markUnavailable()
232
+ return
233
+ }
234
+ loadSelectedOption(selectedValue)
235
+ .then((resolved) => {
236
+ if (cancelled) return
237
+ if (resolved) {
238
+ knownOptionsRef.current.set(resolved.value, resolved)
239
+ setSelectedOption(resolved)
240
+ } else {
241
+ markUnavailable()
242
+ }
243
+ })
244
+ .catch(markUnavailable)
245
+ return () => {
246
+ cancelled = true
247
+ }
248
+ }, [loadSelectedOption, selectedOption, selectedValue, translate])
249
+
250
+ React.useEffect(() => {
251
+ if (!onSnapshot || !selectedOption || selectedOption.unavailable) return
252
+ onSnapshot(selectedOption.snapshot ?? null)
253
+ }, [onSnapshot, selectedOption])
254
+
255
+ const selectedOptionRef = React.useRef(selectedOption)
256
+ React.useEffect(() => {
257
+ selectedOptionRef.current = selectedOption
258
+ }, [selectedOption])
259
+
260
+ const setQueryRef = React.useRef<((value: string) => void) | null>(null)
261
+
262
+ const fetchItems = React.useCallback(async (query: string) => {
263
+ let options: Array<PickerOption<Snapshot>> = []
264
+ try {
265
+ options = await loadOptions(query)
266
+ } catch (error) {
267
+ const message = error instanceof Error && error.message ? error.message : loadError
268
+ flash(message, 'error')
269
+ options = []
270
+ }
271
+ for (const option of options) knownOptionsRef.current.set(option.value, option)
272
+ const selected = selectedOptionRef.current
273
+ const merged = selected && !options.some((option) => option.value === selected.value)
274
+ ? [selected, ...options]
275
+ : options
276
+ return merged.map((option) => ({
277
+ id: option.value,
278
+ title: option.label,
279
+ subtitle: option.subtitle ?? undefined,
280
+ }))
281
+ }, [loadError, loadOptions])
282
+
283
+ return (
284
+ <div id={id} className="space-y-2">
285
+ {selectedValue ? (
286
+ <p className="text-sm text-foreground">
287
+ <span className="font-medium">{selectedOption?.label ?? ''}</span>
288
+ {selectedOption?.subtitle ? (
289
+ <span className="text-muted-foreground"> — {selectedOption.subtitle}</span>
290
+ ) : null}
291
+ </p>
292
+ ) : null}
293
+ <LookupSelect
294
+ value={selectedValue}
295
+ disabled={disabled}
296
+ searchPlaceholder={placeholder}
297
+ fetchItems={fetchItems}
298
+ onReady={(controls) => { setQueryRef.current = controls.setQuery }}
299
+ onChange={(nextValue) => {
300
+ if (!nextValue) {
301
+ onChange(undefined)
302
+ onSnapshot?.(null)
303
+ setSelectedOption(null)
304
+ return
305
+ }
306
+ onChange(nextValue)
307
+ const nextOption = knownOptionsRef.current.get(nextValue) ?? null
308
+ setSelectedOption(nextOption)
309
+ onSnapshot?.(nextOption?.snapshot ?? null)
310
+ // Clearing the query drops LookupSelect back below its minQuery, which
311
+ // collapses the result panel — otherwise it stays open over the form.
312
+ setQueryRef.current?.('')
313
+ }}
314
+ />
315
+ </div>
316
+ )
317
+ }
318
+
319
+ export function ProductSelectField({
320
+ id,
321
+ value,
322
+ onChange,
323
+ onSnapshot,
324
+ placeholder,
325
+ loadError,
326
+ }: Omit<LookupSelectFieldProps<ProductSnapshot>, 'loadOptions' | 'loadSelectedOption' | 'emptyLabel'>) {
327
+ const loadProductOptions = React.useCallback(async (search: string) => {
328
+ const params = new URLSearchParams({ page: '1', pageSize: String(PICKER_PAGE_SIZE) })
329
+ if (search) params.set('search', search)
330
+ const payload = await readApiResultOrThrow<PickerPayload>(
331
+ `/api/catalog/products?${params.toString()}`,
332
+ undefined,
333
+ { errorMessage: loadError },
334
+ )
335
+ const items = Array.isArray(payload.items) ? payload.items : []
336
+ return items
337
+ .map((item) => normalizeProductOption(item))
338
+ .filter((option): option is PickerOption<ProductSnapshot> => option !== null)
339
+ }, [loadError])
340
+
341
+ const loadSelectedProduct = React.useCallback(async (productId: string) => {
342
+ const payload = await readApiResultOrThrow<PickerPayload>(
343
+ `/api/catalog/products?id=${encodeURIComponent(productId)}&pageSize=1`,
344
+ undefined,
345
+ { errorMessage: loadError },
346
+ )
347
+ const items = Array.isArray(payload.items) ? payload.items : []
348
+ return items
349
+ .map((item) => normalizeProductOption(item))
350
+ .find((option): option is PickerOption<ProductSnapshot> => option?.value === productId) ?? null
351
+ }, [loadError])
352
+
353
+ return (
354
+ <LookupSelectField
355
+ id={id}
356
+ value={value}
357
+ onChange={onChange}
358
+ onSnapshot={onSnapshot}
359
+ placeholder={placeholder}
360
+ loadError={loadError}
361
+ loadOptions={loadProductOptions}
362
+ loadSelectedOption={loadSelectedProduct}
363
+ />
364
+ )
365
+ }
366
+
367
+ export function CompanySelectField({
368
+ id,
369
+ value,
370
+ onChange,
371
+ onSnapshot,
372
+ placeholder,
373
+ loadError,
374
+ }: Omit<LookupSelectFieldProps<CompanySnapshot>, 'loadOptions' | 'loadSelectedOption' | 'emptyLabel'>) {
375
+ const loadCompanyOptions = React.useCallback(async (search: string) => {
376
+ const params = new URLSearchParams({
377
+ page: '1',
378
+ pageSize: String(PICKER_PAGE_SIZE),
379
+ sortField: 'name',
380
+ sortDir: 'asc',
381
+ })
382
+ if (search) params.set('search', search)
383
+ const payload = await readApiResultOrThrow<PickerPayload>(
384
+ `/api/customers/companies?${params.toString()}`,
385
+ undefined,
386
+ { errorMessage: loadError },
387
+ )
388
+ const items = Array.isArray(payload.items) ? payload.items : []
389
+ return items
390
+ .map((item) => normalizeCompanyOption(item))
391
+ .filter((option): option is PickerOption<CompanySnapshot> => option !== null)
392
+ }, [loadError])
393
+
394
+ const loadSelectedCompany = React.useCallback(async (companyId: string) => {
395
+ const payload = await readApiResultOrThrow<PickerPayload>(
396
+ `/api/customers/companies?id=${encodeURIComponent(companyId)}&pageSize=1`,
397
+ undefined,
398
+ { errorMessage: loadError },
399
+ )
400
+ const items = Array.isArray(payload.items) ? payload.items : []
401
+ return items
402
+ .map((item) => normalizeCompanyOption(item))
403
+ .find((option): option is PickerOption<CompanySnapshot> => option?.value === companyId) ?? null
404
+ }, [loadError])
405
+
406
+ return (
407
+ <LookupSelectField
408
+ id={id}
409
+ value={value}
410
+ onChange={onChange}
411
+ onSnapshot={onSnapshot}
412
+ placeholder={placeholder}
413
+ loadError={loadError}
414
+ loadOptions={loadCompanyOptions}
415
+ loadSelectedOption={loadSelectedCompany}
416
+ />
417
+ )
418
+ }
419
+
420
+ function collectProductIds(items: unknown[]): string[] {
421
+ const ids: string[] = []
422
+ for (const item of items) {
423
+ if (!isRecord(item)) continue
424
+ const id = readString(item, ['id'])
425
+ if (id) ids.push(id)
426
+ }
427
+ return ids
428
+ }
429
+
430
+ export function MappingSelectField({
431
+ id,
432
+ value,
433
+ onChange,
434
+ placeholder,
435
+ loadError,
436
+ }: Omit<LookupSelectFieldProps<Record<string, unknown>>, 'loadOptions' | 'loadSelectedOption' | 'onSnapshot'>) {
437
+ const translate = useT()
438
+ const loadMappingOptions = React.useCallback(async (search: string) => {
439
+ const mappingParams = new URLSearchParams({ page: '1', pageSize: String(PICKER_PAGE_SIZE) })
440
+ if (!search) {
441
+ const payload = await readApiResultOrThrow<PickerPayload>(
442
+ `/api/eudr/product-mappings?${mappingParams.toString()}`,
443
+ undefined,
444
+ { errorMessage: loadError },
445
+ )
446
+ const items = Array.isArray(payload.items) ? payload.items : []
447
+ return items
448
+ .map((item) => normalizeMappingOption(item, translate))
449
+ .filter((option): option is PickerOption<Record<string, unknown>> => option !== null)
450
+ }
451
+ const productParams = new URLSearchParams({ page: '1', pageSize: String(PICKER_PAGE_SIZE), search })
452
+ const directParams = new URLSearchParams({ page: '1', pageSize: String(PICKER_PAGE_SIZE), search })
453
+ const [productsPayload, directPayload] = await Promise.all([
454
+ readApiResultOrThrow<PickerPayload>(
455
+ `/api/catalog/products?${productParams.toString()}`,
456
+ undefined,
457
+ { errorMessage: loadError },
458
+ ),
459
+ readApiResultOrThrow<PickerPayload>(
460
+ `/api/eudr/product-mappings?${directParams.toString()}`,
461
+ undefined,
462
+ { errorMessage: loadError },
463
+ ),
464
+ ])
465
+ const productIds = collectProductIds(Array.isArray(productsPayload.items) ? productsPayload.items : [])
466
+ let byProductItems: unknown[] = []
467
+ if (productIds.length) {
468
+ const byProductParams = new URLSearchParams({
469
+ page: '1',
470
+ pageSize: String(PICKER_PAGE_SIZE),
471
+ productId: productIds.join(','),
472
+ })
473
+ const byProductPayload = await readApiResultOrThrow<PickerPayload>(
474
+ `/api/eudr/product-mappings?${byProductParams.toString()}`,
475
+ undefined,
476
+ { errorMessage: loadError },
477
+ )
478
+ byProductItems = Array.isArray(byProductPayload.items) ? byProductPayload.items : []
479
+ }
480
+ const directItems = Array.isArray(directPayload.items) ? directPayload.items : []
481
+ const merged = new Map<string, PickerOption<Record<string, unknown>>>()
482
+ for (const item of [...byProductItems, ...directItems]) {
483
+ const option = normalizeMappingOption(item, translate)
484
+ if (option && !merged.has(option.value)) merged.set(option.value, option)
485
+ }
486
+ return Array.from(merged.values())
487
+ }, [loadError, translate])
488
+
489
+ const loadSelectedMapping = React.useCallback(async (mappingId: string) => {
490
+ const payload = await readApiResultOrThrow<PickerPayload>(
491
+ `/api/eudr/product-mappings?id=${encodeURIComponent(mappingId)}&pageSize=1`,
492
+ undefined,
493
+ { errorMessage: loadError },
494
+ )
495
+ const items = Array.isArray(payload.items) ? payload.items : []
496
+ return items
497
+ .map((item) => normalizeMappingOption(item, translate))
498
+ .find((option): option is PickerOption<Record<string, unknown>> => option?.value === mappingId) ?? null
499
+ }, [loadError, translate])
500
+
501
+ return (
502
+ <LookupSelectField
503
+ id={id}
504
+ value={value}
505
+ onChange={onChange}
506
+ placeholder={placeholder}
507
+ loadError={loadError}
508
+ loadOptions={loadMappingOptions}
509
+ loadSelectedOption={loadSelectedMapping}
510
+ />
511
+ )
512
+ }
513
+
514
+ export function StatementSelectField({
515
+ id,
516
+ value,
517
+ onChange,
518
+ placeholder,
519
+ loadError,
520
+ }: Omit<LookupSelectFieldProps<Record<string, unknown>>, 'loadOptions' | 'loadSelectedOption' | 'onSnapshot'>) {
521
+ const translate = useT()
522
+ const loadStatementOptions = React.useCallback(async (search: string) => {
523
+ const params = new URLSearchParams({ page: '1', pageSize: String(PICKER_PAGE_SIZE) })
524
+ if (search) params.set('search', search)
525
+ const payload = await readApiResultOrThrow<PickerPayload>(
526
+ `/api/eudr/statements?${params.toString()}`,
527
+ undefined,
528
+ { errorMessage: loadError },
529
+ )
530
+ const items = Array.isArray(payload.items) ? payload.items : []
531
+ return items
532
+ .map((item) => normalizeStatementOption(item, translate))
533
+ .filter((option): option is PickerOption<Record<string, unknown>> => option !== null)
534
+ }, [loadError, translate])
535
+
536
+ const loadSelectedStatement = React.useCallback(async (statementId: string) => {
537
+ const payload = await readApiResultOrThrow<PickerPayload>(
538
+ `/api/eudr/statements?id=${encodeURIComponent(statementId)}&pageSize=1`,
539
+ undefined,
540
+ { errorMessage: loadError },
541
+ )
542
+ const items = Array.isArray(payload.items) ? payload.items : []
543
+ return items
544
+ .map((item) => normalizeStatementOption(item, translate))
545
+ .find((option): option is PickerOption<Record<string, unknown>> => option?.value === statementId) ?? null
546
+ }, [loadError, translate])
547
+
548
+ return (
549
+ <LookupSelectField
550
+ id={id}
551
+ value={value}
552
+ onChange={onChange}
553
+ placeholder={placeholder}
554
+ loadError={loadError}
555
+ loadOptions={loadStatementOptions}
556
+ loadSelectedOption={loadSelectedStatement}
557
+ />
558
+ )
559
+ }
560
+
561
+ export function parseGeolocationInput(raw: string, translate: Translator): Record<string, unknown> | null {
562
+ const trimmed = raw.trim()
563
+ if (!trimmed) return null
564
+
565
+ let parsed: unknown
566
+ try {
567
+ parsed = JSON.parse(trimmed)
568
+ } catch {
569
+ const message = translate('eudr.errors.geolocationInvalid')
570
+ throw createCrudFormError(message, { geolocation: message })
571
+ }
572
+
573
+ if (!isRecord(parsed)) {
574
+ const message = translate('eudr.errors.geolocationInvalid')
575
+ throw createCrudFormError(message, { geolocation: message })
576
+ }
577
+
578
+ const geoJsonType = parsed.type
579
+ if (
580
+ typeof geoJsonType !== 'string' ||
581
+ !GEOJSON_TYPES.some((allowedType) => allowedType === geoJsonType)
582
+ ) {
583
+ const message = translate('eudr.errors.geolocationInvalid')
584
+ throw createCrudFormError(message, { geolocation: message })
585
+ }
586
+
587
+ if (JSON.stringify(parsed).length > GEOJSON_SIZE_LIMIT) {
588
+ const message = translate('eudr.errors.geolocationTooLarge')
589
+ throw createCrudFormError(message, { geolocation: message })
590
+ }
591
+
592
+ return parsed
593
+ }
594
+
595
+ export { translateEudrCrudError } from './crudErrorI18n'
596
+
597
+ let referencedStatementRowKeySeq = 0
598
+
599
+ function normalizeReferencedStatements(value: unknown): ReferencedStatementValue[] {
600
+ if (!Array.isArray(value)) return []
601
+ return value
602
+ .map((entry) => {
603
+ if (!isRecord(entry)) return null
604
+ const referenceNumber = readString(entry, ['referenceNumber']) ?? ''
605
+ const verificationNumber = readString(entry, ['verificationNumber']) ?? ''
606
+ return verificationNumber
607
+ ? { referenceNumber, verificationNumber }
608
+ : { referenceNumber }
609
+ })
610
+ .filter((entry): entry is ReferencedStatementValue => entry !== null)
611
+ }
612
+
613
+ export function ReferencedStatementsField({
614
+ id,
615
+ value,
616
+ onChange,
617
+ disabled,
618
+ }: {
619
+ id: string
620
+ value: ReferencedStatementValue[] | unknown
621
+ onChange: (value: ReferencedStatementValue[]) => void
622
+ disabled?: boolean
623
+ }) {
624
+ const translate = useT()
625
+ const rows = React.useMemo(() => normalizeReferencedStatements(value), [value])
626
+ const hasIncompleteRows = rows.some((row) => row.referenceNumber.trim().length === 0)
627
+
628
+ const updateRow = React.useCallback((index: number, patch: Partial<ReferencedStatementValue>) => {
629
+ const nextRows = rows.map((row, rowIndex) => {
630
+ if (rowIndex !== index) return row
631
+ const referenceNumber = patch.referenceNumber ?? row.referenceNumber
632
+ const verificationNumber = patch.verificationNumber ?? row.verificationNumber ?? ''
633
+ return verificationNumber.trim()
634
+ ? { referenceNumber, verificationNumber }
635
+ : { referenceNumber }
636
+ })
637
+ onChange(nextRows)
638
+ }, [onChange, rows])
639
+
640
+ // Row identity lives outside the persisted value (which is the API's
641
+ // [{referenceNumber, verificationNumber?}] contract), so keys are tracked
642
+ // alongside it. Indexes as keys make React reuse the wrong input when a middle
643
+ // row is removed, which steals focus from the row that shifts up.
644
+ const rowKeysRef = React.useRef<string[]>([])
645
+ if (rowKeysRef.current.length < rows.length) {
646
+ const nextKeys = [...rowKeysRef.current]
647
+ while (nextKeys.length < rows.length) {
648
+ referencedStatementRowKeySeq += 1
649
+ nextKeys.push(`referenced-statement-${referencedStatementRowKeySeq}`)
650
+ }
651
+ rowKeysRef.current = nextKeys
652
+ } else if (rowKeysRef.current.length > rows.length) {
653
+ rowKeysRef.current = rowKeysRef.current.slice(0, rows.length)
654
+ }
655
+ const rowKeys = rowKeysRef.current
656
+
657
+ const removeRow = React.useCallback((index: number) => {
658
+ rowKeysRef.current = rowKeysRef.current.filter((_, keyIndex) => keyIndex !== index)
659
+ onChange(rows.filter((_, rowIndex) => rowIndex !== index))
660
+ }, [onChange, rows])
661
+
662
+ return (
663
+ <div id={id} className="space-y-3">
664
+ {rows.length > 0 ? (
665
+ <div className="space-y-2">
666
+ {rows.map((row, index) => (
667
+ <div key={rowKeys[index]} className="grid gap-2 md:grid-cols-[1fr_1fr_auto]">
668
+ <Input
669
+ value={row.referenceNumber}
670
+ disabled={disabled}
671
+ placeholder={translate('eudr.statements.form.referenceNumber')}
672
+ onChange={(event) => updateRow(index, { referenceNumber: event.target.value })}
673
+ />
674
+ <Input
675
+ value={row.verificationNumber ?? ''}
676
+ disabled={disabled}
677
+ placeholder={translate('eudr.statements.form.verificationNumber')}
678
+ onChange={(event) => updateRow(index, { verificationNumber: event.target.value })}
679
+ />
680
+ <IconButton
681
+ type="button"
682
+ variant="ghost"
683
+ disabled={disabled}
684
+ aria-label={translate('eudr.statements.form.removeReferencedStatement')}
685
+ onClick={() => removeRow(index)}
686
+ >
687
+ <Trash2 className="size-4" aria-hidden="true" />
688
+ </IconButton>
689
+ </div>
690
+ ))}
691
+ </div>
692
+ ) : (
693
+ <p className="text-sm text-muted-foreground">
694
+ {translate('eudr.statements.form.noReferencedStatements')}
695
+ </p>
696
+ )}
697
+ {hasIncompleteRows ? (
698
+ <p className="text-sm text-status-warning-text">
699
+ {translate('eudr.statements.form.referencedStatementsValidation')}
700
+ </p>
701
+ ) : null}
702
+ <IconButton
703
+ type="button"
704
+ variant="outline"
705
+ disabled={disabled}
706
+ aria-label={translate('eudr.statements.form.addReferencedStatement')}
707
+ onClick={() => onChange([...rows, { referenceNumber: '' }])}
708
+ >
709
+ <Plus className="size-4" aria-hidden="true" />
710
+ </IconButton>
711
+ </div>
712
+ )
713
+ }
714
+
715
+ export { CountrySelectField } from './CountrySelectField'
716
+ export { OrderSelectField } from './OrderSelectField'
717
+ export { PlotMultiSelectField } from './PlotMultiSelectField'