@open-mercato/core 0.6.6-develop.6257.1.6d0af84d26 → 0.6.6-develop.6286.1.d985c5c374

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 (208) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/carrier_shipment_idempotency_key/index.js +19 -0
  3. package/dist/generated/entities/carrier_shipment_idempotency_key/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +10 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/catalog/lib/productResolution.js +45 -0
  9. package/dist/modules/catalog/lib/productResolution.js.map +7 -0
  10. package/dist/modules/communication_channels/workers/channel-import-history.js +1 -1
  11. package/dist/modules/communication_channels/workers/channel-import-history.js.map +2 -2
  12. package/dist/modules/currencies/api/fetch-configs/route.js +34 -0
  13. package/dist/modules/currencies/api/fetch-configs/route.js.map +2 -2
  14. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +17 -15
  15. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  16. package/dist/modules/customers/api/companies/[id]/route.js +176 -161
  17. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  18. package/dist/modules/customers/api/people/[id]/route.js +75 -59
  19. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  20. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +17 -10
  21. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  22. package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js +6 -4
  23. package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js.map +2 -2
  24. package/dist/modules/customers/components/detail/AiActionChips.js +5 -2
  25. package/dist/modules/customers/components/detail/AiActionChips.js.map +2 -2
  26. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +19 -21
  27. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  28. package/dist/modules/data_sync/api/runs/[id]/cancel.js +1 -1
  29. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  30. package/dist/modules/data_sync/backend/data-sync/page.js +9 -45
  31. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  32. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +11 -17
  33. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  34. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +12 -10
  35. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  36. package/dist/modules/data_sync/lib/id-mapping.js +46 -1
  37. package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
  38. package/dist/modules/data_sync/lib/sync-engine.js +2 -2
  39. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  40. package/dist/modules/data_sync/lib/syncRunStatus.js +30 -0
  41. package/dist/modules/data_sync/lib/syncRunStatus.js.map +7 -0
  42. package/dist/modules/entities/api/records.js +6 -0
  43. package/dist/modules/entities/api/records.js.map +2 -2
  44. package/dist/modules/feature_toggles/api/global/route.js +17 -15
  45. package/dist/modules/feature_toggles/api/global/route.js.map +2 -2
  46. package/dist/modules/feature_toggles/api/openapi.js +2 -2
  47. package/dist/modules/feature_toggles/api/openapi.js.map +2 -2
  48. package/dist/modules/feature_toggles/commands/global.js +14 -0
  49. package/dist/modules/feature_toggles/commands/global.js.map +2 -2
  50. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +6 -0
  51. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  52. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +3 -1
  53. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +2 -2
  54. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +12 -5
  55. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
  56. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js +10 -1
  57. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js.map +2 -2
  58. package/dist/modules/payment_gateways/api/cancel/route.js +5 -0
  59. package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
  60. package/dist/modules/payment_gateways/api/capture/route.js +5 -0
  61. package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
  62. package/dist/modules/payment_gateways/api/refund/route.js +5 -0
  63. package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
  64. package/dist/modules/payment_gateways/lib/gateway-service.js +52 -25
  65. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  66. package/dist/modules/payment_gateways/lib/status-machine.js +13 -0
  67. package/dist/modules/payment_gateways/lib/status-machine.js.map +2 -2
  68. package/dist/modules/perspectives/api/[tableId]/route.js +8 -2
  69. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  70. package/dist/modules/perspectives/data/entities.js +33 -3
  71. package/dist/modules/perspectives/data/entities.js.map +2 -2
  72. package/dist/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.js +63 -0
  73. package/dist/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.js.map +7 -0
  74. package/dist/modules/perspectives/services/perspectiveService.js +17 -0
  75. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  76. package/dist/modules/planner/commands/availability-weekly.js +33 -1
  77. package/dist/modules/planner/commands/availability-weekly.js.map +2 -2
  78. package/dist/modules/planner/components/AvailabilityRulesEditor.js +14 -2
  79. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  80. package/dist/modules/progress/api/active/route.js +1 -1
  81. package/dist/modules/progress/api/active/route.js.map +2 -2
  82. package/dist/modules/progress/lib/progressService.js.map +1 -1
  83. package/dist/modules/progress/lib/progressServiceImpl.js +33 -8
  84. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  85. package/dist/modules/query_index/api/status.js +19 -24
  86. package/dist/modules/query_index/api/status.js.map +3 -3
  87. package/dist/modules/query_index/lib/coverage.js +35 -0
  88. package/dist/modules/query_index/lib/coverage.js.map +2 -2
  89. package/dist/modules/query_index/lib/engine.js +11 -6
  90. package/dist/modules/query_index/lib/engine.js.map +2 -2
  91. package/dist/modules/resources/api/resource-types.js +5 -37
  92. package/dist/modules/resources/api/resource-types.js.map +2 -2
  93. package/dist/modules/resources/api/resourceTypeCounts.js +35 -0
  94. package/dist/modules/resources/api/resourceTypeCounts.js.map +7 -0
  95. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -1
  96. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +1 -1
  97. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -1
  98. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  99. package/dist/modules/sales/components/documents/ReturnDialog.js +4 -1
  100. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  101. package/dist/modules/sales/components/documents/ShipmentDialog.js +17 -2
  102. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  103. package/dist/modules/sales/di.js +4 -0
  104. package/dist/modules/sales/di.js.map +2 -2
  105. package/dist/modules/sales/services/salesOrderService.js +26 -0
  106. package/dist/modules/sales/services/salesOrderService.js.map +7 -0
  107. package/dist/modules/shipping_carriers/api/shipments/route.js +8 -0
  108. package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
  109. package/dist/modules/shipping_carriers/data/entities.js +38 -0
  110. package/dist/modules/shipping_carriers/data/entities.js.map +2 -2
  111. package/dist/modules/shipping_carriers/data/validators.js +2 -1
  112. package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
  113. package/dist/modules/shipping_carriers/lib/shipment-idempotency.js +76 -0
  114. package/dist/modules/shipping_carriers/lib/shipment-idempotency.js.map +7 -0
  115. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js +2 -1
  116. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js.map +2 -2
  117. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +20 -2
  118. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
  119. package/dist/modules/shipping_carriers/lib/shipping-service.js +82 -43
  120. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  121. package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js +14 -0
  122. package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js.map +7 -0
  123. package/dist/modules/staff/lib/timesheets-ui/ListView.js +15 -6
  124. package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
  125. package/dist/modules/translations/components/TranslationManager.js +9 -18
  126. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  127. package/generated/entities/carrier_shipment_idempotency_key/index.ts +8 -0
  128. package/generated/entities.ids.generated.ts +2 -1
  129. package/generated/entity-fields-registry.ts +10 -0
  130. package/package.json +9 -8
  131. package/src/modules/catalog/lib/productResolution.ts +75 -0
  132. package/src/modules/communication_channels/workers/channel-import-history.ts +1 -1
  133. package/src/modules/currencies/api/fetch-configs/route.ts +36 -0
  134. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +20 -17
  135. package/src/modules/currencies/i18n/de.json +2 -1
  136. package/src/modules/currencies/i18n/en.json +2 -1
  137. package/src/modules/currencies/i18n/es.json +2 -1
  138. package/src/modules/currencies/i18n/pl.json +2 -1
  139. package/src/modules/customers/api/companies/[id]/route.ts +214 -189
  140. package/src/modules/customers/api/people/[id]/route.ts +106 -80
  141. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +19 -12
  142. package/src/modules/customers/components/detail/ActivitiesAddNewMenu.tsx +8 -6
  143. package/src/modules/customers/components/detail/AiActionChips.tsx +6 -3
  144. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +16 -18
  145. package/src/modules/data_sync/api/runs/[id]/cancel.ts +1 -1
  146. package/src/modules/data_sync/backend/data-sync/page.tsx +11 -58
  147. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +13 -20
  148. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +10 -8
  149. package/src/modules/data_sync/lib/id-mapping.ts +58 -1
  150. package/src/modules/data_sync/lib/sync-engine.ts +2 -2
  151. package/src/modules/data_sync/lib/syncRunStatus.ts +39 -0
  152. package/src/modules/entities/api/records.ts +14 -0
  153. package/src/modules/feature_toggles/api/global/route.ts +16 -14
  154. package/src/modules/feature_toggles/api/openapi.ts +2 -2
  155. package/src/modules/feature_toggles/commands/global.ts +15 -0
  156. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +7 -0
  157. package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +2 -0
  158. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +14 -4
  159. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.ts +15 -1
  160. package/src/modules/payment_gateways/api/cancel/route.ts +5 -0
  161. package/src/modules/payment_gateways/api/capture/route.ts +5 -0
  162. package/src/modules/payment_gateways/api/refund/route.ts +5 -0
  163. package/src/modules/payment_gateways/lib/gateway-service.ts +58 -25
  164. package/src/modules/payment_gateways/lib/status-machine.ts +16 -0
  165. package/src/modules/perspectives/api/[tableId]/route.ts +8 -2
  166. package/src/modules/perspectives/data/entities.ts +43 -3
  167. package/src/modules/perspectives/migrations/.snapshot-open-mercato.json +69 -25
  168. package/src/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.ts +65 -0
  169. package/src/modules/perspectives/services/perspectiveService.ts +17 -0
  170. package/src/modules/planner/commands/availability-weekly.ts +48 -1
  171. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +31 -10
  172. package/src/modules/progress/api/active/route.ts +1 -1
  173. package/src/modules/progress/lib/progressService.ts +2 -2
  174. package/src/modules/progress/lib/progressServiceImpl.ts +33 -8
  175. package/src/modules/query_index/api/status.ts +28 -32
  176. package/src/modules/query_index/lib/coverage.ts +63 -0
  177. package/src/modules/query_index/lib/engine.ts +37 -24
  178. package/src/modules/resources/api/resource-types.ts +4 -44
  179. package/src/modules/resources/api/resourceTypeCounts.ts +74 -0
  180. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +1 -1
  181. package/src/modules/resources/backend/resources/resource-types/page.tsx +1 -0
  182. package/src/modules/sales/components/documents/ReturnDialog.tsx +4 -1
  183. package/src/modules/sales/components/documents/ShipmentDialog.tsx +20 -2
  184. package/src/modules/sales/di.ts +6 -0
  185. package/src/modules/sales/services/salesOrderService.ts +38 -0
  186. package/src/modules/shipping_carriers/api/shipments/route.ts +8 -0
  187. package/src/modules/shipping_carriers/data/entities.ts +33 -0
  188. package/src/modules/shipping_carriers/data/validators.ts +1 -0
  189. package/src/modules/shipping_carriers/i18n/de.json +1 -0
  190. package/src/modules/shipping_carriers/i18n/en.json +1 -0
  191. package/src/modules/shipping_carriers/i18n/es.json +1 -0
  192. package/src/modules/shipping_carriers/i18n/pl.json +1 -0
  193. package/src/modules/shipping_carriers/lib/shipment-idempotency.ts +119 -0
  194. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.ts +3 -1
  195. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +24 -2
  196. package/src/modules/shipping_carriers/lib/shipping-service.ts +87 -43
  197. package/src/modules/shipping_carriers/migrations/.snapshot-open-mercato.json +197 -0
  198. package/src/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.ts +14 -0
  199. package/src/modules/staff/i18n/de.json +1 -0
  200. package/src/modules/staff/i18n/en.json +1 -0
  201. package/src/modules/staff/i18n/es.json +1 -0
  202. package/src/modules/staff/i18n/pl.json +1 -0
  203. package/src/modules/staff/lib/timesheets-ui/ListView.tsx +15 -7
  204. package/src/modules/translations/components/TranslationManager.tsx +17 -24
  205. package/src/modules/translations/i18n/de.json +1 -0
  206. package/src/modules/translations/i18n/en.json +1 -0
  207. package/src/modules/translations/i18n/es.json +1 -0
  208. package/src/modules/translations/i18n/pl.json +1 -0
