@marktiderman/genesis-ui 2.4.0 → 2.5.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/dist/{chunk-GDFNAHI4.js → chunk-IXGAEECT.js} +4 -2
- package/dist/chunk-IXGAEECT.js.map +1 -0
- package/dist/components/data/index.d.ts +10 -2
- package/dist/components/data/index.js +143 -55
- package/dist/components/data/index.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/use-resource-page.d.ts +1 -1
- package/dist/hooks/use-resource-page.js +1 -1
- package/dist/{use-resource-page-DTQS011Q.d.ts → use-resource-page-Dd2VUv30.d.ts} +15 -1
- package/package.json +4 -4
- package/dist/chunk-GDFNAHI4.js.map +0 -1
|
@@ -186,6 +186,8 @@ function useResourcePage(props) {
|
|
|
186
186
|
header: col.header ?? titleCase(col.key),
|
|
187
187
|
sortable: col.sortable ?? true,
|
|
188
188
|
hideBelow: col.hideBelow,
|
|
189
|
+
editable: col.editable,
|
|
190
|
+
accessor: col.accessor,
|
|
189
191
|
render: col.render ? (item, value) => col.render(item, value, {
|
|
190
192
|
update: async () => {
|
|
191
193
|
throw new Error(
|
|
@@ -288,5 +290,5 @@ function useResourcePage(props) {
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
export { titleCase, useResourcePage };
|
|
291
|
-
//# sourceMappingURL=chunk-
|
|
292
|
-
//# sourceMappingURL=chunk-
|
|
293
|
+
//# sourceMappingURL=chunk-IXGAEECT.js.map
|
|
294
|
+
//# sourceMappingURL=chunk-IXGAEECT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-resource-page.ts"],"names":[],"mappings":"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","file":"chunk-IXGAEECT.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { DataTableColumn } from \"../components/data/DataTable\";\nimport type { DataFilterConfig } from \"../components/data/DataFilters\";\nimport type { ViewMode } from \"../components/patterns/view-toggle\";\nimport type { Identifier, FilterParam, SortParam } from \"@marktiderman/genesis-core/provider\";\nimport { useViewPreference } from \"./use-view-preference\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Actions available inside column render functions. */\nexport interface ResourceActions<T> {\n update: (id: Identifier, data: Partial<T>) => Promise<T>;\n remove: (id: Identifier) => Promise<void>;\n refetch: () => void;\n}\n\n/** Column definition — full config form. */\nexport interface ResourceColumnDef<T> {\n key: string;\n header?: string;\n sortable?: boolean;\n hideBelow?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n render?: (\n item: T,\n value: unknown,\n actions: ResourceActions<T>,\n ) => ReactNode;\n /** In-place cell editing, passed straight through to `DataTable`. */\n editable?: DataTableColumn<T>[\"editable\"];\n /**\n * The cell's value, when it isn't `item[key]`. Passed straight through to\n * `DataTable`, which also starts an edit from `String(accessor(item))`.\n */\n accessor?: (item: T) => unknown;\n}\n\nexport interface UseResourcePageProps<T> {\n /** Resource name — used for view-preference storage key. */\n resource?: string;\n /** The data to display (pre-fetched or from useResource). */\n data: T[];\n /** Total count of records (before client-side filtering). */\n total: number;\n /** Column definitions. Strings are expanded to `{ key, header, sortable }`. Omit to auto-generate from data keys. */\n columns?: Array<string | ResourceColumnDef<T>>;\n /** Status filter config. String = field name (options auto-derived). Object = explicit field + options. */\n statusFilter?: string | { field: string; options?: string[] };\n /** Fields to search across. Defaults to all string-valued fields in data. */\n searchFields?: string[];\n /** Initial filter params applied on mount. */\n initialFilters?: FilterParam[];\n /** Callback when filters change. */\n onFiltersChange?: (filters: FilterParam[]) => void;\n /** Default view mode. */\n defaultView?: ViewMode;\n /** Detail interaction mode. */\n detail?: \"panel\" | \"modal\" | \"route\" | \"none\";\n /** Field used for the detail title. Defaults to first column key. */\n titleField?: string;\n /** Field used for the detail subtitle. Defaults to second column key. */\n subtitleField?: string;\n /**\n * Server-controlled mode. The `data` handed in has ALREADY been searched,\n * sorted and paginated by the server, so this hook must not do any of it a\n * second time — `data` is returned untouched and `total` is the server's\n * `total` rather than the length of a client-filtered slice.\n *\n * Search / sort / status state is still tracked (the filter UI stays\n * controlled and `onFiltersChange` still fires) — only the client-side\n * transform of `data` is skipped. Defaults to `false`, so every existing\n * caller keeps its current client-side behavior.\n */\n controlled?: boolean;\n}\n\nexport interface UseResourcePageReturn<T> {\n /** Filtered and sorted data for display. */\n data: T[];\n /** Total count (of the filtered set; the server's `total` when `controlled`). */\n total: number;\n /** Resolved column definitions. */\n columns: ResourceColumnDef<T>[];\n /** Columns mapped to DataTable format. */\n tableColumns: DataTableColumn<T>[];\n /** Ready-to-spread filter config for DataFilters. */\n filterConfig: {\n search: string;\n onSearchChange: (value: string) => void;\n sort: string;\n onSortChange: (sort: string) => void;\n sortOptions: Array<{ value: string; label: string }>;\n statusChips?: {\n options: string[];\n selected: string[];\n onChange: (values: string[]) => void;\n };\n };\n viewMode: ViewMode;\n setViewMode: (mode: ViewMode) => void;\n search: string;\n setSearch: (value: string) => void;\n sort: SortParam | null;\n setSort: (sort: SortParam | null) => void;\n statusValue: string[];\n setStatusValue: (values: string[]) => void;\n selectedItem: T | null;\n setSelectedItem: (item: T | null) => void;\n detailOpen: boolean;\n setDetailOpen: (open: boolean) => void;\n handleRowClick: (item: T) => void;\n titleField: string;\n subtitleField: string;\n statusOptions: string[];\n hasActiveFilters: boolean;\n clearFilters: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a key string to title case.\n * \"created_at\" → \"Created At\"\n * \"firstName\" → \"First Name\"\n */\nexport function titleCase(str: string): string {\n // Split on underscores, hyphens, and camelCase boundaries\n const words = str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase → camel Case\n .replace(/[_-]/g, \" \") // snake_case / kebab-case → spaces\n .split(/\\s+/)\n .filter(Boolean);\n\n return words\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(\" \");\n}\n\n/**\n * Resolve column shorthand into full ResourceColumnDef objects.\n * - undefined + data present → auto-generate from Object.keys(data[0]), skip \"id\"\n * - undefined + empty data → empty array\n * - string items → { key, header: titleCase(key), sortable: true }\n * - object items → defaults applied\n */\nfunction resolveColumns<T>(\n columns: Array<string | ResourceColumnDef<T>> | undefined,\n data: T[],\n): ResourceColumnDef<T>[] {\n if (columns === undefined) {\n if (data.length === 0) return [];\n const keys = Object.keys(data[0] as Record<string, unknown>).filter(\n (k) => k !== \"id\",\n );\n return keys.map((key) => ({\n key,\n header: titleCase(key),\n sortable: true,\n }));\n }\n\n return columns.map((col) => {\n if (typeof col === \"string\") {\n return { key: col, header: titleCase(col), sortable: true };\n }\n return {\n ...col,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n };\n });\n}\n\n/**\n * Derive the list of string fields in a data set (for default search).\n */\nfunction deriveStringFields<T>(data: T[]): string[] {\n if (data.length === 0) return [];\n const first = data[0] as Record<string, unknown>;\n return Object.keys(first).filter((k) => typeof first[k] === \"string\");\n}\n\n/**\n * Derive unique status options from a data array.\n */\nfunction deriveStatusOptions<T>(data: T[], field: string): string[] {\n const values = new Set<string>();\n for (const item of data) {\n const val = (item as Record<string, unknown>)[field];\n if (val != null) values.add(String(val));\n }\n return Array.from(values).sort();\n}\n\n/**\n * Client-side text search across multiple fields.\n */\nfunction matchesSearch<T>(\n item: T,\n query: string,\n fields: string[],\n): boolean {\n if (!query) return true;\n const lower = query.toLowerCase();\n const record = item as Record<string, unknown>;\n return fields.some((f) => {\n const val = record[f];\n return val != null && String(val).toLowerCase().includes(lower);\n });\n}\n\n/**\n * Client-side sort comparator.\n */\nfunction compareItems<T>(a: T, b: T, sort: SortParam): number {\n const ra = a as Record<string, unknown>;\n const rb = b as Record<string, unknown>;\n const va = ra[sort.field];\n const vb = rb[sort.field];\n const sa = va == null ? \"\" : String(va);\n const sb = vb == null ? \"\" : String(vb);\n const cmp = sa.localeCompare(sb, undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n return sort.order === \"asc\" ? cmp : -cmp;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useResourcePage<T = Record<string, unknown>>(\n props: UseResourcePageProps<T>,\n): UseResourcePageReturn<T> {\n const {\n resource,\n data: rawData,\n total: rawTotal,\n columns: columnsProp,\n statusFilter: statusFilterProp,\n searchFields: searchFieldsProp,\n defaultView = \"table\",\n detail = \"panel\",\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n initialFilters,\n onFiltersChange,\n controlled = false,\n } = props;\n\n // -- View mode --\n const [viewMode, setViewMode] = useViewPreference(\n resource ?? \"resource-page\",\n defaultView,\n );\n\n // -- Derive initial values from initialFilters --\n const initialSearch = useMemo(() => {\n if (!initialFilters) return \"\";\n const searchFilter = initialFilters.find(\n (f) => f.operator === \"contains\" && f.field === \"__search__\",\n );\n return searchFilter && \"value\" in searchFilter ? String(searchFilter.value ?? \"\") : \"\";\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialSort = useMemo(() => {\n if (!initialFilters) return null;\n const sortFilter = initialFilters.find(\n (f) => f.field === \"__sort__\",\n );\n if (!sortFilter || !(\"value\" in sortFilter) || typeof sortFilter.value !== \"string\") return null;\n const [field, order] = sortFilter.value.split(\":\");\n if (!field || (order !== \"asc\" && order !== \"desc\")) return null;\n return { field, order } as SortParam;\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialStatus = useMemo(() => {\n if (!initialFilters) return [];\n const statusFilter = initialFilters.find(\n (f) => f.operator === \"in\" && f.field === \"__status__\",\n );\n if (!statusFilter || statusFilter.operator !== \"in\") return [];\n return statusFilter.value.map((v) => String(v));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n // -- Search --\n const [search, setSearch] = useState(initialSearch);\n\n // -- Sort --\n const [sort, setSort] = useState<SortParam | null>(initialSort);\n\n // -- Status filter --\n const [statusValue, setStatusValue] = useState<string[]>(initialStatus);\n\n // -- Detail panel --\n const [selectedItem, setSelectedItem] = useState<T | null>(null);\n const [detailOpen, setDetailOpen] = useState(false);\n\n // -- Resolve columns --\n const resolvedColumns = useMemo(\n () => resolveColumns(columnsProp, rawData),\n [columnsProp, rawData],\n );\n\n // -- Title / subtitle fields --\n const titleField =\n titleFieldProp ?? resolvedColumns[0]?.key ?? \"name\";\n const subtitleField =\n subtitleFieldProp ?? resolvedColumns[1]?.key ?? \"\";\n\n // -- Status filter config --\n const statusFieldName = useMemo(() => {\n if (!statusFilterProp) return null;\n return typeof statusFilterProp === \"string\"\n ? statusFilterProp\n : statusFilterProp.field;\n }, [statusFilterProp]);\n\n const statusOptions = useMemo(() => {\n if (!statusFilterProp) return [];\n if (typeof statusFilterProp === \"object\" && statusFilterProp.options) {\n return statusFilterProp.options;\n }\n if (!statusFieldName) return [];\n return deriveStatusOptions(rawData, statusFieldName);\n }, [statusFilterProp, statusFieldName, rawData]);\n\n // -- Search fields --\n const searchFields = useMemo(\n () => searchFieldsProp ?? deriveStringFields(rawData),\n [searchFieldsProp, rawData],\n );\n\n // -- Client-side filtering + sorting --\n // Skipped entirely in controlled mode: the server already applied the\n // search, the sort and the page window, so re-running them here would\n // filter the current page against a query the server has already honoured\n // (and re-sort a slice of rows that is not the full result set).\n const filteredData = useMemo(() => {\n if (controlled) return rawData;\n\n let result = rawData;\n\n // Text search\n if (search) {\n result = result.filter((item) =>\n matchesSearch(item, search, searchFields),\n );\n }\n\n // Status filter\n if (statusFieldName && statusValue.length > 0) {\n result = result.filter((item) => {\n const val = (item as Record<string, unknown>)[statusFieldName];\n return val != null && statusValue.includes(String(val));\n });\n }\n\n // Sort\n if (sort) {\n result = [...result].sort((a, b) => compareItems(a, b, sort));\n }\n\n return result;\n }, [controlled, rawData, search, searchFields, statusFieldName, statusValue, sort]);\n\n // -- Sort options (built from resolved columns) --\n const sortOptions = useMemo(() => {\n const opts: Array<{ value: string; label: string }> = [];\n for (const col of resolvedColumns) {\n if (col.sortable !== false) {\n opts.push({\n value: `${col.key}:asc`,\n label: `${col.header ?? titleCase(col.key)} (A-Z)`,\n });\n opts.push({\n value: `${col.key}:desc`,\n label: `${col.header ?? titleCase(col.key)} (Z-A)`,\n });\n }\n }\n return opts;\n }, [resolvedColumns]);\n\n // -- Sort string for DataFilters select --\n const sortString = sort ? `${sort.field}:${sort.order}` : \"\";\n\n const handleSortChange = useCallback((value: string) => {\n if (!value) {\n setSort(null);\n return;\n }\n const [field, order] = value.split(\":\");\n setSort({ field, order: order as \"asc\" | \"desc\" });\n }, []);\n\n // -- Map to DataTableColumn --\n const tableColumns: DataTableColumn<T>[] = useMemo(\n () =>\n resolvedColumns.map((col) => ({\n key: col.key,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n hideBelow: col.hideBelow,\n editable: col.editable,\n accessor: col.accessor,\n render: col.render\n ? (item: T, value: unknown) =>\n col.render!(item, value, {\n update: async () => {\n throw new Error(\n \"ResourceActions.update not available in server-data mode\",\n );\n },\n remove: async () => {\n throw new Error(\n \"ResourceActions.remove not available in server-data mode\",\n );\n },\n refetch: () => {\n /* no-op in server-data mode */\n },\n })\n : undefined,\n })),\n [resolvedColumns],\n );\n\n // -- Active filters check --\n const hasActiveFilters =\n search !== \"\" || statusValue.length > 0 || sort !== null;\n\n const clearFilters = useCallback(() => {\n setSearch(\"\");\n setStatusValue([]);\n setSort(null);\n }, []);\n\n // -- Notify parent of filter changes --\n const onFiltersChangeRef = useRef(onFiltersChange);\n onFiltersChangeRef.current = onFiltersChange;\n\n useEffect(() => {\n if (!onFiltersChangeRef.current) return;\n const filters: FilterParam[] = [];\n if (search) {\n filters.push({ field: \"__search__\", operator: \"contains\", value: search });\n }\n if (sort) {\n filters.push({ field: \"__sort__\", operator: \"eq\", value: `${sort.field}:${sort.order}` });\n }\n if (statusValue.length > 0) {\n filters.push({ field: \"__status__\", operator: \"in\", value: statusValue });\n }\n onFiltersChangeRef.current(filters);\n }, [search, sort, statusValue]);\n\n // -- Row click --\n const handleRowClick = useCallback(\n (item: T) => {\n if (detail === \"none\") return;\n setSelectedItem(item);\n setDetailOpen(true);\n },\n [detail],\n );\n\n // -- Filter config (ready to spread into DataFilters) --\n const filterConfig = useMemo(\n () => ({\n search,\n onSearchChange: setSearch,\n sort: sortString,\n onSortChange: handleSortChange,\n sortOptions,\n ...(statusFieldName && statusOptions.length > 0\n ? {\n statusChips: {\n options: statusOptions,\n selected: statusValue,\n onChange: setStatusValue,\n },\n }\n : {}),\n }),\n [\n search,\n sortString,\n handleSortChange,\n sortOptions,\n statusFieldName,\n statusOptions,\n statusValue,\n ],\n );\n\n return {\n data: filteredData,\n // Controlled mode: `filteredData` is one server page, so its length is\n // not the record count — the server's total is.\n total: controlled ? rawTotal : filteredData.length,\n columns: resolvedColumns,\n tableColumns,\n filterConfig,\n viewMode,\n setViewMode,\n search,\n setSearch,\n sort,\n setSort,\n statusValue,\n setStatusValue,\n selectedItem,\n setSelectedItem,\n detailOpen,\n setDetailOpen,\n handleRowClick,\n titleField,\n subtitleField,\n statusOptions,\n hasActiveFilters,\n clearFilters,\n };\n}\n"]}
|
|
@@ -6,8 +6,8 @@ import { V as ViewMode } from '../../view-toggle-CahQ87PW.js';
|
|
|
6
6
|
export { a as ViewToggle } from '../../view-toggle-CahQ87PW.js';
|
|
7
7
|
import { LucideIcon } from 'lucide-react';
|
|
8
8
|
import { Density } from '../../hooks/use-view-settings.js';
|
|
9
|
-
import { R as ResourceActions, a as ResourceColumnDef, D as DataTable } from '../../use-resource-page-
|
|
10
|
-
export { c as DataTableColumn, d as DataTableProps, L as LinkCell, T as ThumbnailCell } from '../../use-resource-page-
|
|
9
|
+
import { R as ResourceActions, a as ResourceColumnDef, D as DataTable } from '../../use-resource-page-Dd2VUv30.js';
|
|
10
|
+
export { c as DataTableColumn, d as DataTableProps, L as LinkCell, T as ThumbnailCell } from '../../use-resource-page-Dd2VUv30.js';
|
|
11
11
|
export { EmptyState } from '../patterns/empty-state.js';
|
|
12
12
|
export { V as ViewSettings } from '../../view-settings-kHKCHMjj.js';
|
|
13
13
|
import { L as LinkComponent, N as NavigateFn } from '../../navigation-DBwcZ6vb.js';
|
|
@@ -512,6 +512,14 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
512
512
|
subtitle?: string;
|
|
513
513
|
icon?: LucideIcon;
|
|
514
514
|
columns?: Array<string | ResourceColumnDef<T>>;
|
|
515
|
+
/**
|
|
516
|
+
* A row's identity, for React keys and bulk selection. Defaults to
|
|
517
|
+
* `String(item.id)`. Supply it for rows keyed some other way — a composite
|
|
518
|
+
* key (`encodeRecordKey` from `@marktiderman/genesis-core/schema`), or a
|
|
519
|
+
* table whose key column isn't called `id`. Detail `route` mode still
|
|
520
|
+
* navigates by `item.id`.
|
|
521
|
+
*/
|
|
522
|
+
getKey?: (item: T) => string;
|
|
515
523
|
/** How row-click detail is shown. Default "panel". */
|
|
516
524
|
detail?: "panel" | "modal" | "route" | "none";
|
|
517
525
|
detailWidth?: "sm" | "md" | "lg";
|
|
@@ -27,7 +27,7 @@ import '../../chunk-F47U5KNW.js';
|
|
|
27
27
|
import { DetailPage } from '../../chunk-AFZ76Q26.js';
|
|
28
28
|
import '../../chunk-D2LIXA2Y.js';
|
|
29
29
|
import '../../chunk-AVFZ4P4B.js';
|
|
30
|
-
import { titleCase, useResourcePage } from '../../chunk-
|
|
30
|
+
import { titleCase, useResourcePage } from '../../chunk-IXGAEECT.js';
|
|
31
31
|
export { useSavedViews } from '../../chunk-GCQFVMPI.js';
|
|
32
32
|
import '../../chunk-FTTNGGLA.js';
|
|
33
33
|
import { useViewSettings } from '../../chunk-OZ7ZAYCS.js';
|
|
@@ -61,7 +61,7 @@ function DataPageShell({
|
|
|
61
61
|
emptyMessage,
|
|
62
62
|
emptyAction,
|
|
63
63
|
hasError = false,
|
|
64
|
-
errorMessage,
|
|
64
|
+
errorMessage: errorMessage2,
|
|
65
65
|
onRetry,
|
|
66
66
|
count,
|
|
67
67
|
totalCount,
|
|
@@ -148,7 +148,7 @@ function DataPageShell({
|
|
|
148
148
|
) : hasError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
149
149
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-10 w-10 text-destructive mb-3" }),
|
|
150
150
|
/* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children: "Something went wrong" }),
|
|
151
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children:
|
|
151
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: errorMessage2 || `Failed to load ${entity}s. Please try again.` }),
|
|
152
152
|
onRetry && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
|
|
153
153
|
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-1 h-4 w-4" }),
|
|
154
154
|
" Retry"
|
|
@@ -639,6 +639,9 @@ function getCellClasses(col) {
|
|
|
639
639
|
const hide = col.hideBelow ? col.hideBelow === "sm" ? "hidden sm:table-cell" : col.hideBelow === "md" ? "hidden md:table-cell" : col.hideBelow === "lg" ? "hidden lg:table-cell" : "hidden xl:table-cell" : "";
|
|
640
640
|
return cn(col.width, hide);
|
|
641
641
|
}
|
|
642
|
+
function errorMessage(error) {
|
|
643
|
+
return error instanceof Error ? error.message : String(error);
|
|
644
|
+
}
|
|
642
645
|
function EditableCell({
|
|
643
646
|
item,
|
|
644
647
|
column,
|
|
@@ -646,13 +649,49 @@ function EditableCell({
|
|
|
646
649
|
}) {
|
|
647
650
|
const [editing, setEditing] = useState(false);
|
|
648
651
|
const [draft, setDraft] = useState(String(value ?? ""));
|
|
652
|
+
const [saving, setSaving] = useState(false);
|
|
653
|
+
const [error, setError] = useState(null);
|
|
654
|
+
const inFlight = useRef(false);
|
|
655
|
+
const failedDraft = useRef(null);
|
|
649
656
|
const editable = column.editable;
|
|
657
|
+
const close = useCallback(() => {
|
|
658
|
+
setEditing(false);
|
|
659
|
+
setError(null);
|
|
660
|
+
failedDraft.current = null;
|
|
661
|
+
}, []);
|
|
650
662
|
const commit = useCallback(
|
|
651
663
|
(v) => {
|
|
652
|
-
|
|
653
|
-
|
|
664
|
+
if (inFlight.current) return;
|
|
665
|
+
let result;
|
|
666
|
+
try {
|
|
667
|
+
result = editable.onSave(item, v);
|
|
668
|
+
} catch (e) {
|
|
669
|
+
setError(errorMessage(e));
|
|
670
|
+
failedDraft.current = v;
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (!result || typeof result.then !== "function") {
|
|
674
|
+
close();
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
inFlight.current = true;
|
|
678
|
+
setSaving(true);
|
|
679
|
+
setError(null);
|
|
680
|
+
result.then(
|
|
681
|
+
() => {
|
|
682
|
+
inFlight.current = false;
|
|
683
|
+
setSaving(false);
|
|
684
|
+
close();
|
|
685
|
+
},
|
|
686
|
+
(e) => {
|
|
687
|
+
inFlight.current = false;
|
|
688
|
+
setSaving(false);
|
|
689
|
+
setError(errorMessage(e));
|
|
690
|
+
failedDraft.current = v;
|
|
691
|
+
}
|
|
692
|
+
);
|
|
654
693
|
},
|
|
655
|
-
[editable, item]
|
|
694
|
+
[editable, item, close]
|
|
656
695
|
);
|
|
657
696
|
if (!editing) {
|
|
658
697
|
return /* @__PURE__ */ jsx(
|
|
@@ -662,32 +701,80 @@ function EditableCell({
|
|
|
662
701
|
onClick: (e) => {
|
|
663
702
|
e.stopPropagation();
|
|
664
703
|
setDraft(String(value ?? ""));
|
|
704
|
+
setError(null);
|
|
705
|
+
failedDraft.current = null;
|
|
665
706
|
setEditing(true);
|
|
666
707
|
},
|
|
667
708
|
children: column.render ? column.render(item, value) : String(value ?? "\u2014")
|
|
668
709
|
}
|
|
669
710
|
);
|
|
670
711
|
}
|
|
712
|
+
const feedback = error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 text-[11px] leading-snug text-destructive", children: error }) : null;
|
|
671
713
|
if (editable.type === "select" && editable.options) {
|
|
672
|
-
return /* @__PURE__ */
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
714
|
+
return /* @__PURE__ */ jsxs(
|
|
715
|
+
"div",
|
|
716
|
+
{
|
|
717
|
+
onClick: (e) => e.stopPropagation(),
|
|
718
|
+
onKeyDown: (e) => {
|
|
719
|
+
if (e.key === "Escape" && !inFlight.current) close();
|
|
720
|
+
},
|
|
721
|
+
"aria-busy": saving || void 0,
|
|
722
|
+
children: [
|
|
723
|
+
/* @__PURE__ */ jsxs(
|
|
724
|
+
Select,
|
|
725
|
+
{
|
|
726
|
+
value: draft,
|
|
727
|
+
disabled: saving,
|
|
728
|
+
onValueChange: (v) => {
|
|
729
|
+
setDraft(v);
|
|
730
|
+
commit(v);
|
|
731
|
+
},
|
|
732
|
+
children: [
|
|
733
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 text-xs w-28", "aria-invalid": error ? true : void 0, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
734
|
+
/* @__PURE__ */ jsx(SelectContent, { children: editable.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt, children: opt }, opt)) })
|
|
735
|
+
]
|
|
736
|
+
}
|
|
737
|
+
),
|
|
738
|
+
feedback,
|
|
739
|
+
error && /* @__PURE__ */ jsx(
|
|
740
|
+
"button",
|
|
741
|
+
{
|
|
742
|
+
type: "button",
|
|
743
|
+
className: "mt-0.5 text-[11px] text-muted-foreground underline-offset-2 hover:underline",
|
|
744
|
+
onClick: close,
|
|
745
|
+
children: "Cancel"
|
|
746
|
+
}
|
|
747
|
+
)
|
|
748
|
+
]
|
|
688
749
|
}
|
|
689
|
-
|
|
690
|
-
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
return /* @__PURE__ */ jsxs("div", { onClick: (e) => e.stopPropagation(), "aria-busy": saving || void 0, children: [
|
|
753
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
754
|
+
/* @__PURE__ */ jsx(
|
|
755
|
+
Input,
|
|
756
|
+
{
|
|
757
|
+
autoFocus: true,
|
|
758
|
+
className: cn("h-7 text-xs w-full", saving && "pr-7 opacity-70"),
|
|
759
|
+
value: draft,
|
|
760
|
+
readOnly: saving,
|
|
761
|
+
"aria-invalid": error ? true : void 0,
|
|
762
|
+
onChange: (e) => setDraft(e.target.value),
|
|
763
|
+
onBlur: () => {
|
|
764
|
+
if (inFlight.current) return;
|
|
765
|
+
if (failedDraft.current === draft) close();
|
|
766
|
+
else commit(draft);
|
|
767
|
+
},
|
|
768
|
+
onKeyDown: (e) => {
|
|
769
|
+
if (e.key === "Enter") commit(draft);
|
|
770
|
+
if (e.key === "Escape" && !inFlight.current) close();
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
),
|
|
774
|
+
saving && /* @__PURE__ */ jsx(Spinner, { className: "absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2", "aria-label": "Saving" })
|
|
775
|
+
] }),
|
|
776
|
+
feedback
|
|
777
|
+
] });
|
|
691
778
|
}
|
|
692
779
|
function ResizeHandle({
|
|
693
780
|
onResize,
|
|
@@ -2036,15 +2123,15 @@ function ResourceFormFieldRenderer({
|
|
|
2036
2123
|
name: field.key,
|
|
2037
2124
|
control,
|
|
2038
2125
|
render: ({ field: f, fieldState }) => {
|
|
2039
|
-
const
|
|
2126
|
+
const errorMessage2 = fieldState.error?.message;
|
|
2040
2127
|
const ariaProps = {
|
|
2041
|
-
"aria-invalid": !!
|
|
2042
|
-
"aria-describedby":
|
|
2128
|
+
"aria-invalid": !!errorMessage2 || void 0,
|
|
2129
|
+
"aria-describedby": errorMessage2 ? `${field.key}-error` : field.description ? `${field.key}-desc` : void 0,
|
|
2043
2130
|
"aria-required": field.required || void 0
|
|
2044
2131
|
};
|
|
2045
2132
|
if (fieldType === "switch") {
|
|
2046
2133
|
return /* @__PURE__ */ jsxs(FormField, { className: "flex flex-row items-center justify-between rounded-lg border p-4", children: [
|
|
2047
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: /* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2134
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: /* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, children: label }) }),
|
|
2048
2135
|
/* @__PURE__ */ jsx(
|
|
2049
2136
|
Switch,
|
|
2050
2137
|
{
|
|
@@ -2068,14 +2155,14 @@ function ResourceFormFieldRenderer({
|
|
|
2068
2155
|
}
|
|
2069
2156
|
),
|
|
2070
2157
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1 leading-none", children: [
|
|
2071
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2072
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2158
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, children: label }),
|
|
2159
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2073
2160
|
] })
|
|
2074
2161
|
] });
|
|
2075
2162
|
}
|
|
2076
2163
|
if (fieldType === "select" && field.options) {
|
|
2077
2164
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2078
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2165
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2079
2166
|
/* @__PURE__ */ jsxs(
|
|
2080
2167
|
Select,
|
|
2081
2168
|
{
|
|
@@ -2095,12 +2182,12 @@ function ResourceFormFieldRenderer({
|
|
|
2095
2182
|
}
|
|
2096
2183
|
),
|
|
2097
2184
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2098
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2185
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2099
2186
|
] });
|
|
2100
2187
|
}
|
|
2101
2188
|
if (fieldType === "relation" && field.relation) {
|
|
2102
2189
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2103
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2190
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2104
2191
|
/* @__PURE__ */ jsx(
|
|
2105
2192
|
RelationField,
|
|
2106
2193
|
{
|
|
@@ -2111,12 +2198,12 @@ function ResourceFormFieldRenderer({
|
|
|
2111
2198
|
}
|
|
2112
2199
|
),
|
|
2113
2200
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2114
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2201
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2115
2202
|
] });
|
|
2116
2203
|
}
|
|
2117
2204
|
if (fieldType === "file" || fieldType === "image") {
|
|
2118
2205
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2119
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2206
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2120
2207
|
/* @__PURE__ */ jsx(
|
|
2121
2208
|
FileUploadField,
|
|
2122
2209
|
{
|
|
@@ -2127,12 +2214,12 @@ function ResourceFormFieldRenderer({
|
|
|
2127
2214
|
}
|
|
2128
2215
|
),
|
|
2129
2216
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2130
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2217
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2131
2218
|
] });
|
|
2132
2219
|
}
|
|
2133
2220
|
if (fieldType === "textarea") {
|
|
2134
2221
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2135
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2222
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2136
2223
|
/* @__PURE__ */ jsx(
|
|
2137
2224
|
Textarea,
|
|
2138
2225
|
{
|
|
@@ -2146,12 +2233,12 @@ function ResourceFormFieldRenderer({
|
|
|
2146
2233
|
}
|
|
2147
2234
|
),
|
|
2148
2235
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2149
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2236
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2150
2237
|
] });
|
|
2151
2238
|
}
|
|
2152
2239
|
if (fieldType === "number") {
|
|
2153
2240
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2154
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2241
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2155
2242
|
/* @__PURE__ */ jsx(
|
|
2156
2243
|
Input,
|
|
2157
2244
|
{
|
|
@@ -2170,11 +2257,11 @@ function ResourceFormFieldRenderer({
|
|
|
2170
2257
|
}
|
|
2171
2258
|
),
|
|
2172
2259
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2173
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2260
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2174
2261
|
] });
|
|
2175
2262
|
}
|
|
2176
2263
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2177
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2264
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2178
2265
|
/* @__PURE__ */ jsx(
|
|
2179
2266
|
Input,
|
|
2180
2267
|
{
|
|
@@ -2188,7 +2275,7 @@ function ResourceFormFieldRenderer({
|
|
|
2188
2275
|
}
|
|
2189
2276
|
),
|
|
2190
2277
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2191
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2278
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2192
2279
|
] });
|
|
2193
2280
|
}
|
|
2194
2281
|
}
|
|
@@ -2475,6 +2562,7 @@ function ResourcePage(props) {
|
|
|
2475
2562
|
subtitle,
|
|
2476
2563
|
icon,
|
|
2477
2564
|
columns: columnsProp,
|
|
2565
|
+
getKey: getKeyProp,
|
|
2478
2566
|
detail = "panel",
|
|
2479
2567
|
detailWidth = "md",
|
|
2480
2568
|
titleField: titleFieldProp,
|
|
@@ -2495,7 +2583,7 @@ function ResourcePage(props) {
|
|
|
2495
2583
|
onSearchChange,
|
|
2496
2584
|
loading: loadingProp,
|
|
2497
2585
|
error: errorProp,
|
|
2498
|
-
errorMessage,
|
|
2586
|
+
errorMessage: errorMessage2,
|
|
2499
2587
|
onRetry: onRetryProp,
|
|
2500
2588
|
savedViews,
|
|
2501
2589
|
onLoadView,
|
|
@@ -2592,6 +2680,10 @@ function ResourcePage(props) {
|
|
|
2592
2680
|
resourceHook.remove,
|
|
2593
2681
|
resourceHook.list
|
|
2594
2682
|
]);
|
|
2683
|
+
const keyOf = useCallback(
|
|
2684
|
+
(item) => getKeyProp ? getKeyProp(item) : String(item.id ?? ""),
|
|
2685
|
+
[getKeyProp]
|
|
2686
|
+
);
|
|
2595
2687
|
const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
|
|
2596
2688
|
const handleToggleSelect = useCallback((id) => {
|
|
2597
2689
|
setSelectedIds((prev) => {
|
|
@@ -2604,13 +2696,9 @@ function ResourcePage(props) {
|
|
|
2604
2696
|
const handleToggleAll = useCallback(() => {
|
|
2605
2697
|
setSelectedIds((prev) => {
|
|
2606
2698
|
if (prev.size === page.data.length) return /* @__PURE__ */ new Set();
|
|
2607
|
-
return new Set(
|
|
2608
|
-
page.data.map(
|
|
2609
|
-
(item) => String(item.id ?? "")
|
|
2610
|
-
)
|
|
2611
|
-
);
|
|
2699
|
+
return new Set(page.data.map(keyOf));
|
|
2612
2700
|
});
|
|
2613
|
-
}, [page.data]);
|
|
2701
|
+
}, [page.data, keyOf]);
|
|
2614
2702
|
const handleClearSelection = useCallback(() => {
|
|
2615
2703
|
setSelectedIds(/* @__PURE__ */ new Set());
|
|
2616
2704
|
}, []);
|
|
@@ -2646,6 +2734,8 @@ function ResourcePage(props) {
|
|
|
2646
2734
|
header: col.header ?? col.key,
|
|
2647
2735
|
sortable: col.sortable ?? true,
|
|
2648
2736
|
hideBelow: col.hideBelow,
|
|
2737
|
+
editable: col.editable,
|
|
2738
|
+
accessor: col.accessor,
|
|
2649
2739
|
render: col.render ? (item, value) => col.render(item, value, actions) : void 0
|
|
2650
2740
|
})) : page.tableColumns;
|
|
2651
2741
|
if (!isServerControlled) return base;
|
|
@@ -2765,9 +2855,7 @@ function ResourcePage(props) {
|
|
|
2765
2855
|
onToggleSelect: hasBulkSelect ? (index) => {
|
|
2766
2856
|
const item = page.data[index];
|
|
2767
2857
|
if (item) {
|
|
2768
|
-
handleToggleSelect(
|
|
2769
|
-
String(item.id ?? "")
|
|
2770
|
-
);
|
|
2858
|
+
handleToggleSelect(keyOf(item));
|
|
2771
2859
|
}
|
|
2772
2860
|
} : void 0,
|
|
2773
2861
|
// Supplied only while there is an open detail to close — the hook
|
|
@@ -2881,7 +2969,7 @@ function ResourcePage(props) {
|
|
|
2881
2969
|
isLoading,
|
|
2882
2970
|
hasError,
|
|
2883
2971
|
isEmpty,
|
|
2884
|
-
errorMessage,
|
|
2972
|
+
errorMessage: errorMessage2,
|
|
2885
2973
|
onRetry: isClientMode ? () => resourceHook.list.refetch() : onRetryProp,
|
|
2886
2974
|
viewMode: page.viewMode,
|
|
2887
2975
|
onViewModeChange: page.setViewMode,
|
|
@@ -2946,7 +3034,7 @@ function ResourcePage(props) {
|
|
|
2946
3034
|
items: page.data,
|
|
2947
3035
|
viewMode: page.viewMode,
|
|
2948
3036
|
gridCols: gridColsProp,
|
|
2949
|
-
getKey:
|
|
3037
|
+
getKey: keyOf,
|
|
2950
3038
|
focusedIndex: keyboardNav.focusedIndex,
|
|
2951
3039
|
keyboardContainerRef: keyboardNav.containerRef,
|
|
2952
3040
|
renderTable: (items) => renderTableProp ? renderTableProp(items) : renderSlot(
|
|
@@ -2955,7 +3043,7 @@ function ResourcePage(props) {
|
|
|
2955
3043
|
{
|
|
2956
3044
|
items,
|
|
2957
3045
|
columns: tableColumns,
|
|
2958
|
-
getKey:
|
|
3046
|
+
getKey: keyOf,
|
|
2959
3047
|
onRowClick: detail !== "none" ? handleRowClick : void 0,
|
|
2960
3048
|
selectable: !!bulkActions && bulkActions.length > 0,
|
|
2961
3049
|
selected: selectedIds,
|