@open-mercato/core 0.6.7-develop.6595.1.e93ecd3cfa → 0.6.7-develop.6600.1.49a5df927f

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 (25) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/attachments/backend/storage/attachments/page.meta.js +2 -2
  3. package/dist/modules/attachments/backend/storage/attachments/page.meta.js.map +1 -1
  4. package/dist/modules/auth/lib/backendChrome.js +1 -1
  5. package/dist/modules/auth/lib/backendChrome.js.map +1 -1
  6. package/dist/modules/customers/components/calendar/CalendarTabs.js +1 -1
  7. package/dist/modules/customers/components/calendar/CalendarTabs.js.map +2 -2
  8. package/dist/modules/sales/backend/sales/documents/[id]/page.js +2 -17
  9. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  10. package/dist/modules/sales/components/ContactEmailDisplay.js +24 -0
  11. package/dist/modules/sales/components/ContactEmailDisplay.js.map +7 -0
  12. package/package.json +7 -7
  13. package/src/modules/attachments/backend/storage/attachments/page.meta.ts +2 -2
  14. package/src/modules/attachments/i18n/de.json +1 -1
  15. package/src/modules/attachments/i18n/en.json +1 -1
  16. package/src/modules/attachments/i18n/es.json +1 -1
  17. package/src/modules/attachments/i18n/pl.json +1 -1
  18. package/src/modules/auth/lib/backendChrome.tsx +1 -1
  19. package/src/modules/customers/components/calendar/CalendarTabs.tsx +1 -1
  20. package/src/modules/customers/i18n/de.json +0 -1
  21. package/src/modules/customers/i18n/en.json +0 -1
  22. package/src/modules/customers/i18n/es.json +0 -1
  23. package/src/modules/customers/i18n/pl.json +0 -1
  24. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +4 -15
  25. package/src/modules/sales/components/ContactEmailDisplay.tsx +27 -0
@@ -1,4 +1,4 @@
1
- [build:core] found 3499 entry points
1
+ [build:core] found 3500 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 188 entry points
4
4
  [build:core:generated] built successfully
@@ -3,8 +3,8 @@ const metadata = {
3
3
  requireFeatures: ["attachments.view"],
4
4
  pageTitle: "Attachments",
5
5
  pageTitleKey: "attachments.library.title",
6
- pageGroup: "Storage",
7
- pageGroupKey: "customers.storage.nav.group",
6
+ pageGroup: "Media",
7
+ pageGroupKey: "attachments.nav.group",
8
8
  pagePriority: 20,
9
9
  pageOrder: 110,
10
10
  icon: "archive",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/attachments/backend/storage/attachments/page.meta.ts"],
4
- "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['attachments.view'],\n pageTitle: 'Attachments',\n pageTitleKey: 'attachments.library.title',\n pageGroup: 'Storage',\n pageGroupKey: 'customers.storage.nav.group',\n pagePriority: 20,\n pageOrder: 110,\n icon: 'archive',\n breadcrumb: [\n { label: 'Attachments', labelKey: 'attachments.library.title' },\n ],\n} as const\n"],
4
+ "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['attachments.view'],\n pageTitle: 'Attachments',\n pageTitleKey: 'attachments.library.title',\n pageGroup: 'Media',\n pageGroupKey: 'attachments.nav.group',\n pagePriority: 20,\n pageOrder: 110,\n icon: 'archive',\n breadcrumb: [\n { label: 'Attachments', labelKey: 'attachments.library.title' },\n ],\n} as const\n"],
5
5
  "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,kBAAkB;AAAA,EACpC,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY;AAAA,IACV,EAAE,OAAO,eAAe,UAAU,4BAA4B;AAAA,EAChE;AACF;",
6
6
  "names": []
7
7
  }
@@ -82,7 +82,7 @@ function normalizeGroupWeights(groups) {
82
82
  "staff.nav.group",
83
83
  "entities.nav.group",
84
84
  "directory.nav.group",
85
- "customers.storage.nav.group"
85
+ "attachments.nav.group"
86
86
  ];
87
87
  const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]));
