@open-mercato/core 0.6.3-develop.3901.1.ddad60693a → 0.6.3

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 (98) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/global.d.js +1 -0
  3. package/dist/global.d.js.map +7 -0
  4. package/dist/modules/catalog/commands/variants.js +11 -5
  5. package/dist/modules/catalog/commands/variants.js.map +2 -2
  6. package/dist/modules/customers/backend/customers/deals/create/page.js +3 -61
  7. package/dist/modules/customers/backend/customers/deals/create/page.js.map +2 -2
  8. package/dist/modules/customers/components/detail/DealForm.js +2 -0
  9. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  10. package/dist/modules/customers/components/detail/create/CreateDealForm.js +233 -0
  11. package/dist/modules/customers/components/detail/create/CreateDealForm.js.map +7 -0
  12. package/dist/modules/customers/components/detail/create/DealAssociationsField.js +209 -0
  13. package/dist/modules/customers/components/detail/create/DealAssociationsField.js.map +7 -0
  14. package/dist/modules/customers/components/detail/create/DealAssociationsSection.js +67 -0
  15. package/dist/modules/customers/components/detail/create/DealAssociationsSection.js.map +7 -0
  16. package/dist/modules/customers/components/detail/create/DealCreateSidebar.js +73 -0
  17. package/dist/modules/customers/components/detail/create/DealCreateSidebar.js.map +7 -0
  18. package/dist/modules/customers/components/detail/create/DealCurrencyField.js +92 -0
  19. package/dist/modules/customers/components/detail/create/DealCurrencyField.js.map +7 -0
  20. package/dist/modules/customers/components/detail/create/DealCustomAttributes.js +81 -0
  21. package/dist/modules/customers/components/detail/create/DealCustomAttributes.js.map +7 -0
  22. package/dist/modules/customers/components/detail/create/DealDetailsFields.js +171 -0
  23. package/dist/modules/customers/components/detail/create/DealDetailsFields.js.map +7 -0
  24. package/dist/modules/customers/components/detail/create/DealFormField.js +24 -0
  25. package/dist/modules/customers/components/detail/create/DealFormField.js.map +7 -0
  26. package/dist/modules/customers/components/detail/create/DealSectionCard.js +29 -0
  27. package/dist/modules/customers/components/detail/create/DealSectionCard.js.map +7 -0
  28. package/dist/modules/customers/components/detail/create/DealTipsCard.js +19 -0
  29. package/dist/modules/customers/components/detail/create/DealTipsCard.js.map +7 -0
  30. package/dist/modules/customers/components/detail/create/PipelineSelect.js +41 -0
  31. package/dist/modules/customers/components/detail/create/PipelineSelect.js.map +7 -0
  32. package/dist/modules/customers/components/detail/create/PipelineStageSelect.js +49 -0
  33. package/dist/modules/customers/components/detail/create/PipelineStageSelect.js.map +7 -0
  34. package/dist/modules/customers/components/detail/create/SuffixInput.js +21 -0
  35. package/dist/modules/customers/components/detail/create/SuffixInput.js.map +7 -0
  36. package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js +270 -0
  37. package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js.map +7 -0
  38. package/dist/modules/customers/components/detail/create/dealFormTypes.js +17 -0
  39. package/dist/modules/customers/components/detail/create/dealFormTypes.js.map +7 -0
  40. package/dist/modules/customers/components/detail/create/dealNumericInput.js +16 -0
  41. package/dist/modules/customers/components/detail/create/dealNumericInput.js.map +7 -0
  42. package/dist/modules/customers/components/detail/create/useDealCustomFields.js +93 -0
  43. package/dist/modules/customers/components/detail/create/useDealCustomFields.js.map +7 -0
  44. package/dist/modules/customers/components/detail/create/useDealPipelines.js +59 -0
  45. package/dist/modules/customers/components/detail/create/useDealPipelines.js.map +7 -0
  46. package/dist/modules/customers/components/formConfig.js +4 -2
  47. package/dist/modules/customers/components/formConfig.js.map +2 -2
  48. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +5 -2
  49. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
  50. package/dist/modules/feature_toggles/lib/feature-flag-check.js +13 -5
  51. package/dist/modules/feature_toggles/lib/feature-flag-check.js.map +2 -2
  52. package/dist/modules/query_index/subscribers/coverage_refresh.js +6 -1
  53. package/dist/modules/query_index/subscribers/coverage_refresh.js.map +2 -2
  54. package/dist/modules/workflows/components/WorkflowGraph.js +29 -186
  55. package/dist/modules/workflows/components/WorkflowGraph.js.map +2 -2
  56. package/dist/modules/workflows/components/WorkflowGraphImpl.js +196 -0
  57. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +7 -0
  58. package/package.json +8 -9
  59. package/src/global.d.ts +9 -0
  60. package/src/modules/catalog/commands/variants.ts +14 -5
  61. package/src/modules/customers/backend/customers/deals/create/page.tsx +3 -64
  62. package/src/modules/customers/components/detail/DealForm.tsx +2 -0
  63. package/src/modules/customers/components/detail/create/CreateDealForm.tsx +254 -0
  64. package/src/modules/customers/components/detail/create/DealAssociationsField.tsx +253 -0
  65. package/src/modules/customers/components/detail/create/DealAssociationsSection.tsx +72 -0
  66. package/src/modules/customers/components/detail/create/DealCreateSidebar.tsx +79 -0
  67. package/src/modules/customers/components/detail/create/DealCurrencyField.tsx +108 -0
  68. package/src/modules/customers/components/detail/create/DealCustomAttributes.tsx +118 -0
  69. package/src/modules/customers/components/detail/create/DealDetailsFields.tsx +171 -0
  70. package/src/modules/customers/components/detail/create/DealFormField.tsx +39 -0
  71. package/src/modules/customers/components/detail/create/DealSectionCard.tsx +40 -0
  72. package/src/modules/customers/components/detail/create/DealTipsCard.tsx +26 -0
  73. package/src/modules/customers/components/detail/create/PipelineSelect.tsx +55 -0
  74. package/src/modules/customers/components/detail/create/PipelineStageSelect.tsx +70 -0
  75. package/src/modules/customers/components/detail/create/SuffixInput.tsx +20 -0
  76. package/src/modules/customers/components/detail/create/dealCustomFieldControl.tsx +310 -0
  77. package/src/modules/customers/components/detail/create/dealFormTypes.ts +29 -0
  78. package/src/modules/customers/components/detail/create/dealNumericInput.ts +20 -0
  79. package/src/modules/customers/components/detail/create/useDealCustomFields.ts +118 -0
  80. package/src/modules/customers/components/detail/create/useDealPipelines.ts +80 -0
  81. package/src/modules/customers/components/formConfig.tsx +3 -0
  82. package/src/modules/customers/i18n/de.json +26 -0
  83. package/src/modules/customers/i18n/en.json +26 -0
  84. package/src/modules/customers/i18n/es.json +26 -0
  85. package/src/modules/customers/i18n/pl.json +26 -0
  86. package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +12 -1
  87. package/src/modules/feature_toggles/lib/feature-flag-check.ts +14 -4
  88. package/src/modules/query_index/subscribers/coverage_refresh.ts +7 -1
  89. package/src/modules/resources/i18n/de.json +1 -0
  90. package/src/modules/resources/i18n/en.json +1 -0
  91. package/src/modules/resources/i18n/es.json +1 -0
  92. package/src/modules/resources/i18n/pl.json +1 -0
  93. package/src/modules/sales/i18n/de.json +2 -0
  94. package/src/modules/sales/i18n/en.json +2 -0
  95. package/src/modules/sales/i18n/es.json +2 -0
  96. package/src/modules/sales/i18n/pl.json +2 -0
  97. package/src/modules/workflows/components/WorkflowGraph.tsx +39 -235
  98. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +233 -0
