@open-mercato/core 0.6.6-develop.6330.1.a261878aa8 → 0.6.6-develop.6331.1.a33b8e99b7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +3 -0
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +2 -2
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +2 -2
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +12 -0
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +12 -0
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/data/entities.js +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/business_rules/api/rules/route.js +3 -3
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/api/sets/route.js +3 -3
- package/dist/modules/business_rules/api/sets/route.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/[id]/page.js +12 -1
- package/dist/modules/business_rules/backend/rules/[id]/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/[id]/page.js +12 -1
- package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
- package/dist/modules/catalog/api/product-media/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +12 -0
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +15 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +12 -1
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +13 -3
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/lib/bulkDelete.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js +15 -3
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +2 -1
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +12 -1
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +3 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id].js +3 -3
- package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users/[id].js +3 -3
- package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
- package/dist/modules/customers/api/addresses/route.js +4 -2
- package/dist/modules/customers/api/addresses/route.js.map +2 -2
- package/dist/modules/customers/api/tags/route.js +8 -3
- package/dist/modules/customers/api/tags/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +13 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +24 -9
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +53 -23
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js +13 -3
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +13 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +5 -5
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +24 -2
- package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
- package/dist/modules/customers/commands/pipelines.js +24 -2
- package/dist/modules/customers/commands/pipelines.js.map +2 -2
- package/dist/modules/customers/components/detail/AddressesSection.js +46 -29
- package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +5 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/notesAdapter.js +25 -18
- package/dist/modules/customers/components/detail/notesAdapter.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +38 -19
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +1 -1
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +32 -15
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +19 -5
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +3 -3
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js +3 -3
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +12 -1
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +12 -0
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +12 -0
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
- package/dist/modules/entities/api/entities.js +2 -2
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +7 -5
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
- package/dist/modules/feature_toggles/data/entities.js +1 -1
- package/dist/modules/feature_toggles/data/entities.js.map +2 -2
- package/dist/modules/feature_toggles/data/validators.js +2 -1
- package/dist/modules/feature_toggles/data/validators.js.map +2 -2
- package/dist/modules/feature_toggles/lib/queries.js +2 -1
- package/dist/modules/feature_toggles/lib/queries.js.map +2 -2
- package/dist/modules/inbox_ops/api/settings/route.js +2 -2
- package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +12 -1
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +12 -1
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/resources/components/detail/notesAdapter.js +25 -18
- package/dist/modules/resources/components/detail/notesAdapter.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +14 -1
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +11 -10
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +6 -1
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +3 -3
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/sales/commands/shared.js +3 -3
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +6 -1
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentDialog.js +6 -3
- package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +7 -3
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +6 -3
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +7 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/di.js +39 -3
- package/dist/modules/sales/di.js.map +2 -2
- package/dist/modules/sales/setup.js +2 -0
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +12 -1
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +12 -1
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +12 -1
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +12 -1
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/commands/job-histories.js +3 -3
- package/dist/modules/staff/commands/job-histories.js.map +2 -2
- package/dist/modules/staff/components/detail/addressesAdapter.js +33 -24
- package/dist/modules/staff/components/detail/addressesAdapter.js.map +2 -2
- package/dist/modules/staff/components/detail/notesAdapter.js +25 -18
- package/dist/modules/staff/components/detail/notesAdapter.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +37 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +7 -1
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +3 -3
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +12 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +11 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +3 -0
- package/src/modules/auth/api/roles/acl/route.ts +2 -2
- package/src/modules/auth/api/users/acl/route.ts +2 -2
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +17 -0
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +17 -0
- package/src/modules/auth/data/entities.ts +2 -2
- package/src/modules/business_rules/api/rules/route.ts +3 -3
- package/src/modules/business_rules/api/sets/route.ts +3 -3
- package/src/modules/business_rules/backend/rules/[id]/page.tsx +17 -1
- package/src/modules/business_rules/backend/sets/[id]/page.tsx +18 -1
- package/src/modules/catalog/api/product-media/route.ts +4 -0
- package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +17 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +21 -0
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +16 -1
- package/src/modules/catalog/components/PriceKindSettings.tsx +15 -7
- package/src/modules/catalog/lib/bulkDelete.ts +6 -0
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -3
- package/src/modules/currencies/backend/currencies/page.tsx +2 -1
- package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +17 -1
- package/src/modules/currencies/backend/exchange-rates/page.tsx +3 -2
- package/src/modules/customer_accounts/api/admin/roles/[id].ts +3 -3
- package/src/modules/customer_accounts/api/admin/users/[id].ts +3 -3
- package/src/modules/customers/api/addresses/route.ts +2 -0
- package/src/modules/customers/api/tags/route.ts +6 -1
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +19 -2
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +29 -8
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +47 -20
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.ts +13 -3
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -2
- package/src/modules/customers/commands/interactions.ts +5 -5
- package/src/modules/customers/commands/pipeline-stages.ts +26 -2
- package/src/modules/customers/commands/pipelines.ts +26 -2
- package/src/modules/customers/components/detail/AddressesSection.tsx +50 -28
- package/src/modules/customers/components/detail/DealForm.tsx +12 -0
- package/src/modules/customers/components/detail/notesAdapter.ts +29 -18
- package/src/modules/customers/i18n/de.json +2 -0
- package/src/modules/customers/i18n/en.json +2 -0
- package/src/modules/customers/i18n/es.json +2 -0
- package/src/modules/customers/i18n/pl.json +2 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +38 -20
- package/src/modules/data_sync/api/schedules/route.ts +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +32 -15
- package/src/modules/data_sync/lib/sync-schedule-service.ts +30 -5
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +3 -3
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +3 -3
- package/src/modules/dictionaries/components/DictionariesManager.tsx +21 -1
- package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +2 -0
- package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +17 -0
- package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +17 -0
- package/src/modules/entities/api/entities.ts +2 -2
- package/src/modules/entities/api/records.ts +7 -5
- package/src/modules/feature_toggles/api/overrides/route.ts +2 -2
- package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +12 -0
- package/src/modules/feature_toggles/data/entities.ts +1 -1
- package/src/modules/feature_toggles/data/validators.ts +1 -0
- package/src/modules/feature_toggles/lib/queries.ts +1 -0
- package/src/modules/inbox_ops/api/settings/route.ts +2 -2
- package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +17 -1
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +17 -1
- package/src/modules/resources/components/detail/notesAdapter.ts +29 -18
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +17 -1
- package/src/modules/sales/commands/documents.ts +11 -10
- package/src/modules/sales/commands/payments.ts +11 -0
- package/src/modules/sales/commands/returns.ts +3 -3
- package/src/modules/sales/commands/shared.ts +10 -4
- package/src/modules/sales/commands/shipments.ts +14 -0
- package/src/modules/sales/components/documents/PaymentDialog.tsx +7 -3
- package/src/modules/sales/components/documents/PaymentsSection.tsx +8 -3
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +7 -3
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +8 -3
- package/src/modules/sales/di.ts +68 -9
- package/src/modules/sales/setup.ts +2 -0
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +17 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +17 -1
- package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +17 -1
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +17 -1
- package/src/modules/staff/commands/job-histories.ts +3 -3
- package/src/modules/staff/components/detail/addressesAdapter.ts +40 -23
- package/src/modules/staff/components/detail/notesAdapter.ts +28 -18
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +74 -0
- package/src/modules/translations/components/TranslationManager.tsx +14 -1
- package/src/modules/workflows/api/definitions/[id]/route.ts +3 -3
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +18 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +19 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/customers/backend/customers/companies-v2/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Building2, Hash, Users, BarChart3, StickyNote } from 'lucide-react'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, RecordNotFoundState, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { CollapsibleZoneLayout, type ZoneSectionDescriptor } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { E } from '#generated/entities.ids.generated'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { DealsSection } from '../../../../components/detail/DealsSection'\nimport { ActivityLogTab } from '../../../../components/detail/ActivityLogTab'\nimport { CompanyPeopleSection, type CompanyPersonSummary } from '../../../../components/detail/CompanyPeopleSection'\nimport type { TagSummary } from '../../../../components/detail/types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport { coerceDisplayName } from '../../../../lib/displayName'\nimport { CompanyDetailHeader } from '../../../../components/detail/CompanyDetailHeader'\nimport { CompanyDetailTabs, resolveLegacyTab, type CompanyTabId } from '../../../../components/detail/CompanyDetailTabs'\nimport { CompanyKpiBar } from '../../../../components/detail/CompanyKpiBar'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { useInteractionMutations } from '../../../../components/detail/hooks/useInteractionMutations'\nimport {\n buildCompanyEditPayload,\n createCompanyEditFields,\n createCompanyDaneFiremyGroups,\n createCompanyEditSchema,\n mapCompanyOverviewToFormValues,\n type CompanyEditFormValues,\n type CompanyOverview,\n} from '../../../../components/formConfig'\n\nexport default function CompanyDetailV2Page({ 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\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const [data, setData] = React.useState<CompanyOverview | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n\n // Tab state\n const initialTab = React.useMemo(() => {\n return resolveLegacyTab(searchParams?.get('tab'))\n }, [searchParams])\n const [activeTab, setActiveTab] = React.useState<CompanyTabId>(initialTab)\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n\n // Form state\n const [isDirty, setIsDirty] = React.useState(false)\n const [isSaving, setIsSaving] = React.useState(false)\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n const { organizationId } = useOrganizationScopeDetail()\n const formSchema = React.useMemo(() => createCompanyEditSchema(), [])\n const formFields = React.useMemo(() => createCompanyEditFields(t), [t])\n const formGroups = React.useMemo(() => createCompanyDaneFiremyGroups(t), [t])\n const initialValues = React.useMemo(\n () => (data ? mapCompanyOverviewToFormValues(data) : undefined),\n [data],\n )\n const zoneSections = React.useMemo<ZoneSectionDescriptor[]>(() => [\n { id: 'identity', icon: Building2, label: t('customers.companies.form.sections.identity', 'Identity') },\n { id: 'contact', icon: Hash, label: t('customers.companies.form.sections.contact', 'Contact') },\n { id: 'classification', icon: Users, label: t('customers.companies.form.sections.classification', 'Classification') },\n { id: 'businessProfile', icon: BarChart3, label: t('customers.companies.form.sections.businessProfile', 'Business profile') },\n { id: 'notes', icon: StickyNote, label: t('customers.companies.form.groups.notes', 'Notes') },\n { id: 'customFields', icon: Hash, label: t('customers.companies.form.groups.customAttributes', 'Custom attributes') },\n ], [t])\n const [scheduleDialogOpen, setScheduleDialogOpen] = React.useState(false)\n const [scheduleEditData, setScheduleEditData] = React.useState<ScheduleActivityEditData | null>(null)\n const [activityRefreshKey, setActivityRefreshKey] = React.useState(0)\n const [dealCount, setDealCount] = React.useState(0)\n\n const currentCompanyId = data?.company?.id ?? null\n const mutationContextId = React.useMemo(\n () => (currentCompanyId ? `customer-company:${currentCompanyId}` : `customer-company:${id ?? 'pending'}`),\n [currentCompanyId, id],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n companyId?: string | null\n resourceKind: string\n resourceId?: string\n data: CompanyOverview | null\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const companyDisplayName = coerceDisplayName(data?.company?.displayName)\n const companyName = companyDisplayName.trim().length\n ? companyDisplayName\n : t('customers.companies.list.deleteFallbackName', 'this company')\n\n // Data loading\n const initialLoadDoneRef = React.useRef(false)\n const loadData = React.useCallback(async () => {\n if (!id) {\n setIsNotFound(true)\n setIsLoading(false)\n return\n }\n if (!initialLoadDoneRef.current) {\n setIsLoading(true)\n }\n setError(null)\n setIsNotFound(false)\n try {\n const payload = await readApiResultOrThrow<CompanyOverview>(\n `/api/customers/companies/${encodeURIComponent(id)}`,\n undefined,\n { errorMessage: t('customers.companies.detail.error.load', 'Failed to load company.') },\n )\n setData(payload as CompanyOverview)\n } catch (err) {\n if ((err as { status?: number }).status === 404) {\n setIsNotFound(true)\n } else {\n const message = err instanceof Error ? err.message : t('customers.companies.detail.error.load', 'Failed to load company.')\n setError(message)\n }\n if (!initialLoadDoneRef.current) setData(null)\n } finally {\n setIsLoading(false)\n initialLoadDoneRef.current = true\n }\n }, [id, t])\n\n React.useEffect(() => {\n loadData().catch((err) => console.warn('[companies-v2] loadData failed', err))\n }, [loadData])\n\n React.useEffect(() => {\n setDealCount(data?.counts?.deals ?? 0)\n }, [data?.counts?.deals])\n\n const handleActivityCreated = React.useCallback(() => {\n setActivityRefreshKey((k) => k + 1)\n loadData().catch((err) => console.warn('[companies-v2] reload after activity failed', err))\n }, [loadData])\n\n // Planned activities for the activity-log tab\n const plannedActivities = React.useMemo(() => {\n return data?.plannedActivitiesPreview ?? []\n }, [data?.plannedActivitiesPreview])\n\n // Injection context for UMES\n const injectionContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n companyId: currentCompanyId,\n resourceKind: 'customers.company',\n resourceId: currentCompanyId ?? (id ?? undefined),\n data,\n retryLastMutation,\n }),\n [currentCompanyId, data, id, mutationContextId, retryLastMutation],\n )\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: injectionContext,\n })\n },\n [injectionContext, runMutation],\n )\n\n const { completeInteraction: handleMarkDone, cancelInteraction: handleCancelActivity } = useInteractionMutations({\n runMutationWithContext,\n onAfterChange: handleActivityCreated,\n logContext: 'customers.companies-v2',\n })\n\n const handleEditActivity = React.useCallback((activity: { id: string; interactionType?: string; title?: string | null; body?: string | null; scheduledAt?: string | null; occurredAt?: string | null; [key: string]: unknown }) => {\n const raw = activity as Record<string, unknown>\n const durationValue = typeof raw.duration === 'number'\n ? raw.duration\n : typeof raw.durationMinutes === 'number'\n ? raw.durationMinutes as number\n : null\n // Forward `customValues` so per-type chip state (callPhoneNumber, callDirection,\n // taskPriority, \u2026) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the original\n // moment instead of \"today\" (#1807 prefill).\n const editPayload = {\n id: activity.id,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt as string : typeof raw.updated_at === 'string' ? raw.updated_at as string : null,\n interactionType: typeof activity.interactionType === 'string' ? activity.interactionType : undefined,\n title: typeof activity.title === 'string' ? activity.title : null,\n body: typeof activity.body === 'string' ? activity.body : null,\n scheduledAt: typeof activity.scheduledAt === 'string' ? activity.scheduledAt : null,\n occurredAt: typeof activity.occurredAt === 'string' ? activity.occurredAt : null,\n durationMinutes: durationValue,\n location: typeof raw.location === 'string' ? raw.location as string : null,\n allDay: typeof raw.allDay === 'boolean' ? raw.allDay as boolean : null,\n recurrenceRule: typeof raw.recurrenceRule === 'string' ? raw.recurrenceRule as string : null,\n recurrenceEnd: typeof raw.recurrenceEnd === 'string' ? raw.recurrenceEnd as string : null,\n participants: Array.isArray(raw.participants) ? raw.participants as ScheduleActivityEditData['participants'] : null,\n reminderMinutes: typeof raw.reminderMinutes === 'number' ? raw.reminderMinutes as number : null,\n visibility: typeof raw.visibility === 'string' ? raw.visibility as string : null,\n linkedEntities: Array.isArray(raw.linkedEntities) ? raw.linkedEntities as ScheduleActivityEditData['linkedEntities'] : null,\n guestPermissions: raw.guestPermissions && typeof raw.guestPermissions === 'object'\n ? raw.guestPermissions as ScheduleActivityEditData['guestPermissions']\n : null,\n customValues: raw.customValues && typeof raw.customValues === 'object'\n ? raw.customValues as Record<string, unknown>\n : null,\n phoneNumber: typeof raw.phoneNumber === 'string' ? raw.phoneNumber as string : null,\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null }\n setScheduleEditData(editPayload)\n setScheduleDialogOpen(true)\n }, [])\n\n const openNewScheduleDialog = React.useCallback(() => {\n setScheduleEditData(null)\n setScheduleDialogOpen(true)\n }, [])\n\n const handleAddActivity = React.useCallback((kind: 'meeting' | 'call' | 'task' | 'email') => {\n setScheduleEditData({\n id: '',\n interactionType: kind,\n title: null,\n body: null,\n scheduledAt: null,\n durationMinutes: null,\n location: null,\n allDay: null,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: null,\n visibility: null,\n linkedEntities: null,\n guestPermissions: null,\n })\n setScheduleDialogOpen(true)\n }, [])\n\n // Injected tabs from UMES\n const { widgets: injectedTabWidgets } = useInjectionWidgets('detail:customers.company:tabs', {\n context: injectionContext,\n triggerOnLoad: true,\n })\n\n const injectedTabs = React.useMemo(\n () =>\n (injectedTabWidgets ?? [])\n .filter((widget) => (widget.placement?.kind ?? 'tab') === 'tab')\n .map((widget) => {\n const tabId = widget.placement?.groupId ?? widget.widgetId\n const label = widget.placement?.groupLabel ?? widget.module.metadata.title ?? tabId\n const priority = typeof widget.placement?.priority === 'number' ? widget.placement.priority : 0\n const render = () => (\n <widget.module.Widget\n context={injectionContext}\n data={data}\n onDataChange={(next: unknown) => setData(next as CompanyOverview)}\n />\n )\n return { id: tabId, label, priority, render }\n })\n .sort((a, b) => b.priority - a.priority),\n [data, injectedTabWidgets, injectionContext],\n )\n\n const injectedTabMap = React.useMemo(() => new Map(injectedTabs.map((tab) => [tab.id, tab.render])), [injectedTabs])\n\n // Tags\n const handleTagsChange = React.useCallback((nextTags: TagSummary[]) => {\n setData((prev) => (prev ? { ...prev, tags: nextTags } : prev))\n }, [])\n const tagsSectionControllerRef = React.useRef<TagsSectionController | null>(null)\n\n // Section action (for tabs that expose add/create buttons)\n const handleSectionActionChange = React.useCallback((action: SectionAction | null) => {\n setSectionAction((prev) => (action !== null ? action : prev))\n }, [])\n\n const handleSectionAction = React.useCallback(() => {\n if (!sectionAction || sectionAction.disabled) return\n sectionAction.onClick()\n }, [sectionAction])\n\n React.useEffect(() => {\n setSectionAction(null)\n }, [activeTab])\n\n // Deals scope\n const dealsScope = React.useMemo(\n () => (currentCompanyId ? ({ kind: 'company', entityId: currentCompanyId } as const) : null),\n [currentCompanyId],\n )\n\n // Delete handler (shared between header and form)\n const handleDelete = React.useCallback(async () => {\n const companyId = data?.company?.id ?? ''\n if (!companyId) return\n const approved = await confirm({\n title: t('customers.companies.detail.deleteConfirmTitle', 'Delete company?'),\n description: t('customers.companies.detail.deleteConfirmDescription', 'This action cannot be undone.'),\n confirmText: t('customers.companies.detail.actions.delete', 'Delete company'),\n cancelText: t('customers.companies.detail.actions.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!approved) return\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader((data?.company as { updatedAt?: string } | undefined)?.updatedAt),\n () => deleteCrud('customers/companies', { id: companyId }),\n ),\n { id: companyId, operation: 'deleteCompany' },\n )\n } catch (err) {\n // The guarded mutation already routes a 409 to the unified conflict bar;\n // surface any other server error (e.g. \"Cannot delete company: linked\n // deals\u2026\") as a flash instead of letting it crash the page.\n if (!surfaceRecordConflict(err, t)) {\n flash(\n err instanceof Error && err.message.trim().length > 0\n ? err.message\n : t('customers.companies.detail.deleteError', 'Failed to delete company.'),\n 'error',\n )\n }\n return\n }\n flash(t('customers.companies.list.deleteSuccess', 'Company deleted.'), 'success')\n router.push('/backend/customers/companies')\n }, [confirm, data?.company?.id, router, runMutationWithContext, t])\n\n // Form submit handler (lifted from CompanyDataTab)\n const handleFormSubmit = React.useCallback(\n async (values: CompanyEditFormValues) => {\n setIsSaving(true)\n try {\n let payload: Record<string, unknown>\n try {\n payload = buildCompanyEditPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error) {\n if (err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.companies.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n if (err.message === 'ANNUAL_REVENUE_INVALID') {\n const message = t('customers.companies.form.annualRevenue.error')\n throw createCrudFormError(message, { annualRevenue: message })\n }\n }\n throw err\n }\n await updateCrud('customers/companies', payload)\n flash(t('customers.companies.form.updateSuccess', 'Company updated.'), 'success')\n await loadData()\n } finally {\n setIsSaving(false)\n }\n },\n [loadData, organizationId, t],\n )\n\n // Save handler (triggers form submit via ref)\n const handleHeaderSave = React.useCallback(() => {\n const form = formWrapperRef.current?.querySelector('form')\n if (form) form.requestSubmit()\n }, [])\n\n // Loading / error states\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.companies.detail.loading', 'Loading company\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('customers.companies.detail.error.notFound', 'Company not found.')}\n backHref=\"/backend/customers/companies\"\n backLabel={t('customers.companies.detail.actions.backToList', 'Back to companies')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data?.company?.id) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error ?? t('customers.companies.detail.error.load', 'Failed to load company.')}\n action={(\n <Button asChild variant=\"outline\">\n <Link href=\"/backend/customers/companies\">\n {t('customers.companies.detail.actions.backToList', 'Back to companies')}\n </Link>\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n const companyId = data.company.id\n const useCanonicalInteractions = data.interactionMode === 'canonical'\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-4\">\n {/* UMES header injection */}\n <InjectionSpot spotId=\"detail:customers.company:header\" context={injectionContext} data={data} />\n <InjectionSpot spotId=\"detail:customers.company:status-badges\" context={injectionContext} data={data} />\n\n {/* Persistent company header */}\n <CompanyDetailHeader\n data={data}\n onTagsChange={handleTagsChange}\n tagsSectionControllerRef={tagsSectionControllerRef}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n onDataReload={() => { loadData().catch((err) => console.warn('[companies-v2] onDataReload failed', err)) }}\n />\n\n {/* KPI bar \u2014 always visible above zones */}\n <CompanyKpiBar data={data} />\n\n {/* Two-zone layout: zone1 = form, zone2 = tabs */}\n <CollapsibleZoneLayout\n pageType=\"company-v2\"\n entityName={companyName}\n isDirty={isDirty}\n sections={zoneSections}\n zone1={\n <div ref={formWrapperRef}>\n <CrudForm<CompanyEditFormValues>\n embedded\n trackDirtyWhenEmbedded\n injectionSpotId=\"customers.company\"\n entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}\n schema={formSchema}\n fields={formFields}\n groups={formGroups}\n initialValues={initialValues}\n onSubmit={handleFormSubmit}\n onDelete={handleDelete}\n optimisticLockUpdatedAt={(data?.company as { updatedAt?: string } | undefined)?.updatedAt}\n hideFooterActions\n collapsibleGroups={{ pageType: 'company-v2', chevronPosition: 'right' }}\n sortableGroups={{ pageType: 'company-v2' }}\n onDirtyChange={setIsDirty}\n />\n </div>\n }\n zone2={\n <CompanyDetailTabs\n activeTab={activeTab}\n onTabChange={setActiveTab}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts?.people ?? 0}\n dealsCount={dealCount}\n activitiesCount={data.counts?.activities ?? 0}\n sectionAction={sectionAction}\n >\n {activeTab === 'people' && (\n <CompanyPeopleSection\n companyId={companyId}\n companyName={companyDisplayName}\n initialPeople={[]}\n addActionLabel={t('customers.companies.detail.people.add', 'Add person')}\n emptyLabel={t('customers.companies.detail.people.empty', 'No people linked to this company yet.')}\n emptyState={{\n title: t('customers.companies.detail.emptyState.people.title', 'Build the account team'),\n actionLabel: t('customers.companies.detail.emptyState.people.action', 'Create person'),\n }}\n onActionChange={handleSectionActionChange}\n translator={detailTranslator}\n runGuardedMutation={runMutationWithContext}\n onPeopleChange={(next) => {\n setData((prev) => {\n if (!prev) return prev\n const nextCount = next.length\n return {\n ...prev,\n people: next,\n counts: prev.counts ? { ...prev.counts, people: nextCount } : prev.counts,\n }\n })\n }}\n />\n )}\n\n {activeTab === 'deals' && (\n <DealsSection\n scope={dealsScope}\n emptyLabel={t('customers.companies.detail.empty.deals', 'No deals linked to this company.')}\n addActionLabel={t('customers.companies.detail.actions.addDeal', 'Add deal')}\n emptyState={{\n title: t('customers.companies.detail.emptyState.deals.title', 'No deals yet'),\n actionLabel: t('customers.companies.detail.emptyState.deals.action', 'Create a deal'),\n }}\n onActionChange={handleSectionActionChange}\n translator={detailTranslator}\n runGuardedMutation={runMutationWithContext}\n onCountDelta={(delta) => setDealCount((current) => Math.max(0, current + delta))}\n />\n )}\n\n {activeTab === 'activity-log' && (\n <ActivityLogTab\n entityId={companyId}\n plannedActivities={plannedActivities}\n onActivityCreated={handleActivityCreated}\n onScheduleRequested={openNewScheduleDialog}\n onAddActivity={handleAddActivity}\n onMarkDone={handleMarkDone}\n onEditActivity={handleEditActivity}\n onCancelActivity={handleCancelActivity}\n runGuardedMutation={runMutationWithContext}\n refreshKey={activityRefreshKey}\n useCanonicalInteractions={useCanonicalInteractions}\n />\n )}\n\n {activeTab === 'changelog' && companyId && (\n <ChangelogTab entityId={companyId} entityType=\"company\" />\n )}\n\n {activeTab === 'files' && (\n <AttachmentsSection\n entityId={E.customers.customer_entity}\n recordId={companyId}\n title={t('customers.companies.detail.tabs.files', 'Files')}\n description={t('customers.companies.detail.files.subtitle', 'Upload and manage files linked to this company.')}\n />\n )}\n\n {/* Injected tabs from UMES */}\n {injectedTabMap.has(activeTab) && injectedTabMap.get(activeTab)!()}\n </CompanyDetailTabs>\n }\n />\n\n {/* UMES footer injection */}\n <InjectionSpot spotId=\"detail:customers.company:footer\" context={injectionContext} data={data} />\n\n {/* Schedule Activity Dialog */}\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={() => { setScheduleDialogOpen(false); setScheduleEditData(null) }}\n entityId={companyId}\n entityName={companyName}\n entityType=\"company\"\n onActivityCreated={handleActivityCreated}\n editData={scheduleEditData}\n />\n {ConfirmDialogElement}\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 { useRouter, useSearchParams, usePathname } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Building2, Hash, Users, BarChart3, StickyNote } from 'lucide-react'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, RecordNotFoundState, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from '@open-mercato/ui/backend/injection/recordContext'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { CollapsibleZoneLayout, type ZoneSectionDescriptor } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { E } from '#generated/entities.ids.generated'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { DealsSection } from '../../../../components/detail/DealsSection'\nimport { ActivityLogTab } from '../../../../components/detail/ActivityLogTab'\nimport { CompanyPeopleSection, type CompanyPersonSummary } from '../../../../components/detail/CompanyPeopleSection'\nimport type { TagSummary } from '../../../../components/detail/types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport { coerceDisplayName } from '../../../../lib/displayName'\nimport { CompanyDetailHeader } from '../../../../components/detail/CompanyDetailHeader'\nimport { CompanyDetailTabs, resolveLegacyTab, type CompanyTabId } from '../../../../components/detail/CompanyDetailTabs'\nimport { CompanyKpiBar } from '../../../../components/detail/CompanyKpiBar'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { useInteractionMutations } from '../../../../components/detail/hooks/useInteractionMutations'\nimport {\n buildCompanyEditPayload,\n createCompanyEditFields,\n createCompanyDaneFiremyGroups,\n createCompanyEditSchema,\n mapCompanyOverviewToFormValues,\n type CompanyEditFormValues,\n type CompanyOverview,\n} from '../../../../components/formConfig'\n\nexport default function CompanyDetailV2Page({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const pathname = usePathname()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const [data, setData] = React.useState<CompanyOverview | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n\n // Tab state\n const initialTab = React.useMemo(() => {\n return resolveLegacyTab(searchParams?.get('tab'))\n }, [searchParams])\n const [activeTab, setActiveTab] = React.useState<CompanyTabId>(initialTab)\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n\n // Form state\n const [isDirty, setIsDirty] = React.useState(false)\n const [isSaving, setIsSaving] = React.useState(false)\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n const { organizationId } = useOrganizationScopeDetail()\n const formSchema = React.useMemo(() => createCompanyEditSchema(), [])\n const formFields = React.useMemo(() => createCompanyEditFields(t), [t])\n const formGroups = React.useMemo(() => createCompanyDaneFiremyGroups(t), [t])\n const initialValues = React.useMemo(\n () => (data ? mapCompanyOverviewToFormValues(data) : undefined),\n [data],\n )\n const zoneSections = React.useMemo<ZoneSectionDescriptor[]>(() => [\n { id: 'identity', icon: Building2, label: t('customers.companies.form.sections.identity', 'Identity') },\n { id: 'contact', icon: Hash, label: t('customers.companies.form.sections.contact', 'Contact') },\n { id: 'classification', icon: Users, label: t('customers.companies.form.sections.classification', 'Classification') },\n { id: 'businessProfile', icon: BarChart3, label: t('customers.companies.form.sections.businessProfile', 'Business profile') },\n { id: 'notes', icon: StickyNote, label: t('customers.companies.form.groups.notes', 'Notes') },\n { id: 'customFields', icon: Hash, label: t('customers.companies.form.groups.customAttributes', 'Custom attributes') },\n ], [t])\n const [scheduleDialogOpen, setScheduleDialogOpen] = React.useState(false)\n const [scheduleEditData, setScheduleEditData] = React.useState<ScheduleActivityEditData | null>(null)\n const [activityRefreshKey, setActivityRefreshKey] = React.useState(0)\n const [dealCount, setDealCount] = React.useState(0)\n\n const currentCompanyId = data?.company?.id ?? null\n const mutationContextId = React.useMemo(\n () => (currentCompanyId ? `customer-company:${currentCompanyId}` : `customer-company:${id ?? 'pending'}`),\n [currentCompanyId, id],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n companyId?: string | null\n resourceKind: string\n resourceId?: string\n data: CompanyOverview | null\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const companyDisplayName = coerceDisplayName(data?.company?.displayName)\n const companyName = companyDisplayName.trim().length\n ? companyDisplayName\n : t('customers.companies.list.deleteFallbackName', 'this company')\n\n // Data loading\n const initialLoadDoneRef = React.useRef(false)\n const loadData = React.useCallback(async () => {\n if (!id) {\n setIsNotFound(true)\n setIsLoading(false)\n return\n }\n if (!initialLoadDoneRef.current) {\n setIsLoading(true)\n }\n setError(null)\n setIsNotFound(false)\n try {\n const payload = await readApiResultOrThrow<CompanyOverview>(\n `/api/customers/companies/${encodeURIComponent(id)}`,\n undefined,\n { errorMessage: t('customers.companies.detail.error.load', 'Failed to load company.') },\n )\n setData(payload as CompanyOverview)\n } catch (err) {\n if ((err as { status?: number }).status === 404) {\n setIsNotFound(true)\n } else {\n const message = err instanceof Error ? err.message : t('customers.companies.detail.error.load', 'Failed to load company.')\n setError(message)\n }\n if (!initialLoadDoneRef.current) setData(null)\n } finally {\n setIsLoading(false)\n initialLoadDoneRef.current = true\n }\n }, [id, t])\n\n React.useEffect(() => {\n loadData().catch((err) => console.warn('[companies-v2] loadData failed', err))\n }, [loadData])\n\n React.useEffect(() => {\n setDealCount(data?.counts?.deals ?? 0)\n }, [data?.counts?.deals])\n\n const handleActivityCreated = React.useCallback(() => {\n setActivityRefreshKey((k) => k + 1)\n loadData().catch((err) => console.warn('[companies-v2] reload after activity failed', err))\n }, [loadData])\n\n // Planned activities for the activity-log tab\n const plannedActivities = React.useMemo(() => {\n return data?.plannedActivitiesPreview ?? []\n }, [data?.plannedActivitiesPreview])\n\n // Injection context for UMES\n const injectionContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n companyId: currentCompanyId,\n resourceKind: 'customers.company',\n resourceId: currentCompanyId ?? (id ?? undefined),\n data,\n retryLastMutation,\n }),\n [currentCompanyId, data, id, mutationContextId, retryLastMutation],\n )\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: injectionContext,\n })\n },\n [injectionContext, runMutation],\n )\n\n const { completeInteraction: handleMarkDone, cancelInteraction: handleCancelActivity } = useInteractionMutations({\n runMutationWithContext,\n onAfterChange: handleActivityCreated,\n logContext: 'customers.companies-v2',\n })\n\n // Publish page-load record context to the AppShell-owned `backend:record:current`\n // mount so the enterprise record_locks widget resolves `customers.company` + id\n // explicitly (the hardcoded path allowlist misses the `companies-v2` route).\n useSetCurrentRecordInjectionContext(\n buildRecordInjectionContext({\n resourceKind: 'customers.company',\n resourceId: currentCompanyId,\n updatedAt: (data?.company as { updatedAt?: string | null; updated_at?: string | null } | undefined)?.updatedAt\n ?? (data?.company as { updated_at?: string | null } | undefined)?.updated_at\n ?? null,\n data: data as Record<string, unknown> | null,\n path: pathname,\n }),\n )\n\n const handleEditActivity = React.useCallback((activity: { id: string; interactionType?: string; title?: string | null; body?: string | null; scheduledAt?: string | null; occurredAt?: string | null; [key: string]: unknown }) => {\n const raw = activity as Record<string, unknown>\n const durationValue = typeof raw.duration === 'number'\n ? raw.duration\n : typeof raw.durationMinutes === 'number'\n ? raw.durationMinutes as number\n : null\n // Forward `customValues` so per-type chip state (callPhoneNumber, callDirection,\n // taskPriority, \u2026) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the original\n // moment instead of \"today\" (#1807 prefill).\n const editPayload = {\n id: activity.id,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt as string : typeof raw.updated_at === 'string' ? raw.updated_at as string : null,\n interactionType: typeof activity.interactionType === 'string' ? activity.interactionType : undefined,\n title: typeof activity.title === 'string' ? activity.title : null,\n body: typeof activity.body === 'string' ? activity.body : null,\n scheduledAt: typeof activity.scheduledAt === 'string' ? activity.scheduledAt : null,\n occurredAt: typeof activity.occurredAt === 'string' ? activity.occurredAt : null,\n durationMinutes: durationValue,\n location: typeof raw.location === 'string' ? raw.location as string : null,\n allDay: typeof raw.allDay === 'boolean' ? raw.allDay as boolean : null,\n recurrenceRule: typeof raw.recurrenceRule === 'string' ? raw.recurrenceRule as string : null,\n recurrenceEnd: typeof raw.recurrenceEnd === 'string' ? raw.recurrenceEnd as string : null,\n participants: Array.isArray(raw.participants) ? raw.participants as ScheduleActivityEditData['participants'] : null,\n reminderMinutes: typeof raw.reminderMinutes === 'number' ? raw.reminderMinutes as number : null,\n visibility: typeof raw.visibility === 'string' ? raw.visibility as string : null,\n linkedEntities: Array.isArray(raw.linkedEntities) ? raw.linkedEntities as ScheduleActivityEditData['linkedEntities'] : null,\n guestPermissions: raw.guestPermissions && typeof raw.guestPermissions === 'object'\n ? raw.guestPermissions as ScheduleActivityEditData['guestPermissions']\n : null,\n customValues: raw.customValues && typeof raw.customValues === 'object'\n ? raw.customValues as Record<string, unknown>\n : null,\n phoneNumber: typeof raw.phoneNumber === 'string' ? raw.phoneNumber as string : null,\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null }\n setScheduleEditData(editPayload)\n setScheduleDialogOpen(true)\n }, [])\n\n const openNewScheduleDialog = React.useCallback(() => {\n setScheduleEditData(null)\n setScheduleDialogOpen(true)\n }, [])\n\n const handleAddActivity = React.useCallback((kind: 'meeting' | 'call' | 'task' | 'email') => {\n setScheduleEditData({\n id: '',\n interactionType: kind,\n title: null,\n body: null,\n scheduledAt: null,\n durationMinutes: null,\n location: null,\n allDay: null,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: null,\n visibility: null,\n linkedEntities: null,\n guestPermissions: null,\n })\n setScheduleDialogOpen(true)\n }, [])\n\n // Injected tabs from UMES\n const { widgets: injectedTabWidgets } = useInjectionWidgets('detail:customers.company:tabs', {\n context: injectionContext,\n triggerOnLoad: true,\n })\n\n const injectedTabs = React.useMemo(\n () =>\n (injectedTabWidgets ?? [])\n .filter((widget) => (widget.placement?.kind ?? 'tab') === 'tab')\n .map((widget) => {\n const tabId = widget.placement?.groupId ?? widget.widgetId\n const label = widget.placement?.groupLabel ?? widget.module.metadata.title ?? tabId\n const priority = typeof widget.placement?.priority === 'number' ? widget.placement.priority : 0\n const render = () => (\n <widget.module.Widget\n context={injectionContext}\n data={data}\n onDataChange={(next: unknown) => setData(next as CompanyOverview)}\n />\n )\n return { id: tabId, label, priority, render }\n })\n .sort((a, b) => b.priority - a.priority),\n [data, injectedTabWidgets, injectionContext],\n )\n\n const injectedTabMap = React.useMemo(() => new Map(injectedTabs.map((tab) => [tab.id, tab.render])), [injectedTabs])\n\n // Tags\n const handleTagsChange = React.useCallback((nextTags: TagSummary[]) => {\n setData((prev) => (prev ? { ...prev, tags: nextTags } : prev))\n }, [])\n const tagsSectionControllerRef = React.useRef<TagsSectionController | null>(null)\n\n // Section action (for tabs that expose add/create buttons)\n const handleSectionActionChange = React.useCallback((action: SectionAction | null) => {\n setSectionAction((prev) => (action !== null ? action : prev))\n }, [])\n\n const handleSectionAction = React.useCallback(() => {\n if (!sectionAction || sectionAction.disabled) return\n sectionAction.onClick()\n }, [sectionAction])\n\n React.useEffect(() => {\n setSectionAction(null)\n }, [activeTab])\n\n // Deals scope\n const dealsScope = React.useMemo(\n () => (currentCompanyId ? ({ kind: 'company', entityId: currentCompanyId } as const) : null),\n [currentCompanyId],\n )\n\n // Delete handler (shared between header and form)\n const handleDelete = React.useCallback(async () => {\n const companyId = data?.company?.id ?? ''\n if (!companyId) return\n const approved = await confirm({\n title: t('customers.companies.detail.deleteConfirmTitle', 'Delete company?'),\n description: t('customers.companies.detail.deleteConfirmDescription', 'This action cannot be undone.'),\n confirmText: t('customers.companies.detail.actions.delete', 'Delete company'),\n cancelText: t('customers.companies.detail.actions.cancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!approved) return\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader((data?.company as { updatedAt?: string } | undefined)?.updatedAt),\n () => deleteCrud('customers/companies', { id: companyId }),\n ),\n { id: companyId, operation: 'deleteCompany' },\n )\n } catch (err) {\n // The guarded mutation already routes a 409 to the unified conflict bar;\n // surface any other server error (e.g. \"Cannot delete company: linked\n // deals\u2026\") as a flash instead of letting it crash the page.\n if (!surfaceRecordConflict(err, t)) {\n flash(\n err instanceof Error && err.message.trim().length > 0\n ? err.message\n : t('customers.companies.detail.deleteError', 'Failed to delete company.'),\n 'error',\n )\n }\n return\n }\n flash(t('customers.companies.list.deleteSuccess', 'Company deleted.'), 'success')\n router.push('/backend/customers/companies')\n }, [confirm, data?.company?.id, router, runMutationWithContext, t])\n\n // Form submit handler (lifted from CompanyDataTab)\n const handleFormSubmit = React.useCallback(\n async (values: CompanyEditFormValues) => {\n setIsSaving(true)\n try {\n let payload: Record<string, unknown>\n try {\n payload = buildCompanyEditPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error) {\n if (err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.companies.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n if (err.message === 'ANNUAL_REVENUE_INVALID') {\n const message = t('customers.companies.form.annualRevenue.error')\n throw createCrudFormError(message, { annualRevenue: message })\n }\n }\n throw err\n }\n await updateCrud('customers/companies', payload)\n flash(t('customers.companies.form.updateSuccess', 'Company updated.'), 'success')\n await loadData()\n } finally {\n setIsSaving(false)\n }\n },\n [loadData, organizationId, t],\n )\n\n // Save handler (triggers form submit via ref)\n const handleHeaderSave = React.useCallback(() => {\n const form = formWrapperRef.current?.querySelector('form')\n if (form) form.requestSubmit()\n }, [])\n\n // Loading / error states\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.companies.detail.loading', 'Loading company\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('customers.companies.detail.error.notFound', 'Company not found.')}\n backHref=\"/backend/customers/companies\"\n backLabel={t('customers.companies.detail.actions.backToList', 'Back to companies')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data?.company?.id) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error ?? t('customers.companies.detail.error.load', 'Failed to load company.')}\n action={(\n <Button asChild variant=\"outline\">\n <Link href=\"/backend/customers/companies\">\n {t('customers.companies.detail.actions.backToList', 'Back to companies')}\n </Link>\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n const companyId = data.company.id\n const useCanonicalInteractions = data.interactionMode === 'canonical'\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-4\">\n {/* UMES header injection */}\n <InjectionSpot spotId=\"detail:customers.company:header\" context={injectionContext} data={data} />\n <InjectionSpot spotId=\"detail:customers.company:status-badges\" context={injectionContext} data={data} />\n\n {/* Persistent company header */}\n <CompanyDetailHeader\n data={data}\n onTagsChange={handleTagsChange}\n tagsSectionControllerRef={tagsSectionControllerRef}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n onDataReload={() => { loadData().catch((err) => console.warn('[companies-v2] onDataReload failed', err)) }}\n />\n\n {/* KPI bar \u2014 always visible above zones */}\n <CompanyKpiBar data={data} />\n\n {/* Two-zone layout: zone1 = form, zone2 = tabs */}\n <CollapsibleZoneLayout\n pageType=\"company-v2\"\n entityName={companyName}\n isDirty={isDirty}\n sections={zoneSections}\n zone1={\n <div ref={formWrapperRef}>\n <CrudForm<CompanyEditFormValues>\n embedded\n trackDirtyWhenEmbedded\n injectionSpotId=\"crud-form:customers.company\"\n entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}\n schema={formSchema}\n fields={formFields}\n groups={formGroups}\n initialValues={initialValues}\n onSubmit={handleFormSubmit}\n onDelete={handleDelete}\n optimisticLockUpdatedAt={(data?.company as { updatedAt?: string } | undefined)?.updatedAt}\n hideFooterActions\n collapsibleGroups={{ pageType: 'company-v2', chevronPosition: 'right' }}\n sortableGroups={{ pageType: 'company-v2' }}\n onDirtyChange={setIsDirty}\n />\n </div>\n }\n zone2={\n <CompanyDetailTabs\n activeTab={activeTab}\n onTabChange={setActiveTab}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts?.people ?? 0}\n dealsCount={dealCount}\n activitiesCount={data.counts?.activities ?? 0}\n sectionAction={sectionAction}\n >\n {activeTab === 'people' && (\n <CompanyPeopleSection\n companyId={companyId}\n companyName={companyDisplayName}\n initialPeople={[]}\n addActionLabel={t('customers.companies.detail.people.add', 'Add person')}\n emptyLabel={t('customers.companies.detail.people.empty', 'No people linked to this company yet.')}\n emptyState={{\n title: t('customers.companies.detail.emptyState.people.title', 'Build the account team'),\n actionLabel: t('customers.companies.detail.emptyState.people.action', 'Create person'),\n }}\n onActionChange={handleSectionActionChange}\n translator={detailTranslator}\n runGuardedMutation={runMutationWithContext}\n onPeopleChange={(next) => {\n setData((prev) => {\n if (!prev) return prev\n const nextCount = next.length\n return {\n ...prev,\n people: next,\n counts: prev.counts ? { ...prev.counts, people: nextCount } : prev.counts,\n }\n })\n }}\n />\n )}\n\n {activeTab === 'deals' && (\n <DealsSection\n scope={dealsScope}\n emptyLabel={t('customers.companies.detail.empty.deals', 'No deals linked to this company.')}\n addActionLabel={t('customers.companies.detail.actions.addDeal', 'Add deal')}\n emptyState={{\n title: t('customers.companies.detail.emptyState.deals.title', 'No deals yet'),\n actionLabel: t('customers.companies.detail.emptyState.deals.action', 'Create a deal'),\n }}\n onActionChange={handleSectionActionChange}\n translator={detailTranslator}\n runGuardedMutation={runMutationWithContext}\n onCountDelta={(delta) => setDealCount((current) => Math.max(0, current + delta))}\n />\n )}\n\n {activeTab === 'activity-log' && (\n <ActivityLogTab\n entityId={companyId}\n plannedActivities={plannedActivities}\n onActivityCreated={handleActivityCreated}\n onScheduleRequested={openNewScheduleDialog}\n onAddActivity={handleAddActivity}\n onMarkDone={handleMarkDone}\n onEditActivity={handleEditActivity}\n onCancelActivity={handleCancelActivity}\n runGuardedMutation={runMutationWithContext}\n refreshKey={activityRefreshKey}\n useCanonicalInteractions={useCanonicalInteractions}\n />\n )}\n\n {activeTab === 'changelog' && companyId && (\n <ChangelogTab entityId={companyId} entityType=\"company\" />\n )}\n\n {activeTab === 'files' && (\n <AttachmentsSection\n entityId={E.customers.customer_entity}\n recordId={companyId}\n title={t('customers.companies.detail.tabs.files', 'Files')}\n description={t('customers.companies.detail.files.subtitle', 'Upload and manage files linked to this company.')}\n />\n )}\n\n {/* Injected tabs from UMES */}\n {injectedTabMap.has(activeTab) && injectedTabMap.get(activeTab)!()}\n </CompanyDetailTabs>\n }\n />\n\n {/* UMES footer injection */}\n <InjectionSpot spotId=\"detail:customers.company:footer\" context={injectionContext} data={data} />\n\n {/* Schedule Activity Dialog */}\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={() => { setScheduleDialogOpen(false); setScheduleEditData(null) }}\n entityId={companyId}\n entityName={companyName}\n entityType=\"company\"\n onActivityCreated={handleActivityCreated}\n editData={scheduleEditData}\n />\n {ConfirmDialogElement}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["companyId"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
3
|
+
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
3
4
|
import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
4
|
-
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
5
7
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
8
|
function normalizePersonAssociationRecord(record, fallbackId) {
|
|
7
9
|
const displayName = typeof record.displayName === "string" && record.displayName.trim().length ? record.displayName.trim() : typeof record.display_name === "string" && record.display_name.trim().length ? record.display_name.trim() : null;
|
|
@@ -36,7 +38,8 @@ function useDealAssociations({
|
|
|
36
38
|
currentDealId,
|
|
37
39
|
data,
|
|
38
40
|
setData,
|
|
39
|
-
runMutationWithContext
|
|
41
|
+
runMutationWithContext,
|
|
42
|
+
onRefresh
|
|
40
43
|
}) {
|
|
41
44
|
const t = useT();
|
|
42
45
|
const [peopleEditorIds, setPeopleEditorIds] = React.useState([]);
|
|
@@ -169,7 +172,10 @@ function useDealAssociations({
|
|
|
169
172
|
setPeopleSaving(true);
|
|
170
173
|
try {
|
|
171
174
|
await runMutationWithContext(
|
|
172
|
-
() =>
|
|
175
|
+
() => withScopedApiRequestHeaders(
|
|
176
|
+
buildOptimisticLockHeader(data?.deal.updatedAt),
|
|
177
|
+
() => updateCrud("customers/deals", { id: currentDealId, personIds: nextIds })
|
|
178
|
+
),
|
|
173
179
|
{ id: currentDealId, personIds: nextIds, operation: "updateDealPeople" }
|
|
174
180
|
);
|
|
175
181
|
const nextPeople = await loadPeopleAssociations(nextIds.slice(0, 3));
|
|
@@ -181,7 +187,7 @@ function useDealAssociations({
|
|
|
181
187
|
counts: { ...prev.counts, people: nextIds.length }
|
|
182
188
|
} : prev
|
|
183
189
|
);
|
|
184
|
-
} catch {
|
|
190
|
+
} catch (error) {
|
|
185
191
|
setPeopleEditorIds(previousIds);
|
|
186
192
|
setData(
|
|
187
193
|
(prev) => prev ? {
|
|
@@ -191,12 +197,14 @@ function useDealAssociations({
|
|
|
191
197
|
counts: { ...prev.counts, people: previousIds.length }
|
|
192
198
|
} : prev
|
|
193
199
|
);
|
|
194
|
-
|
|
200
|
+
if (!surfaceRecordConflict(error, t, { onRefresh: onRefresh ?? null })) {
|
|
201
|
+
flash(t("customers.deals.detail.peopleUpdateError", "Failed to update linked people."), "error");
|
|
202
|
+
}
|
|
195
203
|
} finally {
|
|
196
204
|
setPeopleSaving(false);
|
|
197
205
|
}
|
|
198
206
|
},
|
|
199
|
-
[currentDealId, data?.people, loadPeopleAssociations, peopleEditorIds, runMutationWithContext, setData, t]
|
|
207
|
+
[currentDealId, data?.deal.updatedAt, data?.people, loadPeopleAssociations, onRefresh, peopleEditorIds, runMutationWithContext, setData, t]
|
|
200
208
|
);
|
|
201
209
|
const handleCompaniesAssociationsChange = React.useCallback(
|
|
202
210
|
async (nextIds) => {
|
|
@@ -208,7 +216,10 @@ function useDealAssociations({
|
|
|
208
216
|
setCompaniesSaving(true);
|
|
209
217
|
try {
|
|
210
218
|
await runMutationWithContext(
|
|
211
|
-
() =>
|
|
219
|
+
() => withScopedApiRequestHeaders(
|
|
220
|
+
buildOptimisticLockHeader(data?.deal.updatedAt),
|
|
221
|
+
() => updateCrud("customers/deals", { id: currentDealId, companyIds: nextIds })
|
|
222
|
+
),
|
|
212
223
|
{ id: currentDealId, companyIds: nextIds, operation: "updateDealCompanies" }
|
|
213
224
|
);
|
|
214
225
|
const nextCompanies = await loadCompanyAssociations(nextIds.slice(0, 3));
|
|
@@ -220,7 +231,7 @@ function useDealAssociations({
|
|
|
220
231
|
counts: { ...prev.counts, companies: nextIds.length }
|
|
221
232
|
} : prev
|
|
222
233
|
);
|
|
223
|
-
} catch {
|
|
234
|
+
} catch (error) {
|
|
224
235
|
setCompaniesEditorIds(previousIds);
|
|
225
236
|
setData(
|
|
226
237
|
(prev) => prev ? {
|
|
@@ -230,7 +241,9 @@ function useDealAssociations({
|
|
|
230
241
|
counts: { ...prev.counts, companies: previousIds.length }
|
|
231
242
|
} : prev
|
|
232
243
|
);
|
|
233
|
-
|
|
244
|
+
if (!surfaceRecordConflict(error, t, { onRefresh: onRefresh ?? null })) {
|
|
245
|
+
flash(t("customers.deals.detail.companiesUpdateError", "Failed to update linked companies."), "error");
|
|
246
|
+
}
|
|
234
247
|
} finally {
|
|
235
248
|
setCompaniesSaving(false);
|
|
236
249
|
}
|
|
@@ -239,7 +252,9 @@ function useDealAssociations({
|
|
|
239
252
|
companiesEditorIds,
|
|
240
253
|
currentDealId,
|
|
241
254
|
data?.companies,
|
|
255
|
+
data?.deal.updatedAt,
|
|
242
256
|
loadCompanyAssociations,
|
|
257
|
+
onRefresh,
|
|
243
258
|
runMutationWithContext,
|
|
244
259
|
setData,
|
|
245
260
|
t
|
package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/hooks/useDealAssociations.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n CompanyAssociationApiRecord,\n DealAssociation,\n DealDetailPayload,\n GuardedMutationRunner,\n PersonAssociationApiRecord,\n} from './types'\n\nexport function normalizePersonAssociationRecord(\n record: PersonAssociationApiRecord,\n fallbackId: string,\n): DealAssociation {\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n const email =\n typeof record.primaryEmail === 'string' && record.primaryEmail.trim().length\n ? record.primaryEmail.trim()\n : typeof record.primary_email === 'string' && record.primary_email.trim().length\n ? record.primary_email.trim()\n : null\n const phone =\n typeof record.primaryPhone === 'string' && record.primaryPhone.trim().length\n ? record.primaryPhone.trim()\n : typeof record.primary_phone === 'string' && record.primary_phone.trim().length\n ? record.primary_phone.trim()\n : null\n const profile = record.personProfile ?? record.person_profile ?? null\n const jobTitle =\n profile && typeof profile.jobTitle === 'string' && profile.jobTitle.trim().length\n ? profile.jobTitle.trim()\n : null\n return {\n id: typeof record.id === 'string' ? record.id : fallbackId,\n label: displayName ?? email ?? phone ?? fallbackId,\n subtitle: jobTitle ?? email ?? phone ?? null,\n kind: 'person',\n }\n}\n\nexport function normalizeCompanyAssociationRecord(\n record: CompanyAssociationApiRecord,\n fallbackId: string,\n): DealAssociation {\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n const profile = record.companyProfile ?? record.company_profile ?? null\n const domain =\n typeof record.domain === 'string' && record.domain.trim().length\n ? record.domain.trim()\n : profile && typeof profile.domain === 'string' && profile.domain.trim().length\n ? profile.domain.trim()\n : null\n const website =\n typeof record.websiteUrl === 'string' && record.websiteUrl.trim().length\n ? record.websiteUrl.trim()\n : typeof record.website_url === 'string' && record.website_url.trim().length\n ? record.website_url.trim()\n : profile && typeof profile.websiteUrl === 'string' && profile.websiteUrl.trim().length\n ? profile.websiteUrl.trim()\n : null\n return {\n id: typeof record.id === 'string' ? record.id : fallbackId,\n label: displayName ?? domain ?? website ?? fallbackId,\n subtitle: domain ?? website ?? null,\n kind: 'company',\n }\n}\n\nfunction sameIdList(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n return left.every((value, index) => value === right[index])\n}\n\ntype LinkedPageResult = {\n items: DealAssociation[]\n totalPages: number\n total: number\n}\n\ntype UseDealAssociationsOptions = {\n currentDealId: string | null\n data: DealDetailPayload | null\n setData: React.Dispatch<React.SetStateAction<DealDetailPayload | null>>\n runMutationWithContext: GuardedMutationRunner\n}\n\ntype UseDealAssociationsResult = {\n peopleEditorIds: string[]\n companiesEditorIds: string[]\n peopleSaving: boolean\n companiesSaving: boolean\n handlePeopleAssociationsChange: (nextIds: string[]) => Promise<void>\n handleCompaniesAssociationsChange: (nextIds: string[]) => Promise<void>\n loadLinkedPeoplePage: (page: number, query: string) => Promise<LinkedPageResult>\n loadLinkedCompaniesPage: (page: number, query: string) => Promise<LinkedPageResult>\n}\n\nexport function useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n}: UseDealAssociationsOptions): UseDealAssociationsResult {\n const t = useT()\n const [peopleEditorIds, setPeopleEditorIds] = React.useState<string[]>([])\n const [companiesEditorIds, setCompaniesEditorIds] = React.useState<string[]>([])\n const [peopleSaving, setPeopleSaving] = React.useState(false)\n const [companiesSaving, setCompaniesSaving] = React.useState(false)\n\n React.useEffect(() => {\n setPeopleEditorIds(data?.linkedPersonIds ?? [])\n setCompaniesEditorIds(data?.linkedCompanyIds ?? [])\n }, [data?.linkedCompanyIds, data?.linkedPersonIds])\n\n const loadPeopleAssociations = React.useCallback(async (ids: string[]): Promise<DealAssociation[]> => {\n const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)))\n if (!uniqueIds.length) return []\n try {\n const params = new URLSearchParams({\n ids: uniqueIds.join(','),\n pageSize: String(Math.max(uniqueIds.length, 1)),\n })\n const payload = await readApiResultOrThrow<{ items?: PersonAssociationApiRecord[] }>(\n `/api/customers/people?${params.toString()}`,\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const byId = new Map<string, PersonAssociationApiRecord>()\n items.forEach((record) => {\n if (record && typeof record.id === 'string') byId.set(record.id, record)\n })\n return uniqueIds.map((personId) => {\n const record = byId.get(personId)\n return record\n ? normalizePersonAssociationRecord(record, personId)\n : {\n id: personId,\n label: personId,\n subtitle: null,\n kind: 'person' as const,\n }\n })\n } catch {\n return uniqueIds.map((personId) => ({\n id: personId,\n label: personId,\n subtitle: null,\n kind: 'person' as const,\n }))\n }\n }, [])\n\n const loadCompanyAssociations = React.useCallback(async (ids: string[]): Promise<DealAssociation[]> => {\n const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)))\n if (!uniqueIds.length) return []\n try {\n const params = new URLSearchParams({\n ids: uniqueIds.join(','),\n pageSize: String(Math.max(uniqueIds.length, 1)),\n })\n const payload = await readApiResultOrThrow<{ items?: CompanyAssociationApiRecord[] }>(\n `/api/customers/companies?${params.toString()}`,\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const byId = new Map<string, CompanyAssociationApiRecord>()\n items.forEach((record) => {\n if (record && typeof record.id === 'string') byId.set(record.id, record)\n })\n return uniqueIds.map((companyId) => {\n const record = byId.get(companyId)\n return record\n ? normalizeCompanyAssociationRecord(record, companyId)\n : {\n id: companyId,\n label: companyId,\n subtitle: null,\n kind: 'company' as const,\n }\n })\n } catch {\n return uniqueIds.map((companyId) => ({\n id: companyId,\n label: companyId,\n subtitle: null,\n kind: 'company' as const,\n }))\n }\n }, [])\n\n const loadLinkedPeoplePage = React.useCallback(\n async (page: number, query: string): Promise<LinkedPageResult> => {\n if (!currentDealId) {\n return { items: [], totalPages: 1, total: 0 }\n }\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sort: 'name-asc',\n })\n if (query.trim().length > 0) {\n params.set('search', query.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: DealAssociation[]\n total?: number\n totalPages?: number\n }>(`/api/customers/deals/${encodeURIComponent(currentDealId)}/people?${params.toString()}`)\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n total: typeof payload.total === 'number' ? payload.total : 0,\n }\n },\n [currentDealId],\n )\n\n const loadLinkedCompaniesPage = React.useCallback(\n async (page: number, query: string): Promise<LinkedPageResult> => {\n if (!currentDealId) {\n return { items: [], totalPages: 1, total: 0 }\n }\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sort: 'name-asc',\n })\n if (query.trim().length > 0) {\n params.set('search', query.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: DealAssociation[]\n total?: number\n totalPages?: number\n }>(`/api/customers/deals/${encodeURIComponent(currentDealId)}/companies?${params.toString()}`)\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n total: typeof payload.total === 'number' ? payload.total : 0,\n }\n },\n [currentDealId],\n )\n\n const handlePeopleAssociationsChange = React.useCallback(\n async (nextIds: string[]) => {\n if (!currentDealId) return\n if (sameIdList(nextIds, peopleEditorIds)) return\n const previousIds = peopleEditorIds\n const previousPeople = data?.people ?? []\n setPeopleEditorIds(nextIds)\n setPeopleSaving(true)\n try {\n await runMutationWithContext(\n () => updateCrud('customers/deals', { id: currentDealId, personIds: nextIds }),\n { id: currentDealId, personIds: nextIds, operation: 'updateDealPeople' },\n )\n const nextPeople = await loadPeopleAssociations(nextIds.slice(0, 3))\n setData((prev) =>\n prev\n ? {\n ...prev,\n people: nextPeople,\n linkedPersonIds: nextIds,\n counts: { ...prev.counts, people: nextIds.length },\n }\n : prev,\n )\n } catch {\n setPeopleEditorIds(previousIds)\n setData((prev) =>\n prev\n ? {\n ...prev,\n people: previousPeople,\n linkedPersonIds: previousIds,\n counts: { ...prev.counts, people: previousIds.length },\n }\n : prev,\n )\n flash(t('customers.deals.detail.peopleUpdateError', 'Failed to update linked people.'), 'error')\n } finally {\n setPeopleSaving(false)\n }\n },\n [currentDealId, data?.people, loadPeopleAssociations, peopleEditorIds, runMutationWithContext, setData, t],\n )\n\n const handleCompaniesAssociationsChange = React.useCallback(\n async (nextIds: string[]) => {\n if (!currentDealId) return\n if (sameIdList(nextIds, companiesEditorIds)) return\n const previousIds = companiesEditorIds\n const previousCompanies = data?.companies ?? []\n setCompaniesEditorIds(nextIds)\n setCompaniesSaving(true)\n try {\n await runMutationWithContext(\n () => updateCrud('customers/deals', { id: currentDealId, companyIds: nextIds }),\n { id: currentDealId, companyIds: nextIds, operation: 'updateDealCompanies' },\n )\n const nextCompanies = await loadCompanyAssociations(nextIds.slice(0, 3))\n setData((prev) =>\n prev\n ? {\n ...prev,\n companies: nextCompanies,\n linkedCompanyIds: nextIds,\n counts: { ...prev.counts, companies: nextIds.length },\n }\n : prev,\n )\n } catch {\n setCompaniesEditorIds(previousIds)\n setData((prev) =>\n prev\n ? {\n ...prev,\n companies: previousCompanies,\n linkedCompanyIds: previousIds,\n counts: { ...prev.counts, companies: previousIds.length },\n }\n : prev,\n )\n flash(t('customers.deals.detail.companiesUpdateError', 'Failed to update linked companies.'), 'error')\n } finally {\n setCompaniesSaving(false)\n }\n },\n [\n companiesEditorIds,\n currentDealId,\n data?.companies,\n loadCompanyAssociations,\n runMutationWithContext,\n setData,\n t,\n ],\n )\n\n return {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n CompanyAssociationApiRecord,\n DealAssociation,\n DealDetailPayload,\n GuardedMutationRunner,\n PersonAssociationApiRecord,\n} from './types'\n\nexport function normalizePersonAssociationRecord(\n record: PersonAssociationApiRecord,\n fallbackId: string,\n): DealAssociation {\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n const email =\n typeof record.primaryEmail === 'string' && record.primaryEmail.trim().length\n ? record.primaryEmail.trim()\n : typeof record.primary_email === 'string' && record.primary_email.trim().length\n ? record.primary_email.trim()\n : null\n const phone =\n typeof record.primaryPhone === 'string' && record.primaryPhone.trim().length\n ? record.primaryPhone.trim()\n : typeof record.primary_phone === 'string' && record.primary_phone.trim().length\n ? record.primary_phone.trim()\n : null\n const profile = record.personProfile ?? record.person_profile ?? null\n const jobTitle =\n profile && typeof profile.jobTitle === 'string' && profile.jobTitle.trim().length\n ? profile.jobTitle.trim()\n : null\n return {\n id: typeof record.id === 'string' ? record.id : fallbackId,\n label: displayName ?? email ?? phone ?? fallbackId,\n subtitle: jobTitle ?? email ?? phone ?? null,\n kind: 'person',\n }\n}\n\nexport function normalizeCompanyAssociationRecord(\n record: CompanyAssociationApiRecord,\n fallbackId: string,\n): DealAssociation {\n const displayName =\n typeof record.displayName === 'string' && record.displayName.trim().length\n ? record.displayName.trim()\n : typeof record.display_name === 'string' && record.display_name.trim().length\n ? record.display_name.trim()\n : null\n const profile = record.companyProfile ?? record.company_profile ?? null\n const domain =\n typeof record.domain === 'string' && record.domain.trim().length\n ? record.domain.trim()\n : profile && typeof profile.domain === 'string' && profile.domain.trim().length\n ? profile.domain.trim()\n : null\n const website =\n typeof record.websiteUrl === 'string' && record.websiteUrl.trim().length\n ? record.websiteUrl.trim()\n : typeof record.website_url === 'string' && record.website_url.trim().length\n ? record.website_url.trim()\n : profile && typeof profile.websiteUrl === 'string' && profile.websiteUrl.trim().length\n ? profile.websiteUrl.trim()\n : null\n return {\n id: typeof record.id === 'string' ? record.id : fallbackId,\n label: displayName ?? domain ?? website ?? fallbackId,\n subtitle: domain ?? website ?? null,\n kind: 'company',\n }\n}\n\nfunction sameIdList(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n return left.every((value, index) => value === right[index])\n}\n\ntype LinkedPageResult = {\n items: DealAssociation[]\n totalPages: number\n total: number\n}\n\ntype UseDealAssociationsOptions = {\n currentDealId: string | null\n data: DealDetailPayload | null\n setData: React.Dispatch<React.SetStateAction<DealDetailPayload | null>>\n runMutationWithContext: GuardedMutationRunner\n /** Re-fetch the deal detail; wired into the conflict bar's refresh action on a 409. */\n onRefresh?: (() => void) | null\n}\n\ntype UseDealAssociationsResult = {\n peopleEditorIds: string[]\n companiesEditorIds: string[]\n peopleSaving: boolean\n companiesSaving: boolean\n handlePeopleAssociationsChange: (nextIds: string[]) => Promise<void>\n handleCompaniesAssociationsChange: (nextIds: string[]) => Promise<void>\n loadLinkedPeoplePage: (page: number, query: string) => Promise<LinkedPageResult>\n loadLinkedCompaniesPage: (page: number, query: string) => Promise<LinkedPageResult>\n}\n\nexport function useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n onRefresh,\n}: UseDealAssociationsOptions): UseDealAssociationsResult {\n const t = useT()\n const [peopleEditorIds, setPeopleEditorIds] = React.useState<string[]>([])\n const [companiesEditorIds, setCompaniesEditorIds] = React.useState<string[]>([])\n const [peopleSaving, setPeopleSaving] = React.useState(false)\n const [companiesSaving, setCompaniesSaving] = React.useState(false)\n\n React.useEffect(() => {\n setPeopleEditorIds(data?.linkedPersonIds ?? [])\n setCompaniesEditorIds(data?.linkedCompanyIds ?? [])\n }, [data?.linkedCompanyIds, data?.linkedPersonIds])\n\n const loadPeopleAssociations = React.useCallback(async (ids: string[]): Promise<DealAssociation[]> => {\n const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)))\n if (!uniqueIds.length) return []\n try {\n const params = new URLSearchParams({\n ids: uniqueIds.join(','),\n pageSize: String(Math.max(uniqueIds.length, 1)),\n })\n const payload = await readApiResultOrThrow<{ items?: PersonAssociationApiRecord[] }>(\n `/api/customers/people?${params.toString()}`,\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const byId = new Map<string, PersonAssociationApiRecord>()\n items.forEach((record) => {\n if (record && typeof record.id === 'string') byId.set(record.id, record)\n })\n return uniqueIds.map((personId) => {\n const record = byId.get(personId)\n return record\n ? normalizePersonAssociationRecord(record, personId)\n : {\n id: personId,\n label: personId,\n subtitle: null,\n kind: 'person' as const,\n }\n })\n } catch {\n return uniqueIds.map((personId) => ({\n id: personId,\n label: personId,\n subtitle: null,\n kind: 'person' as const,\n }))\n }\n }, [])\n\n const loadCompanyAssociations = React.useCallback(async (ids: string[]): Promise<DealAssociation[]> => {\n const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)))\n if (!uniqueIds.length) return []\n try {\n const params = new URLSearchParams({\n ids: uniqueIds.join(','),\n pageSize: String(Math.max(uniqueIds.length, 1)),\n })\n const payload = await readApiResultOrThrow<{ items?: CompanyAssociationApiRecord[] }>(\n `/api/customers/companies?${params.toString()}`,\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const byId = new Map<string, CompanyAssociationApiRecord>()\n items.forEach((record) => {\n if (record && typeof record.id === 'string') byId.set(record.id, record)\n })\n return uniqueIds.map((companyId) => {\n const record = byId.get(companyId)\n return record\n ? normalizeCompanyAssociationRecord(record, companyId)\n : {\n id: companyId,\n label: companyId,\n subtitle: null,\n kind: 'company' as const,\n }\n })\n } catch {\n return uniqueIds.map((companyId) => ({\n id: companyId,\n label: companyId,\n subtitle: null,\n kind: 'company' as const,\n }))\n }\n }, [])\n\n const loadLinkedPeoplePage = React.useCallback(\n async (page: number, query: string): Promise<LinkedPageResult> => {\n if (!currentDealId) {\n return { items: [], totalPages: 1, total: 0 }\n }\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sort: 'name-asc',\n })\n if (query.trim().length > 0) {\n params.set('search', query.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: DealAssociation[]\n total?: number\n totalPages?: number\n }>(`/api/customers/deals/${encodeURIComponent(currentDealId)}/people?${params.toString()}`)\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n total: typeof payload.total === 'number' ? payload.total : 0,\n }\n },\n [currentDealId],\n )\n\n const loadLinkedCompaniesPage = React.useCallback(\n async (page: number, query: string): Promise<LinkedPageResult> => {\n if (!currentDealId) {\n return { items: [], totalPages: 1, total: 0 }\n }\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sort: 'name-asc',\n })\n if (query.trim().length > 0) {\n params.set('search', query.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: DealAssociation[]\n total?: number\n totalPages?: number\n }>(`/api/customers/deals/${encodeURIComponent(currentDealId)}/companies?${params.toString()}`)\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n total: typeof payload.total === 'number' ? payload.total : 0,\n }\n },\n [currentDealId],\n )\n\n const handlePeopleAssociationsChange = React.useCallback(\n async (nextIds: string[]) => {\n if (!currentDealId) return\n if (sameIdList(nextIds, peopleEditorIds)) return\n const previousIds = peopleEditorIds\n const previousPeople = data?.people ?? []\n setPeopleEditorIds(nextIds)\n setPeopleSaving(true)\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(data?.deal.updatedAt),\n () => updateCrud('customers/deals', { id: currentDealId, personIds: nextIds }),\n ),\n { id: currentDealId, personIds: nextIds, operation: 'updateDealPeople' },\n )\n const nextPeople = await loadPeopleAssociations(nextIds.slice(0, 3))\n setData((prev) =>\n prev\n ? {\n ...prev,\n people: nextPeople,\n linkedPersonIds: nextIds,\n counts: { ...prev.counts, people: nextIds.length },\n }\n : prev,\n )\n } catch (error) {\n setPeopleEditorIds(previousIds)\n setData((prev) =>\n prev\n ? {\n ...prev,\n people: previousPeople,\n linkedPersonIds: previousIds,\n counts: { ...prev.counts, people: previousIds.length },\n }\n : prev,\n )\n // runMutationWithContext already surfaces the conflict bar on a 409; only\n // fall back to the generic flash when this is not a record conflict.\n if (!surfaceRecordConflict(error, t, { onRefresh: onRefresh ?? null })) {\n flash(t('customers.deals.detail.peopleUpdateError', 'Failed to update linked people.'), 'error')\n }\n } finally {\n setPeopleSaving(false)\n }\n },\n [currentDealId, data?.deal.updatedAt, data?.people, loadPeopleAssociations, onRefresh, peopleEditorIds, runMutationWithContext, setData, t],\n )\n\n const handleCompaniesAssociationsChange = React.useCallback(\n async (nextIds: string[]) => {\n if (!currentDealId) return\n if (sameIdList(nextIds, companiesEditorIds)) return\n const previousIds = companiesEditorIds\n const previousCompanies = data?.companies ?? []\n setCompaniesEditorIds(nextIds)\n setCompaniesSaving(true)\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(data?.deal.updatedAt),\n () => updateCrud('customers/deals', { id: currentDealId, companyIds: nextIds }),\n ),\n { id: currentDealId, companyIds: nextIds, operation: 'updateDealCompanies' },\n )\n const nextCompanies = await loadCompanyAssociations(nextIds.slice(0, 3))\n setData((prev) =>\n prev\n ? {\n ...prev,\n companies: nextCompanies,\n linkedCompanyIds: nextIds,\n counts: { ...prev.counts, companies: nextIds.length },\n }\n : prev,\n )\n } catch (error) {\n setCompaniesEditorIds(previousIds)\n setData((prev) =>\n prev\n ? {\n ...prev,\n companies: previousCompanies,\n linkedCompanyIds: previousIds,\n counts: { ...prev.counts, companies: previousIds.length },\n }\n : prev,\n )\n // runMutationWithContext already surfaces the conflict bar on a 409; only\n // fall back to the generic flash when this is not a record conflict.\n if (!surfaceRecordConflict(error, t, { onRefresh: onRefresh ?? null })) {\n flash(t('customers.deals.detail.companiesUpdateError', 'Failed to update linked companies.'), 'error')\n }\n } finally {\n setCompaniesSaving(false)\n }\n },\n [\n companiesEditorIds,\n currentDealId,\n data?.companies,\n data?.deal.updatedAt,\n loadCompanyAssociations,\n onRefresh,\n runMutationWithContext,\n setData,\n t,\n ],\n )\n\n return {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
3
|
-
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
3
|
+
import { readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
4
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
5
|
+
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
6
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
7
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
8
|
function useDealClosure({
|
|
5
9
|
currentDealId,
|
|
10
|
+
dealUpdatedAt,
|
|
6
11
|
runMutationWithContext,
|
|
7
12
|
confirmDiscardIfDirty,
|
|
8
13
|
onClosed
|
|
9
14
|
}) {
|
|
15
|
+
const t = useT();
|
|
10
16
|
const [lostDialogOpen, setLostDialogOpen] = React.useState(false);
|
|
11
17
|
const [wonPopupOpen, setWonPopupOpen] = React.useState(false);
|
|
12
18
|
const [lostPopupOpen, setLostPopupOpen] = React.useState(false);
|
|
@@ -26,43 +32,67 @@ function useDealClosure({
|
|
|
26
32
|
const handleWon = React.useCallback(async () => {
|
|
27
33
|
if (!currentDealId) return;
|
|
28
34
|
if (!await confirmDiscardIfDirty()) return;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
try {
|
|
36
|
+
await runMutationWithContext(
|
|
37
|
+
() => withScopedApiRequestHeaders(
|
|
38
|
+
buildOptimisticLockHeader(dealUpdatedAt),
|
|
39
|
+
() => updateCrud("customers/deals", { id: currentDealId, closureOutcome: "won", status: "win" })
|
|
40
|
+
),
|
|
41
|
+
{ id: currentDealId, closureOutcome: "won", status: "win", operation: "closeWon" }
|
|
42
|
+
);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (!surfaceRecordConflict(err, t, { onRefresh: () => {
|
|
45
|
+
void onClosed();
|
|
46
|
+
} })) {
|
|
47
|
+
flash(t("customers.deals.detail.closeWonError", "Failed to mark deal as won."), "error");
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
33
51
|
const stats = await fetchDealStats();
|
|
34
52
|
setWonStats(stats);
|
|
35
53
|
setWonPopupOpen(true);
|
|
36
54
|
await onClosed();
|
|
37
|
-
}, [confirmDiscardIfDirty, currentDealId, fetchDealStats, onClosed, runMutationWithContext]);
|
|
55
|
+
}, [confirmDiscardIfDirty, currentDealId, dealUpdatedAt, fetchDealStats, onClosed, runMutationWithContext, t]);
|
|
38
56
|
const handleLostConfirm = React.useCallback(
|
|
39
57
|
async (input) => {
|
|
40
58
|
if (!currentDealId) return;
|
|
41
59
|
if (!await confirmDiscardIfDirty()) return;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
try {
|
|
61
|
+
await runMutationWithContext(
|
|
62
|
+
() => withScopedApiRequestHeaders(
|
|
63
|
+
buildOptimisticLockHeader(dealUpdatedAt),
|
|
64
|
+
() => updateCrud("customers/deals", {
|
|
65
|
+
id: currentDealId,
|
|
66
|
+
closureOutcome: "lost",
|
|
67
|
+
status: "loose",
|
|
68
|
+
lossReasonId: input.lossReasonId,
|
|
69
|
+
lossNotes: input.lossNotes ?? null
|
|
70
|
+
})
|
|
71
|
+
),
|
|
72
|
+
{
|
|
73
|
+
id: currentDealId,
|
|
74
|
+
closureOutcome: "lost",
|
|
75
|
+
status: "loose",
|
|
76
|
+
lossReasonId: input.lossReasonId,
|
|
77
|
+
lossNotes: input.lossNotes ?? null,
|
|
78
|
+
operation: "closeLost"
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (!surfaceRecordConflict(err, t, { onRefresh: () => {
|
|
83
|
+
void onClosed();
|
|
84
|
+
} })) {
|
|
85
|
+
flash(t("customers.deals.detail.closeLostError", "Failed to mark deal as lost."), "error");
|
|
57
86
|
}
|
|
58
|
-
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
59
89
|
setLostDialogOpen(false);
|
|
60
90
|
const stats = await fetchDealStats();
|
|
61
91
|
setLostStats(stats);
|
|
62
92
|
setLostPopupOpen(true);
|
|
63
93
|
await onClosed();
|
|
64
94
|
},
|
|
65
|
-
[confirmDiscardIfDirty, currentDealId, fetchDealStats, onClosed, runMutationWithContext]
|
|
95
|
+
[confirmDiscardIfDirty, currentDealId, dealUpdatedAt, fetchDealStats, onClosed, runMutationWithContext, t]
|
|
66
96
|
);
|
|
67
97
|
const openLostDialog = React.useCallback(() => setLostDialogOpen(true), []);
|
|
68
98
|
const closeLostDialog = React.useCallback(() => setLostDialogOpen(false), []);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/hooks/useDealClosure.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { DealStatsPayload, GuardedMutationRunner } from './types'\n\ntype UseDealClosureOptions = {\n currentDealId: string | null\n runMutationWithContext: GuardedMutationRunner\n confirmDiscardIfDirty: () => Promise<boolean>\n onClosed: () => Promise<void>\n}\n\ntype UseDealClosureResult = {\n lostDialogOpen: boolean\n wonPopupOpen: boolean\n lostPopupOpen: boolean\n wonStats: DealStatsPayload | null\n lostStats: DealStatsPayload | null\n openLostDialog: () => void\n closeLostDialog: () => void\n closeWonPopup: () => void\n closeLostPopup: () => void\n handleWon: () => Promise<void>\n handleLostConfirm: (input: { lossReasonId: string; lossNotes?: string }) => Promise<void>\n}\n\nexport function useDealClosure({\n currentDealId,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed,\n}: UseDealClosureOptions): UseDealClosureResult {\n const [lostDialogOpen, setLostDialogOpen] = React.useState(false)\n const [wonPopupOpen, setWonPopupOpen] = React.useState(false)\n const [lostPopupOpen, setLostPopupOpen] = React.useState(false)\n const [wonStats, setWonStats] = React.useState<DealStatsPayload | null>(null)\n const [lostStats, setLostStats] = React.useState<DealStatsPayload | null>(null)\n\n const fetchDealStats = React.useCallback(async (): Promise<DealStatsPayload | null> => {\n if (!currentDealId) return null\n try {\n return await readApiResultOrThrow<DealStatsPayload>(\n `/api/customers/deals/${encodeURIComponent(currentDealId)}/stats`,\n )\n } catch (statsError) {\n console.error('customers.deals.detail.stats failed', statsError)\n return null\n }\n }, [currentDealId])\n\n const handleWon = React.useCallback(async () => {\n if (!currentDealId) return\n if (!(await confirmDiscardIfDirty())) return\n await runMutationWithContext(\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,kBAAkB;AAC3B,SAAS,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DealStatsPayload, GuardedMutationRunner } from './types'\n\ntype UseDealClosureOptions = {\n currentDealId: string | null\n dealUpdatedAt: string | null\n runMutationWithContext: GuardedMutationRunner\n confirmDiscardIfDirty: () => Promise<boolean>\n onClosed: () => Promise<void>\n}\n\ntype UseDealClosureResult = {\n lostDialogOpen: boolean\n wonPopupOpen: boolean\n lostPopupOpen: boolean\n wonStats: DealStatsPayload | null\n lostStats: DealStatsPayload | null\n openLostDialog: () => void\n closeLostDialog: () => void\n closeWonPopup: () => void\n closeLostPopup: () => void\n handleWon: () => Promise<void>\n handleLostConfirm: (input: { lossReasonId: string; lossNotes?: string }) => Promise<void>\n}\n\nexport function useDealClosure({\n currentDealId,\n dealUpdatedAt,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed,\n}: UseDealClosureOptions): UseDealClosureResult {\n const t = useT()\n const [lostDialogOpen, setLostDialogOpen] = React.useState(false)\n const [wonPopupOpen, setWonPopupOpen] = React.useState(false)\n const [lostPopupOpen, setLostPopupOpen] = React.useState(false)\n const [wonStats, setWonStats] = React.useState<DealStatsPayload | null>(null)\n const [lostStats, setLostStats] = React.useState<DealStatsPayload | null>(null)\n\n const fetchDealStats = React.useCallback(async (): Promise<DealStatsPayload | null> => {\n if (!currentDealId) return null\n try {\n return await readApiResultOrThrow<DealStatsPayload>(\n `/api/customers/deals/${encodeURIComponent(currentDealId)}/stats`,\n )\n } catch (statsError) {\n console.error('customers.deals.detail.stats failed', statsError)\n return null\n }\n }, [currentDealId])\n\n const handleWon = React.useCallback(async () => {\n if (!currentDealId) return\n if (!(await confirmDiscardIfDirty())) return\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealUpdatedAt),\n () => updateCrud('customers/deals', { id: currentDealId, closureOutcome: 'won', status: 'win' }),\n ),\n { id: currentDealId, closureOutcome: 'won', status: 'win', operation: 'closeWon' },\n )\n } catch (err) {\n if (!surfaceRecordConflict(err, t, { onRefresh: () => { void onClosed() } })) {\n flash(t('customers.deals.detail.closeWonError', 'Failed to mark deal as won.'), 'error')\n }\n return\n }\n const stats = await fetchDealStats()\n setWonStats(stats)\n setWonPopupOpen(true)\n await onClosed()\n }, [confirmDiscardIfDirty, currentDealId, dealUpdatedAt, fetchDealStats, onClosed, runMutationWithContext, t])\n\n const handleLostConfirm = React.useCallback(\n async (input: { lossReasonId: string; lossNotes?: string }) => {\n if (!currentDealId) return\n if (!(await confirmDiscardIfDirty())) return\n try {\n await runMutationWithContext(\n () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealUpdatedAt),\n () =>\n updateCrud('customers/deals', {\n id: currentDealId,\n closureOutcome: 'lost',\n status: 'loose',\n lossReasonId: input.lossReasonId,\n lossNotes: input.lossNotes ?? null,\n }),\n ),\n {\n id: currentDealId,\n closureOutcome: 'lost',\n status: 'loose',\n lossReasonId: input.lossReasonId,\n lossNotes: input.lossNotes ?? null,\n operation: 'closeLost',\n },\n )\n } catch (err) {\n if (!surfaceRecordConflict(err, t, { onRefresh: () => { void onClosed() } })) {\n flash(t('customers.deals.detail.closeLostError', 'Failed to mark deal as lost.'), 'error')\n }\n return\n }\n setLostDialogOpen(false)\n const stats = await fetchDealStats()\n setLostStats(stats)\n setLostPopupOpen(true)\n await onClosed()\n },\n [confirmDiscardIfDirty, currentDealId, dealUpdatedAt, fetchDealStats, onClosed, runMutationWithContext, t],\n )\n\n const openLostDialog = React.useCallback(() => setLostDialogOpen(true), [])\n const closeLostDialog = React.useCallback(() => setLostDialogOpen(false), [])\n const closeWonPopup = React.useCallback(() => setWonPopupOpen(false), [])\n const closeLostPopup = React.useCallback(() => setLostPopupOpen(false), [])\n\n return {\n lostDialogOpen,\n wonPopupOpen,\n lostPopupOpen,\n wonStats,\n lostStats,\n openLostDialog,\n closeLostDialog,\n closeWonPopup,\n closeLostPopup,\n handleWon,\n handleLostConfirm,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
3
3
|
import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
4
|
+
import { withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
6
|
+
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
4
7
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
8
|
function useDealPipeline({
|
|
6
9
|
currentDealId,
|
|
@@ -19,13 +22,20 @@ function useDealPipeline({
|
|
|
19
22
|
setIsStageSaving(true);
|
|
20
23
|
try {
|
|
21
24
|
await runMutationWithContext(
|
|
22
|
-
() =>
|
|
25
|
+
() => withScopedApiRequestHeaders(
|
|
26
|
+
buildOptimisticLockHeader(data.deal.updatedAt),
|
|
27
|
+
() => updateCrud("customers/deals", { id: currentDealId, pipelineStageId: nextStageId })
|
|
28
|
+
),
|
|
23
29
|
{ id: currentDealId, pipelineStageId: nextStageId, operation: "updateDealStage" }
|
|
24
30
|
);
|
|
25
31
|
flash(t("customers.deals.detail.stageUpdateSuccess", "Deal stage updated."), "success");
|
|
26
32
|
await onStageChanged();
|
|
27
|
-
} catch {
|
|
28
|
-
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (!surfaceRecordConflict(err, t, { onRefresh: () => {
|
|
35
|
+
void onStageChanged();
|
|
36
|
+
} })) {
|
|
37
|
+
flash(t("customers.deals.detail.stageUpdateError", "Failed to update deal stage."), "error");
|
|
38
|
+
}
|
|
29
39
|
} finally {
|
|
30
40
|
setIsStageSaving(false);
|
|
31
41
|
}
|