@open-mercato/core 0.6.4-develop.4011.1.4f3ed9ae3e → 0.6.4-develop.4015.1.efaafadf79

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/backend/customers/people/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { groupBulkDeleteFailures, runBulkDelete } from '@open-mercato/ui/backend/utils/bulkDelete'\nimport { coalesceLastOperations } from '@open-mercato/ui/backend/operations/store'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { E } from '#generated/entities.ids.generated'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport type { FilterFieldDef, FilterOption as AdvancedFilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { AdvancedFilterTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { createEmptyTree, makeRuleTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { deserializeAdvancedFilter, deserializeTree, flatToTree, mapDictionaryColorToTone, serializeTree } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'\nimport {\n DictionaryValue,\n createEmptyCustomerDictionaryMaps,\n renderDictionaryColor,\n renderDictionaryIcon,\n type CustomerDictionaryKind,\n type CustomerDictionaryMap,\n} from '../../../lib/dictionaries'\nimport {\n useCustomFieldDefs,\n} from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport { useAutoDiscoveredFields } from '@open-mercato/ui/backend/utils/useAutoDiscoveredFields'\nimport { useAdvancedFilterTree } from '@open-mercato/ui/backend/hooks/useAdvancedFilter'\nimport { AdvancedFilterPanel } from '@open-mercato/ui/backend/filters/AdvancedFilterPanel'\nimport { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'\nimport type { FilterPreset } from '@open-mercato/ui/backend/filters/QuickFilters'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { ensureCustomerDictionary } from '../../../components/detail/hooks/useCustomerDictionary'\nimport {\n ensureCurrentUserFilterOption,\n fetchAssignableStaffMembers,\n mapAssignableStaffToFilterOptions,\n} from '../../../components/detail/assignableStaff'\nimport { CollectionPreviewCell, normalizeCollectionLabels } from '../../../components/list/CollectionPreviewCell'\n\ntype DictionaryOptionWithTone = AdvancedFilterOption & FilterOption\n\nfunction makePeoplePresets(): FilterPreset[] {\n return [\n {\n id: 'recently-active',\n labelKey: 'customers.people.presets.recentlyActive',\n iconName: 'clock',\n build: ({ now }) => {\n const cutoff = new Date(now.getTime() - 7 * 24 * 3600 * 1000).toISOString().slice(0, 10)\n return makeRuleTree({ field: 'next_interaction_at', operator: 'is_after', value: cutoff })\n },\n },\n {\n id: 'my-contacts',\n labelKey: 'customers.people.presets.myContacts',\n requiresUser: true,\n build: ({ userId }) => makeRuleTree({ field: 'owner_user_id', operator: 'is', value: userId }),\n },\n {\n id: 'hot-leads',\n labelKey: 'customers.people.presets.hotLeads',\n build: () => makeRuleTree({ field: 'lifecycle_stage', operator: 'is', value: 'lead' }),\n },\n {\n id: 'stale-30',\n labelKey: 'customers.people.presets.stale30',\n build: ({ now }) => {\n const cutoff = new Date(now.getTime() - 30 * 24 * 3600 * 1000).toISOString().slice(0, 10)\n return makeRuleTree({ field: 'next_interaction_at', operator: 'is_before', value: cutoff })\n },\n },\n ]\n}\n\ntype PersonRow = {\n id: string\n name: string\n description?: string | null\n email?: string | null\n phone?: string | null\n firstName?: string | null\n lastName?: string | null\n preferredName?: string | null\n jobTitle?: string | null\n department?: string | null\n seniority?: string | null\n timezone?: string | null\n linkedInUrl?: string | null\n twitterUrl?: string | null\n companyEntityId?: string | null\n status?: string | null\n lifecycleStage?: string | null\n nextInteractionAt?: string | null\n nextInteractionName?: string | null\n nextInteractionIcon?: string | null\n nextInteractionColor?: string | null\n organizationId?: string | null\n source?: string | null\n ownerUserId?: string | null\n} & Record<string, unknown>\n\ntype PeopleResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n page?: number\n totalPages?: number\n}\n\ntype DictionaryKindKey = CustomerDictionaryKind\ntype DictionaryMap = CustomerDictionaryMap\n\nconst NO_MATCH_TAG_SENTINEL = '__no_match__'\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\nfunction mapApiItem(item: Record<string, unknown>): PersonRow | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const name = typeof item.display_name === 'string' ? item.display_name : ''\n const description = typeof item.description === 'string' ? item.description : null\n const email = typeof item.primary_email === 'string' ? item.primary_email : null\n const phone = typeof item.primary_phone === 'string' ? item.primary_phone : null\n const firstName = typeof item.first_name === 'string' ? item.first_name : null\n const lastName = typeof item.last_name === 'string' ? item.last_name : null\n const preferredName = typeof item.preferred_name === 'string' ? item.preferred_name : null\n const jobTitle = typeof item.job_title === 'string' ? item.job_title : null\n const department = typeof item.department === 'string' ? item.department : null\n const seniority = typeof item.seniority === 'string' ? item.seniority : null\n const timezone = typeof item.timezone === 'string' ? item.timezone : null\n const linkedInUrl = typeof item.linked_in_url === 'string' ? item.linked_in_url : null\n const twitterUrl = typeof item.twitter_url === 'string' ? item.twitter_url : null\n const companyEntityId = typeof item.company_entity_id === 'string' ? item.company_entity_id : null\n const status = typeof item.status === 'string' ? item.status : null\n const lifecycleStage = typeof item.lifecycle_stage === 'string' ? item.lifecycle_stage : null\n const nextInteractionAt = typeof item.next_interaction_at === 'string' ? item.next_interaction_at : null\n const nextInteractionName = typeof item.next_interaction_name === 'string' ? item.next_interaction_name : null\n const nextInteractionIcon = typeof item.next_interaction_icon === 'string' ? item.next_interaction_icon : null\n const nextInteractionColor = typeof item.next_interaction_color === 'string' ? item.next_interaction_color : null\n const organizationId = typeof item.organization_id === 'string' ? item.organization_id : null\n const source = typeof item.source === 'string' ? item.source : null\n const customFields: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) {\n customFields[key] = value\n }\n }\n return withDataTableNamespaces({\n id,\n name,\n description,\n email,\n phone,\n firstName,\n lastName,\n preferredName,\n jobTitle,\n department,\n seniority,\n timezone,\n linkedInUrl,\n twitterUrl,\n companyEntityId,\n status,\n lifecycleStage,\n nextInteractionAt,\n nextInteractionName,\n nextInteractionIcon,\n nextInteractionColor,\n organizationId,\n source,\n ...customFields,\n }, item)\n}\n\nexport default function CustomersPeoplePage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [rows, setRows] = React.useState<PersonRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize, setPageSize] = React.useState(20)\n const [sorting, setSorting] = React.useState<import('@tanstack/react-table').SortingState>([])\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const pathname = usePathname()\n const searchParams = useSearchParams()\n // One-shot URL hydration used as the hook's initial value. The hook is the\n // single source of truth from this point on \u2014 the page MUST NOT keep a\n // parallel `useState<AdvancedFilterTree>` (see spec \"Migration & Backward\n // Compatibility\" \u2192 state ownership).\n const initialFilterTree = React.useMemo<AdvancedFilterTree>(() => {\n if (!searchParams) return createEmptyTree()\n const record: Record<string, string> = {}\n searchParams.forEach((value, key) => {\n if (key.startsWith('filter[')) record[key] = value\n })\n const v2 = deserializeTree(record)\n if (v2) return v2\n const flat = deserializeAdvancedFilter(record)\n if (flat) return flatToTree(flat)\n return createEmptyTree()\n // searchParams is intentionally evaluated once on mount \u2014 subsequent URL\n // changes flow through the hook, not back through hydration.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n // `filterPanel` lives at the top of the component so derived state below\n // (URL params, data fetch, export config) can read `filterPanel.appliedTree`\n // directly. Real `FilterFieldDef[]` arrives later from `useAutoDiscoveredFields`\n // (it depends on columns) and is synced into the hook via a small effect at\n // the bottom of the component. The hook reads fields through a ref at\n // validation time only \u2014 first validation cannot fire before user input, by\n // which point fields have settled, so the empty initial value is safe.\n const [panelFields, setPanelFields] = React.useState<FilterFieldDef[]>([])\n const [filtersOpen, setFiltersOpen] = React.useState(false)\n const filtersTriggerRef = React.useRef<HTMLButtonElement | null>(null)\n const filterPanel = useAdvancedFilterTree({\n initial: initialFilterTree,\n fields: panelFields,\n onApply: () => setPage(1),\n })\n const advancedFilterState = filterPanel.appliedTree\n const handleAdvancedFilterClear = React.useCallback(() => {\n filterPanel.clear()\n setPage(1)\n }, [filterPanel])\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [dictionaryMaps, setDictionaryMaps] = React.useState<Record<DictionaryKindKey, DictionaryMap>>(createEmptyCustomerDictionaryMaps())\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const t = useT()\n const router = useRouter()\n const handlePageSizeChange = React.useCallback((newSize: number) => {\n setPageSize(newSize)\n setPage(1)\n }, [])\n\n const bulkMutationContextId = 'customers-people-list:bulk-delete'\n const { runMutation: runBulkMutation, retryLastMutation: retryBulkMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: bulkMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const singleMutationContextId = 'customers-people-list:single-delete'\n const { runMutation: runSingleMutation, retryLastMutation: retrySingleMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: singleMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const fetchDictionaryEntries = React.useCallback(async (kind: DictionaryKindKey) => {\n try {\n const data = await ensureCustomerDictionary(queryClient, kind, scopeVersion)\n setDictionaryMaps((prev) => ({\n ...prev,\n [kind]: data.map,\n }))\n return data.entries\n } catch {\n return []\n }\n }, [queryClient, scopeVersion])\n const dictionaryOptions = React.useMemo(() => {\n const toOptions = (map?: DictionaryMap | null): DictionaryOptionWithTone[] =>\n Object.values(map ?? {})\n .map((entry) => {\n const tone = mapDictionaryColorToTone(entry.color)\n const option: DictionaryOptionWithTone = { value: entry.value, label: entry.label }\n if (tone) option.tone = tone\n return option\n })\n .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))\n return {\n statuses: toOptions(dictionaryMaps.statuses),\n sources: toOptions(dictionaryMaps.sources),\n lifecycleStages: toOptions(dictionaryMaps['lifecycle-stages']),\n }\n }, [dictionaryMaps])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadAll() {\n if (cancelled) return\n setDictionaryMaps(createEmptyCustomerDictionaryMaps())\n await Promise.all([\n fetchDictionaryEntries('statuses'),\n fetchDictionaryEntries('sources'),\n fetchDictionaryEntries('lifecycle-stages'),\n ])\n }\n loadAll().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [fetchDictionaryEntries, scopeVersion, reloadToken])\n\n const { data: customFieldDefs = [] } = useCustomFieldDefs(\n [E.customers.customer_entity, E.customers.customer_person_profile],\n { keyExtras: [scopeVersion, reloadToken] },\n )\n const currentUserId = useCurrentUserId()\n const [ownerFilterOptions, setOwnerFilterOptions] = React.useState<AdvancedFilterOption[]>([])\n React.useEffect(() => {\n const controller = new AbortController()\n let cancelled = false\n void fetchAssignableStaffMembers('', { pageSize: 100, signal: controller.signal })\n .then((items) => {\n if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items))\n })\n .catch(() => {\n if (!cancelled) setOwnerFilterOptions([])\n })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [scopeVersion])\n const resolvedOwnerFilterOptions = React.useMemo(\n () => ensureCurrentUserFilterOption(\n ownerFilterOptions,\n currentUserId,\n t('customers.filters.currentUser', 'Current user'),\n ),\n [currentUserId, ownerFilterOptions, t],\n )\n const loadOwnerFilterOptions = React.useCallback(async (query?: string): Promise<AdvancedFilterOption[]> => {\n const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })\n return mapAssignableStaffToFilterOptions(items)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (sorting.length > 0) {\n params.set('sort', sorting[0].id)\n params.set('order', sorting[0].desc ? 'desc' : 'asc')\n }\n if (search.trim()) params.set('search', search.trim())\n const advancedParams = serializeTree(advancedFilterState)\n for (const [key, val] of Object.entries(advancedParams)) {\n params.set(key, val)\n }\n return params.toString()\n }, [advancedFilterState, page, pageSize, search, sorting])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n // Mirror page state into the URL so a refresh restores the same filter tree,\n // including nested subgroups. Without this effect the People page would\n // discard everything the user typed into the filter panel on refresh\n // (the previous behavior \u2014 top-level rules only \"appeared\" to survive\n // because a stale localStorage perspective snapshot was being re-applied).\n const queryRef = React.useRef(searchParams?.toString() ?? '')\n React.useEffect(() => {\n if (!pathname) return\n const params = new URLSearchParams()\n if (search.trim().length) params.set('search', search.trim())\n if (page > 1) params.set('page', String(page))\n if (sorting.length > 0) {\n params.set('sort', sorting[0].id)\n params.set('order', sorting[0].desc ? 'desc' : 'asc')\n }\n const advancedParams = serializeTree(advancedFilterState)\n for (const [key, val] of Object.entries(advancedParams)) {\n params.set(key, val)\n }\n const next = params.toString()\n if (queryRef.current === next) return\n queryRef.current = next\n router.replace(next ? `${pathname}?${next}` : pathname, { scroll: false })\n }, [pathname, router, page, search, sorting, advancedFilterState])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/people', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/people', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: PeopleResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<PeopleResponse>(`/api/customers/people?${queryParams}`, undefined, { fallback })\n if (!call.ok) {\n const errorPayload = call.result as { error?: string } | undefined\n const message = typeof errorPayload?.error === 'string' ? errorPayload.error : t('customers.people.list.error.load')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map((item) => mapApiItem(item as Record<string, unknown>)).filter((row): row is PersonRow => !!row))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n setCacheStatus(null)\n const message = err instanceof Error ? err.message : t('customers.people.list.error.load')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (person: PersonRow) => {\n if (!person?.id) return\n const name = person.name || t('customers.people.list.deleteFallbackName')\n const confirmed = await confirm({\n title: t('customers.people.list.deleteConfirm', undefined, { name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runSingleMutation({\n operation: async () => {\n await apiCallOrThrow(\n `/api/customers/people?id=${encodeURIComponent(person.id)}`,\n {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n },\n { errorMessage: t('customers.people.list.deleteError') },\n )\n },\n context: {\n formId: singleMutationContextId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n retryLastMutation: retrySingleMutation,\n },\n })\n setRows((prev) => prev.filter((row) => row.id !== person.id))\n setTotal((prev) => Math.max(prev - 1, 0))\n handleRefresh()\n flash(t('customers.people.list.deleteSuccess'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customers.people.list.deleteError')\n flash(message, 'error')\n }\n }, [confirm, handleRefresh, retrySingleMutation, runSingleMutation, singleMutationContextId, t])\n\n const handleBulkDelete = React.useCallback(async (selectedRows: PersonRow[]) => {\n const confirmed = await confirm({\n title: t('customers.people.list.bulkDelete.title', 'Delete {count} people?', { count: selectedRows.length }),\n description: t('customers.people.list.bulkDelete.description', 'This action cannot be undone.'),\n variant: 'destructive',\n })\n if (!confirmed) return false\n\n const { succeeded, failures } = await runBulkMutation({\n operation: async () =>\n runBulkDelete(\n selectedRows,\n async (row) => {\n await apiCallOrThrow(`/api/customers/people?id=${encodeURIComponent(row.id)}`, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n })\n },\n {\n fallbackErrorMessage: t('customers.people.list.deleteError', 'Failed to delete person.'),\n logTag: 'customers.people.list',\n progress: {\n jobType: 'customers.people.bulk_delete',\n name: t('customers.people.list.bulkDelete.progressName', 'Delete selected people'),\n description: t(\n 'customers.people.list.bulkDelete.progressDescription',\n '{count} people selected for deletion',\n { count: selectedRows.length },\n ),\n meta: { source: 'customers.people.list' },\n },\n },\n ),\n context: {\n formId: bulkMutationContextId,\n resourceKind: 'customers.person',\n retryLastMutation: retryBulkMutation,\n },\n })\n\n if (succeeded.length > 0) {\n const succeededIds = new Set(succeeded.map((r) => r.id))\n setRows((prev) => prev.filter((r) => !succeededIds.has(r.id)))\n setTotal((prev) => Math.max(0, prev - succeeded.length))\n setReloadToken((prev) => prev + 1)\n if (succeeded.length > 1) {\n coalesceLastOperations(succeeded.length, {\n commandId: 'customers.people.delete',\n actionLabel: t('customers.people.list.bulkDelete.operationLabel', 'Delete {count} people', { count: succeeded.length }),\n resourceKind: 'customers.person',\n })\n }\n if (failures.length === 0) {\n flash(\n t('customers.people.list.bulkDelete.success', '{count} people deleted', { count: succeeded.length }),\n 'success',\n )\n } else {\n flash(\n t('customers.people.list.bulkDelete.partial', '{deleted} of {total} people deleted; {failed} failed', {\n deleted: succeeded.length,\n total: selectedRows.length,\n failed: failures.length,\n }),\n 'warning',\n )\n }\n }\n\n for (const group of groupBulkDeleteFailures(failures)) {\n const message = group.count === 1\n ? group.sampleMessage\n : t(\n 'customers.people.list.bulkDelete.failedGroup',\n '{count} people could not be deleted: {message}',\n { count: group.count, message: group.sampleMessage },\n )\n flash(message, 'error')\n }\n\n return succeeded.length > 0\n }, [bulkMutationContextId, confirm, retryBulkMutation, runBulkMutation, t])\n\n const columns = React.useMemo<ColumnDef<PersonRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>\n const renderDictionaryCell = (kind: DictionaryKindKey, rawValue: string | null | undefined) => (\n <DictionaryValue\n value={rawValue}\n map={dictionaryMaps[kind]}\n fallback={rawValue ? <span>{rawValue}</span> : noValue}\n className=\"text-sm\"\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\"\n iconClassName=\"h-4 w-4\"\n colorClassName=\"h-3 w-3 rounded-full\"\n />\n )\n\n const renderCustomFieldCell = (value: unknown) => {\n if (value == null) return noValue\n if (Array.isArray(value)) {\n if (!value.length) return noValue\n const normalized = normalizeCollectionLabels(\n value.map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item\n return String(item)\n }),\n )\n if (!normalized.length) return noValue\n return <CollectionPreviewCell labels={normalized} maxVisible={2} />\n }\n if (typeof value === 'boolean') {\n return (\n <span className=\"text-sm\">\n {value\n ? t('customers.people.list.booleanYes', 'Yes')\n : t('customers.people.list.booleanNo', 'No')}\n </span>\n )\n }\n const stringValue = typeof value === 'string' ? value.trim() : String(value)\n if (!stringValue) return noValue\n return <span className=\"text-sm\">{stringValue}</span>\n }\n\n const baseColumns: ColumnDef<PersonRow>[] = [\n {\n accessorKey: 'name',\n header: t('customers.people.list.columns.name'),\n meta: {\n alwaysVisible: true,\n columnChooserGroup: 'Basic Info',\n filterKey: 'display_name',\n filterGroup: 'CRM',\n maxWidth: '240px',\n },\n cell: ({ row }) => (\n <Link href={`/backend/customers/people-v2/${row.original.id}`} className=\"font-medium hover:underline\">\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customers.people.list.columns.email'),\n meta: {\n columnChooserGroup: 'Contact',\n filterKey: 'primary_email',\n filterGroup: 'Contact',\n filterIconName: 'mail',\n maxWidth: '220px',\n },\n cell: ({ row }) => row.original.email || <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>,\n },\n {\n accessorKey: 'status',\n header: t('customers.people.list.columns.status'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.statuses,\n columnChooserGroup: 'Basic Info',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('statuses', row.original.status),\n },\n {\n accessorKey: 'lifecycleStage',\n header: t('customers.people.list.columns.lifecycleStage'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.lifecycleStages,\n columnChooserGroup: 'Basic Info',\n filterKey: 'lifecycle_stage',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('lifecycle-stages', row.original.lifecycleStage),\n },\n {\n accessorKey: 'nextInteractionAt',\n header: t('customers.people.list.columns.nextInteraction'),\n meta: {\n columnChooserGroup: 'Dates',\n filterKey: 'next_interaction_at',\n filterGroup: 'Activity',\n filterIconName: 'calendar',\n tooltipContent: (row: PersonRow) => {\n if (!row.nextInteractionAt) return undefined\n const date = formatDate(row.nextInteractionAt, '')\n const name = row.nextInteractionName || ''\n return [date, name].filter(Boolean).join(' - ')\n },\n },\n cell: ({ row }) =>\n row.original.nextInteractionAt\n ? (\n <div className=\"flex items-start gap-2 text-sm\">\n {row.original.nextInteractionIcon ? (\n <span className=\"mt-0.5 inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\">\n {renderDictionaryIcon(row.original.nextInteractionIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <div className=\"flex flex-col\">\n <span>{formatDate(row.original.nextInteractionAt, t('customers.people.list.noValue'))}</span>\n {row.original.nextInteractionName ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.nextInteractionName}</span>\n ) : null}\n </div>\n {row.original.nextInteractionColor ? (\n <span className=\"mt-1\">\n {renderDictionaryColor(row.original.nextInteractionColor, 'h-3 w-3 rounded-full border border-border')}\n </span>\n ) : null}\n </div>\n )\n : <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>,\n },\n {\n accessorKey: 'source',\n header: t('customers.people.list.columns.source'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.sources,\n columnChooserGroup: 'Basic Info',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('sources', row.original.source),\n },\n {\n accessorKey: 'ownerUserId',\n header: t('customers.people.list.columns.owner', 'Owner'),\n meta: {\n columnChooserGroup: 'CRM',\n filterType: 'select',\n filterOptions: resolvedOwnerFilterOptions,\n filterLoadOptions: loadOwnerFilterOptions,\n filterGroup: 'CRM',\n filterIconName: 'user-round',\n filterKey: 'owner_user_id',\n hidden: true,\n },\n cell: ({ row }) => row.original.ownerUserId ?? null,\n },\n {\n accessorKey: 'firstName',\n header: t('customers.people.form.firstName', 'First name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.first_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.firstName || noValue,\n },\n {\n accessorKey: 'lastName',\n header: t('customers.people.form.lastName', 'Last name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.last_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.lastName || noValue,\n },\n {\n accessorKey: 'preferredName',\n header: t('customers.people.form.preferredName', 'Preferred name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.preferred_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.preferredName || noValue,\n },\n {\n accessorKey: 'jobTitle',\n header: t('customers.people.form.jobTitle', 'Job title'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.job_title',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.jobTitle || noValue,\n },\n {\n accessorKey: 'department',\n header: t('customers.people.detail.fields.department', 'Department'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.department',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.department || noValue,\n },\n {\n accessorKey: 'seniority',\n header: t('customers.people.detail.fields.seniority', 'Seniority'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.seniority',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.seniority || noValue,\n },\n {\n accessorKey: 'timezone',\n header: t('customers.people.detail.fields.timezone', 'Timezone'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.timezone',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.timezone || noValue,\n },\n {\n accessorKey: 'linkedInUrl',\n header: t('customers.people.detail.fields.linkedIn', 'LinkedIn'),\n meta: {\n columnChooserGroup: 'Socials',\n hidden: true,\n filterKey: 'person_profile.linked_in_url',\n filterGroup: 'Socials',\n },\n cell: ({ row }) => row.original.linkedInUrl || noValue,\n },\n {\n accessorKey: 'twitterUrl',\n header: t('customers.people.detail.fields.twitter', 'Twitter'),\n meta: {\n columnChooserGroup: 'Socials',\n hidden: true,\n filterKey: 'person_profile.twitter_url',\n filterGroup: 'Socials',\n },\n cell: ({ row }) => row.original.twitterUrl || noValue,\n },\n {\n accessorKey: 'description',\n header: t('customers.people.form.description', 'Description'),\n meta: {\n columnChooserGroup: 'Notes',\n hidden: true,\n filterKey: 'description',\n filterGroup: 'Notes',\n },\n cell: ({ row }) => row.original.description || noValue,\n },\n ]\n\n const customColumns = customFieldDefs\n .filter((def) => supportsCustomFieldColumn(def))\n .map<ColumnDef<PersonRow>>((def) => ({\n accessorKey: `cf_${def.key}`,\n header: def.label || def.key,\n meta: {\n columnChooserGroup: def.group?.title ?? 'Custom Fields',\n filterGroup: def.group?.title ?? 'Custom Fields',\n filterType: mapCustomFieldKindToFilterType(def.kind),\n filterOptions: normalizeCustomFieldFilterOptions(def.options),\n hidden: def.listVisible === false,\n maxWidth: '220px',\n },\n cell: ({ getValue }) => renderCustomFieldCell(getValue()),\n }))\n\n return [...baseColumns, ...customColumns]\n }, [customFieldDefs, dictionaryMaps, dictionaryOptions, loadOwnerFilterOptions, resolvedOwnerFilterOptions, t])\n\n const { advancedFilterFields } = useAutoDiscoveredFields({ columns, customFieldDefs })\n\n // Sync auto-discovered fields into the `filterPanel` declared at the top of\n // the component. See the comment on the `panelFields` state for why this\n // late-binding is safe. Bail out by content (field-key list) \u2014 every render\n // of `useAutoDiscoveredFields` produces fresh `FilterFieldDef` object refs\n // even when the set of fields hasn't actually changed, so a naive reference\n // setState would loop (\"Maximum update depth exceeded\").\n React.useEffect(() => {\n setPanelFields((prev) => {\n if (prev === advancedFilterFields) return prev\n if (prev.length === advancedFilterFields.length) {\n let same = true\n for (let i = 0; i < prev.length; i++) {\n if (prev[i].key !== advancedFilterFields[i].key) { same = false; break }\n }\n if (same) return prev\n }\n return advancedFilterFields\n })\n }, [advancedFilterFields])\n\n const peoplePresets = React.useMemo<FilterPreset[]>(() => makePeoplePresets(), [])\n\n return (\n <Page>\n <PageBody>\n <DataTable<PersonRow>\n stickyFirstColumn\n stickyActionsColumn\n title={t('customers.people.list.title')}\n refreshButton={{\n label: t('customers.people.list.actions.refresh'),\n onRefresh: () => { setSearch(''); setPage(1); handleRefresh() },\n }}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customers/people/create\">\n {t('customers.people.list.actions.new')}\n </Link>\n </Button>\n )}\n columns={columns}\n columnChooser={{ auto: true }}\n data={rows}\n exporter={exportConfig}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customers.people.list.searchPlaceholder')}\n entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}\n perspective={{ tableId: 'customers.people.list' }}\n onRowClick={(row) => router.push(`/backend/customers/people-v2/${row.id}`)}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n bulkActions={[\n {\n id: 'delete',\n label: t('customers.people.list.bulkDelete.action', 'Delete selected'),\n destructive: true,\n onExecute: handleBulkDelete,\n },\n ]}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customers.people.list.actions.view'),\n onSelect: () => { router.push(`/backend/customers/people-v2/${row.id}`) },\n },\n {\n id: 'open-new-tab',\n label: t('customers.people.list.actions.openInNewTab'),\n onSelect: () => window.open(`/backend/customers/people-v2/${row.id}`, '_blank', 'noopener'),\n },\n {\n id: 'delete',\n label: t('customers.people.list.actions.delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n advancedFilter={{\n auto: true,\n value: filterPanel.tree,\n onChange: filterPanel.setTree,\n onApply: () => filterPanel.flush(),\n onClear: handleAdvancedFilterClear,\n triggerRef: filtersTriggerRef,\n externalPopover: true,\n onTriggerClick: () => setFiltersOpen((prev) => !prev),\n onApplyTree: (tree) => {\n filterPanel.replaceTree(tree)\n setPage(1)\n },\n }}\n activeFilterChips={(\n <ActiveFilterChips\n tree={filterPanel.tree}\n fields={advancedFilterFields}\n popoverOpen={filtersOpen}\n onRemoveNode={(id) => filterPanel.dispatch({ type: 'removeNode', nodeId: id })}\n onOpen={() => setFiltersOpen(true)}\n />\n )}\n filterAwareEmptyState={{\n active: advancedFilterState.root.children.length > 0,\n entityNamePlural: t('customers.people.entityPlural', 'people'),\n canRemoveLast: filterPanel.tree.root.children.length > 0,\n onClearAll: handleAdvancedFilterClear,\n onRemoveLast: () => filterPanel.dispatch({ type: 'removeLast' }),\n }}\n virtualized\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange }}\n isLoading={isLoading}\n />\n <AdvancedFilterPanel\n fields={advancedFilterFields}\n value={filterPanel.tree}\n onChange={filterPanel.setTree}\n onApply={filterPanel.flush}\n onClear={handleAdvancedFilterClear}\n onFlush={filterPanel.flush}\n pendingErrors={filterPanel.pendingErrors}\n userId={currentUserId}\n presets={peoplePresets}\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n triggerRef={filtersTriggerRef}\n savedFilterStorageKey=\"customers.people.list\"\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { groupBulkDeleteFailures, runBulkDelete } from '@open-mercato/ui/backend/utils/bulkDelete'\nimport { coalesceLastOperations } from '@open-mercato/ui/backend/operations/store'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { E } from '#generated/entities.ids.generated'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport type { FilterFieldDef, FilterOption as AdvancedFilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { AdvancedFilterTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { createEmptyTree, makeRuleTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport { deserializeAdvancedFilter, deserializeTree, flatToTree, mapDictionaryColorToTone, serializeTree } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'\nimport {\n DictionaryValue,\n createEmptyCustomerDictionaryMaps,\n renderDictionaryColor,\n renderDictionaryIcon,\n type CustomerDictionaryKind,\n type CustomerDictionaryMap,\n} from '../../../lib/dictionaries'\nimport {\n useCustomFieldDefs,\n} from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport { useAutoDiscoveredFields } from '@open-mercato/ui/backend/utils/useAutoDiscoveredFields'\nimport { useAdvancedFilterTree } from '@open-mercato/ui/backend/hooks/useAdvancedFilter'\nimport { AdvancedFilterPanel } from '@open-mercato/ui/backend/filters/AdvancedFilterPanel'\nimport { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'\nimport type { FilterPreset } from '@open-mercato/ui/backend/filters/QuickFilters'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { ensureCustomerDictionary } from '../../../components/detail/hooks/useCustomerDictionary'\nimport {\n ensureCurrentUserFilterOption,\n fetchAssignableStaffMembers,\n mapAssignableStaffToFilterOptions,\n} from '../../../components/detail/assignableStaff'\nimport { CollectionPreviewCell, normalizeCollectionLabels } from '../../../components/list/CollectionPreviewCell'\nimport { appendCustomerListSortParams } from '../listSorting'\n\ntype DictionaryOptionWithTone = AdvancedFilterOption & FilterOption\n\nfunction makePeoplePresets(): FilterPreset[] {\n return [\n {\n id: 'recently-active',\n labelKey: 'customers.people.presets.recentlyActive',\n iconName: 'clock',\n build: ({ now }) => {\n const cutoff = new Date(now.getTime() - 7 * 24 * 3600 * 1000).toISOString().slice(0, 10)\n return makeRuleTree({ field: 'next_interaction_at', operator: 'is_after', value: cutoff })\n },\n },\n {\n id: 'my-contacts',\n labelKey: 'customers.people.presets.myContacts',\n requiresUser: true,\n build: ({ userId }) => makeRuleTree({ field: 'owner_user_id', operator: 'is', value: userId }),\n },\n {\n id: 'hot-leads',\n labelKey: 'customers.people.presets.hotLeads',\n build: () => makeRuleTree({ field: 'lifecycle_stage', operator: 'is', value: 'lead' }),\n },\n {\n id: 'stale-30',\n labelKey: 'customers.people.presets.stale30',\n build: ({ now }) => {\n const cutoff = new Date(now.getTime() - 30 * 24 * 3600 * 1000).toISOString().slice(0, 10)\n return makeRuleTree({ field: 'next_interaction_at', operator: 'is_before', value: cutoff })\n },\n },\n ]\n}\n\ntype PersonRow = {\n id: string\n name: string\n description?: string | null\n email?: string | null\n phone?: string | null\n firstName?: string | null\n lastName?: string | null\n preferredName?: string | null\n jobTitle?: string | null\n department?: string | null\n seniority?: string | null\n timezone?: string | null\n linkedInUrl?: string | null\n twitterUrl?: string | null\n companyEntityId?: string | null\n status?: string | null\n lifecycleStage?: string | null\n nextInteractionAt?: string | null\n nextInteractionName?: string | null\n nextInteractionIcon?: string | null\n nextInteractionColor?: string | null\n organizationId?: string | null\n source?: string | null\n ownerUserId?: string | null\n} & Record<string, unknown>\n\ntype PeopleResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n page?: number\n totalPages?: number\n}\n\ntype DictionaryKindKey = CustomerDictionaryKind\ntype DictionaryMap = CustomerDictionaryMap\n\nconst NO_MATCH_TAG_SENTINEL = '__no_match__'\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\nfunction mapApiItem(item: Record<string, unknown>): PersonRow | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const name = typeof item.display_name === 'string' ? item.display_name : ''\n const description = typeof item.description === 'string' ? item.description : null\n const email = typeof item.primary_email === 'string' ? item.primary_email : null\n const phone = typeof item.primary_phone === 'string' ? item.primary_phone : null\n const firstName = typeof item.first_name === 'string' ? item.first_name : null\n const lastName = typeof item.last_name === 'string' ? item.last_name : null\n const preferredName = typeof item.preferred_name === 'string' ? item.preferred_name : null\n const jobTitle = typeof item.job_title === 'string' ? item.job_title : null\n const department = typeof item.department === 'string' ? item.department : null\n const seniority = typeof item.seniority === 'string' ? item.seniority : null\n const timezone = typeof item.timezone === 'string' ? item.timezone : null\n const linkedInUrl = typeof item.linked_in_url === 'string' ? item.linked_in_url : null\n const twitterUrl = typeof item.twitter_url === 'string' ? item.twitter_url : null\n const companyEntityId = typeof item.company_entity_id === 'string' ? item.company_entity_id : null\n const status = typeof item.status === 'string' ? item.status : null\n const lifecycleStage = typeof item.lifecycle_stage === 'string' ? item.lifecycle_stage : null\n const nextInteractionAt = typeof item.next_interaction_at === 'string' ? item.next_interaction_at : null\n const nextInteractionName = typeof item.next_interaction_name === 'string' ? item.next_interaction_name : null\n const nextInteractionIcon = typeof item.next_interaction_icon === 'string' ? item.next_interaction_icon : null\n const nextInteractionColor = typeof item.next_interaction_color === 'string' ? item.next_interaction_color : null\n const organizationId = typeof item.organization_id === 'string' ? item.organization_id : null\n const source = typeof item.source === 'string' ? item.source : null\n const customFields: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) {\n customFields[key] = value\n }\n }\n return withDataTableNamespaces({\n id,\n name,\n description,\n email,\n phone,\n firstName,\n lastName,\n preferredName,\n jobTitle,\n department,\n seniority,\n timezone,\n linkedInUrl,\n twitterUrl,\n companyEntityId,\n status,\n lifecycleStage,\n nextInteractionAt,\n nextInteractionName,\n nextInteractionIcon,\n nextInteractionColor,\n organizationId,\n source,\n ...customFields,\n }, item)\n}\n\nexport default function CustomersPeoplePage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [rows, setRows] = React.useState<PersonRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize, setPageSize] = React.useState(20)\n const [sorting, setSorting] = React.useState<SortingState>([])\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const pathname = usePathname()\n const searchParams = useSearchParams()\n // One-shot URL hydration used as the hook's initial value. The hook is the\n // single source of truth from this point on \u2014 the page MUST NOT keep a\n // parallel `useState<AdvancedFilterTree>` (see spec \"Migration & Backward\n // Compatibility\" \u2192 state ownership).\n const initialFilterTree = React.useMemo<AdvancedFilterTree>(() => {\n if (!searchParams) return createEmptyTree()\n const record: Record<string, string> = {}\n searchParams.forEach((value, key) => {\n if (key.startsWith('filter[')) record[key] = value\n })\n const v2 = deserializeTree(record)\n if (v2) return v2\n const flat = deserializeAdvancedFilter(record)\n if (flat) return flatToTree(flat)\n return createEmptyTree()\n // searchParams is intentionally evaluated once on mount \u2014 subsequent URL\n // changes flow through the hook, not back through hydration.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n // `filterPanel` lives at the top of the component so derived state below\n // (URL params, data fetch, export config) can read `filterPanel.appliedTree`\n // directly. Real `FilterFieldDef[]` arrives later from `useAutoDiscoveredFields`\n // (it depends on columns) and is synced into the hook via a small effect at\n // the bottom of the component. The hook reads fields through a ref at\n // validation time only \u2014 first validation cannot fire before user input, by\n // which point fields have settled, so the empty initial value is safe.\n const [panelFields, setPanelFields] = React.useState<FilterFieldDef[]>([])\n const [filtersOpen, setFiltersOpen] = React.useState(false)\n const filtersTriggerRef = React.useRef<HTMLButtonElement | null>(null)\n const filterPanel = useAdvancedFilterTree({\n initial: initialFilterTree,\n fields: panelFields,\n onApply: () => setPage(1),\n })\n const advancedFilterState = filterPanel.appliedTree\n const handleAdvancedFilterClear = React.useCallback(() => {\n filterPanel.clear()\n setPage(1)\n }, [filterPanel])\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [dictionaryMaps, setDictionaryMaps] = React.useState<Record<DictionaryKindKey, DictionaryMap>>(createEmptyCustomerDictionaryMaps())\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const t = useT()\n const router = useRouter()\n const handlePageSizeChange = React.useCallback((newSize: number) => {\n setPageSize(newSize)\n setPage(1)\n }, [])\n const handleSortingChange = React.useCallback((nextSorting: SortingState) => {\n setSorting(nextSorting)\n setPage(1)\n }, [])\n\n const bulkMutationContextId = 'customers-people-list:bulk-delete'\n const { runMutation: runBulkMutation, retryLastMutation: retryBulkMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: bulkMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const singleMutationContextId = 'customers-people-list:single-delete'\n const { runMutation: runSingleMutation, retryLastMutation: retrySingleMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: singleMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const fetchDictionaryEntries = React.useCallback(async (kind: DictionaryKindKey) => {\n try {\n const data = await ensureCustomerDictionary(queryClient, kind, scopeVersion)\n setDictionaryMaps((prev) => ({\n ...prev,\n [kind]: data.map,\n }))\n return data.entries\n } catch {\n return []\n }\n }, [queryClient, scopeVersion])\n const dictionaryOptions = React.useMemo(() => {\n const toOptions = (map?: DictionaryMap | null): DictionaryOptionWithTone[] =>\n Object.values(map ?? {})\n .map((entry) => {\n const tone = mapDictionaryColorToTone(entry.color)\n const option: DictionaryOptionWithTone = { value: entry.value, label: entry.label }\n if (tone) option.tone = tone\n return option\n })\n .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))\n return {\n statuses: toOptions(dictionaryMaps.statuses),\n sources: toOptions(dictionaryMaps.sources),\n lifecycleStages: toOptions(dictionaryMaps['lifecycle-stages']),\n }\n }, [dictionaryMaps])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadAll() {\n if (cancelled) return\n setDictionaryMaps(createEmptyCustomerDictionaryMaps())\n await Promise.all([\n fetchDictionaryEntries('statuses'),\n fetchDictionaryEntries('sources'),\n fetchDictionaryEntries('lifecycle-stages'),\n ])\n }\n loadAll().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [fetchDictionaryEntries, scopeVersion, reloadToken])\n\n const { data: customFieldDefs = [] } = useCustomFieldDefs(\n [E.customers.customer_entity, E.customers.customer_person_profile],\n { keyExtras: [scopeVersion, reloadToken] },\n )\n const currentUserId = useCurrentUserId()\n const [ownerFilterOptions, setOwnerFilterOptions] = React.useState<AdvancedFilterOption[]>([])\n React.useEffect(() => {\n const controller = new AbortController()\n let cancelled = false\n void fetchAssignableStaffMembers('', { pageSize: 100, signal: controller.signal })\n .then((items) => {\n if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items))\n })\n .catch(() => {\n if (!cancelled) setOwnerFilterOptions([])\n })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [scopeVersion])\n const resolvedOwnerFilterOptions = React.useMemo(\n () => ensureCurrentUserFilterOption(\n ownerFilterOptions,\n currentUserId,\n t('customers.filters.currentUser', 'Current user'),\n ),\n [currentUserId, ownerFilterOptions, t],\n )\n const loadOwnerFilterOptions = React.useCallback(async (query?: string): Promise<AdvancedFilterOption[]> => {\n const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })\n return mapAssignableStaffToFilterOptions(items)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n appendCustomerListSortParams(params, sorting)\n if (search.trim()) params.set('search', search.trim())\n const advancedParams = serializeTree(advancedFilterState)\n for (const [key, val] of Object.entries(advancedParams)) {\n params.set(key, val)\n }\n return params.toString()\n }, [advancedFilterState, page, pageSize, search, sorting])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n // Mirror page state into the URL so a refresh restores the same filter tree,\n // including nested subgroups. Without this effect the People page would\n // discard everything the user typed into the filter panel on refresh\n // (the previous behavior \u2014 top-level rules only \"appeared\" to survive\n // because a stale localStorage perspective snapshot was being re-applied).\n const queryRef = React.useRef(searchParams?.toString() ?? '')\n React.useEffect(() => {\n if (!pathname) return\n const params = new URLSearchParams()\n if (search.trim().length) params.set('search', search.trim())\n if (page > 1) params.set('page', String(page))\n appendCustomerListSortParams(params, sorting)\n const advancedParams = serializeTree(advancedFilterState)\n for (const [key, val] of Object.entries(advancedParams)) {\n params.set(key, val)\n }\n const next = params.toString()\n if (queryRef.current === next) return\n queryRef.current = next\n router.replace(next ? `${pathname}?${next}` : pathname, { scroll: false })\n }, [pathname, router, page, search, sorting, advancedFilterState])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/people', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/people', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: PeopleResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<PeopleResponse>(`/api/customers/people?${queryParams}`, undefined, { fallback })\n if (!call.ok) {\n const errorPayload = call.result as { error?: string } | undefined\n const message = typeof errorPayload?.error === 'string' ? errorPayload.error : t('customers.people.list.error.load')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map((item) => mapApiItem(item as Record<string, unknown>)).filter((row): row is PersonRow => !!row))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n setCacheStatus(null)\n const message = err instanceof Error ? err.message : t('customers.people.list.error.load')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (person: PersonRow) => {\n if (!person?.id) return\n const name = person.name || t('customers.people.list.deleteFallbackName')\n const confirmed = await confirm({\n title: t('customers.people.list.deleteConfirm', undefined, { name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runSingleMutation({\n operation: async () => {\n await apiCallOrThrow(\n `/api/customers/people?id=${encodeURIComponent(person.id)}`,\n {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n },\n { errorMessage: t('customers.people.list.deleteError') },\n )\n },\n context: {\n formId: singleMutationContextId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n retryLastMutation: retrySingleMutation,\n },\n })\n setRows((prev) => prev.filter((row) => row.id !== person.id))\n setTotal((prev) => Math.max(prev - 1, 0))\n handleRefresh()\n flash(t('customers.people.list.deleteSuccess'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customers.people.list.deleteError')\n flash(message, 'error')\n }\n }, [confirm, handleRefresh, retrySingleMutation, runSingleMutation, singleMutationContextId, t])\n\n const handleBulkDelete = React.useCallback(async (selectedRows: PersonRow[]) => {\n const confirmed = await confirm({\n title: t('customers.people.list.bulkDelete.title', 'Delete {count} people?', { count: selectedRows.length }),\n description: t('customers.people.list.bulkDelete.description', 'This action cannot be undone.'),\n variant: 'destructive',\n })\n if (!confirmed) return false\n\n const { succeeded, failures } = await runBulkMutation({\n operation: async () =>\n runBulkDelete(\n selectedRows,\n async (row) => {\n await apiCallOrThrow(`/api/customers/people?id=${encodeURIComponent(row.id)}`, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n })\n },\n {\n fallbackErrorMessage: t('customers.people.list.deleteError', 'Failed to delete person.'),\n logTag: 'customers.people.list',\n progress: {\n jobType: 'customers.people.bulk_delete',\n name: t('customers.people.list.bulkDelete.progressName', 'Delete selected people'),\n description: t(\n 'customers.people.list.bulkDelete.progressDescription',\n '{count} people selected for deletion',\n { count: selectedRows.length },\n ),\n meta: { source: 'customers.people.list' },\n },\n },\n ),\n context: {\n formId: bulkMutationContextId,\n resourceKind: 'customers.person',\n retryLastMutation: retryBulkMutation,\n },\n })\n\n if (succeeded.length > 0) {\n const succeededIds = new Set(succeeded.map((r) => r.id))\n setRows((prev) => prev.filter((r) => !succeededIds.has(r.id)))\n setTotal((prev) => Math.max(0, prev - succeeded.length))\n setReloadToken((prev) => prev + 1)\n if (succeeded.length > 1) {\n coalesceLastOperations(succeeded.length, {\n commandId: 'customers.people.delete',\n actionLabel: t('customers.people.list.bulkDelete.operationLabel', 'Delete {count} people', { count: succeeded.length }),\n resourceKind: 'customers.person',\n })\n }\n if (failures.length === 0) {\n flash(\n t('customers.people.list.bulkDelete.success', '{count} people deleted', { count: succeeded.length }),\n 'success',\n )\n } else {\n flash(\n t('customers.people.list.bulkDelete.partial', '{deleted} of {total} people deleted; {failed} failed', {\n deleted: succeeded.length,\n total: selectedRows.length,\n failed: failures.length,\n }),\n 'warning',\n )\n }\n }\n\n for (const group of groupBulkDeleteFailures(failures)) {\n const message = group.count === 1\n ? group.sampleMessage\n : t(\n 'customers.people.list.bulkDelete.failedGroup',\n '{count} people could not be deleted: {message}',\n { count: group.count, message: group.sampleMessage },\n )\n flash(message, 'error')\n }\n\n return succeeded.length > 0\n }, [bulkMutationContextId, confirm, retryBulkMutation, runBulkMutation, t])\n\n const columns = React.useMemo<ColumnDef<PersonRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>\n const renderDictionaryCell = (kind: DictionaryKindKey, rawValue: string | null | undefined) => (\n <DictionaryValue\n value={rawValue}\n map={dictionaryMaps[kind]}\n fallback={rawValue ? <span>{rawValue}</span> : noValue}\n className=\"text-sm\"\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\"\n iconClassName=\"h-4 w-4\"\n colorClassName=\"h-3 w-3 rounded-full\"\n />\n )\n\n const renderCustomFieldCell = (value: unknown) => {\n if (value == null) return noValue\n if (Array.isArray(value)) {\n if (!value.length) return noValue\n const normalized = normalizeCollectionLabels(\n value.map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item\n return String(item)\n }),\n )\n if (!normalized.length) return noValue\n return <CollectionPreviewCell labels={normalized} maxVisible={2} />\n }\n if (typeof value === 'boolean') {\n return (\n <span className=\"text-sm\">\n {value\n ? t('customers.people.list.booleanYes', 'Yes')\n : t('customers.people.list.booleanNo', 'No')}\n </span>\n )\n }\n const stringValue = typeof value === 'string' ? value.trim() : String(value)\n if (!stringValue) return noValue\n return <span className=\"text-sm\">{stringValue}</span>\n }\n\n const baseColumns: ColumnDef<PersonRow>[] = [\n {\n accessorKey: 'name',\n header: t('customers.people.list.columns.name'),\n meta: {\n alwaysVisible: true,\n columnChooserGroup: 'Basic Info',\n filterKey: 'display_name',\n filterGroup: 'CRM',\n maxWidth: '240px',\n },\n cell: ({ row }) => (\n <Link href={`/backend/customers/people-v2/${row.original.id}`} className=\"font-medium hover:underline\">\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customers.people.list.columns.email'),\n meta: {\n columnChooserGroup: 'Contact',\n filterKey: 'primary_email',\n filterGroup: 'Contact',\n filterIconName: 'mail',\n maxWidth: '220px',\n },\n cell: ({ row }) => row.original.email || <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>,\n },\n {\n accessorKey: 'status',\n header: t('customers.people.list.columns.status'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.statuses,\n columnChooserGroup: 'Basic Info',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('statuses', row.original.status),\n },\n {\n accessorKey: 'lifecycleStage',\n header: t('customers.people.list.columns.lifecycleStage'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.lifecycleStages,\n columnChooserGroup: 'Basic Info',\n filterKey: 'lifecycle_stage',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('lifecycle-stages', row.original.lifecycleStage),\n },\n {\n accessorKey: 'nextInteractionAt',\n header: t('customers.people.list.columns.nextInteraction'),\n meta: {\n columnChooserGroup: 'Dates',\n filterKey: 'next_interaction_at',\n filterGroup: 'Activity',\n filterIconName: 'calendar',\n tooltipContent: (row: PersonRow) => {\n if (!row.nextInteractionAt) return undefined\n const date = formatDate(row.nextInteractionAt, '')\n const name = row.nextInteractionName || ''\n return [date, name].filter(Boolean).join(' - ')\n },\n },\n cell: ({ row }) =>\n row.original.nextInteractionAt\n ? (\n <div className=\"flex items-start gap-2 text-sm\">\n {row.original.nextInteractionIcon ? (\n <span className=\"mt-0.5 inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-card\">\n {renderDictionaryIcon(row.original.nextInteractionIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <div className=\"flex flex-col\">\n <span>{formatDate(row.original.nextInteractionAt, t('customers.people.list.noValue'))}</span>\n {row.original.nextInteractionName ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.nextInteractionName}</span>\n ) : null}\n </div>\n {row.original.nextInteractionColor ? (\n <span className=\"mt-1\">\n {renderDictionaryColor(row.original.nextInteractionColor, 'h-3 w-3 rounded-full border border-border')}\n </span>\n ) : null}\n </div>\n )\n : <span className=\"text-muted-foreground text-sm\">{t('customers.people.list.noValue')}</span>,\n },\n {\n accessorKey: 'source',\n header: t('customers.people.list.columns.source'),\n meta: {\n filterType: 'select' as const,\n filterOptions: dictionaryOptions.sources,\n columnChooserGroup: 'Basic Info',\n filterGroup: 'CRM',\n },\n cell: ({ row }) => renderDictionaryCell('sources', row.original.source),\n },\n {\n accessorKey: 'ownerUserId',\n header: t('customers.people.list.columns.owner', 'Owner'),\n meta: {\n columnChooserGroup: 'CRM',\n filterType: 'select',\n filterOptions: resolvedOwnerFilterOptions,\n filterLoadOptions: loadOwnerFilterOptions,\n filterGroup: 'CRM',\n filterIconName: 'user-round',\n filterKey: 'owner_user_id',\n hidden: true,\n },\n cell: ({ row }) => row.original.ownerUserId ?? null,\n },\n {\n accessorKey: 'firstName',\n header: t('customers.people.form.firstName', 'First name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.first_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.firstName || noValue,\n },\n {\n accessorKey: 'lastName',\n header: t('customers.people.form.lastName', 'Last name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.last_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.lastName || noValue,\n },\n {\n accessorKey: 'preferredName',\n header: t('customers.people.form.preferredName', 'Preferred name'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.preferred_name',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.preferredName || noValue,\n },\n {\n accessorKey: 'jobTitle',\n header: t('customers.people.form.jobTitle', 'Job title'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.job_title',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.jobTitle || noValue,\n },\n {\n accessorKey: 'department',\n header: t('customers.people.detail.fields.department', 'Department'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.department',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.department || noValue,\n },\n {\n accessorKey: 'seniority',\n header: t('customers.people.detail.fields.seniority', 'Seniority'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.seniority',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.seniority || noValue,\n },\n {\n accessorKey: 'timezone',\n header: t('customers.people.detail.fields.timezone', 'Timezone'),\n meta: {\n columnChooserGroup: 'Profile',\n hidden: true,\n filterKey: 'person_profile.timezone',\n filterGroup: 'Profile',\n },\n cell: ({ row }) => row.original.timezone || noValue,\n },\n {\n accessorKey: 'linkedInUrl',\n header: t('customers.people.detail.fields.linkedIn', 'LinkedIn'),\n meta: {\n columnChooserGroup: 'Socials',\n hidden: true,\n filterKey: 'person_profile.linked_in_url',\n filterGroup: 'Socials',\n },\n cell: ({ row }) => row.original.linkedInUrl || noValue,\n },\n {\n accessorKey: 'twitterUrl',\n header: t('customers.people.detail.fields.twitter', 'Twitter'),\n meta: {\n columnChooserGroup: 'Socials',\n hidden: true,\n filterKey: 'person_profile.twitter_url',\n filterGroup: 'Socials',\n },\n cell: ({ row }) => row.original.twitterUrl || noValue,\n },\n {\n accessorKey: 'description',\n header: t('customers.people.form.description', 'Description'),\n meta: {\n columnChooserGroup: 'Notes',\n hidden: true,\n filterKey: 'description',\n filterGroup: 'Notes',\n },\n cell: ({ row }) => row.original.description || noValue,\n },\n ]\n\n const customColumns = customFieldDefs\n .filter((def) => supportsCustomFieldColumn(def))\n .map<ColumnDef<PersonRow>>((def) => ({\n accessorKey: `cf_${def.key}`,\n header: def.label || def.key,\n enableSorting: true,\n meta: {\n columnChooserGroup: def.group?.title ?? 'Custom Fields',\n filterGroup: def.group?.title ?? 'Custom Fields',\n filterType: mapCustomFieldKindToFilterType(def.kind),\n filterOptions: normalizeCustomFieldFilterOptions(def.options),\n hidden: def.listVisible === false,\n maxWidth: '220px',\n },\n cell: ({ getValue }) => renderCustomFieldCell(getValue()),\n }))\n\n return [...baseColumns, ...customColumns]\n }, [customFieldDefs, dictionaryMaps, dictionaryOptions, loadOwnerFilterOptions, resolvedOwnerFilterOptions, t])\n\n const { advancedFilterFields } = useAutoDiscoveredFields({ columns, customFieldDefs })\n\n // Sync auto-discovered fields into the `filterPanel` declared at the top of\n // the component. See the comment on the `panelFields` state for why this\n // late-binding is safe. Bail out by content (field-key list) \u2014 every render\n // of `useAutoDiscoveredFields` produces fresh `FilterFieldDef` object refs\n // even when the set of fields hasn't actually changed, so a naive reference\n // setState would loop (\"Maximum update depth exceeded\").\n React.useEffect(() => {\n setPanelFields((prev) => {\n if (prev === advancedFilterFields) return prev\n if (prev.length === advancedFilterFields.length) {\n let same = true\n for (let i = 0; i < prev.length; i++) {\n if (prev[i].key !== advancedFilterFields[i].key) { same = false; break }\n }\n if (same) return prev\n }\n return advancedFilterFields\n })\n }, [advancedFilterFields])\n\n const peoplePresets = React.useMemo<FilterPreset[]>(() => makePeoplePresets(), [])\n\n return (\n <Page>\n <PageBody>\n <DataTable<PersonRow>\n stickyFirstColumn\n stickyActionsColumn\n title={t('customers.people.list.title')}\n refreshButton={{\n label: t('customers.people.list.actions.refresh'),\n onRefresh: () => { setSearch(''); setPage(1); handleRefresh() },\n }}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customers/people/create\">\n {t('customers.people.list.actions.new')}\n </Link>\n </Button>\n )}\n columns={columns}\n columnChooser={{ auto: true }}\n data={rows}\n exporter={exportConfig}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customers.people.list.searchPlaceholder')}\n entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}\n perspective={{ tableId: 'customers.people.list' }}\n onRowClick={(row) => router.push(`/backend/customers/people-v2/${row.id}`)}\n sortable\n manualSorting\n sorting={sorting}\n onSortingChange={handleSortingChange}\n bulkActions={[\n {\n id: 'delete',\n label: t('customers.people.list.bulkDelete.action', 'Delete selected'),\n destructive: true,\n onExecute: handleBulkDelete,\n },\n ]}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customers.people.list.actions.view'),\n onSelect: () => { router.push(`/backend/customers/people-v2/${row.id}`) },\n },\n {\n id: 'open-new-tab',\n label: t('customers.people.list.actions.openInNewTab'),\n onSelect: () => window.open(`/backend/customers/people-v2/${row.id}`, '_blank', 'noopener'),\n },\n {\n id: 'delete',\n label: t('customers.people.list.actions.delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n advancedFilter={{\n auto: true,\n value: filterPanel.tree,\n onChange: filterPanel.setTree,\n onApply: () => filterPanel.flush(),\n onClear: handleAdvancedFilterClear,\n triggerRef: filtersTriggerRef,\n externalPopover: true,\n onTriggerClick: () => setFiltersOpen((prev) => !prev),\n onApplyTree: (tree) => {\n filterPanel.replaceTree(tree)\n setPage(1)\n },\n }}\n activeFilterChips={(\n <ActiveFilterChips\n tree={filterPanel.tree}\n fields={advancedFilterFields}\n popoverOpen={filtersOpen}\n onRemoveNode={(id) => filterPanel.dispatch({ type: 'removeNode', nodeId: id })}\n onOpen={() => setFiltersOpen(true)}\n />\n )}\n filterAwareEmptyState={{\n active: advancedFilterState.root.children.length > 0,\n entityNamePlural: t('customers.people.entityPlural', 'people'),\n canRemoveLast: filterPanel.tree.root.children.length > 0,\n onClearAll: handleAdvancedFilterClear,\n onRemoveLast: () => filterPanel.dispatch({ type: 'removeLast' }),\n }}\n virtualized\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange }}\n isLoading={isLoading}\n />\n <AdvancedFilterPanel\n fields={advancedFilterFields}\n value={filterPanel.tree}\n onChange={filterPanel.setTree}\n onApply={filterPanel.flush}\n onClear={handleAdvancedFilterClear}\n onFlush={filterPanel.flush}\n pendingErrors={filterPanel.pendingErrors}\n userId={currentUserId}\n presets={peoplePresets}\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n triggerRef={filtersTriggerRef}\n savedFilterStorageKey=\"customers.people.list\"\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.4-develop.4011.1.4f3ed9ae3e",
3
+ "version": "0.6.4-develop.4015.1.efaafadf79",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -243,16 +243,16 @@
243
243
  "zod": "^4.4.3"
