@asteby/metacore-runtime-react 6.4.0 → 7.1.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 +24 -0
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +37 -9
- package/dist/dynamic-crud-page.d.ts.map +1 -1
- package/dist/dynamic-crud-page.js +5 -1
- package/dist/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +15 -3
- package/package.json +3 -3
- package/src/dynamic-columns.tsx +39 -9
- package/src/dynamic-crud-page.tsx +5 -1
- package/src/dynamic-table.tsx +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 7.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0cd085c: Zero-config CRUD UX from a single column flag.
|
|
8
|
+
|
|
9
|
+
Three changes that move polish from each app's metadata into the SDK default behaviour, so a model only needs `enableCRUDActions: true` plus `filterable: true` on the columns it wants searchable to get the same UX link / ops / hub render today:
|
|
10
|
+
1. **Auto-derive filter chip type from column type.** A column flagged `filterable: true` without options or a `searchEndpoint` no longer falls back to "no filter" — it picks the FilterableColumnHeader variant that matches the column type: `text` for text/email/phone/tags, `number_range` for numeric columns, `boolean` for booleans, `select` when options/endpoint are present.
|
|
11
|
+
2. **Auto-render the row Actions column when `enableCRUDActions` is on.** If the host metadata already declares its own `actions[]`, those win. When it doesn't, the SDK falls back to the canonical View / Edit / Delete trio wired to DynamicTable's existing `view` / `edit` / `delete` handlers — no host-side glue.
|
|
12
|
+
3. **`<DynamicCRUDPage>` defaults `hideRefresh` to `true`.** The page-level Refresh button duplicated the one DynamicTable's internal toolbar already ships next to "View"; the page chrome now defers to it. Apps that want both back can pass `hideRefresh={false}`.
|
|
13
|
+
|
|
14
|
+
## 7.0.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 3450876: Add `getInitials(name)` helper to `@asteby/metacore-ui/lib`.
|
|
19
|
+
|
|
20
|
+
Pulls a duplicated 6-line snippet (`name.split(' ').map(n => n[0]).slice(0, 2).join('').toUpperCase()`) out of every avatar across the platform — chat headers, profile dropdowns, dynamic-table avatar cells, sidebar nav. Trims whitespace, caps token count, and falls back to a single character when the input is empty.
|
|
21
|
+
|
|
22
|
+
`runtime-react`'s avatar cell renderer now uses it; visually identical, one less inline lambda.
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [3450876]
|
|
25
|
+
- @asteby/metacore-ui@0.7.0
|
|
26
|
+
|
|
3
27
|
## 6.4.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAwHD;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAwHD;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAiXnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
|
package/dist/dynamic-columns.js
CHANGED
|
@@ -15,7 +15,7 @@ import * as icons from 'lucide-react';
|
|
|
15
15
|
import { MoreHorizontal } from 'lucide-react';
|
|
16
16
|
import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Checkbox, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@asteby/metacore-ui';
|
|
17
17
|
import { DataTableColumnHeader, FilterableColumnHeader, } from '@asteby/metacore-ui/data-table';
|
|
18
|
-
import { generateBadgeStyles } from '@asteby/metacore-ui/lib';
|
|
18
|
+
import { generateBadgeStyles, getInitials } from '@asteby/metacore-ui/lib';
|
|
19
19
|
import { OptionsContext } from './options-context';
|
|
20
20
|
import { DynamicIcon } from './dynamic-icon';
|
|
21
21
|
const defaultGetImageUrl = (path) => path;
|
|
@@ -207,12 +207,7 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
|
|
|
207
207
|
else if (value) {
|
|
208
208
|
avatarSrc = `${apiBaseUrl}${col.basePath || ''}${value}`;
|
|
209
209
|
}
|
|
210
|
-
return (_jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [_jsxs(Avatar, { className: "h-8 w-8 rounded-lg ring-1 ring-border/50", children: [_jsx(AvatarImage, { src: getImageUrl(avatarSrc || ''), alt: String(name), className: "object-cover" }), _jsx(AvatarFallback, { className: "text-[10px] font-bold bg-primary/5 text-primary rounded-lg", children: String(name)
|
|
211
|
-
.split(' ')
|
|
212
|
-
.map((n) => n[0])
|
|
213
|
-
.slice(0, 2)
|
|
214
|
-
.join('')
|
|
215
|
-
.toUpperCase() })] }), _jsxs("div", { className: "flex flex-col min-w-0 overflow-hidden", children: [_jsx("span", { className: "font-medium text-sm truncate leading-none mb-0.5 text-foreground/90", children: String(name) }), desc && (_jsx("span", { className: "text-[11px] text-muted-foreground truncate leading-none", children: String(desc) }))] })] }));
|
|
210
|
+
return (_jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [_jsxs(Avatar, { className: "h-8 w-8 rounded-lg ring-1 ring-border/50", children: [_jsx(AvatarImage, { src: getImageUrl(avatarSrc || ''), alt: String(name), className: "object-cover" }), _jsx(AvatarFallback, { className: "text-[10px] font-bold bg-primary/5 text-primary rounded-lg", children: getInitials(String(name)) })] }), _jsxs("div", { className: "flex flex-col min-w-0 overflow-hidden", children: [_jsx("span", { className: "font-medium text-sm truncate leading-none mb-0.5 text-foreground/90", children: String(name) }), desc && (_jsx("span", { className: "text-[11px] text-muted-foreground truncate leading-none", children: String(desc) }))] })] }));
|
|
216
211
|
}
|
|
217
212
|
case 'relation-badge-list':
|
|
218
213
|
return renderRelationBadges(value, col);
|
|
@@ -271,14 +266,47 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
|
|
|
271
266
|
enableHiding: true,
|
|
272
267
|
});
|
|
273
268
|
});
|
|
274
|
-
|
|
269
|
+
// Resolve which actions to surface in the row dropdown:
|
|
270
|
+
// 1. If the host metadata declares its own actions, use them as-is.
|
|
271
|
+
// 2. Otherwise, when enableCRUDActions is true, fall back to the
|
|
272
|
+
// canonical View / Edit / Delete trio so any model with CRUD on
|
|
273
|
+
// gets the same dropdown without the host having to declare it.
|
|
274
|
+
// The DynamicTable wires `view`/`edit`/`delete` to its own dialogs
|
|
275
|
+
// through onAction, so labels/icons are the only thing this needs to
|
|
276
|
+
// ship.
|
|
277
|
+
const explicitActions = metadata.actions ?? [];
|
|
278
|
+
const hasExplicitActions = (metadata.hasActions ?? explicitActions.length > 0) && explicitActions.length > 0;
|
|
279
|
+
const defaultCRUDActions = metadata.enableCRUDActions
|
|
280
|
+
? [
|
|
281
|
+
{
|
|
282
|
+
key: 'view',
|
|
283
|
+
name: 'view',
|
|
284
|
+
label: t ? t('datatable.view_record') : 'Ver',
|
|
285
|
+
icon: 'Eye',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
key: 'edit',
|
|
289
|
+
name: 'edit',
|
|
290
|
+
label: t ? t('datatable.edit') : 'Editar',
|
|
291
|
+
icon: 'Pencil',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
key: 'delete',
|
|
295
|
+
name: 'delete',
|
|
296
|
+
label: t ? t('datatable.delete') : 'Eliminar',
|
|
297
|
+
icon: 'Trash2',
|
|
298
|
+
},
|
|
299
|
+
]
|
|
300
|
+
: [];
|
|
301
|
+
const resolvedActions = hasExplicitActions ? explicitActions : defaultCRUDActions;
|
|
302
|
+
if (resolvedActions.length > 0) {
|
|
275
303
|
columns.push({
|
|
276
304
|
id: 'actions',
|
|
277
305
|
header: () => _jsx("div", { className: "text-right", children: t ? t('common.actions') : 'Acciones' }),
|
|
278
306
|
size: 80,
|
|
279
307
|
maxSize: 80,
|
|
280
308
|
meta: {},
|
|
281
|
-
cell: ({ row }) => (_jsx("div", { className: "flex items-center justify-end", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "h-8 w-8 p-0", children: [_jsx("span", { className: "sr-only", children: "Abrir men\u00FA" }), _jsx(MoreHorizontal, { className: "h-4 w-4" })] }) }), _jsx(DropdownMenuContent, { align: "end", children:
|
|
309
|
+
cell: ({ row }) => (_jsx("div", { className: "flex items-center justify-end", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "h-8 w-8 p-0", children: [_jsx("span", { className: "sr-only", children: "Abrir men\u00FA" }), _jsx(MoreHorizontal, { className: "h-4 w-4" })] }) }), _jsx(DropdownMenuContent, { align: "end", children: resolvedActions
|
|
282
310
|
.filter((action) => {
|
|
283
311
|
if (!action.condition)
|
|
284
312
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-crud-page.d.ts","sourceRoot":"","sources":["../src/dynamic-crud-page.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAKN,MAAM,OAAO,CAAA;AAWd,MAAM,WAAW,sBAAsB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;mDAC+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AASD,MAAM,WAAW,sBAAsB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACjC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,sDAAsD;IACtD,OAAO,CAAC,EAAE,sBAAsB,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"dynamic-crud-page.d.ts","sourceRoot":"","sources":["../src/dynamic-crud-page.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAKN,MAAM,OAAO,CAAA;AAWd,MAAM,WAAW,sBAAsB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;mDAC+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AASD,MAAM,WAAW,sBAAsB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACjC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,sDAAsD;IACtD,OAAO,CAAC,EAAE,sBAAsB,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,2CAsL1D"}
|
|
@@ -87,7 +87,11 @@ export function DynamicCRUDPage(props) {
|
|
|
87
87
|
const effectiveHideCreate = hideCreate || ext?.hideCreate;
|
|
88
88
|
const effectiveHideExport = hideExport || ext?.hideExport;
|
|
89
89
|
const effectiveHideImport = hideImport || ext?.hideImport;
|
|
90
|
-
|
|
90
|
+
// Refresh defaults to hidden in the page header — <DynamicTable> ships
|
|
91
|
+
// its own refresh icon next to the View / column-visibility toolbar, so
|
|
92
|
+
// showing one in the page chrome is just visual duplication. Apps that
|
|
93
|
+
// want it back can pass `hideRefresh={false}`.
|
|
94
|
+
const effectiveHideRefresh = hideRefresh ?? ext?.hideRefresh ?? true;
|
|
91
95
|
const showCreate = enableCRUD && !effectiveHideCreate;
|
|
92
96
|
const showImport = enableCRUD && !effectiveHideImport;
|
|
93
97
|
const showExport = !effectiveHideExport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AASnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AASnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,2CAsrBnB"}
|
package/dist/dynamic-table.js
CHANGED
|
@@ -469,8 +469,20 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
|
|
|
469
469
|
continue;
|
|
470
470
|
const hasStaticOptions = (c.options?.length ?? 0) > 0;
|
|
471
471
|
const hasEndpoint = !!c.searchEndpoint;
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
// Pick the filter UI from column type:
|
|
473
|
+
// - explicit options or searchEndpoint → multi-select dropdown
|
|
474
|
+
// - boolean → boolean toggle (renders as select under the hood)
|
|
475
|
+
// - number / number_range / numeric → number range
|
|
476
|
+
// - everything else (text, email, phone, tags…) → text contains
|
|
477
|
+
let filterType = 'select';
|
|
478
|
+
if (hasStaticOptions || hasEndpoint)
|
|
479
|
+
filterType = 'select';
|
|
480
|
+
else if (c.type === 'boolean')
|
|
481
|
+
filterType = 'boolean';
|
|
482
|
+
else if (c.type === 'number')
|
|
483
|
+
filterType = 'number_range';
|
|
484
|
+
else
|
|
485
|
+
filterType = 'text';
|
|
474
486
|
const options = hasStaticOptions
|
|
475
487
|
? c.options.map(o => ({
|
|
476
488
|
label: o.label,
|
|
@@ -482,7 +494,7 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
|
|
|
482
494
|
? filterOptionsMap.get(c.searchEndpoint) || []
|
|
483
495
|
: [];
|
|
484
496
|
map.set(c.key, {
|
|
485
|
-
filterType
|
|
497
|
+
filterType,
|
|
486
498
|
filterKey: c.key,
|
|
487
499
|
options,
|
|
488
500
|
selectedValues: dynamicFilters[c.key] || [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"date-fns": ">=3",
|
|
31
31
|
"react-day-picker": ">=8",
|
|
32
32
|
"@asteby/metacore-sdk": "^2.2.0",
|
|
33
|
-
"@asteby/metacore-ui": "^0.
|
|
33
|
+
"@asteby/metacore-ui": "^0.7.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@tanstack/react-router": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"typescript": "^5.6.0",
|
|
57
57
|
"zustand": "^5.0.0",
|
|
58
58
|
"@asteby/metacore-sdk": "2.2.0",
|
|
59
|
-
"@asteby/metacore-ui": "0.
|
|
59
|
+
"@asteby/metacore-ui": "0.7.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsc -p tsconfig.json",
|
package/src/dynamic-columns.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
FilterableColumnHeader,
|
|
32
32
|
type ColumnFilterMeta,
|
|
33
33
|
} from '@asteby/metacore-ui/data-table'
|
|
34
|
-
import { generateBadgeStyles } from '@asteby/metacore-ui/lib'
|
|
34
|
+
import { generateBadgeStyles, getInitials } from '@asteby/metacore-ui/lib'
|
|
35
35
|
import { OptionsContext } from './options-context'
|
|
36
36
|
import { DynamicIcon } from './dynamic-icon'
|
|
37
37
|
import type { TableMetadata, ColumnDefinition } from './types'
|
|
@@ -329,12 +329,7 @@ export function makeDefaultGetDynamicColumns(
|
|
|
329
329
|
className="object-cover"
|
|
330
330
|
/>
|
|
331
331
|
<AvatarFallback className="text-[10px] font-bold bg-primary/5 text-primary rounded-lg">
|
|
332
|
-
{String(name)
|
|
333
|
-
.split(' ')
|
|
334
|
-
.map((n: string) => n[0])
|
|
335
|
-
.slice(0, 2)
|
|
336
|
-
.join('')
|
|
337
|
-
.toUpperCase()}
|
|
332
|
+
{getInitials(String(name))}
|
|
338
333
|
</AvatarFallback>
|
|
339
334
|
</Avatar>
|
|
340
335
|
<div className="flex flex-col min-w-0 overflow-hidden">
|
|
@@ -466,7 +461,42 @@ export function makeDefaultGetDynamicColumns(
|
|
|
466
461
|
})
|
|
467
462
|
})
|
|
468
463
|
|
|
469
|
-
|
|
464
|
+
// Resolve which actions to surface in the row dropdown:
|
|
465
|
+
// 1. If the host metadata declares its own actions, use them as-is.
|
|
466
|
+
// 2. Otherwise, when enableCRUDActions is true, fall back to the
|
|
467
|
+
// canonical View / Edit / Delete trio so any model with CRUD on
|
|
468
|
+
// gets the same dropdown without the host having to declare it.
|
|
469
|
+
// The DynamicTable wires `view`/`edit`/`delete` to its own dialogs
|
|
470
|
+
// through onAction, so labels/icons are the only thing this needs to
|
|
471
|
+
// ship.
|
|
472
|
+
const explicitActions = metadata.actions ?? []
|
|
473
|
+
const hasExplicitActions = (metadata.hasActions ?? explicitActions.length > 0) && explicitActions.length > 0
|
|
474
|
+
const defaultCRUDActions: typeof explicitActions =
|
|
475
|
+
metadata.enableCRUDActions
|
|
476
|
+
? [
|
|
477
|
+
{
|
|
478
|
+
key: 'view',
|
|
479
|
+
name: 'view',
|
|
480
|
+
label: t ? t('datatable.view_record') : 'Ver',
|
|
481
|
+
icon: 'Eye',
|
|
482
|
+
} as any,
|
|
483
|
+
{
|
|
484
|
+
key: 'edit',
|
|
485
|
+
name: 'edit',
|
|
486
|
+
label: t ? t('datatable.edit') : 'Editar',
|
|
487
|
+
icon: 'Pencil',
|
|
488
|
+
} as any,
|
|
489
|
+
{
|
|
490
|
+
key: 'delete',
|
|
491
|
+
name: 'delete',
|
|
492
|
+
label: t ? t('datatable.delete') : 'Eliminar',
|
|
493
|
+
icon: 'Trash2',
|
|
494
|
+
} as any,
|
|
495
|
+
]
|
|
496
|
+
: []
|
|
497
|
+
const resolvedActions = hasExplicitActions ? explicitActions : defaultCRUDActions
|
|
498
|
+
|
|
499
|
+
if (resolvedActions.length > 0) {
|
|
470
500
|
columns.push({
|
|
471
501
|
id: 'actions',
|
|
472
502
|
header: () => <div className="text-right">{t ? t('common.actions') : 'Acciones'}</div>,
|
|
@@ -483,7 +513,7 @@ export function makeDefaultGetDynamicColumns(
|
|
|
483
513
|
</Button>
|
|
484
514
|
</DropdownMenuTrigger>
|
|
485
515
|
<DropdownMenuContent align="end">
|
|
486
|
-
{
|
|
516
|
+
{resolvedActions
|
|
487
517
|
.filter((action) => {
|
|
488
518
|
if (!action.condition) return true
|
|
489
519
|
const { field, operator, value } = action.condition
|
|
@@ -156,7 +156,11 @@ export function DynamicCRUDPage(props: DynamicCRUDPageProps) {
|
|
|
156
156
|
const effectiveHideCreate = hideCreate || ext?.hideCreate
|
|
157
157
|
const effectiveHideExport = hideExport || ext?.hideExport
|
|
158
158
|
const effectiveHideImport = hideImport || ext?.hideImport
|
|
159
|
-
|
|
159
|
+
// Refresh defaults to hidden in the page header — <DynamicTable> ships
|
|
160
|
+
// its own refresh icon next to the View / column-visibility toolbar, so
|
|
161
|
+
// showing one in the page chrome is just visual duplication. Apps that
|
|
162
|
+
// want it back can pass `hideRefresh={false}`.
|
|
163
|
+
const effectiveHideRefresh = hideRefresh ?? ext?.hideRefresh ?? true
|
|
160
164
|
const showCreate = enableCRUD && !effectiveHideCreate
|
|
161
165
|
const showImport = enableCRUD && !effectiveHideImport
|
|
162
166
|
const showExport = !effectiveHideExport
|
package/src/dynamic-table.tsx
CHANGED
|
@@ -507,7 +507,17 @@ export function DynamicTable({
|
|
|
507
507
|
if (!c.filterable || map.has(c.key)) continue
|
|
508
508
|
const hasStaticOptions = (c.options?.length ?? 0) > 0
|
|
509
509
|
const hasEndpoint = !!c.searchEndpoint
|
|
510
|
-
|
|
510
|
+
// Pick the filter UI from column type:
|
|
511
|
+
// - explicit options or searchEndpoint → multi-select dropdown
|
|
512
|
+
// - boolean → boolean toggle (renders as select under the hood)
|
|
513
|
+
// - number / number_range / numeric → number range
|
|
514
|
+
// - everything else (text, email, phone, tags…) → text contains
|
|
515
|
+
let filterType: ColumnFilterConfig['filterType'] = 'select'
|
|
516
|
+
if (hasStaticOptions || hasEndpoint) filterType = 'select'
|
|
517
|
+
else if (c.type === 'boolean') filterType = 'boolean'
|
|
518
|
+
else if (c.type === 'number') filterType = 'number_range'
|
|
519
|
+
else filterType = 'text'
|
|
520
|
+
|
|
511
521
|
const options = hasStaticOptions
|
|
512
522
|
? c.options!.map(o => ({
|
|
513
523
|
label: o.label,
|
|
@@ -519,7 +529,7 @@ export function DynamicTable({
|
|
|
519
529
|
? filterOptionsMap.get(c.searchEndpoint!) || []
|
|
520
530
|
: []
|
|
521
531
|
map.set(c.key, {
|
|
522
|
-
filterType
|
|
532
|
+
filterType,
|
|
523
533
|
filterKey: c.key,
|
|
524
534
|
options,
|
|
525
535
|
selectedValues: dynamicFilters[c.key] || [],
|