@asteby/metacore-runtime-react 23.7.1 → 23.9.0
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/CHANGELOG.md +39 -0
- package/dist/custom-stages.d.ts +109 -2
- package/dist/custom-stages.d.ts.map +1 -1
- package/dist/custom-stages.js +261 -26
- package/dist/dynamic-kanban.d.ts.map +1 -1
- package/dist/dynamic-kanban.js +414 -47
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/stage-layout.d.ts +23 -0
- package/dist/stage-layout.d.ts.map +1 -0
- package/dist/stage-layout.js +70 -0
- package/dist/stage-overrides.d.ts +25 -0
- package/dist/stage-overrides.d.ts.map +1 -0
- package/dist/stage-overrides.js +64 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/dynamic-kanban-lane-reorder.test.tsx +228 -0
- package/src/__tests__/dynamic-kanban.test.tsx +75 -0
- package/src/__tests__/stage-layout.test.tsx +210 -0
- package/src/__tests__/stage-overrides.test.tsx +293 -0
- package/src/custom-stages.tsx +745 -111
- package/src/dynamic-kanban.tsx +679 -159
- package/src/index.ts +18 -0
- package/src/stage-layout.ts +93 -0
- package/src/stage-overrides.ts +95 -0
- package/src/types.ts +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 23.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0c41860: DynamicKanban: per-lane configuration gear (⚙). Every board lane — declared
|
|
8
|
+
stages (Backlog, Done…) included, not just custom ones — now carries a subtle
|
|
9
|
+
gear next to the search/filter/⚡ header icons that opens a "Configurar etapa"
|
|
10
|
+
dialog to rename the lane, recolor it, and attach extra CONDITIONS (a
|
|
11
|
+
field/operator/value builder reusing the same eq/neq/contains/in component as
|
|
12
|
+
custom stages).
|
|
13
|
+
- Declared lanes persist through a new `/stage-overrides` endpoint
|
|
14
|
+
(`useStageOverrides`), with a "Restablecer etapa" action that drops the
|
|
15
|
+
override; custom real stages persist through their existing `/custom-stages`
|
|
16
|
+
CRUD (which now also accepts `filters`). The gear unifies both behind one UI.
|
|
17
|
+
- A lane that carries conditions queries its data — and counts its header —
|
|
18
|
+
with the stage scope PLUS those conditions (serialized like smart-lane
|
|
19
|
+
filters), and shows a filter indicator + tooltip listing them. Cards can still
|
|
20
|
+
be dragged into the lane (the drop only sets the stage value).
|
|
21
|
+
- The dialog pre-populates the whole form from the lane's live metadata (label,
|
|
22
|
+
color and every condition, editable) and opens with a "Condiciones actuales"
|
|
23
|
+
chip row summarizing the effective query: a locked base "Etapa = <label>" chip,
|
|
24
|
+
an "Etapa final" chip (with tooltip) for terminal stages, and one
|
|
25
|
+
editable/removable chip per extra condition. An overridden lane shows a
|
|
26
|
+
"Personalizada" badge and a two-step "Restablecer al original" that spells out
|
|
27
|
+
exactly what reverts (from `metadata.stages[].original` when the host serves it).
|
|
28
|
+
- Non-intrusive: when `/stage-overrides` is absent the gear simply doesn't
|
|
29
|
+
render on declared lanes. All copy goes through `t('dynamic.stage_config.*')`
|
|
30
|
+
with Spanish defaults.
|
|
31
|
+
|
|
32
|
+
New exports: `useStageOverrides`, `StageConfigDialog`, `StageConditionBuilder`,
|
|
33
|
+
`cardMatchesStageFilters`, and their types. `StageMeta` gains optional
|
|
34
|
+
`overridden` and `filters`.
|
|
35
|
+
|
|
36
|
+
## 23.8.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- a3d3fdf: DynamicKanban: reordenar las columnas del tablero arrastrándolas por su encabezado (estilo Trello/Bitrix). Aplica a todas las lanes —etapas declaradas, etapas custom y smart lanes— con reorden optimista y persistencia por organización vía el endpoint `/stage-layout` del host (`GET`/`PUT` orden completo/`DELETE` restablece). Si el host no expone el endpoint, el drag de columnas queda deshabilitado en silencio y el tablero funciona igual. Se agrega el hook `useStageLayout` y un affordance "Restablecer orden" cuando existe un orden custom.
|
|
41
|
+
|
|
3
42
|
## 23.7.1
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
package/dist/custom-stages.d.ts
CHANGED
|
@@ -57,7 +57,11 @@ export declare function mergeLaneStages(declared: StageMeta[], customLaneStages:
|
|
|
57
57
|
* - in → `f_<field>=IN:v1,v2,...` (comma-separated after `IN:`)
|
|
58
58
|
* Empty fields/values are skipped.
|
|
59
59
|
*/
|
|
60
|
-
export declare function smartLaneParams(filters:
|
|
60
|
+
export declare function smartLaneParams(filters: {
|
|
61
|
+
field: string;
|
|
62
|
+
op: string;
|
|
63
|
+
value: string;
|
|
64
|
+
}[] | undefined): Record<string, string>;
|
|
61
65
|
/**
|
|
62
66
|
* Resolves the smart lanes to paint. The kernel's `metadata.smart_lanes` is the
|
|
63
67
|
* source of truth for rendering (ops #704); the CRUD list only backs the
|
|
@@ -75,6 +79,17 @@ export declare function isCustomStageDraftValid(draft: {
|
|
|
75
79
|
type: CustomStageType;
|
|
76
80
|
filters: CustomStageFilter[];
|
|
77
81
|
}): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Whether a card passes a set of extra lane `filters` (the same conditions a
|
|
84
|
+
* smart lane uses, layered on top of a real stage). Ops:
|
|
85
|
+
* - eq → equal (string compare)
|
|
86
|
+
* - neq → not equal
|
|
87
|
+
* - contains → the card's value (array or string) includes the value
|
|
88
|
+
* - in → the card's value is one of the comma-separated candidates
|
|
89
|
+
* Empty/absent filters pass. Pure — a client-side belt-and-suspenders over the
|
|
90
|
+
* initial (unscoped) board page; the server scopes the per-lane top-up queries.
|
|
91
|
+
*/
|
|
92
|
+
export declare function cardMatchesStageFilters(card: any, filters: CustomStageFilter[] | undefined): boolean;
|
|
78
93
|
/** Slugify a label into a stable-ish lane key (used only when creating). */
|
|
79
94
|
export declare function slugifyStageKey(label: string): string;
|
|
80
95
|
export interface UseCustomStagesResult {
|
|
@@ -111,6 +126,23 @@ export interface CustomStageLaneMenuProps {
|
|
|
111
126
|
}
|
|
112
127
|
/** The ⋮ menu shown in a custom lane's header. */
|
|
113
128
|
export declare function CustomStageLaneMenu({ stage, onEdit, onDelete, }: CustomStageLaneMenuProps): React.JSX.Element;
|
|
129
|
+
export interface StageConditionBuilderProps {
|
|
130
|
+
/** The current filter rows. */
|
|
131
|
+
filters: CustomStageFilter[];
|
|
132
|
+
/** Called with the next filter rows on any add/remove/patch. */
|
|
133
|
+
onChange: (filters: CustomStageFilter[]) => void;
|
|
134
|
+
/** Columns offered in the field dropdown (visible, non-id). */
|
|
135
|
+
fieldChoices: ColumnDefinition[];
|
|
136
|
+
/** Optional heading shown above the rows. */
|
|
137
|
+
label?: string;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The reusable field/operator/value condition builder. Emits `CustomStageFilter[]`
|
|
141
|
+
* through `onChange`. Same ops (eq/neq/contains/in) and testids the smart-lane
|
|
142
|
+
* editor has always used, so it drops in for both the custom-stage smart lane
|
|
143
|
+
* and the declared-stage config dialog.
|
|
144
|
+
*/
|
|
145
|
+
export declare function StageConditionBuilder({ filters, onChange, fieldChoices, label, }: StageConditionBuilderProps): React.JSX.Element;
|
|
114
146
|
export interface CustomStageDialogProps {
|
|
115
147
|
open: boolean;
|
|
116
148
|
onOpenChange: (open: boolean) => void;
|
|
@@ -144,6 +176,69 @@ export interface CustomStageDeleteDialogProps {
|
|
|
144
176
|
* cards to; confirming again retries the delete with `reassign_to`.
|
|
145
177
|
*/
|
|
146
178
|
export declare function CustomStageDeleteDialog({ open, onOpenChange, stage, reassignTargets, onConfirm, }: CustomStageDeleteDialogProps): React.JSX.Element | null;
|
|
179
|
+
export type StageConfigKind = 'declared' | 'custom';
|
|
180
|
+
/** What the gear opens the config dialog against — a declared or custom lane. */
|
|
181
|
+
export interface StageConfigTarget {
|
|
182
|
+
kind: StageConfigKind;
|
|
183
|
+
/** Stable lane key. */
|
|
184
|
+
stageKey: string;
|
|
185
|
+
/** Custom-stage id — required (and only used) when `kind === 'custom'`. */
|
|
186
|
+
id?: CustomStage['id'];
|
|
187
|
+
label: string;
|
|
188
|
+
color: string;
|
|
189
|
+
filters: CustomStageFilter[];
|
|
190
|
+
/**
|
|
191
|
+
* Declared kind: an override is currently applied → shows a "Personalizada"
|
|
192
|
+
* badge + "Restablecer al original". Ignored for custom stages (they reset
|
|
193
|
+
* via their own delete flow).
|
|
194
|
+
*/
|
|
195
|
+
overridden?: boolean;
|
|
196
|
+
/** Terminal stage (e.g. "Done") — surfaces an "Etapa final" chip + tooltip. */
|
|
197
|
+
isFinal?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* The manifest ORIGINAL (pre-override) values, when the host serves them
|
|
200
|
+
* (`metadata.stages[].original`). Drives the "Restablecer al original" confirm
|
|
201
|
+
* so the user sees exactly what reverts. Absent → a generic confirm.
|
|
202
|
+
*/
|
|
203
|
+
original?: {
|
|
204
|
+
label?: string;
|
|
205
|
+
color?: string;
|
|
206
|
+
filters?: CustomStageFilter[];
|
|
207
|
+
};
|
|
208
|
+
/** The backing CustomStage (custom kind) so "Eliminar" can trigger delete. */
|
|
209
|
+
customStage?: CustomStage;
|
|
210
|
+
}
|
|
211
|
+
/** A legible operator glyph for a condition chip: = / ≠ / contiene / en. */
|
|
212
|
+
export declare function stageFilterOpSymbol(op: string): string;
|
|
213
|
+
export interface StageConfigDialogProps {
|
|
214
|
+
open: boolean;
|
|
215
|
+
onOpenChange: (open: boolean) => void;
|
|
216
|
+
/** Columns for the condition builder. */
|
|
217
|
+
columns: ColumnDefinition[];
|
|
218
|
+
/** The lane being configured, or null. */
|
|
219
|
+
target: StageConfigTarget | null;
|
|
220
|
+
/** Declared: upsert the lane override (PUT /stage-overrides). */
|
|
221
|
+
onSaveOverride: (stageKey: string, patch: {
|
|
222
|
+
label: string;
|
|
223
|
+
color: string;
|
|
224
|
+
filters: CustomStageFilter[];
|
|
225
|
+
}) => Promise<void>;
|
|
226
|
+
/** Declared: reset the lane to its manifest default (DELETE /stage-overrides). */
|
|
227
|
+
onResetOverride: (stageKey: string) => Promise<void>;
|
|
228
|
+
/** Custom: update the stage via its own CRUD (PUT /custom-stages/:id). */
|
|
229
|
+
onUpdateCustom: (id: CustomStage['id'], patch: Partial<CustomStage>) => Promise<void>;
|
|
230
|
+
/** Custom: hand off to the existing delete (reassign) flow. */
|
|
231
|
+
onDeleteCustom: (stage: CustomStage) => void;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The gear (⚙) dialog. Renames, recolors and attaches extra CONDITIONS to a
|
|
235
|
+
* lane. One UI, two backends: a DECLARED lane persists through `/stage-overrides`
|
|
236
|
+
* (with a "Restablecer etapa" that drops the override); a CUSTOM real stage
|
|
237
|
+
* persists through its own `/custom-stages` CRUD (and deletes via that flow). The
|
|
238
|
+
* conditions narrow which cards the lane shows/counts but never stop it being a
|
|
239
|
+
* drop target — dropping a card only sets the stage value.
|
|
240
|
+
*/
|
|
241
|
+
export declare function StageConfigDialog({ open, onOpenChange, columns, target, onSaveOverride, onResetOverride, onUpdateCustom, onDeleteCustom, }: StageConfigDialogProps): React.JSX.Element | null;
|
|
147
242
|
export interface SmartLaneProps {
|
|
148
243
|
stage: CustomStage;
|
|
149
244
|
model: string;
|
|
@@ -159,6 +254,18 @@ export interface SmartLaneProps {
|
|
|
159
254
|
refreshTrigger?: any;
|
|
160
255
|
onEdit: (stage: CustomStage) => void;
|
|
161
256
|
onDelete: (stage: CustomStage) => void;
|
|
257
|
+
/**
|
|
258
|
+
* Optional drag-and-drop wiring (from the kanban's sortable wrapper) so a
|
|
259
|
+
* smart lane can be reordered by its header like a real stage. Absent → the
|
|
260
|
+
* lane is static.
|
|
261
|
+
*/
|
|
262
|
+
dnd?: {
|
|
263
|
+
setNodeRef: (el: HTMLElement | null) => void;
|
|
264
|
+
style?: React.CSSProperties;
|
|
265
|
+
isDragging?: boolean;
|
|
266
|
+
handleRef?: (el: HTMLElement | null) => void;
|
|
267
|
+
handleProps?: Record<string, any>;
|
|
268
|
+
};
|
|
162
269
|
}
|
|
163
270
|
/**
|
|
164
271
|
* A virtual lane defined by `filters`. It runs its OWN list query (the board's
|
|
@@ -166,6 +273,6 @@ export interface SmartLaneProps {
|
|
|
166
273
|
* main board page loaded. Cards render read-only — a smart lane is a saved view,
|
|
167
274
|
* not a drop target — and the header carries a funnel glyph + the custom menu.
|
|
168
275
|
*/
|
|
169
|
-
export declare function SmartLane({ stage, model, endpoint, defaultFilters, pageSize, isDark, renderCard, refreshTrigger, onEdit, onDelete, }: SmartLaneProps): React.JSX.Element;
|
|
276
|
+
export declare function SmartLane({ stage, model, endpoint, defaultFilters, pageSize, isDark, renderCard, refreshTrigger, onEdit, onDelete, dnd, }: SmartLaneProps): React.JSX.Element;
|
|
170
277
|
export type { ApiClient };
|
|
171
278
|
//# sourceMappingURL=custom-stages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-stages.d.ts","sourceRoot":"","sources":["../src/custom-stages.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAoD,MAAM,OAAO,CAAA;AA4BxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EACR,gBAAgB,EAChB,SAAS,EACT,aAAa,EAEhB,MAAM,SAAS,CAAA;AAMhB,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,CAAA;AAC/C,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,KAAK,GAAG,UAAU,GAAG,IAAI,CAAA;AAElE,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,mBAAmB,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,eAAe,CAAA;IACrB,uEAAuE;IACvE,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;AAGpD,eAAO,MAAM,mBAAmB,+EAStB,CAAA;AAEV,eAAO,MAAM,uBAAuB,EAAE,mBAAmB,EAKxD,CAAA;AAMD,+DAA+D;AAC/D,wBAAgB,sBAAsB,CAAC,KAAK,SAAK,GAAG,iBAAiB,CAEpE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,GAAG;IAClE,UAAU,EAAE,WAAW,EAAE,CAAA;IACzB,WAAW,EAAE,WAAW,EAAE,CAAA;CAC7B,CASA;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,SAAS,EAAE,EACrB,gBAAgB,EAAE,WAAW,EAAE,GAChC;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"custom-stages.d.ts","sourceRoot":"","sources":["../src/custom-stages.tsx"],"names":[],"mappings":"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"}
|
package/dist/custom-stages.js
CHANGED
|
@@ -20,7 +20,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
20
20
|
// All UI text goes through t() with a Spanish defaultValue.
|
|
21
21
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
22
22
|
import { useTranslation } from 'react-i18next';
|
|
23
|
-
import { Plus, Trash2, Pencil, MoreVertical, Filter, X } from 'lucide-react';
|
|
23
|
+
import { Plus, Trash2, Pencil, MoreVertical, Filter, Flag, GripVertical, Lock, RotateCcw, X } from 'lucide-react';
|
|
24
24
|
import { toast } from 'sonner';
|
|
25
25
|
import { Badge, Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, } from '@asteby/metacore-ui/primitives';
|
|
26
26
|
import { generateBadgeStyles, optionColor } from '@asteby/metacore-ui/lib';
|
|
@@ -85,6 +85,8 @@ export function mergeLaneStages(declared, customLaneStages) {
|
|
|
85
85
|
label: cs.label,
|
|
86
86
|
color: cs.color,
|
|
87
87
|
order: cs.position,
|
|
88
|
+
custom: true,
|
|
89
|
+
filters: cs.filters,
|
|
88
90
|
});
|
|
89
91
|
}
|
|
90
92
|
lanes.sort((a, b) => {
|
|
@@ -172,6 +174,42 @@ export function isCustomStageDraftValid(draft) {
|
|
|
172
174
|
}
|
|
173
175
|
return true;
|
|
174
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Whether a card passes a set of extra lane `filters` (the same conditions a
|
|
179
|
+
* smart lane uses, layered on top of a real stage). Ops:
|
|
180
|
+
* - eq → equal (string compare)
|
|
181
|
+
* - neq → not equal
|
|
182
|
+
* - contains → the card's value (array or string) includes the value
|
|
183
|
+
* - in → the card's value is one of the comma-separated candidates
|
|
184
|
+
* Empty/absent filters pass. Pure — a client-side belt-and-suspenders over the
|
|
185
|
+
* initial (unscoped) board page; the server scopes the per-lane top-up queries.
|
|
186
|
+
*/
|
|
187
|
+
export function cardMatchesStageFilters(card, filters) {
|
|
188
|
+
if (!filters || filters.length === 0)
|
|
189
|
+
return true;
|
|
190
|
+
return filters.every((f) => {
|
|
191
|
+
if (!f.field || String(f.value ?? '').trim() === '')
|
|
192
|
+
return true;
|
|
193
|
+
const target = String(f.value).trim();
|
|
194
|
+
const raw = card?.[f.field];
|
|
195
|
+
switch (f.op) {
|
|
196
|
+
case 'neq':
|
|
197
|
+
return String(raw ?? '') !== target;
|
|
198
|
+
case 'contains':
|
|
199
|
+
if (Array.isArray(raw))
|
|
200
|
+
return raw.map(String).includes(target);
|
|
201
|
+
return String(raw ?? '').includes(target);
|
|
202
|
+
case 'in':
|
|
203
|
+
return target
|
|
204
|
+
.split(',')
|
|
205
|
+
.map((s) => s.trim())
|
|
206
|
+
.includes(String(raw ?? ''));
|
|
207
|
+
case 'eq':
|
|
208
|
+
default:
|
|
209
|
+
return String(raw ?? '') === target;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
175
213
|
/** Slugify a label into a stable-ish lane key (used only when creating). */
|
|
176
214
|
export function slugifyStageKey(label) {
|
|
177
215
|
return (label
|
|
@@ -279,6 +317,36 @@ export function CustomStageLaneMenu({ stage, onEdit, onDelete, }) {
|
|
|
279
317
|
defaultValue: 'Opciones de la etapa',
|
|
280
318
|
}), "data-testid": `custom-stage-menu-${stage.key}`, children: _jsx(MoreVertical, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: () => onEdit(stage), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), t('dynamic.custom_stages.edit', { defaultValue: 'Editar' })] }), _jsxs(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => onDelete(stage), children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('dynamic.custom_stages.delete', { defaultValue: 'Eliminar' })] })] })] }));
|
|
281
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* The reusable field/operator/value condition builder. Emits `CustomStageFilter[]`
|
|
322
|
+
* through `onChange`. Same ops (eq/neq/contains/in) and testids the smart-lane
|
|
323
|
+
* editor has always used, so it drops in for both the custom-stage smart lane
|
|
324
|
+
* and the declared-stage config dialog.
|
|
325
|
+
*/
|
|
326
|
+
export function StageConditionBuilder({ filters, onChange, fieldChoices, label, }) {
|
|
327
|
+
const { t } = useTranslation();
|
|
328
|
+
const patchFilter = (i, patch) => onChange(filters.map((f, idx) => (idx === i ? { ...f, ...patch } : f)));
|
|
329
|
+
const addFilter = () => onChange([...filters, emptyCustomStageFilter(fieldChoices[0]?.key ?? '')]);
|
|
330
|
+
const removeFilter = (i) => onChange(filters.filter((_, idx) => idx !== i));
|
|
331
|
+
return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/30 p-3", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [_jsx(Filter, { className: "h-3.5 w-3.5" }), label ??
|
|
332
|
+
t('dynamic.custom_stages.conditions_label', {
|
|
333
|
+
defaultValue: 'Condiciones',
|
|
334
|
+
})] }), filters.map((f, i) => (_jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr_auto] items-center gap-1.5", "data-testid": `custom-stage-condition-${i}`, children: [_jsxs(Select, { value: f.field, onValueChange: (v) => patchFilter(i, { field: v }), children: [_jsx(SelectTrigger, { className: "h-8 text-xs", "data-testid": `custom-stage-condition-field-${i}`, children: _jsx(SelectValue, { placeholder: t('dynamic.custom_stages.field_placeholder', { defaultValue: 'Campo' }) }) }), _jsx(SelectContent, { children: fieldChoices.map((c) => (_jsx(SelectItem, { value: c.key, className: "text-xs", children: t(c.label, { defaultValue: c.label }) }, c.key))) })] }), _jsxs(Select, { value: f.op, onValueChange: (v) => patchFilter(i, { op: v }), children: [_jsx(SelectTrigger, { className: "h-8 w-[104px] text-xs", "data-testid": `custom-stage-condition-op-${i}`, children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CUSTOM_STAGE_FILTER_OPS.map((op) => (_jsx(SelectItem, { value: op, className: "text-xs", children: t(`dynamic.custom_stages.op.${op}`, {
|
|
335
|
+
defaultValue: op === 'eq'
|
|
336
|
+
? 'es igual'
|
|
337
|
+
: op === 'neq'
|
|
338
|
+
? 'distinto'
|
|
339
|
+
: op === 'contains'
|
|
340
|
+
? 'contiene'
|
|
341
|
+
: 'en lista',
|
|
342
|
+
}) }, op))) })] }), _jsx(Input, { value: f.value, onChange: (e) => patchFilter(i, { value: e.target.value }), placeholder: t('dynamic.custom_stages.value_placeholder', {
|
|
343
|
+
defaultValue: 'Valor',
|
|
344
|
+
}), className: "h-8 text-xs", "data-testid": `custom-stage-condition-value-${i}` }), _jsx("button", { type: "button", onClick: () => removeFilter(i), disabled: filters.length === 1, className: "flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-destructive disabled:opacity-40", "aria-label": t('dynamic.custom_stages.remove_condition', {
|
|
345
|
+
defaultValue: 'Quitar condición',
|
|
346
|
+
}), "data-testid": `custom-stage-condition-remove-${i}`, children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }, i))), _jsxs(Button, { variant: "ghost", size: "sm", className: "h-7 justify-start gap-1 text-xs", onClick: addFilter, "data-testid": "custom-stage-add-condition", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), t('dynamic.custom_stages.add_condition', {
|
|
347
|
+
defaultValue: 'Agregar condición',
|
|
348
|
+
})] })] }));
|
|
349
|
+
}
|
|
282
350
|
export function CustomStageDialog({ open, onOpenChange, model, columns, initial, nextPosition, onCreate, onUpdate, }) {
|
|
283
351
|
const { t } = useTranslation();
|
|
284
352
|
const isDark = typeof document !== 'undefined' &&
|
|
@@ -308,12 +376,6 @@ export function CustomStageDialog({ open, onOpenChange, model, columns, initial,
|
|
|
308
376
|
setFilters([emptyCustomStageFilter(fieldChoices[0]?.key ?? '')]);
|
|
309
377
|
}
|
|
310
378
|
}, [open, initial, fieldChoices]);
|
|
311
|
-
const patchFilter = (i, patch) => setFilters((prev) => prev.map((f, idx) => (idx === i ? { ...f, ...patch } : f)));
|
|
312
|
-
const addFilter = () => setFilters((prev) => [
|
|
313
|
-
...prev,
|
|
314
|
-
emptyCustomStageFilter(fieldChoices[0]?.key ?? ''),
|
|
315
|
-
]);
|
|
316
|
-
const removeFilter = (i) => setFilters((prev) => prev.filter((_, idx) => idx !== i));
|
|
317
379
|
const valid = isCustomStageDraftValid({ label, type, filters });
|
|
318
380
|
const submit = async () => {
|
|
319
381
|
if (!valid || saving)
|
|
@@ -389,23 +451,7 @@ export function CustomStageDialog({ open, onOpenChange, model, columns, initial,
|
|
|
389
451
|
defaultValue: 'Etapa normal',
|
|
390
452
|
}) }), _jsx(SelectItem, { value: "smart", children: t('dynamic.custom_stages.type_smart', {
|
|
391
453
|
defaultValue: 'Etapa inteligente',
|
|
392
|
-
}) })] })] })] }), type === 'smart' && (
|
|
393
|
-
defaultValue: 'Condiciones',
|
|
394
|
-
})] }), filters.map((f, i) => (_jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr_auto] items-center gap-1.5", "data-testid": `custom-stage-condition-${i}`, children: [_jsxs(Select, { value: f.field, onValueChange: (v) => patchFilter(i, { field: v }), children: [_jsx(SelectTrigger, { className: "h-8 text-xs", "data-testid": `custom-stage-condition-field-${i}`, children: _jsx(SelectValue, { placeholder: t('dynamic.custom_stages.field_placeholder', { defaultValue: 'Campo' }) }) }), _jsx(SelectContent, { children: fieldChoices.map((c) => (_jsx(SelectItem, { value: c.key, className: "text-xs", children: t(c.label, { defaultValue: c.label }) }, c.key))) })] }), _jsxs(Select, { value: f.op, onValueChange: (v) => patchFilter(i, { op: v }), children: [_jsx(SelectTrigger, { className: "h-8 w-[104px] text-xs", "data-testid": `custom-stage-condition-op-${i}`, children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CUSTOM_STAGE_FILTER_OPS.map((op) => (_jsx(SelectItem, { value: op, className: "text-xs", children: t(`dynamic.custom_stages.op.${op}`, {
|
|
395
|
-
defaultValue: op === 'eq'
|
|
396
|
-
? 'es igual'
|
|
397
|
-
: op === 'neq'
|
|
398
|
-
? 'distinto'
|
|
399
|
-
: op === 'contains'
|
|
400
|
-
? 'contiene'
|
|
401
|
-
: 'en lista',
|
|
402
|
-
}) }, op))) })] }), _jsx(Input, { value: f.value, onChange: (e) => patchFilter(i, { value: e.target.value }), placeholder: t('dynamic.custom_stages.value_placeholder', {
|
|
403
|
-
defaultValue: 'Valor',
|
|
404
|
-
}), className: "h-8 text-xs", "data-testid": `custom-stage-condition-value-${i}` }), _jsx("button", { type: "button", onClick: () => removeFilter(i), disabled: filters.length === 1, className: "flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-destructive disabled:opacity-40", "aria-label": t('dynamic.custom_stages.remove_condition', {
|
|
405
|
-
defaultValue: 'Quitar condición',
|
|
406
|
-
}), "data-testid": `custom-stage-condition-remove-${i}`, children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }, i))), _jsxs(Button, { variant: "ghost", size: "sm", className: "h-7 justify-start gap-1 text-xs", onClick: addFilter, "data-testid": "custom-stage-add-condition", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), t('dynamic.custom_stages.add_condition', {
|
|
407
|
-
defaultValue: 'Agregar condición',
|
|
408
|
-
})] })] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving, children: t('dynamic.custom_stages.cancel', { defaultValue: 'Cancelar' }) }), _jsx(Button, { onClick: () => void submit(), disabled: !valid || saving, "data-testid": "custom-stage-save", children: initial
|
|
454
|
+
}) })] })] })] }), type === 'smart' && (_jsx(StageConditionBuilder, { filters: filters, onChange: setFilters, fieldChoices: fieldChoices }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving, children: t('dynamic.custom_stages.cancel', { defaultValue: 'Cancelar' }) }), _jsx(Button, { onClick: () => void submit(), disabled: !valid || saving, "data-testid": "custom-stage-save", children: initial
|
|
409
455
|
? t('dynamic.custom_stages.save', { defaultValue: 'Guardar' })
|
|
410
456
|
: t('dynamic.custom_stages.create', { defaultValue: 'Crear etapa' }) })] })] }) }));
|
|
411
457
|
}
|
|
@@ -478,13 +524,202 @@ export function CustomStageDeleteDialog({ open, onOpenChange, stage, reassignTar
|
|
|
478
524
|
defaultValue: 'Eliminar',
|
|
479
525
|
}) })] })] }) }));
|
|
480
526
|
}
|
|
527
|
+
/** A legible operator glyph for a condition chip: = / ≠ / contiene / en. */
|
|
528
|
+
export function stageFilterOpSymbol(op) {
|
|
529
|
+
switch (op) {
|
|
530
|
+
case 'neq':
|
|
531
|
+
return '≠';
|
|
532
|
+
case 'contains':
|
|
533
|
+
return 'contiene';
|
|
534
|
+
case 'in':
|
|
535
|
+
return 'en';
|
|
536
|
+
case 'eq':
|
|
537
|
+
default:
|
|
538
|
+
return '=';
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* The gear (⚙) dialog. Renames, recolors and attaches extra CONDITIONS to a
|
|
543
|
+
* lane. One UI, two backends: a DECLARED lane persists through `/stage-overrides`
|
|
544
|
+
* (with a "Restablecer etapa" that drops the override); a CUSTOM real stage
|
|
545
|
+
* persists through its own `/custom-stages` CRUD (and deletes via that flow). The
|
|
546
|
+
* conditions narrow which cards the lane shows/counts but never stop it being a
|
|
547
|
+
* drop target — dropping a card only sets the stage value.
|
|
548
|
+
*/
|
|
549
|
+
export function StageConfigDialog({ open, onOpenChange, columns, target, onSaveOverride, onResetOverride, onUpdateCustom, onDeleteCustom, }) {
|
|
550
|
+
const { t } = useTranslation();
|
|
551
|
+
const isDark = typeof document !== 'undefined' &&
|
|
552
|
+
document.documentElement.classList.contains('dark');
|
|
553
|
+
const fieldChoices = useMemo(() => customStageFilterFields(columns), [columns]);
|
|
554
|
+
const [label, setLabel] = useState('');
|
|
555
|
+
const [color, setColor] = useState(CUSTOM_STAGE_COLORS[0]);
|
|
556
|
+
const [filters, setFilters] = useState([]);
|
|
557
|
+
const [saving, setSaving] = useState(false);
|
|
558
|
+
const [resetting, setResetting] = useState(false);
|
|
559
|
+
// Two-step reset: the first click reveals a confirm panel listing exactly
|
|
560
|
+
// what reverts (the manifest original), the second actually drops the override.
|
|
561
|
+
const [confirmingReset, setConfirmingReset] = useState(false);
|
|
562
|
+
// Re-seed the WHOLE form from the lane's current (already-override-applied)
|
|
563
|
+
// metadata each time the dialog opens — the user always sees the live state,
|
|
564
|
+
// never a blank form. Label, color and every extra condition pre-fill editable.
|
|
565
|
+
useEffect(() => {
|
|
566
|
+
if (!open || !target)
|
|
567
|
+
return;
|
|
568
|
+
setLabel(target.label);
|
|
569
|
+
setColor(target.color || CUSTOM_STAGE_COLORS[0]);
|
|
570
|
+
setFilters(target.filters?.map((f) => ({ ...f })) ?? []);
|
|
571
|
+
setConfirmingReset(false);
|
|
572
|
+
}, [open, target]);
|
|
573
|
+
if (!target)
|
|
574
|
+
return null;
|
|
575
|
+
const valid = label.trim().length > 0;
|
|
576
|
+
// Only complete conditions are persisted; empty rows are dropped.
|
|
577
|
+
const cleanFilters = () => filters.filter((f) => f.field && String(f.value).trim() !== '');
|
|
578
|
+
// The conditions that make up the lane's EFFECTIVE query, for the chip row:
|
|
579
|
+
// the immovable stage scope first, then each extra (removable) condition.
|
|
580
|
+
const chipFilters = filters.filter((f) => f.field && String(f.value).trim() !== '');
|
|
581
|
+
const removeFilterAt = (i) => setFilters((prev) => prev.filter((_, idx) => idx !== i));
|
|
582
|
+
const submit = async () => {
|
|
583
|
+
if (!valid || saving)
|
|
584
|
+
return;
|
|
585
|
+
setSaving(true);
|
|
586
|
+
try {
|
|
587
|
+
if (target.kind === 'declared') {
|
|
588
|
+
await onSaveOverride(target.stageKey, {
|
|
589
|
+
label: label.trim(),
|
|
590
|
+
color,
|
|
591
|
+
filters: cleanFilters(),
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
else if (target.id != null) {
|
|
595
|
+
await onUpdateCustom(target.id, {
|
|
596
|
+
label: label.trim(),
|
|
597
|
+
color,
|
|
598
|
+
filters: cleanFilters(),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
toast.success(t('dynamic.stage_config.saved', {
|
|
602
|
+
defaultValue: 'Etapa configurada',
|
|
603
|
+
}));
|
|
604
|
+
onOpenChange(false);
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
// toast already surfaced by the caller's hook; keep the draft open.
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
setSaving(false);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const reset = async () => {
|
|
614
|
+
if (resetting)
|
|
615
|
+
return;
|
|
616
|
+
setResetting(true);
|
|
617
|
+
try {
|
|
618
|
+
await onResetOverride(target.stageKey);
|
|
619
|
+
toast.success(t('dynamic.stage_config.reset_done', {
|
|
620
|
+
defaultValue: 'Etapa restablecida',
|
|
621
|
+
}));
|
|
622
|
+
onOpenChange(false);
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
toast.error(t('dynamic.stage_config.reset_error', {
|
|
626
|
+
defaultValue: 'No se pudo restablecer la etapa',
|
|
627
|
+
}));
|
|
628
|
+
}
|
|
629
|
+
finally {
|
|
630
|
+
setResetting(false);
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const headerDot = generateBadgeStyles(color || optionColor(target.stageKey), { isDark });
|
|
634
|
+
// A one-line, human summary of the manifest original (for the reset confirm).
|
|
635
|
+
const original = target.original;
|
|
636
|
+
const originalConditions = original?.filters && original.filters.length > 0
|
|
637
|
+
? original.filters
|
|
638
|
+
.map((f) => `${f.field} ${stageFilterOpSymbol(f.op)} ${f.value}`)
|
|
639
|
+
.join(', ')
|
|
640
|
+
: t('dynamic.stage_config.original_no_conditions', {
|
|
641
|
+
defaultValue: 'sin condiciones',
|
|
642
|
+
});
|
|
643
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx("span", { className: "inline-block size-3 shrink-0 rounded-full", style: {
|
|
644
|
+
backgroundColor: headerDot.backgroundColor ||
|
|
645
|
+
headerDot.background,
|
|
646
|
+
}, "aria-hidden": true }), t('dynamic.stage_config.title', {
|
|
647
|
+
defaultValue: 'Configurar etapa',
|
|
648
|
+
}), _jsx("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[10px] font-normal text-muted-foreground", children: target.stageKey }), target.kind === 'declared' && target.overridden && (_jsx(Badge, { variant: "secondary", className: "ml-auto text-[10px] font-medium", "data-testid": "stage-config-personalizada", children: t('dynamic.stage_config.customized', {
|
|
649
|
+
defaultValue: 'Personalizada',
|
|
650
|
+
}) }))] }), _jsx(DialogDescription, { children: t('dynamic.stage_config.description', {
|
|
651
|
+
defaultValue: 'Renombra la columna, cambia su color y agrega condiciones para acotar qué tarjetas muestra y cuenta.',
|
|
652
|
+
}) })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: t('dynamic.stage_config.current_conditions_label', {
|
|
653
|
+
defaultValue: 'Condiciones actuales',
|
|
654
|
+
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", "data-testid": "stage-config-current-conditions", children: [_jsxs(Badge, { variant: "outline", className: "cursor-default gap-1 border-dashed text-[11px] font-normal text-muted-foreground opacity-80", title: t('dynamic.stage_config.base_chip_hint', {
|
|
655
|
+
defaultValue: 'Alcance base de la etapa: no se puede quitar.',
|
|
656
|
+
}), "data-testid": "stage-config-base-chip", children: [_jsx(Lock, { className: "h-3 w-3" }), t('dynamic.stage_config.base_chip', {
|
|
657
|
+
defaultValue: 'Etapa = {{label}}',
|
|
658
|
+
label: label || target.label,
|
|
659
|
+
})] }), target.isFinal && (_jsxs(Badge, { variant: "outline", className: "cursor-default gap-1 text-[11px] font-normal text-muted-foreground", title: t('dynamic.stage_config.final_chip_hint', {
|
|
660
|
+
defaultValue: 'Etapa terminal: al mover una tarjeta aquí se marca como cerrada (p. ej. en GitHub cierra el issue).',
|
|
661
|
+
}), "data-testid": "stage-config-final-chip", children: [_jsx(Flag, { className: "h-3 w-3" }), t('dynamic.stage_config.final_chip', {
|
|
662
|
+
defaultValue: 'Etapa final',
|
|
663
|
+
})] })), chipFilters.map((f) => {
|
|
664
|
+
// Chip index maps back to its position in `filters`.
|
|
665
|
+
const i = filters.indexOf(f);
|
|
666
|
+
return (_jsxs(Badge, { variant: "secondary", className: "gap-1 pr-1 text-[11px] font-normal", "data-testid": `stage-config-filter-chip-${i}`, children: [_jsx("span", { className: "font-mono", children: f.field }), _jsx("span", { className: "opacity-70", children: stageFilterOpSymbol(f.op) }), _jsx("span", { children: f.value }), _jsx("button", { type: "button", onClick: () => removeFilterAt(i), className: "ml-0.5 rounded p-0.5 hover:bg-background/60", "aria-label": t('dynamic.stage_config.remove_chip', { defaultValue: 'Quitar condición' }), "data-testid": `stage-config-filter-chip-remove-${i}`, children: _jsx(X, { className: "h-2.5 w-2.5" }) })] }, i));
|
|
667
|
+
})] })] }), _jsx("div", { className: "border-t" }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs", children: t('dynamic.stage_config.name_label', {
|
|
668
|
+
defaultValue: 'Nombre',
|
|
669
|
+
}) }), _jsx(Input, { value: label, onChange: (e) => setLabel(e.target.value), placeholder: t('dynamic.stage_config.name_placeholder', {
|
|
670
|
+
defaultValue: 'Nombre de la etapa',
|
|
671
|
+
}), "data-testid": "stage-config-name", autoFocus: true })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs", children: t('dynamic.stage_config.color_label', {
|
|
672
|
+
defaultValue: 'Color',
|
|
673
|
+
}) }), _jsx("div", { className: "flex flex-wrap gap-1.5", "data-testid": "stage-config-colors", children: CUSTOM_STAGE_COLORS.map((c) => {
|
|
674
|
+
const style = generateBadgeStyles(c, { isDark });
|
|
675
|
+
const selected = c === color;
|
|
676
|
+
return (_jsx("button", { type: "button", onClick: () => setColor(c), className: `size-6 rounded-full border-2 transition-transform hover:scale-110 ${selected
|
|
677
|
+
? 'border-foreground'
|
|
678
|
+
: 'border-transparent'}`, style: {
|
|
679
|
+
backgroundColor: style.backgroundColor ||
|
|
680
|
+
style.background,
|
|
681
|
+
}, "aria-label": c, "aria-pressed": selected, "data-testid": `stage-config-color-${c}` }, c));
|
|
682
|
+
}) })] }), _jsx(StageConditionBuilder, { filters: filters, onChange: setFilters, fieldChoices: fieldChoices, label: t('dynamic.stage_config.conditions_label', {
|
|
683
|
+
defaultValue: 'Condiciones (opcional)',
|
|
684
|
+
}) }), filters.length === 0 && (_jsxs(Button, { variant: "ghost", size: "sm", className: "-mt-2 h-7 justify-start gap-1 text-xs", onClick: () => setFilters([
|
|
685
|
+
emptyCustomStageFilter(fieldChoices[0]?.key ?? ''),
|
|
686
|
+
]), "data-testid": "stage-config-add-first-condition", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), t('dynamic.stage_config.add_condition', {
|
|
687
|
+
defaultValue: 'Agregar condición',
|
|
688
|
+
})] })), confirmingReset && (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-dashed bg-muted/30 p-3 text-xs", "data-testid": "stage-config-reset-confirm-panel", children: [_jsx("p", { className: "font-medium", children: t('dynamic.stage_config.reset_confirm_title', {
|
|
689
|
+
defaultValue: 'Se restablecerá la etapa a su versión original:',
|
|
690
|
+
}) }), _jsxs("ul", { className: "list-inside list-disc text-muted-foreground", children: [_jsx("li", { children: t('dynamic.stage_config.reset_confirm_label', {
|
|
691
|
+
defaultValue: 'Nombre: {{label}}',
|
|
692
|
+
label: original?.label ?? target.stageKey,
|
|
693
|
+
}) }), _jsx("li", { children: t('dynamic.stage_config.reset_confirm_color', {
|
|
694
|
+
defaultValue: 'Color: {{color}}',
|
|
695
|
+
color: original?.color ?? '—',
|
|
696
|
+
}) }), _jsx("li", { children: t('dynamic.stage_config.reset_confirm_conditions', {
|
|
697
|
+
defaultValue: 'Condiciones: {{list}}',
|
|
698
|
+
list: originalConditions,
|
|
699
|
+
}) })] }), _jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [_jsx(Button, { variant: "outline", size: "sm", className: "h-7 text-xs", onClick: () => setConfirmingReset(false), disabled: resetting, children: t('dynamic.stage_config.cancel', {
|
|
700
|
+
defaultValue: 'Cancelar',
|
|
701
|
+
}) }), _jsx(Button, { variant: "destructive", size: "sm", className: "h-7 text-xs", onClick: () => void reset(), disabled: resetting, "data-testid": "stage-config-reset-confirm", children: t('dynamic.stage_config.reset_confirm', {
|
|
702
|
+
defaultValue: 'Restablecer al original',
|
|
703
|
+
}) })] })] }))] }), _jsxs(DialogFooter, { className: "sm:justify-between", children: [_jsxs("div", { children: [target.kind === 'declared' && target.overridden && (_jsxs(Button, { variant: "ghost", className: "gap-1.5 text-muted-foreground", onClick: () => setConfirmingReset(true), disabled: saving || resetting || confirmingReset, "data-testid": "stage-config-reset", children: [_jsx(RotateCcw, { className: "h-4 w-4" }), t('dynamic.stage_config.reset', {
|
|
704
|
+
defaultValue: 'Restablecer al original',
|
|
705
|
+
})] })), target.kind === 'custom' && target.customStage && (_jsxs(Button, { variant: "ghost", className: "gap-1.5 text-destructive hover:text-destructive", onClick: () => {
|
|
706
|
+
onOpenChange(false);
|
|
707
|
+
onDeleteCustom(target.customStage);
|
|
708
|
+
}, disabled: saving, "data-testid": "stage-config-delete", children: [_jsx(Trash2, { className: "h-4 w-4" }), t('dynamic.stage_config.delete', {
|
|
709
|
+
defaultValue: 'Eliminar etapa',
|
|
710
|
+
})] }))] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || resetting, children: t('dynamic.stage_config.cancel', {
|
|
711
|
+
defaultValue: 'Cancelar',
|
|
712
|
+
}) }), _jsx(Button, { onClick: () => void submit(), disabled: !valid || saving || resetting, "data-testid": "stage-config-save", children: t('dynamic.stage_config.save', {
|
|
713
|
+
defaultValue: 'Guardar',
|
|
714
|
+
}) })] })] })] }) }));
|
|
715
|
+
}
|
|
481
716
|
/**
|
|
482
717
|
* A virtual lane defined by `filters`. It runs its OWN list query (the board's
|
|
483
718
|
* shared records don't include it), so it stays correct regardless of what the
|
|
484
719
|
* main board page loaded. Cards render read-only — a smart lane is a saved view,
|
|
485
720
|
* not a drop target — and the header carries a funnel glyph + the custom menu.
|
|
486
721
|
*/
|
|
487
|
-
export function SmartLane({ stage, model, endpoint, defaultFilters, pageSize = 50, isDark, renderCard, refreshTrigger, onEdit, onDelete, }) {
|
|
722
|
+
export function SmartLane({ stage, model, endpoint, defaultFilters, pageSize = 50, isDark, renderCard, refreshTrigger, onEdit, onDelete, dnd, }) {
|
|
488
723
|
const { t } = useTranslation();
|
|
489
724
|
const api = useApi();
|
|
490
725
|
const [records, setRecords] = useState([]);
|
|
@@ -527,7 +762,7 @@ export function SmartLane({ stage, model, endpoint, defaultFilters, pageSize = 5
|
|
|
527
762
|
isDark,
|
|
528
763
|
});
|
|
529
764
|
const count = total ?? records.length;
|
|
530
|
-
return (_jsxs("div", { className: "flex min-w-[280px] max-w-[420px] flex-1 shrink-0 flex-col rounded-xl border border-dashed bg-muted/20", "data-smart-stage": stage.key, "data-testid": `smart-lane-${stage.key}`, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5", children: [_jsxs("div", { className:
|
|
765
|
+
return (_jsxs("div", { ref: dnd?.setNodeRef, className: "group/lane flex min-w-[280px] max-w-[420px] flex-1 shrink-0 flex-col rounded-xl border border-dashed bg-muted/20", style: { opacity: dnd?.isDragging ? 0.6 : 1, ...dnd?.style }, "data-smart-stage": stage.key, "data-testid": `smart-lane-${stage.key}`, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5", children: [_jsxs("div", { ref: dnd ? dnd.handleRef : undefined, ...(dnd ? dnd.handleProps : {}), className: `flex min-w-0 items-center gap-1.5 ${dnd ? 'cursor-grab active:cursor-grabbing' : ''}`, children: [dnd && (_jsx(GripVertical, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/40 opacity-0 transition-opacity group-hover/lane:opacity-70", "aria-hidden": true })), _jsx(Badge, { variant: "outline", className: "border-0 text-xs font-semibold", style: headerStyle, children: t(stage.label, { defaultValue: stage.label }) }), _jsx("span", { className: "text-muted-foreground", title: t('dynamic.custom_stages.smart_hint', {
|
|
531
766
|
defaultValue: 'Etapa inteligente (por condiciones)',
|
|
532
767
|
}), children: _jsx(Filter, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-xs font-medium tabular-nums text-muted-foreground", children: count })] }), _jsx(CustomStageLaneMenu, { stage: stage, onEdit: onEdit, onDelete: onDelete })] }), _jsx("div", { className: "flex min-h-[55vh] max-h-[70vh] min-w-0 flex-col gap-2 overflow-y-auto px-2 pb-3", children: loading && records.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : records.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('dynamic.custom_stages.smart_empty', {
|
|
533
768
|
defaultValue: 'Sin tarjetas que cumplan las condiciones',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
|