@open-mercato/core 0.6.1-develop.3256.1.fe3dec2464 → 0.6.1-develop.3276.1.1fcd145078
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/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/authFixtures.js.map +1 -1
- package/dist/helpers/integration/authUi.js +10 -2
- package/dist/helpers/integration/authUi.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +3 -1
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js +10 -1
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +28 -27
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +21 -3
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +6 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +4 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +56 -2
- package/dist/modules/auth/backend/users/page.js.map +3 -3
- package/dist/modules/auth/commands/users.js +14 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +8 -0
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -9
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +26 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +108 -99
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +13 -5
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +10 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +15 -8
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/RolesSection.js +35 -22
- package/dist/modules/customers/components/detail/RolesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +73 -64
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js +11 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +2 -9
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +14 -29
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +10 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +79 -95
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +49 -70
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js +1 -1
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +1 -1
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +19 -19
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/resources/components/ResourceCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceCrudForm.js.map +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnDialog.js +11 -8
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
- package/dist/modules/staff/backend/staff/profile/create/page.js +1 -1
- package/dist/modules/staff/backend/staff/profile/create/page.js.map +2 -2
- package/dist/modules/staff/components/TeamForm.js +1 -1
- package/dist/modules/staff/components/TeamForm.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
- package/package.json +5 -5
- package/src/helpers/integration/authFixtures.ts +1 -1
- package/src/helpers/integration/authUi.ts +11 -2
- package/src/helpers/integration/salesUi.ts +10 -1
- package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +7 -3
- package/src/modules/audit_logs/lib/display-helpers.tsx +22 -14
- package/src/modules/auth/api/users/route.ts +22 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +9 -1
- package/src/modules/auth/backend/users/create/page.tsx +6 -2
- package/src/modules/auth/backend/users/page.tsx +60 -3
- package/src/modules/auth/commands/users.ts +15 -0
- package/src/modules/auth/data/validators.ts +8 -0
- package/src/modules/auth/i18n/de.json +4 -0
- package/src/modules/auth/i18n/en.json +4 -0
- package/src/modules/auth/i18n/es.json +4 -0
- package/src/modules/auth/i18n/pl.json +4 -0
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +12 -13
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +17 -13
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/components/PipelineSettings.tsx +95 -95
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +7 -4
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +13 -8
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +16 -12
- package/src/modules/customers/components/detail/RolesSection.tsx +34 -31
- package/src/modules/customers/components/detail/TasksSection.tsx +16 -8
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +77 -51
- package/src/modules/customers/components/linking/LinkEntityDialog.tsx +7 -3
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -7
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +19 -54
- package/src/modules/dictionaries/components/DictionariesManager.tsx +7 -4
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +101 -146
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +60 -103
- package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +1 -1
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +17 -17
- package/src/modules/resources/components/ResourceCrudForm.tsx +1 -1
- package/src/modules/resources/components/ResourceTypeCrudForm.tsx +1 -1
- package/src/modules/sales/components/documents/ReturnDialog.tsx +10 -7
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/staff/backend/staff/profile/create/page.tsx +1 -1
- package/src/modules/staff/components/TeamForm.tsx +1 -1
- package/src/modules/staff/components/TeamMemberForm.tsx +1 -1
- package/src/modules/staff/components/TeamRoleForm.tsx +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Building2, Users } from 'lucide-react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, NotesSection } from '@open-mercato/ui/backend/detail'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { CollapsibleZoneLayout } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { E } from '#generated/entities.ids.generated'\n\nimport { ActivitiesSection } from '../../../../components/detail/ActivitiesSection'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { DealClosureActionBar } from '../../../../components/detail/DealClosureActionBar'\nimport { DealDetailHeader } from '../../../../components/detail/DealDetailHeader'\nimport { DealDetailTabs, resolveLegacyTab, type DealTabId } from '../../../../components/detail/DealDetailTabs'\nimport { DealForm, useDealAssociationLookups } from '../../../../components/detail/DealForm'\nimport { DealLinkedEntitiesTab } from '../../../../components/detail/DealLinkedEntitiesTab'\nimport { ConfirmDealLostDialog } from '../../../../components/detail/ConfirmDealLostDialog'\nimport { DealLostSummaryDialog } from '../../../../components/detail/DealLostSummaryDialog'\nimport { DealWonPopup } from '../../../../components/detail/DealWonPopup'\nimport { InlineActivityComposer } from '../../../../components/detail/InlineActivityComposer'\nimport { PipelineStepper } from '../../../../components/detail/PipelineStepper'\nimport { PlannedActivitiesSection } from '../../../../components/detail/PlannedActivitiesSection'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { createCustomerNotesAdapter } from '../../../../components/detail/notesAdapter'\nimport type { InteractionSummary } from '../../../../components/detail/types'\nimport { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../../../../lib/markdownPreference'\nimport { ICON_SUGGESTIONS } from '../../../../lib/dictionaries'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\nimport { formatCurrency, startOfNextQuarter } from './hooks/formatters'\nimport type { DealDetailPayload } from './hooks/types'\nimport { useDealActivities } from './hooks/useDealActivities'\nimport { useDealAssociations } from './hooks/useDealAssociations'\nimport { useDealClosure } from './hooks/useDealClosure'\nimport { useDealData } from './hooks/useDealData'\nimport { useDealFormHandlers } from './hooks/useDealFormHandlers'\nimport { useDealInjectedTabs } from './hooks/useDealInjectedTabs'\nimport { useDealMutationContext } from './hooks/useDealMutationContext'\nimport { useDealPipeline } from './hooks/useDealPipeline'\nimport { useScheduleDialog } from './hooks/useScheduleDialog'\n\nexport default function DealDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id ?? ''\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const { data, setData, isLoading, error, loadData } = useDealData(id)\n const [isDirty, setIsDirty] = React.useState(false)\n const {\n scheduleDialogOpen,\n scheduleEditData,\n openSchedule,\n openEdit: openScheduleEdit,\n closeSchedule,\n } = useScheduleDialog()\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n\n const initialTab = React.useMemo(() => resolveLegacyTab(searchParams?.get('tab')), [searchParams])\n const [activeTab, setActiveTab] = React.useState<DealTabId>(initialTab)\n\n React.useEffect(() => {\n setActiveTab(initialTab)\n }, [initialTab])\n\n const currentDealId = data?.deal.id ?? id\n const { injectionContext, runMutationWithContext } = useDealMutationContext({\n currentDealId,\n fallbackId: id,\n data,\n })\n\n const notesAdapter = React.useMemo(\n () => createCustomerNotesAdapter(detailTranslator, { runMutation: runMutationWithContext }),\n [detailTranslator, runMutationWithContext],\n )\n\n const { injectedTabs, injectedTabMap } = useDealInjectedTabs({\n injectionContext,\n data,\n setData,\n })\n\n const { searchPeoplePage, fetchPeopleByIds, searchCompaniesPage, fetchCompaniesByIds } = useDealAssociationLookups({\n excludeLinkedDealId: data?.deal.id ?? null,\n })\n\n const {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone,\n handleCancelActivity,\n } = useDealActivities({ dealId: id, runMutationWithContext })\n\n React.useEffect(() => {\n void Promise.all([loadData(), loadPlannedActivities()])\n }, [loadData, loadPlannedActivities])\n\n const activityEntities = React.useMemo(\n () => (data\n ? [...data.people, ...data.companies].map((entry) => ({\n id: entry.id,\n label: entry.subtitle ? `${entry.label} \u00B7 ${entry.subtitle}` : entry.label,\n kind: entry.kind,\n }))\n : []),\n [data],\n )\n const [selectedActivityEntityId, setSelectedActivityEntityId] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedActivityEntityId((current) => {\n if (activityEntities.length === 1) return activityEntities[0].id\n if (current && activityEntities.some((entry) => entry.id === current)) return current\n return null\n })\n }, [activityEntities])\n\n const selectedActivityEntity = React.useMemo(\n () => activityEntities.find((entry) => entry.id === selectedActivityEntityId) ?? null,\n [activityEntities, selectedActivityEntityId],\n )\n\n const dealOptions = React.useMemo(\n () => data ? [{ id: data.deal.id, label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal') }] : [],\n [data, t],\n )\n\n const entityOptions = React.useMemo(\n () => activityEntities.map(({ id, label }) => ({ id, label })),\n [activityEntities],\n )\n\n const confirmDiscardIfDirty = React.useCallback(async () => {\n if (!isDirty) return true\n return confirm({\n title: t('customers.deals.detail.unsavedTitle', 'Discard unsaved changes?'),\n description: t(\n 'customers.deals.detail.unsavedDescription',\n 'You have unsaved edits in this deal. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.deals.detail.unsavedConfirm', 'Discard changes'),\n cancelText: t('customers.deals.detail.unsavedCancel', 'Keep editing'),\n variant: 'destructive',\n })\n }, [confirm, isDirty, t])\n\n const {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n } = useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n })\n\n const { isStageSaving, handleStageChange } = useDealPipeline({\n currentDealId,\n data,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onStageChanged: loadData,\n })\n\n const {\n lostDialogOpen,\n wonPopupOpen,\n lostPopupOpen,\n wonStats,\n lostStats,\n openLostDialog,\n closeLostDialog,\n closeWonPopup,\n closeLostPopup,\n handleWon,\n handleLostConfirm,\n } = useDealClosure({\n currentDealId,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed: loadData,\n })\n\n const handleTabChange = React.useCallback(async (tab: DealTabId) => {\n if (!(await confirmDiscardIfDirty())) return\n setActiveTab(tab)\n const nextParams = new URLSearchParams(searchParams?.toString() ?? '')\n nextParams.set('tab', tab)\n router.replace(`/backend/customers/deals/${encodeURIComponent(id)}?${nextParams.toString()}`, { scroll: false })\n }, [confirmDiscardIfDirty, id, router, searchParams])\n\n const { isSaving, handleFormSubmit, handleDelete, handleHeaderSave } = useDealFormHandlers({\n data,\n currentDealId,\n loadData,\n runMutationWithContext,\n formWrapperRef,\n confirm,\n })\n\n const handleEditActivity = React.useCallback((activity: InteractionSummary) => {\n if (activity.entityId && activityEntities.some((entry) => entry.id === activity.entityId)) {\n setSelectedActivityEntityId(activity.entityId)\n }\n // Forward `customValues` so per-type chip state (callPhoneNumber,\n // callDirection, taskPriority) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the\n // original moment instead of \"today\" (#1807 prefill).\n const rawActivity = activity as unknown as Record<string, unknown>\n openScheduleEdit({\n id: activity.id,\n interactionType: activity.interactionType,\n title: activity.title ?? null,\n body: activity.body ?? null,\n scheduledAt: activity.scheduledAt ?? null,\n occurredAt: activity.occurredAt ?? null,\n durationMinutes: activity.duration ?? null,\n location: activity.location ?? null,\n allDay: activity.allDay ?? null,\n recurrenceRule: activity.recurrenceRule ?? null,\n recurrenceEnd: activity.recurrenceEnd ?? null,\n participants: activity.participants ?? null,\n reminderMinutes: activity.reminderMinutes ?? null,\n visibility: activity.visibility ?? null,\n linkedEntities: activity.linkedEntities ?? null,\n guestPermissions: activity.guestPermissions ?? null,\n ...(rawActivity.customValues && typeof rawActivity.customValues === 'object'\n ? { customValues: rawActivity.customValues as Record<string, unknown> }\n : {}),\n ...(typeof rawActivity.phoneNumber === 'string'\n ? { phoneNumber: rawActivity.phoneNumber as string }\n : {}),\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null })\n }, [activityEntities, openScheduleEdit])\n\n const handleViewDashboard = React.useCallback(() => {\n closeWonPopup()\n router.push('/backend')\n }, [closeWonPopup, router])\n\n const handleBackToPipeline = React.useCallback(() => {\n closeWonPopup()\n closeLostPopup()\n router.push('/backend/customers/deals/pipeline')\n }, [closeLostPopup, closeWonPopup, router])\n\n const handleScheduleLostFollowUp = React.useCallback(() => {\n if (!data || !selectedActivityEntity) return\n const nextQuarterDate = startOfNextQuarter(new Date())\n closeLostPopup()\n openScheduleEdit({\n id: '',\n interactionType: 'task',\n title: data.deal.title\n ? t('customers.deals.detail.lost.followUpTitle', 'Revisit {{title}}', { title: data.deal.title })\n : t('customers.deals.detail.lost.followUpFallbackTitle', 'Revisit closed deal'),\n body: data.deal.lossNotes ?? null,\n scheduledAt: nextQuarterDate.toISOString(),\n durationMinutes: 30,\n location: null,\n allDay: false,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: 1440,\n visibility: 'team',\n linkedEntities: [\n {\n id: data.deal.id,\n type: 'deal',\n label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n },\n ],\n })\n }, [closeLostPopup, data, openScheduleEdit, selectedActivityEntity, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.deals.detail.loading', 'Loading deal\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error || t('customers.deals.detail.error.notFound', 'Deal not found.')}\n action={(\n <Button asChild variant=\"outline\">\n <Link href=\"/backend/customers/deals\">\n {t('customers.deals.detail.actions.backToList', 'Back to deals')}\n </Link>\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n const amountLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency)\n const currentPipelineName = data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null\n const dealName = data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')\n\n const zone1Content = (\n <div ref={formWrapperRef}>\n <DealForm\n mode=\"edit\"\n embedded\n trackDirtyWhenEmbedded\n hideFooterActions\n singleColumnGroups\n showAssociationsGroup={false}\n showVersionHistory={false}\n showCancelAction={false}\n onDirtyChange={setIsDirty}\n collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: 'left' }}\n sortableGroups={{ pageType: 'deal-detail-v3' }}\n initialValues={{\n ...data.deal,\n valueAmount:\n typeof data.deal.valueAmount === 'string' && data.deal.valueAmount.trim().length\n ? Number(data.deal.valueAmount)\n : null,\n personIds: data.linkedPersonIds,\n companyIds: data.linkedCompanyIds,\n customFields: data.customFields,\n ...Object.fromEntries(Object.entries(data.customFields ?? {}).map(([key, value]) => [`cf_${key}`, value])),\n }}\n onSubmit={handleFormSubmit}\n onCancel={() => { void loadData() }}\n onDelete={handleDelete}\n />\n </div>\n )\n\n const zone2Content = (\n <div className=\"rounded-[10px] border border-border bg-card px-5 py-5\">\n {(() => {\n const injected = injectedTabMap.get(activeTab)\n if (injected) return injected()\n\n if (activeTab === 'activities') {\n const activityEntitySelection = activityEntities.length > 1 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <label htmlFor=\"deal-activity-entity\" className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n </label>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'customers.deals.detail.activities.selectEntityDescription',\n 'Pick the person or company that should own new deal activities and follow-ups.',\n )}\n </div>\n <select\n id=\"deal-activity-entity\"\n aria-label={t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n className=\"mt-4 h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n value={selectedActivityEntityId ?? ''}\n onChange={(event) => setSelectedActivityEntityId(event.target.value || null)}\n >\n <option value=\"\">\n {t('customers.deals.detail.activities.selectEntityPlaceholder', 'Select a person or company')}\n </option>\n {activityEntities.map((entry) => (\n <option key={entry.id} value={entry.id}>\n {entry.label}\n </option>\n ))}\n </select>\n </div>\n ) : null\n\n return (\n <div className=\"space-y-4\">\n {activityEntities.length > 1 ? activityEntitySelection : null}\n {activityEntities.length === 0 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.linkEntityTitle', 'Link a person or company first')}\n </div>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t('customers.deals.detail.activities.linkEntityDescription', 'Activities on a deal still need a customer record for timeline ownership.')}\n </div>\n <div className=\"mt-4 flex gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('people')}>\n {t('customers.deals.detail.tabs.people', 'People')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('companies')}>\n {t('customers.deals.detail.tabs.companies', 'Companies')}\n </Button>\n </div>\n </div>\n ) : selectedActivityEntity ? (\n <InlineActivityComposer\n entityType={selectedActivityEntity.kind}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n onActivityCreated={() => { void handleActivityCreated() }}\n runGuardedMutation={runMutationWithContext}\n onScheduleRequested={openSchedule}\n />\n ) : (\n <div className=\"rounded-[10px] border border-dashed border-border bg-muted/10 px-5 py-5\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.selectEntityRequiredTitle', 'Choose a person or company to continue')}\n </div>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'customers.deals.detail.activities.selectEntityRequiredDescription',\n 'Select the customer record that should receive new deal activities before logging or scheduling anything.',\n )}\n </div>\n </div>\n )}\n <PlannedActivitiesSection\n activities={plannedActivities}\n onComplete={(interactionId) => { void handleMarkDone(interactionId) }}\n onSchedule={selectedActivityEntity ? openSchedule : undefined}\n onEdit={handleEditActivity}\n onCancel={(interactionId) => { void handleCancelActivity(interactionId) }}\n />\n {selectedActivityEntity ? (\n <ActivitiesSection\n entityId={selectedActivityEntity.id}\n entityName={selectedActivityEntity.label}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={selectedActivityEntity.id}\n addActionLabel={t('customers.deals.detail.activitiesAdd', 'Log activity')}\n emptyState={{\n title: t('customers.deals.detail.activitiesEmptyTitle', 'No activities yet'),\n actionLabel: t('customers.deals.detail.activitiesEmptyAction', 'Log activity'),\n }}\n runGuardedMutation={runMutationWithContext}\n onDataRefresh={() => { void handleActivityCreated() }}\n refreshKey={activityRefreshKey}\n onEditActivity={handleEditActivity}\n />\n ) : null}\n </div>\n )\n }\n\n if (activeTab === 'people') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.peopleSingular', 'Person')}\n entityLabelPlural={t('customers.deals.detail.tabs.people', 'People')}\n manageLabel={t('customers.deals.detail.peopleEditorTitle', 'Manage linked people')}\n searchPlaceholder={t('customers.deals.detail.peopleSearch', 'Search linked people\u2026')}\n linkedItems={data.people}\n linkedCount={data.counts.people}\n selectedIds={peopleEditorIds}\n disabled={peopleSaving || isSaving}\n savePending={peopleSaving}\n hrefBuilder={(personId) => `/backend/customers/people-v2/${encodeURIComponent(personId)}`}\n onSaveSelection={(next) => handlePeopleAssociationsChange(next)}\n loadLinkedPage={loadLinkedPeoplePage}\n searchEntities={searchPeoplePage}\n fetchEntitiesByIds={fetchPeopleByIds}\n icon={<Users className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'companies') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.companySingular', 'Company')}\n entityLabelPlural={t('customers.deals.detail.tabs.companies', 'Companies')}\n manageLabel={t('customers.deals.detail.companiesEditorTitle', 'Manage linked companies')}\n searchPlaceholder={t('customers.deals.detail.companiesSearch', 'Search linked companies\u2026')}\n linkedItems={data.companies}\n linkedCount={data.counts.companies}\n selectedIds={companiesEditorIds}\n disabled={companiesSaving || isSaving}\n savePending={companiesSaving}\n hrefBuilder={(companyId) => `/backend/customers/companies-v2/${encodeURIComponent(companyId)}`}\n onSaveSelection={(next) => handleCompaniesAssociationsChange(next)}\n loadLinkedPage={loadLinkedCompaniesPage}\n searchEntities={searchCompaniesPage}\n fetchEntitiesByIds={fetchCompaniesByIds}\n icon={<Building2 className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'notes') {\n return (\n <NotesSection\n entityId={null}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n emptyLabel={t('customers.deals.detail.notesEmpty', 'No notes yet.')}\n viewerUserId={data.viewer?.userId ?? null}\n viewerName={data.viewer?.name ?? null}\n viewerEmail={data.viewer?.email ?? null}\n addActionLabel={t('customers.deals.detail.notesAdd', 'Add note')}\n emptyState={{\n title: t('customers.deals.detail.notesEmptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('customers.deals.detail.notesEmptyAction', 'Add a note'),\n }}\n translator={detailTranslator}\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n readMarkdownPreference={readMarkdownPreferenceCookie}\n writeMarkdownPreference={writeMarkdownPreferenceCookie}\n />\n )\n }\n\n if (activeTab === 'files') {\n return (\n <AttachmentsSection\n entityId={E.customers.customer_deal}\n recordId={data.deal.id}\n title={t('customers.deals.detail.tabs.files', 'Files')}\n description={t('customers.deals.detail.files.subtitle', 'Upload and manage files linked to this deal.')}\n />\n )\n }\n\n if (activeTab === 'changelog') {\n return <ChangelogTab entityId={data.deal.id} entityType=\"deal\" />\n }\n\n return null\n })()}\n </div>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <InjectionSpot spotId=\"detail:customers.deal:header\" context={injectionContext} data={data} />\n\n <DealDetailHeader\n deal={data.deal}\n owner={data.owner}\n people={data.people}\n companies={data.companies}\n pipelineName={currentPipelineName}\n stageOptions={data.pipelineStages}\n currentStageId={data.deal.pipelineStageId}\n onStageChange={handleStageChange}\n isStageSaving={isStageSaving}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n />\n\n <InjectionSpot spotId=\"detail:customers.deal:status-badges\" context={injectionContext} data={data} />\n\n <PipelineStepper\n stages={data.pipelineStages}\n transitions={data.stageTransitions}\n currentStageId={data.deal.pipelineStageId}\n pipelineName={currentPipelineName}\n closureOutcome={data.deal.closureOutcome}\n footer={data.deal.closureOutcome ? null : (\n <DealClosureActionBar\n embedded\n closureOutcome={data.deal.closureOutcome}\n onWon={() => { void handleWon() }}\n onLost={openLostDialog}\n />\n )}\n />\n\n <DealDetailTabs\n activeTab={activeTab}\n onTabChange={handleTabChange}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts.people}\n companiesCount={data.counts.companies}\n >\n <CollapsibleZoneLayout\n pageType=\"deal-detail-v3\"\n entityName={dealName}\n isDirty={isDirty}\n zone1DefaultWidth=\"540px\"\n zone1={zone1Content}\n zone2={zone2Content}\n />\n </DealDetailTabs>\n\n <InjectionSpot spotId=\"detail:customers.deal:footer\" context={injectionContext} data={data} />\n </div>\n\n {ConfirmDialogElement}\n\n {selectedActivityEntity ? (\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={closeSchedule}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n entityType={selectedActivityEntity.kind}\n entityName={selectedActivityEntity.label}\n companyName={selectedActivityEntity.kind === 'company' ? selectedActivityEntity.label : data.companies[0]?.label ?? null}\n onActivityCreated={() => { void handleActivityCreated() }}\n editData={scheduleEditData}\n />\n ) : null}\n\n <ConfirmDealLostDialog\n open={lostDialogOpen}\n onClose={closeLostDialog}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n dealValue={amountLabel}\n companyName={data.companies[0]?.label ?? null}\n onConfirm={handleLostConfirm}\n />\n\n <DealWonPopup\n open={wonPopupOpen}\n onClose={closeWonPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n stats={wonStats}\n onViewDashboard={handleViewDashboard}\n onBackToPipeline={handleBackToPipeline}\n />\n\n <DealLostSummaryDialog\n open={lostPopupOpen}\n onClose={closeLostPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n lossNotes={data.deal.lossNotes}\n stats={lostStats}\n onBackToPipeline={handleBackToPipeline}\n onScheduleFollowUp={selectedActivityEntity ? handleScheduleLostFollowUp : undefined}\n />\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 { Building2, UserSearch, Users } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, NotesSection } from '@open-mercato/ui/backend/detail'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { CollapsibleZoneLayout } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { E } from '#generated/entities.ids.generated'\n\nimport { ActivitiesSection } from '../../../../components/detail/ActivitiesSection'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { DealClosureActionBar } from '../../../../components/detail/DealClosureActionBar'\nimport { DealDetailHeader } from '../../../../components/detail/DealDetailHeader'\nimport { DealDetailTabs, resolveLegacyTab, type DealTabId } from '../../../../components/detail/DealDetailTabs'\nimport { DealForm, useDealAssociationLookups } from '../../../../components/detail/DealForm'\nimport { DealLinkedEntitiesTab } from '../../../../components/detail/DealLinkedEntitiesTab'\nimport { ConfirmDealLostDialog } from '../../../../components/detail/ConfirmDealLostDialog'\nimport { DealLostSummaryDialog } from '../../../../components/detail/DealLostSummaryDialog'\nimport { DealWonPopup } from '../../../../components/detail/DealWonPopup'\nimport { InlineActivityComposer } from '../../../../components/detail/InlineActivityComposer'\nimport { PipelineStepper } from '../../../../components/detail/PipelineStepper'\nimport { PlannedActivitiesSection } from '../../../../components/detail/PlannedActivitiesSection'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { createCustomerNotesAdapter } from '../../../../components/detail/notesAdapter'\nimport type { InteractionSummary } from '../../../../components/detail/types'\nimport { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../../../../lib/markdownPreference'\nimport { ICON_SUGGESTIONS } from '../../../../lib/dictionaries'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\nimport { formatCurrency, startOfNextQuarter } from './hooks/formatters'\nimport type { DealDetailPayload } from './hooks/types'\nimport { useDealActivities } from './hooks/useDealActivities'\nimport { useDealAssociations } from './hooks/useDealAssociations'\nimport { useDealClosure } from './hooks/useDealClosure'\nimport { useDealData } from './hooks/useDealData'\nimport { useDealFormHandlers } from './hooks/useDealFormHandlers'\nimport { useDealInjectedTabs } from './hooks/useDealInjectedTabs'\nimport { useDealMutationContext } from './hooks/useDealMutationContext'\nimport { useDealPipeline } from './hooks/useDealPipeline'\nimport { useScheduleDialog } from './hooks/useScheduleDialog'\n\nexport default function DealDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id ?? ''\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const { data, setData, isLoading, error, loadData } = useDealData(id)\n const [isDirty, setIsDirty] = React.useState(false)\n const {\n scheduleDialogOpen,\n scheduleEditData,\n openSchedule,\n openEdit: openScheduleEdit,\n closeSchedule,\n } = useScheduleDialog()\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n\n const initialTab = React.useMemo(() => resolveLegacyTab(searchParams?.get('tab')), [searchParams])\n const [activeTab, setActiveTab] = React.useState<DealTabId>(initialTab)\n\n React.useEffect(() => {\n setActiveTab(initialTab)\n }, [initialTab])\n\n const currentDealId = data?.deal.id ?? id\n const { injectionContext, runMutationWithContext } = useDealMutationContext({\n currentDealId,\n fallbackId: id,\n data,\n })\n\n const notesAdapter = React.useMemo(\n () => createCustomerNotesAdapter(detailTranslator, { runMutation: runMutationWithContext }),\n [detailTranslator, runMutationWithContext],\n )\n\n const { injectedTabs, injectedTabMap } = useDealInjectedTabs({\n injectionContext,\n data,\n setData,\n })\n\n const { searchPeoplePage, fetchPeopleByIds, searchCompaniesPage, fetchCompaniesByIds } = useDealAssociationLookups({\n excludeLinkedDealId: data?.deal.id ?? null,\n })\n\n const {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone,\n handleCancelActivity,\n } = useDealActivities({ dealId: id, runMutationWithContext })\n\n React.useEffect(() => {\n void Promise.all([loadData(), loadPlannedActivities()])\n }, [loadData, loadPlannedActivities])\n\n const activityEntities = React.useMemo(\n () => (data\n ? [...data.people, ...data.companies].map((entry) => ({\n id: entry.id,\n label: entry.subtitle ? `${entry.label} \u00B7 ${entry.subtitle}` : entry.label,\n kind: entry.kind,\n }))\n : []),\n [data],\n )\n const [selectedActivityEntityId, setSelectedActivityEntityId] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedActivityEntityId((current) => {\n if (activityEntities.length === 1) return activityEntities[0].id\n if (current && activityEntities.some((entry) => entry.id === current)) return current\n return null\n })\n }, [activityEntities])\n\n const selectedActivityEntity = React.useMemo(\n () => activityEntities.find((entry) => entry.id === selectedActivityEntityId) ?? null,\n [activityEntities, selectedActivityEntityId],\n )\n\n const dealOptions = React.useMemo(\n () => data ? [{ id: data.deal.id, label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal') }] : [],\n [data, t],\n )\n\n const entityOptions = React.useMemo(\n () => activityEntities.map(({ id, label }) => ({ id, label })),\n [activityEntities],\n )\n\n const confirmDiscardIfDirty = React.useCallback(async () => {\n if (!isDirty) return true\n return confirm({\n title: t('customers.deals.detail.unsavedTitle', 'Discard unsaved changes?'),\n description: t(\n 'customers.deals.detail.unsavedDescription',\n 'You have unsaved edits in this deal. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.deals.detail.unsavedConfirm', 'Discard changes'),\n cancelText: t('customers.deals.detail.unsavedCancel', 'Keep editing'),\n variant: 'destructive',\n })\n }, [confirm, isDirty, t])\n\n const {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n } = useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n })\n\n const { isStageSaving, handleStageChange } = useDealPipeline({\n currentDealId,\n data,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onStageChanged: loadData,\n })\n\n const {\n lostDialogOpen,\n wonPopupOpen,\n lostPopupOpen,\n wonStats,\n lostStats,\n openLostDialog,\n closeLostDialog,\n closeWonPopup,\n closeLostPopup,\n handleWon,\n handleLostConfirm,\n } = useDealClosure({\n currentDealId,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed: loadData,\n })\n\n const handleTabChange = React.useCallback(async (tab: DealTabId) => {\n if (!(await confirmDiscardIfDirty())) return\n setActiveTab(tab)\n const nextParams = new URLSearchParams(searchParams?.toString() ?? '')\n nextParams.set('tab', tab)\n router.replace(`/backend/customers/deals/${encodeURIComponent(id)}?${nextParams.toString()}`, { scroll: false })\n }, [confirmDiscardIfDirty, id, router, searchParams])\n\n const { isSaving, handleFormSubmit, handleDelete, handleHeaderSave } = useDealFormHandlers({\n data,\n currentDealId,\n loadData,\n runMutationWithContext,\n formWrapperRef,\n confirm,\n })\n\n const handleEditActivity = React.useCallback((activity: InteractionSummary) => {\n if (activity.entityId && activityEntities.some((entry) => entry.id === activity.entityId)) {\n setSelectedActivityEntityId(activity.entityId)\n }\n // Forward `customValues` so per-type chip state (callPhoneNumber,\n // callDirection, taskPriority) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the\n // original moment instead of \"today\" (#1807 prefill).\n const rawActivity = activity as unknown as Record<string, unknown>\n openScheduleEdit({\n id: activity.id,\n interactionType: activity.interactionType,\n title: activity.title ?? null,\n body: activity.body ?? null,\n scheduledAt: activity.scheduledAt ?? null,\n occurredAt: activity.occurredAt ?? null,\n durationMinutes: activity.duration ?? null,\n location: activity.location ?? null,\n allDay: activity.allDay ?? null,\n recurrenceRule: activity.recurrenceRule ?? null,\n recurrenceEnd: activity.recurrenceEnd ?? null,\n participants: activity.participants ?? null,\n reminderMinutes: activity.reminderMinutes ?? null,\n visibility: activity.visibility ?? null,\n linkedEntities: activity.linkedEntities ?? null,\n guestPermissions: activity.guestPermissions ?? null,\n ...(rawActivity.customValues && typeof rawActivity.customValues === 'object'\n ? { customValues: rawActivity.customValues as Record<string, unknown> }\n : {}),\n ...(typeof rawActivity.phoneNumber === 'string'\n ? { phoneNumber: rawActivity.phoneNumber as string }\n : {}),\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null })\n }, [activityEntities, openScheduleEdit])\n\n const handleViewDashboard = React.useCallback(() => {\n closeWonPopup()\n router.push('/backend')\n }, [closeWonPopup, router])\n\n const handleBackToPipeline = React.useCallback(() => {\n closeWonPopup()\n closeLostPopup()\n router.push('/backend/customers/deals/pipeline')\n }, [closeLostPopup, closeWonPopup, router])\n\n const handleScheduleLostFollowUp = React.useCallback(() => {\n if (!data || !selectedActivityEntity) return\n const nextQuarterDate = startOfNextQuarter(new Date())\n closeLostPopup()\n openScheduleEdit({\n id: '',\n interactionType: 'task',\n title: data.deal.title\n ? t('customers.deals.detail.lost.followUpTitle', 'Revisit {{title}}', { title: data.deal.title })\n : t('customers.deals.detail.lost.followUpFallbackTitle', 'Revisit closed deal'),\n body: data.deal.lossNotes ?? null,\n scheduledAt: nextQuarterDate.toISOString(),\n durationMinutes: 30,\n location: null,\n allDay: false,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: 1440,\n visibility: 'team',\n linkedEntities: [\n {\n id: data.deal.id,\n type: 'deal',\n label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n },\n ],\n })\n }, [closeLostPopup, data, openScheduleEdit, selectedActivityEntity, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.deals.detail.loading', 'Loading deal\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error || t('customers.deals.detail.error.notFound', 'Deal not found.')}\n action={(\n <Button asChild variant=\"outline\">\n <Link href=\"/backend/customers/deals\">\n {t('customers.deals.detail.actions.backToList', 'Back to deals')}\n </Link>\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n const amountLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency)\n const currentPipelineName = data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null\n const dealName = data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')\n\n const zone1Content = (\n <div ref={formWrapperRef}>\n <DealForm\n mode=\"edit\"\n embedded\n trackDirtyWhenEmbedded\n hideFooterActions\n singleColumnGroups\n showAssociationsGroup={false}\n showVersionHistory={false}\n showCancelAction={false}\n onDirtyChange={setIsDirty}\n collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: 'right' }}\n sortableGroups={{ pageType: 'deal-detail-v3' }}\n initialValues={{\n ...data.deal,\n valueAmount:\n typeof data.deal.valueAmount === 'string' && data.deal.valueAmount.trim().length\n ? Number(data.deal.valueAmount)\n : null,\n personIds: data.linkedPersonIds,\n companyIds: data.linkedCompanyIds,\n customFields: data.customFields,\n ...Object.fromEntries(Object.entries(data.customFields ?? {}).map(([key, value]) => [`cf_${key}`, value])),\n }}\n onSubmit={handleFormSubmit}\n onCancel={() => { void loadData() }}\n onDelete={handleDelete}\n />\n </div>\n )\n\n const zone2Content = (\n <div className=\"rounded-[10px] border border-border bg-card px-5 py-5\">\n {(() => {\n const injected = injectedTabMap.get(activeTab)\n if (injected) return injected()\n\n if (activeTab === 'activities') {\n const activityEntitySelection = activityEntities.length > 1 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <label htmlFor=\"deal-activity-entity\" className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n </label>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'customers.deals.detail.activities.selectEntityDescription',\n 'Pick the person or company that should own new deal activities and follow-ups.',\n )}\n </div>\n <select\n id=\"deal-activity-entity\"\n aria-label={t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n className=\"mt-4 h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n value={selectedActivityEntityId ?? ''}\n onChange={(event) => setSelectedActivityEntityId(event.target.value || null)}\n >\n <option value=\"\">\n {t('customers.deals.detail.activities.selectEntityPlaceholder', 'Select a person or company')}\n </option>\n {activityEntities.map((entry) => (\n <option key={entry.id} value={entry.id}>\n {entry.label}\n </option>\n ))}\n </select>\n </div>\n ) : null\n\n return (\n <div className=\"space-y-4\">\n {activityEntities.length > 1 ? activityEntitySelection : null}\n {activityEntities.length === 0 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.linkEntityTitle', 'Link a person or company first')}\n </div>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t('customers.deals.detail.activities.linkEntityDescription', 'Activities on a deal still need a customer record for timeline ownership.')}\n </div>\n <div className=\"mt-4 flex gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('people')}>\n {t('customers.deals.detail.tabs.people', 'People')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('companies')}>\n {t('customers.deals.detail.tabs.companies', 'Companies')}\n </Button>\n </div>\n </div>\n ) : selectedActivityEntity ? (\n <InlineActivityComposer\n entityType={selectedActivityEntity.kind}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n onActivityCreated={() => { void handleActivityCreated() }}\n runGuardedMutation={runMutationWithContext}\n onScheduleRequested={openSchedule}\n />\n ) : (\n <EmptyState\n size=\"sm\"\n icon={<UserSearch className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('customers.deals.detail.activities.selectEntityRequiredTitle', 'Choose a person or company to continue')}\n description={t(\n 'customers.deals.detail.activities.selectEntityRequiredDescription',\n 'Select the customer record that should receive new deal activities before logging or scheduling anything.',\n )}\n />\n )}\n <PlannedActivitiesSection\n activities={plannedActivities}\n onComplete={(interactionId) => { void handleMarkDone(interactionId) }}\n onSchedule={selectedActivityEntity ? openSchedule : undefined}\n onEdit={handleEditActivity}\n onCancel={(interactionId) => { void handleCancelActivity(interactionId) }}\n />\n {selectedActivityEntity ? (\n <ActivitiesSection\n entityId={selectedActivityEntity.id}\n entityName={selectedActivityEntity.label}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={selectedActivityEntity.id}\n addActionLabel={t('customers.deals.detail.activitiesAdd', 'Log activity')}\n emptyState={{\n title: t('customers.deals.detail.activitiesEmptyTitle', 'No activities yet'),\n actionLabel: t('customers.deals.detail.activitiesEmptyAction', 'Log activity'),\n }}\n runGuardedMutation={runMutationWithContext}\n onDataRefresh={() => { void handleActivityCreated() }}\n refreshKey={activityRefreshKey}\n onEditActivity={handleEditActivity}\n />\n ) : null}\n </div>\n )\n }\n\n if (activeTab === 'people') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.peopleSingular', 'Person')}\n entityLabelPlural={t('customers.deals.detail.tabs.people', 'People')}\n manageLabel={t('customers.deals.detail.peopleEditorTitle', 'Manage linked people')}\n searchPlaceholder={t('customers.deals.detail.peopleSearch', 'Search linked people\u2026')}\n linkedItems={data.people}\n linkedCount={data.counts.people}\n selectedIds={peopleEditorIds}\n disabled={peopleSaving || isSaving}\n savePending={peopleSaving}\n hrefBuilder={(personId) => `/backend/customers/people-v2/${encodeURIComponent(personId)}`}\n onSaveSelection={(next) => handlePeopleAssociationsChange(next)}\n loadLinkedPage={loadLinkedPeoplePage}\n searchEntities={searchPeoplePage}\n fetchEntitiesByIds={fetchPeopleByIds}\n icon={<Users className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'companies') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.companySingular', 'Company')}\n entityLabelPlural={t('customers.deals.detail.tabs.companies', 'Companies')}\n manageLabel={t('customers.deals.detail.companiesEditorTitle', 'Manage linked companies')}\n searchPlaceholder={t('customers.deals.detail.companiesSearch', 'Search linked companies\u2026')}\n linkedItems={data.companies}\n linkedCount={data.counts.companies}\n selectedIds={companiesEditorIds}\n disabled={companiesSaving || isSaving}\n savePending={companiesSaving}\n hrefBuilder={(companyId) => `/backend/customers/companies-v2/${encodeURIComponent(companyId)}`}\n onSaveSelection={(next) => handleCompaniesAssociationsChange(next)}\n loadLinkedPage={loadLinkedCompaniesPage}\n searchEntities={searchCompaniesPage}\n fetchEntitiesByIds={fetchCompaniesByIds}\n icon={<Building2 className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'notes') {\n return (\n <NotesSection\n entityId={null}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n emptyLabel={t('customers.deals.detail.notesEmpty', 'No notes yet.')}\n viewerUserId={data.viewer?.userId ?? null}\n viewerName={data.viewer?.name ?? null}\n viewerEmail={data.viewer?.email ?? null}\n addActionLabel={t('customers.deals.detail.notesAdd', 'Add note')}\n emptyState={{\n title: t('customers.deals.detail.notesEmptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('customers.deals.detail.notesEmptyAction', 'Add a note'),\n }}\n translator={detailTranslator}\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n readMarkdownPreference={readMarkdownPreferenceCookie}\n writeMarkdownPreference={writeMarkdownPreferenceCookie}\n />\n )\n }\n\n if (activeTab === 'files') {\n return (\n <AttachmentsSection\n entityId={E.customers.customer_deal}\n recordId={data.deal.id}\n title={t('customers.deals.detail.tabs.files', 'Files')}\n description={t('customers.deals.detail.files.subtitle', 'Upload and manage files linked to this deal.')}\n />\n )\n }\n\n if (activeTab === 'changelog') {\n return <ChangelogTab entityId={data.deal.id} entityType=\"deal\" />\n }\n\n return null\n })()}\n </div>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <InjectionSpot spotId=\"detail:customers.deal:header\" context={injectionContext} data={data} />\n\n <DealDetailHeader\n deal={data.deal}\n owner={data.owner}\n people={data.people}\n companies={data.companies}\n pipelineName={currentPipelineName}\n stageOptions={data.pipelineStages}\n currentStageId={data.deal.pipelineStageId}\n onStageChange={handleStageChange}\n isStageSaving={isStageSaving}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n />\n\n <InjectionSpot spotId=\"detail:customers.deal:status-badges\" context={injectionContext} data={data} />\n\n <PipelineStepper\n stages={data.pipelineStages}\n transitions={data.stageTransitions}\n currentStageId={data.deal.pipelineStageId}\n pipelineName={currentPipelineName}\n closureOutcome={data.deal.closureOutcome}\n footer={data.deal.closureOutcome ? null : (\n <DealClosureActionBar\n embedded\n closureOutcome={data.deal.closureOutcome}\n onWon={() => { void handleWon() }}\n onLost={openLostDialog}\n />\n )}\n />\n\n <DealDetailTabs\n activeTab={activeTab}\n onTabChange={handleTabChange}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts.people}\n companiesCount={data.counts.companies}\n >\n <CollapsibleZoneLayout\n pageType=\"deal-detail-v3\"\n entityName={dealName}\n isDirty={isDirty}\n zone1DefaultWidth=\"540px\"\n zone1={zone1Content}\n zone2={zone2Content}\n />\n </DealDetailTabs>\n\n <InjectionSpot spotId=\"detail:customers.deal:footer\" context={injectionContext} data={data} />\n </div>\n\n {ConfirmDialogElement}\n\n {selectedActivityEntity ? (\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={closeSchedule}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n entityType={selectedActivityEntity.kind}\n entityName={selectedActivityEntity.label}\n companyName={selectedActivityEntity.kind === 'company' ? selectedActivityEntity.label : data.companies[0]?.label ?? null}\n onActivityCreated={() => { void handleActivityCreated() }}\n editData={scheduleEditData}\n />\n ) : null}\n\n <ConfirmDealLostDialog\n open={lostDialogOpen}\n onClose={closeLostDialog}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n dealValue={amountLabel}\n companyName={data.companies[0]?.label ?? null}\n onConfirm={handleLostConfirm}\n />\n\n <DealWonPopup\n open={wonPopupOpen}\n onClose={closeWonPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n stats={wonStats}\n onViewDashboard={handleViewDashboard}\n onBackToPipeline={handleBackToPipeline}\n />\n\n <DealLostSummaryDialog\n open={lostPopupOpen}\n onClose={closeLostPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n lossNotes={data.deal.lossNotes}\n stats={lostStats}\n onBackToPipeline={handleBackToPipeline}\n onScheduleFollowUp={selectedActivityEntity ? handleScheduleLostFollowUp : undefined}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["id"]
|
|
7
7
|
}
|
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
SelectTrigger,
|
|
12
12
|
SelectValue
|
|
13
13
|
} from "@open-mercato/ui/primitives/select";
|
|
14
|
+
import { Layers, MoveRight, Workflow } from "lucide-react";
|
|
14
15
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
15
16
|
import { ErrorNotice } from "@open-mercato/ui/primitives/ErrorNotice";
|
|
17
|
+
import { EmptyState } from "@open-mercato/ui/primitives/empty-state";
|
|
16
18
|
import { apiCallOrThrow, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
17
19
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
18
20
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
@@ -399,7 +401,14 @@ function SalesPipelinePage() {
|
|
|
399
401
|
] })
|
|
400
402
|
] })
|
|
401
403
|
] }),
|
|
402
|
-
!selectedPipelineId ? /* @__PURE__ */ jsx(
|
|
404
|
+
!selectedPipelineId ? /* @__PURE__ */ jsx(
|
|
405
|
+
EmptyState,
|
|
406
|
+
{
|
|
407
|
+
icon: /* @__PURE__ */ jsx(Workflow, { className: "h-8 w-8", "aria-hidden": "true" }),
|
|
408
|
+
title: translate("customers.deals.pipeline.noPipeline", "No pipeline selected. Create a pipeline in settings."),
|
|
409
|
+
className: "h-[50vh] w-full"
|
|
410
|
+
}
|
|
411
|
+
) : 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(
|
|
403
412
|
ErrorNotice,
|
|
404
413
|
{
|
|
405
414
|
message: dealsQuery.error instanceof Error ? dealsQuery.error.message : translate("customers.deals.pipeline.loadError", "Failed to load deals.")
|
|
@@ -410,7 +419,14 @@ function SalesPipelinePage() {
|
|
|
410
419
|
"Showing the first {count} deals. Refine your filters to see more.",
|
|
411
420
|
{ count: deals.length }
|
|
412
421
|
) }) : null,
|
|
413
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 pb-6 md:flex-row md:overflow-x-auto", children: stages.length === 0 ? /* @__PURE__ */ jsx(
|
|
422
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 pb-6 md:flex-row md:overflow-x-auto", children: stages.length === 0 ? /* @__PURE__ */ jsx(
|
|
423
|
+
EmptyState,
|
|
424
|
+
{
|
|
425
|
+
icon: /* @__PURE__ */ jsx(Layers, { className: "h-8 w-8", "aria-hidden": "true" }),
|
|
426
|
+
title: translate("customers.deals.pipeline.noStages", "Define pipeline stages to start tracking deals."),
|
|
427
|
+
className: "h-[50vh] w-full"
|
|
428
|
+
}
|
|
429
|
+
) : stages.map((stage) => {
|
|
414
430
|
const stageKey = stage.value ?? null;
|
|
415
431
|
const laneDeals = groupedDeals.get(stageKey) ?? [];
|
|
416
432
|
const sortedLaneDeals = sortDeals(laneDeals, sortBy);
|
|
@@ -423,7 +439,14 @@ function SalesPipelinePage() {
|
|
|
423
439
|
onDrop: handleDrop(stage),
|
|
424
440
|
children: [
|
|
425
441
|
renderLaneHeader(stage, laneDeals.length),
|
|
426
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto px-4 py-3", children: sortedLaneDeals.length === 0 ? /* @__PURE__ */ jsx(
|
|
442
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto px-4 py-3", children: sortedLaneDeals.length === 0 ? /* @__PURE__ */ jsx(
|
|
443
|
+
EmptyState,
|
|
444
|
+
{
|
|
445
|
+
size: "sm",
|
|
446
|
+
icon: /* @__PURE__ */ jsx(MoveRight, { className: "h-6 w-6", "aria-hidden": "true" }),
|
|
447
|
+
title: translate("customers.deals.pipeline.emptyLane", "No deals in this stage yet.")
|
|
448
|
+
}
|
|
449
|
+
) : sortedLaneDeals.map((deal) => {
|
|
427
450
|
const isDragging = draggingId === deal.id || pendingDealId === deal.id && updateStageMutation.isPending;
|
|
428
451
|
const valueLabel = formatCurrency(
|
|
429
452
|
deal.valueAmount,
|
|
@@ -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 {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\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((value: string) => {\n if (sortOptions.includes(value as SortOption)) setSortBy(value as SortOption)\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 value={selectedPipelineId || undefined}\n onValueChange={(value) => setSelectedPipelineId(value || null)}\n >\n <SelectTrigger className=\"w-auto min-w-[12rem]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pipelinesQuery.data.map((p) => (\n <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>\n ))}\n </SelectContent>\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 value={sortBy} onValueChange={handleSortChange}>\n <SelectTrigger className=\"w-auto min-w-[14rem]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"probability\">\n {translate('customers.deals.pipeline.sort.probability', 'Probability (high to low)')}\n </SelectItem>\n <SelectItem value=\"createdAt\">\n {translate('customers.deals.pipeline.sort.createdAt', 'Created (newest first)')}\n </SelectItem>\n <SelectItem value=\"expectedCloseAt\">\n {translate('customers.deals.pipeline.sort.expectedCloseAt', 'Expected close (soonest first)')}\n </SelectItem>\n </SelectContent>\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/30\">\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/30 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-v2/${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-v2/${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 {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Layers, MoveRight, Workflow } from 'lucide-react'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\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((value: string) => {\n if (sortOptions.includes(value as SortOption)) setSortBy(value as SortOption)\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 value={selectedPipelineId || undefined}\n onValueChange={(value) => setSelectedPipelineId(value || null)}\n >\n <SelectTrigger className=\"w-auto min-w-[12rem]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pipelinesQuery.data.map((p) => (\n <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>\n ))}\n </SelectContent>\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 value={sortBy} onValueChange={handleSortChange}>\n <SelectTrigger className=\"w-auto min-w-[14rem]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"probability\">\n {translate('customers.deals.pipeline.sort.probability', 'Probability (high to low)')}\n </SelectItem>\n <SelectItem value=\"createdAt\">\n {translate('customers.deals.pipeline.sort.createdAt', 'Created (newest first)')}\n </SelectItem>\n <SelectItem value=\"expectedCloseAt\">\n {translate('customers.deals.pipeline.sort.expectedCloseAt', 'Expected close (soonest first)')}\n </SelectItem>\n </SelectContent>\n </Select>\n </label>\n </div>\n </div>\n\n {!selectedPipelineId ? (\n <EmptyState\n icon={<Workflow className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={translate('customers.deals.pipeline.noPipeline', 'No pipeline selected. Create a pipeline in settings.')}\n className=\"h-[50vh] w-full\"\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 <EmptyState\n icon={<Layers className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={translate('customers.deals.pipeline.noStages', 'Define pipeline stages to start tracking deals.')}\n className=\"h-[50vh] w-full\"\n />\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 <EmptyState\n size=\"sm\"\n icon={<MoveRight className=\"h-6 w-6\" aria-hidden=\"true\" />}\n title={translate('customers.deals.pipeline.emptyLane', 'No deals in this stage yet.')}\n />\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-v2/${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-v2/${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
|
}
|
|
@@ -347,7 +347,7 @@ function PersonDetailV2Page({ params }) {
|
|
|
347
347
|
onSubmit: handleFormSubmit,
|
|
348
348
|
onDelete: handleFormDelete,
|
|
349
349
|
hideFooterActions: true,
|
|
350
|
-
collapsibleGroups: { pageType: "person-v2", chevronPosition: "
|
|
350
|
+
collapsibleGroups: { pageType: "person-v2", chevronPosition: "right" },
|
|
351
351
|
sortableGroups: { pageType: "person-v2" },
|
|
352
352
|
onDirtyChange: setIsDirty
|
|
353
353
|
}
|