@open-mercato/core 0.10.1-develop.7761.1.9dfaf470ed → 0.10.1-develop.7763.1.caea616ebf

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 (92) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/profile/route.js +16 -2
  3. package/dist/modules/auth/api/profile/route.js.map +2 -2
  4. package/dist/modules/auth/lib/userDataSubject.js +26 -0
  5. package/dist/modules/auth/lib/userDataSubject.js.map +7 -0
  6. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +1 -0
  7. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  8. package/dist/modules/customers/api/companies/[id]/route.js +3 -2
  9. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  10. package/dist/modules/customers/api/deals/[id]/route.js +11 -12
  11. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  12. package/dist/modules/customers/api/people/[id]/route.js +3 -2
  13. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  14. package/dist/modules/customers/backend/customers/companies/create/page.js +6 -0
  15. package/dist/modules/customers/backend/customers/companies/create/page.js.map +2 -2
  16. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +25 -25
  17. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  18. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.js +8 -27
  19. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.js.map +2 -2
  20. package/dist/modules/customers/backend/customers/deals/[id]/page.js +175 -171
  21. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +25 -24
  23. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +2 -2
  24. package/dist/modules/customers/backend/customers/people/create/page.js +9 -0
  25. package/dist/modules/customers/backend/customers/people/create/page.js.map +2 -2
  26. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +140 -144
  27. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  28. package/dist/modules/customers/components/detail/CompanyDetailHeader.js +2 -0
  29. package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +2 -2
  30. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +13 -4
  31. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
  32. package/dist/modules/customers/components/detail/CreatePersonDialog.js +13 -3
  33. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
  34. package/dist/modules/customers/components/detail/CustomerDetailSidebar.js +29 -0
  35. package/dist/modules/customers/components/detail/CustomerDetailSidebar.js.map +7 -0
  36. package/dist/modules/customers/components/detail/DealDetailHeader.js +4 -2
  37. package/dist/modules/customers/components/detail/DealDetailHeader.js.map +2 -2
  38. package/dist/modules/customers/components/detail/DealDetailTabs.js +9 -1
  39. package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
  40. package/dist/modules/customers/components/detail/DealForm.js +5 -0
  41. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  42. package/dist/modules/customers/components/detail/PersonDetailHeader.js +2 -0
  43. package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +2 -2
  44. package/dist/modules/customers/components/detail/PersonDetailTabs.js +9 -1
  45. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  46. package/dist/modules/customers/components/detail/create/CreateDealForm.js +160 -176
  47. package/dist/modules/customers/components/detail/create/CreateDealForm.js.map +2 -2
  48. package/dist/modules/customers/components/detail/useCustomerInjectedTabs.js +47 -0
  49. package/dist/modules/customers/components/detail/useCustomerInjectedTabs.js.map +7 -0
  50. package/dist/modules/customers/components/formConfig.js +3 -0
  51. package/dist/modules/customers/components/formConfig.js.map +2 -2
  52. package/dist/modules/customers/extension-points.js +14 -4
  53. package/dist/modules/customers/extension-points.js.map +2 -2
  54. package/dist/modules/customers/lib/detailResponseEnrichment.js +38 -0
  55. package/dist/modules/customers/lib/detailResponseEnrichment.js.map +7 -0
  56. package/dist/modules/dashboards/api/layout/[itemId]/route.js +11 -4
  57. package/dist/modules/dashboards/api/layout/[itemId]/route.js.map +2 -2
  58. package/dist/modules/dashboards/api/layout/route.js +25 -13
  59. package/dist/modules/dashboards/api/layout/route.js.map +2 -2
  60. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +4 -4
  61. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
  62. package/package.json +7 -7
  63. package/src/modules/auth/api/profile/route.ts +15 -1
  64. package/src/modules/auth/lib/userDataSubject.ts +30 -0
  65. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -0
  66. package/src/modules/customers/api/companies/[id]/route.ts +3 -2
  67. package/src/modules/customers/api/deals/[id]/route.ts +12 -12
  68. package/src/modules/customers/api/people/[id]/route.ts +3 -2
  69. package/src/modules/customers/backend/customers/companies/create/page.tsx +6 -0
  70. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -30
  71. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.tsx +9 -44
  72. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -3
  73. package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +25 -27
  74. package/src/modules/customers/backend/customers/people/create/page.tsx +9 -0
  75. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +21 -33
  76. package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +3 -0
  77. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +15 -4
  78. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +15 -4
  79. package/src/modules/customers/components/detail/CustomerDetailSidebar.tsx +41 -0
  80. package/src/modules/customers/components/detail/DealDetailHeader.tsx +4 -1
  81. package/src/modules/customers/components/detail/DealDetailTabs.tsx +11 -1
  82. package/src/modules/customers/components/detail/DealForm.tsx +5 -0
  83. package/src/modules/customers/components/detail/PersonDetailHeader.tsx +3 -0
  84. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +11 -1
  85. package/src/modules/customers/components/detail/create/CreateDealForm.tsx +126 -209
  86. package/src/modules/customers/components/detail/useCustomerInjectedTabs.tsx +64 -0
  87. package/src/modules/customers/components/formConfig.tsx +3 -0
  88. package/src/modules/customers/extension-points.ts +14 -4
  89. package/src/modules/customers/lib/detailResponseEnrichment.ts +53 -0
  90. package/src/modules/dashboards/api/layout/[itemId]/route.ts +12 -4
  91. package/src/modules/dashboards/api/layout/route.ts +25 -12
  92. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +4 -4
@@ -34,6 +34,7 @@ function CompanyDetailHeader({
34
34
  onDelete,
35
35
  isDirty,
36
36
  isSaving,
37
+ actions,
37
38
  onFocusField,
38
39
  onDataReload
39
40
  }) {
@@ -143,6 +144,7 @@ function CompanyDetailHeader({
143
144
  ] })
144
145
  ] }),
