@open-mercato/core 0.4.6-develop-e321a4e2a1 → 0.4.6-main-24e64eef39

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 (180) hide show
  1. package/AGENTS.md +0 -22
  2. package/dist/modules/api_docs/frontend/docs/api/page.js +1 -1
  3. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  4. package/dist/modules/attachments/api/library/[id]/route.js +1 -0
  5. package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
  6. package/dist/modules/attachments/components/AttachmentLibrary.js +1 -1
  7. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  8. package/dist/modules/attachments/lib/partitionEnv.js +1 -1
  9. package/dist/modules/attachments/lib/partitionEnv.js.map +2 -2
  10. package/dist/modules/auth/backend/users/page.js +1 -1
  11. package/dist/modules/auth/backend/users/page.js.map +2 -2
  12. package/dist/modules/auth/cli.js +1 -1
  13. package/dist/modules/auth/cli.js.map +2 -2
  14. package/dist/modules/auth/commands/users.js +1 -1
  15. package/dist/modules/auth/commands/users.js.map +2 -2
  16. package/dist/modules/business_rules/components/utils/formHelpers.js +1 -1
  17. package/dist/modules/business_rules/components/utils/formHelpers.js.map +2 -2
  18. package/dist/modules/catalog/backend/catalog/products/create/page.js +1 -1
  19. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  20. package/dist/modules/catalog/commands/products.js +1 -1
  21. package/dist/modules/catalog/commands/products.js.map +2 -2
  22. package/dist/modules/catalog/commands/shared.js +1 -1
  23. package/dist/modules/catalog/commands/shared.js.map +2 -2
  24. package/dist/modules/catalog/components/PriceKindSettings.js +1 -1
  25. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  26. package/dist/modules/catalog/components/products/productForm.js +1 -1
  27. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  28. package/dist/modules/configs/lib/upgrade-actions.js.map +1 -1
  29. package/dist/modules/currencies/services/providers/raiffeisen.js +1 -1
  30. package/dist/modules/currencies/services/providers/raiffeisen.js.map +2 -2
  31. package/dist/modules/customers/backend/customers/companies/page.js +3 -3
  32. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  33. package/dist/modules/customers/backend/customers/deals/page.js +3 -3
  34. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  35. package/dist/modules/customers/backend/customers/people/page.js +3 -3
  36. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  37. package/dist/modules/customers/cli.js +2 -2
  38. package/dist/modules/customers/cli.js.map +2 -2
  39. package/dist/modules/customers/lib/detailHelpers.js +1 -1
  40. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  41. package/dist/modules/entities/cli.js +1 -1
  42. package/dist/modules/entities/cli.js.map +2 -2
  43. package/dist/modules/entities/lib/field-definitions.js +1 -1
  44. package/dist/modules/entities/lib/field-definitions.js.map +2 -2
  45. package/dist/modules/entities/lib/install-from-ce.js +1 -1
  46. package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
  47. package/dist/modules/inbox_ops/lib/emailParser.js +1 -1
  48. package/dist/modules/inbox_ops/lib/emailParser.js.map +2 -2
  49. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +0 -8
  50. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
  51. package/dist/modules/perspectives/services/perspectiveService.js +1 -1
  52. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  53. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -3
  54. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  55. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -7
  56. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  57. package/dist/modules/query_index/lib/engine.js +1 -1
  58. package/dist/modules/query_index/lib/engine.js.map +2 -2
  59. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  60. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  61. package/dist/modules/resources/backend/resources/resources/page.js +3 -3
  62. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  63. package/dist/modules/resources/commands/resources.js +1 -1
  64. package/dist/modules/resources/commands/resources.js.map +2 -2
  65. package/dist/modules/resources/lib/seeds.js +1 -1
  66. package/dist/modules/resources/lib/seeds.js.map +2 -2
  67. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +1 -1
  68. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
  69. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +1 -1
  70. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
  71. package/dist/modules/sales/backend/sales/channels/page.js +3 -3
  72. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  73. package/dist/modules/sales/commands/documents.js +2 -2
  74. package/dist/modules/sales/commands/documents.js.map +2 -2
  75. package/dist/modules/sales/components/channels/offerTableUtils.js +2 -3
  76. package/dist/modules/sales/components/channels/offerTableUtils.js.map +2 -2
  77. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +4 -4
  78. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  79. package/dist/modules/sales/components/documents/ShipmentDialog.js +1 -1
  80. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  81. package/dist/modules/sales/lib/shipments/snapshots.js.map +1 -1
  82. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -8
  83. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
  84. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -8
  85. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +2 -2
  86. package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -3
  87. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  88. package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -3
  89. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  90. package/dist/modules/staff/backend/staff/team-members/page.js +3 -3
  91. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  92. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  93. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  94. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -3
  95. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  96. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  97. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  98. package/dist/modules/workflows/backend/instances/page.js +2 -2
  99. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  100. package/dist/modules/workflows/backend/tasks/page.js +1 -1
  101. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  102. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
  103. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  104. package/dist/modules/workflows/lib/graph-utils.js +1 -1
  105. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  106. package/package.json +2 -2
  107. package/src/modules/api_docs/frontend/docs/api/page.tsx +1 -1
  108. package/src/modules/attachments/api/library/[id]/route.ts +1 -0
  109. package/src/modules/attachments/components/AttachmentLibrary.tsx +1 -1
  110. package/src/modules/attachments/lib/partitionEnv.ts +1 -1
  111. package/src/modules/auth/backend/users/page.tsx +1 -1
  112. package/src/modules/auth/cli.ts +1 -1
  113. package/src/modules/auth/commands/users.ts +1 -1
  114. package/src/modules/business_rules/components/utils/formHelpers.ts +1 -1
  115. package/src/modules/catalog/backend/catalog/products/create/page.tsx +1 -1
  116. package/src/modules/catalog/commands/products.ts +1 -1
  117. package/src/modules/catalog/commands/shared.ts +1 -1
  118. package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
  119. package/src/modules/catalog/components/products/productForm.ts +1 -1
  120. package/src/modules/configs/lib/upgrade-actions.ts +1 -1
  121. package/src/modules/currencies/services/providers/raiffeisen.ts +1 -1
  122. package/src/modules/customers/backend/customers/companies/page.tsx +3 -3
  123. package/src/modules/customers/backend/customers/deals/page.tsx +3 -3
  124. package/src/modules/customers/backend/customers/people/page.tsx +3 -3
  125. package/src/modules/customers/cli.ts +2 -2
  126. package/src/modules/customers/lib/detailHelpers.ts +1 -1
  127. package/src/modules/entities/cli.ts +1 -1
  128. package/src/modules/entities/lib/field-definitions.ts +1 -1
  129. package/src/modules/entities/lib/install-from-ce.ts +1 -1
  130. package/src/modules/inbox_ops/lib/emailParser.ts +1 -1
  131. package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +0 -8
  132. package/src/modules/perspectives/services/perspectiveService.ts +1 -1
  133. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -3
  134. package/src/modules/query_index/components/QueryIndexesTable.tsx +7 -7
  135. package/src/modules/query_index/lib/engine.ts +1 -1
  136. package/src/modules/resources/backend/resources/resource-types/page.tsx +3 -2
  137. package/src/modules/resources/backend/resources/resources/page.tsx +3 -3
  138. package/src/modules/resources/commands/resources.ts +1 -1
  139. package/src/modules/resources/lib/seeds.ts +1 -1
  140. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +1 -1
  141. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +1 -1
  142. package/src/modules/sales/backend/sales/channels/page.tsx +3 -3
  143. package/src/modules/sales/commands/documents.ts +2 -2
  144. package/src/modules/sales/components/channels/offerTableUtils.tsx +2 -3
  145. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +4 -4
  146. package/src/modules/sales/components/documents/ShipmentDialog.tsx +1 -1
  147. package/src/modules/sales/lib/shipments/snapshots.ts +1 -1
  148. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -8
  149. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -8
  150. package/src/modules/staff/backend/staff/leave-requests/page.tsx +3 -3
  151. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +3 -3
  152. package/src/modules/staff/backend/staff/team-members/page.tsx +3 -3
  153. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  154. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -3
  155. package/src/modules/staff/backend/staff/teams/page.tsx +3 -2
  156. package/src/modules/workflows/backend/instances/page.tsx +2 -2
  157. package/src/modules/workflows/backend/tasks/page.tsx +1 -1
  158. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
  159. package/src/modules/workflows/lib/graph-utils.ts +1 -1
  160. package/dist/modules/integrations/acl.js +0 -8
  161. package/dist/modules/integrations/acl.js.map +0 -7
  162. package/dist/modules/integrations/data/enrichers.js +0 -72
  163. package/dist/modules/integrations/data/enrichers.js.map +0 -7
  164. package/dist/modules/integrations/data/entities.js +0 -63
  165. package/dist/modules/integrations/data/entities.js.map +0 -7
  166. package/dist/modules/integrations/index.js +0 -9
  167. package/dist/modules/integrations/index.js.map +0 -7
  168. package/dist/modules/integrations/setup.js +0 -13
  169. package/dist/modules/integrations/setup.js.map +0 -7
  170. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -69
  171. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +0 -7
  172. package/dist/modules/integrations/widgets/injection-table.js +0 -13
  173. package/dist/modules/integrations/widgets/injection-table.js.map +0 -7
  174. package/src/modules/integrations/acl.ts +0 -4
  175. package/src/modules/integrations/data/enrichers.ts +0 -98
  176. package/src/modules/integrations/data/entities.ts +0 -46
  177. package/src/modules/integrations/index.ts +0 -5
  178. package/src/modules/integrations/setup.ts +0 -11
  179. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -94
  180. package/src/modules/integrations/widgets/injection-table.ts +0 -17
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/resources/lib/seeds.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Dictionary, DictionaryEntry, type DictionaryManagerVisibility } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { normalizeDictionaryValue, sanitizeDictionaryColor, sanitizeDictionaryIcon } from '@open-mercato/core/modules/dictionaries/lib/utils'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { ensureCustomFieldDefinitions } from '@open-mercato/core/modules/entities/lib/field-definitions'\nimport { CustomFieldEntityConfig, CustomFieldValue } from '@open-mercato/core/modules/entities/data/entities'\nimport { setRecordCustomFields } from '@open-mercato/core/modules/entities/lib/helpers'\nimport {\n ResourcesResource,\n ResourcesResourceActivity,\n ResourcesResourceTag,\n ResourcesResourceTagAssignment,\n ResourcesResourceType,\n} from '../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n RESOURCES_RESOURCE_CUSTOM_FIELD_SETS,\n RESOURCES_RESOURCE_FIELDSET_DENTAL_CHAIR,\n RESOURCES_RESOURCE_FIELDSETS,\n RESOURCES_RESOURCE_FIELDSET_HAIR_KIT,\n RESOURCES_RESOURCE_FIELDSET_LAPTOP,\n RESOURCES_RESOURCE_FIELDSET_ROOM,\n RESOURCES_RESOURCE_FIELDSET_SEAT,\n RESOURCES_RESOURCE_FIELDSET_VEHICLE,\n resolveResourcesResourceFieldsetCode,\n} from './resourceCustomFields'\nimport { RESOURCES_CAPACITY_UNIT_DEFAULTS, RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from './capacityUnits'\n\nexport type ResourcesSeedScope = { tenantId: string; organizationId: string }\n\ntype DictionarySeedEntry = {\n value: string\n label?: string\n color?: string | null\n icon?: string | null\n}\n\ntype ResourcesResourceTypeSeed = {\n key: string\n name: string\n description?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\ntype ResourcesResourceTagSeed = {\n key: string\n slug: string\n label: string\n color?: string | null\n description?: string | null\n}\n\ntype ResourcesResourceSeed = {\n key: string\n name: string\n typeKey?: string | null\n tagKeys: string[]\n}\n\ntype ResourcesResourceActivitySeed = {\n resourceKey: string\n activityType: string\n subject?: string | null\n body?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n authorUserId?: string | null\n daysAgo?: number\n customFields?: Record<string, string | number | boolean | null>\n}\n\nconst RESOURCES_ACTIVITY_TYPE_DICTIONARY_KEY = 'resources.activity-types'\nconst RESOURCES_ADDRESS_TYPE_DICTIONARY_KEY = 'resources.address-types'\n\nconst RESOURCES_ACTIVITY_TYPE_DEFAULTS: DictionarySeedEntry[] = [\n { value: 'Planned maintenance', label: 'Planned maintenance', icon: 'lucide:calendar-cog', color: '#2563eb' },\n { value: 'Undergoing maintenance', label: 'Undergoing maintenance', icon: 'lucide:wrench', color: '#f59e0b' },\n { value: 'Maintenance completed', label: 'Maintenance completed', icon: 'lucide:check-circle-2', color: '#16a34a' },\n { value: 'Inspection scheduled', label: 'Inspection scheduled', icon: 'lucide:clipboard-check', color: '#0ea5e9' },\n { value: 'Calibration', label: 'Calibration', icon: 'lucide:gauge', color: '#6366f1' },\n { value: 'Out of service', label: 'Out of service', icon: 'lucide:ban', color: '#ef4444' },\n { value: 'Back in service', label: 'Back in service', icon: 'lucide:refresh-ccw', color: '#22c55e' },\n]\n\nconst RESOURCES_ADDRESS_TYPE_DEFAULTS: DictionarySeedEntry[] = [\n { value: 'main address', label: 'Main address' },\n { value: 'alternative address', label: 'Alternative address' },\n { value: 'current address', label: 'Current address' },\n]\n\nasync function ensureResourceFieldsetConfig(em: EntityManager, scope: ResourcesSeedScope) {\n const now = new Date()\n let config = await em.findOne(CustomFieldEntityConfig, {\n entityId: E.resources.resources_resource,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!config) {\n config = em.create(CustomFieldEntityConfig, {\n entityId: E.resources.resources_resource,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n }\n config.configJson = {\n fieldsets: RESOURCES_RESOURCE_FIELDSETS,\n singleFieldsetPerRecord: true,\n }\n config.isActive = true\n config.updatedAt = now\n em.persist(config)\n}\n\nasync function ensureResourceCustomFields(em: EntityManager, scope: ResourcesSeedScope) {\n await ensureResourceFieldsetConfig(em, scope)\n await ensureCustomFieldDefinitions(em, RESOURCES_RESOURCE_CUSTOM_FIELD_SETS, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.flush()\n}\n\nexport async function seedResourcesCapacityUnits(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n let dictionary = await em.findOne(Dictionary, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n deletedAt: null,\n })\n if (!dictionary) {\n dictionary = em.create(Dictionary, {\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n name: 'Resource capacity units',\n description: 'Units for resource capacity (spots, units, quantity, etc.).',\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isSystem: true,\n isActive: true,\n managerVisibility: 'default' satisfies DictionaryManagerVisibility,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(dictionary)\n await em.flush()\n }\n\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingMap = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const unit of RESOURCES_CAPACITY_UNIT_DEFAULTS) {\n const normalized = unit.value.trim().toLowerCase()\n if (!normalized || existingMap.has(normalized)) continue\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value: unit.value,\n normalizedValue: normalized,\n label: unit.label,\n color: null,\n icon: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nasync function ensureResourcesDictionary(\n em: EntityManager,\n scope: ResourcesSeedScope,\n definition: { key: string; name: string; description: string },\n): Promise<Dictionary> {\n let dictionary = await em.findOne(Dictionary, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: definition.key,\n deletedAt: null,\n })\n if (!dictionary) {\n dictionary = em.create(Dictionary, {\n key: definition.key,\n name: definition.name,\n description: definition.description,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isSystem: true,\n isActive: true,\n managerVisibility: 'default' satisfies DictionaryManagerVisibility,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(dictionary)\n await em.flush()\n }\n return dictionary\n}\n\nexport async function seedResourcesActivityTypes(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const dictionary = await ensureResourcesDictionary(em, scope, {\n key: RESOURCES_ACTIVITY_TYPE_DICTIONARY_KEY,\n name: 'Resource activity types',\n description: 'Activity types for resource timelines (maintenance, inspections, etc.).',\n })\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingByValue = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const seed of RESOURCES_ACTIVITY_TYPE_DEFAULTS) {\n const value = seed.value.trim()\n if (!value) continue\n const normalizedValue = normalizeDictionaryValue(value)\n if (!normalizedValue) continue\n const color = sanitizeDictionaryColor(seed.color)\n const icon = sanitizeDictionaryIcon(seed.icon)\n const existing = existingByValue.get(normalizedValue)\n if (existing) {\n let updated = false\n if (!existing.label?.trim() && (seed.label ?? '').trim()) {\n existing.label = (seed.label ?? value).trim()\n updated = true\n }\n if (color !== undefined && existing.color !== color) {\n existing.color = color\n updated = true\n }\n if (icon !== undefined && existing.icon !== icon) {\n existing.icon = icon\n updated = true\n }\n if (updated) {\n existing.updatedAt = new Date()\n em.persist(existing)\n }\n continue\n }\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value,\n normalizedValue,\n label: (seed.label ?? value).trim(),\n color: color ?? null,\n icon: icon ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nexport async function seedResourcesAddressTypes(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const dictionary = await ensureResourcesDictionary(em, scope, {\n key: RESOURCES_ADDRESS_TYPE_DICTIONARY_KEY,\n name: 'Resource address types',\n description: 'Address types for resources (main, alternative, current).',\n })\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingByValue = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const seed of RESOURCES_ADDRESS_TYPE_DEFAULTS) {\n const value = seed.value.trim()\n if (!value) continue\n const normalizedValue = normalizeDictionaryValue(value)\n if (!normalizedValue) continue\n const existing = existingByValue.get(normalizedValue)\n if (existing) {\n if (!existing.label?.trim() && (seed.label ?? '').trim()) {\n existing.label = (seed.label ?? value).trim()\n existing.updatedAt = new Date()\n em.persist(existing)\n }\n continue\n }\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value,\n normalizedValue,\n label: (seed.label ?? value).trim(),\n color: null,\n icon: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nfunction normalizeAssetTag(value: string): string {\n const trimmed = value.trim()\n if (!trimmed) return 'RESOURCE'\n const upper = trimmed.toUpperCase()\n const normalized = upper.replace(/[^A-Z0-9]+/g, '-').replace(/(?:^-+|-+$)/g, '')\n return normalized || 'RESOURCE'\n}\n\nfunction buildBaseResourceCustomValues(\n resourceName: string,\n seedKey?: string | null,\n): Record<string, string | number | boolean | null> {\n const tagSource = seedKey?.trim().length ? seedKey : resourceName\n const assetTag = normalizeAssetTag(tagSource ?? resourceName)\n return {\n asset_tag: assetTag,\n owner: 'Operations',\n warranty_expires: '2026-12-31',\n ops_notes: `Seeded for ${resourceName}.`,\n }\n}\n\nfunction buildResourceTypeCustomValues(\n fieldsetCode: string,\n seedKey?: string | null,\n): Record<string, string | number | boolean | null> {\n switch (fieldsetCode) {\n case RESOURCES_RESOURCE_FIELDSET_ROOM:\n return {\n room_floor: '1',\n room_zone: 'North Wing',\n room_projector: true,\n room_whiteboard: true,\n room_access_notes: 'Keycard access required after 6pm.',\n }\n case RESOURCES_RESOURCE_FIELDSET_LAPTOP: {\n const serialSource = seedKey?.trim().length ? seedKey : 'resource'\n return {\n laptop_serial: `LT-${normalizeAssetTag(serialSource)}`,\n laptop_cpu: 'Intel i7',\n laptop_ram_gb: 16,\n laptop_storage_gb: 512,\n laptop_os: 'windows',\n laptop_accessories: 'Docking station, charger, spare mouse.',\n }\n }\n case RESOURCES_RESOURCE_FIELDSET_SEAT:\n return {\n seat_style: 'standard',\n seat_heated: false,\n seat_positioning: 'Adjust headrest for extended sessions.',\n }\n case RESOURCES_RESOURCE_FIELDSET_HAIR_KIT:\n return {\n kit_inventory: 'Shears, clippers, comb set, cape, spray bottle.',\n kit_restock_cycle: 'Monthly',\n kit_maintenance: 'Replace clipper blades every quarter.',\n }\n case RESOURCES_RESOURCE_FIELDSET_DENTAL_CHAIR:\n return {\n chair_model: 'DX-300',\n chair_ultrasonic: true,\n chair_last_disinfected: '2025-01-01',\n chair_inspection_notes: 'Monthly inspection scheduled.',\n }\n case RESOURCES_RESOURCE_FIELDSET_VEHICLE:\n return buildVehicleCustomValues(seedKey)\n default:\n return {}\n }\n}\n\ntype VehicleSeedDetails = {\n model: string\n plate: string\n fuelType: string\n mileageKm: number\n lastService: string\n}\n\nconst VEHICLE_SEED_DETAILS: Record<string, VehicleSeedDetails> = {\n 'company-car-1': {\n model: 'Tesla Model 3',\n plate: 'WWA 4K32',\n fuelType: 'electric',\n mileageKm: 18240,\n lastService: '2025-02-18',\n },\n 'company-car-2': {\n model: 'Volvo XC40 Recharge',\n plate: 'WPR 9L18',\n fuelType: 'electric',\n mileageKm: 22610,\n lastService: '2024-12-05',\n },\n}\n\nfunction buildVehicleCustomValues(seedKey?: string | null): Record<string, string | number | boolean | null> {\n const details = seedKey ? VEHICLE_SEED_DETAILS[seedKey] : undefined\n if (!details) {\n return {\n vehicle_plate: 'OM-2034',\n vehicle_model: 'Polestar 2',\n vehicle_fuel_type: 'electric',\n vehicle_mileage_km: 18200,\n vehicle_last_service: '2025-02-18',\n }\n }\n return {\n vehicle_plate: details.plate,\n vehicle_model: details.model,\n vehicle_fuel_type: details.fuelType,\n vehicle_mileage_km: details.mileageKm,\n vehicle_last_service: details.lastService,\n }\n}\n\nasync function fillMissingResourceCustomFields(\n em: EntityManager,\n scope: ResourcesSeedScope,\n resource: ResourcesResource,\n customValues: Record<string, string | number | boolean | null>,\n) {\n const keys = Object.keys(customValues)\n if (!keys.length) return\n const existingValues = await em.find(CustomFieldValue, {\n entityId: E.resources.resources_resource,\n recordId: resource.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fieldKey: { $in: keys },\n })\n const existingKeys = new Set(existingValues.map((value) => value.fieldKey))\n const missingValues: Record<string, string | number | boolean | null> = {}\n for (const key of keys) {\n if (!existingKeys.has(key)) {\n missingValues[key] = customValues[key] ?? null\n }\n }\n if (Object.keys(missingValues).length === 0) return\n await setRecordCustomFields(em, {\n entityId: E.resources.resources_resource,\n recordId: resource.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n values: missingValues,\n })\n}\n\nasync function fillMissingResourceActivityCustomFields(\n em: EntityManager,\n scope: ResourcesSeedScope,\n activity: ResourcesResourceActivity,\n customValues: Record<string, string | number | boolean | null>,\n) {\n const keys = Object.keys(customValues)\n if (!keys.length) return\n const existingValues = await em.find(CustomFieldValue, {\n entityId: E.resources.resources_resource_activity,\n recordId: activity.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fieldKey: { $in: keys },\n })\n const existingKeys = new Set(existingValues.map((value) => value.fieldKey))\n const missingValues: Record<string, string | number | boolean | null> = {}\n for (const key of keys) {\n if (!existingKeys.has(key)) {\n missingValues[key] = customValues[key] ?? null\n }\n }\n if (Object.keys(missingValues).length === 0) return\n await setRecordCustomFields(em, {\n entityId: E.resources.resources_resource_activity,\n recordId: activity.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n values: missingValues,\n })\n}\n\nconst RESOURCE_TYPE_SEEDS: ResourcesResourceTypeSeed[] = [\n {\n key: 'meeting_room',\n name: 'Meeting room',\n description: 'Collaborative space with A/V equipment.',\n appearanceIcon: 'lucide:video',\n appearanceColor: '#2563eb',\n },\n {\n key: 'focus_room',\n name: 'Focus room',\n description: 'Quiet room for heads-down work.',\n appearanceIcon: 'lucide:door-closed',\n appearanceColor: '#0ea5e9',\n },\n {\n key: 'laptop',\n name: 'Engineering laptop',\n description: 'Portable computer for the delivery team.',\n appearanceIcon: 'lucide:cpu',\n appearanceColor: '#14b8a6',\n },\n {\n key: 'company_car',\n name: 'Company car',\n description: 'Shared vehicle for client visits and errands.',\n appearanceIcon: 'lucide:car',\n appearanceColor: '#7c3aed',\n },\n]\n\nconst TAG_SEEDS: ResourcesResourceTagSeed[] = [\n { key: 'room', slug: 'room', label: 'Room', color: '#1d4ed8' },\n { key: 'focus', slug: 'focus', label: 'Focus', color: '#0f766e' },\n { key: 'tech', slug: 'tech', label: 'Tech', color: '#2563eb' },\n { key: 'equipment', slug: 'equipment', label: 'Equipment', color: '#6b21a8' },\n { key: 'vehicle', slug: 'vehicle', label: 'Vehicle', color: '#7c3aed' },\n { key: 'collaboration', slug: 'collaboration', label: 'Collaboration', color: '#14b8a6' },\n]\n\nconst RESOURCE_SEEDS: ResourcesResourceSeed[] = [\n { key: 'meeting-room-a', name: 'Meeting Room A', typeKey: 'meeting_room', tagKeys: ['room', 'collaboration'] },\n { key: 'meeting-room-b', name: 'Meeting Room B', typeKey: 'meeting_room', tagKeys: ['room', 'collaboration'] },\n { key: 'focus-room-1', name: 'Focus Room 1', typeKey: 'focus_room', tagKeys: ['room', 'focus'] },\n { key: 'focus-room-2', name: 'Focus Room 2', typeKey: 'focus_room', tagKeys: ['room', 'focus'] },\n { key: 'engineering-laptop-1', name: 'Engineering Laptop 1', typeKey: 'laptop', tagKeys: ['tech', 'equipment'] },\n { key: 'engineering-laptop-2', name: 'Engineering Laptop 2', typeKey: 'laptop', tagKeys: ['tech', 'equipment'] },\n { key: 'company-car-1', name: 'Tesla Model 3 - WWA 4K32', typeKey: 'company_car', tagKeys: ['vehicle'] },\n { key: 'company-car-2', name: 'Volvo XC40 Recharge - WPR 9L18', typeKey: 'company_car', tagKeys: ['vehicle'] },\n]\n\nconst RESOURCE_ACTIVITY_SEEDS: ResourcesResourceActivitySeed[] = [\n {\n resourceKey: 'meeting-room-a',\n activityType: 'Inspection scheduled',\n subject: 'AV equipment inspection',\n body: 'Quarterly AV inspection scheduled with facilities.',\n appearanceIcon: 'lucide:clipboard-check',\n appearanceColor: '#0ea5e9',\n daysAgo: 10,\n customFields: {\n activity_priority: 'normal',\n work_order: 'WO-1021',\n requires_follow_up: true,\n },\n },\n {\n resourceKey: 'engineering-laptop-1',\n activityType: 'Calibration',\n subject: 'Battery health calibration',\n body: 'Battery calibration completed after OS update.',\n appearanceIcon: 'lucide:gauge',\n appearanceColor: '#6366f1',\n daysAgo: 4,\n customFields: {\n activity_priority: 'low',\n work_order: 'WO-1148',\n requires_follow_up: false,\n },\n },\n {\n resourceKey: 'company-car-1',\n activityType: 'Maintenance completed',\n subject: 'Tire rotation completed',\n body: 'Rotation completed and tire pressure set to spec.',\n appearanceIcon: 'lucide:check-circle-2',\n appearanceColor: '#16a34a',\n daysAgo: 22,\n customFields: {\n activity_priority: 'high',\n work_order: 'WO-0987',\n requires_follow_up: false,\n },\n },\n]\n\nexport async function seedResourcesResourceExamples(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const now = new Date()\n await seedResourcesActivityTypes(em, scope)\n await ensureResourceCustomFields(em, scope)\n const typeNames = RESOURCE_TYPE_SEEDS.map((seed) => seed.name)\n const existingTypes = await findWithDecryption(\n em,\n ResourcesResourceType,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: { $in: typeNames },\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const typeByName = new Map(existingTypes.map((type) => [type.name.toLowerCase(), type]))\n const typeByKey = new Map<string, ResourcesResourceType>()\n for (const seed of RESOURCE_TYPE_SEEDS) {\n const existing = typeByName.get(seed.name.toLowerCase())\n if (existing) {\n if (!existing.appearanceIcon && seed.appearanceIcon) {\n existing.appearanceIcon = seed.appearanceIcon\n }\n if (!existing.appearanceColor && seed.appearanceColor) {\n existing.appearanceColor = seed.appearanceColor\n }\n if (existing.appearanceIcon || existing.appearanceColor) {\n existing.updatedAt = now\n em.persist(existing)\n }\n typeByKey.set(seed.key, existing)\n continue\n }\n const record = em.create(ResourcesResourceType, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n description: seed.description ?? null,\n appearanceIcon: seed.appearanceIcon ?? null,\n appearanceColor: seed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n typeByKey.set(seed.key, record)\n }\n await em.flush()\n\n const allTypes = await findWithDecryption(\n em,\n ResourcesResourceType,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const typeNameById = new Map(allTypes.map((type) => [type.id, type.name]))\n\n const tagSlugs = TAG_SEEDS.map((seed) => seed.slug)\n const existingTags = await findWithDecryption(\n em,\n ResourcesResourceTag,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n slug: { $in: tagSlugs },\n },\n undefined,\n scope,\n )\n const tagBySlug = new Map(existingTags.map((tag) => [tag.slug.toLowerCase(), tag]))\n const tagByKey = new Map<string, ResourcesResourceTag>()\n for (const seed of TAG_SEEDS) {\n const existing = tagBySlug.get(seed.slug.toLowerCase())\n if (existing) {\n tagByKey.set(seed.key, existing)\n continue\n }\n const tag = em.create(ResourcesResourceTag, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n slug: seed.slug,\n label: seed.label,\n color: seed.color ?? null,\n description: seed.description ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(tag)\n tagByKey.set(seed.key, tag)\n }\n await em.flush()\n\n const resourceNames = RESOURCE_SEEDS.map((seed) => seed.name)\n const existingResources = await findWithDecryption(\n em,\n ResourcesResource,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: { $in: resourceNames },\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const resourceByName = new Map(existingResources.map((resource) => [resource.name.toLowerCase(), resource]))\n const resourceByKey = new Map<string, ResourcesResource>()\n for (const seed of RESOURCE_SEEDS) {\n const existing = resourceByName.get(seed.name.toLowerCase())\n if (existing) {\n if (!existing.resourceTypeId && seed.typeKey) {\n const typeId = typeByKey.get(seed.typeKey)?.id ?? null\n if (typeId) {\n existing.resourceTypeId = typeId\n existing.updatedAt = now\n em.persist(existing)\n }\n }\n resourceByKey.set(seed.key, existing)\n continue\n }\n const resourceTypeId = seed.typeKey ? typeByKey.get(seed.typeKey)?.id ?? null : null\n const resource = em.create(ResourcesResource, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n resourceTypeId,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(resource)\n resourceByKey.set(seed.key, resource)\n }\n await em.flush()\n\n const resourcesInScope = await findWithDecryption(\n em,\n ResourcesResource,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const seedKeyByName = new Map(RESOURCE_SEEDS.map((seed) => [seed.name.toLowerCase(), seed.key]))\n for (const resource of resourcesInScope) {\n const resourceName = resource.name ?? 'Resource'\n const seedKey = seedKeyByName.get(resourceName.toLowerCase()) ?? null\n const typeName = resource.resourceTypeId ? typeNameById.get(resource.resourceTypeId) ?? null : null\n const fieldsetCode = resolveResourcesResourceFieldsetCode(typeName ?? resourceName)\n const customValues = {\n ...buildBaseResourceCustomValues(resourceName, seedKey),\n ...buildResourceTypeCustomValues(fieldsetCode, seedKey),\n }\n await fillMissingResourceCustomFields(em, scope, resource, customValues)\n }\n\n const activityDate = (daysAgo?: number) => {\n const date = new Date(now)\n if (typeof daysAgo === 'number') {\n date.setDate(date.getDate() - daysAgo)\n }\n return date\n }\n\n const resourceIds = Array.from(resourceByKey.values())\n .map((resource) => resource.id)\n .filter((id): id is string => typeof id === 'string')\n\n if (resourceIds.length > 0) {\n const existingActivities = await findWithDecryption(\n em,\n ResourcesResourceActivity,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: { $in: resourceIds },\n },\n { populate: ['resource'] },\n scope,\n )\n const activityByKey = new Map<string, ResourcesResourceActivity>()\n for (const activity of existingActivities) {\n const resourceId = typeof activity.resource === 'string' ? activity.resource : activity.resource.id\n const subject = activity.subject?.trim().toLowerCase() ?? ''\n const body = activity.body?.trim().toLowerCase() ?? ''\n const occurredAt = activity.occurredAt ? activity.occurredAt.toISOString().slice(0, 10) : ''\n const key = `${resourceId}:${activity.activityType}:${subject}:${body}:${occurredAt}`\n activityByKey.set(key, activity)\n }\n\n for (const seed of RESOURCE_ACTIVITY_SEEDS) {\n const resource = resourceByKey.get(seed.resourceKey)\n if (!resource) continue\n const resourceId = resource.id\n const occurredAt = activityDate(seed.daysAgo)\n const subject = seed.subject?.trim() ?? null\n const body = seed.body?.trim() ?? null\n const key = `${resourceId}:${seed.activityType}:${subject?.toLowerCase() ?? ''}:${body?.toLowerCase() ?? ''}:${occurredAt.toISOString().slice(0, 10)}`\n const existing = activityByKey.get(key)\n if (existing) {\n if (seed.customFields) {\n await fillMissingResourceActivityCustomFields(em, scope, existing, seed.customFields)\n }\n continue\n }\n const activity = em.create(ResourcesResourceActivity, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: em.getReference(ResourcesResource, resourceId),\n activityType: seed.activityType,\n subject,\n body,\n occurredAt,\n authorUserId: seed.authorUserId ?? null,\n appearanceIcon: seed.appearanceIcon ?? null,\n appearanceColor: seed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(activity)\n activityByKey.set(key, activity)\n if (seed.customFields) {\n await em.flush()\n await fillMissingResourceActivityCustomFields(em, scope, activity, seed.customFields)\n }\n }\n await em.flush()\n }\n\n const resourceList = Array.from(resourceByKey.values())\n if (resourceList.length > 0) {\n const existingAssignments = await findWithDecryption(\n em,\n ResourcesResourceTagAssignment,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: { $in: resourceList.map((resource) => resource.id) },\n },\n { populate: ['resource', 'tag'] },\n scope,\n )\n const assignmentMap = new Map<string, Set<string>>()\n for (const assignment of existingAssignments) {\n const resourceId = assignment.resource?.id\n const tagId = assignment.tag?.id\n if (!resourceId || !tagId) continue\n const set = assignmentMap.get(resourceId) ?? new Set<string>()\n set.add(tagId)\n assignmentMap.set(resourceId, set)\n }\n\n for (const seed of RESOURCE_SEEDS) {\n const resource = resourceByKey.get(seed.key)\n if (!resource) continue\n const existing = assignmentMap.get(resource.id) ?? new Set<string>()\n for (const tagKey of seed.tagKeys) {\n const tag = tagByKey.get(tagKey)\n if (!tag || existing.has(tag.id)) continue\n const assignment = em.create(ResourcesResourceTagAssignment, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: em.getReference(ResourcesResource, resource.id),\n tag: em.getReference(ResourcesResourceTag, tag.id),\n createdAt: now,\n updatedAt: now,\n })\n em.persist(assignment)\n existing.add(tag.id)\n }\n assignmentMap.set(resource.id, existing)\n }\n await em.flush()\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Dictionary, DictionaryEntry, type DictionaryManagerVisibility } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { normalizeDictionaryValue, sanitizeDictionaryColor, sanitizeDictionaryIcon } from '@open-mercato/core/modules/dictionaries/lib/utils'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { ensureCustomFieldDefinitions } from '@open-mercato/core/modules/entities/lib/field-definitions'\nimport { CustomFieldEntityConfig, CustomFieldValue } from '@open-mercato/core/modules/entities/data/entities'\nimport { setRecordCustomFields } from '@open-mercato/core/modules/entities/lib/helpers'\nimport {\n ResourcesResource,\n ResourcesResourceActivity,\n ResourcesResourceTag,\n ResourcesResourceTagAssignment,\n ResourcesResourceType,\n} from '../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n RESOURCES_RESOURCE_CUSTOM_FIELD_SETS,\n RESOURCES_RESOURCE_FIELDSET_DENTAL_CHAIR,\n RESOURCES_RESOURCE_FIELDSETS,\n RESOURCES_RESOURCE_FIELDSET_HAIR_KIT,\n RESOURCES_RESOURCE_FIELDSET_LAPTOP,\n RESOURCES_RESOURCE_FIELDSET_ROOM,\n RESOURCES_RESOURCE_FIELDSET_SEAT,\n RESOURCES_RESOURCE_FIELDSET_VEHICLE,\n resolveResourcesResourceFieldsetCode,\n} from './resourceCustomFields'\nimport { RESOURCES_CAPACITY_UNIT_DEFAULTS, RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from './capacityUnits'\n\nexport type ResourcesSeedScope = { tenantId: string; organizationId: string }\n\ntype DictionarySeedEntry = {\n value: string\n label?: string\n color?: string | null\n icon?: string | null\n}\n\ntype ResourcesResourceTypeSeed = {\n key: string\n name: string\n description?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\ntype ResourcesResourceTagSeed = {\n key: string\n slug: string\n label: string\n color?: string | null\n description?: string | null\n}\n\ntype ResourcesResourceSeed = {\n key: string\n name: string\n typeKey?: string | null\n tagKeys: string[]\n}\n\ntype ResourcesResourceActivitySeed = {\n resourceKey: string\n activityType: string\n subject?: string | null\n body?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n authorUserId?: string | null\n daysAgo?: number\n customFields?: Record<string, string | number | boolean | null>\n}\n\nconst RESOURCES_ACTIVITY_TYPE_DICTIONARY_KEY = 'resources.activity-types'\nconst RESOURCES_ADDRESS_TYPE_DICTIONARY_KEY = 'resources.address-types'\n\nconst RESOURCES_ACTIVITY_TYPE_DEFAULTS: DictionarySeedEntry[] = [\n { value: 'Planned maintenance', label: 'Planned maintenance', icon: 'lucide:calendar-cog', color: '#2563eb' },\n { value: 'Undergoing maintenance', label: 'Undergoing maintenance', icon: 'lucide:wrench', color: '#f59e0b' },\n { value: 'Maintenance completed', label: 'Maintenance completed', icon: 'lucide:check-circle-2', color: '#16a34a' },\n { value: 'Inspection scheduled', label: 'Inspection scheduled', icon: 'lucide:clipboard-check', color: '#0ea5e9' },\n { value: 'Calibration', label: 'Calibration', icon: 'lucide:gauge', color: '#6366f1' },\n { value: 'Out of service', label: 'Out of service', icon: 'lucide:ban', color: '#ef4444' },\n { value: 'Back in service', label: 'Back in service', icon: 'lucide:refresh-ccw', color: '#22c55e' },\n]\n\nconst RESOURCES_ADDRESS_TYPE_DEFAULTS: DictionarySeedEntry[] = [\n { value: 'main address', label: 'Main address' },\n { value: 'alternative address', label: 'Alternative address' },\n { value: 'current address', label: 'Current address' },\n]\n\nasync function ensureResourceFieldsetConfig(em: EntityManager, scope: ResourcesSeedScope) {\n const now = new Date()\n let config = await em.findOne(CustomFieldEntityConfig, {\n entityId: E.resources.resources_resource,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!config) {\n config = em.create(CustomFieldEntityConfig, {\n entityId: E.resources.resources_resource,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n }\n config.configJson = {\n fieldsets: RESOURCES_RESOURCE_FIELDSETS,\n singleFieldsetPerRecord: true,\n }\n config.isActive = true\n config.updatedAt = now\n em.persist(config)\n}\n\nasync function ensureResourceCustomFields(em: EntityManager, scope: ResourcesSeedScope) {\n await ensureResourceFieldsetConfig(em, scope)\n await ensureCustomFieldDefinitions(em, RESOURCES_RESOURCE_CUSTOM_FIELD_SETS, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.flush()\n}\n\nexport async function seedResourcesCapacityUnits(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n let dictionary = await em.findOne(Dictionary, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n deletedAt: null,\n })\n if (!dictionary) {\n dictionary = em.create(Dictionary, {\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n name: 'Resource capacity units',\n description: 'Units for resource capacity (spots, units, quantity, etc.).',\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isSystem: true,\n isActive: true,\n managerVisibility: 'default' satisfies DictionaryManagerVisibility,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(dictionary)\n await em.flush()\n }\n\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingMap = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const unit of RESOURCES_CAPACITY_UNIT_DEFAULTS) {\n const normalized = unit.value.trim().toLowerCase()\n if (!normalized || existingMap.has(normalized)) continue\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value: unit.value,\n normalizedValue: normalized,\n label: unit.label,\n color: null,\n icon: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nasync function ensureResourcesDictionary(\n em: EntityManager,\n scope: ResourcesSeedScope,\n definition: { key: string; name: string; description: string },\n): Promise<Dictionary> {\n let dictionary = await em.findOne(Dictionary, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: definition.key,\n deletedAt: null,\n })\n if (!dictionary) {\n dictionary = em.create(Dictionary, {\n key: definition.key,\n name: definition.name,\n description: definition.description,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isSystem: true,\n isActive: true,\n managerVisibility: 'default' satisfies DictionaryManagerVisibility,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(dictionary)\n await em.flush()\n }\n return dictionary\n}\n\nexport async function seedResourcesActivityTypes(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const dictionary = await ensureResourcesDictionary(em, scope, {\n key: RESOURCES_ACTIVITY_TYPE_DICTIONARY_KEY,\n name: 'Resource activity types',\n description: 'Activity types for resource timelines (maintenance, inspections, etc.).',\n })\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingByValue = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const seed of RESOURCES_ACTIVITY_TYPE_DEFAULTS) {\n const value = seed.value.trim()\n if (!value) continue\n const normalizedValue = normalizeDictionaryValue(value)\n if (!normalizedValue) continue\n const color = sanitizeDictionaryColor(seed.color)\n const icon = sanitizeDictionaryIcon(seed.icon)\n const existing = existingByValue.get(normalizedValue)\n if (existing) {\n let updated = false\n if (!existing.label?.trim() && (seed.label ?? '').trim()) {\n existing.label = (seed.label ?? value).trim()\n updated = true\n }\n if (color !== undefined && existing.color !== color) {\n existing.color = color\n updated = true\n }\n if (icon !== undefined && existing.icon !== icon) {\n existing.icon = icon\n updated = true\n }\n if (updated) {\n existing.updatedAt = new Date()\n em.persist(existing)\n }\n continue\n }\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value,\n normalizedValue,\n label: (seed.label ?? value).trim(),\n color: color ?? null,\n icon: icon ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nexport async function seedResourcesAddressTypes(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const dictionary = await ensureResourcesDictionary(em, scope, {\n key: RESOURCES_ADDRESS_TYPE_DICTIONARY_KEY,\n name: 'Resource address types',\n description: 'Address types for resources (main, alternative, current).',\n })\n const existingEntries = await em.find(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingByValue = new Map(existingEntries.map((entry) => [entry.normalizedValue, entry]))\n for (const seed of RESOURCES_ADDRESS_TYPE_DEFAULTS) {\n const value = seed.value.trim()\n if (!value) continue\n const normalizedValue = normalizeDictionaryValue(value)\n if (!normalizedValue) continue\n const existing = existingByValue.get(normalizedValue)\n if (existing) {\n if (!existing.label?.trim() && (seed.label ?? '').trim()) {\n existing.label = (seed.label ?? value).trim()\n existing.updatedAt = new Date()\n em.persist(existing)\n }\n continue\n }\n const entry = em.create(DictionaryEntry, {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n value,\n normalizedValue,\n label: (seed.label ?? value).trim(),\n color: null,\n icon: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n }\n await em.flush()\n}\n\nfunction normalizeAssetTag(value: string): string {\n const trimmed = value.trim()\n if (!trimmed) return 'RESOURCE'\n const upper = trimmed.toUpperCase()\n const normalized = upper.replace(/[^A-Z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n return normalized || 'RESOURCE'\n}\n\nfunction buildBaseResourceCustomValues(\n resourceName: string,\n seedKey?: string | null,\n): Record<string, string | number | boolean | null> {\n const tagSource = seedKey?.trim().length ? seedKey : resourceName\n const assetTag = normalizeAssetTag(tagSource ?? resourceName)\n return {\n asset_tag: assetTag,\n owner: 'Operations',\n warranty_expires: '2026-12-31',\n ops_notes: `Seeded for ${resourceName}.`,\n }\n}\n\nfunction buildResourceTypeCustomValues(\n fieldsetCode: string,\n seedKey?: string | null,\n): Record<string, string | number | boolean | null> {\n switch (fieldsetCode) {\n case RESOURCES_RESOURCE_FIELDSET_ROOM:\n return {\n room_floor: '1',\n room_zone: 'North Wing',\n room_projector: true,\n room_whiteboard: true,\n room_access_notes: 'Keycard access required after 6pm.',\n }\n case RESOURCES_RESOURCE_FIELDSET_LAPTOP: {\n const serialSource = seedKey?.trim().length ? seedKey : 'resource'\n return {\n laptop_serial: `LT-${normalizeAssetTag(serialSource)}`,\n laptop_cpu: 'Intel i7',\n laptop_ram_gb: 16,\n laptop_storage_gb: 512,\n laptop_os: 'windows',\n laptop_accessories: 'Docking station, charger, spare mouse.',\n }\n }\n case RESOURCES_RESOURCE_FIELDSET_SEAT:\n return {\n seat_style: 'standard',\n seat_heated: false,\n seat_positioning: 'Adjust headrest for extended sessions.',\n }\n case RESOURCES_RESOURCE_FIELDSET_HAIR_KIT:\n return {\n kit_inventory: 'Shears, clippers, comb set, cape, spray bottle.',\n kit_restock_cycle: 'Monthly',\n kit_maintenance: 'Replace clipper blades every quarter.',\n }\n case RESOURCES_RESOURCE_FIELDSET_DENTAL_CHAIR:\n return {\n chair_model: 'DX-300',\n chair_ultrasonic: true,\n chair_last_disinfected: '2025-01-01',\n chair_inspection_notes: 'Monthly inspection scheduled.',\n }\n case RESOURCES_RESOURCE_FIELDSET_VEHICLE:\n return buildVehicleCustomValues(seedKey)\n default:\n return {}\n }\n}\n\ntype VehicleSeedDetails = {\n model: string\n plate: string\n fuelType: string\n mileageKm: number\n lastService: string\n}\n\nconst VEHICLE_SEED_DETAILS: Record<string, VehicleSeedDetails> = {\n 'company-car-1': {\n model: 'Tesla Model 3',\n plate: 'WWA 4K32',\n fuelType: 'electric',\n mileageKm: 18240,\n lastService: '2025-02-18',\n },\n 'company-car-2': {\n model: 'Volvo XC40 Recharge',\n plate: 'WPR 9L18',\n fuelType: 'electric',\n mileageKm: 22610,\n lastService: '2024-12-05',\n },\n}\n\nfunction buildVehicleCustomValues(seedKey?: string | null): Record<string, string | number | boolean | null> {\n const details = seedKey ? VEHICLE_SEED_DETAILS[seedKey] : undefined\n if (!details) {\n return {\n vehicle_plate: 'OM-2034',\n vehicle_model: 'Polestar 2',\n vehicle_fuel_type: 'electric',\n vehicle_mileage_km: 18200,\n vehicle_last_service: '2025-02-18',\n }\n }\n return {\n vehicle_plate: details.plate,\n vehicle_model: details.model,\n vehicle_fuel_type: details.fuelType,\n vehicle_mileage_km: details.mileageKm,\n vehicle_last_service: details.lastService,\n }\n}\n\nasync function fillMissingResourceCustomFields(\n em: EntityManager,\n scope: ResourcesSeedScope,\n resource: ResourcesResource,\n customValues: Record<string, string | number | boolean | null>,\n) {\n const keys = Object.keys(customValues)\n if (!keys.length) return\n const existingValues = await em.find(CustomFieldValue, {\n entityId: E.resources.resources_resource,\n recordId: resource.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fieldKey: { $in: keys },\n })\n const existingKeys = new Set(existingValues.map((value) => value.fieldKey))\n const missingValues: Record<string, string | number | boolean | null> = {}\n for (const key of keys) {\n if (!existingKeys.has(key)) {\n missingValues[key] = customValues[key] ?? null\n }\n }\n if (Object.keys(missingValues).length === 0) return\n await setRecordCustomFields(em, {\n entityId: E.resources.resources_resource,\n recordId: resource.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n values: missingValues,\n })\n}\n\nasync function fillMissingResourceActivityCustomFields(\n em: EntityManager,\n scope: ResourcesSeedScope,\n activity: ResourcesResourceActivity,\n customValues: Record<string, string | number | boolean | null>,\n) {\n const keys = Object.keys(customValues)\n if (!keys.length) return\n const existingValues = await em.find(CustomFieldValue, {\n entityId: E.resources.resources_resource_activity,\n recordId: activity.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fieldKey: { $in: keys },\n })\n const existingKeys = new Set(existingValues.map((value) => value.fieldKey))\n const missingValues: Record<string, string | number | boolean | null> = {}\n for (const key of keys) {\n if (!existingKeys.has(key)) {\n missingValues[key] = customValues[key] ?? null\n }\n }\n if (Object.keys(missingValues).length === 0) return\n await setRecordCustomFields(em, {\n entityId: E.resources.resources_resource_activity,\n recordId: activity.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n values: missingValues,\n })\n}\n\nconst RESOURCE_TYPE_SEEDS: ResourcesResourceTypeSeed[] = [\n {\n key: 'meeting_room',\n name: 'Meeting room',\n description: 'Collaborative space with A/V equipment.',\n appearanceIcon: 'lucide:video',\n appearanceColor: '#2563eb',\n },\n {\n key: 'focus_room',\n name: 'Focus room',\n description: 'Quiet room for heads-down work.',\n appearanceIcon: 'lucide:door-closed',\n appearanceColor: '#0ea5e9',\n },\n {\n key: 'laptop',\n name: 'Engineering laptop',\n description: 'Portable computer for the delivery team.',\n appearanceIcon: 'lucide:cpu',\n appearanceColor: '#14b8a6',\n },\n {\n key: 'company_car',\n name: 'Company car',\n description: 'Shared vehicle for client visits and errands.',\n appearanceIcon: 'lucide:car',\n appearanceColor: '#7c3aed',\n },\n]\n\nconst TAG_SEEDS: ResourcesResourceTagSeed[] = [\n { key: 'room', slug: 'room', label: 'Room', color: '#1d4ed8' },\n { key: 'focus', slug: 'focus', label: 'Focus', color: '#0f766e' },\n { key: 'tech', slug: 'tech', label: 'Tech', color: '#2563eb' },\n { key: 'equipment', slug: 'equipment', label: 'Equipment', color: '#6b21a8' },\n { key: 'vehicle', slug: 'vehicle', label: 'Vehicle', color: '#7c3aed' },\n { key: 'collaboration', slug: 'collaboration', label: 'Collaboration', color: '#14b8a6' },\n]\n\nconst RESOURCE_SEEDS: ResourcesResourceSeed[] = [\n { key: 'meeting-room-a', name: 'Meeting Room A', typeKey: 'meeting_room', tagKeys: ['room', 'collaboration'] },\n { key: 'meeting-room-b', name: 'Meeting Room B', typeKey: 'meeting_room', tagKeys: ['room', 'collaboration'] },\n { key: 'focus-room-1', name: 'Focus Room 1', typeKey: 'focus_room', tagKeys: ['room', 'focus'] },\n { key: 'focus-room-2', name: 'Focus Room 2', typeKey: 'focus_room', tagKeys: ['room', 'focus'] },\n { key: 'engineering-laptop-1', name: 'Engineering Laptop 1', typeKey: 'laptop', tagKeys: ['tech', 'equipment'] },\n { key: 'engineering-laptop-2', name: 'Engineering Laptop 2', typeKey: 'laptop', tagKeys: ['tech', 'equipment'] },\n { key: 'company-car-1', name: 'Tesla Model 3 - WWA 4K32', typeKey: 'company_car', tagKeys: ['vehicle'] },\n { key: 'company-car-2', name: 'Volvo XC40 Recharge - WPR 9L18', typeKey: 'company_car', tagKeys: ['vehicle'] },\n]\n\nconst RESOURCE_ACTIVITY_SEEDS: ResourcesResourceActivitySeed[] = [\n {\n resourceKey: 'meeting-room-a',\n activityType: 'Inspection scheduled',\n subject: 'AV equipment inspection',\n body: 'Quarterly AV inspection scheduled with facilities.',\n appearanceIcon: 'lucide:clipboard-check',\n appearanceColor: '#0ea5e9',\n daysAgo: 10,\n customFields: {\n activity_priority: 'normal',\n work_order: 'WO-1021',\n requires_follow_up: true,\n },\n },\n {\n resourceKey: 'engineering-laptop-1',\n activityType: 'Calibration',\n subject: 'Battery health calibration',\n body: 'Battery calibration completed after OS update.',\n appearanceIcon: 'lucide:gauge',\n appearanceColor: '#6366f1',\n daysAgo: 4,\n customFields: {\n activity_priority: 'low',\n work_order: 'WO-1148',\n requires_follow_up: false,\n },\n },\n {\n resourceKey: 'company-car-1',\n activityType: 'Maintenance completed',\n subject: 'Tire rotation completed',\n body: 'Rotation completed and tire pressure set to spec.',\n appearanceIcon: 'lucide:check-circle-2',\n appearanceColor: '#16a34a',\n daysAgo: 22,\n customFields: {\n activity_priority: 'high',\n work_order: 'WO-0987',\n requires_follow_up: false,\n },\n },\n]\n\nexport async function seedResourcesResourceExamples(\n em: EntityManager,\n scope: ResourcesSeedScope,\n) {\n const now = new Date()\n await seedResourcesActivityTypes(em, scope)\n await ensureResourceCustomFields(em, scope)\n const typeNames = RESOURCE_TYPE_SEEDS.map((seed) => seed.name)\n const existingTypes = await findWithDecryption(\n em,\n ResourcesResourceType,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: { $in: typeNames },\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const typeByName = new Map(existingTypes.map((type) => [type.name.toLowerCase(), type]))\n const typeByKey = new Map<string, ResourcesResourceType>()\n for (const seed of RESOURCE_TYPE_SEEDS) {\n const existing = typeByName.get(seed.name.toLowerCase())\n if (existing) {\n if (!existing.appearanceIcon && seed.appearanceIcon) {\n existing.appearanceIcon = seed.appearanceIcon\n }\n if (!existing.appearanceColor && seed.appearanceColor) {\n existing.appearanceColor = seed.appearanceColor\n }\n if (existing.appearanceIcon || existing.appearanceColor) {\n existing.updatedAt = now\n em.persist(existing)\n }\n typeByKey.set(seed.key, existing)\n continue\n }\n const record = em.create(ResourcesResourceType, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n description: seed.description ?? null,\n appearanceIcon: seed.appearanceIcon ?? null,\n appearanceColor: seed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n typeByKey.set(seed.key, record)\n }\n await em.flush()\n\n const allTypes = await findWithDecryption(\n em,\n ResourcesResourceType,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const typeNameById = new Map(allTypes.map((type) => [type.id, type.name]))\n\n const tagSlugs = TAG_SEEDS.map((seed) => seed.slug)\n const existingTags = await findWithDecryption(\n em,\n ResourcesResourceTag,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n slug: { $in: tagSlugs },\n },\n undefined,\n scope,\n )\n const tagBySlug = new Map(existingTags.map((tag) => [tag.slug.toLowerCase(), tag]))\n const tagByKey = new Map<string, ResourcesResourceTag>()\n for (const seed of TAG_SEEDS) {\n const existing = tagBySlug.get(seed.slug.toLowerCase())\n if (existing) {\n tagByKey.set(seed.key, existing)\n continue\n }\n const tag = em.create(ResourcesResourceTag, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n slug: seed.slug,\n label: seed.label,\n color: seed.color ?? null,\n description: seed.description ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(tag)\n tagByKey.set(seed.key, tag)\n }\n await em.flush()\n\n const resourceNames = RESOURCE_SEEDS.map((seed) => seed.name)\n const existingResources = await findWithDecryption(\n em,\n ResourcesResource,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: { $in: resourceNames },\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const resourceByName = new Map(existingResources.map((resource) => [resource.name.toLowerCase(), resource]))\n const resourceByKey = new Map<string, ResourcesResource>()\n for (const seed of RESOURCE_SEEDS) {\n const existing = resourceByName.get(seed.name.toLowerCase())\n if (existing) {\n if (!existing.resourceTypeId && seed.typeKey) {\n const typeId = typeByKey.get(seed.typeKey)?.id ?? null\n if (typeId) {\n existing.resourceTypeId = typeId\n existing.updatedAt = now\n em.persist(existing)\n }\n }\n resourceByKey.set(seed.key, existing)\n continue\n }\n const resourceTypeId = seed.typeKey ? typeByKey.get(seed.typeKey)?.id ?? null : null\n const resource = em.create(ResourcesResource, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n resourceTypeId,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(resource)\n resourceByKey.set(seed.key, resource)\n }\n await em.flush()\n\n const resourcesInScope = await findWithDecryption(\n em,\n ResourcesResource,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n const seedKeyByName = new Map(RESOURCE_SEEDS.map((seed) => [seed.name.toLowerCase(), seed.key]))\n for (const resource of resourcesInScope) {\n const resourceName = resource.name ?? 'Resource'\n const seedKey = seedKeyByName.get(resourceName.toLowerCase()) ?? null\n const typeName = resource.resourceTypeId ? typeNameById.get(resource.resourceTypeId) ?? null : null\n const fieldsetCode = resolveResourcesResourceFieldsetCode(typeName ?? resourceName)\n const customValues = {\n ...buildBaseResourceCustomValues(resourceName, seedKey),\n ...buildResourceTypeCustomValues(fieldsetCode, seedKey),\n }\n await fillMissingResourceCustomFields(em, scope, resource, customValues)\n }\n\n const activityDate = (daysAgo?: number) => {\n const date = new Date(now)\n if (typeof daysAgo === 'number') {\n date.setDate(date.getDate() - daysAgo)\n }\n return date\n }\n\n const resourceIds = Array.from(resourceByKey.values())\n .map((resource) => resource.id)\n .filter((id): id is string => typeof id === 'string')\n\n if (resourceIds.length > 0) {\n const existingActivities = await findWithDecryption(\n em,\n ResourcesResourceActivity,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: { $in: resourceIds },\n },\n { populate: ['resource'] },\n scope,\n )\n const activityByKey = new Map<string, ResourcesResourceActivity>()\n for (const activity of existingActivities) {\n const resourceId = typeof activity.resource === 'string' ? activity.resource : activity.resource.id\n const subject = activity.subject?.trim().toLowerCase() ?? ''\n const body = activity.body?.trim().toLowerCase() ?? ''\n const occurredAt = activity.occurredAt ? activity.occurredAt.toISOString().slice(0, 10) : ''\n const key = `${resourceId}:${activity.activityType}:${subject}:${body}:${occurredAt}`\n activityByKey.set(key, activity)\n }\n\n for (const seed of RESOURCE_ACTIVITY_SEEDS) {\n const resource = resourceByKey.get(seed.resourceKey)\n if (!resource) continue\n const resourceId = resource.id\n const occurredAt = activityDate(seed.daysAgo)\n const subject = seed.subject?.trim() ?? null\n const body = seed.body?.trim() ?? null\n const key = `${resourceId}:${seed.activityType}:${subject?.toLowerCase() ?? ''}:${body?.toLowerCase() ?? ''}:${occurredAt.toISOString().slice(0, 10)}`\n const existing = activityByKey.get(key)\n if (existing) {\n if (seed.customFields) {\n await fillMissingResourceActivityCustomFields(em, scope, existing, seed.customFields)\n }\n continue\n }\n const activity = em.create(ResourcesResourceActivity, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: em.getReference(ResourcesResource, resourceId),\n activityType: seed.activityType,\n subject,\n body,\n occurredAt,\n authorUserId: seed.authorUserId ?? null,\n appearanceIcon: seed.appearanceIcon ?? null,\n appearanceColor: seed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(activity)\n activityByKey.set(key, activity)\n if (seed.customFields) {\n await em.flush()\n await fillMissingResourceActivityCustomFields(em, scope, activity, seed.customFields)\n }\n }\n await em.flush()\n }\n\n const resourceList = Array.from(resourceByKey.values())\n if (resourceList.length > 0) {\n const existingAssignments = await findWithDecryption(\n em,\n ResourcesResourceTagAssignment,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: { $in: resourceList.map((resource) => resource.id) },\n },\n { populate: ['resource', 'tag'] },\n scope,\n )\n const assignmentMap = new Map<string, Set<string>>()\n for (const assignment of existingAssignments) {\n const resourceId = assignment.resource?.id\n const tagId = assignment.tag?.id\n if (!resourceId || !tagId) continue\n const set = assignmentMap.get(resourceId) ?? new Set<string>()\n set.add(tagId)\n assignmentMap.set(resourceId, set)\n }\n\n for (const seed of RESOURCE_SEEDS) {\n const resource = resourceByKey.get(seed.key)\n if (!resource) continue\n const existing = assignmentMap.get(resource.id) ?? new Set<string>()\n for (const tagKey of seed.tagKeys) {\n const tag = tagByKey.get(tagKey)\n if (!tag || existing.has(tag.id)) continue\n const assignment = em.create(ResourcesResourceTagAssignment, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resource: em.getReference(ResourcesResource, resource.id),\n tag: em.getReference(ResourcesResourceTag, tag.id),\n createdAt: now,\n updatedAt: now,\n })\n em.persist(assignment)\n existing.add(tag.id)\n }\n assignmentMap.set(resource.id, existing)\n }\n await em.flush()\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -28,7 +28,7 @@ const metadata = {
28
28
  function normalizeOrganizationIds(organizationIds) {
29
29
  if (organizationIds === null) return null;
30
30
  const set = new Set(organizationIds);
31
- return Array.from(set).sort((a, b) => a.localeCompare(b));
31
+ return Array.from(set).sort();
32
32
  }