88
88
  groups.sort((a, b) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/lib/backendChrome.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport type { BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'\nimport type {\n BackendChromePayload,\n BackendChromeNavGroup,\n BackendChromeNavItem,\n BackendChromeSectionGroup,\n BackendChromeSectionItem,\n} from '@open-mercato/shared/modules/navigation/backendChrome'\nimport {\n buildAdminNav,\n buildSettingsSections,\n computeSettingsPathPrefixes,\n convertToSectionNavGroups,\n type AdminNavItem,\n} from '@open-mercato/ui/backend/utils/nav'\nimport { resolveRegisteredLucideIconNode } from '@open-mercato/ui/backend/icons/lucideRegistry'\nimport { profilePathPrefixes, profileSections } from './profile-sections'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { filterGrantsByEnabledModules } from '@open-mercato/shared/security/enabledModulesRegistry'\nimport {\n getSelectedOrganizationFromRequest,\n resolveFeatureCheckContext,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n applySidebarPreference,\n loadFirstRoleSidebarPreference,\n loadSidebarPreference,\n} from '@open-mercato/core/modules/auth/services/sidebarPreferencesService'\nimport type { SidebarPreferencesSettings } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\ntype TranslationFn = (key: string | undefined, fallback: string) => string\n\ntype RouteModule = {\n id: string\n backendRoutes?: BackendRouteManifestEntry[]\n}\n\nexport function groupBackendRoutesByModule(routes: BackendRouteManifestEntry[]): RouteModule[] {\n return Array.from(\n routes.reduce((grouped, route) => {\n const list = grouped.get(route.moduleId) ?? []\n list.push(route)\n grouped.set(route.moduleId, list)\n return grouped\n }, new Map<string, BackendRouteManifestEntry[]>()),\n ).map(([id, backendRoutes]) => ({ id, backendRoutes }))\n}\n\ntype SerializableSectionItem = {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}\n\ntype SerializableSectionGroup = {\n id: string\n label: string\n labelKey?: string\n order?: number\n items: SerializableSectionItem[]\n}\n\ntype ResolvedNavItem = Omit<BackendChromeNavItem, 'defaultTitle' | 'children'> & {\n defaultTitle: string\n children?: ResolvedNavItem[]\n}\n\ntype ResolveBackendChromePayloadArgs = {\n auth: Exclude<AuthContext, null>\n locale: string\n modules: RouteModule[]\n translate: TranslationFn\n request?: Request\n selectedOrganizationId?: string | null\n selectedTenantId?: string | null\n}\n\nconst settingsSectionOrder: Record<string, number> = {\n system: 1,\n auth: 2,\n 'customer-portal': 3,\n 'data-designer': 4,\n 'module-configs': 5,\n currencies: 6,\n directory: 7,\n 'feature-toggles': 8,\n}\n\ntype NavGroupWithWeight = Omit<BackendChromeNavGroup, 'id' | 'defaultName' | 'items'> & {\n id: string\n defaultName: string\n items: ResolvedNavItem[]\n weight: number\n}\n\nlet renderToStaticMarkupPromise: Promise<typeof import('react-dom/server')> | null = null\n\nasync function serializeIconMarkup(icon: React.ReactNode | undefined): Promise<string | undefined> {\n if (!icon) return undefined\n if (!renderToStaticMarkupPromise) {\n renderToStaticMarkupPromise = import('react-dom/server')\n }\n const { renderToStaticMarkup } = await renderToStaticMarkupPromise\n\n const normalizedIcon = typeof icon === 'string'\n ? resolveRegisteredLucideIconNode(icon, 'size-4')\n : icon\n\n if (!normalizedIcon) return undefined\n\n try {\n const markup = renderToStaticMarkup(<>{normalizedIcon}</>)\n return markup.trim().length > 0 ? markup : undefined\n } catch {\n // Some icon values may be client-only component references after dependency upgrades.\n // Avoid taking down the entire nav payload because one icon cannot be rendered server-side.\n return undefined\n }\n}\n\nasync function serializeNavItem(item: AdminNavItem): Promise<ResolvedNavItem> {\n return {\n id: item.href,\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n pageContext: item.pageContext,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeNavItem(child))) : undefined,\n }\n}\n\nfunction normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'customers.storage.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n group.weight =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n })\n return groups\n}\n\nasync function groupEntries(entries: AdminNavItem[]): Promise<NavGroupWithWeight[]> {\n const groupMap = new Map<string, NavGroupWithWeight>()\n for (const entry of entries) {\n const weight = entry.priority ?? entry.order ?? 10_000\n const serializedItem = await serializeNavItem(entry)\n const existing = groupMap.get(entry.groupId)\n if (existing) {\n existing.items.push(serializedItem)\n if (weight < existing.weight) existing.weight = weight\n continue\n }\n groupMap.set(entry.groupId, {\n id: entry.groupId,\n name: entry.group,\n defaultName: entry.groupDefaultName,\n items: [serializedItem],\n weight,\n })\n }\n return normalizeGroupWeights(Array.from(groupMap.values()))\n}\n\nfunction adoptSidebarDefaults(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const adoptItems = (items: ResolvedNavItem[]): ResolvedNavItem[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nasync function serializeSectionItem(item: {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}): Promise<BackendChromeSectionItem> {\n return {\n id: item.id,\n label: item.label,\n labelKey: item.labelKey,\n href: item.href,\n order: item.order,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeSectionItem(child))) : undefined,\n }\n}\n\nasync function serializeSectionGroups(groups: SerializableSectionGroup[]): Promise<BackendChromeSectionGroup[]> {\n return Promise.all(groups.map(async (group) => ({\n id: group.id,\n label: group.label,\n labelKey: group.labelKey,\n order: group.order,\n items: await Promise.all(group.items.map((item) => serializeSectionItem(item))),\n })))\n}\n\nasync function loadScopedContainer(): Promise<AwilixContainer> {\n return createRequestContainer()\n}\n\nexport async function resolveBackendChromePayload({\n auth,\n locale,\n modules,\n translate,\n request,\n selectedOrganizationId,\n selectedTenantId,\n}: ResolveBackendChromePayloadArgs): Promise<BackendChromePayload> {\n const container = await loadScopedContainer()\n const em = container.resolve('em') as EntityManager\n const rbac = container.resolve('rbacService') as {\n loadAcl: (userId: string, scope: { tenantId: string | null; organizationId: string | null }) => Promise<{\n isSuperAdmin: boolean\n features: string[]\n }>\n userHasAllFeatures: (userId: string, required: string[], scope: { tenantId: string | null; organizationId: string | null }) => Promise<boolean>\n }\n\n let scopedOrganizationId: string | null = auth.orgId ?? null\n let scopedTenantId: string | null = auth.tenantId ?? null\n let allowNavigation = true\n\n try {\n const { organizationId, scope, allowedOrganizationIds } = await resolveFeatureCheckContext({\n container,\n auth,\n request,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n })\n scopedOrganizationId = organizationId\n scopedTenantId = scope.tenantId ?? auth.tenantId ?? null\n if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {\n allowNavigation = false\n }\n } catch {\n scopedOrganizationId = auth.orgId ?? null\n scopedTenantId = auth.tenantId ?? null\n }\n\n const acl = allowNavigation\n ? await rbac.loadAcl(auth.sub, {\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n })\n : { isSuperAdmin: false, features: [] }\n\n const rawGrantedFeatures = acl.isSuperAdmin ? ['*'] : acl.features\n const grantedFeatures = filterGrantsByEnabledModules(rawGrantedFeatures)\n const featureChecker = async (features: string[]): Promise<string[]> => {\n if (!allowNavigation || !features.length) return []\n const context = {\n tenantId: scopedTenantId ?? auth.tenantId ?? null,\n organizationId: scopedOrganizationId ?? null,\n }\n const hasAll = await rbac.userHasAllFeatures(auth.sub, features, context)\n if (hasAll) return features\n\n const granted: string[] = []\n for (const feature of features) {\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, [feature], context)\n if (hasFeature) granted.push(feature)\n }\n return granted\n }\n\n let userEntities: Array<{ entityId: string; label: string; href: string }> = []\n if (allowNavigation) {\n try {\n const where: FilterQuery<CustomEntity> = {\n isActive: true,\n showInSidebar: true,\n }\n where.$and = [\n { $or: [{ organizationId: scopedOrganizationId ?? undefined }, { organizationId: null }] },\n { $or: [{ tenantId: scopedTenantId ?? undefined }, { tenantId: null }] },\n ]\n const entities = await em.find(CustomEntity, where, { orderBy: { label: 'asc' } })\n userEntities = entities.map((entity) => ({\n entityId: entity.entityId,\n label: entity.label,\n href: `/backend/entities/user/${encodeURIComponent(entity.entityId)}/records`,\n }))\n } catch {\n userEntities = []\n }\n }\n\n const ctxAuth = {\n roles: auth.roles || [],\n sub: auth.sub,\n tenantId: scopedTenantId,\n orgId: scopedOrganizationId,\n }\n const entries = allowNavigation\n ? await buildAdminNav(\n modules,\n { auth: ctxAuth },\n userEntities,\n translate,\n { checkFeatures: featureChecker },\n )\n : []\n\n let rolePreference: SidebarPreferencesSettings | null = null\n let userPreference: SidebarPreferencesSettings | null = null\n\n if (Array.isArray(auth.roles) && auth.roles.length > 0) {\n const roleRecords = scopedTenantId\n ? await em.find(Role, {\n name: { $in: auth.roles },\n tenantId: scopedTenantId,\n })\n : []\n const roleIds = Array.isArray(roleRecords) ? roleRecords.map((role) => role.id) : []\n if (roleIds.length > 0) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: scopedTenantId,\n locale,\n })\n }\n }\n\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n if (effectiveUserId) {\n userPreference = await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n locale,\n })\n }\n\n const baseGroups = await groupEntries(entries)\n const groupsWithRole = rolePreference\n ? applySidebarPreference<NavGroupWithWeight>(baseGroups, rolePreference)\n : baseGroups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n const appliedGroups = userPreference\n ? applySidebarPreference<NavGroupWithWeight>(baseForUser, userPreference)\n : baseForUser\n\n const settingsSections = await serializeSectionGroups(\n convertToSectionNavGroups(\n buildSettingsSections(entries, settingsSectionOrder),\n translate,\n ),\n )\n\n const requestOrganizationId = request ? getSelectedOrganizationFromRequest(request) : null\n const fallbackOrganizationId = selectedOrganizationId ?? requestOrganizationId ?? auth.orgId ?? null\n const brandOrganizationId = scopedOrganizationId\n ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null)\n\n let brand: BackendChromePayload['brand'] = null\n if (brandOrganizationId && scopedTenantId) {\n try {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: brandOrganizationId, tenant: scopedTenantId, deletedAt: null },\n undefined,\n { tenantId: scopedTenantId, organizationId: brandOrganizationId },\n )\n if (organization?.logoUrl) {\n brand = {\n name: organization.name,\n logo: {\n src: organization.logoUrl,\n alt: `${organization.name} logo`,\n },\n }\n }\n } catch {\n brand = null\n }\n }\n\n return {\n groups: appliedGroups.map(({ weight: _weight, ...group }) => group),\n settingsSections,\n settingsPathPrefixes: computeSettingsPathPrefixes(buildSettingsSections(entries, settingsSectionOrder)),\n profileSections: await serializeSectionGroups(profileSections),\n profilePathPrefixes,\n grantedFeatures,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n brand,\n }\n}\n"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport type { BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'\nimport type {\n BackendChromePayload,\n BackendChromeNavGroup,\n BackendChromeNavItem,\n BackendChromeSectionGroup,\n BackendChromeSectionItem,\n} from '@open-mercato/shared/modules/navigation/backendChrome'\nimport {\n buildAdminNav,\n buildSettingsSections,\n computeSettingsPathPrefixes,\n convertToSectionNavGroups,\n type AdminNavItem,\n} from '@open-mercato/ui/backend/utils/nav'\nimport { resolveRegisteredLucideIconNode } from '@open-mercato/ui/backend/icons/lucideRegistry'\nimport { profilePathPrefixes, profileSections } from './profile-sections'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { filterGrantsByEnabledModules } from '@open-mercato/shared/security/enabledModulesRegistry'\nimport {\n getSelectedOrganizationFromRequest,\n resolveFeatureCheckContext,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n applySidebarPreference,\n loadFirstRoleSidebarPreference,\n loadSidebarPreference,\n} from '@open-mercato/core/modules/auth/services/sidebarPreferencesService'\nimport type { SidebarPreferencesSettings } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\ntype TranslationFn = (key: string | undefined, fallback: string) => string\n\ntype RouteModule = {\n id: string\n backendRoutes?: BackendRouteManifestEntry[]\n}\n\nexport function groupBackendRoutesByModule(routes: BackendRouteManifestEntry[]): RouteModule[] {\n return Array.from(\n routes.reduce((grouped, route) => {\n const list = grouped.get(route.moduleId) ?? []\n list.push(route)\n grouped.set(route.moduleId, list)\n return grouped\n }, new Map<string, BackendRouteManifestEntry[]>()),\n ).map(([id, backendRoutes]) => ({ id, backendRoutes }))\n}\n\ntype SerializableSectionItem = {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}\n\ntype SerializableSectionGroup = {\n id: string\n label: string\n labelKey?: string\n order?: number\n items: SerializableSectionItem[]\n}\n\ntype ResolvedNavItem = Omit<BackendChromeNavItem, 'defaultTitle' | 'children'> & {\n defaultTitle: string\n children?: ResolvedNavItem[]\n}\n\ntype ResolveBackendChromePayloadArgs = {\n auth: Exclude<AuthContext, null>\n locale: string\n modules: RouteModule[]\n translate: TranslationFn\n request?: Request\n selectedOrganizationId?: string | null\n selectedTenantId?: string | null\n}\n\nconst settingsSectionOrder: Record<string, number> = {\n system: 1,\n auth: 2,\n 'customer-portal': 3,\n 'data-designer': 4,\n 'module-configs': 5,\n currencies: 6,\n directory: 7,\n 'feature-toggles': 8,\n}\n\ntype NavGroupWithWeight = Omit<BackendChromeNavGroup, 'id' | 'defaultName' | 'items'> & {\n id: string\n defaultName: string\n items: ResolvedNavItem[]\n weight: number\n}\n\nlet renderToStaticMarkupPromise: Promise<typeof import('react-dom/server')> | null = null\n\nasync function serializeIconMarkup(icon: React.ReactNode | undefined): Promise<string | undefined> {\n if (!icon) return undefined\n if (!renderToStaticMarkupPromise) {\n renderToStaticMarkupPromise = import('react-dom/server')\n }\n const { renderToStaticMarkup } = await renderToStaticMarkupPromise\n\n const normalizedIcon = typeof icon === 'string'\n ? resolveRegisteredLucideIconNode(icon, 'size-4')\n : icon\n\n if (!normalizedIcon) return undefined\n\n try {\n const markup = renderToStaticMarkup(<>{normalizedIcon}</>)\n return markup.trim().length > 0 ? markup : undefined\n } catch {\n // Some icon values may be client-only component references after dependency upgrades.\n // Avoid taking down the entire nav payload because one icon cannot be rendered server-side.\n return undefined\n }\n}\n\nasync function serializeNavItem(item: AdminNavItem): Promise<ResolvedNavItem> {\n return {\n id: item.href,\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n pageContext: item.pageContext,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeNavItem(child))) : undefined,\n }\n}\n\nfunction normalizeGroupWeights(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'attachments.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n group.weight =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n })\n return groups\n}\n\nasync function groupEntries(entries: AdminNavItem[]): Promise<NavGroupWithWeight[]> {\n const groupMap = new Map<string, NavGroupWithWeight>()\n for (const entry of entries) {\n const weight = entry.priority ?? entry.order ?? 10_000\n const serializedItem = await serializeNavItem(entry)\n const existing = groupMap.get(entry.groupId)\n if (existing) {\n existing.items.push(serializedItem)\n if (weight < existing.weight) existing.weight = weight\n continue\n }\n groupMap.set(entry.groupId, {\n id: entry.groupId,\n name: entry.group,\n defaultName: entry.groupDefaultName,\n items: [serializedItem],\n weight,\n })\n }\n return normalizeGroupWeights(Array.from(groupMap.values()))\n}\n\nfunction adoptSidebarDefaults(groups: NavGroupWithWeight[]): NavGroupWithWeight[] {\n const adoptItems = (items: ResolvedNavItem[]): ResolvedNavItem[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nasync function serializeSectionItem(item: {\n id: string\n label: string\n labelKey?: string\n href: string\n icon?: React.ReactNode\n order?: number\n children?: SerializableSectionItem[]\n}): Promise<BackendChromeSectionItem> {\n return {\n id: item.id,\n label: item.label,\n labelKey: item.labelKey,\n href: item.href,\n order: item.order,\n iconName: typeof item.icon === 'string' ? item.icon : undefined,\n iconMarkup: await serializeIconMarkup(item.icon),\n children: item.children ? await Promise.all(item.children.map((child) => serializeSectionItem(child))) : undefined,\n }\n}\n\nasync function serializeSectionGroups(groups: SerializableSectionGroup[]): Promise<BackendChromeSectionGroup[]> {\n return Promise.all(groups.map(async (group) => ({\n id: group.id,\n label: group.label,\n labelKey: group.labelKey,\n order: group.order,\n items: await Promise.all(group.items.map((item) => serializeSectionItem(item))),\n })))\n}\n\nasync function loadScopedContainer(): Promise<AwilixContainer> {\n return createRequestContainer()\n}\n\nexport async function resolveBackendChromePayload({\n auth,\n locale,\n modules,\n translate,\n request,\n selectedOrganizationId,\n selectedTenantId,\n}: ResolveBackendChromePayloadArgs): Promise<BackendChromePayload> {\n const container = await loadScopedContainer()\n const em = container.resolve('em') as EntityManager\n const rbac = container.resolve('rbacService') as {\n loadAcl: (userId: string, scope: { tenantId: string | null; organizationId: string | null }) => Promise<{\n isSuperAdmin: boolean\n features: string[]\n }>\n userHasAllFeatures: (userId: string, required: string[], scope: { tenantId: string | null; organizationId: string | null }) => Promise<boolean>\n }\n\n let scopedOrganizationId: string | null = auth.orgId ?? null\n let scopedTenantId: string | null = auth.tenantId ?? null\n let allowNavigation = true\n\n try {\n const { organizationId, scope, allowedOrganizationIds } = await resolveFeatureCheckContext({\n container,\n auth,\n request,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n })\n scopedOrganizationId = organizationId\n scopedTenantId = scope.tenantId ?? auth.tenantId ?? null\n if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {\n allowNavigation = false\n }\n } catch {\n scopedOrganizationId = auth.orgId ?? null\n scopedTenantId = auth.tenantId ?? null\n }\n\n const acl = allowNavigation\n ? await rbac.loadAcl(auth.sub, {\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n })\n : { isSuperAdmin: false, features: [] }\n\n const rawGrantedFeatures = acl.isSuperAdmin ? ['*'] : acl.features\n const grantedFeatures = filterGrantsByEnabledModules(rawGrantedFeatures)\n const featureChecker = async (features: string[]): Promise<string[]> => {\n if (!allowNavigation || !features.length) return []\n const context = {\n tenantId: scopedTenantId ?? auth.tenantId ?? null,\n organizationId: scopedOrganizationId ?? null,\n }\n const hasAll = await rbac.userHasAllFeatures(auth.sub, features, context)\n if (hasAll) return features\n\n const granted: string[] = []\n for (const feature of features) {\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, [feature], context)\n if (hasFeature) granted.push(feature)\n }\n return granted\n }\n\n let userEntities: Array<{ entityId: string; label: string; href: string }> = []\n if (allowNavigation) {\n try {\n const where: FilterQuery<CustomEntity> = {\n isActive: true,\n showInSidebar: true,\n }\n where.$and = [\n { $or: [{ organizationId: scopedOrganizationId ?? undefined }, { organizationId: null }] },\n { $or: [{ tenantId: scopedTenantId ?? undefined }, { tenantId: null }] },\n ]\n const entities = await em.find(CustomEntity, where, { orderBy: { label: 'asc' } })\n userEntities = entities.map((entity) => ({\n entityId: entity.entityId,\n label: entity.label,\n href: `/backend/entities/user/${encodeURIComponent(entity.entityId)}/records`,\n }))\n } catch {\n userEntities = []\n }\n }\n\n const ctxAuth = {\n roles: auth.roles || [],\n sub: auth.sub,\n tenantId: scopedTenantId,\n orgId: scopedOrganizationId,\n }\n const entries = allowNavigation\n ? await buildAdminNav(\n modules,\n { auth: ctxAuth },\n userEntities,\n translate,\n { checkFeatures: featureChecker },\n )\n : []\n\n let rolePreference: SidebarPreferencesSettings | null = null\n let userPreference: SidebarPreferencesSettings | null = null\n\n if (Array.isArray(auth.roles) && auth.roles.length > 0) {\n const roleRecords = scopedTenantId\n ? await em.find(Role, {\n name: { $in: auth.roles },\n tenantId: scopedTenantId,\n })\n : []\n const roleIds = Array.isArray(roleRecords) ? roleRecords.map((role) => role.id) : []\n if (roleIds.length > 0) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: scopedTenantId,\n locale,\n })\n }\n }\n\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n if (effectiveUserId) {\n userPreference = await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: scopedTenantId,\n organizationId: scopedOrganizationId,\n locale,\n })\n }\n\n const baseGroups = await groupEntries(entries)\n const groupsWithRole = rolePreference\n ? applySidebarPreference<NavGroupWithWeight>(baseGroups, rolePreference)\n : baseGroups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n const appliedGroups = userPreference\n ? applySidebarPreference<NavGroupWithWeight>(baseForUser, userPreference)\n : baseForUser\n\n const settingsSections = await serializeSectionGroups(\n convertToSectionNavGroups(\n buildSettingsSections(entries, settingsSectionOrder),\n translate,\n ),\n )\n\n const requestOrganizationId = request ? getSelectedOrganizationFromRequest(request) : null\n const fallbackOrganizationId = selectedOrganizationId ?? requestOrganizationId ?? auth.orgId ?? null\n const brandOrganizationId = scopedOrganizationId\n ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null)\n\n let brand: BackendChromePayload['brand'] = null\n if (brandOrganizationId && scopedTenantId) {\n try {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: brandOrganizationId, tenant: scopedTenantId, deletedAt: null },\n undefined,\n { tenantId: scopedTenantId, organizationId: brandOrganizationId },\n )\n if (organization?.logoUrl) {\n brand = {\n name: organization.name,\n logo: {\n src: organization.logoUrl,\n alt: `${organization.name} logo`,\n },\n }\n }\n } catch {\n brand = null\n }\n }\n\n return {\n groups: appliedGroups.map(({ weight: _weight, ...group }) => group),\n settingsSections,\n settingsPathPrefixes: computeSettingsPathPrefixes(buildSettingsSections(entries, settingsSectionOrder)),\n profileSections: await serializeSectionGroups(profileSections),\n profilePathPrefixes,\n grantedFeatures,\n roles: Array.isArray(auth.roles) ? auth.roles : [],\n brand,\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -23,7 +23,7 @@ function CalendarTabs({ tab, counts, view, onTabChange, onViewChange }) {
