@open-mercato/core 0.4.5-develop-0f0e676c72 → 0.4.5-develop-e694581d9f

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 (113) hide show
  1. package/dist/generated/entities/customer_deal/index.js +4 -0
  2. package/dist/generated/entities/customer_deal/index.js.map +2 -2
  3. package/dist/generated/entities/customer_pipeline/index.js +17 -0
  4. package/dist/generated/entities/customer_pipeline/index.js.map +7 -0
  5. package/dist/generated/entities/customer_pipeline_stage/index.js +19 -0
  6. package/dist/generated/entities/customer_pipeline_stage/index.js.map +7 -0
  7. package/dist/generated/entities.ids.generated.js +2 -0
  8. package/dist/generated/entities.ids.generated.js.map +2 -2
  9. package/dist/generated/entity-fields-registry.js +4 -0
  10. package/dist/generated/entity-fields-registry.js.map +2 -2
  11. package/dist/modules/customers/acl.js +2 -0
  12. package/dist/modules/customers/acl.js.map +2 -2
  13. package/dist/modules/customers/api/deals/[id]/route.js +4 -0
  14. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  15. package/dist/modules/customers/api/deals/route.js +12 -0
  16. package/dist/modules/customers/api/deals/route.js.map +2 -2
  17. package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -1
  18. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  19. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +69 -0
  20. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +7 -0
  21. package/dist/modules/customers/api/pipeline-stages/route.js +275 -0
  22. package/dist/modules/customers/api/pipeline-stages/route.js.map +7 -0
  23. package/dist/modules/customers/api/pipelines/route.js +245 -0
  24. package/dist/modules/customers/api/pipelines/route.js.map +7 -0
  25. package/dist/modules/customers/backend/config/customers/page.js +2 -0
  26. package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
  27. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +439 -0
  28. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +7 -0
  29. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +17 -0
  30. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +7 -0
  31. package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -1
  32. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  33. package/dist/modules/customers/backend/customers/deals/page.js +35 -1
  34. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  35. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +102 -74
  36. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  37. package/dist/modules/customers/cli.js +28 -2
  38. package/dist/modules/customers/cli.js.map +2 -2
  39. package/dist/modules/customers/commands/deals.js +34 -2
  40. package/dist/modules/customers/commands/deals.js.map +2 -2
  41. package/dist/modules/customers/commands/index.js +2 -0
  42. package/dist/modules/customers/commands/index.js.map +2 -2
  43. package/dist/modules/customers/commands/pipeline-stages.js +126 -0
  44. package/dist/modules/customers/commands/pipeline-stages.js.map +7 -0
  45. package/dist/modules/customers/commands/pipelines.js +87 -0
  46. package/dist/modules/customers/commands/pipelines.js.map +7 -0
  47. package/dist/modules/customers/components/DictionarySettings.js +0 -5
  48. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  49. package/dist/modules/customers/components/PipelineSettings.js +474 -0
  50. package/dist/modules/customers/components/PipelineSettings.js.map +7 -0
  51. package/dist/modules/customers/components/detail/DealForm.js +84 -12
  52. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  53. package/dist/modules/customers/data/entities.js +78 -0
  54. package/dist/modules/customers/data/entities.js.map +2 -2
  55. package/dist/modules/customers/data/validators.js +44 -0
  56. package/dist/modules/customers/data/validators.js.map +2 -2
  57. package/dist/modules/customers/migrations/Migration20260218191730.js +77 -0
  58. package/dist/modules/customers/migrations/Migration20260218191730.js.map +7 -0
  59. package/dist/modules/customers/setup.js +7 -3
  60. package/dist/modules/customers/setup.js.map +2 -2
  61. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +46 -44
  62. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  63. package/dist/modules/translations/api/context.js +10 -1
  64. package/dist/modules/translations/api/context.js.map +2 -2
  65. package/dist/modules/translations/commands/index.js +2 -0
  66. package/dist/modules/translations/commands/index.js.map +7 -0
  67. package/dist/modules/translations/commands/translations.js +160 -0
  68. package/dist/modules/translations/commands/translations.js.map +7 -0
  69. package/dist/modules/translations/index.js +1 -0
  70. package/dist/modules/translations/index.js.map +2 -2
  71. package/dist/modules/workflows/migrations/Migration20260222205305.js +14 -0
  72. package/dist/modules/workflows/migrations/Migration20260222205305.js.map +7 -0
  73. package/generated/entities/customer_deal/index.ts +2 -0
  74. package/generated/entities/customer_pipeline/index.ts +7 -0
  75. package/generated/entities/customer_pipeline_stage/index.ts +8 -0
  76. package/generated/entities.ids.generated.ts +2 -0
  77. package/generated/entity-fields-registry.ts +4 -0
  78. package/package.json +2 -2
  79. package/src/modules/customers/acl.ts +2 -0
  80. package/src/modules/customers/api/deals/[id]/route.ts +4 -0
  81. package/src/modules/customers/api/deals/route.ts +12 -0
  82. package/src/modules/customers/api/dictionaries/[kind]/route.ts +21 -1
  83. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +71 -0
  84. package/src/modules/customers/api/pipeline-stages/route.ts +296 -0
  85. package/src/modules/customers/api/pipelines/route.ts +261 -0
  86. package/src/modules/customers/backend/config/customers/page.tsx +2 -0
  87. package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +13 -0
  88. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +512 -0
  89. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
  90. package/src/modules/customers/backend/customers/deals/page.tsx +33 -1
  91. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +119 -79
  92. package/src/modules/customers/cli.ts +29 -1
  93. package/src/modules/customers/commands/deals.ts +44 -1
  94. package/src/modules/customers/commands/index.ts +2 -0
  95. package/src/modules/customers/commands/pipeline-stages.ts +156 -0
  96. package/src/modules/customers/commands/pipelines.ts +105 -0
  97. package/src/modules/customers/components/DictionarySettings.tsx +0 -5
  98. package/src/modules/customers/components/PipelineSettings.tsx +570 -0
  99. package/src/modules/customers/components/detail/DealForm.tsx +89 -11
  100. package/src/modules/customers/data/entities.ts +64 -0
  101. package/src/modules/customers/data/validators.ts +57 -0
  102. package/src/modules/customers/i18n/de.json +4 -0
  103. package/src/modules/customers/i18n/en.json +4 -0
  104. package/src/modules/customers/i18n/es.json +4 -0
  105. package/src/modules/customers/i18n/pl.json +5 -1
  106. package/src/modules/customers/migrations/Migration20260218191730.ts +84 -0
  107. package/src/modules/customers/setup.ts +5 -1
  108. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -60
  109. package/src/modules/translations/api/context.ts +12 -0
  110. package/src/modules/translations/commands/index.ts +1 -0
  111. package/src/modules/translations/commands/translations.ts +253 -0
  112. package/src/modules/translations/index.ts +1 -0
  113. package/src/modules/workflows/migrations/Migration20260222205305.ts +13 -0
