@open-mercato/core 0.4.5-develop-0f0e676c72 → 0.4.5-develop-e694581d9f
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.
- package/dist/generated/entities/customer_deal/index.js +4 -0
- package/dist/generated/entities/customer_deal/index.js.map +2 -2
- package/dist/generated/entities/customer_pipeline/index.js +17 -0
- package/dist/generated/entities/customer_pipeline/index.js.map +7 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js +19 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +4 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/acl.js +2 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +4 -0
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +12 -0
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -1
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +69 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +7 -0
- package/dist/modules/customers/api/pipeline-stages/route.js +275 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +7 -0
- package/dist/modules/customers/api/pipelines/route.js +245 -0
- package/dist/modules/customers/api/pipelines/route.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/page.js +2 -0
- package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +439 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +17 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +35 -1
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +102 -74
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/cli.js +28 -2
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +34 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/index.js +2 -0
- package/dist/modules/customers/commands/index.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +126 -0
- package/dist/modules/customers/commands/pipeline-stages.js.map +7 -0
- package/dist/modules/customers/commands/pipelines.js +87 -0
- package/dist/modules/customers/commands/pipelines.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +0 -5
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +474 -0
- package/dist/modules/customers/components/PipelineSettings.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +84 -12
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/data/entities.js +78 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +44 -0
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260218191730.js +77 -0
- package/dist/modules/customers/migrations/Migration20260218191730.js.map +7 -0
- package/dist/modules/customers/setup.js +7 -3
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +46 -44
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +10 -1
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/translations/commands/index.js +2 -0
- package/dist/modules/translations/commands/index.js.map +7 -0
- package/dist/modules/translations/commands/translations.js +160 -0
- package/dist/modules/translations/commands/translations.js.map +7 -0
- package/dist/modules/translations/index.js +1 -0
- package/dist/modules/translations/index.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260222205305.js +14 -0
- package/dist/modules/workflows/migrations/Migration20260222205305.js.map +7 -0
- package/generated/entities/customer_deal/index.ts +2 -0
- package/generated/entities/customer_pipeline/index.ts +7 -0
- package/generated/entities/customer_pipeline_stage/index.ts +8 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +4 -0
- package/package.json +2 -2
- package/src/modules/customers/acl.ts +2 -0
- package/src/modules/customers/api/deals/[id]/route.ts +4 -0
- package/src/modules/customers/api/deals/route.ts +12 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +21 -1
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +71 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +296 -0
- package/src/modules/customers/api/pipelines/route.ts +261 -0
- package/src/modules/customers/backend/config/customers/page.tsx +2 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +13 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +512 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
- package/src/modules/customers/backend/customers/deals/page.tsx +33 -1
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +119 -79
- package/src/modules/customers/cli.ts +29 -1
- package/src/modules/customers/commands/deals.ts +44 -1
- package/src/modules/customers/commands/index.ts +2 -0
- package/src/modules/customers/commands/pipeline-stages.ts +156 -0
- package/src/modules/customers/commands/pipelines.ts +105 -0
- package/src/modules/customers/components/DictionarySettings.tsx +0 -5
- package/src/modules/customers/components/PipelineSettings.tsx +570 -0
- package/src/modules/customers/components/detail/DealForm.tsx +89 -11
- package/src/modules/customers/data/entities.ts +64 -0
- package/src/modules/customers/data/validators.ts +57 -0
- package/src/modules/customers/i18n/de.json +4 -0
- package/src/modules/customers/i18n/en.json +4 -0
- package/src/modules/customers/i18n/es.json +4 -0
- package/src/modules/customers/i18n/pl.json +5 -1
- package/src/modules/customers/migrations/Migration20260218191730.ts +84 -0
- package/src/modules/customers/setup.ts +5 -1
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -60
- package/src/modules/translations/api/context.ts +12 -0
- package/src/modules/translations/commands/index.ts +1 -0
- package/src/modules/translations/commands/translations.ts +253 -0
- package/src/modules/translations/index.ts +1 -0
- package/src/modules/workflows/migrations/Migration20260222205305.ts +13 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/backend/customers/deals/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 { useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildCrudExportUrl, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\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 {\n DictionaryValue,\n type CustomerDictionaryKind,\n type CustomerDictionaryMap,\n} from '../../../lib/dictionaries'\nimport {\n ensureCustomerDictionary,\n invalidateCustomerDictionary,\n} from '../../../components/detail/hooks/useCustomerDictionary'\nimport {\n useCustomFieldDefs,\n filterCustomFieldDefs,\n} from '@open-mercato/ui/backend/utils/customFieldDefs'\n\ntype DealRow = {\n id: string\n title: string\n status?: string | null\n pipelineStage?: string | null\n valueAmount?: number | null\n valueCurrency?: string | null\n probability?: number | null\n expectedCloseAt?: string | null\n updatedAt?: string | null\n companies: { id: string; label: string }[]\n people: { id: string; label: string }[]\n} & Record<string, unknown>\n\ntype DealsResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\ntype FilterOption = { value: string; label: string }\n\ntype DictionaryKey = Extract<CustomerDictionaryKind, 'deal-statuses' | 'pipeline-stages'>\n\ntype PersonLookupRecord = {\n id: string\n name: string | null\n email: string | null\n phone: string | null\n}\n\ntype CompanyLookupRecord = {\n id: string\n name: string | null\n domain: string | null\n email: string | null\n}\n\nfunction parsePersonLookupRecord(item: unknown): PersonLookupRecord | null {\n if (typeof item !== 'object' || item === null) return null\n const record = item as Record<string, unknown>\n const id = typeof record.id === 'string' ? record.id : null\n if (!id || !isUuid(id)) return null\n const name = typeof record.display_name === 'string' ? record.display_name : null\n const email = typeof record.primary_email === 'string' ? record.primary_email : null\n const phone = typeof record.primary_phone === 'string' ? record.primary_phone : null\n return { id, name, email, phone }\n}\n\nfunction parseCompanyLookupRecord(item: unknown): CompanyLookupRecord | null {\n if (typeof item !== 'object' || item === null) return null\n const record = item as Record<string, unknown>\n const id = typeof record.id === 'string' ? record.id : null\n if (!id || !isUuid(id)) return null\n const name = typeof record.display_name === 'string' ? record.display_name : null\n const domain = typeof record.primary_domain === 'string' ? record.primary_domain : null\n const email = typeof record.primary_email === 'string' ? record.primary_email : null\n return { id, name, domain, email }\n}\n\ntype OptionsState = {\n options: FilterOption[]\n idToLabel: Record<string, string>\n labelToId: Record<string, string>\n}\n\nconst EMPTY_OPTIONS_STATE: OptionsState = {\n options: [],\n idToLabel: {},\n labelToId: {},\n}\n\nconst PAGE_SIZE = 20\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\nfunction isUuid(value: string | null | undefined): value is string {\n if (!value) return false\n return UUID_REGEX.test(value.trim())\n}\n\nfunction normalizeIdCandidates(raw: Array<string>): string[] {\n const set = new Set<string>()\n raw.forEach((candidate) => {\n candidate\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n .forEach((part) => {\n if (isUuid(part)) set.add(part)\n })\n })\n return Array.from(set)\n}\n\nfunction extractIdsFromParams(params: URLSearchParams | null | undefined, key: string): string[] {\n if (!params) return []\n const values = params.getAll(key)\n return normalizeIdCandidates(values)\n}\n\nfunction ensureUniqueLabel(base: string, occupied: Set<string>): string {\n const trimmed = base.trim() || 'Unnamed'\n if (!occupied.has(trimmed)) {\n occupied.add(trimmed)\n return trimmed\n }\n let counter = 2\n let candidate = `${trimmed} \u2022 ${counter}`\n while (occupied.has(candidate)) {\n counter += 1\n candidate = `${trimmed} \u2022 ${counter}`\n }\n occupied.add(candidate)\n return candidate\n}\n\nasync function fetchPeopleLookup(query?: string): Promise<PersonLookupRecord[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '20')\n if (query && query.trim().length) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/people?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n return items\n .map((item) => parsePersonLookupRecord(item))\n .filter((record): record is PersonLookupRecord => record !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchPeopleLookupByIds(ids: string[]): Promise<PersonLookupRecord[]> {\n const unique = Array.from(new Set(ids.filter((id) => isUuid(id))))\n if (!unique.length) return []\n const results = await Promise.all(\n unique.map(async (id) => {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/people?${search.toString()}`)\n if (!call.ok) return null\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const match = items\n .map((item) => parsePersonLookupRecord(item))\n .find((record) => record?.id === id)\n return match ?? null\n } catch {\n return null\n }\n }),\n )\n return results.filter((record): record is PersonLookupRecord => !!record)\n}\n\nasync function fetchCompaniesLookup(query?: string): Promise<CompanyLookupRecord[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '20')\n if (query && query.trim().length) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/companies?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n return items\n .map((item) => parseCompanyLookupRecord(item))\n .filter((record): record is CompanyLookupRecord => record !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchCompaniesLookupByIds(ids: string[]): Promise<CompanyLookupRecord[]> {\n const unique = Array.from(new Set(ids.filter((id) => isUuid(id))))\n if (!unique.length) return []\n const results = await Promise.all(\n unique.map(async (id) => {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/companies?${search.toString()}`)\n if (!call.ok) return null\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const match = items\n .map((item) => parseCompanyLookupRecord(item))\n .find((record) => record?.id === id)\n return match ?? null\n } catch {\n return null\n }\n }),\n )\n return results.filter((record): record is CompanyLookupRecord => !!record)\n}\n\nfunction formatCurrency(amount: number | null | undefined, currency: string | null | undefined, fallback: string): string {\n if (typeof amount !== 'number' || Number.isNaN(amount)) return fallback\n try {\n if (currency && currency.trim().length) {\n const formatter = new Intl.NumberFormat(undefined, { style: 'currency', currency })\n return formatter.format(amount)\n }\n const formatter = new Intl.NumberFormat(undefined, { style: 'decimal', maximumFractionDigits: 2 })\n return formatter.format(amount)\n } catch {\n return currency ? `${amount} ${currency}` : String(amount)\n }\n}\n\nfunction formatDateValue(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 arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\nexport default function CustomersDealsPage() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const [rows, setRows] = React.useState<DealRow[]>([])\n const [page, setPage] = React.useState(() => {\n const raw = Number(searchParams?.get('page') ?? '1')\n return Number.isFinite(raw) && raw > 0 ? raw : 1\n })\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState(() => searchParams?.get('search')?.trim() ?? '')\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [pendingDeleteId, setPendingDeleteId] = React.useState<string | null>(null)\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n\n const initialPersonIds = React.useMemo(\n () => extractIdsFromParams(searchParams, 'personId'),\n [searchParams],\n )\n const initialCompanyIds = React.useMemo(\n () => extractIdsFromParams(searchParams, 'companyId'),\n [searchParams],\n )\n\n const [selectedPersonIds, setSelectedPersonIds] = React.useState<string[]>(initialPersonIds)\n const [selectedCompanyIds, setSelectedCompanyIds] = React.useState<string[]>(initialCompanyIds)\n\n const [peopleState, setPeopleState] = React.useState<OptionsState>(EMPTY_OPTIONS_STATE)\n const [companiesState, setCompaniesState] = React.useState<OptionsState>(EMPTY_OPTIONS_STATE)\n const peopleCacheRef = React.useRef<Map<string, FilterOption[]>>(new Map())\n const companiesCacheRef = React.useRef<Map<string, FilterOption[]>>(new Map())\n\n const buildPersonLabel = React.useCallback((record: PersonLookupRecord): string => {\n const parts: string[] = []\n const name = record.name?.trim()\n if (name) parts.push(name)\n const email = record.email?.trim()\n if (email && !parts.includes(email)) parts.push(email)\n const phone = record.phone?.trim()\n if (!parts.length && phone) parts.push(phone)\n if (!parts.length) parts.push(t('customers.deals.list.unnamedPerson', 'Unnamed person'))\n return parts.join(' \u2022 ')\n }, [t])\n\n const buildCompanyLabel = React.useCallback((record: CompanyLookupRecord): string => {\n const parts: string[] = []\n const name = record.name?.trim()\n if (name) parts.push(name)\n const domain = record.domain?.trim()\n if (domain && !parts.includes(domain)) parts.push(domain)\n const email = record.email?.trim()\n if (!parts.length && email) parts.push(email)\n if (!parts.length) parts.push(t('customers.deals.list.unnamedCompany', 'Unnamed company'))\n return parts.join(' \u2022 ')\n }, [t])\n\n const ingestPeopleRecords = React.useCallback((records: PersonLookupRecord[]) => {\n if (!records.length) return [] as FilterOption[]\n const queryMap = new Map<string, FilterOption>()\n setPeopleState((prev) => {\n const idToLabel = { ...prev.idToLabel }\n const labelToId: Record<string, string> = {}\n const merged = new Map(prev.options.map((opt) => [opt.value, opt]))\n const occupied = new Set<string>()\n Object.entries(prev.labelToId).forEach(([label, id]) => {\n occupied.add(label)\n labelToId[label] = id\n })\n records.forEach((record) => {\n if (!isUuid(record.id)) return\n const base = buildPersonLabel(record)\n let previousLabel = idToLabel[record.id]\n if (previousLabel) {\n // remove previous label before reassigning\n delete labelToId[previousLabel]\n occupied.delete(previousLabel)\n }\n const label = ensureUniqueLabel(base, occupied)\n idToLabel[record.id] = label\n labelToId[label] = record.id\n const option = { value: record.id, label }\n merged.set(record.id, option)\n queryMap.set(record.id, option)\n })\n const nextOptions = Array.from(merged.values()).sort((a, b) => a.label.localeCompare(b.label))\n return { options: nextOptions, idToLabel, labelToId }\n })\n return Array.from(queryMap.values()).sort((a, b) => a.label.localeCompare(b.label))\n }, [buildPersonLabel])\n\n const ingestCompanyRecords = React.useCallback((records: CompanyLookupRecord[]) => {\n if (!records.length) return [] as FilterOption[]\n const queryMap = new Map<string, FilterOption>()\n setCompaniesState((prev) => {\n const idToLabel = { ...prev.idToLabel }\n const labelToId: Record<string, string> = {}\n const merged = new Map(prev.options.map((opt) => [opt.value, opt]))\n const occupied = new Set<string>()\n Object.entries(prev.labelToId).forEach(([label, id]) => {\n occupied.add(label)\n labelToId[label] = id\n })\n records.forEach((record) => {\n if (!isUuid(record.id)) return\n const base = buildCompanyLabel(record)\n let previousLabel = idToLabel[record.id]\n if (previousLabel) {\n delete labelToId[previousLabel]\n occupied.delete(previousLabel)\n }\n const label = ensureUniqueLabel(base, occupied)\n idToLabel[record.id] = label\n labelToId[label] = record.id\n const option = { value: record.id, label }\n merged.set(record.id, option)\n queryMap.set(record.id, option)\n })\n const nextOptions = Array.from(merged.values()).sort((a, b) => a.label.localeCompare(b.label))\n return { options: nextOptions, idToLabel, labelToId }\n })\n return Array.from(queryMap.values()).sort((a, b) => a.label.localeCompare(b.label))\n }, [buildCompanyLabel])\n\n const loadPeopleOptions = React.useCallback(async (query?: string) => {\n const normalizedQuery = (query || '').trim().toLowerCase()\n const cacheKey = `${scopeVersion}|${normalizedQuery}`\n const cached = peopleCacheRef.current.get(cacheKey)\n if (cached) return cached\n const records = await fetchPeopleLookup(query)\n const options = ingestPeopleRecords(records)\n peopleCacheRef.current.set(cacheKey, options)\n return options\n }, [scopeVersion, ingestPeopleRecords])\n\n const loadCompanyOptions = React.useCallback(async (query?: string) => {\n const normalizedQuery = (query || '').trim().toLowerCase()\n const cacheKey = `${scopeVersion}|${normalizedQuery}`\n const cached = companiesCacheRef.current.get(cacheKey)\n if (cached) return cached\n const records = await fetchCompaniesLookup(query)\n const options = ingestCompanyRecords(records)\n companiesCacheRef.current.set(cacheKey, options)\n return options\n }, [scopeVersion, ingestCompanyRecords])\n\n React.useEffect(() => {\n let cancelled = false\n if (!selectedPersonIds.length) return\n const missing = selectedPersonIds.filter((id) => !peopleState.idToLabel[id])\n if (!missing.length) return\n fetchPeopleLookupByIds(missing).then((records) => {\n if (cancelled) return\n ingestPeopleRecords(records)\n })\n return () => { cancelled = true }\n }, [selectedPersonIds, peopleState.idToLabel, ingestPeopleRecords])\n\n React.useEffect(() => {\n let cancelled = false\n if (!selectedCompanyIds.length) return\n const missing = selectedCompanyIds.filter((id) => !companiesState.idToLabel[id])\n if (!missing.length) return\n fetchCompaniesLookupByIds(missing).then((records) => {\n if (cancelled) return\n ingestCompanyRecords(records)\n })\n return () => { cancelled = true }\n }, [selectedCompanyIds, companiesState.idToLabel, ingestCompanyRecords])\n\n const [dictionaryMaps, setDictionaryMaps] = React.useState<Record<DictionaryKey, CustomerDictionaryMap>>({\n 'deal-statuses': {},\n 'pipeline-stages': {},\n })\n\n const fetchDictionaryEntries = React.useCallback(\n async (kind: DictionaryKey) => {\n try {\n const data = await ensureCustomerDictionary(queryClient, kind, scopeVersion)\n setDictionaryMaps((prev) => ({ ...prev, [kind]: data.map }))\n } catch {\n setDictionaryMaps((prev) => ({ ...prev, [kind]: {} }))\n }\n },\n [queryClient, scopeVersion],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function loadDictionaries() {\n if (cancelled) return\n await Promise.all([fetchDictionaryEntries('deal-statuses'), fetchDictionaryEntries('pipeline-stages')])\n }\n loadDictionaries().catch(() => {})\n return () => { cancelled = true }\n }, [fetchDictionaryEntries, reloadToken])\n\n React.useEffect(() => {\n peopleCacheRef.current.clear()\n companiesCacheRef.current.clear()\n setPeopleState((prev) => {\n if (!prev.options.length && !Object.keys(prev.idToLabel).length) return prev\n return { ...EMPTY_OPTIONS_STATE }\n })\n setCompaniesState((prev) => {\n if (!prev.options.length && !Object.keys(prev.idToLabel).length) return prev\n return { ...EMPTY_OPTIONS_STATE }\n })\n }, [scopeVersion, reloadToken])\n\n const syncFilterLabels = React.useCallback((\n key: 'people' | 'companies',\n ids: string[],\n idToLabel: Record<string, string>,\n ) => {\n setFilterValues((prev) => {\n const current = Array.isArray(prev[key]) ? (prev[key] as string[]) : []\n if (!ids.length) {\n if (!current.length) return prev\n const next = { ...prev }\n delete next[key]\n return next\n }\n const labels: string[] = []\n ids.forEach((id) => {\n const label = idToLabel[id]\n if (label && !labels.includes(label)) labels.push(label)\n })\n if (labels.length < ids.length) return prev\n if (arraysEqual(current, labels)) return prev\n return { ...prev, [key]: labels }\n })\n }, [])\n\n React.useEffect(() => {\n syncFilterLabels('people', selectedPersonIds, peopleState.idToLabel)\n }, [selectedPersonIds, peopleState.idToLabel, syncFilterLabels])\n\n React.useEffect(() => {\n syncFilterLabels('companies', selectedCompanyIds, companiesState.idToLabel)\n }, [selectedCompanyIds, companiesState.idToLabel, syncFilterLabels])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value.trim())\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = { ...values }\n const rawPeople = Array.isArray(values.people) ? (values.people as string[]) : []\n const nextPersonIds: string[] = []\n rawPeople.forEach((value) => {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (!trimmed) return\n const mapped = peopleState.labelToId[trimmed]\n if (mapped && !nextPersonIds.includes(mapped)) nextPersonIds.push(mapped)\n })\n setSelectedPersonIds(nextPersonIds)\n if (nextPersonIds.length) {\n next.people = Array.from(new Set(rawPeople.map((value) => (typeof value === 'string' ? value.trim() : '')).filter((value) => value.length > 0)))\n } else {\n delete next.people\n }\n\n const rawCompanies = Array.isArray(values.companies) ? (values.companies as string[]) : []\n const nextCompanyIds: string[] = []\n rawCompanies.forEach((value) => {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (!trimmed) return\n const mapped = companiesState.labelToId[trimmed]\n if (mapped && !nextCompanyIds.includes(mapped)) nextCompanyIds.push(mapped)\n })\n setSelectedCompanyIds(nextCompanyIds)\n if (nextCompanyIds.length) {\n next.companies = Array.from(new Set(rawCompanies.map((value) => (typeof value === 'string' ? value.trim() : '')).filter((value) => value.length > 0)))\n } else {\n delete next.companies\n }\n\n setFilterValues(next)\n setPage(1)\n }, [peopleState.labelToId, companiesState.labelToId])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setSelectedPersonIds([])\n setSelectedCompanyIds([])\n setPage(1)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim().length) params.set('search', search.trim())\n if (selectedPersonIds.length) params.set('personId', selectedPersonIds.join(','))\n if (selectedCompanyIds.length) params.set('companyId', selectedCompanyIds.join(','))\n Object.entries(filterValues).forEach(([key, value]) => {\n if (key === 'people' || key === 'companies') return\n if (value == null) return\n if (Array.isArray(value)) {\n const normalized = value\n .map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item.trim()\n return String(item).trim()\n })\n .filter((item) => item.length > 0)\n if (normalized.length) params.set(key, normalized.join(','))\n } else if (typeof value === 'object') {\n const obj = value as Record<string, unknown>\n const from = typeof obj.from === 'string' ? obj.from.trim() : ''\n const to = typeof obj.to === 'string' ? obj.to.trim() : ''\n if (from) params.set(`${key}[from]`, from)\n if (to) params.set(`${key}[to]`, to)\n } else {\n const stringValue = typeof value === 'string' ? value.trim() : String(value)\n if (stringValue) params.set(key, stringValue)\n }\n })\n return params.toString()\n }, [filterValues, page, search, selectedCompanyIds, selectedPersonIds])\n\n const currentParams = React.useMemo(\n () => Object.fromEntries(new URLSearchParams(queryParams)),\n [queryParams],\n )\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/deals', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/deals', { ...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: DealsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<DealsResponse>(`/api/customers/deals?${queryParams}`, undefined, { fallback })\n if (!call.ok) {\n const message =\n typeof (call.result as { error?: string } | undefined)?.error === 'string'\n ? (call.result as { error?: string }).error!\n : t('customers.deals.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 const mapped = items\n .map((item) => mapDeal(item as Record<string, unknown>))\n .filter((row): row is DealRow => !!row)\n setRows(mapped)\n setTotal(typeof payload.total === 'number' ? payload.total : mapped.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.deals.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 React.useEffect(() => {\n if (totalPages > 0 && page > totalPages) {\n setPage(totalPages)\n }\n }, [page, totalPages])\n\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 (selectedPersonIds.length) selectedPersonIds.forEach((id) => params.append('personId', id))\n if (selectedCompanyIds.length) selectedCompanyIds.forEach((id) => params.append('companyId', id))\n if (page > 1) params.set('page', String(page))\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, selectedPersonIds, selectedCompanyIds])\n\n const handleRefresh = React.useCallback(() => {\n peopleCacheRef.current.clear()\n companiesCacheRef.current.clear()\n void Promise.all([\n invalidateCustomerDictionary(queryClient, 'deal-statuses'),\n invalidateCustomerDictionary(queryClient, 'pipeline-stages'),\n ])\n setReloadToken((token) => token + 1)\n }, [queryClient])\n\n const handleDeleteDeal = React.useCallback(\n async (dealId: string) => {\n if (pendingDeleteId) return\n const confirmed = await confirm({\n title: t(\n 'customers.deals.list.deleteConfirm',\n 'Delete this deal? This action cannot be undone.',\n ),\n variant: 'destructive',\n })\n if (!confirmed) return\n setPendingDeleteId(dealId)\n try {\n await deleteCrud('customers/deals', {\n body: { id: dealId },\n errorMessage: t('customers.deals.list.deleteError', 'Failed to delete deal.'),\n })\n flash(t('customers.deals.list.deleteSuccess', 'Deal deleted.'), 'success')\n setRows((prev) => prev.filter((row) => row.id !== dealId))\n setTotal((prev) => Math.max(0, prev - 1))\n handleRefresh()\n } catch (err) {\n const message =\n err instanceof Error && err.message\n ? err.message\n : t('customers.deals.list.deleteError', 'Failed to delete deal.')\n flash(message, 'error')\n } finally {\n setPendingDeleteId(null)\n }\n },\n [confirm, handleRefresh, pendingDeleteId, t],\n )\n\n const personOptions = peopleState.options\n const companyOptions = companiesState.options\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'people',\n label: t('customers.deals.list.filters.people'),\n type: 'tags',\n options: personOptions,\n loadOptions: loadPeopleOptions,\n placeholder: t('customers.deals.list.filters.peoplePlaceholder'),\n },\n {\n id: 'companies',\n label: t('customers.deals.list.filters.companies'),\n type: 'tags',\n options: companyOptions,\n loadOptions: loadCompanyOptions,\n placeholder: t('customers.deals.list.filters.companiesPlaceholder'),\n },\n ], [companyOptions, loadCompanyOptions, loadPeopleOptions, personOptions, t])\n\n const { data: customFieldDefs = [] } = useCustomFieldDefs([E.customers.customer_deal], {\n keyExtras: [scopeVersion, reloadToken],\n })\n\n const columns = React.useMemo<ColumnDef<DealRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">{t('customers.deals.list.noValue')}</span>\n const renderDictionaryCell = (kind: DictionaryKey, value: string | null | undefined) => (\n <DictionaryValue\n value={value}\n map={dictionaryMaps[kind]}\n fallback={value ? <span className=\"text-sm\">{value}</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 const renderAssociationList = (\n items: { id: string; label: string }[],\n fallbackLabel: string,\n ) => {\n if (!items.length) return noValue\n return (\n <ul className=\"flex flex-wrap gap-1 text-sm\">\n {items.map((entry) => (\n <li key={entry.id} className=\"rounded border px-2 py-0.5 text-xs bg-muted\">\n {entry.label && entry.label.trim().length ? entry.label : fallbackLabel}\n </li>\n ))}\n </ul>\n )\n }\n\n const customColumns = filterCustomFieldDefs(customFieldDefs, 'list').map<ColumnDef<DealRow>>((def) => ({\n accessorKey: `cf_${def.key}`,\n header: def.label || def.key,\n cell: ({ getValue }) => {\n const value = getValue()\n if (value == null) return noValue\n if (Array.isArray(value)) {\n const normalized = value\n .map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item.trim()\n return String(item).trim()\n })\n .filter((item) => item.length > 0)\n if (!normalized.length) return noValue\n return <span className=\"text-sm\">{normalized.join(', ')}</span>\n }\n if (typeof value === 'boolean') {\n return (\n <span className=\"text-sm\">\n {value\n ? t('customers.deals.list.booleanYes', 'Yes')\n : t('customers.deals.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\n return [\n {\n accessorKey: 'title',\n header: t('customers.deals.list.columns.title'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.title}</span>,\n },\n {\n accessorKey: 'status',\n header: t('customers.deals.list.columns.status'),\n cell: ({ row }) => renderDictionaryCell('deal-statuses', row.original.status),\n },\n {\n accessorKey: 'pipelineStage',\n header: t('customers.deals.list.columns.pipelineStage'),\n cell: ({ row }) => renderDictionaryCell('pipeline-stages', row.original.pipelineStage),\n },\n {\n accessorKey: 'valueAmount',\n header: t('customers.deals.list.columns.value'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium\">\n {formatCurrency(row.original.valueAmount ?? null, row.original.valueCurrency ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n {\n accessorKey: 'probability',\n header: t('customers.deals.list.columns.probability'),\n cell: ({ row }) => {\n const value = row.original.probability\n if (typeof value === 'number' && Number.isFinite(value)) {\n return <span className=\"text-sm\">{`${Math.min(Math.max(value, 0), 100)}%`}</span>\n }\n return noValue\n },\n },\n {\n accessorKey: 'expectedCloseAt',\n header: t('customers.deals.list.columns.expectedClose'),\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {formatDateValue(row.original.expectedCloseAt ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n {\n accessorKey: 'companies',\n header: t('customers.deals.list.columns.companies'),\n cell: ({ row }) => renderAssociationList(row.original.companies, t('customers.deals.list.unnamedCompany')),\n },\n {\n accessorKey: 'people',\n header: t('customers.deals.list.columns.people'),\n cell: ({ row }) => renderAssociationList(row.original.people, t('customers.deals.list.unnamedPerson')),\n },\n {\n accessorKey: 'updatedAt',\n header: t('customers.deals.list.columns.updatedAt'),\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {formatDateValue(row.original.updatedAt ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n ...customColumns,\n ]\n }, [customFieldDefs, dictionaryMaps, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<DealRow>\n title={t('customers.deals.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customers/deals/create\">\n {t('customers.deals.list.actions.new', 'New deal')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n onRowClick={(row) => {\n router.push(`/backend/customers/deals/${row.id}`)\n }}\n rowActions={(row) => {\n const isDeleting = pendingDeleteId === row.id\n return (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('customers.deals.list.actions.edit', 'Edit'),\n onSelect: () => { router.push(`/backend/customers/deals/${row.id}`) },\n },\n {\n id: 'open-new-tab',\n label: t('customers.deals.list.actions.openInNewTab', 'Open in new tab'),\n onSelect: () => {\n if (typeof window !== 'undefined') {\n window.open(`/backend/customers/deals/${row.id}`, '_blank', 'noopener')\n }\n },\n },\n {\n id: 'delete',\n label: isDeleting\n ? t('customers.deals.list.actions.deleting', 'Deleting\u2026')\n : t('customers.deals.list.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => handleDeleteDeal(row.id),\n },\n ]}\n />\n )\n }}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('customers.deals.list.searchPlaceholder')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: (nextPage) => setPage(nextPage),\n cacheStatus,\n }}\n isLoading={isLoading}\n refreshButton={{\n label: t('customers.deals.list.refresh'),\n onRefresh: handleRefresh,\n }}\n exporter={exportConfig}\n entityId={E.customers.customer_deal}\n perspective={{ tableId: 'customers.deals.list' }}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n\nfunction mapDeal(item: Record<string, unknown>): DealRow | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const title = typeof item.title === 'string' ? item.title : ''\n const status = typeof item.status === 'string' ? item.status : null\n const pipelineStage = typeof item.pipeline_stage === 'string' ? item.pipeline_stage : null\n const valueAmountRaw = item.value_amount\n const valueAmount =\n typeof valueAmountRaw === 'number'\n ? valueAmountRaw\n : typeof valueAmountRaw === 'string' && valueAmountRaw.trim()\n ? Number(valueAmountRaw)\n : null\n const valueCurrency =\n typeof item.value_currency === 'string' && item.value_currency.trim().length\n ? item.value_currency.trim().toUpperCase()\n : null\n const probabilityRaw = item.probability\n const probability =\n typeof probabilityRaw === 'number'\n ? probabilityRaw\n : typeof probabilityRaw === 'string' && probabilityRaw.trim().length\n ? Number(probabilityRaw)\n : null\n const expectedCloseAt = typeof item.expected_close_at === 'string' ? item.expected_close_at : null\n const updatedAt = typeof item.updated_at === 'string' ? item.updated_at : null\n const peopleRaw = Array.isArray(item.people) ? item.people : []\n const companiesRaw = Array.isArray(item.companies) ? item.companies : []\n const people = peopleRaw\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const data = entry as Record<string, unknown>\n const pid = typeof data.id === 'string' ? data.id : null\n if (!pid) return null\n const label = typeof data.label === 'string' ? data.label : ''\n return { id: pid, label }\n })\n .filter((entry): entry is { id: string; label: string } => !!entry)\n const companies = companiesRaw\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const data = entry as Record<string, unknown>\n const cid = typeof data.id === 'string' ? data.id : null\n if (!cid) return null\n const label = typeof data.label === 'string' ? data.label : ''\n return { id: cid, label }\n })\n .filter((entry): entry is { id: string; label: string } => !!entry)\n const customFields: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) customFields[key] = value\n }\n return {\n id,\n title,\n status,\n pipelineStage,\n valueAmount,\n valueCurrency,\n probability,\n expectedCloseAt,\n updatedAt,\n people,\n companies,\n ...customFields,\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 { useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildCrudExportUrl, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\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 {\n DictionaryValue,\n type CustomerDictionaryKind,\n type CustomerDictionaryMap,\n} from '../../../lib/dictionaries'\nimport {\n ensureCustomerDictionary,\n invalidateCustomerDictionary,\n} from '../../../components/detail/hooks/useCustomerDictionary'\nimport {\n useCustomFieldDefs,\n filterCustomFieldDefs,\n} from '@open-mercato/ui/backend/utils/customFieldDefs'\n\ntype DealRow = {\n id: string\n title: string\n status?: string | null\n pipelineStage?: string | null\n pipelineStageId?: string | null\n pipelineId?: string | null\n valueAmount?: number | null\n valueCurrency?: string | null\n probability?: number | null\n expectedCloseAt?: string | null\n updatedAt?: string | null\n companies: { id: string; label: string }[]\n people: { id: string; label: string }[]\n} & Record<string, unknown>\n\ntype DealsResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\ntype FilterOption = { value: string; label: string }\n\ntype DictionaryKey = Extract<CustomerDictionaryKind, 'deal-statuses' | 'pipeline-stages'>\n\ntype PersonLookupRecord = {\n id: string\n name: string | null\n email: string | null\n phone: string | null\n}\n\ntype CompanyLookupRecord = {\n id: string\n name: string | null\n domain: string | null\n email: string | null\n}\n\nfunction parsePersonLookupRecord(item: unknown): PersonLookupRecord | null {\n if (typeof item !== 'object' || item === null) return null\n const record = item as Record<string, unknown>\n const id = typeof record.id === 'string' ? record.id : null\n if (!id || !isUuid(id)) return null\n const name = typeof record.display_name === 'string' ? record.display_name : null\n const email = typeof record.primary_email === 'string' ? record.primary_email : null\n const phone = typeof record.primary_phone === 'string' ? record.primary_phone : null\n return { id, name, email, phone }\n}\n\nfunction parseCompanyLookupRecord(item: unknown): CompanyLookupRecord | null {\n if (typeof item !== 'object' || item === null) return null\n const record = item as Record<string, unknown>\n const id = typeof record.id === 'string' ? record.id : null\n if (!id || !isUuid(id)) return null\n const name = typeof record.display_name === 'string' ? record.display_name : null\n const domain = typeof record.primary_domain === 'string' ? record.primary_domain : null\n const email = typeof record.primary_email === 'string' ? record.primary_email : null\n return { id, name, domain, email }\n}\n\ntype OptionsState = {\n options: FilterOption[]\n idToLabel: Record<string, string>\n labelToId: Record<string, string>\n}\n\nconst EMPTY_OPTIONS_STATE: OptionsState = {\n options: [],\n idToLabel: {},\n labelToId: {},\n}\n\nconst PAGE_SIZE = 20\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\nfunction isUuid(value: string | null | undefined): value is string {\n if (!value) return false\n return UUID_REGEX.test(value.trim())\n}\n\nfunction normalizeIdCandidates(raw: Array<string>): string[] {\n const set = new Set<string>()\n raw.forEach((candidate) => {\n candidate\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n .forEach((part) => {\n if (isUuid(part)) set.add(part)\n })\n })\n return Array.from(set)\n}\n\nfunction extractIdsFromParams(params: URLSearchParams | null | undefined, key: string): string[] {\n if (!params) return []\n const values = params.getAll(key)\n return normalizeIdCandidates(values)\n}\n\nfunction ensureUniqueLabel(base: string, occupied: Set<string>): string {\n const trimmed = base.trim() || 'Unnamed'\n if (!occupied.has(trimmed)) {\n occupied.add(trimmed)\n return trimmed\n }\n let counter = 2\n let candidate = `${trimmed} \u2022 ${counter}`\n while (occupied.has(candidate)) {\n counter += 1\n candidate = `${trimmed} \u2022 ${counter}`\n }\n occupied.add(candidate)\n return candidate\n}\n\nasync function fetchPeopleLookup(query?: string): Promise<PersonLookupRecord[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '20')\n if (query && query.trim().length) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/people?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n return items\n .map((item) => parsePersonLookupRecord(item))\n .filter((record): record is PersonLookupRecord => record !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchPeopleLookupByIds(ids: string[]): Promise<PersonLookupRecord[]> {\n const unique = Array.from(new Set(ids.filter((id) => isUuid(id))))\n if (!unique.length) return []\n const results = await Promise.all(\n unique.map(async (id) => {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/people?${search.toString()}`)\n if (!call.ok) return null\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const match = items\n .map((item) => parsePersonLookupRecord(item))\n .find((record) => record?.id === id)\n return match ?? null\n } catch {\n return null\n }\n }),\n )\n return results.filter((record): record is PersonLookupRecord => !!record)\n}\n\nasync function fetchCompaniesLookup(query?: string): Promise<CompanyLookupRecord[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '20')\n if (query && query.trim().length) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/companies?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n return items\n .map((item) => parseCompanyLookupRecord(item))\n .filter((record): record is CompanyLookupRecord => record !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchCompaniesLookupByIds(ids: string[]): Promise<CompanyLookupRecord[]> {\n const unique = Array.from(new Set(ids.filter((id) => isUuid(id))))\n if (!unique.length) return []\n const results = await Promise.all(\n unique.map(async (id) => {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/customers/companies?${search.toString()}`)\n if (!call.ok) return null\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const match = items\n .map((item) => parseCompanyLookupRecord(item))\n .find((record) => record?.id === id)\n return match ?? null\n } catch {\n return null\n }\n }),\n )\n return results.filter((record): record is CompanyLookupRecord => !!record)\n}\n\nfunction formatCurrency(amount: number | null | undefined, currency: string | null | undefined, fallback: string): string {\n if (typeof amount !== 'number' || Number.isNaN(amount)) return fallback\n try {\n if (currency && currency.trim().length) {\n const formatter = new Intl.NumberFormat(undefined, { style: 'currency', currency })\n return formatter.format(amount)\n }\n const formatter = new Intl.NumberFormat(undefined, { style: 'decimal', maximumFractionDigits: 2 })\n return formatter.format(amount)\n } catch {\n return currency ? `${amount} ${currency}` : String(amount)\n }\n}\n\nfunction formatDateValue(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 arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\nexport default function CustomersDealsPage() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const [rows, setRows] = React.useState<DealRow[]>([])\n const [page, setPage] = React.useState(() => {\n const raw = Number(searchParams?.get('page') ?? '1')\n return Number.isFinite(raw) && raw > 0 ? raw : 1\n })\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState(() => searchParams?.get('search')?.trim() ?? '')\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [pendingDeleteId, setPendingDeleteId] = React.useState<string | null>(null)\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n\n const initialPersonIds = React.useMemo(\n () => extractIdsFromParams(searchParams, 'personId'),\n [searchParams],\n )\n const initialCompanyIds = React.useMemo(\n () => extractIdsFromParams(searchParams, 'companyId'),\n [searchParams],\n )\n\n const [selectedPersonIds, setSelectedPersonIds] = React.useState<string[]>(initialPersonIds)\n const [selectedCompanyIds, setSelectedCompanyIds] = React.useState<string[]>(initialCompanyIds)\n\n const [peopleState, setPeopleState] = React.useState<OptionsState>(EMPTY_OPTIONS_STATE)\n const [companiesState, setCompaniesState] = React.useState<OptionsState>(EMPTY_OPTIONS_STATE)\n const peopleCacheRef = React.useRef<Map<string, FilterOption[]>>(new Map())\n const companiesCacheRef = React.useRef<Map<string, FilterOption[]>>(new Map())\n\n const buildPersonLabel = React.useCallback((record: PersonLookupRecord): string => {\n const parts: string[] = []\n const name = record.name?.trim()\n if (name) parts.push(name)\n const email = record.email?.trim()\n if (email && !parts.includes(email)) parts.push(email)\n const phone = record.phone?.trim()\n if (!parts.length && phone) parts.push(phone)\n if (!parts.length) parts.push(t('customers.deals.list.unnamedPerson', 'Unnamed person'))\n return parts.join(' \u2022 ')\n }, [t])\n\n const buildCompanyLabel = React.useCallback((record: CompanyLookupRecord): string => {\n const parts: string[] = []\n const name = record.name?.trim()\n if (name) parts.push(name)\n const domain = record.domain?.trim()\n if (domain && !parts.includes(domain)) parts.push(domain)\n const email = record.email?.trim()\n if (!parts.length && email) parts.push(email)\n if (!parts.length) parts.push(t('customers.deals.list.unnamedCompany', 'Unnamed company'))\n return parts.join(' \u2022 ')\n }, [t])\n\n const ingestPeopleRecords = React.useCallback((records: PersonLookupRecord[]) => {\n if (!records.length) return [] as FilterOption[]\n const queryMap = new Map<string, FilterOption>()\n setPeopleState((prev) => {\n const idToLabel = { ...prev.idToLabel }\n const labelToId: Record<string, string> = {}\n const merged = new Map(prev.options.map((opt) => [opt.value, opt]))\n const occupied = new Set<string>()\n Object.entries(prev.labelToId).forEach(([label, id]) => {\n occupied.add(label)\n labelToId[label] = id\n })\n records.forEach((record) => {\n if (!isUuid(record.id)) return\n const base = buildPersonLabel(record)\n let previousLabel = idToLabel[record.id]\n if (previousLabel) {\n // remove previous label before reassigning\n delete labelToId[previousLabel]\n occupied.delete(previousLabel)\n }\n const label = ensureUniqueLabel(base, occupied)\n idToLabel[record.id] = label\n labelToId[label] = record.id\n const option = { value: record.id, label }\n merged.set(record.id, option)\n queryMap.set(record.id, option)\n })\n const nextOptions = Array.from(merged.values()).sort((a, b) => a.label.localeCompare(b.label))\n return { options: nextOptions, idToLabel, labelToId }\n })\n return Array.from(queryMap.values()).sort((a, b) => a.label.localeCompare(b.label))\n }, [buildPersonLabel])\n\n const ingestCompanyRecords = React.useCallback((records: CompanyLookupRecord[]) => {\n if (!records.length) return [] as FilterOption[]\n const queryMap = new Map<string, FilterOption>()\n setCompaniesState((prev) => {\n const idToLabel = { ...prev.idToLabel }\n const labelToId: Record<string, string> = {}\n const merged = new Map(prev.options.map((opt) => [opt.value, opt]))\n const occupied = new Set<string>()\n Object.entries(prev.labelToId).forEach(([label, id]) => {\n occupied.add(label)\n labelToId[label] = id\n })\n records.forEach((record) => {\n if (!isUuid(record.id)) return\n const base = buildCompanyLabel(record)\n let previousLabel = idToLabel[record.id]\n if (previousLabel) {\n delete labelToId[previousLabel]\n occupied.delete(previousLabel)\n }\n const label = ensureUniqueLabel(base, occupied)\n idToLabel[record.id] = label\n labelToId[label] = record.id\n const option = { value: record.id, label }\n merged.set(record.id, option)\n queryMap.set(record.id, option)\n })\n const nextOptions = Array.from(merged.values()).sort((a, b) => a.label.localeCompare(b.label))\n return { options: nextOptions, idToLabel, labelToId }\n })\n return Array.from(queryMap.values()).sort((a, b) => a.label.localeCompare(b.label))\n }, [buildCompanyLabel])\n\n const loadPeopleOptions = React.useCallback(async (query?: string) => {\n const normalizedQuery = (query || '').trim().toLowerCase()\n const cacheKey = `${scopeVersion}|${normalizedQuery}`\n const cached = peopleCacheRef.current.get(cacheKey)\n if (cached) return cached\n const records = await fetchPeopleLookup(query)\n const options = ingestPeopleRecords(records)\n peopleCacheRef.current.set(cacheKey, options)\n return options\n }, [scopeVersion, ingestPeopleRecords])\n\n const loadCompanyOptions = React.useCallback(async (query?: string) => {\n const normalizedQuery = (query || '').trim().toLowerCase()\n const cacheKey = `${scopeVersion}|${normalizedQuery}`\n const cached = companiesCacheRef.current.get(cacheKey)\n if (cached) return cached\n const records = await fetchCompaniesLookup(query)\n const options = ingestCompanyRecords(records)\n companiesCacheRef.current.set(cacheKey, options)\n return options\n }, [scopeVersion, ingestCompanyRecords])\n\n React.useEffect(() => {\n let cancelled = false\n if (!selectedPersonIds.length) return\n const missing = selectedPersonIds.filter((id) => !peopleState.idToLabel[id])\n if (!missing.length) return\n fetchPeopleLookupByIds(missing).then((records) => {\n if (cancelled) return\n ingestPeopleRecords(records)\n })\n return () => { cancelled = true }\n }, [selectedPersonIds, peopleState.idToLabel, ingestPeopleRecords])\n\n React.useEffect(() => {\n let cancelled = false\n if (!selectedCompanyIds.length) return\n const missing = selectedCompanyIds.filter((id) => !companiesState.idToLabel[id])\n if (!missing.length) return\n fetchCompaniesLookupByIds(missing).then((records) => {\n if (cancelled) return\n ingestCompanyRecords(records)\n })\n return () => { cancelled = true }\n }, [selectedCompanyIds, companiesState.idToLabel, ingestCompanyRecords])\n\n const [dictionaryMaps, setDictionaryMaps] = React.useState<Record<DictionaryKey, CustomerDictionaryMap>>({\n 'deal-statuses': {},\n 'pipeline-stages': {},\n })\n\n const [pipelineNames, setPipelineNames] = React.useState<Record<string, string>>({})\n\n const fetchDictionaryEntries = React.useCallback(\n async (kind: DictionaryKey) => {\n try {\n const data = await ensureCustomerDictionary(queryClient, kind, scopeVersion)\n setDictionaryMaps((prev) => ({ ...prev, [kind]: data.map }))\n } catch {\n setDictionaryMaps((prev) => ({ ...prev, [kind]: {} }))\n }\n },\n [queryClient, scopeVersion],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function loadDictionaries() {\n if (cancelled) return\n await Promise.all([fetchDictionaryEntries('deal-statuses'), fetchDictionaryEntries('pipeline-stages')])\n }\n loadDictionaries().catch(() => {})\n return () => { cancelled = true }\n }, [fetchDictionaryEntries, reloadToken])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadPipelines() {\n try {\n const call = await apiCall<{ items?: Array<{ id: string; name: string }> }>('/api/customers/pipelines')\n if (cancelled || !call.ok) return\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const map: Record<string, string> = {}\n items.forEach((p) => { if (p.id && p.name) map[p.id] = p.name })\n setPipelineNames(map)\n } catch {}\n }\n loadPipelines().catch(() => {})\n return () => { cancelled = true }\n }, [reloadToken, scopeVersion])\n\n React.useEffect(() => {\n peopleCacheRef.current.clear()\n companiesCacheRef.current.clear()\n setPeopleState((prev) => {\n if (!prev.options.length && !Object.keys(prev.idToLabel).length) return prev\n return { ...EMPTY_OPTIONS_STATE }\n })\n setCompaniesState((prev) => {\n if (!prev.options.length && !Object.keys(prev.idToLabel).length) return prev\n return { ...EMPTY_OPTIONS_STATE }\n })\n }, [scopeVersion, reloadToken])\n\n const syncFilterLabels = React.useCallback((\n key: 'people' | 'companies',\n ids: string[],\n idToLabel: Record<string, string>,\n ) => {\n setFilterValues((prev) => {\n const current = Array.isArray(prev[key]) ? (prev[key] as string[]) : []\n if (!ids.length) {\n if (!current.length) return prev\n const next = { ...prev }\n delete next[key]\n return next\n }\n const labels: string[] = []\n ids.forEach((id) => {\n const label = idToLabel[id]\n if (label && !labels.includes(label)) labels.push(label)\n })\n if (labels.length < ids.length) return prev\n if (arraysEqual(current, labels)) return prev\n return { ...prev, [key]: labels }\n })\n }, [])\n\n React.useEffect(() => {\n syncFilterLabels('people', selectedPersonIds, peopleState.idToLabel)\n }, [selectedPersonIds, peopleState.idToLabel, syncFilterLabels])\n\n React.useEffect(() => {\n syncFilterLabels('companies', selectedCompanyIds, companiesState.idToLabel)\n }, [selectedCompanyIds, companiesState.idToLabel, syncFilterLabels])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value.trim())\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = { ...values }\n const rawPeople = Array.isArray(values.people) ? (values.people as string[]) : []\n const nextPersonIds: string[] = []\n rawPeople.forEach((value) => {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (!trimmed) return\n const mapped = peopleState.labelToId[trimmed]\n if (mapped && !nextPersonIds.includes(mapped)) nextPersonIds.push(mapped)\n })\n setSelectedPersonIds(nextPersonIds)\n if (nextPersonIds.length) {\n next.people = Array.from(new Set(rawPeople.map((value) => (typeof value === 'string' ? value.trim() : '')).filter((value) => value.length > 0)))\n } else {\n delete next.people\n }\n\n const rawCompanies = Array.isArray(values.companies) ? (values.companies as string[]) : []\n const nextCompanyIds: string[] = []\n rawCompanies.forEach((value) => {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (!trimmed) return\n const mapped = companiesState.labelToId[trimmed]\n if (mapped && !nextCompanyIds.includes(mapped)) nextCompanyIds.push(mapped)\n })\n setSelectedCompanyIds(nextCompanyIds)\n if (nextCompanyIds.length) {\n next.companies = Array.from(new Set(rawCompanies.map((value) => (typeof value === 'string' ? value.trim() : '')).filter((value) => value.length > 0)))\n } else {\n delete next.companies\n }\n\n setFilterValues(next)\n setPage(1)\n }, [peopleState.labelToId, companiesState.labelToId])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setSelectedPersonIds([])\n setSelectedCompanyIds([])\n setPage(1)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim().length) params.set('search', search.trim())\n if (selectedPersonIds.length) params.set('personId', selectedPersonIds.join(','))\n if (selectedCompanyIds.length) params.set('companyId', selectedCompanyIds.join(','))\n Object.entries(filterValues).forEach(([key, value]) => {\n if (key === 'people' || key === 'companies') return\n if (value == null) return\n if (Array.isArray(value)) {\n const normalized = value\n .map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item.trim()\n return String(item).trim()\n })\n .filter((item) => item.length > 0)\n if (normalized.length) params.set(key, normalized.join(','))\n } else if (typeof value === 'object') {\n const obj = value as Record<string, unknown>\n const from = typeof obj.from === 'string' ? obj.from.trim() : ''\n const to = typeof obj.to === 'string' ? obj.to.trim() : ''\n if (from) params.set(`${key}[from]`, from)\n if (to) params.set(`${key}[to]`, to)\n } else {\n const stringValue = typeof value === 'string' ? value.trim() : String(value)\n if (stringValue) params.set(key, stringValue)\n }\n })\n return params.toString()\n }, [filterValues, page, search, selectedCompanyIds, selectedPersonIds])\n\n const currentParams = React.useMemo(\n () => Object.fromEntries(new URLSearchParams(queryParams)),\n [queryParams],\n )\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/deals', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('customers/deals', { ...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: DealsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<DealsResponse>(`/api/customers/deals?${queryParams}`, undefined, { fallback })\n if (!call.ok) {\n const message =\n typeof (call.result as { error?: string } | undefined)?.error === 'string'\n ? (call.result as { error?: string }).error!\n : t('customers.deals.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 const mapped = items\n .map((item) => mapDeal(item as Record<string, unknown>))\n .filter((row): row is DealRow => !!row)\n setRows(mapped)\n setTotal(typeof payload.total === 'number' ? payload.total : mapped.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.deals.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 React.useEffect(() => {\n if (totalPages > 0 && page > totalPages) {\n setPage(totalPages)\n }\n }, [page, totalPages])\n\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 (selectedPersonIds.length) selectedPersonIds.forEach((id) => params.append('personId', id))\n if (selectedCompanyIds.length) selectedCompanyIds.forEach((id) => params.append('companyId', id))\n if (page > 1) params.set('page', String(page))\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, selectedPersonIds, selectedCompanyIds])\n\n const handleRefresh = React.useCallback(() => {\n peopleCacheRef.current.clear()\n companiesCacheRef.current.clear()\n void Promise.all([\n invalidateCustomerDictionary(queryClient, 'deal-statuses'),\n invalidateCustomerDictionary(queryClient, 'pipeline-stages'),\n ])\n setReloadToken((token) => token + 1)\n }, [queryClient])\n\n const handleDeleteDeal = React.useCallback(\n async (dealId: string) => {\n if (pendingDeleteId) return\n const confirmed = await confirm({\n title: t(\n 'customers.deals.list.deleteConfirm',\n 'Delete this deal? This action cannot be undone.',\n ),\n variant: 'destructive',\n })\n if (!confirmed) return\n setPendingDeleteId(dealId)\n try {\n await deleteCrud('customers/deals', {\n body: { id: dealId },\n errorMessage: t('customers.deals.list.deleteError', 'Failed to delete deal.'),\n })\n flash(t('customers.deals.list.deleteSuccess', 'Deal deleted.'), 'success')\n setRows((prev) => prev.filter((row) => row.id !== dealId))\n setTotal((prev) => Math.max(0, prev - 1))\n handleRefresh()\n } catch (err) {\n const message =\n err instanceof Error && err.message\n ? err.message\n : t('customers.deals.list.deleteError', 'Failed to delete deal.')\n flash(message, 'error')\n } finally {\n setPendingDeleteId(null)\n }\n },\n [confirm, handleRefresh, pendingDeleteId, t],\n )\n\n const personOptions = peopleState.options\n const companyOptions = companiesState.options\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'people',\n label: t('customers.deals.list.filters.people'),\n type: 'tags',\n options: personOptions,\n loadOptions: loadPeopleOptions,\n placeholder: t('customers.deals.list.filters.peoplePlaceholder'),\n },\n {\n id: 'companies',\n label: t('customers.deals.list.filters.companies'),\n type: 'tags',\n options: companyOptions,\n loadOptions: loadCompanyOptions,\n placeholder: t('customers.deals.list.filters.companiesPlaceholder'),\n },\n ], [companyOptions, loadCompanyOptions, loadPeopleOptions, personOptions, t])\n\n const { data: customFieldDefs = [] } = useCustomFieldDefs([E.customers.customer_deal], {\n keyExtras: [scopeVersion, reloadToken],\n })\n\n const columns = React.useMemo<ColumnDef<DealRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">{t('customers.deals.list.noValue')}</span>\n const renderDictionaryCell = (kind: DictionaryKey, value: string | null | undefined) => (\n <DictionaryValue\n value={value}\n map={dictionaryMaps[kind]}\n fallback={value ? <span className=\"text-sm\">{value}</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 const renderAssociationList = (\n items: { id: string; label: string }[],\n fallbackLabel: string,\n ) => {\n if (!items.length) return noValue\n return (\n <ul className=\"flex flex-wrap gap-1 text-sm\">\n {items.map((entry) => (\n <li key={entry.id} className=\"rounded border px-2 py-0.5 text-xs bg-muted\">\n {entry.label && entry.label.trim().length ? entry.label : fallbackLabel}\n </li>\n ))}\n </ul>\n )\n }\n\n const customColumns = filterCustomFieldDefs(customFieldDefs, 'list').map<ColumnDef<DealRow>>((def) => ({\n accessorKey: `cf_${def.key}`,\n header: def.label || def.key,\n cell: ({ getValue }) => {\n const value = getValue()\n if (value == null) return noValue\n if (Array.isArray(value)) {\n const normalized = value\n .map((item) => {\n if (item == null) return ''\n if (typeof item === 'string') return item.trim()\n return String(item).trim()\n })\n .filter((item) => item.length > 0)\n if (!normalized.length) return noValue\n return <span className=\"text-sm\">{normalized.join(', ')}</span>\n }\n if (typeof value === 'boolean') {\n return (\n <span className=\"text-sm\">\n {value\n ? t('customers.deals.list.booleanYes', 'Yes')\n : t('customers.deals.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\n return [\n {\n accessorKey: 'title',\n header: t('customers.deals.list.columns.title'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.title}</span>,\n },\n {\n accessorKey: 'status',\n header: t('customers.deals.list.columns.status'),\n cell: ({ row }) => renderDictionaryCell('deal-statuses', row.original.status),\n },\n {\n accessorKey: 'pipelineStage',\n header: t('customers.deals.list.columns.pipelineStage'),\n cell: ({ row }) => renderDictionaryCell('pipeline-stages', row.original.pipelineStage),\n },\n {\n accessorKey: 'pipelineId',\n header: t('customers.deals.list.columns.pipeline', 'Pipeline'),\n cell: ({ row }) => {\n const name = row.original.pipelineId ? pipelineNames[row.original.pipelineId] : null\n return name ? <span className=\"text-sm\">{name}</span> : noValue\n },\n },\n {\n accessorKey: 'valueAmount',\n header: t('customers.deals.list.columns.value'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium\">\n {formatCurrency(row.original.valueAmount ?? null, row.original.valueCurrency ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n {\n accessorKey: 'probability',\n header: t('customers.deals.list.columns.probability'),\n cell: ({ row }) => {\n const value = row.original.probability\n if (typeof value === 'number' && Number.isFinite(value)) {\n return <span className=\"text-sm\">{`${Math.min(Math.max(value, 0), 100)}%`}</span>\n }\n return noValue\n },\n },\n {\n accessorKey: 'expectedCloseAt',\n header: t('customers.deals.list.columns.expectedClose'),\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {formatDateValue(row.original.expectedCloseAt ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n {\n accessorKey: 'companies',\n header: t('customers.deals.list.columns.companies'),\n cell: ({ row }) => renderAssociationList(row.original.companies, t('customers.deals.list.unnamedCompany')),\n },\n {\n accessorKey: 'people',\n header: t('customers.deals.list.columns.people'),\n cell: ({ row }) => renderAssociationList(row.original.people, t('customers.deals.list.unnamedPerson')),\n },\n {\n accessorKey: 'updatedAt',\n header: t('customers.deals.list.columns.updatedAt'),\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {formatDateValue(row.original.updatedAt ?? null, t('customers.deals.list.noValue'))}\n </span>\n ),\n },\n ...customColumns,\n ]\n }, [customFieldDefs, dictionaryMaps, pipelineNames, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<DealRow>\n title={t('customers.deals.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customers/deals/create\">\n {t('customers.deals.list.actions.new', 'New deal')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n onRowClick={(row) => {\n router.push(`/backend/customers/deals/${row.id}`)\n }}\n rowActions={(row) => {\n const isDeleting = pendingDeleteId === row.id\n return (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('customers.deals.list.actions.edit', 'Edit'),\n onSelect: () => { router.push(`/backend/customers/deals/${row.id}`) },\n },\n {\n id: 'open-new-tab',\n label: t('customers.deals.list.actions.openInNewTab', 'Open in new tab'),\n onSelect: () => {\n if (typeof window !== 'undefined') {\n window.open(`/backend/customers/deals/${row.id}`, '_blank', 'noopener')\n }\n },\n },\n {\n id: 'delete',\n label: isDeleting\n ? t('customers.deals.list.actions.deleting', 'Deleting\u2026')\n : t('customers.deals.list.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => handleDeleteDeal(row.id),\n },\n ]}\n />\n )\n }}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('customers.deals.list.searchPlaceholder')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: (nextPage) => setPage(nextPage),\n cacheStatus,\n }}\n isLoading={isLoading}\n refreshButton={{\n label: t('customers.deals.list.refresh'),\n onRefresh: handleRefresh,\n }}\n exporter={exportConfig}\n entityId={E.customers.customer_deal}\n perspective={{ tableId: 'customers.deals.list' }}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n\nfunction mapDeal(item: Record<string, unknown>): DealRow | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const title = typeof item.title === 'string' ? item.title : ''\n const status = typeof item.status === 'string' ? item.status : null\n const pipelineStage = typeof item.pipeline_stage === 'string' ? item.pipeline_stage : null\n const pipelineStageId = typeof item.pipeline_stage_id === 'string' ? item.pipeline_stage_id : null\n const pipelineId = typeof item.pipeline_id === 'string' ? item.pipeline_id : null\n const valueAmountRaw = item.value_amount\n const valueAmount =\n typeof valueAmountRaw === 'number'\n ? valueAmountRaw\n : typeof valueAmountRaw === 'string' && valueAmountRaw.trim()\n ? Number(valueAmountRaw)\n : null\n const valueCurrency =\n typeof item.value_currency === 'string' && item.value_currency.trim().length\n ? item.value_currency.trim().toUpperCase()\n : null\n const probabilityRaw = item.probability\n const probability =\n typeof probabilityRaw === 'number'\n ? probabilityRaw\n : typeof probabilityRaw === 'string' && probabilityRaw.trim().length\n ? Number(probabilityRaw)\n : null\n const expectedCloseAt = typeof item.expected_close_at === 'string' ? item.expected_close_at : null\n const updatedAt = typeof item.updated_at === 'string' ? item.updated_at : null\n const peopleRaw = Array.isArray(item.people) ? item.people : []\n const companiesRaw = Array.isArray(item.companies) ? item.companies : []\n const people = peopleRaw\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const data = entry as Record<string, unknown>\n const pid = typeof data.id === 'string' ? data.id : null\n if (!pid) return null\n const label = typeof data.label === 'string' ? data.label : ''\n return { id: pid, label }\n })\n .filter((entry): entry is { id: string; label: string } => !!entry)\n const companies = companiesRaw\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const data = entry as Record<string, unknown>\n const cid = typeof data.id === 'string' ? data.id : null\n if (!cid) return null\n const label = typeof data.label === 'string' ? data.label : ''\n return { id: cid, label }\n })\n .filter((entry): entry is { id: string; label: string } => !!entry)\n const customFields: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) customFields[key] = value\n }\n return {\n id,\n title,\n status,\n pipelineStage,\n pipelineStageId,\n pipelineId,\n valueAmount,\n valueCurrency,\n probability,\n expectedCloseAt,\n updatedAt,\n people,\n companies,\n ...customFields,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["formatter"]
|
|
7
7
|
}
|
|
@@ -11,12 +11,8 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
|
11
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
12
|
import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
13
13
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
14
|
-
import {
|
|
15
|
-
customerDictionaryQueryOptions
|
|
16
|
-
} from "../../../../components/detail/hooks/useCustomerDictionary.js";
|
|
17
|
-
import { renderDictionaryColor, renderDictionaryIcon } from "../../../../lib/dictionaries.js";
|
|
18
14
|
const DEALS_QUERY_LIMIT = 100;
|
|
19
|
-
const dealsQueryKey = (scopeVersion) => ["customers", "deals", "pipeline", `scope:${scopeVersion}`];
|
|
15
|
+
const dealsQueryKey = (scopeVersion, pipelineId) => ["customers", "deals", "pipeline", `scope:${scopeVersion}`, `pipeline:${pipelineId ?? "none"}`];
|
|
20
16
|
const sortOptions = ["probability", "createdAt", "expectedCloseAt"];
|
|
21
17
|
function normalizeAmount(value) {
|
|
22
18
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
@@ -41,36 +37,16 @@ function normalizeTimestamp(value) {
|
|
|
41
37
|
if (Number.isNaN(date.getTime())) return { iso: null, ts: null };
|
|
42
38
|
return { iso: date.toISOString(), ts: date.getTime() };
|
|
43
39
|
}
|
|
44
|
-
function
|
|
45
|
-
const result =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
label,
|
|
55
|
-
color: typeof entry.color === "string" ? entry.color : null,
|
|
56
|
-
icon: typeof entry.icon === "string" ? entry.icon : null
|
|
57
|
-
});
|
|
58
|
-
seen.add(entry.value);
|
|
59
|
-
});
|
|
60
|
-
const unknownStages = /* @__PURE__ */ new Map();
|
|
61
|
-
deals.forEach((deal) => {
|
|
62
|
-
if (!deal.pipelineStage || seen.has(deal.pipelineStage)) return;
|
|
63
|
-
if (unknownStages.has(deal.pipelineStage)) return;
|
|
64
|
-
unknownStages.set(deal.pipelineStage, {
|
|
65
|
-
id: `stage:${deal.pipelineStage}`,
|
|
66
|
-
value: deal.pipelineStage,
|
|
67
|
-
label: deal.pipelineStage,
|
|
68
|
-
color: null,
|
|
69
|
-
icon: null
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
unknownStages.forEach((entry) => result.push(entry));
|
|
73
|
-
const hasUnassigned = deals.some((deal) => !deal.pipelineStage);
|
|
40
|
+
function buildStageDefinitionsFromPipelineStages(pipelineStages, deals, t) {
|
|
41
|
+
const result = pipelineStages.slice().sort((a, b) => a.order - b.order).map((stage) => ({
|
|
42
|
+
id: stage.id,
|
|
43
|
+
value: stage.id,
|
|
44
|
+
label: stage.label,
|
|
45
|
+
color: null,
|
|
46
|
+
icon: null
|
|
47
|
+
}));
|
|
48
|
+
const knownIds = new Set(pipelineStages.map((s) => s.id));
|
|
49
|
+
const hasUnassigned = deals.some((deal) => !deal.pipelineStageId || !knownIds.has(deal.pipelineStageId));
|
|
74
50
|
if (hasUnassigned) {
|
|
75
51
|
result.push({
|
|
76
52
|
id: "stage:__unassigned",
|
|
@@ -85,10 +61,10 @@ function buildStageDefinitions(dictionary, deals, t) {
|
|
|
85
61
|
function createDealMap(deals) {
|
|
86
62
|
return deals.reduce((acc, deal) => acc.set(deal.id, deal), /* @__PURE__ */ new Map());
|
|
87
63
|
}
|
|
88
|
-
function
|
|
64
|
+
function groupDealsByStageId(deals) {
|
|
89
65
|
const byStage = /* @__PURE__ */ new Map();
|
|
90
66
|
deals.forEach((deal) => {
|
|
91
|
-
const stageKey = deal.
|
|
67
|
+
const stageKey = deal.pipelineStageId ?? null;
|
|
92
68
|
const bucket = byStage.get(stageKey) ?? [];
|
|
93
69
|
bucket.push(deal);
|
|
94
70
|
byStage.set(stageKey, bucket);
|
|
@@ -160,10 +136,43 @@ function SalesPipelinePage() {
|
|
|
160
136
|
const queryClient = useQueryClient();
|
|
161
137
|
const [sortBy, setSortBy] = React.useState("probability");
|
|
162
138
|
const [pendingDealId, setPendingDealId] = React.useState(null);
|
|
163
|
-
const
|
|
164
|
-
const
|
|
139
|
+
const [selectedPipelineId, setSelectedPipelineId] = React.useState(null);
|
|
140
|
+
const pipelinesQuery = useQuery({
|
|
141
|
+
queryKey: ["customers", "pipelines", `scope:${scopeVersion}`],
|
|
142
|
+
staleTime: 6e4,
|
|
143
|
+
queryFn: async () => {
|
|
144
|
+
const payload = await readApiResultOrThrow(
|
|
145
|
+
"/api/customers/pipelines",
|
|
146
|
+
void 0,
|
|
147
|
+
{ errorMessage: translate("customers.deals.pipeline.loadError", "Failed to load pipelines.") }
|
|
148
|
+
);
|
|
149
|
+
return payload?.items ?? [];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
React.useEffect(() => {
|
|
153
|
+
if (selectedPipelineId) return;
|
|
154
|
+
const pipelines = pipelinesQuery.data;
|
|
155
|
+
if (!pipelines || !pipelines.length) return;
|
|
156
|
+
const defaultPipeline = pipelines.find((p) => p.isDefault) ?? pipelines[0];
|
|
157
|
+
if (defaultPipeline) setSelectedPipelineId(defaultPipeline.id);
|
|
158
|
+
}, [pipelinesQuery.data, selectedPipelineId]);
|
|
159
|
+
const stagesQuery = useQuery({
|
|
160
|
+
queryKey: ["customers", "pipeline-stages", `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],
|
|
161
|
+
enabled: !!selectedPipelineId,
|
|
162
|
+
staleTime: 3e4,
|
|
163
|
+
queryFn: async () => {
|
|
164
|
+
const payload = await readApiResultOrThrow(
|
|
165
|
+
`/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(selectedPipelineId)}`,
|
|
166
|
+
void 0,
|
|
167
|
+
{ errorMessage: translate("customers.deals.pipeline.loadError", "Failed to load stages.") }
|
|
168
|
+
);
|
|
169
|
+
return payload?.items ?? [];
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const dealsKey = React.useMemo(() => dealsQueryKey(scopeVersion, selectedPipelineId), [scopeVersion, selectedPipelineId]);
|
|
165
173
|
const dealsQuery = useQuery({
|
|
166
174
|
queryKey: dealsKey,
|
|
175
|
+
enabled: !!selectedPipelineId,
|
|
167
176
|
staleTime: 3e4,
|
|
168
177
|
queryFn: async () => {
|
|
169
178
|
const search = new URLSearchParams();
|
|
@@ -171,6 +180,7 @@ function SalesPipelinePage() {
|
|
|
171
180
|
search.set("pageSize", String(DEALS_QUERY_LIMIT));
|
|
172
181
|
search.set("sortField", "createdAt");
|
|
173
182
|
search.set("sortDir", "desc");
|
|
183
|
+
if (selectedPipelineId) search.set("pipelineId", selectedPipelineId);
|
|
174
184
|
const payload = await readApiResultOrThrow(
|
|
175
185
|
`/api/customers/deals?${search.toString()}`,
|
|
176
186
|
void 0,
|
|
@@ -215,6 +225,8 @@ function SalesPipelinePage() {
|
|
|
215
225
|
title,
|
|
216
226
|
status,
|
|
217
227
|
pipelineStage: stage,
|
|
228
|
+
pipelineId: typeof data.pipeline_id === "string" ? data.pipeline_id : null,
|
|
229
|
+
pipelineStageId: typeof data.pipeline_stage_id === "string" ? data.pipeline_stage_id : null,
|
|
218
230
|
valueAmount: amount,
|
|
219
231
|
valueCurrency: currency,
|
|
220
232
|
probability,
|
|
@@ -234,10 +246,10 @@ function SalesPipelinePage() {
|
|
|
234
246
|
const deals = dealsQuery.data?.deals ?? [];
|
|
235
247
|
const total = dealsQuery.data?.total ?? deals.length;
|
|
236
248
|
const dealMap = React.useMemo(() => createDealMap(deals), [deals]);
|
|
237
|
-
const groupedDeals = React.useMemo(() =>
|
|
249
|
+
const groupedDeals = React.useMemo(() => groupDealsByStageId(deals), [deals]);
|
|
238
250
|
const stages = React.useMemo(
|
|
239
|
-
() =>
|
|
240
|
-
[
|
|
251
|
+
() => buildStageDefinitionsFromPipelineStages(stagesQuery.data ?? [], deals, t),
|
|
252
|
+
[stagesQuery.data, deals, t]
|
|
241
253
|
);
|
|
242
254
|
const dateFormatter = React.useMemo(
|
|
243
255
|
() => new Intl.DateTimeFormat(void 0, {
|
|
@@ -246,25 +258,25 @@ function SalesPipelinePage() {
|
|
|
246
258
|
[]
|
|
247
259
|
);
|
|
248
260
|
const updateStageMutation = useMutation({
|
|
249
|
-
mutationFn: async ({ id,
|
|
261
|
+
mutationFn: async ({ id, pipelineStageId }) => {
|
|
250
262
|
await apiCallOrThrow(
|
|
251
263
|
"/api/customers/deals",
|
|
252
264
|
{
|
|
253
265
|
method: "PUT",
|
|
254
266
|
headers: { "content-type": "application/json" },
|
|
255
|
-
body: JSON.stringify({ id,
|
|
267
|
+
body: JSON.stringify({ id, pipelineStageId })
|
|
256
268
|
},
|
|
257
269
|
{ errorMessage: translate("customers.deals.pipeline.moveError", "Failed to update deal stage.") }
|
|
258
270
|
);
|
|
259
|
-
return { id,
|
|
271
|
+
return { id, pipelineStageId };
|
|
260
272
|
},
|
|
261
|
-
onMutate: async ({ id,
|
|
273
|
+
onMutate: async ({ id, pipelineStageId }) => {
|
|
262
274
|
setPendingDealId(id);
|
|
263
275
|
await queryClient.cancelQueries({ queryKey: dealsKey });
|
|
264
276
|
const previous = queryClient.getQueryData(dealsKey);
|
|
265
277
|
if (previous) {
|
|
266
278
|
const nextDeals = previous.deals.map(
|
|
267
|
-
(deal) => deal.id === id ? { ...deal,
|
|
279
|
+
(deal) => deal.id === id ? { ...deal, pipelineStageId } : deal
|
|
268
280
|
);
|
|
269
281
|
queryClient.setQueryData(dealsKey, { ...previous, deals: nextDeals });
|
|
270
282
|
}
|
|
@@ -317,10 +329,10 @@ function SalesPipelinePage() {
|
|
|
317
329
|
);
|
|
318
330
|
return;
|
|
319
331
|
}
|
|
320
|
-
if (deal.
|
|
321
|
-
updateStageMutation.mutate({ id: dealId,
|
|
332
|
+
if (deal.pipelineStageId === stage.value) return;
|
|
333
|
+
updateStageMutation.mutate({ id: dealId, pipelineStageId: stage.value });
|
|
322
334
|
},
|
|
323
|
-
[dealMap, draggingId,
|
|
335
|
+
[dealMap, draggingId, translate, updateStageMutation]
|
|
324
336
|
);
|
|
325
337
|
const handleDragOver = React.useCallback(
|
|
326
338
|
(stageId) => (event) => {
|
|
@@ -331,16 +343,10 @@ function SalesPipelinePage() {
|
|
|
331
343
|
[activeLane]
|
|
332
344
|
);
|
|
333
345
|
const renderLaneHeader = (stage, count) => {
|
|
334
|
-
return /* @__PURE__ */
|
|
335
|
-
/* @__PURE__ */
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: stage.label }),
|
|
339
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: translate("customers.deals.pipeline.countLabel", "Deals: {count}", { count }) })
|
|
340
|
-
] })
|
|
341
|
-
] }),
|
|
342
|
-
stage.color ? renderDictionaryColor(stage.color, "h-3 w-3 rounded-full") : null
|
|
343
|
-
] });
|
|
346
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between gap-3 border-b border-border px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
347
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: stage.label }),
|
|
348
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: translate("customers.deals.pipeline.countLabel", "Deals: {count}", { count }) })
|
|
349
|
+
] }) }) });
|
|
344
350
|
};
|
|
345
351
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
346
352
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
@@ -351,24 +357,46 @@ function SalesPipelinePage() {
|
|
|
351
357
|
"Track deals by pipeline stage and drag them between lanes to update progress."
|
|
352
358
|
) })
|
|
353
359
|
] }),
|
|
354
|
-
/* @__PURE__ */ jsxs("
|
|
355
|
-
/* @__PURE__ */
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
361
|
+
pipelinesQuery.data && pipelinesQuery.data.length > 0 ? /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm font-medium text-muted-foreground", children: [
|
|
362
|
+
/* @__PURE__ */ jsx("span", { children: translate("customers.deals.pipeline.switch.label", "Pipeline") }),
|
|
363
|
+
/* @__PURE__ */ jsx(
|
|
364
|
+
"select",
|
|
365
|
+
{
|
|
366
|
+
className: "h-9 rounded-md border border-border bg-background px-3 text-sm text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
367
|
+
value: selectedPipelineId ?? "",
|
|
368
|
+
onChange: (e) => setSelectedPipelineId(e.target.value || null),
|
|
369
|
+
children: pipelinesQuery.data.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
] }) : null,
|
|
373
|
+
/* @__PURE__ */ jsx(
|
|
374
|
+
Link,
|
|
358
375
|
{
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
children: [
|
|
363
|
-
/* @__PURE__ */ jsx("option", { value: "probability", children: translate("customers.deals.pipeline.sort.probability", "Probability (high to low)") }),
|
|
364
|
-
/* @__PURE__ */ jsx("option", { value: "createdAt", children: translate("customers.deals.pipeline.sort.createdAt", "Created (newest first)") }),
|
|
365
|
-
/* @__PURE__ */ jsx("option", { value: "expectedCloseAt", children: translate("customers.deals.pipeline.sort.expectedCloseAt", "Expected close (soonest first)") })
|
|
366
|
-
]
|
|
376
|
+
href: "/backend/config/customers/pipeline-stages",
|
|
377
|
+
className: "text-sm font-medium text-primary hover:underline",
|
|
378
|
+
children: translate("customers.deals.pipeline.manageStages", "Manage stages")
|
|
367
379
|
}
|
|
368
|
-
)
|
|
380
|
+
),
|
|
381
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm font-medium text-muted-foreground", children: [
|
|
382
|
+
/* @__PURE__ */ jsx("span", { children: translate("customers.deals.pipeline.sort.label", "Sort by") }),
|
|
383
|
+
/* @__PURE__ */ jsxs(
|
|
384
|
+
"select",
|
|
385
|
+
{
|
|
386
|
+
className: "h-9 rounded-md border border-border bg-background px-3 text-sm text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
387
|
+
value: sortBy,
|
|
388
|
+
onChange: handleSortChange,
|
|
389
|
+
children: [
|
|
390
|
+
/* @__PURE__ */ jsx("option", { value: "probability", children: translate("customers.deals.pipeline.sort.probability", "Probability (high to low)") }),
|
|
391
|
+
/* @__PURE__ */ jsx("option", { value: "createdAt", children: translate("customers.deals.pipeline.sort.createdAt", "Created (newest first)") }),
|
|
392
|
+
/* @__PURE__ */ jsx("option", { value: "expectedCloseAt", children: translate("customers.deals.pipeline.sort.expectedCloseAt", "Expected close (soonest first)") })
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
] })
|
|
369
397
|
] })
|
|
370
398
|
] }),
|
|
371
|
-
dealsQuery.isLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-[50vh] items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, {}) }) : dealsQuery.isError ? /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsx(
|
|
399
|
+
!selectedPipelineId ? /* @__PURE__ */ jsx("div", { className: "flex h-[50vh] items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: translate("customers.deals.pipeline.noPipeline", "No pipeline selected. Create a pipeline in settings.") }) }) : dealsQuery.isLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-[50vh] items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, {}) }) : dealsQuery.isError ? /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsx(
|
|
372
400
|
ErrorNotice,
|
|
373
401
|
{
|
|
374
402
|
message: dealsQuery.error instanceof Error ? dealsQuery.error.message : translate("customers.deals.pipeline.loadError", "Failed to load deals.")
|