refine-mantine 1.0.0-dev.1

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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/dist/index.d.ts +276 -0
  4. package/dist/index.js +1576 -0
  5. package/package.json +89 -0
  6. package/src/App.tsx +33 -0
  7. package/src/Router.tsx +97 -0
  8. package/src/components/ColorSchemeToggle.tsx +13 -0
  9. package/src/components/breadcrumb/Breadcrumb.tsx +80 -0
  10. package/src/components/buttons/CloneButton.story.tsx +22 -0
  11. package/src/components/buttons/CloneButton.tsx +86 -0
  12. package/src/components/buttons/CreateButton.story.tsx +22 -0
  13. package/src/components/buttons/CreateButton.tsx +81 -0
  14. package/src/components/buttons/DeleteButton.story.tsx +22 -0
  15. package/src/components/buttons/DeleteButton.tsx +133 -0
  16. package/src/components/buttons/EditButton.story.tsx +22 -0
  17. package/src/components/buttons/EditButton.tsx +87 -0
  18. package/src/components/buttons/ExportButton.story.tsx +28 -0
  19. package/src/components/buttons/ExportButton.tsx +48 -0
  20. package/src/components/buttons/ImportButton.story.tsx +44 -0
  21. package/src/components/buttons/ImportButton.tsx +61 -0
  22. package/src/components/buttons/ListButton.story.tsx +25 -0
  23. package/src/components/buttons/ListButton.tsx +91 -0
  24. package/src/components/buttons/RefreshButton.story.tsx +22 -0
  25. package/src/components/buttons/RefreshButton.tsx +59 -0
  26. package/src/components/buttons/SaveButton.story.tsx +22 -0
  27. package/src/components/buttons/SaveButton.tsx +51 -0
  28. package/src/components/buttons/ShowButton.story.tsx +22 -0
  29. package/src/components/buttons/ShowButton.tsx +83 -0
  30. package/src/components/crud/Create.story.tsx +83 -0
  31. package/src/components/crud/Create.tsx +146 -0
  32. package/src/components/crud/Edit.story.tsx +173 -0
  33. package/src/components/crud/Edit.tsx +236 -0
  34. package/src/components/crud/List.story.tsx +98 -0
  35. package/src/components/crud/List.tsx +109 -0
  36. package/src/components/crud/Show.tsx +220 -0
  37. package/src/components/layout/Layout.story.tsx +28 -0
  38. package/src/components/layout/Layout.tsx +257 -0
  39. package/src/components/notification/AutoSaveIndicator.story.tsx +41 -0
  40. package/src/components/notification/AutoSaveIndicator.tsx +58 -0
  41. package/src/components/notification/Message.story.tsx +30 -0
  42. package/src/components/notification/Message.tsx +79 -0
  43. package/src/components/table/ColumnFilter.tsx +107 -0
  44. package/src/components/table/ColumnSorter.tsx +26 -0
  45. package/src/components/table/Table.story.tsx +146 -0
  46. package/src/components/table/Table.tsx +64 -0
  47. package/src/favicon.svg +1 -0
  48. package/src/hooks/useForm.ts +271 -0
  49. package/src/hooks/useOtp.ts +45 -0
  50. package/src/index.ts +36 -0
  51. package/src/main.tsx +4 -0
  52. package/src/pages/auth/DefaultTitle.tsx +13 -0
  53. package/src/pages/auth/ForgotPasswordPage.story.tsx +14 -0
  54. package/src/pages/auth/ForgotPasswordPage.tsx +128 -0
  55. package/src/pages/auth/LoginPage.story.tsx +98 -0
  56. package/src/pages/auth/LoginPage.tsx +256 -0
  57. package/src/pages/auth/RegisterPage.story.tsx +18 -0
  58. package/src/pages/auth/RegisterPage.tsx +151 -0
  59. package/src/pages/auth/UpdatePasswordPage.story.tsx +27 -0
  60. package/src/pages/auth/UpdatePasswordPage.tsx +124 -0
  61. package/src/pages/category/CategoryCreate.tsx +1 -0
  62. package/src/pages/category/CategoryEdit.tsx +1 -0
  63. package/src/pages/category/CategoryList.tsx +1 -0
  64. package/src/pages/category/CategoryShow.tsx +1 -0
  65. package/src/pages/product/ProductCreate.tsx +1 -0
  66. package/src/pages/product/ProductEdit.tsx +1 -0
  67. package/src/pages/product/ProductList.tsx +1 -0
  68. package/src/pages/product/ProductShow.tsx +1 -0
  69. package/src/providers/authProvider.ts +105 -0
  70. package/src/providers/i18nProvider.ts +7 -0
  71. package/src/providers/notificationProvider.tsx +122 -0
  72. package/src/resources.tsx +63 -0
  73. package/src/theme.ts +5 -0
  74. package/src/utils/paths.ts +52 -0
  75. package/src/utils/wait.ts +4 -0
  76. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,256 @@