145
146
  /* @__PURE__ */ jsx("div", { className: "flex w-full shrink-0 flex-col items-start gap-3 sm:w-auto sm:items-end", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-wrap items-center justify-start gap-2 sm:w-auto sm:justify-end", children: [
147
+ actions,
146
148
  /* @__PURE__ */ jsx(
147
149
  SendObjectMessageDialog,
148
150
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/CompanyDetailHeader.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Phone, Mail, Trash2, Building2, Globe, Pencil, MapPin } from 'lucide-react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { CompanyTagsDialog } from './CompanyTagsDialog'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { invalidateCustomerDictionary, useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { renderDictionaryIcon } from '../../../dictionaries/components/dictionaryAppearance'\nimport type { TagSummary } from './types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport type { CompanyOverview } from '../formConfig'\nimport type { CustomerDictionaryMap } from '@open-mercato/core/modules/customers/lib/dictionaries'\nimport { formatFallbackLabel } from './utils'\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\ntype CompanyDetailHeaderProps = {\n data: CompanyOverview\n onTagsChange: (tags: TagSummary[]) => void\n tagsSectionControllerRef: React.RefObject<TagsSectionController | null>\n onSave: () => void\n onDelete: () => Promise<void>\n isDirty: boolean\n isSaving: boolean\n onFocusField?: (fieldName: string) => void\n onDataReload?: () => void\n}\n\nfunction CompanyDictionaryBadge({ value, map }: { value: string; map: CustomerDictionaryMap | undefined }) {\n const entry = map?.[value]\n const color = entry?.color ?? null\n const icon = entry?.icon ?? null\n const label = entry?.label ?? formatFallbackLabel(value)\n const colorStyle: React.CSSProperties | undefined = color\n ? { color, borderColor: color, backgroundColor: `${color}1A` }\n : undefined\n return (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\" style={colorStyle}>\n {icon ? renderDictionaryIcon(icon, 'size-2.5') : null}\n {label}\n </Badge>\n )\n}\n\nexport function CompanyDetailHeader({\n data,\n onTagsChange,\n tagsSectionControllerRef,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n onFocusField,\n onDataReload,\n}: CompanyDetailHeaderProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const [manageTagsOpen, setManageTagsOpen] = React.useState(false)\n const company = data.company\n const profile = data.profile\n const displayName = company.displayName || t('customers.companies.detail.untitled', 'Untitled')\n const visibleCustomTags = React.useMemo(\n () => (data.tags?.filter((tag) => !['status', 'lifecycle_stage', 'source'].includes(tag.id)).slice(0, 6) ?? []),\n [data.tags],\n )\n const hiddenCustomTagsCount = Math.max(\n 0,\n (data.tags?.filter((tag) => !['status', 'lifecycle_stage', 'source'].includes(tag.id)).length ?? 0) - visibleCustomTags.length,\n )\n\n const industryLabel = profile?.industry ?? null\n const sizeLabel = profile?.sizeBucket ?? null\n const subtitle = [industryLabel, sizeLabel ? t('customers.companies.detail.header.employees', '{count} employees', { count: sizeLabel }) : null].filter(Boolean).join(' \\u00b7 ')\n\n // Primary address for header\n const primaryAddress = React.useMemo(() => {\n const addresses = (data as Record<string, unknown>).addresses as Array<{ isPrimary?: boolean; city?: string; region?: string; postalCode?: string }> | undefined\n if (!addresses || !Array.isArray(addresses)) return null\n return addresses.find((a) => a.isPrimary) ?? addresses[0] ?? null\n }, [data])\n\n const locationText = React.useMemo(() => {\n if (!primaryAddress) return null\n return [primaryAddress.city, primaryAddress.region, primaryAddress.postalCode].filter(Boolean).join(', ')\n }, [primaryAddress])\n\n // Fetch dictionary maps for colored badge rendering\n const companyOrgId = company.organizationId ?? null\n const { data: statusDict } = useCustomerDictionary('statuses', 0, companyOrgId)\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages', 0, companyOrgId)\n const { data: sourceDict } = useCustomerDictionary('sources', 0, companyOrgId)\n const { data: temperatureDict } = useCustomerDictionary('temperature', 0, companyOrgId)\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters', 0, companyOrgId)\n\n return (\n <div className=\"rounded-lg border bg-card\">\n {/* Top row: avatar + company info + account manager + actions */}\n <div className=\"flex flex-col gap-4 px-6 pt-6 pb-3 sm:flex-row sm:items-start sm:gap-5\">\n {/* Avatar */}\n <Avatar\n label=\"\"\n icon={<Building2 />}\n size=\"xl\"\n variant=\"monochrome\"\n />\n\n {/* Company info */}\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"truncate text-2xl font-bold text-foreground\">{displayName}</h1>\n {subtitle && (\n <p className=\"mt-0.5 text-sm text-muted-foreground\">{subtitle}</p>\n )}\n\n {/* Contact row */}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-5 gap-y-1 text-sm text-muted-foreground\">\n {company.primaryPhone && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Phone className=\"size-3.5\" />\n <a href={`tel:${company.primaryPhone}`} className=\"hover:text-foreground\">{company.primaryPhone}</a>\n </span>\n )}\n {company.primaryEmail && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Mail className=\"size-3.5\" />\n <a href={`mailto:${company.primaryEmail}`} className=\"hover:text-foreground\">{company.primaryEmail}</a>\n </span>\n )}\n {profile?.websiteUrl && (\n <a href={profile.websiteUrl.startsWith('http') ? profile.websiteUrl : `https://${profile.websiteUrl}`} target=\"_blank\" rel=\"noreferrer\" className=\"inline-flex items-center gap-1.5 hover:text-foreground\">\n <Globe className=\"size-3.5\" />\n {profile.websiteUrl}\n </a>\n )}\n {locationText && (\n <span className=\"inline-flex items-center gap-1.5\">\n <MapPin className=\"size-3.5\" />\n {locationText}\n </span>\n )}\n </div>\n\n {/* Status badges + temperature + renewal quarter + inline tags */}\n <div className=\"mt-2.5 flex flex-wrap items-center gap-1.5\">\n {company.status && (\n <CompanyDictionaryBadge value={company.status} map={statusDict?.map} />\n )}\n {company.lifecycleStage && (\n <CompanyDictionaryBadge value={company.lifecycleStage} map={lifecycleDict?.map} />\n )}\n {company.source && (\n <CompanyDictionaryBadge value={company.source} map={sourceDict?.map} />\n )}\n {company.temperature && (\n <CompanyDictionaryBadge value={company.temperature} map={temperatureDict?.map} />\n )}\n {company.renewalQuarter && (\n <CompanyDictionaryBadge value={company.renewalQuarter} map={renewalQuarterDict?.map} />\n )}\n {visibleCustomTags.map((tag) => {\n const colorStyle: React.CSSProperties | undefined = tag.color\n ? { color: tag.color, borderColor: tag.color, backgroundColor: `${tag.color}1A` }\n : undefined\n return (\n <Badge\n key={tag.id}\n variant=\"outline\"\n className=\"rounded-sm gap-1.5 text-xs font-medium\"\n style={colorStyle}\n >\n {tag.label}\n </Badge>\n )\n })}\n {hiddenCustomTagsCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\">\n +{hiddenCustomTagsCount} {t('customers.companies.detail.header.more', 'more')}\n </Badge>\n ) : null}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto rounded-sm px-2 py-1 text-xs font-medium text-muted-foreground hover:text-foreground\"\n onClick={() => setManageTagsOpen(true)}\n >\n <Pencil className=\"mr-1 size-3\" />\n {t('customers.companies.detail.actions.manageTags', 'Edit tags')}\n </Button>\n </div>\n </div>\n\n {/* Right side: actions */}\n <div className=\"flex w-full shrink-0 flex-col items-start gap-3 sm:w-auto sm:items-end\">\n <div className=\"flex w-full flex-wrap items-center justify-start gap-2 sm:w-auto sm:justify-end\">\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'company',\n entityId: company.id,\n previewData: {\n title: displayName,\n subtitle: company.primaryEmail ?? profile?.websiteUrl ?? undefined,\n },\n }}\n viewHref={`/backend/customers/companies-v2/${company.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.companies.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.company\"\n resourceId={company.id}\n organizationId={company.organizationId ?? undefined}\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.companies.detail.actions.delete', 'Delete company')}\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n >\n {t('customers.companies.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n </div>\n\n <CompanyTagsDialog\n open={manageTagsOpen}\n onClose={() => setManageTagsOpen(false)}\n entityId={company.id}\n companyOrganizationId={company.organizationId ?? null}\n companyData={{\n status: company.status,\n lifecycleStage: company.lifecycleStage,\n source: company.source,\n temperature: company.temperature,\n renewalQuarter: company.renewalQuarter,\n industry: profile?.industry ?? null,\n customFields: data.customFields,\n tags: data.tags,\n }}\n onSaved={() => {\n void invalidateCustomerDictionary(queryClient, 'statuses')\n void invalidateCustomerDictionary(queryClient, 'lifecycle-stages')\n void invalidateCustomerDictionary(queryClient, 'sources')\n void invalidateCustomerDictionary(queryClient, 'temperature')\n void invalidateCustomerDictionary(queryClient, 'renewal-quarters')\n onDataReload?.()\n }}\n />\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Phone, Mail, Trash2, Building2, Globe, Pencil, MapPin } from 'lucide-react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { CompanyTagsDialog } from './CompanyTagsDialog'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { invalidateCustomerDictionary, useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { renderDictionaryIcon } from '../../../dictionaries/components/dictionaryAppearance'\nimport type { TagSummary } from './types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport type { CompanyOverview } from '../formConfig'\nimport type { CustomerDictionaryMap } from '@open-mercato/core/modules/customers/lib/dictionaries'\nimport { formatFallbackLabel } from './utils'\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\ntype CompanyDetailHeaderProps = {\n data: CompanyOverview\n onTagsChange: (tags: TagSummary[]) => void\n tagsSectionControllerRef: React.RefObject<TagsSectionController | null>\n onSave: () => void\n onDelete: () => Promise<void>\n isDirty: boolean\n isSaving: boolean\n actions?: React.ReactNode\n onFocusField?: (fieldName: string) => void\n onDataReload?: () => void\n}\n\nfunction CompanyDictionaryBadge({ value, map }: { value: string; map: CustomerDictionaryMap | undefined }) {\n const entry = map?.[value]\n const color = entry?.color ?? null\n const icon = entry?.icon ?? null\n const label = entry?.label ?? formatFallbackLabel(value)\n const colorStyle: React.CSSProperties | undefined = color\n ? { color, borderColor: color, backgroundColor: `${color}1A` }\n : undefined\n return (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\" style={colorStyle}>\n {icon ? renderDictionaryIcon(icon, 'size-2.5') : null}\n {label}\n </Badge>\n )\n}\n\nexport function CompanyDetailHeader({\n data,\n onTagsChange,\n tagsSectionControllerRef,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n actions,\n onFocusField,\n onDataReload,\n}: CompanyDetailHeaderProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const [manageTagsOpen, setManageTagsOpen] = React.useState(false)\n const company = data.company\n const profile = data.profile\n const displayName = company.displayName || t('customers.companies.detail.untitled', 'Untitled')\n const visibleCustomTags = React.useMemo(\n () => (data.tags?.filter((tag) => !['status', 'lifecycle_stage', 'source'].includes(tag.id)).slice(0, 6) ?? []),\n [data.tags],\n )\n const hiddenCustomTagsCount = Math.max(\n 0,\n (data.tags?.filter((tag) => !['status', 'lifecycle_stage', 'source'].includes(tag.id)).length ?? 0) - visibleCustomTags.length,\n )\n\n const industryLabel = profile?.industry ?? null\n const sizeLabel = profile?.sizeBucket ?? null\n const subtitle = [industryLabel, sizeLabel ? t('customers.companies.detail.header.employees', '{count} employees', { count: sizeLabel }) : null].filter(Boolean).join(' \\u00b7 ')\n\n // Primary address for header\n const primaryAddress = React.useMemo(() => {\n const addresses = (data as Record<string, unknown>).addresses as Array<{ isPrimary?: boolean; city?: string; region?: string; postalCode?: string }> | undefined\n if (!addresses || !Array.isArray(addresses)) return null\n return addresses.find((a) => a.isPrimary) ?? addresses[0] ?? null\n }, [data])\n\n const locationText = React.useMemo(() => {\n if (!primaryAddress) return null\n return [primaryAddress.city, primaryAddress.region, primaryAddress.postalCode].filter(Boolean).join(', ')\n }, [primaryAddress])\n\n // Fetch dictionary maps for colored badge rendering\n const companyOrgId = company.organizationId ?? null\n const { data: statusDict } = useCustomerDictionary('statuses', 0, companyOrgId)\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages', 0, companyOrgId)\n const { data: sourceDict } = useCustomerDictionary('sources', 0, companyOrgId)\n const { data: temperatureDict } = useCustomerDictionary('temperature', 0, companyOrgId)\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters', 0, companyOrgId)\n\n return (\n <div className=\"rounded-lg border bg-card\">\n {/* Top row: avatar + company info + account manager + actions */}\n <div className=\"flex flex-col gap-4 px-6 pt-6 pb-3 sm:flex-row sm:items-start sm:gap-5\">\n {/* Avatar */}\n <Avatar\n label=\"\"\n icon={<Building2 />}\n size=\"xl\"\n variant=\"monochrome\"\n />\n\n {/* Company info */}\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"truncate text-2xl font-bold text-foreground\">{displayName}</h1>\n {subtitle && (\n <p className=\"mt-0.5 text-sm text-muted-foreground\">{subtitle}</p>\n )}\n\n {/* Contact row */}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-5 gap-y-1 text-sm text-muted-foreground\">\n {company.primaryPhone && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Phone className=\"size-3.5\" />\n <a href={`tel:${company.primaryPhone}`} className=\"hover:text-foreground\">{company.primaryPhone}</a>\n </span>\n )}\n {company.primaryEmail && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Mail className=\"size-3.5\" />\n <a href={`mailto:${company.primaryEmail}`} className=\"hover:text-foreground\">{company.primaryEmail}</a>\n </span>\n )}\n {profile?.websiteUrl && (\n <a href={profile.websiteUrl.startsWith('http') ? profile.websiteUrl : `https://${profile.websiteUrl}`} target=\"_blank\" rel=\"noreferrer\" className=\"inline-flex items-center gap-1.5 hover:text-foreground\">\n <Globe className=\"size-3.5\" />\n {profile.websiteUrl}\n </a>\n )}\n {locationText && (\n <span className=\"inline-flex items-center gap-1.5\">\n <MapPin className=\"size-3.5\" />\n {locationText}\n </span>\n )}\n </div>\n\n {/* Status badges + temperature + renewal quarter + inline tags */}\n <div className=\"mt-2.5 flex flex-wrap items-center gap-1.5\">\n {company.status && (\n <CompanyDictionaryBadge value={company.status} map={statusDict?.map} />\n )}\n {company.lifecycleStage && (\n <CompanyDictionaryBadge value={company.lifecycleStage} map={lifecycleDict?.map} />\n )}\n {company.source && (\n <CompanyDictionaryBadge value={company.source} map={sourceDict?.map} />\n )}\n {company.temperature && (\n <CompanyDictionaryBadge value={company.temperature} map={temperatureDict?.map} />\n )}\n {company.renewalQuarter && (\n <CompanyDictionaryBadge value={company.renewalQuarter} map={renewalQuarterDict?.map} />\n )}\n {visibleCustomTags.map((tag) => {\n const colorStyle: React.CSSProperties | undefined = tag.color\n ? { color: tag.color, borderColor: tag.color, backgroundColor: `${tag.color}1A` }\n : undefined\n return (\n <Badge\n key={tag.id}\n variant=\"outline\"\n className=\"rounded-sm gap-1.5 text-xs font-medium\"\n style={colorStyle}\n >\n {tag.label}\n </Badge>\n )\n })}\n {hiddenCustomTagsCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\">\n +{hiddenCustomTagsCount} {t('customers.companies.detail.header.more', 'more')}\n </Badge>\n ) : null}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto rounded-sm px-2 py-1 text-xs font-medium text-muted-foreground hover:text-foreground\"\n onClick={() => setManageTagsOpen(true)}\n >\n <Pencil className=\"mr-1 size-3\" />\n {t('customers.companies.detail.actions.manageTags', 'Edit tags')}\n </Button>\n </div>\n </div>\n\n {/* Right side: actions */}\n <div className=\"flex w-full shrink-0 flex-col items-start gap-3 sm:w-auto sm:items-end\">\n <div className=\"flex w-full flex-wrap items-center justify-start gap-2 sm:w-auto sm:justify-end\">\n {actions}\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'company',\n entityId: company.id,\n previewData: {\n title: displayName,\n subtitle: company.primaryEmail ?? profile?.websiteUrl ?? undefined,\n },\n }}\n viewHref={`/backend/customers/companies-v2/${company.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.companies.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.company\"\n resourceId={company.id}\n organizationId={company.organizationId ?? undefined}\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.companies.detail.actions.delete', 'Delete company')}\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n >\n {t('customers.companies.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n </div>\n\n <CompanyTagsDialog\n open={manageTagsOpen}\n onClose={() => setManageTagsOpen(false)}\n entityId={company.id}\n companyOrganizationId={company.organizationId ?? null}\n companyData={{\n status: company.status,\n lifecycleStage: company.lifecycleStage,\n source: company.source,\n temperature: company.temperature,\n renewalQuarter: company.renewalQuarter,\n industry: profile?.industry ?? null,\n customFields: data.customFields,\n tags: data.tags,\n }}\n onSaved={() => {\n void invalidateCustomerDictionary(queryClient, 'statuses')\n void invalidateCustomerDictionary(queryClient, 'lifecycle-stages')\n void invalidateCustomerDictionary(queryClient, 'sources')\n void invalidateCustomerDictionary(queryClient, 'temperature')\n void invalidateCustomerDictionary(queryClient, 'renewal-quarters')\n onDataReload?.()\n }}\n />\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -15,6 +15,7 @@ import {
15
15
  import { registerComponent } from "@open-mercato/shared/modules/widgets/component-registry";
16
16
  import { useRegisteredComponent } from "@open-mercato/ui/backend/injection/useRegisteredComponent";
17
17
  import { useDealsAccess } from "./useDealsAccess.js";
18
+ const COMPANY_DETAIL_TAB_IDS = ["people", "deals", "activity-log", "changelog", "files"];
18
19
  const COMPANY_DETAIL_TABS_COMPONENT_ID = "section:customers.companies.detailTabs";
19
20
  const LEGACY_TAB_MAP = {
20
21
  notes: "people",
@@ -25,10 +26,11 @@ const LEGACY_TAB_MAP = {
25
26
  "dane-firmy": "people",
26
27
  analysis: "people"
27
28
  };
28
- function resolveLegacyTab(tab) {
29
+ function resolveLegacyTab(tab, knownTabIds) {
29
30
  if (!tab) return "people";
30
31
  if (LEGACY_TAB_MAP[tab]) return LEGACY_TAB_MAP[tab];
31
- return tab;
32
+ if (COMPANY_DETAIL_TAB_IDS.includes(tab) || knownTabIds && new Set(knownTabIds).has(tab)) return tab;
33
+ return "people";
32
34
  }
33
35
  function formatTabCount(count) {
34
36
  if (count <= 0) return void 0;
@@ -38,6 +40,7 @@ function DefaultCompanyDetailTabs({
38
40
  activeTab,
39
41
  onTabChange,
40
42
  injectedTabs = [],
43
+ isLoadingInjectedTabs = false,
41
44
  hiddenTabIds = [],
42
45
  peopleCount = 0,
43
46
  dealsCount = 0,
@@ -47,7 +50,7 @@ function DefaultCompanyDetailTabs({
47
50
  children
48
51
  }) {
49
52
  const t = useT();
50
- const { canViewDeals } = useDealsAccess();
53
+ const { canViewDeals, isReady: isDealsAccessReady } = useDealsAccess();
51
54
  const builtInTabs = React.useMemo(
52
55
  () => [
53
56
  {
@@ -89,12 +92,17 @@ function DefaultCompanyDetailTabs({
89
92
  const hidden = new Set(hiddenTabIds);
90
93
  return [
91
94
  ...builtInTabs,
92
- ...injectedTabs.map((tab) => ({
95
+ ...injectedTabs.filter((tab, index) => !builtInTabs.some((native) => native.id === tab.id) && injectedTabs.findIndex((candidate) => candidate.id === tab.id) === index).map((tab) => ({
93
96
  id: tab.id,
94
97
  label: tab.label
95
98
  }))
96
99
  ].filter((tab) => !hidden.has(tab.id));
97
100
  }, [builtInTabs, hiddenTabIds, injectedTabs]);
101
+ React.useEffect(() => {
102
+ if (isLoadingInjectedTabs || !isDealsAccessReady || allTabs.some((tab) => tab.id === activeTab)) return;
103
+ const fallback = allTabs[0]?.id;
104
+ if (fallback) onTabChange(fallback);
105
+ }, [activeTab, allTabs, isLoadingInjectedTabs, onTabChange, isDealsAccessReady]);
98
106
  return /* @__PURE__ */ jsxs("div", { children: [
99
107
  /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-2 border-b", children: [
100
108
  /* @__PURE__ */ jsx(
@@ -149,6 +157,7 @@ function CompanyDetailTabs(props) {
149
157
  }
150
158
  export {
151
159
  COMPANY_DETAIL_TABS_COMPONENT_ID,
160
+ COMPANY_DETAIL_TAB_IDS,
152
161
  CompanyDetailTabs,
153
162
  resolveLegacyTab
154
163
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/CompanyDetailTabs.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport {\n Users,\n Handshake,\n Clock,\n History,\n Paperclip,\n Plus,\n} from 'lucide-react'\nimport type { SectionAction } from '@open-mercato/ui/backend/detail'\nimport { registerComponent } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '@open-mercato/ui/backend/injection/useRegisteredComponent'\nimport { useDealsAccess } from './useDealsAccess'\n\nexport type CompanyTabId =\n | 'people'\n | 'deals'\n | 'activity-log'\n | 'changelog'\n | 'files'\n | string\n\ntype TabDef = {\n id: CompanyTabId\n label: string\n icon?: React.ReactNode\n count?: React.ReactNode\n}\n\nexport const COMPANY_DETAIL_TABS_COMPONENT_ID = 'section:customers.companies.detailTabs'\n\nexport type CompanyDetailTabsProps = {\n activeTab: CompanyTabId\n onTabChange: (tab: CompanyTabId) => void\n injectedTabs?: Array<{ id: string; label: string; priority?: number }>\n hiddenTabIds?: string[]\n peopleCount?: number\n dealsCount?: number\n activitiesCount?: number\n filesCount?: number\n sectionAction?: SectionAction | null\n children: React.ReactNode\n}\n\nconst LEGACY_TAB_MAP: Record<string, CompanyTabId> = {\n notes: 'people',\n activities: 'activity-log',\n addresses: 'people',\n tasks: 'people',\n dashboard: 'people',\n 'dane-firmy': 'people',\n analysis: 'people',\n}\n\nexport function resolveLegacyTab(tab: string | null | undefined): CompanyTabId {\n if (!tab) return 'people'\n if (LEGACY_TAB_MAP[tab]) return LEGACY_TAB_MAP[tab]\n return tab as CompanyTabId\n}\n\nfunction formatTabCount(count: number): string | number | undefined {\n if (count <= 0) return undefined\n return count > 999 ? '999+' : count\n}\n\nfunction DefaultCompanyDetailTabs({\n activeTab,\n onTabChange,\n injectedTabs = [],\n hiddenTabIds = [],\n peopleCount = 0,\n dealsCount = 0,\n activitiesCount = 0,\n filesCount = 0,\n sectionAction = null,\n children,\n}: CompanyDetailTabsProps) {\n const t = useT()\n const { canViewDeals } = useDealsAccess()\n\n const builtInTabs: TabDef[] = React.useMemo(\n () => [\n {\n id: 'people',\n label: t('customers.companies.detail.tabs.people', 'People'),\n icon: <Users className=\"size-4\" />,\n count: formatTabCount(peopleCount),\n },\n ...(canViewDeals\n ? [\n {\n id: 'deals' as CompanyTabId,\n label: t('customers.companies.detail.tabs.deals', 'Deals'),\n icon: <Handshake className=\"size-4\" />,\n count: formatTabCount(dealsCount),\n },\n ]\n : []),\n {\n id: 'activity-log',\n label: t('customers.companies.detail.tabs.activityLog', 'Activity log'),\n icon: <Clock className=\"size-4\" />,\n count: formatTabCount(activitiesCount),\n },\n {\n id: 'changelog',\n label: t('customers.companies.detail.tabs.changelog', 'Changelog'),\n icon: <History className=\"size-4\" />,\n count: 'NEW',\n },\n {\n id: 'files',\n label: t('customers.companies.detail.tabs.files', 'Files'),\n icon: <Paperclip className=\"size-4\" />,\n count: formatTabCount(filesCount),\n },\n ],\n [t, canViewDeals, peopleCount, dealsCount, activitiesCount, filesCount],\n )\n\n const allTabs: TabDef[] = React.useMemo(() => {\n const hidden = new Set(hiddenTabIds)\n return [\n ...builtInTabs,\n ...injectedTabs.map((tab) => ({\n id: tab.id as CompanyTabId,\n label: tab.label,\n })),\n ].filter((tab) => !hidden.has(tab.id))\n }, [builtInTabs, hiddenTabIds, injectedTabs])\n\n return (\n <div>\n {/* Tab navigation */}\n <div className=\"flex items-end justify-between gap-2 border-b\">\n <Tabs\n value={activeTab}\n onValueChange={(value) => onTabChange(value as CompanyTabId)}\n variant=\"underline\"\n className=\"min-w-0 flex-1\"\n >\n <TabsList\n aria-label={t('customers.companies.detail.tabs.label', 'Company detail sections')}\n className=\"-mb-px w-full overflow-x-auto border-b-0 px-1\"\n >\n {allTabs.map((tab) => (\n <TabsTrigger key={tab.id} value={tab.id} leading={tab.icon} count={tab.count}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={sectionAction.onClick}\n disabled={sectionAction.disabled}\n className=\"mb-1.5 mr-1 shrink-0\"\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n\n {/* Tab content */}\n <div className=\"pt-6\" role=\"tabpanel\">\n {children}\n </div>\n </div>\n )\n}\n\nregisterComponent<CompanyDetailTabsProps>({\n id: COMPANY_DETAIL_TABS_COMPONENT_ID,\n component: DefaultCompanyDetailTabs,\n metadata: {\n module: 'customers',\n description: 'Company detail tab navigation and content.',\n },\n})\n\nexport function CompanyDetailTabs(props: CompanyDetailTabsProps) {\n const ResolvedTabs = useRegisteredComponent<CompanyDetailTabsProps>(\n COMPANY_DETAIL_TABS_COMPONENT_ID,\n DefaultCompanyDetailTabs,\n )\n return <ResolvedTabs {...props} />\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport {\n Users,\n Handshake,\n Clock,\n History,\n Paperclip,\n Plus,\n} from 'lucide-react'\nimport type { SectionAction } from '@open-mercato/ui/backend/detail'\nimport { registerComponent } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '@open-mercato/ui/backend/injection/useRegisteredComponent'\nimport { useDealsAccess } from './useDealsAccess'\n\nexport const COMPANY_DETAIL_TAB_IDS = ['people', 'deals', 'activity-log', 'changelog', 'files'] as const\n\nexport type CompanyTabId =\n | 'people'\n | 'deals'\n | 'activity-log'\n | 'changelog'\n | 'files'\n | string\n\ntype TabDef = {\n id: CompanyTabId\n label: string\n icon?: React.ReactNode\n count?: React.ReactNode\n}\n\nexport const COMPANY_DETAIL_TABS_COMPONENT_ID = 'section:customers.companies.detailTabs'\n\nexport type CompanyDetailTabsProps = {\n activeTab: CompanyTabId\n onTabChange: (tab: CompanyTabId) => void\n injectedTabs?: Array<{ id: string; label: string; priority?: number }>\n isLoadingInjectedTabs?: boolean\n hiddenTabIds?: string[]\n peopleCount?: number\n dealsCount?: number\n activitiesCount?: number\n filesCount?: number\n sectionAction?: SectionAction | null\n children: React.ReactNode\n}\n\nconst LEGACY_TAB_MAP: Record<string, CompanyTabId> = {\n notes: 'people',\n activities: 'activity-log',\n addresses: 'people',\n tasks: 'people',\n dashboard: 'people',\n 'dane-firmy': 'people',\n analysis: 'people',\n}\n\nexport function resolveLegacyTab(tab: string | null | undefined, knownTabIds?: Iterable<string>): CompanyTabId {\n if (!tab) return 'people'\n if (LEGACY_TAB_MAP[tab]) return LEGACY_TAB_MAP[tab]\n if ((COMPANY_DETAIL_TAB_IDS as readonly string[]).includes(tab) || (knownTabIds && new Set(knownTabIds).has(tab))) return tab\n return 'people'\n}\n\nfunction formatTabCount(count: number): string | number | undefined {\n if (count <= 0) return undefined\n return count > 999 ? '999+' : count\n}\n\nfunction DefaultCompanyDetailTabs({\n activeTab,\n onTabChange,\n injectedTabs = [],\n isLoadingInjectedTabs = false,\n hiddenTabIds = [],\n peopleCount = 0,\n dealsCount = 0,\n activitiesCount = 0,\n filesCount = 0,\n sectionAction = null,\n children,\n}: CompanyDetailTabsProps) {\n const t = useT()\n const { canViewDeals, isReady: isDealsAccessReady } = useDealsAccess()\n\n const builtInTabs: TabDef[] = React.useMemo(\n () => [\n {\n id: 'people',\n label: t('customers.companies.detail.tabs.people', 'People'),\n icon: <Users className=\"size-4\" />,\n count: formatTabCount(peopleCount),\n },\n ...(canViewDeals\n ? [\n {\n id: 'deals' as CompanyTabId,\n label: t('customers.companies.detail.tabs.deals', 'Deals'),\n icon: <Handshake className=\"size-4\" />,\n count: formatTabCount(dealsCount),\n },\n ]\n : []),\n {\n id: 'activity-log',\n label: t('customers.companies.detail.tabs.activityLog', 'Activity log'),\n icon: <Clock className=\"size-4\" />,\n count: formatTabCount(activitiesCount),\n },\n {\n id: 'changelog',\n label: t('customers.companies.detail.tabs.changelog', 'Changelog'),\n icon: <History className=\"size-4\" />,\n count: 'NEW',\n },\n {\n id: 'files',\n label: t('customers.companies.detail.tabs.files', 'Files'),\n icon: <Paperclip className=\"size-4\" />,\n count: formatTabCount(filesCount),\n },\n ],\n [t, canViewDeals, peopleCount, dealsCount, activitiesCount, filesCount],\n )\n\n const allTabs: TabDef[] = React.useMemo(() => {\n const hidden = new Set(hiddenTabIds)\n return [\n ...builtInTabs,\n ...injectedTabs.filter((tab, index) => !builtInTabs.some((native) => native.id === tab.id) && injectedTabs.findIndex((candidate) => candidate.id === tab.id) === index).map((tab) => ({\n id: tab.id as CompanyTabId,\n label: tab.label,\n })),\n ].filter((tab) => !hidden.has(tab.id))\n }, [builtInTabs, hiddenTabIds, injectedTabs])\n\n React.useEffect(() => {\n if (isLoadingInjectedTabs || !isDealsAccessReady || allTabs.some((tab) => tab.id === activeTab)) return\n const fallback = allTabs[0]?.id\n if (fallback) onTabChange(fallback)\n }, [activeTab, allTabs, isLoadingInjectedTabs, onTabChange, isDealsAccessReady])\n\n return (\n <div>\n {/* Tab navigation */}\n <div className=\"flex items-end justify-between gap-2 border-b\">\n <Tabs\n value={activeTab}\n onValueChange={(value) => onTabChange(value as CompanyTabId)}\n variant=\"underline\"\n className=\"min-w-0 flex-1\"\n >\n <TabsList\n aria-label={t('customers.companies.detail.tabs.label', 'Company detail sections')}\n className=\"-mb-px w-full overflow-x-auto border-b-0 px-1\"\n >\n {allTabs.map((tab) => (\n <TabsTrigger key={tab.id} value={tab.id} leading={tab.icon} count={tab.count}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={sectionAction.onClick}\n disabled={sectionAction.disabled}\n className=\"mb-1.5 mr-1 shrink-0\"\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n\n {/* Tab content */}\n <div className=\"pt-6\" role=\"tabpanel\">\n {children}\n </div>\n </div>\n )\n}\n\nregisterComponent<CompanyDetailTabsProps>({\n id: COMPANY_DETAIL_TABS_COMPONENT_ID,\n component: DefaultCompanyDetailTabs,\n metadata: {\n module: 'customers',\n description: 'Company detail tab navigation and content.',\n },\n})\n\nexport function CompanyDetailTabs(props: CompanyDetailTabsProps) {\n const ResolvedTabs = useRegisteredComponent<CompanyDetailTabsProps>(\n COMPANY_DETAIL_TABS_COMPONENT_ID,\n DefaultCompanyDetailTabs,\n )\n return <ResolvedTabs {...props} />\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,6 +12,7 @@ import { Button } from "@open-mercato/ui/primitives/button";
12
12
  import { Badge } from "@open-mercato/ui/primitives/badge";
13
13
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@open-mercato/ui/primitives/dialog";
14
14
  import { E } from "../../../../generated/entities.ids.generated.js";
15
+ import { extensionPoints } from "../../extension-points.js";
15
16
  import {
16
17
  buildPersonPayload,
17
18
  createPersonFormFields,
@@ -78,20 +79,24 @@ function CreatePersonDialog({
78
79
  }
79
80
  const operation = () => createCrud("customers/people", payload);
80
81
  const response = runGuardedMutation ? await runGuardedMutation(operation, payload) : await operation();
82
+ const newId = response?.result?.id ?? response?.result?.entityId ?? "";
81
83
  flash(
82
84
  companyId ? t("customers.people.createDialog.success", "Person created and linked to company") : t("customers.people.createDialog.successPlain", "Person created"),
83
85
  "success"
84
86
  );
85
- const newId = response?.result?.id ?? response?.result?.entityId ?? "";
87
+ return newId ? { resourceId: newId } : void 0;
88
+ }, [companyId, organizationId, runGuardedMutation, t]);
89
+ const handleSubmitSuccess = React.useCallback(async (values, result) => {
90
+ const payload = buildPersonPayload(values, organizationId);
86
91
  const displayNameFromPayload = typeof payload.displayName === "string" ? payload.displayName : "";
87
92
  onPersonCreated?.({
88
- id: newId,
93
+ id: result?.resourceId ?? "",
89
94
  displayName: displayNameFromPayload,
90
95
  companyId: companyId ?? null,
91
96
  companyName: companyName ?? null
92
97
  });
93
98
  onClose();
94
- }, [companyId, companyName, onClose, onPersonCreated, organizationId, runGuardedMutation, t]);
99
+ }, [companyId, companyName, onClose, onPersonCreated, organizationId]);
95
100
  const handleKeyDown = React.useCallback((event) => {
96
101
  if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
97
102
  const form = event.currentTarget.querySelector("form");
@@ -117,6 +122,10 @@ function CreatePersonDialog({
117
122
  CrudForm,
118
123
  {
119
124
  embedded: true,
125
+ injectionSpotId: extensionPoints.hosts.personForm.spotId,
126
+ legacyInjectionSpotId: "crud-form:customers.customer_entity",
127
+ entityId: "customers.person",
128
+ resourceKind: "customers.person",
120
129
  entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],
121
130
  fields,
122
131
  groups,
@@ -124,6 +133,7 @@ function CreatePersonDialog({
124
133
  submitLabel: t("customers.people.createDialog.submit", "Create person"),
125
134
  schema: formSchema,
126
135
  onSubmit: handleSubmit,
136
+ onSubmitSuccess: handleSubmitSuccess,
127
137
  extraActions: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: onClose, children: t("customers.people.createDialog.cancel", "Cancel") })
128
138
  },
129
139
  `${companyId ?? "no-company"}:${formInstanceKey}`
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/CreatePersonDialog.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Building2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n buildPersonPayload,\n createPersonFormFields,\n createPersonFormGroups,\n createPersonFormSchema,\n type PersonFormValues,\n} from '../formConfig'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type CreatedPersonSummary = {\n id: string\n displayName: string\n /**\n * Always emitted, but nullable now that the dialog also serves the company-less case.\n *\n * This IS a lost guarantee, not a free widening: on a result type, `string | null` breaks a\n * consumer that previously wrote `const owner: string = created.companyId`. It is safe here\n * only because no in-repo consumer reads either field \u2014 the two `onPersonCreated` callers in\n * `CompanyPeopleSection` ignore the argument entirely, and `DealPeopleSection` reads only\n * `id` and `displayName`. Widening a result is not generally safe; do not read this as\n * precedent.\n */\n companyId: string | null\n companyName: string | null\n}\n\ninterface CreatePersonDialogProps {\n open: boolean\n onClose: () => void\n /**\n * When set, the company field is locked to it and the create command links the person on\n * the same request. Omit it for the ordinary editable company picker \u2014 the deal side does,\n * and links the new person to the deal itself afterwards.\n */\n companyId?: string\n companyName?: string\n runGuardedMutation?: GuardedMutationRunner\n onPersonCreated?: (created?: CreatedPersonSummary) => void\n}\n\nexport function CreatePersonDialog({\n open,\n onClose,\n companyId,\n companyName,\n runGuardedMutation,\n onPersonCreated,\n}: CreatePersonDialogProps) {\n const t = useT()\n const { organizationId } = useOrganizationScopeDetail()\n const [formInstanceKey, setFormInstanceKey] = React.useState(0)\n\n React.useEffect(() => {\n if (open) {\n setFormInstanceKey((current) => current + 1)\n }\n }, [open])\n\n const formSchema = React.useMemo(() => createPersonFormSchema(), [])\n\n const fields = React.useMemo<CrudField[]>(() => {\n return createPersonFormFields(t)\n .filter((field) => field.id !== 'addresses')\n .map((field) => {\n if (field.id !== 'companyEntityId' || !companyId) {\n return field\n }\n return {\n id: field.id,\n label: field.label,\n type: 'custom',\n layout: field.layout,\n required: field.required,\n description: field.description,\n disabled: field.disabled,\n readOnly: true,\n component: () => (\n <div className=\"space-y-2\">\n <div className=\"flex min-h-9 items-center gap-2 rounded-md border bg-muted/40 px-3 py-2 text-sm\">\n <Building2 className=\"size-4 text-muted-foreground\" />\n <span className=\"font-medium text-foreground\">{companyName}</span>\n <Badge variant=\"secondary\" className=\"ml-auto rounded-full px-2 py-0 text-xs font-semibold\">\n {t('customers.people.createDialog.auto', 'auto')}\n </Badge>\n </div>\n </div>\n ),\n } satisfies CrudField\n })\n }, [companyId, companyName, t])\n\n const groups = React.useMemo(\n () => createPersonFormGroups(t).filter((group) => group.id !== 'addresses'),\n [t],\n )\n\n const initialValues = React.useMemo<Partial<PersonFormValues>>(\n () => (companyId ? { companyEntityId: companyId } : {}),\n [companyId],\n )\n\n const handleSubmit = React.useCallback(async (values: PersonFormValues) => {\n let payload: Record<string, unknown>\n try {\n payload = buildPersonPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error && err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.people.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n throw err\n }\n\n const operation = () => createCrud<{ id?: string; entityId?: string }>('customers/people', payload)\n const response = runGuardedMutation\n ? await runGuardedMutation(operation, payload)\n : await operation()\n\n flash(\n companyId\n ? t('customers.people.createDialog.success', 'Person created and linked to company')\n : t('customers.people.createDialog.successPlain', 'Person created'),\n 'success',\n )\n const newId = response?.result?.id ?? response?.result?.entityId ?? ''\n const displayNameFromPayload = typeof payload.displayName === 'string' ? payload.displayName : ''\n onPersonCreated?.({\n id: newId,\n displayName: displayNameFromPayload,\n companyId: companyId ?? null,\n companyName: companyName ?? null,\n })\n onClose()\n }, [companyId, companyName, onClose, onPersonCreated, organizationId, runGuardedMutation, t])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n const form = event.currentTarget.querySelector('form')\n if (form) {\n event.preventDefault()\n form.requestSubmit()\n }\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-[1028px]\" onKeyDown={handleKeyDown}>\n <DialogHeader>\n <DialogTitle>{t('customers.people.createDialog.title', 'Add new person')}</DialogTitle>\n {companyId ? (\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Building2 className=\"size-3.5\" />\n <span>{companyName}</span>\n <span className=\"text-xs\">\u00B7</span>\n <span>{t('customers.people.createDialog.autoLink', 'auto-linked to company')}</span>\n </div>\n ) : null}\n </DialogHeader>\n\n <CrudForm<PersonFormValues>\n key={`${companyId ?? 'no-company'}:${formInstanceKey}`}\n embedded\n entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}\n fields={fields}\n groups={groups}\n initialValues={initialValues}\n submitLabel={t('customers.people.createDialog.submit', 'Create person')}\n schema={formSchema}\n onSubmit={handleSubmit}\n extraActions={(\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n {t('customers.people.createDialog.cancel', 'Cancel')}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Building2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { CrudForm, type CrudField, type CrudFormSubmitResult } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { E } from '#generated/entities.ids.generated'\nimport { extensionPoints } from '../../extension-points'\nimport {\n buildPersonPayload,\n createPersonFormFields,\n createPersonFormGroups,\n createPersonFormSchema,\n type PersonFormValues,\n} from '../formConfig'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type CreatedPersonSummary = {\n id: string\n displayName: string\n /**\n * Always emitted, but nullable now that the dialog also serves the company-less case.\n *\n * This IS a lost guarantee, not a free widening: on a result type, `string | null` breaks a\n * consumer that previously wrote `const owner: string = created.companyId`. It is safe here\n * only because no in-repo consumer reads either field \u2014 the two `onPersonCreated` callers in\n * `CompanyPeopleSection` ignore the argument entirely, and `DealPeopleSection` reads only\n * `id` and `displayName`. Widening a result is not generally safe; do not read this as\n * precedent.\n */\n companyId: string | null\n companyName: string | null\n}\n\ninterface CreatePersonDialogProps {\n open: boolean\n onClose: () => void\n /**\n * When set, the company field is locked to it and the create command links the person on\n * the same request. Omit it for the ordinary editable company picker \u2014 the deal side does,\n * and links the new person to the deal itself afterwards.\n */\n companyId?: string\n companyName?: string\n runGuardedMutation?: GuardedMutationRunner\n onPersonCreated?: (created?: CreatedPersonSummary) => void\n}\n\nexport function CreatePersonDialog({\n open,\n onClose,\n companyId,\n companyName,\n runGuardedMutation,\n onPersonCreated,\n}: CreatePersonDialogProps) {\n const t = useT()\n const { organizationId } = useOrganizationScopeDetail()\n const [formInstanceKey, setFormInstanceKey] = React.useState(0)\n\n React.useEffect(() => {\n if (open) {\n setFormInstanceKey((current) => current + 1)\n }\n }, [open])\n\n const formSchema = React.useMemo(() => createPersonFormSchema(), [])\n\n const fields = React.useMemo<CrudField[]>(() => {\n return createPersonFormFields(t)\n .filter((field) => field.id !== 'addresses')\n .map((field) => {\n if (field.id !== 'companyEntityId' || !companyId) {\n return field\n }\n return {\n id: field.id,\n label: field.label,\n type: 'custom',\n layout: field.layout,\n required: field.required,\n description: field.description,\n disabled: field.disabled,\n readOnly: true,\n component: () => (\n <div className=\"space-y-2\">\n <div className=\"flex min-h-9 items-center gap-2 rounded-md border bg-muted/40 px-3 py-2 text-sm\">\n <Building2 className=\"size-4 text-muted-foreground\" />\n <span className=\"font-medium text-foreground\">{companyName}</span>\n <Badge variant=\"secondary\" className=\"ml-auto rounded-full px-2 py-0 text-xs font-semibold\">\n {t('customers.people.createDialog.auto', 'auto')}\n </Badge>\n </div>\n </div>\n ),\n } satisfies CrudField\n })\n }, [companyId, companyName, t])\n\n const groups = React.useMemo(\n () => createPersonFormGroups(t).filter((group) => group.id !== 'addresses'),\n [t],\n )\n\n const initialValues = React.useMemo<Partial<PersonFormValues>>(\n () => (companyId ? { companyEntityId: companyId } : {}),\n [companyId],\n )\n\n const handleSubmit = React.useCallback(async (values: PersonFormValues) => {\n let payload: Record<string, unknown>\n try {\n payload = buildPersonPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error && err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.people.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n throw err\n }\n\n const operation = () => createCrud<{ id?: string; entityId?: string }>('customers/people', payload)\n const response = runGuardedMutation\n ? await runGuardedMutation(operation, payload)\n : await operation()\n\n const newId = response?.result?.id ?? response?.result?.entityId ?? ''\n flash(\n companyId\n ? t('customers.people.createDialog.success', 'Person created and linked to company')\n : t('customers.people.createDialog.successPlain', 'Person created'),\n 'success',\n )\n return newId ? { resourceId: newId } : undefined\n }, [companyId, organizationId, runGuardedMutation, t])\n\n const handleSubmitSuccess = React.useCallback(async (values: PersonFormValues, result: CrudFormSubmitResult | void) => {\n const payload = buildPersonPayload(values, organizationId)\n const displayNameFromPayload = typeof payload.displayName === 'string' ? payload.displayName : ''\n onPersonCreated?.({\n id: result?.resourceId ?? '',\n displayName: displayNameFromPayload,\n companyId: companyId ?? null,\n companyName: companyName ?? null,\n })\n onClose()\n }, [companyId, companyName, onClose, onPersonCreated, organizationId])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n const form = event.currentTarget.querySelector('form')\n if (form) {\n event.preventDefault()\n form.requestSubmit()\n }\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-[1028px]\" onKeyDown={handleKeyDown}>\n <DialogHeader>\n <DialogTitle>{t('customers.people.createDialog.title', 'Add new person')}</DialogTitle>\n {companyId ? (\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Building2 className=\"size-3.5\" />\n <span>{companyName}</span>\n <span className=\"text-xs\">\u00B7</span>\n <span>{t('customers.people.createDialog.autoLink', 'auto-linked to company')}</span>\n </div>\n ) : null}\n </DialogHeader>\n\n <CrudForm<PersonFormValues>\n key={`${companyId ?? 'no-company'}:${formInstanceKey}`}\n embedded\n injectionSpotId={extensionPoints.hosts.personForm.spotId}\n legacyInjectionSpotId=\"crud-form:customers.customer_entity\"\n entityId=\"customers.person\"\n resourceKind=\"customers.person\"\n entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}\n fields={fields}\n groups={groups}\n initialValues={initialValues}\n submitLabel={t('customers.people.createDialog.submit', 'Create person')}\n schema={formSchema}\n onSubmit={handleSubmit}\n onSubmitSuccess={handleSubmitSuccess}\n extraActions={(\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n {t('customers.people.createDialog.cancel', 'Cancel')}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { InjectionSpot, useInjectionWidgets } from "@open-mercato/ui/backend/injection/InjectionSpot";
5
+ function CustomerDetailSidebar({ spotId, context, data, onDataChange, disabled, children }) {
6
+ const { widgets } = useInjectionWidgets(spotId, { context, triggerOnLoad: true });
7
+ const sidebarRef = React.useRef(null);
8
+ const [hasContent, setHasContent] = React.useState(false);
9
+ React.useLayoutEffect(() => {
10
+ const sidebar = sidebarRef.current;
11
+ if (!sidebar) {
12
+ setHasContent(false);
13
+ return;
14
+ }
15
+ const update = () => setHasContent(Array.from(sidebar.childNodes).some((node) => node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim())));
16
+ update();
17
+ const observer = new MutationObserver(update);
18
+ observer.observe(sidebar, { childList: true, subtree: true, characterData: true });
19
+ return () => observer.disconnect();
20
+ }, [widgets, data]);
21
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-4 lg:flex-row", "data-customer-detail-layout": true, children: [
22
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children }),
23
+ widgets.length ? /* @__PURE__ */ jsx("aside", { ref: sidebarRef, className: hasContent ? "w-full space-y-4 lg:w-80 lg:shrink-0" : "hidden", "data-customer-detail-sidebar": spotId, children: /* @__PURE__ */ jsx(InjectionSpot, { spotId, context, data, onDataChange, disabled, widgetsOverride: widgets }) }) : null
24
+ ] });
25
+ }
26
+ export {
27
+ CustomerDetailSidebar
28
+ };
29
+ //# sourceMappingURL=CustomerDetailSidebar.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/customers/components/detail/CustomerDetailSidebar.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\n\nexport function CustomerDetailSidebar<TData>({ spotId, context, data, onDataChange, disabled, children }: {\n spotId: string\n context: unknown\n data: TData\n onDataChange?: (data: TData) => void\n disabled?: boolean\n children: React.ReactNode\n}) {\n const { widgets } = useInjectionWidgets(spotId, { context, triggerOnLoad: true })\n const sidebarRef = React.useRef<HTMLElement>(null)\n const [hasContent, setHasContent] = React.useState(false)\n React.useLayoutEffect(() => {\n const sidebar = sidebarRef.current\n if (!sidebar) {\n setHasContent(false)\n return\n }\n const update = () => setHasContent(Array.from(sidebar.childNodes).some((node) => (\n node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim()))\n )))\n update()\n const observer = new MutationObserver(update)\n observer.observe(sidebar, { childList: true, subtree: true, characterData: true })\n return () => observer.disconnect()\n }, [widgets, data])\n return (\n <div className=\"flex min-w-0 flex-col gap-4 lg:flex-row\" data-customer-detail-layout>\n <div className=\"min-w-0 flex-1\">{children}</div>\n {widgets.length ? (\n <aside ref={sidebarRef} className={hasContent ? 'w-full space-y-4 lg:w-80 lg:shrink-0' : 'hidden'} data-customer-detail-sidebar={spotId}>\n <InjectionSpot spotId={spotId} context={context} data={data} onDataChange={onDataChange} disabled={disabled} widgetsOverride={widgets} />\n </aside>\n ) : null}\n </div>\n )\n}\n"],
5
+ "mappings": ";AA+BI,SACE,KADF;AA7BJ,YAAY,WAAW;AACvB,SAAS,eAAe,2BAA2B;AAE5C,SAAS,sBAA6B,EAAE,QAAQ,SAAS,MAAM,cAAc,UAAU,SAAS,GAOpG;AACD,QAAM,EAAE,QAAQ,IAAI,oBAAoB,QAAQ,EAAE,SAAS,eAAe,KAAK,CAAC;AAChF,QAAM,aAAa,MAAM,OAAoB,IAAI;AACjD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,gBAAgB,MAAM;AAC1B,UAAM,UAAU,WAAW;AAC3B,QAAI,CAAC,SAAS;AACZ,oBAAc,KAAK;AACnB;AAAA,IACF;AACA,UAAM,SAAS,MAAM,cAAc,MAAM,KAAK,QAAQ,UAAU,EAAE,KAAK,CAAC,SACtE,KAAK,aAAa,KAAK,gBAAiB,KAAK,aAAa,KAAK,aAAa,QAAQ,KAAK,aAAa,KAAK,CAAC,CAC7G,CAAC;AACF,WAAO;AACP,UAAM,WAAW,IAAI,iBAAiB,MAAM;AAC5C,aAAS,QAAQ,SAAS,EAAE,WAAW,MAAM,SAAS,MAAM,eAAe,KAAK,CAAC;AACjF,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,SAAS,IAAI,CAAC;AAClB,SACE,qBAAC,SAAI,WAAU,2CAA0C,+BAA2B,MAClF;AAAA,wBAAC,SAAI,WAAU,kBAAkB,UAAS;AAAA,IACzC,QAAQ,SACP,oBAAC,WAAM,KAAK,YAAY,WAAW,aAAa,yCAAyC,UAAU,gCAA8B,QAC/H,8BAAC,iBAAc,QAAgB,SAAkB,MAAY,cAA4B,UAAoB,iBAAiB,SAAS,GACzI,IACE;AAAA,KACN;AAEJ;",
6
+ "names": []
7
+ }
@@ -127,7 +127,8 @@ function DealDetailHeader({
127
127
  onSave,
128
128
  onDelete,
129
129
  isDirty,
130
- isSaving
130
+ isSaving,
131
+ actions
131
132
  }) {
132
133
  const t = useT();
133
134
  const amountLabel = React.useMemo(
@@ -193,7 +194,8 @@ function DealDetailHeader({
193
194
  ] })
194
195
  ] })
195
196
  ] }),
196
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 xl:justify-end", children: [
197
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 xl:justify-end", children: [
198
+ actions,
197
199
  canMoveStage ? /* @__PURE__ */ jsx(
198
200
  StageActionButton,
199
201
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/DealDetailHeader.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, CalendarClock, Check, ChevronDown, FileText, Save, Trash2, Users, Workflow } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { formatFallbackLabel } from './utils'\nimport { isTerminalPipelineOutcomeLabel } from './pipelineStageUtils'\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\ntype DealDetailHeaderProps = {\n deal: {\n id: string\n title: string\n status: string | null\n pipelineStage: string | null\n valueAmount: string | null\n valueCurrency: string | null\n expectedCloseAt: string | null\n createdAt: string\n closureOutcome: 'won' | 'lost' | null\n organizationId: string | null\n }\n owner?: { id: string; name: string; email: string } | null\n people: DealAssociation[]\n companies: DealAssociation[]\n pipelineName?: string | null\n stageOptions?: Array<{ id: string; label: string; order: number }>\n currentStageId?: string | null\n onStageChange?: (stageId: string) => Promise<void> | void\n isStageSaving?: boolean\n onSave: () => void\n onDelete: () => Promise<void> | void\n isDirty: boolean\n isSaving: boolean\n}\n\nfunction formatCurrency(amount: string | null, currency: string | null): string | null {\n if (!amount) return null\n const parsed = Number(amount)\n if (!Number.isFinite(parsed)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return parsed.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(parsed)\n } catch {\n return `${parsed.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatDate(value: string | null): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\ntype HeaderChipVariant = 'info' | 'warning' | 'success' | 'error' | 'neutral'\n\nconst headerChipDotVariantClasses: Record<HeaderChipVariant, string> = {\n info: 'bg-status-info-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n neutral: 'bg-status-neutral-icon',\n}\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\nfunction HeaderChip({\n children,\n icon,\n variant,\n}: {\n children: React.ReactNode\n icon?: React.ReactNode\n variant?: HeaderChipVariant\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-sm bg-muted px-2 py-1 text-xs font-medium leading-none text-muted-foreground\">\n {variant ? <span className={cn('size-2 rounded-full', headerChipDotVariantClasses[variant])} /> : null}\n {icon ? <span className=\"text-muted-foreground\">{icon}</span> : null}\n {children}\n </span>\n )\n}\n\nfunction StageActionButton({\n stageOptions,\n currentStageId,\n onStageChange,\n disabled,\n isSaving,\n}: {\n stageOptions: Array<{ id: string; label: string; order: number }>\n currentStageId: string | null\n onStageChange: (stageId: string) => Promise<void> | void\n disabled: boolean\n isSaving: boolean\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const sortedStages = React.useMemo(\n () =>\n [...stageOptions]\n .filter((stage) => !isTerminalPipelineOutcomeLabel(stage.label))\n .sort((left, right) => left.order - right.order),\n [stageOptions],\n )\n const currentStage = React.useMemo(\n () => sortedStages.find((stage) => stage.id === currentStageId) ?? null,\n [currentStageId, sortedStages],\n )\n\n if (!sortedStages.length) return null\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled || isSaving}\n className=\"h-8 max-w-[13rem] rounded-md px-3\"\n >\n <Workflow className=\"size-4\" />\n <span className=\"truncate\">\n {currentStage?.label ?? t('customers.deals.detail.actions.moveStage', 'Move stage')}\n </span>\n <ChevronDown className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-64 p-1\">\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-xs font-semibold text-foreground\">\n {t('customers.deals.detail.stageMenu.title', 'Update pipeline stage')}\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground\">\n {t('customers.deals.detail.stageMenu.description', 'Move the deal forward without opening the form.')}\n </div>\n </div>\n <div className=\"py-1\">\n {sortedStages.map((stage) => {\n const isCurrent = stage.id === currentStageId\n return (\n <Button\n key={stage.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isSaving || isCurrent}\n className=\"h-auto w-full justify-between rounded-md px-3 py-2 text-left\"\n onClick={() => {\n setOpen(false)\n if (!isCurrent) {\n void onStageChange(stage.id)\n }\n }}\n >\n <span className=\"min-w-0 truncate text-sm font-medium\">\n {stage.label}\n </span>\n {isCurrent ? <Check className=\"size-4 text-foreground\" /> : null}\n </Button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport function DealDetailHeader({\n deal,\n owner,\n people,\n companies,\n pipelineName,\n stageOptions = [],\n currentStageId = null,\n onStageChange,\n isStageSaving = false,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n}: DealDetailHeaderProps) {\n const t = useT()\n const amountLabel = React.useMemo(\n () => formatCurrency(deal.valueAmount, deal.valueCurrency),\n [deal.valueAmount, deal.valueCurrency],\n )\n const createdAtLabel = React.useMemo(() => formatDate(deal.createdAt), [deal.createdAt])\n const expectedCloseLabel = React.useMemo(() => formatDate(deal.expectedCloseAt), [deal.expectedCloseAt])\n const { data: statusDictionary } = useCustomerDictionary('deal-statuses', 0, deal.organizationId ?? null)\n const statusEntry = deal.status ? statusDictionary?.map?.[deal.status] : null\n const companyLabel = React.useMemo(() => {\n const primaryCompany = companies[0]\n if (!primaryCompany) return null\n const extraCount = companies.length - 1\n return extraCount > 0 ? `${primaryCompany.label} +${extraCount}` : primaryCompany.label\n }, [companies])\n const timelineLabel = React.useMemo(() => {\n if (createdAtLabel && expectedCloseLabel) {\n return t('customers.deals.detail.header.timeline', 'Created {{created}} \u00B7 Expected close {{expected}}', {\n created: createdAtLabel,\n expected: expectedCloseLabel,\n })\n }\n if (createdAtLabel) {\n return t('customers.deals.detail.header.createdAt', 'Created {{date}}', { date: createdAtLabel })\n }\n if (expectedCloseLabel) {\n return t('customers.deals.detail.header.expectedClose', 'Expected close {{date}}', { date: expectedCloseLabel })\n }\n return null\n }, [createdAtLabel, expectedCloseLabel, t])\n const statusLabel = statusEntry?.label ?? (deal.status ? formatFallbackLabel(deal.status) : null)\n const showStatusChip = statusLabel && (!deal.closureOutcome || !isTerminalPipelineOutcomeLabel(deal.status))\n const pipelineBadgeLabel = pipelineName ?? null\n const canMoveStage = stageOptions.length > 0 && !deal.closureOutcome && typeof onStageChange === 'function'\n const messageSubtitle = React.useMemo(() => (\n [companyLabel, amountLabel].filter(Boolean).join(' \u00B7 ') || statusLabel || undefined\n ), [amountLabel, companyLabel, statusLabel])\n\n return (\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"flex min-w-0 gap-5\">\n <div className=\"flex size-16 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground\">\n <FileText className=\"size-7\" />\n </div>\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <h1 className=\"min-w-0 truncate text-2xl font-bold leading-tight text-foreground md:text-2xl\">\n {deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n </h1>\n <span className=\"rounded-sm bg-muted px-2 py-1 text-overline font-semibold uppercase tracking-[0.04em] text-muted-foreground\">\n {t('customers.deals.detail.badge.deal', 'Deal')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground\">\n {companyLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <Building2 className=\"size-3.5\" />\n {companyLabel}\n </span>\n ) : null}\n {timelineLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {timelineLabel}\n </span>\n ) : null}\n {amountLabel ? (\n <span className=\"font-semibold text-foreground\">{amountLabel}</span>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {showStatusChip ? (\n <HeaderChip variant=\"info\">\n {statusLabel}\n </HeaderChip>\n ) : null}\n {deal.pipelineStage && !isTerminalPipelineOutcomeLabel(deal.pipelineStage) ? (\n <HeaderChip variant=\"warning\">\n {deal.pipelineStage}\n </HeaderChip>\n ) : null}\n {pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {pipelineBadgeLabel}\n </HeaderChip>\n ) : null}\n {deal.closureOutcome ? (\n <HeaderChip variant={deal.closureOutcome === 'won' ? 'success' : 'error'}>\n {deal.closureOutcome === 'won'\n ? t('customers.deals.detail.badge.won', 'Won')\n : t('customers.deals.detail.badge.lost', 'Lost')}\n </HeaderChip>\n ) : null}\n {!companyLabel && people.length > 0 ? (\n <HeaderChip icon={<Users className=\"size-3.5\" />}>\n {people[0]?.label}\n </HeaderChip>\n ) : null}\n {owner && !pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {owner.name}\n </HeaderChip>\n ) : null}\n </div>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 xl:justify-end\">\n {canMoveStage ? (\n <StageActionButton\n stageOptions={stageOptions}\n currentStageId={currentStageId}\n onStageChange={onStageChange}\n disabled={isDirty || isSaving}\n isSaving={isStageSaving}\n />\n ) : null}\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'deal',\n entityId: deal.id,\n previewData: {\n title: deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n subtitle: messageSubtitle,\n },\n }}\n viewHref={`/backend/customers/deals/${deal.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.deals.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.deal\"\n resourceId={deal.id}\n organizationId={deal.organizationId ?? undefined}\n includeRelated\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.deals.detail.actions.delete', 'Delete')}\n className=\"h-8 w-8 rounded-md\"\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n className=\"h-8 rounded-md px-3\"\n >\n <Save className=\"size-4\" />\n {t('customers.deals.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, CalendarClock, Check, ChevronDown, FileText, Save, Trash2, Users, Workflow } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { formatFallbackLabel } from './utils'\nimport { isTerminalPipelineOutcomeLabel } from './pipelineStageUtils'\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\ntype DealDetailHeaderProps = {\n deal: {\n id: string\n title: string\n status: string | null\n pipelineStage: string | null\n valueAmount: string | null\n valueCurrency: string | null\n expectedCloseAt: string | null\n createdAt: string\n closureOutcome: 'won' | 'lost' | null\n organizationId: string | null\n }\n owner?: { id: string; name: string; email: string } | null\n people: DealAssociation[]\n companies: DealAssociation[]\n pipelineName?: string | null\n stageOptions?: Array<{ id: string; label: string; order: number }>\n currentStageId?: string | null\n onStageChange?: (stageId: string) => Promise<void> | void\n isStageSaving?: boolean\n onSave: () => void\n onDelete: () => Promise<void> | void\n isDirty: boolean\n isSaving: boolean\n actions?: React.ReactNode\n}\n\nfunction formatCurrency(amount: string | null, currency: string | null): string | null {\n if (!amount) return null\n const parsed = Number(amount)\n if (!Number.isFinite(parsed)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return parsed.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(parsed)\n } catch {\n return `${parsed.toLocaleString()} ${currency}`\n }\n}\n\nfunction formatDate(value: string | null): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\ntype HeaderChipVariant = 'info' | 'warning' | 'success' | 'error' | 'neutral'\n\nconst headerChipDotVariantClasses: Record<HeaderChipVariant, string> = {\n info: 'bg-status-info-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n neutral: 'bg-status-neutral-icon',\n}\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\nfunction HeaderChip({\n children,\n icon,\n variant,\n}: {\n children: React.ReactNode\n icon?: React.ReactNode\n variant?: HeaderChipVariant\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-sm bg-muted px-2 py-1 text-xs font-medium leading-none text-muted-foreground\">\n {variant ? <span className={cn('size-2 rounded-full', headerChipDotVariantClasses[variant])} /> : null}\n {icon ? <span className=\"text-muted-foreground\">{icon}</span> : null}\n {children}\n </span>\n )\n}\n\nfunction StageActionButton({\n stageOptions,\n currentStageId,\n onStageChange,\n disabled,\n isSaving,\n}: {\n stageOptions: Array<{ id: string; label: string; order: number }>\n currentStageId: string | null\n onStageChange: (stageId: string) => Promise<void> | void\n disabled: boolean\n isSaving: boolean\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const sortedStages = React.useMemo(\n () =>\n [...stageOptions]\n .filter((stage) => !isTerminalPipelineOutcomeLabel(stage.label))\n .sort((left, right) => left.order - right.order),\n [stageOptions],\n )\n const currentStage = React.useMemo(\n () => sortedStages.find((stage) => stage.id === currentStageId) ?? null,\n [currentStageId, sortedStages],\n )\n\n if (!sortedStages.length) return null\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled || isSaving}\n className=\"h-8 max-w-[13rem] rounded-md px-3\"\n >\n <Workflow className=\"size-4\" />\n <span className=\"truncate\">\n {currentStage?.label ?? t('customers.deals.detail.actions.moveStage', 'Move stage')}\n </span>\n <ChevronDown className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-64 p-1\">\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-xs font-semibold text-foreground\">\n {t('customers.deals.detail.stageMenu.title', 'Update pipeline stage')}\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground\">\n {t('customers.deals.detail.stageMenu.description', 'Move the deal forward without opening the form.')}\n </div>\n </div>\n <div className=\"py-1\">\n {sortedStages.map((stage) => {\n const isCurrent = stage.id === currentStageId\n return (\n <Button\n key={stage.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isSaving || isCurrent}\n className=\"h-auto w-full justify-between rounded-md px-3 py-2 text-left\"\n onClick={() => {\n setOpen(false)\n if (!isCurrent) {\n void onStageChange(stage.id)\n }\n }}\n >\n <span className=\"min-w-0 truncate text-sm font-medium\">\n {stage.label}\n </span>\n {isCurrent ? <Check className=\"size-4 text-foreground\" /> : null}\n </Button>\n )\n })}\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport function DealDetailHeader({\n deal,\n owner,\n people,\n companies,\n pipelineName,\n stageOptions = [],\n currentStageId = null,\n onStageChange,\n isStageSaving = false,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n actions,\n}: DealDetailHeaderProps) {\n const t = useT()\n const amountLabel = React.useMemo(\n () => formatCurrency(deal.valueAmount, deal.valueCurrency),\n [deal.valueAmount, deal.valueCurrency],\n )\n const createdAtLabel = React.useMemo(() => formatDate(deal.createdAt), [deal.createdAt])\n const expectedCloseLabel = React.useMemo(() => formatDate(deal.expectedCloseAt), [deal.expectedCloseAt])\n const { data: statusDictionary } = useCustomerDictionary('deal-statuses', 0, deal.organizationId ?? null)\n const statusEntry = deal.status ? statusDictionary?.map?.[deal.status] : null\n const companyLabel = React.useMemo(() => {\n const primaryCompany = companies[0]\n if (!primaryCompany) return null\n const extraCount = companies.length - 1\n return extraCount > 0 ? `${primaryCompany.label} +${extraCount}` : primaryCompany.label\n }, [companies])\n const timelineLabel = React.useMemo(() => {\n if (createdAtLabel && expectedCloseLabel) {\n return t('customers.deals.detail.header.timeline', 'Created {{created}} \u00B7 Expected close {{expected}}', {\n created: createdAtLabel,\n expected: expectedCloseLabel,\n })\n }\n if (createdAtLabel) {\n return t('customers.deals.detail.header.createdAt', 'Created {{date}}', { date: createdAtLabel })\n }\n if (expectedCloseLabel) {\n return t('customers.deals.detail.header.expectedClose', 'Expected close {{date}}', { date: expectedCloseLabel })\n }\n return null\n }, [createdAtLabel, expectedCloseLabel, t])\n const statusLabel = statusEntry?.label ?? (deal.status ? formatFallbackLabel(deal.status) : null)\n const showStatusChip = statusLabel && (!deal.closureOutcome || !isTerminalPipelineOutcomeLabel(deal.status))\n const pipelineBadgeLabel = pipelineName ?? null\n const canMoveStage = stageOptions.length > 0 && !deal.closureOutcome && typeof onStageChange === 'function'\n const messageSubtitle = React.useMemo(() => (\n [companyLabel, amountLabel].filter(Boolean).join(' \u00B7 ') || statusLabel || undefined\n ), [amountLabel, companyLabel, statusLabel])\n\n return (\n <div className=\"flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"flex min-w-0 gap-5\">\n <div className=\"flex size-16 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground\">\n <FileText className=\"size-7\" />\n </div>\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <h1 className=\"min-w-0 truncate text-2xl font-bold leading-tight text-foreground md:text-2xl\">\n {deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n </h1>\n <span className=\"rounded-sm bg-muted px-2 py-1 text-overline font-semibold uppercase tracking-[0.04em] text-muted-foreground\">\n {t('customers.deals.detail.badge.deal', 'Deal')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground\">\n {companyLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <Building2 className=\"size-3.5\" />\n {companyLabel}\n </span>\n ) : null}\n {timelineLabel ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {timelineLabel}\n </span>\n ) : null}\n {amountLabel ? (\n <span className=\"font-semibold text-foreground\">{amountLabel}</span>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {showStatusChip ? (\n <HeaderChip variant=\"info\">\n {statusLabel}\n </HeaderChip>\n ) : null}\n {deal.pipelineStage && !isTerminalPipelineOutcomeLabel(deal.pipelineStage) ? (\n <HeaderChip variant=\"warning\">\n {deal.pipelineStage}\n </HeaderChip>\n ) : null}\n {pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {pipelineBadgeLabel}\n </HeaderChip>\n ) : null}\n {deal.closureOutcome ? (\n <HeaderChip variant={deal.closureOutcome === 'won' ? 'success' : 'error'}>\n {deal.closureOutcome === 'won'\n ? t('customers.deals.detail.badge.won', 'Won')\n : t('customers.deals.detail.badge.lost', 'Lost')}\n </HeaderChip>\n ) : null}\n {!companyLabel && people.length > 0 ? (\n <HeaderChip icon={<Users className=\"size-3.5\" />}>\n {people[0]?.label}\n </HeaderChip>\n ) : null}\n {owner && !pipelineBadgeLabel ? (\n <HeaderChip icon={<Workflow className=\"size-3.5\" />}>\n {owner.name}\n </HeaderChip>\n ) : null}\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2 xl:justify-end\">\n {actions}\n {canMoveStage ? (\n <StageActionButton\n stageOptions={stageOptions}\n currentStageId={currentStageId}\n onStageChange={onStageChange}\n disabled={isDirty || isSaving}\n isSaving={isStageSaving}\n />\n ) : null}\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'deal',\n entityId: deal.id,\n previewData: {\n title: deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n subtitle: messageSubtitle,\n },\n }}\n viewHref={`/backend/customers/deals/${deal.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.deals.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.deal\"\n resourceId={deal.id}\n organizationId={deal.organizationId ?? undefined}\n includeRelated\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.deals.detail.actions.delete', 'Delete')}\n className=\"h-8 w-8 rounded-md\"\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n className=\"h-8 rounded-md px-3\"\n >\n <Save className=\"size-4\" />\n {t('customers.deals.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,6 +6,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
6
  import { registerComponent } from "@open-mercato/shared/modules/widgets/component-registry";
7
7
  import { useRegisteredComponent } from "@open-mercato/ui/backend/injection/useRegisteredComponent";
8
8
  import { Tabs, TabsList, TabsTrigger } from "@open-mercato/ui/primitives/tabs";
9
+ const DEAL_DETAIL_TAB_IDS = ["activities", "people", "companies", "notes", "files", "changelog"];
9
10
  const DEAL_DETAIL_TABS_COMPONENT_ID = "section:customers.deals.detailTabs";
10
11
  const SUPPORTED_TAB_IDS = /* @__PURE__ */ new Set(["activities", "people", "companies", "notes", "files", "changelog"]);
11
12
  function resolveLegacyTab(tab, knownTabIds) {
@@ -22,6 +23,7 @@ function DefaultDealDetailTabs({
22
23
  activeTab,
23
24
  onTabChange,
24
25
  injectedTabs = [],
26
+ isLoadingInjectedTabs = false,
25
27
  hiddenTabIds = [],
26
28
  peopleCount = 0,
27
29
  companiesCount = 0,
@@ -70,12 +72,17 @@ function DefaultDealDetailTabs({
70
72
  const hidden = new Set(hiddenTabIds);
71
73
  return [
72
74
  ...builtInTabs,
73
- ...injectedTabs.map((tab) => ({
75
+ ...injectedTabs.filter((tab, index) => !builtInTabs.some((native) => native.id === tab.id) && injectedTabs.findIndex((candidate) => candidate.id === tab.id) === index).map((tab) => ({
74
76
  id: tab.id,
75
77
  label: tab.label
76
78
  }))
77
79
  ].filter((tab) => !hidden.has(tab.id));
78
80
  }, [builtInTabs, hiddenTabIds, injectedTabs]);
81
+ React.useEffect(() => {
82
+ if (isLoadingInjectedTabs || allTabs.some((tab) => tab.id === activeTab)) return;
83
+ const fallback = allTabs[0]?.id;
84
+ if (fallback) onTabChange(fallback);
85
+ }, [activeTab, allTabs, isLoadingInjectedTabs, onTabChange]);
79
86
  return /* @__PURE__ */ jsxs("div", { children: [
80
87
  /* @__PURE__ */ jsx(
81
88
  Tabs,
@@ -113,6 +120,7 @@ function DealDetailTabs(props) {
113
120
  }
114
121
  export {
115
122
  DEAL_DETAIL_TABS_COMPONENT_ID,
123
+ DEAL_DETAIL_TAB_IDS,
116
124
  DealDetailTabs,
117
125
  resolveLegacyTab
118
126
  };