23
23
  onValueChange: (value) => onTabChange(value),
24
24
  variant: "underline",
25
25
  children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-stretch justify-between gap-x-3 gap-y-2", children: [
26
- /* @__PURE__ */ jsx("div", { className: "min-w-0 max-w-full overflow-x-auto", children: /* @__PURE__ */ jsxs(TabsList, { className: "shrink-0", children: [
26
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 max-w-full overflow-x-auto scrollbar-hide [mask-image:linear-gradient(to_right,black_calc(100%-1.5rem),transparent)] sm:[mask-image:none]", children: /* @__PURE__ */ jsxs(TabsList, { className: "shrink-0", children: [
27
27
  /* @__PURE__ */ jsx(TabsTrigger, { value: "all", leading: /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" }), children: t("customers.calendar.tabs.all", "All Scheduled") }),
28
28
  /* @__PURE__ */ jsxs(TabsTrigger, { value: "meetings", leading: /* @__PURE__ */ jsx(List, { className: "size-4" }), children: [
29
29
  t("customers.calendar.tabs.meetings", "Meetings"),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/calendar/CalendarTabs.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Clock, LayoutGrid, List } from 'lucide-react'\nimport {\n SegmentedControl,\n SegmentedControlItem,\n} from '@open-mercato/ui/primitives/segmented-control'\nimport { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { CalendarTab, CalendarTabsProps, CalendarView } from './types'\n\nfunction TabCount({ value }: { value: number }) {\n return <span className=\"text-xs font-medium text-muted-foreground\">({value})</span>\n}\n\nexport function CalendarTabs({ tab, counts, view, onTabChange, onViewChange }: CalendarTabsProps) {\n const t = useT()\n\n return (\n <Tabs\n value={tab}\n onValueChange={(value) => onTabChange(value as CalendarTab)}\n variant=\"underline\"\n >\n <div className=\"flex flex-wrap items-stretch justify-between gap-x-3 gap-y-2\">\n <div className=\"min-w-0 max-w-full overflow-x-auto\">\n <TabsList className=\"shrink-0\">\n <TabsTrigger value=\"all\" leading={<LayoutGrid className=\"size-4\" />}>\n {t('customers.calendar.tabs.all', 'All Scheduled')}\n </TabsTrigger>\n <TabsTrigger value=\"meetings\" leading={<List className=\"size-4\" />}>\n {t('customers.calendar.tabs.meetings', 'Meetings')} <TabCount value={counts.meetings} />\n </TabsTrigger>\n <TabsTrigger value=\"events\" leading={<Clock className=\"size-4\" />}>\n {t('customers.calendar.tabs.events', 'Events')} <TabCount value={counts.events} />\n </TabsTrigger>\n </TabsList>\n </div>\n <div className=\"flex min-w-0 flex-1 items-center justify-end border-b border-input\">\n <SegmentedControl\n value={view}\n onValueChange={(value) => onViewChange(value as CalendarView)}\n aria-label={t('customers.calendar.views.label', 'Calendar view')}\n >\n <SegmentedControlItem value=\"day\">\n {t('customers.calendar.views.day', 'Day')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"week\">\n {t('customers.calendar.views.week', 'Week')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"month\">\n {t('customers.calendar.views.month', 'Month')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"agenda\">\n {t('customers.calendar.views.agenda', 'Agenda')}\n </SegmentedControlItem>\n </SegmentedControl>\n </div>\n </div>\n </Tabs>\n )\n}\n"],
5
- "mappings": ";AAaS,SAeqC,KAfrC;AAVT,SAAS,OAAO,YAAY,YAAY;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,MAAM,UAAU,mBAAmB;AAC5C,SAAS,YAAY;AAGrB,SAAS,SAAS,EAAE,MAAM,GAAsB;AAC9C,SAAO,qBAAC,UAAK,WAAU,6CAA4C;AAAA;AAAA,IAAE;AAAA,IAAM;AAAA,KAAC;AAC9E;AAEO,SAAS,aAAa,EAAE,KAAK,QAAQ,MAAM,aAAa,aAAa,GAAsB;AAChG,QAAM,IAAI,KAAK;AAEf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP,eAAe,CAAC,UAAU,YAAY,KAAoB;AAAA,MAC1D,SAAQ;AAAA,MAER,+BAAC,SAAI,WAAU,gEACb;AAAA,4BAAC,SAAI,WAAU,sCACb,+BAAC,YAAS,WAAU,YAClB;AAAA,8BAAC,eAAY,OAAM,OAAM,SAAS,oBAAC,cAAW,WAAU,UAAS,GAC9D,YAAE,+BAA+B,eAAe,GACnD;AAAA,UACA,qBAAC,eAAY,OAAM,YAAW,SAAS,oBAAC,QAAK,WAAU,UAAS,GAC7D;AAAA,cAAE,oCAAoC,UAAU;AAAA,YAAE;AAAA,YAAC,oBAAC,YAAS,OAAO,OAAO,UAAU;AAAA,aACxF;AAAA,UACA,qBAAC,eAAY,OAAM,UAAS,SAAS,oBAAC,SAAM,WAAU,UAAS,GAC5D;AAAA,cAAE,kCAAkC,QAAQ;AAAA,YAAE;AAAA,YAAC,oBAAC,YAAS,OAAO,OAAO,QAAQ;AAAA,aAClF;AAAA,WACF,GACF;AAAA,QACA,oBAAC,SAAI,WAAU,sEACb;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,eAAe,CAAC,UAAU,aAAa,KAAqB;AAAA,YAC5D,cAAY,EAAE,kCAAkC,eAAe;AAAA,YAE/D;AAAA,kCAAC,wBAAqB,OAAM,OACzB,YAAE,gCAAgC,KAAK,GAC1C;AAAA,cACA,oBAAC,wBAAqB,OAAM,QACzB,YAAE,iCAAiC,MAAM,GAC5C;AAAA,cACA,oBAAC,wBAAqB,OAAM,SACzB,YAAE,kCAAkC,OAAO,GAC9C;AAAA,cACA,oBAAC,wBAAqB,OAAM,UACzB,YAAE,mCAAmC,QAAQ,GAChD;AAAA;AAAA;AAAA,QACF,GACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Clock, LayoutGrid, List } from 'lucide-react'\nimport {\n SegmentedControl,\n SegmentedControlItem,\n} from '@open-mercato/ui/primitives/segmented-control'\nimport { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { CalendarTab, CalendarTabsProps, CalendarView } from './types'\n\nfunction TabCount({ value }: { value: number }) {\n return <span className=\"text-xs font-medium text-muted-foreground\">({value})</span>\n}\n\nexport function CalendarTabs({ tab, counts, view, onTabChange, onViewChange }: CalendarTabsProps) {\n const t = useT()\n\n return (\n <Tabs\n value={tab}\n onValueChange={(value) => onTabChange(value as CalendarTab)}\n variant=\"underline\"\n >\n <div className=\"flex flex-wrap items-stretch justify-between gap-x-3 gap-y-2\">\n <div className=\"min-w-0 max-w-full overflow-x-auto scrollbar-hide [mask-image:linear-gradient(to_right,black_calc(100%-1.5rem),transparent)] sm:[mask-image:none]\">\n <TabsList className=\"shrink-0\">\n <TabsTrigger value=\"all\" leading={<LayoutGrid className=\"size-4\" />}>\n {t('customers.calendar.tabs.all', 'All Scheduled')}\n </TabsTrigger>\n <TabsTrigger value=\"meetings\" leading={<List className=\"size-4\" />}>\n {t('customers.calendar.tabs.meetings', 'Meetings')} <TabCount value={counts.meetings} />\n </TabsTrigger>\n <TabsTrigger value=\"events\" leading={<Clock className=\"size-4\" />}>\n {t('customers.calendar.tabs.events', 'Events')} <TabCount value={counts.events} />\n </TabsTrigger>\n </TabsList>\n </div>\n <div className=\"flex min-w-0 flex-1 items-center justify-end border-b border-input\">\n <SegmentedControl\n value={view}\n onValueChange={(value) => onViewChange(value as CalendarView)}\n aria-label={t('customers.calendar.views.label', 'Calendar view')}\n >\n <SegmentedControlItem value=\"day\">\n {t('customers.calendar.views.day', 'Day')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"week\">\n {t('customers.calendar.views.week', 'Week')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"month\">\n {t('customers.calendar.views.month', 'Month')}\n </SegmentedControlItem>\n <SegmentedControlItem value=\"agenda\">\n {t('customers.calendar.views.agenda', 'Agenda')}\n </SegmentedControlItem>\n </SegmentedControl>\n </div>\n </div>\n </Tabs>\n )\n}\n"],
5
+ "mappings": ";AAaS,SAeqC,KAfrC;AAVT,SAAS,OAAO,YAAY,YAAY;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,MAAM,UAAU,mBAAmB;AAC5C,SAAS,YAAY;AAGrB,SAAS,SAAS,EAAE,MAAM,GAAsB;AAC9C,SAAO,qBAAC,UAAK,WAAU,6CAA4C;AAAA;AAAA,IAAE;AAAA,IAAM;AAAA,KAAC;AAC9E;AAEO,SAAS,aAAa,EAAE,KAAK,QAAQ,MAAM,aAAa,aAAa,GAAsB;AAChG,QAAM,IAAI,KAAK;AAEf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP,eAAe,CAAC,UAAU,YAAY,KAAoB;AAAA,MAC1D,SAAQ;AAAA,MAER,+BAAC,SAAI,WAAU,gEACb;AAAA,4BAAC,SAAI,WAAU,qJACb,+BAAC,YAAS,WAAU,YAClB;AAAA,8BAAC,eAAY,OAAM,OAAM,SAAS,oBAAC,cAAW,WAAU,UAAS,GAC9D,YAAE,+BAA+B,eAAe,GACnD;AAAA,UACA,qBAAC,eAAY,OAAM,YAAW,SAAS,oBAAC,QAAK,WAAU,UAAS,GAC7D;AAAA,cAAE,oCAAoC,UAAU;AAAA,YAAE;AAAA,YAAC,oBAAC,YAAS,OAAO,OAAO,UAAU;AAAA,aACxF;AAAA,UACA,qBAAC,eAAY,OAAM,UAAS,SAAS,oBAAC,SAAM,WAAU,UAAS,GAC5D;AAAA,cAAE,kCAAkC,QAAQ;AAAA,YAAE;AAAA,YAAC,oBAAC,YAAS,OAAO,OAAO,QAAQ;AAAA,aAClF;AAAA,WACF,GACF;AAAA,QACA,oBAAC,SAAI,WAAU,sEACb;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,eAAe,CAAC,UAAU,aAAa,KAAqB;AAAA,YAC5D,cAAY,EAAE,kCAAkC,eAAe;AAAA,YAE/D;AAAA,kCAAC,wBAAqB,OAAM,OACzB,YAAE,gCAAgC,KAAK,GAC1C;AAAA,cACA,oBAAC,wBAAqB,OAAM,QACzB,YAAE,iCAAiC,MAAM,GAC5C;AAAA,cACA,oBAAC,wBAAqB,OAAM,SACzB,YAAE,kCAAkC,OAAO,GAC9C;AAAA,cACA,oBAAC,wBAAqB,OAAM,UACzB,YAAE,mCAAmC,QAAQ,GAChD;AAAA;AAAA;AAAA,QACF,GACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -34,6 +34,7 @@ import { mapCrudServerErrorToFormErrors } from "@open-mercato/ui/backend/utils/s
34
34
  import { useT } from "@open-mercato/shared/lib/i18n/context";
35
35
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
36
36
  import { cn } from "@open-mercato/shared/lib/utils";
37
+ import { ContactEmailDisplay } from "@open-mercato/core/modules/sales/components/ContactEmailDisplay";
37
38
  import { DocumentCustomerCard } from "@open-mercato/core/modules/sales/components/DocumentCustomerCard";
38
39
  import { SalesDocumentAddressesSection } from "@open-mercato/core/modules/sales/components/documents/AddressesSection";
39
40
  import { SalesDocumentItemsSection } from "@open-mercato/core/modules/sales/components/documents/ItemsSection";
@@ -3252,23 +3253,7 @@ function SalesDocumentDetailPage({
3252
3253
  }
3253
3254
  ];
3254
3255
  const renderEmailDisplay = React.useCallback(
3255
- ({ value, emptyLabel }) => {
3256
- const emailValue = typeof value === "string" ? value.trim() : "";
3257
- if (!emailValue.length) {
3258
- return /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: emptyLabel });
3259
- }
3260
- return /* @__PURE__ */ jsxs(
3261
- "a",
3262
- {
3263
- className: "inline-flex items-center gap-2 text-sm text-primary hover:text-primary/80 hover:underline",
3264
- href: `mailto:${emailValue}`,
3265
- children: [
3266
- /* @__PURE__ */ jsx(Mail, { className: "h-4 w-4", "aria-hidden": true }),
3267
- /* @__PURE__ */ jsx("span", { className: "truncate", children: emailValue })
3268
- ]
3269
- }
3270
- );
3271
- },
3256
+ ({ value, emptyLabel }) => /* @__PURE__ */ jsx(ContactEmailDisplay, { value, emptyLabel }),
3272
3257
  []
3273
3258
  );
3274
3259
  const tabInjectionSpotId = React.useMemo(() => `sales.document.detail.${kind}:tabs`, [kind]);