@asteby/metacore-runtime-react 20.0.0 → 20.1.1
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 +43 -0
- package/dist/dynamic-kanban.d.ts +2 -2
- package/dist/dynamic-kanban.d.ts.map +1 -1
- package/dist/dynamic-kanban.js +11 -7
- package/package.json +3 -3
- package/src/dynamic-kanban.tsx +14 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 20.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- eb6c65f: Kanban responsive board + cards truncate; sidebar nav lights the default view on a view-less landing
|
|
8
|
+
|
|
9
|
+
Two frontend UX fixes reported against the ops board.
|
|
10
|
+
|
|
11
|
+
**Kanban now adapts instead of overflowing (`@asteby/metacore-runtime-react`).**
|
|
12
|
+
`DynamicKanban` lanes were fixed-width (`w-72 shrink-0`), so with 4+ stages the
|
|
13
|
+
last lane was clipped off-viewport and long card text was cut by the card edge
|
|
14
|
+
with no ellipsis. Lanes are now responsive — `flex-1 min-w-[220px] max-w-[320px]`
|
|
15
|
+
— so they shrink to fit the available width and only scroll horizontally when
|
|
16
|
+
they genuinely can't fit. Card titles now `line-clamp-2 break-words` and the
|
|
17
|
+
secondary field rows carry `min-w-0` so long values ellipsize _inside_ the card
|
|
18
|
+
rather than being clipped by the border. The optimistic drag-to-move (PUT
|
|
19
|
+
`<base>/<id>`) is untouched.
|
|
20
|
+
|
|
21
|
+
**Sidebar nav active-state on the default/view-less landing (`@asteby/metacore-ui`,
|
|
22
|
+
`@asteby/metacore-starter-core`).** Landing on a model's bare list surface
|
|
23
|
+
(e.g. `/m/github_issues?per_page=15` — a transient param, no `view`) lit
|
|
24
|
+
_neither_ the "Tablero" (`?view=kanban`) nor the "Issues" (`?view=list`) nav
|
|
25
|
+
item, because the empty view bucket matched neither sibling's explicit view.
|
|
26
|
+
`checkIsActive` now treats the empty/`view=list`/`view=table` buckets as
|
|
27
|
+
"default-equivalent": a view-less current URL lights the list/default item while
|
|
28
|
+
the board (`?view=kanban`, never a default bucket) stays mutually exclusive. The
|
|
29
|
+
prior Board-vs-Issues exclusivity, `f_` filter and transient (page/sort/search)
|
|
30
|
+
behaviour are all preserved (18 matcher tests, +4 new). Ported to the
|
|
31
|
+
`starter-core` scaffold's embedded copy for parity.
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [eb6c65f]
|
|
34
|
+
- @asteby/metacore-ui@2.6.1
|
|
35
|
+
|
|
36
|
+
## 20.1.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- 8de09a9: DynamicKanban: traduce el label de cada etapa via i18n (`t(stage.label)` con fallback al valor crudo) — antes mostraba la key cruda (ej. `integration_github.stage.backlog`) en vez de "Backlog". Y da min-height a las lanes para que el scroll horizontal del board quede abajo en vez de flotar cuando las columnas están vacías.
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- 8de09a9: fix(kanban): el drag-to-move ya no duplica `/me` en el PUT (causaba 404 "No se pudo mover la tarjeta"); el board respeta el ancho del padre (`min-w-0`) y deja de desbordarse horizontalmente.
|
|
45
|
+
|
|
3
46
|
## 20.0.0
|
|
4
47
|
|
|
5
48
|
### Minor Changes
|
package/dist/dynamic-kanban.d.ts
CHANGED
|
@@ -44,8 +44,8 @@ export interface DynamicKanbanProps {
|
|
|
44
44
|
/** Model key as registered on the backend (e.g. "issue"). */
|
|
45
45
|
model: string;
|
|
46
46
|
/**
|
|
47
|
-
* Data endpoint base
|
|
48
|
-
* PUTs to `<base
|
|
47
|
+
* Data endpoint base — the org-scoped LIST endpoint (e.g.
|
|
48
|
+
* `/data/<model>/me`). The optimistic update PUTs to `<base>/<id>`.
|
|
49
49
|
*/
|
|
50
50
|
endpoint?: string;
|
|
51
51
|
/** Bump to force a metadata + records refetch (same contract as DynamicTable). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqC9B,OAAO,KAAK,EACR,aAAa,EACb,gBAAgB,EAGhB,SAAS,EACT,eAAe,EAClB,MAAM,SAAS,CAAA;AAMhB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,CAejE;AAQD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C,wBAAgB,YAAY,CACxB,OAAO,EAAE,GAAG,EAAE,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,GACpB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAepB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,WAAW,EAAE,eAAe,EAAE,GAAG,SAAS,EAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACX,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAYpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,aAAa,EACvB,SAAS,SAAI,GACd;IAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAkBhE;AA8BD,MAAM,WAAW,kBAAkB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,QAAc,EACd,QAAQ,EACR,QAAQ,GACX,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqC9B,OAAO,KAAK,EACR,aAAa,EACb,gBAAgB,EAGhB,SAAS,EACT,eAAe,EAClB,MAAM,SAAS,CAAA;AAMhB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,CAejE;AAQD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C,wBAAgB,YAAY,CACxB,OAAO,EAAE,GAAG,EAAE,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,GACpB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAepB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,WAAW,EAAE,eAAe,EAAE,GAAG,SAAS,EAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACX,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAYpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,aAAa,EACvB,SAAS,SAAI,GACd;IAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAkBhE;AA8BD,MAAM,WAAW,kBAAkB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,QAAc,EACd,QAAQ,EACR,QAAQ,GACX,EAAE,kBAAkB,qBAmSpB"}
|
package/dist/dynamic-kanban.js
CHANGED
|
@@ -261,7 +261,11 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, pa
|
|
|
261
261
|
setRecords((rs) => rs.map((r) => String(r.id) === cardId ? { ...r, [groupByKey]: destStage } : r));
|
|
262
262
|
try {
|
|
263
263
|
const base = endpoint || `/data/${model}`;
|
|
264
|
-
|
|
264
|
+
// `base` is the org-scoped list endpoint (e.g. `/data/<model>/me`),
|
|
265
|
+
// so the per-record update is just `<base>/<id>` — same convention
|
|
266
|
+
// as DynamicTable/DynamicRelation. Appending an extra `/me` here
|
|
267
|
+
// produced `/data/<model>/me/me/<id>` → 404 on drag-to-move.
|
|
268
|
+
const res = (await api.put(`${base}/${cardId}`, {
|
|
265
269
|
[groupByKey]: destStage,
|
|
266
270
|
}));
|
|
267
271
|
if (res?.data && res.data.success === false) {
|
|
@@ -280,7 +284,7 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, pa
|
|
|
280
284
|
}
|
|
281
285
|
}, [api, endpoint, groupByKey, model, records, stageOfCard, t, transitions]);
|
|
282
286
|
if (loading) {
|
|
283
|
-
return (_jsx("div", { className: "flex gap-4 overflow-x-auto p-1", children: [0, 1, 2, 3].map((i) => (_jsxs("div", { className: "w-
|
|
287
|
+
return (_jsx("div", { className: "flex gap-4 overflow-x-auto p-1", children: [0, 1, 2, 3].map((i) => (_jsxs("div", { className: "min-w-[220px] max-w-[320px] flex-1 space-y-3", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" })] }, i))) }));
|
|
284
288
|
}
|
|
285
289
|
if (!metadata || !groupByKey || stages.length === 0) {
|
|
286
290
|
return (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground", children: t('kanban.noStages', {
|
|
@@ -298,7 +302,7 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, pa
|
|
|
298
302
|
order: Number.MAX_SAFE_INTEGER,
|
|
299
303
|
});
|
|
300
304
|
}
|
|
301
|
-
return (_jsxs(DndContext, { sensors: sensors, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsx("div", { className: "flex gap-4 overflow-x-auto p-1", "data-testid": "kanban-board", children: lanes.map((stage) => {
|
|
305
|
+
return (_jsxs(DndContext, { sensors: sensors, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsx("div", { className: "flex min-w-0 gap-4 overflow-x-auto p-1", "data-testid": "kanban-board", children: lanes.map((stage) => {
|
|
302
306
|
const cards = grouped.get(stage.key) ?? [];
|
|
303
307
|
const droppableAllowed = !activeId ||
|
|
304
308
|
stage.key === activeStage ||
|
|
@@ -318,7 +322,7 @@ function KanbanLane({ stage, count, isDark, dimmed, disabled, children }) {
|
|
|
318
322
|
const headerStyle = generateBadgeStyles(stage.color || optionColor(stage.key), {
|
|
319
323
|
isDark,
|
|
320
324
|
});
|
|
321
|
-
return (_jsxs("div", { ref: setNodeRef, className: "flex w-
|
|
325
|
+
return (_jsxs("div", { ref: setNodeRef, className: "flex min-w-[220px] max-w-[320px] flex-1 flex-col rounded-lg border bg-muted/30 transition-opacity", style: {
|
|
322
326
|
opacity: dimmed ? 0.45 : 1,
|
|
323
327
|
outline: isOver && !disabled ? '2px solid var(--ring, #3b82f6)' : 'none',
|
|
324
328
|
outlineOffset: 2,
|
|
@@ -329,14 +333,14 @@ function KanbanCard({ card, titleCol, fieldCols, actions, locale, timeZone, curr
|
|
|
329
333
|
id: String(card.id),
|
|
330
334
|
});
|
|
331
335
|
const visibleActions = actions.filter((a) => isActionAllowedForRowState(a, card));
|
|
332
|
-
return (_jsx(Card, { ref: setNodeRef, ...attributes, ...listeners, className: "cursor-grab active:cursor-grabbing border-border/70 shadow-sm", style: { opacity: isDragging ? 0.4 : 1 }, onClick: () => onClick?.(card), "data-card-id": String(card.id), children: _jsxs(CardContent, { className: "space-y-1.5 p-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("div", { className: "min-w-0 flex-1 text-sm font-medium leading-snug", children: titleCol ? (_jsx(ActivityValueRenderer, { value: card[titleCol.key], col: titleCol, locale: locale, timeZone: timeZone, currency: currency })) : (_jsx("span", { className: "truncate", children: String(card.id) })) }), visibleActions.length > 0 && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0 -mr-1 -mt-1",
|
|
336
|
+
return (_jsx(Card, { ref: setNodeRef, ...attributes, ...listeners, className: "cursor-grab active:cursor-grabbing border-border/70 shadow-sm", style: { opacity: isDragging ? 0.4 : 1 }, onClick: () => onClick?.(card), "data-card-id": String(card.id), children: _jsxs(CardContent, { className: "space-y-1.5 p-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("div", { className: "line-clamp-2 min-w-0 flex-1 break-words text-sm font-medium leading-snug", children: titleCol ? (_jsx(ActivityValueRenderer, { value: card[titleCol.key], col: titleCol, locale: locale, timeZone: timeZone, currency: currency })) : (_jsx("span", { className: "truncate", children: String(card.id) })) }), visibleActions.length > 0 && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0 -mr-1 -mt-1",
|
|
333
337
|
// Don't start a drag / card click from the menu button.
|
|
334
338
|
onPointerDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: _jsx(MoreHorizontal, { className: "h-4 w-4" }) }) }), _jsx(DropdownMenuContent, { align: "end", onClick: (e) => e.stopPropagation(), children: visibleActions.map((a) => (_jsxs(DropdownMenuItem, { onClick: (e) => {
|
|
335
339
|
e.stopPropagation();
|
|
336
340
|
onAction(a, card);
|
|
337
|
-
}, children: [_jsx(DynamicIcon, { name: a.icon || 'Zap', className: "mr-2 h-4 w-4" }), a.label] }, a.key))) })] }))] }), fieldCols.map((col) => (_jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [_jsxs("span", { className: "shrink-0 opacity-70", children: [col.label, ":"] }), _jsx("span", { className: "min-w-0 truncate", children: _jsx(ActivityValueRenderer, { value: card[col.key], col: col, locale: locale, timeZone: timeZone, currency: currency }) })] }, col.key)))] }) }));
|
|
341
|
+
}, children: [_jsx(DynamicIcon, { name: a.icon || 'Zap', className: "mr-2 h-4 w-4" }), a.label] }, a.key))) })] }))] }), fieldCols.map((col) => (_jsxs("div", { className: "flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground", children: [_jsxs("span", { className: "shrink-0 opacity-70", children: [col.label, ":"] }), _jsx("span", { className: "min-w-0 truncate", children: _jsx(ActivityValueRenderer, { value: card[col.key], col: col, locale: locale, timeZone: timeZone, currency: currency }) })] }, col.key)))] }) }));
|
|
338
342
|
}
|
|
339
343
|
// Static preview rendered inside the DragOverlay (no dnd hooks, no menu).
|
|
340
344
|
function CardPreview({ card, titleCol, fieldCols, locale, timeZone, currency, }) {
|
|
341
|
-
return (_jsx(Card, { className: "w-72 cursor-grabbing border-primary/40 shadow-lg", children: _jsxs(CardContent, { className: "space-y-1.5 p-3", children: [_jsx("div", { className: "text-sm font-medium leading-snug", children: titleCol ? (_jsx(ActivityValueRenderer, { value: card[titleCol.key], col: titleCol, locale: locale, timeZone: timeZone, currency: currency })) : (String(card.id)) }), fieldCols.map((col) => (_jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [_jsxs("span", { className: "shrink-0 opacity-70", children: [col.label, ":"] }), _jsx("span", { className: "min-w-0 truncate", children: _jsx(ActivityValueRenderer, { value: card[col.key], col: col, locale: locale, timeZone: timeZone, currency: currency }) })] }, col.key)))] }) }));
|
|
345
|
+
return (_jsx(Card, { className: "w-72 cursor-grabbing border-primary/40 shadow-lg", children: _jsxs(CardContent, { className: "space-y-1.5 p-3", children: [_jsx("div", { className: "line-clamp-2 break-words text-sm font-medium leading-snug", children: titleCol ? (_jsx(ActivityValueRenderer, { value: card[titleCol.key], col: titleCol, locale: locale, timeZone: timeZone, currency: currency })) : (String(card.id)) }), fieldCols.map((col) => (_jsxs("div", { className: "flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground", children: [_jsxs("span", { className: "shrink-0 opacity-70", children: [col.label, ":"] }), _jsx("span", { className: "min-w-0 truncate", children: _jsx(ActivityValueRenderer, { value: card[col.key], col: col, locale: locale, timeZone: timeZone, currency: currency }) })] }, col.key)))] }) }));
|
|
342
346
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.1.1",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"sonner": ">=1.7",
|
|
39
39
|
"zustand": ">=5",
|
|
40
40
|
"@asteby/metacore-sdk": "^3.2.0",
|
|
41
|
-
"@asteby/metacore-ui": "^2.6.
|
|
41
|
+
"@asteby/metacore-ui": "^2.6.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@tanstack/react-router": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
70
|
"@asteby/metacore-sdk": "3.2.0",
|
|
71
|
-
"@asteby/metacore-ui": "2.6.
|
|
71
|
+
"@asteby/metacore-ui": "2.6.1"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
package/src/dynamic-kanban.tsx
CHANGED
|
@@ -238,8 +238,8 @@ export interface DynamicKanbanProps {
|
|
|
238
238
|
/** Model key as registered on the backend (e.g. "issue"). */
|
|
239
239
|
model: string
|
|
240
240
|
/**
|
|
241
|
-
* Data endpoint base
|
|
242
|
-
* PUTs to `<base
|
|
241
|
+
* Data endpoint base — the org-scoped LIST endpoint (e.g.
|
|
242
|
+
* `/data/<model>/me`). The optimistic update PUTs to `<base>/<id>`.
|
|
243
243
|
*/
|
|
244
244
|
endpoint?: string
|
|
245
245
|
/** Bump to force a metadata + records refetch (same contract as DynamicTable). */
|
|
@@ -411,7 +411,11 @@ export function DynamicKanban({
|
|
|
411
411
|
|
|
412
412
|
try {
|
|
413
413
|
const base = endpoint || `/data/${model}`
|
|
414
|
-
|
|
414
|
+
// `base` is the org-scoped list endpoint (e.g. `/data/<model>/me`),
|
|
415
|
+
// so the per-record update is just `<base>/<id>` — same convention
|
|
416
|
+
// as DynamicTable/DynamicRelation. Appending an extra `/me` here
|
|
417
|
+
// produced `/data/<model>/me/me/<id>` → 404 on drag-to-move.
|
|
418
|
+
const res = (await api.put(`${base}/${cardId}`, {
|
|
415
419
|
[groupByKey]: destStage,
|
|
416
420
|
})) as { data?: ApiResponse<any> }
|
|
417
421
|
if (res?.data && res.data.success === false) {
|
|
@@ -437,7 +441,7 @@ export function DynamicKanban({
|
|
|
437
441
|
return (
|
|
438
442
|
<div className="flex gap-4 overflow-x-auto p-1">
|
|
439
443
|
{[0, 1, 2, 3].map((i) => (
|
|
440
|
-
<div key={i} className="w-
|
|
444
|
+
<div key={i} className="min-w-[220px] max-w-[320px] flex-1 space-y-3">
|
|
441
445
|
<Skeleton className="h-8 w-full" />
|
|
442
446
|
<Skeleton className="h-24 w-full" />
|
|
443
447
|
<Skeleton className="h-24 w-full" />
|
|
@@ -473,7 +477,7 @@ export function DynamicKanban({
|
|
|
473
477
|
|
|
474
478
|
return (
|
|
475
479
|
<DndContext sensors={sensors} onDragStart={onDragStart} onDragEnd={onDragEnd}>
|
|
476
|
-
<div className="flex gap-4 overflow-x-auto p-1" data-testid="kanban-board">
|
|
480
|
+
<div className="flex min-w-0 gap-4 overflow-x-auto p-1" data-testid="kanban-board">
|
|
477
481
|
{lanes.map((stage) => {
|
|
478
482
|
const cards = grouped.get(stage.key) ?? []
|
|
479
483
|
const droppableAllowed =
|
|
@@ -576,7 +580,7 @@ function KanbanLane({ stage, count, isDark, dimmed, disabled, children }: Kanban
|
|
|
576
580
|
return (
|
|
577
581
|
<div
|
|
578
582
|
ref={setNodeRef}
|
|
579
|
-
className="flex w-
|
|
583
|
+
className="flex min-w-[220px] max-w-[320px] flex-1 flex-col rounded-lg border bg-muted/30 transition-opacity"
|
|
580
584
|
style={{
|
|
581
585
|
opacity: dimmed ? 0.45 : 1,
|
|
582
586
|
outline: isOver && !disabled ? '2px solid var(--ring, #3b82f6)' : 'none',
|
|
@@ -651,7 +655,7 @@ function KanbanCard({
|
|
|
651
655
|
>
|
|
652
656
|
<CardContent className="space-y-1.5 p-3">
|
|
653
657
|
<div className="flex items-start justify-between gap-2">
|
|
654
|
-
<div className="min-w-0 flex-1 text-sm font-medium leading-snug">
|
|
658
|
+
<div className="line-clamp-2 min-w-0 flex-1 break-words text-sm font-medium leading-snug">
|
|
655
659
|
{titleCol ? (
|
|
656
660
|
<ActivityValueRenderer
|
|
657
661
|
value={card[titleCol.key]}
|
|
@@ -701,7 +705,7 @@ function KanbanCard({
|
|
|
701
705
|
{fieldCols.map((col) => (
|
|
702
706
|
<div
|
|
703
707
|
key={col.key}
|
|
704
|
-
className="flex items-center gap-1.5 text-xs text-muted-foreground"
|
|
708
|
+
className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground"
|
|
705
709
|
>
|
|
706
710
|
<span className="shrink-0 opacity-70">{col.label}:</span>
|
|
707
711
|
<span className="min-w-0 truncate">
|
|
@@ -732,7 +736,7 @@ function CardPreview({
|
|
|
732
736
|
return (
|
|
733
737
|
<Card className="w-72 cursor-grabbing border-primary/40 shadow-lg">
|
|
734
738
|
<CardContent className="space-y-1.5 p-3">
|
|
735
|
-
<div className="text-sm font-medium leading-snug">
|
|
739
|
+
<div className="line-clamp-2 break-words text-sm font-medium leading-snug">
|
|
736
740
|
{titleCol ? (
|
|
737
741
|
<ActivityValueRenderer
|
|
738
742
|
value={card[titleCol.key]}
|
|
@@ -748,7 +752,7 @@ function CardPreview({
|
|
|
748
752
|
{fieldCols.map((col) => (
|
|
749
753
|
<div
|
|
750
754
|
key={col.key}
|
|
751
|
-
className="flex items-center gap-1.5 text-xs text-muted-foreground"
|
|
755
|
+
className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground"
|
|
752
756
|
>
|
|
753
757
|
<span className="shrink-0 opacity-70">{col.label}:</span>
|
|
754
758
|
<span className="min-w-0 truncate">
|