@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/pipeline/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport {\n customerDictionaryQueryOptions,\n type CustomerDictionaryQueryData,\n} from '../../../../components/detail/hooks/useCustomerDictionary'\nimport { renderDictionaryColor, renderDictionaryIcon } from '../../../../lib/dictionaries'\n\ntype DealAssociation = { id: string; label: string }\n\ntype DealRecord = {\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 expectedCloseAtTs: number | null\n createdAt: string | null\n createdAtTs: number | null\n updatedAt: string | null\n people: DealAssociation[]\n companies: DealAssociation[]\n}\n\ntype DealsQueryData = {\n deals: DealRecord[]\n total: number\n}\n\ntype StageDefinition = {\n id: string\n value: string | null\n label: string\n color: string | null\n icon: string | null\n}\n\ntype SortOption = 'probability' | 'createdAt' | 'expectedCloseAt'\n\nconst DEALS_QUERY_LIMIT = 100\n\nconst dealsQueryKey = (scopeVersion: number) =>\n ['customers', 'deals', 'pipeline', `scope:${scopeVersion}`] as const\n\nconst sortOptions: SortOption[] = ['probability', 'createdAt', 'expectedCloseAt']\n\nfunction normalizeAmount(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction normalizeProbability(value: unknown): number | null {\n const parsed = normalizeAmount(value)\n if (parsed === null) return null\n if (parsed < 0) return 0\n if (parsed > 100) return 100\n return Math.round(parsed)\n}\n\nfunction normalizeTimestamp(value: unknown): { iso: string | null; ts: number | null } {\n if (typeof value !== 'string' || !value.trim().length) return { iso: null, ts: null }\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return { iso: null, ts: null }\n return { iso: date.toISOString(), ts: date.getTime() }\n}\n\nfunction buildStageDefinitions(\n dictionary: CustomerDictionaryQueryData | undefined,\n deals: DealRecord[],\n t: ReturnType<typeof useT>,\n): StageDefinition[] {\n const result: StageDefinition[] = []\n const seen = new Set<string>()\n const dictionaryEntries =\n dictionary?.fullEntries ?? dictionary?.entries?.map((entry) => ({ ...entry, id: entry.value })) ?? []\n\n dictionaryEntries.forEach((entry, index) => {\n if (!entry || typeof entry.value !== 'string') return\n const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label.trim() : entry.value\n result.push({\n id: `stage:${entry.value}:${index}`,\n value: entry.value,\n label,\n color: typeof entry.color === 'string' ? entry.color : null,\n icon: typeof entry.icon === 'string' ? entry.icon : null,\n })\n seen.add(entry.value)\n })\n\n const unknownStages = new Map<string, StageDefinition>()\n deals.forEach((deal) => {\n if (!deal.pipelineStage || seen.has(deal.pipelineStage)) return\n if (unknownStages.has(deal.pipelineStage)) return\n unknownStages.set(deal.pipelineStage, {\n id: `stage:${deal.pipelineStage}`,\n value: deal.pipelineStage,\n label: deal.pipelineStage,\n color: null,\n icon: null,\n })\n })\n\n unknownStages.forEach((entry) => result.push(entry))\n\n const hasUnassigned = deals.some((deal) => !deal.pipelineStage)\n if (hasUnassigned) {\n result.push({\n id: 'stage:__unassigned',\n value: null,\n label: translateWithFallback(t, 'customers.deals.pipeline.unassigned', 'No stage'),\n color: null,\n icon: null,\n })\n }\n\n return result\n}\n\nfunction createDealMap(deals: DealRecord[]): Map<string, DealRecord> {\n return deals.reduce<Map<string, DealRecord>>((acc, deal) => acc.set(deal.id, deal), new Map())\n}\n\nfunction groupDealsByStage(deals: DealRecord[]): Map<string | null, DealRecord[]> {\n const byStage = new Map<string | null, DealRecord[]>()\n deals.forEach((deal) => {\n const stageKey = deal.pipelineStage ?? null\n const bucket = byStage.get(stageKey) ?? []\n bucket.push(deal)\n byStage.set(stageKey, bucket)\n })\n return byStage\n}\n\nfunction sortDeals(deals: DealRecord[], option: SortOption): DealRecord[] {\n const sorted = [...deals]\n sorted.sort((a, b) => {\n if (option === 'probability') {\n const ap = typeof a.probability === 'number' ? a.probability : -1\n const bp = typeof b.probability === 'number' ? b.probability : -1\n if (ap !== bp) return bp - ap\n }\n if (option === 'expectedCloseAt') {\n const at = typeof a.expectedCloseAtTs === 'number' ? a.expectedCloseAtTs : Number.POSITIVE_INFINITY\n const bt = typeof b.expectedCloseAtTs === 'number' ? b.expectedCloseAtTs : Number.POSITIVE_INFINITY\n if (at !== bt) return at - bt\n }\n const at = typeof a.createdAtTs === 'number' ? a.createdAtTs : Number.NEGATIVE_INFINITY\n const bt = typeof b.createdAtTs === 'number' ? b.createdAtTs : Number.NEGATIVE_INFINITY\n if (option === 'createdAt') {\n if (at !== bt) return bt - at\n } else if (option === 'expectedCloseAt' || option === 'probability') {\n if (at !== bt) return bt - at\n }\n return a.title.localeCompare(b.title, undefined, { sensitivity: 'base' })\n })\n return sorted\n}\n\nfunction formatCurrency(amount: number | null, currency: string | null, fallback: string): string {\n if (amount === null || Number.isNaN(amount)) return fallback\n const code = currency && currency.length === 3 ? currency.toUpperCase() : 'USD'\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: code,\n maximumFractionDigits: 2,\n }).format(amount)\n } catch {\n return `${code} ${amount.toFixed(2)}`\n }\n}\n\nfunction formatProbability(probability: number | null, fallback: string): string {\n if (typeof probability !== 'number' || Number.isNaN(probability)) return fallback\n return `${probability}%`\n}\n\nexport default function SalesPipelinePage(): React.ReactElement {\n const t = useT()\n const translate = React.useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>) => {\n const value = translateWithFallback(t, key, fallback, params)\n if (value === fallback && params) {\n return fallback.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (match, doubleToken, singleToken) => {\n const token = (doubleToken ?? singleToken) as string | undefined\n if (!token) return match\n const replacement = params[token]\n if (replacement === undefined) {\n return doubleToken ? `{{${token}}}` : `{${token}}`\n }\n return String(replacement)\n })\n }\n return value\n },\n [t],\n )\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const [sortBy, setSortBy] = React.useState<SortOption>('probability')\n const [pendingDealId, setPendingDealId] = React.useState<string | null>(null)\n const dealsKey = React.useMemo(() => dealsQueryKey(scopeVersion), [scopeVersion])\n\n const { data: dictionaryData } = useQuery(customerDictionaryQueryOptions('pipeline-stages', scopeVersion))\n\n const dealsQuery = useQuery<DealsQueryData>({\n queryKey: dealsKey,\n staleTime: 30_000,\n queryFn: async () => {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', String(DEALS_QUERY_LIMIT))\n search.set('sortField', 'createdAt')\n search.set('sortDir', 'desc')\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/customers/deals?${search.toString()}`,\n undefined,\n { errorMessage: translate('customers.deals.pipeline.loadError', 'Failed to load deals.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const deals: DealRecord[] = []\n items.forEach((item) => {\n if (!item || typeof item !== 'object') return\n const data = item as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : null\n if (!id) return\n const title =\n typeof data.title === 'string' && data.title.trim().length\n ? data.title.trim()\n : translate('customers.deals.pipeline.untitled', 'Untitled deal')\n const status =\n typeof data.status === 'string' && data.status.trim().length ? data.status.trim() : null\n const stage =\n typeof data.pipeline_stage === 'string' && data.pipeline_stage.trim().length\n ? data.pipeline_stage.trim()\n : null\n const amount = normalizeAmount(data.value_amount)\n const currency =\n typeof data.value_currency === 'string' && data.value_currency.trim().length\n ? data.value_currency.trim().toUpperCase()\n : null\n const probability = normalizeProbability(data.probability)\n const expected = normalizeTimestamp(data.expected_close_at)\n const created = normalizeTimestamp(data.created_at)\n const updated = normalizeTimestamp(data.updated_at)\n const rawPeople = Array.isArray(data.people) ? data.people : []\n const people: DealAssociation[] = rawPeople\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const personId = typeof ref.id === 'string' ? ref.id : null\n if (!personId) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length\n ? ref.label.trim()\n : personId\n return { id: personId, label }\n })\n .filter((entry): entry is DealAssociation => !!entry)\n const rawCompanies = Array.isArray(data.companies) ? data.companies : []\n const companies: DealAssociation[] = rawCompanies\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const companyId = typeof ref.id === 'string' ? ref.id : null\n if (!companyId) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length\n ? ref.label.trim()\n : companyId\n return { id: companyId, label }\n })\n .filter((entry): entry is DealAssociation => !!entry)\n deals.push({\n id,\n title,\n status,\n pipelineStage: stage,\n valueAmount: amount,\n valueCurrency: currency,\n probability,\n expectedCloseAt: expected.iso,\n expectedCloseAtTs: expected.ts,\n createdAt: created.iso,\n createdAtTs: created.ts,\n updatedAt: updated.iso,\n people,\n companies,\n })\n })\n\n const total = typeof payload?.total === 'number' ? payload.total : deals.length\n return { deals, total }\n },\n })\n\n const deals = dealsQuery.data?.deals ?? []\n const total = dealsQuery.data?.total ?? deals.length\n const dealMap = React.useMemo(() => createDealMap(deals), [deals])\n const groupedDeals = React.useMemo(() => groupDealsByStage(deals), [deals])\n const stages = React.useMemo(\n () => buildStageDefinitions(dictionaryData, deals, t),\n [dictionaryData, deals, t],\n )\n\n const dateFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(undefined, {\n dateStyle: 'medium',\n }),\n [],\n )\n\n const updateStageMutation = useMutation({\n mutationFn: async ({ id, pipelineStage }: { id: string; pipelineStage: string }) => {\n await apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id, pipelineStage }),\n },\n { errorMessage: translate('customers.deals.pipeline.moveError', 'Failed to update deal stage.') },\n )\n return { id, pipelineStage }\n },\n onMutate: async ({ id, pipelineStage }) => {\n setPendingDealId(id)\n await queryClient.cancelQueries({ queryKey: dealsKey })\n const previous = queryClient.getQueryData<DealsQueryData>(dealsKey)\n if (previous) {\n const nextDeals = previous.deals.map((deal) =>\n deal.id === id ? { ...deal, pipelineStage } : deal,\n )\n queryClient.setQueryData<DealsQueryData>(dealsKey, { ...previous, deals: nextDeals })\n }\n return { previous }\n },\n onError: (error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData<DealsQueryData>(dealsKey, context.previous)\n }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translate('customers.deals.pipeline.moveError', 'Failed to update deal stage.')\n flash(message, 'error')\n },\n onSuccess: () => {\n flash(translate('customers.deals.pipeline.moveSuccess', 'Deal updated.'), 'success')\n },\n onSettled: () => {\n setPendingDealId(null)\n queryClient.invalidateQueries({ queryKey: dealsKey }).catch(() => {})\n },\n })\n\n const [draggingId, setDraggingId] = React.useState<string | null>(null)\n const [activeLane, setActiveLane] = React.useState<string | null>(null)\n const handleActionClick = React.useCallback((event: React.MouseEvent) => {\n event.stopPropagation()\n }, [])\n\n const handleSortChange = React.useCallback((event: React.ChangeEvent<HTMLSelectElement>) => {\n const value = event.target.value as SortOption\n if (sortOptions.includes(value)) setSortBy(value)\n }, [])\n\n const handleDragStart = React.useCallback((dealId: string) => {\n setDraggingId(dealId)\n }, [])\n\n const handleDragEnd = React.useCallback(() => {\n setDraggingId(null)\n setActiveLane(null)\n }, [])\n\n const handleDrop = React.useCallback(\n (stage: StageDefinition) => async (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n setActiveLane(null)\n const dealId = event.dataTransfer.getData('text/plain') || draggingId\n if (!dealId) return\n const deal = dealMap.get(dealId)\n if (!deal) return\n if (stage.value === null) {\n flash(\n translate('customers.deals.pipeline.unassignedDisabled', 'Moving to \"No stage\" is not supported.'),\n 'info',\n )\n return\n }\n if (deal.pipelineStage === stage.value) return\n updateStageMutation.mutate({ id: dealId, pipelineStage: stage.value })\n },\n [dealMap, draggingId, t, updateStageMutation],\n )\n\n const handleDragOver = React.useCallback(\n (stageId: string) => (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n if (activeLane !== stageId) setActiveLane(stageId)\n },\n [activeLane],\n )\n\n const renderLaneHeader = (stage: StageDefinition, count: number) => {\n return (\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {stage.icon ? (\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-muted\">\n {renderDictionaryIcon(stage.icon, 'h-4 w-4 text-muted-foreground')}\n </span>\n ) : null}\n <div className=\"flex flex-col\">\n <span className=\"text-sm font-medium\">{stage.label}</span>\n <span className=\"text-xs text-muted-foreground\">\n {translate('customers.deals.pipeline.countLabel', 'Deals: {count}', { count })}\n </span>\n </div>\n </div>\n {stage.color ? renderDictionaryColor(stage.color, 'h-3 w-3 rounded-full') : null}\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col\">\n <h1 className=\"text-xl font-semibold text-foreground\">\n {translate('customers.deals.pipeline.title', 'Sales Pipeline')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {translate(\n 'customers.deals.pipeline.subtitle',\n 'Track deals by pipeline stage and drag them between lanes to update progress.',\n )}\n </p>\n </div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-muted-foreground\">\n <span>{translate('customers.deals.pipeline.sort.label', 'Sort by')}</span>\n <select\n 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\"\n value={sortBy}\n onChange={handleSortChange}\n >\n <option value=\"probability\">\n {translate('customers.deals.pipeline.sort.probability', 'Probability (high to low)')}\n </option>\n <option value=\"createdAt\">\n {translate('customers.deals.pipeline.sort.createdAt', 'Created (newest first)')}\n </option>\n <option value=\"expectedCloseAt\">\n {translate('customers.deals.pipeline.sort.expectedCloseAt', 'Expected close (soonest first)')}\n </option>\n </select>\n </label>\n </div>\n\n {dealsQuery.isLoading ? (\n <div className=\"flex h-[50vh] items-center justify-center\">\n <Spinner />\n </div>\n ) : dealsQuery.isError ? (\n <div className=\"max-w-xl\">\n <ErrorNotice\n message={\n dealsQuery.error instanceof Error\n ? dealsQuery.error.message\n : translate('customers.deals.pipeline.loadError', 'Failed to load deals.')\n }\n />\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n {total > deals.length ? (\n <div className=\"rounded-md border border-border bg-muted/30 px-4 py-2 text-sm text-muted-foreground\">\n {translate(\n 'customers.deals.pipeline.limitNotice',\n 'Showing the first {count} deals. Refine your filters to see more.',\n { count: deals.length },\n )}\n </div>\n ) : null}\n\n <div className=\"flex flex-col gap-4 pb-6 md:flex-row md:overflow-x-auto\">\n {stages.length === 0 ? (\n <div className=\"flex h-[50vh] w-full items-center justify-center rounded-lg border border-dashed border-border bg-muted/20\">\n <span className=\"text-sm text-muted-foreground\">\n {translate('customers.deals.pipeline.noStages', 'Define pipeline stages to start tracking deals.')}\n </span>\n </div>\n ) : (\n stages.map((stage) => {\n const stageKey = stage.value ?? null\n const laneDeals = groupedDeals.get(stageKey) ?? []\n const sortedLaneDeals = sortDeals(laneDeals, sortBy)\n const isActive = activeLane === stage.id\n return (\n <div\n key={stage.id}\n className={`flex min-h-[60vh] w-full flex-1 flex-col overflow-hidden rounded-lg border border-border bg-card shadow-sm transition-all md:w-72 md:flex-none ${\n isActive ? 'ring-2 ring-ring/40' : ''\n }`}\n onDragOver={handleDragOver(stage.id)}\n onDrop={handleDrop(stage)}\n >\n {renderLaneHeader(stage, laneDeals.length)}\n <div className=\"flex flex-1 flex-col gap-3 overflow-y-auto px-4 py-3\">\n {sortedLaneDeals.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border bg-muted/10 p-4 text-center text-xs text-muted-foreground\">\n {translate('customers.deals.pipeline.emptyLane', 'No deals in this stage yet.')}\n </div>\n ) : (\n sortedLaneDeals.map((deal) => {\n const isDragging = draggingId === deal.id\n || (pendingDealId === deal.id && updateStageMutation.isPending)\n const valueLabel = formatCurrency(\n deal.valueAmount,\n deal.valueCurrency,\n translate('customers.deals.list.noValue', 'No value assigned'),\n )\n const probabilityLabel = formatProbability(\n deal.probability,\n translate('customers.deals.pipeline.noProbability', 'N/A'),\n )\n const expectedLabel = deal.expectedCloseAt\n ? dateFormatter.format(new Date(deal.expectedCloseAt))\n : translate('customers.deals.pipeline.noExpectedClose', 'No date')\n return (\n <div\n key={deal.id}\n className={`group flex cursor-grab flex-col gap-2 rounded-md border border-border bg-background p-4 shadow-xs transition ${\n isDragging ? 'opacity-50' : 'hover:shadow-sm'\n }`}\n draggable\n onDragStart={(event) => {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', deal.id)\n handleDragStart(deal.id)\n }}\n onDragEnd={handleDragEnd}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex flex-col\">\n <span className=\"line-clamp-2 text-sm font-medium text-foreground\">\n {deal.title}\n </span>\n {deal.status ? (\n <span className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {deal.status}\n </span>\n ) : null}\n </div>\n {pendingDealId === deal.id && updateStageMutation.isPending ? (\n <Spinner className=\"size-4\" />\n ) : null}\n </div>\n <div className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.value', 'Value')}</span>\n <span className=\"font-medium text-foreground\">{valueLabel}</span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.probability', 'Probability')}</span>\n <span className=\"font-medium text-foreground\">{probabilityLabel}</span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.expectedClose', 'Expected close')}</span>\n <span className=\"font-medium text-foreground\">{expectedLabel}</span>\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap gap-2 text-xs\">\n <Link\n href={`/backend/customers/deals/${deal.id}`}\n className=\"font-medium text-primary hover:underline\"\n draggable={false}\n onClick={handleActionClick}\n >\n {translate('customers.deals.pipeline.actions.openDeal', 'Open deal')}\n </Link>\n </div>\n {deal.people.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {deal.people.map((person) => (\n <Link\n key={person.id}\n className=\"rounded-full bg-primary/5 px-3 py-1 text-xs text-primary transition-colors hover:bg-primary/10\"\n href={`/backend/customers/people/${person.id}`}\n draggable={false}\n onClick={handleActionClick}\n >\n {person.label}\n </Link>\n ))}\n </div>\n ) : null}\n {deal.companies.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {deal.companies.map((company) => (\n <Link\n key={company.id}\n className=\"rounded-full bg-secondary/10 px-3 py-1 text-xs text-secondary-foreground transition-colors hover:bg-secondary/20\"\n href={`/backend/customers/companies/${company.id}`}\n draggable={false}\n onClick={handleActionClick}\n >\n {company.label}\n </Link>\n ))}\n </div>\n ) : null}\n </div>\n )\n })\n )}\n </div>\n </div>\n )\n })\n )}\n </div>\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\n\ntype DealAssociation = { id: string; label: string }\n\ntype DealRecord = {\n id: string\n title: string\n status: string | null\n pipelineStage: string | null\n pipelineId: string | null\n pipelineStageId: string | null\n valueAmount: number | null\n valueCurrency: string | null\n probability: number | null\n expectedCloseAt: string | null\n expectedCloseAtTs: number | null\n createdAt: string | null\n createdAtTs: number | null\n updatedAt: string | null\n people: DealAssociation[]\n companies: DealAssociation[]\n}\n\ntype DealsQueryData = {\n deals: DealRecord[]\n total: number\n}\n\ntype StageDefinition = {\n id: string\n value: string | null\n label: string\n color: string | null\n icon: string | null\n}\n\ntype SortOption = 'probability' | 'createdAt' | 'expectedCloseAt'\n\ntype PipelineRecord = { id: string; name: string; isDefault: boolean }\ntype PipelineStageRecord = { id: string; label: string; order: number; pipelineId: string }\n\nconst DEALS_QUERY_LIMIT = 100\n\nconst dealsQueryKey = (scopeVersion: number, pipelineId: string | null) =>\n ['customers', 'deals', 'pipeline', `scope:${scopeVersion}`, `pipeline:${pipelineId ?? 'none'}`] as const\n\nconst sortOptions: SortOption[] = ['probability', 'createdAt', 'expectedCloseAt']\n\nfunction normalizeAmount(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction normalizeProbability(value: unknown): number | null {\n const parsed = normalizeAmount(value)\n if (parsed === null) return null\n if (parsed < 0) return 0\n if (parsed > 100) return 100\n return Math.round(parsed)\n}\n\nfunction normalizeTimestamp(value: unknown): { iso: string | null; ts: number | null } {\n if (typeof value !== 'string' || !value.trim().length) return { iso: null, ts: null }\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return { iso: null, ts: null }\n return { iso: date.toISOString(), ts: date.getTime() }\n}\n\nfunction buildStageDefinitionsFromPipelineStages(\n pipelineStages: PipelineStageRecord[],\n deals: DealRecord[],\n t: ReturnType<typeof useT>,\n): StageDefinition[] {\n const result: StageDefinition[] = pipelineStages\n .slice()\n .sort((a, b) => a.order - b.order)\n .map((stage) => ({\n id: stage.id,\n value: stage.id,\n label: stage.label,\n color: null,\n icon: null,\n }))\n\n const knownIds = new Set(pipelineStages.map((s) => s.id))\n const hasUnassigned = deals.some((deal) => !deal.pipelineStageId || !knownIds.has(deal.pipelineStageId))\n if (hasUnassigned) {\n result.push({\n id: 'stage:__unassigned',\n value: null,\n label: translateWithFallback(t, 'customers.deals.pipeline.unassigned', 'No stage'),\n color: null,\n icon: null,\n })\n }\n\n return result\n}\n\nfunction createDealMap(deals: DealRecord[]): Map<string, DealRecord> {\n return deals.reduce<Map<string, DealRecord>>((acc, deal) => acc.set(deal.id, deal), new Map())\n}\n\nfunction groupDealsByStageId(deals: DealRecord[]): Map<string | null, DealRecord[]> {\n const byStage = new Map<string | null, DealRecord[]>()\n deals.forEach((deal) => {\n const stageKey = deal.pipelineStageId ?? null\n const bucket = byStage.get(stageKey) ?? []\n bucket.push(deal)\n byStage.set(stageKey, bucket)\n })\n return byStage\n}\n\nfunction sortDeals(deals: DealRecord[], option: SortOption): DealRecord[] {\n const sorted = [...deals]\n sorted.sort((a, b) => {\n if (option === 'probability') {\n const ap = typeof a.probability === 'number' ? a.probability : -1\n const bp = typeof b.probability === 'number' ? b.probability : -1\n if (ap !== bp) return bp - ap\n }\n if (option === 'expectedCloseAt') {\n const at = typeof a.expectedCloseAtTs === 'number' ? a.expectedCloseAtTs : Number.POSITIVE_INFINITY\n const bt = typeof b.expectedCloseAtTs === 'number' ? b.expectedCloseAtTs : Number.POSITIVE_INFINITY\n if (at !== bt) return at - bt\n }\n const at = typeof a.createdAtTs === 'number' ? a.createdAtTs : Number.NEGATIVE_INFINITY\n const bt = typeof b.createdAtTs === 'number' ? b.createdAtTs : Number.NEGATIVE_INFINITY\n if (option === 'createdAt') {\n if (at !== bt) return bt - at\n } else if (option === 'expectedCloseAt' || option === 'probability') {\n if (at !== bt) return bt - at\n }\n return a.title.localeCompare(b.title, undefined, { sensitivity: 'base' })\n })\n return sorted\n}\n\nfunction formatCurrency(amount: number | null, currency: string | null, fallback: string): string {\n if (amount === null || Number.isNaN(amount)) return fallback\n const code = currency && currency.length === 3 ? currency.toUpperCase() : 'USD'\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: code,\n maximumFractionDigits: 2,\n }).format(amount)\n } catch {\n return `${code} ${amount.toFixed(2)}`\n }\n}\n\nfunction formatProbability(probability: number | null, fallback: string): string {\n if (typeof probability !== 'number' || Number.isNaN(probability)) return fallback\n return `${probability}%`\n}\n\nexport default function SalesPipelinePage(): React.ReactElement {\n const t = useT()\n const translate = React.useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>) => {\n const value = translateWithFallback(t, key, fallback, params)\n if (value === fallback && params) {\n return fallback.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (match, doubleToken, singleToken) => {\n const token = (doubleToken ?? singleToken) as string | undefined\n if (!token) return match\n const replacement = params[token]\n if (replacement === undefined) {\n return doubleToken ? `{{${token}}}` : `{${token}}`\n }\n return String(replacement)\n })\n }\n return value\n },\n [t],\n )\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const [sortBy, setSortBy] = React.useState<SortOption>('probability')\n const [pendingDealId, setPendingDealId] = React.useState<string | null>(null)\n const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)\n\n const pipelinesQuery = useQuery<PipelineRecord[]>({\n queryKey: ['customers', 'pipelines', `scope:${scopeVersion}`],\n staleTime: 60_000,\n queryFn: async () => {\n const payload = await readApiResultOrThrow<{ items: PipelineRecord[] }>(\n '/api/customers/pipelines',\n undefined,\n { errorMessage: translate('customers.deals.pipeline.loadError', 'Failed to load pipelines.') },\n )\n return payload?.items ?? []\n },\n })\n\n React.useEffect(() => {\n if (selectedPipelineId) return\n const pipelines = pipelinesQuery.data\n if (!pipelines || !pipelines.length) return\n const defaultPipeline = pipelines.find((p) => p.isDefault) ?? pipelines[0]\n if (defaultPipeline) setSelectedPipelineId(defaultPipeline.id)\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const stagesQuery = useQuery<PipelineStageRecord[]>({\n queryKey: ['customers', 'pipeline-stages', `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n const payload = await readApiResultOrThrow<{ items: PipelineStageRecord[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(selectedPipelineId!)}`,\n undefined,\n { errorMessage: translate('customers.deals.pipeline.loadError', 'Failed to load stages.') },\n )\n return payload?.items ?? []\n },\n })\n\n const dealsKey = React.useMemo(() => dealsQueryKey(scopeVersion, selectedPipelineId), [scopeVersion, selectedPipelineId])\n\n const dealsQuery = useQuery<DealsQueryData>({\n queryKey: dealsKey,\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', String(DEALS_QUERY_LIMIT))\n search.set('sortField', 'createdAt')\n search.set('sortDir', 'desc')\n if (selectedPipelineId) search.set('pipelineId', selectedPipelineId)\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/customers/deals?${search.toString()}`,\n undefined,\n { errorMessage: translate('customers.deals.pipeline.loadError', 'Failed to load deals.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const deals: DealRecord[] = []\n items.forEach((item) => {\n if (!item || typeof item !== 'object') return\n const data = item as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : null\n if (!id) return\n const title =\n typeof data.title === 'string' && data.title.trim().length\n ? data.title.trim()\n : translate('customers.deals.pipeline.untitled', 'Untitled deal')\n const status =\n typeof data.status === 'string' && data.status.trim().length ? data.status.trim() : null\n const stage =\n typeof data.pipeline_stage === 'string' && data.pipeline_stage.trim().length\n ? data.pipeline_stage.trim()\n : null\n const amount = normalizeAmount(data.value_amount)\n const currency =\n typeof data.value_currency === 'string' && data.value_currency.trim().length\n ? data.value_currency.trim().toUpperCase()\n : null\n const probability = normalizeProbability(data.probability)\n const expected = normalizeTimestamp(data.expected_close_at)\n const created = normalizeTimestamp(data.created_at)\n const updated = normalizeTimestamp(data.updated_at)\n const rawPeople = Array.isArray(data.people) ? data.people : []\n const people: DealAssociation[] = rawPeople\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const personId = typeof ref.id === 'string' ? ref.id : null\n if (!personId) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length\n ? ref.label.trim()\n : personId\n return { id: personId, label }\n })\n .filter((entry): entry is DealAssociation => !!entry)\n const rawCompanies = Array.isArray(data.companies) ? data.companies : []\n const companies: DealAssociation[] = rawCompanies\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const companyId = typeof ref.id === 'string' ? ref.id : null\n if (!companyId) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length\n ? ref.label.trim()\n : companyId\n return { id: companyId, label }\n })\n .filter((entry): entry is DealAssociation => !!entry)\n deals.push({\n id,\n title,\n status,\n pipelineStage: stage,\n pipelineId: typeof data.pipeline_id === 'string' ? data.pipeline_id : null,\n pipelineStageId: typeof data.pipeline_stage_id === 'string' ? data.pipeline_stage_id : null,\n valueAmount: amount,\n valueCurrency: currency,\n probability,\n expectedCloseAt: expected.iso,\n expectedCloseAtTs: expected.ts,\n createdAt: created.iso,\n createdAtTs: created.ts,\n updatedAt: updated.iso,\n people,\n companies,\n })\n })\n\n const total = typeof payload?.total === 'number' ? payload.total : deals.length\n return { deals, total }\n },\n })\n\n const deals = dealsQuery.data?.deals ?? []\n const total = dealsQuery.data?.total ?? deals.length\n const dealMap = React.useMemo(() => createDealMap(deals), [deals])\n const groupedDeals = React.useMemo(() => groupDealsByStageId(deals), [deals])\n const stages = React.useMemo(\n () => buildStageDefinitionsFromPipelineStages(stagesQuery.data ?? [], deals, t),\n [stagesQuery.data, deals, t],\n )\n\n const dateFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(undefined, {\n dateStyle: 'medium',\n }),\n [],\n )\n\n const updateStageMutation = useMutation({\n mutationFn: async ({ id, pipelineStageId }: { id: string; pipelineStageId: string }) => {\n await apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id, pipelineStageId }),\n },\n { errorMessage: translate('customers.deals.pipeline.moveError', 'Failed to update deal stage.') },\n )\n return { id, pipelineStageId }\n },\n onMutate: async ({ id, pipelineStageId }) => {\n setPendingDealId(id)\n await queryClient.cancelQueries({ queryKey: dealsKey })\n const previous = queryClient.getQueryData<DealsQueryData>(dealsKey)\n if (previous) {\n const nextDeals = previous.deals.map((deal) =>\n deal.id === id ? { ...deal, pipelineStageId } : deal,\n )\n queryClient.setQueryData<DealsQueryData>(dealsKey, { ...previous, deals: nextDeals })\n }\n return { previous }\n },\n onError: (error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData<DealsQueryData>(dealsKey, context.previous)\n }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translate('customers.deals.pipeline.moveError', 'Failed to update deal stage.')\n flash(message, 'error')\n },\n onSuccess: () => {\n flash(translate('customers.deals.pipeline.moveSuccess', 'Deal updated.'), 'success')\n },\n onSettled: () => {\n setPendingDealId(null)\n queryClient.invalidateQueries({ queryKey: dealsKey }).catch(() => {})\n },\n })\n\n const [draggingId, setDraggingId] = React.useState<string | null>(null)\n const [activeLane, setActiveLane] = React.useState<string | null>(null)\n const handleActionClick = React.useCallback((event: React.MouseEvent) => {\n event.stopPropagation()\n }, [])\n\n const handleSortChange = React.useCallback((event: React.ChangeEvent<HTMLSelectElement>) => {\n const value = event.target.value as SortOption\n if (sortOptions.includes(value)) setSortBy(value)\n }, [])\n\n const handleDragStart = React.useCallback((dealId: string) => {\n setDraggingId(dealId)\n }, [])\n\n const handleDragEnd = React.useCallback(() => {\n setDraggingId(null)\n setActiveLane(null)\n }, [])\n\n const handleDrop = React.useCallback(\n (stage: StageDefinition) => async (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n setActiveLane(null)\n const dealId = event.dataTransfer.getData('text/plain') || draggingId\n if (!dealId) return\n const deal = dealMap.get(dealId)\n if (!deal) return\n if (stage.value === null) {\n flash(\n translate('customers.deals.pipeline.unassignedDisabled', 'Moving to \"No stage\" is not supported.'),\n 'info',\n )\n return\n }\n if (deal.pipelineStageId === stage.value) return\n updateStageMutation.mutate({ id: dealId, pipelineStageId: stage.value })\n },\n [dealMap, draggingId, translate, updateStageMutation],\n )\n\n const handleDragOver = React.useCallback(\n (stageId: string) => (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n if (activeLane !== stageId) setActiveLane(stageId)\n },\n [activeLane],\n )\n\n const renderLaneHeader = (stage: StageDefinition, count: number) => {\n return (\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex flex-col\">\n <span className=\"text-sm font-medium\">{stage.label}</span>\n <span className=\"text-xs text-muted-foreground\">\n {translate('customers.deals.pipeline.countLabel', 'Deals: {count}', { count })}\n </span>\n </div>\n </div>\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col\">\n <h1 className=\"text-xl font-semibold text-foreground\">\n {translate('customers.deals.pipeline.title', 'Sales Pipeline')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {translate(\n 'customers.deals.pipeline.subtitle',\n 'Track deals by pipeline stage and drag them between lanes to update progress.',\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-4\">\n {pipelinesQuery.data && pipelinesQuery.data.length > 0 ? (\n <label className=\"flex items-center gap-2 text-sm font-medium text-muted-foreground\">\n <span>{translate('customers.deals.pipeline.switch.label', 'Pipeline')}</span>\n <select\n 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\"\n value={selectedPipelineId ?? ''}\n onChange={(e) => setSelectedPipelineId(e.target.value || null)}\n >\n {pipelinesQuery.data.map((p) => (\n <option key={p.id} value={p.id}>{p.name}</option>\n ))}\n </select>\n </label>\n ) : null}\n <Link\n href=\"/backend/config/customers/pipeline-stages\"\n className=\"text-sm font-medium text-primary hover:underline\"\n >\n {translate('customers.deals.pipeline.manageStages', 'Manage stages')}\n </Link>\n <label className=\"flex items-center gap-2 text-sm font-medium text-muted-foreground\">\n <span>{translate('customers.deals.pipeline.sort.label', 'Sort by')}</span>\n <select\n 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\"\n value={sortBy}\n onChange={handleSortChange}\n >\n <option value=\"probability\">\n {translate('customers.deals.pipeline.sort.probability', 'Probability (high to low)')}\n </option>\n <option value=\"createdAt\">\n {translate('customers.deals.pipeline.sort.createdAt', 'Created (newest first)')}\n </option>\n <option value=\"expectedCloseAt\">\n {translate('customers.deals.pipeline.sort.expectedCloseAt', 'Expected close (soonest first)')}\n </option>\n </select>\n </label>\n </div>\n </div>\n\n {!selectedPipelineId ? (\n <div className=\"flex h-[50vh] items-center justify-center\">\n <span className=\"text-sm text-muted-foreground\">\n {translate('customers.deals.pipeline.noPipeline', 'No pipeline selected. Create a pipeline in settings.')}\n </span>\n </div>\n ) : dealsQuery.isLoading ? (\n <div className=\"flex h-[50vh] items-center justify-center\">\n <Spinner />\n </div>\n ) : dealsQuery.isError ? (\n <div className=\"max-w-xl\">\n <ErrorNotice\n message={\n dealsQuery.error instanceof Error\n ? dealsQuery.error.message\n : translate('customers.deals.pipeline.loadError', 'Failed to load deals.')\n }\n />\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n {total > deals.length ? (\n <div className=\"rounded-md border border-border bg-muted/30 px-4 py-2 text-sm text-muted-foreground\">\n {translate(\n 'customers.deals.pipeline.limitNotice',\n 'Showing the first {count} deals. Refine your filters to see more.',\n { count: deals.length },\n )}\n </div>\n ) : null}\n\n <div className=\"flex flex-col gap-4 pb-6 md:flex-row md:overflow-x-auto\">\n {stages.length === 0 ? (\n <div className=\"flex h-[50vh] w-full items-center justify-center rounded-lg border border-dashed border-border bg-muted/20\">\n <span className=\"text-sm text-muted-foreground\">\n {translate('customers.deals.pipeline.noStages', 'Define pipeline stages to start tracking deals.')}\n </span>\n </div>\n ) : (\n stages.map((stage) => {\n const stageKey = stage.value ?? null\n const laneDeals = groupedDeals.get(stageKey) ?? []\n const sortedLaneDeals = sortDeals(laneDeals, sortBy)\n const isActive = activeLane === stage.id\n return (\n <div\n key={stage.id}\n className={`flex min-h-[60vh] w-full flex-1 flex-col overflow-hidden rounded-lg border border-border bg-card shadow-sm transition-all md:w-72 md:flex-none ${\n isActive ? 'ring-2 ring-ring/40' : ''\n }`}\n onDragOver={handleDragOver(stage.id)}\n onDrop={handleDrop(stage)}\n >\n {renderLaneHeader(stage, laneDeals.length)}\n <div className=\"flex flex-1 flex-col gap-3 overflow-y-auto px-4 py-3\">\n {sortedLaneDeals.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border bg-muted/10 p-4 text-center text-xs text-muted-foreground\">\n {translate('customers.deals.pipeline.emptyLane', 'No deals in this stage yet.')}\n </div>\n ) : (\n sortedLaneDeals.map((deal) => {\n const isDragging = draggingId === deal.id\n || (pendingDealId === deal.id && updateStageMutation.isPending)\n const valueLabel = formatCurrency(\n deal.valueAmount,\n deal.valueCurrency,\n translate('customers.deals.list.noValue', 'No value assigned'),\n )\n const probabilityLabel = formatProbability(\n deal.probability,\n translate('customers.deals.pipeline.noProbability', 'N/A'),\n )\n const expectedLabel = deal.expectedCloseAt\n ? dateFormatter.format(new Date(deal.expectedCloseAt))\n : translate('customers.deals.pipeline.noExpectedClose', 'No date')\n return (\n <div\n key={deal.id}\n className={`group flex cursor-grab flex-col gap-2 rounded-md border border-border bg-background p-4 shadow-xs transition ${\n isDragging ? 'opacity-50' : 'hover:shadow-sm'\n }`}\n draggable\n onDragStart={(event) => {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', deal.id)\n handleDragStart(deal.id)\n }}\n onDragEnd={handleDragEnd}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex flex-col\">\n <span className=\"line-clamp-2 text-sm font-medium text-foreground\">\n {deal.title}\n </span>\n {deal.status ? (\n <span className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {deal.status}\n </span>\n ) : null}\n </div>\n {pendingDealId === deal.id && updateStageMutation.isPending ? (\n <Spinner className=\"size-4\" />\n ) : null}\n </div>\n <div className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.value', 'Value')}</span>\n <span className=\"font-medium text-foreground\">{valueLabel}</span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.probability', 'Probability')}</span>\n <span className=\"font-medium text-foreground\">{probabilityLabel}</span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span>{translate('customers.deals.pipeline.card.expectedClose', 'Expected close')}</span>\n <span className=\"font-medium text-foreground\">{expectedLabel}</span>\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap gap-2 text-xs\">\n <Link\n href={`/backend/customers/deals/${deal.id}`}\n className=\"font-medium text-primary hover:underline\"\n draggable={false}\n onClick={handleActionClick}\n >\n {translate('customers.deals.pipeline.actions.openDeal', 'Open deal')}\n </Link>\n </div>\n {deal.people.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {deal.people.map((person) => (\n <Link\n key={person.id}\n className=\"rounded-full bg-primary/5 px-3 py-1 text-xs text-primary transition-colors hover:bg-primary/10\"\n href={`/backend/customers/people/${person.id}`}\n draggable={false}\n onClick={handleActionClick}\n >\n {person.label}\n </Link>\n ))}\n </div>\n ) : null}\n {deal.companies.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {deal.companies.map((company) => (\n <Link\n key={company.id}\n className=\"rounded-full bg-secondary/10 px-3 py-1 text-xs text-secondary-foreground transition-colors hover:bg-secondary/20\"\n href={`/backend/customers/companies/${company.id}`}\n draggable={false}\n onClick={handleActionClick}\n >\n {company.label}\n </Link>\n ))}\n </div>\n ) : null}\n </div>\n )\n })\n )}\n </div>\n </div>\n )\n })\n )}\n </div>\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["at", "bt", "deals", "total"]
|
|
7
7
|
}
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
CustomerDealCompanyLink,
|
|
19
19
|
CustomerActivity,
|
|
20
20
|
CustomerAddress,
|
|
21
|
-
CustomerComment
|
|
21
|
+
CustomerComment,
|
|
22
|
+
CustomerPipeline,
|
|
23
|
+
CustomerPipelineStage
|
|
22
24
|
} from "./data/entities.js";
|
|
23
25
|
import { ensureDictionaryEntry } from "./commands/shared.js";
|
|
24
26
|
const DEAL_STATUS_DEFAULTS = [
|
|
@@ -2305,6 +2307,29 @@ const seedStressTest = {
|
|
|
2305
2307
|
}
|
|
2306
2308
|
}
|
|
2307
2309
|
};
|
|
2310
|
+
async function seedDefaultPipeline(em, { tenantId, organizationId }) {
|
|
2311
|
+
const existing = await em.findOne(CustomerPipeline, { tenantId, organizationId, isDefault: true });
|
|
2312
|
+
if (existing) return;
|
|
2313
|
+
const pipeline = em.create(CustomerPipeline, {
|
|
2314
|
+
tenantId,
|
|
2315
|
+
organizationId,
|
|
2316
|
+
name: "Default Pipeline",
|
|
2317
|
+
isDefault: true
|
|
2318
|
+
});
|
|
2319
|
+
em.persist(pipeline);
|
|
2320
|
+
await em.flush();
|
|
2321
|
+
for (let i = 0; i < PIPELINE_STAGE_DEFAULTS.length; i++) {
|
|
2322
|
+
const entry = PIPELINE_STAGE_DEFAULTS[i];
|
|
2323
|
+
em.persist(em.create(CustomerPipelineStage, {
|
|
2324
|
+
tenantId,
|
|
2325
|
+
organizationId,
|
|
2326
|
+
pipelineId: pipeline.id,
|
|
2327
|
+
label: entry.label,
|
|
2328
|
+
order: i
|
|
2329
|
+
}));
|
|
2330
|
+
}
|
|
2331
|
+
await em.flush();
|
|
2332
|
+
}
|
|
2308
2333
|
const customersCliCommands = [seedDictionaries, seedExamples, seedStressTest];
|
|
2309
2334
|
var cli_default = customersCliCommands;
|
|
2310
2335
|
const CUSTOMER_CUSTOM_FIELD_SETS = [
|
|
@@ -2401,6 +2426,7 @@ export {
|
|
|
2401
2426
|
seedCurrencyDictionary,
|
|
2402
2427
|
seedCustomerDictionaries,
|
|
2403
2428
|
seedCustomerExamples,
|
|
2404
|
-
seedCustomerStressTest
|
|
2429
|
+
seedCustomerStressTest,
|
|
2430
|
+
seedDefaultPipeline
|
|
2405
2431
|
};
|
|
2406
2432
|
//# sourceMappingURL=cli.js.map
|