@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,41 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Flag } from "lucide-react";
4
+ import {
5
+ Select,
6
+ SelectContent,
7
+ SelectItem,
8
+ SelectTrigger,
9
+ SelectTriggerLeading,
10
+ SelectValue
11
+ } from "@open-mercato/ui/primitives/select";
12
+ function PipelineSelect({
13
+ id,
14
+ pipelines,
15
+ value,
16
+ onChange,
17
+ disabled = false,
18
+ placeholder
19
+ }) {
20
+ return /* @__PURE__ */ jsxs(
21
+ Select,
22
+ {
23
+ value: typeof value === "string" && value ? value : void 0,
24
+ onValueChange: (next) => onChange(next ?? ""),
25
+ disabled,
26
+ children: [
27
+ /* @__PURE__ */ jsxs(SelectTrigger, { id, size: "default", children: [
28
+ /* @__PURE__ */ jsx(SelectTriggerLeading, { children: /* @__PURE__ */ jsx(Flag, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }) }),
29
+ /* @__PURE__ */ jsx(SelectValue, { placeholder })
30
+ ] }),
31
+ /* @__PURE__ */ jsx(SelectContent, { children: pipelines.map((pipeline) => /* @__PURE__ */ jsx(SelectItem, { value: pipeline.id, children: pipeline.name }, pipeline.id)) })
32
+ ]
33
+ }
34
+ );
35
+ }
36
+ var PipelineSelect_default = PipelineSelect;
37
+ export {
38
+ PipelineSelect,
39
+ PipelineSelect_default as default
40
+ };
41
+ //# sourceMappingURL=PipelineSelect.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/PipelineSelect.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Flag } from 'lucide-react'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectTriggerLeading,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport type { PipelineOption } from './useDealPipelines'\n\nexport type PipelineSelectProps = {\n id?: string\n pipelines: PipelineOption[]\n value?: string | null\n onChange: (id: string) => void\n disabled?: boolean\n placeholder: string\n}\n\nexport function PipelineSelect({\n id,\n pipelines,\n value,\n onChange,\n disabled = false,\n placeholder,\n}: PipelineSelectProps) {\n return (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => onChange(next ?? '')}\n disabled={disabled}\n >\n <SelectTrigger id={id} size=\"default\">\n <SelectTriggerLeading>\n <Flag className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </SelectTriggerLeading>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {pipelines.map((pipeline) => (\n <SelectItem key={pipeline.id} value={pipeline.id}>\n {pipeline.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n\nexport default PipelineSelect\n"],
5
+ "mappings": ";AAqCM,SAEI,KAFJ;AAlCN,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYA,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,YAAY,QAAQ,QAAQ;AAAA,MACpD,eAAe,CAAC,SAAS,SAAS,QAAQ,EAAE;AAAA,MAC5C;AAAA,MAEA;AAAA,6BAAC,iBAAc,IAAQ,MAAK,WAC1B;AAAA,8BAAC,wBACC,8BAAC,QAAK,WAAU,gCAA+B,eAAY,QAAO,GACpE;AAAA,UACA,oBAAC,eAAY,aAA0B;AAAA,WACzC;AAAA,QACA,oBAAC,iBACE,oBAAU,IAAI,CAAC,aACd,oBAAC,cAA6B,OAAO,SAAS,IAC3C,mBAAS,QADK,SAAS,EAE1B,CACD,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,yBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import {
5
+ Select,
6
+ SelectContent,
7
+ SelectItem,
8
+ SelectTrigger,
9
+ SelectValue
10
+ } from "@open-mercato/ui/primitives/select";
11
+ function PipelineStageSelect({
12
+ id,
13
+ stages,
14
+ value,
15
+ onChange,
16
+ disabled = false,
17
+ placeholder,
18
+ formatCount
19
+ }) {
20
+ const selectedIndex = React.useMemo(
21
+ () => value ? stages.findIndex((stage) => stage.id === value) : -1,
22
+ [stages, value]
23
+ );
24
+ const selectedStage = selectedIndex >= 0 ? stages[selectedIndex] : null;
25
+ return /* @__PURE__ */ jsxs(
26
+ Select,
27
+ {
28
+ value: typeof value === "string" && value ? value : void 0,
29
+ onValueChange: (next) => onChange(next ?? ""),
30
+ disabled: disabled || !stages.length,
31
+ children: [
32
+ /* @__PURE__ */ jsx(SelectTrigger, { id, size: "default", children: /* @__PURE__ */ jsx(SelectValue, { placeholder, children: selectedStage ? /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2 truncate", children: [
33
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selectedStage.label }),
34
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatCount(selectedIndex + 1, stages.length) })
35
+ ] }) : null }) }),
36
+ /* @__PURE__ */ jsx(SelectContent, { children: stages.map((stage, index) => /* @__PURE__ */ jsxs(SelectItem, { value: stage.id, children: [
37
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: stage.label }),
38
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatCount(index + 1, stages.length) })
39
+ ] }, stage.id)) })
40
+ ]
41
+ }
42
+ );
43
+ }
44
+ var PipelineStageSelect_default = PipelineStageSelect;
45
+ export {
46
+ PipelineStageSelect,
47
+ PipelineStageSelect_default as default
48
+ };
49
+ //# sourceMappingURL=PipelineStageSelect.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/PipelineStageSelect.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport type { PipelineStageOption } from './useDealPipelines'\n\nexport type PipelineStageSelectProps = {\n id?: string\n stages: PipelineStageOption[]\n value?: string | null\n onChange: (id: string) => void\n disabled?: boolean\n placeholder: string\n formatCount: (position: number, total: number) => string\n}\n\nexport function PipelineStageSelect({\n id,\n stages,\n value,\n onChange,\n disabled = false,\n placeholder,\n formatCount,\n}: PipelineStageSelectProps) {\n const selectedIndex = React.useMemo(\n () => (value ? stages.findIndex((stage) => stage.id === value) : -1),\n [stages, value],\n )\n const selectedStage = selectedIndex >= 0 ? stages[selectedIndex] : null\n\n return (\n <Select\n value={typeof value === 'string' && value ? value : undefined}\n onValueChange={(next) => onChange(next ?? '')}\n disabled={disabled || !stages.length}\n >\n <SelectTrigger id={id} size=\"default\">\n <SelectValue placeholder={placeholder}>\n {selectedStage ? (\n <span className=\"flex min-w-0 items-center gap-2 truncate\">\n <span className=\"truncate\">{selectedStage.label}</span>\n <span className=\"text-muted-foreground\">\n {formatCount(selectedIndex + 1, stages.length)}\n </span>\n </span>\n ) : null}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n {stages.map((stage, index) => (\n <SelectItem key={stage.id} value={stage.id}>\n <span className=\"truncate\">{stage.label}</span>\n <span className=\"text-muted-foreground\">\n {formatCount(index + 1, stages.length)}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n\nexport default PipelineStageSelect\n"],
5
+ "mappings": ";AA8CY,SACE,KADF;AA5CZ,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAaA,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,gBAAgB,MAAM;AAAA,IAC1B,MAAO,QAAQ,OAAO,UAAU,CAAC,UAAU,MAAM,OAAO,KAAK,IAAI;AAAA,IACjE,CAAC,QAAQ,KAAK;AAAA,EAChB;AACA,QAAM,gBAAgB,iBAAiB,IAAI,OAAO,aAAa,IAAI;AAEnE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,YAAY,QAAQ,QAAQ;AAAA,MACpD,eAAe,CAAC,SAAS,SAAS,QAAQ,EAAE;AAAA,MAC5C,UAAU,YAAY,CAAC,OAAO;AAAA,MAE9B;AAAA,4BAAC,iBAAc,IAAQ,MAAK,WAC1B,8BAAC,eAAY,aACV,0BACC,qBAAC,UAAK,WAAU,4CACd;AAAA,8BAAC,UAAK,WAAU,YAAY,wBAAc,OAAM;AAAA,UAChD,oBAAC,UAAK,WAAU,yBACb,sBAAY,gBAAgB,GAAG,OAAO,MAAM,GAC/C;AAAA,WACF,IACE,MACN,GACF;AAAA,QACA,oBAAC,iBACE,iBAAO,IAAI,CAAC,OAAO,UAClB,qBAAC,cAA0B,OAAO,MAAM,IACtC;AAAA,8BAAC,UAAK,WAAU,YAAY,gBAAM,OAAM;AAAA,UACxC,oBAAC,UAAK,WAAU,yBACb,sBAAY,QAAQ,GAAG,OAAO,MAAM,GACvC;AAAA,aAJe,MAAM,EAKvB,CACD,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,8BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Input } from "@open-mercato/ui/primitives/input";
5
+ const SuffixInput = React.forwardRef(
6
+ ({ suffix, ...props }, ref) => {
7
+ return /* @__PURE__ */ jsx(
8
+ Input,
9
+ {
10
+ ref,
11
+ rightIcon: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: suffix }),
12
+ ...props
13
+ }
14
+ );
15
+ }
16
+ );
17
+ SuffixInput.displayName = "SuffixInput";
18
+ export {
19
+ SuffixInput
20
+ };
21
+ //# sourceMappingURL=SuffixInput.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/SuffixInput.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Input } from '@open-mercato/ui/primitives/input'\n\nexport type SuffixInputProps = React.ComponentPropsWithoutRef<typeof Input> & { suffix: string }\n\nexport const SuffixInput = React.forwardRef<HTMLInputElement, SuffixInputProps>(\n ({ suffix, ...props }, ref) => {\n return (\n <Input\n ref={ref}\n rightIcon={<span className=\"text-sm font-medium text-muted-foreground\">{suffix}</span>}\n {...props}\n />\n )\n }\n)\n\nSuffixInput.displayName = 'SuffixInput'\n"],
5
+ "mappings": ";AAYmB;AAVnB,YAAY,WAAW;AACvB,SAAS,aAAa;AAIf,MAAM,cAAc,MAAM;AAAA,EAC/B,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ;AAC7B,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW,oBAAC,UAAK,WAAU,6CAA6C,kBAAO;AAAA,QAC9E,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AAEA,YAAY,cAAc;",
6
+ "names": []
7
+ }
@@ -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": "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",
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
+ }