@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,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
+ }
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Sparkles } from "lucide-react";
4
+ import { DealSectionCard } from "./DealSectionCard.js";
5
+ import { DealTipsCard } from "./DealTipsCard.js";
6
+ import {
7
+ DealCustomAttributes
8
+ } from "./DealCustomAttributes.js";
9
+ function DealCreateSidebar({
10
+ tr,
11
+ customValues,
12
+ onCustomChange,
13
+ errors,
14
+ disabled,
15
+ customCount,
16
+ manageHref,
17
+ onCustomLoaded
18
+ }) {
19
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
20
+ /* @__PURE__ */ jsx(
21
+ DealSectionCard,
22
+ {
23
+ icon: Sparkles,
24
+ title: tr("customers.deals.create.sections.custom.title", "Custom attributes"),
25
+ subtitle: tr("customers.deals.create.sections.custom.subtitle", "{count} fields defined for this tenant", {
26
+ count: customCount
27
+ }),
28
+ children: /* @__PURE__ */ jsx(
29
+ DealCustomAttributes,
30
+ {
31
+ values: customValues,
32
+ onChange: onCustomChange,
33
+ errors,
34
+ disabled,
35
+ manageHref,
36
+ labels: {
37
+ manage: tr("customers.deals.create.sections.custom.manage", "Manage fields"),
38
+ empty: tr("customers.deals.create.sections.custom.empty", "No custom fields defined for deals yet."),
39
+ loading: tr("customers.deals.create.sections.custom.loading", "Loading custom fields\u2026")
40
+ },
41
+ onLoaded: onCustomLoaded
42
+ }
43
+ )
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsx(
47
+ DealTipsCard,
48
+ {
49
+ title: tr("customers.deals.create.tips.title", "Tips for better deals"),
50
+ tips: [
51
+ tr(
52
+ "customers.deals.create.tips.item1",
53
+ 'Use the company name + short deliverable format in the title (e.g. "Copperleaf \u2014 Q3 Renewal")'
54
+ ),
55
+ tr(
56
+ "customers.deals.create.tips.item2",
57
+ "Set probability based on pipeline stage: Qual 10-25%, Proposal 30-50%, Negotiation 50-75%, Contract 75-90%"
58
+ ),
59
+ tr(
60
+ "customers.deals.create.tips.item3",
61
+ "Link primary decision maker as first person \u2014 they get default email CC on activities"
62
+ )
63
+ ]
64
+ }
65
+ )
66
+ ] });
67
+ }
68
+ var DealCreateSidebar_default = DealCreateSidebar;
69
+ export {
70
+ DealCreateSidebar,
71
+ DealCreateSidebar_default as default
72
+ };
73
+ //# sourceMappingURL=DealCreateSidebar.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/DealCreateSidebar.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Sparkles } from 'lucide-react'\nimport { DealSectionCard } from './DealSectionCard'\nimport { DealTipsCard } from './DealTipsCard'\nimport {\n DealCustomAttributes,\n type DealCustomAttributesLoadState,\n} from './DealCustomAttributes'\nimport type { Translate } from './dealFormTypes'\n\nexport type DealCreateSidebarProps = {\n tr: Translate\n customValues: Record<string, unknown>\n onCustomChange: (key: string, value: unknown) => void\n errors: Record<string, string>\n disabled: boolean\n customCount: number\n manageHref: string\n onCustomLoaded: (state: DealCustomAttributesLoadState) => void\n}\n\nexport function DealCreateSidebar({\n tr,\n customValues,\n onCustomChange,\n errors,\n disabled,\n customCount,\n manageHref,\n onCustomLoaded,\n}: DealCreateSidebarProps) {\n return (\n <div className=\"space-y-4\">\n <DealSectionCard\n icon={Sparkles}\n title={tr('customers.deals.create.sections.custom.title', 'Custom attributes')}\n subtitle={tr('customers.deals.create.sections.custom.subtitle', '{count} fields defined for this tenant', {\n count: customCount,\n })}\n >\n <DealCustomAttributes\n values={customValues}\n onChange={onCustomChange}\n errors={errors}\n disabled={disabled}\n manageHref={manageHref}\n labels={{\n manage: tr('customers.deals.create.sections.custom.manage', 'Manage fields'),\n empty: tr('customers.deals.create.sections.custom.empty', 'No custom fields defined for deals yet.'),\n loading: tr('customers.deals.create.sections.custom.loading', 'Loading custom fields\u2026'),\n }}\n onLoaded={onCustomLoaded}\n />\n </DealSectionCard>\n\n <DealTipsCard\n title={tr('customers.deals.create.tips.title', 'Tips for better deals')}\n tips={[\n tr(\n 'customers.deals.create.tips.item1',\n 'Use the company name + short deliverable format in the title (e.g. \"Copperleaf \u2014 Q3 Renewal\")',\n ),\n tr(\n 'customers.deals.create.tips.item2',\n 'Set probability based on pipeline stage: Qual 10-25%, Proposal 30-50%, Negotiation 50-75%, Contract 75-90%',\n ),\n tr(\n 'customers.deals.create.tips.item3',\n 'Link primary decision maker as first person \u2014 they get default email CC on activities',\n ),\n ]}\n />\n </div>\n )\n}\n\nexport default DealCreateSidebar\n"],
5
+ "mappings": ";AAkCI,SAQI,KARJ;AA/BJ,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,OAEK;AAcA,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,SACE,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAO,GAAG,gDAAgD,mBAAmB;AAAA,QAC7E,UAAU,GAAG,mDAAmD,0CAA0C;AAAA,UACxG,OAAO;AAAA,QACT,CAAC;AAAA,QAED;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ;AAAA,YACR,UAAU;AAAA,YACV;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,cACN,QAAQ,GAAG,iDAAiD,eAAe;AAAA,cAC3E,OAAO,GAAG,gDAAgD,yCAAyC;AAAA,cACnG,SAAS,GAAG,kDAAkD,6BAAwB;AAAA,YACxF;AAAA,YACA,UAAU;AAAA;AAAA,QACZ;AAAA;AAAA,IACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,GAAG,qCAAqC,uBAAuB;AAAA,QACtE,MAAM;AAAA,UACJ;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,IAAO,4BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "@open-mercato/shared/lib/utils";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { DictionaryEntrySelect } from "@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect";
7
+ import { useCurrencyDictionary } from "../hooks/useCurrencyDictionary.js";
8
+ const CURRENCY_PRIORITY = ["EUR", "USD", "GBP", "PLN"];
9
+ function DealCurrencyField({ id, value, onChange, disabled = false }) {
10
+ const t = useT();
11
+ const { data, error: rawError, isLoading, refetch } = useCurrencyDictionary();
12
+ const dictError = rawError ? rawError instanceof Error ? rawError.message : String(rawError) : null;
13
+ const resolvedError = React.useMemo(() => {
14
+ if (dictError) return dictError;
15
+ if (!isLoading && !data) {
16
+ return t("customers.deals.form.currency.missing", "Currency dictionary is not configured yet.");
17
+ }
18
+ return null;
19
+ }, [data, dictError, isLoading, t]);
20
+ const fetchOptions = React.useCallback(async () => {
21
+ let payload = data ?? null;
22
+ if (!payload) {
23
+ try {
24
+ payload = await refetch();
25
+ } catch (err) {
26
+ const message = err instanceof Error ? err.message : String(err ?? "");
27
+ throw new Error(message || t("customers.deals.form.currency.error", "Failed to load currency dictionary."));
28
+ }
29
+ }
30
+ if (!payload) {
31
+ throw new Error(t("customers.deals.form.currency.missing", "Currency dictionary is not configured yet."));
32
+ }
33
+ const priorityOrder = /* @__PURE__ */ new Map();
34
+ CURRENCY_PRIORITY.forEach((code, index) => priorityOrder.set(code, index));
35
+ const prioritized = [];
36
+ const remainder = [];
37
+ payload.entries.forEach((entry) => {
38
+ const code = entry.value.toUpperCase();
39
+ const label = entry.label && entry.label.length ? `${code} \u2013 ${entry.label}` : code;
40
+ const option = { value: code, label, color: null, icon: null };
41
+ if (priorityOrder.has(code)) prioritized.push(option);
42
+ else remainder.push(option);
43
+ });
44
+ prioritized.sort((a, b) => priorityOrder.get(a.value) - priorityOrder.get(b.value));
45
+ remainder.sort((a, b) => a.label.localeCompare(b.label, void 0, { sensitivity: "base" }));
46
+ return [...prioritized, ...remainder];
47
+ }, [data, refetch, t]);
48
+ const labels = React.useMemo(
49
+ () => ({
50
+ placeholder: t("customers.deals.form.currency.placeholder", "Select currency\u2026"),
51
+ addLabel: t("customers.deals.form.currency.add", "Add currency"),
52
+ dialogTitle: t("customers.deals.form.currency.dialogTitle", "Add currency"),
53
+ valueLabel: t("customers.deals.form.currency.valueLabel", "Currency code"),
54
+ valuePlaceholder: t("customers.deals.form.currency.valuePlaceholder", "e.g. USD"),
55
+ labelLabel: t("customers.deals.form.currency.labelLabel", "Label"),
56
+ labelPlaceholder: t("customers.deals.form.currency.labelPlaceholder", "Display name shown in UI"),
57
+ emptyError: t("customers.deals.form.currency.error.required", "Currency code is required."),
58
+ cancelLabel: t("customers.deals.form.currency.cancel", "Cancel"),
59
+ saveLabel: t("customers.deals.form.currency.save", "Save"),
60
+ errorLoad: t("customers.deals.form.currency.error", "Failed to load currency dictionary."),
61
+ errorSave: t("customers.deals.form.currency.error", "Failed to load currency dictionary."),
62
+ loadingLabel: t("customers.deals.form.currency.loading", "Loading currencies\u2026"),
63
+ manageTitle: t("customers.deals.form.currency.manage", "Manage currency dictionary")
64
+ }),
65
+ [t]
66
+ );
67
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
68
+ /* @__PURE__ */ jsx(
69
+ DictionaryEntrySelect,
70
+ {
71
+ id,
72
+ value: value || void 0,
73
+ onChange: (next) => onChange(next ?? ""),
74
+ fetchOptions,
75
+ labels,
76
+ manageHref: "/backend/config/dictionaries?key=currency",
77
+ allowInlineCreate: false,
78
+ allowAppearance: false,
79
+ selectClassName: "w-full",
80
+ disabled,
81
+ showLabelInput: false
82
+ }
83
+ ),
84
+ resolvedError ? /* @__PURE__ */ jsx("p", { className: cn("text-xs", dictError ? "text-status-error-text" : "text-muted-foreground"), children: resolvedError }) : null
85
+ ] });
86
+ }
87
+ var DealCurrencyField_default = DealCurrencyField;
88
+ export {
89
+ DealCurrencyField,
90
+ DealCurrencyField_default as default
91
+ };
92
+ //# sourceMappingURL=DealCurrencyField.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/DealCurrencyField.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DictionaryEntrySelect } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { useCurrencyDictionary } from '../hooks/useCurrencyDictionary'\n\nconst CURRENCY_PRIORITY = ['EUR', 'USD', 'GBP', 'PLN'] as const\n\nexport type DealCurrencyFieldProps = {\n id?: string\n value: string\n onChange: (code: string) => void\n disabled?: boolean\n}\n\nexport function DealCurrencyField({ id, value, onChange, disabled = false }: DealCurrencyFieldProps) {\n const t = useT()\n const { data, error: rawError, isLoading, refetch } = useCurrencyDictionary()\n const dictError = rawError\n ? rawError instanceof Error\n ? rawError.message\n : String(rawError)\n : null\n\n const resolvedError = React.useMemo(() => {\n if (dictError) return dictError\n if (!isLoading && !data) {\n return t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.')\n }\n return null\n }, [data, dictError, isLoading, t])\n\n const fetchOptions = React.useCallback(async () => {\n let payload = data ?? null\n if (!payload) {\n try {\n payload = await refetch()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err ?? '')\n throw new Error(message || t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'))\n }\n }\n if (!payload) {\n throw new Error(t('customers.deals.form.currency.missing', 'Currency dictionary is not configured yet.'))\n }\n const priorityOrder = new Map<string, number>()\n CURRENCY_PRIORITY.forEach((code, index) => priorityOrder.set(code, index))\n const prioritized: { value: string; label: string; color: string | null; icon: string | null }[] = []\n const remainder: { value: string; label: string; color: string | null; icon: string | null }[] = []\n payload.entries.forEach((entry) => {\n const code = entry.value.toUpperCase()\n const label = entry.label && entry.label.length ? `${code} \u2013 ${entry.label}` : code\n const option = { value: code, label, color: null, icon: null }\n if (priorityOrder.has(code)) prioritized.push(option)\n else remainder.push(option)\n })\n prioritized.sort((a, b) => priorityOrder.get(a.value)! - priorityOrder.get(b.value)!)\n remainder.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))\n return [...prioritized, ...remainder]\n }, [data, refetch, t])\n\n const labels = React.useMemo(\n () => ({\n placeholder: t('customers.deals.form.currency.placeholder', 'Select currency\u2026'),\n addLabel: t('customers.deals.form.currency.add', 'Add currency'),\n dialogTitle: t('customers.deals.form.currency.dialogTitle', 'Add currency'),\n valueLabel: t('customers.deals.form.currency.valueLabel', 'Currency code'),\n valuePlaceholder: t('customers.deals.form.currency.valuePlaceholder', 'e.g. USD'),\n labelLabel: t('customers.deals.form.currency.labelLabel', 'Label'),\n labelPlaceholder: t('customers.deals.form.currency.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('customers.deals.form.currency.error.required', 'Currency code is required.'),\n cancelLabel: t('customers.deals.form.currency.cancel', 'Cancel'),\n saveLabel: t('customers.deals.form.currency.save', 'Save'),\n errorLoad: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n errorSave: t('customers.deals.form.currency.error', 'Failed to load currency dictionary.'),\n loadingLabel: t('customers.deals.form.currency.loading', 'Loading currencies\u2026'),\n manageTitle: t('customers.deals.form.currency.manage', 'Manage currency dictionary'),\n }),\n [t],\n )\n\n return (\n <div className=\"space-y-1\">\n <DictionaryEntrySelect\n id={id}\n value={value || undefined}\n onChange={(next) => onChange(next ?? '')}\n fetchOptions={fetchOptions}\n labels={labels}\n manageHref=\"/backend/config/dictionaries?key=currency\"\n allowInlineCreate={false}\n allowAppearance={false}\n selectClassName=\"w-full\"\n disabled={disabled}\n showLabelInput={false}\n />\n {resolvedError ? (\n <p className={cn('text-xs', dictError ? 'text-status-error-text' : 'text-muted-foreground')}>\n {resolvedError}\n </p>\n ) : null}\n </div>\n )\n}\n\nexport default DealCurrencyField\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Plus } from "lucide-react";
5
+ import { fetchCustomFieldFormStructure } from "@open-mercato/ui/backend/utils/customFieldForms";
6
+ import { LinkButton } from "@open-mercato/ui/primitives/link-button";
7
+ import { Spinner } from "@open-mercato/ui/primitives/spinner";
8
+ import { E } from "../../../../../generated/entities.ids.generated.js";
9
+ import { DealCustomFieldControl } from "./dealCustomFieldControl.js";
10
+ function ManageFieldsLink({ href, label }) {
11
+ return /* @__PURE__ */ jsx(LinkButton, { asChild: true, variant: "gray", size: "sm", children: /* @__PURE__ */ jsxs("a", { href, className: "inline-flex items-center gap-1", children: [
12
+ /* @__PURE__ */ jsx(Plus, { className: "size-3.5" }),
13
+ label
14
+ ] }) });
15
+ }
16
+ function DealCustomAttributes({
17
+ values,
18
+ onChange,
19
+ errors,
20
+ disabled = false,
21
+ manageHref,
22
+ labels,
23
+ onLoaded
24
+ }) {
25
+ const [fields, setFields] = React.useState([]);
26
+ const [isLoading, setIsLoading] = React.useState(true);
27
+ const onLoadedRef = React.useRef(onLoaded);
28
+ React.useEffect(() => {
29
+ onLoadedRef.current = onLoaded;
30
+ }, [onLoaded]);
31
+ React.useEffect(() => {
32
+ let cancelled = false;
33
+ setIsLoading(true);
34
+ fetchCustomFieldFormStructure([E.customers.customer_deal]).then((result) => {
35
+ if (cancelled) return;
36
+ setFields(result.fields);
37
+ setIsLoading(false);
38
+ onLoadedRef.current?.({ fields: result.fields, definitions: result.definitions });
39
+ }).catch(() => {
40
+ if (cancelled) return;
41
+ setFields([]);
42
+ setIsLoading(false);
43
+ onLoadedRef.current?.({ fields: [], definitions: [] });
44
+ });
45
+ return () => {
46
+ cancelled = true;
47
+ };
48
+ }, []);
49
+ if (isLoading) {
50
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
51
+ /* @__PURE__ */ jsx(Spinner, { className: "size-4" }),
52
+ labels.loading
53
+ ] });
54
+ }
55
+ if (fields.length === 0) {
56
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
57
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.empty }),
58
+ /* @__PURE__ */ jsx(ManageFieldsLink, { href: manageHref, label: labels.manage })
59
+ ] });
60
+ }
61
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
62
+ fields.map((field) => /* @__PURE__ */ jsx(
63
+ DealCustomFieldControl,
64
+ {
65
+ field,
66
+ value: values[field.id],
67
+ onChange: (next) => onChange(field.id, next),
68
+ error: errors?.[field.id],
69
+ disabled
70
+ },
71
+ field.id
72
+ )),
73
+ /* @__PURE__ */ jsx("div", { className: "pt-1", children: /* @__PURE__ */ jsx(ManageFieldsLink, { href: manageHref, label: labels.manage }) })
74
+ ] });
75
+ }
76
+ var DealCustomAttributes_default = DealCustomAttributes;
77
+ export {
78
+ DealCustomAttributes,
79
+ DealCustomAttributes_default as default
80
+ };
81
+ //# sourceMappingURL=DealCustomAttributes.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/components/detail/create/DealCustomAttributes.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Plus } from 'lucide-react'\nimport type { CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { fetchCustomFieldFormStructure } from '@open-mercato/ui/backend/utils/customFieldForms'\nimport { LinkButton } from '@open-mercato/ui/primitives/link-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { E } from '#generated/entities.ids.generated'\nimport { DealCustomFieldControl } from './dealCustomFieldControl'\n\nexport type DealCustomAttributesLoadState = {\n fields: CrudField[]\n definitions: CustomFieldDefDto[]\n}\n\nexport type DealCustomAttributesProps = {\n values: Record<string, unknown>\n onChange: (key: string, value: unknown) => void\n errors?: Record<string, string>\n disabled?: boolean\n manageHref: string\n labels: {\n manage: string\n empty: string\n loading: string\n }\n onLoaded?: (state: DealCustomAttributesLoadState) => void\n}\n\nfunction ManageFieldsLink({ href, label }: { href: string; label: string }) {\n return (\n <LinkButton asChild variant=\"gray\" size=\"sm\">\n <a href={href} className=\"inline-flex items-center gap-1\">\n <Plus className=\"size-3.5\" />\n {label}\n </a>\n </LinkButton>\n )\n}\n\nexport function DealCustomAttributes({\n values,\n onChange,\n errors,\n disabled = false,\n manageHref,\n labels,\n onLoaded,\n}: DealCustomAttributesProps) {\n const [fields, setFields] = React.useState<CrudField[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n const onLoadedRef = React.useRef(onLoaded)\n\n React.useEffect(() => {\n onLoadedRef.current = onLoaded\n }, [onLoaded])\n\n React.useEffect(() => {\n let cancelled = false\n setIsLoading(true)\n fetchCustomFieldFormStructure([E.customers.customer_deal])\n .then((result) => {\n if (cancelled) return\n setFields(result.fields)\n setIsLoading(false)\n onLoadedRef.current?.({ fields: result.fields, definitions: result.definitions })\n })\n .catch(() => {\n if (cancelled) return\n setFields([])\n setIsLoading(false)\n onLoadedRef.current?.({ fields: [], definitions: [] })\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n if (isLoading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"size-4\" />\n {labels.loading}\n </div>\n )\n }\n\n if (fields.length === 0) {\n return (\n <div className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">{labels.empty}</p>\n <ManageFieldsLink href={manageHref} label={labels.manage} />\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {fields.map((field) => (\n <DealCustomFieldControl\n key={field.id}\n field={field}\n value={values[field.id]}\n onChange={(next) => onChange(field.id, next)}\n error={errors?.[field.id]}\n disabled={disabled}\n />\n ))}\n <div className=\"pt-1\">\n <ManageFieldsLink href={manageHref} label={labels.manage} />\n </div>\n </div>\n )\n}\n\nexport default DealCustomAttributes\n"],
5
+ "mappings": ";AAkCM,SACE,KADF;AAhCN,YAAY,WAAW;AACvB,SAAS,YAAY;AAGrB,SAAS,qCAAqC;AAC9C,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,SAAS;AAClB,SAAS,8BAA8B;AAqBvC,SAAS,iBAAiB,EAAE,MAAM,MAAM,GAAoC;AAC1E,SACE,oBAAC,cAAW,SAAO,MAAC,SAAQ,QAAO,MAAK,MACtC,+BAAC,OAAE,MAAY,WAAU,kCACvB;AAAA,wBAAC,QAAK,WAAU,YAAW;AAAA,IAC1B;AAAA,KACH,GACF;AAEJ;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAsB,CAAC,CAAC;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AACrD,QAAM,cAAc,MAAM,OAAO,QAAQ;AAEzC,QAAM,UAAU,MAAM;AACpB,gBAAY,UAAU;AAAA,EACxB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,iBAAa,IAAI;AACjB,kCAA8B,CAAC,EAAE,UAAU,aAAa,CAAC,EACtD,KAAK,CAAC,WAAW;AAChB,UAAI,UAAW;AACf,gBAAU,OAAO,MAAM;AACvB,mBAAa,KAAK;AAClB,kBAAY,UAAU,EAAE,QAAQ,OAAO,QAAQ,aAAa,OAAO,YAAY,CAAC;AAAA,IAClF,CAAC,EACA,MAAM,MAAM;AACX,UAAI,UAAW;AACf,gBAAU,CAAC,CAAC;AACZ,mBAAa,KAAK;AAClB,kBAAY,UAAU,EAAE,QAAQ,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC;AAAA,IACvD,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,MAAI,WAAW;AACb,WACE,qBAAC,SAAI,WAAU,yDACb;AAAA,0BAAC,WAAQ,WAAU,UAAS;AAAA,MAC3B,OAAO;AAAA,OACV;AAAA,EAEJ;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,OAAE,WAAU,iCAAiC,iBAAO,OAAM;AAAA,MAC3D,oBAAC,oBAAiB,MAAM,YAAY,OAAO,OAAO,QAAQ;AAAA,OAC5D;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,WAAO,IAAI,CAAC,UACX;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,OAAO,OAAO,MAAM,EAAE;AAAA,QACtB,UAAU,CAAC,SAAS,SAAS,MAAM,IAAI,IAAI;AAAA,QAC3C,OAAO,SAAS,MAAM,EAAE;AAAA,QACxB;AAAA;AAAA,MALK,MAAM;AAAA,IAMb,CACD;AAAA,IACD,oBAAC,SAAI,WAAU,QACb,8BAAC,oBAAiB,MAAM,YAAY,OAAO,OAAO,QAAQ,GAC5D;AAAA,KACF;AAEJ;AAEA,IAAO,+BAAQ;",
6
+ "names": []
7
+ }