1
+ import {
2
+ Anchor,
3
+ Button,
4
+ type ButtonProps,
5
+ Card,
6
+ type CardProps,
7
+ Center,
8
+ Collapse,
9
+ Divider,
10
+ Input,
11
+ LoadingOverlay,
12
+ type LoadingOverlayProps,
13
+ PinInput,
14
+ type PinInputProps,
15
+ ScrollArea,
16
+ type ScrollAreaProps,
17
+ Stack,
18
+ type StackProps,
19
+ Text,
20
+ TextInput,
21
+ type TextInputProps,
22
+ Title
23
+ } from "@mantine/core";
24
+ import { type FormValidateInput, isEmail, isNotEmpty, useForm } from "@mantine/form";
25
+ import { Link, type OAuthProvider, type LoginFormTypes as RefineLoginFormTypes, useLogin, useTranslate } from "@refinedev/core";
26
+ import { IconAt, IconLockPassword } from "@tabler/icons-react";
27
+ import { type ReactNode, useCallback } from "react";
28
+ import type { OtpHandler } from "@/hooks/useOtp";
29
+ import { DefaultTitle } from "./DefaultTitle";
30
+
31
+ export type TranslateFn = ReturnType<typeof useTranslate>;
32
+
33
+ export interface LoginArgs {
34
+ providerName?: string; // providerName prop is used by several AuthPage implementations
35
+ email?: string;
36
+ password?: string;
37
+ otpHandler?: OtpHandler;
38
+ translate?: TranslateFn;
39
+ [key: string]: unknown;
40
+ }
41
+
42
+ export interface OAuthProviderMantine extends OAuthProvider {
43
+ buttonProps?: ButtonProps;
44
+ }
45
+
46
+ export type LoginPageProps = {
47
+ providers?: OAuthProviderMantine[];
48
+ mutationVariables?: RefineLoginFormTypes;
49
+ registerLink?: string;
50
+ forgotPasswordLink?: string;
51
+ validate?: FormValidateInput<LoginForm>;
52
+ // customization
53
+ wrapperProps?: StackProps;
54
+ scrollAreaProps?: ScrollAreaProps;
55
+ emailFieldProps?: TextInputProps;
56
+ passwordFieldProps?: TextInputProps;
57
+ cardProps?: CardProps;
58
+ loadingOverlayProps?: LoadingOverlayProps;
59
+ otpInputProps?: PinInputProps;
60
+ submitButtonProps?: ButtonProps;
61
+ cancelButtonProps?: ButtonProps;
62
+ // components
63
+ icon?: ReactNode;
64
+ title?: ReactNode;
65
+ // otp promise handler
66
+ otpHandler?: OtpHandler;
67
+ } & (
68
+ {
69
+ method: "oauth";
70
+ providers: OAuthProvider[];
71
+ } | {
72
+ method: "otp" | "mfa" | "password";
73
+ }
74
+ );
75
+
76
+ interface LoginForm {
77
+ email: string;
78
+ password: string;
79
+ }
80
+
81
+ export const LoginPage: React.FC<LoginPageProps> = (p) => {
82
+ const translate = useTranslate();
83
+ const login = useLogin<LoginArgs>();
84
+
85
+ const { getInputProps, onSubmit, key } = useForm<LoginForm>({
86
+ initialValues: {
87
+ email: "",
88
+ password: "",
89
+ },
90
+ validate: {
91
+ email: isEmail(
92
+ translate("pages.login.validate.invalidEmail", "Email is not valid")
93
+ ),
94
+ password: (p.method === "password" || p.method === "mfa")
95
+ ? isNotEmpty(translate("pages.login.validate.passwordRequired", "Password is required"))
96
+ : undefined,
97
+ ...p.validate,
98
+ },
99
+ });
100
+
101
+ const handleProviderLogin = useCallback((provider: OAuthProvider) => {
102
+ login.mutate({
103
+ providerName: provider.name,
104
+ translate,
105
+ ...p.mutationVariables
106
+ });
107
+ }, [login, translate, p.mutationVariables]);
108
+
109
+ const handleLogin = onSubmit(({ email, password }) => {
110
+ login.mutate({
111
+ email,
112
+ password,
113
+ otpHandler: p.otpHandler,
114
+ translate,
115
+ ...p.mutationVariables,
116
+ });
117
+ });
118
+
119
+ return (
120
+ <Stack h="100vh" align="center" justify="center" {...p.wrapperProps}>
121
+ <ScrollArea type="never" {...p.scrollAreaProps}>
122
+ {p.icon ?? <DefaultTitle />}
123
+ <Card shadow="sm" padding="lg" radius="md" withBorder {...p.cardProps}>
124
+ <LoadingOverlay
125
+ visible={login.isPending && !p.otpHandler?.isPending}
126
+ {...p.loadingOverlayProps}
127
+ />
128
+ {p.title ?? (
129
+ <Title order={5} mb="lg" ta="center">
130
+ {translate("pages.login.title", "Sign in to your account")}
131
+ </Title>
132
+ )}
133
+ <Collapse in={!p.otpHandler?.isPending}>
134
+ {p.providers?.length && (
135
+ <Providers
136
+ providers={p.providers}
137
+ withDivider={p.method !== "oauth"}
138
+ onClickProvider={handleProviderLogin}
139
+ />
140
+ )}
141
+ </Collapse>
142
+ {p.method !== "oauth" && (
143
+ <form onSubmit={handleLogin}>
144
+ <Collapse in={!p.otpHandler?.isPending}>
145
+ <TextInput
146
+ mb="xs"
147
+ type="email"
148
+ label={translate("pages.login.fields.email", "Email")}
149
+ leftSection={<IconAt size={18} />}
150
+ placeholder={translate("pages.login.fields.emailPlaceholder", "name@example.com")}
151
+ key={key("email")}
152
+ {...getInputProps("email")}
153
+ {...p.emailFieldProps}
154
+ />
155
+ {(p.method === "mfa" || p.method === "password") && (
156
+ <TextInput
157
+ label={translate("pages.login.fields.password", "Password")}
158
+ leftSection={<IconLockPassword size={18} />}
159
+ placeholder={translate("pages.login.fields.passwordPlaceholder", "●●●●●●●●")}
160
+ type="password"
161
+ key={key("password")}
162
+ {...getInputProps("password")}
163
+ {...p.passwordFieldProps}
164
+ />
165
+ )}
166
+ {p.forgotPasswordLink &&
167
+ <Text size="xs" mt="xs" ta="end">
168
+ {/** biome-ignore lint/suspicious/noExplicitAny: refines type is messed up */}
169
+ <Anchor component={Link as any} to={p.forgotPasswordLink}>
170
+ {translate( "pages.login.buttons.forgotPassword", "Forgot password?")}
171
+ </Anchor>
172
+ </Text>
173
+ }
174
+ <Button
175
+ fullWidth
176
+ mt="lg"
177
+ type="submit"
178
+ {...p.submitButtonProps}
179
+ >
180
+ {translate("pages.login.signin", "Login")}
181
+ </Button>
182
+ {p.registerLink &&
183
+ <Text mt="lg" size="xs" ta="center">
184
+ {translate("pages.login.buttons.noAccount", "Don’t have an account?")}
185
+ {" "}
186
+ {/** biome-ignore lint/suspicious/noExplicitAny: that's fine */}
187
+ <Anchor component={Link as any} to={p.registerLink}>
188
+ {translate("pages.login.register", "Sign up")}
189
+ </Anchor>
190
+ </Text>
191
+ }
192
+ </Collapse>
193
+ <Collapse in={!!p.otpHandler?.isPending}>
194
+ <Input.Wrapper
195
+ label={translate("pages.login.fields.otp", "Enter or paste your auth token")}
196
+ >
197
+ <Center>
198
+ <PinInput
199
+ type="number"
200
+ oneTimeCode
201
+ disabled={login.isPending && !p.otpHandler?.isPending}
202
+ onComplete={p.otpHandler?.resolve}
203
+ {...p.otpInputProps}
204
+ />
205
+ </Center>
206
+ </Input.Wrapper>
207
+ <Button
208
+ fullWidth
209
+ mt="lg"
210
+ variant="outline"
211
+ onClick={p.otpHandler?.reject}
212
+ {...p.cancelButtonProps}
213
+ >
214
+ {translate("pages.login.buttons.cancel", "Cancel")}
215
+ </Button>
216
+ </Collapse>
217
+ </form>
218
+ )}
219
+ </Card>
220
+ </ScrollArea>
221
+ </Stack>
222
+ );
223
+ };
224
+
225
+ const Providers = (p: {
226
+ providers: OAuthProviderMantine[];
227
+ withDivider: boolean;
228
+ onClickProvider: (provider: OAuthProvider) => void;
229
+ }) => {
230
+ const translate = useTranslate();
231
+
232
+ return (
233
+ <>
234
+ <Stack gap={8}>
235
+ {p.providers.map((provider) =>
236
+ <Button
237
+ key={provider.name}
238
+ fullWidth
239
+ leftSection={provider.icon}
240
+ onClick={() => p.onClickProvider(provider)}
241
+ {...provider.buttonProps}
242
+ >
243
+ {provider.label ?? provider.name}
244
+ </Button>
245
+ )}
246
+ </Stack>
247
+ {p.withDivider && (
248
+ <Divider
249
+ my="md"
250
+ labelPosition="center"
251
+ label={translate("pages.login.divider", "or")}
252
+ />
253
+ )}
254
+ </>
255
+ );
256
+ }
@@ -0,0 +1,18 @@
1
+ import { RegisterPage } from './RegisterPage';
2
+
3
+ export default {
4
+ title: 'Auth/RegisterPage',
5
+ component: RegisterPage,
6
+ };
7
+
8
+ export const WithPassword = () =>
9
+ <RegisterPage />;
10
+
11
+ export const WithPasswordConfirmation = () =>
12
+ <RegisterPage withConfirmation />;
13
+
14
+ export const WithLink = () =>
15
+ <RegisterPage
16
+ withConfirmation
17
+ loginLink="/login"
18
+ />;
@@ -0,0 +1,151 @@
1
+ import {
2
+ Anchor,
3
+ Button,
4
+ type ButtonProps,
5
+ Card,
6
+ type CardProps,
7
+ LoadingOverlay,
8
+ type LoadingOverlayProps,
9
+ type PinInputProps,
10
+ ScrollArea,
11
+ type ScrollAreaProps,
12
+ Stack,
13
+ type StackProps,
14
+ Text,
15
+ TextInput,
16
+ type TextInputProps,
17
+ Title
18
+ } from "@mantine/core";
19
+ import { type FormValidateInput, isEmail, isNotEmpty, useForm } from "@mantine/form";
20
+ import { Link, type RegisterFormTypes, useRegister, useTranslate } from "@refinedev/core";
21
+ import { IconAt, IconLockPassword } from "@tabler/icons-react";
22
+ import type { ReactNode } from "react";
23
+ import { DefaultTitle } from "./DefaultTitle";
24
+
25
+ export interface RegisterPageProps {
26
+ withConfirmation?: boolean;
27
+ mutationVariables?: RegisterFormTypes;
28
+ loginLink?: string;
29
+ validate?: FormValidateInput<RegisterForm>;
30
+ // props
31
+ wrapperProps?: StackProps;
32
+ scrollAreaProps?: ScrollAreaProps;
33
+ emailFieldProps?: TextInputProps;
34
+ passwordFieldProps?: TextInputProps;
35
+ passwordConfirmationFieldProps?: TextInputProps;
36
+ cardProps?: CardProps;
37
+ loadingOverlayProps?: LoadingOverlayProps;
38
+ otpInputProps?: PinInputProps;
39
+ submitButtonProps?: ButtonProps;
40
+ cancelButtonProps?: ButtonProps;
41
+ // components
42
+ icon?: ReactNode;
43
+ title?: ReactNode;
44
+ }
45
+
46
+ export interface RegisterForm {
47
+ email: string;
48
+ password: string;
49
+ passwordConfirmation: string;
50
+ }
51
+
52
+ export const RegisterPage: React.FC<RegisterPageProps> = (p) => {
53
+ const translate = useTranslate();
54
+
55
+ const { getInputProps, onSubmit, key } = useForm<RegisterForm>({
56
+ initialValues: {
57
+ email: "",
58
+ password: "",
59
+ passwordConfirmation: "",
60
+ },
61
+ validate: {
62
+ email: isEmail(
63
+ translate("pages.register.validate.invalidEmail", "Email is not valid")
64
+ ),
65
+ password: isNotEmpty(translate("pages.register.validate.passwordRequired", "Password is required")),
66
+ passwordConfirmation: (value, form) =>
67
+ p.withConfirmation && form.password !== value
68
+ ? translate("pages.register.validate.passwordMismatch", "Passwords do not match")
69
+ : undefined,
70
+ ...p.validate,
71
+ },
72
+ });
73
+
74
+ const register = useRegister<RegisterFormTypes>();
75
+
76
+ const handleSubmit = onSubmit(({ email, password }) => {
77
+ register.mutate({ email, password, ...p.mutationVariables });
78
+ });
79
+
80
+ return (
81
+ <Stack h="100vh" align="center" justify="center" {...p.wrapperProps}>
82
+ <ScrollArea type="never" {...p.scrollAreaProps}>
83
+ {p.icon ?? <DefaultTitle />}
84
+ <Card shadow="sm" padding="lg" radius="md" withBorder {...p.cardProps}>
85
+ <LoadingOverlay
86
+ visible={register.isPending}
87
+ {...p.loadingOverlayProps}
88
+ />
89
+ {p.title ?? (
90
+ <Title order={5} mb="lg" ta="center">
91
+ {translate("pages.register.title", "Sign up for your account")}
92
+ </Title>
93
+ )}
94
+ <form onSubmit={handleSubmit}>
95
+ <TextInput
96
+ mb="xs"
97
+ type="email"
98
+ label={translate("pages.register.email", "Email")}
99
+ leftSection={<IconAt size={18} />}
100
+ placeholder={translate("pages.register.emailPlaceholder", "name@example.com")}
101
+ key={key("email")}
102
+ {...getInputProps("email")}
103
+ {...p.emailFieldProps}
104
+ />
105
+ <TextInput
106
+ mb="sm"
107
+ label={translate("pages.register.password", "Password")}
108
+ leftSection={<IconLockPassword size={18} />}
109
+ placeholder={translate("pages.register.passwordPlaceholder", "●●●●●●●●")}
110
+ type="password"
111
+ key={key("password")}
112
+ {...getInputProps("password")}
113
+ {...p.passwordFieldProps}
114
+ />
115
+ {p.withConfirmation && (
116
+ <TextInput
117
+ mb="sm"
118
+ label={translate("pages.register.passwordConfirmation", "Confirm Password")}
119
+ leftSection={<IconLockPassword size={18} />}
120
+ placeholder={translate("pages.register.passwordConfirmationPlaceholder", "●●●●●●●●")}
121
+ type="password"
122
+ key={key("passwordConfirmation")}
123
+ {...getInputProps("passwordConfirmation")}
124
+ {...p.passwordConfirmationFieldProps}
125
+ />
126
+ )}
127
+ <Button
128
+ mt="lg"
129
+ fullWidth
130
+ type="submit"
131
+ disabled={register.isPending}
132
+ {...p.submitButtonProps}
133
+ >
134
+ {translate("pages.register.submit", "Sign up")}
135
+ </Button>
136
+ </form>
137
+ {p.loginLink &&
138
+ <Text mt="lg" size="xs" ta="center">
139
+ {translate("pages.register.haveAccount", "Have an account?")}
140
+ {" "}
141
+ {/** biome-ignore lint/suspicious/noExplicitAny: refines types are messed up */}
142
+ <Anchor component={Link as any} to={p.loginLink}>
143
+ {translate("pages.register.login", "Sign in")}
144
+ </Anchor>
145
+ </Text>
146
+ }
147
+ </Card>
148
+ </ScrollArea>
149
+ </Stack>
150
+ );
151
+ };
@@ -0,0 +1,27 @@
1
+ import { hasLength } from '@mantine/form';
2
+ import { UpdatePasswordPage } from './UpdatePasswordPage';
3
+
4
+ export default {
5
+ title: 'Auth/UpdatePasswordPage',
6
+ component: UpdatePasswordPage,
7
+ };
8
+
9
+ export const WithPasswordConfirmation = () =>
10
+ <UpdatePasswordPage />;
11
+
12
+ export const WithAsterisk = () =>
13
+ <UpdatePasswordPage
14
+ passwordFieldProps={{
15
+ withAsterisk: true,
16
+ }}
17
+ confirmPasswordFieldProps={{
18
+ withAsterisk: true,
19
+ }}
20
+ />;
21
+
22
+ export const WithCustomValidation = () =>
23
+ <UpdatePasswordPage
24
+ validate={{
25
+ password: hasLength({ min: 2, max: 10 }, 'Password must be 2-10 characters long'),
26
+ }}
27
+ />;
@@ -0,0 +1,124 @@
1
+ import {
2
+ Button,
3
+ type ButtonProps,
4
+ Card,
5
+ type CardProps,
6
+ LoadingOverlay,
7
+ type LoadingOverlayProps,
8
+ Stack,
9
+ type StackProps,
10
+ TextInput,
11
+ type TextInputProps,
12
+ Title,
13
+ } from "@mantine/core";
14
+ import { type FormValidateInput, isNotEmpty } from "@mantine/form";
15
+ import {
16
+ type UpdatePasswordFormTypes,
17
+ useParsed,
18
+ useTranslate,
19
+ useUpdatePassword,
20
+ } from "@refinedev/core";
21
+ import { IconLockPassword } from "@tabler/icons-react";
22
+ import type { ReactNode } from "react";
23
+ import { useForm } from "@/hooks/useForm";
24
+ import { DefaultTitle } from "./DefaultTitle";
25
+
26
+ export interface UpdatePasswordPageProps {
27
+ mutationVariables?: UpdatePasswordFormTypes;
28
+ validate?: FormValidateInput<UpdatePasswordFormTypes>;
29
+ withToken?: boolean;
30
+ // props
31
+ wrapperProps?: StackProps;
32
+ passwordFieldProps?: TextInputProps;
33
+ confirmPasswordFieldProps?: TextInputProps;
34
+ cardProps?: CardProps;
35
+ loadingOverlayProps?: LoadingOverlayProps;
36
+ submitButtonProps?: ButtonProps;
37
+ // components
38
+ icon?: ReactNode;
39
+ title?: ReactNode;
40
+ }
41
+
42
+ export const UpdatePasswordPage: React.FC<UpdatePasswordPageProps> = (p) => {
43
+ const translate = useTranslate();
44
+ const { mutate: updatePassword, isPending } = useUpdatePassword();
45
+
46
+ const { params } = useParsed<{ token: string }>();
47
+ const token = params?.token;
48
+
49
+ const { getInputProps, onSubmit, key } = useForm({
50
+ initialValues: {
51
+ password: "",
52
+ confirmPassword: "",
53
+ },
54
+ validate: {
55
+ password: isNotEmpty(translate("pages.updatePassword.validate.passwordRequired", "Password is required")),
56
+ confirmPassword: (value, form) =>
57
+ form.password !== value
58
+ ? translate("pages.updatePassword.validate.passwordMismatch", "Passwords do not match")
59
+ : undefined,
60
+ ...p.validate,
61
+ },
62
+ });
63
+
64
+ const handleSubmit = onSubmit(({password, confirmPassword}) => {
65
+ if (!p.withToken || token) {
66
+ updatePassword({
67
+ password,
68
+ confirmPassword,
69
+ token,
70
+ translate,
71
+ });
72
+ }
73
+ });
74
+
75
+ return (
76
+ <Stack h="100vh" align="center" justify="center" {...p.wrapperProps}>
77
+ {p.icon ?? <DefaultTitle />}
78
+ <Card shadow="sm" padding="lg" radius="md" withBorder {...p.cardProps}>
79
+ <LoadingOverlay
80
+ visible={isPending}
81
+ {...p.loadingOverlayProps}
82
+ />
83
+ {p.title ?? (
84
+ <Title order={5} mb="lg" ta="center">
85
+ {translate("pages.updatePassword.title", "Update Password")}
86
+ </Title>
87
+ )}
88
+ <form onSubmit={handleSubmit}>
89
+ <TextInput
90
+ mb="sm"
91
+ label={translate("pages.updatePassword.fields.password", "New Password")}
92
+ leftSection={<IconLockPassword size={18} />}
93
+ placeholder="●●●●●●●●"
94
+ type="password"
95
+ key={key("password")}
96
+ {...getInputProps("password")}
97
+ {...p.passwordFieldProps}
98
+ />
99
+ <TextInput
100
+ mb="lg"
101
+ label={translate(
102
+ "pages.updatePassword.fields.confirmPassword",
103
+ "Confirm New Password",
104
+ )}
105
+ leftSection={<IconLockPassword size={18} />}
106
+ placeholder="●●●●●●●●"
107
+ type="password"
108
+ key={key("confirmPassword")}
109
+ {...getInputProps("confirmPassword")}
110
+ {...p.confirmPasswordFieldProps}
111
+ />
112
+ <Button
113
+ type="submit"
114
+ fullWidth
115
+ disabled={isPending}
116
+ {...p.submitButtonProps}
117
+ >
118
+ {translate("pages.updatePassword.buttons.submit", "Update")}
119
+ </Button>
120
+ </form>
121
+ </Card>
122
+ </Stack>
123
+ );
124
+ };
@@ -0,0 +1 @@
1
+ export const CategoryCreate = () => <></>;
@@ -0,0 +1 @@
1
+ export const CategoryEdit = () => <></>;
@@ -0,0 +1 @@
1
+ export const CategoryList = () => <></>;
@@ -0,0 +1 @@
1
+ export const CategoryShow = () => <></>;
@@ -0,0 +1 @@
1
+ export const ProductCreate = () => <></>;
@@ -0,0 +1 @@
1
+ export const ProductEdit = () => <></>;
@@ -0,0 +1 @@
1
+ export const ProductList = () => <></>;
@@ -0,0 +1 @@
1
+ export const ProductShow = () => <></>;