@open-mercato/core 0.6.4-develop.3929.1.fcf7afece2 → 0.6.4-develop.3944.1.4100aa7fbe

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.
Files changed (72) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/backend/customers/deals/create/page.js +3 -61
  3. package/dist/modules/customers/backend/customers/deals/create/page.js.map +2 -2
  4. package/dist/modules/customers/components/detail/DealForm.js +2 -0
  5. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  6. package/dist/modules/customers/components/detail/create/CreateDealForm.js +233 -0
  7. package/dist/modules/customers/components/detail/create/CreateDealForm.js.map +7 -0
  8. package/dist/modules/customers/components/detail/create/DealAssociationsField.js +209 -0
  9. package/dist/modules/customers/components/detail/create/DealAssociationsField.js.map +7 -0
  10. package/dist/modules/customers/components/detail/create/DealAssociationsSection.js +67 -0
  11. package/dist/modules/customers/components/detail/create/DealAssociationsSection.js.map +7 -0
  12. package/dist/modules/customers/components/detail/create/DealCreateSidebar.js +73 -0
  13. package/dist/modules/customers/components/detail/create/DealCreateSidebar.js.map +7 -0
  14. package/dist/modules/customers/components/detail/create/DealCurrencyField.js +92 -0
  15. package/dist/modules/customers/components/detail/create/DealCurrencyField.js.map +7 -0
  16. package/dist/modules/customers/components/detail/create/DealCustomAttributes.js +81 -0
  17. package/dist/modules/customers/components/detail/create/DealCustomAttributes.js.map +7 -0
  18. package/dist/modules/customers/components/detail/create/DealDetailsFields.js +171 -0
  19. package/dist/modules/customers/components/detail/create/DealDetailsFields.js.map +7 -0
  20. package/dist/modules/customers/components/detail/create/DealFormField.js +24 -0
  21. package/dist/modules/customers/components/detail/create/DealFormField.js.map +7 -0
  22. package/dist/modules/customers/components/detail/create/DealSectionCard.js +29 -0
  23. package/dist/modules/customers/components/detail/create/DealSectionCard.js.map +7 -0
  24. package/dist/modules/customers/components/detail/create/DealTipsCard.js +19 -0
  25. package/dist/modules/customers/components/detail/create/DealTipsCard.js.map +7 -0
  26. package/dist/modules/customers/components/detail/create/PipelineSelect.js +41 -0
  27. package/dist/modules/customers/components/detail/create/PipelineSelect.js.map +7 -0
  28. package/dist/modules/customers/components/detail/create/PipelineStageSelect.js +49 -0
  29. package/dist/modules/customers/components/detail/create/PipelineStageSelect.js.map +7 -0
  30. package/dist/modules/customers/components/detail/create/SuffixInput.js +21 -0
  31. package/dist/modules/customers/components/detail/create/SuffixInput.js.map +7 -0
  32. package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js +270 -0
  33. package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js.map +7 -0
  34. package/dist/modules/customers/components/detail/create/dealFormTypes.js +17 -0
  35. package/dist/modules/customers/components/detail/create/dealFormTypes.js.map +7 -0
  36. package/dist/modules/customers/components/detail/create/dealNumericInput.js +16 -0
  37. package/dist/modules/customers/components/detail/create/dealNumericInput.js.map +7 -0
  38. package/dist/modules/customers/components/detail/create/useDealCustomFields.js +93 -0
  39. package/dist/modules/customers/components/detail/create/useDealCustomFields.js.map +7 -0
  40. package/dist/modules/customers/components/detail/create/useDealPipelines.js +59 -0
  41. package/dist/modules/customers/components/detail/create/useDealPipelines.js.map +7 -0
  42. package/dist/modules/customers/components/formConfig.js +4 -2
  43. package/dist/modules/customers/components/formConfig.js.map +2 -2
  44. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +5 -2
  45. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
  46. package/package.json +7 -7
  47. package/src/modules/customers/backend/customers/deals/create/page.tsx +3 -64
  48. package/src/modules/customers/components/detail/DealForm.tsx +2 -0
  49. package/src/modules/customers/components/detail/create/CreateDealForm.tsx +254 -0
  50. package/src/modules/customers/components/detail/create/DealAssociationsField.tsx +253 -0
  51. package/src/modules/customers/components/detail/create/DealAssociationsSection.tsx +72 -0
  52. package/src/modules/customers/components/detail/create/DealCreateSidebar.tsx +79 -0
  53. package/src/modules/customers/components/detail/create/DealCurrencyField.tsx +108 -0
  54. package/src/modules/customers/components/detail/create/DealCustomAttributes.tsx +118 -0
  55. package/src/modules/customers/components/detail/create/DealDetailsFields.tsx +171 -0
  56. package/src/modules/customers/components/detail/create/DealFormField.tsx +39 -0
  57. package/src/modules/customers/components/detail/create/DealSectionCard.tsx +40 -0
  58. package/src/modules/customers/components/detail/create/DealTipsCard.tsx +26 -0
  59. package/src/modules/customers/components/detail/create/PipelineSelect.tsx +55 -0
  60. package/src/modules/customers/components/detail/create/PipelineStageSelect.tsx +70 -0
  61. package/src/modules/customers/components/detail/create/SuffixInput.tsx +20 -0
  62. package/src/modules/customers/components/detail/create/dealCustomFieldControl.tsx +310 -0
  63. package/src/modules/customers/components/detail/create/dealFormTypes.ts +29 -0
  64. package/src/modules/customers/components/detail/create/dealNumericInput.ts +20 -0
  65. package/src/modules/customers/components/detail/create/useDealCustomFields.ts +118 -0
  66. package/src/modules/customers/components/detail/create/useDealPipelines.ts +80 -0
  67. package/src/modules/customers/components/formConfig.tsx +3 -0
  68. package/src/modules/customers/i18n/de.json +26 -0
  69. package/src/modules/customers/i18n/en.json +26 -0
  70. package/src/modules/customers/i18n/es.json +26 -0
  71. package/src/modules/customers/i18n/pl.json +26 -0
  72. package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +12 -1
