ding-react-admin 1.0.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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3067 -0
- package/dist/src/app/AdminApp.d.ts +7 -0
- package/dist/src/app/AdminApp.d.ts.map +1 -0
- package/dist/src/components/AuthAlternateLink.d.ts +7 -0
- package/dist/src/components/AuthAlternateLink.d.ts.map +1 -0
- package/dist/src/components/DensitySwitch.d.ts +2 -0
- package/dist/src/components/DensitySwitch.d.ts.map +1 -0
- package/dist/src/components/ThemeSwitch.d.ts +2 -0
- package/dist/src/components/ThemeSwitch.d.ts.map +1 -0
- package/dist/src/components/ThemeToolbar.d.ts +2 -0
- package/dist/src/components/ThemeToolbar.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +5 -0
- package/dist/src/components/index.d.ts.map +1 -0
- package/dist/src/context/AppThemeProvider.d.ts +13 -0
- package/dist/src/context/AppThemeProvider.d.ts.map +1 -0
- package/dist/src/context/AuthProvider.d.ts +18 -0
- package/dist/src/context/AuthProvider.d.ts.map +1 -0
- package/dist/src/context/DataProvider.d.ts +10 -0
- package/dist/src/context/DataProvider.d.ts.map +1 -0
- package/dist/src/context/PermissionsProvider.d.ts +18 -0
- package/dist/src/context/PermissionsProvider.d.ts.map +1 -0
- package/dist/src/crud/FilterBar.d.ts +6 -0
- package/dist/src/crud/FilterBar.d.ts.map +1 -0
- package/dist/src/crud/FormSteps.d.ts +20 -0
- package/dist/src/crud/FormSteps.d.ts.map +1 -0
- package/dist/src/crud/FormTabs.d.ts +13 -0
- package/dist/src/crud/FormTabs.d.ts.map +1 -0
- package/dist/src/crud/InlineFormSet.d.ts +29 -0
- package/dist/src/crud/InlineFormSet.d.ts.map +1 -0
- package/dist/src/crud/ListActionsBar.d.ts +15 -0
- package/dist/src/crud/ListActionsBar.d.ts.map +1 -0
- package/dist/src/crud/ResourceForm.d.ts +21 -0
- package/dist/src/crud/ResourceForm.d.ts.map +1 -0
- package/dist/src/crud/ResourceFormModal.d.ts +10 -0
- package/dist/src/crud/ResourceFormModal.d.ts.map +1 -0
- package/dist/src/crud/ResourceList.d.ts +38 -0
- package/dist/src/crud/ResourceList.d.ts.map +1 -0
- package/dist/src/crud/columns/BooleanColumn.d.ts +6 -0
- package/dist/src/crud/columns/BooleanColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/CustomColumn.d.ts +9 -0
- package/dist/src/crud/columns/CustomColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/DateColumn.d.ts +6 -0
- package/dist/src/crud/columns/DateColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/NumberColumn.d.ts +6 -0
- package/dist/src/crud/columns/NumberColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/ReferenceColumn.d.ts +6 -0
- package/dist/src/crud/columns/ReferenceColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/ReferenceManyColumn.d.ts +6 -0
- package/dist/src/crud/columns/ReferenceManyColumn.d.ts.map +1 -0
- package/dist/src/crud/columns/TextColumn.d.ts +7 -0
- package/dist/src/crud/columns/TextColumn.d.ts.map +1 -0
- package/dist/src/crud/context/FilterContext.d.ts +17 -0
- package/dist/src/crud/context/FilterContext.d.ts.map +1 -0
- package/dist/src/crud/context/FormContext.d.ts +16 -0
- package/dist/src/crud/context/FormContext.d.ts.map +1 -0
- package/dist/src/crud/context/FormFieldsContext.d.ts +14 -0
- package/dist/src/crud/context/FormFieldsContext.d.ts.map +1 -0
- package/dist/src/crud/context/FormSectionContext.d.ts +7 -0
- package/dist/src/crud/context/FormSectionContext.d.ts.map +1 -0
- package/dist/src/crud/context/InlineFormContext.d.ts +16 -0
- package/dist/src/crud/context/InlineFormContext.d.ts.map +1 -0
- package/dist/src/crud/context/ListContext.d.ts +22 -0
- package/dist/src/crud/context/ListContext.d.ts.map +1 -0
- package/dist/src/crud/fields/BooleanField.d.ts +7 -0
- package/dist/src/crud/fields/BooleanField.d.ts.map +1 -0
- package/dist/src/crud/fields/DateField.d.ts +8 -0
- package/dist/src/crud/fields/DateField.d.ts.map +1 -0
- package/dist/src/crud/fields/FieldWrapper.d.ts +29 -0
- package/dist/src/crud/fields/FieldWrapper.d.ts.map +1 -0
- package/dist/src/crud/fields/NumberField.d.ts +12 -0
- package/dist/src/crud/fields/NumberField.d.ts.map +1 -0
- package/dist/src/crud/fields/PasswordField.d.ts +11 -0
- package/dist/src/crud/fields/PasswordField.d.ts.map +1 -0
- package/dist/src/crud/fields/ReferenceField.d.ts +18 -0
- package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -0
- package/dist/src/crud/fields/ReferenceManyField.d.ts +9 -0
- package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -0
- package/dist/src/crud/fields/SelectField.d.ts +10 -0
- package/dist/src/crud/fields/SelectField.d.ts.map +1 -0
- package/dist/src/crud/fields/TextField.d.ts +10 -0
- package/dist/src/crud/fields/TextField.d.ts.map +1 -0
- package/dist/src/crud/fields/useInlineOrFormField.d.ts +10 -0
- package/dist/src/crud/fields/useInlineOrFormField.d.ts.map +1 -0
- package/dist/src/crud/filters/BooleanFilter.d.ts +4 -0
- package/dist/src/crud/filters/BooleanFilter.d.ts.map +1 -0
- package/dist/src/crud/filters/DateFilter.d.ts +4 -0
- package/dist/src/crud/filters/DateFilter.d.ts.map +1 -0
- package/dist/src/crud/filters/NumberFilter.d.ts +4 -0
- package/dist/src/crud/filters/NumberFilter.d.ts.map +1 -0
- package/dist/src/crud/filters/ReferenceFilter.d.ts +13 -0
- package/dist/src/crud/filters/ReferenceFilter.d.ts.map +1 -0
- package/dist/src/crud/filters/SelectFilter.d.ts +7 -0
- package/dist/src/crud/filters/SelectFilter.d.ts.map +1 -0
- package/dist/src/crud/filters/TextFilter.d.ts +6 -0
- package/dist/src/crud/filters/TextFilter.d.ts.map +1 -0
- package/dist/src/crud/index.d.ts +44 -0
- package/dist/src/crud/index.d.ts.map +1 -0
- package/dist/src/crud/types.d.ts +99 -0
- package/dist/src/crud/types.d.ts.map +1 -0
- package/dist/src/crud/utils/formErrors.d.ts +9 -0
- package/dist/src/crud/utils/formErrors.d.ts.map +1 -0
- package/dist/src/crud/utils/formSectionErrors.d.ts +6 -0
- package/dist/src/crud/utils/formSectionErrors.d.ts.map +1 -0
- package/dist/src/crud/utils/getByPath.d.ts +3 -0
- package/dist/src/crud/utils/getByPath.d.ts.map +1 -0
- package/dist/src/crud/utils/inlineArrayName.d.ts +3 -0
- package/dist/src/crud/utils/inlineArrayName.d.ts.map +1 -0
- package/dist/src/crud/utils/pickBySources.d.ts +6 -0
- package/dist/src/crud/utils/pickBySources.d.ts.map +1 -0
- package/dist/src/crud/utils/setByPath.d.ts +3 -0
- package/dist/src/crud/utils/setByPath.d.ts.map +1 -0
- package/dist/src/crud/utils/sortQueryParam.d.ts +7 -0
- package/dist/src/crud/utils/sortQueryParam.d.ts.map +1 -0
- package/dist/src/crud/utils/useChoices.d.ts +10 -0
- package/dist/src/crud/utils/useChoices.d.ts.map +1 -0
- package/dist/src/crud/utils/useListQueryState.d.ts +22 -0
- package/dist/src/crud/utils/useListQueryState.d.ts.map +1 -0
- package/dist/src/data/createMemoryResourceHandlers.d.ts +23 -0
- package/dist/src/data/createMemoryResourceHandlers.d.ts.map +1 -0
- package/dist/src/data/createRestResourceHandlers.d.ts +22 -0
- package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -0
- package/dist/src/data/dataProviderTypes.d.ts +67 -0
- package/dist/src/data/dataProviderTypes.d.ts.map +1 -0
- package/dist/src/data/dataUtils.test.d.ts +2 -0
- package/dist/src/data/dataUtils.test.d.ts.map +1 -0
- package/dist/src/data/inMemoryList.d.ts +12 -0
- package/dist/src/data/inMemoryList.d.ts.map +1 -0
- package/dist/src/data/parseFormErrorHelpers.d.ts +35 -0
- package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -0
- package/dist/src/data/parseFormErrorHelpers.test.d.ts +2 -0
- package/dist/src/data/parseFormErrorHelpers.test.d.ts.map +1 -0
- package/dist/src/data/resourceHandlers.d.ts +33 -0
- package/dist/src/data/resourceHandlers.d.ts.map +1 -0
- package/dist/src/data/sortHelpers.d.ts +17 -0
- package/dist/src/data/sortHelpers.d.ts.map +1 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/layouts/AdminLayout.d.ts +3 -0
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -0
- package/dist/src/layouts/AuthPageLayout.d.ts +11 -0
- package/dist/src/layouts/AuthPageLayout.d.ts.map +1 -0
- package/dist/src/pages/LoginPage.d.ts +3 -0
- package/dist/src/pages/LoginPage.d.ts.map +1 -0
- package/dist/src/pages/PlaceholderPage.d.ts +4 -0
- package/dist/src/pages/PlaceholderPage.d.ts.map +1 -0
- package/dist/src/permissions/resourcePermissions.d.ts +22 -0
- package/dist/src/permissions/resourcePermissions.d.ts.map +1 -0
- package/dist/src/router/createAdminRouter.d.ts +3 -0
- package/dist/src/router/createAdminRouter.d.ts.map +1 -0
- package/dist/src/router/guards.d.ts +20 -0
- package/dist/src/router/guards.d.ts.map +1 -0
- package/dist/src/router/routeAccess.d.ts +21 -0
- package/dist/src/router/routeAccess.d.ts.map +1 -0
- package/dist/src/types.d.ts +123 -0
- package/dist/src/types.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/app/AdminApp.tsx +34 -0
- package/src/components/AuthAlternateLink.tsx +23 -0
- package/src/components/DensitySwitch.tsx +31 -0
- package/src/components/ThemeSwitch.tsx +28 -0
- package/src/components/ThemeToolbar.tsx +34 -0
- package/src/components/index.ts +4 -0
- package/src/context/AppThemeProvider.tsx +128 -0
- package/src/context/AuthProvider.tsx +77 -0
- package/src/context/DataProvider.tsx +30 -0
- package/src/context/PermissionsProvider.tsx +57 -0
- package/src/crud/FilterBar.tsx +36 -0
- package/src/crud/FormSteps.tsx +135 -0
- package/src/crud/FormTabs.tsx +126 -0
- package/src/crud/InlineFormSet.tsx +231 -0
- package/src/crud/ListActionsBar.tsx +106 -0
- package/src/crud/ResourceForm.tsx +281 -0
- package/src/crud/ResourceFormModal.tsx +121 -0
- package/src/crud/ResourceList.tsx +633 -0
- package/src/crud/columns/BooleanColumn.tsx +32 -0
- package/src/crud/columns/CustomColumn.tsx +33 -0
- package/src/crud/columns/DateColumn.tsx +28 -0
- package/src/crud/columns/NumberColumn.tsx +31 -0
- package/src/crud/columns/ReferenceColumn.tsx +85 -0
- package/src/crud/columns/ReferenceManyColumn.tsx +67 -0
- package/src/crud/columns/TextColumn.tsx +38 -0
- package/src/crud/context/FilterContext.tsx +71 -0
- package/src/crud/context/FormContext.tsx +39 -0
- package/src/crud/context/FormFieldsContext.tsx +76 -0
- package/src/crud/context/FormSectionContext.tsx +22 -0
- package/src/crud/context/InlineFormContext.tsx +71 -0
- package/src/crud/context/ListContext.tsx +93 -0
- package/src/crud/fields/BooleanField.tsx +32 -0
- package/src/crud/fields/DateField.tsx +47 -0
- package/src/crud/fields/FieldWrapper.tsx +109 -0
- package/src/crud/fields/NumberField.tsx +50 -0
- package/src/crud/fields/PasswordField.tsx +98 -0
- package/src/crud/fields/ReferenceField.tsx +191 -0
- package/src/crud/fields/ReferenceManyField.tsx +70 -0
- package/src/crud/fields/SelectField.tsx +42 -0
- package/src/crud/fields/TextField.tsx +44 -0
- package/src/crud/fields/useInlineOrFormField.tsx +91 -0
- package/src/crud/filters/BooleanFilter.tsx +38 -0
- package/src/crud/filters/DateFilter.tsx +35 -0
- package/src/crud/filters/NumberFilter.tsx +33 -0
- package/src/crud/filters/ReferenceFilter.tsx +173 -0
- package/src/crud/filters/SelectFilter.tsx +44 -0
- package/src/crud/filters/TextFilter.tsx +36 -0
- package/src/crud/index.ts +73 -0
- package/src/crud/types.ts +130 -0
- package/src/crud/utils/formErrors.ts +38 -0
- package/src/crud/utils/formSectionErrors.ts +85 -0
- package/src/crud/utils/getByPath.ts +13 -0
- package/src/crud/utils/inlineArrayName.ts +4 -0
- package/src/crud/utils/pickBySources.ts +24 -0
- package/src/crud/utils/setByPath.ts +18 -0
- package/src/crud/utils/sortQueryParam.ts +37 -0
- package/src/crud/utils/useChoices.ts +182 -0
- package/src/crud/utils/useListQueryState.ts +191 -0
- package/src/data/createMemoryResourceHandlers.ts +78 -0
- package/src/data/createRestResourceHandlers.ts +57 -0
- package/src/data/dataProviderTypes.ts +101 -0
- package/src/data/dataUtils.test.ts +108 -0
- package/src/data/inMemoryList.ts +97 -0
- package/src/data/parseFormErrorHelpers.test.ts +84 -0
- package/src/data/parseFormErrorHelpers.ts +224 -0
- package/src/data/resourceHandlers.ts +124 -0
- package/src/data/sortHelpers.ts +48 -0
- package/src/index.ts +182 -0
- package/src/layouts/AdminLayout.tsx +381 -0
- package/src/layouts/AuthPageLayout.tsx +81 -0
- package/src/pages/LoginPage.tsx +80 -0
- package/src/pages/PlaceholderPage.tsx +9 -0
- package/src/permissions/resourcePermissions.ts +47 -0
- package/src/router/createAdminRouter.tsx +64 -0
- package/src/router/guards.tsx +63 -0
- package/src/router/routeAccess.ts +89 -0
- package/src/types.ts +135 -0
- package/src/vite-env.d.ts +1 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export { AdminApp } from "./app/AdminApp";
|
|
2
|
+
export { AdminLayout } from "./layouts/AdminLayout";
|
|
3
|
+
export {
|
|
4
|
+
AuthProvider,
|
|
5
|
+
createSessionStorageAuthAdapter,
|
|
6
|
+
useAuth,
|
|
7
|
+
type AuthProviderProps,
|
|
8
|
+
} from "./context/AuthProvider";
|
|
9
|
+
export {
|
|
10
|
+
DataProvider,
|
|
11
|
+
useDataProvider,
|
|
12
|
+
type DataProviderProps,
|
|
13
|
+
} from "./context/DataProvider";
|
|
14
|
+
export {
|
|
15
|
+
PermissionsProvider,
|
|
16
|
+
createPermissionsChecker,
|
|
17
|
+
useCan,
|
|
18
|
+
usePermissions,
|
|
19
|
+
type PermissionsProviderProps,
|
|
20
|
+
} from "./context/PermissionsProvider";
|
|
21
|
+
export {
|
|
22
|
+
AppThemeProvider,
|
|
23
|
+
useThemeMode,
|
|
24
|
+
} from "./context/AppThemeProvider";
|
|
25
|
+
export type {
|
|
26
|
+
CreateResult,
|
|
27
|
+
DataProvider as DataProviderContract,
|
|
28
|
+
DeleteResult,
|
|
29
|
+
GetListParams,
|
|
30
|
+
GetListResult,
|
|
31
|
+
GetOneResult,
|
|
32
|
+
Identifier,
|
|
33
|
+
PaginationParams,
|
|
34
|
+
SortOrder,
|
|
35
|
+
SortSpec,
|
|
36
|
+
UpdateParams,
|
|
37
|
+
UpdateResult,
|
|
38
|
+
FormMutation,
|
|
39
|
+
FormValidationErrors,
|
|
40
|
+
ParseFormError,
|
|
41
|
+
ParseFormErrorContext,
|
|
42
|
+
} from "./data/dataProviderTypes";
|
|
43
|
+
export { combineResourceHandlers } from "./data/resourceHandlers";
|
|
44
|
+
export {
|
|
45
|
+
parseDjangoDRFFormErrors,
|
|
46
|
+
parseDotNetFormErrors,
|
|
47
|
+
parseNodeFormErrors,
|
|
48
|
+
getErrorBody,
|
|
49
|
+
applyInlineFieldPaths,
|
|
50
|
+
asStringMessages,
|
|
51
|
+
finalizeFormErrors,
|
|
52
|
+
} from "./data/parseFormErrorHelpers";
|
|
53
|
+
export type {
|
|
54
|
+
DotNetFormErrorOptions,
|
|
55
|
+
NodeFormErrorOptions,
|
|
56
|
+
} from "./data/parseFormErrorHelpers";
|
|
57
|
+
export { createMemoryResourceHandlers } from "./data/createMemoryResourceHandlers";
|
|
58
|
+
export { createRestResourceHandlers } from "./data/createRestResourceHandlers";
|
|
59
|
+
export type { RestResourceHandlersConfig } from "./data/createRestResourceHandlers";
|
|
60
|
+
export {
|
|
61
|
+
toDjangoRestOrdering,
|
|
62
|
+
toODataOrderBy,
|
|
63
|
+
toJsonApiSort,
|
|
64
|
+
} from "./data/sortHelpers";
|
|
65
|
+
export {
|
|
66
|
+
applyInMemoryListParams,
|
|
67
|
+
filterRows,
|
|
68
|
+
getById as getRowById,
|
|
69
|
+
} from "./data/inMemoryList";
|
|
70
|
+
export type {
|
|
71
|
+
CombineResourceHandlersOptions,
|
|
72
|
+
ResourceAction,
|
|
73
|
+
ResourceGuard,
|
|
74
|
+
ResourceHandlerEntry,
|
|
75
|
+
ResourceHandlerMap,
|
|
76
|
+
ResourceHandlers,
|
|
77
|
+
} from "./data/resourceHandlers";
|
|
78
|
+
export type { ResourcePermissions } from "./permissions/resourcePermissions";
|
|
79
|
+
export { filterNavByPermission } from "./permissions/resourcePermissions";
|
|
80
|
+
export type { MemoryResourceHandlersConfig } from "./data/createMemoryResourceHandlers";
|
|
81
|
+
export type { PermissionsChecker } from "./context/PermissionsProvider";
|
|
82
|
+
export { LoginPage } from "./pages/LoginPage";
|
|
83
|
+
export { PlaceholderPage } from "./pages/PlaceholderPage";
|
|
84
|
+
export { AuthPageLayout } from "./layouts/AuthPageLayout";
|
|
85
|
+
export {
|
|
86
|
+
AuthAlternateLink,
|
|
87
|
+
DensitySwitch,
|
|
88
|
+
ThemeSwitch,
|
|
89
|
+
ThemeToolbar,
|
|
90
|
+
} from "./components";
|
|
91
|
+
export { Guard, GuestOnly, Protected, RequirePermission } from "./router/guards";
|
|
92
|
+
export { createAdminRouter } from "./router/createAdminRouter";
|
|
93
|
+
export {
|
|
94
|
+
deriveAuthPaths,
|
|
95
|
+
getRouteAccess,
|
|
96
|
+
partitionAdminRoutes,
|
|
97
|
+
} from "./router/routeAccess";
|
|
98
|
+
export type {
|
|
99
|
+
AdminAppProps,
|
|
100
|
+
AdminLayoutProps,
|
|
101
|
+
AdminRouteChild,
|
|
102
|
+
AppThemeProviderProps,
|
|
103
|
+
AuthAdapter,
|
|
104
|
+
LoginCredentials,
|
|
105
|
+
AuthRedirects,
|
|
106
|
+
CreateAdminRouterOptions,
|
|
107
|
+
AuthAlternateLinkProps,
|
|
108
|
+
AuthPageLayoutProps,
|
|
109
|
+
LoginPageProps,
|
|
110
|
+
NavItem,
|
|
111
|
+
RouteAccess,
|
|
112
|
+
ThemeDensity,
|
|
113
|
+
ThemeMode,
|
|
114
|
+
} from "./types";
|
|
115
|
+
export {
|
|
116
|
+
ResourceList,
|
|
117
|
+
FilterBar,
|
|
118
|
+
ResourceForm,
|
|
119
|
+
ResourceFormModal,
|
|
120
|
+
InlineFormSet,
|
|
121
|
+
FormTabs,
|
|
122
|
+
FormTab,
|
|
123
|
+
FormSteps,
|
|
124
|
+
FormStep,
|
|
125
|
+
saveInlineRows,
|
|
126
|
+
loadInlineRows,
|
|
127
|
+
TextField,
|
|
128
|
+
NumberField,
|
|
129
|
+
BooleanField,
|
|
130
|
+
DateField,
|
|
131
|
+
SelectField,
|
|
132
|
+
PasswordField,
|
|
133
|
+
ReferenceField,
|
|
134
|
+
ReferenceManyField,
|
|
135
|
+
TextColumn,
|
|
136
|
+
NumberColumn,
|
|
137
|
+
BooleanColumn,
|
|
138
|
+
DateColumn,
|
|
139
|
+
ReferenceColumn,
|
|
140
|
+
ReferenceManyColumn,
|
|
141
|
+
CustomColumn,
|
|
142
|
+
TextFilter,
|
|
143
|
+
NumberFilter,
|
|
144
|
+
BooleanFilter,
|
|
145
|
+
DateFilter,
|
|
146
|
+
SelectFilter,
|
|
147
|
+
ReferenceFilter,
|
|
148
|
+
ReferenceManyFilter,
|
|
149
|
+
useListQueryState,
|
|
150
|
+
useChoices,
|
|
151
|
+
getByPath,
|
|
152
|
+
pickBySources,
|
|
153
|
+
inlineArrayName,
|
|
154
|
+
useResourceListContext,
|
|
155
|
+
useRegisterFormSource,
|
|
156
|
+
} from "./crud";
|
|
157
|
+
export type {
|
|
158
|
+
ResourceFormProps,
|
|
159
|
+
ResourceFormInlineConfig,
|
|
160
|
+
ResourceFormModalProps,
|
|
161
|
+
InlineFormSetProps,
|
|
162
|
+
FormTabsProps,
|
|
163
|
+
FormTabProps,
|
|
164
|
+
FormStepsProps,
|
|
165
|
+
FormStepProps,
|
|
166
|
+
SaveInlineOptions,
|
|
167
|
+
InlineFormSetLayout,
|
|
168
|
+
ResourceListProps,
|
|
169
|
+
BaseSourceProps,
|
|
170
|
+
ChoiceOption,
|
|
171
|
+
ChoicesLoader,
|
|
172
|
+
ReferenceProps,
|
|
173
|
+
DisplayProps,
|
|
174
|
+
EditMode,
|
|
175
|
+
FieldRules,
|
|
176
|
+
ResourceListBuiltInActions,
|
|
177
|
+
ResourceListBulkAction,
|
|
178
|
+
ResourceListBulkActionHelpers,
|
|
179
|
+
ResourceListRowActionsHelpers,
|
|
180
|
+
ListQueryState,
|
|
181
|
+
ListQueryActions,
|
|
182
|
+
} from "./crud";
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LogoutOutlined,
|
|
3
|
+
MenuOutlined,
|
|
4
|
+
UserOutlined,
|
|
5
|
+
} from "@ant-design/icons";
|
|
6
|
+
import {
|
|
7
|
+
Avatar,
|
|
8
|
+
Button,
|
|
9
|
+
Drawer,
|
|
10
|
+
Dropdown,
|
|
11
|
+
Grid,
|
|
12
|
+
Layout,
|
|
13
|
+
Menu,
|
|
14
|
+
theme,
|
|
15
|
+
Typography,
|
|
16
|
+
} from "antd";
|
|
17
|
+
import type { MenuProps } from "antd";
|
|
18
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
19
|
+
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
|
20
|
+
import { ThemeToolbar } from "../components/ThemeToolbar";
|
|
21
|
+
import { useAuth } from "../context/AuthProvider";
|
|
22
|
+
import { usePermissions } from "../context/PermissionsProvider";
|
|
23
|
+
import { useThemeMode } from "../context/AppThemeProvider";
|
|
24
|
+
import { filterNavByPermission } from "../permissions/resourcePermissions";
|
|
25
|
+
import type { AdminLayoutProps, NavItem } from "../types";
|
|
26
|
+
|
|
27
|
+
const LIGHT_SIDEBAR_BG = "#001529";
|
|
28
|
+
const DEFAULT_SIDER_KEY = "ding-react-admin-sider-collapsed";
|
|
29
|
+
|
|
30
|
+
function readSiderCollapsed(key: string): boolean {
|
|
31
|
+
try {
|
|
32
|
+
return localStorage.getItem(key) === "1";
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function useIsMobileNav(): boolean {
|
|
39
|
+
const screens = Grid.useBreakpoint();
|
|
40
|
+
return screens.lg !== true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Paths that correspond to actual routes (leaf menu rows only). */
|
|
44
|
+
function collectLeafPaths(items: NavItem[]): Set<string> {
|
|
45
|
+
const set = new Set<string>();
|
|
46
|
+
function walk(nodes: NavItem[]) {
|
|
47
|
+
for (const n of nodes) {
|
|
48
|
+
if (n.children?.length) walk(n.children);
|
|
49
|
+
else set.add(n.path);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
walk(items);
|
|
53
|
+
return set;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Submenu keys that must stay open for `pathname` to remain visible in the tree. */
|
|
57
|
+
function ancestorKeysForActivePath(
|
|
58
|
+
items: NavItem[],
|
|
59
|
+
pathname: string,
|
|
60
|
+
): string[] {
|
|
61
|
+
function dfs(nodes: NavItem[]): string[] | null {
|
|
62
|
+
for (const node of nodes) {
|
|
63
|
+
if (node.children?.length) {
|
|
64
|
+
const sub = dfs(node.children);
|
|
65
|
+
if (sub !== null) return [node.path, ...sub];
|
|
66
|
+
} else if (node.path === pathname) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return dfs(items) ?? [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function navItemsToAntdItems(
|
|
76
|
+
items: NavItem[],
|
|
77
|
+
): NonNullable<MenuProps["items"]> {
|
|
78
|
+
return items.map((item) => {
|
|
79
|
+
const IconComp = item.Icon;
|
|
80
|
+
const icon = IconComp ? <IconComp /> : undefined;
|
|
81
|
+
if (item.children?.length) {
|
|
82
|
+
return {
|
|
83
|
+
key: item.path,
|
|
84
|
+
icon,
|
|
85
|
+
label: item.label,
|
|
86
|
+
children: navItemsToAntdItems(item.children),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
key: item.path,
|
|
91
|
+
icon,
|
|
92
|
+
label: item.label,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type NavMenuProps = {
|
|
98
|
+
menuItems: MenuProps["items"];
|
|
99
|
+
selectedKeys: string[];
|
|
100
|
+
inlineCollapsed: boolean;
|
|
101
|
+
openKeys?: string[];
|
|
102
|
+
onOpenChange?: (keys: string[]) => void;
|
|
103
|
+
onNavigate: (key: string) => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function NavMenu({
|
|
107
|
+
menuItems,
|
|
108
|
+
selectedKeys,
|
|
109
|
+
inlineCollapsed,
|
|
110
|
+
openKeys,
|
|
111
|
+
onOpenChange,
|
|
112
|
+
onNavigate,
|
|
113
|
+
}: NavMenuProps) {
|
|
114
|
+
return (
|
|
115
|
+
<Menu
|
|
116
|
+
mode="inline"
|
|
117
|
+
theme="dark"
|
|
118
|
+
inlineCollapsed={inlineCollapsed}
|
|
119
|
+
selectedKeys={selectedKeys}
|
|
120
|
+
{...(!inlineCollapsed && openKeys !== undefined && onOpenChange
|
|
121
|
+
? { openKeys, onOpenChange }
|
|
122
|
+
: {})}
|
|
123
|
+
items={menuItems}
|
|
124
|
+
onClick={({ key }) => onNavigate(key)}
|
|
125
|
+
style={{ background: "transparent", borderInlineEnd: "none" }}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function AdminLayout({
|
|
131
|
+
navItems,
|
|
132
|
+
brand = "Admin",
|
|
133
|
+
collapsedBrand = "A",
|
|
134
|
+
mobileDrawerTitle,
|
|
135
|
+
headerExtras,
|
|
136
|
+
userMenuItems,
|
|
137
|
+
onUserMenuClick,
|
|
138
|
+
loginPath = "/login",
|
|
139
|
+
siderCollapsedStorageKey = DEFAULT_SIDER_KEY,
|
|
140
|
+
}: AdminLayoutProps) {
|
|
141
|
+
const navigate = useNavigate();
|
|
142
|
+
const location = useLocation();
|
|
143
|
+
const { resolved } = useThemeMode();
|
|
144
|
+
const isAppDark = resolved === "dark";
|
|
145
|
+
const { logout } = useAuth();
|
|
146
|
+
const can = usePermissions();
|
|
147
|
+
const [collapsed, setCollapsed] = useState(() =>
|
|
148
|
+
readSiderCollapsed(siderCollapsedStorageKey),
|
|
149
|
+
);
|
|
150
|
+
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
|
151
|
+
const isMobile = useIsMobileNav();
|
|
152
|
+
const { token } = theme.useToken();
|
|
153
|
+
|
|
154
|
+
const drawerTitle = mobileDrawerTitle ?? brand;
|
|
155
|
+
|
|
156
|
+
const handleLogout = () => {
|
|
157
|
+
logout();
|
|
158
|
+
navigate(loginPath, { replace: true });
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handleCollapse = useCallback(
|
|
162
|
+
(next: boolean) => {
|
|
163
|
+
setCollapsed(next);
|
|
164
|
+
try {
|
|
165
|
+
localStorage.setItem(siderCollapsedStorageKey, next ? "1" : "0");
|
|
166
|
+
} catch {
|
|
167
|
+
/* ignore */
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
[siderCollapsedStorageKey],
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!isMobile) setMobileNavOpen(false);
|
|
175
|
+
}, [isMobile]);
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
setMobileNavOpen(false);
|
|
179
|
+
}, [location.pathname]);
|
|
180
|
+
|
|
181
|
+
const visibleNavItems = useMemo(
|
|
182
|
+
() => filterNavByPermission(navItems, can),
|
|
183
|
+
[navItems, can],
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const leafPaths = useMemo(
|
|
187
|
+
() => collectLeafPaths(visibleNavItems),
|
|
188
|
+
[visibleNavItems],
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const menuItems: MenuProps["items"] = useMemo(
|
|
192
|
+
() => navItemsToAntdItems(visibleNavItems),
|
|
193
|
+
[visibleNavItems],
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const requiredOpenKeys = useMemo(
|
|
197
|
+
() => ancestorKeysForActivePath(visibleNavItems, location.pathname),
|
|
198
|
+
[visibleNavItems, location.pathname],
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const [openKeys, setOpenKeys] = useState<string[]>(() =>
|
|
202
|
+
ancestorKeysForActivePath(visibleNavItems, location.pathname),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
setOpenKeys((prev) => [...new Set([...prev, ...requiredOpenKeys])]);
|
|
207
|
+
}, [requiredOpenKeys]);
|
|
208
|
+
|
|
209
|
+
const handleOpenChange = useCallback((keys: string[]) => {
|
|
210
|
+
setOpenKeys(keys);
|
|
211
|
+
}, []);
|
|
212
|
+
|
|
213
|
+
const defaultUserMenuItems: MenuProps["items"] = useMemo(
|
|
214
|
+
() => [
|
|
215
|
+
{
|
|
216
|
+
key: "logout",
|
|
217
|
+
icon: <LogoutOutlined />,
|
|
218
|
+
label: "Log out",
|
|
219
|
+
danger: true,
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
[],
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const resolvedUserItems = userMenuItems ?? defaultUserMenuItems;
|
|
226
|
+
|
|
227
|
+
const onDropdownMenuClick: MenuProps["onClick"] = (info) => {
|
|
228
|
+
if (onUserMenuClick) {
|
|
229
|
+
onUserMenuClick(info);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (info.key === "logout") handleLogout();
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const siderBg = isAppDark ? token.colorBgContainer : LIGHT_SIDEBAR_BG;
|
|
236
|
+
const selectedKeys = [location.pathname];
|
|
237
|
+
|
|
238
|
+
const goNav = (key: string) => {
|
|
239
|
+
if (!leafPaths.has(key)) return;
|
|
240
|
+
navigate(key);
|
|
241
|
+
if (isMobile) setMobileNavOpen(false);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<Layout
|
|
246
|
+
style={{
|
|
247
|
+
minHeight: "100vh",
|
|
248
|
+
width: "100%",
|
|
249
|
+
background: token.colorBgLayout,
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{!isMobile && (
|
|
253
|
+
<Layout.Sider
|
|
254
|
+
collapsible
|
|
255
|
+
collapsed={collapsed}
|
|
256
|
+
onCollapse={handleCollapse}
|
|
257
|
+
collapsedWidth={64}
|
|
258
|
+
style={{
|
|
259
|
+
background: siderBg,
|
|
260
|
+
borderInlineEnd: isAppDark
|
|
261
|
+
? `1px solid ${token.colorSplit}`
|
|
262
|
+
: undefined,
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
<div
|
|
266
|
+
style={{
|
|
267
|
+
height: 64,
|
|
268
|
+
display: "flex",
|
|
269
|
+
alignItems: "center",
|
|
270
|
+
justifyContent: "center",
|
|
271
|
+
fontWeight: 600,
|
|
272
|
+
}}
|
|
273
|
+
>
|
|
274
|
+
<Typography.Text
|
|
275
|
+
strong
|
|
276
|
+
style={{ color: token.colorTextLightSolid }}
|
|
277
|
+
>
|
|
278
|
+
{collapsed ? collapsedBrand : brand}
|
|
279
|
+
</Typography.Text>
|
|
280
|
+
</div>
|
|
281
|
+
<NavMenu
|
|
282
|
+
menuItems={menuItems}
|
|
283
|
+
selectedKeys={selectedKeys}
|
|
284
|
+
inlineCollapsed={collapsed}
|
|
285
|
+
openKeys={openKeys}
|
|
286
|
+
onOpenChange={handleOpenChange}
|
|
287
|
+
onNavigate={goNav}
|
|
288
|
+
/>
|
|
289
|
+
</Layout.Sider>
|
|
290
|
+
)}
|
|
291
|
+
{isMobile && (
|
|
292
|
+
<Drawer
|
|
293
|
+
title={
|
|
294
|
+
<Typography.Text
|
|
295
|
+
strong
|
|
296
|
+
style={{ color: token.colorTextLightSolid }}
|
|
297
|
+
>
|
|
298
|
+
{drawerTitle}
|
|
299
|
+
</Typography.Text>
|
|
300
|
+
}
|
|
301
|
+
placement="left"
|
|
302
|
+
size={280}
|
|
303
|
+
onClose={() => setMobileNavOpen(false)}
|
|
304
|
+
open={mobileNavOpen}
|
|
305
|
+
styles={{
|
|
306
|
+
header: {
|
|
307
|
+
background: siderBg,
|
|
308
|
+
borderBottom: `1px solid ${token.colorSplit}`,
|
|
309
|
+
},
|
|
310
|
+
body: { padding: 0, background: siderBg },
|
|
311
|
+
}}
|
|
312
|
+
destroyOnHidden
|
|
313
|
+
>
|
|
314
|
+
<NavMenu
|
|
315
|
+
menuItems={menuItems}
|
|
316
|
+
selectedKeys={selectedKeys}
|
|
317
|
+
inlineCollapsed={false}
|
|
318
|
+
openKeys={openKeys}
|
|
319
|
+
onOpenChange={handleOpenChange}
|
|
320
|
+
onNavigate={goNav}
|
|
321
|
+
/>
|
|
322
|
+
</Drawer>
|
|
323
|
+
)}
|
|
324
|
+
<Layout>
|
|
325
|
+
<Layout.Header
|
|
326
|
+
style={{
|
|
327
|
+
background: token.colorBgContainer,
|
|
328
|
+
paddingInline: token.paddingLG,
|
|
329
|
+
display: "flex",
|
|
330
|
+
alignItems: "center",
|
|
331
|
+
gap: token.marginSM,
|
|
332
|
+
lineHeight: "normal",
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
335
|
+
{isMobile && (
|
|
336
|
+
<Button
|
|
337
|
+
type="text"
|
|
338
|
+
icon={<MenuOutlined />}
|
|
339
|
+
onClick={() => setMobileNavOpen(true)}
|
|
340
|
+
aria-label="Open navigation"
|
|
341
|
+
/>
|
|
342
|
+
)}
|
|
343
|
+
<div style={{ flex: 1, minWidth: 0 }} />
|
|
344
|
+
{headerExtras}
|
|
345
|
+
<ThemeToolbar />
|
|
346
|
+
<Dropdown
|
|
347
|
+
menu={{
|
|
348
|
+
items: resolvedUserItems,
|
|
349
|
+
onClick: onDropdownMenuClick,
|
|
350
|
+
}}
|
|
351
|
+
trigger={["click"]}
|
|
352
|
+
>
|
|
353
|
+
<Button
|
|
354
|
+
type="text"
|
|
355
|
+
style={{
|
|
356
|
+
display: "inline-flex",
|
|
357
|
+
alignItems: "center",
|
|
358
|
+
gap: token.marginXS,
|
|
359
|
+
maxWidth: isMobile ? 44 : undefined,
|
|
360
|
+
paddingInline: isMobile ? token.paddingXS : undefined,
|
|
361
|
+
}}
|
|
362
|
+
aria-label="Account menu"
|
|
363
|
+
>
|
|
364
|
+
<Avatar size="small" icon={<UserOutlined />} />
|
|
365
|
+
{!isMobile && (
|
|
366
|
+
<Typography.Text type="secondary">User</Typography.Text>
|
|
367
|
+
)}
|
|
368
|
+
</Button>
|
|
369
|
+
</Dropdown>
|
|
370
|
+
</Layout.Header>
|
|
371
|
+
<Layout.Content
|
|
372
|
+
style={{
|
|
373
|
+
margin: isMobile ? token.marginSM : token.marginLG,
|
|
374
|
+
}}
|
|
375
|
+
>
|
|
376
|
+
<Outlet />
|
|
377
|
+
</Layout.Content>
|
|
378
|
+
</Layout>
|
|
379
|
+
</Layout>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Flex, theme } from "antd";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { ThemeToolbar } from "../components/ThemeToolbar";
|
|
4
|
+
|
|
5
|
+
export type AuthPageLayoutProps = {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** Brand mark centered above the card. */
|
|
8
|
+
brand?: ReactNode;
|
|
9
|
+
/** Row below the card (e.g. link to register or login). */
|
|
10
|
+
footer?: ReactNode;
|
|
11
|
+
showThemeToolbar?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function AuthPageLayout({
|
|
15
|
+
children,
|
|
16
|
+
brand,
|
|
17
|
+
footer,
|
|
18
|
+
showThemeToolbar = true,
|
|
19
|
+
}: AuthPageLayoutProps) {
|
|
20
|
+
const { token } = theme.useToken();
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Flex
|
|
24
|
+
vertical
|
|
25
|
+
align="stretch"
|
|
26
|
+
style={{
|
|
27
|
+
height: "100dvh",
|
|
28
|
+
maxHeight: "100dvh",
|
|
29
|
+
width: "100%",
|
|
30
|
+
overflow: "hidden",
|
|
31
|
+
background: token.colorBgLayout,
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{showThemeToolbar ? (
|
|
35
|
+
<Flex
|
|
36
|
+
justify="flex-end"
|
|
37
|
+
style={{
|
|
38
|
+
flexShrink: 0,
|
|
39
|
+
width: "100%",
|
|
40
|
+
padding: 16,
|
|
41
|
+
background: token.colorBgLayout,
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<ThemeToolbar />
|
|
45
|
+
</Flex>
|
|
46
|
+
) : null}
|
|
47
|
+
{brand ? (
|
|
48
|
+
<div
|
|
49
|
+
style={{
|
|
50
|
+
flexShrink: 0,
|
|
51
|
+
textAlign: "center",
|
|
52
|
+
padding: "0 24px 16px",
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{brand}
|
|
56
|
+
</div>
|
|
57
|
+
) : null}
|
|
58
|
+
<Flex
|
|
59
|
+
flex={1}
|
|
60
|
+
vertical
|
|
61
|
+
align="center"
|
|
62
|
+
justify="flex-start"
|
|
63
|
+
style={{
|
|
64
|
+
width: "100%",
|
|
65
|
+
minHeight: 0,
|
|
66
|
+
padding: "0 24px 24px",
|
|
67
|
+
overflow: "auto",
|
|
68
|
+
overflowX: "hidden",
|
|
69
|
+
background: token.colorBgLayout,
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
{footer ? (
|
|
74
|
+
<div style={{ marginTop: 16, width: "100%", maxWidth: 520 }}>
|
|
75
|
+
{footer}
|
|
76
|
+
</div>
|
|
77
|
+
) : null}
|
|
78
|
+
</Flex>
|
|
79
|
+
</Flex>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Button, Card, Form, Input, Typography } from "antd";
|
|
2
|
+
import { useNavigate } from "react-router-dom";
|
|
3
|
+
import { AuthAlternateLink } from "../components/AuthAlternateLink";
|
|
4
|
+
import { useAuth } from "../context/AuthProvider";
|
|
5
|
+
import { AuthPageLayout } from "../layouts/AuthPageLayout";
|
|
6
|
+
import type { LoginPageProps } from "../types";
|
|
7
|
+
|
|
8
|
+
export function LoginPage({
|
|
9
|
+
title = "Sign in",
|
|
10
|
+
description = "Use any username and password to continue.",
|
|
11
|
+
logo,
|
|
12
|
+
brand,
|
|
13
|
+
extraFields,
|
|
14
|
+
showThemeToolbar = true,
|
|
15
|
+
afterLoginPath = "/",
|
|
16
|
+
alternateAuth,
|
|
17
|
+
footer,
|
|
18
|
+
}: LoginPageProps) {
|
|
19
|
+
const { login } = useAuth();
|
|
20
|
+
const navigate = useNavigate();
|
|
21
|
+
const brandNode = brand ?? logo;
|
|
22
|
+
|
|
23
|
+
const resolvedFooter =
|
|
24
|
+
footer ??
|
|
25
|
+
(alternateAuth ? (
|
|
26
|
+
<AuthAlternateLink
|
|
27
|
+
prompt={alternateAuth.prompt ?? "Don't have an account?"}
|
|
28
|
+
linkText={alternateAuth.linkText}
|
|
29
|
+
to={alternateAuth.to}
|
|
30
|
+
/>
|
|
31
|
+
) : null);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<AuthPageLayout
|
|
35
|
+
brand={brandNode}
|
|
36
|
+
footer={resolvedFooter}
|
|
37
|
+
showThemeToolbar={showThemeToolbar}
|
|
38
|
+
>
|
|
39
|
+
<Card style={{ width: "100%", maxWidth: 360 }} title={title}>
|
|
40
|
+
{description ? (
|
|
41
|
+
<Typography.Paragraph type="secondary" style={{ marginTop: 0 }}>
|
|
42
|
+
{description}
|
|
43
|
+
</Typography.Paragraph>
|
|
44
|
+
) : null}
|
|
45
|
+
<Form
|
|
46
|
+
layout="vertical"
|
|
47
|
+
onFinish={async (values: Record<string, unknown>) => {
|
|
48
|
+
await login({
|
|
49
|
+
username: String(values.username ?? ""),
|
|
50
|
+
password: String(values.password ?? ""),
|
|
51
|
+
...values,
|
|
52
|
+
});
|
|
53
|
+
navigate(afterLoginPath, { replace: true });
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<Form.Item
|
|
57
|
+
name="username"
|
|
58
|
+
label="Username"
|
|
59
|
+
rules={[{ required: true, message: "Required" }]}
|
|
60
|
+
>
|
|
61
|
+
<Input autoComplete="username" />
|
|
62
|
+
</Form.Item>
|
|
63
|
+
<Form.Item
|
|
64
|
+
name="password"
|
|
65
|
+
label="Password"
|
|
66
|
+
rules={[{ required: true, message: "Required" }]}
|
|
67
|
+
>
|
|
68
|
+
<Input.Password autoComplete="current-password" />
|
|
69
|
+
</Form.Item>
|
|
70
|
+
{extraFields}
|
|
71
|
+
<Form.Item style={{ marginBottom: 0 }}>
|
|
72
|
+
<Button type="primary" htmlType="submit" block>
|
|
73
|
+
Log in
|
|
74
|
+
</Button>
|
|
75
|
+
</Form.Item>
|
|
76
|
+
</Form>
|
|
77
|
+
</Card>
|
|
78
|
+
</AuthPageLayout>
|
|
79
|
+
);
|
|
80
|
+
}
|