@pattern-stack/frontend-patterns 0.2.0-alpha.4 → 0.2.0-alpha.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/dist/atoms/components/data/DataTable/DataTable.d.ts +1 -1
- package/dist/atoms/components/data/DataTable/DataTable.d.ts.map +1 -1
- package/dist/atoms/components/data/DataTable/DataTable.types.d.ts +28 -0
- package/dist/atoms/components/data/DataTable/DataTable.types.d.ts.map +1 -1
- package/dist/atoms/hooks/index.d.ts +1 -0
- package/dist/atoms/hooks/index.d.ts.map +1 -1
- package/dist/atoms/hooks/useEntityDetail.d.ts +43 -0
- package/dist/atoms/hooks/useEntityDetail.d.ts.map +1 -0
- package/dist/atoms/primitives/table.d.ts.map +1 -1
- package/dist/atoms/services/api/client.d.ts +12 -2
- package/dist/atoms/services/api/client.d.ts.map +1 -1
- package/dist/atoms/services/auth-service.d.ts +15 -0
- package/dist/atoms/services/auth-service.d.ts.map +1 -1
- package/dist/atoms/services/index.d.ts +2 -2
- package/dist/atoms/services/index.d.ts.map +1 -1
- package/dist/atoms/types/auth.d.ts +38 -2
- package/dist/atoms/types/auth.d.ts.map +1 -1
- package/dist/atoms/types/ui-config.d.ts +11 -0
- package/dist/atoms/types/ui-config.d.ts.map +1 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts +3 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts.map +1 -1
- package/dist/features/auth/providers/NoAuthProvider.d.ts +17 -0
- package/dist/features/auth/providers/NoAuthProvider.d.ts.map +1 -0
- package/dist/features/auth/providers/index.d.ts +1 -0
- package/dist/features/auth/providers/index.d.ts.map +1 -1
- package/dist/frontend-patterns.css +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +350 -79
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +348 -77
- package/dist/index.js.map +1 -1
- package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts +61 -0
- package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts.map +1 -0
- package/dist/molecules/layout/FieldGrid/index.d.ts +2 -0
- package/dist/molecules/layout/FieldGrid/index.d.ts.map +1 -0
- package/dist/molecules/layout/index.d.ts +1 -0
- package/dist/molecules/layout/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts.map +1 -1
- package/package.json +4 -5
package/dist/index.es.js
CHANGED
|
@@ -15,9 +15,9 @@ import { cva } from "class-variance-authority";
|
|
|
15
15
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
16
16
|
import { useQuery, useQueryClient, useMutation, QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
17
17
|
import { Slot } from "@radix-ui/react-slot";
|
|
18
|
-
import { createPortal } from "react-dom";
|
|
18
|
+
import { createPortal, flushSync } from "react-dom";
|
|
19
19
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
20
|
-
import { useLocation, useNavigate, useSearchParams, Outlet, BrowserRouter, Routes, Route } from "react-router-dom";
|
|
20
|
+
import { useLocation, useNavigate, useSearchParams, Outlet, Navigate, BrowserRouter, Routes, Route } from "react-router-dom";
|
|
21
21
|
import { createRoot } from "react-dom/client";
|
|
22
22
|
const APP_NAME = "Design System Showcase";
|
|
23
23
|
const API_ENDPOINTS = {
|
|
@@ -34,8 +34,8 @@ const UI_CONFIG = {
|
|
|
34
34
|
TOAST_DURATION: 5e3
|
|
35
35
|
};
|
|
36
36
|
const env = {
|
|
37
|
-
API_BASE_URL: "
|
|
38
|
-
OPENAPI_URL: "
|
|
37
|
+
API_BASE_URL: "",
|
|
38
|
+
OPENAPI_URL: "/openapi.json",
|
|
39
39
|
NODE_ENV: "development",
|
|
40
40
|
IS_DEVELOPMENT: false,
|
|
41
41
|
IS_PRODUCTION: false,
|
|
@@ -2009,16 +2009,18 @@ function renderField(value, fieldName, fieldType, breakpoint, item, customRender
|
|
|
2009
2009
|
function cn(...inputs) {
|
|
2010
2010
|
return twMerge(clsx(inputs));
|
|
2011
2011
|
}
|
|
2012
|
-
const API_BASE_URL = "http://localhost:8080";
|
|
2013
2012
|
let globalAuthService = null;
|
|
2014
2013
|
function setGlobalAuthService(authService) {
|
|
2015
2014
|
globalAuthService = authService;
|
|
2016
2015
|
}
|
|
2016
|
+
function getGlobalAuthService() {
|
|
2017
|
+
return globalAuthService;
|
|
2018
|
+
}
|
|
2017
2019
|
class ApiClient {
|
|
2018
2020
|
constructor(config = {}) {
|
|
2019
2021
|
__publicField(this, "instance");
|
|
2020
2022
|
this.instance = axios.create({
|
|
2021
|
-
baseURL: config.baseURL ||
|
|
2023
|
+
baseURL: config.baseURL || "",
|
|
2022
2024
|
timeout: config.timeout || 1e4,
|
|
2023
2025
|
headers: {
|
|
2024
2026
|
"Content-Type": "application/json",
|
|
@@ -2058,11 +2060,12 @@ class ApiClient {
|
|
|
2058
2060
|
this.instance.interceptors.response.use(
|
|
2059
2061
|
(response) => response,
|
|
2060
2062
|
async (error) => {
|
|
2061
|
-
var _a;
|
|
2062
|
-
|
|
2063
|
+
var _a, _b, _c, _d;
|
|
2064
|
+
const status = (_a = error.response) == null ? void 0 : _a.status;
|
|
2065
|
+
if (status === 401 || status === 403) {
|
|
2063
2066
|
if (globalAuthService) {
|
|
2064
2067
|
const tokenData = globalAuthService.getTokenData();
|
|
2065
|
-
if ((tokenData == null ? void 0 : tokenData.refreshToken) && !error.config._retry) {
|
|
2068
|
+
if (status === 401 && (tokenData == null ? void 0 : tokenData.refreshToken) && !error.config._retry) {
|
|
2066
2069
|
error.config._retry = true;
|
|
2067
2070
|
try {
|
|
2068
2071
|
await globalAuthService.refreshToken();
|
|
@@ -2070,11 +2073,12 @@ class ApiClient {
|
|
|
2070
2073
|
error.config.headers.Authorization = `Bearer ${newTokenData == null ? void 0 : newTokenData.token}`;
|
|
2071
2074
|
return this.instance.request(error.config);
|
|
2072
2075
|
} catch {
|
|
2073
|
-
globalAuthService.clearAuth();
|
|
2074
|
-
window.location.reload();
|
|
2075
2076
|
}
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2077
|
+
}
|
|
2078
|
+
globalAuthService.clearAuth();
|
|
2079
|
+
const errorInfo = { status, message: (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.detail };
|
|
2080
|
+
const handled = (_d = globalAuthService.onAuthError) == null ? void 0 : _d.call(globalAuthService, errorInfo);
|
|
2081
|
+
if (!handled) {
|
|
2078
2082
|
window.location.reload();
|
|
2079
2083
|
}
|
|
2080
2084
|
} else {
|
|
@@ -2120,6 +2124,9 @@ class ApiClient {
|
|
|
2120
2124
|
return response.data;
|
|
2121
2125
|
}
|
|
2122
2126
|
}
|
|
2127
|
+
function createApiClient(config = {}) {
|
|
2128
|
+
return new ApiClient(config);
|
|
2129
|
+
}
|
|
2123
2130
|
const apiClient = new ApiClient();
|
|
2124
2131
|
function useToast() {
|
|
2125
2132
|
const [toasts, setToasts] = useState([]);
|
|
@@ -2390,7 +2397,7 @@ function useEntityData(entity, options = {}) {
|
|
|
2390
2397
|
const dataQuery = useQuery({
|
|
2391
2398
|
queryKey: [entity, "list", { limit, offset, filters }],
|
|
2392
2399
|
queryFn: () => apiClient.get(
|
|
2393
|
-
`/api/v1/${entity}
|
|
2400
|
+
`/api/v1/${entity}?${dataParams.toString()}`
|
|
2394
2401
|
),
|
|
2395
2402
|
enabled
|
|
2396
2403
|
});
|
|
@@ -2418,6 +2425,40 @@ function useEntityData(entity, options = {}) {
|
|
|
2418
2425
|
refetch
|
|
2419
2426
|
};
|
|
2420
2427
|
}
|
|
2428
|
+
function useEntityDetail(entity, id, options = {}) {
|
|
2429
|
+
var _a;
|
|
2430
|
+
const { view = "detail", sourceId, enabled = true } = options;
|
|
2431
|
+
const metadataParams = new URLSearchParams();
|
|
2432
|
+
metadataParams.set("view", view);
|
|
2433
|
+
if (sourceId) metadataParams.set("source_id", sourceId);
|
|
2434
|
+
const dataQuery = useQuery({
|
|
2435
|
+
queryKey: [entity, "detail", id],
|
|
2436
|
+
queryFn: () => apiClient.get(`/api/v1/${entity}/${id}`),
|
|
2437
|
+
enabled: enabled && !!id
|
|
2438
|
+
});
|
|
2439
|
+
const metadataQuery = useQuery({
|
|
2440
|
+
queryKey: [entity, "metadata", { view, sourceId }],
|
|
2441
|
+
queryFn: () => apiClient.get(
|
|
2442
|
+
`/api/v1/${entity}/fields/metadata?${metadataParams.toString()}`
|
|
2443
|
+
),
|
|
2444
|
+
enabled,
|
|
2445
|
+
staleTime: 5 * 60 * 1e3
|
|
2446
|
+
// 5 minutes
|
|
2447
|
+
});
|
|
2448
|
+
const refetch = () => {
|
|
2449
|
+
dataQuery.refetch();
|
|
2450
|
+
metadataQuery.refetch();
|
|
2451
|
+
};
|
|
2452
|
+
return {
|
|
2453
|
+
data: dataQuery.data ?? null,
|
|
2454
|
+
fields: ((_a = metadataQuery.data) == null ? void 0 : _a.columns) ?? [],
|
|
2455
|
+
isLoading: dataQuery.isLoading || metadataQuery.isLoading,
|
|
2456
|
+
isLoadingData: dataQuery.isLoading,
|
|
2457
|
+
isLoadingMetadata: metadataQuery.isLoading,
|
|
2458
|
+
error: dataQuery.error ?? metadataQuery.error ?? null,
|
|
2459
|
+
refetch
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2421
2462
|
const buttonVariants = cva(
|
|
2422
2463
|
"inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98]",
|
|
2423
2464
|
{
|
|
@@ -4108,7 +4149,9 @@ const TableHead$1 = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
4108
4149
|
{
|
|
4109
4150
|
ref,
|
|
4110
4151
|
className: cn(
|
|
4111
|
-
"h-12 px-4 xs:px-2 2xs:px-1 text-left align-middle font-semibold text-gray-700 text-xs uppercase tracking-wider
|
|
4152
|
+
"h-12 px-4 xs:px-2 2xs:px-1 text-left align-middle font-semibold text-gray-700 text-xs uppercase tracking-wider",
|
|
4153
|
+
// Compact padding for checkbox columns
|
|
4154
|
+
"[&:has(input[type=checkbox])]:px-2 [&:has(input[type=checkbox])]:w-8",
|
|
4112
4155
|
className
|
|
4113
4156
|
),
|
|
4114
4157
|
...props
|
|
@@ -4120,7 +4163,9 @@ const TableCell$1 = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
4120
4163
|
{
|
|
4121
4164
|
ref,
|
|
4122
4165
|
className: cn(
|
|
4123
|
-
"p-4 xs:p-2 2xs:p-1 align-middle
|
|
4166
|
+
"p-4 xs:p-2 2xs:p-1 align-middle",
|
|
4167
|
+
// Compact padding for checkbox columns
|
|
4168
|
+
"[&:has(input[type=checkbox])]:px-2 [&:has(input[type=checkbox])]:w-8",
|
|
4124
4169
|
className
|
|
4125
4170
|
),
|
|
4126
4171
|
...props
|
|
@@ -4170,7 +4215,12 @@ function DataTable({
|
|
|
4170
4215
|
ui,
|
|
4171
4216
|
error = null,
|
|
4172
4217
|
errorTitle,
|
|
4173
|
-
errorRetry
|
|
4218
|
+
errorRetry,
|
|
4219
|
+
selectable = false,
|
|
4220
|
+
selectedIds,
|
|
4221
|
+
onSelectionChange,
|
|
4222
|
+
getRowId,
|
|
4223
|
+
selectAllMode = "page"
|
|
4174
4224
|
}) {
|
|
4175
4225
|
const [searchTerm, setSearchTerm] = useState("");
|
|
4176
4226
|
const [sortColumn, setSortColumn] = useState(null);
|
|
@@ -4233,6 +4283,85 @@ function DataTable({
|
|
|
4233
4283
|
return true;
|
|
4234
4284
|
});
|
|
4235
4285
|
}, [normalizedColumns, currentBreakpoint, responsive]);
|
|
4286
|
+
const defaultGetRowId = useCallback((row) => {
|
|
4287
|
+
if ("id" in row && typeof row.id === "string") return row.id;
|
|
4288
|
+
if ("id" in row && typeof row.id === "number") return String(row.id);
|
|
4289
|
+
throw new Error("DataTable: selectable requires getRowId prop or row.id field");
|
|
4290
|
+
}, []);
|
|
4291
|
+
const effectiveGetRowId = getRowId ?? defaultGetRowId;
|
|
4292
|
+
const selectableData = selectAllMode === "all" ? sortedData : paginatedData;
|
|
4293
|
+
const selectableIds = useMemo(
|
|
4294
|
+
() => {
|
|
4295
|
+
if (!selectable) return /* @__PURE__ */ new Set();
|
|
4296
|
+
return new Set(selectableData.map((row) => effectiveGetRowId(row)));
|
|
4297
|
+
},
|
|
4298
|
+
[selectable, selectableData, effectiveGetRowId]
|
|
4299
|
+
);
|
|
4300
|
+
const selectedInScope = useMemo(
|
|
4301
|
+
() => {
|
|
4302
|
+
if (!selectable || !selectedIds) return /* @__PURE__ */ new Set();
|
|
4303
|
+
return new Set([...selectedIds].filter((id) => selectableIds.has(id)));
|
|
4304
|
+
},
|
|
4305
|
+
[selectable, selectedIds, selectableIds]
|
|
4306
|
+
);
|
|
4307
|
+
const isAllSelected = selectableIds.size > 0 && selectedInScope.size === selectableIds.size;
|
|
4308
|
+
const isIndeterminate = selectedInScope.size > 0 && selectedInScope.size < selectableIds.size;
|
|
4309
|
+
const handleSelectAll = useCallback(() => {
|
|
4310
|
+
if (!onSelectionChange) return;
|
|
4311
|
+
if (isAllSelected) {
|
|
4312
|
+
const newSelection = new Set([...selectedIds ?? []].filter((id) => !selectableIds.has(id)));
|
|
4313
|
+
onSelectionChange(newSelection);
|
|
4314
|
+
} else {
|
|
4315
|
+
const newSelection = /* @__PURE__ */ new Set([...selectedIds ?? [], ...selectableIds]);
|
|
4316
|
+
onSelectionChange(newSelection);
|
|
4317
|
+
}
|
|
4318
|
+
}, [isAllSelected, selectedIds, selectableIds, onSelectionChange]);
|
|
4319
|
+
const handleSelectRow = useCallback((rowId) => {
|
|
4320
|
+
if (!onSelectionChange) return;
|
|
4321
|
+
const newSelection = new Set(selectedIds);
|
|
4322
|
+
if (newSelection.has(rowId)) {
|
|
4323
|
+
newSelection.delete(rowId);
|
|
4324
|
+
} else {
|
|
4325
|
+
newSelection.add(rowId);
|
|
4326
|
+
}
|
|
4327
|
+
onSelectionChange(newSelection);
|
|
4328
|
+
}, [selectedIds, onSelectionChange]);
|
|
4329
|
+
const selectionColumn = useMemo(() => {
|
|
4330
|
+
return {
|
|
4331
|
+
key: "__selection__",
|
|
4332
|
+
header: /* @__PURE__ */ jsx(
|
|
4333
|
+
Checkbox$1,
|
|
4334
|
+
{
|
|
4335
|
+
checked: isAllSelected,
|
|
4336
|
+
indeterminate: isIndeterminate,
|
|
4337
|
+
onCheckedChange: handleSelectAll,
|
|
4338
|
+
"aria-label": isAllSelected ? "Deselect all rows" : "Select all rows",
|
|
4339
|
+
size: "sm"
|
|
4340
|
+
}
|
|
4341
|
+
),
|
|
4342
|
+
cell: (row) => {
|
|
4343
|
+
const rowId = effectiveGetRowId(row);
|
|
4344
|
+
const isSelected = (selectedIds == null ? void 0 : selectedIds.has(rowId)) ?? false;
|
|
4345
|
+
return /* @__PURE__ */ jsx(
|
|
4346
|
+
Checkbox$1,
|
|
4347
|
+
{
|
|
4348
|
+
checked: isSelected,
|
|
4349
|
+
onCheckedChange: () => handleSelectRow(rowId),
|
|
4350
|
+
"aria-label": isSelected ? `Deselect row ${rowId}` : `Select row ${rowId}`,
|
|
4351
|
+
size: "sm",
|
|
4352
|
+
onClick: (e) => e.stopPropagation()
|
|
4353
|
+
}
|
|
4354
|
+
);
|
|
4355
|
+
},
|
|
4356
|
+
width: "32px",
|
|
4357
|
+
sortable: false,
|
|
4358
|
+
filterable: false
|
|
4359
|
+
};
|
|
4360
|
+
}, [isAllSelected, isIndeterminate, selectedIds, effectiveGetRowId, handleSelectAll, handleSelectRow]);
|
|
4361
|
+
const effectiveColumns = useMemo(() => {
|
|
4362
|
+
if (!selectable) return visibleColumns;
|
|
4363
|
+
return [selectionColumn, ...visibleColumns];
|
|
4364
|
+
}, [selectable, selectionColumn, visibleColumns]);
|
|
4236
4365
|
const handleSort = (columnKey) => {
|
|
4237
4366
|
if (sortColumn === columnKey) {
|
|
4238
4367
|
if (sortDirection === "asc") {
|
|
@@ -4295,8 +4424,8 @@ function DataTable({
|
|
|
4295
4424
|
children: [
|
|
4296
4425
|
showSearch && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("div", { className: "relative flex-1 max-w-sm", children: /* @__PURE__ */ jsx(Skeleton$1, { className: "h-10 w-full" }) }) }),
|
|
4297
4426
|
/* @__PURE__ */ jsx("div", { className: "rounded border overflow-hidden", children: /* @__PURE__ */ jsxs(Table$1, { children: [
|
|
4298
|
-
/* @__PURE__ */ jsx(TableHeader$1, { children: /* @__PURE__ */ jsx(TableRow$1, { children:
|
|
4299
|
-
/* @__PURE__ */ jsx(TableBody$1, { children: Array.from({ length: loadingItemCount }, (_, index) => /* @__PURE__ */ jsx(TableRow$1, { children:
|
|
4427
|
+
/* @__PURE__ */ jsx(TableHeader$1, { children: /* @__PURE__ */ jsx(TableRow$1, { children: effectiveColumns.map((column) => /* @__PURE__ */ jsx(TableHead$1, { style: { width: column.width }, children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Skeleton$1, { className: "h-4 w-20" }) }) }, column.key)) }) }),
|
|
4428
|
+
/* @__PURE__ */ jsx(TableBody$1, { children: Array.from({ length: loadingItemCount }, (_, index) => /* @__PURE__ */ jsx(TableRow$1, { children: effectiveColumns.map((column) => /* @__PURE__ */ jsx(TableCell$1, { children: /* @__PURE__ */ jsx(Skeleton$1, { className: "h-4 w-full max-w-32" }) }, column.key)) }, index)) })
|
|
4300
4429
|
] }) }),
|
|
4301
4430
|
showPagination && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
4302
4431
|
/* @__PURE__ */ jsx(Skeleton$1, { className: "h-4 w-48" }),
|
|
@@ -4358,7 +4487,7 @@ function DataTable({
|
|
|
4358
4487
|
data: paginatedData,
|
|
4359
4488
|
onItemClick: onRowClick
|
|
4360
4489
|
}) }) : /* @__PURE__ */ jsx("div", { className: "rounded border overflow-hidden", children: /* @__PURE__ */ jsxs(Table$1, { children: [
|
|
4361
|
-
/* @__PURE__ */ jsx(TableHeader$1, { children: /* @__PURE__ */ jsx(TableRow$1, { children:
|
|
4490
|
+
/* @__PURE__ */ jsx(TableHeader$1, { children: /* @__PURE__ */ jsx(TableRow$1, { children: effectiveColumns.map((column) => {
|
|
4362
4491
|
const width = column.width;
|
|
4363
4492
|
return /* @__PURE__ */ jsx(
|
|
4364
4493
|
TableHead$1,
|
|
@@ -4381,7 +4510,7 @@ function DataTable({
|
|
|
4381
4510
|
/* @__PURE__ */ jsx(TableBody$1, { children: paginatedData.length === 0 ? /* @__PURE__ */ jsx(TableRow$1, { children: /* @__PURE__ */ jsx(
|
|
4382
4511
|
TableCell$1,
|
|
4383
4512
|
{
|
|
4384
|
-
colSpan:
|
|
4513
|
+
colSpan: effectiveColumns.length,
|
|
4385
4514
|
className: "text-center py-8 text-muted-foreground",
|
|
4386
4515
|
children: emptyMessage
|
|
4387
4516
|
}
|
|
@@ -4393,7 +4522,7 @@ function DataTable({
|
|
|
4393
4522
|
(hover || onRowClick) && "hover:bg-muted/50"
|
|
4394
4523
|
),
|
|
4395
4524
|
onClick: () => onRowClick == null ? void 0 : onRowClick(item),
|
|
4396
|
-
children:
|
|
4525
|
+
children: effectiveColumns.map((column) => /* @__PURE__ */ jsx(TableCell$1, { children: renderCell(column, item) }, column.key))
|
|
4397
4526
|
},
|
|
4398
4527
|
index
|
|
4399
4528
|
)) })
|
|
@@ -9218,6 +9347,55 @@ const ListToolbar = ({
|
|
|
9218
9347
|
}
|
|
9219
9348
|
);
|
|
9220
9349
|
};
|
|
9350
|
+
const FieldGrid = ({
|
|
9351
|
+
data,
|
|
9352
|
+
fields,
|
|
9353
|
+
sections,
|
|
9354
|
+
columns = 2,
|
|
9355
|
+
showLabels = true,
|
|
9356
|
+
compact = false,
|
|
9357
|
+
showEmpty = true,
|
|
9358
|
+
className
|
|
9359
|
+
}) => {
|
|
9360
|
+
const gridCols = {
|
|
9361
|
+
1: "grid-cols-1",
|
|
9362
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
9363
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
9364
|
+
};
|
|
9365
|
+
const renderValue = (field) => {
|
|
9366
|
+
const value = data[field.field];
|
|
9367
|
+
if (value === null || value === void 0 || value === "") {
|
|
9368
|
+
if (!showEmpty) return null;
|
|
9369
|
+
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" });
|
|
9370
|
+
}
|
|
9371
|
+
const format = field.format;
|
|
9372
|
+
return renderField(value, field.field, field.type, "lg", data, void 0, format);
|
|
9373
|
+
};
|
|
9374
|
+
const renderField_ = (field) => {
|
|
9375
|
+
const value = data[field.field];
|
|
9376
|
+
if (!showEmpty && (value === null || value === void 0 || value === "")) {
|
|
9377
|
+
return null;
|
|
9378
|
+
}
|
|
9379
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", compact ? "py-1" : "py-2"), children: [
|
|
9380
|
+
showLabels && /* @__PURE__ */ jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: field.label }),
|
|
9381
|
+
/* @__PURE__ */ jsx("dd", { className: "text-sm text-foreground", children: renderValue(field) })
|
|
9382
|
+
] }, field.field);
|
|
9383
|
+
};
|
|
9384
|
+
const renderFields = (fieldList) => /* @__PURE__ */ jsx("dl", { className: cn("grid gap-x-6", gridCols[columns]), children: fieldList.map(renderField_) });
|
|
9385
|
+
if (sections && sections.length > 0) {
|
|
9386
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-6", className), children: sections.map((section) => /* @__PURE__ */ jsxs(Card, { className: cn(compact ? "p-4" : "p-6"), children: [
|
|
9387
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
9388
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground", children: section.title }),
|
|
9389
|
+
section.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: section.description })
|
|
9390
|
+
] }),
|
|
9391
|
+
renderFields(section.fields)
|
|
9392
|
+
] }, section.title)) });
|
|
9393
|
+
}
|
|
9394
|
+
if (fields && fields.length > 0) {
|
|
9395
|
+
return /* @__PURE__ */ jsx(Card, { className: cn(compact ? "p-4" : "p-6", className), children: renderFields(fields) });
|
|
9396
|
+
}
|
|
9397
|
+
return null;
|
|
9398
|
+
};
|
|
9221
9399
|
const Pagination = ({
|
|
9222
9400
|
currentPage,
|
|
9223
9401
|
totalPages,
|
|
@@ -9897,9 +10075,11 @@ function ProtectedRoute({
|
|
|
9897
10075
|
children,
|
|
9898
10076
|
requirePermission,
|
|
9899
10077
|
requireRole,
|
|
9900
|
-
fallback
|
|
10078
|
+
fallback,
|
|
10079
|
+
loginPath = "/login"
|
|
9901
10080
|
}) {
|
|
9902
10081
|
const { isAuthenticated, isLoading, hasPermission, hasRole } = useAuthContext();
|
|
10082
|
+
const location = useLocation();
|
|
9903
10083
|
if (isLoading) {
|
|
9904
10084
|
return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
9905
10085
|
/* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto" }),
|
|
@@ -9907,7 +10087,7 @@ function ProtectedRoute({
|
|
|
9907
10087
|
] }) });
|
|
9908
10088
|
}
|
|
9909
10089
|
if (!isAuthenticated) {
|
|
9910
|
-
return /* @__PURE__ */ jsx(
|
|
10090
|
+
return /* @__PURE__ */ jsx(Navigate, { to: loginPath, state: { from: location }, replace: true });
|
|
9911
10091
|
}
|
|
9912
10092
|
if (requirePermission && !hasPermission(requirePermission)) {
|
|
9913
10093
|
return fallback || /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
@@ -9934,17 +10114,39 @@ function LogoutButton() {
|
|
|
9934
10114
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: logout, children: "Logout" })
|
|
9935
10115
|
] });
|
|
9936
10116
|
}
|
|
10117
|
+
const AUTH_TOKEN_KEY = "auth_token";
|
|
10118
|
+
const DEFAULT_ENDPOINTS = {
|
|
10119
|
+
login: "/auth/login",
|
|
10120
|
+
register: "/auth/register",
|
|
10121
|
+
refresh: "/auth/refresh",
|
|
10122
|
+
me: "/auth/me",
|
|
10123
|
+
logout: "/auth/logout"
|
|
10124
|
+
};
|
|
9937
10125
|
class AuthService {
|
|
9938
10126
|
constructor(config) {
|
|
9939
10127
|
__publicField(this, "config");
|
|
10128
|
+
__publicField(this, "apiClient");
|
|
9940
10129
|
__publicField(this, "refreshPromise", null);
|
|
10130
|
+
__publicField(this, "endpoints");
|
|
9941
10131
|
this.config = {
|
|
10132
|
+
mode: "pattern-stack",
|
|
9942
10133
|
tokenStorage: "localStorage",
|
|
9943
10134
|
tokenRefreshBuffer: 5,
|
|
9944
10135
|
// 5 minutes before expiry
|
|
9945
10136
|
autoRefresh: true,
|
|
9946
10137
|
...config
|
|
9947
10138
|
};
|
|
10139
|
+
this.endpoints = {
|
|
10140
|
+
...DEFAULT_ENDPOINTS,
|
|
10141
|
+
...config.endpoints
|
|
10142
|
+
};
|
|
10143
|
+
this.apiClient = createApiClient({ baseURL: this.config.apiUrl || "" });
|
|
10144
|
+
}
|
|
10145
|
+
/**
|
|
10146
|
+
* Get the auth mode
|
|
10147
|
+
*/
|
|
10148
|
+
get mode() {
|
|
10149
|
+
return this.config.mode || "pattern-stack";
|
|
9948
10150
|
}
|
|
9949
10151
|
getStorageKey(key) {
|
|
9950
10152
|
return `auth_${key}`;
|
|
@@ -10028,21 +10230,33 @@ class AuthService {
|
|
|
10028
10230
|
this.removeItem("user");
|
|
10029
10231
|
}
|
|
10030
10232
|
async login(credentials) {
|
|
10031
|
-
const response = await apiClient.post(`${this.config.apiUrl}${this.config.endpoints.login}`, credentials);
|
|
10032
10233
|
let token;
|
|
10033
10234
|
let refreshToken;
|
|
10034
10235
|
let expiresIn;
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10236
|
+
let user;
|
|
10237
|
+
if (this.mode === "pattern-stack") {
|
|
10238
|
+
const response = await this.apiClient.post(
|
|
10239
|
+
this.endpoints.login,
|
|
10240
|
+
credentials
|
|
10241
|
+
);
|
|
10242
|
+
token = response.access_token;
|
|
10243
|
+
refreshToken = response.refresh_token;
|
|
10244
|
+
user = response.user;
|
|
10245
|
+
} else if (this.mode === "custom" && this.config.responseMapper) {
|
|
10246
|
+
const rawResponse = await this.apiClient.post(
|
|
10247
|
+
this.endpoints.login,
|
|
10248
|
+
credentials
|
|
10249
|
+
);
|
|
10250
|
+
const mapped = this.config.responseMapper(rawResponse);
|
|
10251
|
+
token = mapped.token;
|
|
10252
|
+
refreshToken = mapped.refreshToken;
|
|
10253
|
+
expiresIn = mapped.expiresIn;
|
|
10254
|
+
user = mapped.user;
|
|
10039
10255
|
} else {
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
expiresIn = oldResponse.expiresIn;
|
|
10256
|
+
throw new Error(
|
|
10257
|
+
`Invalid auth mode: ${this.mode}. Use 'pattern-stack' or 'custom' with responseMapper.`
|
|
10258
|
+
);
|
|
10044
10259
|
}
|
|
10045
|
-
const user = response.user;
|
|
10046
10260
|
const expiresAt = expiresIn ? Date.now() + expiresIn * 1e3 : void 0;
|
|
10047
10261
|
this.setTokenData({ token, refreshToken, expiresAt });
|
|
10048
10262
|
this.setStoredUser(user);
|
|
@@ -10068,21 +10282,27 @@ class AuthService {
|
|
|
10068
10282
|
}
|
|
10069
10283
|
async performTokenRefresh(currentRefreshToken) {
|
|
10070
10284
|
try {
|
|
10071
|
-
const response = await apiClient.post(`${this.config.apiUrl}${this.config.endpoints.refresh}`, {
|
|
10072
|
-
refresh_token: currentRefreshToken
|
|
10073
|
-
});
|
|
10074
10285
|
let token;
|
|
10075
10286
|
let newRefreshToken;
|
|
10076
10287
|
let expiresIn;
|
|
10077
|
-
if ("
|
|
10078
|
-
const
|
|
10079
|
-
|
|
10288
|
+
if (this.mode === "pattern-stack") {
|
|
10289
|
+
const response = await this.apiClient.post(
|
|
10290
|
+
this.endpoints.refresh,
|
|
10291
|
+
{ refresh_token: currentRefreshToken }
|
|
10292
|
+
);
|
|
10293
|
+
token = response.access_token;
|
|
10080
10294
|
newRefreshToken = currentRefreshToken;
|
|
10295
|
+
} else if (this.mode === "custom" && this.config.responseMapper) {
|
|
10296
|
+
const rawResponse = await this.apiClient.post(
|
|
10297
|
+
this.endpoints.refresh,
|
|
10298
|
+
{ refresh_token: currentRefreshToken }
|
|
10299
|
+
);
|
|
10300
|
+
const mapped = this.config.responseMapper(rawResponse);
|
|
10301
|
+
token = mapped.token;
|
|
10302
|
+
newRefreshToken = mapped.refreshToken || currentRefreshToken;
|
|
10303
|
+
expiresIn = mapped.expiresIn;
|
|
10081
10304
|
} else {
|
|
10082
|
-
|
|
10083
|
-
token = oldResponse.token;
|
|
10084
|
-
newRefreshToken = oldResponse.refreshToken;
|
|
10085
|
-
expiresIn = oldResponse.expiresIn;
|
|
10305
|
+
throw new Error(`Invalid auth mode for token refresh: ${this.mode}`);
|
|
10086
10306
|
}
|
|
10087
10307
|
const expiresAt = expiresIn ? Date.now() + expiresIn * 1e3 : void 0;
|
|
10088
10308
|
this.setTokenData({
|
|
@@ -10124,36 +10344,39 @@ class AuthService {
|
|
|
10124
10344
|
const tokenData = this.getTokenData();
|
|
10125
10345
|
if (!(tokenData == null ? void 0 : tokenData.token)) return null;
|
|
10126
10346
|
try {
|
|
10127
|
-
return await apiClient.get(
|
|
10128
|
-
`${this.config.apiUrl}${this.config.endpoints.me}`
|
|
10129
|
-
);
|
|
10347
|
+
return await this.apiClient.get(this.endpoints.me);
|
|
10130
10348
|
} catch {
|
|
10131
10349
|
return null;
|
|
10132
10350
|
}
|
|
10133
10351
|
}
|
|
10134
10352
|
async logout() {
|
|
10135
10353
|
const tokenData = this.getTokenData();
|
|
10136
|
-
if (this.
|
|
10354
|
+
if (this.endpoints.logout && (tokenData == null ? void 0 : tokenData.token)) {
|
|
10137
10355
|
try {
|
|
10138
|
-
await apiClient.post(
|
|
10139
|
-
`${this.config.apiUrl}${this.config.endpoints.logout}`
|
|
10140
|
-
);
|
|
10356
|
+
await this.apiClient.post(this.endpoints.logout);
|
|
10141
10357
|
} catch {
|
|
10142
10358
|
}
|
|
10143
10359
|
}
|
|
10144
10360
|
this.clearAuth();
|
|
10145
10361
|
}
|
|
10362
|
+
/**
|
|
10363
|
+
* Get the configured onAuthError handler
|
|
10364
|
+
*/
|
|
10365
|
+
get onAuthError() {
|
|
10366
|
+
return this.config.onAuthError;
|
|
10367
|
+
}
|
|
10146
10368
|
}
|
|
10147
10369
|
function AuthProvider({
|
|
10148
10370
|
children,
|
|
10149
10371
|
config
|
|
10150
10372
|
}) {
|
|
10373
|
+
var _a;
|
|
10151
10374
|
const [user, setUser] = useState(null);
|
|
10152
10375
|
const [isLoading, setIsLoading] = useState(true);
|
|
10153
10376
|
const [permissions, setPermissions] = useState([]);
|
|
10154
10377
|
const authService = useMemo(() => {
|
|
10155
10378
|
const defaultConfig = {
|
|
10156
|
-
apiUrl:
|
|
10379
|
+
apiUrl: (config == null ? void 0 : config.apiUrl) ?? "",
|
|
10157
10380
|
endpoints: {
|
|
10158
10381
|
login: "/auth/login",
|
|
10159
10382
|
register: "/auth/register",
|
|
@@ -10168,7 +10391,7 @@ function AuthProvider({
|
|
|
10168
10391
|
enabled: false
|
|
10169
10392
|
}
|
|
10170
10393
|
};
|
|
10171
|
-
const mergedConfig = config ? { ...defaultConfig, ...config } : defaultConfig;
|
|
10394
|
+
const mergedConfig = config ? { ...defaultConfig, ...config, apiUrl: config.apiUrl ?? defaultConfig.apiUrl } : defaultConfig;
|
|
10172
10395
|
const service = new AuthService(mergedConfig);
|
|
10173
10396
|
setGlobalAuthService(service);
|
|
10174
10397
|
return service;
|
|
@@ -10215,48 +10438,56 @@ function AuthProvider({
|
|
|
10215
10438
|
};
|
|
10216
10439
|
initAuth();
|
|
10217
10440
|
}, [authService]);
|
|
10218
|
-
const login = async (credentials) => {
|
|
10219
|
-
var _a;
|
|
10441
|
+
const login = useCallback(async (credentials) => {
|
|
10220
10442
|
setIsLoading(true);
|
|
10221
10443
|
try {
|
|
10222
|
-
const
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10444
|
+
const loggedInUser = await authService.login(credentials);
|
|
10445
|
+
flushSync(() => {
|
|
10446
|
+
var _a2;
|
|
10447
|
+
setUser(loggedInUser);
|
|
10448
|
+
if (((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled) && "permissions" in loggedInUser) {
|
|
10449
|
+
const userPermissions = loggedInUser.permissions;
|
|
10450
|
+
setPermissions(
|
|
10451
|
+
Array.isArray(userPermissions) ? userPermissions : []
|
|
10452
|
+
);
|
|
10453
|
+
}
|
|
10454
|
+
setIsLoading(false);
|
|
10455
|
+
});
|
|
10456
|
+
} catch (error) {
|
|
10231
10457
|
setIsLoading(false);
|
|
10458
|
+
throw error;
|
|
10232
10459
|
}
|
|
10233
|
-
};
|
|
10234
|
-
const logout = async () => {
|
|
10460
|
+
}, [authService, (_a = config == null ? void 0 : config.permissions) == null ? void 0 : _a.enabled]);
|
|
10461
|
+
const logout = useCallback(async () => {
|
|
10235
10462
|
setIsLoading(true);
|
|
10236
10463
|
try {
|
|
10237
10464
|
await authService.logout();
|
|
10238
|
-
|
|
10239
|
-
|
|
10465
|
+
flushSync(() => {
|
|
10466
|
+
setUser(null);
|
|
10467
|
+
setPermissions([]);
|
|
10468
|
+
setIsLoading(false);
|
|
10469
|
+
});
|
|
10240
10470
|
} catch (error) {
|
|
10241
10471
|
console.error("Logout error:", error);
|
|
10242
10472
|
authService.clearAuth();
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10473
|
+
flushSync(() => {
|
|
10474
|
+
setUser(null);
|
|
10475
|
+
setPermissions([]);
|
|
10476
|
+
setIsLoading(false);
|
|
10477
|
+
});
|
|
10247
10478
|
}
|
|
10248
|
-
};
|
|
10479
|
+
}, [authService]);
|
|
10249
10480
|
const refreshToken = async () => {
|
|
10250
10481
|
await authService.refreshToken();
|
|
10251
10482
|
};
|
|
10252
10483
|
const hasPermission = (permission) => {
|
|
10253
|
-
var
|
|
10254
|
-
if (!((
|
|
10484
|
+
var _a2;
|
|
10485
|
+
if (!((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled)) return true;
|
|
10255
10486
|
return permissions.includes(permission);
|
|
10256
10487
|
};
|
|
10257
10488
|
const hasRole = (role) => {
|
|
10258
|
-
var
|
|
10259
|
-
if (!((
|
|
10489
|
+
var _a2;
|
|
10490
|
+
if (!((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled)) return true;
|
|
10260
10491
|
if (user && "role" in user) {
|
|
10261
10492
|
return user.role === role;
|
|
10262
10493
|
}
|
|
@@ -10465,6 +10696,32 @@ function MockAuthProvider({
|
|
|
10465
10696
|
};
|
|
10466
10697
|
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
|
|
10467
10698
|
}
|
|
10699
|
+
function NoAuthProvider({ children }) {
|
|
10700
|
+
const noOpLogin = async () => {
|
|
10701
|
+
console.warn("NoAuthProvider: login() called but auth is disabled");
|
|
10702
|
+
};
|
|
10703
|
+
const noOpLogout = async () => {
|
|
10704
|
+
console.warn("NoAuthProvider: logout() called but auth is disabled");
|
|
10705
|
+
};
|
|
10706
|
+
const noOpRefresh = async () => {
|
|
10707
|
+
console.warn("NoAuthProvider: refreshToken() called but auth is disabled");
|
|
10708
|
+
};
|
|
10709
|
+
const value = {
|
|
10710
|
+
user: null,
|
|
10711
|
+
isAuthenticated: true,
|
|
10712
|
+
// Always authenticated in no-auth mode
|
|
10713
|
+
isLoading: false,
|
|
10714
|
+
permissions: [],
|
|
10715
|
+
login: noOpLogin,
|
|
10716
|
+
logout: noOpLogout,
|
|
10717
|
+
refreshToken: noOpRefresh,
|
|
10718
|
+
hasPermission: () => true,
|
|
10719
|
+
// All permissions granted
|
|
10720
|
+
hasRole: () => true
|
|
10721
|
+
// All roles granted
|
|
10722
|
+
};
|
|
10723
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
|
|
10724
|
+
}
|
|
10468
10725
|
const MockAuthContext = createContext(
|
|
10469
10726
|
void 0
|
|
10470
10727
|
);
|
|
@@ -13604,7 +13861,13 @@ function createReactApp(config) {
|
|
|
13604
13861
|
tree = /* @__PURE__ */ jsx(NavigationProvider, { initialNavigation: navigation, children: /* @__PURE__ */ jsx(SidebarProvider, { children: tree }) });
|
|
13605
13862
|
}
|
|
13606
13863
|
if (enableAuth) {
|
|
13607
|
-
|
|
13864
|
+
const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
|
|
13865
|
+
if (authMode === "none") {
|
|
13866
|
+
setGlobalAuthService(null);
|
|
13867
|
+
tree = /* @__PURE__ */ jsx(NoAuthProvider, { children: tree });
|
|
13868
|
+
} else {
|
|
13869
|
+
tree = /* @__PURE__ */ jsx(AuthProvider, { config: auth, children: tree });
|
|
13870
|
+
}
|
|
13608
13871
|
}
|
|
13609
13872
|
if (enableQuery) {
|
|
13610
13873
|
tree = /* @__PURE__ */ jsxs(QueryClientProvider, { client: queryClient, children: [
|
|
@@ -13622,7 +13885,8 @@ function createReactApp(config) {
|
|
|
13622
13885
|
routes.push({ path, component });
|
|
13623
13886
|
},
|
|
13624
13887
|
render() {
|
|
13625
|
-
const
|
|
13888
|
+
const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
|
|
13889
|
+
const shouldProtect = enableAuth && authMode !== "none" && (auth == null ? void 0 : auth.requireAuth) !== false;
|
|
13626
13890
|
const publicPaths = (auth == null ? void 0 : auth.publicPaths) ?? ["/login", "/register", "/forgot-password"];
|
|
13627
13891
|
const isPublicPath = (path) => {
|
|
13628
13892
|
return publicPaths.some((p) => path === p || path.startsWith(p + "/"));
|
|
@@ -16810,12 +17074,14 @@ export {
|
|
|
16810
17074
|
APIDataTemplate,
|
|
16811
17075
|
API_ENDPOINTS,
|
|
16812
17076
|
APP_NAME,
|
|
17077
|
+
AUTH_TOKEN_KEY,
|
|
16813
17078
|
Accordion,
|
|
16814
17079
|
ActivityFeed,
|
|
16815
17080
|
AdminCRUDTemplate,
|
|
16816
17081
|
AdminDashboardTemplate,
|
|
16817
17082
|
AdminDetailTemplate,
|
|
16818
17083
|
Alert,
|
|
17084
|
+
ApiClient,
|
|
16819
17085
|
AppHeader,
|
|
16820
17086
|
AppLayout,
|
|
16821
17087
|
AuthDivider,
|
|
@@ -16882,6 +17148,7 @@ export {
|
|
|
16882
17148
|
EnhancedDataTemplate,
|
|
16883
17149
|
EntityIcon,
|
|
16884
17150
|
ErrorBoundary2 as ErrorBoundary,
|
|
17151
|
+
FieldGrid,
|
|
16885
17152
|
FileUpload,
|
|
16886
17153
|
FormField,
|
|
16887
17154
|
FormGroup,
|
|
@@ -16901,6 +17168,7 @@ export {
|
|
|
16901
17168
|
Modal,
|
|
16902
17169
|
NavMenu,
|
|
16903
17170
|
NavigationProvider,
|
|
17171
|
+
NoAuthProvider,
|
|
16904
17172
|
PageTemplate,
|
|
16905
17173
|
PageTitle,
|
|
16906
17174
|
Pagination,
|
|
@@ -16964,6 +17232,7 @@ export {
|
|
|
16964
17232
|
breakpoints,
|
|
16965
17233
|
cn,
|
|
16966
17234
|
createAPIDataTemplate,
|
|
17235
|
+
createApiClient,
|
|
16967
17236
|
createReactApp,
|
|
16968
17237
|
createSimpleApp,
|
|
16969
17238
|
defaultFieldRenderers,
|
|
@@ -16977,6 +17246,7 @@ export {
|
|
|
16977
17246
|
getContainerHeightClass,
|
|
16978
17247
|
getCurrentBreakpoint,
|
|
16979
17248
|
getFieldType,
|
|
17249
|
+
getGlobalAuthService,
|
|
16980
17250
|
getIcon,
|
|
16981
17251
|
getNavigationItems,
|
|
16982
17252
|
getResponsiveClasses,
|
|
@@ -17000,6 +17270,7 @@ export {
|
|
|
17000
17270
|
useCreateExample,
|
|
17001
17271
|
useDeleteExample,
|
|
17002
17272
|
useEntityData,
|
|
17273
|
+
useEntityDetail,
|
|
17003
17274
|
useErrorBoundary,
|
|
17004
17275
|
useFieldMetadata,
|
|
17005
17276
|
useGetExample,
|