@open-mercato/core 0.6.3-develop.3651.1.d3a548a5f1 → 0.6.3-develop.3653.1.b8558737ae
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_settings/index.js +2 -0
- package/dist/generated/entities/customer_settings/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +332 -0
- package/dist/modules/customers/api/deals/aggregate/route.js.map +7 -0
- package/dist/modules/customers/api/deals/bulk-update-owner/route.js +188 -0
- package/dist/modules/customers/api/deals/bulk-update-owner/route.js.map +7 -0
- package/dist/modules/customers/api/deals/bulk-update-stage/route.js +188 -0
- package/dist/modules/customers/api/deals/bulk-update-stage/route.js.map +7 -0
- package/dist/modules/customers/api/deals/route.js +255 -122
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +169 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/deals/page.js +181 -0
- package/dist/modules/customers/backend/config/customers/deals/page.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/deals/page.meta.js +38 -0
- package/dist/modules/customers/backend/config/customers/deals/page.meta.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/create/page.js +17 -6
- package/dist/modules/customers/backend/customers/deals/create/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +2 -0
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js +217 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js +335 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js +29 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js +160 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +157 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js +114 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js +36 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js +199 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js +71 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js +233 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js +126 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js +31 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js +389 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js +181 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js +199 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js +44 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js +70 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +278 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js +135 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js +83 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js +155 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +318 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js +138 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +126 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +71 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +2330 -415
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +3 -3
- package/dist/modules/customers/commands/deals.js +6 -0
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +48 -13
- package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
- package/dist/modules/customers/commands/settings.js +45 -5
- package/dist/modules/customers/commands/settings.js.map +2 -2
- package/dist/modules/customers/data/enrichers.js +130 -1
- package/dist/modules/customers/data/enrichers.js.map +2 -2
- package/dist/modules/customers/data/entities.js +4 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +29 -2
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/bulkDeals.js +150 -0
- package/dist/modules/customers/lib/bulkDeals.js.map +7 -0
- package/dist/modules/customers/lib/kysely.js +12 -0
- package/dist/modules/customers/lib/kysely.js.map +7 -0
- package/dist/modules/customers/lib/stuckDeals.js +32 -0
- package/dist/modules/customers/lib/stuckDeals.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260513203311_customers.js +13 -0
- package/dist/modules/customers/migrations/Migration20260513203311_customers.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js +52 -0
- package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +7 -0
- package/dist/modules/customers/workers/deals-bulk-update-owner.js +41 -0
- package/dist/modules/customers/workers/deals-bulk-update-owner.js.map +7 -0
- package/dist/modules/customers/workers/deals-bulk-update-stage.js +41 -0
- package/dist/modules/customers/workers/deals-bulk-update-stage.js.map +7 -0
- package/generated/entities/customer_settings/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +7 -7
- package/src/modules/customers/api/deals/aggregate/route.ts +438 -0
- package/src/modules/customers/api/deals/bulk-update-owner/route.ts +216 -0
- package/src/modules/customers/api/deals/bulk-update-stage/route.ts +228 -0
- package/src/modules/customers/api/deals/route.ts +330 -135
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +195 -0
- package/src/modules/customers/backend/config/customers/deals/page.meta.ts +36 -0
- package/src/modules/customers/backend/config/customers/deals/page.tsx +205 -0
- package/src/modules/customers/backend/customers/deals/create/page.tsx +25 -6
- package/src/modules/customers/backend/customers/deals/page.tsx +2 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.tsx +277 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.tsx +425 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.tsx +31 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.tsx +171 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +196 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.tsx +138 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChipButton.tsx +47 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.tsx +232 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.tsx +110 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.tsx +310 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.tsx +169 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.tsx +54 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/DealCard.tsx +562 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.tsx +214 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.tsx +257 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.tsx +62 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.tsx +101 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +378 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.tsx +192 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.tsx +107 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.tsx +196 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +408 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.tsx +164 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +171 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +84 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/constants.ts +16 -0
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +2841 -550
- package/src/modules/customers/commands/deals.ts +19 -0
- package/src/modules/customers/commands/pipeline-stages.ts +68 -13
- package/src/modules/customers/commands/settings.ts +64 -5
- package/src/modules/customers/data/enrichers.ts +209 -2
- package/src/modules/customers/data/entities.ts +4 -1
- package/src/modules/customers/data/validators.ts +36 -2
- package/src/modules/customers/i18n/de.json +244 -0
- package/src/modules/customers/i18n/en.json +244 -0
- package/src/modules/customers/i18n/es.json +244 -0
- package/src/modules/customers/i18n/pl.json +244 -0
- package/src/modules/customers/lib/bulkDeals.ts +231 -0
- package/src/modules/customers/lib/kysely.ts +60 -0
- package/src/modules/customers/lib/stuckDeals.ts +88 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +16 -0
- package/src/modules/customers/migrations/Migration20260513203311_customers.ts +13 -0
- package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +71 -0
- package/src/modules/customers/workers/deals-bulk-update-owner.ts +46 -0
- package/src/modules/customers/workers/deals-bulk-update-stage.ts +46 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Tag } from "lucide-react";
|
|
5
|
+
import {
|
|
6
|
+
Popover,
|
|
7
|
+
PopoverContent,
|
|
8
|
+
PopoverTrigger
|
|
9
|
+
} from "@open-mercato/ui/primitives/popover";
|
|
10
|
+
import { Radio, RadioGroup } from "@open-mercato/ui/primitives/radio";
|
|
11
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
|
+
import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
13
|
+
import { ChipButton } from "./ChipButton.js";
|
|
14
|
+
import { FilterPopoverShell } from "./FilterPopoverShell.js";
|
|
15
|
+
const SORT_LABEL_KEYS = {
|
|
16
|
+
updated_desc: { key: "customers.deals.kanban.sort.option.updatedNewest", fallback: "Updated (newest)" },
|
|
17
|
+
updated_asc: { key: "customers.deals.kanban.sort.option.updatedOldest", fallback: "Updated (oldest)" },
|
|
18
|
+
created_desc: { key: "customers.deals.kanban.sort.option.createdNewest", fallback: "Created (newest)" },
|
|
19
|
+
value_desc: { key: "customers.deals.kanban.sort.option.valueHigh", fallback: "Value (high to low)" },
|
|
20
|
+
value_asc: { key: "customers.deals.kanban.sort.option.valueLow", fallback: "Value (low to high)" },
|
|
21
|
+
probability_desc: {
|
|
22
|
+
key: "customers.deals.kanban.sort.option.probabilityHigh",
|
|
23
|
+
fallback: "Probability (high to low)"
|
|
24
|
+
},
|
|
25
|
+
close_asc: { key: "customers.deals.kanban.sort.option.closeSoonest", fallback: "Close date (soonest)" },
|
|
26
|
+
owner_asc: { key: "customers.deals.kanban.sort.option.ownerAsc", fallback: "Owner (A \u2192 Z)" }
|
|
27
|
+
};
|
|
28
|
+
const SORT_ORDER = [
|
|
29
|
+
"updated_desc",
|
|
30
|
+
"updated_asc",
|
|
31
|
+
"created_desc",
|
|
32
|
+
"value_desc",
|
|
33
|
+
"value_asc",
|
|
34
|
+
"probability_desc",
|
|
35
|
+
"close_asc",
|
|
36
|
+
"owner_asc"
|
|
37
|
+
];
|
|
38
|
+
const DEFAULT_SORT = "updated_desc";
|
|
39
|
+
function SortByPopover({ value, onApply }) {
|
|
40
|
+
const t = useT();
|
|
41
|
+
const [open, setOpen] = React.useState(false);
|
|
42
|
+
const [draft, setDraft] = React.useState(value);
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
if (open) setDraft(value);
|
|
45
|
+
}, [open, value]);
|
|
46
|
+
const chipLabel = translateWithFallback(t, "customers.deals.kanban.sort.label", "Sort");
|
|
47
|
+
const chipValue = translateWithFallback(t, SORT_LABEL_KEYS[value].key, SORT_LABEL_KEYS[value].fallback);
|
|
48
|
+
const handleApply = () => {
|
|
49
|
+
onApply(draft);
|
|
50
|
+
setOpen(false);
|
|
51
|
+
};
|
|
52
|
+
const handleKeyDown = (event) => {
|
|
53
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
handleApply();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const footerLeft = /* @__PURE__ */ jsx("span", { children: translateWithFallback(
|
|
59
|
+
t,
|
|
60
|
+
"customers.deals.kanban.sort.defaultHint",
|
|
61
|
+
"Default: {label}",
|
|
62
|
+
{
|
|
63
|
+
label: translateWithFallback(
|
|
64
|
+
t,
|
|
65
|
+
SORT_LABEL_KEYS[DEFAULT_SORT].key,
|
|
66
|
+
SORT_LABEL_KEYS[DEFAULT_SORT].fallback
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
) });
|
|
70
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
71
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ChipButton, { label: chipLabel, value: chipValue, active: value !== DEFAULT_SORT }) }),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
PopoverContent,
|
|
74
|
+
{
|
|
75
|
+
className: "w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl",
|
|
76
|
+
align: "end",
|
|
77
|
+
onKeyDown: handleKeyDown,
|
|
78
|
+
children: /* @__PURE__ */ jsx(
|
|
79
|
+
FilterPopoverShell,
|
|
80
|
+
{
|
|
81
|
+
title: translateWithFallback(t, "customers.deals.kanban.sort.title", "Sort by"),
|
|
82
|
+
leadingIcon: /* @__PURE__ */ jsx(Tag, { className: "size-4" }),
|
|
83
|
+
onClose: () => setOpen(false),
|
|
84
|
+
onCancel: () => setOpen(false),
|
|
85
|
+
onApply: handleApply,
|
|
86
|
+
footerLeft,
|
|
87
|
+
children: /* @__PURE__ */ jsx(
|
|
88
|
+
RadioGroup,
|
|
89
|
+
{
|
|
90
|
+
value: draft,
|
|
91
|
+
onValueChange: (next) => setDraft(next),
|
|
92
|
+
className: "gap-0",
|
|
93
|
+
children: SORT_ORDER.map((option) => {
|
|
94
|
+
const isSelected = draft === option;
|
|
95
|
+
const isDefault = option === DEFAULT_SORT;
|
|
96
|
+
const rowClass = isSelected ? "bg-muted" : "bg-card";
|
|
97
|
+
const inputId = `sort-by-${option}`;
|
|
98
|
+
return /* @__PURE__ */ jsxs(
|
|
99
|
+
"label",
|
|
100
|
+
{
|
|
101
|
+
htmlFor: inputId,
|
|
102
|
+
className: `flex w-full cursor-pointer items-center gap-3 rounded-md px-4 py-2.5 text-left transition-colors hover:bg-muted ${rowClass}`,
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ jsx(Radio, { id: inputId, value: option }),
|
|
105
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-px", children: [
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
"span",
|
|
108
|
+
{
|
|
109
|
+
className: `text-sm leading-normal text-foreground ${isSelected ? "font-semibold" : "font-normal"}`,
|
|
110
|
+
children: translateWithFallback(
|
|
111
|
+
t,
|
|
112
|
+
SORT_LABEL_KEYS[option].key,
|
|
113
|
+
SORT_LABEL_KEYS[option].fallback
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
isDefault && isSelected ? /* @__PURE__ */ jsx("span", { className: "text-xs font-normal leading-normal text-muted-foreground", children: translateWithFallback(t, "customers.deals.kanban.sort.default", "Default") }) : null
|
|
118
|
+
] })
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
option
|
|
122
|
+
);
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
var SortByPopover_default = SortByPopover;
|
|
133
|
+
export {
|
|
134
|
+
SORT_LABEL_KEYS,
|
|
135
|
+
SortByPopover,
|
|
136
|
+
SortByPopover_default as default
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=SortByPopover.js.map
|
package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Tag } from 'lucide-react'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@open-mercato/ui/primitives/popover'\nimport { Radio, RadioGroup } from '@open-mercato/ui/primitives/radio'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { ChipButton } from './ChipButton'\nimport { FilterPopoverShell } from './FilterPopoverShell'\n\nexport type SortOption =\n | 'updated_desc'\n | 'updated_asc'\n | 'created_desc'\n | 'value_desc'\n | 'value_asc'\n | 'probability_desc'\n | 'close_asc'\n | 'owner_asc'\n\nexport const SORT_LABEL_KEYS: Record<SortOption, { key: string; fallback: string }> = {\n updated_desc: { key: 'customers.deals.kanban.sort.option.updatedNewest', fallback: 'Updated (newest)' },\n updated_asc: { key: 'customers.deals.kanban.sort.option.updatedOldest', fallback: 'Updated (oldest)' },\n created_desc: { key: 'customers.deals.kanban.sort.option.createdNewest', fallback: 'Created (newest)' },\n value_desc: { key: 'customers.deals.kanban.sort.option.valueHigh', fallback: 'Value (high to low)' },\n value_asc: { key: 'customers.deals.kanban.sort.option.valueLow', fallback: 'Value (low to high)' },\n probability_desc: {\n key: 'customers.deals.kanban.sort.option.probabilityHigh',\n fallback: 'Probability (high to low)',\n },\n close_asc: { key: 'customers.deals.kanban.sort.option.closeSoonest', fallback: 'Close date (soonest)' },\n owner_asc: { key: 'customers.deals.kanban.sort.option.ownerAsc', fallback: 'Owner (A \u2192 Z)' },\n}\n\nconst SORT_ORDER: SortOption[] = [\n 'updated_desc',\n 'updated_asc',\n 'created_desc',\n 'value_desc',\n 'value_asc',\n 'probability_desc',\n 'close_asc',\n 'owner_asc',\n]\n\nconst DEFAULT_SORT: SortOption = 'updated_desc'\n\ntype SortByPopoverProps = {\n value: SortOption\n onApply: (next: SortOption) => void\n}\n\nexport function SortByPopover({ value, onApply }: SortByPopoverProps): React.ReactElement {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<SortOption>(value)\n\n React.useEffect(() => {\n if (open) setDraft(value)\n }, [open, value])\n\n const chipLabel = translateWithFallback(t, 'customers.deals.kanban.sort.label', 'Sort')\n const chipValue = translateWithFallback(t, SORT_LABEL_KEYS[value].key, SORT_LABEL_KEYS[value].fallback)\n\n const handleApply = () => {\n onApply(draft)\n setOpen(false)\n }\n\n // Cmd/Ctrl+Enter from anywhere inside the popover confirms \u2014 parity with the dialog\n // primary-action shortcut (`AGENTS.md` UI Interaction rules).\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleApply()\n }\n }\n\n const footerLeft = (\n <span>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.sort.defaultHint',\n 'Default: {label}',\n {\n label: translateWithFallback(\n t,\n SORT_LABEL_KEYS[DEFAULT_SORT].key,\n SORT_LABEL_KEYS[DEFAULT_SORT].fallback,\n ),\n },\n )}\n </span>\n )\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <ChipButton label={chipLabel} value={chipValue} active={value !== DEFAULT_SORT} />\n </PopoverTrigger>\n <PopoverContent\n className=\"w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl\"\n align=\"end\"\n onKeyDown={handleKeyDown}\n >\n <FilterPopoverShell\n title={translateWithFallback(t, 'customers.deals.kanban.sort.title', 'Sort by')}\n leadingIcon={<Tag className=\"size-4\" />}\n onClose={() => setOpen(false)}\n onCancel={() => setOpen(false)}\n onApply={handleApply}\n footerLeft={footerLeft}\n >\n <RadioGroup\n value={draft}\n onValueChange={(next) => setDraft(next as SortOption)}\n className=\"gap-0\"\n >\n {SORT_ORDER.map((option) => {\n const isSelected = draft === option\n const isDefault = option === DEFAULT_SORT\n const rowClass = isSelected ? 'bg-muted' : 'bg-card'\n const inputId = `sort-by-${option}`\n return (\n <label\n key={option}\n htmlFor={inputId}\n className={`flex w-full cursor-pointer items-center gap-3 rounded-md px-4 py-2.5 text-left transition-colors hover:bg-muted ${rowClass}`}\n >\n <Radio id={inputId} value={option} />\n <div className=\"flex min-w-0 flex-1 flex-col gap-px\">\n <span\n className={`text-sm leading-normal text-foreground ${\n isSelected ? 'font-semibold' : 'font-normal'\n }`}\n >\n {translateWithFallback(\n t,\n SORT_LABEL_KEYS[option].key,\n SORT_LABEL_KEYS[option].fallback,\n )}\n </span>\n {isDefault && isSelected ? (\n <span className=\"text-xs font-normal leading-normal text-muted-foreground\">\n {translateWithFallback(t, 'customers.deals.kanban.sort.default', 'Default')}\n </span>\n ) : null}\n </div>\n </label>\n )\n })}\n </RadioGroup>\n </FilterPopoverShell>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport default SortByPopover\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
|
+
import {
|
|
7
|
+
Popover,
|
|
8
|
+
PopoverContent,
|
|
9
|
+
PopoverTrigger
|
|
10
|
+
} from "@open-mercato/ui/primitives/popover";
|
|
11
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
|
+
import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
13
|
+
import { ChipButton } from "./ChipButton.js";
|
|
14
|
+
import { FilterPopoverShell } from "./FilterPopoverShell.js";
|
|
15
|
+
const STATUS_OPTIONS = [
|
|
16
|
+
{
|
|
17
|
+
value: "open",
|
|
18
|
+
labelKey: "customers.deals.kanban.filter.status.open",
|
|
19
|
+
labelFallback: "Open",
|
|
20
|
+
dotClass: "bg-status-success-icon"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: "win",
|
|
24
|
+
labelKey: "customers.deals.kanban.filter.status.won",
|
|
25
|
+
labelFallback: "Won",
|
|
26
|
+
dotClass: "bg-status-warning-icon"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: "loose",
|
|
30
|
+
labelKey: "customers.deals.kanban.filter.status.lost",
|
|
31
|
+
labelFallback: "Lost",
|
|
32
|
+
dotClass: "bg-status-neutral-icon"
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
function StatusFilterPopover({ values, onApply }) {
|
|
36
|
+
const t = useT();
|
|
37
|
+
const [open, setOpen] = React.useState(false);
|
|
38
|
+
const [draft, setDraft] = React.useState(values);
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
if (open) setDraft(values);
|
|
41
|
+
}, [open, values]);
|
|
42
|
+
const chipLabel = translateWithFallback(t, "customers.deals.kanban.filter.status", "Status");
|
|
43
|
+
const chipValue = values.length === 0 ? translateWithFallback(t, "customers.deals.kanban.filter.all", "All") : values.map((value) => {
|
|
44
|
+
const option = STATUS_OPTIONS.find((entry) => entry.value === value);
|
|
45
|
+
return option ? translateWithFallback(t, option.labelKey, option.labelFallback) : value;
|
|
46
|
+
}).join(", ");
|
|
47
|
+
const toggleDraft = (value) => {
|
|
48
|
+
setDraft(
|
|
49
|
+
(prev) => prev.includes(value) ? prev.filter((entry) => entry !== value) : [...prev, value]
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
const handleApply = () => {
|
|
53
|
+
onApply(draft);
|
|
54
|
+
setOpen(false);
|
|
55
|
+
};
|
|
56
|
+
const handleKeyDown = (event) => {
|
|
57
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
handleApply();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
63
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ChipButton, { label: chipLabel, value: chipValue, active: values.length > 0 }) }),
|
|
64
|
+
/* @__PURE__ */ jsx(
|
|
65
|
+
PopoverContent,
|
|
66
|
+
{
|
|
67
|
+
className: "w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl",
|
|
68
|
+
align: "start",
|
|
69
|
+
onKeyDown: handleKeyDown,
|
|
70
|
+
children: /* @__PURE__ */ jsxs(
|
|
71
|
+
FilterPopoverShell,
|
|
72
|
+
{
|
|
73
|
+
title: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold", children: translateWithFallback(t, "customers.deals.kanban.filter.status.title.label", "Filter : ") }),
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "font-normal", children: translateWithFallback(t, "customers.deals.kanban.filter.status", "Status") })
|
|
76
|
+
] }),
|
|
77
|
+
onClose: () => setOpen(false),
|
|
78
|
+
onCancel: () => setOpen(false),
|
|
79
|
+
onApply: handleApply,
|
|
80
|
+
footerLeft: /* @__PURE__ */ jsxs("span", { children: [
|
|
81
|
+
draft.length,
|
|
82
|
+
" ",
|
|
83
|
+
translateWithFallback(t, "customers.deals.kanban.filter.selected", "selected")
|
|
84
|
+
] }),
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase leading-normal tracking-wide text-muted-foreground", children: translateWithFallback(t, "customers.deals.kanban.filter.status", "Status") }),
|
|
87
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-2", children: STATUS_OPTIONS.map((option) => {
|
|
88
|
+
const isSelected = draft.includes(option.value);
|
|
89
|
+
const label = translateWithFallback(t, option.labelKey, option.labelFallback);
|
|
90
|
+
return /* @__PURE__ */ jsxs(
|
|
91
|
+
Button,
|
|
92
|
+
{
|
|
93
|
+
type: "button",
|
|
94
|
+
variant: "ghost",
|
|
95
|
+
size: "2xs",
|
|
96
|
+
onClick: () => toggleDraft(option.value),
|
|
97
|
+
"aria-pressed": isSelected,
|
|
98
|
+
className: `gap-1.5 rounded-full px-2.5 py-1.5 text-xs leading-normal ${isSelected ? "bg-muted font-semibold text-foreground" : "border border-border bg-card font-normal text-muted-foreground hover:bg-muted"}`,
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
"span",
|
|
102
|
+
{
|
|
103
|
+
className: `inline-block size-2 shrink-0 rounded-full ${option.dotClass}`,
|
|
104
|
+
"aria-hidden": "true"
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
108
|
+
isSelected ? /* @__PURE__ */ jsx(X, { className: "size-2.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }) : null
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
option.value
|
|
112
|
+
);
|
|
113
|
+
}) })
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
var StatusFilterPopover_default = StatusFilterPopover;
|
|
122
|
+
export {
|
|
123
|
+
StatusFilterPopover,
|
|
124
|
+
StatusFilterPopover_default as default
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=StatusFilterPopover.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { ChipButton } from './ChipButton'\nimport { FilterPopoverShell } from './FilterPopoverShell'\n\n/**\n * Filter options exposed to the operator.\n *\n * The deal `status` column accepts the historical 4-value enum (`open` | `closed` | `win` |\n * `loose`), but the codebase only ever **writes** `loose` for a lost deal \u2014 `closed` was a\n * latent unused state from an earlier iteration. Exposing a separate \"Lost (closed)\" filter\n * option therefore filtered to nothing and confused operators. We display a single \"Lost\"\n * choice and intentionally map it to the canonical wire value `loose` so the filter matches\n * what the rest of the app actually persists. (Renaming the column / migrating the data to\n * `lost` is a deeper data-model change tracked separately.)\n */\nconst STATUS_OPTIONS: Array<{\n value: string\n labelKey: string\n labelFallback: string\n /** Tone selects the small \u25CF-dot color on the pill (per Figma: green / amber / gray) */\n dotClass: string\n}> = [\n {\n value: 'open',\n labelKey: 'customers.deals.kanban.filter.status.open',\n labelFallback: 'Open',\n dotClass: 'bg-status-success-icon',\n },\n {\n value: 'win',\n labelKey: 'customers.deals.kanban.filter.status.won',\n labelFallback: 'Won',\n dotClass: 'bg-status-warning-icon',\n },\n {\n value: 'loose',\n labelKey: 'customers.deals.kanban.filter.status.lost',\n labelFallback: 'Lost',\n dotClass: 'bg-status-neutral-icon',\n },\n]\n\ntype StatusFilterPopoverProps = {\n values: string[]\n onApply: (next: string[]) => void\n}\n\nexport function StatusFilterPopover({ values, onApply }: StatusFilterPopoverProps): React.ReactElement {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<string[]>(values)\n\n React.useEffect(() => {\n if (open) setDraft(values)\n }, [open, values])\n\n const chipLabel = translateWithFallback(t, 'customers.deals.kanban.filter.status', 'Status')\n const chipValue =\n values.length === 0\n ? translateWithFallback(t, 'customers.deals.kanban.filter.all', 'All')\n : values\n .map((value) => {\n const option = STATUS_OPTIONS.find((entry) => entry.value === value)\n return option\n ? translateWithFallback(t, option.labelKey, option.labelFallback)\n : value\n })\n .join(', ')\n\n const toggleDraft = (value: string) => {\n setDraft((prev) =>\n prev.includes(value) ? prev.filter((entry) => entry !== value) : [...prev, value],\n )\n }\n\n const handleApply = () => {\n onApply(draft)\n setOpen(false)\n }\n\n // Cmd/Ctrl+Enter from anywhere inside the popover confirms \u2014 parity with the dialog\n // primary-action shortcut (`AGENTS.md` UI Interaction rules).\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleApply()\n }\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <ChipButton label={chipLabel} value={chipValue} active={values.length > 0} />\n </PopoverTrigger>\n <PopoverContent\n className=\"w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl\"\n align=\"start\"\n onKeyDown={handleKeyDown}\n >\n <FilterPopoverShell\n title={\n <>\n <span className=\"font-bold\">\n {translateWithFallback(t, 'customers.deals.kanban.filter.status.title.label', 'Filter : ')}\n </span>\n <span className=\"font-normal\">\n {translateWithFallback(t, 'customers.deals.kanban.filter.status', 'Status')}\n </span>\n </>\n }\n onClose={() => setOpen(false)}\n onCancel={() => setOpen(false)}\n onApply={handleApply}\n footerLeft={\n <span>\n {draft.length}{' '}\n {translateWithFallback(t, 'customers.deals.kanban.filter.selected', 'selected')}\n </span>\n }\n >\n <span className=\"text-xs font-semibold uppercase leading-normal tracking-wide text-muted-foreground\">\n {translateWithFallback(t, 'customers.deals.kanban.filter.status', 'Status')}\n </span>\n <div className=\"flex flex-wrap items-center gap-x-1.5 gap-y-2\">\n {STATUS_OPTIONS.map((option) => {\n const isSelected = draft.includes(option.value)\n const label = translateWithFallback(t, option.labelKey, option.labelFallback)\n return (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"2xs\"\n onClick={() => toggleDraft(option.value)}\n aria-pressed={isSelected}\n className={`gap-1.5 rounded-full px-2.5 py-1.5 text-xs leading-normal ${\n isSelected\n ? 'bg-muted font-semibold text-foreground'\n : 'border border-border bg-card font-normal text-muted-foreground hover:bg-muted'\n }`}\n >\n <span\n className={`inline-block size-2 shrink-0 rounded-full ${option.dotClass}`}\n aria-hidden=\"true\"\n />\n <span>{label}</span>\n {isSelected ? (\n <X className=\"size-2.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n </Button>\n )\n })}\n </div>\n </FilterPopoverShell>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport default StatusFilterPopover\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
|
+
import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
6
|
+
const VIEW_KANBAN_HREF = "/backend/customers/deals/pipeline";
|
|
7
|
+
const VIEW_LIST_HREF = "/backend/customers/deals";
|
|
8
|
+
function ViewTabsRow({ active, className }) {
|
|
9
|
+
const t = useT();
|
|
10
|
+
const labels = {
|
|
11
|
+
kanban: translateWithFallback(t, "customers.deals.kanban.view.kanban", "Kanban"),
|
|
12
|
+
list: translateWithFallback(t, "customers.deals.kanban.view.list", "List")
|
|
13
|
+
};
|
|
14
|
+
const baseTab = "inline-flex items-center px-3.5 py-2.5 text-sm leading-normal transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2";
|
|
15
|
+
const activeTab = "border-b-2 border-foreground font-semibold text-foreground";
|
|
16
|
+
const inactiveTab = "border-b-2 border-transparent font-normal text-muted-foreground hover:text-foreground";
|
|
17
|
+
const isKanbanActive = active === "kanban";
|
|
18
|
+
const isListActive = active === "list";
|
|
19
|
+
return /* @__PURE__ */ jsxs(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
role: "tablist",
|
|
23
|
+
"aria-label": translateWithFallback(t, "customers.deals.kanban.view.tablistLabel", "Deals views"),
|
|
24
|
+
className: `flex items-end gap-1 border-b border-border ${className ?? ""}`.trim(),
|
|
25
|
+
children: [
|
|
26
|
+
isKanbanActive ? /* @__PURE__ */ jsx(
|
|
27
|
+
"span",
|
|
28
|
+
{
|
|
29
|
+
role: "tab",
|
|
30
|
+
"aria-selected": true,
|
|
31
|
+
className: `${baseTab} ${activeTab}`,
|
|
32
|
+
children: labels.kanban
|
|
33
|
+
}
|
|
34
|
+
) : /* @__PURE__ */ jsx(
|
|
35
|
+
Link,
|
|
36
|
+
{
|
|
37
|
+
href: VIEW_KANBAN_HREF,
|
|
38
|
+
role: "tab",
|
|
39
|
+
"aria-selected": false,
|
|
40
|
+
className: `${baseTab} ${inactiveTab}`,
|
|
41
|
+
children: labels.kanban
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
isListActive ? /* @__PURE__ */ jsx(
|
|
45
|
+
"span",
|
|
46
|
+
{
|
|
47
|
+
role: "tab",
|
|
48
|
+
"aria-selected": true,
|
|
49
|
+
className: `${baseTab} ${activeTab}`,
|
|
50
|
+
children: labels.list
|
|
51
|
+
}
|
|
52
|
+
) : /* @__PURE__ */ jsx(
|
|
53
|
+
Link,
|
|
54
|
+
{
|
|
55
|
+
href: VIEW_LIST_HREF,
|
|
56
|
+
role: "tab",
|
|
57
|
+
"aria-selected": false,
|
|
58
|
+
className: `${baseTab} ${inactiveTab}`,
|
|
59
|
+
children: labels.list
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
var ViewTabsRow_default = ViewTabsRow;
|
|
67
|
+
export {
|
|
68
|
+
ViewTabsRow,
|
|
69
|
+
ViewTabsRow_default as default
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=ViewTabsRow.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\n\nexport type KanbanView = 'kanban' | 'list'\n\ntype ViewTabsRowProps = {\n active: KanbanView\n className?: string\n}\n\nconst VIEW_KANBAN_HREF = '/backend/customers/deals/pipeline'\nconst VIEW_LIST_HREF = '/backend/customers/deals'\n\nexport function ViewTabsRow({ active, className }: ViewTabsRowProps): React.ReactElement {\n const t = useT()\n const labels = {\n kanban: translateWithFallback(t, 'customers.deals.kanban.view.kanban', 'Kanban'),\n list: translateWithFallback(t, 'customers.deals.kanban.view.list', 'List'),\n }\n\n const baseTab =\n 'inline-flex items-center px-3.5 py-2.5 text-sm leading-normal transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'\n const activeTab = 'border-b-2 border-foreground font-semibold text-foreground'\n const inactiveTab = 'border-b-2 border-transparent font-normal text-muted-foreground hover:text-foreground'\n\n // Renders the active tab as a non-navigating `<span>` and the inactive tab as a `<Link>`,\n // so the user can always round-trip between kanban and list from either page. Previously\n // the kanban tab was a hardcoded `<span>` because the row was only rendered on the kanban\n // page \u2014 adding it to the list page (item 7 of the SPEC-048 UX review) requires it to\n // navigate back to /pipeline.\n const isKanbanActive = active === 'kanban'\n const isListActive = active === 'list'\n\n return (\n <div\n role=\"tablist\"\n aria-label={translateWithFallback(t, 'customers.deals.kanban.view.tablistLabel', 'Deals views')}\n className={`flex items-end gap-1 border-b border-border ${className ?? ''}`.trim()}\n >\n {isKanbanActive ? (\n <span\n role=\"tab\"\n aria-selected={true}\n className={`${baseTab} ${activeTab}`}\n >\n {labels.kanban}\n </span>\n ) : (\n <Link\n href={VIEW_KANBAN_HREF}\n role=\"tab\"\n aria-selected={false}\n className={`${baseTab} ${inactiveTab}`}\n >\n {labels.kanban}\n </Link>\n )}\n {isListActive ? (\n <span\n role=\"tab\"\n aria-selected={true}\n className={`${baseTab} ${activeTab}`}\n >\n {labels.list}\n </span>\n ) : (\n <Link\n href={VIEW_LIST_HREF}\n role=\"tab\"\n aria-selected={false}\n className={`${baseTab} ${inactiveTab}`}\n >\n {labels.list}\n </Link>\n )}\n </div>\n )\n}\n\nexport default ViewTabsRow\n"],
|
|
5
|
+
"mappings": ";AAsCI,SAMI,KANJ;AAnCJ,OAAO,UAAU;AACjB,SAAS,YAAY;AACrB,SAAS,6BAA6B;AAStC,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AAEhB,SAAS,YAAY,EAAE,QAAQ,UAAU,GAAyC;AACvF,QAAM,IAAI,KAAK;AACf,QAAM,SAAS;AAAA,IACb,QAAQ,sBAAsB,GAAG,sCAAsC,QAAQ;AAAA,IAC/E,MAAM,sBAAsB,GAAG,oCAAoC,MAAM;AAAA,EAC3E;AAEA,QAAM,UACJ;AACF,QAAM,YAAY;AAClB,QAAM,cAAc;AAOpB,QAAM,iBAAiB,WAAW;AAClC,QAAM,eAAe,WAAW;AAEhC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,sBAAsB,GAAG,4CAA4C,aAAa;AAAA,MAC9F,WAAW,+CAA+C,aAAa,EAAE,GAAG,KAAK;AAAA,MAEhF;AAAA,yBACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,WAAW,GAAG,OAAO,IAAI,SAAS;AAAA,YAEjC,iBAAO;AAAA;AAAA,QACV,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,WAAW,GAAG,OAAO,IAAI,WAAW;AAAA,YAEnC,iBAAO;AAAA;AAAA,QACV;AAAA,QAED,eACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,WAAW,GAAG,OAAO,IAAI,SAAS;AAAA,YAEjC,iBAAO;AAAA;AAAA,QACV,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,WAAW,GAAG,OAAO,IAAI,WAAW;AAAA,YAEnC,iBAAO;AAAA;AAAA,QACV;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,IAAO,sBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/constants.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Default kanban lane width in pixels (matches SPEC-048 Figma node 982:335). Operators can\n * override per-lane via the drag-resize handle; this constant is the fallback used when no\n * override is recorded.\n *\n * Centralised here so the value can't drift between the Lane wrapper, the AddStageLane CTA,\n * and the page-level DragOverlay \u2014 three places that previously hand-wrote `w-[308px]` and\n * `style={{ width: '308px' }}` independently.\n */\nexport const LANE_WIDTH_PX = 308\n\n/**\n * Tailwind utility class encoding `LANE_WIDTH_PX`. Use this in `className` templates;\n * use `LANE_WIDTH_PX` in inline `style` blocks where the width is genuinely dynamic.\n */\nexport const LANE_WIDTH_CLASS = 'w-[308px]'\n"],
|
|
5
|
+
"mappings": "AASO,MAAM,gBAAgB;AAMtB,MAAM,mBAAmB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|