244
244
  },
245
245
  "peerDependencies": {
246
- "@open-mercato/ai-assistant": "0.6.4-develop.4011.1.4f3ed9ae3e",
247
- "@open-mercato/shared": "0.6.4-develop.4011.1.4f3ed9ae3e",
248
- "@open-mercato/ui": "0.6.4-develop.4011.1.4f3ed9ae3e",
246
+ "@open-mercato/ai-assistant": "0.6.4-develop.4015.1.efaafadf79",
247
+ "@open-mercato/shared": "0.6.4-develop.4015.1.efaafadf79",
248
+ "@open-mercato/ui": "0.6.4-develop.4015.1.efaafadf79",
249
249
  "react": "^19.0.0",
250
250
  "react-dom": "^19.0.0"
251
251
  },
252
252
  "devDependencies": {
253
- "@open-mercato/ai-assistant": "0.6.4-develop.4011.1.4f3ed9ae3e",
254
- "@open-mercato/shared": "0.6.4-develop.4011.1.4f3ed9ae3e",
255
- "@open-mercato/ui": "0.6.4-develop.4011.1.4f3ed9ae3e",
253
+ "@open-mercato/ai-assistant": "0.6.4-develop.4015.1.efaafadf79",
254
+ "@open-mercato/shared": "0.6.4-develop.4015.1.efaafadf79",
255
+ "@open-mercato/ui": "0.6.4-develop.4015.1.efaafadf79",
256
256
  "@testing-library/dom": "^10.4.1",
257
257
  "@testing-library/jest-dom": "^6.9.1",
258
258
  "@testing-library/react": "^16.3.1",
@@ -114,6 +114,12 @@ const crud = makeCrudRoute({
114
114
  ],
115
115
  sortFieldMap: {
116
116
  name: 'display_name',
117
+ email: 'primary_email',
118
+ primaryEmail: 'primary_email',
119
+ status: 'status',
120
+ lifecycleStage: 'lifecycle_stage',
121
+ source: 'source',
122
+ nextInteractionAt: 'next_interaction_at',
117
123
  createdAt: 'created_at',
118
124
  updatedAt: 'updated_at',
119
125
  },
@@ -108,6 +108,12 @@ const crud = makeCrudRoute({
108
108
  ],
109
109
  sortFieldMap: {
110
110
  name: 'display_name',
111
+ email: 'primary_email',
112
+ primaryEmail: 'primary_email',
113
+ status: 'status',
114
+ lifecycleStage: 'lifecycle_stage',
115
+ source: 'source',
116
+ nextInteractionAt: 'next_interaction_at',
111
117
  createdAt: 'created_at',
112
118
  updatedAt: 'updated_at',
113
119
  },
@@ -5,7 +5,7 @@ import Link from 'next/link'
5
5
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
7
  import { DataTable, type DataTableExportFormat, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'
8
- import type { ColumnDef } from '@tanstack/react-table'
8
+ import type { ColumnDef, SortingState } from '@tanstack/react-table'
9
9
  import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
11
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
@@ -53,6 +53,7 @@ import {
53
53
  mapAssignableStaffToFilterOptions,
54
54
  } from '../../../components/detail/assignableStaff'
55
55
  import { CollectionPreviewCell, normalizeCollectionLabels } from '../../../components/list/CollectionPreviewCell'
56
+ import { appendCustomerListSortParams } from '../listSorting'
56
57
 
57
58
  type DictionaryOptionWithTone = AdvancedFilterOption & FilterOption
58
59
 
@@ -191,7 +192,7 @@ export default function CustomersCompaniesPage() {
191
192
  const [rows, setRows] = React.useState<CompanyRow[]>([])
192
193
  const [page, setPage] = React.useState(1)
193
194
  const [pageSize, setPageSize] = React.useState(20)
194
- const [sorting, setSorting] = React.useState<import('@tanstack/react-table').SortingState>([])
195
+ const [sorting, setSorting] = React.useState<SortingState>([])
195
196
  const [total, setTotal] = React.useState(0)
196
197
  const [totalPages, setTotalPages] = React.useState(1)
197
198
  const [search, setSearch] = React.useState('')
@@ -246,6 +247,10 @@ export default function CustomersCompaniesPage() {
246
247
  setPageSize(newSize)
247
248
  setPage(1)
248
249
  }, [])
250
+ const handleSortingChange = React.useCallback((nextSorting: SortingState) => {
251
+ setSorting(nextSorting)
252
+ setPage(1)
253
+ }, [])
249
254
 
250
255
  const bulkMutationContextId = 'customers-companies-list:bulk-delete'
251
256
  const { runMutation: runBulkMutation, retryLastMutation: retryBulkMutation } = useGuardedMutation<{
@@ -350,10 +355,7 @@ export default function CustomersCompaniesPage() {
350
355
  const params = new URLSearchParams()
351
356
  params.set('page', String(page))
352
357
  params.set('pageSize', String(pageSize))
353
- if (sorting.length > 0) {
354
- params.set('sort', sorting[0].id)
355
- params.set('order', sorting[0].desc ? 'desc' : 'asc')
356
- }
358
+ appendCustomerListSortParams(params, sorting)
357
359
  if (search.trim()) params.set('search', search.trim())
358
360
  const advancedParams = serializeTree(advancedFilterState)
359
361
  for (const [key, val] of Object.entries(advancedParams)) {
@@ -374,10 +376,7 @@ export default function CustomersCompaniesPage() {
374
376
  const params = new URLSearchParams()
375
377
  if (search.trim().length) params.set('search', search.trim())
376
378
  if (page > 1) params.set('page', String(page))
377
- if (sorting.length > 0) {
378
- params.set('sort', sorting[0].id)
379
- params.set('order', sorting[0].desc ? 'desc' : 'asc')
380
- }
379
+ appendCustomerListSortParams(params, sorting)
381
380
  const advancedParams = serializeTree(advancedFilterState)
382
381
  for (const [key, val] of Object.entries(advancedParams)) {
383
382
  params.set(key, val)
@@ -820,6 +819,7 @@ export default function CustomersCompaniesPage() {
820
819
  .map<ColumnDef<CompanyRow>>((def) => ({
821
820
  accessorKey: `cf_${def.key}`,
822
821
  header: def.label || def.key,
822
+ enableSorting: true,
823
823
  meta: {
824
824
  columnChooserGroup: def.group?.title ?? 'Custom Fields',
825
825
  filterGroup: def.group?.title ?? 'Custom Fields',
@@ -887,8 +887,9 @@ export default function CustomersCompaniesPage() {
887
887
  onRowClick={(row) => router.push(`/backend/customers/companies-v2/${row.id}`)}
888
888
  perspective={{ tableId: 'customers.companies.list' }}
889
889
  sortable
890
+ manualSorting
890
891
  sorting={sorting}
891
- onSortingChange={setSorting}
892
+ onSortingChange={handleSortingChange}
892
893
  bulkActions={[
893
894
  {
894
895
  id: 'delete',
@@ -0,0 +1,27 @@
1
+ import type { SortingState } from '@tanstack/react-table'
2
+
3
+ const CUSTOMER_LIST_SORT_FIELDS: Record<string, string> = {
4
+ name: 'name',
5
+ email: 'primaryEmail',
6
+ status: 'status',
7
+ lifecycleStage: 'lifecycleStage',
8
+ source: 'source',
9
+ nextInteractionAt: 'nextInteractionAt',
10
+ }
11
+
12
+ export function resolveCustomerListSortField(columnId: string): string | null {
13
+ const normalized = columnId.trim()
14
+ if (!normalized) return null
15
+ if (normalized.startsWith('cf:')) return normalized
16
+ if (normalized.startsWith('cf_')) return `cf:${normalized.slice(3)}`
17
+ return CUSTOMER_LIST_SORT_FIELDS[normalized] ?? null
18
+ }
19
+
20
+ export function appendCustomerListSortParams(params: URLSearchParams, sorting: SortingState): void {
21
+ const activeSort = sorting[0]
22
+ if (!activeSort) return
23
+ const sortField = resolveCustomerListSortField(activeSort.id)
24
+ if (!sortField) return
25
+ params.set('sortField', sortField)
26
+ params.set('sortDir', activeSort.desc ? 'desc' : 'asc')
27
+ }
@@ -5,7 +5,7 @@ import Link from 'next/link'
5
5
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
7
  import { DataTable, type DataTableExportFormat, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'
8
- import type { ColumnDef } from '@tanstack/react-table'
8
+ import type { ColumnDef, SortingState } from '@tanstack/react-table'
9
9
  import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
11
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
@@ -53,6 +53,7 @@ import {
53
53
  mapAssignableStaffToFilterOptions,
54
54
  } from '../../../components/detail/assignableStaff'
55
55
  import { CollectionPreviewCell, normalizeCollectionLabels } from '../../../components/list/CollectionPreviewCell'
56
+ import { appendCustomerListSortParams } from '../listSorting'
56
57
 
57
58
  type DictionaryOptionWithTone = AdvancedFilterOption & FilterOption
58
59
 
@@ -199,7 +200,7 @@ export default function CustomersPeoplePage() {
199
200
  const [rows, setRows] = React.useState<PersonRow[]>([])
200
201
  const [page, setPage] = React.useState(1)
201
202
  const [pageSize, setPageSize] = React.useState(20)
202
- const [sorting, setSorting] = React.useState<import('@tanstack/react-table').SortingState>([])
203
+ const [sorting, setSorting] = React.useState<SortingState>([])
203
204
  const [total, setTotal] = React.useState(0)
204
205
  const [totalPages, setTotalPages] = React.useState(1)
205
206
  const [search, setSearch] = React.useState('')
@@ -256,6 +257,10 @@ export default function CustomersPeoplePage() {
256
257
  setPageSize(newSize)
257
258
  setPage(1)
258
259
  }, [])
260
+ const handleSortingChange = React.useCallback((nextSorting: SortingState) => {
261
+ setSorting(nextSorting)
262
+ setPage(1)
263
+ }, [])
259
264
 
260
265
  const bulkMutationContextId = 'customers-people-list:bulk-delete'
261
266
  const { runMutation: runBulkMutation, retryLastMutation: retryBulkMutation } = useGuardedMutation<{
@@ -361,10 +366,7 @@ export default function CustomersPeoplePage() {
361
366
  const params = new URLSearchParams()
362
367
  params.set('page', String(page))
363
368
  params.set('pageSize', String(pageSize))
364
- if (sorting.length > 0) {
365
- params.set('sort', sorting[0].id)
366
- params.set('order', sorting[0].desc ? 'desc' : 'asc')
367
- }
369
+ appendCustomerListSortParams(params, sorting)
368
370
  if (search.trim()) params.set('search', search.trim())
369
371
  const advancedParams = serializeTree(advancedFilterState)
370
372
  for (const [key, val] of Object.entries(advancedParams)) {
@@ -386,10 +388,7 @@ export default function CustomersPeoplePage() {
386
388
  const params = new URLSearchParams()
387
389
  if (search.trim().length) params.set('search', search.trim())
388
390
  if (page > 1) params.set('page', String(page))
389
- if (sorting.length > 0) {
390
- params.set('sort', sorting[0].id)
391
- params.set('order', sorting[0].desc ? 'desc' : 'asc')
392
- }
391
+ appendCustomerListSortParams(params, sorting)
393
392
  const advancedParams = serializeTree(advancedFilterState)
394
393
  for (const [key, val] of Object.entries(advancedParams)) {
395
394
  params.set(key, val)
@@ -847,6 +846,7 @@ export default function CustomersPeoplePage() {
847
846
  .map<ColumnDef<PersonRow>>((def) => ({
848
847
  accessorKey: `cf_${def.key}`,
849
848
  header: def.label || def.key,
849
+ enableSorting: true,
850
850
  meta: {
851
851
  columnChooserGroup: def.group?.title ?? 'Custom Fields',
852
852
  filterGroup: def.group?.title ?? 'Custom Fields',
@@ -914,8 +914,9 @@ export default function CustomersPeoplePage() {
914
914
  perspective={{ tableId: 'customers.people.list' }}
915
915
  onRowClick={(row) => router.push(`/backend/customers/people-v2/${row.id}`)}
916
916
  sortable
917
+ manualSorting
917
918
  sorting={sorting}
918
- onSortingChange={setSorting}
919
+ onSortingChange={handleSortingChange}
919
920
  bulkActions={[
920
921
  {
921
922
  id: 'delete',