@asteby/metacore-runtime-react 7.1.3 → 7.1.5
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/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +28 -18
- package/package.json +6 -6
- package/src/dynamic-table.tsx +24 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 7.1.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 922d63b: Add `<MetacoreAppShell>` — single-line provider wiring for metacore apps.
|
|
8
|
+
|
|
9
|
+
Today every app reproduces the same eight-deep wedding cake of providers (QueryClient + ApiProvider + PWAProvider + Toaster + install/update/offline prompts + metadata cache invalidation). The new shell collapses it into:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { MetacoreAppShell } from "@asteby/metacore-app-providers";
|
|
13
|
+
|
|
14
|
+
<MetacoreAppShell api={api} queryClient={queryClient}>
|
|
15
|
+
<RouterProvider router={router} />
|
|
16
|
+
</MetacoreAppShell>;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
What it bundles:
|
|
20
|
+
- `QueryClientProvider` (when `queryClient` is supplied)
|
|
21
|
+
- `ApiProvider` from `runtime-react`
|
|
22
|
+
- `PWAProvider` + `PWAInstallPrompt` + `PWAUpdatePrompt` + `OfflineIndicator`
|
|
23
|
+
- `Toaster` from `metacore-ui`
|
|
24
|
+
- A `MetadataInvalidator` that clears `useMetadataCache` the moment the PWA layer reports a new service worker — so the next mount of `<DynamicTable>` fetches fresh column / filter / actions definitions instead of replaying yesterday's metadata. Resolves the stale-cache bug where adding `filterable: true` to a column on the backend was invisible until users cleared localStorage.
|
|
25
|
+
|
|
26
|
+
Apps that want a subset can pass `hidePWAInstall` / `hidePWAUpdate` / `hideOfflineIndicator` / `hideToaster` / `disableMetadataInvalidate` to opt out per layer.
|
|
27
|
+
|
|
28
|
+
`runtime-react` patch: also switches `<DynamicTable>` to stale-while-revalidate metadata fetch (paint with cache, always re-fetch in background) so the shell isn't the only path that picks up backend changes.
|
|
29
|
+
|
|
30
|
+
- 922d63b: Auto-derive `date_range` filter for `type: 'date'` columns.
|
|
31
|
+
|
|
32
|
+
The zero-config filter chip in 7.1.0 picked the right variant for text/number/boolean/select but mapped `type: 'date'` to a generic text filter. `FilterableColumnHeader` already supports `date_range` — pointing the auto-derive at it makes any column flagged `filterable: true` with `type: 'date'` light up the calendar range picker without app-side glue.
|
|
33
|
+
|
|
34
|
+
## 7.1.4
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- c985453: Auto-derive `date_range` filter for `type: 'date'` columns.
|
|
39
|
+
|
|
40
|
+
The zero-config filter chip in 7.1.0 picked the right variant for text/number/boolean/select but mapped `type: 'date'` to a generic text filter. `FilterableColumnHeader` already supports `date_range` — pointing the auto-derive at it makes any column flagged `filterable: true` with `type: 'date'` light up the calendar range picker without app-side glue.
|
|
41
|
+
|
|
3
42
|
## 7.1.3
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
|
@@ -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,2CA8rBnB"}
|
package/dist/dynamic-table.js
CHANGED
|
@@ -191,35 +191,42 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
|
|
|
191
191
|
return;
|
|
192
192
|
metaInitRef.current = true;
|
|
193
193
|
const initMetadataAndOptions = async () => {
|
|
194
|
-
let meta;
|
|
195
194
|
const cached = getMetadata(model);
|
|
195
|
+
// Stale-while-revalidate: paint with the cached metadata if any so
|
|
196
|
+
// the table renders instantly, then always re-fetch in the
|
|
197
|
+
// background so a backend metadata change (new column, new
|
|
198
|
+
// filterable flag) propagates without users having to clear
|
|
199
|
+
// localStorage.
|
|
196
200
|
if (cached) {
|
|
197
|
-
|
|
198
|
-
setMetadata(meta);
|
|
201
|
+
setMetadata(cached);
|
|
199
202
|
if (!urlHadPerPage.current)
|
|
200
|
-
setPagination((prev) => ({ ...prev, pageSize:
|
|
203
|
+
setPagination((prev) => ({ ...prev, pageSize: cached.defaultPerPage || 10 }));
|
|
201
204
|
setLoading(false);
|
|
202
205
|
}
|
|
203
206
|
else {
|
|
204
207
|
setLoading(true);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
}
|
|
209
|
+
let meta = cached || null;
|
|
210
|
+
try {
|
|
211
|
+
const res = await api.get(`/metadata/table/${model}`);
|
|
212
|
+
if (res.data.success) {
|
|
213
|
+
const fresh = res.data.data;
|
|
214
|
+
meta = fresh;
|
|
215
|
+
setMetadata(fresh);
|
|
216
|
+
cacheMetadata(model, fresh);
|
|
212
217
|
if (!urlHadPerPage.current)
|
|
213
|
-
setPagination((prev) => ({ ...prev, pageSize:
|
|
218
|
+
setPagination((prev) => ({ ...prev, pageSize: fresh.defaultPerPage || 10 }));
|
|
214
219
|
}
|
|
215
|
-
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (!cached)
|
|
216
223
|
console.error('Error al cargar la configuración de la tabla', error);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
finally {
|
|
220
|
-
setLoading(false);
|
|
221
|
-
}
|
|
222
224
|
}
|
|
225
|
+
finally {
|
|
226
|
+
setLoading(false);
|
|
227
|
+
}
|
|
228
|
+
if (!meta)
|
|
229
|
+
return;
|
|
223
230
|
const columnEndpoints = meta.columns.filter(c => c.useOptions && c.searchEndpoint).map(c => c.searchEndpoint);
|
|
224
231
|
const filterEndpoints = (meta.filters || []).filter(f => f.searchEndpoint && (f.type === 'select' || f.type === 'boolean')).map(f => f.searchEndpoint);
|
|
225
232
|
const allEndpoints = [...columnEndpoints, ...filterEndpoints];
|
|
@@ -473,6 +480,7 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
|
|
|
473
480
|
// - explicit options or searchEndpoint → multi-select dropdown
|
|
474
481
|
// - boolean → boolean toggle (renders as select under the hood)
|
|
475
482
|
// - number / number_range / numeric → number range
|
|
483
|
+
// - date → date range picker (start/end calendar)
|
|
476
484
|
// - everything else (text, email, phone, tags…) → text contains
|
|
477
485
|
let filterType = 'select';
|
|
478
486
|
if (hasStaticOptions || hasEndpoint)
|
|
@@ -481,6 +489,8 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
|
|
|
481
489
|
filterType = 'boolean';
|
|
482
490
|
else if (c.type === 'number')
|
|
483
491
|
filterType = 'number_range';
|
|
492
|
+
else if (c.type === 'date')
|
|
493
|
+
filterType = 'date_range';
|
|
484
494
|
else
|
|
485
495
|
filterType = 'text';
|
|
486
496
|
const options = hasStaticOptions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.5",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"@tanstack/react-table": "^8.20.0",
|
|
47
47
|
"@types/react": "^19.0.0",
|
|
48
48
|
"date-fns": "^4.1.0",
|
|
49
|
-
"i18next": "^
|
|
50
|
-
"lucide-react": "^0.
|
|
49
|
+
"i18next": "^26.0.0",
|
|
50
|
+
"lucide-react": "^1.0.0",
|
|
51
51
|
"react": "^19.2.4",
|
|
52
|
-
"react-day-picker": "^
|
|
52
|
+
"react-day-picker": "^9.0.0",
|
|
53
53
|
"react-dom": "^19.2.4",
|
|
54
|
-
"react-i18next": "^
|
|
55
|
-
"sonner": "^
|
|
54
|
+
"react-i18next": "^17.0.0",
|
|
55
|
+
"sonner": "^2.0.0",
|
|
56
56
|
"typescript": "^5.6.0",
|
|
57
57
|
"zustand": "^5.0.0",
|
|
58
58
|
"@asteby/metacore-sdk": "2.2.0",
|
package/src/dynamic-table.tsx
CHANGED
|
@@ -269,29 +269,35 @@ export function DynamicTable({
|
|
|
269
269
|
if (metaInitRef.current) return
|
|
270
270
|
metaInitRef.current = true
|
|
271
271
|
const initMetadataAndOptions = async () => {
|
|
272
|
-
let meta: TableMetadata
|
|
273
272
|
const cached = getMetadata(model)
|
|
273
|
+
// Stale-while-revalidate: paint with the cached metadata if any so
|
|
274
|
+
// the table renders instantly, then always re-fetch in the
|
|
275
|
+
// background so a backend metadata change (new column, new
|
|
276
|
+
// filterable flag) propagates without users having to clear
|
|
277
|
+
// localStorage.
|
|
274
278
|
if (cached) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!urlHadPerPage.current) setPagination((prev: PaginationState) => ({ ...prev, pageSize: meta.defaultPerPage || 10 }))
|
|
279
|
+
setMetadata(cached)
|
|
280
|
+
if (!urlHadPerPage.current) setPagination((prev: PaginationState) => ({ ...prev, pageSize: cached.defaultPerPage || 10 }))
|
|
278
281
|
setLoading(false)
|
|
279
282
|
} else {
|
|
280
283
|
setLoading(true)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
} finally {
|
|
292
|
-
setLoading(false)
|
|
284
|
+
}
|
|
285
|
+
let meta: TableMetadata | null = cached || null
|
|
286
|
+
try {
|
|
287
|
+
const res = await api.get(`/metadata/table/${model}`) as { data: ApiResponse<TableMetadata> }
|
|
288
|
+
if (res.data.success) {
|
|
289
|
+
const fresh = res.data.data
|
|
290
|
+
meta = fresh
|
|
291
|
+
setMetadata(fresh)
|
|
292
|
+
cacheMetadata(model, fresh)
|
|
293
|
+
if (!urlHadPerPage.current) setPagination((prev: PaginationState) => ({ ...prev, pageSize: fresh.defaultPerPage || 10 }))
|
|
293
294
|
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
if (!cached) console.error('Error al cargar la configuración de la tabla', error)
|
|
297
|
+
} finally {
|
|
298
|
+
setLoading(false)
|
|
294
299
|
}
|
|
300
|
+
if (!meta) return
|
|
295
301
|
const columnEndpoints = meta.columns.filter(c => c.useOptions && c.searchEndpoint).map(c => c.searchEndpoint!)
|
|
296
302
|
const filterEndpoints = (meta.filters || []).filter(f => f.searchEndpoint && (f.type === 'select' || f.type === 'boolean')).map(f => f.searchEndpoint!)
|
|
297
303
|
const allEndpoints = [...columnEndpoints, ...filterEndpoints]
|
|
@@ -511,11 +517,13 @@ export function DynamicTable({
|
|
|
511
517
|
// - explicit options or searchEndpoint → multi-select dropdown
|
|
512
518
|
// - boolean → boolean toggle (renders as select under the hood)
|
|
513
519
|
// - number / number_range / numeric → number range
|
|
520
|
+
// - date → date range picker (start/end calendar)
|
|
514
521
|
// - everything else (text, email, phone, tags…) → text contains
|
|
515
522
|
let filterType: ColumnFilterConfig['filterType'] = 'select'
|
|
516
523
|
if (hasStaticOptions || hasEndpoint) filterType = 'select'
|
|
517
524
|
else if (c.type === 'boolean') filterType = 'boolean'
|
|
518
525
|
else if (c.type === 'number') filterType = 'number_range'
|
|
526
|
+
else if (c.type === 'date') filterType = 'date_range'
|
|
519
527
|
else filterType = 'text'
|
|
520
528
|
|
|
521
529
|
const options = hasStaticOptions
|