@open-mercato/core 0.6.6-develop.6330.1.a261878aa8 → 0.6.6-develop.6331.1.a33b8e99b7

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 (244) hide show
  1. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +3 -0
  2. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  3. package/dist/modules/auth/api/roles/acl/route.js +2 -2
  4. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  5. package/dist/modules/auth/api/users/acl/route.js +2 -2
  6. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  7. package/dist/modules/auth/backend/roles/[id]/edit/page.js +12 -0
  8. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  9. package/dist/modules/auth/backend/users/[id]/edit/page.js +12 -0
  10. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  11. package/dist/modules/auth/data/entities.js +2 -2
  12. package/dist/modules/auth/data/entities.js.map +2 -2
  13. package/dist/modules/business_rules/api/rules/route.js +3 -3
  14. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  15. package/dist/modules/business_rules/api/sets/route.js +3 -3
  16. package/dist/modules/business_rules/api/sets/route.js.map +2 -2
  17. package/dist/modules/business_rules/backend/rules/[id]/page.js +12 -1
  18. package/dist/modules/business_rules/backend/rules/[id]/page.js.map +2 -2
  19. package/dist/modules/business_rules/backend/sets/[id]/page.js +12 -1
  20. package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
  21. package/dist/modules/catalog/api/product-media/route.js.map +2 -2
  22. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +12 -0
  23. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  24. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +15 -0
  25. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  26. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +12 -1
  27. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  28. package/dist/modules/catalog/components/PriceKindSettings.js +13 -3
  29. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  30. package/dist/modules/catalog/lib/bulkDelete.js.map +2 -2
  31. package/dist/modules/currencies/backend/currencies/[id]/page.js +15 -3
  32. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  33. package/dist/modules/currencies/backend/currencies/page.js +2 -1
  34. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  35. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +12 -1
  36. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
  37. package/dist/modules/currencies/backend/exchange-rates/page.js +3 -2
  38. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  39. package/dist/modules/customer_accounts/api/admin/roles/[id].js +3 -3
  40. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  41. package/dist/modules/customer_accounts/api/admin/users/[id].js +3 -3
  42. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  43. package/dist/modules/customers/api/addresses/route.js +4 -2
  44. package/dist/modules/customers/api/addresses/route.js.map +2 -2
  45. package/dist/modules/customers/api/tags/route.js +8 -3
  46. package/dist/modules/customers/api/tags/route.js.map +2 -2
  47. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +13 -2
  48. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  49. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +24 -9
  50. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
  51. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +53 -23
  52. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +2 -2
  53. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js +13 -3
  54. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js.map +2 -2
  55. package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -2
  56. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  57. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +13 -2
  58. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  59. package/dist/modules/customers/commands/interactions.js +5 -5
  60. package/dist/modules/customers/commands/interactions.js.map +2 -2
  61. package/dist/modules/customers/commands/pipeline-stages.js +24 -2
  62. package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
  63. package/dist/modules/customers/commands/pipelines.js +24 -2
  64. package/dist/modules/customers/commands/pipelines.js.map +2 -2
  65. package/dist/modules/customers/components/detail/AddressesSection.js +46 -29
  66. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  67. package/dist/modules/customers/components/detail/DealForm.js +5 -1
  68. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  69. package/dist/modules/customers/components/detail/notesAdapter.js +25 -18
  70. package/dist/modules/customers/components/detail/notesAdapter.js.map +2 -2
  71. package/dist/modules/data_sync/api/schedules/[id]/route.js +38 -19
  72. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  73. package/dist/modules/data_sync/api/schedules/route.js +1 -1
  74. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  75. package/dist/modules/data_sync/backend/data-sync/page.js +32 -15
  76. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  77. package/dist/modules/data_sync/lib/sync-schedule-service.js +19 -5
  78. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  79. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +3 -3
  80. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  81. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +3 -3
  82. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  83. package/dist/modules/dictionaries/components/DictionariesManager.js +12 -1
  84. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  85. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  86. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +12 -0
  87. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  88. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +12 -0
  89. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  90. package/dist/modules/entities/api/entities.js +2 -2
  91. package/dist/modules/entities/api/entities.js.map +2 -2
  92. package/dist/modules/entities/api/records.js +7 -5
  93. package/dist/modules/entities/api/records.js.map +2 -2
  94. package/dist/modules/feature_toggles/api/overrides/route.js +2 -2
  95. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  96. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  97. package/dist/modules/feature_toggles/data/entities.js +1 -1
  98. package/dist/modules/feature_toggles/data/entities.js.map +2 -2
  99. package/dist/modules/feature_toggles/data/validators.js +2 -1
  100. package/dist/modules/feature_toggles/data/validators.js.map +2 -2
  101. package/dist/modules/feature_toggles/lib/queries.js +2 -1
  102. package/dist/modules/feature_toggles/lib/queries.js.map +2 -2
  103. package/dist/modules/inbox_ops/api/settings/route.js +2 -2
  104. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  105. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +12 -1
  106. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  107. package/dist/modules/resources/backend/resources/resources/[id]/page.js +12 -1
  108. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  109. package/dist/modules/resources/components/detail/notesAdapter.js +25 -18
  110. package/dist/modules/resources/components/detail/notesAdapter.js.map +2 -2
  111. package/dist/modules/sales/backend/sales/documents/[id]/page.js +14 -1
  112. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  113. package/dist/modules/sales/commands/documents.js +11 -10
  114. package/dist/modules/sales/commands/documents.js.map +2 -2
  115. package/dist/modules/sales/commands/payments.js +6 -1
  116. package/dist/modules/sales/commands/payments.js.map +2 -2
  117. package/dist/modules/sales/commands/returns.js +3 -3
  118. package/dist/modules/sales/commands/returns.js.map +2 -2
  119. package/dist/modules/sales/commands/shared.js +3 -3
  120. package/dist/modules/sales/commands/shared.js.map +2 -2
  121. package/dist/modules/sales/commands/shipments.js +6 -1
  122. package/dist/modules/sales/commands/shipments.js.map +2 -2
  123. package/dist/modules/sales/components/documents/PaymentDialog.js +6 -3
  124. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  125. package/dist/modules/sales/components/documents/PaymentsSection.js +7 -3
  126. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  127. package/dist/modules/sales/components/documents/ShipmentDialog.js +6 -3
  128. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  129. package/dist/modules/sales/components/documents/ShipmentsSection.js +7 -3
  130. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  131. package/dist/modules/sales/di.js +39 -3
  132. package/dist/modules/sales/di.js.map +2 -2
  133. package/dist/modules/sales/setup.js +2 -0
  134. package/dist/modules/sales/setup.js.map +2 -2
  135. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +12 -1
  136. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  137. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +12 -1
  138. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  139. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +12 -1
  140. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  141. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +12 -1
  142. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  143. package/dist/modules/staff/commands/job-histories.js +3 -3
  144. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  145. package/dist/modules/staff/components/detail/addressesAdapter.js +33 -24
  146. package/dist/modules/staff/components/detail/addressesAdapter.js.map +2 -2
  147. package/dist/modules/staff/components/detail/notesAdapter.js +25 -18
  148. package/dist/modules/staff/components/detail/notesAdapter.js.map +2 -2
  149. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +37 -0
  150. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  151. package/dist/modules/translations/components/TranslationManager.js +7 -1
  152. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  153. package/dist/modules/workflows/api/definitions/[id]/route.js +3 -3
  154. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  155. package/dist/modules/workflows/backend/definitions/[id]/page.js +12 -1
  156. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  157. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +11 -1
  158. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  159. package/package.json +7 -7
  160. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +3 -0
  161. package/src/modules/auth/api/roles/acl/route.ts +2 -2
  162. package/src/modules/auth/api/users/acl/route.ts +2 -2
  163. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +17 -0
  164. package/src/modules/auth/backend/users/[id]/edit/page.tsx +17 -0
  165. package/src/modules/auth/data/entities.ts +2 -2
  166. package/src/modules/business_rules/api/rules/route.ts +3 -3
  167. package/src/modules/business_rules/api/sets/route.ts +3 -3
  168. package/src/modules/business_rules/backend/rules/[id]/page.tsx +17 -1
  169. package/src/modules/business_rules/backend/sets/[id]/page.tsx +18 -1
  170. package/src/modules/catalog/api/product-media/route.ts +4 -0
  171. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +17 -0
  172. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +21 -0
  173. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +16 -1
  174. package/src/modules/catalog/components/PriceKindSettings.tsx +15 -7
  175. package/src/modules/catalog/lib/bulkDelete.ts +6 -0
  176. package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -3
  177. package/src/modules/currencies/backend/currencies/page.tsx +2 -1
  178. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +17 -1
  179. package/src/modules/currencies/backend/exchange-rates/page.tsx +3 -2
  180. package/src/modules/customer_accounts/api/admin/roles/[id].ts +3 -3
  181. package/src/modules/customer_accounts/api/admin/users/[id].ts +3 -3
  182. package/src/modules/customers/api/addresses/route.ts +2 -0
  183. package/src/modules/customers/api/tags/route.ts +6 -1
  184. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +19 -2
  185. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +29 -8
  186. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +47 -20
  187. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.ts +13 -3
  188. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
  189. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -2
  190. package/src/modules/customers/commands/interactions.ts +5 -5
  191. package/src/modules/customers/commands/pipeline-stages.ts +26 -2
  192. package/src/modules/customers/commands/pipelines.ts +26 -2
  193. package/src/modules/customers/components/detail/AddressesSection.tsx +50 -28
  194. package/src/modules/customers/components/detail/DealForm.tsx +12 -0
  195. package/src/modules/customers/components/detail/notesAdapter.ts +29 -18
  196. package/src/modules/customers/i18n/de.json +2 -0
  197. package/src/modules/customers/i18n/en.json +2 -0
  198. package/src/modules/customers/i18n/es.json +2 -0
  199. package/src/modules/customers/i18n/pl.json +2 -0
  200. package/src/modules/data_sync/api/schedules/[id]/route.ts +38 -20
  201. package/src/modules/data_sync/api/schedules/route.ts +1 -1
  202. package/src/modules/data_sync/backend/data-sync/page.tsx +32 -15
  203. package/src/modules/data_sync/lib/sync-schedule-service.ts +30 -5
  204. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +3 -3
  205. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +3 -3
  206. package/src/modules/dictionaries/components/DictionariesManager.tsx +21 -1
  207. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +2 -0
  208. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +17 -0
  209. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +17 -0
  210. package/src/modules/entities/api/entities.ts +2 -2
  211. package/src/modules/entities/api/records.ts +7 -5
  212. package/src/modules/feature_toggles/api/overrides/route.ts +2 -2
  213. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +12 -0
  214. package/src/modules/feature_toggles/data/entities.ts +1 -1
  215. package/src/modules/feature_toggles/data/validators.ts +1 -0
  216. package/src/modules/feature_toggles/lib/queries.ts +1 -0
  217. package/src/modules/inbox_ops/api/settings/route.ts +2 -2
  218. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +17 -1
  219. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +17 -1
  220. package/src/modules/resources/components/detail/notesAdapter.ts +29 -18
  221. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +17 -1
  222. package/src/modules/sales/commands/documents.ts +11 -10
  223. package/src/modules/sales/commands/payments.ts +11 -0
  224. package/src/modules/sales/commands/returns.ts +3 -3
  225. package/src/modules/sales/commands/shared.ts +10 -4
  226. package/src/modules/sales/commands/shipments.ts +14 -0
  227. package/src/modules/sales/components/documents/PaymentDialog.tsx +7 -3
  228. package/src/modules/sales/components/documents/PaymentsSection.tsx +8 -3
  229. package/src/modules/sales/components/documents/ShipmentDialog.tsx +7 -3
  230. package/src/modules/sales/components/documents/ShipmentsSection.tsx +8 -3
  231. package/src/modules/sales/di.ts +68 -9
  232. package/src/modules/sales/setup.ts +2 -0
  233. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +17 -1
  234. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +17 -1
  235. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +17 -1
  236. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +17 -1
  237. package/src/modules/staff/commands/job-histories.ts +3 -3
  238. package/src/modules/staff/components/detail/addressesAdapter.ts +40 -23
  239. package/src/modules/staff/components/detail/notesAdapter.ts +28 -18
  240. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +74 -0
  241. package/src/modules/translations/components/TranslationManager.tsx +14 -1
  242. package/src/modules/workflows/api/definitions/[id]/route.ts +3 -3
  243. package/src/modules/workflows/backend/definitions/[id]/page.tsx +18 -1
  244. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +19 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/data_sync/lib/sync-schedule-service.ts"],