@@ -0,0 +1,233 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Briefcase, Save } from "lucide-react";
6
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
7
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
8
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
9
+ import { createCrud } from "@open-mercato/ui/backend/utils/crud";
10
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
11
+ import { FormHeader } from "@open-mercato/ui/backend/forms";
12
+ import { Button } from "@open-mercato/ui/primitives/button";
13
+ import { Spinner } from "@open-mercato/ui/primitives/spinner";
14
+ import { dealFormSchema } from "../DealForm.js";
15
+ import { createDictionarySelectLabels } from "../utils.js";
16
+ import { DealSectionCard } from "./DealSectionCard.js";
17
+ import { DealDetailsFields } from "./DealDetailsFields.js";
18
+ import { DealAssociationsSection } from "./DealAssociationsSection.js";
19
+ import { DealCreateSidebar } from "./DealCreateSidebar.js";
20
+ import { useDealPipelines } from "./useDealPipelines.js";
21
+ import { useDealCustomFields } from "./useDealCustomFields.js";
22
+ import { EMPTY_VALUES } from "./dealFormTypes.js";
23
+ const CONTEXT_ID = "customers.deals.create";
24
+ const DEAL_ENTITY_ID = "customers:customer_deal";
25
+ const CUSTOM_FIELDS_MANAGE_HREF = `/backend/entities/system/${encodeURIComponent(DEAL_ENTITY_ID)}`;
26
+ function CreateDealForm({ returnTo }) {
27
+ const t = useT();
28
+ const router = useRouter();
29
+ const tr = React.useCallback(
30
+ (key, fallback, params) => translateWithFallback(t, key, fallback, params),
31
+ [t]
32
+ );
33
+ const [values, setValues] = React.useState(EMPTY_VALUES);
34
+ const [errors, setErrors] = React.useState({});
35
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
36
+ const { pipelines, stages, loadStages } = useDealPipelines();
37
+ const {
38
+ customValues,
39
+ customFieldsLoaded,
40
+ customCount,
41
+ handleCustomChange,
42
+ handleCustomAttributesLoaded,
43
+ validateCustomFields,
44
+ collectNormalizedCustomValues
45
+ } = useDealCustomFields(tr);
46
+ const { runMutation, retryLastMutation } = useGuardedMutation({
47
+ contextId: CONTEXT_ID,
48
+ blockedMessage: tr("ui.forms.flash.saveBlocked", "Save blocked by validation")
49
+ });
50
+ const statusLabels = React.useMemo(
51
+ () => createDictionarySelectLabels("deal-statuses", (key, fallback) => tr(key, fallback ?? key)),
52
+ [tr]
53
+ );
54
+ const patch = React.useCallback((partial) => {
55
+ setValues((current) => ({ ...current, ...partial }));
56
+ }, []);
57
+ const handlePipelineChange = React.useCallback(
58
+ (id) => {
59
+ patch({ pipelineId: id, pipelineStageId: "" });
60
+ loadStages(id).catch(() => {
61
+ });
62
+ },
63
+ [loadStages, patch]
64
+ );
65
+ const handleCancel = React.useCallback(() => {
66
+ router.push(returnTo);
67
+ }, [returnTo, router]);
68
+ const handleSubmit = React.useCallback(async () => {
69
+ if (isSubmitting) return;
70
+ if (!customFieldsLoaded) {
71
+ flash(tr("customers.deals.create.sections.custom.loading", "Loading custom fields..."), "error");
72
+ return;
73
+ }
74
+ const merged = { ...values, ...customValues };
75
+ const parsed = dealFormSchema.safeParse(merged);
76
+ if (!parsed.success) {
77
+ const fieldErrors = {};
78
+ for (const issue of parsed.error.issues) {
79
+ const key = typeof issue.path[0] === "string" ? issue.path[0] : void 0;
80
+ if (key && !fieldErrors[key]) fieldErrors[key] = tr(issue.message, issue.message);
81
+ }
82
+ setErrors(fieldErrors);
83
+ const firstMessage = Object.values(fieldErrors)[0];
84
+ if (firstMessage) flash(firstMessage, "error");
85
+ return;
86
+ }
87
+ const customFieldErrors = validateCustomFields(merged);
88
+ if (Object.keys(customFieldErrors).length) {
89
+ setErrors(customFieldErrors);
90
+ const firstMessage = Object.values(customFieldErrors)[0];
91
+ if (firstMessage) flash(firstMessage, "error");
92
+ return;
93
+ }
94
+ setErrors({});
95
+ setIsSubmitting(true);
96
+ try {
97
+ const data = parsed.data;
98
+ const expectedCloseAt = data.expectedCloseAt && data.expectedCloseAt.length ? new Date(data.expectedCloseAt).toISOString() : void 0;
99
+ const payload = {
100
+ title: data.title,
101
+ status: data.status || void 0,
102
+ pipelineId: data.pipelineId || void 0,
103
+ pipelineStageId: data.pipelineStageId || void 0,
104
+ valueAmount: typeof data.valueAmount === "number" ? data.valueAmount : void 0,
105
+ valueCurrency: data.valueCurrency || void 0,
106
+ probability: typeof data.probability === "number" ? data.probability : void 0,
107
+ expectedCloseAt,
108
+ description: data.description && data.description.length ? data.description : void 0,
109
+ personIds: values.personIds.length ? values.personIds : void 0,
110
+ companyIds: values.companyIds.length ? values.companyIds : void 0
111
+ };
112
+ const custom = collectNormalizedCustomValues(merged);
113
+ if (Object.keys(custom).length) payload.customFields = custom;
114
+ await runMutation({
115
+ operation: () => createCrud("customers/deals", payload, {
116
+ errorMessage: tr("customers.deals.create.error", "Failed to create deal.")
117
+ }),
118
+ context: { formId: CONTEXT_ID, resourceKind: "customers.deal", retryLastMutation },
119
+ mutationPayload: payload
120
+ });
121
+ flash(tr("customers.people.detail.deals.success", "Deal created."), "success");
122
+ router.push(returnTo);
123
+ } catch (err) {
124
+ const message = err instanceof Error ? err.message : tr("customers.deals.create.error", "Failed to create deal.");
125
+ flash(message, "error");
126
+ } finally {
127
+ setIsSubmitting(false);
128
+ }
129
+ }, [
130
+ collectNormalizedCustomValues,
131
+ customFieldsLoaded,
132
+ customValues,
133
+ isSubmitting,
134
+ retryLastMutation,
135
+ returnTo,
136
+ router,
137
+ runMutation,
138
+ tr,
139
+ validateCustomFields,
140
+ values
141
+ ]);
142
+ const onKeyDown = React.useCallback(
143
+ (event) => {
144
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
145
+ event.preventDefault();
146
+ handleSubmit();
147
+ }
148
+ },
149
+ [handleSubmit]
150
+ );
151
+ const onFormSubmit = React.useCallback(
152
+ (event) => {
153
+ event.preventDefault();
154
+ handleSubmit();
155
+ },
156
+ [handleSubmit]
157
+ );
158
+ const cancelLabel = tr("customers.deals.create.cancel", "Cancel");
159
+ const submitLabel = tr("customers.deals.create.submit", "Create deal");
160
+ const submitDisabled = !customFieldsLoaded;
161
+ return /* @__PURE__ */ jsxs("form", { className: "mx-auto max-w-screen-2xl", onKeyDown, onSubmit: onFormSubmit, noValidate: true, children: [
162
+ /* @__PURE__ */ jsx(
163
+ FormHeader,
164
+ {
165
+ backHref: returnTo,
166
+ backLabel: tr("customers.deals.create.back", "Back to deals")
167
+ }
168
+ ),
169
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-1 gap-5 lg:grid-cols-[minmax(0,1fr)_330px]", children: [
170
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
171
+ /* @__PURE__ */ jsx(
172
+ DealSectionCard,
173
+ {
174
+ icon: Briefcase,
175
+ title: tr("customers.deals.create.title", "Create deal"),
176
+ subtitle: tr("customers.deals.create.sections.details.subtitle", "Core opportunity info"),
177
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
178
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleCancel, disabled: isSubmitting, children: cancelLabel }),
179
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleSubmit, disabled: isSubmitting || submitDisabled, children: [
180
+ isSubmitting ? /* @__PURE__ */ jsx(Spinner, { className: "size-4" }) : /* @__PURE__ */ jsx(Save, { className: "size-4" }),
181
+ submitLabel
182
+ ] })
183
+ ] }),
184
+ children: /* @__PURE__ */ jsx(
185
+ DealDetailsFields,
186
+ {
187
+ values,
188
+ errors,
189
+ isSubmitting,
190
+ patch,
191
+ onPipelineChange: handlePipelineChange,
192
+ pipelines,
193
+ stages,
194
+ statusLabels,
195
+ tr
196
+ }
197
+ )
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsx(
201
+ DealAssociationsSection,
202
+ {
203
+ tr,
204
+ personIds: values.personIds,
205
+ companyIds: values.companyIds,
206
+ onPeopleChange: (next) => patch({ personIds: next }),
207
+ onCompaniesChange: (next) => patch({ companyIds: next }),
208
+ disabled: isSubmitting
209
+ }
210
+ )
211
+ ] }),
212
+ /* @__PURE__ */ jsx(
213
+ DealCreateSidebar,
214
+ {
215
+ tr,
216
+ customValues,
217
+ onCustomChange: handleCustomChange,
218
+ errors,
219
+ disabled: isSubmitting,
220
+ customCount,
221
+ manageHref: CUSTOM_FIELDS_MANAGE_HREF,
222
+ onCustomLoaded: handleCustomAttributesLoaded
223
+ }
224
+ )
225
+ ] })
226
+ ] });
227
+ }
228
+ var CreateDealForm_default = CreateDealForm;
229
+ export {
230
+ CreateDealForm,
231
+ CreateDealForm_default as default
232
+ };
233
+ //# sourceMappingURL=CreateDealForm.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/CreateDealForm.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Briefcase, Save } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { dealFormSchema } from '../DealForm'\nimport { createDictionarySelectLabels } from '../utils'\nimport { DealSectionCard } from './DealSectionCard'\nimport { DealDetailsFields } from './DealDetailsFields'\nimport { DealAssociationsSection } from './DealAssociationsSection'\nimport { DealCreateSidebar } from './DealCreateSidebar'\nimport { useDealPipelines } from './useDealPipelines'\nimport { useDealCustomFields } from './useDealCustomFields'\nimport { EMPTY_VALUES, type BaseValues } from './dealFormTypes'\n\nconst CONTEXT_ID = 'customers.deals.create'\nconst DEAL_ENTITY_ID = 'customers:customer_deal'\nconst CUSTOM_FIELDS_MANAGE_HREF = `/backend/entities/system/${encodeURIComponent(DEAL_ENTITY_ID)}`\n\nexport type CreateDealFormProps = {\n returnTo: string\n}\n\nexport function CreateDealForm({ returnTo }: CreateDealFormProps) {\n const t = useT()\n const router = useRouter()\n const tr = React.useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>) =>\n translateWithFallback(t, key, fallback, params),\n [t],\n )\n\n const [values, setValues] = React.useState<BaseValues>(EMPTY_VALUES)\n const [errors, setErrors] = React.useState<Record<string, string>>({})\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const { pipelines, stages, loadStages } = useDealPipelines()\n const {\n customValues,\n customFieldsLoaded,\n customCount,\n handleCustomChange,\n handleCustomAttributesLoaded,\n validateCustomFields,\n collectNormalizedCustomValues,\n } = useDealCustomFields(tr)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: CONTEXT_ID,\n blockedMessage: tr('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const statusLabels = React.useMemo(\n () => createDictionarySelectLabels('deal-statuses', (key, fallback) => tr(key, fallback ?? key)),\n [tr],\n )\n\n const patch = React.useCallback((partial: Partial<BaseValues>) => {\n setValues((current) => ({ ...current, ...partial }))\n }, [])\n\n const handlePipelineChange = React.useCallback(\n (id: string) => {\n patch({ pipelineId: id, pipelineStageId: '' })\n // loadStages resets stages to [] on failure; the rejection is intentionally ignored here.\n loadStages(id).catch(() => {})\n },\n [loadStages, patch],\n )\n\n const handleCancel = React.useCallback(() => {\n router.push(returnTo)\n }, [returnTo, router])\n\n const handleSubmit = React.useCallback(async () => {\n if (isSubmitting) return\n if (!customFieldsLoaded) {\n flash(tr('customers.deals.create.sections.custom.loading', 'Loading custom fields...'), 'error')\n return\n }\n const merged = { ...values, ...customValues }\n const parsed = dealFormSchema.safeParse(merged)\n if (!parsed.success) {\n const fieldErrors: Record<string, string> = {}\n for (const issue of parsed.error.issues) {\n const key = typeof issue.path[0] === 'string' ? issue.path[0] : undefined\n if (key && !fieldErrors[key]) fieldErrors[key] = tr(issue.message, issue.message)\n }\n setErrors(fieldErrors)\n const firstMessage = Object.values(fieldErrors)[0]\n if (firstMessage) flash(firstMessage, 'error')\n return\n }\n\n const customFieldErrors = validateCustomFields(merged)\n if (Object.keys(customFieldErrors).length) {\n setErrors(customFieldErrors)\n const firstMessage = Object.values(customFieldErrors)[0]\n if (firstMessage) flash(firstMessage, 'error')\n return\n }\n\n setErrors({})\n setIsSubmitting(true)\n try {\n const data = parsed.data\n const expectedCloseAt =\n data.expectedCloseAt && data.expectedCloseAt.length\n ? new Date(data.expectedCloseAt).toISOString()\n : undefined\n const payload: Record<string, unknown> = {\n title: data.title,\n status: data.status || undefined,\n pipelineId: data.pipelineId || undefined,\n pipelineStageId: data.pipelineStageId || undefined,\n valueAmount: typeof data.valueAmount === 'number' ? data.valueAmount : undefined,\n valueCurrency: data.valueCurrency || undefined,\n probability: typeof data.probability === 'number' ? data.probability : undefined,\n expectedCloseAt,\n description: data.description && data.description.length ? data.description : undefined,\n personIds: values.personIds.length ? values.personIds : undefined,\n companyIds: values.companyIds.length ? values.companyIds : undefined,\n }\n const custom = collectNormalizedCustomValues(merged)\n if (Object.keys(custom).length) payload.customFields = custom\n\n await runMutation({\n operation: () =>\n createCrud('customers/deals', payload, {\n errorMessage: tr('customers.deals.create.error', 'Failed to create deal.'),\n }),\n context: { formId: CONTEXT_ID, resourceKind: 'customers.deal', retryLastMutation },\n mutationPayload: payload,\n })\n flash(tr('customers.people.detail.deals.success', 'Deal created.'), 'success')\n router.push(returnTo)\n } catch (err) {\n const message = err instanceof Error ? err.message : tr('customers.deals.create.error', 'Failed to create deal.')\n flash(message, 'error')\n } finally {\n setIsSubmitting(false)\n }\n }, [\n collectNormalizedCustomValues,\n customFieldsLoaded,\n customValues,\n isSubmitting,\n retryLastMutation,\n returnTo,\n router,\n runMutation,\n tr,\n validateCustomFields,\n values,\n ])\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSubmit()\n }\n },\n [handleSubmit],\n )\n\n const onFormSubmit = React.useCallback(\n (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n handleSubmit()\n },\n [handleSubmit],\n )\n\n const cancelLabel = tr('customers.deals.create.cancel', 'Cancel')\n const submitLabel = tr('customers.deals.create.submit', 'Create deal')\n const submitDisabled = !customFieldsLoaded\n\n return (\n <form className=\"mx-auto max-w-screen-2xl\" onKeyDown={onKeyDown} onSubmit={onFormSubmit} noValidate>\n <FormHeader\n backHref={returnTo}\n backLabel={tr('customers.deals.create.back', 'Back to deals')}\n />\n\n <div className=\"mt-6 grid grid-cols-1 gap-5 lg:grid-cols-[minmax(0,1fr)_330px]\">\n <div className=\"space-y-4\">\n <DealSectionCard\n icon={Briefcase}\n title={tr('customers.deals.create.title', 'Create deal')}\n subtitle={tr('customers.deals.create.sections.details.subtitle', 'Core opportunity info')}\n actions={\n <>\n <Button type=\"button\" variant=\"outline\" onClick={handleCancel} disabled={isSubmitting}>\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={handleSubmit} disabled={isSubmitting || submitDisabled}>\n {isSubmitting ? <Spinner className=\"size-4\" /> : <Save className=\"size-4\" />}\n {submitLabel}\n </Button>\n </>\n }\n >\n <DealDetailsFields\n values={values}\n errors={errors}\n isSubmitting={isSubmitting}\n patch={patch}\n onPipelineChange={handlePipelineChange}\n pipelines={pipelines}\n stages={stages}\n statusLabels={statusLabels}\n tr={tr}\n />\n </DealSectionCard>\n\n <DealAssociationsSection\n tr={tr}\n personIds={values.personIds}\n companyIds={values.companyIds}\n onPeopleChange={(next) => patch({ personIds: next })}\n onCompaniesChange={(next) => patch({ companyIds: next })}\n disabled={isSubmitting}\n />\n </div>\n\n <DealCreateSidebar\n tr={tr}\n customValues={customValues}\n onCustomChange={handleCustomChange}\n errors={errors}\n disabled={isSubmitting}\n customCount={customCount}\n manageHref={CUSTOM_FIELDS_MANAGE_HREF}\n onCustomLoaded={handleCustomAttributesLoaded}\n />\n </div>\n </form>\n )\n}\n\nexport default CreateDealForm\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,209 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Building2, X } from "lucide-react";
5
+ import { Avatar } from "@open-mercato/ui/primitives/avatar";
6
+ import { Button } from "@open-mercato/ui/primitives/button";
7
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
8
+ import { SearchInput } from "@open-mercato/ui/primitives/search-input";
9
+ import { useDealAssociationLookups } from "../DealForm.js";
10
+ function sanitizeIdList(input) {
11
+ if (!Array.isArray(input)) return [];
12
+ const set = /* @__PURE__ */ new Set();
13
+ input.forEach((candidate) => {
14
+ if (typeof candidate !== "string") return;
15
+ const trimmed = candidate.trim();
16
+ if (!trimmed.length) return;
17
+ set.add(trimmed);
18
+ });
19
+ return Array.from(set);
20
+ }
21
+ function DealAssociationsField({
22
+ id,
23
+ kind,
24
+ value,
25
+ onChange,
26
+ disabled = false,
27
+ labels
28
+ }) {
29
+ const lookups = useDealAssociationLookups();
30
+ const search = kind === "people" ? lookups.searchPeople : lookups.searchCompanies;
31
+ const fetchByIds = kind === "people" ? lookups.fetchPeopleByIds : lookups.fetchCompaniesByIds;
32
+ const [input, setInput] = React.useState("");
33
+ const [suggestions, setSuggestions] = React.useState([]);
34
+ const [cache, setCache] = React.useState(() => /* @__PURE__ */ new Map());
35
+ const [loading, setLoading] = React.useState(false);
36
+ const [error, setError] = React.useState(null);
37
+ const normalizedValue = React.useMemo(() => sanitizeIdList(value), [value]);
38
+ React.useEffect(() => {
39
+ if (!normalizedValue.length) return;
40
+ const missing = normalizedValue.filter((id2) => !cache.has(id2));
41
+ if (!missing.length) return;
42
+ let cancelled = false;
43
+ (async () => {
44
+ try {
45
+ const entries = await fetchByIds(missing);
46
+ if (cancelled) return;
47
+ setCache((prev) => {
48
+ const next = new Map(prev);
49
+ entries.forEach((entry) => {
50
+ if (entry?.id) next.set(entry.id, entry);
51
+ });
52
+ return next;
53
+ });
54
+ } catch {
55
+ if (!cancelled) setError(labels.error);
56
+ }
57
+ })().catch(() => {
58
+ });
59
+ return () => {
60
+ cancelled = true;
61
+ };
62
+ }, [cache, fetchByIds, labels.error, normalizedValue]);
63
+ React.useEffect(() => {
64
+ const query = input.trim();
65
+ if (disabled || query.length === 0) {
66
+ setLoading(false);
67
+ setSuggestions([]);
68
+ return;
69
+ }
70
+ let cancelled = false;
71
+ const handler = window.setTimeout(async () => {
72
+ setLoading(true);
73
+ try {
74
+ const results = await search(query);
75
+ if (cancelled) return;
76
+ setSuggestions(results);
77
+ setCache((prev) => {
78
+ const next = new Map(prev);
79
+ results.forEach((entry) => {
80
+ if (entry?.id) next.set(entry.id, entry);
81
+ });
82
+ return next;
83
+ });
84
+ setError(null);
85
+ } catch {
86
+ if (!cancelled) {
87
+ setError(labels.error);
88
+ setSuggestions([]);
89
+ }
90
+ } finally {
91
+ if (!cancelled) setLoading(false);
92
+ }
93
+ }, 200);
94
+ return () => {
95
+ cancelled = true;
96
+ window.clearTimeout(handler);
97
+ };
98
+ }, [disabled, input, labels.error, search]);
99
+ const filteredSuggestions = React.useMemo(
100
+ () => suggestions.filter((option) => !normalizedValue.includes(option.id)),
101
+ [normalizedValue, suggestions]
102
+ );
103
+ const selectedOptions = React.useMemo(
104
+ () => normalizedValue.map((id2) => cache.get(id2) ?? { id: id2, label: id2 }),
105
+ [cache, normalizedValue]
106
+ );
107
+ const addOption = React.useCallback(
108
+ (option) => {
109
+ if (!option?.id) return;
110
+ if (normalizedValue.includes(option.id)) return;
111
+ onChange([...normalizedValue, option.id]);
112
+ setCache((prev) => {
113
+ const next = new Map(prev);
114
+ next.set(option.id, option);
115
+ return next;
116
+ });
117
+ setInput("");
118
+ setSuggestions([]);
119
+ },
120
+ [normalizedValue, onChange]
121
+ );
122
+ const removeOption = React.useCallback(
123
+ (id2) => {
124
+ onChange(normalizedValue.filter((candidate) => candidate !== id2));
125
+ },
126
+ [normalizedValue, onChange]
127
+ );
128
+ const renderLeading = React.useCallback(
129
+ (option) => kind === "people" ? /* @__PURE__ */ jsx(Avatar, { label: option.label, size: "xs" }) : /* @__PURE__ */ jsx(Building2, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
130
+ [kind]
131
+ );
132
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
133
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-input bg-background p-2", children: [
134
+ selectedOptions.length ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: selectedOptions.map((option) => /* @__PURE__ */ jsxs(
135
+ "span",
136
+ {
137
+ className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2 py-1 text-xs text-foreground",
138
+ children: [
139
+ renderLeading(option),
140
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
141
+ /* @__PURE__ */ jsx(
142
+ IconButton,
143
+ {
144
+ type: "button",
145
+ variant: "ghost",
146
+ size: "xs",
147
+ "aria-label": `${labels.remove} ${option.label}`,
148
+ onClick: () => removeOption(option.id),
149
+ disabled,
150
+ children: /* @__PURE__ */ jsx(X, { className: "size-3" })
151
+ }
152
+ )
153
+ ]
154
+ },
155
+ option.id
156
+ )) }) : null,
157
+ /* @__PURE__ */ jsx(
158
+ SearchInput,
159
+ {
160
+ id,
161
+ size: "default",
162
+ value: input,
163
+ onChange: setInput,
164
+ placeholder: labels.placeholder,
165
+ disabled,
166
+ onKeyDown: (event) => {
167
+ if (event.key === "Enter") {
168
+ event.preventDefault();
169
+ const nextOption = filteredSuggestions[0];
170
+ if (nextOption) addOption(nextOption);
171
+ } else if (event.key === "Backspace" && !input.length && normalizedValue.length) {
172
+ removeOption(normalizedValue[normalizedValue.length - 1]);
173
+ }
174
+ }
175
+ }
176
+ )
177
+ ] }),
178
+ loading ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: labels.loading }) : null,
179
+ !loading && input.trim().length > 0 && filteredSuggestions.length ? /* @__PURE__ */ jsx("div", { className: "flex max-h-72 flex-col gap-1 overflow-y-auto rounded-md border border-border bg-popover p-1 shadow-md", children: filteredSuggestions.slice(0, 8).map((option) => /* @__PURE__ */ jsxs(
180
+ Button,
181
+ {
182
+ type: "button",
183
+ variant: "ghost",
184
+ size: "sm",
185
+ className: "h-auto justify-start px-2 py-1 text-left font-normal",
186
+ onMouseDown: (event) => event.preventDefault(),
187
+ onClick: () => addOption(option),
188
+ disabled,
189
+ "aria-label": option.label,
190
+ children: [
191
+ renderLeading(option),
192
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col items-start", children: [
193
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm", children: option.label }),
194
+ option.subtitle ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: option.subtitle }) : null
195
+ ] })
196
+ ]
197
+ },
198
+ option.id
199
+ )) }) : null,
200
+ !loading && input.trim().length > 0 && !filteredSuggestions.length ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: labels.noResults }) : null,
201
+ error ? /* @__PURE__ */ jsx("div", { className: "text-xs text-status-error-text", children: error }) : null
202
+ ] });
203
+ }
204
+ var DealAssociationsField_default = DealAssociationsField;
205
+ export {
206
+ DealAssociationsField,
207
+ DealAssociationsField_default as default
208
+ };
209
+ //# sourceMappingURL=DealAssociationsField.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/DealAssociationsField.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, X } from 'lucide-react'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { useDealAssociationLookups } from '../DealForm'\n\ntype AssociationOption = {\n id: string\n label: string\n subtitle?: string | null\n}\n\nexport type DealAssociationsFieldProps = {\n id?: string\n kind: 'people' | 'companies'\n value: string[]\n onChange: (next: string[]) => void\n disabled?: boolean\n labels: {\n placeholder: string\n empty: string\n loading: string\n noResults: string\n remove: string\n error: string\n }\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\nexport function DealAssociationsField({\n id,\n kind,\n value,\n onChange,\n disabled = false,\n labels,\n}: DealAssociationsFieldProps) {\n const lookups = useDealAssociationLookups()\n const search = kind === 'people' ? lookups.searchPeople : lookups.searchCompanies\n const fetchByIds = kind === 'people' ? lookups.fetchPeopleByIds : lookups.fetchCompaniesByIds\n\n const [input, setInput] = React.useState('')\n const [suggestions, setSuggestions] = React.useState<AssociationOption[]>([])\n const [cache, setCache] = React.useState<Map<string, AssociationOption>>(() => 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 (!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(labels.error)\n }\n })().catch(() => {\n // The inner try/catch already surfaces failures via setError; this guards the IIFE promise only.\n })\n return () => {\n cancelled = true\n }\n }, [cache, fetchByIds, labels.error, normalizedValue])\n\n React.useEffect(() => {\n const query = input.trim()\n // Only query once the operator starts typing. Searching on an empty string\n // returns the first page of *every* person/company in the tenant \u2014 useless as\n // a suggestion list and a performance trap at scale (thousands of records).\n if (disabled || query.length === 0) {\n setLoading(false)\n setSuggestions([])\n return\n }\n let cancelled = false\n const handler = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await search(query)\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(labels.error)\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, input, labels.error, 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: AssociationOption) => {\n if (!option?.id) return\n if (normalizedValue.includes(option.id)) return\n onChange([...normalizedValue, option.id])\n setCache((prev) => {\n const next = new Map(prev)\n next.set(option.id, option)\n return next\n })\n setInput('')\n setSuggestions([])\n },\n [normalizedValue, onChange],\n )\n\n const removeOption = React.useCallback(\n (id: string) => {\n onChange(normalizedValue.filter((candidate) => candidate !== id))\n },\n [normalizedValue, onChange],\n )\n\n const renderLeading = React.useCallback(\n (option: AssociationOption) =>\n kind === 'people' ? (\n <Avatar label={option.label} size=\"xs\" />\n ) : (\n <Building2 className=\"size-3.5 text-muted-foreground\" aria-hidden=\"true\" />\n ),\n [kind],\n )\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex flex-col gap-2 rounded-md border border-input bg-background p-2\">\n {selectedOptions.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {selectedOptions.map((option) => (\n <span\n key={option.id}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-muted px-2 py-1 text-xs text-foreground\"\n >\n {renderLeading(option)}\n <span className=\"truncate\">{option.label}</span>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={`${labels.remove} ${option.label}`}\n onClick={() => removeOption(option.id)}\n disabled={disabled}\n >\n <X className=\"size-3\" />\n </IconButton>\n </span>\n ))}\n </div>\n ) : null}\n <SearchInput\n id={id}\n size=\"default\"\n value={input}\n onChange={setInput}\n placeholder={labels.placeholder}\n disabled={disabled}\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 />\n </div>\n {loading ? <div className=\"text-xs text-muted-foreground\">{labels.loading}</div> : null}\n {!loading && input.trim().length > 0 && filteredSuggestions.length ? (\n <div className=\"flex max-h-72 flex-col gap-1 overflow-y-auto rounded-md border border-border bg-popover p-1 shadow-md\">\n {filteredSuggestions.slice(0, 8).map((option) => (\n <Button\n key={option.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto justify-start px-2 py-1 text-left font-normal\"\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => addOption(option)}\n disabled={disabled}\n aria-label={option.label}\n >\n {renderLeading(option)}\n <span className=\"flex min-w-0 flex-col items-start\">\n <span className=\"truncate text-sm\">{option.label}</span>\n {option.subtitle ? (\n <span className=\"truncate text-xs text-muted-foreground\">{option.subtitle}</span>\n ) : null}\n </span>\n </Button>\n ))}\n </div>\n ) : null}\n {!loading && input.trim().length > 0 && !filteredSuggestions.length ? (\n <div className=\"text-xs text-muted-foreground\">{labels.noResults}</div>\n ) : null}\n {error ? <div className=\"text-xs text-status-error-text\">{error}</div> : null}\n </div>\n )\n}\n\nexport default DealAssociationsField\n"],
5
+ "mappings": "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",
6
+ "names": ["id"]
7
+ }
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Users } from "lucide-react";
4
+ import { DealSectionCard } from "./DealSectionCard.js";
5
+ import { DealFormField } from "./DealFormField.js";
6
+ import { DealAssociationsField } from "./DealAssociationsField.js";
7
+ function DealAssociationsSection({
8
+ tr,
9
+ personIds,
10
+ companyIds,
11
+ onPeopleChange,
12
+ onCompaniesChange,
13
+ disabled
14
+ }) {
15
+ const peopleLabels = {
16
+ placeholder: tr("customers.deals.create.associations.peoplePlaceholder", "Search people by name or email\u2026"),
17
+ empty: tr("customers.deals.form.people.empty", "No people linked yet."),
18
+ loading: tr("customers.deals.form.people.loading", "Searching people\u2026"),
19
+ noResults: tr("customers.deals.form.people.noResults", "No people match your search."),
20
+ remove: tr("customers.deals.form.assignees.remove", "Remove"),
21
+ error: tr("customers.deals.form.people.error", "Failed to load people.")
22
+ };
23
+ const companyLabels = {
24
+ placeholder: tr("customers.deals.create.associations.companiesPlaceholder", "Search companies by name or domain\u2026"),
25
+ empty: tr("customers.deals.form.companies.empty", "No companies linked yet."),
26
+ loading: tr("customers.deals.form.companies.loading", "Searching companies\u2026"),
27
+ noResults: tr("customers.deals.form.companies.noResults", "No companies match your search."),
28
+ remove: tr("customers.deals.form.assignees.remove", "Remove"),
29
+ error: tr("customers.deals.form.companies.error", "Failed to load companies.")
30
+ };
31
+ return /* @__PURE__ */ jsxs(
32
+ DealSectionCard,
33
+ {
34
+ icon: Users,
35
+ title: tr("customers.deals.create.sections.associations.title", "Associations"),
36
+ subtitle: tr("customers.deals.create.sections.associations.subtitle", "Link people and companies to this deal"),
37
+ children: [
38
+ /* @__PURE__ */ jsx(DealFormField, { fieldId: "personIds", label: tr("customers.people.detail.deals.fields.people", "People"), children: /* @__PURE__ */ jsx(
39
+ DealAssociationsField,
40
+ {
41
+ kind: "people",
42
+ value: personIds,
43
+ onChange: onPeopleChange,
44
+ disabled,
45
+ labels: peopleLabels
46
+ }
47
+ ) }),
48
+ /* @__PURE__ */ jsx(DealFormField, { fieldId: "companyIds", label: tr("customers.people.detail.deals.fields.companies", "Companies"), children: /* @__PURE__ */ jsx(
49
+ DealAssociationsField,
50
+ {
51
+ kind: "companies",
52
+ value: companyIds,
53
+ onChange: onCompaniesChange,
54
+ disabled,
55
+ labels: companyLabels
56
+ }
57
+ ) })
58
+ ]
59
+ }
60
+ );
61
+ }
62
+ var DealAssociationsSection_default = DealAssociationsSection;
63
+ export {
64
+ DealAssociationsSection,
65
+ DealAssociationsSection_default as default
66
+ };
67
+ //# sourceMappingURL=DealAssociationsSection.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/DealAssociationsSection.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Users } from 'lucide-react'\nimport { DealSectionCard } from './DealSectionCard'\nimport { DealFormField } from './DealFormField'\nimport { DealAssociationsField } from './DealAssociationsField'\nimport type { Translate } from './dealFormTypes'\n\nexport type DealAssociationsSectionProps = {\n tr: Translate\n personIds: string[]\n companyIds: string[]\n onPeopleChange: (next: string[]) => void\n onCompaniesChange: (next: string[]) => void\n disabled: boolean\n}\n\nexport function DealAssociationsSection({\n tr,\n personIds,\n companyIds,\n onPeopleChange,\n onCompaniesChange,\n disabled,\n}: DealAssociationsSectionProps) {\n const peopleLabels = {\n placeholder: tr('customers.deals.create.associations.peoplePlaceholder', 'Search people by name or email\u2026'),\n empty: tr('customers.deals.form.people.empty', 'No people linked yet.'),\n loading: tr('customers.deals.form.people.loading', 'Searching people\u2026'),\n noResults: tr('customers.deals.form.people.noResults', 'No people match your search.'),\n remove: tr('customers.deals.form.assignees.remove', 'Remove'),\n error: tr('customers.deals.form.people.error', 'Failed to load people.'),\n }\n const companyLabels = {\n placeholder: tr('customers.deals.create.associations.companiesPlaceholder', 'Search companies by name or domain\u2026'),\n empty: tr('customers.deals.form.companies.empty', 'No companies linked yet.'),\n loading: tr('customers.deals.form.companies.loading', 'Searching companies\u2026'),\n noResults: tr('customers.deals.form.companies.noResults', 'No companies match your search.'),\n remove: tr('customers.deals.form.assignees.remove', 'Remove'),\n error: tr('customers.deals.form.companies.error', 'Failed to load companies.'),\n }\n\n return (\n <DealSectionCard\n icon={Users}\n title={tr('customers.deals.create.sections.associations.title', 'Associations')}\n subtitle={tr('customers.deals.create.sections.associations.subtitle', 'Link people and companies to this deal')}\n >\n <DealFormField fieldId=\"personIds\" label={tr('customers.people.detail.deals.fields.people', 'People')}>\n <DealAssociationsField\n kind=\"people\"\n value={personIds}\n onChange={onPeopleChange}\n disabled={disabled}\n labels={peopleLabels}\n />\n </DealFormField>\n <DealFormField fieldId=\"companyIds\" label={tr('customers.people.detail.deals.fields.companies', 'Companies')}>\n <DealAssociationsField\n kind=\"companies\"\n value={companyIds}\n onChange={onCompaniesChange}\n disabled={disabled}\n labels={companyLabels}\n />\n </DealFormField>\n </DealSectionCard>\n )\n}\n\nexport default DealAssociationsSection\n"],
5
+ "mappings": ";AA4CI,SAMI,KANJ;AAzCJ,SAAS,aAAa;AACtB,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AAY/B,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,eAAe;AAAA,IACnB,aAAa,GAAG,yDAAyD,sCAAiC;AAAA,IAC1G,OAAO,GAAG,qCAAqC,uBAAuB;AAAA,IACtE,SAAS,GAAG,uCAAuC,wBAAmB;AAAA,IACtE,WAAW,GAAG,yCAAyC,8BAA8B;AAAA,IACrF,QAAQ,GAAG,yCAAyC,QAAQ;AAAA,IAC5D,OAAO,GAAG,qCAAqC,wBAAwB;AAAA,EACzE;AACA,QAAM,gBAAgB;AAAA,IACpB,aAAa,GAAG,4DAA4D,0CAAqC;AAAA,IACjH,OAAO,GAAG,wCAAwC,0BAA0B;AAAA,IAC5E,SAAS,GAAG,0CAA0C,2BAAsB;AAAA,IAC5E,WAAW,GAAG,4CAA4C,iCAAiC;AAAA,IAC3F,QAAQ,GAAG,yCAAyC,QAAQ;AAAA,IAC5D,OAAO,GAAG,wCAAwC,2BAA2B;AAAA,EAC/E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,OAAO,GAAG,sDAAsD,cAAc;AAAA,MAC9E,UAAU,GAAG,yDAAyD,wCAAwC;AAAA,MAE9G;AAAA,4BAAC,iBAAc,SAAQ,aAAY,OAAO,GAAG,+CAA+C,QAAQ,GAClG;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU;AAAA,YACV;AAAA,YACA,QAAQ;AAAA;AAAA,QACV,GACF;AAAA,QACA,oBAAC,iBAAc,SAAQ,cAAa,OAAO,GAAG,kDAAkD,WAAW,GACzG;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU;AAAA,YACV;AAAA,YACA,QAAQ;AAAA;AAAA,QACV,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,kCAAQ;",
6
+ "names": []
7
+ }