@open-mercato/core 0.6.8-develop.7008.1.1ab31c0ca9 → 0.6.8-develop.7012.1.1dbd6f5fbd
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.
|
@@ -189,7 +189,8 @@ function EntityMultiSelect({
|
|
|
189
189
|
search,
|
|
190
190
|
fetchByIds,
|
|
191
191
|
disabled = false,
|
|
192
|
-
autoFocus = false
|
|
192
|
+
autoFocus = false,
|
|
193
|
+
minQueryLength = 1
|
|
193
194
|
}) {
|
|
194
195
|
const [input, setInput] = React.useState("");
|
|
195
196
|
const [suggestions, setSuggestions] = React.useState([]);
|
|
@@ -237,11 +238,18 @@ function EntityMultiSelect({
|
|
|
237
238
|
setLoading(false);
|
|
238
239
|
return;
|
|
239
240
|
}
|
|
241
|
+
const term = input.trim();
|
|
242
|
+
if (term.length < minQueryLength) {
|
|
243
|
+
setSuggestions([]);
|
|
244
|
+
setLoading(false);
|
|
245
|
+
setError(null);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
240
248
|
let cancelled = false;
|
|
241
249
|
const handler = window.setTimeout(async () => {
|
|
242
250
|
setLoading(true);
|
|
243
251
|
try {
|
|
244
|
-
const results = await search(
|
|
252
|
+
const results = await search(term);
|
|
245
253
|
if (cancelled) return;
|
|
246
254
|
setSuggestions(results);
|
|
247
255
|
setCache((prev) => {
|
|
@@ -265,7 +273,7 @@ function EntityMultiSelect({
|
|
|
265
273
|
cancelled = true;
|
|
266
274
|
window.clearTimeout(handler);
|
|
267
275
|
};
|
|
268
|
-
}, [disabled, errorLabel, input, search]);
|
|
276
|
+
}, [disabled, errorLabel, input, minQueryLength, search]);
|
|
269
277
|
const filteredSuggestions = React.useMemo(
|
|
270
278
|
() => suggestions.filter((option) => !normalizedValue.includes(option.id)),
|
|
271
279
|
[normalizedValue, suggestions]
|
|
@@ -469,7 +477,8 @@ function DealPeopleSelector({
|
|
|
469
477
|
onChange,
|
|
470
478
|
options = [],
|
|
471
479
|
disabled = false,
|
|
472
|
-
autoFocus = false
|
|
480
|
+
autoFocus = false,
|
|
481
|
+
minQueryLength
|
|
473
482
|
}) {
|
|
474
483
|
const t = useT();
|
|
475
484
|
const { searchPeople, fetchPeopleByIds } = useDealAssociationLookups();
|
|
@@ -488,7 +497,8 @@ function DealPeopleSelector({
|
|
|
488
497
|
search: searchPeople,
|
|
489
498
|
fetchByIds: fetchPeopleByIds,
|
|
490
499
|
disabled,
|
|
491
|
-
autoFocus
|
|
500
|
+
autoFocus,
|
|
501
|
+
minQueryLength
|
|
492
502
|
}
|
|
493
503
|
);
|
|
494
504
|
}
|
|
@@ -496,7 +506,8 @@ function DealCompaniesSelector({
|
|
|
496
506
|
value,
|
|
497
507
|
onChange,
|
|
498
508
|
options = [],
|
|
499
|
-
disabled = false
|
|
509
|
+
disabled = false,
|
|
510
|
+
minQueryLength
|
|
500
511
|
}) {
|
|
501
512
|
const t = useT();
|
|
502
513
|
const { searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups();
|
|
@@ -514,7 +525,8 @@ function DealCompaniesSelector({
|
|
|
514
525
|
errorLabel: t("customers.deals.form.companies.error", "Failed to load companies."),
|
|
515
526
|
search: searchCompanies,
|
|
516
527
|
fetchByIds: fetchCompaniesByIds,
|
|
517
|
-
disabled
|
|
528
|
+
disabled,
|
|
529
|
+
minQueryLength
|
|
518
530
|
}
|
|
519
531
|
);
|
|
520
532
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/detail/DealForm.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { DEAL_DESCRIPTION_MAX_LENGTH } from '../../data/validators'\nimport { DictionarySelectField } from '../formConfig'\nimport { createDictionarySelectLabels } from './utils'\nimport { E } from '#generated/entities.ids.generated'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { useCurrencyDictionary } from './hooks/useCurrencyDictionary'\nimport { DictionaryEntrySelect } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { normalizeCustomFieldSubmitValue } from './customFieldUtils'\n\nexport type DealFormBaseValues = {\n title: string\n status?: string | null\n pipelineStage?: string | null\n pipelineId?: string | null\n pipelineStageId?: string | null\n valueAmount?: number | null\n valueCurrency?: string | null\n probability?: number | null\n expectedCloseAt?: string | null\n description?: string | null\n personIds?: string[]\n companyIds?: string[]\n}\n\ntype PipelineOption = { id: string; name: string; isDefault: boolean }\ntype PipelineStageOption = { id: string; label: string; order: number }\n\nexport type DealFormSubmitPayload = {\n base: DealFormBaseValues\n custom: Record<string, unknown>\n}\n\nexport type DealFormProps = {\n mode: 'create' | 'edit'\n initialValues?: Partial<DealFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: DealFormSubmitPayload) => Promise<void>\n onCancel: () => void\n onDelete?: () => Promise<void> | void\n submitLabel?: string\n cancelLabel?: string\n isSubmitting?: boolean\n embedded?: boolean\n trackDirtyWhenEmbedded?: boolean\n title?: string\n backHref?: string\n hideFooterActions?: boolean\n onDirtyChange?: (dirty: boolean) => void\n collapsibleGroups?: boolean | { pageType: string; chevronPosition?: 'left' | 'right' }\n sortableGroups?: boolean | { pageType: string }\n singleColumnGroups?: boolean\n showAssociationsGroup?: boolean\n showVersionHistory?: boolean\n showCancelAction?: boolean\n initialPipelineOptions?: PipelineOption[]\n initialPipelineStageOptions?: PipelineStageOption[]\n /**\n * Injection spot id for the form-scoped record_locks widget (e.g.\n * `customers.deal`). Mirrors how people-v2/companies-v2 mount their save-time\n * `crud-form:*` widget so a deal save conflict surfaces the merge dialog.\n */\n injectionSpotId?: string\n /** Optimistic-lock version (`deal.updatedAt`) for the embedded CrudForm. */\n optimisticLockUpdatedAt?: string | null\n}\n\ntype EntityOption = {\n id: string\n label: string\n subtitle?: string | null\n}\n\nexport type DealLookupOption = EntityOption\n\ntype EntityMultiSelectProps = {\n value: string[]\n onChange: (next: string[]) => void\n initialOptions?: EntityOption[]\n placeholder: string\n emptyLabel: string\n loadingLabel: string\n noResultsLabel: string\n removeLabel: string\n errorLabel: string\n search: (query: string) => Promise<EntityOption[]>\n fetchByIds: (ids: string[]) => Promise<EntityOption[]>\n disabled?: boolean\n autoFocus?: boolean\n}\n\nconst DEAL_ENTITY_IDS = [E.customers.customer_deal]\nconst CURRENCY_PRIORITY = ['EUR', 'USD', 'GBP', 'PLN'] as const\nconst PIPELINE_OPTIONS_TTL_MS = 60_000\nconst PIPELINE_STAGE_OPTIONS_TTL_MS = 30_000\n\ntype MetadataCacheEntry<T> = {\n expiresAt: number\n promise: Promise<T>\n}\n\nlet pipelineOptionsCache: MetadataCacheEntry<PipelineOption[]> | null = null\nconst pipelineStageOptionsCache = new Map<string, MetadataCacheEntry<PipelineStageOption[]>>()\n\nfunction isFreshCacheEntry<T>(entry: MetadataCacheEntry<T> | null | undefined): entry is MetadataCacheEntry<T> {\n return Boolean(entry && entry.expiresAt > Date.now())\n}\n\nfunction normalizePipelineOptions(options: PipelineOption[] | undefined): PipelineOption[] {\n const byId = new Map<string, PipelineOption>()\n for (const option of options ?? []) {\n if (!option.id) continue\n byId.set(option.id, {\n id: option.id,\n name: option.name,\n isDefault: option.isDefault === true,\n })\n }\n return Array.from(byId.values())\n}\n\nfunction mergePipelineOptions(seed: PipelineOption[], loaded: PipelineOption[]): PipelineOption[] {\n const byId = new Map<string, PipelineOption>()\n for (const option of seed) byId.set(option.id, option)\n for (const option of loaded) byId.set(option.id, option)\n return Array.from(byId.values())\n}\n\nfunction normalizePipelineStageOptions(options: PipelineStageOption[] | undefined): PipelineStageOption[] {\n return [...(options ?? [])]\n .filter((option) => option.id)\n .sort((left, right) => left.order - right.order)\n}\n\nasync function fetchPipelineOptions(): Promise<PipelineOption[]> {\n if (isFreshCacheEntry(pipelineOptionsCache)) return pipelineOptionsCache.promise\n const entry: MetadataCacheEntry<PipelineOption[]> = {\n expiresAt: Date.now() + PIPELINE_OPTIONS_TTL_MS,\n promise: apiCall<{ items: PipelineOption[] }>('/api/customers/pipelines')\n .then((call) => (call.ok && call.result?.items ? normalizePipelineOptions(call.result.items) : [])),\n }\n pipelineOptionsCache = entry\n try {\n return await entry.promise\n } catch (error) {\n if (pipelineOptionsCache === entry) pipelineOptionsCache = null\n throw error\n }\n}\n\nasync function fetchPipelineStageOptions(pipelineId: string): Promise<PipelineStageOption[]> {\n const cached = pipelineStageOptionsCache.get(pipelineId)\n if (isFreshCacheEntry(cached)) return cached.promise\n const entry: MetadataCacheEntry<PipelineStageOption[]> = {\n expiresAt: Date.now() + PIPELINE_STAGE_OPTIONS_TTL_MS,\n promise: apiCall<{ items: PipelineStageOption[] }>(`/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`)\n .then((call) => (call.ok && call.result?.items ? normalizePipelineStageOptions(call.result.items) : [])),\n }\n pipelineStageOptionsCache.set(pipelineId, entry)\n try {\n return await entry.promise\n } catch (error) {\n if (pipelineStageOptionsCache.get(pipelineId) === entry) pipelineStageOptionsCache.delete(pipelineId)\n throw error\n }\n}\n\nexport function resetDealPipelineMetadataCacheForTests() {\n pipelineOptionsCache = null\n pipelineStageOptionsCache.clear()\n}\n\nconst schema = z.object({\n title: z\n .string()\n .trim()\n .min(1, 'customers.people.detail.deals.titleRequired')\n .max(200, 'customers.people.detail.deals.titleTooLong'),\n status: z\n .string()\n .trim()\n .max(50, 'customers.people.detail.deals.statusTooLong')\n .optional(),\n pipelineStage: z\n .string()\n .trim()\n .max(100, 'customers.people.detail.deals.pipelineTooLong')\n .optional(),\n pipelineId: z.preprocess(\n (v) => (typeof v === 'string' && !v.trim() ? undefined : v),\n z.string().uuid('customers.people.detail.deals.pipelineIdInvalid').optional(),\n ),\n pipelineStageId: z.preprocess(\n (v) => (typeof v === 'string' && !v.trim() ? undefined : v),\n z.string().uuid('customers.people.detail.deals.pipelineStageIdInvalid').optional(),\n ),\n valueAmount: z\n .preprocess((value) => {\n if (value === '' || value === null || value === undefined) return undefined\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return undefined\n const parsed = Number(trimmed)\n if (Number.isNaN(parsed)) return value\n return parsed\n }\n return value\n }, z\n .number()\n .min(0, 'customers.people.detail.deals.valueInvalid')\n .optional())\n .optional(),\n valueCurrency: z\n .string()\n .transform((value) => value.trim().toUpperCase())\n .refine(\n (value) => !value || /^[A-Z]{3}$/.test(value),\n 'customers.people.detail.deals.currencyInvalid',\n )\n .optional(),\n probability: z\n .preprocess((value) => {\n if (value === '' || value === null || value === undefined) return undefined\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return undefined\n const parsed = Number(trimmed)\n if (Number.isNaN(parsed)) return value\n return parsed\n }\n return value\n }, z\n .number()\n .min(0, 'customers.people.detail.deals.probabilityInvalid')\n .max(100, 'customers.people.detail.deals.probabilityInvalid')\n .optional())\n .optional(),\n expectedCloseAt: z\n .string()\n .transform((value) => value.trim())\n .refine(\n (value) => {\n if (!value) return true\n const parsed = new Date(value)\n return !Number.isNaN(parsed.getTime())\n },\n 'customers.people.detail.deals.expectedCloseInvalid',\n )\n .optional(),\n description: z\n .string()\n .max(DEAL_DESCRIPTION_MAX_LENGTH, 'customers.people.detail.deals.descriptionTooLong')\n .optional(),\n personIds: z.array(z.string().trim().min(1)).optional(),\n companyIds: z.array(z.string().trim().min(1)).optional(),\n}).passthrough()\n\nexport const dealFormSchema = schema\n\nimport { toDateInputValue as toDateInputValueOrNull } from '@open-mercato/shared/lib/date/format'\n\nfunction toDateInputValue(value: string | null | undefined): string {\n return toDateInputValueOrNull(value) ?? ''\n}\n\nfunction normalizeCurrency(value: string | null | undefined): string {\n if (!value) return ''\n return value.trim().slice(0, 3).toUpperCase()\n}\n\nfunction sanitizeIdList(input: unknown): string[] {\n if (!Array.isArray(input)) return []\n const set = new Set<string>()\n input.forEach((candidate) => {\n if (typeof candidate !== 'string') return\n const trimmed = candidate.trim()\n if (!trimmed.length) return\n set.add(trimmed)\n })\n return Array.from(set)\n}\n\nfunction extractPersonOption(record: Record<string, unknown>): EntityOption | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\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 as string).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 as string).trim()\n : null\n const label = displayName ?? email ?? id\n const subtitle = email && email !== label ? email : null\n return { id, label, subtitle }\n}\n\nfunction extractCompanyOption(record: Record<string, unknown>): EntityOption | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\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 as string).trim()\n : null\n const domain =\n typeof record.domain === 'string' && record.domain.trim().length\n ? record.domain.trim()\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 as string).trim()\n : null\n const label = displayName ?? domain ?? id\n const subtitle = domain && domain !== label ? domain : null\n return { id, label, subtitle }\n}\n\nfunction EntityMultiSelect({\n value,\n onChange,\n initialOptions = [],\n placeholder,\n emptyLabel,\n loadingLabel,\n noResultsLabel,\n removeLabel,\n errorLabel,\n search,\n fetchByIds,\n disabled = false,\n autoFocus = false,\n}: EntityMultiSelectProps) {\n const [input, setInput] = React.useState('')\n const [suggestions, setSuggestions] = React.useState<EntityOption[]>([])\n const [cache, setCache] = React.useState<Map<string, EntityOption>>(() => new Map())\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const normalizedValue = React.useMemo(() => sanitizeIdList(value), [value])\n\n React.useEffect(() => {\n if (!initialOptions.length) return\n setCache((prev) => {\n const next = new Map(prev)\n initialOptions.forEach((option) => {\n if (option?.id) next.set(option.id, option)\n })\n return next\n })\n }, [initialOptions])\n\n React.useEffect(() => {\n if (!normalizedValue.length) return\n const missing = normalizedValue.filter((id) => !cache.has(id))\n if (!missing.length) return\n let cancelled = false\n ;(async () => {\n try {\n const entries = await fetchByIds(missing)\n if (cancelled) return\n setCache((prev) => {\n const next = new Map(prev)\n entries.forEach((entry) => {\n if (entry?.id) next.set(entry.id, entry)\n })\n return next\n })\n } catch {\n if (!cancelled) setError(errorLabel)\n }\n })().catch(() => {})\n return () => { cancelled = true }\n }, [cache, errorLabel, fetchByIds, normalizedValue])\n\n React.useEffect(() => {\n if (disabled) {\n setLoading(false)\n return\n }\n let cancelled = false\n const handler = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await search(input.trim())\n if (cancelled) return\n setSuggestions(results)\n setCache((prev) => {\n const next = new Map(prev)\n results.forEach((entry) => {\n if (entry?.id) next.set(entry.id, entry)\n })\n return next\n })\n setError(null)\n } catch {\n if (!cancelled) {\n setError(errorLabel)\n setSuggestions([])\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handler)\n }\n }, [disabled, errorLabel, input, search])\n\n const filteredSuggestions = React.useMemo(\n () => suggestions.filter((option) => !normalizedValue.includes(option.id)),\n [normalizedValue, suggestions],\n )\n\n const selectedOptions = React.useMemo(\n () => normalizedValue.map((id) => cache.get(id) ?? { id, label: id }),\n [cache, normalizedValue],\n )\n\n const addOption = React.useCallback(\n (option: EntityOption) => {\n if (!option?.id) return\n if (normalizedValue.includes(option.id)) return\n const next = [...normalizedValue, option.id]\n onChange(next)\n setCache((prev) => {\n const nextCache = new Map(prev)\n nextCache.set(option.id, option)\n return nextCache\n })\n setInput('')\n setSuggestions([])\n },\n [normalizedValue, onChange],\n )\n\n const removeOption = React.useCallback(\n (id: string) => {\n const next = normalizedValue.filter((candidate) => candidate !== id)\n onChange(next)\n },\n [normalizedValue, onChange],\n )\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2 rounded border px-2 py-1\">\n {selectedOptions.map((option) => (\n <span key={option.id} className=\"inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs\">\n {option.label}\n <IconButton\n variant=\"ghost\"\n size=\"xs\"\n className=\"opacity-60 hover:opacity-100\"\n onClick={() => removeOption(option.id)}\n aria-label={`${removeLabel} ${option.label}`}\n disabled={disabled}\n >\n \u00D7\n </IconButton>\n </span>\n ))}\n <input\n type=\"text\"\n className=\"flex-1 min-w-[160px] border-0 bg-transparent py-1 text-sm outline-none\"\n value={input}\n placeholder={placeholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n const nextOption = filteredSuggestions[0]\n if (nextOption) addOption(nextOption)\n } else if (event.key === 'Backspace' && !input.length && normalizedValue.length) {\n removeOption(normalizedValue[normalizedValue.length - 1])\n }\n }}\n disabled={disabled}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n />\n </div>\n {loading ? <div className=\"text-xs text-muted-foreground\">{loadingLabel}</div> : null}\n {!loading && filteredSuggestions.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {filteredSuggestions.slice(0, 10).map((option) => (\n <Button\n key={option.id}\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto px-2 py-1 text-xs font-normal\"\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => addOption(option)}\n disabled={disabled}\n aria-label={option.label}\n >\n <span className=\"flex flex-col items-start\">\n <span>{option.label}</span>\n {option.subtitle ? (\n <span className=\"text-overline text-muted-foreground\">{option.subtitle}</span>\n ) : null}\n </span>\n </Button>\n ))}\n </div>\n ) : null}\n {!loading && !filteredSuggestions.length && input.trim().length ? (\n <div className=\"text-xs text-muted-foreground\">{noResultsLabel}</div>\n ) : null}\n {error ? <div className=\"text-xs text-status-error-text\">{error}</div> : null}\n {!normalizedValue.length && !input.trim().length ? (\n <div className=\"text-xs text-muted-foreground\">{emptyLabel}</div>\n ) : null}\n </div>\n )\n}\n\nexport function useDealAssociationLookups(options?: { excludeLinkedDealId?: string | null }) {\n const searchPeoplePage = React.useCallback(async (query: string, page = 1): Promise<{ items: EntityOption[]; totalPages: number }> => {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sortField: 'name',\n sortDir: 'asc',\n })\n if (query.trim().length) params.set('search', query.trim())\n if (options?.excludeLinkedDealId) params.set('excludeLinkedDealId', options.excludeLinkedDealId)\n const call = await apiCall<Record<string, unknown>>(`/api/customers/people?${params.toString()}`)\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? String(call.result?.error) : 'Failed to search people')\n }\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n return {\n items: items\n .map((item: unknown) => (item && typeof item === 'object' ? extractPersonOption(item as Record<string, unknown>) : null))\n .filter((entry: EntityOption | null): entry is EntityOption => entry !== null),\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n }, [options?.excludeLinkedDealId])\n\n const searchPeople = React.useCallback(async (query: string): Promise<EntityOption[]> => {\n const result = await searchPeoplePage(query, 1)\n return result.items\n }, [searchPeoplePage])\n\n const fetchPeopleByIds = React.useCallback(async (ids: string[]): Promise<EntityOption[]> => {\n const unique = sanitizeIdList(ids)\n if (!unique.length) return []\n try {\n const params = new URLSearchParams({\n ids: unique.join(','),\n pageSize: String(Math.max(unique.length, 1)),\n })\n const call = await apiCall<Record<string, unknown>>(`/api/customers/people?${params.toString()}`)\n if (!call.ok) throw new Error()\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n const optionMap = new Map<string, EntityOption>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const option = extractPersonOption(item as Record<string, unknown>)\n if (option?.id) optionMap.set(option.id, option)\n })\n return unique.map((id) => optionMap.get(id) ?? { id, label: id })\n } catch {\n return unique.map((id) => ({ id, label: id }))\n }\n }, [])\n\n const searchCompaniesPage = React.useCallback(async (query: string, page = 1): Promise<{ items: EntityOption[]; totalPages: number }> => {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sortField: 'name',\n sortDir: 'asc',\n })\n if (query.trim().length) params.set('search', query.trim())\n if (options?.excludeLinkedDealId) params.set('excludeLinkedDealId', options.excludeLinkedDealId)\n const call = await apiCall<Record<string, unknown>>(`/api/customers/companies?${params.toString()}`)\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? String(call.result?.error) : 'Failed to search companies')\n }\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n return {\n items: items\n .map((item: unknown) => (item && typeof item === 'object' ? extractCompanyOption(item as Record<string, unknown>) : null))\n .filter((entry: EntityOption | null): entry is EntityOption => entry !== null),\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n }, [options?.excludeLinkedDealId])\n\n const searchCompanies = React.useCallback(async (query: string): Promise<EntityOption[]> => {\n const result = await searchCompaniesPage(query, 1)\n return result.items\n }, [searchCompaniesPage])\n\n const fetchCompaniesByIds = React.useCallback(async (ids: string[]): Promise<EntityOption[]> => {\n const unique = sanitizeIdList(ids)\n if (!unique.length) return []\n try {\n const params = new URLSearchParams({\n ids: unique.join(','),\n pageSize: String(Math.max(unique.length, 1)),\n })\n const call = await apiCall<Record<string, unknown>>(`/api/customers/companies?${params.toString()}`)\n if (!call.ok) throw new Error()\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n const optionMap = new Map<string, EntityOption>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const option = extractCompanyOption(item as Record<string, unknown>)\n if (option?.id) optionMap.set(option.id, option)\n })\n return unique.map((id) => optionMap.get(id) ?? { id, label: id })\n } catch {\n return unique.map((id) => ({ id, label: id }))\n }\n }, [])\n\n return {\n searchPeople,\n searchPeoplePage,\n fetchPeopleByIds,\n searchCompanies,\n searchCompaniesPage,\n fetchCompaniesByIds,\n }\n}\n\nexport function DealPeopleSelector({\n value,\n onChange,\n options = [],\n disabled = false,\n autoFocus = false,\n}: {\n value: string[]\n onChange: (next: string[]) => void\n options?: EntityOption[]\n disabled?: boolean\n autoFocus?: boolean\n}) {\n const t = useT()\n const { searchPeople, fetchPeopleByIds } = useDealAssociationLookups()\n\n return (\n <EntityMultiSelect\n value={value}\n onChange={onChange}\n initialOptions={options}\n placeholder={t('customers.deals.form.people.searchPlaceholder', 'Search people\u2026')}\n emptyLabel={t('customers.deals.form.people.empty', 'No people linked yet.')}\n loadingLabel={t('customers.deals.form.people.loading', 'Searching people\u2026')}\n noResultsLabel={t('customers.deals.form.people.noResults', 'No people match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.people.error', 'Failed to load people.')}\n search={searchPeople}\n fetchByIds={fetchPeopleByIds}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n )\n}\n\nexport function DealCompaniesSelector({\n value,\n onChange,\n options = [],\n disabled = false,\n}: {\n value: string[]\n onChange: (next: string[]) => void\n options?: EntityOption[]\n disabled?: boolean\n}) {\n const t = useT()\n const { searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups()\n\n return (\n <EntityMultiSelect\n value={value}\n onChange={onChange}\n initialOptions={options}\n placeholder={t('customers.deals.form.companies.searchPlaceholder', 'Search companies\u2026')}\n emptyLabel={t('customers.deals.form.companies.empty', 'No companies linked yet.')}\n loadingLabel={t('customers.deals.form.companies.loading', 'Searching companies\u2026')}\n noResultsLabel={t('customers.deals.form.companies.noResults', 'No companies match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.companies.error', 'Failed to load companies.')}\n search={searchCompanies}\n fetchByIds={fetchCompaniesByIds}\n disabled={disabled}\n />\n )\n}\n\nexport function DealForm({\n mode,\n initialValues,\n onSubmit,\n onCancel,\n onDelete,\n submitLabel,\n cancelLabel,\n isSubmitting = false,\n embedded = true,\n trackDirtyWhenEmbedded = false,\n title,\n backHref,\n hideFooterActions = false,\n onDirtyChange,\n collapsibleGroups,\n sortableGroups,\n singleColumnGroups = false,\n showAssociationsGroup = true,\n showVersionHistory = true,\n showCancelAction = true,\n initialPipelineOptions,\n initialPipelineStageOptions,\n injectionSpotId,\n optimisticLockUpdatedAt,\n}: DealFormProps) {\n const t = useT()\n const [pending, setPending] = React.useState(false)\n const {\n data: currencyDictionaryData,\n error: currencyDictionaryErrorRaw,\n isLoading: currencyDictionaryLoading,\n refetch: refetchCurrencyDictionary,\n } = useCurrencyDictionary()\n const currencyDictionaryError = currencyDictionaryErrorRaw\n ? currencyDictionaryErrorRaw instanceof Error\n ? currencyDictionaryErrorRaw.message\n : String(currencyDictionaryErrorRaw)\n : null\n\n const translate = React.useCallback(\n (key: string, fallback: string) => {\n const value = t(key)\n return value === key ? fallback : value\n },\n [t],\n )\n\n const dictionaryLabels = React.useMemo(() => ({\n status: createDictionarySelectLabels('deal-statuses', translate),\n }), [translate])\n\n const resolvedCurrencyError = React.useMemo(() => {\n if (currencyDictionaryError) return currencyDictionaryError\n if (!currencyDictionaryLoading && !currencyDictionaryData) {\n return t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.')\n }\n return null\n }, [currencyDictionaryData, currencyDictionaryError, currencyDictionaryLoading, t])\n\n const fetchCurrencyOptions = React.useCallback(async () => {\n let payload = currencyDictionaryData ?? null\n if (!payload) {\n try {\n payload = await refetchCurrencyDictionary()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err ?? '')\n throw new Error(message || t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'))\n }\n }\n if (!payload) {\n throw new Error(t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.'))\n }\n const priorityOrder = new Map<string, number>()\n CURRENCY_PRIORITY.forEach((code, index) => priorityOrder.set(code, index))\n const prioritized: { value: string; label: string; color: string | null; icon: string | null }[] = []\n const remainder: { value: string; label: string; color: string | null; icon: string | null }[] = []\n payload.entries.forEach((entry) => {\n const value = entry.value.toUpperCase()\n const label = entry.label && entry.label.length ? `${value} \u2013 ${entry.label}` : value\n const option = { value, label, color: null, icon: null }\n if (priorityOrder.has(value)) prioritized.push(option)\n else remainder.push(option)\n })\n prioritized.sort((a, b) => (priorityOrder.get(a.value)! - priorityOrder.get(b.value)!))\n remainder.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))\n return [...prioritized, ...remainder]\n }, [currencyDictionaryData, refetchCurrencyDictionary, t])\n\n const currencyDictionaryLabels = React.useMemo(() => ({\n placeholder: t('customers.deals.form.currency.placeholder', 'Select currency\u2026'),\n addLabel: t('customers.deals.form.currency.add', 'Add currency'),\n dialogTitle: t('customers.deals.form.currency.dialogTitle', 'Add currency'),\n valueLabel: t('customers.deals.form.currency.valueLabel', 'Currency code'),\n valuePlaceholder: t('customers.deals.form.currency.valuePlaceholder', 'e.g. USD'),\n labelLabel: t('customers.deals.form.currency.labelLabel', 'Label'),\n labelPlaceholder: t('customers.deals.form.currency.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('customers.deals.form.currency.error.required', 'Currency code is required.'),\n cancelLabel: t('customers.deals.form.currency.cancel', 'Cancel'),\n saveLabel: t('customers.deals.form.currency.save', 'Save'),\n errorLoad: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n errorSave: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n loadingLabel: t('customers.deals.form.currency.loading', 'Loading currencies\u2026'),\n manageTitle: t('customers.deals.form.currency.manage', 'Manage currency dictionary'),\n }), [t])\n\n const { searchPeople, fetchPeopleByIds, searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups()\n\n const disabled = pending || isSubmitting\n const canDelete = mode === 'edit' && typeof onDelete === 'function'\n\n const mountedRef = React.useRef(false)\n const seedPipelineOptions = React.useMemo(\n () => normalizePipelineOptions(initialPipelineOptions),\n [initialPipelineOptions],\n )\n const seedPipelineStageOptions = React.useMemo(\n () => Array.isArray(initialPipelineStageOptions) ? normalizePipelineStageOptions(initialPipelineStageOptions) : null,\n [initialPipelineStageOptions],\n )\n\n const [pipelines, setPipelines] = React.useState<PipelineOption[]>(() => seedPipelineOptions)\n const [pipelineStages, setPipelineStages] = React.useState<PipelineStageOption[]>(() => seedPipelineStageOptions ?? [])\n\n React.useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n }\n }, [])\n\n const loadStagesForPipeline = React.useCallback(async (pipelineId: string) => {\n if (!pipelineId) {\n if (mountedRef.current) setPipelineStages([])\n return\n }\n try {\n const stages = await fetchPipelineStageOptions(pipelineId)\n if (mountedRef.current) setPipelineStages(stages)\n } catch {\n if (mountedRef.current) setPipelineStages([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n ;(async () => {\n try {\n const loaded = await fetchPipelineOptions()\n if (cancelled || !mountedRef.current) return\n setPipelines(mergePipelineOptions(seedPipelineOptions, loaded))\n } catch {\n if (!cancelled && mountedRef.current && seedPipelineOptions.length > 0) {\n setPipelines(seedPipelineOptions)\n }\n }\n })().catch(() => {})\n return () => { cancelled = true }\n }, [seedPipelineOptions])\n\n React.useEffect(() => {\n const pid = initialValues?.pipelineId\n if (typeof pid === 'string' && pid.length) {\n if (seedPipelineStageOptions) {\n setPipelineStages(seedPipelineStageOptions)\n return\n }\n loadStagesForPipeline(pid).catch(() => {})\n } else {\n setPipelineStages([])\n }\n }, [initialValues?.pipelineId, loadStagesForPipeline, seedPipelineStageOptions])\n\n const baseFields = React.useMemo<CrudField[]>(() => [\n {\n id: 'title',\n label: t('customers.people.detail.deals.fields.title', 'Title'),\n type: 'text',\n required: true,\n },\n {\n id: 'status',\n label: t('customers.people.detail.deals.fields.status', 'Status'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <DictionarySelectField\n kind=\"deal-statuses\"\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n labels={dictionaryLabels.status}\n selectClassName=\"w-full\"\n />\n ),\n } as CrudField,\n {\n id: 'pipelineId',\n label: t('customers.people.detail.deals.fields.pipeline', 'Pipeline'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue, setFormValue }) => (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => {\n setValue(next ?? '')\n setFormValue?.('pipelineStageId', '')\n loadStagesForPipeline(next ?? '').catch(() => {})\n }}\n disabled={disabled}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('customers.deals.form.pipeline.placeholder', 'Select pipeline\u2026')} />\n </SelectTrigger>\n <SelectContent>\n {pipelines.map((p) => (\n <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n } as CrudField,\n {\n id: 'pipelineStageId',\n label: t('customers.people.detail.deals.fields.pipelineStage', 'Pipeline stage'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => setValue(next ?? '')}\n disabled={disabled || !pipelineStages.length}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('customers.deals.form.pipelineStage.placeholder', 'Select stage\u2026')} />\n </SelectTrigger>\n <SelectContent>\n {pipelineStages.map((s) => (\n <SelectItem key={s.id} value={s.id}>{s.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n } as CrudField,\n {\n id: 'valueAmount',\n label: t('customers.people.detail.deals.fields.valueAmount', 'Amount'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'valueCurrency',\n label: t('customers.people.detail.deals.fields.valueCurrency', 'Currency'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <div className=\"space-y-1\">\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n fetchOptions={fetchCurrencyOptions}\n labels={currencyDictionaryLabels}\n manageHref=\"/backend/config/dictionaries?key=currency\"\n allowInlineCreate={false}\n allowAppearance={false}\n selectClassName=\"w-full\"\n sortOptions=\"none\"\n disabled={disabled}\n showLabelInput={false}\n />\n {resolvedCurrencyError ? (\n <div className=\"text-xs text-muted-foreground\">{resolvedCurrencyError}</div>\n ) : null}\n </div>\n ),\n } as CrudField,\n {\n id: 'probability',\n label: t('customers.people.detail.deals.fields.probability', 'Probability (%)'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'expectedCloseAt',\n label: t('customers.people.detail.deals.fields.expectedCloseAt', 'Expected close'),\n type: 'date',\n layout: 'half',\n },\n {\n id: 'description',\n label: t('customers.people.detail.deals.fields.description', 'Description'),\n type: 'textarea',\n },\n {\n id: 'personIds',\n label: t('customers.people.detail.deals.fields.people', 'People'),\n type: 'custom',\n component: ({ value, setValue, autoFocus }) => (\n <EntityMultiSelect\n value={Array.isArray(value) ? value : []}\n onChange={(next) => setValue(next)}\n placeholder={t('customers.deals.form.people.searchPlaceholder', 'Search people\u2026')}\n emptyLabel={t('customers.deals.form.people.empty', 'No people linked yet.')}\n loadingLabel={t('customers.deals.form.people.loading', 'Searching people\u2026')}\n noResultsLabel={t('customers.deals.form.people.noResults', 'No people match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.people.error', 'Failed to load people.')}\n search={searchPeople}\n fetchByIds={fetchPeopleByIds}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n ),\n } as CrudField,\n {\n id: 'companyIds',\n label: t('customers.people.detail.deals.fields.companies', 'Companies'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <EntityMultiSelect\n value={Array.isArray(value) ? value : []}\n onChange={(next) => setValue(next)}\n placeholder={t('customers.deals.form.companies.searchPlaceholder', 'Search companies\u2026')}\n emptyLabel={t('customers.deals.form.companies.empty', 'No companies linked yet.')}\n loadingLabel={t('customers.deals.form.companies.loading', 'Searching companies\u2026')}\n noResultsLabel={t('customers.deals.form.companies.noResults', 'No companies match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.companies.error', 'Failed to load companies.')}\n search={searchCompanies}\n fetchByIds={fetchCompaniesByIds}\n disabled={disabled}\n />\n ),\n } as CrudField,\n ], [currencyDictionaryLabels, fetchCurrencyOptions, resolvedCurrencyError, pipelines, pipelineStages, loadStagesForPipeline, dictionaryLabels.status, disabled, fetchCompaniesByIds, fetchPeopleByIds, searchCompanies, searchPeople, t])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const nextGroups: CrudFormGroup[] = [\n {\n id: 'details',\n title: t('customers.people.detail.deals.form.details', 'Deal details'),\n column: 1,\n fields: ['title', 'status', 'pipelineId', 'pipelineStageId', 'valueAmount', 'valueCurrency', 'probability', 'expectedCloseAt', 'description'],\n },\n ...(showAssociationsGroup\n ? [{\n id: 'associations',\n title: t('customers.people.detail.deals.form.associations', 'Associations'),\n column: 1,\n fields: ['personIds', 'companyIds'],\n } satisfies CrudFormGroup]\n : []),\n {\n id: 'custom',\n title: t('customers.people.detail.deals.form.customFields', 'Custom fields'),\n column: 2,\n kind: 'customFields',\n },\n ]\n return nextGroups.map((group) => (singleColumnGroups ? { ...group, column: 1 } : group))\n }, [showAssociationsGroup, singleColumnGroups, t])\n\n const embeddedInitialValues = React.useMemo(() => {\n const normalizeNumber = (value: unknown): number | null => {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isNaN(parsed) ? null : parsed\n }\n return null\n }\n\n const resolveIdsFromSource = (source: unknown): string[] => {\n if (Array.isArray(source)) {\n return sanitizeIdList(\n source.map((entry) => {\n if (typeof entry === 'string') return entry\n if (entry && typeof entry === 'object' && 'id' in entry) {\n const candidate = (entry as { id?: unknown }).id\n if (typeof candidate === 'string') return candidate\n }\n return null\n }),\n )\n }\n return []\n }\n\n return {\n id: typeof initialValues?.id === 'string' ? initialValues.id : undefined,\n title: initialValues?.title ?? '',\n status: initialValues?.status ?? '',\n pipelineStage: initialValues?.pipelineStage ?? '',\n pipelineId: initialValues?.pipelineId ?? (typeof (initialValues as Record<string, unknown>)?.pipeline_id === 'string' ? (initialValues as Record<string, unknown>).pipeline_id as string : ''),\n pipelineStageId: initialValues?.pipelineStageId ?? (typeof (initialValues as Record<string, unknown>)?.pipeline_stage_id === 'string' ? (initialValues as Record<string, unknown>).pipeline_stage_id as string : ''),\n valueAmount: normalizeNumber(initialValues?.valueAmount ?? null),\n valueCurrency: normalizeCurrency(initialValues?.valueCurrency ?? null),\n probability: normalizeNumber(initialValues?.probability ?? null),\n expectedCloseAt: toDateInputValue(initialValues?.expectedCloseAt ?? null),\n description: initialValues?.description ?? '',\n personIds: sanitizeIdList(initialValues?.personIds ?? resolveIdsFromSource(initialValues?.people)),\n companyIds: sanitizeIdList(initialValues?.companyIds ?? resolveIdsFromSource(initialValues?.companies)),\n ...Object.fromEntries(\n Object.entries(initialValues ?? {})\n .filter(([key]) => key.startsWith('cf_'))\n .map(([key, value]) => [key, value]),\n ),\n }\n }, [initialValues])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n if (pending || isSubmitting) return\n setPending(true)\n try {\n const parsed = schema.safeParse(values)\n if (!parsed.success) {\n throw buildDealValidationError(parsed.error.issues, t)\n }\n const expectedCloseAt =\n parsed.data.expectedCloseAt && parsed.data.expectedCloseAt.length\n ? new Date(parsed.data.expectedCloseAt).toISOString()\n : undefined\n const personIds = sanitizeIdList(parsed.data.personIds)\n const companyIds = sanitizeIdList(parsed.data.companyIds)\n const base: DealFormBaseValues = {\n title: parsed.data.title,\n status: parsed.data.status || undefined,\n pipelineStage: parsed.data.pipelineStage || undefined,\n pipelineId: parsed.data.pipelineId || undefined,\n pipelineStageId: parsed.data.pipelineStageId || undefined,\n valueAmount:\n typeof parsed.data.valueAmount === 'number' ? parsed.data.valueAmount : undefined,\n valueCurrency: parsed.data.valueCurrency || undefined,\n probability:\n typeof parsed.data.probability === 'number' ? parsed.data.probability : undefined,\n expectedCloseAt,\n description: parsed.data.description && parsed.data.description.length\n ? parsed.data.description\n : undefined,\n personIds,\n companyIds,\n }\n const customEntries = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n })\n await onSubmit({ base, custom: customEntries })\n } finally {\n setPending(false)\n }\n },\n [isSubmitting, onSubmit, pending, t],\n )\n\n return (\n <CrudForm<Record<string, unknown>>\n embedded={embedded}\n trackDirtyWhenEmbedded={trackDirtyWhenEmbedded}\n title={title}\n backHref={backHref}\n hideFooterActions={hideFooterActions}\n onDirtyChange={onDirtyChange}\n injectionSpotId={injectionSpotId}\n optimisticLockUpdatedAt={optimisticLockUpdatedAt}\n collapsibleGroups={collapsibleGroups}\n sortableGroups={sortableGroups}\n versionHistory={showVersionHistory && mode === 'edit' && initialValues?.id\n ? { resourceKind: 'customers.deal', resourceId: String(initialValues.id) }\n : undefined}\n schema={schema}\n fields={baseFields}\n groups={groups}\n entityIds={DEAL_ENTITY_IDS}\n initialValues={embeddedInitialValues}\n onSubmit={handleSubmit}\n onDelete={canDelete ? onDelete : undefined}\n deleteVisible={canDelete}\n submitLabel={\n submitLabel ??\n (mode === 'edit'\n ? t('customers.people.detail.deals.update', 'Update deal (\u2318/Ctrl + Enter)')\n : t('customers.people.detail.deals.save', 'Save deal (\u2318/Ctrl + Enter)'))\n }\n extraActions={showCancelAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={pending || isSubmitting}\n >\n {cancelLabel ?? t('customers.people.detail.deals.cancel', 'Cancel')}\n </Button>\n ) : undefined}\n />\n )\n}\n\nexport function buildDealValidationError(issues: z.ZodIssue[], t: (key: string, fallback?: string) => string) {\n const issue = issues[0]\n const message =\n typeof issue?.message === 'string'\n ? issue.message\n : t('customers.people.detail.deals.error', 'Failed to save deal.')\n const firstPath = Array.isArray(issue?.path) ? issue?.path?.[0] : undefined\n const field = typeof firstPath === 'string' ? firstPath : undefined\n throw createCrudFormError(message, field ? { [field]: message } : undefined)\n}\n\nexport default DealForm\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { DEAL_DESCRIPTION_MAX_LENGTH } from '../../data/validators'\nimport { DictionarySelectField } from '../formConfig'\nimport { createDictionarySelectLabels } from './utils'\nimport { E } from '#generated/entities.ids.generated'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { useCurrencyDictionary } from './hooks/useCurrencyDictionary'\nimport { DictionaryEntrySelect } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { normalizeCustomFieldSubmitValue } from './customFieldUtils'\n\nexport type DealFormBaseValues = {\n title: string\n status?: string | null\n pipelineStage?: string | null\n pipelineId?: string | null\n pipelineStageId?: string | null\n valueAmount?: number | null\n valueCurrency?: string | null\n probability?: number | null\n expectedCloseAt?: string | null\n description?: string | null\n personIds?: string[]\n companyIds?: string[]\n}\n\ntype PipelineOption = { id: string; name: string; isDefault: boolean }\ntype PipelineStageOption = { id: string; label: string; order: number }\n\nexport type DealFormSubmitPayload = {\n base: DealFormBaseValues\n custom: Record<string, unknown>\n}\n\nexport type DealFormProps = {\n mode: 'create' | 'edit'\n initialValues?: Partial<DealFormBaseValues & Record<string, unknown>>\n onSubmit: (payload: DealFormSubmitPayload) => Promise<void>\n onCancel: () => void\n onDelete?: () => Promise<void> | void\n submitLabel?: string\n cancelLabel?: string\n isSubmitting?: boolean\n embedded?: boolean\n trackDirtyWhenEmbedded?: boolean\n title?: string\n backHref?: string\n hideFooterActions?: boolean\n onDirtyChange?: (dirty: boolean) => void\n collapsibleGroups?: boolean | { pageType: string; chevronPosition?: 'left' | 'right' }\n sortableGroups?: boolean | { pageType: string }\n singleColumnGroups?: boolean\n showAssociationsGroup?: boolean\n showVersionHistory?: boolean\n showCancelAction?: boolean\n initialPipelineOptions?: PipelineOption[]\n initialPipelineStageOptions?: PipelineStageOption[]\n /**\n * Injection spot id for the form-scoped record_locks widget (e.g.\n * `customers.deal`). Mirrors how people-v2/companies-v2 mount their save-time\n * `crud-form:*` widget so a deal save conflict surfaces the merge dialog.\n */\n injectionSpotId?: string\n /** Optimistic-lock version (`deal.updatedAt`) for the embedded CrudForm. */\n optimisticLockUpdatedAt?: string | null\n}\n\ntype EntityOption = {\n id: string\n label: string\n subtitle?: string | null\n}\n\nexport type DealLookupOption = EntityOption\n\ntype EntityMultiSelectProps = {\n value: string[]\n onChange: (next: string[]) => void\n initialOptions?: EntityOption[]\n placeholder: string\n emptyLabel: string\n loadingLabel: string\n noResultsLabel: string\n removeLabel: string\n errorLabel: string\n search: (query: string) => Promise<EntityOption[]>\n fetchByIds: (ids: string[]) => Promise<EntityOption[]>\n disabled?: boolean\n autoFocus?: boolean\n /**\n * Minimum trimmed query length before a lookup request is issued. Defaults to\n * `1`, so a blank input never queries the address book (issue #5118). Set `0`\n * to opt back into prefetching an unfiltered first page on mount.\n */\n minQueryLength?: number\n}\n\nconst DEAL_ENTITY_IDS = [E.customers.customer_deal]\nconst CURRENCY_PRIORITY = ['EUR', 'USD', 'GBP', 'PLN'] as const\nconst PIPELINE_OPTIONS_TTL_MS = 60_000\nconst PIPELINE_STAGE_OPTIONS_TTL_MS = 30_000\n\ntype MetadataCacheEntry<T> = {\n expiresAt: number\n promise: Promise<T>\n}\n\nlet pipelineOptionsCache: MetadataCacheEntry<PipelineOption[]> | null = null\nconst pipelineStageOptionsCache = new Map<string, MetadataCacheEntry<PipelineStageOption[]>>()\n\nfunction isFreshCacheEntry<T>(entry: MetadataCacheEntry<T> | null | undefined): entry is MetadataCacheEntry<T> {\n return Boolean(entry && entry.expiresAt > Date.now())\n}\n\nfunction normalizePipelineOptions(options: PipelineOption[] | undefined): PipelineOption[] {\n const byId = new Map<string, PipelineOption>()\n for (const option of options ?? []) {\n if (!option.id) continue\n byId.set(option.id, {\n id: option.id,\n name: option.name,\n isDefault: option.isDefault === true,\n })\n }\n return Array.from(byId.values())\n}\n\nfunction mergePipelineOptions(seed: PipelineOption[], loaded: PipelineOption[]): PipelineOption[] {\n const byId = new Map<string, PipelineOption>()\n for (const option of seed) byId.set(option.id, option)\n for (const option of loaded) byId.set(option.id, option)\n return Array.from(byId.values())\n}\n\nfunction normalizePipelineStageOptions(options: PipelineStageOption[] | undefined): PipelineStageOption[] {\n return [...(options ?? [])]\n .filter((option) => option.id)\n .sort((left, right) => left.order - right.order)\n}\n\nasync function fetchPipelineOptions(): Promise<PipelineOption[]> {\n if (isFreshCacheEntry(pipelineOptionsCache)) return pipelineOptionsCache.promise\n const entry: MetadataCacheEntry<PipelineOption[]> = {\n expiresAt: Date.now() + PIPELINE_OPTIONS_TTL_MS,\n promise: apiCall<{ items: PipelineOption[] }>('/api/customers/pipelines')\n .then((call) => (call.ok && call.result?.items ? normalizePipelineOptions(call.result.items) : [])),\n }\n pipelineOptionsCache = entry\n try {\n return await entry.promise\n } catch (error) {\n if (pipelineOptionsCache === entry) pipelineOptionsCache = null\n throw error\n }\n}\n\nasync function fetchPipelineStageOptions(pipelineId: string): Promise<PipelineStageOption[]> {\n const cached = pipelineStageOptionsCache.get(pipelineId)\n if (isFreshCacheEntry(cached)) return cached.promise\n const entry: MetadataCacheEntry<PipelineStageOption[]> = {\n expiresAt: Date.now() + PIPELINE_STAGE_OPTIONS_TTL_MS,\n promise: apiCall<{ items: PipelineStageOption[] }>(`/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`)\n .then((call) => (call.ok && call.result?.items ? normalizePipelineStageOptions(call.result.items) : [])),\n }\n pipelineStageOptionsCache.set(pipelineId, entry)\n try {\n return await entry.promise\n } catch (error) {\n if (pipelineStageOptionsCache.get(pipelineId) === entry) pipelineStageOptionsCache.delete(pipelineId)\n throw error\n }\n}\n\nexport function resetDealPipelineMetadataCacheForTests() {\n pipelineOptionsCache = null\n pipelineStageOptionsCache.clear()\n}\n\nconst schema = z.object({\n title: z\n .string()\n .trim()\n .min(1, 'customers.people.detail.deals.titleRequired')\n .max(200, 'customers.people.detail.deals.titleTooLong'),\n status: z\n .string()\n .trim()\n .max(50, 'customers.people.detail.deals.statusTooLong')\n .optional(),\n pipelineStage: z\n .string()\n .trim()\n .max(100, 'customers.people.detail.deals.pipelineTooLong')\n .optional(),\n pipelineId: z.preprocess(\n (v) => (typeof v === 'string' && !v.trim() ? undefined : v),\n z.string().uuid('customers.people.detail.deals.pipelineIdInvalid').optional(),\n ),\n pipelineStageId: z.preprocess(\n (v) => (typeof v === 'string' && !v.trim() ? undefined : v),\n z.string().uuid('customers.people.detail.deals.pipelineStageIdInvalid').optional(),\n ),\n valueAmount: z\n .preprocess((value) => {\n if (value === '' || value === null || value === undefined) return undefined\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return undefined\n const parsed = Number(trimmed)\n if (Number.isNaN(parsed)) return value\n return parsed\n }\n return value\n }, z\n .number()\n .min(0, 'customers.people.detail.deals.valueInvalid')\n .optional())\n .optional(),\n valueCurrency: z\n .string()\n .transform((value) => value.trim().toUpperCase())\n .refine(\n (value) => !value || /^[A-Z]{3}$/.test(value),\n 'customers.people.detail.deals.currencyInvalid',\n )\n .optional(),\n probability: z\n .preprocess((value) => {\n if (value === '' || value === null || value === undefined) return undefined\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return undefined\n const parsed = Number(trimmed)\n if (Number.isNaN(parsed)) return value\n return parsed\n }\n return value\n }, z\n .number()\n .min(0, 'customers.people.detail.deals.probabilityInvalid')\n .max(100, 'customers.people.detail.deals.probabilityInvalid')\n .optional())\n .optional(),\n expectedCloseAt: z\n .string()\n .transform((value) => value.trim())\n .refine(\n (value) => {\n if (!value) return true\n const parsed = new Date(value)\n return !Number.isNaN(parsed.getTime())\n },\n 'customers.people.detail.deals.expectedCloseInvalid',\n )\n .optional(),\n description: z\n .string()\n .max(DEAL_DESCRIPTION_MAX_LENGTH, 'customers.people.detail.deals.descriptionTooLong')\n .optional(),\n personIds: z.array(z.string().trim().min(1)).optional(),\n companyIds: z.array(z.string().trim().min(1)).optional(),\n}).passthrough()\n\nexport const dealFormSchema = schema\n\nimport { toDateInputValue as toDateInputValueOrNull } from '@open-mercato/shared/lib/date/format'\n\nfunction toDateInputValue(value: string | null | undefined): string {\n return toDateInputValueOrNull(value) ?? ''\n}\n\nfunction normalizeCurrency(value: string | null | undefined): string {\n if (!value) return ''\n return value.trim().slice(0, 3).toUpperCase()\n}\n\nfunction sanitizeIdList(input: unknown): string[] {\n if (!Array.isArray(input)) return []\n const set = new Set<string>()\n input.forEach((candidate) => {\n if (typeof candidate !== 'string') return\n const trimmed = candidate.trim()\n if (!trimmed.length) return\n set.add(trimmed)\n })\n return Array.from(set)\n}\n\nfunction extractPersonOption(record: Record<string, unknown>): EntityOption | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\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 as string).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 as string).trim()\n : null\n const label = displayName ?? email ?? id\n const subtitle = email && email !== label ? email : null\n return { id, label, subtitle }\n}\n\nfunction extractCompanyOption(record: Record<string, unknown>): EntityOption | null {\n const id = typeof record.id === 'string' ? record.id : null\n if (!id) return null\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 as string).trim()\n : null\n const domain =\n typeof record.domain === 'string' && record.domain.trim().length\n ? record.domain.trim()\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 as string).trim()\n : null\n const label = displayName ?? domain ?? id\n const subtitle = domain && domain !== label ? domain : null\n return { id, label, subtitle }\n}\n\nfunction EntityMultiSelect({\n value,\n onChange,\n initialOptions = [],\n placeholder,\n emptyLabel,\n loadingLabel,\n noResultsLabel,\n removeLabel,\n errorLabel,\n search,\n fetchByIds,\n disabled = false,\n autoFocus = false,\n minQueryLength = 1,\n}: EntityMultiSelectProps) {\n const [input, setInput] = React.useState('')\n const [suggestions, setSuggestions] = React.useState<EntityOption[]>([])\n const [cache, setCache] = React.useState<Map<string, EntityOption>>(() => new Map())\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const normalizedValue = React.useMemo(() => sanitizeIdList(value), [value])\n\n React.useEffect(() => {\n if (!initialOptions.length) return\n setCache((prev) => {\n const next = new Map(prev)\n initialOptions.forEach((option) => {\n if (option?.id) next.set(option.id, option)\n })\n return next\n })\n }, [initialOptions])\n\n React.useEffect(() => {\n if (!normalizedValue.length) return\n const missing = normalizedValue.filter((id) => !cache.has(id))\n if (!missing.length) return\n let cancelled = false\n ;(async () => {\n try {\n const entries = await fetchByIds(missing)\n if (cancelled) return\n setCache((prev) => {\n const next = new Map(prev)\n entries.forEach((entry) => {\n if (entry?.id) next.set(entry.id, entry)\n })\n return next\n })\n } catch {\n if (!cancelled) setError(errorLabel)\n }\n })().catch(() => {})\n return () => { cancelled = true }\n }, [cache, errorLabel, fetchByIds, normalizedValue])\n\n React.useEffect(() => {\n if (disabled) {\n setLoading(false)\n return\n }\n const term = input.trim()\n if (term.length < minQueryLength) {\n setSuggestions([])\n setLoading(false)\n setError(null)\n return\n }\n let cancelled = false\n const handler = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await search(term)\n if (cancelled) return\n setSuggestions(results)\n setCache((prev) => {\n const next = new Map(prev)\n results.forEach((entry) => {\n if (entry?.id) next.set(entry.id, entry)\n })\n return next\n })\n setError(null)\n } catch {\n if (!cancelled) {\n setError(errorLabel)\n setSuggestions([])\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n window.clearTimeout(handler)\n }\n }, [disabled, errorLabel, input, minQueryLength, search])\n\n const filteredSuggestions = React.useMemo(\n () => suggestions.filter((option) => !normalizedValue.includes(option.id)),\n [normalizedValue, suggestions],\n )\n\n const selectedOptions = React.useMemo(\n () => normalizedValue.map((id) => cache.get(id) ?? { id, label: id }),\n [cache, normalizedValue],\n )\n\n const addOption = React.useCallback(\n (option: EntityOption) => {\n if (!option?.id) return\n if (normalizedValue.includes(option.id)) return\n const next = [...normalizedValue, option.id]\n onChange(next)\n setCache((prev) => {\n const nextCache = new Map(prev)\n nextCache.set(option.id, option)\n return nextCache\n })\n setInput('')\n setSuggestions([])\n },\n [normalizedValue, onChange],\n )\n\n const removeOption = React.useCallback(\n (id: string) => {\n const next = normalizedValue.filter((candidate) => candidate !== id)\n onChange(next)\n },\n [normalizedValue, onChange],\n )\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2 rounded border px-2 py-1\">\n {selectedOptions.map((option) => (\n <span key={option.id} className=\"inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs\">\n {option.label}\n <IconButton\n variant=\"ghost\"\n size=\"xs\"\n className=\"opacity-60 hover:opacity-100\"\n onClick={() => removeOption(option.id)}\n aria-label={`${removeLabel} ${option.label}`}\n disabled={disabled}\n >\n \u00D7\n </IconButton>\n </span>\n ))}\n <input\n type=\"text\"\n className=\"flex-1 min-w-[160px] border-0 bg-transparent py-1 text-sm outline-none\"\n value={input}\n placeholder={placeholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n const nextOption = filteredSuggestions[0]\n if (nextOption) addOption(nextOption)\n } else if (event.key === 'Backspace' && !input.length && normalizedValue.length) {\n removeOption(normalizedValue[normalizedValue.length - 1])\n }\n }}\n disabled={disabled}\n autoFocus={autoFocus}\n data-crud-focus-target=\"\"\n />\n </div>\n {loading ? <div className=\"text-xs text-muted-foreground\">{loadingLabel}</div> : null}\n {!loading && filteredSuggestions.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {filteredSuggestions.slice(0, 10).map((option) => (\n <Button\n key={option.id}\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto px-2 py-1 text-xs font-normal\"\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => addOption(option)}\n disabled={disabled}\n aria-label={option.label}\n >\n <span className=\"flex flex-col items-start\">\n <span>{option.label}</span>\n {option.subtitle ? (\n <span className=\"text-overline text-muted-foreground\">{option.subtitle}</span>\n ) : null}\n </span>\n </Button>\n ))}\n </div>\n ) : null}\n {!loading && !filteredSuggestions.length && input.trim().length ? (\n <div className=\"text-xs text-muted-foreground\">{noResultsLabel}</div>\n ) : null}\n {error ? <div className=\"text-xs text-status-error-text\">{error}</div> : null}\n {!normalizedValue.length && !input.trim().length ? (\n <div className=\"text-xs text-muted-foreground\">{emptyLabel}</div>\n ) : null}\n </div>\n )\n}\n\nexport function useDealAssociationLookups(options?: { excludeLinkedDealId?: string | null }) {\n const searchPeoplePage = React.useCallback(async (query: string, page = 1): Promise<{ items: EntityOption[]; totalPages: number }> => {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sortField: 'name',\n sortDir: 'asc',\n })\n if (query.trim().length) params.set('search', query.trim())\n if (options?.excludeLinkedDealId) params.set('excludeLinkedDealId', options.excludeLinkedDealId)\n const call = await apiCall<Record<string, unknown>>(`/api/customers/people?${params.toString()}`)\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? String(call.result?.error) : 'Failed to search people')\n }\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n return {\n items: items\n .map((item: unknown) => (item && typeof item === 'object' ? extractPersonOption(item as Record<string, unknown>) : null))\n .filter((entry: EntityOption | null): entry is EntityOption => entry !== null),\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n }, [options?.excludeLinkedDealId])\n\n const searchPeople = React.useCallback(async (query: string): Promise<EntityOption[]> => {\n const result = await searchPeoplePage(query, 1)\n return result.items\n }, [searchPeoplePage])\n\n const fetchPeopleByIds = React.useCallback(async (ids: string[]): Promise<EntityOption[]> => {\n const unique = sanitizeIdList(ids)\n if (!unique.length) return []\n try {\n const params = new URLSearchParams({\n ids: unique.join(','),\n pageSize: String(Math.max(unique.length, 1)),\n })\n const call = await apiCall<Record<string, unknown>>(`/api/customers/people?${params.toString()}`)\n if (!call.ok) throw new Error()\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n const optionMap = new Map<string, EntityOption>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const option = extractPersonOption(item as Record<string, unknown>)\n if (option?.id) optionMap.set(option.id, option)\n })\n return unique.map((id) => optionMap.get(id) ?? { id, label: id })\n } catch {\n return unique.map((id) => ({ id, label: id }))\n }\n }, [])\n\n const searchCompaniesPage = React.useCallback(async (query: string, page = 1): Promise<{ items: EntityOption[]; totalPages: number }> => {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n sortField: 'name',\n sortDir: 'asc',\n })\n if (query.trim().length) params.set('search', query.trim())\n if (options?.excludeLinkedDealId) params.set('excludeLinkedDealId', options.excludeLinkedDealId)\n const call = await apiCall<Record<string, unknown>>(`/api/customers/companies?${params.toString()}`)\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? String(call.result?.error) : 'Failed to search companies')\n }\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n return {\n items: items\n .map((item: unknown) => (item && typeof item === 'object' ? extractCompanyOption(item as Record<string, unknown>) : null))\n .filter((entry: EntityOption | null): entry is EntityOption => entry !== null),\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n }, [options?.excludeLinkedDealId])\n\n const searchCompanies = React.useCallback(async (query: string): Promise<EntityOption[]> => {\n const result = await searchCompaniesPage(query, 1)\n return result.items\n }, [searchCompaniesPage])\n\n const fetchCompaniesByIds = React.useCallback(async (ids: string[]): Promise<EntityOption[]> => {\n const unique = sanitizeIdList(ids)\n if (!unique.length) return []\n try {\n const params = new URLSearchParams({\n ids: unique.join(','),\n pageSize: String(Math.max(unique.length, 1)),\n })\n const call = await apiCall<Record<string, unknown>>(`/api/customers/companies?${params.toString()}`)\n if (!call.ok) throw new Error()\n const payload = call.result ?? {}\n const items = Array.isArray(payload.items) ? payload.items : []\n const optionMap = new Map<string, EntityOption>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const option = extractCompanyOption(item as Record<string, unknown>)\n if (option?.id) optionMap.set(option.id, option)\n })\n return unique.map((id) => optionMap.get(id) ?? { id, label: id })\n } catch {\n return unique.map((id) => ({ id, label: id }))\n }\n }, [])\n\n return {\n searchPeople,\n searchPeoplePage,\n fetchPeopleByIds,\n searchCompanies,\n searchCompaniesPage,\n fetchCompaniesByIds,\n }\n}\n\nexport function DealPeopleSelector({\n value,\n onChange,\n options = [],\n disabled = false,\n autoFocus = false,\n minQueryLength,\n}: {\n value: string[]\n onChange: (next: string[]) => void\n options?: EntityOption[]\n disabled?: boolean\n autoFocus?: boolean\n minQueryLength?: number\n}) {\n const t = useT()\n const { searchPeople, fetchPeopleByIds } = useDealAssociationLookups()\n\n return (\n <EntityMultiSelect\n value={value}\n onChange={onChange}\n initialOptions={options}\n placeholder={t('customers.deals.form.people.searchPlaceholder', 'Search people\u2026')}\n emptyLabel={t('customers.deals.form.people.empty', 'No people linked yet.')}\n loadingLabel={t('customers.deals.form.people.loading', 'Searching people\u2026')}\n noResultsLabel={t('customers.deals.form.people.noResults', 'No people match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.people.error', 'Failed to load people.')}\n search={searchPeople}\n fetchByIds={fetchPeopleByIds}\n disabled={disabled}\n autoFocus={autoFocus}\n minQueryLength={minQueryLength}\n />\n )\n}\n\nexport function DealCompaniesSelector({\n value,\n onChange,\n options = [],\n disabled = false,\n minQueryLength,\n}: {\n value: string[]\n onChange: (next: string[]) => void\n options?: EntityOption[]\n disabled?: boolean\n minQueryLength?: number\n}) {\n const t = useT()\n const { searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups()\n\n return (\n <EntityMultiSelect\n value={value}\n onChange={onChange}\n initialOptions={options}\n placeholder={t('customers.deals.form.companies.searchPlaceholder', 'Search companies\u2026')}\n emptyLabel={t('customers.deals.form.companies.empty', 'No companies linked yet.')}\n loadingLabel={t('customers.deals.form.companies.loading', 'Searching companies\u2026')}\n noResultsLabel={t('customers.deals.form.companies.noResults', 'No companies match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.companies.error', 'Failed to load companies.')}\n search={searchCompanies}\n fetchByIds={fetchCompaniesByIds}\n disabled={disabled}\n minQueryLength={minQueryLength}\n />\n )\n}\n\nexport function DealForm({\n mode,\n initialValues,\n onSubmit,\n onCancel,\n onDelete,\n submitLabel,\n cancelLabel,\n isSubmitting = false,\n embedded = true,\n trackDirtyWhenEmbedded = false,\n title,\n backHref,\n hideFooterActions = false,\n onDirtyChange,\n collapsibleGroups,\n sortableGroups,\n singleColumnGroups = false,\n showAssociationsGroup = true,\n showVersionHistory = true,\n showCancelAction = true,\n initialPipelineOptions,\n initialPipelineStageOptions,\n injectionSpotId,\n optimisticLockUpdatedAt,\n}: DealFormProps) {\n const t = useT()\n const [pending, setPending] = React.useState(false)\n const {\n data: currencyDictionaryData,\n error: currencyDictionaryErrorRaw,\n isLoading: currencyDictionaryLoading,\n refetch: refetchCurrencyDictionary,\n } = useCurrencyDictionary()\n const currencyDictionaryError = currencyDictionaryErrorRaw\n ? currencyDictionaryErrorRaw instanceof Error\n ? currencyDictionaryErrorRaw.message\n : String(currencyDictionaryErrorRaw)\n : null\n\n const translate = React.useCallback(\n (key: string, fallback: string) => {\n const value = t(key)\n return value === key ? fallback : value\n },\n [t],\n )\n\n const dictionaryLabels = React.useMemo(() => ({\n status: createDictionarySelectLabels('deal-statuses', translate),\n }), [translate])\n\n const resolvedCurrencyError = React.useMemo(() => {\n if (currencyDictionaryError) return currencyDictionaryError\n if (!currencyDictionaryLoading && !currencyDictionaryData) {\n return t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.')\n }\n return null\n }, [currencyDictionaryData, currencyDictionaryError, currencyDictionaryLoading, t])\n\n const fetchCurrencyOptions = React.useCallback(async () => {\n let payload = currencyDictionaryData ?? null\n if (!payload) {\n try {\n payload = await refetchCurrencyDictionary()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err ?? '')\n throw new Error(message || t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'))\n }\n }\n if (!payload) {\n throw new Error(t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.'))\n }\n const priorityOrder = new Map<string, number>()\n CURRENCY_PRIORITY.forEach((code, index) => priorityOrder.set(code, index))\n const prioritized: { value: string; label: string; color: string | null; icon: string | null }[] = []\n const remainder: { value: string; label: string; color: string | null; icon: string | null }[] = []\n payload.entries.forEach((entry) => {\n const value = entry.value.toUpperCase()\n const label = entry.label && entry.label.length ? `${value} \u2013 ${entry.label}` : value\n const option = { value, label, color: null, icon: null }\n if (priorityOrder.has(value)) prioritized.push(option)\n else remainder.push(option)\n })\n prioritized.sort((a, b) => (priorityOrder.get(a.value)! - priorityOrder.get(b.value)!))\n remainder.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))\n return [...prioritized, ...remainder]\n }, [currencyDictionaryData, refetchCurrencyDictionary, t])\n\n const currencyDictionaryLabels = React.useMemo(() => ({\n placeholder: t('customers.deals.form.currency.placeholder', 'Select currency\u2026'),\n addLabel: t('customers.deals.form.currency.add', 'Add currency'),\n dialogTitle: t('customers.deals.form.currency.dialogTitle', 'Add currency'),\n valueLabel: t('customers.deals.form.currency.valueLabel', 'Currency code'),\n valuePlaceholder: t('customers.deals.form.currency.valuePlaceholder', 'e.g. USD'),\n labelLabel: t('customers.deals.form.currency.labelLabel', 'Label'),\n labelPlaceholder: t('customers.deals.form.currency.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('customers.deals.form.currency.error.required', 'Currency code is required.'),\n cancelLabel: t('customers.deals.form.currency.cancel', 'Cancel'),\n saveLabel: t('customers.deals.form.currency.save', 'Save'),\n errorLoad: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n errorSave: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n loadingLabel: t('customers.deals.form.currency.loading', 'Loading currencies\u2026'),\n manageTitle: t('customers.deals.form.currency.manage', 'Manage currency dictionary'),\n }), [t])\n\n const { searchPeople, fetchPeopleByIds, searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups()\n\n const disabled = pending || isSubmitting\n const canDelete = mode === 'edit' && typeof onDelete === 'function'\n\n const mountedRef = React.useRef(false)\n const seedPipelineOptions = React.useMemo(\n () => normalizePipelineOptions(initialPipelineOptions),\n [initialPipelineOptions],\n )\n const seedPipelineStageOptions = React.useMemo(\n () => Array.isArray(initialPipelineStageOptions) ? normalizePipelineStageOptions(initialPipelineStageOptions) : null,\n [initialPipelineStageOptions],\n )\n\n const [pipelines, setPipelines] = React.useState<PipelineOption[]>(() => seedPipelineOptions)\n const [pipelineStages, setPipelineStages] = React.useState<PipelineStageOption[]>(() => seedPipelineStageOptions ?? [])\n\n React.useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n }\n }, [])\n\n const loadStagesForPipeline = React.useCallback(async (pipelineId: string) => {\n if (!pipelineId) {\n if (mountedRef.current) setPipelineStages([])\n return\n }\n try {\n const stages = await fetchPipelineStageOptions(pipelineId)\n if (mountedRef.current) setPipelineStages(stages)\n } catch {\n if (mountedRef.current) setPipelineStages([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n ;(async () => {\n try {\n const loaded = await fetchPipelineOptions()\n if (cancelled || !mountedRef.current) return\n setPipelines(mergePipelineOptions(seedPipelineOptions, loaded))\n } catch {\n if (!cancelled && mountedRef.current && seedPipelineOptions.length > 0) {\n setPipelines(seedPipelineOptions)\n }\n }\n })().catch(() => {})\n return () => { cancelled = true }\n }, [seedPipelineOptions])\n\n React.useEffect(() => {\n const pid = initialValues?.pipelineId\n if (typeof pid === 'string' && pid.length) {\n if (seedPipelineStageOptions) {\n setPipelineStages(seedPipelineStageOptions)\n return\n }\n loadStagesForPipeline(pid).catch(() => {})\n } else {\n setPipelineStages([])\n }\n }, [initialValues?.pipelineId, loadStagesForPipeline, seedPipelineStageOptions])\n\n const baseFields = React.useMemo<CrudField[]>(() => [\n {\n id: 'title',\n label: t('customers.people.detail.deals.fields.title', 'Title'),\n type: 'text',\n required: true,\n },\n {\n id: 'status',\n label: t('customers.people.detail.deals.fields.status', 'Status'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <DictionarySelectField\n kind=\"deal-statuses\"\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n labels={dictionaryLabels.status}\n selectClassName=\"w-full\"\n />\n ),\n } as CrudField,\n {\n id: 'pipelineId',\n label: t('customers.people.detail.deals.fields.pipeline', 'Pipeline'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue, setFormValue }) => (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => {\n setValue(next ?? '')\n setFormValue?.('pipelineStageId', '')\n loadStagesForPipeline(next ?? '').catch(() => {})\n }}\n disabled={disabled}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('customers.deals.form.pipeline.placeholder', 'Select pipeline\u2026')} />\n </SelectTrigger>\n <SelectContent>\n {pipelines.map((p) => (\n <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n } as CrudField,\n {\n id: 'pipelineStageId',\n label: t('customers.people.detail.deals.fields.pipelineStage', 'Pipeline stage'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => setValue(next ?? '')}\n disabled={disabled || !pipelineStages.length}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('customers.deals.form.pipelineStage.placeholder', 'Select stage\u2026')} />\n </SelectTrigger>\n <SelectContent>\n {pipelineStages.map((s) => (\n <SelectItem key={s.id} value={s.id}>{s.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ),\n } as CrudField,\n {\n id: 'valueAmount',\n label: t('customers.people.detail.deals.fields.valueAmount', 'Amount'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'valueCurrency',\n label: t('customers.people.detail.deals.fields.valueCurrency', 'Currency'),\n type: 'custom',\n layout: 'half',\n component: ({ value, setValue }) => (\n <div className=\"space-y-1\">\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => setValue(next ?? '')}\n fetchOptions={fetchCurrencyOptions}\n labels={currencyDictionaryLabels}\n manageHref=\"/backend/config/dictionaries?key=currency\"\n allowInlineCreate={false}\n allowAppearance={false}\n selectClassName=\"w-full\"\n sortOptions=\"none\"\n disabled={disabled}\n showLabelInput={false}\n />\n {resolvedCurrencyError ? (\n <div className=\"text-xs text-muted-foreground\">{resolvedCurrencyError}</div>\n ) : null}\n </div>\n ),\n } as CrudField,\n {\n id: 'probability',\n label: t('customers.people.detail.deals.fields.probability', 'Probability (%)'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'expectedCloseAt',\n label: t('customers.people.detail.deals.fields.expectedCloseAt', 'Expected close'),\n type: 'date',\n layout: 'half',\n },\n {\n id: 'description',\n label: t('customers.people.detail.deals.fields.description', 'Description'),\n type: 'textarea',\n },\n {\n id: 'personIds',\n label: t('customers.people.detail.deals.fields.people', 'People'),\n type: 'custom',\n component: ({ value, setValue, autoFocus }) => (\n <EntityMultiSelect\n value={Array.isArray(value) ? value : []}\n onChange={(next) => setValue(next)}\n placeholder={t('customers.deals.form.people.searchPlaceholder', 'Search people\u2026')}\n emptyLabel={t('customers.deals.form.people.empty', 'No people linked yet.')}\n loadingLabel={t('customers.deals.form.people.loading', 'Searching people\u2026')}\n noResultsLabel={t('customers.deals.form.people.noResults', 'No people match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.people.error', 'Failed to load people.')}\n search={searchPeople}\n fetchByIds={fetchPeopleByIds}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n ),\n } as CrudField,\n {\n id: 'companyIds',\n label: t('customers.people.detail.deals.fields.companies', 'Companies'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <EntityMultiSelect\n value={Array.isArray(value) ? value : []}\n onChange={(next) => setValue(next)}\n placeholder={t('customers.deals.form.companies.searchPlaceholder', 'Search companies\u2026')}\n emptyLabel={t('customers.deals.form.companies.empty', 'No companies linked yet.')}\n loadingLabel={t('customers.deals.form.companies.loading', 'Searching companies\u2026')}\n noResultsLabel={t('customers.deals.form.companies.noResults', 'No companies match your search.')}\n removeLabel={t('customers.deals.form.assignees.remove', 'Remove')}\n errorLabel={t('customers.deals.form.companies.error', 'Failed to load companies.')}\n search={searchCompanies}\n fetchByIds={fetchCompaniesByIds}\n disabled={disabled}\n />\n ),\n } as CrudField,\n ], [currencyDictionaryLabels, fetchCurrencyOptions, resolvedCurrencyError, pipelines, pipelineStages, loadStagesForPipeline, dictionaryLabels.status, disabled, fetchCompaniesByIds, fetchPeopleByIds, searchCompanies, searchPeople, t])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const nextGroups: CrudFormGroup[] = [\n {\n id: 'details',\n title: t('customers.people.detail.deals.form.details', 'Deal details'),\n column: 1,\n fields: ['title', 'status', 'pipelineId', 'pipelineStageId', 'valueAmount', 'valueCurrency', 'probability', 'expectedCloseAt', 'description'],\n },\n ...(showAssociationsGroup\n ? [{\n id: 'associations',\n title: t('customers.people.detail.deals.form.associations', 'Associations'),\n column: 1,\n fields: ['personIds', 'companyIds'],\n } satisfies CrudFormGroup]\n : []),\n {\n id: 'custom',\n title: t('customers.people.detail.deals.form.customFields', 'Custom fields'),\n column: 2,\n kind: 'customFields',\n },\n ]\n return nextGroups.map((group) => (singleColumnGroups ? { ...group, column: 1 } : group))\n }, [showAssociationsGroup, singleColumnGroups, t])\n\n const embeddedInitialValues = React.useMemo(() => {\n const normalizeNumber = (value: unknown): number | null => {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isNaN(parsed) ? null : parsed\n }\n return null\n }\n\n const resolveIdsFromSource = (source: unknown): string[] => {\n if (Array.isArray(source)) {\n return sanitizeIdList(\n source.map((entry) => {\n if (typeof entry === 'string') return entry\n if (entry && typeof entry === 'object' && 'id' in entry) {\n const candidate = (entry as { id?: unknown }).id\n if (typeof candidate === 'string') return candidate\n }\n return null\n }),\n )\n }\n return []\n }\n\n return {\n id: typeof initialValues?.id === 'string' ? initialValues.id : undefined,\n title: initialValues?.title ?? '',\n status: initialValues?.status ?? '',\n pipelineStage: initialValues?.pipelineStage ?? '',\n pipelineId: initialValues?.pipelineId ?? (typeof (initialValues as Record<string, unknown>)?.pipeline_id === 'string' ? (initialValues as Record<string, unknown>).pipeline_id as string : ''),\n pipelineStageId: initialValues?.pipelineStageId ?? (typeof (initialValues as Record<string, unknown>)?.pipeline_stage_id === 'string' ? (initialValues as Record<string, unknown>).pipeline_stage_id as string : ''),\n valueAmount: normalizeNumber(initialValues?.valueAmount ?? null),\n valueCurrency: normalizeCurrency(initialValues?.valueCurrency ?? null),\n probability: normalizeNumber(initialValues?.probability ?? null),\n expectedCloseAt: toDateInputValue(initialValues?.expectedCloseAt ?? null),\n description: initialValues?.description ?? '',\n personIds: sanitizeIdList(initialValues?.personIds ?? resolveIdsFromSource(initialValues?.people)),\n companyIds: sanitizeIdList(initialValues?.companyIds ?? resolveIdsFromSource(initialValues?.companies)),\n ...Object.fromEntries(\n Object.entries(initialValues ?? {})\n .filter(([key]) => key.startsWith('cf_'))\n .map(([key, value]) => [key, value]),\n ),\n }\n }, [initialValues])\n\n const handleSubmit = React.useCallback(\n async (values: Record<string, unknown>) => {\n if (pending || isSubmitting) return\n setPending(true)\n try {\n const parsed = schema.safeParse(values)\n if (!parsed.success) {\n throw buildDealValidationError(parsed.error.issues, t)\n }\n const expectedCloseAt =\n parsed.data.expectedCloseAt && parsed.data.expectedCloseAt.length\n ? new Date(parsed.data.expectedCloseAt).toISOString()\n : undefined\n const personIds = sanitizeIdList(parsed.data.personIds)\n const companyIds = sanitizeIdList(parsed.data.companyIds)\n const base: DealFormBaseValues = {\n title: parsed.data.title,\n status: parsed.data.status || undefined,\n pipelineStage: parsed.data.pipelineStage || undefined,\n pipelineId: parsed.data.pipelineId || undefined,\n pipelineStageId: parsed.data.pipelineStageId || undefined,\n valueAmount:\n typeof parsed.data.valueAmount === 'number' ? parsed.data.valueAmount : undefined,\n valueCurrency: parsed.data.valueCurrency || undefined,\n probability:\n typeof parsed.data.probability === 'number' ? parsed.data.probability : undefined,\n expectedCloseAt,\n description: parsed.data.description && parsed.data.description.length\n ? parsed.data.description\n : undefined,\n personIds,\n companyIds,\n }\n const customEntries = collectCustomFieldValues(values, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n })\n await onSubmit({ base, custom: customEntries })\n } finally {\n setPending(false)\n }\n },\n [isSubmitting, onSubmit, pending, t],\n )\n\n return (\n <CrudForm<Record<string, unknown>>\n embedded={embedded}\n trackDirtyWhenEmbedded={trackDirtyWhenEmbedded}\n title={title}\n backHref={backHref}\n hideFooterActions={hideFooterActions}\n onDirtyChange={onDirtyChange}\n injectionSpotId={injectionSpotId}\n optimisticLockUpdatedAt={optimisticLockUpdatedAt}\n collapsibleGroups={collapsibleGroups}\n sortableGroups={sortableGroups}\n versionHistory={showVersionHistory && mode === 'edit' && initialValues?.id\n ? { resourceKind: 'customers.deal', resourceId: String(initialValues.id) }\n : undefined}\n schema={schema}\n fields={baseFields}\n groups={groups}\n entityIds={DEAL_ENTITY_IDS}\n initialValues={embeddedInitialValues}\n onSubmit={handleSubmit}\n onDelete={canDelete ? onDelete : undefined}\n deleteVisible={canDelete}\n submitLabel={\n submitLabel ??\n (mode === 'edit'\n ? t('customers.people.detail.deals.update', 'Update deal (\u2318/Ctrl + Enter)')\n : t('customers.people.detail.deals.save', 'Save deal (\u2318/Ctrl + Enter)'))\n }\n extraActions={showCancelAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={pending || isSubmitting}\n >\n {cancelLabel ?? t('customers.people.detail.deals.cancel', 'Cancel')}\n </Button>\n ) : undefined}\n />\n )\n}\n\nexport function buildDealValidationError(issues: z.ZodIssue[], t: (key: string, fallback?: string) => string) {\n const issue = issues[0]\n const message =\n typeof issue?.message === 'string'\n ? issue.message\n : t('customers.people.detail.deals.error', 'Failed to save deal.')\n const firstPath = Array.isArray(issue?.path) ? issue?.path?.[0] : undefined\n const field = typeof firstPath === 'string' ? firstPath : undefined\n throw createCrudFormError(message, field ? { [field]: message } : undefined)\n}\n\nexport default DealForm\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -254,16 +254,16 @@
|
|
|
254
254
|
"zod": "^4.4.3"
|
|
255
255
|
},
|
|
256
256
|
"peerDependencies": {
|
|
257
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
258
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
259
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
257
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
258
|
+
"@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
259
|
+
"@open-mercato/ui": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
260
260
|
"react": "^19.0.0",
|
|
261
261
|
"react-dom": "^19.0.0"
|
|
262
262
|
},
|
|
263
263
|
"devDependencies": {
|
|
264
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
265
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
266
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
264
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
265
|
+
"@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
266
|
+
"@open-mercato/ui": "0.6.8-develop.7012.1.1dbd6f5fbd",
|
|
267
267
|
"@testing-library/dom": "^10.4.1",
|
|
268
268
|
"@testing-library/jest-dom": "^7.0.0",
|
|
269
269
|
"@testing-library/react": "^16.3.1",
|
|
@@ -102,6 +102,12 @@ type EntityMultiSelectProps = {
|
|
|
102
102
|
fetchByIds: (ids: string[]) => Promise<EntityOption[]>
|
|
103
103
|
disabled?: boolean
|
|
104
104
|
autoFocus?: boolean
|
|
105
|
+
/**
|
|
106
|
+
* Minimum trimmed query length before a lookup request is issued. Defaults to
|
|
107
|
+
* `1`, so a blank input never queries the address book (issue #5118). Set `0`
|
|
108
|
+
* to opt back into prefetching an unfiltered first page on mount.
|
|
109
|
+
*/
|
|
110
|
+
minQueryLength?: number
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
const DEAL_ENTITY_IDS = [E.customers.customer_deal]
|
|
@@ -353,6 +359,7 @@ function EntityMultiSelect({
|
|
|
353
359
|
fetchByIds,
|
|
354
360
|
disabled = false,
|
|
355
361
|
autoFocus = false,
|
|
362
|
+
minQueryLength = 1,
|
|
356
363
|
}: EntityMultiSelectProps) {
|
|
357
364
|
const [input, setInput] = React.useState('')
|
|
358
365
|
const [suggestions, setSuggestions] = React.useState<EntityOption[]>([])
|
|
@@ -401,11 +408,18 @@ function EntityMultiSelect({
|
|
|
401
408
|
setLoading(false)
|
|
402
409
|
return
|
|
403
410
|
}
|
|
411
|
+
const term = input.trim()
|
|
412
|
+
if (term.length < minQueryLength) {
|
|
413
|
+
setSuggestions([])
|
|
414
|
+
setLoading(false)
|
|
415
|
+
setError(null)
|
|
416
|
+
return
|
|
417
|
+
}
|
|
404
418
|
let cancelled = false
|
|
405
419
|
const handler = window.setTimeout(async () => {
|
|
406
420
|
setLoading(true)
|
|
407
421
|
try {
|
|
408
|
-
const results = await search(
|
|
422
|
+
const results = await search(term)
|
|
409
423
|
if (cancelled) return
|
|
410
424
|
setSuggestions(results)
|
|
411
425
|
setCache((prev) => {
|
|
@@ -429,7 +443,7 @@ function EntityMultiSelect({
|
|
|
429
443
|
cancelled = true
|
|
430
444
|
window.clearTimeout(handler)
|
|
431
445
|
}
|
|
432
|
-
}, [disabled, errorLabel, input, search])
|
|
446
|
+
}, [disabled, errorLabel, input, minQueryLength, search])
|
|
433
447
|
|
|
434
448
|
const filteredSuggestions = React.useMemo(
|
|
435
449
|
() => suggestions.filter((option) => !normalizedValue.includes(option.id)),
|
|
@@ -660,12 +674,14 @@ export function DealPeopleSelector({
|
|
|
660
674
|
options = [],
|
|
661
675
|
disabled = false,
|
|
662
676
|
autoFocus = false,
|
|
677
|
+
minQueryLength,
|
|
663
678
|
}: {
|
|
664
679
|
value: string[]
|
|
665
680
|
onChange: (next: string[]) => void
|
|
666
681
|
options?: EntityOption[]
|
|
667
682
|
disabled?: boolean
|
|
668
683
|
autoFocus?: boolean
|
|
684
|
+
minQueryLength?: number
|
|
669
685
|
}) {
|
|
670
686
|
const t = useT()
|
|
671
687
|
const { searchPeople, fetchPeopleByIds } = useDealAssociationLookups()
|
|
@@ -685,6 +701,7 @@ export function DealPeopleSelector({
|
|
|
685
701
|
fetchByIds={fetchPeopleByIds}
|
|
686
702
|
disabled={disabled}
|
|
687
703
|
autoFocus={autoFocus}
|
|
704
|
+
minQueryLength={minQueryLength}
|
|
688
705
|
/>
|
|
689
706
|
)
|
|
690
707
|
}
|
|
@@ -694,11 +711,13 @@ export function DealCompaniesSelector({
|
|
|
694
711
|
onChange,
|
|
695
712
|
options = [],
|
|
696
713
|
disabled = false,
|
|
714
|
+
minQueryLength,
|
|
697
715
|
}: {
|
|
698
716
|
value: string[]
|
|
699
717
|
onChange: (next: string[]) => void
|
|
700
718
|
options?: EntityOption[]
|
|
701
719
|
disabled?: boolean
|
|
720
|
+
minQueryLength?: number
|
|
702
721
|
}) {
|
|
703
722
|
const t = useT()
|
|
704
723
|
const { searchCompanies, fetchCompaniesByIds } = useDealAssociationLookups()
|
|
@@ -717,6 +736,7 @@ export function DealCompaniesSelector({
|
|
|
717
736
|
search={searchCompanies}
|
|
718
737
|
fetchByIds={fetchCompaniesByIds}
|
|
719
738
|
disabled={disabled}
|
|
739
|
+
minQueryLength={minQueryLength}
|
|
720
740
|
/>
|
|
721
741
|
)
|
|
722
742
|
}
|