4
- "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { SyncSchedule } from '../data/entities'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n}\n\ntype SchedulerServiceLike = {\n register: (registration: {\n id: string\n name: string\n description?: string\n scopeType: 'organization'\n organizationId: string\n tenantId: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone?: string\n targetType: 'queue'\n targetQueue: string\n targetPayload: Record<string, unknown>\n requireFeature?: string\n sourceType: 'module'\n sourceModule: string\n isEnabled?: boolean\n }) => Promise<void>\n unregister: (scheduleId: string) => Promise<void>\n}\n\nexport function createSyncScheduleService(em: EntityManager, schedulerService?: SchedulerServiceLike) {\n function requireScheduler(): SchedulerServiceLike {\n if (!schedulerService) {\n throw new Error('Scheduler module is not available')\n }\n return schedulerService\n }\n\n function buildScheduleName(row: SyncSchedule): string {\n return `Data sync: ${row.integrationId} ${row.entityType} ${row.direction}`\n }\n\n function buildScheduleDescription(row: SyncSchedule): string {\n return `Scheduled ${row.direction} for ${row.integrationId} (${row.entityType})`\n }\n\n async function getById(id: string, scope: SyncScope): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n async function getByKey(\n integrationId: string,\n entityType: string,\n direction: 'import' | 'export',\n scope: SyncScope,\n ): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n integrationId,\n entityType,\n direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return {\n getById,\n getByKey,\n\n async listSchedules(query: {\n integrationId?: string\n entityType?: string\n direction?: 'import' | 'export'\n page: number\n pageSize: number\n }, scope: SyncScope): Promise<{ items: SyncSchedule[]; total: number }> {\n const where: FilterQuery<SyncSchedule> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (query.integrationId) where.integrationId = query.integrationId\n if (query.entityType) where.entityType = query.entityType\n if (query.direction) where.direction = query.direction\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncSchedule,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: query.pageSize,\n offset: (query.page - 1) * query.pageSize,\n },\n scope,\n )\n\n return { items, total }\n },\n\n async saveSchedule(input: {\n id?: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n expectedUpdatedAt?: string | null\n }, scope: SyncScope): Promise<SyncSchedule> {\n const existing = input.id\n ? await getById(input.id, scope)\n : await getByKey(input.integrationId, input.entityType, input.direction, scope)\n\n if (existing) {\n enforceCommandOptimisticLock({\n resourceKind: 'data_sync.schedule',\n resourceId: existing.id,\n current: existing.updatedAt ?? null,\n expected: input.expectedUpdatedAt ?? null,\n })\n }\n\n const row = existing ?? em.create(SyncSchedule, {\n id: randomUUID(),\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n scheduleType: input.scheduleType,\n scheduleValue: input.scheduleValue,\n timezone: input.timezone,\n fullSync: input.fullSync,\n isEnabled: input.isEnabled,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n row.integrationId = input.integrationId\n row.entityType = input.entityType\n row.direction = input.direction\n row.scheduleType = input.scheduleType\n row.scheduleValue = input.scheduleValue\n row.timezone = input.timezone\n row.fullSync = input.fullSync\n row.isEnabled = input.isEnabled\n row.scheduledJobId = row.scheduledJobId ?? row.id\n\n if (!existing) {\n em.persist(row)\n }\n\n await em.flush()\n\n await requireScheduler().register({\n id: row.scheduledJobId,\n name: buildScheduleName(row),\n description: buildScheduleDescription(row),\n scopeType: 'organization',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n scheduleType: row.scheduleType,\n scheduleValue: row.scheduleValue,\n timezone: row.timezone,\n targetType: 'queue',\n targetQueue: 'data-sync-scheduled',\n targetPayload: {\n scheduleId: row.id,\n scope,\n },\n requireFeature: 'data_sync.run',\n sourceType: 'module',\n sourceModule: 'data_sync',\n isEnabled: row.isEnabled,\n })\n\n return row\n },\n\n async deleteSchedule(id: string, scope: SyncScope): Promise<boolean> {\n const row = await getById(id, scope)\n if (!row) return false\n\n const scheduledJobId = row.scheduledJobId ?? row.id\n await requireScheduler().unregister(scheduledJobId)\n\n row.deletedAt = new Date()\n row.isEnabled = false\n await em.flush()\n return true\n },\n }\n}\n\nexport type SyncScheduleService = ReturnType<typeof createSyncScheduleService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { enforceCommandOptimisticLockWithGuards, enforceRecordGoneIsConflict } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { SyncSchedule } from '../data/entities'\n\ntype SyncScope = {\n organizationId: string\n tenantId: string\n}\n\ntype SchedulerServiceLike = {\n register: (registration: {\n id: string\n name: string\n description?: string\n scopeType: 'organization'\n organizationId: string\n tenantId: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone?: string\n targetType: 'queue'\n targetQueue: string\n targetPayload: Record<string, unknown>\n requireFeature?: string\n sourceType: 'module'\n sourceModule: string\n isEnabled?: boolean\n }) => Promise<void>\n unregister: (scheduleId: string) => Promise<void>\n}\n\nexport function createSyncScheduleService(em: EntityManager, schedulerService?: SchedulerServiceLike) {\n function requireScheduler(): SchedulerServiceLike {\n if (!schedulerService) {\n throw new Error('Scheduler module is not available')\n }\n return schedulerService\n }\n\n function buildScheduleName(row: SyncSchedule): string {\n return `Data sync: ${row.integrationId} ${row.entityType} ${row.direction}`\n }\n\n function buildScheduleDescription(row: SyncSchedule): string {\n return `Scheduled ${row.direction} for ${row.integrationId} (${row.entityType})`\n }\n\n async function getById(id: string, scope: SyncScope): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n async function getByKey(\n integrationId: string,\n entityType: string,\n direction: 'import' | 'export',\n scope: SyncScope,\n ): Promise<SyncSchedule | null> {\n return findOneWithDecryption(\n em,\n SyncSchedule,\n {\n integrationId,\n entityType,\n direction,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return {\n getById,\n getByKey,\n\n async listSchedules(query: {\n integrationId?: string\n entityType?: string\n direction?: 'import' | 'export'\n page: number\n pageSize: number\n }, scope: SyncScope): Promise<{ items: SyncSchedule[]; total: number }> {\n const where: FilterQuery<SyncSchedule> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (query.integrationId) where.integrationId = query.integrationId\n if (query.entityType) where.entityType = query.entityType\n if (query.direction) where.direction = query.direction\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SyncSchedule,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: query.pageSize,\n offset: (query.page - 1) * query.pageSize,\n },\n scope,\n )\n\n return { items, total }\n },\n\n async saveSchedule(input: {\n id?: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n expectedUpdatedAt?: string | null\n }, scope: SyncScope, container?: AwilixContainer): Promise<SyncSchedule> {\n const existing = input.id\n ? await getById(input.id, scope)\n : await getByKey(input.integrationId, input.entityType, input.direction, scope)\n\n if (existing && container) {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'data_sync.schedule',\n resourceId: existing.id,\n current: existing.updatedAt ?? null,\n expected: input.expectedUpdatedAt ?? null,\n })\n } else if (!existing && input.expectedUpdatedAt) {\n // Concurrent delete-then-edit race: the client edited a schedule that was\n // removed before this keyed upsert ran. Surface the unified conflict\n // instead of silently re-creating it. No-op when no expected version was\n // sent (a genuine create) or when OM_OPTIMISTIC_LOCK is off.\n enforceRecordGoneIsConflict({\n resourceKind: 'data_sync.schedule',\n resourceId: input.id ?? `${input.integrationId}:${input.entityType}:${input.direction}`,\n expected: input.expectedUpdatedAt,\n })\n }\n\n const row = existing ?? em.create(SyncSchedule, {\n id: randomUUID(),\n integrationId: input.integrationId,\n entityType: input.entityType,\n direction: input.direction,\n scheduleType: input.scheduleType,\n scheduleValue: input.scheduleValue,\n timezone: input.timezone,\n fullSync: input.fullSync,\n isEnabled: input.isEnabled,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n row.integrationId = input.integrationId\n row.entityType = input.entityType\n row.direction = input.direction\n row.scheduleType = input.scheduleType\n row.scheduleValue = input.scheduleValue\n row.timezone = input.timezone\n row.fullSync = input.fullSync\n row.isEnabled = input.isEnabled\n row.scheduledJobId = row.scheduledJobId ?? row.id\n\n if (!existing) {\n em.persist(row)\n }\n\n await em.flush()\n\n await requireScheduler().register({\n id: row.scheduledJobId,\n name: buildScheduleName(row),\n description: buildScheduleDescription(row),\n scopeType: 'organization',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n scheduleType: row.scheduleType,\n scheduleValue: row.scheduleValue,\n timezone: row.timezone,\n targetType: 'queue',\n targetQueue: 'data-sync-scheduled',\n targetPayload: {\n scheduleId: row.id,\n scope,\n },\n requireFeature: 'data_sync.run',\n sourceType: 'module',\n sourceModule: 'data_sync',\n isEnabled: row.isEnabled,\n })\n\n return row\n },\n\n async deleteSchedule(\n id: string,\n scope: SyncScope,\n container?: AwilixContainer,\n expectedUpdatedAt?: string | null,\n ): Promise<boolean> {\n const row = await getById(id, scope)\n if (!row) return false\n\n if (container) {\n await enforceCommandOptimisticLockWithGuards(container, {\n resourceKind: 'data_sync.schedule',\n resourceId: row.id,\n current: row.updatedAt ?? null,\n expected: expectedUpdatedAt ?? null,\n })\n }\n\n const scheduledJobId = row.scheduledJobId ?? row.id\n await requireScheduler().unregister(scheduledJobId)\n\n row.deletedAt = new Date()\n row.isEnabled = false\n await em.flush()\n return true\n },\n }\n}\n\nexport type SyncScheduleService = ReturnType<typeof createSyncScheduleService>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { Dictionary, DictionaryEntry } from "@open-mercato/core/modules/dictiona
4
4
  import { resolveDictionariesRouteContext, resolveDictionaryActorId } from "@open-mercato/core/modules/dictionaries/api/context";