@@ -0,0 +1,439 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Page, PageBody } from "@open-mercato/ui/backend/Page";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
7
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
8
+ import { Button } from "@open-mercato/ui/primitives/button";
9
+ import { Input } from "@open-mercato/ui/primitives/input";
10
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@open-mercato/ui/primitives/dialog";
11
+ import { Spinner } from "@open-mercato/ui/primitives/spinner";
12
+ import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
13
+ import { AppearanceSelector } from "@open-mercato/core/modules/dictionaries/components/AppearanceSelector";
14
+ import { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from "@open-mercato/core/modules/dictionaries/components/dictionaryAppearance";
15
+ function PipelineStagesPage() {
16
+ const t = useT();
17
+ const { confirm, ConfirmDialogElement } = useConfirmDialog();
18
+ const [pipelines, setPipelines] = React.useState([]);
19
+ const [selectedPipelineId, setSelectedPipelineId] = React.useState(null);
20
+ const [stages, setStages] = React.useState([]);
21
+ const [loadingPipelines, setLoadingPipelines] = React.useState(true);
22
+ const [loadingStages, setLoadingStages] = React.useState(false);
23
+ const [pipelineDialog, setPipelineDialog] = React.useState(null);
24
+ const [stageDialog, setStageDialog] = React.useState(null);
25
+ const [pipelineName, setPipelineName] = React.useState("");
26
+ const [pipelineIsDefault, setPipelineIsDefault] = React.useState(false);
27
+ const [stageName, setStageName] = React.useState("");
28
+ const [stageColor, setStageColor] = React.useState(null);
29
+ const [stageIcon, setStageIcon] = React.useState(null);
30
+ const [saving, setSaving] = React.useState(false);
31
+ const selectedPipeline = React.useMemo(
32
+ () => pipelines.find((p) => p.id === selectedPipelineId) ?? null,
33
+ [pipelines, selectedPipelineId]
34
+ );
35
+ const loadPipelines = React.useCallback(async () => {
36
+ setLoadingPipelines(true);
37
+ try {
38
+ const result = await apiCall("/api/customers/pipelines");
39
+ if (result.ok && result.result?.items) {
40
+ const items = result.result.items;
41
+ setPipelines(items);
42
+ if (!selectedPipelineId && items.length > 0) {
43
+ const defaultPipeline = items.find((p) => p.isDefault) ?? items[0];
44
+ setSelectedPipelineId(defaultPipeline.id);
45
+ }
46
+ }
47
+ } catch {
48
+ flash(t("customers.config.pipelineStages.errorLoadPipelines", "Failed to load pipelines"), "error");
49
+ } finally {
50
+ setLoadingPipelines(false);
51
+ }
52
+ }, [selectedPipelineId, t]);
53
+ const loadStages = React.useCallback(async (pipelineId) => {
54
+ setLoadingStages(true);
55
+ try {
56
+ const result = await apiCall(
57
+ `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`
58
+ );
59
+ if (result.ok && result.result?.items) {
60
+ setStages(result.result.items);
61
+ }
62
+ } catch {
63
+ flash(t("customers.config.pipelineStages.errorLoadStages", "Failed to load pipeline stages"), "error");
64
+ } finally {
65
+ setLoadingStages(false);
66
+ }
67
+ }, [t]);
68
+ React.useEffect(() => {
69
+ void loadPipelines();
70
+ }, [loadPipelines]);
71
+ React.useEffect(() => {
72
+ if (selectedPipelineId) {
73
+ void loadStages(selectedPipelineId);
74
+ } else {
75
+ setStages([]);
76
+ }
77
+ }, [selectedPipelineId, loadStages]);
78
+ function openCreatePipeline() {
79
+ setPipelineName("");
80
+ setPipelineIsDefault(false);
81
+ setPipelineDialog({ mode: "create" });
82
+ }
83
+ function openEditPipeline(pipeline) {
84
+ setPipelineName(pipeline.name);
85
+ setPipelineIsDefault(pipeline.isDefault);
86
+ setPipelineDialog({ mode: "edit", pipeline });
87
+ }
88
+ async function savePipeline() {
89
+ if (!pipelineName.trim()) return;
90
+ setSaving(true);
91
+ try {
92
+ if (pipelineDialog?.mode === "create") {
93
+ const result = await apiCall("/api/customers/pipelines", {
94
+ method: "POST",
95
+ body: JSON.stringify({ name: pipelineName.trim(), isDefault: pipelineIsDefault }),
96
+ headers: { "Content-Type": "application/json" }
97
+ });
98
+ if (!result.ok) {
99
+ flash(t("customers.config.pipelineStages.errorCreatePipeline", "Failed to create pipeline"), "error");
100
+ return;
101
+ }
102
+ flash(t("customers.config.pipelineStages.createdPipeline", "Pipeline created"), "success");
103
+ const newId = result.result?.id ?? null;
104
+ await loadPipelines();
105
+ if (newId) setSelectedPipelineId(newId);
106
+ } else if (pipelineDialog?.mode === "edit") {
107
+ const result = await apiCall("/api/customers/pipelines", {
108
+ method: "PUT",
109
+ body: JSON.stringify({ id: pipelineDialog.pipeline.id, name: pipelineName.trim(), isDefault: pipelineIsDefault }),
110
+ headers: { "Content-Type": "application/json" }
111
+ });
112
+ if (!result.ok) {
113
+ flash(t("customers.config.pipelineStages.errorUpdatePipeline", "Failed to update pipeline"), "error");
114
+ return;
115
+ }
116
+ flash(t("customers.config.pipelineStages.updatedPipeline", "Pipeline updated"), "success");
117
+ await loadPipelines();
118
+ }
119
+ setPipelineDialog(null);
120
+ } finally {
121
+ setSaving(false);
122
+ }
123
+ }
124
+ async function deletePipeline(pipeline) {
125
+ const confirmed = await confirm({
126
+ title: t("customers.config.pipelineStages.deletePipelineTitle", "Delete pipeline?"),
127
+ text: t(
128
+ "customers.config.pipelineStages.deletePipelineDesc",
129
+ "This pipeline and all its stages will be permanently removed. Deals assigned to it will lose their pipeline assignment."
130
+ ),
131
+ confirmText: t("customers.config.pipelineStages.deletePipelineConfirm", "Delete"),
132
+ variant: "destructive"
133
+ });
134
+ if (!confirmed) return;
135
+ const result = await apiCall("/api/customers/pipelines", {
136
+ method: "DELETE",
137
+ body: JSON.stringify({ id: pipeline.id }),
138
+ headers: { "Content-Type": "application/json" }
139
+ });
140
+ if (!result.ok) {
141
+ const error = result.result?.error;
142
+ flash(error ?? t("customers.config.pipelineStages.errorDeletePipeline", "Failed to delete pipeline"), "error");
143
+ return;
144
+ }
145
+ flash(t("customers.config.pipelineStages.deletedPipeline", "Pipeline deleted"), "success");
146
+ if (selectedPipelineId === pipeline.id) setSelectedPipelineId(null);
147
+ await loadPipelines();
148
+ }
149
+ function openCreateStage() {
150
+ setStageName("");
151
+ setStageColor(null);
152
+ setStageIcon(null);
153
+ setStageDialog({ mode: "create" });
154
+ }
155
+ function openEditStage(stage) {
156
+ setStageName(stage.label);
157
+ setStageColor(stage.color);
158
+ setStageIcon(stage.icon);
159
+ setStageDialog({ mode: "edit", stage });
160
+ }
161
+ async function saveStage() {
162
+ if (!stageName.trim() || !selectedPipelineId) return;
163
+ setSaving(true);
164
+ try {
165
+ if (stageDialog?.mode === "create") {
166
+ const result = await apiCall("/api/customers/pipeline-stages", {
167
+ method: "POST",
168
+ body: JSON.stringify({ pipelineId: selectedPipelineId, label: stageName.trim(), color: stageColor, icon: stageIcon }),
169
+ headers: { "Content-Type": "application/json" }
170
+ });
171
+ if (!result.ok) {
172
+ flash(t("customers.config.pipelineStages.errorCreateStage", "Failed to create stage"), "error");
173
+ return;
174
+ }
175
+ flash(t("customers.config.pipelineStages.createdStage", "Stage created"), "success");
176
+ } else if (stageDialog?.mode === "edit") {
177
+ const result = await apiCall("/api/customers/pipeline-stages", {
178
+ method: "PUT",
179
+ body: JSON.stringify({ id: stageDialog.stage.id, label: stageName.trim(), color: stageColor, icon: stageIcon }),
180
+ headers: { "Content-Type": "application/json" }
181
+ });
182
+ if (!result.ok) {
183
+ flash(t("customers.config.pipelineStages.errorUpdateStage", "Failed to update stage"), "error");
184
+ return;
185
+ }
186
+ flash(t("customers.config.pipelineStages.updatedStage", "Stage updated"), "success");
187
+ }
188
+ setStageDialog(null);
189
+ await loadStages(selectedPipelineId);
190
+ } finally {
191
+ setSaving(false);
192
+ }
193
+ }
194
+ async function deleteStage(stage) {
195
+ const confirmed = await confirm({
196
+ title: t("customers.config.pipelineStages.deleteStagTitle", "Delete stage?"),
197
+ text: t(
198
+ "customers.config.pipelineStages.deleteStageDesc",
199
+ "This stage will be permanently removed. Deals assigned to it will lose their stage assignment."
200
+ ),
201
+ confirmText: t("customers.config.pipelineStages.deleteStageConfirm", "Delete"),
202
+ variant: "destructive"
203
+ });
204
+ if (!confirmed) return;
205
+ const result = await apiCall("/api/customers/pipeline-stages", {
206
+ method: "DELETE",
207
+ body: JSON.stringify({ id: stage.id }),
208
+ headers: { "Content-Type": "application/json" }
209
+ });
210
+ if (!result.ok) {
211
+ const error = result.result?.error;
212
+ flash(error ?? t("customers.config.pipelineStages.errorDeleteStage", "Failed to delete stage"), "error");
213
+ return;
214
+ }
215
+ flash(t("customers.config.pipelineStages.deletedStage", "Stage deleted"), "success");
216
+ if (selectedPipelineId) await loadStages(selectedPipelineId);
217
+ }
218
+ async function moveStage(index, direction) {
219
+ const nextIndex = direction === "up" ? index - 1 : index + 1;
220
+ if (nextIndex < 0 || nextIndex >= stages.length) return;
221
+ const reordered = [...stages];
222
+ const [moved] = reordered.splice(index, 1);
223
+ reordered.splice(nextIndex, 0, moved);
224
+ const updated = reordered.map((stage, i) => ({ ...stage, order: i }));
225
+ setStages(updated);
226
+ const result = await apiCall("/api/customers/pipeline-stages/reorder", {
227
+ method: "POST",
228
+ body: JSON.stringify({ stages: updated.map((s) => ({ id: s.id, order: s.order })) }),
229
+ headers: { "Content-Type": "application/json" }
230
+ });
231
+ if (!result.ok) {
232
+ flash(t("customers.config.pipelineStages.errorReorder", "Failed to reorder stages"), "error");
233
+ if (selectedPipelineId) await loadStages(selectedPipelineId);
234
+ }
235
+ }
236
+ const appearanceLabels = React.useMemo(() => ({
237
+ colorLabel: t("customers.config.pipelineStages.colorLabel", "Color"),
238
+ colorHelp: t("customers.config.pipelineStages.colorHelp", "Pick a highlight color for this entry."),
239
+ colorClearLabel: t("customers.config.pipelineStages.colorClear", "Remove color"),
240
+ iconLabel: t("customers.config.pipelineStages.iconLabel", "Icon"),
241
+ iconPlaceholder: t("customers.config.pipelineStages.iconPlaceholder", "Type an emoji or pick one of the suggestions."),
242
+ iconPickerTriggerLabel: t("customers.config.pipelineStages.iconBrowse", "Browse icons and emojis"),
243
+ iconSearchPlaceholder: t("customers.config.pipelineStages.iconSearchPlaceholder", "Search icons or emojis\u2026"),
244
+ iconSearchEmptyLabel: t("customers.config.pipelineStages.iconSearchEmpty", "No icons match your search."),
245
+ iconSuggestionsLabel: t("customers.config.pipelineStages.iconSuggestions", "Suggestions"),
246
+ iconClearLabel: t("customers.config.pipelineStages.iconClear", "Remove icon"),
247
+ previewEmptyLabel: t("customers.config.pipelineStages.previewEmpty", "None")
248
+ }), [t]);
249
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { children: [
250
+ /* @__PURE__ */ jsxs("div", { className: "space-y-6 max-w-2xl", children: [
251
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
252
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: t("customers.config.pipelineStages.title", "Pipeline stages") }),
253
+ /* @__PURE__ */ jsxs(
254
+ "a",
255
+ {
256
+ href: "/backend/customers/deals/pipeline",
257
+ className: "text-sm text-muted-foreground hover:underline",
258
+ children: [
259
+ t("customers.config.pipelineStages.viewBoard", "View pipeline board"),
260
+ " \u2192"
261
+ ]
262
+ }
263
+ )
264
+ ] }),
265
+ loadingPipelines ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
266
+ /* @__PURE__ */ jsx(Spinner, { size: "sm" }),
267
+ t("customers.config.pipelineStages.loadingPipelines", "Loading pipelines\u2026")
268
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
269
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
270
+ /* @__PURE__ */ jsxs(
271
+ "select",
272
+ {
273
+ className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring",
274
+ value: selectedPipelineId ?? "",
275
+ onChange: (e) => setSelectedPipelineId(e.target.value || null),
276
+ children: [
277
+ pipelines.length === 0 && /* @__PURE__ */ jsx("option", { value: "", children: t("customers.config.pipelineStages.noPipelines", "No pipelines yet") }),
278
+ pipelines.map((p) => /* @__PURE__ */ jsxs("option", { value: p.id, children: [
279
+ p.name,
280
+ p.isDefault ? ` (${t("customers.config.pipelineStages.default", "default")})` : ""
281
+ ] }, p.id))
282
+ ]
283
+ }
284
+ ),
285
+ selectedPipeline && /* @__PURE__ */ jsxs(Fragment, { children: [
286
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => openEditPipeline(selectedPipeline), children: t("customers.config.pipelineStages.editPipeline", "Edit") }),
287
+ /* @__PURE__ */ jsx(
288
+ Button,
289
+ {
290
+ variant: "outline",
291
+ size: "sm",
292
+ className: "text-destructive",
293
+ onClick: () => deletePipeline(selectedPipeline),
294
+ children: t("customers.config.pipelineStages.deletePipeline", "Delete")
295
+ }
296
+ )
297
+ ] }),
298
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: openCreatePipeline, children: t("customers.config.pipelineStages.addPipeline", "+ Add pipeline") })
299
+ ] }),
300
+ selectedPipelineId && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
301
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
302
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: t("customers.config.pipelineStages.stagesTitle", "Stages") }),
303
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: openCreateStage, children: t("customers.config.pipelineStages.addStage", "+ Add stage") })
304
+ ] }),
305
+ loadingStages ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
306
+ /* @__PURE__ */ jsx(Spinner, { size: "sm" }),
307
+ t("customers.config.pipelineStages.loadingStages", "Loading stages\u2026")
308
+ ] }) : stages.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("customers.config.pipelineStages.noStages", "No stages yet. Add your first stage.") }) : /* @__PURE__ */ jsx("div", { className: "divide-y rounded-md border", children: stages.map((stage, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
309
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
310
+ /* @__PURE__ */ jsx(
311
+ "button",
312
+ {
313
+ type: "button",
314
+ className: "text-muted-foreground hover:text-foreground disabled:opacity-30",
315
+ onClick: () => moveStage(index, "up"),
316
+ disabled: index === 0,
317
+ "aria-label": t("customers.config.pipelineStages.moveUp", "Move up"),
318
+ children: "\u2191"
319
+ }
320
+ ),
321
+ /* @__PURE__ */ jsx(
322
+ "button",
323
+ {
324
+ type: "button",
325
+ className: "text-muted-foreground hover:text-foreground disabled:opacity-30",
326
+ onClick: () => moveStage(index, "down"),
327
+ disabled: index === stages.length - 1,
328
+ "aria-label": t("customers.config.pipelineStages.moveDown", "Move down"),
329
+ children: "\u2193"
330
+ }
331
+ )
332
+ ] }),
333
+ /* @__PURE__ */ jsxs("span", { className: "flex-1 text-sm flex items-center gap-2", children: [
334
+ stage.color ? renderDictionaryColor(stage.color) : null,
335
+ stage.icon ? renderDictionaryIcon(stage.icon) : null,
336
+ stage.label
337
+ ] }),
338
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => openEditStage(stage), children: t("customers.config.pipelineStages.editStage", "Edit") }),
339
+ /* @__PURE__ */ jsx(
340
+ Button,
341
+ {
342
+ variant: "ghost",
343
+ size: "sm",
344
+ className: "text-destructive",
345
+ onClick: () => deleteStage(stage),
346
+ children: t("customers.config.pipelineStages.deleteStage", "Delete")
347
+ }
348
+ )
349
+ ] }, stage.id)) })
350
+ ] })
351
+ ] })
352
+ ] }),
353
+ /* @__PURE__ */ jsx(Dialog, { open: pipelineDialog !== null, onOpenChange: (open) => {
354
+ if (!open) setPipelineDialog(null);
355
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
356
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: pipelineDialog?.mode === "create" ? t("customers.config.pipelineStages.createPipelineTitle", "Create pipeline") : t("customers.config.pipelineStages.editPipelineTitle", "Edit pipeline") }) }),
357
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
358
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
359
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: t("customers.config.pipelineStages.pipelineName", "Name") }),
360
+ /* @__PURE__ */ jsx(
361
+ Input,
362
+ {
363
+ value: pipelineName,
364
+ onChange: (e) => setPipelineName(e.target.value),
365
+ placeholder: t("customers.config.pipelineStages.pipelineNamePlaceholder", "e.g. Sales Pipeline"),
366
+ onKeyDown: (e) => {
367
+ if (e.key === "Enter") {
368
+ e.preventDefault();
369
+ void savePipeline();
370
+ }
371
+ },
372
+ autoFocus: true
373
+ }
374
+ )
375
+ ] }),
376
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm cursor-pointer", children: [
377
+ /* @__PURE__ */ jsx(
378
+ "input",
379
+ {
380
+ type: "checkbox",
381
+ checked: pipelineIsDefault,
382
+ onChange: (e) => setPipelineIsDefault(e.target.checked)
383
+ }
384
+ ),
385
+ t("customers.config.pipelineStages.setAsDefault", "Set as default pipeline")
386
+ ] })
387
+ ] }),
388
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
389
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setPipelineDialog(null), disabled: saving, children: t("customers.config.pipelineStages.cancel", "Cancel") }),
390
+ /* @__PURE__ */ jsx(Button, { onClick: () => void savePipeline(), disabled: saving || !pipelineName.trim(), children: saving ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : t("customers.config.pipelineStages.save", "Save") })
391
+ ] })
392
+ ] }) }),
393
+ /* @__PURE__ */ jsx(Dialog, { open: stageDialog !== null, onOpenChange: (open) => {
394
+ if (!open) setStageDialog(null);
395
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
396
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: stageDialog?.mode === "create" ? t("customers.config.pipelineStages.createStageTitle", "Create stage") : t("customers.config.pipelineStages.editStageTitle", "Edit stage") }) }),
397
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
398
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
399
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: t("customers.config.pipelineStages.stageName", "Stage name") }),
400
+ /* @__PURE__ */ jsx(
401
+ Input,
402
+ {
403
+ value: stageName,
404
+ onChange: (e) => setStageName(e.target.value),
405
+ placeholder: t("customers.config.pipelineStages.stageNamePlaceholder", "e.g. Qualification"),
406
+ onKeyDown: (e) => {
407
+ if (e.key === "Enter") {
408
+ e.preventDefault();
409
+ void saveStage();
410
+ }
411
+ },
412
+ autoFocus: true
413
+ }
414
+ )
415
+ ] }),
416
+ /* @__PURE__ */ jsx(
417
+ AppearanceSelector,
418
+ {
419
+ color: stageColor,
420
+ icon: stageIcon,
421
+ onColorChange: setStageColor,
422
+ onIconChange: setStageIcon,
423
+ labels: appearanceLabels,
424
+ iconSuggestions: ICON_SUGGESTIONS
425
+ }
426
+ )
427
+ ] }),
428
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
429
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setStageDialog(null), disabled: saving, children: t("customers.config.pipelineStages.cancel", "Cancel") }),
430
+ /* @__PURE__ */ jsx(Button, { onClick: () => void saveStage(), disabled: saving || !stageName.trim(), children: saving ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : t("customers.config.pipelineStages.save", "Save") })
431
+ ] })
432
+ ] }) }),
433
+ ConfirmDialogElement
434
+ ] }) });
435
+ }
436
+ export {
437
+ PipelineStagesPage as default
438
+ };
439
+ //# sourceMappingURL=page.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/customers/backend/config/customers/pipeline-stages/page.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@open-mercato/ui/primitives/dialog'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { AppearanceSelector, type AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\ntype Pipeline = {\n id: string\n name: string\n isDefault: boolean\n}\n\ntype PipelineStage = {\n id: string\n pipelineId: string\n label: string\n order: number\n color: string | null\n icon: string | null\n}\n\ntype PipelineDialogState =\n | { mode: 'create' }\n | { mode: 'edit'; pipeline: Pipeline }\n | null\n\ntype StageDialogState =\n | { mode: 'create' }\n | { mode: 'edit'; stage: PipelineStage }\n | null\n\nexport default function PipelineStagesPage() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [pipelines, setPipelines] = React.useState<Pipeline[]>([])\n const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)\n const [stages, setStages] = React.useState<PipelineStage[]>([])\n const [loadingPipelines, setLoadingPipelines] = React.useState(true)\n const [loadingStages, setLoadingStages] = React.useState(false)\n const [pipelineDialog, setPipelineDialog] = React.useState<PipelineDialogState>(null)\n const [stageDialog, setStageDialog] = React.useState<StageDialogState>(null)\n const [pipelineName, setPipelineName] = React.useState('')\n const [pipelineIsDefault, setPipelineIsDefault] = React.useState(false)\n const [stageName, setStageName] = React.useState('')\n const [stageColor, setStageColor] = React.useState<string | null>(null)\n const [stageIcon, setStageIcon] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n\n const selectedPipeline = React.useMemo(\n () => pipelines.find((p) => p.id === selectedPipelineId) ?? null,\n [pipelines, selectedPipelineId],\n )\n\n const loadPipelines = React.useCallback(async () => {\n setLoadingPipelines(true)\n try {\n const result = await apiCall<{ items: Pipeline[] }>('/api/customers/pipelines')\n if (result.ok && result.result?.items) {\n const items = result.result.items\n setPipelines(items)\n if (!selectedPipelineId && items.length > 0) {\n const defaultPipeline = items.find((p) => p.isDefault) ?? items[0]\n setSelectedPipelineId(defaultPipeline.id)\n }\n }\n } catch {\n flash(t('customers.config.pipelineStages.errorLoadPipelines', 'Failed to load pipelines'), 'error')\n } finally {\n setLoadingPipelines(false)\n }\n }, [selectedPipelineId, t])\n\n const loadStages = React.useCallback(async (pipelineId: string) => {\n setLoadingStages(true)\n try {\n const result = await apiCall<{ items: PipelineStage[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`\n )\n if (result.ok && result.result?.items) {\n setStages(result.result.items)\n }\n } catch {\n flash(t('customers.config.pipelineStages.errorLoadStages', 'Failed to load pipeline stages'), 'error')\n } finally {\n setLoadingStages(false)\n }\n }, [t])\n\n React.useEffect(() => {\n void loadPipelines()\n }, [loadPipelines])\n\n React.useEffect(() => {\n if (selectedPipelineId) {\n void loadStages(selectedPipelineId)\n } else {\n setStages([])\n }\n }, [selectedPipelineId, loadStages])\n\n function openCreatePipeline() {\n setPipelineName('')\n setPipelineIsDefault(false)\n setPipelineDialog({ mode: 'create' })\n }\n\n function openEditPipeline(pipeline: Pipeline) {\n setPipelineName(pipeline.name)\n setPipelineIsDefault(pipeline.isDefault)\n setPipelineDialog({ mode: 'edit', pipeline })\n }\n\n async function savePipeline() {\n if (!pipelineName.trim()) return\n setSaving(true)\n try {\n if (pipelineDialog?.mode === 'create') {\n const result = await apiCall<{ id: string }>('/api/customers/pipelines', {\n method: 'POST',\n body: JSON.stringify({ name: pipelineName.trim(), isDefault: pipelineIsDefault }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n flash(t('customers.config.pipelineStages.errorCreatePipeline', 'Failed to create pipeline'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.createdPipeline', 'Pipeline created'), 'success')\n const newId = result.result?.id ?? null\n await loadPipelines()\n if (newId) setSelectedPipelineId(newId)\n } else if (pipelineDialog?.mode === 'edit') {\n const result = await apiCall('/api/customers/pipelines', {\n method: 'PUT',\n body: JSON.stringify({ id: pipelineDialog.pipeline.id, name: pipelineName.trim(), isDefault: pipelineIsDefault }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n flash(t('customers.config.pipelineStages.errorUpdatePipeline', 'Failed to update pipeline'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.updatedPipeline', 'Pipeline updated'), 'success')\n await loadPipelines()\n }\n setPipelineDialog(null)\n } finally {\n setSaving(false)\n }\n }\n\n async function deletePipeline(pipeline: Pipeline) {\n const confirmed = await confirm({\n title: t('customers.config.pipelineStages.deletePipelineTitle', 'Delete pipeline?'),\n text: t(\n 'customers.config.pipelineStages.deletePipelineDesc',\n 'This pipeline and all its stages will be permanently removed. Deals assigned to it will lose their pipeline assignment.',\n ),\n confirmText: t('customers.config.pipelineStages.deletePipelineConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n const result = await apiCall('/api/customers/pipelines', {\n method: 'DELETE',\n body: JSON.stringify({ id: pipeline.id }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n const error = (result.result as { error?: string })?.error\n flash(error ?? t('customers.config.pipelineStages.errorDeletePipeline', 'Failed to delete pipeline'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.deletedPipeline', 'Pipeline deleted'), 'success')\n if (selectedPipelineId === pipeline.id) setSelectedPipelineId(null)\n await loadPipelines()\n }\n\n function openCreateStage() {\n setStageName('')\n setStageColor(null)\n setStageIcon(null)\n setStageDialog({ mode: 'create' })\n }\n\n function openEditStage(stage: PipelineStage) {\n setStageName(stage.label)\n setStageColor(stage.color)\n setStageIcon(stage.icon)\n setStageDialog({ mode: 'edit', stage })\n }\n\n async function saveStage() {\n if (!stageName.trim() || !selectedPipelineId) return\n setSaving(true)\n try {\n if (stageDialog?.mode === 'create') {\n const result = await apiCall('/api/customers/pipeline-stages', {\n method: 'POST',\n body: JSON.stringify({ pipelineId: selectedPipelineId, label: stageName.trim(), color: stageColor, icon: stageIcon }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n flash(t('customers.config.pipelineStages.errorCreateStage', 'Failed to create stage'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.createdStage', 'Stage created'), 'success')\n } else if (stageDialog?.mode === 'edit') {\n const result = await apiCall('/api/customers/pipeline-stages', {\n method: 'PUT',\n body: JSON.stringify({ id: stageDialog.stage.id, label: stageName.trim(), color: stageColor, icon: stageIcon }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n flash(t('customers.config.pipelineStages.errorUpdateStage', 'Failed to update stage'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.updatedStage', 'Stage updated'), 'success')\n }\n setStageDialog(null)\n await loadStages(selectedPipelineId)\n } finally {\n setSaving(false)\n }\n }\n\n async function deleteStage(stage: PipelineStage) {\n const confirmed = await confirm({\n title: t('customers.config.pipelineStages.deleteStagTitle', 'Delete stage?'),\n text: t(\n 'customers.config.pipelineStages.deleteStageDesc',\n 'This stage will be permanently removed. Deals assigned to it will lose their stage assignment.',\n ),\n confirmText: t('customers.config.pipelineStages.deleteStageConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n const result = await apiCall('/api/customers/pipeline-stages', {\n method: 'DELETE',\n body: JSON.stringify({ id: stage.id }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n const error = (result.result as { error?: string })?.error\n flash(error ?? t('customers.config.pipelineStages.errorDeleteStage', 'Failed to delete stage'), 'error')\n return\n }\n flash(t('customers.config.pipelineStages.deletedStage', 'Stage deleted'), 'success')\n if (selectedPipelineId) await loadStages(selectedPipelineId)\n }\n\n async function moveStage(index: number, direction: 'up' | 'down') {\n const nextIndex = direction === 'up' ? index - 1 : index + 1\n if (nextIndex < 0 || nextIndex >= stages.length) return\n\n const reordered = [...stages]\n const [moved] = reordered.splice(index, 1)\n reordered.splice(nextIndex, 0, moved)\n\n const updated = reordered.map((stage, i) => ({ ...stage, order: i }))\n setStages(updated)\n\n const result = await apiCall('/api/customers/pipeline-stages/reorder', {\n method: 'POST',\n body: JSON.stringify({ stages: updated.map((s) => ({ id: s.id, order: s.order })) }),\n headers: { 'Content-Type': 'application/json' },\n })\n if (!result.ok) {\n flash(t('customers.config.pipelineStages.errorReorder', 'Failed to reorder stages'), 'error')\n if (selectedPipelineId) await loadStages(selectedPipelineId)\n }\n }\n\n const appearanceLabels = React.useMemo<AppearanceSelectorLabels>(() => ({\n colorLabel: t('customers.config.pipelineStages.colorLabel', 'Color'),\n colorHelp: t('customers.config.pipelineStages.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('customers.config.pipelineStages.colorClear', 'Remove color'),\n iconLabel: t('customers.config.pipelineStages.iconLabel', 'Icon'),\n iconPlaceholder: t('customers.config.pipelineStages.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('customers.config.pipelineStages.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('customers.config.pipelineStages.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('customers.config.pipelineStages.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('customers.config.pipelineStages.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('customers.config.pipelineStages.iconClear', 'Remove icon'),\n previewEmptyLabel: t('customers.config.pipelineStages.previewEmpty', 'None'),\n }), [t])\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6 max-w-2xl\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">\n {t('customers.config.pipelineStages.title', 'Pipeline stages')}\n </h2>\n <a\n href=\"/backend/customers/deals/pipeline\"\n className=\"text-sm text-muted-foreground hover:underline\"\n >\n {t('customers.config.pipelineStages.viewBoard', 'View pipeline board')} \u2192\n </a>\n </div>\n\n {loadingPipelines ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" />\n {t('customers.config.pipelineStages.loadingPipelines', 'Loading pipelines\u2026')}\n </div>\n ) : (\n <>\n <div className=\"flex items-center gap-3\">\n <select\n className=\"flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n value={selectedPipelineId ?? ''}\n onChange={(e) => setSelectedPipelineId(e.target.value || null)}\n >\n {pipelines.length === 0 && (\n <option value=\"\">\n {t('customers.config.pipelineStages.noPipelines', 'No pipelines yet')}\n </option>\n )}\n {pipelines.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}{p.isDefault ? ` (${t('customers.config.pipelineStages.default', 'default')})` : ''}\n </option>\n ))}\n </select>\n {selectedPipeline && (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={() => openEditPipeline(selectedPipeline)}>\n {t('customers.config.pipelineStages.editPipeline', 'Edit')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-destructive\"\n onClick={() => deletePipeline(selectedPipeline)}\n >\n {t('customers.config.pipelineStages.deletePipeline', 'Delete')}\n </Button>\n </>\n )}\n <Button variant=\"outline\" size=\"sm\" onClick={openCreatePipeline}>\n {t('customers.config.pipelineStages.addPipeline', '+ Add pipeline')}\n </Button>\n </div>\n\n {selectedPipelineId && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-sm font-medium text-muted-foreground\">\n {t('customers.config.pipelineStages.stagesTitle', 'Stages')}\n </h3>\n <Button size=\"sm\" onClick={openCreateStage}>\n {t('customers.config.pipelineStages.addStage', '+ Add stage')}\n </Button>\n </div>\n\n {loadingStages ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" />\n {t('customers.config.pipelineStages.loadingStages', 'Loading stages\u2026')}\n </div>\n ) : stages.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.config.pipelineStages.noStages', 'No stages yet. Add your first stage.')}\n </p>\n ) : (\n <div className=\"divide-y rounded-md border\">\n {stages.map((stage, index) => (\n <div key={stage.id} className=\"flex items-center gap-3 px-4 py-3\">\n <div className=\"flex flex-col gap-1\">\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground disabled:opacity-30\"\n onClick={() => moveStage(index, 'up')}\n disabled={index === 0}\n aria-label={t('customers.config.pipelineStages.moveUp', 'Move up')}\n >\n \u2191\n </button>\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground disabled:opacity-30\"\n onClick={() => moveStage(index, 'down')}\n disabled={index === stages.length - 1}\n aria-label={t('customers.config.pipelineStages.moveDown', 'Move down')}\n >\n \u2193\n </button>\n </div>\n <span className=\"flex-1 text-sm flex items-center gap-2\">\n {stage.color ? renderDictionaryColor(stage.color) : null}\n {stage.icon ? renderDictionaryIcon(stage.icon) : null}\n {stage.label}\n </span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => openEditStage(stage)}>\n {t('customers.config.pipelineStages.editStage', 'Edit')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive\"\n onClick={() => deleteStage(stage)}\n >\n {t('customers.config.pipelineStages.deleteStage', 'Delete')}\n </Button>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n </>\n )}\n </div>\n\n <Dialog open={pipelineDialog !== null} onOpenChange={(open) => { if (!open) setPipelineDialog(null) }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {pipelineDialog?.mode === 'create'\n ? t('customers.config.pipelineStages.createPipelineTitle', 'Create pipeline')\n : t('customers.config.pipelineStages.editPipelineTitle', 'Edit pipeline')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {t('customers.config.pipelineStages.pipelineName', 'Name')}\n </label>\n <Input\n value={pipelineName}\n onChange={(e) => setPipelineName(e.target.value)}\n placeholder={t('customers.config.pipelineStages.pipelineNamePlaceholder', 'e.g. Sales Pipeline')}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); void savePipeline() } }}\n autoFocus\n />\n </div>\n <label className=\"flex items-center gap-2 text-sm cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={pipelineIsDefault}\n onChange={(e) => setPipelineIsDefault(e.target.checked)}\n />\n {t('customers.config.pipelineStages.setAsDefault', 'Set as default pipeline')}\n </label>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setPipelineDialog(null)} disabled={saving}>\n {t('customers.config.pipelineStages.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void savePipeline()} disabled={saving || !pipelineName.trim()}>\n {saving ? <Spinner size=\"sm\" /> : t('customers.config.pipelineStages.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n <Dialog open={stageDialog !== null} onOpenChange={(open) => { if (!open) setStageDialog(null) }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {stageDialog?.mode === 'create'\n ? t('customers.config.pipelineStages.createStageTitle', 'Create stage')\n : t('customers.config.pipelineStages.editStageTitle', 'Edit stage')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {t('customers.config.pipelineStages.stageName', 'Stage name')}\n </label>\n <Input\n value={stageName}\n onChange={(e) => setStageName(e.target.value)}\n placeholder={t('customers.config.pipelineStages.stageNamePlaceholder', 'e.g. Qualification')}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); void saveStage() } }}\n autoFocus\n />\n </div>\n <AppearanceSelector\n color={stageColor}\n icon={stageIcon}\n onColorChange={setStageColor}\n onIconChange={setStageIcon}\n labels={appearanceLabels}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setStageDialog(null)} disabled={saving}>\n {t('customers.config.pipelineStages.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void saveStage()} disabled={saving || !stageName.trim()}>\n {saving ? <Spinner size=\"sm\" /> : t('customers.config.pipelineStages.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ const metadata = {
2
+ requireAuth: true,
3
+ requireFeatures: ["customers.pipelines.manage"],
4
+ pageTitle: "Pipeline stages",
5
+ pageTitleKey: "customers.config.nav.pipelineStages",
6
+ pageGroup: "Module Configs",
7
+ pageGroupKey: "settings.sections.moduleConfigs",
8
+ pageOrder: 4,
9
+ pageContext: "settings",
10
+ breadcrumb: [
11
+ { label: "Pipeline stages", labelKey: "customers.config.nav.pipelineStages" }
12
+ ]
13
+ };
14
+ export {
15
+ metadata
16
+ };
17
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts"],
4
+ "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['customers.pipelines.manage'],\n pageTitle: 'Pipeline stages',\n pageTitleKey: 'customers.config.nav.pipelineStages',\n pageGroup: 'Module Configs',\n pageGroupKey: 'settings.sections.moduleConfigs',\n pageOrder: 4,\n pageContext: 'settings' as const,\n breadcrumb: [\n { label: 'Pipeline stages', labelKey: 'customers.config.nav.pipelineStages' },\n ],\n} as const\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,4BAA4B;AAAA,EAC9C,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,aAAa;AAAA,EACb,YAAY;AAAA,IACV,EAAE,OAAO,mBAAmB,UAAU,sCAAsC;AAAA,EAC9E;AACF;",
6
+ "names": []
7
+ }
@@ -160,6 +160,8 @@ function DealDetailPage({ params }) {
160
160
  title: base.title,
161
161
  status: base.status ?? void 0,
162
162
  pipelineStage: base.pipelineStage ?? void 0,
163
+ pipelineId: base.pipelineId ?? void 0,
164
+ pipelineStageId: base.pipelineStageId ?? void 0,
163
165
  valueAmount: typeof base.valueAmount === "number" ? base.valueAmount : void 0,
164
166
  valueCurrency: base.valueCurrency ?? void 0,
165
167
  probability: typeof base.probability === "number" ? base.probability : void 0,
@@ -278,7 +280,9 @@ function DealDetailPage({ params }) {
278
280
  const valueLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency) ?? t("customers.deals.detail.noValue", "Not provided");
279
281
  const expectedCloseLabel = formatDate(data.deal.expectedCloseAt, t("customers.deals.detail.noValue", "Not provided"));
280
282
  const statusLabel = resolveDictionaryLabel(data.deal.status, statusDictionaryMap) ?? t("customers.deals.detail.noStatus", "No status");
283
+ const statusDictEntry = data.deal.status ? statusDictionaryMap?.[data.deal.status] ?? null : null;
281
284
  const pipelineLabel = resolveDictionaryLabel(data.deal.pipelineStage, pipelineDictionaryMap);
285
+ const pipelineDictEntry = data.deal.pipelineStage ? pipelineDictionaryMap?.[data.deal.pipelineStage] ?? null : null;
282
286
  const peopleSummaryLabel = data.people.length === 1 ? t("customers.deals.detail.peopleSummaryOne") : t("customers.deals.detail.peopleSummaryMany", void 0, { count: data.people.length });
283
287
  const companiesSummaryLabel = data.companies.length === 1 ? t("customers.deals.detail.companiesSummaryOne") : t("customers.deals.detail.companiesSummaryMany", void 0, { count: data.companies.length });
284
288
  const viewer = data.viewer ?? null;
@@ -355,9 +359,21 @@ function DealDetailPage({ params }) {
355
359
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase text-muted-foreground", children: t("customers.deals.detail.fields.probability", "Probability") }),
356
360
  /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-foreground", children: probabilityLabel })
357
361
  ] }),
