@open-mercato/core 0.6.7-develop.6696.1.4236709f7e → 0.6.7-develop.6725.1.07a748cfc2

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 (154) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/admin/nav.js +14 -4
  3. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  4. package/dist/modules/auth/api/users/route.js +8 -19
  5. package/dist/modules/auth/api/users/route.js.map +2 -2
  6. package/dist/modules/auth/frontend/login.js +4 -1
  7. package/dist/modules/auth/frontend/login.js.map +2 -2
  8. package/dist/modules/auth/lib/backendChrome.js +31 -14
  9. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  10. package/dist/modules/customers/api/companies/[id]/route.js +3 -4
  11. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  12. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -2
  13. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  14. package/dist/modules/customers/api/utils.js +12 -18
  15. package/dist/modules/customers/api/utils.js.map +2 -2
  16. package/dist/modules/customers/cli.js +11 -4
  17. package/dist/modules/customers/cli.js.map +2 -2
  18. package/dist/modules/customers/components/detail/ActiveDealCard.js +2 -1
  19. package/dist/modules/customers/components/detail/ActiveDealCard.js.map +2 -2
  20. package/dist/modules/customers/components/detail/CompanyKpiBar.js +6 -5
  21. package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
  22. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +4 -2
  23. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  24. package/dist/modules/customers/components/detail/dashboard/helpers.js +4 -3
  25. package/dist/modules/customers/components/detail/dashboard/helpers.js.map +2 -2
  26. package/dist/modules/customers/components/formConfig.js +14 -10
  27. package/dist/modules/customers/components/formConfig.js.map +2 -2
  28. package/dist/modules/customers/lib/dealStatus.js +38 -0
  29. package/dist/modules/customers/lib/dealStatus.js.map +7 -0
  30. package/dist/modules/dashboards/api/widgets/data/batch/route.js +4 -2
  31. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  32. package/dist/modules/dashboards/api/widgets/data/route.js +20 -2
  33. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  34. package/dist/modules/dashboards/lib/aggregations.js +76 -40
  35. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  36. package/dist/modules/dashboards/lib/exactDecimal.js +69 -0
  37. package/dist/modules/dashboards/lib/exactDecimal.js.map +7 -0
  38. package/dist/modules/dashboards/services/widgetDataService.js +228 -5
  39. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  40. package/dist/modules/feature_toggles/components/FeatureGuard.js +3 -2
  41. package/dist/modules/feature_toggles/components/FeatureGuard.js.map +2 -2
  42. package/dist/modules/feature_toggles/components/hooks/staleTime.js +5 -0
  43. package/dist/modules/feature_toggles/components/hooks/staleTime.js.map +7 -0
  44. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js +5 -2
  45. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js.map +2 -2
  46. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js +5 -2
  47. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js.map +2 -2
  48. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js +5 -2
  49. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js.map +2 -2
  50. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js +5 -2
  51. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js.map +2 -2
  52. package/dist/modules/inbox_ops/api/proposals/route.js +16 -1
  53. package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
  54. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js +32 -0
  55. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js.map +7 -0
  56. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -5
  57. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
  58. package/dist/modules/integrations/widgets/injection/external-ids/widget.js +14 -0
  59. package/dist/modules/integrations/widgets/injection/external-ids/widget.js.map +7 -0
  60. package/dist/modules/messages/lib/searchLookup.js +8 -16
  61. package/dist/modules/messages/lib/searchLookup.js.map +2 -2
  62. package/dist/modules/payment_gateways/api/sessions/route.js +5 -0
  63. package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
  64. package/dist/modules/payment_gateways/di.js +17 -1
  65. package/dist/modules/payment_gateways/di.js.map +2 -2
  66. package/dist/modules/payment_gateways/lib/gateway-service.js +8 -0
  67. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  68. package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js +58 -0
  69. package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js.map +7 -0
  70. package/dist/modules/query_index/api/openapi.js +13 -1
  71. package/dist/modules/query_index/api/openapi.js.map +2 -2
  72. package/dist/modules/query_index/api/status.js +165 -105
  73. package/dist/modules/query_index/api/status.js.map +2 -2
  74. package/dist/modules/query_index/components/QueryIndexesTable.js +128 -41
  75. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  76. package/dist/modules/query_index/lib/batch.js +11 -4
  77. package/dist/modules/query_index/lib/batch.js.map +2 -2
  78. package/dist/modules/query_index/lib/document.js +12 -5
  79. package/dist/modules/query_index/lib/document.js.map +2 -2
  80. package/dist/modules/query_index/lib/engine.js +36 -4
  81. package/dist/modules/query_index/lib/engine.js.map +2 -2
  82. package/dist/modules/query_index/lib/indexer.js +1 -1
  83. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  84. package/dist/modules/query_index/lib/search-tokens.js +7 -4
  85. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  86. package/dist/modules/sales/api/documents/factory.js +14 -3
  87. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  88. package/dist/modules/sales/commands/documents.js +22 -1
  89. package/dist/modules/sales/commands/documents.js.map +2 -2
  90. package/dist/modules/sales/components/useSalesChannelsEnabled.js +2 -35
  91. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +2 -2
  92. package/dist/modules/sales/data/validators.js +25 -4
  93. package/dist/modules/sales/data/validators.js.map +2 -2
  94. package/dist/modules/sales/di.js +7 -0
  95. package/dist/modules/sales/di.js.map +2 -2
  96. package/dist/modules/sales/services/paymentOrderTotalResolver.js +52 -0
  97. package/dist/modules/sales/services/paymentOrderTotalResolver.js.map +7 -0
  98. package/package.json +7 -7
  99. package/src/modules/auth/api/admin/nav.ts +24 -4
  100. package/src/modules/auth/api/users/route.ts +8 -28
  101. package/src/modules/auth/frontend/login.tsx +4 -1
  102. package/src/modules/auth/lib/backendChrome.tsx +51 -13
  103. package/src/modules/customers/api/companies/[id]/route.ts +3 -4
  104. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +3 -2
  105. package/src/modules/customers/api/utils.ts +12 -31
  106. package/src/modules/customers/cli.ts +11 -4
  107. package/src/modules/customers/components/detail/ActiveDealCard.tsx +2 -1
  108. package/src/modules/customers/components/detail/CompanyKpiBar.tsx +6 -5
  109. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +4 -0
  110. package/src/modules/customers/components/detail/dashboard/helpers.ts +4 -3
  111. package/src/modules/customers/components/formConfig.tsx +12 -8
  112. package/src/modules/customers/lib/dealStatus.ts +48 -0
  113. package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -0
  114. package/src/modules/dashboards/api/widgets/data/route.ts +21 -0
  115. package/src/modules/dashboards/lib/aggregations.ts +122 -43
  116. package/src/modules/dashboards/lib/exactDecimal.ts +96 -0
  117. package/src/modules/dashboards/services/widgetDataService.ts +342 -4
  118. package/src/modules/feature_toggles/components/FeatureGuard.tsx +9 -2
  119. package/src/modules/feature_toggles/components/hooks/staleTime.ts +5 -0
  120. package/src/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.ts +13 -1
  121. package/src/modules/feature_toggles/components/hooks/useFeatureFlagJson.ts +12 -3
  122. package/src/modules/feature_toggles/components/hooks/useFeatureFlagNumber.ts +10 -1
  123. package/src/modules/feature_toggles/components/hooks/useFeatureFlagString.ts +10 -1
  124. package/src/modules/inbox_ops/api/proposals/route.ts +20 -1
  125. package/src/modules/inbox_ops/subscribers/search-reindex-proposal-created.ts +39 -0
  126. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -5
  127. package/src/modules/integrations/widgets/injection/external-ids/widget.ts +13 -0
  128. package/src/modules/messages/lib/searchLookup.ts +11 -34
  129. package/src/modules/payment_gateways/api/sessions/route.ts +5 -0
  130. package/src/modules/payment_gateways/di.ts +28 -2
  131. package/src/modules/payment_gateways/i18n/de.json +3 -0
  132. package/src/modules/payment_gateways/i18n/en.json +3 -0
  133. package/src/modules/payment_gateways/i18n/es.json +3 -0
  134. package/src/modules/payment_gateways/i18n/pl.json +3 -0
  135. package/src/modules/payment_gateways/lib/gateway-service.ts +14 -0
  136. package/src/modules/payment_gateways/lib/order-amount-reconciliation.ts +93 -0
  137. package/src/modules/query_index/api/openapi.ts +12 -0
  138. package/src/modules/query_index/api/status.ts +238 -136
  139. package/src/modules/query_index/components/QueryIndexesTable.tsx +192 -46
  140. package/src/modules/query_index/i18n/de.json +5 -3
  141. package/src/modules/query_index/i18n/en.json +5 -3
  142. package/src/modules/query_index/i18n/es.json +5 -3
  143. package/src/modules/query_index/i18n/pl.json +5 -3
  144. package/src/modules/query_index/lib/batch.ts +11 -4
  145. package/src/modules/query_index/lib/document.ts +35 -4
  146. package/src/modules/query_index/lib/engine.ts +53 -5
  147. package/src/modules/query_index/lib/indexer.ts +1 -1
  148. package/src/modules/query_index/lib/search-tokens.ts +13 -4
  149. package/src/modules/sales/api/documents/factory.ts +28 -3
  150. package/src/modules/sales/commands/documents.ts +26 -2
  151. package/src/modules/sales/components/useSalesChannelsEnabled.ts +2 -48
  152. package/src/modules/sales/data/validators.ts +50 -5
  153. package/src/modules/sales/di.ts +9 -0
  154. package/src/modules/sales/services/paymentOrderTotalResolver.ts +67 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.7-develop.6696.1.4236709f7e",