@@ -22,8 +22,8 @@ const featureToggleSchema = z.object({
22
22
  category: z.string().nullable().optional(),
23
23
  type: toggleTypeSchema,
24
24
  defaultValue: z.any().nullable().optional(),
25
- created_at: z.string().optional(),
26
- updated_at: z.string().optional()
25
+ createdAt: z.string().nullable().optional(),
26
+ updatedAt: z.string().nullable().optional()
27
27
  }).passthrough();
28
28
  const featureToggleListResponseSchema = z.object({
29
29
  items: z.array(featureToggleSchema),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/feature_toggles/api/openapi.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport {\n toggleCreateSchema,\n toggleUpdateSchema,\n toggleTypeSchema,\n changeOverrideStateBaseSchema,\n overrideListQuerySchema,\n getToggleOverrideQuerySchema,\n featureToggleOverrideResponseSchema,\n overrideRowIdSchema,\n} from '../data/validators'\n\nexport const featureTogglesTag = 'Feature Toggles'\n\nexport const featureToggleErrorSchema = z\n .object({\n error: z.string(),\n details: z.any().optional(),\n })\n .passthrough()\n\nexport const featureToggleSchema = z\n .object({\n id: z.string().uuid(),\n identifier: z.string(),\n name: z.string(),\n description: z.string().nullable().optional(),\n category: z.string().nullable().optional(),\n type: toggleTypeSchema,\n defaultValue: z.any().nullable().optional(),\n created_at: z.string().optional(),\n updated_at: z.string().optional(),\n })\n .passthrough()\n\nexport const featureToggleListResponseSchema = z.object({\n items: z.array(featureToggleSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n})\n\nexport { toggleCreateSchema, toggleUpdateSchema }\n\n// Documents the actual override-list row shape returned by GET /api/feature_toggles/overrides\n// (see lib/queries.ts \u2192 getOverrides). Each row reports whether the tenant has a\n// per-tenant override via `isOverride`; inherited rows (no override) carry an\n// empty-string `id` sentinel instead of a UUID, hence `overrideRowIdSchema`.\nexport const featureToggleOverrideSchema = z\n .object({\n id: overrideRowIdSchema,\n toggleId: z.string().uuid(),\n tenantName: z.string(),\n tenantId: z.string().uuid(),\n identifier: z.string(),\n name: z.string(),\n category: z.string(),\n isOverride: z.boolean(),\n })\n .passthrough()\n\nexport const featureToggleOverrideListResponseSchema = z.object({\n items: z.array(featureToggleOverrideSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n isSuperAdmin: z.boolean(),\n})\n\n\nexport { changeOverrideStateBaseSchema, overrideListQuerySchema, getToggleOverrideQuerySchema, featureToggleOverrideResponseSchema }\n\nexport const changeOverrideStateResponseSchema = z.object({\n ok: z.literal(true),\n overrideToggleId: z.string().uuid().nullable(),\n})\n\nexport const checkResponseSchema = z.object({\n enabled: z.boolean(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkStringResponseSchema = z.object({\n valueType: z.literal('string'),\n value: z.string(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkNumberResponseSchema = z.object({\n valueType: z.literal('number'),\n value: z.number(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkJsonResponseSchema = z.object({\n valueType: z.literal('json'),\n value: z.unknown(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport {\n toggleCreateSchema,\n toggleUpdateSchema,\n toggleTypeSchema,\n changeOverrideStateBaseSchema,\n overrideListQuerySchema,\n getToggleOverrideQuerySchema,\n featureToggleOverrideResponseSchema,\n overrideRowIdSchema,\n} from '../data/validators'\n\nexport const featureTogglesTag = 'Feature Toggles'\n\nexport const featureToggleErrorSchema = z\n .object({\n error: z.string(),\n details: z.any().optional(),\n })\n .passthrough()\n\nexport const featureToggleSchema = z\n .object({\n id: z.string().uuid(),\n identifier: z.string(),\n name: z.string(),\n description: z.string().nullable().optional(),\n category: z.string().nullable().optional(),\n type: toggleTypeSchema,\n defaultValue: z.any().nullable().optional(),\n createdAt: z.string().nullable().optional(),\n updatedAt: z.string().nullable().optional(),\n })\n .passthrough()\n\nexport const featureToggleListResponseSchema = z.object({\n items: z.array(featureToggleSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n})\n\nexport { toggleCreateSchema, toggleUpdateSchema }\n\n// Documents the actual override-list row shape returned by GET /api/feature_toggles/overrides\n// (see lib/queries.ts \u2192 getOverrides). Each row reports whether the tenant has a\n// per-tenant override via `isOverride`; inherited rows (no override) carry an\n// empty-string `id` sentinel instead of a UUID, hence `overrideRowIdSchema`.\nexport const featureToggleOverrideSchema = z\n .object({\n id: overrideRowIdSchema,\n toggleId: z.string().uuid(),\n tenantName: z.string(),\n tenantId: z.string().uuid(),\n identifier: z.string(),\n name: z.string(),\n category: z.string(),\n isOverride: z.boolean(),\n })\n .passthrough()\n\nexport const featureToggleOverrideListResponseSchema = z.object({\n items: z.array(featureToggleOverrideSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n isSuperAdmin: z.boolean(),\n})\n\n\nexport { changeOverrideStateBaseSchema, overrideListQuerySchema, getToggleOverrideQuerySchema, featureToggleOverrideResponseSchema }\n\nexport const changeOverrideStateResponseSchema = z.object({\n ok: z.literal(true),\n overrideToggleId: z.string().uuid().nullable(),\n})\n\nexport const checkResponseSchema = z.object({\n enabled: z.boolean(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkStringResponseSchema = z.object({\n valueType: z.literal('string'),\n value: z.string(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkNumberResponseSchema = z.object({\n valueType: z.literal('number'),\n value: z.number(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n\nexport const checkJsonResponseSchema = z.object({\n valueType: z.literal('json'),\n value: z.unknown(),\n source: z.enum(['override', 'default', 'fallback', 'missing']),\n toggleId: z.string(),\n identifier: z.string(),\n tenantId: z.string(),\n}).passthrough()\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { toggleCreateSchema, toggleUpdateSchema } from "../data/validators.js";
3
3
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
4
  import { registerCommand } from "@open-mercato/shared/lib/commands";
5
5
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
6
+ import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
6
7
  import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, requireId } from "@open-mercato/shared/lib/commands/helpers";
7
8
  import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
8
9
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
@@ -14,6 +15,7 @@ function assertGlobalToggleSuperAdmin(ctx) {
14
15
  }
15
16
  }
16
17
  const featureToggleCrudIndexer = { entityType: E.feature_toggles.feature_toggle };
18
+ const FEATURE_TOGGLE_LOCK_RESOURCE_KIND = "feature_toggles.feature_toggle";
17
19
  function featureToggleIdentifiers(toggle, ctx) {
18
20
  return {
19
21
  id: toggle.id,
@@ -173,6 +175,12 @@ const updateToggleCommand = {
173
175
  const em = ctx.container.resolve("em").fork();
174
176
  const toggle = await em.findOne(FeatureToggle, { id: parsed.id, deletedAt: null });
175
177
  if (!toggle) throw new CrudHttpError(404, { error: "Toggle not found" });
178
+ enforceCommandOptimisticLock({
179
+ resourceKind: FEATURE_TOGGLE_LOCK_RESOURCE_KIND,
180
+ resourceId: toggle.id,
181
+ current: toggle.updatedAt ?? null,
182
+ request: ctx.request ?? null
183
+ });
176
184
  const previousIdentifier = toggle.identifier;
177
185
  if (parsed.identifier && parsed.identifier !== toggle.identifier) {
178
186
  const existing = await em.findOne(FeatureToggle, { identifier: parsed.identifier });
@@ -294,6 +302,12 @@ const deleteToggleCommand = {
294
302
  const em = ctx.container.resolve("em").fork();
295
303
  const toggle = await em.findOne(FeatureToggle, { id, deletedAt: null });
296
304
  if (!toggle) throw new CrudHttpError(404, { error: "Feature toggle not found" });
305
+ enforceCommandOptimisticLock({
306
+ resourceKind: FEATURE_TOGGLE_LOCK_RESOURCE_KIND,
307
+ resourceId: toggle.id,
308
+ current: toggle.updatedAt ?? null,
309
+ request: ctx.request ?? null
310
+ });
297
311
  const featureTogglesService = ctx.container.resolve("featureTogglesService");
298
312
  await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier);
299
313
  toggle.deletedAt = /* @__PURE__ */ new Date();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/feature_toggles/commands/global.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { FeatureToggle, FeatureToggleOverride } from '../data/entities'\nimport { ToggleCreateInput, toggleCreateSchema, ToggleUpdateInput, toggleUpdateSchema } from '../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, requireId } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { FeatureTogglesService } from '../lib/feature-flag-check'\nimport { E } from '#generated/entities.ids.generated'\n\nfunction assertGlobalToggleSuperAdmin(ctx: { auth?: { [key: string]: unknown } | null; systemActor?: boolean }): void {\n // Trusted server-side callers (CLI seed-defaults/toggle-*, tenant setup) run\n // without an authenticated actor and opt in via `systemActor`. HTTP request\n // paths never set it and always carry a real `auth` actor, so an authenticated\n // but non-super-admin caller \u2014 the cross-tenant escalation vector (#2266) \u2014\n // stays denied.\n if (ctx.systemActor === true) return\n if (ctx.auth?.isSuperAdmin !== true) {\n throw new CrudHttpError(403, { error: 'Global feature toggles can only be managed by a super administrator.' })\n }\n}\n\ntype ToggleSnapshot = {\n id: string\n identifier: string\n name: string\n description: string | null\n category: string | null\n type: 'boolean' | 'string' | 'number' | 'json'\n defaultValue: any\n}\n\ntype OverrideSnapshot = {\n id: string\n toggleId: string\n tenantId: string\n value?: any\n}\n\ntype ToggleUndoPayload = {\n after?: ToggleSnapshot | null\n before?: ToggleSnapshot | null\n overrides?: OverrideSnapshot[]\n}\n\nconst featureToggleCrudIndexer = { entityType: E.feature_toggles.feature_toggle }\n\nfunction featureToggleIdentifiers(\n toggle: FeatureToggle | ToggleSnapshot,\n ctx: { auth?: { tenantId?: string | null } | null },\n) {\n return {\n id: toggle.id,\n organizationId: null,\n tenantId: ctx.auth?.tenantId ?? null,\n }\n}\n\nasync function loadToggleSnapshot(em: EntityManager, id: string): Promise<ToggleSnapshot | null> {\n const toggle = await em.findOne(FeatureToggle, { id })\n if (!toggle) return null\n return {\n id: toggle.id,\n identifier: toggle.identifier,\n name: toggle.name,\n description: toggle.description ?? null,\n category: toggle.category ?? null,\n type: toggle.type ?? 'boolean',\n defaultValue: toggle.defaultValue ?? null,\n }\n}\n\nasync function loadOverrideSnapshots(em: EntityManager, toggleId: string): Promise<OverrideSnapshot[]> {\n const overrides = await em.find(FeatureToggleOverride, { toggle: toggleId })\n return overrides.map(o => ({\n id: o.id,\n toggleId: o.toggle.id,\n tenantId: o.tenantId,\n value: o.value,\n }))\n}\n\nconst createToggleCommand: CommandHandler<ToggleCreateInput, { toggleId: string }> = {\n id: 'feature_toggles.global.create',\n async execute(rawInput, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const parsed = toggleCreateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(FeatureToggle, { identifier: parsed.identifier })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Feature toggle identifier already exists' })\n }\n const toggle = em.create(FeatureToggle, {\n identifier: parsed.identifier,\n name: parsed.name,\n description: parsed.description,\n category: parsed.category,\n type: parsed.type,\n defaultValue: parsed.defaultValue,\n })\n em.persist(toggle)\n await em.flush()\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n\n return { toggleId: toggle.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadToggleSnapshot(em, result.toggleId)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadToggleSnapshot(em, result.toggleId)\n return {\n actionLabel: translate('feature_toggles.audit.toggles.create', 'Create toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: result.toggleId,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot ?? null,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const toggleId = logEntry?.resourceId ?? null\n if (!toggleId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const overrides = await em.find(FeatureToggleOverride, { toggle: toggleId })\n if (overrides.length > 0) {\n em.remove(overrides)\n }\n const toggle = await em.findOne(FeatureToggle, { id: toggleId })\n if (toggle) {\n em.remove(toggle)\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<ToggleSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for toggle create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: after.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: after.id,\n identifier: after.identifier,\n name: after.name,\n description: after.description,\n category: after.category,\n type: after.type,\n defaultValue: after.defaultValue,\n })\n em.persist(toggle)\n } else {\n toggle.deletedAt = null\n toggle.identifier = after.identifier\n toggle.name = after.name\n toggle.description = after.description\n toggle.category = after.category\n toggle.type = after.type\n toggle.defaultValue = after.defaultValue\n }\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n return { toggleId: toggle.id }\n },\n}\n\nconst updateToggleCommand: CommandHandler<ToggleUpdateInput, { toggleId: string }> = {\n id: 'feature_toggles.global.update',\n async prepare(rawInput, ctx) {\n const parsed = toggleUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadToggleSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const parsed = toggleUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const toggle = await em.findOne(FeatureToggle, { id: parsed.id, deletedAt: null })\n if (!toggle) throw new CrudHttpError(404, { error: 'Toggle not found' })\n const previousIdentifier = toggle.identifier\n if (parsed.identifier && parsed.identifier !== toggle.identifier) {\n const existing = await em.findOne(FeatureToggle, { identifier: parsed.identifier })\n if (existing && existing.id !== toggle.id) {\n throw new CrudHttpError(400, { error: 'Feature toggle identifier already exists' })\n }\n }\n toggle.identifier = parsed.identifier ?? toggle.identifier\n toggle.name = parsed.name ?? toggle.name\n toggle.description = parsed.description ?? toggle.description\n toggle.category = parsed.category ?? toggle.category\n toggle.type = parsed.type ?? toggle.type\n toggle.defaultValue = parsed.defaultValue ?? toggle.defaultValue\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n if (previousIdentifier !== toggle.identifier) {\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(previousIdentifier)\n }\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n return { toggleId: toggle.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as ToggleSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = await loadToggleSnapshot(em, before.id)\n const changes =\n afterSnapshot && before\n ? buildChanges(\n before as unknown as Record<string, unknown>,\n afterSnapshot as unknown as Record<string, unknown>,\n [\n 'identifier',\n 'name',\n 'description',\n 'category',\n 'failMode',\n 'type',\n 'defaultValue',\n ]\n )\n : {}\n\n return {\n actionLabel: translate('feature_toggles.audit.toggles.update', 'Update toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: before.id,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n changes,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ToggleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: before.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: before.id,\n identifier: before.identifier,\n name: before.name,\n description: before.description,\n category: before.category,\n type: before.type,\n defaultValue: before.defaultValue,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(toggle)\n } else {\n toggle.identifier = before.identifier\n toggle.name = before.name\n toggle.description = before.description\n toggle.category = before.category\n toggle.type = before.type\n toggle.defaultValue = before.defaultValue\n toggle.deletedAt = null\n }\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n }\n}\n\nconst deleteToggleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { toggleId: string }> =\n{\n id: 'feature_toggles.global.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Feature toggle id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadToggleSnapshot(em, id)\n const overrides = await loadOverrideSnapshots(em, id)\n return snapshot ? { before: snapshot, overrides } : {}\n },\n async execute(input, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const id = requireId(input, 'Feature toggle id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const toggle = await em.findOne(FeatureToggle, { id, deletedAt: null })\n if (!toggle) throw new CrudHttpError(404, { error: 'Feature toggle not found' })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n\n toggle.deletedAt = new Date()\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n\n return { toggleId: toggle.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ToggleSnapshot | undefined\n const overrides = (snapshots as any).overrides as OverrideSnapshot[] | undefined\n\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('feature_toggles.audit.toggles.delete', 'Delete toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: before.id,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n overrides,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ToggleUndoPayload>(logEntry)\n const before = payload?.before\n const overrides = payload?.overrides || []\n\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: before.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: before.id,\n identifier: before.identifier,\n name: before.name,\n description: before.description,\n category: before.category,\n type: before.type,\n defaultValue: before.defaultValue,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(toggle)\n\n for (const ov of overrides) {\n const override = em.create(FeatureToggleOverride, {\n id: ov.id,\n toggle: toggle,\n tenantId: ov.tenantId,\n value: ov.value ?? null,\n })\n em.persist(override)\n }\n } else {\n toggle.identifier = before.identifier\n toggle.name = before.name\n toggle.description = before.description\n toggle.category = before.category\n toggle.type = before.type\n toggle.defaultValue = before.defaultValue\n toggle.deletedAt = null\n }\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createToggleCommand)\nregisterCommand(updateToggleCommand)\nregisterCommand(deleteToggleCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { FeatureToggle, FeatureToggleOverride } from '../data/entities'\nimport { ToggleCreateInput, toggleCreateSchema, ToggleUpdateInput, toggleUpdateSchema } from '../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, requireId } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { FeatureTogglesService } from '../lib/feature-flag-check'\nimport { E } from '#generated/entities.ids.generated'\n\nfunction assertGlobalToggleSuperAdmin(ctx: { auth?: { [key: string]: unknown } | null; systemActor?: boolean }): void {\n // Trusted server-side callers (CLI seed-defaults/toggle-*, tenant setup) run\n // without an authenticated actor and opt in via `systemActor`. HTTP request\n // paths never set it and always carry a real `auth` actor, so an authenticated\n // but non-super-admin caller \u2014 the cross-tenant escalation vector (#2266) \u2014\n // stays denied.\n if (ctx.systemActor === true) return\n if (ctx.auth?.isSuperAdmin !== true) {\n throw new CrudHttpError(403, { error: 'Global feature toggles can only be managed by a super administrator.' })\n }\n}\n\ntype ToggleSnapshot = {\n id: string\n identifier: string\n name: string\n description: string | null\n category: string | null\n type: 'boolean' | 'string' | 'number' | 'json'\n defaultValue: any\n}\n\ntype OverrideSnapshot = {\n id: string\n toggleId: string\n tenantId: string\n value?: any\n}\n\ntype ToggleUndoPayload = {\n after?: ToggleSnapshot | null\n before?: ToggleSnapshot | null\n overrides?: OverrideSnapshot[]\n}\n\nconst featureToggleCrudIndexer = { entityType: E.feature_toggles.feature_toggle }\n\nconst FEATURE_TOGGLE_LOCK_RESOURCE_KIND = 'feature_toggles.feature_toggle'\n\nfunction featureToggleIdentifiers(\n toggle: FeatureToggle | ToggleSnapshot,\n ctx: { auth?: { tenantId?: string | null } | null },\n) {\n return {\n id: toggle.id,\n organizationId: null,\n tenantId: ctx.auth?.tenantId ?? null,\n }\n}\n\nasync function loadToggleSnapshot(em: EntityManager, id: string): Promise<ToggleSnapshot | null> {\n const toggle = await em.findOne(FeatureToggle, { id })\n if (!toggle) return null\n return {\n id: toggle.id,\n identifier: toggle.identifier,\n name: toggle.name,\n description: toggle.description ?? null,\n category: toggle.category ?? null,\n type: toggle.type ?? 'boolean',\n defaultValue: toggle.defaultValue ?? null,\n }\n}\n\nasync function loadOverrideSnapshots(em: EntityManager, toggleId: string): Promise<OverrideSnapshot[]> {\n const overrides = await em.find(FeatureToggleOverride, { toggle: toggleId })\n return overrides.map(o => ({\n id: o.id,\n toggleId: o.toggle.id,\n tenantId: o.tenantId,\n value: o.value,\n }))\n}\n\nconst createToggleCommand: CommandHandler<ToggleCreateInput, { toggleId: string }> = {\n id: 'feature_toggles.global.create',\n async execute(rawInput, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const parsed = toggleCreateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(FeatureToggle, { identifier: parsed.identifier })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Feature toggle identifier already exists' })\n }\n const toggle = em.create(FeatureToggle, {\n identifier: parsed.identifier,\n name: parsed.name,\n description: parsed.description,\n category: parsed.category,\n type: parsed.type,\n defaultValue: parsed.defaultValue,\n })\n em.persist(toggle)\n await em.flush()\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n\n return { toggleId: toggle.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadToggleSnapshot(em, result.toggleId)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadToggleSnapshot(em, result.toggleId)\n return {\n actionLabel: translate('feature_toggles.audit.toggles.create', 'Create toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: result.toggleId,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot ?? null,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const toggleId = logEntry?.resourceId ?? null\n if (!toggleId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const overrides = await em.find(FeatureToggleOverride, { toggle: toggleId })\n if (overrides.length > 0) {\n em.remove(overrides)\n }\n const toggle = await em.findOne(FeatureToggle, { id: toggleId })\n if (toggle) {\n em.remove(toggle)\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<ToggleSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for toggle create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: after.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: after.id,\n identifier: after.identifier,\n name: after.name,\n description: after.description,\n category: after.category,\n type: after.type,\n defaultValue: after.defaultValue,\n })\n em.persist(toggle)\n } else {\n toggle.deletedAt = null\n toggle.identifier = after.identifier\n toggle.name = after.name\n toggle.description = after.description\n toggle.category = after.category\n toggle.type = after.type\n toggle.defaultValue = after.defaultValue\n }\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n return { toggleId: toggle.id }\n },\n}\n\nconst updateToggleCommand: CommandHandler<ToggleUpdateInput, { toggleId: string }> = {\n id: 'feature_toggles.global.update',\n async prepare(rawInput, ctx) {\n const parsed = toggleUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadToggleSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const parsed = toggleUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const toggle = await em.findOne(FeatureToggle, { id: parsed.id, deletedAt: null })\n if (!toggle) throw new CrudHttpError(404, { error: 'Toggle not found' })\n enforceCommandOptimisticLock({\n resourceKind: FEATURE_TOGGLE_LOCK_RESOURCE_KIND,\n resourceId: toggle.id,\n current: toggle.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n const previousIdentifier = toggle.identifier\n if (parsed.identifier && parsed.identifier !== toggle.identifier) {\n const existing = await em.findOne(FeatureToggle, { identifier: parsed.identifier })\n if (existing && existing.id !== toggle.id) {\n throw new CrudHttpError(400, { error: 'Feature toggle identifier already exists' })\n }\n }\n toggle.identifier = parsed.identifier ?? toggle.identifier\n toggle.name = parsed.name ?? toggle.name\n toggle.description = parsed.description ?? toggle.description\n toggle.category = parsed.category ?? toggle.category\n toggle.type = parsed.type ?? toggle.type\n toggle.defaultValue = parsed.defaultValue ?? toggle.defaultValue\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n if (previousIdentifier !== toggle.identifier) {\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(previousIdentifier)\n }\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n return { toggleId: toggle.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as ToggleSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = await loadToggleSnapshot(em, before.id)\n const changes =\n afterSnapshot && before\n ? buildChanges(\n before as unknown as Record<string, unknown>,\n afterSnapshot as unknown as Record<string, unknown>,\n [\n 'identifier',\n 'name',\n 'description',\n 'category',\n 'failMode',\n 'type',\n 'defaultValue',\n ]\n )\n : {}\n\n return {\n actionLabel: translate('feature_toggles.audit.toggles.update', 'Update toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: before.id,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n changes,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ToggleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: before.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: before.id,\n identifier: before.identifier,\n name: before.name,\n description: before.description,\n category: before.category,\n type: before.type,\n defaultValue: before.defaultValue,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(toggle)\n } else {\n toggle.identifier = before.identifier\n toggle.name = before.name\n toggle.description = before.description\n toggle.category = before.category\n toggle.type = before.type\n toggle.defaultValue = before.defaultValue\n toggle.deletedAt = null\n }\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n }\n}\n\nconst deleteToggleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { toggleId: string }> =\n{\n id: 'feature_toggles.global.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Feature toggle id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadToggleSnapshot(em, id)\n const overrides = await loadOverrideSnapshots(em, id)\n return snapshot ? { before: snapshot, overrides } : {}\n },\n async execute(input, ctx) {\n assertGlobalToggleSuperAdmin(ctx)\n const id = requireId(input, 'Feature toggle id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const toggle = await em.findOne(FeatureToggle, { id, deletedAt: null })\n if (!toggle) throw new CrudHttpError(404, { error: 'Feature toggle not found' })\n enforceCommandOptimisticLock({\n resourceKind: FEATURE_TOGGLE_LOCK_RESOURCE_KIND,\n resourceId: toggle.id,\n current: toggle.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n\n toggle.deletedAt = new Date()\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n\n return { toggleId: toggle.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ToggleSnapshot | undefined\n const overrides = (snapshots as any).overrides as OverrideSnapshot[] | undefined\n\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('feature_toggles.audit.toggles.delete', 'Delete toggle'),\n resourceKind: 'feature_toggles.global',\n resourceId: before.id,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n overrides,\n } satisfies ToggleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ToggleUndoPayload>(logEntry)\n const before = payload?.before\n const overrides = payload?.overrides || []\n\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let toggle = await em.findOne(FeatureToggle, { id: before.id })\n if (!toggle) {\n toggle = em.create(FeatureToggle, {\n id: before.id,\n identifier: before.identifier,\n name: before.name,\n description: before.description,\n category: before.category,\n type: before.type,\n defaultValue: before.defaultValue,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(toggle)\n\n for (const ov of overrides) {\n const override = em.create(FeatureToggleOverride, {\n id: ov.id,\n toggle: toggle,\n tenantId: ov.tenantId,\n value: ov.value ?? null,\n })\n em.persist(override)\n }\n } else {\n toggle.identifier = before.identifier\n toggle.name = before.name\n toggle.description = before.description\n toggle.category = before.category\n toggle.type = before.type\n toggle.defaultValue = before.defaultValue\n toggle.deletedAt = null\n }\n const featureTogglesService = ctx.container.resolve('featureTogglesService') as FeatureTogglesService\n await featureTogglesService.invalidateIsEnabledCacheByIdentifierTag(toggle.identifier)\n await em.flush()\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: toggle,\n identifiers: featureToggleIdentifiers(toggle, ctx),\n syncOrigin: ctx.syncOrigin,\n indexer: featureToggleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createToggleCommand)\nregisterCommand(updateToggleCommand)\nregisterCommand(deleteToggleCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -104,6 +104,12 @@ function InboxOpsProposalsPage() {
104
104
  React.useEffect(() => {
105
105
  if (initialLoadComplete) loadProposals();
106
106
  }, [page, statusFilter, categoryFilter, search, scopeVersion]);
107
+ React.useEffect(() => {
108
+ if (initialLoadComplete) {
109
+ loadCounts();
110
+ loadSettings();
111
+ }
112
+ }, [scopeVersion]);
107
113
  const handleCopyAddress = React.useCallback(() => {
108
114
  if (settings?.inboxAddress) {
109
115
  navigator.clipboard.writeText(settings.inboxAddress);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/inbox_ops/backend/inbox-ops/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Settings, Inbox, Copy } from 'lucide-react'\nimport { CategoryBadge, useCategoryLabels } from '../../components/proposals/CategoryBadge'\n\ntype ProposalRow = {\n id: string\n summary: string\n confidence: string\n status: string\n category?: string | null\n inboxEmailId: string\n createdAt: string\n participants?: { name: string; email: string }[]\n actionCount?: number\n pendingActionCount?: number\n discrepancyCount?: number\n emailSubject?: string | null\n emailFrom?: string | null\n receivedAt?: string | null\n}\n\ntype ProposalListResponse = {\n items?: ProposalRow[]\n total?: number\n page?: number\n totalPages?: number\n}\n\ntype StatusCounts = {\n pending: number\n partial: number\n accepted: number\n rejected: number\n byCategory?: Record<string, number>\n}\n\nconst STATUS_COLORS: Record<string, string> = {\n pending: 'bg-yellow-100 text-yellow-800',\n partial: 'bg-blue-100 text-blue-800',\n accepted: 'bg-green-100 text-green-800',\n rejected: 'bg-red-100 text-red-800',\n processing: 'bg-purple-100 text-purple-800',\n}\n\nfunction ConfidenceBadge({ value }: { value: string }) {\n const num = parseFloat(value)\n const pct = Math.round(num * 100)\n const color = num >= 0.8 ? 'bg-green-100 text-green-800' : num >= 0.6 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800'\n return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${color}`}>{pct}%</span>\n}\n\nfunction StatusBadge({ status }: { status: string }) {\n const t = useT()\n const statusLabels: Record<string, string> = {\n pending: t('inbox_ops.status.pending', 'Pending'),\n partial: t('inbox_ops.status.partial', 'Partial'),\n accepted: t('inbox_ops.status.accepted', 'Accepted'),\n rejected: t('inbox_ops.status.rejected', 'Rejected'),\n processing: t('inbox_ops.status.processing', 'Processing'),\n }\n const color = STATUS_COLORS[status] || 'bg-gray-100 text-gray-800'\n return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${color}`}>{statusLabels[status] || status}</span>\n}\n\nexport default function InboxOpsProposalsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-proposals',\n })\n\n const [items, setItems] = React.useState<ProposalRow[]>([])\n const [total, setTotal] = React.useState(0)\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [initialLoadComplete, setInitialLoadComplete] = React.useState(false)\n const [counts, setCounts] = React.useState<StatusCounts>({ pending: 0, partial: 0, accepted: 0, rejected: 0 })\n const [settings, setSettings] = React.useState<{ inboxAddress?: string } | null>(null)\n const [copied, setCopied] = React.useState(false)\n\n const statusFilter = typeof filterValues.status === 'string' ? filterValues.status : undefined\n const categoryFilter = typeof filterValues.category === 'string' ? filterValues.category : undefined\n\n const loadProposals = React.useCallback(async () => {\n setIsLoading(true)\n setError(null)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (statusFilter) params.set('status', statusFilter)\n if (categoryFilter) params.set('category', categoryFilter)\n if (search.trim()) params.set('search', search.trim())\n\n try {\n const result = await apiCall<ProposalListResponse>(`/api/inbox_ops/proposals?${params}`)\n if (result?.ok && result.result?.items) {\n setItems(result.result.items)\n setTotal(result.result.total || 0)\n } else {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))\n }\n } catch {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))\n }\n setIsLoading(false)\n }, [page, pageSize, statusFilter, categoryFilter, search, scopeVersion, t])\n\n const loadCounts = React.useCallback(async () => {\n const result = await apiCall<StatusCounts>('/api/inbox_ops/proposals/counts')\n if (result?.ok && result.result) setCounts(result.result)\n }, [scopeVersion])\n\n const loadSettings = React.useCallback(async () => {\n const result = await apiCall<{ settings: { inboxAddress?: string } | null }>('/api/inbox_ops/settings')\n if (result?.ok && result.result?.settings) setSettings(result.result.settings)\n }, [scopeVersion])\n\n React.useEffect(() => {\n Promise.all([loadProposals(), loadCounts(), loadSettings()]).then(() => {\n setInitialLoadComplete(true)\n })\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n React.useEffect(() => {\n if (initialLoadComplete) loadProposals()\n }, [page, statusFilter, categoryFilter, search, scopeVersion]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleCopyAddress = React.useCallback(() => {\n if (settings?.inboxAddress) {\n navigator.clipboard.writeText(settings.inboxAddress)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }, [settings])\n\n const handleRefresh = React.useCallback(() => {\n loadProposals()\n loadCounts()\n }, [loadProposals, loadCounts])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleRejectProposal = React.useCallback(async (proposalId: string) => {\n const confirmed = await confirm({\n title: t('inbox_ops.action.reject_all', 'Reject Proposal'),\n text: t('inbox_ops.action.reject_all_confirm', 'Reject all pending actions in this proposal?'),\n })\n if (!confirmed) return\n\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/reject`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.action.proposal_rejected', 'Proposal rejected'), 'success')\n loadProposals()\n loadCounts()\n } else {\n flash(t('inbox_ops.flash.action_reject_failed', 'Failed to reject'), 'error')\n }\n }, [confirm, t, loadProposals, loadCounts, runMutation])\n\n const categoryLabels = useCategoryLabels()\n const byCategory = counts.byCategory || {}\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('inbox_ops.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'pending', label: `${t('inbox_ops.status.pending', 'Pending')} (${counts.pending})` },\n { value: 'partial', label: `${t('inbox_ops.status.partial', 'Partial')} (${counts.partial})` },\n { value: 'accepted', label: `${t('inbox_ops.status.accepted', 'Accepted')} (${counts.accepted})` },\n { value: 'rejected', label: `${t('inbox_ops.status.rejected', 'Rejected')} (${counts.rejected})` },\n ],\n },\n {\n id: 'category',\n label: t('inbox_ops.category', 'Category'),\n type: 'select',\n options: [\n { value: 'rfq', label: `${categoryLabels.rfq} (${byCategory.rfq || 0})` },\n { value: 'order', label: `${categoryLabels.order} (${byCategory.order || 0})` },\n { value: 'order_update', label: `${categoryLabels.order_update} (${byCategory.order_update || 0})` },\n { value: 'complaint', label: `${categoryLabels.complaint} (${byCategory.complaint || 0})` },\n { value: 'shipping_update', label: `${categoryLabels.shipping_update} (${byCategory.shipping_update || 0})` },\n { value: 'inquiry', label: `${categoryLabels.inquiry} (${byCategory.inquiry || 0})` },\n { value: 'payment', label: `${categoryLabels.payment} (${byCategory.payment || 0})` },\n { value: 'other', label: `${categoryLabels.other} (${byCategory.other || 0})` },\n ],\n },\n ], [t, counts, categoryLabels, byCategory])\n\n const columns: ColumnDef<ProposalRow>[] = React.useMemo(() => [\n {\n accessorKey: 'summary',\n header: t('inbox_ops.summary', 'Summary'),\n cell: ({ row }) => (\n <div className=\"min-w-0\">\n <Link\n href={`/backend/inbox-ops/proposals/${row.original.id}`}\n className=\"text-sm font-medium text-primary hover:underline truncate max-w-[300px] block\"\n >\n {row.original.emailSubject || row.original.summary?.slice(0, 80) || t('inbox_ops.untitled_proposal', 'Untitled proposal')}\n </Link>\n {row.original.emailFrom && (\n <span className=\"text-xs text-muted-foreground truncate block\">{row.original.emailFrom}</span>\n )}\n </div>\n ),\n },\n {\n accessorKey: 'status',\n header: t('inbox_ops.list.status', 'Status'),\n cell: ({ row }) => <StatusBadge status={row.original.status} />,\n },\n {\n accessorKey: 'category',\n header: t('inbox_ops.category', 'Category'),\n cell: ({ row }) => <CategoryBadge category={row.original.category} />,\n },\n {\n id: 'actions_count',\n header: t('inbox_ops.list.progress', 'Progress'),\n cell: ({ row }) => {\n const pending = row.original.pendingActionCount ?? 0\n const total = row.original.actionCount ?? 0\n if (total === 0) return <span className=\"text-sm text-muted-foreground\">\u2014</span>\n return (\n <span className=\"text-sm text-muted-foreground\">\n {t('inbox_ops.list.action_summary', '{pending}/{total} actions')\n .replace('{pending}', String(pending))\n .replace('{total}', String(total))}\n </span>\n )\n },\n },\n {\n accessorKey: 'confidence',\n header: t('inbox_ops.confidence', 'Confidence'),\n cell: ({ row }) => <ConfidenceBadge value={row.original.confidence} />,\n },\n {\n accessorKey: 'receivedAt',\n header: t('inbox_ops.received_at', 'Received'),\n cell: ({ row }) => {\n const dateStr = row.original.receivedAt || row.original.createdAt\n const d = new Date(dateStr)\n return <span className=\"text-sm text-muted-foreground\">{d.toLocaleDateString()} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>\n },\n },\n ], [t])\n\n const totalCount = counts.pending + counts.partial + counts.accepted + counts.rejected\n\n const emptyStateContent = initialLoadComplete && totalCount === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <Inbox className=\"h-12 w-12 text-muted-foreground mb-4\" />\n <h2 className=\"text-lg font-semibold mb-2\">{t('inbox_ops.empty.title', 'Forward emails to start')}</h2>\n {settings?.inboxAddress && (\n <div className=\"mt-4 flex items-center gap-2 bg-muted rounded-lg px-4 py-3\">\n <code className=\"text-sm font-mono\">{settings.inboxAddress}</code>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCopyAddress}>\n <Copy className=\"h-4 w-4\" />\n {copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}\n </Button>\n </div>\n )}\n <ol className=\"mt-6 text-sm text-muted-foreground text-left space-y-2\">\n <li>1. {t('inbox_ops.empty.step1', 'Forward any email thread to this address')}</li>\n <li>2. {t('inbox_ops.empty.step2', \"We'll analyze it and propose actions\")}</li>\n <li>3. {t('inbox_ops.empty.step3', 'Review and accept with one click')}</li>\n </ol>\n </div>\n ) : undefined\n\n if (error && !initialLoadComplete) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <DataTable<ProposalRow>\n stickyActionsColumn\n title={t('inbox_ops.title', 'AI Inbox Actions')}\n refreshButton={{\n label: t('inbox_ops.list.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {settings?.inboxAddress && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCopyAddress}>\n <Copy className=\"h-4 w-4\" />\n <span className=\"hidden md:inline ml-1\">\n {copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}\n </span>\n </Button>\n )}\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href=\"/backend/inbox-ops/settings\">\n <Settings className=\"h-4 w-4\" />\n <span className=\"hidden md:inline ml-1\">{t('inbox_ops.list.actions.settings', 'Settings')}</span>\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={items}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('inbox_ops.list.searchPlaceholder', 'Search proposals...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onRowClick={(row) => router.push(`/backend/inbox-ops/proposals/${row.id}`)}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('inbox_ops.list.actions.view', 'View'),\n onSelect: () => router.push(`/backend/inbox-ops/proposals/${row.id}`),\n },\n ...(row.status === 'pending' || row.status === 'partial' ? [{\n id: 'reject',\n label: t('inbox_ops.list.actions.reject', 'Reject'),\n destructive: true,\n onSelect: () => handleRejectProposal(row.id),\n }] : []),\n ]} />\n )}\n pagination={{\n page,\n pageSize,\n total,\n totalPages: Math.ceil(total / pageSize),\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n emptyState={emptyStateContent}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Settings, Inbox, Copy } from 'lucide-react'\nimport { CategoryBadge, useCategoryLabels } from '../../components/proposals/CategoryBadge'\n\ntype ProposalRow = {\n id: string\n summary: string\n confidence: string\n status: string\n category?: string | null\n inboxEmailId: string\n createdAt: string\n participants?: { name: string; email: string }[]\n actionCount?: number\n pendingActionCount?: number\n discrepancyCount?: number\n emailSubject?: string | null\n emailFrom?: string | null\n receivedAt?: string | null\n}\n\ntype ProposalListResponse = {\n items?: ProposalRow[]\n total?: number\n page?: number\n totalPages?: number\n}\n\ntype StatusCounts = {\n pending: number\n partial: number\n accepted: number\n rejected: number\n byCategory?: Record<string, number>\n}\n\nconst STATUS_COLORS: Record<string, string> = {\n pending: 'bg-yellow-100 text-yellow-800',\n partial: 'bg-blue-100 text-blue-800',\n accepted: 'bg-green-100 text-green-800',\n rejected: 'bg-red-100 text-red-800',\n processing: 'bg-purple-100 text-purple-800',\n}\n\nfunction ConfidenceBadge({ value }: { value: string }) {\n const num = parseFloat(value)\n const pct = Math.round(num * 100)\n const color = num >= 0.8 ? 'bg-green-100 text-green-800' : num >= 0.6 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800'\n return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${color}`}>{pct}%</span>\n}\n\nfunction StatusBadge({ status }: { status: string }) {\n const t = useT()\n const statusLabels: Record<string, string> = {\n pending: t('inbox_ops.status.pending', 'Pending'),\n partial: t('inbox_ops.status.partial', 'Partial'),\n accepted: t('inbox_ops.status.accepted', 'Accepted'),\n rejected: t('inbox_ops.status.rejected', 'Rejected'),\n processing: t('inbox_ops.status.processing', 'Processing'),\n }\n const color = STATUS_COLORS[status] || 'bg-gray-100 text-gray-800'\n return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${color}`}>{statusLabels[status] || status}</span>\n}\n\nexport default function InboxOpsProposalsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-proposals',\n })\n\n const [items, setItems] = React.useState<ProposalRow[]>([])\n const [total, setTotal] = React.useState(0)\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [initialLoadComplete, setInitialLoadComplete] = React.useState(false)\n const [counts, setCounts] = React.useState<StatusCounts>({ pending: 0, partial: 0, accepted: 0, rejected: 0 })\n const [settings, setSettings] = React.useState<{ inboxAddress?: string } | null>(null)\n const [copied, setCopied] = React.useState(false)\n\n const statusFilter = typeof filterValues.status === 'string' ? filterValues.status : undefined\n const categoryFilter = typeof filterValues.category === 'string' ? filterValues.category : undefined\n\n const loadProposals = React.useCallback(async () => {\n setIsLoading(true)\n setError(null)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (statusFilter) params.set('status', statusFilter)\n if (categoryFilter) params.set('category', categoryFilter)\n if (search.trim()) params.set('search', search.trim())\n\n try {\n const result = await apiCall<ProposalListResponse>(`/api/inbox_ops/proposals?${params}`)\n if (result?.ok && result.result?.items) {\n setItems(result.result.items)\n setTotal(result.result.total || 0)\n } else {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))\n }\n } catch {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))\n }\n setIsLoading(false)\n }, [page, pageSize, statusFilter, categoryFilter, search, scopeVersion, t])\n\n const loadCounts = React.useCallback(async () => {\n const result = await apiCall<StatusCounts>('/api/inbox_ops/proposals/counts')\n if (result?.ok && result.result) setCounts(result.result)\n }, [scopeVersion])\n\n const loadSettings = React.useCallback(async () => {\n const result = await apiCall<{ settings: { inboxAddress?: string } | null }>('/api/inbox_ops/settings')\n if (result?.ok && result.result?.settings) setSettings(result.result.settings)\n }, [scopeVersion])\n\n React.useEffect(() => {\n Promise.all([loadProposals(), loadCounts(), loadSettings()]).then(() => {\n setInitialLoadComplete(true)\n })\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n React.useEffect(() => {\n if (initialLoadComplete) loadProposals()\n }, [page, statusFilter, categoryFilter, search, scopeVersion]) // eslint-disable-line react-hooks/exhaustive-deps\n\n React.useEffect(() => {\n if (initialLoadComplete) {\n loadCounts()\n loadSettings()\n }\n }, [scopeVersion]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleCopyAddress = React.useCallback(() => {\n if (settings?.inboxAddress) {\n navigator.clipboard.writeText(settings.inboxAddress)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }, [settings])\n\n const handleRefresh = React.useCallback(() => {\n loadProposals()\n loadCounts()\n }, [loadProposals, loadCounts])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleRejectProposal = React.useCallback(async (proposalId: string) => {\n const confirmed = await confirm({\n title: t('inbox_ops.action.reject_all', 'Reject Proposal'),\n text: t('inbox_ops.action.reject_all_confirm', 'Reject all pending actions in this proposal?'),\n })\n if (!confirmed) return\n\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/reject`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.action.proposal_rejected', 'Proposal rejected'), 'success')\n loadProposals()\n loadCounts()\n } else {\n flash(t('inbox_ops.flash.action_reject_failed', 'Failed to reject'), 'error')\n }\n }, [confirm, t, loadProposals, loadCounts, runMutation])\n\n const categoryLabels = useCategoryLabels()\n const byCategory = counts.byCategory || {}\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('inbox_ops.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'pending', label: `${t('inbox_ops.status.pending', 'Pending')} (${counts.pending})` },\n { value: 'partial', label: `${t('inbox_ops.status.partial', 'Partial')} (${counts.partial})` },\n { value: 'accepted', label: `${t('inbox_ops.status.accepted', 'Accepted')} (${counts.accepted})` },\n { value: 'rejected', label: `${t('inbox_ops.status.rejected', 'Rejected')} (${counts.rejected})` },\n ],\n },\n {\n id: 'category',\n label: t('inbox_ops.category', 'Category'),\n type: 'select',\n options: [\n { value: 'rfq', label: `${categoryLabels.rfq} (${byCategory.rfq || 0})` },\n { value: 'order', label: `${categoryLabels.order} (${byCategory.order || 0})` },\n { value: 'order_update', label: `${categoryLabels.order_update} (${byCategory.order_update || 0})` },\n { value: 'complaint', label: `${categoryLabels.complaint} (${byCategory.complaint || 0})` },\n { value: 'shipping_update', label: `${categoryLabels.shipping_update} (${byCategory.shipping_update || 0})` },\n { value: 'inquiry', label: `${categoryLabels.inquiry} (${byCategory.inquiry || 0})` },\n { value: 'payment', label: `${categoryLabels.payment} (${byCategory.payment || 0})` },\n { value: 'other', label: `${categoryLabels.other} (${byCategory.other || 0})` },\n ],\n },\n ], [t, counts, categoryLabels, byCategory])\n\n const columns: ColumnDef<ProposalRow>[] = React.useMemo(() => [\n {\n accessorKey: 'summary',\n header: t('inbox_ops.summary', 'Summary'),\n cell: ({ row }) => (\n <div className=\"min-w-0\">\n <Link\n href={`/backend/inbox-ops/proposals/${row.original.id}`}\n className=\"text-sm font-medium text-primary hover:underline truncate max-w-[300px] block\"\n >\n {row.original.emailSubject || row.original.summary?.slice(0, 80) || t('inbox_ops.untitled_proposal', 'Untitled proposal')}\n </Link>\n {row.original.emailFrom && (\n <span className=\"text-xs text-muted-foreground truncate block\">{row.original.emailFrom}</span>\n )}\n </div>\n ),\n },\n {\n accessorKey: 'status',\n header: t('inbox_ops.list.status', 'Status'),\n cell: ({ row }) => <StatusBadge status={row.original.status} />,\n },\n {\n accessorKey: 'category',\n header: t('inbox_ops.category', 'Category'),\n cell: ({ row }) => <CategoryBadge category={row.original.category} />,\n },\n {\n id: 'actions_count',\n header: t('inbox_ops.list.progress', 'Progress'),\n cell: ({ row }) => {\n const pending = row.original.pendingActionCount ?? 0\n const total = row.original.actionCount ?? 0\n if (total === 0) return <span className=\"text-sm text-muted-foreground\">\u2014</span>\n return (\n <span className=\"text-sm text-muted-foreground\">\n {t('inbox_ops.list.action_summary', '{pending}/{total} actions')\n .replace('{pending}', String(pending))\n .replace('{total}', String(total))}\n </span>\n )\n },\n },\n {\n accessorKey: 'confidence',\n header: t('inbox_ops.confidence', 'Confidence'),\n cell: ({ row }) => <ConfidenceBadge value={row.original.confidence} />,\n },\n {\n accessorKey: 'receivedAt',\n header: t('inbox_ops.received_at', 'Received'),\n cell: ({ row }) => {\n const dateStr = row.original.receivedAt || row.original.createdAt\n const d = new Date(dateStr)\n return <span className=\"text-sm text-muted-foreground\">{d.toLocaleDateString()} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>\n },\n },\n ], [t])\n\n const totalCount = counts.pending + counts.partial + counts.accepted + counts.rejected\n\n const emptyStateContent = initialLoadComplete && totalCount === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <Inbox className=\"h-12 w-12 text-muted-foreground mb-4\" />\n <h2 className=\"text-lg font-semibold mb-2\">{t('inbox_ops.empty.title', 'Forward emails to start')}</h2>\n {settings?.inboxAddress && (\n <div className=\"mt-4 flex items-center gap-2 bg-muted rounded-lg px-4 py-3\">\n <code className=\"text-sm font-mono\">{settings.inboxAddress}</code>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCopyAddress}>\n <Copy className=\"h-4 w-4\" />\n {copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}\n </Button>\n </div>\n )}\n <ol className=\"mt-6 text-sm text-muted-foreground text-left space-y-2\">\n <li>1. {t('inbox_ops.empty.step1', 'Forward any email thread to this address')}</li>\n <li>2. {t('inbox_ops.empty.step2', \"We'll analyze it and propose actions\")}</li>\n <li>3. {t('inbox_ops.empty.step3', 'Review and accept with one click')}</li>\n </ol>\n </div>\n ) : undefined\n\n if (error && !initialLoadComplete) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <DataTable<ProposalRow>\n stickyActionsColumn\n title={t('inbox_ops.title', 'AI Inbox Actions')}\n refreshButton={{\n label: t('inbox_ops.list.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {settings?.inboxAddress && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCopyAddress}>\n <Copy className=\"h-4 w-4\" />\n <span className=\"hidden md:inline ml-1\">\n {copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}\n </span>\n </Button>\n )}\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href=\"/backend/inbox-ops/settings\">\n <Settings className=\"h-4 w-4\" />\n <span className=\"hidden md:inline ml-1\">{t('inbox_ops.list.actions.settings', 'Settings')}</span>\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={items}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('inbox_ops.list.searchPlaceholder', 'Search proposals...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onRowClick={(row) => router.push(`/backend/inbox-ops/proposals/${row.id}`)}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('inbox_ops.list.actions.view', 'View'),\n onSelect: () => router.push(`/backend/inbox-ops/proposals/${row.id}`),\n },\n ...(row.status === 'pending' || row.status === 'partial' ? [{\n id: 'reject',\n label: t('inbox_ops.list.actions.reject', 'Reject'),\n destructive: true,\n onSelect: () => handleRejectProposal(row.id),\n }] : []),\n ]} />\n )}\n pagination={{\n page,\n pageSize,\n total,\n totalPages: Math.ceil(total / pageSize),\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n emptyState={emptyStateContent}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["total"]
7
7
  }
@@ -53,7 +53,9 @@ function useMessageDetails(id) {
53
53
  attachments: queryState.attachments,
54
54
  isArchived,
55
55
  onDeleted: () => router.push("/backend/messages"),
56
- refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead
56
+ onMarkedUnread: () => router.push("/backend/messages"),
57
+ refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,
58
+ suppressAutoMarkRead: queryState.suppressAutoMarkRead
57
59
  });
58
60
  const conversationState = useMessageDetailsConversation({
59
61
  detail: queryState.detail,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/messages/components/message-detail/hooks/useMessageDetails.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const invalidateMessageQueries = React.useCallback(\n (payload: Record<string, unknown>) => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n const messageId = typeof payload.messageId === 'string' ? payload.messageId : null\n if (messageId && messageId !== id) {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', messageId] })\n }\n },\n [id, queryClient],\n )\n\n useAppEvent(\n 'messages.message.*',\n (evt) => {\n invalidateMessageQueries((evt.payload ?? {}) as Record<string, unknown>)\n },\n [invalidateMessageQueries],\n )\n\n useAppEvent(\n 'om:bridge:reconnected',\n () => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n },\n [id, queryClient],\n )\n\n const isArchived = (queryState.detail?.recipients ?? []).some((item) => item.status === 'archived')\n\n const actionState = useMessageDetailsActions({\n id,\n t,\n detail: queryState.detail,\n detailQuery: queryState.detailQuery,\n attachments: queryState.attachments,\n isArchived,\n onDeleted: () => router.push('/backend/messages'),\n refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,\n })\n\n const conversationState = useMessageDetailsConversation({\n detail: queryState.detail,\n t,\n })\n\n const backToList = React.useCallback(() => {\n router.push('/backend/messages')\n }, [router])\n\n return {\n t,\n router,\n backToList,\n ...queryState,\n ...conversationState,\n ...actionState,\n isArchived,\n }\n}\n"],
5
- "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,2BAA2B,MAAM;AAAA,IACrC,CAAC,YAAqC;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,MAAM,EAAE,CAAC;AACrE,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAC3E,YAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC9E,UAAI,aAAa,cAAc,IAAI;AACjC,aAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,SAAS,EAAE,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA;AAAA,IACE;AAAA,IACA,CAAC,QAAQ;AACP,+BAA0B,IAAI,WAAW,CAAC,CAA6B;AAAA,IACzE;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAEA;AAAA,IACE;AAAA,IACA,MAAM;AACJ,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,WAAW,QAAQ,cAAc,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,UAAU;AAElG,QAAM,cAAc,yBAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,MAAM,OAAO,KAAK,mBAAmB;AAAA,IAChD,kCAAkC,WAAW;AAAA,EAC/C,CAAC;AAED,QAAM,oBAAoB,8BAA8B;AAAA,IACtD,QAAQ,WAAW;AAAA,IACnB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,WAAO,KAAK,mBAAmB;AAAA,EACjC,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const invalidateMessageQueries = React.useCallback(\n (payload: Record<string, unknown>) => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n const messageId = typeof payload.messageId === 'string' ? payload.messageId : null\n if (messageId && messageId !== id) {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', messageId] })\n }\n },\n [id, queryClient],\n )\n\n useAppEvent(\n 'messages.message.*',\n (evt) => {\n invalidateMessageQueries((evt.payload ?? {}) as Record<string, unknown>)\n },\n [invalidateMessageQueries],\n )\n\n useAppEvent(\n 'om:bridge:reconnected',\n () => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n },\n [id, queryClient],\n )\n\n const isArchived = (queryState.detail?.recipients ?? []).some((item) => item.status === 'archived')\n\n const actionState = useMessageDetailsActions({\n id,\n t,\n detail: queryState.detail,\n detailQuery: queryState.detailQuery,\n attachments: queryState.attachments,\n isArchived,\n onDeleted: () => router.push('/backend/messages'),\n onMarkedUnread: () => router.push('/backend/messages'),\n refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,\n suppressAutoMarkRead: queryState.suppressAutoMarkRead,\n })\n\n const conversationState = useMessageDetailsConversation({\n detail: queryState.detail,\n t,\n })\n\n const backToList = React.useCallback(() => {\n router.push('/backend/messages')\n }, [router])\n\n return {\n t,\n router,\n backToList,\n ...queryState,\n ...conversationState,\n ...actionState,\n isArchived,\n }\n}\n"],
5
+ "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,2BAA2B,MAAM;AAAA,IACrC,CAAC,YAAqC;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,MAAM,EAAE,CAAC;AACrE,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAC3E,YAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC9E,UAAI,aAAa,cAAc,IAAI;AACjC,aAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,SAAS,EAAE,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA;AAAA,IACE;AAAA,IACA,CAAC,QAAQ;AACP,+BAA0B,IAAI,WAAW,CAAC,CAA6B;AAAA,IACzE;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAEA;AAAA,IACE;AAAA,IACA,MAAM;AACJ,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,WAAW,QAAQ,cAAc,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,UAAU;AAElG,QAAM,cAAc,yBAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,MAAM,OAAO,KAAK,mBAAmB;AAAA,IAChD,gBAAgB,MAAM,OAAO,KAAK,mBAAmB;AAAA,IACrD,kCAAkC,WAAW;AAAA,IAC7C,sBAAsB,WAAW;AAAA,EACnC,CAAC;AAED,QAAM,oBAAoB,8BAA8B;AAAA,IACtD,QAAQ,WAAW;AAAA,IACnB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,WAAO,KAAK,mBAAmB;AAAA,EACjC,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -12,7 +12,9 @@ function useMessageDetailsActions({
12
12
  attachments,
13
13
  isArchived,
14
14
  onDeleted,
15
- refreshDetailWithoutAutoMarkRead
15
+ onMarkedUnread,
16
+ refreshDetailWithoutAutoMarkRead,
17
+ suppressAutoMarkRead
16
18
  }) {
17
19
  const [replyOpen, setReplyOpen] = React.useState(false);
18
20
  const [forwardOpen, setForwardOpen] = React.useState(false);
@@ -45,6 +47,7 @@ function useMessageDetailsActions({
45
47
  context: { resourceKind: "message", messageId: id, action: "state-change", retryLastMutation },
46
48
  mutationPayload: { messageId: id, action: "state-change", url, method }
47
49
  });
50
+ options?.onSuccess?.();
48
51
  } catch (err) {
49
52
  flash(
50
53
  err instanceof Error ? err.message : t("messages.errors.stateChangeFailed", "Failed to update message state."),
@@ -131,14 +134,15 @@ function useMessageDetailsActions({
131
134
  });
132
135
  }, [id, runConversationAction, t]);
133
136
  const markConversationUnread = React.useCallback(async (messageId) => {
137
+ suppressAutoMarkRead();
134
138
  const targetMessageId = messageId ?? id;
135
139
  await runConversationAction("markAllUnread", {
136
140
  url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation/read`,
137
141
  method: "DELETE",
138
142
  successMessage: t("messages.flash.conversationMarkedUnread", "Conversation marked unread."),
139
- skipDetailAutoMarkRead: true
143
+ onSuccess: onMarkedUnread
140
144
  });
141
- }, [id, runConversationAction, t]);
145
+ }, [id, onMarkedUnread, runConversationAction, suppressAutoMarkRead, t]);
142
146
  const markConversationRead = React.useCallback(async (messageId) => {
143
147
  const targetMessageId = messageId ?? id;
144
148
  await runConversationAction("markAllRead", {
@@ -285,12 +289,15 @@ function useMessageDetailsActions({
285
289
  return byObjectId;
286
290
  }, [detail?.actionData?.actions]);
287
291
  const toggleRead = React.useCallback(async () => {
292
+ if (detail?.isRead) {
293
+ suppressAutoMarkRead();
294
+ }
288
295
  await requestAndRefresh(
289
296
  `/api/messages/${encodeURIComponent(id)}/read`,
290
297
  detail?.isRead ? "DELETE" : "PUT",
291
- detail?.isRead ? { skipDetailAutoMarkRead: true } : void 0
298
+ detail?.isRead ? { skipDetailAutoMarkRead: true, onSuccess: onMarkedUnread } : void 0
292
299
  );
293
- }, [detail?.isRead, id, requestAndRefresh]);
300
+ }, [detail?.isRead, id, onMarkedUnread, requestAndRefresh, suppressAutoMarkRead]);
294
301
  const toggleArchive = React.useCallback(async () => {
295
302
  await requestAndRefresh(
296
303
  `/api/messages/${encodeURIComponent(id)}/archive`,