@@ -0,0 +1,270 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { format } from "date-fns/format";
5
+ import { parseISO } from "date-fns/parseISO";
6
+ import { Label } from "@open-mercato/ui/primitives/label";
7
+ import { Input } from "@open-mercato/ui/primitives/input";
8
+ import { Textarea } from "@open-mercato/ui/primitives/textarea";
9
+ import { CheckboxField } from "@open-mercato/ui/primitives/checkbox-field";
10
+ import { DatePicker } from "@open-mercato/ui/primitives/date-picker";
11
+ import { TagInput } from "@open-mercato/ui/primitives/tag-input";
12
+ import {
13
+ Select,
14
+ SelectContent,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue
18
+ } from "@open-mercato/ui/primitives/select";
19
+ import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
20
+ const SELECT_CLEAR_SENTINEL = "__deal_custom_field_select_clear__";
21
+ function toStringArray(value) {
22
+ if (!Array.isArray(value)) return [];
23
+ return value.filter((item) => typeof item === "string");
24
+ }
25
+ function toDate(value) {
26
+ if (value instanceof Date) return value;
27
+ if (typeof value === "string" && value) {
28
+ const parsed = parseISO(value);
29
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
30
+ }
31
+ return null;
32
+ }
33
+ function useResolvedOptions(field) {
34
+ const builtin = field.type === "custom" ? null : field;
35
+ const staticOptions = builtin?.options;
36
+ const loadOptions = builtin?.loadOptions;
37
+ const [loadedOptions, setLoadedOptions] = React.useState([]);
38
+ React.useEffect(() => {
39
+ if (staticOptions && staticOptions.length > 0) return;
40
+ if (typeof loadOptions !== "function") return;
41
+ let cancelled = false;
42
+ loadOptions().then((options) => {
43
+ if (!cancelled) setLoadedOptions(options);
44
+ }).catch(() => {
45
+ if (!cancelled) setLoadedOptions([]);
46
+ });
47
+ return () => {
48
+ cancelled = true;
49
+ };
50
+ }, [staticOptions, loadOptions]);
51
+ if (staticOptions && staticOptions.length > 0) return staticOptions;
52
+ return loadedOptions;
53
+ }
54
+ function FieldShell({
55
+ label,
56
+ required,
57
+ description,
58
+ error,
59
+ children
60
+ }) {
61
+ const fieldId = React.useId();
62
+ const control = React.isValidElement(children) ? React.cloneElement(children, { id: fieldId }) : children;
63
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
64
+ label.trim().length > 0 ? /* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
65
+ label,
66
+ required ? /* @__PURE__ */ jsx("span", { className: "text-destructive", children: " *" }) : null
67
+ ] }) : null,
68
+ control,
69
+ description ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: description }) : null,
70
+ error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: error }) : null
71
+ ] });
72
+ }
73
+ function DealCustomFieldControl({
74
+ field,
75
+ value,
76
+ onChange,
77
+ error,
78
+ disabled = false
79
+ }) {
80
+ const options = useResolvedOptions(field);
81
+ const ariaInvalid = error ? true : void 0;
82
+ if (field.type === "checkbox") {
83
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
84
+ /* @__PURE__ */ jsx(
85
+ CheckboxField,
86
+ {
87
+ label: field.label,
88
+ description: field.description,
89
+ checked: value === true,
90
+ onCheckedChange: (next) => onChange(next === true),
91
+ disabled,
92
+ "aria-invalid": ariaInvalid
93
+ }
94
+ ),
95
+ error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: error }) : null
96
+ ] });
97
+ }
98
+ if (field.type === "custom") {
99
+ return /* @__PURE__ */ jsx(
100
+ FieldShell,
101
+ {
102
+ label: field.label,
103
+ required: field.required,
104
+ description: field.description,
105
+ error,
106
+ children: field.component({
107
+ id: field.id,
108
+ value,
109
+ error,
110
+ setValue: onChange,
111
+ disabled,
112
+ autoFocus: false
113
+ })
114
+ }
115
+ );
116
+ }
117
+ let control;
118
+ switch (field.type) {
119
+ case "number":
120
+ control = /* @__PURE__ */ jsx(
121
+ Input,
122
+ {
123
+ type: "number",
124
+ inputMode: "numeric",
125
+ value: typeof value === "number" || typeof value === "string" ? String(value) : "",
126
+ placeholder: field.placeholder,
127
+ disabled,
128
+ "aria-invalid": ariaInvalid,
129
+ onChange: (event) => {
130
+ const raw = event.target.value;
131
+ if (raw === "") {
132
+ onChange(void 0);
133
+ return;
134
+ }
135
+ const parsed = Number(raw);
136
+ onChange(Number.isNaN(parsed) ? raw : parsed);
137
+ }
138
+ }
139
+ );
140
+ break;
141
+ case "date":
142
+ case "datepicker":
143
+ control = /* @__PURE__ */ jsx(
144
+ DatePicker,
145
+ {
146
+ value: toDate(value),
147
+ onChange: (date) => onChange(date ? format(date, "yyyy-MM-dd") : void 0),
148
+ disabled,
149
+ placeholder: field.placeholder,
150
+ "aria-invalid": ariaInvalid
151
+ }
152
+ );
153
+ break;
154
+ case "datetime":
155
+ case "datetime-local":
156
+ control = /* @__PURE__ */ jsx(
157
+ DatePicker,
158
+ {
159
+ withTime: true,
160
+ value: toDate(value),
161
+ onChange: (date) => onChange(date ? date.toISOString() : void 0),
162
+ disabled,
163
+ placeholder: field.placeholder,
164
+ "aria-invalid": ariaInvalid
165
+ }
166
+ );
167
+ break;
168
+ case "tags":
169
+ control = /* @__PURE__ */ jsx(
170
+ TagInput,
171
+ {
172
+ value: toStringArray(value),
173
+ onChange: (next) => onChange(next),
174
+ placeholder: field.placeholder,
175
+ disabled,
176
+ "aria-invalid": ariaInvalid
177
+ }
178
+ );
179
+ break;
180
+ case "richtext":
181
+ control = /* @__PURE__ */ jsx(
182
+ Textarea,
183
+ {
184
+ value: value == null ? "" : String(value),
185
+ placeholder: field.placeholder,
186
+ disabled,
187
+ "aria-invalid": ariaInvalid,
188
+ onChange: (event) => onChange(event.target.value)
189
+ }
190
+ );
191
+ break;
192
+ case "select": {
193
+ if (field.multiple) {
194
+ const selected = toStringArray(value);
195
+ control = /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3", children: options.map((option) => {
196
+ const checked = selected.includes(option.value);
197
+ return /* @__PURE__ */ jsxs("label", { className: "inline-flex cursor-pointer items-center gap-2", children: [
198
+ /* @__PURE__ */ jsx(
199
+ Checkbox,
200
+ {
201
+ checked,
202
+ disabled,
203
+ onCheckedChange: (state) => {
204
+ const next = new Set(selected);
205
+ if (state === true) next.add(option.value);
206
+ else next.delete(option.value);
207
+ onChange(Array.from(next));
208
+ }
209
+ }
210
+ ),
211
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: option.label })
212
+ ] }, option.value);
213
+ }) });
214
+ } else {
215
+ const selectedValue = Array.isArray(value) ? String(value[0] ?? "") : value == null ? "" : String(value);
216
+ control = /* @__PURE__ */ jsxs(
217
+ Select,
218
+ {
219
+ value: selectedValue,
220
+ disabled,
221
+ onValueChange: (next) => {
222
+ if (!next || next === SELECT_CLEAR_SENTINEL) {
223
+ onChange(null);
224
+ return;
225
+ }
226
+ onChange(next);
227
+ },
228
+ children: [
229
+ /* @__PURE__ */ jsx(SelectTrigger, { "aria-invalid": ariaInvalid, children: /* @__PURE__ */ jsx(SelectValue, { placeholder: field.placeholder }) }),
230
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
231
+ !field.required && selectedValue ? /* @__PURE__ */ jsx(SelectItem, { value: SELECT_CLEAR_SENTINEL, children: "\u2014" }) : null,
232
+ options.filter((option) => option.value !== "").map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value))
233
+ ] })
234
+ ]
235
+ }
236
+ );
237
+ }
238
+ break;
239
+ }
240
+ case "text":
241
+ default:
242
+ control = /* @__PURE__ */ jsx(
243
+ Input,
244
+ {
245
+ value: value == null ? "" : String(value),
246
+ placeholder: field.placeholder,
247
+ disabled,
248
+ "aria-invalid": ariaInvalid,
249
+ onChange: (event) => onChange(event.target.value)
250
+ }
251
+ );
252
+ break;
253
+ }
254
+ return /* @__PURE__ */ jsx(
255
+ FieldShell,
256
+ {
257
+ label: field.label,
258
+ required: field.required,
259
+ description: field.description,
260
+ error,
261
+ children: control
262
+ }
263
+ );
264
+ }
265
+ var dealCustomFieldControl_default = DealCustomFieldControl;
266
+ export {
267
+ DealCustomFieldControl,
268
+ dealCustomFieldControl_default as default
269
+ };
270
+ //# sourceMappingURL=dealCustomFieldControl.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/dealCustomFieldControl.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { format } from 'date-fns/format'\nimport { parseISO } from 'date-fns/parseISO'\nimport type { CrudField, CrudFieldOption } from '@open-mercato/ui/backend/CrudForm'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'\nimport { DatePicker } from '@open-mercato/ui/primitives/date-picker'\nimport { TagInput } from '@open-mercato/ui/primitives/tag-input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\n\nexport type DealCustomFieldControlProps = {\n field: CrudField\n value: unknown\n onChange: (value: unknown) => void\n error?: string\n disabled?: boolean\n}\n\nconst SELECT_CLEAR_SENTINEL = '__deal_custom_field_select_clear__'\n\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value.filter((item): item is string => typeof item === 'string')\n}\n\nfunction toDate(value: unknown): Date | null {\n if (value instanceof Date) return value\n if (typeof value === 'string' && value) {\n const parsed = parseISO(value)\n return Number.isNaN(parsed.getTime()) ? null : parsed\n }\n return null\n}\n\nfunction useResolvedOptions(field: CrudField): CrudFieldOption[] {\n const builtin = field.type === 'custom' ? null : field\n const staticOptions = builtin?.options\n const loadOptions = builtin?.loadOptions\n const [loadedOptions, setLoadedOptions] = React.useState<CrudFieldOption[]>([])\n\n React.useEffect(() => {\n if (staticOptions && staticOptions.length > 0) return\n if (typeof loadOptions !== 'function') return\n let cancelled = false\n loadOptions()\n .then((options) => {\n if (!cancelled) setLoadedOptions(options)\n })\n .catch(() => {\n if (!cancelled) setLoadedOptions([])\n })\n return () => {\n cancelled = true\n }\n }, [staticOptions, loadOptions])\n\n if (staticOptions && staticOptions.length > 0) return staticOptions\n return loadedOptions\n}\n\nfunction FieldShell({\n label,\n required,\n description,\n error,\n children,\n}: {\n label: string\n required?: boolean\n description?: React.ReactNode\n error?: string\n children: React.ReactNode\n}) {\n const fieldId = React.useId()\n const control = React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement<{ id?: string }>, { id: fieldId })\n : children\n return (\n <div className=\"space-y-1\">\n {label.trim().length > 0 ? (\n <Label htmlFor={fieldId}>\n {label}\n {required ? <span className=\"text-destructive\"> *</span> : null}\n </Label>\n ) : null}\n {control}\n {description ? <p className=\"text-xs text-muted-foreground\">{description}</p> : null}\n {error ? <p className=\"text-xs text-status-error-text\">{error}</p> : null}\n </div>\n )\n}\n\nexport function DealCustomFieldControl({\n field,\n value,\n onChange,\n error,\n disabled = false,\n}: DealCustomFieldControlProps) {\n const options = useResolvedOptions(field)\n const ariaInvalid = error ? true : undefined\n\n if (field.type === 'checkbox') {\n return (\n <div className=\"space-y-1\">\n <CheckboxField\n label={field.label}\n description={field.description}\n checked={value === true}\n onCheckedChange={(next) => onChange(next === true)}\n disabled={disabled}\n aria-invalid={ariaInvalid}\n />\n {error ? <p className=\"text-xs text-status-error-text\">{error}</p> : null}\n </div>\n )\n }\n\n if (field.type === 'custom') {\n return (\n <FieldShell\n label={field.label}\n required={field.required}\n description={field.description}\n error={error}\n >\n {field.component({\n id: field.id,\n value,\n error,\n setValue: onChange,\n disabled,\n autoFocus: false,\n })}\n </FieldShell>\n )\n }\n\n let control: React.ReactNode\n\n switch (field.type) {\n case 'number':\n control = (\n <Input\n type=\"number\"\n inputMode=\"numeric\"\n value={typeof value === 'number' || typeof value === 'string' ? String(value) : ''}\n placeholder={field.placeholder}\n disabled={disabled}\n aria-invalid={ariaInvalid}\n onChange={(event) => {\n const raw = event.target.value\n if (raw === '') {\n onChange(undefined)\n return\n }\n const parsed = Number(raw)\n onChange(Number.isNaN(parsed) ? raw : parsed)\n }}\n />\n )\n break\n case 'date':\n case 'datepicker':\n control = (\n <DatePicker\n value={toDate(value)}\n onChange={(date) => onChange(date ? format(date, 'yyyy-MM-dd') : undefined)}\n disabled={disabled}\n placeholder={field.placeholder}\n aria-invalid={ariaInvalid}\n />\n )\n break\n case 'datetime':\n case 'datetime-local':\n control = (\n <DatePicker\n withTime\n value={toDate(value)}\n onChange={(date) => onChange(date ? date.toISOString() : undefined)}\n disabled={disabled}\n placeholder={field.placeholder}\n aria-invalid={ariaInvalid}\n />\n )\n break\n case 'tags':\n control = (\n <TagInput\n value={toStringArray(value)}\n onChange={(next) => onChange(next)}\n placeholder={field.placeholder}\n disabled={disabled}\n aria-invalid={ariaInvalid}\n />\n )\n break\n case 'richtext':\n control = (\n <Textarea\n value={value == null ? '' : String(value)}\n placeholder={field.placeholder}\n disabled={disabled}\n aria-invalid={ariaInvalid}\n onChange={(event) => onChange(event.target.value)}\n />\n )\n break\n case 'select': {\n if (field.multiple) {\n const selected = toStringArray(value)\n control = (\n <div className=\"flex flex-wrap gap-3\">\n {options.map((option) => {\n const checked = selected.includes(option.value)\n return (\n <label key={option.value} className=\"inline-flex cursor-pointer items-center gap-2\">\n <Checkbox\n checked={checked}\n disabled={disabled}\n onCheckedChange={(state) => {\n const next = new Set(selected)\n if (state === true) next.add(option.value)\n else next.delete(option.value)\n onChange(Array.from(next))\n }}\n />\n <span className=\"text-sm\">{option.label}</span>\n </label>\n )\n })}\n </div>\n )\n } else {\n const selectedValue = Array.isArray(value)\n ? String(value[0] ?? '')\n : value == null\n ? ''\n : String(value)\n control = (\n <Select\n value={selectedValue}\n disabled={disabled}\n onValueChange={(next) => {\n if (!next || next === SELECT_CLEAR_SENTINEL) {\n onChange(null)\n return\n }\n onChange(next)\n }}\n >\n <SelectTrigger aria-invalid={ariaInvalid}>\n <SelectValue placeholder={field.placeholder} />\n </SelectTrigger>\n <SelectContent>\n {!field.required && selectedValue ? (\n <SelectItem value={SELECT_CLEAR_SENTINEL}>\u2014</SelectItem>\n ) : null}\n {options\n .filter((option) => option.value !== '')\n .map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n break\n }\n case 'text':\n default:\n control = (\n <Input\n value={value == null ? '' : String(value)}\n placeholder={field.placeholder}\n disabled={disabled}\n aria-invalid={ariaInvalid}\n onChange={(event) => onChange(event.target.value)}\n />\n )\n break\n }\n\n return (\n <FieldShell\n label={field.label}\n required={field.required}\n description={field.description}\n error={error}\n >\n {control}\n </FieldShell>\n )\n}\n\nexport default DealCustomFieldControl\n"],
5
+ "mappings": ";AA2FQ,SAEc,KAFd;AAzFR,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAEzB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AAUzB,MAAM,wBAAwB;AAE9B,SAAS,cAAc,OAA0B;AAC/C,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ;AACxE;AAEA,SAAS,OAAO,OAA6B;AAC3C,MAAI,iBAAiB,KAAM,QAAO;AAClC,MAAI,OAAO,UAAU,YAAY,OAAO;AACtC,UAAM,SAAS,SAAS,KAAK;AAC7B,WAAO,OAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,OAAO;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAqC;AAC/D,QAAM,UAAU,MAAM,SAAS,WAAW,OAAO;AACjD,QAAM,gBAAgB,SAAS;AAC/B,QAAM,cAAc,SAAS;AAC7B,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA4B,CAAC,CAAC;AAE9E,QAAM,UAAU,MAAM;AACpB,QAAI,iBAAiB,cAAc,SAAS,EAAG;AAC/C,QAAI,OAAO,gBAAgB,WAAY;AACvC,QAAI,YAAY;AAChB,gBAAY,EACT,KAAK,CAAC,YAAY;AACjB,UAAI,CAAC,UAAW,kBAAiB,OAAO;AAAA,IAC1C,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,UAAW,kBAAiB,CAAC,CAAC;AAAA,IACrC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,eAAe,WAAW,CAAC;AAE/B,MAAI,iBAAiB,cAAc,SAAS,EAAG,QAAO;AACtD,SAAO;AACT;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,UAAU,MAAM,MAAM;AAC5B,QAAM,UAAU,MAAM,eAAe,QAAQ,IACzC,MAAM,aAAa,UAAiD,EAAE,IAAI,QAAQ,CAAC,IACnF;AACJ,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,UAAM,KAAK,EAAE,SAAS,IACrB,qBAAC,SAAM,SAAS,SACb;AAAA;AAAA,MACA,WAAW,oBAAC,UAAK,WAAU,oBAAmB,gBAAE,IAAU;AAAA,OAC7D,IACE;AAAA,IACH;AAAA,IACA,cAAc,oBAAC,OAAE,WAAU,iCAAiC,uBAAY,IAAO;AAAA,IAC/E,QAAQ,oBAAC,OAAE,WAAU,kCAAkC,iBAAM,IAAO;AAAA,KACvE;AAEJ;AAEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAgC;AAC9B,QAAM,UAAU,mBAAmB,KAAK;AACxC,QAAM,cAAc,QAAQ,OAAO;AAEnC,MAAI,MAAM,SAAS,YAAY;AAC7B,WACE,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM;AAAA,UACb,aAAa,MAAM;AAAA,UACnB,SAAS,UAAU;AAAA,UACnB,iBAAiB,CAAC,SAAS,SAAS,SAAS,IAAI;AAAA,UACjD;AAAA,UACA,gBAAc;AAAA;AAAA,MAChB;AAAA,MACC,QAAQ,oBAAC,OAAE,WAAU,kCAAkC,iBAAM,IAAO;AAAA,OACvE;AAAA,EAEJ;AAEA,MAAI,MAAM,SAAS,UAAU;AAC3B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,UAAU,MAAM;AAAA,QAChB,aAAa,MAAM;AAAA,QACnB;AAAA,QAEC,gBAAM,UAAU;AAAA,UACf,IAAI,MAAM;AAAA,UACV;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,MAAI;AAEJ,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;AAAA,UAChF,aAAa,MAAM;AAAA,UACnB;AAAA,UACA,gBAAc;AAAA,UACd,UAAU,CAAC,UAAU;AACnB,kBAAM,MAAM,MAAM,OAAO;AACzB,gBAAI,QAAQ,IAAI;AACd,uBAAS,MAAS;AAClB;AAAA,YACF;AACA,kBAAM,SAAS,OAAO,GAAG;AACzB,qBAAS,OAAO,MAAM,MAAM,IAAI,MAAM,MAAM;AAAA,UAC9C;AAAA;AAAA,MACF;AAEF;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,OAAO,KAAK;AAAA,UACnB,UAAU,CAAC,SAAS,SAAS,OAAO,OAAO,MAAM,YAAY,IAAI,MAAS;AAAA,UAC1E;AAAA,UACA,aAAa,MAAM;AAAA,UACnB,gBAAc;AAAA;AAAA,MAChB;AAEF;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,UAAQ;AAAA,UACR,OAAO,OAAO,KAAK;AAAA,UACnB,UAAU,CAAC,SAAS,SAAS,OAAO,KAAK,YAAY,IAAI,MAAS;AAAA,UAClE;AAAA,UACA,aAAa,MAAM;AAAA,UACnB,gBAAc;AAAA;AAAA,MAChB;AAEF;AAAA,IACF,KAAK;AACH,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,cAAc,KAAK;AAAA,UAC1B,UAAU,CAAC,SAAS,SAAS,IAAI;AAAA,UACjC,aAAa,MAAM;AAAA,UACnB;AAAA,UACA,gBAAc;AAAA;AAAA,MAChB;AAEF;AAAA,IACF,KAAK;AACH,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS,OAAO,KAAK,OAAO,KAAK;AAAA,UACxC,aAAa,MAAM;AAAA,UACnB;AAAA,UACA,gBAAc;AAAA,UACd,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA;AAAA,MAClD;AAEF;AAAA,IACF,KAAK,UAAU;AACb,UAAI,MAAM,UAAU;AAClB,cAAM,WAAW,cAAc,KAAK;AACpC,kBACE,oBAAC,SAAI,WAAU,wBACZ,kBAAQ,IAAI,CAAC,WAAW;AACvB,gBAAM,UAAU,SAAS,SAAS,OAAO,KAAK;AAC9C,iBACE,qBAAC,WAAyB,WAAU,iDAClC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,iBAAiB,CAAC,UAAU;AAC1B,wBAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,sBAAI,UAAU,KAAM,MAAK,IAAI,OAAO,KAAK;AAAA,sBACpC,MAAK,OAAO,OAAO,KAAK;AAC7B,2BAAS,MAAM,KAAK,IAAI,CAAC;AAAA,gBAC3B;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,UAAK,WAAU,WAAW,iBAAO,OAAM;AAAA,eAX9B,OAAO,KAYnB;AAAA,QAEJ,CAAC,GACH;AAAA,MAEJ,OAAO;AACL,cAAM,gBAAgB,MAAM,QAAQ,KAAK,IACrC,OAAO,MAAM,CAAC,KAAK,EAAE,IACrB,SAAS,OACP,KACA,OAAO,KAAK;AAClB,kBACE;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP;AAAA,YACA,eAAe,CAAC,SAAS;AACvB,kBAAI,CAAC,QAAQ,SAAS,uBAAuB;AAC3C,yBAAS,IAAI;AACb;AAAA,cACF;AACA,uBAAS,IAAI;AAAA,YACf;AAAA,YAEA;AAAA,kCAAC,iBAAc,gBAAc,aAC3B,8BAAC,eAAY,aAAa,MAAM,aAAa,GAC/C;AAAA,cACA,qBAAC,iBACE;AAAA,iBAAC,MAAM,YAAY,gBAClB,oBAAC,cAAW,OAAO,uBAAuB,oBAAC,IACzC;AAAA,gBACH,QACE,OAAO,CAAC,WAAW,OAAO,UAAU,EAAE,EACtC,IAAI,CAAC,WACJ,oBAAC,cAA8B,OAAO,OAAO,OAC1C,iBAAO,SADO,OAAO,KAExB,CACD;AAAA,iBACL;AAAA;AAAA;AAAA,QACF;AAAA,MAEJ;AACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL;AACE,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS,OAAO,KAAK,OAAO,KAAK;AAAA,UACxC,aAAa,MAAM;AAAA,UACnB;AAAA,UACA,gBAAc;AAAA,UACd,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA;AAAA,MAClD;AAEF;AAAA,EACJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,iCAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ const EMPTY_VALUES = {
2
+ title: "",
3
+ status: "",
4
+ pipelineId: "",
5
+ pipelineStageId: "",
6
+ valueAmount: "",
7
+ valueCurrency: "",
8
+ probability: "",
9
+ expectedCloseAt: "",
10
+ description: "",
11
+ personIds: [],
12
+ companyIds: []
13
+ };
14
+ export {
15
+ EMPTY_VALUES
16
+ };
17
+ //# sourceMappingURL=dealFormTypes.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/dealFormTypes.ts"],
4
+ "sourcesContent": ["export type Translate = (key: string, fallback: string, params?: Record<string, string | number>) => string\n\nexport type BaseValues = {\n title: string\n status: string\n pipelineId: string\n pipelineStageId: string\n valueAmount: string\n valueCurrency: string\n probability: string\n expectedCloseAt: string\n description: string\n personIds: string[]\n companyIds: string[]\n}\n\nexport const EMPTY_VALUES: BaseValues = {\n title: '',\n status: '',\n pipelineId: '',\n pipelineStageId: '',\n valueAmount: '',\n valueCurrency: '',\n probability: '',\n expectedCloseAt: '',\n description: '',\n personIds: [],\n companyIds: [],\n}\n"],
5
+ "mappings": "AAgBO,MAAM,eAA2B;AAAA,EACtC,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,eAAe;AAAA,EACf,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,WAAW,CAAC;AAAA,EACZ,YAAY,CAAC;AACf;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ function sanitizeAmount(raw) {
2
+ const cleaned = raw.replace(/[^\d.]/g, "");
3
+ const firstDot = cleaned.indexOf(".");
4
+ if (firstDot === -1) return cleaned;
5
+ return cleaned.slice(0, firstDot + 1) + cleaned.slice(firstDot + 1).replace(/\./g, "");
6
+ }
7
+ function sanitizeProbability(raw) {
8
+ const digits = raw.replace(/\D/g, "");
9
+ if (!digits) return "";
10
+ return String(Math.min(100, Number(digits)));
11
+ }
12
+ export {
13
+ sanitizeAmount,
14
+ sanitizeProbability
15
+ };
16
+ //# sourceMappingURL=dealNumericInput.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/dealNumericInput.ts"],
4
+ "sourcesContent": ["/**\n * Input sanitizers for the deal create form's numeric fields. They guarantee the\n * underlying state only ever holds a valid numeric string (or empty), so non-numeric\n * input can never reach the zod schema and surface a raw \"expected number\" type error.\n */\n\n/** Keep only digits and a single decimal point; strips letters, signs, and extra dots. */\nexport function sanitizeAmount(raw: string): string {\n const cleaned = raw.replace(/[^\\d.]/g, '')\n const firstDot = cleaned.indexOf('.')\n if (firstDot === -1) return cleaned\n return cleaned.slice(0, firstDot + 1) + cleaned.slice(firstDot + 1).replace(/\\./g, '')\n}\n\n/** Digits only, clamped to 0\u2013100, so probability is always a valid percentage. */\nexport function sanitizeProbability(raw: string): string {\n const digits = raw.replace(/\\D/g, '')\n if (!digits) return ''\n return String(Math.min(100, Number(digits)))\n}\n"],
5
+ "mappings": "AAOO,SAAS,eAAe,KAAqB;AAClD,QAAM,UAAU,IAAI,QAAQ,WAAW,EAAE;AACzC,QAAM,WAAW,QAAQ,QAAQ,GAAG;AACpC,MAAI,aAAa,GAAI,QAAO;AAC5B,SAAO,QAAQ,MAAM,GAAG,WAAW,CAAC,IAAI,QAAQ,MAAM,WAAW,CAAC,EAAE,QAAQ,OAAO,EAAE;AACvF;AAGO,SAAS,oBAAoB,KAAqB;AACvD,QAAM,SAAS,IAAI,QAAQ,OAAO,EAAE;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,KAAK,IAAI,KAAK,OAAO,MAAM,CAAC,CAAC;AAC7C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import {
4
+ validateValuesAgainstDefs
5
+ } from "@open-mercato/shared/modules/entities/validation";
6
+ import { collectCustomFieldValues } from "@open-mercato/ui/backend/utils/customFieldValues";
7
+ import { normalizeCustomFieldSubmitValue } from "../customFieldUtils.js";
8
+ function isEmptyCustomFieldValue(field, value) {
9
+ if (value === void 0 || value === null) return true;
10
+ if (typeof value === "string") return value.trim() === "";
11
+ if (Array.isArray(value)) return value.length === 0;
12
+ return field.type === "checkbox" && value !== true;
13
+ }
14
+ function mapCustomDefinitionsForValidation(definitions) {
15
+ return definitions.map((definition) => ({
16
+ key: definition.key,
17
+ kind: definition.kind,
18
+ configJson: {
19
+ validation: Array.isArray(definition.validation) ? definition.validation : []
20
+ }
21
+ }));
22
+ }
23
+ function useDealCustomFields(tr) {
24
+ const [customValues, setCustomValues] = React.useState({});
25
+ const [customFields, setCustomFields] = React.useState([]);
26
+ const [customDefinitions, setCustomDefinitions] = React.useState([]);
27
+ const [customFieldsLoaded, setCustomFieldsLoaded] = React.useState(false);
28
+ const [customCount, setCustomCount] = React.useState(0);
29
+ const customDefaultsAppliedRef = React.useRef(false);
30
+ const handleCustomChange = React.useCallback((key, value) => {
31
+ setCustomValues((current) => ({ ...current, [key]: value }));
32
+ }, []);
33
+ const handleCustomAttributesLoaded = React.useCallback((state) => {
34
+ setCustomFields(state.fields);
35
+ setCustomDefinitions(state.definitions);
36
+ setCustomFieldsLoaded(true);
37
+ setCustomCount(state.fields.length);
38
+ if (customDefaultsAppliedRef.current || state.definitions.length === 0) return;
39
+ customDefaultsAppliedRef.current = true;
40
+ setCustomValues((current) => {
41
+ let changed = false;
42
+ const next = { ...current };
43
+ for (const definition of state.definitions) {
44
+ if (definition.defaultValue === void 0 || definition.defaultValue === null) continue;
45
+ const fieldId = `cf_${definition.key}`;
46
+ if (next[fieldId] !== void 0) continue;
47
+ next[fieldId] = definition.defaultValue;
48
+ changed = true;
49
+ }
50
+ return changed ? next : current;
51
+ });
52
+ }, []);
53
+ const collectNormalizedCustomValues = React.useCallback((source) => collectCustomFieldValues(source, {
54
+ transform: (value) => normalizeCustomFieldSubmitValue(value)
55
+ }), []);
56
+ const validateCustomFields = React.useCallback((source) => {
57
+ const fieldErrors = {};
58
+ const requiredMessage = tr("ui.forms.errors.required", "Required");
59
+ for (const field of customFields) {
60
+ if (!field.required) continue;
61
+ if (isEmptyCustomFieldValue(field, source[field.id])) {
62
+ fieldErrors[field.id] = requiredMessage;
63
+ }
64
+ }
65
+ if (customDefinitions.length > 0) {
66
+ const result = validateValuesAgainstDefs(
67
+ collectNormalizedCustomValues(source),
68
+ mapCustomDefinitionsForValidation(customDefinitions)
69
+ );
70
+ if (!result.ok) {
71
+ for (const [fieldId, message] of Object.entries(result.fieldErrors)) {
72
+ if (!fieldErrors[fieldId]) fieldErrors[fieldId] = tr(message, message);
73
+ }
74
+ }
75
+ }
76
+ return fieldErrors;
77
+ }, [collectNormalizedCustomValues, customDefinitions, customFields, tr]);
78
+ return {
79
+ customValues,
80
+ customFieldsLoaded,
81
+ customCount,
82
+ handleCustomChange,
83
+ handleCustomAttributesLoaded,
84
+ validateCustomFields,
85
+ collectNormalizedCustomValues
86
+ };
87
+ }
88
+ var useDealCustomFields_default = useDealCustomFields;
89
+ export {
90
+ useDealCustomFields_default as default,
91
+ useDealCustomFields
92
+ };
93
+ //# sourceMappingURL=useDealCustomFields.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/useDealCustomFields.ts"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n validateValuesAgainstDefs,\n type CustomFieldDefLike,\n} from '@open-mercato/shared/modules/entities/validation'\nimport type { CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { normalizeCustomFieldSubmitValue } from '../customFieldUtils'\nimport type { DealCustomAttributesLoadState } from './DealCustomAttributes'\nimport type { Translate } from './dealFormTypes'\n\nfunction isEmptyCustomFieldValue(field: CrudField, value: unknown): boolean {\n if (value === undefined || value === null) return true\n if (typeof value === 'string') return value.trim() === ''\n if (Array.isArray(value)) return value.length === 0\n return field.type === 'checkbox' && value !== true\n}\n\nfunction mapCustomDefinitionsForValidation(definitions: CustomFieldDefDto[]): CustomFieldDefLike[] {\n return definitions.map((definition) => ({\n key: definition.key,\n kind: definition.kind,\n configJson: {\n validation: Array.isArray(definition.validation) ? definition.validation : [],\n },\n }))\n}\n\nexport type UseDealCustomFieldsResult = {\n customValues: Record<string, unknown>\n customFieldsLoaded: boolean\n customCount: number\n handleCustomChange: (key: string, value: unknown) => void\n handleCustomAttributesLoaded: (state: DealCustomAttributesLoadState) => void\n validateCustomFields: (source: Record<string, unknown>) => Record<string, string>\n collectNormalizedCustomValues: (source: Record<string, unknown>) => Record<string, unknown>\n}\n\nexport function useDealCustomFields(tr: Translate): UseDealCustomFieldsResult {\n const [customValues, setCustomValues] = React.useState<Record<string, unknown>>({})\n const [customFields, setCustomFields] = React.useState<CrudField[]>([])\n const [customDefinitions, setCustomDefinitions] = React.useState<CustomFieldDefDto[]>([])\n const [customFieldsLoaded, setCustomFieldsLoaded] = React.useState(false)\n const [customCount, setCustomCount] = React.useState(0)\n const customDefaultsAppliedRef = React.useRef(false)\n\n const handleCustomChange = React.useCallback((key: string, value: unknown) => {\n setCustomValues((current) => ({ ...current, [key]: value }))\n }, [])\n\n const handleCustomAttributesLoaded = React.useCallback((state: DealCustomAttributesLoadState) => {\n setCustomFields(state.fields)\n setCustomDefinitions(state.definitions)\n setCustomFieldsLoaded(true)\n setCustomCount(state.fields.length)\n\n if (customDefaultsAppliedRef.current || state.definitions.length === 0) return\n customDefaultsAppliedRef.current = true\n setCustomValues((current) => {\n let changed = false\n const next = { ...current }\n for (const definition of state.definitions) {\n if (definition.defaultValue === undefined || definition.defaultValue === null) continue\n const fieldId = `cf_${definition.key}`\n if (next[fieldId] !== undefined) continue\n next[fieldId] = definition.defaultValue\n changed = true\n }\n return changed ? next : current\n })\n }, [])\n\n const collectNormalizedCustomValues = React.useCallback((source: Record<string, unknown>) =>\n collectCustomFieldValues(source, {\n transform: (value) => normalizeCustomFieldSubmitValue(value),\n }), [])\n\n const validateCustomFields = React.useCallback((source: Record<string, unknown>) => {\n const fieldErrors: Record<string, string> = {}\n const requiredMessage = tr('ui.forms.errors.required', 'Required')\n\n for (const field of customFields) {\n if (!field.required) continue\n if (isEmptyCustomFieldValue(field, source[field.id])) {\n fieldErrors[field.id] = requiredMessage\n }\n }\n\n if (customDefinitions.length > 0) {\n const result = validateValuesAgainstDefs(\n collectNormalizedCustomValues(source),\n mapCustomDefinitionsForValidation(customDefinitions),\n )\n if (!result.ok) {\n for (const [fieldId, message] of Object.entries(result.fieldErrors)) {\n if (!fieldErrors[fieldId]) fieldErrors[fieldId] = tr(message, message)\n }\n }\n }\n\n return fieldErrors\n }, [collectNormalizedCustomValues, customDefinitions, customFields, tr])\n\n return {\n customValues,\n customFieldsLoaded,\n customCount,\n handleCustomChange,\n handleCustomAttributesLoaded,\n validateCustomFields,\n collectNormalizedCustomValues,\n }\n}\n\nexport default useDealCustomFields\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
4
+ function useDealPipelines() {
5
+ const [pipelines, setPipelines] = React.useState([]);
6
+ const [stages, setStages] = React.useState([]);
7
+ const mountedRef = React.useRef(true);
8
+ React.useEffect(() => {
9
+ mountedRef.current = true;
10
+ return () => {
11
+ mountedRef.current = false;
12
+ };
13
+ }, []);
14
+ React.useEffect(() => {
15
+ let cancelled = false;
16
+ (async () => {
17
+ try {
18
+ const call = await apiCall("/api/customers/pipelines");
19
+ if (cancelled || !mountedRef.current) return;
20
+ if (call.ok && call.result?.items) {
21
+ setPipelines(call.result.items);
22
+ }
23
+ } catch {
24
+ if (!cancelled && mountedRef.current) setPipelines([]);
25
+ }
26
+ })().catch(() => {
27
+ });
28
+ return () => {
29
+ cancelled = true;
30
+ };
31
+ }, []);
32
+ const loadStages = React.useCallback(async (pipelineId) => {
33
+ if (!pipelineId) {
34
+ if (mountedRef.current) setStages([]);
35
+ return;
36
+ }
37
+ try {
38
+ const call = await apiCall(
39
+ `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`
40
+ );
41
+ if (!mountedRef.current) return;
42
+ if (call.ok && call.result?.items) {
43
+ const sorted = [...call.result.items].sort((a, b) => a.order - b.order);
44
+ setStages(sorted);
45
+ } else {
46
+ setStages([]);
47
+ }
48
+ } catch {
49
+ if (mountedRef.current) setStages([]);
50
+ }
51
+ }, []);
52
+ return { pipelines, stages, loadStages };
53
+ }
54
+ var useDealPipelines_default = useDealPipelines;
55
+ export {
56
+ useDealPipelines_default as default,
57
+ useDealPipelines
58
+ };
59
+ //# sourceMappingURL=useDealPipelines.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/useDealPipelines.ts"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\n\nexport type PipelineOption = {\n id: string\n name: string\n isDefault: boolean\n}\n\nexport type PipelineStageOption = {\n id: string\n label: string\n order: number\n}\n\nexport type UseDealPipelinesResult = {\n pipelines: PipelineOption[]\n stages: PipelineStageOption[]\n loadStages: (pipelineId: string) => Promise<void>\n}\n\nexport function useDealPipelines(): UseDealPipelinesResult {\n const [pipelines, setPipelines] = React.useState<PipelineOption[]>([])\n const [stages, setStages] = React.useState<PipelineStageOption[]>([])\n const mountedRef = React.useRef(true)\n\n React.useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n ;(async () => {\n try {\n const call = await apiCall<{ items: PipelineOption[] }>('/api/customers/pipelines')\n if (cancelled || !mountedRef.current) return\n if (call.ok && call.result?.items) {\n setPipelines(call.result.items)\n }\n } catch {\n if (!cancelled && mountedRef.current) setPipelines([])\n }\n })().catch(() => {\n // The inner try/catch already resets pipelines on failure; this guards the IIFE promise only.\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n const loadStages = React.useCallback(async (pipelineId: string) => {\n if (!pipelineId) {\n if (mountedRef.current) setStages([])\n return\n }\n try {\n const call = await apiCall<{ items: PipelineStageOption[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`,\n )\n if (!mountedRef.current) return\n if (call.ok && call.result?.items) {\n const sorted = [...call.result.items].sort((a, b) => a.order - b.order)\n setStages(sorted)\n } else {\n setStages([])\n }\n } catch {\n if (mountedRef.current) setStages([])\n }\n }, [])\n\n return { pipelines, stages, loadStages }\n}\n\nexport default useDealPipelines\n"],
5
+ "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,eAAe;AAoBjB,SAAS,mBAA2C;AACzD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA2B,CAAC,CAAC;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAgC,CAAC,CAAC;AACpE,QAAM,aAAa,MAAM,OAAO,IAAI;AAEpC,QAAM,UAAU,MAAM;AACpB,eAAW,UAAU;AACrB,WAAO,MAAM;AACX,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AACf,KAAC,YAAY;AACZ,UAAI;AACF,cAAM,OAAO,MAAM,QAAqC,0BAA0B;AAClF,YAAI,aAAa,CAAC,WAAW,QAAS;AACtC,YAAI,KAAK,MAAM,KAAK,QAAQ,OAAO;AACjC,uBAAa,KAAK,OAAO,KAAK;AAAA,QAChC;AAAA,MACF,QAAQ;AACN,YAAI,CAAC,aAAa,WAAW,QAAS,cAAa,CAAC,CAAC;AAAA,MACvD;AAAA,IACF,GAAG,EAAE,MAAM,MAAM;AAAA,IAEjB,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM,YAAY,OAAO,eAAuB;AACjE,QAAI,CAAC,YAAY;AACf,UAAI,WAAW,QAAS,WAAU,CAAC,CAAC;AACpC;AAAA,IACF;AACA,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,6CAA6C,mBAAmB,UAAU,CAAC;AAAA,MAC7E;AACA,UAAI,CAAC,WAAW,QAAS;AACzB,UAAI,KAAK,MAAM,KAAK,QAAQ,OAAO;AACjC,cAAM,SAAS,CAAC,GAAG,KAAK,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACtE,kBAAU,MAAM;AAAA,MAClB,OAAO;AACL,kBAAU,CAAC,CAAC;AAAA,MACd;AAAA,IACF,QAAQ;AACN,UAAI,WAAW,QAAS,WAAU,CAAC,CAAC;AAAA,IACtC;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,WAAW,QAAQ,WAAW;AACzC;AAEA,IAAO,2BAAQ;",
6
+ "names": []
7
+ }
@@ -71,7 +71,8 @@ function DictionarySelectField({
71
71
  allowInlineCreate = false,
72
72
  allowAppearance = false,
73
73
  showManage = false,
74
- showLabelInput = true
74
+ showLabelInput = true,
75
+ showActiveAppearance = true
75
76
  }) {
76
77
  const t = useT();
77
78
  const queryClient = useQueryClient();
@@ -159,7 +160,8 @@ function DictionarySelectField({
159
160
  allowInlineCreate,
160
161
  allowAppearance,
161
162
  showManage,
162
- showLabelInput
163
+ showLabelInput,
164
+ showActiveAppearance
163
165
  }
164
166
  );
165
167
  }