5
5
  import { updateDictionaryEntrySchema } from "@open-mercato/core/modules/dictionaries/data/validators";
6
6
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
- import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
7
+ import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
8
8
  import {
9
9
  runCrudMutationGuardAfterSuccess,
10
10
  validateCrudMutationGuard
@@ -66,7 +66,7 @@ async function PATCH(req, ctx) {
66
66
  });
67
67
  const dictionary = await loadDictionary(context, dictionaryId);
68
68
  const entry = await loadEntry(context, dictionary, entryId);
69
- enforceCommandOptimisticLock({
69
+ await enforceCommandOptimisticLockWithGuards(context.container, {
70
70
  resourceKind: "dictionaries.entry",
71
71
  resourceId: entry.id,
72
72
  current: entry.updatedAt ?? null,
@@ -166,7 +166,7 @@ async function DELETE(req, ctx) {
166
166
  });
167
167
  const dictionary = await loadDictionary(context, dictionaryId);
168
168
  const entry = await loadEntry(context, dictionary, entryId);
169
- enforceCommandOptimisticLock({
169
+ await enforceCommandOptimisticLockWithGuards(context.container, {
170
170
  resourceKind: "dictionaries.entry",
171
171
  resourceId: entry.id,
172
172
  current: entry.updatedAt ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/dictionaries/api/%5BdictionaryId%5D/entries/%5BentryId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { updateDictionaryEntrySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n dictionaryEntryParamsSchema,\n dictionaryEntryResponseSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n updateDictionaryEntrySchema as updateEntryDocSchema,\n} from '../../../openapi'\nconst paramsSchema = z.object({\n dictionaryId: z.string().uuid(),\n entryId: z.string().uuid(),\n})\n\nasync function loadDictionary(context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>, id: string) {\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionary = await context.em.findOne(Dictionary, {\n id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nasync function loadEntry(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n dictionary: Dictionary,\n entryId: string,\n) {\n const entry = await context.em.findOne(DictionaryEntry, {\n id: entryId,\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: context.tenantId,\n })\n if (!entry) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.entry_not_found', 'Dictionary entry not found') })\n }\n return entry\n}\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n enforceCommandOptimisticLock({\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n const rawBody = await req.json().catch(() => ({}))\n const payload = updateDictionaryEntrySchema.parse(rawBody)\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n // These nested routes don't use the CRUD factory, so invoke the command bus explicitly.\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const input = { ...(payload as Record<string, unknown>), id: entryId }\n const { result, logEntry } = await commandBus.execute('dictionaries.entries.update', {\n input,\n ctx: context.ctx,\n })\n const updateResult = (result ?? {}) as { entryId?: string | null }\n const updatedEntryId = typeof updateResult.entryId === 'string' ? updateResult.entryId : null\n if (!updatedEntryId) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: updatedEntryId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const updated = await findOneWithDecryption(\n context.em.fork(),\n DictionaryEntry,\n updatedEntryId,\n { populate: ['dictionary'] },\n { tenantId: context.auth.tenantId ?? null, organizationId: context.auth.orgId ?? null },\n )\n if (!updated) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n const response = NextResponse.json({\n id: updated.id,\n value: updated.value,\n label: updated.label,\n color: updated.color,\n icon: updated.icon,\n position: updated.position ?? 0,\n isDefault: updated.isDefault ?? false,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: updatedEntryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id/entries/:entryId.PATCH] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to update dictionary entry' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n enforceCommandOptimisticLock({\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute('dictionaries.entries.delete', {\n input: { body: { id: entry.id } },\n ctx: context.ctx,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: entry.id,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id/entries/:entryId.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to delete dictionary entry' }, { status: 500 })\n }\n}\n\nconst dictionaryEntryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary entry',\n description: 'Updates the specified dictionary entry using the command bus pipeline.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: updateEntryDocSchema,\n description: 'Fields to update on the dictionary entry.',\n },\n responses: [\n { status: 200, description: 'Dictionary entry updated.', schema: dictionaryEntryResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update entry', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryEntryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary entry',\n description: 'Deletes the specified dictionary entry via the command bus.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Entry deleted.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete entry', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary entry resource',\n pathParams: dictionaryEntryParamsSchema,\n methods: {\n PATCH: dictionaryEntryPatchDoc,\n DELETE: dictionaryEntryDeleteDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { updateDictionaryEntrySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n dictionaryEntryParamsSchema,\n dictionaryEntryResponseSchema,\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n updateDictionaryEntrySchema as updateEntryDocSchema,\n} from '../../../openapi'\nconst paramsSchema = z.object({\n dictionaryId: z.string().uuid(),\n entryId: z.string().uuid(),\n})\n\nasync function loadDictionary(context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>, id: string) {\n if (!context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const dictionary = await context.em.findOne(Dictionary, {\n id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nasync function loadEntry(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n dictionary: Dictionary,\n entryId: string,\n) {\n const entry = await context.em.findOne(DictionaryEntry, {\n id: entryId,\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: context.tenantId,\n })\n if (!entry) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.entry_not_found', 'Dictionary entry not found') })\n }\n return entry\n}\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n if (!context.auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n const rawBody = await req.json().catch(() => ({}))\n const payload = updateDictionaryEntrySchema.parse(rawBody)\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n // These nested routes don't use the CRUD factory, so invoke the command bus explicitly.\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const input = { ...(payload as Record<string, unknown>), id: entryId }\n const { result, logEntry } = await commandBus.execute('dictionaries.entries.update', {\n input,\n ctx: context.ctx,\n })\n const updateResult = (result ?? {}) as { entryId?: string | null }\n const updatedEntryId = typeof updateResult.entryId === 'string' ? updateResult.entryId : null\n if (!updatedEntryId) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: updatedEntryId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const updated = await findOneWithDecryption(\n context.em.fork(),\n DictionaryEntry,\n updatedEntryId,\n { populate: ['dictionary'] },\n { tenantId: context.auth.tenantId ?? null, organizationId: context.auth.orgId ?? null },\n )\n if (!updated) {\n throw new CrudHttpError(500, { error: context.translate('dictionaries.errors.entry_update_failed', 'Failed to update dictionary entry') })\n }\n const response = NextResponse.json({\n id: updated.id,\n value: updated.value,\n label: updated.label,\n color: updated.color,\n icon: updated.icon,\n position: updated.position ?? 0,\n isDefault: updated.isDefault ?? false,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: updatedEntryId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id/entries/:entryId.PATCH] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to update dictionary entry' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string; entryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId, entryId } = paramsSchema.parse({\n dictionaryId: ctx.params?.dictionaryId,\n entryId: ctx.params?.entryId,\n })\n const dictionary = await loadDictionary(context, dictionaryId)\n const entry = await loadEntry(context, dictionary, entryId)\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n current: entry.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n const commandBus = (context.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute('dictionaries.entries.delete', {\n input: { body: { id: entry.id } },\n ctx: context.ctx,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.entry',\n resourceId: entry.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ ok: true })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'dictionaries.entry',\n resourceId: entry.id,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id/entries/:entryId.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to delete dictionary entry' }, { status: 500 })\n }\n}\n\nconst dictionaryEntryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary entry',\n description: 'Updates the specified dictionary entry using the command bus pipeline.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: updateEntryDocSchema,\n description: 'Fields to update on the dictionary entry.',\n },\n responses: [\n { status: 200, description: 'Dictionary entry updated.', schema: dictionaryEntryResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update entry', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryEntryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary entry',\n description: 'Deletes the specified dictionary entry via the command bus.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Entry deleted.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary or entry not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete entry', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary entry resource',\n pathParams: dictionaryEntryParamsSchema,\n methods: {\n PATCH: dictionaryEntryPatchDoc,\n DELETE: dictionaryEntryDeleteDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,7 +3,7 @@ import { z } from "zod";
3
3
  import { Dictionary } from "@open-mercato/core/modules/dictionaries/data/entities";
4
4
  import { resolveDictionariesRouteContext, resolveDictionaryActorId } from "@open-mercato/core/modules/dictionaries/api/context";
5
5
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
6
- import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
6
+ import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
7
7
  import {
8
8
  runCrudMutationGuardAfterSuccess,
9
9
  validateCrudMutationGuard
@@ -91,7 +91,7 @@ async function PATCH(req, ctx) {
91
91
  const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId });
92
92
  const payload = updateSchema.parse(await req.json().catch(() => ({})));
93
93
  const dictionary = await loadDictionary(context, dictionaryId);
94
- enforceCommandOptimisticLock({
94
+ await enforceCommandOptimisticLockWithGuards(context.container, {
95
95
  resourceKind: "dictionaries.dictionary",
96
96
  resourceId: dictionary.id,
97
97
  current: dictionary.updatedAt ?? null,
@@ -203,7 +203,7 @@ async function DELETE(req, ctx) {
203
203
  const context = await resolveDictionariesRouteContext(req);
204
204
  const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId });
205
205
  const dictionary = await loadDictionary(context, dictionaryId);
206
- enforceCommandOptimisticLock({
206
+ await enforceCommandOptimisticLockWithGuards(context.container, {
207
207
  resourceKind: "dictionaries.dictionary",
208
208
  resourceId: dictionary.id,
209
209
  current: dictionary.updatedAt ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/dictionaries/api/%5BdictionaryId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n resolveDictionaryEntrySortMode,\n} from '@open-mercato/core/modules/dictionaries/lib/entrySort'\nimport {\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n dictionaryDetailSchema,\n dictionaryIdParamsSchema,\n dictionaryUpdateSchema,\n upsertDictionarySchema,\n} from '../openapi'\nimport { dictionaryKeySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\n\nconst paramsSchema = z.object({ dictionaryId: z.string().uuid() })\n// System dictionaries use namespaced keys (e.g. `sales.deal_loss_reason`,\n// `resources.activity-types`) that the strict create-key regex rejects. The\n// manager edit dialog disables the key field but still resubmits the existing\n// key, so the update parse must accept any stored key verbatim. The strict\n// user-key regex is only enforced below when the key actually changes.\nconst updateKeySchema = z.string().trim().min(1).max(100)\nconst updateSchema = upsertDictionarySchema\n .partial()\n .extend({ key: updateKeySchema.optional() })\n .refine((data) => Object.keys(data).length > 0, {\n message: 'Provide at least one field to update.',\n })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['dictionaries.view'] },\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nfunction isProtectedCurrencyDictionary(dictionary: Dictionary) {\n const key = dictionary.key?.trim().toLowerCase() ?? ''\n return key === 'currency' || key === 'currencies'\n}\n\nasync function loadDictionary(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n id: string,\n options: { allowInherited?: boolean } = {},\n) {\n const { allowInherited = false } = options\n if (!allowInherited && !context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const baseFilter = {\n id,\n tenantId: context.tenantId,\n deletedAt: null,\n }\n const filter = allowInherited\n ? {\n ...baseFilter,\n ...(context.readableOrganizationIds.length\n ? { organizationId: { $in: context.readableOrganizationIds } }\n : {}),\n }\n : {\n ...baseFilter,\n organizationId: context.organizationId,\n }\n const dictionary = await context.em.findOne(Dictionary, filter)\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nexport async function GET(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const dictionary = await loadDictionary(context, dictionaryId, { allowInherited: true })\n return NextResponse.json({\n id: dictionary.id,\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description,\n isSystem: dictionary.isSystem,\n isActive: dictionary.isActive,\n managerVisibility: dictionary.managerVisibility,\n entrySortMode: resolveDictionaryEntrySortMode(dictionary.entrySortMode),\n organizationId: dictionary.organizationId,\n isInherited: context.organizationId ? dictionary.organizationId !== context.organizationId : false,\n createdAt: dictionary.createdAt,\n updatedAt: dictionary.updatedAt,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id.GET] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to load dictionary' }, { status: 500 })\n }\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const payload = updateSchema.parse(await req.json().catch(() => ({})))\n const dictionary = await loadDictionary(context, dictionaryId)\n\n enforceCommandOptimisticLock({\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n current: dictionary.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (isProtectedCurrencyDictionary(dictionary)) {\n if (payload.key && payload.key.trim().toLowerCase() !== dictionary.key) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n if (payload.isActive === false) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n }\n\n if (payload.key) {\n const key = payload.key.trim().toLowerCase()\n if (key !== dictionary.key) {\n const strictKey = dictionaryKeySchema.safeParse(key)\n if (!strictKey.success) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.invalid_key', 'Use lowercase letters, numbers, hyphen, or underscore.') })\n }\n const organizationId = context.organizationId\n if (!organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const existing = await context.em.findOne(Dictionary, {\n key,\n organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(409, { error: context.translate('dictionaries.errors.duplicate', 'A dictionary with this key already exists') })\n }\n dictionary.key = key\n }\n }\n\n if (payload.name) {\n dictionary.name = payload.name.trim()\n }\n if (payload.description !== undefined) {\n dictionary.description = payload.description ? payload.description.trim() : null\n }\n if (payload.isActive !== undefined) {\n dictionary.isActive = Boolean(payload.isActive)\n if (!dictionary.isActive) {\n dictionary.deletedAt = dictionary.deletedAt ?? new Date()\n } else {\n dictionary.deletedAt = null\n }\n }\n if (payload.entrySortMode !== undefined) {\n dictionary.entrySortMode = payload.entrySortMode\n }\n\n dictionary.updatedAt = new Date()\n await context.em.flush()\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({\n id: dictionary.id,\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description,\n isSystem: dictionary.isSystem,\n isActive: dictionary.isActive,\n managerVisibility: dictionary.managerVisibility,\n entrySortMode: resolveDictionaryEntrySortMode(dictionary.entrySortMode),\n createdAt: dictionary.createdAt,\n updatedAt: dictionary.updatedAt,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: err.issues[0]?.message ?? 'Validation failed' }, { status: 400 })\n }\n console.error('[dictionaries/:id.PATCH] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to update dictionary' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const dictionary = await loadDictionary(context, dictionaryId)\n\n enforceCommandOptimisticLock({\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n current: dictionary.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (isProtectedCurrencyDictionary(dictionary)) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n\n dictionary.isActive = false\n dictionary.deletedAt = dictionary.deletedAt ?? new Date()\n await context.em.flush()\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to delete dictionary' }, { status: 500 })\n }\n}\n\nconst dictionaryGetDoc: OpenApiMethodDoc = {\n summary: 'Get dictionary',\n description: 'Returns details for the specified dictionary, including inheritance flags.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Dictionary details.', schema: dictionaryDetailSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid parameters', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to load dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary',\n description: 'Updates mutable attributes of the dictionary. Currency dictionaries are protected from modification.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: dictionaryUpdateSchema,\n description: 'Fields to update on the dictionary.',\n },\n responses: [\n { status: 200, description: 'Dictionary updated.', schema: dictionaryDetailSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or protected dictionary', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 409, description: 'Dictionary key already exists', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary',\n description: 'Soft deletes the dictionary unless it is the protected currency dictionary.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Dictionary archived.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Protected dictionary cannot be deleted', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary resource',\n pathParams: dictionaryIdParamsSchema,\n methods: {\n GET: dictionaryGetDoc,\n PATCH: dictionaryPatchDoc,\n DELETE: dictionaryDeleteDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext, resolveDictionaryActorId } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n resolveDictionaryEntrySortMode,\n} from '@open-mercato/core/modules/dictionaries/lib/entrySort'\nimport {\n dictionariesErrorSchema,\n dictionariesOkSchema,\n dictionariesTag,\n dictionaryDetailSchema,\n dictionaryIdParamsSchema,\n dictionaryUpdateSchema,\n upsertDictionarySchema,\n} from '../openapi'\nimport { dictionaryKeySchema } from '@open-mercato/core/modules/dictionaries/data/validators'\n\nconst paramsSchema = z.object({ dictionaryId: z.string().uuid() })\n// System dictionaries use namespaced keys (e.g. `sales.deal_loss_reason`,\n// `resources.activity-types`) that the strict create-key regex rejects. The\n// manager edit dialog disables the key field but still resubmits the existing\n// key, so the update parse must accept any stored key verbatim. The strict\n// user-key regex is only enforced below when the key actually changes.\nconst updateKeySchema = z.string().trim().min(1).max(100)\nconst updateSchema = upsertDictionarySchema\n .partial()\n .extend({ key: updateKeySchema.optional() })\n .refine((data) => Object.keys(data).length > 0, {\n message: 'Provide at least one field to update.',\n })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['dictionaries.view'] },\n PATCH: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['dictionaries.manage'] },\n}\n\nfunction isProtectedCurrencyDictionary(dictionary: Dictionary) {\n const key = dictionary.key?.trim().toLowerCase() ?? ''\n return key === 'currency' || key === 'currencies'\n}\n\nasync function loadDictionary(\n context: Awaited<ReturnType<typeof resolveDictionariesRouteContext>>,\n id: string,\n options: { allowInherited?: boolean } = {},\n) {\n const { allowInherited = false } = options\n if (!allowInherited && !context.organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const baseFilter = {\n id,\n tenantId: context.tenantId,\n deletedAt: null,\n }\n const filter = allowInherited\n ? {\n ...baseFilter,\n ...(context.readableOrganizationIds.length\n ? { organizationId: { $in: context.readableOrganizationIds } }\n : {}),\n }\n : {\n ...baseFilter,\n organizationId: context.organizationId,\n }\n const dictionary = await context.em.findOne(Dictionary, filter)\n if (!dictionary) {\n throw new CrudHttpError(404, { error: context.translate('dictionaries.errors.not_found', 'Dictionary not found') })\n }\n return dictionary\n}\n\nexport async function GET(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const dictionary = await loadDictionary(context, dictionaryId, { allowInherited: true })\n return NextResponse.json({\n id: dictionary.id,\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description,\n isSystem: dictionary.isSystem,\n isActive: dictionary.isActive,\n managerVisibility: dictionary.managerVisibility,\n entrySortMode: resolveDictionaryEntrySortMode(dictionary.entrySortMode),\n organizationId: dictionary.organizationId,\n isInherited: context.organizationId ? dictionary.organizationId !== context.organizationId : false,\n createdAt: dictionary.createdAt,\n updatedAt: dictionary.updatedAt,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id.GET] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to load dictionary' }, { status: 500 })\n }\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const payload = updateSchema.parse(await req.json().catch(() => ({})))\n const dictionary = await loadDictionary(context, dictionaryId)\n\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n current: dictionary.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (isProtectedCurrencyDictionary(dictionary)) {\n if (payload.key && payload.key.trim().toLowerCase() !== dictionary.key) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n if (payload.isActive === false) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n }\n\n if (payload.key) {\n const key = payload.key.trim().toLowerCase()\n if (key !== dictionary.key) {\n const strictKey = dictionaryKeySchema.safeParse(key)\n if (!strictKey.success) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.invalid_key', 'Use lowercase letters, numbers, hyphen, or underscore.') })\n }\n const organizationId = context.organizationId\n if (!organizationId) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })\n }\n const existing = await context.em.findOne(Dictionary, {\n key,\n organizationId,\n tenantId: context.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(409, { error: context.translate('dictionaries.errors.duplicate', 'A dictionary with this key already exists') })\n }\n dictionary.key = key\n }\n }\n\n if (payload.name) {\n dictionary.name = payload.name.trim()\n }\n if (payload.description !== undefined) {\n dictionary.description = payload.description ? payload.description.trim() : null\n }\n if (payload.isActive !== undefined) {\n dictionary.isActive = Boolean(payload.isActive)\n if (!dictionary.isActive) {\n dictionary.deletedAt = dictionary.deletedAt ?? new Date()\n } else {\n dictionary.deletedAt = null\n }\n }\n if (payload.entrySortMode !== undefined) {\n dictionary.entrySortMode = payload.entrySortMode\n }\n\n dictionary.updatedAt = new Date()\n await context.em.flush()\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({\n id: dictionary.id,\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description,\n isSystem: dictionary.isSystem,\n isActive: dictionary.isActive,\n managerVisibility: dictionary.managerVisibility,\n entrySortMode: resolveDictionaryEntrySortMode(dictionary.entrySortMode),\n createdAt: dictionary.createdAt,\n updatedAt: dictionary.updatedAt,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: err.issues[0]?.message ?? 'Validation failed' }, { status: 400 })\n }\n console.error('[dictionaries/:id.PATCH] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to update dictionary' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { dictionaryId?: string } }) {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const { dictionaryId } = paramsSchema.parse({ dictionaryId: ctx.params?.dictionaryId })\n const dictionary = await loadDictionary(context, dictionaryId)\n\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n current: dictionary.updatedAt ?? null,\n request: req,\n })\n\n const guardUserId = resolveDictionaryActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (isProtectedCurrencyDictionary(dictionary)) {\n throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })\n }\n\n dictionary.isActive = false\n dictionary.deletedAt = dictionary.deletedAt ?? new Date()\n await context.em.flush()\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[dictionaries/:id.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Failed to delete dictionary' }, { status: 500 })\n }\n}\n\nconst dictionaryGetDoc: OpenApiMethodDoc = {\n summary: 'Get dictionary',\n description: 'Returns details for the specified dictionary, including inheritance flags.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Dictionary details.', schema: dictionaryDetailSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid parameters', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to load dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryPatchDoc: OpenApiMethodDoc = {\n summary: 'Update dictionary',\n description: 'Updates mutable attributes of the dictionary. Currency dictionaries are protected from modification.',\n tags: [dictionariesTag],\n requestBody: {\n contentType: 'application/json',\n schema: dictionaryUpdateSchema,\n description: 'Fields to update on the dictionary.',\n },\n responses: [\n { status: 200, description: 'Dictionary updated.', schema: dictionaryDetailSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or protected dictionary', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 409, description: 'Dictionary key already exists', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to update dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nconst dictionaryDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete dictionary',\n description: 'Soft deletes the dictionary unless it is the protected currency dictionary.',\n tags: [dictionariesTag],\n responses: [\n { status: 200, description: 'Dictionary archived.', schema: dictionariesOkSchema },\n ],\n errors: [\n { status: 400, description: 'Protected dictionary cannot be deleted', schema: dictionariesErrorSchema },\n { status: 401, description: 'Authentication required', schema: dictionariesErrorSchema },\n { status: 404, description: 'Dictionary not found', schema: dictionariesErrorSchema },\n { status: 500, description: 'Failed to delete dictionary', schema: dictionariesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dictionariesTag,\n summary: 'Dictionary resource',\n pathParams: dictionaryIdParamsSchema,\n methods: {\n GET: dictionaryGetDoc,\n PATCH: dictionaryPatchDoc,\n DELETE: dictionaryDeleteDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { useSearchParams } from "next/navigation";
4
+ import { useSearchParams, usePathname } from "next/navigation";
5
5
  import { Book, Plus, Pencil, Trash2 } from "lucide-react";
6
6
  import { EmptyState } from "@open-mercato/ui/primitives/empty-state";
7
7
  import { Button } from "@open-mercato/ui/primitives/button";
@@ -18,6 +18,7 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
18
18
  import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
19
19
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
20
20
  import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
21
+ import { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from "@open-mercato/ui/backend/injection/recordContext";
21
22
  import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
22
23
  import { useT } from "@open-mercato/shared/lib/i18n/context";
23
24
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
@@ -30,6 +31,7 @@ function DictionariesManager() {
30
31
  const t = useT();
31
32
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
32
33
  const searchParams = useSearchParams();
34
+ const pathname = usePathname();
33
35
  const [items, setItems] = React.useState([]);
34
36
  const [selectedId, setSelectedId] = React.useState(null);
35
37
  const [loading, setLoading] = React.useState(true);
@@ -304,6 +306,15 @@ function DictionariesManager() {
304
306
  [confirm, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t]
305
307
  );
306
308
  const selectedDictionary = items.find((item) => item.id === selectedId) ?? null;
309
+ useSetCurrentRecordInjectionContext(
310
+ buildRecordInjectionContext({
311
+ resourceKind: "dictionaries.dictionary",
312
+ resourceId: selectedDictionary?.id ?? null,
313
+ updatedAt: selectedDictionary?.updatedAt ?? null,
314
+ data: selectedDictionary ?? null,
315
+ path: pathname
316
+ })
317
+ );
307
318
  return /* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-[320px_1fr]", children: [
308
319
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 shadow-sm", children: [
309
320
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dictionaries/components/DictionariesManager.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport { Book, Plus, Pencil, Trash2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntriesEditor } from './DictionaryEntriesEditor'\nimport {\n DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n dictionaryEntrySortModes,\n type DictionaryEntrySortMode,\n} from '../lib/entrySort'\n\nexport type DictionarySummary = {\n id: string\n key: string\n name: string\n description?: string | null\n isSystem?: boolean\n isActive?: boolean\n entrySortMode: DictionaryEntrySortMode\n organizationId: string\n isInherited: boolean\n managerVisibility: 'default' | 'hidden'\n updatedAt?: string | null\n}\n\ntype DialogState = {\n mode: 'create' | 'edit'\n dictionary?: DictionarySummary\n}\n\ntype DictionaryFormState = {\n key: string\n name: string\n description: string\n entrySortMode: DictionaryEntrySortMode\n}\n\nexport function DictionariesManager() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const searchParams = useSearchParams()\n const [items, setItems] = React.useState<DictionarySummary[]>([])\n const [selectedId, setSelectedId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<DictionaryFormState>({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n const [errors, setErrors] = React.useState<{ key?: string; name?: string }>({})\n const [submitting, setSubmitting] = React.useState(false)\n const [deleting, setDeleting] = React.useState<string | null>(null)\n const selectionFromQueryApplied = React.useRef(false)\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId?: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'dictionaries:dictionary',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const inheritedManageMessage = t('dictionaries.config.error.inheritedManage', 'Inherited dictionaries must be managed at the parent organization.')\n const requestedDictionaryId = searchParams?.get('dictionaryId') ?? null\n const requestedDictionaryKey = searchParams?.get('key')?.trim().toLowerCase() ?? null\n const entrySortOptions = React.useMemo(\n () => dictionaryEntrySortModes.map((mode) => ({\n value: mode,\n label:\n mode === 'label_asc'\n ? t('dictionaries.config.sortModes.labelAsc', 'A to Z')\n : mode === 'label_desc'\n ? t('dictionaries.config.sortModes.labelDesc', 'Z to A')\n : mode === 'value_asc'\n ? t('dictionaries.config.sortModes.valueAsc', 'Value A to Z')\n : mode === 'value_desc'\n ? t('dictionaries.config.sortModes.valueDesc', 'Value Z to A')\n : mode === 'created_at_asc'\n ? t('dictionaries.config.sortModes.createdAtAsc', 'Oldest first')\n : t('dictionaries.config.sortModes.createdAtDesc', 'Newest first'),\n })),\n [t],\n )\n\n const loadDictionaries = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ items?: unknown[]; error?: string }>('/api/dictionaries')\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to load dictionaries')\n }\n const resultItems = Array.isArray(call.result?.items) ? call.result!.items : []\n const list: DictionarySummary[] = Array.isArray(resultItems)\n ? resultItems.map((item: any): DictionarySummary => ({\n id: String(item.id),\n key: String(item.key),\n name: String(item.name ?? item.key),\n description: typeof item.description === 'string' ? item.description : null,\n isSystem: Boolean(item.isSystem),\n isActive: item.isActive !== false,\n entrySortMode: dictionaryEntrySortModes.includes(item.entrySortMode as DictionaryEntrySortMode)\n ? (item.entrySortMode as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n organizationId: typeof item.organizationId === 'string' ? item.organizationId : '',\n isInherited: item.isInherited === true,\n managerVisibility:\n item.managerVisibility === 'hidden' ? 'hidden' : 'default',\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }))\n : []\n const filtered = list.filter((dictionary: DictionarySummary) => dictionary.managerVisibility !== 'hidden')\n setItems(filtered)\n setSelectedId((current) => {\n if (current && filtered.some((dict: DictionarySummary) => dict.id === current)) {\n return current\n }\n return filtered.length ? filtered[0].id : null\n })\n } catch (err) {\n console.error('Failed to load dictionaries', err)\n flash(t('dictionaries.config.error.load', 'Failed to load dictionaries.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n loadDictionaries().catch(() => {})\n }, [loadDictionaries])\n\n React.useEffect(() => {\n if (selectionFromQueryApplied.current) return\n if (!items.length) return\n if (!requestedDictionaryId && !requestedDictionaryKey) return\n if (requestedDictionaryId) {\n const match = items.find((dictionary) => dictionary.id === requestedDictionaryId)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n return\n }\n if (requestedDictionaryKey) {\n const match = items.find((dictionary) => dictionary.key.toLowerCase() === requestedDictionaryKey)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n }\n }, [items, requestedDictionaryId, requestedDictionaryKey, selectedId])\n\n const openCreateDialog = React.useCallback(() => {\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setDialog({ mode: 'create' })\n setErrors({})\n }, [])\n\n const openEditDialog = React.useCallback((dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n setForm({\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description ?? '',\n entrySortMode: dictionary.entrySortMode,\n })\n setDialog({ mode: 'edit', dictionary })\n setErrors({})\n }, [inheritedManageMessage])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setErrors({})\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n if (dialog.mode === 'edit' && dialog.dictionary?.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n const trimmedKey = form.key.trim()\n const trimmedName = form.name.trim()\n const nextErrors: { key?: string; name?: string } = {}\n if (!trimmedKey) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorRequired', 'Key is required.')\n } else if (trimmedKey.length > 100) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorLength', 'Key must be at most 100 characters long.')\n } else if (!/^[a-z0-9][a-z0-9_-]*$/.test(trimmedKey)) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorPattern', 'Use lowercase letters, numbers, hyphen, or underscore.')\n }\n if (!trimmedName) {\n nextErrors.name = t('dictionaries.config.dialog.nameErrorRequired', 'Name is required.')\n }\n if (nextErrors.key || nextErrors.name) {\n setErrors(nextErrors)\n return\n }\n setSubmitting(true)\n try {\n const payload = {\n key: trimmedKey,\n name: trimmedName,\n description: form.description.trim() || undefined,\n entrySortMode: form.entrySortMode,\n }\n if (dialog.mode === 'create') {\n await runMutation({\n operation: async () => {\n const call = await apiCall<Record<string, unknown>>('/api/dictionaries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to create dictionary')\n }\n return call\n },\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.create', 'Dictionary created.'), 'success')\n } else if (dialog.dictionary) {\n const dictionaryId = dialog.dictionary.id\n const expectedUpdatedAt = dialog.dictionary.updatedAt\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(expectedUpdatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionaryId}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.update', 'Dictionary updated.'), 'success')\n }\n closeDialog()\n await loadDictionaries()\n setErrors({})\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to save dictionary', err)\n flash(t('dictionaries.config.error.save', 'Failed to save dictionary.'), 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.description, form.entrySortMode, form.key, form.name, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t])\n\n const handleDelete = React.useCallback(\n async (dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n if (dictionary.isSystem) {\n flash(t('dictionaries.config.error.system', 'System dictionaries cannot be deleted.'), 'error')\n return\n }\n const rawConfirm = t('dictionaries.config.delete.confirm', { name: dictionary.name })\n const confirmMessage = rawConfirm && rawConfirm !== 'dictionaries.config.delete.confirm'\n ? rawConfirm\n : `Delete dictionary \"${dictionary.name}\"?`\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeleting(dictionary.id)\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dictionary.updatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n retryLastMutation,\n },\n mutationPayload: { id: dictionary.id },\n })\n flash(t('dictionaries.config.success.delete', 'Dictionary deleted.'), 'success')\n await loadDictionaries()\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to delete dictionary', err)\n flash(t('dictionaries.config.error.delete', 'Failed to delete dictionary.'), 'error')\n } finally {\n setDeleting(null)\n }\n },\n [confirm, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t],\n )\n\n const selectedDictionary = items.find((item) => item.id === selectedId) ?? null\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[320px_1fr]\">\n <div className=\"rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold\">\n {t('dictionaries.config.list.title', 'Dictionaries')}\n </h2>\n <Button type=\"button\" size=\"sm\" onClick={openCreateDialog}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('dictionaries.config.list.add', 'New dictionary')}\n </Button>\n </div>\n <div className=\"mt-4 space-y-2\">\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('dictionaries.config.list.loading', 'Loading dictionaries\u2026')}\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('dictionaries.config.list.empty', 'No dictionaries yet. Create one to get started.')}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {items.map((dictionary) => (\n <li key={dictionary.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-pressed={dictionary.id === selectedId}\n className={`flex w-full cursor-pointer select-none items-center justify-between rounded border px-3 py-2 text-left text-sm transition ${\n dictionary.id === selectedId ? 'border-primary bg-primary/5 text-primary' : 'border-border hover:bg-muted'\n }`}\n onClick={() => setSelectedId(dictionary.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setSelectedId(dictionary.id)\n }\n }}\n >\n <div>\n <div className=\"flex items-center gap-2 font-medium\">\n <span>{dictionary.name}</span>\n {dictionary.isInherited ? (\n <span className=\"rounded-full border border-border px-2 py-0.5 text-overline font-normal uppercase tracking-wide text-muted-foreground\">\n {t('dictionaries.config.list.inherited', 'Inherited')}\n </span>\n ) : null}\n </div>\n <div className=\"text-xs text-muted-foreground\">{dictionary.key}</div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(dictionary)\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited || deleting === dictionary.id}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(dictionary)\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n <div>\n {selectedDictionary ? (\n <DictionaryEntriesEditor\n dictionaryId={selectedDictionary.id}\n dictionaryName={selectedDictionary.name}\n readOnly={selectedDictionary.isInherited}\n />\n ) : (\n <EmptyState\n icon={<Book className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('dictionaries.config.entries.placeholder', 'Select a dictionary to manage its entries.')}\n className=\"h-full\"\n />\n )}\n </div>\n\n <Dialog open={dialog != null} onOpenChange={(open) => (open ? undefined : closeDialog())}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'create'\n ? t('dictionaries.config.dialog.createTitle', 'Create dictionary')\n : t('dictionaries.config.dialog.editTitle', 'Edit dictionary')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.keyLabel', 'Key')}</label>\n <input\n value={form.key}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, key: next }))\n if (errors.key) setErrors((prev) => ({ ...prev, key: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.keyPlaceholder', 'slug_name')}\n disabled={dialog?.mode === 'edit'}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:bg-muted ${errors.key ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.key ? 'true' : 'false'}\n aria-describedby=\"dictionary-key-hint\"\n />\n <p\n id=\"dictionary-key-hint\"\n className={`text-xs ${errors.key ? 'text-destructive' : 'text-muted-foreground'}`}\n >\n {errors.key ?? t('dictionaries.config.dialog.keyHint', 'Use lowercase letters, numbers, hyphen, or underscore.')}\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.nameLabel', 'Name')}</label>\n <input\n value={form.name}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, name: next }))\n if (errors.name) setErrors((prev) => ({ ...prev, name: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.namePlaceholder', 'Display name')}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${errors.name ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.name ? 'true' : 'false'}\n />\n {errors.name ? (\n <p className=\"text-xs text-destructive\">{errors.name}</p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.descriptionLabel', 'Description')}</label>\n <textarea\n value={form.description}\n onChange={(event) => setForm((prev) => ({ ...prev, description: event.target.value }))}\n className=\"min-h-[120px] w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={t('dictionaries.config.dialog.descriptionPlaceholder', 'Explain how this dictionary is used (optional).')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.entrySortModeLabel', 'Entry sort order')}</label>\n <Select\n value={form.entrySortMode}\n onValueChange={(next) => setForm((prev) => ({\n ...prev,\n entrySortMode: dictionaryEntrySortModes.includes(next as DictionaryEntrySortMode)\n ? (next as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n }))}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entrySortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-muted-foreground\">\n {t('dictionaries.config.dialog.entrySortModeHelp', 'Controls the order returned by dictionary entry APIs and dropdowns.')}\n </p>\n </div>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog} disabled={submitting}>\n {t('dictionaries.config.dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSubmit} disabled={submitting}>\n {submitting ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('dictionaries.config.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useSearchParams, usePathname } from 'next/navigation'\nimport { Book, Plus, Pencil, Trash2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from '@open-mercato/ui/backend/injection/recordContext'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntriesEditor } from './DictionaryEntriesEditor'\nimport {\n DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n dictionaryEntrySortModes,\n type DictionaryEntrySortMode,\n} from '../lib/entrySort'\n\nexport type DictionarySummary = {\n id: string\n key: string\n name: string\n description?: string | null\n isSystem?: boolean\n isActive?: boolean\n entrySortMode: DictionaryEntrySortMode\n organizationId: string\n isInherited: boolean\n managerVisibility: 'default' | 'hidden'\n updatedAt?: string | null\n}\n\ntype DialogState = {\n mode: 'create' | 'edit'\n dictionary?: DictionarySummary\n}\n\ntype DictionaryFormState = {\n key: string\n name: string\n description: string\n entrySortMode: DictionaryEntrySortMode\n}\n\nexport function DictionariesManager() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const searchParams = useSearchParams()\n const pathname = usePathname()\n const [items, setItems] = React.useState<DictionarySummary[]>([])\n const [selectedId, setSelectedId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<DictionaryFormState>({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n const [errors, setErrors] = React.useState<{ key?: string; name?: string }>({})\n const [submitting, setSubmitting] = React.useState(false)\n const [deleting, setDeleting] = React.useState<string | null>(null)\n const selectionFromQueryApplied = React.useRef(false)\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId?: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'dictionaries:dictionary',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const inheritedManageMessage = t('dictionaries.config.error.inheritedManage', 'Inherited dictionaries must be managed at the parent organization.')\n const requestedDictionaryId = searchParams?.get('dictionaryId') ?? null\n const requestedDictionaryKey = searchParams?.get('key')?.trim().toLowerCase() ?? null\n const entrySortOptions = React.useMemo(\n () => dictionaryEntrySortModes.map((mode) => ({\n value: mode,\n label:\n mode === 'label_asc'\n ? t('dictionaries.config.sortModes.labelAsc', 'A to Z')\n : mode === 'label_desc'\n ? t('dictionaries.config.sortModes.labelDesc', 'Z to A')\n : mode === 'value_asc'\n ? t('dictionaries.config.sortModes.valueAsc', 'Value A to Z')\n : mode === 'value_desc'\n ? t('dictionaries.config.sortModes.valueDesc', 'Value Z to A')\n : mode === 'created_at_asc'\n ? t('dictionaries.config.sortModes.createdAtAsc', 'Oldest first')\n : t('dictionaries.config.sortModes.createdAtDesc', 'Newest first'),\n })),\n [t],\n )\n\n const loadDictionaries = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ items?: unknown[]; error?: string }>('/api/dictionaries')\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to load dictionaries')\n }\n const resultItems = Array.isArray(call.result?.items) ? call.result!.items : []\n const list: DictionarySummary[] = Array.isArray(resultItems)\n ? resultItems.map((item: any): DictionarySummary => ({\n id: String(item.id),\n key: String(item.key),\n name: String(item.name ?? item.key),\n description: typeof item.description === 'string' ? item.description : null,\n isSystem: Boolean(item.isSystem),\n isActive: item.isActive !== false,\n entrySortMode: dictionaryEntrySortModes.includes(item.entrySortMode as DictionaryEntrySortMode)\n ? (item.entrySortMode as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n organizationId: typeof item.organizationId === 'string' ? item.organizationId : '',\n isInherited: item.isInherited === true,\n managerVisibility:\n item.managerVisibility === 'hidden' ? 'hidden' : 'default',\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }))\n : []\n const filtered = list.filter((dictionary: DictionarySummary) => dictionary.managerVisibility !== 'hidden')\n setItems(filtered)\n setSelectedId((current) => {\n if (current && filtered.some((dict: DictionarySummary) => dict.id === current)) {\n return current\n }\n return filtered.length ? filtered[0].id : null\n })\n } catch (err) {\n console.error('Failed to load dictionaries', err)\n flash(t('dictionaries.config.error.load', 'Failed to load dictionaries.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n loadDictionaries().catch(() => {})\n }, [loadDictionaries])\n\n React.useEffect(() => {\n if (selectionFromQueryApplied.current) return\n if (!items.length) return\n if (!requestedDictionaryId && !requestedDictionaryKey) return\n if (requestedDictionaryId) {\n const match = items.find((dictionary) => dictionary.id === requestedDictionaryId)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n return\n }\n if (requestedDictionaryKey) {\n const match = items.find((dictionary) => dictionary.key.toLowerCase() === requestedDictionaryKey)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n }\n }, [items, requestedDictionaryId, requestedDictionaryKey, selectedId])\n\n const openCreateDialog = React.useCallback(() => {\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setDialog({ mode: 'create' })\n setErrors({})\n }, [])\n\n const openEditDialog = React.useCallback((dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n setForm({\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description ?? '',\n entrySortMode: dictionary.entrySortMode,\n })\n setDialog({ mode: 'edit', dictionary })\n setErrors({})\n }, [inheritedManageMessage])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setErrors({})\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n if (dialog.mode === 'edit' && dialog.dictionary?.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n const trimmedKey = form.key.trim()\n const trimmedName = form.name.trim()\n const nextErrors: { key?: string; name?: string } = {}\n if (!trimmedKey) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorRequired', 'Key is required.')\n } else if (trimmedKey.length > 100) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorLength', 'Key must be at most 100 characters long.')\n } else if (!/^[a-z0-9][a-z0-9_-]*$/.test(trimmedKey)) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorPattern', 'Use lowercase letters, numbers, hyphen, or underscore.')\n }\n if (!trimmedName) {\n nextErrors.name = t('dictionaries.config.dialog.nameErrorRequired', 'Name is required.')\n }\n if (nextErrors.key || nextErrors.name) {\n setErrors(nextErrors)\n return\n }\n setSubmitting(true)\n try {\n const payload = {\n key: trimmedKey,\n name: trimmedName,\n description: form.description.trim() || undefined,\n entrySortMode: form.entrySortMode,\n }\n if (dialog.mode === 'create') {\n await runMutation({\n operation: async () => {\n const call = await apiCall<Record<string, unknown>>('/api/dictionaries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to create dictionary')\n }\n return call\n },\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.create', 'Dictionary created.'), 'success')\n } else if (dialog.dictionary) {\n const dictionaryId = dialog.dictionary.id\n const expectedUpdatedAt = dialog.dictionary.updatedAt\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(expectedUpdatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionaryId}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.update', 'Dictionary updated.'), 'success')\n }\n closeDialog()\n await loadDictionaries()\n setErrors({})\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to save dictionary', err)\n flash(t('dictionaries.config.error.save', 'Failed to save dictionary.'), 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.description, form.entrySortMode, form.key, form.name, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t])\n\n const handleDelete = React.useCallback(\n async (dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n if (dictionary.isSystem) {\n flash(t('dictionaries.config.error.system', 'System dictionaries cannot be deleted.'), 'error')\n return\n }\n const rawConfirm = t('dictionaries.config.delete.confirm', { name: dictionary.name })\n const confirmMessage = rawConfirm && rawConfirm !== 'dictionaries.config.delete.confirm'\n ? rawConfirm\n : `Delete dictionary \"${dictionary.name}\"?`\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeleting(dictionary.id)\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dictionary.updatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n retryLastMutation,\n },\n mutationPayload: { id: dictionary.id },\n })\n flash(t('dictionaries.config.success.delete', 'Dictionary deleted.'), 'success')\n await loadDictionaries()\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to delete dictionary', err)\n flash(t('dictionaries.config.error.delete', 'Failed to delete dictionary.'), 'error')\n } finally {\n setDeleting(null)\n }\n },\n [confirm, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t],\n )\n\n const selectedDictionary = items.find((item) => item.id === selectedId) ?? null\n\n // Publish the open dictionary's record context to the AppShell-owned\n // `backend:record:current` mount so the enterprise record_locks widget resolves\n // `dictionaries.dictionary` + id while the manager is open. Editing here is\n // inline/dialog-driven (not a `[id]` detail route or a CrudForm with a stable\n // `injectionSpotId`), so mounting presence on the manager keyed to the selected\n // dictionary is the simplest place to hold the lock \u2014 it mirrors the\n // `dictionaries.dictionary` resourceKind the dictionary PATCH route guards on.\n // Cleared automatically when no dictionary is selected or on unmount.\n useSetCurrentRecordInjectionContext(\n buildRecordInjectionContext({\n resourceKind: 'dictionaries.dictionary',\n resourceId: selectedDictionary?.id ?? null,\n updatedAt: selectedDictionary?.updatedAt ?? null,\n data: (selectedDictionary ?? null) as Record<string, unknown> | null,\n path: pathname,\n }),\n )\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[320px_1fr]\">\n <div className=\"rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold\">\n {t('dictionaries.config.list.title', 'Dictionaries')}\n </h2>\n <Button type=\"button\" size=\"sm\" onClick={openCreateDialog}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('dictionaries.config.list.add', 'New dictionary')}\n </Button>\n </div>\n <div className=\"mt-4 space-y-2\">\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('dictionaries.config.list.loading', 'Loading dictionaries\u2026')}\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('dictionaries.config.list.empty', 'No dictionaries yet. Create one to get started.')}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {items.map((dictionary) => (\n <li key={dictionary.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-pressed={dictionary.id === selectedId}\n className={`flex w-full cursor-pointer select-none items-center justify-between rounded border px-3 py-2 text-left text-sm transition ${\n dictionary.id === selectedId ? 'border-primary bg-primary/5 text-primary' : 'border-border hover:bg-muted'\n }`}\n onClick={() => setSelectedId(dictionary.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setSelectedId(dictionary.id)\n }\n }}\n >\n <div>\n <div className=\"flex items-center gap-2 font-medium\">\n <span>{dictionary.name}</span>\n {dictionary.isInherited ? (\n <span className=\"rounded-full border border-border px-2 py-0.5 text-overline font-normal uppercase tracking-wide text-muted-foreground\">\n {t('dictionaries.config.list.inherited', 'Inherited')}\n </span>\n ) : null}\n </div>\n <div className=\"text-xs text-muted-foreground\">{dictionary.key}</div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(dictionary)\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited || deleting === dictionary.id}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(dictionary)\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n <div>\n {selectedDictionary ? (\n <DictionaryEntriesEditor\n dictionaryId={selectedDictionary.id}\n dictionaryName={selectedDictionary.name}\n readOnly={selectedDictionary.isInherited}\n />\n ) : (\n <EmptyState\n icon={<Book className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('dictionaries.config.entries.placeholder', 'Select a dictionary to manage its entries.')}\n className=\"h-full\"\n />\n )}\n </div>\n\n <Dialog open={dialog != null} onOpenChange={(open) => (open ? undefined : closeDialog())}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'create'\n ? t('dictionaries.config.dialog.createTitle', 'Create dictionary')\n : t('dictionaries.config.dialog.editTitle', 'Edit dictionary')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.keyLabel', 'Key')}</label>\n <input\n value={form.key}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, key: next }))\n if (errors.key) setErrors((prev) => ({ ...prev, key: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.keyPlaceholder', 'slug_name')}\n disabled={dialog?.mode === 'edit'}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:bg-muted ${errors.key ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.key ? 'true' : 'false'}\n aria-describedby=\"dictionary-key-hint\"\n />\n <p\n id=\"dictionary-key-hint\"\n className={`text-xs ${errors.key ? 'text-destructive' : 'text-muted-foreground'}`}\n >\n {errors.key ?? t('dictionaries.config.dialog.keyHint', 'Use lowercase letters, numbers, hyphen, or underscore.')}\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.nameLabel', 'Name')}</label>\n <input\n value={form.name}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, name: next }))\n if (errors.name) setErrors((prev) => ({ ...prev, name: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.namePlaceholder', 'Display name')}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${errors.name ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.name ? 'true' : 'false'}\n />\n {errors.name ? (\n <p className=\"text-xs text-destructive\">{errors.name}</p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.descriptionLabel', 'Description')}</label>\n <textarea\n value={form.description}\n onChange={(event) => setForm((prev) => ({ ...prev, description: event.target.value }))}\n className=\"min-h-[120px] w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={t('dictionaries.config.dialog.descriptionPlaceholder', 'Explain how this dictionary is used (optional).')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.entrySortModeLabel', 'Entry sort order')}</label>\n <Select\n value={form.entrySortMode}\n onValueChange={(next) => setForm((prev) => ({\n ...prev,\n entrySortMode: dictionaryEntrySortModes.includes(next as DictionaryEntrySortMode)\n ? (next as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n }))}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entrySortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-muted-foreground\">\n {t('dictionaries.config.dialog.entrySortModeHelp', 'Controls the order returned by dictionary entry APIs and dropdowns.')}\n </p>\n </div>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog} disabled={submitting}>\n {t('dictionaries.config.dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSubmit} disabled={submitting}>\n {submitting ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('dictionaries.config.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }