@open-mercato/core 0.6.8-develop.7012.1.1dbd6f5fbd → 0.6.8-develop.7015.1.af90a2ddc7

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 (31) hide show
  1. package/dist/modules/customers/api/companies/[id]/people/route.js +19 -63
  2. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  3. package/dist/modules/customers/api/companies/[id]/route.js +9 -85
  4. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  5. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
  6. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  7. package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
  8. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  9. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
  10. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  11. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
  12. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  13. package/dist/modules/customers/data/validators.js +10 -2
  14. package/dist/modules/customers/data/validators.js.map +2 -2
  15. package/dist/modules/customers/events.js +5 -0
  16. package/dist/modules/customers/events.js.map +2 -2
  17. package/dist/modules/customers/lib/personCompanies.js +100 -2
  18. package/dist/modules/customers/lib/personCompanies.js.map +2 -2
  19. package/dist/modules/query_index/lib/search-tokens.js +79 -3
  20. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  21. package/package.json +7 -7
  22. package/src/modules/customers/api/companies/[id]/people/route.ts +20 -72
  23. package/src/modules/customers/api/companies/[id]/route.ts +11 -97
  24. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
  25. package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
  26. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
  27. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
  28. package/src/modules/customers/data/validators.ts +18 -3
  29. package/src/modules/customers/events.ts +5 -0
  30. package/src/modules/customers/lib/personCompanies.ts +136 -1
  31. package/src/modules/query_index/lib/search-tokens.ts +135 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/CompanyPeopleSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Users, Link2, Plus, Filter } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport {\n readVersionedIdSet,\n writeVersionedIdSet,\n} from '@open-mercato/shared/lib/browser/versionedPreference'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { SectionAction, TabEmptyStateConfig, Translator } from './types'\nimport { CreatePersonDialog } from './CreatePersonDialog'\nimport { PersonCard } from './PersonCard'\nimport { coerceDisplayName } from '../../lib/displayName'\nimport { DecisionMakersFooter } from './DecisionMakersFooter'\nimport { RolesSection } from './RolesSection'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createPersonLinkAdapter } from '../linking/adapters/personAdapter'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type CompanyPersonSummary = {\n id: string\n displayName: string\n primaryEmail?: string | null\n primaryPhone?: string | null\n status?: string | null\n lifecycleStage?: string | null\n jobTitle?: string | null\n department?: string | null\n createdAt?: string | null\n organizationId?: string | null\n temperature?: string | null\n source?: string | null\n linkedAt?: string | null\n}\n\nexport type CompanyPeopleSectionProps = {\n companyId: string\n companyName?: string\n initialPeople: CompanyPersonSummary[]\n addActionLabel: string\n emptyLabel: string\n emptyState: TabEmptyStateConfig\n onPeopleChange?: (next: CompanyPersonSummary[]) => void\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst COMPANY_PEOPLE_PAGE_SIZE = 20\nconst STARRED_PEOPLE_STORAGE_VERSION = 1\n\nfunction normalizeCompanyPerson(record: Record<string, unknown>): CompanyPersonSummary | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n if (!displayName) return null\n return {\n id,\n displayName,\n primaryEmail:\n typeof record.primaryEmail === 'string'\n ? record.primaryEmail\n : typeof record.primary_email === 'string'\n ? record.primary_email\n : null,\n primaryPhone:\n typeof record.primaryPhone === 'string'\n ? record.primaryPhone\n : typeof record.primary_phone === 'string'\n ? record.primary_phone\n : null,\n status:\n typeof record.status === 'string'\n ? record.status\n : null,\n lifecycleStage:\n typeof record.lifecycleStage === 'string'\n ? record.lifecycleStage\n : typeof record.lifecycle_stage === 'string'\n ? record.lifecycle_stage\n : null,\n jobTitle:\n typeof record.jobTitle === 'string'\n ? record.jobTitle\n : typeof record.job_title === 'string'\n ? record.job_title\n : null,\n department:\n typeof record.department === 'string'\n ? record.department\n : null,\n createdAt:\n typeof record.createdAt === 'string'\n ? record.createdAt\n : typeof record.created_at === 'string'\n ? record.created_at\n : null,\n organizationId:\n typeof record.organizationId === 'string'\n ? record.organizationId\n : typeof record.organization_id === 'string'\n ? record.organization_id\n : null,\n temperature:\n typeof record.temperature === 'string'\n ? record.temperature\n : null,\n source:\n typeof record.source === 'string'\n ? record.source\n : null,\n linkedAt:\n typeof record.linkedAt === 'string'\n ? record.linkedAt\n : typeof record.linked_at === 'string'\n ? record.linked_at\n : null,\n }\n}\n\nfunction mergeCompanyPeople(items: CompanyPersonSummary[]): CompanyPersonSummary[] {\n const merged = new Map<string, CompanyPersonSummary>()\n items.forEach((item) => merged.set(item.id, item))\n return Array.from(merged.values())\n}\n\nfunction matchesCompanyPersonSearch(person: CompanyPersonSummary, query: string): boolean {\n const normalizedQuery = query.trim().toLowerCase()\n if (!normalizedQuery.length) return true\n const haystack = [\n person.displayName,\n person.jobTitle ?? '',\n person.primaryEmail ?? '',\n person.primaryPhone ?? '',\n person.department ?? '',\n ]\n .join(' ')\n .toLowerCase()\n return haystack.includes(normalizedQuery)\n}\n\nfunction sortCompanyPeople(\n items: CompanyPersonSummary[],\n sortMode: 'name-asc' | 'name-desc' | 'recent',\n): CompanyPersonSummary[] {\n return [...items].sort((left, right) => {\n if (sortMode === 'recent') {\n const leftTimestamp = Date.parse(left.linkedAt ?? left.createdAt ?? '') || 0\n const rightTimestamp = Date.parse(right.linkedAt ?? right.createdAt ?? '') || 0\n return rightTimestamp - leftTimestamp\n }\n const leftLabel = coerceDisplayName(left.displayName).trim().toLowerCase()\n const rightLabel = coerceDisplayName(right.displayName).trim().toLowerCase()\n if (sortMode === 'name-desc') return rightLabel.localeCompare(leftLabel)\n return leftLabel.localeCompare(rightLabel)\n })\n}\n\nexport function CompanyPeopleSection({\n companyId,\n companyName,\n initialPeople,\n addActionLabel,\n emptyLabel,\n emptyState,\n onPeopleChange,\n onActionChange,\n translator,\n onLoadingChange,\n onDataRefresh,\n runGuardedMutation,\n}: CompanyPeopleSectionProps) {\n const tHook = useT()\n const fallbackTranslator = React.useMemo<Translator>(\n () => createTranslatorWithFallback(tHook),\n [tHook],\n )\n const translate: Translator = translator ?? fallbackTranslator\n const [people, setPeople] = React.useState<CompanyPersonSummary[]>(initialPeople)\n const [removingId, setRemovingId] = React.useState<string | null>(null)\n const [linkDialogOpen, setLinkDialogOpen] = React.useState(false)\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n const [searchQuery, setSearchQuery] = React.useState('')\n const [sortMode, setSortMode] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [filtersOpen, setFiltersOpen] = React.useState(true)\n const [visiblePeople, setVisiblePeople] = React.useState<CompanyPersonSummary[]>([])\n const [listPage, setListPage] = React.useState(1)\n const [listTotalPages, setListTotalPages] = React.useState(1)\n const [listTotalCount, setListTotalCount] = React.useState(initialPeople.length)\n const [listLoading, setListLoading] = React.useState(true)\n const [starredIds, setStarredIds] = React.useState<Set<string>>(\n () => readVersionedIdSet(`om:starred-people:${companyId}`, STARRED_PEOPLE_STORAGE_VERSION),\n )\n const pendingPeopleChangeRef = React.useRef(false)\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const toggleStar = React.useCallback(\n (personId: string) => {\n setStarredIds((prev) => {\n const next = new Set(prev)\n if (next.has(personId)) next.delete(personId)\n else next.add(personId)\n writeVersionedIdSet(`om:starred-people:${companyId}`, STARRED_PEOPLE_STORAGE_VERSION, next)\n return next\n })\n },\n [companyId],\n )\n\n const displayedPeople = React.useMemo(\n () => (visiblePeople.length > 0 ? visiblePeople : people),\n [people, visiblePeople],\n )\n const totalLinkedPeople = listTotalCount > 0 ? listTotalCount : displayedPeople.length\n const decisionMakerNames = React.useMemo(\n () =>\n displayedPeople\n .filter((person) => starredIds.has(person.id))\n .map((person) => person.displayName),\n [displayedPeople, starredIds],\n )\n\n React.useEffect(() => {\n const action: SectionAction = {\n label: addActionLabel,\n onClick: () => {\n setCreateDialogOpen(true)\n },\n }\n onActionChange?.(action)\n return () => {\n onActionChange?.(null)\n }\n }, [addActionLabel, onActionChange])\n\n React.useEffect(() => {\n pendingPeopleChangeRef.current = false\n setPeople(initialPeople)\n }, [initialPeople])\n\n React.useEffect(() => {\n if (!pendingPeopleChangeRef.current) return\n pendingPeopleChangeRef.current = false\n onPeopleChange?.(people)\n }, [onPeopleChange, people])\n\n const loadVisiblePeople = React.useCallback(async () => {\n setListLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(listPage),\n pageSize: String(COMPANY_PEOPLE_PAGE_SIZE),\n sort: sortMode,\n })\n if (searchQuery.trim().length > 0) {\n params.set('search', searchQuery.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: CompanyPersonSummary[]\n page?: number\n total?: number\n totalPages?: number\n }>(\n `/api/customers/companies/${encodeURIComponent(companyId)}/people?${params.toString()}`,\n undefined,\n {\n errorMessage: translate(\n 'customers.companies.detail.people.loadError',\n 'Failed to load people.',\n ),\n },\n )\n const nextTotalCount = typeof payload.total === 'number' ? payload.total : 0\n setVisiblePeople(Array.isArray(payload.items) ? payload.items : [])\n setListPage(typeof payload.page === 'number' ? payload.page : listPage)\n setListTotalCount((current) =>\n searchQuery.trim().length > 0 ? Math.max(current, nextTotalCount) : nextTotalCount,\n )\n setListTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch {\n setVisiblePeople([])\n if (searchQuery.trim().length === 0) {\n setListTotalCount(0)\n }\n setListTotalPages(1)\n } finally {\n setListLoading(false)\n }\n }, [companyId, listPage, searchQuery, sortMode, translate])\n\n React.useEffect(() => {\n void loadVisiblePeople()\n }, [loadVisiblePeople])\n\n useAppEvent('customers.person_company_link.deleted', (event) => {\n const payload = event.payload as { companyEntityId?: string | null } | null | undefined\n if (payload && payload.companyEntityId === companyId) {\n void loadVisiblePeople()\n }\n }, [companyId, loadVisiblePeople])\n\n React.useEffect(() => {\n setListPage(1)\n }, [searchQuery, sortMode])\n\n const applyPeopleChange = React.useCallback(\n (updater: (current: CompanyPersonSummary[]) => CompanyPersonSummary[]) => {\n setPeople((current) => {\n const next = updater(current)\n if (next !== current) {\n pendingPeopleChangeRef.current = true\n }\n return next\n })\n },\n [],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n optionsById: Record<string, LinkEntityOption>\n }) => {\n if (!addedIds.length) return\n onLoadingChange?.(true)\n try {\n for (const personId of addedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ companyId }),\n },\n {\n errorMessage: translate(\n 'customers.companies.detail.people.linkError',\n 'Failed to link person to company.',\n ),\n },\n ),\n {\n personId,\n companyId,\n },\n )\n }\n const optimisticPeople: CompanyPersonSummary[] = addedIds\n .map((personId): CompanyPersonSummary | null => {\n const option = optionsById[personId]\n if (!option) return null\n return {\n id: option.id,\n displayName: option.label,\n primaryEmail: null,\n primaryPhone: null,\n jobTitle: option.subtitle ?? null,\n }\n })\n .filter((entry): entry is CompanyPersonSummary => entry !== null)\n if (optimisticPeople.length > 0) {\n applyPeopleChange((current) => mergeCompanyPeople([...current, ...optimisticPeople]))\n setListTotalCount((current) => current + optimisticPeople.length)\n }\n await loadVisiblePeople()\n flash(\n addedIds.length === 1\n ? translate(\n 'customers.companies.detail.people.linkSuccess',\n 'Person linked to company.',\n )\n : translate(\n 'customers.companies.detail.people.linkSuccessMultiple',\n '{{count}} people linked to company.',\n { count: String(addedIds.length) },\n ),\n 'success',\n )\n } catch (err) {\n try {\n await onDataRefresh?.()\n } catch {\n // preserve original linking error for the user\n }\n const message =\n err instanceof Error\n ? err.message\n : translate(\n 'customers.companies.detail.people.linkError',\n 'Failed to link person to company.',\n )\n flash(message, 'error')\n throw err\n } finally {\n onLoadingChange?.(false)\n }\n },\n [\n applyPeopleChange,\n companyId,\n loadVisiblePeople,\n onDataRefresh,\n onLoadingChange,\n runWriteMutation,\n translate,\n ],\n )\n\n const personLinkAdapter = React.useMemo(\n () =>\n createPersonLinkAdapter({\n dialogTitle: translate('customers.linking.person.dialogTitle', 'Link person'),\n dialogSubtitle: companyName\n ? translate(\n 'customers.linking.person.dialogSubtitleFor',\n 'Link an existing contact to {{name}}',\n { name: companyName },\n )\n : translate(\n 'customers.linking.person.dialogSubtitle',\n 'Link an existing contact to this company',\n ),\n sectionLabel: translate('customers.linking.person.sectionLabel', 'MATCHING CONTACTS'),\n searchPlaceholder: translate(\n 'customers.linking.person.searchPlaceholder',\n 'Search all people\u2026',\n ),\n searchEmptyHint: translate(\n 'customers.linking.person.searchEmpty',\n 'No matching people found.',\n ),\n selectedEmptyHint: translate(\n 'customers.linking.person.selectedEmpty',\n 'No people selected.',\n ),\n confirmButtonLabel: translate('customers.linking.person.confirmButton', 'Link person'),\n showLinkSettings: true,\n roleOptions: [\n { id: 'decision_maker', label: 'Decision maker' },\n { id: 'budget_holder', label: 'Budget holder' },\n { id: 'stakeholder', label: 'Stakeholder' },\n { id: 'contact', label: 'Contact' },\n ],\n excludeLinkedCompanyId: companyId,\n addNew: {\n title: translate('customers.linking.person.addNew', 'Add new contact'),\n subtitle: translate(\n 'customers.linking.person.addNewSubtitle',\n 'Company will be filled in automatically',\n ),\n render: ({ onCancel }) => (\n <CreatePersonDialog\n open\n onClose={onCancel}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n // CreatePersonDialog already created and linked the person to this company\n // via the companyEntityId payload field. Refresh the on-page list and close\n // both the nested and outer dialogs so the user can see the new entry.\n void loadVisiblePeople()\n void onDataRefresh?.()\n setLinkDialogOpen(false)\n onCancel()\n }}\n />\n ),\n },\n }),\n [companyId, companyName, loadVisiblePeople, onDataRefresh, runWriteMutation, translate],\n )\n\n const handleRemove = React.useCallback(\n async (personId: string) => {\n if (!personId || removingId) return\n setRemovingId(personId)\n onLoadingChange?.(true)\n try {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: translate(\n 'customers.companies.detail.people.removeError',\n 'Failed to unlink person from company.',\n ),\n },\n ),\n {\n personId,\n companyId,\n },\n )\n await loadVisiblePeople()\n flash(\n translate(\n 'customers.companies.detail.people.removeSuccess',\n 'Person unlinked from company.',\n ),\n 'success',\n )\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : translate(\n 'customers.companies.detail.people.removeError',\n 'Failed to unlink person from company.',\n )\n flash(message, 'error')\n } finally {\n setRemovingId(null)\n onLoadingChange?.(false)\n }\n },\n [\n companyId,\n loadVisiblePeople,\n onLoadingChange,\n removingId,\n runWriteMutation,\n translate,\n ],\n )\n\n const linkAction = (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setLinkDialogOpen(true)}\n >\n <Link2 className=\"mr-1.5 h-4 w-4\" />\n {translate(\n 'customers.companies.detail.people.linkAction',\n 'Link existing person',\n )}\n </Button>\n )\n const addPersonAction = (\n <Button type=\"button\" size=\"sm\" onClick={() => setCreateDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {addActionLabel}\n </Button>\n )\n\n if (!listLoading && totalLinkedPeople === 0) {\n return (\n <>\n <EmptyState\n icon={<Users className=\"h-10 w-10 text-muted-foreground\" />}\n title={emptyState.title}\n actionLabel={emptyState.actionLabel}\n onAction={() => setCreateDialogOpen(true)}\n >\n <p className=\"text-sm text-muted-foreground\">{emptyLabel}</p>\n <div className=\"mt-4\">{linkAction}</div>\n </EmptyState>\n <LinkEntityDialog\n open={linkDialogOpen}\n onOpenChange={setLinkDialogOpen}\n adapter={personLinkAdapter}\n initialSelectedIds={[]}\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n <CreatePersonDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n setCreateDialogOpen(false)\n void loadVisiblePeople()\n void onDataRefresh?.()\n }}\n />\n </>\n )\n }\n\n return (\n <>\n <div className=\"space-y-4\">\n <RolesSection\n entityType=\"company\"\n entityId={companyId}\n entityName={companyName ?? null}\n />\n\n <section className=\"rounded-lg border bg-card px-4 py-4 sm:px-5\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-base font-semibold\">\n {translate(\n 'customers.companies.detail.people.sectionTitle',\n 'People',\n )}\n </h3>\n <Badge\n variant=\"secondary\"\n className=\"rounded-full px-2 py-0 text-xs font-semibold\"\n >\n {totalLinkedPeople}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {translate(\n 'customers.companies.detail.people.sectionSubtitle',\n 'Employees and decision makers on the client side',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2 xl:justify-end\">\n {linkAction}\n {addPersonAction}\n </div>\n </div>\n\n {totalLinkedPeople > 0 ? (\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n {filtersOpen ? (\n <div className=\"min-w-0 flex-1\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n placeholder={translate(\n 'customers.companies.detail.people.searchPlaceholder',\n 'Search by name, role, email...',\n )}\n className=\"h-10 w-full rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-2 lg:justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setFiltersOpen((current) => !current)}\n className=\"h-10\"\n >\n <Filter className=\"mr-1.5 h-4 w-4\" />\n {translate(\n 'customers.companies.detail.people.filter',\n 'Filters',\n )}\n </Button>\n {filtersOpen ? (\n <select\n value={sortMode}\n onChange={(event) =>\n setSortMode(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 min-w-[11rem] rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {translate(\n 'customers.companies.detail.people.sortNameAsc',\n 'Sort: Name A-Z',\n )}\n </option>\n <option value=\"name-desc\">\n {translate(\n 'customers.companies.detail.people.sortNameDesc',\n 'Sort: Name Z-A',\n )}\n </option>\n <option value=\"recent\">\n {translate(\n 'customers.companies.detail.people.sortRecent',\n 'Sort: Recently linked',\n )}\n </option>\n </select>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {listLoading ? (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {translate('customers.companies.detail.people.loading', 'Loading people\u2026')}\n </p>\n ) : visiblePeople.length > 0 ? (\n <>\n <div\n className=\"grid items-start gap-4\"\n style={{\n gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 19.5rem), 1fr))',\n }}\n >\n {visiblePeople.map((person) => (\n <PersonCard\n key={person.id}\n person={person}\n isStarred={starredIds.has(person.id)}\n onToggleStar={toggleStar}\n onUnlink={handleRemove}\n />\n ))}\n </div>\n {listTotalPages > 1 ? (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n {translate(\n 'customers.companies.detail.people.pageSummary',\n 'Page {{page}} of {{total}}',\n {\n page: listPage,\n total: listTotalPages,\n },\n )}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setListPage((current) => Math.max(1, current - 1))}\n disabled={listPage <= 1}\n >\n {translate('customers.companies.detail.people.previous', 'Previous')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n setListPage((current) => Math.min(listTotalPages, current + 1))\n }\n disabled={listPage >= listTotalPages}\n >\n {translate('customers.companies.detail.people.next', 'Next')}\n </Button>\n </div>\n </div>\n ) : null}\n </>\n ) : totalLinkedPeople > 0 ? (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {translate(\n 'customers.companies.detail.people.noSearchResults',\n 'No people match your search.',\n )}\n </p>\n ) : null}\n </div>\n </section>\n\n <DecisionMakersFooter\n names={decisionMakerNames}\n onSendInvitation={() => {\n const starredEmails = displayedPeople\n .filter((person) => starredIds.has(person.id) && person.primaryEmail)\n .map((person) => person.primaryEmail!)\n if (starredEmails.length > 0) {\n window.open(`mailto:${starredEmails.join(',')}`, '_blank')\n }\n }}\n />\n </div>\n\n <LinkEntityDialog\n open={linkDialogOpen}\n onOpenChange={setLinkDialogOpen}\n adapter={personLinkAdapter}\n initialSelectedIds={[]}\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n\n <CreatePersonDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n setCreateDialogOpen(false)\n void loadVisiblePeople()\n void onDataRefresh?.()\n }}\n />\n </>\n )\n}\n\nexport default CompanyPeopleSection\n\nexport { mergeCompanyPeople, matchesCompanyPersonSearch, sortCompanyPeople, normalizeCompanyPerson }\n"],
5
- "mappings": ";AAweY,SAqGN,UArGM,KA+ER,YA/EQ;AAteZ,YAAY,WAAW;AACvB,SAAS,OAAO,OAAO,MAAM,cAAc;AAC3C,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,gBAAgB,4BAA4B;AACrD;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY;AACrB,SAAS,oCAAoC;AAC7C,SAAS,mBAAmB;AAE5B,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,wBAA+C;AACxD,SAAS,+BAA+B;AAsCxC,MAAM,2BAA2B;AACjC,MAAM,iCAAiC;AAEvC,SAAS,uBAAuB,QAA8D;AAC5F,QAAM,KAAK,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK;AACvD,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,cACJ,OAAO,OAAO,gBAAgB,YAAY,OAAO,YAAY,KAAK,EAAE,SAChE,OAAO,YAAY,KAAK,IACxB,OAAO,OAAO,iBAAiB,YAAY,OAAO,aAAa,KAAK,EAAE,SACpE,OAAO,aAAa,KAAK,IACzB;AACR,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,cACE,OAAO,OAAO,iBAAiB,WAC3B,OAAO,eACP,OAAO,OAAO,kBAAkB,WAC9B,OAAO,gBACP;AAAA,IACR,cACE,OAAO,OAAO,iBAAiB,WAC3B,OAAO,eACP,OAAO,OAAO,kBAAkB,WAC9B,OAAO,gBACP;AAAA,IACR,QACE,OAAO,OAAO,WAAW,WACrB,OAAO,SACP;AAAA,IACN,gBACE,OAAO,OAAO,mBAAmB,WAC7B,OAAO,iBACP,OAAO,OAAO,oBAAoB,WAChC,OAAO,kBACP;AAAA,IACR,UACE,OAAO,OAAO,aAAa,WACvB,OAAO,WACP,OAAO,OAAO,cAAc,WAC1B,OAAO,YACP;AAAA,IACR,YACE,OAAO,OAAO,eAAe,WACzB,OAAO,aACP;AAAA,IACN,WACE,OAAO,OAAO,cAAc,WACxB,OAAO,YACP,OAAO,OAAO,eAAe,WAC3B,OAAO,aACP;AAAA,IACR,gBACE,OAAO,OAAO,mBAAmB,WAC7B,OAAO,iBACP,OAAO,OAAO,oBAAoB,WAChC,OAAO,kBACP;AAAA,IACR,aACE,OAAO,OAAO,gBAAgB,WAC1B,OAAO,cACP;AAAA,IACN,QACE,OAAO,OAAO,WAAW,WACrB,OAAO,SACP;AAAA,IACN,UACE,OAAO,OAAO,aAAa,WACvB,OAAO,WACP,OAAO,OAAO,cAAc,WAC1B,OAAO,YACP;AAAA,EACV;AACF;AAEA,SAAS,mBAAmB,OAAuD;AACjF,QAAM,SAAS,oBAAI,IAAkC;AACrD,QAAM,QAAQ,CAAC,SAAS,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC;AACjD,SAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AACnC;AAEA,SAAS,2BAA2B,QAA8B,OAAwB;AACxF,QAAM,kBAAkB,MAAM,KAAK,EAAE,YAAY;AACjD,MAAI,CAAC,gBAAgB,OAAQ,QAAO;AACpC,QAAM,WAAW;AAAA,IACf,OAAO;AAAA,IACP,OAAO,YAAY;AAAA,IACnB,OAAO,gBAAgB;AAAA,IACvB,OAAO,gBAAgB;AAAA,IACvB,OAAO,cAAc;AAAA,EACvB,EACG,KAAK,GAAG,EACR,YAAY;AACf,SAAO,SAAS,SAAS,eAAe;AAC1C;AAEA,SAAS,kBACP,OACA,UACwB;AACxB,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU;AACtC,QAAI,aAAa,UAAU;AACzB,YAAM,gBAAgB,KAAK,MAAM,KAAK,YAAY,KAAK,aAAa,EAAE,KAAK;AAC3E,YAAM,iBAAiB,KAAK,MAAM,MAAM,YAAY,MAAM,aAAa,EAAE,KAAK;AAC9E,aAAO,iBAAiB;AAAA,IAC1B;AACA,UAAM,YAAY,kBAAkB,KAAK,WAAW,EAAE,KAAK,EAAE,YAAY;AACzE,UAAM,aAAa,kBAAkB,MAAM,WAAW,EAAE,KAAK,EAAE,YAAY;AAC3E,QAAI,aAAa,YAAa,QAAO,WAAW,cAAc,SAAS;AACvE,WAAO,UAAU,cAAc,UAAU;AAAA,EAC3C,CAAC;AACH;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,QAAQ,KAAK;AACnB,QAAM,qBAAqB,MAAM;AAAA,IAC/B,MAAM,6BAA6B,KAAK;AAAA,IACxC,CAAC,KAAK;AAAA,EACR;AACA,QAAM,YAAwB,cAAc;AAC5C,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAiC,aAAa;AAChF,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAwB,IAAI;AACtE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE;AACvD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAA8C,UAAU;AAC9F,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,IAAI;AACzD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAiC,CAAC,CAAC;AACnF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,CAAC;AAChD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,CAAC;AAC5D,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,cAAc,MAAM;AAC/E,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,IAAI;AACzD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM;AAAA,IACxC,MAAM,mBAAmB,qBAAqB,SAAS,IAAI,8BAA8B;AAAA,EAC3F;AACA,QAAM,yBAAyB,MAAM,OAAO,KAAK;AAEjD,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OACE,WACA,oBACe;AACf,UAAI,CAAC,oBAAoB;AACvB,eAAO,UAAU;AAAA,MACnB;AACA,aAAO,mBAAmB,WAAW,eAAe;AAAA,IACtD;AAAA,IACA,CAAC,kBAAkB;AAAA,EACrB;AAEA,QAAM,aAAa,MAAM;AAAA,IACvB,CAAC,aAAqB;AACpB,oBAAc,CAAC,SAAS;AACtB,cAAM,OAAO,IAAI,IAAI,IAAI;AACzB,YAAI,KAAK,IAAI,QAAQ,EAAG,MAAK,OAAO,QAAQ;AAAA,YACvC,MAAK,IAAI,QAAQ;AACtB,4BAAoB,qBAAqB,SAAS,IAAI,gCAAgC,IAAI;AAC1F,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAO,cAAc,SAAS,IAAI,gBAAgB;AAAA,IAClD,CAAC,QAAQ,aAAa;AAAA,EACxB;AACA,QAAM,oBAAoB,iBAAiB,IAAI,iBAAiB,gBAAgB;AAChF,QAAM,qBAAqB,MAAM;AAAA,IAC/B,MACE,gBACG,OAAO,CAAC,WAAW,WAAW,IAAI,OAAO,EAAE,CAAC,EAC5C,IAAI,CAAC,WAAW,OAAO,WAAW;AAAA,IACvC,CAAC,iBAAiB,UAAU;AAAA,EAC9B;AAEA,QAAM,UAAU,MAAM;AACpB,UAAM,SAAwB;AAAA,MAC5B,OAAO;AAAA,MACP,SAAS,MAAM;AACb,4BAAoB,IAAI;AAAA,MAC1B;AAAA,IACF;AACA,qBAAiB,MAAM;AACvB,WAAO,MAAM;AACX,uBAAiB,IAAI;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,gBAAgB,cAAc,CAAC;AAEnC,QAAM,UAAU,MAAM;AACpB,2BAAuB,UAAU;AACjC,cAAU,aAAa;AAAA,EACzB,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,uBAAuB,QAAS;AACrC,2BAAuB,UAAU;AACjC,qBAAiB,MAAM;AAAA,EACzB,GAAG,CAAC,gBAAgB,MAAM,CAAC;AAE3B,QAAM,oBAAoB,MAAM,YAAY,YAAY;AACtD,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,MAAM,OAAO,QAAQ;AAAA,QACrB,UAAU,OAAO,wBAAwB;AAAA,QACzC,MAAM;AAAA,MACR,CAAC;AACD,UAAI,YAAY,KAAK,EAAE,SAAS,GAAG;AACjC,eAAO,IAAI,UAAU,YAAY,KAAK,CAAC;AAAA,MACzC;AACA,YAAM,UAAU,MAAM;AAAA,QAMpB,4BAA4B,mBAAmB,SAAS,CAAC,WAAW,OAAO,SAAS,CAAC;AAAA,QACrF;AAAA,QACA;AAAA,UACE,cAAc;AAAA,YACZ;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,YAAM,iBAAiB,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAC3E,uBAAiB,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAClE,kBAAY,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO,QAAQ;AACtE;AAAA,QAAkB,CAAC,YACjB,YAAY,KAAK,EAAE,SAAS,IAAI,KAAK,IAAI,SAAS,cAAc,IAAI;AAAA,MACtE;AACA,wBAAkB,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa,CAAC;AAAA,IACnF,QAAQ;AACN,uBAAiB,CAAC,CAAC;AACnB,UAAI,YAAY,KAAK,EAAE,WAAW,GAAG;AACnC,0BAAkB,CAAC;AAAA,MACrB;AACA,wBAAkB,CAAC;AAAA,IACrB,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,WAAW,UAAU,aAAa,UAAU,SAAS,CAAC;AAE1D,QAAM,UAAU,MAAM;AACpB,SAAK,kBAAkB;AAAA,EACzB,GAAG,CAAC,iBAAiB,CAAC;AAEtB,cAAY,yCAAyC,CAAC,UAAU;AAC9D,UAAM,UAAU,MAAM;AACtB,QAAI,WAAW,QAAQ,oBAAoB,WAAW;AACpD,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,WAAW,iBAAiB,CAAC;AAEjC,QAAM,UAAU,MAAM;AACpB,gBAAY,CAAC;AAAA,EACf,GAAG,CAAC,aAAa,QAAQ,CAAC;AAE1B,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,YAAyE;AACxE,gBAAU,CAAC,YAAY;AACrB,cAAM,OAAO,QAAQ,OAAO;AAC5B,YAAI,SAAS,SAAS;AACpB,iCAAuB,UAAU;AAAA,QACnC;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF,MAIM;AACJ,UAAI,CAAC,SAAS,OAAQ;AACtB,wBAAkB,IAAI;AACtB,UAAI;AACF,mBAAW,YAAY,UAAU;AAC/B,gBAAM;AAAA,YACJ;AAAA;AAAA,cAEE;AAAA,gBACE,yBAAyB,mBAAmB,QAAQ,CAAC;AAAA,gBACrD;AAAA,kBACE,QAAQ;AAAA,kBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,kBAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC;AAAA,gBACpC;AAAA,gBACA;AAAA,kBACE,cAAc;AAAA,oBACZ;AAAA,oBACA;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA;AAAA,YACF;AAAA,cACE;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,cAAM,mBAA2C,SAC9C,IAAI,CAAC,aAA0C;AAC9C,gBAAM,SAAS,YAAY,QAAQ;AACnC,cAAI,CAAC,OAAQ,QAAO;AACpB,iBAAO;AAAA,YACL,IAAI,OAAO;AAAA,YACX,aAAa,OAAO;AAAA,YACpB,cAAc;AAAA,YACd,cAAc;AAAA,YACd,UAAU,OAAO,YAAY;AAAA,UAC/B;AAAA,QACF,CAAC,EACA,OAAO,CAAC,UAAyC,UAAU,IAAI;AAClE,YAAI,iBAAiB,SAAS,GAAG;AAC/B,4BAAkB,CAAC,YAAY,mBAAmB,CAAC,GAAG,SAAS,GAAG,gBAAgB,CAAC,CAAC;AACpF,4BAAkB,CAAC,YAAY,UAAU,iBAAiB,MAAM;AAAA,QAClE;AACA,cAAM,kBAAkB;AACxB;AAAA,UACE,SAAS,WAAW,IAChB;AAAA,YACE;AAAA,YACA;AAAA,UACF,IACA;AAAA,YACE;AAAA,YACA;AAAA,YACA,EAAE,OAAO,OAAO,SAAS,MAAM,EAAE;AAAA,UACnC;AAAA,UACJ;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAI;AACF,gBAAM,gBAAgB;AAAA,QACxB,QAAQ;AAAA,QAER;AACA,cAAM,UACJ,eAAe,QACX,IAAI,UACJ;AAAA,UACE;AAAA,UACA;AAAA,QACF;AACN,cAAM,SAAS,OAAO;AACtB,cAAM;AAAA,MACR,UAAE;AACA,0BAAkB,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,MACE,wBAAwB;AAAA,MACtB,aAAa,UAAU,wCAAwC,aAAa;AAAA,MAC5E,gBAAgB,cACZ;AAAA,QACE;AAAA,QACA;AAAA,QACA,EAAE,MAAM,YAAY;AAAA,MACtB,IACA;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAAA,MACJ,cAAc,UAAU,yCAAyC,mBAAmB;AAAA,MACpF,mBAAmB;AAAA,QACjB;AAAA,QACA;AAAA,MACF;AAAA,MACA,iBAAiB;AAAA,QACf;AAAA,QACA;AAAA,MACF;AAAA,MACA,mBAAmB;AAAA,QACjB;AAAA,QACA;AAAA,MACF;AAAA,MACA,oBAAoB,UAAU,0CAA0C,aAAa;AAAA,MACrF,kBAAkB;AAAA,MAClB,aAAa;AAAA,QACX,EAAE,IAAI,kBAAkB,OAAO,iBAAiB;AAAA,QAChD,EAAE,IAAI,iBAAiB,OAAO,gBAAgB;AAAA,QAC9C,EAAE,IAAI,eAAe,OAAO,cAAc;AAAA,QAC1C,EAAE,IAAI,WAAW,OAAO,UAAU;AAAA,MACpC;AAAA,MACA,wBAAwB;AAAA,MACxB,QAAQ;AAAA,QACN,OAAO,UAAU,mCAAmC,iBAAiB;AAAA,QACrE,UAAU;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,QACA,QAAQ,CAAC,EAAE,SAAS,MAClB;AAAA,UAAC;AAAA;AAAA,YACC,MAAI;AAAA,YACJ,SAAS;AAAA,YACT;AAAA,YACA,aAAa,eAAe;AAAA,YAC5B,oBAAoB;AAAA,YACpB,iBAAiB,MAAM;AAIrB,mBAAK,kBAAkB;AACvB,mBAAK,gBAAgB;AACrB,gCAAkB,KAAK;AACvB,uBAAS;AAAA,YACX;AAAA;AAAA,QACF;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,IACH,CAAC,WAAW,aAAa,mBAAmB,eAAe,kBAAkB,SAAS;AAAA,EACxF;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,aAAqB;AAC1B,UAAI,CAAC,YAAY,WAAY;AAC7B,oBAAc,QAAQ;AACtB,wBAAkB,IAAI;AACtB,UAAI;AACF,cAAM;AAAA,UACJ;AAAA;AAAA,YAEE;AAAA,cACE,yBAAyB,mBAAmB,QAAQ,CAAC,cAAc,mBAAmB,SAAS,CAAC;AAAA,cAChG,EAAE,QAAQ,SAAS;AAAA,cACnB;AAAA,gBACE,cAAc;AAAA,kBACZ;AAAA,kBACA;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA;AAAA,UACF;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,cAAM,kBAAkB;AACxB;AAAA,UACE;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,UACJ,eAAe,QACX,IAAI,UACJ;AAAA,UACE;AAAA,UACA;AAAA,QACF;AACN,cAAM,SAAS,OAAO;AAAA,MACxB,UAAE;AACA,sBAAc,IAAI;AAClB,0BAAkB,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aACJ;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,SAAS,MAAM,kBAAkB,IAAI;AAAA,MAErC;AAAA,4BAAC,SAAM,WAAU,kBAAiB;AAAA,QACjC;AAAA,UACC;AAAA,UACA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEF,QAAM,kBACJ,qBAAC,UAAO,MAAK,UAAS,MAAK,MAAK,SAAS,MAAM,oBAAoB,IAAI,GACrE;AAAA,wBAAC,QAAK,WAAU,kBAAiB;AAAA,IAChC;AAAA,KACH;AAGF,MAAI,CAAC,eAAe,sBAAsB,GAAG;AAC3C,WACE,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,oBAAC,SAAM,WAAU,mCAAkC;AAAA,UACzD,OAAO,WAAW;AAAA,UAClB,aAAa,WAAW;AAAA,UACxB,UAAU,MAAM,oBAAoB,IAAI;AAAA,UAExC;AAAA,gCAAC,OAAE,WAAU,iCAAiC,sBAAW;AAAA,YACzD,oBAAC,SAAI,WAAU,QAAQ,sBAAW;AAAA;AAAA;AAAA,MACpC;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,cAAc;AAAA,UACd,SAAS;AAAA,UACT,oBAAoB,CAAC;AAAA,UACrB,WAAW;AAAA,UACX,oBAAoB;AAAA;AAAA,MACtB;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,SAAS,MAAM,oBAAoB,KAAK;AAAA,UACxC;AAAA,UACA,aAAa,eAAe;AAAA,UAC5B,oBAAoB;AAAA,UACpB,iBAAiB,MAAM;AACrB,gCAAoB,KAAK;AACzB,iBAAK,kBAAkB;AACvB,iBAAK,gBAAgB;AAAA,UACvB;AAAA;AAAA,MACF;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,iCACE;AAAA,yBAAC,SAAI,WAAU,aACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,YAAW;AAAA,UACX,UAAU;AAAA,UACV,YAAY,eAAe;AAAA;AAAA,MAC7B;AAAA,MAEA,oBAAC,aAAQ,WAAU,+CACjB,+BAAC,SAAI,WAAU,uBACb;AAAA,6BAAC,SAAI,WAAU,qEACb;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,iCAAC,SAAI,WAAU,2BACb;AAAA,kCAAC,QAAG,WAAU,2BACX;AAAA,gBACC;AAAA,gBACA;AAAA,cACF,GACF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,WAAU;AAAA,kBAET;AAAA;AAAA,cACH;AAAA,eACF;AAAA,YACA,oBAAC,OAAE,WAAU,iCACV;AAAA,cACC;AAAA,cACA;AAAA,YACF,GACF;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,oDACZ;AAAA;AAAA,YACA;AAAA,aACH;AAAA,WACF;AAAA,QAEC,oBAAoB,IACnB,qBAAC,SAAI,WAAU,mDACZ;AAAA,wBACC,oBAAC,SAAI,WAAU,kBACb;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,eAAe,MAAM,OAAO,KAAK;AAAA,cACtD,aAAa;AAAA,gBACX;AAAA,gBACA;AAAA,cACF;AAAA,cACA,WAAU;AAAA;AAAA,UACZ,GACF,IACE;AAAA,UACJ,qBAAC,SAAI,WAAU,oDACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,CAAC,YAAY,CAAC,OAAO;AAAA,gBACnD,WAAU;AAAA,gBAEV;AAAA,sCAAC,UAAO,WAAU,kBAAiB;AAAA,kBAClC;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF;AAAA;AAAA;AAAA,YACF;AAAA,YACC,cACC;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU,CAAC,UACT,YAAY,MAAM,OAAO,KAA4C;AAAA,gBAEvE,WAAU;AAAA,gBAEV;AAAA,sCAAC,YAAO,OAAM,YACX;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,kBACA,oBAAC,YAAO,OAAM,aACX;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,kBACA,oBAAC,YAAO,OAAM,UACX;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF,GACF;AAAA;AAAA;AAAA,YACF,IACE;AAAA,aACN;AAAA,WACF,IACE;AAAA,QAEH,cACC,oBAAC,OAAE,WAAU,kDACV,oBAAU,6CAA6C,sBAAiB,GAC3E,IACE,cAAc,SAAS,IACzB,iCACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,qBAAqB;AAAA,cACvB;AAAA,cAEC,wBAAc,IAAI,CAAC,WAClB;AAAA,gBAAC;AAAA;AAAA,kBAEC;AAAA,kBACA,WAAW,WAAW,IAAI,OAAO,EAAE;AAAA,kBACnC,cAAc;AAAA,kBACd,UAAU;AAAA;AAAA,gBAJL,OAAO;AAAA,cAKd,CACD;AAAA;AAAA,UACH;AAAA,UACC,iBAAiB,IAChB,qBAAC,SAAI,WAAU,kGACb;AAAA,gCAAC,UACE;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,OAAO;AAAA,cACT;AAAA,YACF,GACF;AAAA,YACA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,YAAY,CAAC,YAAY,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;AAAA,kBAChE,UAAU,YAAY;AAAA,kBAErB,oBAAU,8CAA8C,UAAU;AAAA;AAAA,cACrE;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MACP,YAAY,CAAC,YAAY,KAAK,IAAI,gBAAgB,UAAU,CAAC,CAAC;AAAA,kBAEhE,UAAU,YAAY;AAAA,kBAErB,oBAAU,0CAA0C,MAAM;AAAA;AAAA,cAC7D;AAAA,eACF;AAAA,aACF,IACE;AAAA,WACN,IACE,oBAAoB,IACtB,oBAAC,OAAE,WAAU,kDACV;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF,IACE;AAAA,SACN,GACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,kBAAkB,MAAM;AACtB,kBAAM,gBAAgB,gBACnB,OAAO,CAAC,WAAW,WAAW,IAAI,OAAO,EAAE,KAAK,OAAO,YAAY,EACnE,IAAI,CAAC,WAAW,OAAO,YAAa;AACvC,gBAAI,cAAc,SAAS,GAAG;AAC5B,qBAAO,KAAK,UAAU,cAAc,KAAK,GAAG,CAAC,IAAI,QAAQ;AAAA,YAC3D;AAAA,UACF;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,SAAS;AAAA,QACT,oBAAoB,CAAC;AAAA,QACrB,WAAW;AAAA,QACX,oBAAoB;AAAA;AAAA,IACtB;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,MAAM,oBAAoB,KAAK;AAAA,QACxC;AAAA,QACA,aAAa,eAAe;AAAA,QAC5B,oBAAoB;AAAA,QACpB,iBAAiB,MAAM;AACrB,8BAAoB,KAAK;AACzB,eAAK,kBAAkB;AACvB,eAAK,gBAAgB;AAAA,QACvB;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,IAAO,+BAAQ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Users, Link2, Plus, Filter } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport {\n readVersionedIdSet,\n writeVersionedIdSet,\n} from '@open-mercato/shared/lib/browser/versionedPreference'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\nimport type { SectionAction, TabEmptyStateConfig, Translator } from './types'\nimport { CreatePersonDialog } from './CreatePersonDialog'\nimport { PersonCard } from './PersonCard'\nimport { coerceDisplayName } from '../../lib/displayName'\nimport { DecisionMakersFooter } from './DecisionMakersFooter'\nimport { RolesSection } from './RolesSection'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createPersonLinkAdapter } from '../linking/adapters/personAdapter'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type CompanyPersonSummary = {\n id: string\n displayName: string\n primaryEmail?: string | null\n primaryPhone?: string | null\n status?: string | null\n lifecycleStage?: string | null\n jobTitle?: string | null\n department?: string | null\n createdAt?: string | null\n organizationId?: string | null\n temperature?: string | null\n source?: string | null\n linkedAt?: string | null\n}\n\nexport type CompanyPeopleSectionProps = {\n companyId: string\n companyName?: string\n initialPeople: CompanyPersonSummary[]\n addActionLabel: string\n emptyLabel: string\n emptyState: TabEmptyStateConfig\n onPeopleChange?: (next: CompanyPersonSummary[]) => void\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst COMPANY_PEOPLE_PAGE_SIZE = 20\nconst STARRED_PEOPLE_STORAGE_VERSION = 1\n\nfunction normalizeCompanyPerson(record: Record<string, unknown>): CompanyPersonSummary | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n if (!displayName) return null\n return {\n id,\n displayName,\n primaryEmail:\n typeof record.primaryEmail === 'string'\n ? record.primaryEmail\n : typeof record.primary_email === 'string'\n ? record.primary_email\n : null,\n primaryPhone:\n typeof record.primaryPhone === 'string'\n ? record.primaryPhone\n : typeof record.primary_phone === 'string'\n ? record.primary_phone\n : null,\n status:\n typeof record.status === 'string'\n ? record.status\n : null,\n lifecycleStage:\n typeof record.lifecycleStage === 'string'\n ? record.lifecycleStage\n : typeof record.lifecycle_stage === 'string'\n ? record.lifecycle_stage\n : null,\n jobTitle:\n typeof record.jobTitle === 'string'\n ? record.jobTitle\n : typeof record.job_title === 'string'\n ? record.job_title\n : null,\n department:\n typeof record.department === 'string'\n ? record.department\n : null,\n createdAt:\n typeof record.createdAt === 'string'\n ? record.createdAt\n : typeof record.created_at === 'string'\n ? record.created_at\n : null,\n organizationId:\n typeof record.organizationId === 'string'\n ? record.organizationId\n : typeof record.organization_id === 'string'\n ? record.organization_id\n : null,\n temperature:\n typeof record.temperature === 'string'\n ? record.temperature\n : null,\n source:\n typeof record.source === 'string'\n ? record.source\n : null,\n linkedAt:\n typeof record.linkedAt === 'string'\n ? record.linkedAt\n : typeof record.linked_at === 'string'\n ? record.linked_at\n : null,\n }\n}\n\nfunction mergeCompanyPeople(items: CompanyPersonSummary[]): CompanyPersonSummary[] {\n const merged = new Map<string, CompanyPersonSummary>()\n items.forEach((item) => merged.set(item.id, item))\n return Array.from(merged.values())\n}\n\nfunction matchesCompanyPersonSearch(person: CompanyPersonSummary, query: string): boolean {\n const normalizedQuery = query.trim().toLowerCase()\n if (!normalizedQuery.length) return true\n const haystack = [\n person.displayName,\n person.jobTitle ?? '',\n person.primaryEmail ?? '',\n person.primaryPhone ?? '',\n person.department ?? '',\n ]\n .join(' ')\n .toLowerCase()\n return haystack.includes(normalizedQuery)\n}\n\nfunction sortCompanyPeople(\n items: CompanyPersonSummary[],\n sortMode: 'name-asc' | 'name-desc' | 'recent',\n): CompanyPersonSummary[] {\n return [...items].sort((left, right) => {\n if (sortMode === 'recent') {\n const leftTimestamp = Date.parse(left.linkedAt ?? left.createdAt ?? '') || 0\n const rightTimestamp = Date.parse(right.linkedAt ?? right.createdAt ?? '') || 0\n return rightTimestamp - leftTimestamp\n }\n const leftLabel = coerceDisplayName(left.displayName).trim().toLowerCase()\n const rightLabel = coerceDisplayName(right.displayName).trim().toLowerCase()\n if (sortMode === 'name-desc') return rightLabel.localeCompare(leftLabel)\n return leftLabel.localeCompare(rightLabel)\n })\n}\n\nexport function CompanyPeopleSection({\n companyId,\n companyName,\n initialPeople,\n addActionLabel,\n emptyLabel,\n emptyState,\n onPeopleChange,\n onActionChange,\n translator,\n onLoadingChange,\n onDataRefresh,\n runGuardedMutation,\n}: CompanyPeopleSectionProps) {\n const tHook = useT()\n const fallbackTranslator = React.useMemo<Translator>(\n () => createTranslatorWithFallback(tHook),\n [tHook],\n )\n const translate: Translator = translator ?? fallbackTranslator\n const [people, setPeople] = React.useState<CompanyPersonSummary[]>(initialPeople)\n const [removingId, setRemovingId] = React.useState<string | null>(null)\n const [linkDialogOpen, setLinkDialogOpen] = React.useState(false)\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n const [searchQuery, setSearchQuery] = React.useState('')\n const [sortMode, setSortMode] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [filtersOpen, setFiltersOpen] = React.useState(true)\n const [visiblePeople, setVisiblePeople] = React.useState<CompanyPersonSummary[]>([])\n const [listPage, setListPage] = React.useState(1)\n const [listTotalPages, setListTotalPages] = React.useState(1)\n const [listTotalCount, setListTotalCount] = React.useState(initialPeople.length)\n const [listLoading, setListLoading] = React.useState(true)\n const [starredIds, setStarredIds] = React.useState<Set<string>>(\n () => readVersionedIdSet(`om:starred-people:${companyId}`, STARRED_PEOPLE_STORAGE_VERSION),\n )\n const pendingPeopleChangeRef = React.useRef(false)\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const toggleStar = React.useCallback(\n (personId: string) => {\n setStarredIds((prev) => {\n const next = new Set(prev)\n if (next.has(personId)) next.delete(personId)\n else next.add(personId)\n writeVersionedIdSet(`om:starred-people:${companyId}`, STARRED_PEOPLE_STORAGE_VERSION, next)\n return next\n })\n },\n [companyId],\n )\n\n const displayedPeople = React.useMemo(\n () => (visiblePeople.length > 0 ? visiblePeople : people),\n [people, visiblePeople],\n )\n const totalLinkedPeople = listTotalCount > 0 ? listTotalCount : displayedPeople.length\n const decisionMakerNames = React.useMemo(\n () =>\n displayedPeople\n .filter((person) => starredIds.has(person.id))\n .map((person) => person.displayName),\n [displayedPeople, starredIds],\n )\n\n React.useEffect(() => {\n const action: SectionAction = {\n label: addActionLabel,\n onClick: () => {\n setCreateDialogOpen(true)\n },\n }\n onActionChange?.(action)\n return () => {\n onActionChange?.(null)\n }\n }, [addActionLabel, onActionChange])\n\n React.useEffect(() => {\n pendingPeopleChangeRef.current = false\n setPeople(initialPeople)\n }, [initialPeople])\n\n React.useEffect(() => {\n if (!pendingPeopleChangeRef.current) return\n pendingPeopleChangeRef.current = false\n onPeopleChange?.(people)\n }, [onPeopleChange, people])\n\n const loadVisiblePeople = React.useCallback(async () => {\n setListLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(listPage),\n pageSize: String(COMPANY_PEOPLE_PAGE_SIZE),\n sort: sortMode,\n })\n if (searchQuery.trim().length > 0) {\n params.set('search', searchQuery.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: CompanyPersonSummary[]\n page?: number\n total?: number\n totalPages?: number\n }>(\n `/api/customers/companies/${encodeURIComponent(companyId)}/people?${params.toString()}`,\n undefined,\n {\n errorMessage: translate(\n 'customers.companies.detail.people.loadError',\n 'Failed to load people.',\n ),\n },\n )\n const nextTotalCount = typeof payload.total === 'number' ? payload.total : 0\n setVisiblePeople(Array.isArray(payload.items) ? payload.items : [])\n setListPage(typeof payload.page === 'number' ? payload.page : listPage)\n setListTotalCount((current) =>\n searchQuery.trim().length > 0 ? Math.max(current, nextTotalCount) : nextTotalCount,\n )\n setListTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch {\n setVisiblePeople([])\n if (searchQuery.trim().length === 0) {\n setListTotalCount(0)\n }\n setListTotalPages(1)\n } finally {\n setListLoading(false)\n }\n }, [companyId, listPage, searchQuery, sortMode, translate])\n\n React.useEffect(() => {\n void loadVisiblePeople()\n }, [loadVisiblePeople])\n\n const reloadOnCompanyDetach = React.useCallback((event: AppEventPayload) => {\n const payload = event.payload as { companyEntityId?: string | null } | null | undefined\n if (payload && payload.companyEntityId === companyId) {\n void loadVisiblePeople()\n }\n }, [companyId, loadVisiblePeople])\n\n useAppEvent('customers.person_company_link.deleted', reloadOnCompanyDetach, [reloadOnCompanyDetach])\n // Legacy profile-only assignments have no link row, so their detach broadcasts this sibling\n // event instead of `customers.person_company_link.deleted` (#5114). Without it, other viewers\n // of the same company keep listing a person who is already gone.\n useAppEvent('customers.person.company_assignment.detached', reloadOnCompanyDetach, [reloadOnCompanyDetach])\n\n React.useEffect(() => {\n setListPage(1)\n }, [searchQuery, sortMode])\n\n const applyPeopleChange = React.useCallback(\n (updater: (current: CompanyPersonSummary[]) => CompanyPersonSummary[]) => {\n setPeople((current) => {\n const next = updater(current)\n if (next !== current) {\n pendingPeopleChangeRef.current = true\n }\n return next\n })\n },\n [],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n optionsById: Record<string, LinkEntityOption>\n }) => {\n if (!addedIds.length) return\n onLoadingChange?.(true)\n try {\n for (const personId of addedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ companyId }),\n },\n {\n errorMessage: translate(\n 'customers.companies.detail.people.linkError',\n 'Failed to link person to company.',\n ),\n },\n ),\n {\n personId,\n companyId,\n },\n )\n }\n const optimisticPeople: CompanyPersonSummary[] = addedIds\n .map((personId): CompanyPersonSummary | null => {\n const option = optionsById[personId]\n if (!option) return null\n return {\n id: option.id,\n displayName: option.label,\n primaryEmail: null,\n primaryPhone: null,\n jobTitle: option.subtitle ?? null,\n }\n })\n .filter((entry): entry is CompanyPersonSummary => entry !== null)\n if (optimisticPeople.length > 0) {\n applyPeopleChange((current) => mergeCompanyPeople([...current, ...optimisticPeople]))\n setListTotalCount((current) => current + optimisticPeople.length)\n }\n await loadVisiblePeople()\n flash(\n addedIds.length === 1\n ? translate(\n 'customers.companies.detail.people.linkSuccess',\n 'Person linked to company.',\n )\n : translate(\n 'customers.companies.detail.people.linkSuccessMultiple',\n '{{count}} people linked to company.',\n { count: String(addedIds.length) },\n ),\n 'success',\n )\n } catch (err) {\n try {\n await onDataRefresh?.()\n } catch {\n // preserve original linking error for the user\n }\n const message =\n err instanceof Error\n ? err.message\n : translate(\n 'customers.companies.detail.people.linkError',\n 'Failed to link person to company.',\n )\n flash(message, 'error')\n throw err\n } finally {\n onLoadingChange?.(false)\n }\n },\n [\n applyPeopleChange,\n companyId,\n loadVisiblePeople,\n onDataRefresh,\n onLoadingChange,\n runWriteMutation,\n translate,\n ],\n )\n\n const personLinkAdapter = React.useMemo(\n () =>\n createPersonLinkAdapter({\n dialogTitle: translate('customers.linking.person.dialogTitle', 'Link person'),\n dialogSubtitle: companyName\n ? translate(\n 'customers.linking.person.dialogSubtitleFor',\n 'Link an existing contact to {{name}}',\n { name: companyName },\n )\n : translate(\n 'customers.linking.person.dialogSubtitle',\n 'Link an existing contact to this company',\n ),\n sectionLabel: translate('customers.linking.person.sectionLabel', 'MATCHING CONTACTS'),\n searchPlaceholder: translate(\n 'customers.linking.person.searchPlaceholder',\n 'Search all people\u2026',\n ),\n searchEmptyHint: translate(\n 'customers.linking.person.searchEmpty',\n 'No matching people found.',\n ),\n selectedEmptyHint: translate(\n 'customers.linking.person.selectedEmpty',\n 'No people selected.',\n ),\n confirmButtonLabel: translate('customers.linking.person.confirmButton', 'Link person'),\n showLinkSettings: true,\n roleOptions: [\n { id: 'decision_maker', label: 'Decision maker' },\n { id: 'budget_holder', label: 'Budget holder' },\n { id: 'stakeholder', label: 'Stakeholder' },\n { id: 'contact', label: 'Contact' },\n ],\n excludeLinkedCompanyId: companyId,\n addNew: {\n title: translate('customers.linking.person.addNew', 'Add new contact'),\n subtitle: translate(\n 'customers.linking.person.addNewSubtitle',\n 'Company will be filled in automatically',\n ),\n render: ({ onCancel }) => (\n <CreatePersonDialog\n open\n onClose={onCancel}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n // CreatePersonDialog already created and linked the person to this company\n // via the companyEntityId payload field. Refresh the on-page list and close\n // both the nested and outer dialogs so the user can see the new entry.\n void loadVisiblePeople()\n void onDataRefresh?.()\n setLinkDialogOpen(false)\n onCancel()\n }}\n />\n ),\n },\n }),\n [companyId, companyName, loadVisiblePeople, onDataRefresh, runWriteMutation, translate],\n )\n\n const handleRemove = React.useCallback(\n async (personId: string) => {\n if (!personId || removingId) return\n setRemovingId(personId)\n onLoadingChange?.(true)\n try {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: translate(\n 'customers.companies.detail.people.removeError',\n 'Failed to unlink person from company.',\n ),\n },\n ),\n {\n personId,\n companyId,\n },\n )\n await loadVisiblePeople()\n flash(\n translate(\n 'customers.companies.detail.people.removeSuccess',\n 'Person unlinked from company.',\n ),\n 'success',\n )\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : translate(\n 'customers.companies.detail.people.removeError',\n 'Failed to unlink person from company.',\n )\n flash(message, 'error')\n } finally {\n setRemovingId(null)\n onLoadingChange?.(false)\n }\n },\n [\n companyId,\n loadVisiblePeople,\n onLoadingChange,\n removingId,\n runWriteMutation,\n translate,\n ],\n )\n\n const linkAction = (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setLinkDialogOpen(true)}\n >\n <Link2 className=\"mr-1.5 h-4 w-4\" />\n {translate(\n 'customers.companies.detail.people.linkAction',\n 'Link existing person',\n )}\n </Button>\n )\n const addPersonAction = (\n <Button type=\"button\" size=\"sm\" onClick={() => setCreateDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {addActionLabel}\n </Button>\n )\n\n if (!listLoading && totalLinkedPeople === 0) {\n return (\n <>\n <EmptyState\n icon={<Users className=\"h-10 w-10 text-muted-foreground\" />}\n title={emptyState.title}\n actionLabel={emptyState.actionLabel}\n onAction={() => setCreateDialogOpen(true)}\n >\n <p className=\"text-sm text-muted-foreground\">{emptyLabel}</p>\n <div className=\"mt-4\">{linkAction}</div>\n </EmptyState>\n <LinkEntityDialog\n open={linkDialogOpen}\n onOpenChange={setLinkDialogOpen}\n adapter={personLinkAdapter}\n initialSelectedIds={[]}\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n <CreatePersonDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n setCreateDialogOpen(false)\n void loadVisiblePeople()\n void onDataRefresh?.()\n }}\n />\n </>\n )\n }\n\n return (\n <>\n <div className=\"space-y-4\">\n <RolesSection\n entityType=\"company\"\n entityId={companyId}\n entityName={companyName ?? null}\n />\n\n <section className=\"rounded-lg border bg-card px-4 py-4 sm:px-5\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-base font-semibold\">\n {translate(\n 'customers.companies.detail.people.sectionTitle',\n 'People',\n )}\n </h3>\n <Badge\n variant=\"secondary\"\n className=\"rounded-full px-2 py-0 text-xs font-semibold\"\n >\n {totalLinkedPeople}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {translate(\n 'customers.companies.detail.people.sectionSubtitle',\n 'Employees and decision makers on the client side',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2 xl:justify-end\">\n {linkAction}\n {addPersonAction}\n </div>\n </div>\n\n {totalLinkedPeople > 0 ? (\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n {filtersOpen ? (\n <div className=\"min-w-0 flex-1\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n placeholder={translate(\n 'customers.companies.detail.people.searchPlaceholder',\n 'Search by name, role, email...',\n )}\n className=\"h-10 w-full rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-2 lg:justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setFiltersOpen((current) => !current)}\n className=\"h-10\"\n >\n <Filter className=\"mr-1.5 h-4 w-4\" />\n {translate(\n 'customers.companies.detail.people.filter',\n 'Filters',\n )}\n </Button>\n {filtersOpen ? (\n <select\n value={sortMode}\n onChange={(event) =>\n setSortMode(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 min-w-[11rem] rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {translate(\n 'customers.companies.detail.people.sortNameAsc',\n 'Sort: Name A-Z',\n )}\n </option>\n <option value=\"name-desc\">\n {translate(\n 'customers.companies.detail.people.sortNameDesc',\n 'Sort: Name Z-A',\n )}\n </option>\n <option value=\"recent\">\n {translate(\n 'customers.companies.detail.people.sortRecent',\n 'Sort: Recently linked',\n )}\n </option>\n </select>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {listLoading ? (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {translate('customers.companies.detail.people.loading', 'Loading people\u2026')}\n </p>\n ) : visiblePeople.length > 0 ? (\n <>\n <div\n className=\"grid items-start gap-4\"\n style={{\n gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 19.5rem), 1fr))',\n }}\n >\n {visiblePeople.map((person) => (\n <PersonCard\n key={person.id}\n person={person}\n isStarred={starredIds.has(person.id)}\n onToggleStar={toggleStar}\n onUnlink={handleRemove}\n />\n ))}\n </div>\n {listTotalPages > 1 ? (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n {translate(\n 'customers.companies.detail.people.pageSummary',\n 'Page {{page}} of {{total}}',\n {\n page: listPage,\n total: listTotalPages,\n },\n )}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setListPage((current) => Math.max(1, current - 1))}\n disabled={listPage <= 1}\n >\n {translate('customers.companies.detail.people.previous', 'Previous')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n setListPage((current) => Math.min(listTotalPages, current + 1))\n }\n disabled={listPage >= listTotalPages}\n >\n {translate('customers.companies.detail.people.next', 'Next')}\n </Button>\n </div>\n </div>\n ) : null}\n </>\n ) : totalLinkedPeople > 0 ? (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {translate(\n 'customers.companies.detail.people.noSearchResults',\n 'No people match your search.',\n )}\n </p>\n ) : null}\n </div>\n </section>\n\n <DecisionMakersFooter\n names={decisionMakerNames}\n onSendInvitation={() => {\n const starredEmails = displayedPeople\n .filter((person) => starredIds.has(person.id) && person.primaryEmail)\n .map((person) => person.primaryEmail!)\n if (starredEmails.length > 0) {\n window.open(`mailto:${starredEmails.join(',')}`, '_blank')\n }\n }}\n />\n </div>\n\n <LinkEntityDialog\n open={linkDialogOpen}\n onOpenChange={setLinkDialogOpen}\n adapter={personLinkAdapter}\n initialSelectedIds={[]}\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n\n <CreatePersonDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n companyId={companyId}\n companyName={companyName ?? companyId}\n runGuardedMutation={runWriteMutation}\n onPersonCreated={() => {\n setCreateDialogOpen(false)\n void loadVisiblePeople()\n void onDataRefresh?.()\n }}\n />\n </>\n )\n}\n\nexport default CompanyPeopleSection\n\nexport { mergeCompanyPeople, matchesCompanyPersonSearch, sortCompanyPeople, normalizeCompanyPerson }\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -306,12 +306,14 @@ function PersonCompaniesSection({
306
306
  },
307
307
  [_personName, confirm, loadData, onChanged, personId, runWriteMutation, t, unlinkingId]
308
308
  );
309
- useAppEvent("customers.person_company_link.deleted", (event) => {
309
+ const reloadOnPersonDetach = React.useCallback((event) => {
310
310
  const payload = event.payload;
311
311
  if (payload && payload.personEntityId === personId) {
312
312
  void loadData({ showLoading: false });
313
313
  }
314
314
  }, [personId, loadData]);
315
+ useAppEvent("customers.person_company_link.deleted", reloadOnPersonDetach, [reloadOnPersonDetach]);
316
+ useAppEvent("customers.person.company_assignment.detached", reloadOnPersonDetach, [reloadOnPersonDetach]);
315
317
  return /* @__PURE__ */ jsxs(Fragment, { children: [
316
318
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
317
319
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-2xl border border-border/70 bg-card px-4 py-4 sm:flex-row sm:items-center sm:justify-between", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/PersonCompaniesSection.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ArrowLeft, ArrowRight, Building2, Link2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { CompanyCard, type EnrichedCompanyData } from './CompanyCard'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createCompanyLinkAdapter } from '../linking/adapters/companyAdapter'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype LinkedCompanySummary = {\n id: string\n displayName: string\n isPrimary: boolean\n}\n\ntype PersonCompaniesSectionProps = {\n personId: string\n personName: string\n initialLinkedCompanies?: LinkedCompanySummary[]\n onChanged?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst LINKED_PAGE_SIZE = 20\n\nfunction sameIdSet(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n const rightSet = new Set(right)\n return left.every((value) => rightSet.has(value))\n}\n\nfunction Pagination({\n page,\n totalPages,\n onPageChange,\n}: {\n page: number\n totalPages: number\n onPageChange: (page: number) => void\n}) {\n if (totalPages <= 1) return null\n return (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n Page {page} of {totalPages}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n >\n <ArrowLeft className=\"mr-1.5 size-3.5\" />\n Previous\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n >\n Next\n <ArrowRight className=\"ml-1.5 size-3.5\" />\n </Button>\n </div>\n </div>\n )\n}\n\nexport function PersonCompaniesSection({\n personId,\n personName: _personName,\n initialLinkedCompanies = [],\n onChanged,\n runGuardedMutation,\n}: PersonCompaniesSectionProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [items, setItems] = React.useState<EnrichedCompanyData[]>([])\n const [loading, setLoading] = React.useState(true)\n const [unlinkingId, setUnlinkingId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sort, setSort] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [page, setPage] = React.useState(1)\n const [totalPages, setTotalPages] = React.useState(1)\n const [linkedCompanies, setLinkedCompanies] = React.useState<LinkedCompanySummary[]>(\n initialLinkedCompanies,\n )\n const [dialogOpen, setDialogOpen] = React.useState(false)\n\n const { data: statusDict } = useCustomerDictionary('statuses')\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages')\n const { data: temperatureDict } = useCustomerDictionary('temperature')\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters')\n const { data: roleDict } = useCustomerDictionary('person-company-roles')\n\n React.useEffect(() => {\n setLinkedCompanies(initialLinkedCompanies)\n }, [initialLinkedCompanies])\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const loadData = React.useCallback(\n async (options?: { showLoading?: boolean }) => {\n const showLoading = options?.showLoading ?? true\n if (showLoading) setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINKED_PAGE_SIZE),\n sort,\n })\n if (search.trim().length > 0) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: EnrichedCompanyData[]\n totalPages?: number\n }>(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/enriched?${params.toString()}`,\n { cache: 'no-store' },\n )\n const nextItems = Array.isArray(payload?.items) ? payload.items : []\n setItems(nextItems)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('customers.people.detail.companies.loadError', 'Failed to load companies.')\n flash(message, 'error')\n setItems([])\n setTotalPages(1)\n } finally {\n if (showLoading) setLoading(false)\n }\n },\n [page, personId, search, sort, t],\n )\n\n React.useEffect(() => {\n void loadData()\n }, [loadData])\n\n React.useEffect(() => {\n setPage(1)\n }, [search, sort])\n\n const linkedIds = React.useMemo(\n () => linkedCompanies.map((entry) => entry.id),\n [linkedCompanies],\n )\n const linkedPrimaryId = React.useMemo(\n () =>\n linkedCompanies.find((entry) => entry.isPrimary)?.id ??\n linkedCompanies[0]?.id ??\n null,\n [linkedCompanies],\n )\n\n const companyLinkAdapter = React.useMemo(\n () =>\n createCompanyLinkAdapter({\n dialogTitle: t('customers.linking.company.dialogTitle', 'Link company'),\n dialogSubtitle: _personName\n ? t('customers.linking.company.dialogSubtitleFor', 'Link an existing company to {{name}}', {\n name: _personName,\n })\n : t('customers.linking.company.dialogSubtitle', 'Link an existing company to this person'),\n sectionLabel: t('customers.linking.company.sectionLabel', 'MATCHING COMPANIES'),\n searchPlaceholder: t(\n 'customers.linking.company.searchPlaceholder',\n 'Search all companies\u2026',\n ),\n searchEmptyHint: t(\n 'customers.linking.company.searchEmpty',\n 'No matching companies found.',\n ),\n selectedEmptyHint: t(\n 'customers.linking.company.selectedEmpty',\n 'No companies selected.',\n ),\n confirmButtonLabel: t('customers.linking.company.confirmButton', 'Link company'),\n excludeLinkedPersonId: personId,\n }),\n [_personName, personId, t],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n removedIds,\n nextSelectedIds,\n primaryId,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n nextSelectedIds: string[]\n primaryId?: string | null\n optionsById: Record<string, LinkEntityOption>\n }) => {\n const currentPrimaryId = linkedPrimaryId\n const nextPrimaryId = nextSelectedIds.length\n ? primaryId && nextSelectedIds.includes(primaryId)\n ? primaryId\n : nextSelectedIds[0]\n : null\n\n try {\n for (const companyId of removedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n ),\n { companyId, personId, operation: 'removePersonCompanyLink' },\n )\n }\n\n for (const companyId of addedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n companyId,\n isPrimary: nextPrimaryId === companyId,\n }),\n },\n ),\n { companyId, personId, operation: 'addPersonCompanyLink' },\n )\n }\n\n if (\n nextPrimaryId &&\n nextPrimaryId !== currentPrimaryId &&\n !addedIds.includes(nextPrimaryId)\n ) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link set-primary (add/remove)\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(nextPrimaryId)}`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isPrimary: true }),\n },\n ),\n { companyId: nextPrimaryId, personId, operation: 'setPrimaryPersonCompanyLink' },\n )\n }\n\n const nextLinkedCompanies: LinkedCompanySummary[] = nextSelectedIds.map((id) => {\n const option = optionsById[id]\n const fromCurrent = linkedCompanies.find((entry) => entry.id === id)\n return {\n id,\n displayName: option?.label ?? fromCurrent?.displayName ?? id,\n isPrimary: id === nextPrimaryId,\n }\n })\n setLinkedCompanies(nextLinkedCompanies)\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.manageSuccess', 'Linked companies updated.'),\n 'success',\n )\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.manageError',\n 'Failed to update linked companies.',\n )\n flash(message, 'error')\n throw error\n }\n },\n [linkedCompanies, linkedPrimaryId, loadData, onChanged, personId, runWriteMutation, t],\n )\n\n const handleUnlink = React.useCallback(\n async (companyId: string, displayName: string) => {\n if (!companyId || unlinkingId) return\n const confirmed = await confirm({\n title: t('customers.people.detail.companies.unlinkConfirmTitle', 'Unlink company'),\n description: t(\n 'customers.people.detail.companies.unlinkConfirm',\n 'Unlink {{company}} from {{person}}?',\n { company: displayName, person: _personName },\n ),\n confirmText: t('customers.people.detail.companies.unlinkAction', 'Unlink'),\n cancelText: t('customers.linking.actions.cancel', 'Cancel'),\n })\n if (!confirmed) return\n setUnlinkingId(companyId)\n try {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n ),\n },\n ),\n { companyId, personId, operation: 'unlinkPersonCompanyLink' },\n )\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.unlinkSuccess', 'Company unlinked.'),\n 'success',\n )\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n )\n flash(message, 'error')\n } finally {\n setUnlinkingId(null)\n }\n },\n [_personName, confirm, loadData, onChanged, personId, runWriteMutation, t, unlinkingId],\n )\n\n useAppEvent('customers.person_company_link.deleted', (event) => {\n const payload = event.payload as { personEntityId?: string | null } | null | undefined\n if (payload && payload.personEntityId === personId) {\n void loadData({ showLoading: false })\n }\n }, [personId, loadData])\n\n return (\n <>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 rounded-2xl border border-border/70 bg-card px-4 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.people.detail.companies.manageTitle', 'Manage linked companies')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {t('customers.people.detail.companies.summary', '{{count}} linked companies', {\n count: linkedCompanies.length,\n })}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={t(\n 'customers.people.detail.companies.searchPlaceholder',\n 'Search linked companies\u2026',\n )}\n className=\"sm:w-[260px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setDialogOpen(true)}\n >\n <Link2 className=\"mr-2 size-4\" />\n {t('customers.people.detail.companies.manageAction', 'Manage links')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end\">\n <select\n value={sort}\n onChange={(event) =>\n setSort(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {t('customers.people.detail.companies.sortNameAsc', 'Sort: Name A-Z')}\n </option>\n <option value=\"name-desc\">\n {t('customers.people.detail.companies.sortNameDesc', 'Sort: Name Z-A')}\n </option>\n <option value=\"recent\">\n {t('customers.people.detail.companies.sortRecent', 'Sort: Recently active')}\n </option>\n </select>\n </div>\n\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((idx) => (\n <div\n key={idx}\n className=\"h-[320px] animate-pulse rounded-2xl border border-border/60 bg-muted/30\"\n />\n ))}\n </div>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={\n search.trim().length\n ? t(\n 'customers.people.detail.companies.noSearchResults',\n 'No linked companies match your search.',\n )\n : t(\n 'customers.people.detail.empty.companies',\n 'No company linked to this person.',\n )\n }\n />\n ) : (\n <>\n <div className=\"space-y-4\">\n {items.map((item) => (\n <CompanyCard\n key={item.companyId}\n data={item}\n personName={_personName}\n statusMap={statusDict?.map}\n lifecycleMap={lifecycleDict?.map}\n temperatureMap={temperatureDict?.map}\n renewalQuarterMap={renewalQuarterDict?.map}\n roleMap={roleDict?.map}\n onUnlink={() => handleUnlink(item.companyId, item.displayName)}\n unlinkLabel={t('customers.people.detail.companies.unlinkAction', 'Unlink')}\n unlinkDisabled={unlinkingId === item.companyId}\n />\n ))}\n </div>\n <Pagination page={page} totalPages={totalPages} onPageChange={setPage} />\n </>\n )}\n </div>\n\n <LinkEntityDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n adapter={companyLinkAdapter}\n initialSelectedIds={linkedIds}\n initialPrimaryId={linkedPrimaryId}\n primarySupported\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n\nexport default PersonCompaniesSection\n\nexport { sameIdSet }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ArrowLeft, ArrowRight, Building2, Link2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\nimport { CompanyCard, type EnrichedCompanyData } from './CompanyCard'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createCompanyLinkAdapter } from '../linking/adapters/companyAdapter'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype LinkedCompanySummary = {\n id: string\n displayName: string\n isPrimary: boolean\n}\n\ntype PersonCompaniesSectionProps = {\n personId: string\n personName: string\n initialLinkedCompanies?: LinkedCompanySummary[]\n onChanged?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst LINKED_PAGE_SIZE = 20\n\nfunction sameIdSet(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n const rightSet = new Set(right)\n return left.every((value) => rightSet.has(value))\n}\n\nfunction Pagination({\n page,\n totalPages,\n onPageChange,\n}: {\n page: number\n totalPages: number\n onPageChange: (page: number) => void\n}) {\n if (totalPages <= 1) return null\n return (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n Page {page} of {totalPages}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n >\n <ArrowLeft className=\"mr-1.5 size-3.5\" />\n Previous\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n >\n Next\n <ArrowRight className=\"ml-1.5 size-3.5\" />\n </Button>\n </div>\n </div>\n )\n}\n\nexport function PersonCompaniesSection({\n personId,\n personName: _personName,\n initialLinkedCompanies = [],\n onChanged,\n runGuardedMutation,\n}: PersonCompaniesSectionProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [items, setItems] = React.useState<EnrichedCompanyData[]>([])\n const [loading, setLoading] = React.useState(true)\n const [unlinkingId, setUnlinkingId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sort, setSort] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [page, setPage] = React.useState(1)\n const [totalPages, setTotalPages] = React.useState(1)\n const [linkedCompanies, setLinkedCompanies] = React.useState<LinkedCompanySummary[]>(\n initialLinkedCompanies,\n )\n const [dialogOpen, setDialogOpen] = React.useState(false)\n\n const { data: statusDict } = useCustomerDictionary('statuses')\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages')\n const { data: temperatureDict } = useCustomerDictionary('temperature')\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters')\n const { data: roleDict } = useCustomerDictionary('person-company-roles')\n\n React.useEffect(() => {\n setLinkedCompanies(initialLinkedCompanies)\n }, [initialLinkedCompanies])\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const loadData = React.useCallback(\n async (options?: { showLoading?: boolean }) => {\n const showLoading = options?.showLoading ?? true\n if (showLoading) setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINKED_PAGE_SIZE),\n sort,\n })\n if (search.trim().length > 0) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: EnrichedCompanyData[]\n totalPages?: number\n }>(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/enriched?${params.toString()}`,\n { cache: 'no-store' },\n )\n const nextItems = Array.isArray(payload?.items) ? payload.items : []\n setItems(nextItems)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('customers.people.detail.companies.loadError', 'Failed to load companies.')\n flash(message, 'error')\n setItems([])\n setTotalPages(1)\n } finally {\n if (showLoading) setLoading(false)\n }\n },\n [page, personId, search, sort, t],\n )\n\n React.useEffect(() => {\n void loadData()\n }, [loadData])\n\n React.useEffect(() => {\n setPage(1)\n }, [search, sort])\n\n const linkedIds = React.useMemo(\n () => linkedCompanies.map((entry) => entry.id),\n [linkedCompanies],\n )\n const linkedPrimaryId = React.useMemo(\n () =>\n linkedCompanies.find((entry) => entry.isPrimary)?.id ??\n linkedCompanies[0]?.id ??\n null,\n [linkedCompanies],\n )\n\n const companyLinkAdapter = React.useMemo(\n () =>\n createCompanyLinkAdapter({\n dialogTitle: t('customers.linking.company.dialogTitle', 'Link company'),\n dialogSubtitle: _personName\n ? t('customers.linking.company.dialogSubtitleFor', 'Link an existing company to {{name}}', {\n name: _personName,\n })\n : t('customers.linking.company.dialogSubtitle', 'Link an existing company to this person'),\n sectionLabel: t('customers.linking.company.sectionLabel', 'MATCHING COMPANIES'),\n searchPlaceholder: t(\n 'customers.linking.company.searchPlaceholder',\n 'Search all companies\u2026',\n ),\n searchEmptyHint: t(\n 'customers.linking.company.searchEmpty',\n 'No matching companies found.',\n ),\n selectedEmptyHint: t(\n 'customers.linking.company.selectedEmpty',\n 'No companies selected.',\n ),\n confirmButtonLabel: t('customers.linking.company.confirmButton', 'Link company'),\n excludeLinkedPersonId: personId,\n }),\n [_personName, personId, t],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n removedIds,\n nextSelectedIds,\n primaryId,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n nextSelectedIds: string[]\n primaryId?: string | null\n optionsById: Record<string, LinkEntityOption>\n }) => {\n const currentPrimaryId = linkedPrimaryId\n const nextPrimaryId = nextSelectedIds.length\n ? primaryId && nextSelectedIds.includes(primaryId)\n ? primaryId\n : nextSelectedIds[0]\n : null\n\n try {\n for (const companyId of removedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n ),\n { companyId, personId, operation: 'removePersonCompanyLink' },\n )\n }\n\n for (const companyId of addedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n companyId,\n isPrimary: nextPrimaryId === companyId,\n }),\n },\n ),\n { companyId, personId, operation: 'addPersonCompanyLink' },\n )\n }\n\n if (\n nextPrimaryId &&\n nextPrimaryId !== currentPrimaryId &&\n !addedIds.includes(nextPrimaryId)\n ) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link set-primary (add/remove)\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(nextPrimaryId)}`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isPrimary: true }),\n },\n ),\n { companyId: nextPrimaryId, personId, operation: 'setPrimaryPersonCompanyLink' },\n )\n }\n\n const nextLinkedCompanies: LinkedCompanySummary[] = nextSelectedIds.map((id) => {\n const option = optionsById[id]\n const fromCurrent = linkedCompanies.find((entry) => entry.id === id)\n return {\n id,\n displayName: option?.label ?? fromCurrent?.displayName ?? id,\n isPrimary: id === nextPrimaryId,\n }\n })\n setLinkedCompanies(nextLinkedCompanies)\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.manageSuccess', 'Linked companies updated.'),\n 'success',\n )\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.manageError',\n 'Failed to update linked companies.',\n )\n flash(message, 'error')\n throw error\n }\n },\n [linkedCompanies, linkedPrimaryId, loadData, onChanged, personId, runWriteMutation, t],\n )\n\n const handleUnlink = React.useCallback(\n async (companyId: string, displayName: string) => {\n if (!companyId || unlinkingId) return\n const confirmed = await confirm({\n title: t('customers.people.detail.companies.unlinkConfirmTitle', 'Unlink company'),\n description: t(\n 'customers.people.detail.companies.unlinkConfirm',\n 'Unlink {{company}} from {{person}}?',\n { company: displayName, person: _personName },\n ),\n confirmText: t('customers.people.detail.companies.unlinkAction', 'Unlink'),\n cancelText: t('customers.linking.actions.cancel', 'Cancel'),\n })\n if (!confirmed) return\n setUnlinkingId(companyId)\n try {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n ),\n },\n ),\n { companyId, personId, operation: 'unlinkPersonCompanyLink' },\n )\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.unlinkSuccess', 'Company unlinked.'),\n 'success',\n )\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n )\n flash(message, 'error')\n } finally {\n setUnlinkingId(null)\n }\n },\n [_personName, confirm, loadData, onChanged, personId, runWriteMutation, t, unlinkingId],\n )\n\n const reloadOnPersonDetach = React.useCallback((event: AppEventPayload) => {\n const payload = event.payload as { personEntityId?: string | null } | null | undefined\n if (payload && payload.personEntityId === personId) {\n void loadData({ showLoading: false })\n }\n }, [personId, loadData])\n\n useAppEvent('customers.person_company_link.deleted', reloadOnPersonDetach, [reloadOnPersonDetach])\n // Legacy profile-only assignments have no link row, so their detach broadcasts this sibling\n // event instead of `customers.person_company_link.deleted` (#5114). Without it, other viewers\n // of the same person keep listing a company that is already detached.\n useAppEvent('customers.person.company_assignment.detached', reloadOnPersonDetach, [reloadOnPersonDetach])\n\n return (\n <>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 rounded-2xl border border-border/70 bg-card px-4 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.people.detail.companies.manageTitle', 'Manage linked companies')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {t('customers.people.detail.companies.summary', '{{count}} linked companies', {\n count: linkedCompanies.length,\n })}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={t(\n 'customers.people.detail.companies.searchPlaceholder',\n 'Search linked companies\u2026',\n )}\n className=\"sm:w-[260px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setDialogOpen(true)}\n >\n <Link2 className=\"mr-2 size-4\" />\n {t('customers.people.detail.companies.manageAction', 'Manage links')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end\">\n <select\n value={sort}\n onChange={(event) =>\n setSort(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {t('customers.people.detail.companies.sortNameAsc', 'Sort: Name A-Z')}\n </option>\n <option value=\"name-desc\">\n {t('customers.people.detail.companies.sortNameDesc', 'Sort: Name Z-A')}\n </option>\n <option value=\"recent\">\n {t('customers.people.detail.companies.sortRecent', 'Sort: Recently active')}\n </option>\n </select>\n </div>\n\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((idx) => (\n <div\n key={idx}\n className=\"h-[320px] animate-pulse rounded-2xl border border-border/60 bg-muted/30\"\n />\n ))}\n </div>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={\n search.trim().length\n ? t(\n 'customers.people.detail.companies.noSearchResults',\n 'No linked companies match your search.',\n )\n : t(\n 'customers.people.detail.empty.companies',\n 'No company linked to this person.',\n )\n }\n />\n ) : (\n <>\n <div className=\"space-y-4\">\n {items.map((item) => (\n <CompanyCard\n key={item.companyId}\n data={item}\n personName={_personName}\n statusMap={statusDict?.map}\n lifecycleMap={lifecycleDict?.map}\n temperatureMap={temperatureDict?.map}\n renewalQuarterMap={renewalQuarterDict?.map}\n roleMap={roleDict?.map}\n onUnlink={() => handleUnlink(item.companyId, item.displayName)}\n unlinkLabel={t('customers.people.detail.companies.unlinkAction', 'Unlink')}\n unlinkDisabled={unlinkingId === item.companyId}\n />\n ))}\n </div>\n <Pagination page={page} totalPages={totalPages} onPageChange={setPage} />\n </>\n )}\n </div>\n\n <LinkEntityDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n adapter={companyLinkAdapter}\n initialSelectedIds={linkedIds}\n initialPrimaryId={linkedPrimaryId}\n primarySupported\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n\nexport default PersonCompaniesSection\n\nexport { sameIdSet }\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -524,8 +524,16 @@ const personCompanyLinkUpdateSchema = scopedSchema.extend({
524
524
  isPrimary: z.boolean()
525
525
  });
526
526
  const personCompanyLinkDeleteSchema = scopedSchema.extend({
527
- linkId: uuid()
528
- });
527
+ linkId: uuid().optional(),
528
+ personEntityId: uuid().optional(),
529
+ companyEntityId: uuid().optional()
530
+ }).refine(
531
+ (payload) => Boolean(payload.linkId) || Boolean(payload.personEntityId && payload.companyEntityId),
532
+ {
533
+ message: "Provide either linkId or both personEntityId and companyEntityId.",
534
+ path: ["linkId"]
535
+ }
536
+ );
529
537
  export {
530
538
  ACTIVITY_DATE_REQUIRED_MESSAGE_KEY,
531
539
  ACTIVITY_PHONE_INVALID_MESSAGE_KEY,