362
+ /* @__PURE__ */ jsxs("div", { children: [
363
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase text-muted-foreground", children: t("customers.deals.detail.fields.status", "Status") }),
364
+ /* @__PURE__ */ jsxs("p", { className: "text-base text-foreground flex items-center gap-2", children: [
365
+ statusDictEntry?.color ? renderDictionaryColor(statusDictEntry.color) : null,
366
+ statusDictEntry?.icon ? renderDictionaryIcon(statusDictEntry.icon) : null,
367
+ statusLabel
368
+ ] })
369
+ ] }),
358
370
  /* @__PURE__ */ jsxs("div", { children: [
359
371
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase text-muted-foreground", children: t("customers.deals.detail.fields.pipeline", "Pipeline stage") }),
360
- /* @__PURE__ */ jsx("p", { className: "text-base text-foreground", children: pipelineLabel ?? t("customers.deals.detail.noValue", "Not provided") })
372
+ /* @__PURE__ */ jsxs("p", { className: "text-base text-foreground flex items-center gap-2", children: [
373
+ pipelineDictEntry?.color ? renderDictionaryColor(pipelineDictEntry.color) : null,
374
+ pipelineDictEntry?.icon ? renderDictionaryIcon(pipelineDictEntry.icon) : null,
375
+ pipelineLabel ?? t("customers.deals.detail.noValue", "Not provided")
376
+ ] })
361
377
  ] }),
362
378
  /* @__PURE__ */ jsxs("div", { children: [
363
379
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase text-muted-foreground", children: t("customers.deals.detail.fields.expectedClose", "Expected close") }),
@@ -477,6 +493,8 @@ function DealDetailPage({ params }) {
477
493
  title: data.deal.title ?? "",
478
494
  status: data.deal.status ?? "",
479
495
  pipelineStage: data.deal.pipelineStage ?? "",
496
+ pipelineId: data.deal.pipelineId ?? "",
497
+ pipelineStageId: data.deal.pipelineStageId ?? "",
480
498
  valueAmount: data.deal.valueAmount ? Number(data.deal.valueAmount) : null,
481
499
  valueCurrency: data.deal.valueCurrency ?? void 0,
482
500
  probability: data.deal.probability ?? null,