3
+ "version": "0.6.7-develop.6725.1.07a748cfc2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -254,16 +254,16 @@
254
254
  "zod": "^4.4.3"
255
255
  },
256
256
  "peerDependencies": {
257
- "@open-mercato/ai-assistant": "0.6.7-develop.6696.1.4236709f7e",
258
- "@open-mercato/shared": "0.6.7-develop.6696.1.4236709f7e",
259
- "@open-mercato/ui": "0.6.7-develop.6696.1.4236709f7e",
257
+ "@open-mercato/ai-assistant": "0.6.7-develop.6725.1.07a748cfc2",
258
+ "@open-mercato/shared": "0.6.7-develop.6725.1.07a748cfc2",
259
+ "@open-mercato/ui": "0.6.7-develop.6725.1.07a748cfc2",
260
260
  "react": "^19.0.0",
261
261
  "react-dom": "^19.0.0"
262
262
  },
263
263
  "devDependencies": {
264
- "@open-mercato/ai-assistant": "0.6.7-develop.6696.1.4236709f7e",
265
- "@open-mercato/shared": "0.6.7-develop.6696.1.4236709f7e",
266
- "@open-mercato/ui": "0.6.7-develop.6696.1.4236709f7e",
264
+ "@open-mercato/ai-assistant": "0.6.7-develop.6725.1.07a748cfc2",
265
+ "@open-mercato/shared": "0.6.7-develop.6725.1.07a748cfc2",
266
+ "@open-mercato/ui": "0.6.7-develop.6725.1.07a748cfc2",
267
267
  "@testing-library/dom": "^10.4.1",
268
268
  "@testing-library/jest-dom": "^6.9.1",
269
269
  "@testing-library/react": "^16.3.1",
@@ -5,6 +5,7 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
5
5
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
6
6
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
7
7
  import { getBackendRouteManifests } from '@open-mercato/shared/modules/registry'
8
+ import { getModuleSurfaceFingerprint } from '@open-mercato/shared/lib/modules/surfaceFingerprint'
8
9
  import { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'
9
10
  import { groupBackendRoutesByModule, resolveBackendChromePayload } from '../../lib/backendChrome'
10
11
 
@@ -12,6 +13,13 @@ export const metadata = {
12
13
  GET: { requireAuth: true },
13
14
  }
14
15
 
16
+ // The fingerprint covers the module set, each module's declared features and
17
+ // the serializable route manifest, so the TTL only has to bound what it cannot
18
+ // see — chiefly a route `icon` swap, which serializes identically. Rebuilding
19
+ // this payload is expensive (a `renderToStaticMarkup` per nav icon plus several
20
+ // scoped queries), so the bound is generous rather than aggressive.
21
+ const NAV_CACHE_TTL_MS = 30 * 60 * 1000
22
+
15
23
  const sidebarNavItemSchema: z.ZodType<{
16
24
  id?: string
17
25
  href: string
@@ -90,6 +98,13 @@ const adminNavResponseSchema = z.object({
90
98
  profilePathPrefixes: z.array(z.string()),
91
99
  grantedFeatures: z.array(z.string()),
92
100
  roles: z.array(z.string()),
101
+ // Present when a single organization is in scope; `null` under an all-organizations selection or
102
+ // when the lookup fails. Declared optional so the response contract stays additive for clients
103
+ // generated against an older schema.
104
+ currentOrganization: z.object({
105
+ id: z.string(),
106
+ name: z.string(),
107
+ }).nullable().optional(),
93
108
  })
94
109
 
95
110
  const adminNavErrorSchema = z.object({
@@ -104,7 +119,7 @@ export async function GET(req: Request) {
104
119
  const container = await createRequestContainer()
105
120
  const cache = container.resolve('cache') as {
106
121
  get?: (key: string) => Promise<unknown>
107
- set?: (key: string, value: unknown, options?: { tags?: string[] }) => Promise<void>
122
+ set?: (key: string, value: unknown, options?: { tags?: string[]; ttl?: number }) => Promise<void>
108
123
  } | null
109
124
  let selectedOrganizationId: string | null | undefined
110
125
  let selectedTenantId: string | null | undefined
@@ -138,8 +153,13 @@ export async function GET(req: Request) {
138
153
  selectedTenantId = auth.tenantId ?? null
139
154
  }
140
155
 
141
- const cacheVersion = 'v4'
142
- const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}`
156
+ // v6: the payload gained `currentOrganization`, and the payload also embeds the enabled-module set,
157
+ // its declared features, and the backend route manifest. The selection is part of the key because
158
+ // the resolved organization cannot distinguish "all organizations" from "my own organization".
159
+ // The fingerprint invalidates module-surface changes; the TTL bounds anything it cannot observe.
160
+ const cacheVersion = `v6:${getModuleSurfaceFingerprint()}`
161
+ const cacheSelection = selectedOrganizationId === undefined ? 'default' : (selectedOrganizationId ?? 'null')
162
+ const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}:${cacheSelection}`
143
163
  try {
144
164
  if (cache?.get) {
145
165
  const cached = await cache.get(cacheKey)
@@ -172,7 +192,7 @@ export async function GET(req: Request) {
172
192
  `nav:sidebar:scope:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}:${locale}`,
173
193
  ...((Array.isArray(auth.roles) ? auth.roles : []).map((role) => `nav:sidebar:role:${role}`)),
174
194
  ].filter(Boolean) as string[]
175
- await cache.set(cacheKey, payload, { tags })
195
+ await cache.set(cacheKey, payload, { tags, ttl: NAV_CACHE_TTL_MS })
176
196
  }
177
197
  } catch {
178
198
  // ignore cache write failures
@@ -23,9 +23,7 @@ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/
23
23
  import { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'
24
24
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
25
25
  import { parseBooleanFlag } from '@open-mercato/shared/lib/boolean'
26
- import { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'
27
- import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
28
- import { sql } from 'kysely'
26
+ import { findEntityIdsBySearchTokensCompat, type SearchTokenDatabase } from '@open-mercato/shared/lib/search/tokenLookup'
29
27
  import { normalizeDisplayNameInput } from '@open-mercato/core/modules/auth/lib/displayName'
30
28
  import {
31
29
  getSelectedTenantFromRequest,
@@ -504,31 +502,13 @@ async function findUserIdsBySearchTokens(
504
502
  tenantScope: string | null | undefined,
505
503
  field?: string,
506
504
  ): Promise<string[] | null> {
507
- const trimmed = search.trim()
508
- if (!trimmed) return null
509
- const searchConfig = resolveSearchConfig()
510
- if (!searchConfig.enabled) return []
511
- const { hashes } = tokenizeText(trimmed, searchConfig)
512
- if (!hashes.length) return []
513
-
514
- const db = (em as any).getKysely() as any
515
- let query = db
516
- .selectFrom('search_tokens')
517
- .select('entity_id')
518
- .where('entity_type', '=', entityType)
519
- .where('token_hash', 'in', hashes)
520
- .groupBy('entity_id')
521
- .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)
522
- if (field) {
523
- query = query.where('field', '=', field)
524
- }
525
- if (tenantScope !== undefined) {
526
- query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)
527
- }
528
- const rows = (await query.execute()) as Array<{ entity_id?: unknown }>
529
- return rows
530
- .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))
531
- .filter((id): id is string => typeof id === 'string' && id.length > 0)
505
+ return findEntityIdsBySearchTokensCompat({
506
+ db: em.getKysely<SearchTokenDatabase>(),
507
+ entityType,
508
+ query: search,
509
+ fields: field ? [field] : undefined,
510
+ scope: { tenantId: tenantScope },
511
+ })
532
512
  }
533
513
 
534
514
  async function assertCanModifySuperAdminTarget(req: Request, targetUserId: string) {
@@ -135,7 +135,10 @@ export default function LoginPage() {
135
135
  try {
136
136
  const res = await apiCall<{ userId?: string }>('/api/auth/feature-check', {
137
137
  method: 'POST',
138
- headers: { 'content-type': 'application/json' },
138
+ headers: {
139
+ 'content-type': 'application/json',
140
+ 'x-om-unauthorized-redirect': '0',
141
+ },
139
142
  body: JSON.stringify({ features: [] }),
140
143
  cache: 'no-store',
141
144
  })
@@ -22,6 +22,7 @@ import { resolveRegisteredLucideIconNode } from '@open-mercato/ui/backend/icons/
22
22
  import { profilePathPrefixes, profileSections } from './profile-sections'
23
23
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
24
24
  import { filterGrantsByEnabledModules } from '@open-mercato/shared/security/enabledModulesRegistry'
25
+ import { getNavGroupOrderOverride } from '@open-mercato/shared/modules/overrides'
25
26
  import {
26
27
  getSelectedOrganizationFromRequest,
27
28
  resolveFeatureCheckContext,
@@ -147,19 +148,35 @@ async function serializeNavItem(item: AdminNavItem): Promise<ResolvedNavItem> {
147
148
  }
148
149
  }
149
150
 
151
+ const defaultGroupOrder = [
152
+ 'customers.nav.group',
153
+ 'catalog.nav.group',
154
+ 'customers~sales.nav.group',
155
+ 'wms.nav.group',
156
+ 'resources.nav.group',
157
+ 'staff.nav.group',
158
+ 'entities.nav.group',
159
+ 'directory.nav.group',
160
+ 'attachments.nav.group',
161
+ ]
162
+
163
+ /**
164
+ * Group ids ranked ahead of everything else, most significant first.
165
+ *
166
+ * An app may prepend its own ids via `overrides.nav.groupOrder` in `modules.ts`; ids it does not name
167
+ * keep the ordering they have today. With no override configured this returns `defaultGroupOrder`
168
+ * itself, so ordering is unchanged for every existing install.
169
+ */
170
+ function resolveGroupOrder(): string[] {
171
+ const override = getNavGroupOrderOverride()
172
+ if (!override || override.length === 0) return defaultGroupOrder
173
+ const overridden = new Set(override)
174
+ return [...override, ...defaultGroupOrder.filter((id) => !overridden.has(id))]
175
+ }
176
+
150
177
  function normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {
151
- const defaultGroupOrder = [
152
- 'customers.nav.group',
153
- 'catalog.nav.group',
154
- 'customers~sales.nav.group',
155
- 'wms.nav.group',
156
- 'resources.nav.group',
157
- 'staff.nav.group',
158
- 'entities.nav.group',
159
- 'directory.nav.group',
160
- 'attachments.nav.group',
161
- ]
162
- const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))
178
+ const groupOrder = resolveGroupOrder()
179
+ const groupOrderIndex = new Map(groupOrder.map((id, index) => [id, index]))
163
180
  groups.sort((a, b) => {
164
181
  const aIndex = groupOrderIndex.get(a.id)
165
182
  const bIndex = groupOrderIndex.get(b.id)
@@ -171,7 +188,7 @@ function normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight
171
188
  if (a.weight !== b.weight) return a.weight - b.weight
172
189
  return a.name.localeCompare(b.name)
173
190
  })
174
- const defaultGroupCount = defaultGroupOrder.length
191
+ const defaultGroupCount = groupOrder.length
175
192
  groups.forEach((group, index) => {
176
193
  const rank = groupOrderIndex.get(group.id)
177
194
  const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000
@@ -275,6 +292,11 @@ export async function resolveBackendChromePayload({
275
292
 
276
293
  let scopedOrganizationId: string | null = auth.orgId ?? null
277
294
  let scopedTenantId: string | null = auth.tenantId ?? null
295
+ // The organization the caller actually *selected*, as distinct from the one the scope resolver fell
296
+ // back to. `resolveFeatureCheckContext` resolves `organizationId` to `auth.orgId` when no concrete
297
+ // organization is selected — which is precisely what an all-organizations view produces — so the
298
+ // resolved id cannot answer "which organization am I viewing".
299
+ let concretelySelectedOrganizationId: string | null = null
278
300
  let allowNavigation = true
279
301
 
280
302
  try {
@@ -287,12 +309,14 @@ export async function resolveBackendChromePayload({
287
309
  })
288
310
  scopedOrganizationId = organizationId
289
311
  scopedTenantId = scope.tenantId ?? auth.tenantId ?? null
312
+ concretelySelectedOrganizationId = scope.selectedId ?? null
290
313
  if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {
291
314
  allowNavigation = false
292
315
  }
293
316
  } catch {
294
317
  scopedOrganizationId = auth.orgId ?? null
295
318
  scopedTenantId = auth.tenantId ?? null
319
+ concretelySelectedOrganizationId = null
296
320
  }
297
321
 
298
322
  const acl = allowNavigation
@@ -411,6 +435,11 @@ export async function resolveBackendChromePayload({
411
435
  ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null)
412
436
 
413
437
  let brand: BackendChromePayload['brand'] = null
438
+ // Resolved here rather than left to callers. `brand` only populates when the organization has a
439
+ // logo, so it is a branding channel, not a dependable "which organization am I viewing" source.
440
+ // Without this field every downstream app has to fetch `/api/directory/organization-switcher` and
441
+ // walk its tree for the selected id. The row is already loaded below, so the name costs nothing.
442
+ let currentOrganization: BackendChromePayload['currentOrganization'] = null
414
443
  if (brandOrganizationId && scopedTenantId) {
415
444
  try {
416
445
  const organization = await findOneWithDecryption(
@@ -420,6 +449,12 @@ export async function resolveBackendChromePayload({
420
449
  undefined,
421
450
  { tenantId: scopedTenantId, organizationId: brandOrganizationId },
422
451
  )
452
+ // Only when a concrete organization was selected. Under an all-organizations view
453
+ // `brandOrganizationId` still resolves (to the caller's own organization, which is what keeps
454
+ // branding working), so gating on the loaded row alone would misreport the scope.
455
+ if (organization && concretelySelectedOrganizationId === brandOrganizationId) {
456
+ currentOrganization = { id: String(organization.id), name: organization.name }
457
+ }
423
458
  if (organization?.logoUrl) {
424
459
  brand = {
425
460
  name: organization.name,
@@ -430,7 +465,9 @@ export async function resolveBackendChromePayload({
430
465
  }
431
466
  }
432
467
  } catch {
468
+ // Fail soft, as before: a failed organization lookup must not take down the nav payload.
433
469
  brand = null
470
+ currentOrganization = null
434
471
  }
435
472
  }
436
473
 
@@ -443,5 +480,6 @@ export async function resolveBackendChromePayload({
443
480
  grantedFeatures,
444
481
  roles: Array.isArray(auth.roles) ? auth.roles : [],
445
482
  brand,
483
+ currentOrganization,
446
484
  }
447
485
  }
@@ -29,6 +29,7 @@ import {
29
29
  mergeCompanyCustomFieldValues,
30
30
  } from '../../../lib/customFieldRouting'
31
31
  import { isOpenInteractionStatus, TERMINAL_INTERACTION_STATUS_LIST } from '../../../lib/interactionStatus'
32
+ import { isOpenDealStatus, isWonDealStatus } from '../../../lib/dealStatus'
32
33
  import {
33
34
  CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,
34
35
  EXAMPLE_TODO_SOURCE,
@@ -1015,10 +1016,8 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
1015
1016
  .map((value) => (value instanceof Date ? value.toISOString() : typeof value === 'string' ? value : ''))
1016
1017
  .filter((value) => value.length > 0),
1017
1018
  )
1018
- const activeDeals = dealLinksForMetrics.filter(
1019
- (deal) => deal.status !== 'won' && deal.status !== 'lost' && deal.status !== 'closed',
1020
- )
1021
- const wonDeals = dealLinksForMetrics.filter((deal) => deal.status === 'won')
1019
+ const activeDeals = dealLinksForMetrics.filter((deal) => isOpenDealStatus(deal.status))
1020
+ const wonDeals = dealLinksForMetrics.filter((deal) => isWonDealStatus(deal.status))
1022
1021
  const activeDealsValue = activeDeals.reduce((sum, deal) => sum + (parseDealAmount(deal.valueAmount) ?? 0), 0)
1023
1022
  const ltvValue = wonDeals.length
1024
1023
  ? wonDeals.reduce((sum, deal) => sum + (parseDealAmount(deal.valueAmount) ?? 0), 0)
@@ -25,6 +25,7 @@ import {
25
25
  filterActivePersonCompanyLinks,
26
26
  withActiveCustomerPersonCompanyLinkFilter,
27
27
  } from '../../../../../lib/personCompanyLinkTable'
28
+ import { isOpenDealStatus, isWonDealStatus } from '../../../../../lib/dealStatus'
28
29
  import { createLogger } from '@open-mercato/shared/lib/logger'
29
30
 
30
31
  const logger = createLogger('customers')
@@ -280,13 +281,13 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
280
281
 
281
282
  const activeDeals = companyDealLinks
282
283
  .map((dcl) => dcl.deal as CustomerDeal)
283
- .filter((deal) => deal.status !== 'win' && deal.status !== 'loose' && !deal.deletedAt)
284
+ .filter((deal) => isOpenDealStatus(deal.status) && !deal.deletedAt)
284
285
  .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
285
286
  const activeDeal = activeDeals.length > 0 ? activeDeals[0] : null
286
287
 
287
288
  const wonDeals = companyDealLinks
288
289
  .map((dcl) => dcl.deal as CustomerDeal)
289
- .filter((deal) => deal.status === 'win' && !deal.deletedAt)
290
+ .filter((deal) => isWonDealStatus(deal.status) && !deal.deletedAt)
290
291
  let clv: { amount: number; currency: string } | null = null
291
292
  if (wonDeals.length > 0) {
292
293
  const currencies = new Map<string, number>()
@@ -4,8 +4,7 @@ import { sql } from 'kysely'
4
4
  import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
5
5
  import type { EntityId } from '@open-mercato/shared/modules/entities'
6
6
  import type { QueryCustomFieldSource, QueryJoinEdge, QueryEngine } from '@open-mercato/shared/lib/query/types'
7
- import { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'
8
- import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
7
+ import { findEntityIdsBySearchTokensCompat, type SearchTokenDatabase } from '@open-mercato/shared/lib/search/tokenLookup'
9
8
  import { SortDir } from '@open-mercato/shared/lib/query/types'
10
9
 
11
10
  const { withScopedPayload, parseScopedCommandInput } = createScopedApiHelpers({
@@ -106,36 +105,18 @@ async function findSearchTokenEntityIds({
106
105
  fields,
107
106
  query,
108
107
  }: SearchTokenMatchInput): Promise<string[] | null> {
109
- const trimmed = query.trim()
110
- if (!trimmed) return null
111
-
112
- const tokens = tokenizeText(trimmed, resolveSearchConfig())
113
- if (!tokens.hashes.length) return []
114
-
115
108
  const em = ctx.container.resolve('em') as EntityManager
116
- const db = em.getKysely<any>() as any
117
- let searchQuery = db
118
- .selectFrom('search_tokens')
119
- .select('entity_id')
120
- .where('entity_type', '=', entityType)
121
- .where('field', 'in', fields)
122
- .where('token_hash', 'in', tokens.hashes)
123
- .groupBy('entity_id')
124
- .having(sql<boolean>`count(distinct token_hash) >= ${tokens.hashes.length}`)
125
-
126
- if (ctx.auth?.tenantId !== undefined) {
127
- searchQuery = searchQuery.where(sql<boolean>`tenant_id is not distinct from ${ctx.auth?.tenantId ?? null}`)
128
- }
129
- if (ctx.selectedOrganizationId) {
130
- searchQuery = searchQuery.where('organization_id', '=', ctx.selectedOrganizationId)
131
- } else if (Array.isArray(ctx.organizationIds) && ctx.organizationIds.length > 0) {
132
- searchQuery = searchQuery.where('organization_id', 'in', ctx.organizationIds)
133
- }
134
-
135
- const rows = await searchQuery.execute() as Array<{ entity_id?: unknown }>
136
- return rows
137
- .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))
138
- .filter((id): id is string => typeof id === 'string' && id.length > 0)
109
+ return findEntityIdsBySearchTokensCompat({
110
+ db: em.getKysely<SearchTokenDatabase>(),
111
+ entityType,
112
+ query,
113
+ fields,
114
+ scope: {
115
+ tenantId: ctx.auth?.tenantId !== undefined ? ctx.auth?.tenantId ?? null : undefined,
116
+ organizationId: ctx.selectedOrganizationId ?? undefined,
117
+ organizationIds: ctx.organizationIds,
118
+ },
119
+ })
139
120
  }
140
121
 
141
122
  async function mapScopedEntityIds({
@@ -12,6 +12,7 @@ import { E as CoreEntities } from '#generated/entities.ids.generated'
12
12
  import { createProgressBar } from '@open-mercato/shared/lib/cli/progress'
13
13
  import { buildIndexDocument, type IndexCustomFieldValue } from '@open-mercato/core/modules/query_index/lib/document'
14
14
  import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
15
+ import { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'
15
16
  import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
16
17
  import type { EntityId } from '@open-mercato/shared/modules/entities'
17
18
  import {
@@ -1994,6 +1995,7 @@ async function seedCustomerStressTest(
1994
1995
  updated_at: Date
1995
1996
  deleted_at: null
1996
1997
  }> = []
1998
+ const searchConfig = resolveSearchConfig()
1997
1999
  for (const [entityType, bucket] of pendingIndexDocs.entries()) {
1998
2000
  for (const entry of bucket.values()) {
1999
2001
  if (!entry.baseRow || Object.keys(entry.baseRow).length === 0) continue
@@ -2002,10 +2004,15 @@ async function seedCustomerStressTest(
2002
2004
  entity_id: entry.recordId,
2003
2005
  organization_id: entry.organizationId,
2004
2006
  tenant_id: entry.tenantId,
2005
- doc: buildIndexDocument(entry.baseRow, entry.customFields, {
2006
- organizationId: entry.organizationId,
2007
- tenantId: entry.tenantId,
2008
- }),
2007
+ doc: buildIndexDocument(
2008
+ entry.baseRow,
2009
+ entry.customFields,
2010
+ {
2011
+ organizationId: entry.organizationId,
2012
+ tenantId: entry.tenantId,
2013
+ },
2014
+ { entityType, config: searchConfig },
2015
+ ),
2009
2016
  index_version: 1,
2010
2017
  created_at: entry.createdAt,
2011
2018
  updated_at: entry.updatedAt,
@@ -7,6 +7,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
7
7
  import { Badge } from '@open-mercato/ui/primitives/badge'
8
8
  import { IconButton } from '@open-mercato/ui/primitives/icon-button'
9
9
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { isOpenDealStatus } from '../../lib/dealStatus'
10
11
  import type { DealSummary } from '../formConfig'
11
12
  import { formatCurrency } from './utils'
12
13
 
@@ -25,7 +26,7 @@ export function ActiveDealCard({ deals, onHide }: ActiveDealCardProps) {
25
26
  const [pipelineStages, setPipelineStages] = React.useState<PipelineStageOption[]>([])
26
27
 
27
28
  const activeDeals = React.useMemo(
28
- () => deals.filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed'),
29
+ () => deals.filter((d) => isOpenDealStatus(d.status)),
29
30
  [deals],
30
31
  )
31
32
 
@@ -11,6 +11,7 @@ import {
11
11
  writeVersionedIdSet,
12
12
  clearVersionedPreference,
13
13
  } from '@open-mercato/shared/lib/browser/versionedPreference'
14
+ import { isOpenDealStatus, isWonDealStatus } from '../../lib/dealStatus'
14
15
  import type { CompanyOverview, DealSummary, InteractionSummary } from '../formConfig'
15
16
  import { formatCurrency } from './utils'
16
17
 
@@ -19,7 +20,7 @@ const STORAGE_VERSION = 1
19
20
 
20
21
  function sumActiveDeals(deals: DealSummary[]): number {
21
22
  return deals
22
- .filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
23
+ .filter((d) => isOpenDealStatus(d.status))
23
24
  .reduce((sum, d) => {
24
25
  const amount = typeof d.valueAmount === 'number' ? d.valueAmount : parseFloat(String(d.valueAmount ?? '0'))
25
26
  return sum + (Number.isFinite(amount) ? amount : 0)
@@ -27,7 +28,7 @@ function sumActiveDeals(deals: DealSummary[]): number {
27
28
  }
28
29
 
29
30
  function getActiveDeals(deals: DealSummary[]): DealSummary[] {
30
- return deals.filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
31
+ return deals.filter((d) => isOpenDealStatus(d.status))
31
32
  }
32
33
 
33
34
  function computeActivityTrend(interactions: InteractionSummary[]): KpiTrend | undefined {
@@ -51,7 +52,7 @@ function computeActivityTrend(interactions: InteractionSummary[]): KpiTrend | un
51
52
  }
52
53
 
53
54
  function computeDealTrend(deals: DealSummary[]): KpiTrend | undefined {
54
- const active = deals.filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
55
+ const active = deals.filter((d) => isOpenDealStatus(d.status))
55
56
  if (active.length === 0) return undefined
56
57
  const now = Date.now()
57
58
  const monthMs = 30 * 86_400_000
@@ -81,7 +82,7 @@ export function CompanyKpiBar({ data }: CompanyKpiBarProps) {
81
82
 
82
83
  const ltvValue = React.useMemo(() => {
83
84
  if (data.kpis?.ltvValue !== undefined) return data.kpis.ltvValue
84
- const wonDeals = data.deals.filter((d) => d.status === 'won')
85
+ const wonDeals = data.deals.filter((d) => isWonDealStatus(d.status))
85
86
  if (wonDeals.length === 0) return null
86
87
  return wonDeals.reduce((sum, d) => {
87
88
  const amt = typeof d.valueAmount === 'number' ? d.valueAmount : parseFloat(String(d.valueAmount ?? '0'))
@@ -154,7 +155,7 @@ export function CompanyKpiBar({ data }: CompanyKpiBarProps) {
154
155
  : `${v} ${v === 1 ? t('customers.companies.dashboard.kpi.year', 'year') : t('customers.companies.dashboard.kpi.years', 'years')}`
155
156
  : undefined,
156
157
  comparisonLabel: clientTenureYears !== null
157
- ? `${data.kpis?.completedDealsCount ?? data.deals.filter((d) => d.status === 'won').length} ${t('customers.companies.dashboard.kpi.completedDeals', 'completed deals')}`
158
+ ? `${data.kpis?.completedDealsCount ?? data.deals.filter((d) => isWonDealStatus(d.status)).length} ${t('customers.companies.dashboard.kpi.completedDeals', 'completed deals')}`
158
159
  : t('customers.companies.dashboard.kpi.noInteractions', 'No interactions yet'),
159
160
  },
160
161
  ], [t, activeDealsValue, dealTrend, dealCurrency, activeDeals.length, activityTrend, ltvValue, clientTenureYears, data.deals, data.interactions.length, data.kpis])
@@ -99,6 +99,8 @@ interface ScheduleActivityDialogProps {
99
99
  onActivityCreated?: () => void
100
100
  /** When provided, dialog opens in edit mode with pre-filled data */
101
101
  editData?: ScheduleActivityEditData | null
102
+ /** Default country ISO2 code for phone number input when value is empty/unparsed */
103
+ defaultCountryIso2?: string
102
104
  }
103
105
 
104
106
  export function ScheduleActivityDialog({
@@ -111,6 +113,7 @@ export function ScheduleActivityDialog({
111
113
  entityType,
112
114
  onActivityCreated,
113
115
  editData,
116
+ defaultCountryIso2,
114
117
  }: ScheduleActivityDialogProps) {
115
118
  const t = useT()
116
119
  const state = useScheduleFormState({ open, editData: editData ?? null })
@@ -689,6 +692,7 @@ export function ScheduleActivityDialog({
689
692
  externalError={callPhoneError}
690
693
  invalidLabel={callPhoneInvalidMessage}
691
694
  minDigits={7}
695
+ defaultCountryIso2={defaultCountryIso2}
692
696
  />
693
697
  </div>
694
698
  ) : (
@@ -1,9 +1,10 @@
1
1
  import type { KpiTrend } from '@open-mercato/ui/backend/charts/KpiCard'
2
+ import { isOpenDealStatus } from '../../../lib/dealStatus'
2
3
  import type { DealSummary, InteractionSummary, TodoLinkSummary } from '../../formConfig'
3
4
 
4
5
  export function sumActiveDeals(deals: DealSummary[]): number {
5
6
  return deals
6
- .filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
7
+ .filter((d) => isOpenDealStatus(d.status))
7
8
  .reduce((sum, d) => {
8
9
  const amount = typeof d.valueAmount === 'number' ? d.valueAmount : parseFloat(String(d.valueAmount ?? '0'))
9
10
  return sum + (Number.isFinite(amount) ? amount : 0)
@@ -11,7 +12,7 @@ export function sumActiveDeals(deals: DealSummary[]): number {
11
12
  }
12
13
 
13
14
  export function getActiveDeals(deals: DealSummary[]): DealSummary[] {
14
- return deals.filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
15
+ return deals.filter((d) => isOpenDealStatus(d.status))
15
16
  }
16
17
 
17
18
  export function getOpenTasks(todos: TodoLinkSummary[]): TodoLinkSummary[] {
@@ -68,7 +69,7 @@ export function computeActivityTrend(interactions: InteractionSummary[]): KpiTre
68
69
  }
69
70
 
70
71
  export function computeDealTrend(deals: DealSummary[]): KpiTrend | undefined {
71
- const active = deals.filter((d) => d.status !== 'won' && d.status !== 'lost' && d.status !== 'closed')
72
+ const active = deals.filter((d) => isOpenDealStatus(d.status))
72
73
  if (active.length === 0) return undefined
73
74
  const now = Date.now()
74
75
  const monthMs = 30 * 86_400_000
@@ -418,7 +418,7 @@ const companyDictionaryFieldDefinitions: DictionaryFieldDefinition[] = [
418
418
  },
419
419
  ]
420
420
 
421
- const createPrimaryPhoneField = (t: Translator): CrudField => ({
421
+ const createPrimaryPhoneField = (t: Translator, defaultCountryIso2?: string): CrudField => ({
422
422
  id: 'primaryPhone',
423
423
  label: t('customers.people.form.primaryPhone'),
424
424
  type: 'custom',
@@ -447,6 +447,7 @@ const createPrimaryPhoneField = (t: Translator): CrudField => ({
447
447
  invalidLabel={t('customers.people.form.primaryPhone.invalid', 'Enter a valid phone number with country code (e.g. +1 212 555 1234)')}
448
448
  minDigits={7}
449
449
  onDuplicateLookup={!disabled && !error ? duplicateLookup : undefined}
450
+ defaultCountryIso2={defaultCountryIso2}
450
451
  />
451
452
  )
452
453
  },
@@ -869,7 +870,8 @@ export const createDisplayNameSection = (t: Translator) =>
869
870
  )
870
871
  }
871
872
 
872
- export const createPersonFormFields = (t: Translator): CrudField[] => {
873
+ export const createPersonFormFields = (t: Translator, options?: { defaultCountryIso2?: string }): CrudField[] => {
874
+ const defaultCountryIso2 = options?.defaultCountryIso2
873
875
  const contactSection = createSectionHeadingField('__contactInformationSection', t('customers.people.form.sections.contactInformation'))
874
876
  const companySection = createSectionHeadingField('__companyInformationSection', t('customers.people.form.sections.companyInformation'))
875
877
  const dictionaryFields: CrudField[] = dictionaryFieldDefinitions.map((definition) => ({
@@ -930,7 +932,7 @@ export const createPersonFormFields = (t: Translator): CrudField[] => {
930
932
  },
931
933
  contactSection,
932
934
  createPrimaryEmailField(t),
933
- createPrimaryPhoneField(t),
935
+ createPrimaryPhoneField(t, defaultCountryIso2),
934
936
  companySection,
935
937
  {
936
938
  id: 'companyEntityId',
@@ -1225,7 +1227,8 @@ export const createCompanyFormSchema = () =>
1225
1227
  })
1226
1228
  .passthrough()
1227
1229
 
1228
- export const createCompanyFormFields = (t: Translator): CrudField[] => {
1230
+ export const createCompanyFormFields = (t: Translator, options?: { defaultCountryIso2?: string }): CrudField[] => {
1231
+ const defaultCountryIso2 = options?.defaultCountryIso2
1229
1232
  const dictionaryFields: CrudField[] = companyDictionaryFieldDefinitions.map((definition) => ({
1230
1233
  id: definition.id,
1231
1234
  label: t(definition.labelKey),
@@ -1270,6 +1273,7 @@ export const createCompanyFormFields = (t: Translator): CrudField[] => {
1270
1273
  placeholder={t('customers.companies.form.primaryPhonePlaceholder', '+1 555 123 4567')}
1271
1274
  invalidLabel={t('customers.people.form.primaryPhone.invalid', 'Enter a valid phone number with country code (e.g. +1 212 555 1234)')}
1272
1275
  minDigits={7}
1276
+ defaultCountryIso2={defaultCountryIso2}
1273
1277
  />
1274
1278
  ),
1275
1279
  } as CrudField,
@@ -1651,8 +1655,8 @@ const buildIndustryLabels = (t: Translator): DictionarySelectLabels => ({
1651
1655
  manageTitle: t('customers.people.form.dictionary.manage'),
1652
1656
  })
1653
1657
 
1654
- export const createCompanyEditFields = (t: Translator): CrudField[] => {
1655
- const baseFields = createCompanyFormFields(t)
1658
+ export const createCompanyEditFields = (t: Translator, options?: { defaultCountryIso2?: string }): CrudField[] => {
1659
+ const baseFields = createCompanyFormFields(t, options)
1656
1660
  const industryLabels = buildIndustryLabels(t)
1657
1661
 
1658
1662
  return baseFields.map((field) => {
@@ -1676,8 +1680,8 @@ export const createCompanyEditFields = (t: Translator): CrudField[] => {
1676
1680
  })
1677
1681
  }
1678
1682
 
1679
- export const createPersonEditFields = (t: Translator): CrudField[] => {
1680
- const baseFields = createPersonFormFields(t)
1683
+ export const createPersonEditFields = (t: Translator, options?: { defaultCountryIso2?: string }): CrudField[] => {
1684
+ const baseFields = createPersonFormFields(t, options)
1681
1685
  return [
1682
1686
  ...baseFields,
1683
1687
  {