ding-react-admin 2.0.0 → 2.0.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/LICENSE +21 -21
- package/README.md +254 -195
- package/dist/index.css +1 -0
- package/dist/index.js +1773 -1170
- package/dist/src/components/NavMenuLabel.d.ts +8 -0
- package/dist/src/components/NavMenuLabel.d.ts.map +1 -0
- package/dist/src/components/NavMenuSearch.d.ts +9 -0
- package/dist/src/components/NavMenuSearch.d.ts.map +1 -0
- package/dist/src/components/ScrollableArea.d.ts +10 -0
- package/dist/src/components/ScrollableArea.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +3 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/context/AuthProvider.d.ts +2 -0
- package/dist/src/context/AuthProvider.d.ts.map +1 -1
- package/dist/src/crud/FormGlobalErrorsAlert.d.ts +5 -0
- package/dist/src/crud/FormGlobalErrorsAlert.d.ts.map +1 -0
- package/dist/src/crud/ResourceForm.d.ts +1 -4
- package/dist/src/crud/ResourceForm.d.ts.map +1 -1
- package/dist/src/crud/ResourceFormModal.d.ts +7 -1
- package/dist/src/crud/ResourceFormModal.d.ts.map +1 -1
- package/dist/src/crud/ResourceRecordForm.d.ts +27 -0
- package/dist/src/crud/ResourceRecordForm.d.ts.map +1 -0
- package/dist/src/crud/fields/ReferenceField.d.ts +12 -2
- package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
- package/dist/src/crud/fields/ReferenceInputActions.d.ts +16 -0
- package/dist/src/crud/fields/ReferenceInputActions.d.ts.map +1 -0
- package/dist/src/crud/fields/ReferenceManyField.d.ts +10 -1
- package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
- package/dist/src/crud/filters/ReferenceFilter.d.ts +1 -1
- package/dist/src/crud/filters/ReferenceFilter.d.ts.map +1 -1
- package/dist/src/crud/index.d.ts +5 -0
- package/dist/src/crud/index.d.ts.map +1 -1
- package/dist/src/crud/types.d.ts +12 -0
- package/dist/src/crud/types.d.ts.map +1 -1
- package/dist/src/crud/utils/choiceSelectionUtils.d.ts +10 -0
- package/dist/src/crud/utils/choiceSelectionUtils.d.ts.map +1 -0
- package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts +2 -0
- package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts.map +1 -0
- package/dist/src/crud/utils/formErrors.d.ts +18 -7
- package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
- package/dist/src/crud/utils/formErrors.test.d.ts +2 -0
- package/dist/src/crud/utils/formErrors.test.d.ts.map +1 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +1 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -1
- package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts +4 -0
- package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts.map +1 -0
- package/dist/src/crud/utils/toFormData.d.ts +1 -1
- package/dist/src/crud/utils/toFormData.d.ts.map +1 -1
- package/dist/src/crud/utils/uploadReferenceUtils.d.ts +5 -0
- package/dist/src/crud/utils/uploadReferenceUtils.d.ts.map +1 -0
- package/dist/src/crud/utils/useChoices.d.ts +27 -1
- package/dist/src/crud/utils/useChoices.d.ts.map +1 -1
- package/dist/src/crud/utils/useFormRecord.d.ts +9 -7
- package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -1
- package/dist/src/data/parseFormErrorHelpers.d.ts +6 -27
- package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/layouts/AdminLayout.d.ts +1 -1
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
- package/dist/src/layouts/AuthPageLayout.d.ts.map +1 -1
- package/dist/src/layouts/navFilter.d.ts +5 -0
- package/dist/src/layouts/navFilter.d.ts.map +1 -0
- package/dist/src/layouts/navMenuItems.d.ts +6 -0
- package/dist/src/layouts/navMenuItems.d.ts.map +1 -0
- package/dist/src/types.d.ts +12 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +85 -59
- package/src/app/AdminApp.tsx +34 -34
- package/src/components/AuthAlternateLink.tsx +23 -23
- package/src/components/DensitySwitch.tsx +31 -31
- package/src/components/NavMenuLabel.tsx +26 -0
- package/src/components/NavMenuSearch.tsx +76 -0
- package/src/components/ScrollableArea.tsx +46 -0
- package/src/components/ThemeSwitch.tsx +28 -28
- package/src/components/ThemeToolbar.tsx +34 -34
- package/src/components/index.ts +7 -4
- package/src/components/navMenu.css +21 -0
- package/src/components/scrollable.css +28 -0
- package/src/context/AppThemeProvider.tsx +128 -128
- package/src/context/AuthProvider.tsx +89 -77
- package/src/context/DataProvider.tsx +30 -30
- package/src/context/PermissionsProvider.tsx +57 -57
- package/src/crud/FilterBar.tsx +36 -36
- package/src/crud/FormGlobalErrorsAlert.tsx +36 -0
- package/src/crud/FormSteps.tsx +135 -135
- package/src/crud/FormTabs.tsx +126 -126
- package/src/crud/InlineFormSet.tsx +162 -162
- package/src/crud/ListActionsBar.tsx +106 -106
- package/src/crud/ResourceForm.tsx +83 -160
- package/src/crud/ResourceFormModal.tsx +75 -133
- package/src/crud/ResourceRecordForm.tsx +166 -0
- package/src/crud/columns/BooleanColumn.tsx +32 -32
- package/src/crud/columns/CustomColumn.tsx +33 -33
- package/src/crud/columns/DateColumn.tsx +28 -28
- package/src/crud/columns/ImageColumn.tsx +58 -58
- package/src/crud/columns/NumberColumn.tsx +31 -31
- package/src/crud/columns/ReferenceColumn.tsx +85 -85
- package/src/crud/columns/ReferenceManyColumn.tsx +67 -67
- package/src/crud/columns/TextColumn.tsx +38 -38
- package/src/crud/context/FilterContext.tsx +71 -71
- package/src/crud/context/FormContext.tsx +39 -39
- package/src/crud/context/FormSectionContext.tsx +22 -22
- package/src/crud/context/InlineFieldsRegistry.tsx +61 -61
- package/src/crud/context/ListContext.tsx +93 -93
- package/src/crud/fields/BooleanField.tsx +38 -38
- package/src/crud/fields/DateField.tsx +53 -53
- package/src/crud/fields/FileField.tsx +123 -123
- package/src/crud/fields/ImageField.tsx +120 -120
- package/src/crud/fields/NumberField.tsx +52 -52
- package/src/crud/fields/PasswordField.tsx +99 -99
- package/src/crud/fields/ReferenceField.tsx +237 -96
- package/src/crud/fields/ReferenceInputActions.tsx +106 -0
- package/src/crud/fields/ReferenceManyField.tsx +200 -76
- package/src/crud/fields/SelectField.tsx +48 -48
- package/src/crud/fields/TextField.tsx +46 -46
- package/src/crud/fields/uploadFieldUtils.ts +21 -21
- package/src/crud/fields/useUploadPreviewUrl.ts +20 -20
- package/src/crud/filters/BooleanFilter.tsx +38 -38
- package/src/crud/filters/DateFilter.tsx +35 -35
- package/src/crud/filters/NumberFilter.tsx +33 -33
- package/src/crud/filters/ReferenceFilter.tsx +192 -173
- package/src/crud/filters/SelectFilter.tsx +44 -44
- package/src/crud/filters/TextFilter.tsx +36 -36
- package/src/crud/index.ts +99 -94
- package/src/crud/types.ts +146 -134
- package/src/crud/utils/buildFormPayload.ts +24 -24
- package/src/crud/utils/buildInlineRowsPayload.ts +37 -37
- package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -29
- package/src/crud/utils/choiceSelectionUtils.test.ts +45 -0
- package/src/crud/utils/choiceSelectionUtils.ts +116 -0
- package/src/crud/utils/formErrors.test.ts +168 -0
- package/src/crud/utils/formErrors.ts +138 -42
- package/src/crud/utils/formSectionErrors.ts +85 -85
- package/src/crud/utils/getFormValue.ts +13 -13
- package/src/crud/utils/hasUploadValues.ts +9 -9
- package/src/crud/utils/nestedFieldPath.ts +8 -8
- package/src/crud/utils/prepareFormSubmitBody.ts +22 -17
- package/src/crud/utils/referenceSelectNotFoundContent.tsx +7 -0
- package/src/crud/utils/setFormValue.ts +18 -18
- package/src/crud/utils/sortQueryParam.ts +37 -37
- package/src/crud/utils/toFormData.ts +79 -81
- package/src/crud/utils/uploadReferenceUtils.ts +44 -0
- package/src/crud/utils/useAbortableEffect.ts +17 -17
- package/src/crud/utils/useChoices.ts +330 -182
- package/src/crud/utils/useFormRecord.ts +179 -156
- package/src/crud/utils/useListQueryState.ts +191 -191
- package/src/data/abortError.test.ts +19 -19
- package/src/data/abortError.ts +10 -10
- package/src/data/createMemoryResourceHandlers.ts +78 -78
- package/src/data/createRestResourceHandlers.ts +63 -63
- package/src/data/dataUtils.test.ts +228 -220
- package/src/data/inMemoryList.ts +97 -97
- package/src/data/parseFormErrorHelpers.test.ts +201 -138
- package/src/data/parseFormErrorHelpers.ts +376 -311
- package/src/data/sortHelpers.ts +48 -48
- package/src/index.ts +211 -207
- package/src/layouts/AdminLayout.tsx +530 -382
- package/src/layouts/AuthPageLayout.tsx +87 -81
- package/src/layouts/navFilter.ts +49 -0
- package/src/layouts/navMenuItems.tsx +44 -0
- package/src/pages/LoginPage.tsx +80 -80
- package/src/pages/PlaceholderPage.tsx +9 -9
- package/src/permissions/resourcePermissions.ts +47 -47
- package/src/router/createAdminRouter.tsx +64 -64
- package/src/router/guards.tsx +63 -63
- package/src/router/routeAccess.ts +89 -89
- package/src/types.ts +10 -0
- package/src/vite-env.d.ts +1 -1
package/src/router/guards.tsx
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import type { ReactElement } from "react";
|
|
2
|
-
import { Navigate } from "react-router-dom";
|
|
3
|
-
import { useAuth } from "../context/AuthProvider";
|
|
4
|
-
import { usePermissions } from "../context/PermissionsProvider";
|
|
5
|
-
|
|
6
|
-
export function Guard({
|
|
7
|
-
when,
|
|
8
|
-
redirect,
|
|
9
|
-
children,
|
|
10
|
-
}: {
|
|
11
|
-
when: boolean;
|
|
12
|
-
redirect: string;
|
|
13
|
-
children: ReactElement;
|
|
14
|
-
}) {
|
|
15
|
-
return when ? children : <Navigate to={redirect} replace />;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function Protected({
|
|
19
|
-
children,
|
|
20
|
-
redirectTo = "/login",
|
|
21
|
-
}: {
|
|
22
|
-
children: ReactElement;
|
|
23
|
-
redirectTo?: string;
|
|
24
|
-
}) {
|
|
25
|
-
const { isAuthenticated } = useAuth();
|
|
26
|
-
return (
|
|
27
|
-
<Guard when={isAuthenticated} redirect={redirectTo}>
|
|
28
|
-
{children}
|
|
29
|
-
</Guard>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function GuestOnly({
|
|
34
|
-
children,
|
|
35
|
-
redirectTo = "/",
|
|
36
|
-
}: {
|
|
37
|
-
children: ReactElement;
|
|
38
|
-
redirectTo?: string;
|
|
39
|
-
}) {
|
|
40
|
-
const { isAuthenticated } = useAuth();
|
|
41
|
-
return (
|
|
42
|
-
<Guard when={!isAuthenticated} redirect={redirectTo}>
|
|
43
|
-
{children}
|
|
44
|
-
</Guard>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function RequirePermission({
|
|
49
|
-
permission,
|
|
50
|
-
redirect,
|
|
51
|
-
children,
|
|
52
|
-
}: {
|
|
53
|
-
permission: string;
|
|
54
|
-
redirect: string;
|
|
55
|
-
children: ReactElement;
|
|
56
|
-
}) {
|
|
57
|
-
const can = usePermissions();
|
|
58
|
-
return (
|
|
59
|
-
<Guard when={can(permission)} redirect={redirect}>
|
|
60
|
-
{children}
|
|
61
|
-
</Guard>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Navigate } from "react-router-dom";
|
|
3
|
+
import { useAuth } from "../context/AuthProvider";
|
|
4
|
+
import { usePermissions } from "../context/PermissionsProvider";
|
|
5
|
+
|
|
6
|
+
export function Guard({
|
|
7
|
+
when,
|
|
8
|
+
redirect,
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
when: boolean;
|
|
12
|
+
redirect: string;
|
|
13
|
+
children: ReactElement;
|
|
14
|
+
}) {
|
|
15
|
+
return when ? children : <Navigate to={redirect} replace />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Protected({
|
|
19
|
+
children,
|
|
20
|
+
redirectTo = "/login",
|
|
21
|
+
}: {
|
|
22
|
+
children: ReactElement;
|
|
23
|
+
redirectTo?: string;
|
|
24
|
+
}) {
|
|
25
|
+
const { isAuthenticated } = useAuth();
|
|
26
|
+
return (
|
|
27
|
+
<Guard when={isAuthenticated} redirect={redirectTo}>
|
|
28
|
+
{children}
|
|
29
|
+
</Guard>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function GuestOnly({
|
|
34
|
+
children,
|
|
35
|
+
redirectTo = "/",
|
|
36
|
+
}: {
|
|
37
|
+
children: ReactElement;
|
|
38
|
+
redirectTo?: string;
|
|
39
|
+
}) {
|
|
40
|
+
const { isAuthenticated } = useAuth();
|
|
41
|
+
return (
|
|
42
|
+
<Guard when={!isAuthenticated} redirect={redirectTo}>
|
|
43
|
+
{children}
|
|
44
|
+
</Guard>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function RequirePermission({
|
|
49
|
+
permission,
|
|
50
|
+
redirect,
|
|
51
|
+
children,
|
|
52
|
+
}: {
|
|
53
|
+
permission: string;
|
|
54
|
+
redirect: string;
|
|
55
|
+
children: ReactElement;
|
|
56
|
+
}) {
|
|
57
|
+
const can = usePermissions();
|
|
58
|
+
return (
|
|
59
|
+
<Guard when={can(permission)} redirect={redirect}>
|
|
60
|
+
{children}
|
|
61
|
+
</Guard>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import type { RouteObject } from "react-router-dom";
|
|
2
|
-
import type { AdminRouteChild, AuthRedirects, RouteAccess } from "../types";
|
|
3
|
-
|
|
4
|
-
export function getRouteAccess(route: AdminRouteChild): RouteAccess {
|
|
5
|
-
return route.access ?? "protected";
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function partitionAdminRoutes(routes: AdminRouteChild[]) {
|
|
9
|
-
const guest: AdminRouteChild[] = [];
|
|
10
|
-
const publicRoutes: AdminRouteChild[] = [];
|
|
11
|
-
const protectedRoutes: AdminRouteChild[] = [];
|
|
12
|
-
|
|
13
|
-
for (const route of routes) {
|
|
14
|
-
const access = getRouteAccess(route);
|
|
15
|
-
if (access === "guest") guest.push(route);
|
|
16
|
-
else if (access === "public") publicRoutes.push(route);
|
|
17
|
-
else protectedRoutes.push(route);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return { guest, public: publicRoutes, protected: protectedRoutes };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** React Router path segment for a top-level route (`/login` → `login`). */
|
|
24
|
-
export function toRouterSegment(path: string): string {
|
|
25
|
-
return path.replace(/^\/+/, "");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function toAbsolutePath(path: string): string {
|
|
29
|
-
return `/${toRouterSegment(path)}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resolve redirect targets from declared routes and optional overrides.
|
|
34
|
-
* Throws when protected or guest routes exist but paths cannot be determined.
|
|
35
|
-
*/
|
|
36
|
-
export function deriveAuthPaths(
|
|
37
|
-
routes: AdminRouteChild[],
|
|
38
|
-
redirects?: AuthRedirects,
|
|
39
|
-
): { loginPath: string; homePath: string } {
|
|
40
|
-
const { guest, protected: protectedRoutes } = partitionAdminRoutes(routes);
|
|
41
|
-
const firstGuest = guest.find((r) => "path" in r && r.path);
|
|
42
|
-
const indexProtected = protectedRoutes.find(
|
|
43
|
-
(r) => "index" in r && r.index,
|
|
44
|
-
);
|
|
45
|
-
const firstProtectedPath = protectedRoutes.find(
|
|
46
|
-
(r) => "path" in r && r.path,
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
let loginPath: string | undefined = redirects?.unauthenticated;
|
|
50
|
-
if (!loginPath && firstGuest && "path" in firstGuest && firstGuest.path) {
|
|
51
|
-
loginPath = toAbsolutePath(firstGuest.path);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let homePath: string | undefined = redirects?.afterLogin;
|
|
55
|
-
if (!homePath) {
|
|
56
|
-
if (indexProtected) homePath = "/";
|
|
57
|
-
else if (
|
|
58
|
-
firstProtectedPath &&
|
|
59
|
-
"path" in firstProtectedPath &&
|
|
60
|
-
firstProtectedPath.path
|
|
61
|
-
) {
|
|
62
|
-
homePath = toAbsolutePath(firstProtectedPath.path);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (protectedRoutes.length > 0 && !loginPath) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
'createAdminRouter: protected routes require redirects.unauthenticated or a guest route (access: "guest").',
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (guest.length > 0 && !homePath) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
'createAdminRouter: guest routes require redirects.afterLogin or a protected route (index or path).',
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
loginPath: loginPath ?? "/",
|
|
80
|
-
homePath: homePath ?? "/",
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function toProtectedRouteObject(route: AdminRouteChild): RouteObject {
|
|
85
|
-
if ("index" in route && route.index) {
|
|
86
|
-
return { index: true, element: route.element };
|
|
87
|
-
}
|
|
88
|
-
return { path: route.path!, element: route.element };
|
|
89
|
-
}
|
|
1
|
+
import type { RouteObject } from "react-router-dom";
|
|
2
|
+
import type { AdminRouteChild, AuthRedirects, RouteAccess } from "../types";
|
|
3
|
+
|
|
4
|
+
export function getRouteAccess(route: AdminRouteChild): RouteAccess {
|
|
5
|
+
return route.access ?? "protected";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function partitionAdminRoutes(routes: AdminRouteChild[]) {
|
|
9
|
+
const guest: AdminRouteChild[] = [];
|
|
10
|
+
const publicRoutes: AdminRouteChild[] = [];
|
|
11
|
+
const protectedRoutes: AdminRouteChild[] = [];
|
|
12
|
+
|
|
13
|
+
for (const route of routes) {
|
|
14
|
+
const access = getRouteAccess(route);
|
|
15
|
+
if (access === "guest") guest.push(route);
|
|
16
|
+
else if (access === "public") publicRoutes.push(route);
|
|
17
|
+
else protectedRoutes.push(route);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { guest, public: publicRoutes, protected: protectedRoutes };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** React Router path segment for a top-level route (`/login` → `login`). */
|
|
24
|
+
export function toRouterSegment(path: string): string {
|
|
25
|
+
return path.replace(/^\/+/, "");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function toAbsolutePath(path: string): string {
|
|
29
|
+
return `/${toRouterSegment(path)}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve redirect targets from declared routes and optional overrides.
|
|
34
|
+
* Throws when protected or guest routes exist but paths cannot be determined.
|
|
35
|
+
*/
|
|
36
|
+
export function deriveAuthPaths(
|
|
37
|
+
routes: AdminRouteChild[],
|
|
38
|
+
redirects?: AuthRedirects,
|
|
39
|
+
): { loginPath: string; homePath: string } {
|
|
40
|
+
const { guest, protected: protectedRoutes } = partitionAdminRoutes(routes);
|
|
41
|
+
const firstGuest = guest.find((r) => "path" in r && r.path);
|
|
42
|
+
const indexProtected = protectedRoutes.find(
|
|
43
|
+
(r) => "index" in r && r.index,
|
|
44
|
+
);
|
|
45
|
+
const firstProtectedPath = protectedRoutes.find(
|
|
46
|
+
(r) => "path" in r && r.path,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
let loginPath: string | undefined = redirects?.unauthenticated;
|
|
50
|
+
if (!loginPath && firstGuest && "path" in firstGuest && firstGuest.path) {
|
|
51
|
+
loginPath = toAbsolutePath(firstGuest.path);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let homePath: string | undefined = redirects?.afterLogin;
|
|
55
|
+
if (!homePath) {
|
|
56
|
+
if (indexProtected) homePath = "/";
|
|
57
|
+
else if (
|
|
58
|
+
firstProtectedPath &&
|
|
59
|
+
"path" in firstProtectedPath &&
|
|
60
|
+
firstProtectedPath.path
|
|
61
|
+
) {
|
|
62
|
+
homePath = toAbsolutePath(firstProtectedPath.path);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (protectedRoutes.length > 0 && !loginPath) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'createAdminRouter: protected routes require redirects.unauthenticated or a guest route (access: "guest").',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (guest.length > 0 && !homePath) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'createAdminRouter: guest routes require redirects.afterLogin or a protected route (index or path).',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
loginPath: loginPath ?? "/",
|
|
80
|
+
homePath: homePath ?? "/",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function toProtectedRouteObject(route: AdminRouteChild): RouteObject {
|
|
85
|
+
if ("index" in route && route.index) {
|
|
86
|
+
return { index: true, element: route.element };
|
|
87
|
+
}
|
|
88
|
+
return { path: route.path!, element: route.element };
|
|
89
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -35,6 +35,11 @@ export type AuthAdapter = {
|
|
|
35
35
|
logout: () => void;
|
|
36
36
|
/** Synchronous read of session (e.g. token). Used after mount and after login/logout. */
|
|
37
37
|
getToken: () => string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Label for the account menu in `AdminLayout` (top-right button).
|
|
40
|
+
* Return `null` when no user is loaded. Defaults to `"User"` when omitted.
|
|
41
|
+
*/
|
|
42
|
+
getUserLabel?: () => string | null;
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
/** How a route participates in auth when using `AdminApp` / `createAdminRouter`. */
|
|
@@ -77,6 +82,11 @@ export type AdminLayoutProps = {
|
|
|
77
82
|
loginPath?: string;
|
|
78
83
|
/** localStorage key for sider collapsed state. Default `ding-react-admin-sider-collapsed`. */
|
|
79
84
|
siderCollapsedStorageKey?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Sidebar menu search. Enabled by default; pass `false` to hide.
|
|
87
|
+
* Use an object to customize the placeholder.
|
|
88
|
+
*/
|
|
89
|
+
navSearch?: boolean | { placeholder?: string };
|
|
80
90
|
};
|
|
81
91
|
|
|
82
92
|
export type { AuthPageLayoutProps } from "./layouts/AuthPageLayout";
|
package/src/vite-env.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
1
|
+
/// <reference types="vite/client" />
|