@asteby/metacore-runtime-react 18.28.2 → 19.0.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 19.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - feat(kanban): new `DynamicKanban` view-type renderer, a sibling of `DynamicTable` sharing the same contract (`model` + `endpoint` + injected `ApiProvider`). Reads `view_type`/`group_by` from the table metadata (RFC §1.2) and derives board lanes from the model-level `stages[]` (or, as a fallback, the `group_by` column's status `options`). Records are fetched through the same `/data/:model` path and bucketed by `row[group_by]`; cards render through the existing `ActivityValueRenderer` (title + 2-3 fields) and reuse the `ActionModalDispatcher`/`useModelActions('row')` plumbing for the per-card action menu.
8
+
9
+ Drag-to-move (via `@dnd-kit/core`) is **optimistic**: dropping a card into another lane mutates local state immediately and fires `PUT /data/:model/me/:id { <group_by>: <dest> }`; on failure the move reverts and a toast surfaces — sidestepping the "refetch loses scroll/selection" gap. When the metadata declares `transitions[]`, a card may only drop into a stage reachable from its current one (disallowed lanes dim and reject the drop; the kernel still validates server-side).
10
+
11
+ Also adds `DynamicView`, a metadata-driven dispatcher that routes `view_type === 'kanban'` → `DynamicKanban`, else → `DynamicTable`, plus the pure helpers `deriveStages`, `groupByStage`, `isTransitionAllowed`, `applyOptimisticMove`, `selectCardColumns`, `resolveViewRenderer`. New `TableMetadata` fields (`view_type`, `group_by`, `stages`, `transitions`) and `StageMeta`/`StageTransition` types are purely additive. New deps: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`.
12
+ ## 18.28.3
13
+
14
+ ### Patch Changes
15
+
16
+ - e5e9fca: feat(action-modal): a field-action modal now renders the acted-on record's related-lists (model metadata.relations) below the form, as read-only context — e.g. the reception history of a transfer shown right inside the "Recibir" modal. Create actions (no record) render nothing; reuses the same DynamicRelations the detail view uses.
17
+
3
18
  ## 18.28.2
4
19
 
5
20
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AA+CA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAiDlB"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAgDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAiDlB"}
@@ -15,6 +15,7 @@ import { toast } from 'sonner';
15
15
  import { useApi } from './api-context';
16
16
  import { DynamicIcon } from './dynamic-icon';
17
17
  import { DynamicLineItems } from './dynamic-line-items';
18
+ import { DynamicRelations } from './dynamic-relations';
18
19
  import { DynamicSelectField } from './dynamic-select-field';
19
20
  import { DynamicDateField } from './dynamic-date-field';
20
21
  import { UploadField } from './upload-field';
@@ -137,6 +138,34 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
137
138
  const api = useApi();
138
139
  const [formData, setFormData] = useState({});
139
140
  const [executing, setExecuting] = useState(false);
141
+ // Related records to surface BELOW the form, as read-only context for the
142
+ // record being acted on — e.g. the reception history of a transfer while
143
+ // receiving against it. Sourced from the model's metadata.relations (the
144
+ // same declarative relations the detail view renders). Only row actions on a
145
+ // real record show them; create actions (no record.id) render nothing.
146
+ const [relations, setRelations] = useState([]);
147
+ const recordId = record?.id;
148
+ useEffect(() => {
149
+ let cancelled = false;
150
+ if (!open || recordId == null) {
151
+ setRelations([]);
152
+ return;
153
+ }
154
+ api.get(`/metadata/table/${model}`)
155
+ .then((res) => {
156
+ if (cancelled)
157
+ return;
158
+ const rels = res?.data?.relations ?? res?.data?.data?.relations ?? [];
159
+ setRelations(Array.isArray(rels) ? rels : []);
160
+ })
161
+ .catch(() => {
162
+ if (!cancelled)
163
+ setRelations([]);
164
+ });
165
+ return () => {
166
+ cancelled = true;
167
+ };
168
+ }, [open, model, recordId, api]);
140
169
  useEffect(() => {
141
170
  if (open && action.fields) {
142
171
  const defaults = {};
@@ -209,12 +238,12 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
209
238
  : hasLineItems
210
239
  ? '820px'
211
240
  : undefined;
212
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90vh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-lg'), style: { maxHeight: '90vh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), action.label] }), action.confirmMessage && _jsx(DialogDescription, { children: action.confirmMessage })] }), _jsx("div", { className: "-mx-1 grid min-h-0 flex-1 gap-4 overflow-y-auto px-1 py-4 sm:grid-cols-2", children: action.fields?.map((field) => {
213
- const fullWidth = isLineItemsField(field) ||
214
- resolveWidget(field) === 'textarea' ||
215
- resolveWidget(field) === 'richtext';
216
- return (_jsxs("div", { className: 'grid gap-2 ' + (fullWidth ? 'sm:col-span-2' : ''), children: [_jsxs(Label, { htmlFor: field.key, children: [field.label, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
217
- }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), action.label] })] })] }) }));
241
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90vh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-lg'), style: { maxHeight: '90vh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), action.label] }), action.confirmMessage && _jsx(DialogDescription, { children: action.confirmMessage })] }), _jsxs("div", { className: "-mx-1 grid min-h-0 flex-1 gap-4 overflow-y-auto px-1 py-4 sm:grid-cols-2", children: [action.fields?.map((field) => {
242
+ const fullWidth = isLineItemsField(field) ||
243
+ resolveWidget(field) === 'textarea' ||
244
+ resolveWidget(field) === 'richtext';
245
+ return (_jsxs("div", { className: 'grid gap-2 ' + (fullWidth ? 'sm:col-span-2' : ''), children: [_jsxs(Label, { htmlFor: field.key, children: [field.label, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
246
+ }), relations.length > 0 && (_jsx("div", { className: "sm:col-span-2", children: _jsx(DynamicRelations, { record: record, relations: relations }) }))] }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), action.label] })] })] }) }));
218
247
  }
219
248
  function renderField(field, value, onChange,
220
249
  // Full current form values — lets a line-items grid (and any cascading
@@ -0,0 +1,66 @@
1
+ import * as React from 'react';
2
+ import type { TableMetadata, ColumnDefinition, StageMeta, StageTransition } from './types';
3
+ /**
4
+ * Resolves the board lanes for a kanban view. Prefers the model-level
5
+ * `metadata.stages` (the kernel's `stages[]`); falls back to the `group_by`
6
+ * column's `options` (the kernel projects the stage machine onto the status
7
+ * display). Returns lanes sorted by `order` (then declared order). Empty when
8
+ * neither source is present — the caller renders a "no stages" notice.
9
+ */
10
+ export declare function deriveStages(metadata: TableMetadata): StageMeta[];
11
+ /**
12
+ * Buckets records into a `stageKey → rows[]` map, one entry per declared stage
13
+ * (in stage order), plus a trailing `__unassigned__` bucket for rows whose
14
+ * stage value matches no declared lane (so nothing silently vanishes). Empty
15
+ * lanes are kept so the board always shows every stage.
16
+ */
17
+ export declare const UNASSIGNED_LANE = "__unassigned__";
18
+ export declare function groupByStage(records: any[], groupByKey: string, stages: StageMeta[]): Map<string, any[]>;
19
+ /**
20
+ * Whether a card may move `from → to` given the declared transitions. No
21
+ * transitions declared → unrestricted (the kernel still validates server-side).
22
+ * A move to the same stage is always a no-op "allowed". `'*'` is a wildcard on
23
+ * either side.
24
+ */
25
+ export declare function isTransitionAllowed(transitions: StageTransition[] | undefined, from: string, to: string): boolean;
26
+ /**
27
+ * Returns a NEW grouping with `cardId` moved from `fromStage` to `toStage`
28
+ * (appended to the destination lane). Pure — does not mutate the input map.
29
+ * Used by the optimistic drop handler so the board updates before the PUT
30
+ * resolves, and so the previous grouping can be restored on failure.
31
+ */
32
+ export declare function applyOptimisticMove(grouped: Map<string, any[]>, cardId: string | number, fromStage: string, toStage: string, groupByKey: string): Map<string, any[]>;
33
+ /**
34
+ * Picks the columns shown on a card: a `title` column (first searchable column,
35
+ * else first text-ish column) and up to `maxFields` secondary columns. Excludes
36
+ * the group_by column (it's the lane itself) and any column hidden from the
37
+ * table view (visibility modal/list, or `hidden`).
38
+ */
39
+ export declare function selectCardColumns(metadata: TableMetadata, maxFields?: number): {
40
+ title: ColumnDefinition | null;
41
+ fields: ColumnDefinition[];
42
+ };
43
+ export interface DynamicKanbanProps {
44
+ /** Model key as registered on the backend (e.g. "issue"). */
45
+ model: string;
46
+ /**
47
+ * Data endpoint base. Defaults to `/data/<model>`. The optimistic update
48
+ * PUTs to `<base>/me/<id>`.
49
+ */
50
+ endpoint?: string;
51
+ /** Bump to force a metadata + records refetch (same contract as DynamicTable). */
52
+ refreshTrigger?: any;
53
+ /** Called when a card is clicked (outside its action menu). */
54
+ onCardClick?: (row: any) => void;
55
+ /**
56
+ * Max cards fetched per lane render. Kanban shows all cards at once (no
57
+ * pagination UI), so it requests a single large page. Defaults to 200.
58
+ */
59
+ pageSize?: number;
60
+ /** IANA timezone for datetime card fields (org config). */
61
+ timeZone?: string;
62
+ /** ISO 4217 currency for money card fields (org config). */
63
+ currency?: string;
64
+ }
65
+ export declare function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, pageSize, timeZone, currency, }: DynamicKanbanProps): React.JSX.Element;
66
+ //# sourceMappingURL=dynamic-kanban.d.ts.map
@@ -0,0 +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,qBA+RpB"}
@@ -0,0 +1,341 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { DndContext, DragOverlay, PointerSensor, useSensor, useSensors, useDraggable, useDroppable, } from '@dnd-kit/core';
5
+ import { MoreHorizontal } from 'lucide-react';
6
+ import { toast } from 'sonner';
7
+ import { Badge, Button, Card, CardContent, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, ScrollArea, Skeleton, } from '@asteby/metacore-ui/primitives';
8
+ import { generateBadgeStyles, optionColor } from '@asteby/metacore-ui/lib';
9
+ import { useApi } from './api-context';
10
+ import { useMetadataCache } from './metadata-cache';
11
+ import { ActivityValueRenderer } from './activity-value-renderer';
12
+ import { ActionModalDispatcher } from './action-modal-dispatcher';
13
+ import { useModelActions } from './model-action-toolbar';
14
+ import { DynamicIcon } from './dynamic-icon';
15
+ import { isColumnVisibleInTable } from './column-visibility';
16
+ import { isActionAllowedForRowState } from './dynamic-columns';
17
+ // ---------------------------------------------------------------------------
18
+ // Pure helpers (exported for unit tests — no React, no transport)
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Resolves the board lanes for a kanban view. Prefers the model-level
22
+ * `metadata.stages` (the kernel's `stages[]`); falls back to the `group_by`
23
+ * column's `options` (the kernel projects the stage machine onto the status
24
+ * display). Returns lanes sorted by `order` (then declared order). Empty when
25
+ * neither source is present — the caller renders a "no stages" notice.
26
+ */
27
+ export function deriveStages(metadata) {
28
+ const fromMeta = metadata.stages;
29
+ if (fromMeta && fromMeta.length > 0) {
30
+ return [...fromMeta].sort(sortByOrder);
31
+ }
32
+ const groupBy = metadata.group_by;
33
+ if (!groupBy)
34
+ return [];
35
+ const col = metadata.columns.find((c) => c.key === groupBy);
36
+ const opts = col?.options ?? [];
37
+ return opts.map((o, i) => ({
38
+ key: String(o.value),
39
+ label: o.label,
40
+ color: o.color,
41
+ order: i,
42
+ }));
43
+ }
44
+ function sortByOrder(a, b) {
45
+ const ao = a.order ?? Number.MAX_SAFE_INTEGER;
46
+ const bo = b.order ?? Number.MAX_SAFE_INTEGER;
47
+ return ao - bo;
48
+ }
49
+ /**
50
+ * Buckets records into a `stageKey → rows[]` map, one entry per declared stage
51
+ * (in stage order), plus a trailing `__unassigned__` bucket for rows whose
52
+ * stage value matches no declared lane (so nothing silently vanishes). Empty
53
+ * lanes are kept so the board always shows every stage.
54
+ */
55
+ export const UNASSIGNED_LANE = '__unassigned__';
56
+ export function groupByStage(records, groupByKey, stages) {
57
+ const map = new Map();
58
+ for (const s of stages)
59
+ map.set(s.key, []);
60
+ const known = new Set(stages.map((s) => s.key));
61
+ for (const row of records) {
62
+ const raw = row?.[groupByKey];
63
+ const key = raw === null || raw === undefined ? '' : String(raw);
64
+ if (known.has(key)) {
65
+ map.get(key).push(row);
66
+ }
67
+ else {
68
+ if (!map.has(UNASSIGNED_LANE))
69
+ map.set(UNASSIGNED_LANE, []);
70
+ map.get(UNASSIGNED_LANE).push(row);
71
+ }
72
+ }
73
+ return map;
74
+ }
75
+ /**
76
+ * Whether a card may move `from → to` given the declared transitions. No
77
+ * transitions declared → unrestricted (the kernel still validates server-side).
78
+ * A move to the same stage is always a no-op "allowed". `'*'` is a wildcard on
79
+ * either side.
80
+ */
81
+ export function isTransitionAllowed(transitions, from, to) {
82
+ if (from === to)
83
+ return true;
84
+ if (!transitions || transitions.length === 0)
85
+ return true;
86
+ return transitions.some((t) => (t.from === from || t.from === '*') && (t.to === to || t.to === '*'));
87
+ }
88
+ /**
89
+ * Returns a NEW grouping with `cardId` moved from `fromStage` to `toStage`
90
+ * (appended to the destination lane). Pure — does not mutate the input map.
91
+ * Used by the optimistic drop handler so the board updates before the PUT
92
+ * resolves, and so the previous grouping can be restored on failure.
93
+ */
94
+ export function applyOptimisticMove(grouped, cardId, fromStage, toStage, groupByKey) {
95
+ const next = new Map();
96
+ for (const [k, rows] of grouped)
97
+ next.set(k, [...rows]);
98
+ const fromRows = next.get(fromStage) ?? [];
99
+ const idx = fromRows.findIndex((r) => String(r.id) === String(cardId));
100
+ if (idx === -1)
101
+ return next;
102
+ const [moved] = fromRows.splice(idx, 1);
103
+ const updated = { ...moved, [groupByKey]: toStage };
104
+ const toRows = next.get(toStage) ?? [];
105
+ toRows.push(updated);
106
+ next.set(toStage, toRows);
107
+ return next;
108
+ }
109
+ /**
110
+ * Picks the columns shown on a card: a `title` column (first searchable column,
111
+ * else first text-ish column) and up to `maxFields` secondary columns. Excludes
112
+ * the group_by column (it's the lane itself) and any column hidden from the
113
+ * table view (visibility modal/list, or `hidden`).
114
+ */
115
+ export function selectCardColumns(metadata, maxFields = 3) {
116
+ const groupBy = metadata.group_by;
117
+ const visible = metadata.columns.filter((c) => c.key !== groupBy &&
118
+ !c.hidden &&
119
+ isColumnVisibleInTable(c) &&
120
+ c.key !== 'id');
121
+ const title = visible.find((c) => c.searchable) ??
122
+ visible.find((c) => c.type === 'text' || c.cellStyle === 'truncate-text') ??
123
+ visible[0] ??
124
+ null;
125
+ const fields = visible
126
+ .filter((c) => c.key !== title?.key)
127
+ .slice(0, maxFields);
128
+ return { title, fields };
129
+ }
130
+ // ---------------------------------------------------------------------------
131
+ // Theme hook (mirrors the private one in dynamic-columns / activity-renderer)
132
+ // ---------------------------------------------------------------------------
133
+ function useIsDarkTheme() {
134
+ const [isDark, setIsDark] = useState(() => typeof document !== 'undefined' &&
135
+ document.documentElement.classList.contains('dark'));
136
+ useEffect(() => {
137
+ if (typeof document === 'undefined')
138
+ return;
139
+ const sync = () => setIsDark(document.documentElement.classList.contains('dark'));
140
+ const observer = new MutationObserver(sync);
141
+ observer.observe(document.documentElement, {
142
+ attributes: true,
143
+ attributeFilter: ['class'],
144
+ });
145
+ return () => observer.disconnect();
146
+ }, []);
147
+ return isDark;
148
+ }
149
+ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, pageSize = 200, timeZone, currency, }) {
150
+ const { t, i18n } = useTranslation();
151
+ const api = useApi();
152
+ const isDark = useIsDarkTheme();
153
+ const { getMetadata, setMetadata: cacheMetadata } = useMetadataCache();
154
+ const cachedMeta = getMetadata(model);
155
+ const [metadata, setMetadata] = useState(cachedMeta || null);
156
+ const [records, setRecords] = useState([]);
157
+ const [loading, setLoading] = useState(!cachedMeta);
158
+ const [loadingData, setLoadingData] = useState(true);
159
+ // Active drag card id (for the DragOverlay + drop-zone highlighting).
160
+ const [activeId, setActiveId] = useState(null);
161
+ const [actionModal, setActionModal] = useState({ action: null, record: null });
162
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
163
+ // ---- metadata fetch (same path as DynamicTable) ----
164
+ useEffect(() => {
165
+ let cancelled = false;
166
+ const cached = getMetadata(model);
167
+ if (cached) {
168
+ setMetadata(cached);
169
+ setLoading(false);
170
+ }
171
+ else {
172
+ setLoading(true);
173
+ }
174
+ api
175
+ .get(`/metadata/table/${model}`)
176
+ .then((res) => {
177
+ if (cancelled)
178
+ return;
179
+ const body = res.data;
180
+ if (body.success) {
181
+ setMetadata(body.data);
182
+ cacheMetadata(model, body.data);
183
+ }
184
+ })
185
+ .catch((err) => {
186
+ if (!cancelled && !cached)
187
+ console.error('Error al cargar la configuración del tablero', err);
188
+ })
189
+ .finally(() => {
190
+ if (!cancelled)
191
+ setLoading(false);
192
+ });
193
+ return () => {
194
+ cancelled = true;
195
+ };
196
+ // eslint-disable-next-line react-hooks/exhaustive-deps
197
+ }, [model]);
198
+ // ---- records fetch (same path as DynamicTable, single large page) ----
199
+ const fetchData = useCallback(async () => {
200
+ if (!metadata)
201
+ return;
202
+ setLoadingData(true);
203
+ try {
204
+ const res = (await api.get(endpoint || `/data/${model}`, {
205
+ params: { page: 1, per_page: pageSize },
206
+ }));
207
+ if (res.data.success)
208
+ setRecords(res.data.data || []);
209
+ }
210
+ catch (err) {
211
+ console.error('Error al cargar las tarjetas', err);
212
+ }
213
+ finally {
214
+ setLoadingData(false);
215
+ }
216
+ }, [api, endpoint, model, metadata, pageSize]);
217
+ useEffect(() => {
218
+ if (metadata)
219
+ void fetchData();
220
+ // eslint-disable-next-line react-hooks/exhaustive-deps
221
+ }, [metadata, refreshTrigger]);
222
+ const stages = useMemo(() => (metadata ? deriveStages(metadata) : []), [metadata]);
223
+ const groupByKey = metadata?.group_by || '';
224
+ const transitions = metadata?.transitions;
225
+ const grouped = useMemo(() => groupByStage(records, groupByKey, stages), [records, groupByKey, stages]);
226
+ const { title: titleCol, fields: fieldCols } = useMemo(() => (metadata ? selectCardColumns(metadata) : { title: null, fields: [] }), [metadata]);
227
+ // Row-placement actions reused verbatim from the table's plumbing.
228
+ const rowActions = useModelActions(model, ['row'], metadata?.actions);
229
+ const cardById = useMemo(() => {
230
+ const m = new Map();
231
+ for (const r of records)
232
+ m.set(String(r.id), r);
233
+ return m;
234
+ }, [records]);
235
+ const stageOfCard = useCallback((id) => {
236
+ const card = cardById.get(id);
237
+ const raw = card?.[groupByKey];
238
+ return raw === null || raw === undefined ? '' : String(raw);
239
+ }, [cardById, groupByKey]);
240
+ const onDragStart = useCallback((e) => {
241
+ setActiveId(String(e.active.id));
242
+ }, []);
243
+ const onDragEnd = useCallback(async (e) => {
244
+ setActiveId(null);
245
+ const { active, over } = e;
246
+ if (!over)
247
+ return;
248
+ const cardId = String(active.id);
249
+ const destStage = String(over.id);
250
+ const srcStage = stageOfCard(cardId);
251
+ if (srcStage === destStage)
252
+ return;
253
+ if (!isTransitionAllowed(transitions, srcStage, destStage)) {
254
+ toast.error(t('kanban.invalidTransition', {
255
+ defaultValue: 'Movimiento no permitido entre estas etapas',
256
+ }));
257
+ return;
258
+ }
259
+ // OPTIMISTIC: move the card in local state immediately.
260
+ const prevRecords = records;
261
+ setRecords((rs) => rs.map((r) => String(r.id) === cardId ? { ...r, [groupByKey]: destStage } : r));
262
+ try {
263
+ const base = endpoint || `/data/${model}`;
264
+ const res = (await api.put(`${base}/me/${cardId}`, {
265
+ [groupByKey]: destStage,
266
+ }));
267
+ if (res?.data && res.data.success === false) {
268
+ throw new Error(res.data.message || 'update_failed');
269
+ }
270
+ }
271
+ catch (err) {
272
+ // REVERT + toast on failure.
273
+ setRecords(prevRecords);
274
+ toast.error(t('kanban.moveFailed', {
275
+ defaultValue: 'No se pudo mover la tarjeta',
276
+ }) +
277
+ (err?.response?.data?.message
278
+ ? `: ${err.response.data.message}`
279
+ : ''));
280
+ }
281
+ }, [api, endpoint, groupByKey, model, records, stageOfCard, t, transitions]);
282
+ 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-72 shrink-0 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
+ }
285
+ if (!metadata || !groupByKey || stages.length === 0) {
286
+ return (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground", children: t('kanban.noStages', {
287
+ defaultValue: 'Este modelo no declara etapas para la vista de tablero.',
288
+ }) }));
289
+ }
290
+ const activeCard = activeId ? cardById.get(activeId) : null;
291
+ const activeStage = activeId ? stageOfCard(activeId) : '';
292
+ const lanes = [...stages];
293
+ if (grouped.has(UNASSIGNED_LANE)) {
294
+ lanes.push({
295
+ key: UNASSIGNED_LANE,
296
+ label: t('kanban.unassigned', { defaultValue: 'Sin etapa' }),
297
+ color: 'slate',
298
+ order: Number.MAX_SAFE_INTEGER,
299
+ });
300
+ }
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) => {
302
+ const cards = grouped.get(stage.key) ?? [];
303
+ const droppableAllowed = !activeId ||
304
+ stage.key === activeStage ||
305
+ isTransitionAllowed(transitions, activeStage, stage.key);
306
+ return (_jsx(KanbanLane, { stage: stage, count: cards.length, isDark: isDark, dimmed: !!activeId && !droppableAllowed, disabled: !!activeId && !droppableAllowed, children: loadingData && cards.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : cards.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('kanban.emptyLane', { defaultValue: 'Sin tarjetas' }) })) : (cards.map((card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, actions: rowActions, locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: (action, record) => setActionModal({ action, record }) }, String(card.id))))) }, stage.key));
307
+ }) }), _jsx(DragOverlay, { children: activeCard ? (_jsx(CardPreview, { card: activeCard, titleCol: titleCol, fieldCols: fieldCols, locale: i18n.language, timeZone: timeZone, currency: currency })) : null }), actionModal.action && (_jsx(ActionModalDispatcher, { open: !!actionModal.action, onOpenChange: (open) => {
308
+ if (!open)
309
+ setActionModal({ action: null, record: null });
310
+ }, action: actionModal.action, model: model, record: actionModal.record ?? {}, endpoint: endpoint ?? `/data/${model}/me`, onSuccess: () => {
311
+ setActionModal({ action: null, record: null });
312
+ void fetchData();
313
+ } }))] }));
314
+ }
315
+ function KanbanLane({ stage, count, isDark, dimmed, disabled, children }) {
316
+ const { setNodeRef, isOver } = useDroppable({ id: stage.key, disabled });
317
+ const headerStyle = generateBadgeStyles(stage.color || optionColor(stage.key), {
318
+ isDark,
319
+ });
320
+ return (_jsxs("div", { ref: setNodeRef, className: "flex w-72 shrink-0 flex-col rounded-lg border bg-muted/30 transition-opacity", style: {
321
+ opacity: dimmed ? 0.45 : 1,
322
+ outline: isOver && !disabled ? '2px solid var(--ring, #3b82f6)' : 'none',
323
+ outlineOffset: 2,
324
+ }, "data-stage": stage.key, "data-disabled": disabled || undefined, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5", children: [_jsx(Badge, { variant: "outline", className: "border-0 text-xs font-semibold", style: headerStyle, children: stage.label }), _jsx("span", { className: "text-xs font-medium tabular-nums text-muted-foreground", children: count })] }), _jsx(ScrollArea, { className: "max-h-[70vh]", children: _jsx("div", { className: "flex flex-col gap-2 px-2 pb-3", children: children }) })] }));
325
+ }
326
+ function KanbanCard({ card, titleCol, fieldCols, actions, locale, timeZone, currency, onClick, onAction, }) {
327
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
328
+ id: String(card.id),
329
+ });
330
+ const visibleActions = actions.filter((a) => isActionAllowedForRowState(a, card));
331
+ 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",
332
+ // Don't start a drag / card click from the menu button.
333
+ 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) => {
334
+ e.stopPropagation();
335
+ onAction(a, card);
336
+ }, 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)))] }) }));
337
+ }
338
+ // Static preview rendered inside the DragOverlay (no dnd hooks, no menu).
339
+ function CardPreview({ card, titleCol, fieldCols, locale, timeZone, currency, }) {
340
+ 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)))] }) }));
341
+ }
@@ -0,0 +1,18 @@
1
+ import { type DynamicTableProps } from './dynamic-table';
2
+ import { type DynamicKanbanProps } from './dynamic-kanban';
3
+ /**
4
+ * Pure routing decision: which renderer a `view_type` maps onto. Exported so a
5
+ * host that resolves metadata itself can branch without mounting this wrapper.
6
+ */
7
+ export declare function resolveViewRenderer(viewType: string | undefined): 'kanban' | 'table';
8
+ export interface DynamicViewProps extends DynamicTableProps {
9
+ /**
10
+ * Props forwarded to <DynamicKanban> when the model resolves to a kanban
11
+ * view. `model`/`endpoint`/`refreshTrigger`/`timeZone`/`currency` are shared
12
+ * with the table props and forwarded automatically; this is for the
13
+ * kanban-only extras (e.g. `onCardClick`, `pageSize`).
14
+ */
15
+ kanbanProps?: Partial<Omit<DynamicKanbanProps, 'model' | 'endpoint'>>;
16
+ }
17
+ export declare function DynamicView({ kanbanProps, ...tableProps }: DynamicViewProps): import("react").JSX.Element | null;
18
+ //# sourceMappingURL=dynamic-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-view.d.ts","sourceRoot":"","sources":["../src/dynamic-view.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAGzE;;;GAGG;AACH,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC7B,QAAQ,GAAG,OAAO,CAEpB;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACvD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC,CAAA;CACxE;AAED,wBAAgB,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,UAAU,EAAE,EAAE,gBAAgB,sCAwD3E"}
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // DynamicView — the single entry point a host renders for a model's "list"
3
+ // surface. It picks the renderer from the model's `view_type`:
4
+ // - `'kanban'` → <DynamicKanban>
5
+ // - anything else / absent → <DynamicTable> (the default)
6
+ //
7
+ // The decision is metadata-driven (RFC §1.2): the kernel serves `view_type` +
8
+ // `group_by` on the table metadata, derived from the nav item. A host that
9
+ // already knows the view type can skip this and render the concrete component
10
+ // directly; a generic host route (e.g. ops `/m/$model`) mounts <DynamicView>
11
+ // and lets the metadata decide, so the same model can expose a `table` nav and
12
+ // a `kanban` nav with no host code change.
13
+ //
14
+ // Both child components fetch their own metadata (cache-backed), so the extra
15
+ // read this wrapper does to learn `view_type` is served from the same cache —
16
+ // no duplicate network round-trip in practice.
17
+ import { useEffect, useState } from 'react';
18
+ import { useApi } from './api-context';
19
+ import { useMetadataCache } from './metadata-cache';
20
+ import { DynamicTable } from './dynamic-table';
21
+ import { DynamicKanban } from './dynamic-kanban';
22
+ /**
23
+ * Pure routing decision: which renderer a `view_type` maps onto. Exported so a
24
+ * host that resolves metadata itself can branch without mounting this wrapper.
25
+ */
26
+ export function resolveViewRenderer(viewType) {
27
+ return viewType === 'kanban' ? 'kanban' : 'table';
28
+ }
29
+ export function DynamicView({ kanbanProps, ...tableProps }) {
30
+ const { model, endpoint, refreshTrigger, timeZone, currency } = tableProps;
31
+ const api = useApi();
32
+ const cached = useMetadataCache((s) => s.getMetadata(model));
33
+ const setMeta = useMetadataCache((s) => s.setMetadata);
34
+ const [viewType, setViewType] = useState(cached?.view_type);
35
+ const [resolved, setResolved] = useState(!!cached);
36
+ useEffect(() => {
37
+ let cancelled = false;
38
+ const c = useMetadataCache.getState().getMetadata(model);
39
+ if (c) {
40
+ setViewType(c.view_type);
41
+ setResolved(true);
42
+ }
43
+ api
44
+ .get(`/metadata/table/${model}`)
45
+ .then((res) => {
46
+ if (cancelled)
47
+ return;
48
+ const body = res.data;
49
+ const meta = body?.success ? body.data : res.data;
50
+ if (meta) {
51
+ setViewType(meta.view_type);
52
+ setMeta(model, meta);
53
+ }
54
+ })
55
+ .catch(() => {
56
+ /* fall back to the table renderer */
57
+ })
58
+ .finally(() => {
59
+ if (!cancelled)
60
+ setResolved(true);
61
+ });
62
+ return () => {
63
+ cancelled = true;
64
+ };
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [model]);
67
+ // Until we know the view type, render nothing transient-heavy: default to the
68
+ // table renderer only once resolved to avoid a table→kanban flash.
69
+ if (!resolved && !cached)
70
+ return null;
71
+ if (resolveViewRenderer(viewType) === 'kanban') {
72
+ return (_jsx(DynamicKanban, { model: model, endpoint: endpoint, refreshTrigger: refreshTrigger, timeZone: timeZone, currency: currency, ...kanbanProps }));
73
+ }
74
+ return _jsx(DynamicTable, { ...tableProps });
75
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './types';
2
2
  export * from './options-context';
3
3
  export * from './dynamic-table';
4
+ export { DynamicKanban, type DynamicKanbanProps, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
5
+ export { DynamicView, resolveViewRenderer, type DynamicViewProps, } from './dynamic-view';
4
6
  export * from './dynamic-form';
5
7
  export { ActionModalDispatcher, type ActionModalProps, } from './action-modal-dispatcher';
6
8
  export { ModelActionToolbar, useModelActions, type ModelActionToolbarProps, type ActionPlacement, } from './model-action-toolbar';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}