33
33
  function buildCacheKey(params) {
34
34
  const hash = createHash("sha256");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-orders/route.ts"],
4
- "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesOrder } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:orders']\r\nconst WIDGET_CACHE_ID = 'sales:new-orders'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-orders'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewOrdersWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n orderNumber: string\r\n status: string | null\r\n fulfillmentStatus: string | null\r\n paymentStatus: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort((a, b) => a.localeCompare(b))\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n // Use shared WidgetDataService to fetch aggregated widget data\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesOrder> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [orders, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesOrder,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = orders.map((order) => ({\r\n id: order.id,\r\n orderNumber: order.orderNumber,\r\n status: order.status ?? null,\r\n fulfillmentStatus: order.fulfillmentStatus ?? null,\r\n paymentStatus: order.paymentStatus ?? null,\r\n customerName: extractCustomerName(order.customerSnapshot) ?? null,\r\n customerEntityId: order.customerEntityId ?? null,\r\n netAmount: order.grandTotalNetAmount ?? '0',\r\n grossAmount: order.grandTotalGrossAmount ?? '0',\r\n currency: order.currencyCode ?? null,\r\n createdAt: order.createdAt.toISOString(),\r\n }))\r\n\r\n const response: NewOrdersWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newOrders failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newOrders.error', 'Failed to load orders') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst orderItemSchema = z.object({\r\n id: z.string().uuid(),\r\n orderNumber: z.string(),\r\n status: z.string().nullable(),\r\n fulfillmentStatus: z.string().nullable(),\r\n paymentStatus: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(orderItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New orders dashboard widget',\r\n description: 'Fetches recently created sales orders for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales orders',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent orders', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesOrder } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:orders']\r\nconst WIDGET_CACHE_ID = 'sales:new-orders'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-orders'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewOrdersWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n orderNumber: string\r\n status: string | null\r\n fulfillmentStatus: string | null\r\n paymentStatus: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort()\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n // Use shared WidgetDataService to fetch aggregated widget data\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesOrder> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [orders, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesOrder,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = orders.map((order) => ({\r\n id: order.id,\r\n orderNumber: order.orderNumber,\r\n status: order.status ?? null,\r\n fulfillmentStatus: order.fulfillmentStatus ?? null,\r\n paymentStatus: order.paymentStatus ?? null,\r\n customerName: extractCustomerName(order.customerSnapshot) ?? null,\r\n customerEntityId: order.customerEntityId ?? null,\r\n netAmount: order.grandTotalNetAmount ?? '0',\r\n grossAmount: order.grandTotalGrossAmount ?? '0',\r\n currency: order.currencyCode ?? null,\r\n createdAt: order.createdAt.toISOString(),\r\n }))\r\n\r\n const response: NewOrdersWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newOrders failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newOrders.error', 'Failed to load orders') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst orderItemSchema = z.object({\r\n id: z.string().uuid(),\r\n orderNumber: z.string(),\r\n status: z.string().nullable(),\r\n fulfillmentStatus: z.string().nullable(),\r\n paymentStatus: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(orderItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New orders dashboard widget',\r\n description: 'Fetches recently created sales orders for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales orders',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent orders', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -28,7 +28,7 @@ const metadata = {
28
28
  function normalizeOrganizationIds(organizationIds) {
29
29
  if (organizationIds === null) return null;
30
30
  const set = new Set(organizationIds);
31
- return Array.from(set).sort((a, b) => a.localeCompare(b));
31
+ return Array.from(set).sort();
32
32
  }
33
33
  function buildCacheKey(params) {
34
34
  const hash = createHash("sha256");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-quotes/route.ts"],
4
- "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesQuote } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:quotes']\r\nconst WIDGET_CACHE_ID = 'sales:new-quotes'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-quotes'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewQuotesWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n quoteNumber: string\r\n status: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n validFrom: string | null\r\n validUntil: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n convertedOrderId: string | null\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort((a, b) => a.localeCompare(b))\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesQuote> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [quotes, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesQuote,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = quotes.map((quote) => ({\r\n id: quote.id,\r\n quoteNumber: quote.quoteNumber,\r\n status: quote.status ?? null,\r\n customerName: extractCustomerName(quote.customerSnapshot) ?? null,\r\n customerEntityId: quote.customerEntityId ?? null,\r\n validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,\r\n validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,\r\n netAmount: quote.grandTotalNetAmount ?? '0',\r\n grossAmount: quote.grandTotalGrossAmount ?? '0',\r\n currency: quote.currencyCode ?? null,\r\n createdAt: quote.createdAt.toISOString(),\r\n convertedOrderId: quote.convertedOrderId ?? null,\r\n }))\r\n\r\n const response: NewQuotesWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newQuotes failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newQuotes.error', 'Failed to load quotes') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst quoteItemSchema = z.object({\r\n id: z.string().uuid(),\r\n quoteNumber: z.string(),\r\n status: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n validFrom: z.string().nullable(),\r\n validUntil: z.string().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n convertedOrderId: z.string().uuid().nullable(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(quoteItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New quotes dashboard widget',\r\n description: 'Fetches recently created sales quotes for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales quotes',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent quotes', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesQuote } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:quotes']\r\nconst WIDGET_CACHE_ID = 'sales:new-quotes'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-quotes'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewQuotesWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n quoteNumber: string\r\n status: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n validFrom: string | null\r\n validUntil: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n convertedOrderId: string | null\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort()\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesQuote> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [quotes, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesQuote,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = quotes.map((quote) => ({\r\n id: quote.id,\r\n quoteNumber: quote.quoteNumber,\r\n status: quote.status ?? null,\r\n customerName: extractCustomerName(quote.customerSnapshot) ?? null,\r\n customerEntityId: quote.customerEntityId ?? null,\r\n validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,\r\n validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,\r\n netAmount: quote.grandTotalNetAmount ?? '0',\r\n grossAmount: quote.grandTotalGrossAmount ?? '0',\r\n currency: quote.currencyCode ?? null,\r\n createdAt: quote.createdAt.toISOString(),\r\n convertedOrderId: quote.convertedOrderId ?? null,\r\n }))\r\n\r\n const response: NewQuotesWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newQuotes failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newQuotes.error', 'Failed to load quotes') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst quoteItemSchema = z.object({\r\n id: z.string().uuid(),\r\n quoteNumber: z.string(),\r\n status: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n validFrom: z.string().nullable(),\r\n validUntil: z.string().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n convertedOrderId: z.string().uuid().nullable(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(quoteItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New quotes dashboard widget',\r\n description: 'Fetches recently created sales quotes for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales quotes',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent quotes', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import * as React from "react";
4
4
  import { useRouter } from "next/navigation";
5
5
  import Link from "next/link";
6
6
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
7
- import { DataTable, withDataTableNamespaces } from "@open-mercato/ui/backend/DataTable";
7
+ import { DataTable } from "@open-mercato/ui/backend/DataTable";
8
8
  import { Button } from "@open-mercato/ui/primitives/button";
9
9
  import { RowActions } from "@open-mercato/ui/backend/RowActions";
10
10
  import { BooleanIcon } from "@open-mercato/ui/backend/ValueIcons";
@@ -161,7 +161,7 @@ function SalesChannelsPage() {
161
161
  }
162
162
  function mapApiChannel(item) {
163
163
  const id = typeof item.id === "string" ? item.id : "";
164
- return withDataTableNamespaces({
164
+ return {
165
165
  id,
166
166
  name: typeof item.name === "string" ? item.name : id,
167
167
  code: typeof item.code === "string" && item.code.length ? item.code : null,
@@ -169,7 +169,7 @@ function mapApiChannel(item) {
169
169
  offerCount: typeof item.offerCount === "number" ? item.offerCount : typeof item.offer_count === "number" ? item.offer_count : 0,
170
170
  isActive: item.isActive === true || item.is_active === true,
171
171
  updatedAt: typeof item.updatedAt === "string" ? item.updatedAt : typeof item.updated_at === "string" ? item.updated_at : null
172
- }, item);
172
+ };
173
173
  }
174
174
  export {
175
175
  SalesChannelsPage as default
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/sales/backend/sales/channels/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport Link from 'next/link'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype ChannelRow = {\n id: string\n name: string\n code: string | null\n description: string | null\n offerCount: number\n isActive: boolean\n updatedAt: string | null\n}\n\ntype ChannelsResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\n\nexport default function SalesChannelsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ChannelRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [sorting, setSorting] = React.useState<SortingState>([])\n const [search, setSearch] = React.useState('')\n const [isLoading, setLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const columns = React.useMemo<ColumnDef<ChannelRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('sales.channels.table.name', 'Name'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'code',\n header: t('sales.channels.table.code', 'Code'),\n cell: ({ row }) => row.original.code ? (\n <span className=\"font-mono text-xs\">{row.original.code}</span>\n ) : (\n <span className=\"text-xs text-muted-foreground\">\u2014</span>\n ),\n },\n {\n accessorKey: 'offerCount',\n header: t('sales.channels.table.offers', 'Product offers'),\n cell: ({ row }) => (\n <span className=\"text-sm font-semibold\">{row.original.offerCount}</span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('sales.channels.table.active', 'Active'),\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: t('sales.channels.table.updated', 'Updated'),\n cell: ({ row }) =>\n row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{new Date(row.original.updatedAt).toLocaleDateString()}</span>\n : <span className=\"text-xs text-muted-foreground\">\u2014</span>,\n },\n ], [t])\n\n const loadChannels = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n })\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (search.trim().length) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<ChannelsResponse>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('sales.channels.table.errors.load', 'Failed to load channels.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapApiChannel))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (err) {\n console.error('sales.channels.list', err)\n flash(t('sales.channels.table.errors.load', 'Failed to load channels.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [page, search, sorting, t])\n\n React.useEffect(() => {\n void loadChannels()\n }, [loadChannels, scopeVersion, reloadToken])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (row: ChannelRow) => {\n try {\n await deleteCrud('sales/channels', row.id, {\n errorMessage: t('sales.channels.table.errors.delete', 'Failed to delete channel.'),\n })\n flash(t('sales.channels.table.messages.deleted', 'Channel deleted.'), 'success')\n handleRefresh()\n } catch (err) {\n console.error('sales.channels.delete', err)\n }\n }, [handleRefresh, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<ChannelRow>\n title={(\n <div className=\"flex flex-col\">\n <span>{t('sales.channels.nav.title', 'Sales channels')}</span>\n <span className=\"text-sm font-normal text-muted-foreground\">\n {t('sales.channels.table.subtitle', 'Organize catalog offers per marketplace or storefront.')}\n </span>\n </div>\n )}\n actions={(\n <Button asChild>\n <Link href=\"/backend/sales/channels/create\">\n {t('sales.channels.actions.create', 'Add channel')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n sorting={sorting}\n onSortingChange={setSorting}\n isLoading={isLoading}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('sales.channels.table.search', 'Search channels\u2026')}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={{\n label: t('sales.channels.table.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('sales.channels.table.actions.edit', 'Edit'),\n href: `/backend/sales/channels/${row.id}/edit`,\n },\n {\n id: 'delete',\n label: t('sales.channels.table.actions.delete', 'Delete'),\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/sales/channels/${row.id}/edit`)}\n emptyState={\n <div className=\"py-10 text-center text-sm text-muted-foreground\">\n {t('sales.channels.table.empty', 'No channels yet.')}\n </div>\n }\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction mapApiChannel(item: Record<string, unknown>): ChannelRow {\n const id = typeof item.id === 'string' ? item.id : ''\n return withDataTableNamespaces({\n id,\n name: typeof item.name === 'string' ? item.name : id,\n code: typeof item.code === 'string' && item.code.length ? item.code : null,\n description: typeof item.description === 'string' && item.description.length ? item.description : null,\n offerCount: typeof item.offerCount === 'number'\n ? item.offerCount\n : typeof item.offer_count === 'number'\n ? item.offer_count\n : 0,\n isActive: item.isActive === true || item.is_active === true,\n updatedAt: typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null,\n }, item)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport Link from 'next/link'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype ChannelRow = {\n id: string\n name: string\n code: string | null\n description: string | null\n offerCount: number\n isActive: boolean\n updatedAt: string | null\n}\n\ntype ChannelsResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\n\nexport default function SalesChannelsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ChannelRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [sorting, setSorting] = React.useState<SortingState>([])\n const [search, setSearch] = React.useState('')\n const [isLoading, setLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const columns = React.useMemo<ColumnDef<ChannelRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('sales.channels.table.name', 'Name'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'code',\n header: t('sales.channels.table.code', 'Code'),\n cell: ({ row }) => row.original.code ? (\n <span className=\"font-mono text-xs\">{row.original.code}</span>\n ) : (\n <span className=\"text-xs text-muted-foreground\">\u2014</span>\n ),\n },\n {\n accessorKey: 'offerCount',\n header: t('sales.channels.table.offers', 'Product offers'),\n cell: ({ row }) => (\n <span className=\"text-sm font-semibold\">{row.original.offerCount}</span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('sales.channels.table.active', 'Active'),\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: t('sales.channels.table.updated', 'Updated'),\n cell: ({ row }) =>\n row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{new Date(row.original.updatedAt).toLocaleDateString()}</span>\n : <span className=\"text-xs text-muted-foreground\">\u2014</span>,\n },\n ], [t])\n\n const loadChannels = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n })\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (search.trim().length) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<ChannelsResponse>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('sales.channels.table.errors.load', 'Failed to load channels.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapApiChannel))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (err) {\n console.error('sales.channels.list', err)\n flash(t('sales.channels.table.errors.load', 'Failed to load channels.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [page, search, sorting, t])\n\n React.useEffect(() => {\n void loadChannels()\n }, [loadChannels, scopeVersion, reloadToken])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (row: ChannelRow) => {\n try {\n await deleteCrud('sales/channels', row.id, {\n errorMessage: t('sales.channels.table.errors.delete', 'Failed to delete channel.'),\n })\n flash(t('sales.channels.table.messages.deleted', 'Channel deleted.'), 'success')\n handleRefresh()\n } catch (err) {\n console.error('sales.channels.delete', err)\n }\n }, [handleRefresh, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<ChannelRow>\n title={(\n <div className=\"flex flex-col\">\n <span>{t('sales.channels.nav.title', 'Sales channels')}</span>\n <span className=\"text-sm font-normal text-muted-foreground\">\n {t('sales.channels.table.subtitle', 'Organize catalog offers per marketplace or storefront.')}\n </span>\n </div>\n )}\n actions={(\n <Button asChild>\n <Link href=\"/backend/sales/channels/create\">\n {t('sales.channels.actions.create', 'Add channel')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n sorting={sorting}\n onSortingChange={setSorting}\n isLoading={isLoading}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('sales.channels.table.search', 'Search channels\u2026')}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={{\n label: t('sales.channels.table.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('sales.channels.table.actions.edit', 'Edit'),\n href: `/backend/sales/channels/${row.id}/edit`,\n },\n {\n id: 'delete',\n label: t('sales.channels.table.actions.delete', 'Delete'),\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/sales/channels/${row.id}/edit`)}\n emptyState={\n <div className=\"py-10 text-center text-sm text-muted-foreground\">\n {t('sales.channels.table.empty', 'No channels yet.')}\n </div>\n }\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction mapApiChannel(item: Record<string, unknown>): ChannelRow {\n const id = typeof item.id === 'string' ? item.id : ''\n return {\n id,\n name: typeof item.name === 'string' ? item.name : id,\n code: typeof item.code === 'string' && item.code.length ? item.code : null,\n description: typeof item.description === 'string' && item.description.length ? item.description : null,\n offerCount: typeof item.offerCount === 'number'\n ? item.offerCount\n : typeof item.offer_count === 'number'\n ? item.offer_count\n : 0,\n isActive: item.isActive === true || item.is_active === true,\n updatedAt: typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null,\n }\n}\n"],
5
+ "mappings": ";AAqDQ,SACE,KADF;AAnDR,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AAEjB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,aAAa;AACtB,SAAS,4BAA4B;AACrC,SAAS,kBAAkB;AAC3B,SAAS,mCAAmC;AAC5C,SAAS,YAAY;AAkBrB,MAAM,YAAY;AAEH,SAAR,oBAAqC;AAC1C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAuB,CAAC,CAAC;AACvD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC;AACxC,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,CAAC;AAC1C,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,CAAC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAuB,CAAC,CAAC;AAC7D,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAC7C,QAAM,CAAC,WAAW,UAAU,IAAI,MAAM,SAAS,IAAI;AACnD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC;AAEtD,QAAM,UAAU,MAAM,QAAiC,MAAM;AAAA,IAC3D;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,6BAA6B,MAAM;AAAA,MAC7C,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,SAAI,WAAU,iBACb;AAAA,4BAAC,UAAK,WAAU,eAAe,cAAI,SAAS,MAAK;AAAA,QAChD,IAAI,SAAS,cACZ,oBAAC,UAAK,WAAU,iCAAiC,cAAI,SAAS,aAAY,IACxE;AAAA,SACN;AAAA,MAEF,MAAM,EAAE,QAAQ,KAAK;AAAA,IACvB;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,6BAA6B,MAAM;AAAA,MAC7C,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,OAC9B,oBAAC,UAAK,WAAU,qBAAqB,cAAI,SAAS,MAAK,IAEvD,oBAAC,UAAK,WAAU,iCAAgC,oBAAC;AAAA,IAErD;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,+BAA+B,gBAAgB;AAAA,MACzD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,yBAAyB,cAAI,SAAS,YAAW;AAAA,IAErE;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,+BAA+B,QAAQ;AAAA,MACjD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,eAAY,OAAO,IAAI,SAAS,UAAU;AAAA,IAChE;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,gCAAgC,SAAS;AAAA,MACnD,MAAM,CAAC,EAAE,IAAI,MACX,IAAI,SAAS,YACT,oBAAC,UAAK,WAAU,iCAAiC,cAAI,KAAK,IAAI,SAAS,SAAS,EAAE,mBAAmB,GAAE,IACvG,oBAAC,UAAK,WAAU,iCAAgC,oBAAC;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,MAAM,OAAO,IAAI;AAAA,QACjB,UAAU,OAAO,SAAS;AAAA,MAC5B,CAAC;AACD,YAAM,OAAO,QAAQ,CAAC;AACtB,UAAI,MAAM,IAAI;AACZ,eAAO,IAAI,aAAa,KAAK,EAAE;AAC/B,eAAO,IAAI,WAAW,KAAK,OAAO,SAAS,KAAK;AAAA,MAClD;AACA,UAAI,OAAO,KAAK,EAAE,QAAQ;AACxB,eAAO,IAAI,UAAU,OAAO,KAAK,CAAC;AAAA,MACpC;AACA,YAAM,UAAU,MAAM;AAAA,QACpB,uBAAuB,OAAO,SAAS,CAAC;AAAA,QACxC;AAAA,QACA,EAAE,cAAc,EAAE,oCAAoC,0BAA0B,EAAE;AAAA,MACpF;AACA,YAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,cAAQ,MAAM,IAAI,aAAa,CAAC;AAChC,eAAS,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ,MAAM,MAAM;AACzE,oBAAc,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,SAAS,CAAC,CAAC;AAAA,IAC9H,SAAS,KAAK;AACZ,cAAQ,MAAM,uBAAuB,GAAG;AACxC,YAAM,EAAE,oCAAoC,0BAA0B,GAAG,OAAO;AAAA,IAClF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC;AAE7B,QAAM,UAAU,MAAM;AACpB,SAAK,aAAa;AAAA,EACpB,GAAG,CAAC,cAAc,cAAc,WAAW,CAAC;AAE5C,QAAM,qBAAqB,MAAM,YAAY,CAAC,UAAkB;AAC9D,cAAU,KAAK;AACf,YAAQ,CAAC;AAAA,EACX,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,mBAAe,CAAC,UAAU,QAAQ,CAAC;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,MAAM,YAAY,OAAO,QAAoB;AAChE,QAAI;AACF,YAAM,WAAW,kBAAkB,IAAI,IAAI;AAAA,QACzC,cAAc,EAAE,sCAAsC,2BAA2B;AAAA,MACnF,CAAC;AACD,YAAM,EAAE,yCAAyC,kBAAkB,GAAG,SAAS;AAC/E,oBAAc;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,MAAM,yBAAyB,GAAG;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,eAAe,CAAC,CAAC;AAErB,SACE,oBAAC,QACC,8BAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,OACE,qBAAC,SAAI,WAAU,iBACb;AAAA,4BAAC,UAAM,YAAE,4BAA4B,gBAAgB,GAAE;AAAA,QACvD,oBAAC,UAAK,WAAU,6CACb,YAAE,iCAAiC,wDAAwD,GAC9F;AAAA,SACF;AAAA,MAEF,SACE,oBAAC,UAAO,SAAO,MACb,8BAAC,QAAK,MAAK,kCACR,YAAE,iCAAiC,aAAa,GACnD,GACF;AAAA,MAEF;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,MACA,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,mBAAmB,EAAE,+BAA+B,uBAAkB;AAAA,MACtE,YAAY;AAAA,QACV;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,cAAc;AAAA,MAChB;AAAA,MACA,eAAe;AAAA,QACb,OAAO,EAAE,gCAAgC,SAAS;AAAA,QAClD,WAAW;AAAA,QACX,cAAc;AAAA,MAChB;AAAA,MACA,YAAY,CAAC,QACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL;AAAA,cACE,IAAI;AAAA,cACJ,OAAO,EAAE,qCAAqC,MAAM;AAAA,cACpD,MAAM,2BAA2B,IAAI,EAAE;AAAA,YACzC;AAAA,YACA;AAAA,cACE,IAAI;AAAA,cACJ,OAAO,EAAE,uCAAuC,QAAQ;AAAA,cACxD,UAAU,MAAM,aAAa,GAAG;AAAA,YAClC;AAAA,UACF;AAAA;AAAA,MACF;AAAA,MAEF,YAAY,CAAC,QAAQ,OAAO,KAAK,2BAA2B,IAAI,EAAE,OAAO;AAAA,MACzE,YACE,oBAAC,SAAI,WAAU,mDACZ,YAAE,8BAA8B,kBAAkB,GACrD;AAAA;AAAA,EAEJ,GACF,GACF;AAEJ;AAEA,SAAS,cAAc,MAA2C;AAChE,QAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,SAAO;AAAA,IACL;AAAA,IACA,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,IAClD,MAAM,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,KAAK,OAAO;AAAA,IACtE,aAAa,OAAO,KAAK,gBAAgB,YAAY,KAAK,YAAY,SAAS,KAAK,cAAc;AAAA,IAClG,YAAY,OAAO,KAAK,eAAe,WACnC,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL;AAAA,IACN,UAAU,KAAK,aAAa,QAAQ,KAAK,cAAc;AAAA,IACvD,WAAW,OAAO,KAAK,cAAc,WACjC,KAAK,YACL,OAAO,KAAK,eAAe,WACzB,KAAK,aACL;AAAA,EACR;AACF;",
6
6
  "names": []
7
7
  }
@@ -2136,8 +2136,8 @@ function normalizeTagIds(tags) {
2136
2136
  function buildTagChange(beforeTags, afterTags) {
2137
2137
  const beforeIds = normalizeTagIds(beforeTags?.map((tag) => tag.tagId));
2138
2138
  const afterIds = normalizeTagIds(afterTags?.map((tag) => tag.tagId));
2139
- beforeIds.sort((a, b) => a.localeCompare(b));
2140
- afterIds.sort((a, b) => a.localeCompare(b));
2139
+ beforeIds.sort();
2140
+ afterIds.sort();
2141
2141
  if (beforeIds.length === afterIds.length && beforeIds.every((id, index) => id === afterIds[index])) {
2142
2142
  return null;
2143
2143
  }