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,271 @@
1
+ import { get, has, set } from "@/utils/paths";
2
+ import type { UseFormInput } from "@mantine/form";
3
+ import {
4
+ type UseFormReturnType as UseMantineFormReturnType,
5
+ useForm as useMantineForm,
6
+ } from "@mantine/form";
7
+ import { useDebouncedCallback } from "@mantine/hooks";
8
+ import {
9
+ type BaseRecord,
10
+ type HttpError,
11
+ type UseFormProps as UseFormCoreProps,
12
+ type UseFormReturnType as UseFormReturnTypeCore,
13
+ flattenObjectKeys,
14
+ useForm as useFormCore,
15
+ useRefineContext,
16
+ useTranslate,
17
+ useWarnAboutChange,
18
+ } from "@refinedev/core";
19
+ import type React from "react";
20
+ import { useEffect } from "react";
21
+
22
+ type FormVariableType<TVariables, TTransformed> = ReturnType<
23
+ NonNullable<
24
+ UseFormInput<
25
+ TVariables,
26
+ (values: TVariables) => TTransformed
27
+ >["transformValues"]
28
+ >
29
+ >;
30
+
31
+ export type UseFormReturnType<
32
+ TQueryFnData extends BaseRecord = BaseRecord,
33
+ TError extends HttpError = HttpError,
34
+ TVariables = Record<string, unknown>,
35
+ TTransformed = TVariables,
36
+ TData extends BaseRecord = TQueryFnData,
37
+ TResponse extends BaseRecord = TData,
38
+ TResponseError extends HttpError = TError,
39
+ > = UseMantineFormReturnType<
40
+ TVariables,
41
+ (values: TVariables) => TTransformed
42
+ > & {
43
+ refineCore: UseFormReturnTypeCore<
44
+ TQueryFnData,
45
+ TError,
46
+ FormVariableType<TVariables, TTransformed>,
47
+ TData,
48
+ TResponse,
49
+ TResponseError
50
+ >;
51
+ saveButtonProps: {
52
+ disabled: boolean;
53
+ onClick: (e: React.PointerEvent<HTMLButtonElement>) => void;
54
+ };
55
+ };
56
+
57
+ export type UseFormProps<
58
+ TQueryFnData extends BaseRecord = BaseRecord,
59
+ TError extends HttpError = HttpError,
60
+ TVariables = Record<string, unknown>,
61
+ TTransformed = TVariables,
62
+ TData extends BaseRecord = TQueryFnData,
63
+ TResponse extends BaseRecord = TData,
64
+ TResponseError extends HttpError = TError,
65
+ > = {
66
+ refineCoreProps?: UseFormCoreProps<
67
+ TQueryFnData,
68
+ TError,
69
+ FormVariableType<TVariables, TTransformed>,
70
+ TData,
71
+ TResponse,
72
+ TResponseError
73
+ > & {
74
+ warnWhenUnsavedChanges?: boolean;
75
+ };
76
+ } & UseFormInput<TVariables, (values: TVariables) => TTransformed> & {
77
+ /**
78
+ * Disables server-side validation
79
+ * @default false
80
+ * @see {@link https://refine.dev/docs/advanced-tutorials/forms/server-side-form-validation/}
81
+ */
82
+ disableServerSideValidation?: boolean;
83
+ };
84
+
85
+ export const useForm = <
86
+ TQueryFnData extends BaseRecord = BaseRecord,
87
+ TError extends HttpError = HttpError,
88
+ // biome-ignore lint/suspicious/noExplicitAny: that's fine
89
+ TVariables extends Record<string, any> = Record<string, any>,
90
+ TTransformed = TVariables,
91
+ TData extends BaseRecord = TQueryFnData,
92
+ TResponse extends BaseRecord = TData,
93
+ TResponseError extends HttpError = TError,
94
+ >({
95
+ refineCoreProps,
96
+ disableServerSideValidation: disableServerSideValidationProp = false,
97
+ ...rest
98
+ }: UseFormProps<
99
+ TQueryFnData,
100
+ TError,
101
+ TVariables,
102
+ TTransformed,
103
+ TData,
104
+ TResponse,
105
+ TResponseError
106
+ > = {}): UseFormReturnType<
107
+ TQueryFnData,
108
+ TError,
109
+ TVariables,
110
+ TTransformed,
111
+ TData,
112
+ TResponse,
113
+ TResponseError
114
+ > => {
115
+ const { options } = useRefineContext();
116
+ const disableServerSideValidation =
117
+ options?.disableServerSideValidation || disableServerSideValidationProp;
118
+
119
+ const translate = useTranslate();
120
+
121
+ const warnWhenUnsavedChangesProp = refineCoreProps?.warnWhenUnsavedChanges;
122
+
123
+ const { warnWhenUnsavedChanges: warnWhenUnsavedChangesRefine, setWarnWhen } =
124
+ useWarnAboutChange();
125
+ const warnWhenUnsavedChanges =
126
+ warnWhenUnsavedChangesProp ?? warnWhenUnsavedChangesRefine;
127
+
128
+ const useMantineFormResult = useMantineForm<
129
+ TVariables,
130
+ (values: TVariables) => TTransformed
131
+ >({
132
+ ...rest,
133
+ });
134
+
135
+ const {
136
+ setValues,
137
+ onSubmit: onMantineSubmit,
138
+ isDirty,
139
+ resetDirty,
140
+ setFieldError,
141
+ values,
142
+ } = useMantineFormResult;
143
+
144
+ const useFormCoreResult = useFormCore<
145
+ TQueryFnData,
146
+ TError,
147
+ FormVariableType<TVariables, TTransformed>,
148
+ TData,
149
+ TResponse,
150
+ TResponseError
151
+ >({
152
+ ...refineCoreProps,
153
+ onMutationError: (error, _variables, _context) => {
154
+ if (disableServerSideValidation) {
155
+ refineCoreProps?.onMutationError?.(error, _variables, _context);
156
+ return;
157
+ }
158
+
159
+ const errors = error?.errors;
160
+
161
+ for (const key in errors) {
162
+ const fieldError = errors[key];
163
+
164
+ let newError = "";
165
+
166
+ if (Array.isArray(fieldError)) {
167
+ newError = fieldError.join(" ");
168
+ }
169
+
170
+ if (typeof fieldError === "string") {
171
+ newError = fieldError;
172
+ }
173
+
174
+ if (typeof fieldError === "boolean") {
175
+ newError = "Field is not valid.";
176
+ }
177
+
178
+ if (typeof fieldError === "object" && "key" in fieldError) {
179
+ const translatedMessage = translate(
180
+ fieldError.key,
181
+ fieldError.message,
182
+ );
183
+
184
+ newError = translatedMessage;
185
+ }
186
+ setFieldError(key, newError);
187
+ }
188
+
189
+ refineCoreProps?.onMutationError?.(error, _variables, _context);
190
+ },
191
+ });
192
+
193
+ const { query, formLoading, onFinish, onFinishAutoSave } = useFormCoreResult;
194
+
195
+ // biome-ignore lint/correctness/useExhaustiveDependencies: code taken from refine, will check later
196
+ useEffect(() => {
197
+ if (typeof query?.data !== "undefined") {
198
+ // biome-ignore lint/suspicious/noExplicitAny: that's fine
199
+ const fields: any = {};
200
+
201
+ const registeredFields = flattenObjectKeys(rest.initialValues ?? {});
202
+
203
+ const data = query?.data?.data ?? {};
204
+
205
+ Object.keys(registeredFields).forEach((key) => {
206
+ const hasValue = has(data, key);
207
+ const dataValue = get(data, key);
208
+
209
+ if (hasValue) {
210
+ set(fields, key, dataValue);
211
+ }
212
+ });
213
+
214
+ setValues(fields);
215
+ resetDirty(fields);
216
+ }
217
+ }, [query?.data]);
218
+
219
+ const isValuesChanged = isDirty();
220
+
221
+ // biome-ignore lint/correctness/useExhaustiveDependencies: code taken from refine, will check later
222
+ useEffect(() => {
223
+ if (warnWhenUnsavedChanges) {
224
+ setWarnWhen(isValuesChanged);
225
+ }
226
+ }, [isValuesChanged]);
227
+
228
+ // workaround since refines auto save is buggy
229
+ // https://github.com/refinedev/refine/issues/7082
230
+ const handleAutoSave = useDebouncedCallback((transformedValues: TTransformed) => {
231
+ onFinishAutoSave(transformedValues)
232
+ .catch((error) => error);
233
+ }, refineCoreProps?.autoSave?.debounce ?? 1000);
234
+
235
+ // biome-ignore lint/correctness/useExhaustiveDependencies: code taken from refine, will check later
236
+ useEffect(() => {
237
+ if (isValuesChanged && refineCoreProps?.autoSave && values) {
238
+ setWarnWhen(false);
239
+
240
+ const transformedValues = rest.transformValues
241
+ ? rest.transformValues(values)
242
+ : (values as unknown as TTransformed);
243
+
244
+ handleAutoSave(transformedValues);
245
+ }
246
+ }, [values]);
247
+
248
+ const onSubmit: (typeof useMantineFormResult)["onSubmit"] =
249
+ (handleSubmit, handleValidationFailure) => async (e) => {
250
+ setWarnWhen(false);
251
+ return await onMantineSubmit(handleSubmit, handleValidationFailure)(e);
252
+ };
253
+
254
+ const saveButtonProps = {
255
+ disabled: formLoading,
256
+ onClick: (e: React.PointerEvent<HTMLButtonElement>) => {
257
+ onSubmit(
258
+ (v) => onFinish(v).catch((error) => error),
259
+ () => false,
260
+ // @ts-expect-error event type is not compatible with pointer event
261
+ )(e);
262
+ },
263
+ };
264
+
265
+ return {
266
+ ...useMantineFormResult,
267
+ onSubmit,
268
+ refineCore: useFormCoreResult,
269
+ saveButtonProps,
270
+ };
271
+ };
@@ -0,0 +1,45 @@
1
+
2
+ import { useCallback, useEffect, useState } from "react";
3
+
4
+ export type OtpHandler = ReturnType<typeof useOtp>;
5
+
6
+ export const useOtp = () => {
7
+ const [otpPromise, setOtpPromise] = useState<{
8
+ resolve: (otp: string) => void;
9
+ reject: () => void;
10
+ }>();
11
+
12
+ useEffect(() => {
13
+ return () => {
14
+ otpPromise?.reject();
15
+ };
16
+ }, [otpPromise]);
17
+
18
+ const request = useCallback(() => {
19
+ return new Promise<string>((resolve, reject) => {
20
+ setOtpPromise({ resolve, reject });
21
+ });
22
+ }, []);
23
+
24
+ const reject = useCallback(() => {
25
+ otpPromise?.reject();
26
+ setOtpPromise(undefined);
27
+ }, [otpPromise]);
28
+
29
+ const resolve = useCallback(
30
+ (otp: string) => {
31
+ otpPromise?.resolve(otp);
32
+ setOtpPromise(undefined);
33
+ },
34
+ [otpPromise],
35
+ );
36
+
37
+ const isPending = !!otpPromise;
38
+
39
+ return {
40
+ isPending,
41
+ request,
42
+ reject,
43
+ resolve,
44
+ };
45
+ };
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ // breadcrumbs
2
+ export * from "@/components/breadcrumb/Breadcrumb";
3
+ // buttons
4
+ export * from "@/components/buttons/CreateButton";
5
+ export * from "@/components/buttons/DeleteButton";
6
+ export * from "@/components/buttons/EditButton";
7
+ export * from "@/components/buttons/ListButton";
8
+ export * from "@/components/buttons/RefreshButton";
9
+ export * from "@/components/buttons/SaveButton";
10
+ export * from "@/components/buttons/ShowButton";
11
+ // crud
12
+ export * from "@/components/crud/Create";
13
+ export * from "@/components/crud/Edit";
14
+ export * from "@/components/crud/List";
15
+ export * from "@/components/crud/Show";
16
+ // layout
17
+ export * from "@/components/layout/Layout";
18
+ // notification
19
+ export * from "@/components/notification/Message";
20
+ // table
21
+ export * from "@/components/table/ColumnFilter";
22
+ export * from "@/components/table/ColumnSorter";
23
+ export * from "@/components/table/Table";
24
+ // uncategorized components
25
+ export * from "@/components/notification/AutoSaveIndicator";
26
+ export * from "@/components/ColorSchemeToggle";
27
+ // hooks
28
+ export * from "@/hooks/useForm";
29
+ export * from "@/hooks/useOtp";
30
+ // pages
31
+ export * from "@/pages/auth/LoginPage";
32
+ export * from "@/pages/auth/RegisterPage";
33
+ // providers
34
+ export type * from "@/providers/authProvider";
35
+ export * from "@/providers/notificationProvider";
36
+
package/src/main.tsx ADDED
@@ -0,0 +1,4 @@
1
+ import ReactDOM from 'react-dom/client';
2
+ import { App } from './App';
3
+
4
+ ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
@@ -0,0 +1,13 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { useRefineOptions } from "@refinedev/core";
3
+
4
+ export const DefaultTitle = () => {
5
+ const { title } = useRefineOptions();
6
+
7
+ return (
8
+ <Stack align="center" mb="lg">
9
+ {title.icon}
10
+ {title.text}
11
+ </Stack>
12
+ );
13
+ }
@@ -0,0 +1,14 @@
1
+ import { ForgotPasswordPage } from './ForgotPasswordPage';
2
+
3
+ export default {
4
+ title: 'Auth/ForgotPasswordPage',
5
+ component: ForgotPasswordPage,
6
+ };
7
+
8
+ export const Basic = () =>
9
+ <ForgotPasswordPage />;
10
+
11
+ export const WithLoginLink = () =>
12
+ <ForgotPasswordPage
13
+ loginLink="/login"
14
+ />;
@@ -0,0 +1,128 @@
1
+ /** biome-ignore-all lint/correctness/useUniqueElementIds: test ids for playwright */
2
+ import {
3
+ Anchor,
4
+ Button,
5
+ type ButtonProps,
6
+ Card,
7
+ type CardProps,
8
+ LoadingOverlay,
9
+ type LoadingOverlayProps,
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 } from "@mantine/form";
20
+ import { Link, useForgotPassword, useTranslate } from "@refinedev/core";
21
+ import { IconAt } 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 ForgotPasswordPageProps {
27
+ loginLink?: string;
28
+ validate?: FormValidateInput<ForgotPasswordForm>;
29
+ // customization
30
+ wrapperProps?: StackProps;
31
+ scrollAreaProps?: ScrollAreaProps;
32
+ emailFieldProps?: TextInputProps;
33
+ cardProps?: CardProps;
34
+ loadingOverlayProps?: LoadingOverlayProps;
35
+ submitButtonProps?: ButtonProps;
36
+ // components
37
+ icon?: ReactNode;
38
+ title?: ReactNode;
39
+ }
40
+
41
+ export interface ForgotPasswordForm {
42
+ email: string;
43
+ }
44
+
45
+ export const ForgotPasswordPage: React.FC<ForgotPasswordPageProps> = (p: {
46
+ loginLink?: string;
47
+ validate?: FormValidateInput<ForgotPasswordForm>;
48
+ // customization
49
+ wrapperProps?: StackProps;
50
+ scrollAreaProps?: ScrollAreaProps;
51
+ emailFieldProps?: TextInputProps;
52
+ cardProps?: CardProps;
53
+ loadingOverlayProps?: LoadingOverlayProps;
54
+ submitButtonProps?: ButtonProps;
55
+ // components
56
+ icon?: ReactNode;
57
+ title?: ReactNode;
58
+ }) => {
59
+ const translate = useTranslate();
60
+ const { mutate: forgotPassword, isPending } = useForgotPassword();
61
+
62
+ const { getInputProps, onSubmit, key } = useForm({
63
+ initialValues: {
64
+ email: "",
65
+ },
66
+ validate: {
67
+ email: isEmail(
68
+ translate("pages.login.invalidEmail", "Email is not valid")
69
+ ),
70
+ },
71
+ ...p.validate,
72
+ });
73
+
74
+ const handleResetPassword = onSubmit(({ email }) => {
75
+ forgotPassword({ email, translate });
76
+ });
77
+
78
+ return (
79
+ <Stack h="100vh" align="center" justify="center" {...p.wrapperProps}>
80
+ <ScrollArea type="never" {...p.scrollAreaProps}>
81
+ {p.icon ?? <DefaultTitle />}
82
+ <Card shadow="sm" padding="lg" radius="md" withBorder {...p.cardProps}>
83
+ <LoadingOverlay
84
+ visible={isPending }
85
+ {...p.loadingOverlayProps}
86
+ />
87
+ {p.title ?? (
88
+ <Title order={5} mb="lg" ta="center">
89
+ {translate("pages.forgotPassword.title", "Reset your password")}
90
+ </Title>
91
+ )}
92
+ <form onSubmit={handleResetPassword}>
93
+ <TextInput
94
+ mb="lg"
95
+ type="email"
96
+ label={translate("pages.forgotPassword.fields.email", "Email")}
97
+ leftSection={<IconAt size={18} />}
98
+ placeholder={translate("pages.forgotPassword.fields.emailPlaceholder", "name@example.com")}
99
+ key={key("email")}
100
+ {...getInputProps("email")}
101
+ {...p.emailFieldProps}
102
+ />
103
+ <Button
104
+ fullWidth
105
+ variant="outline"
106
+ type="submit"
107
+ {...p.submitButtonProps}
108
+ >
109
+ {translate("pages.forgotPassword.buttons.submit", "Send reset instructions")}
110
+ </Button>
111
+ {p.loginLink &&
112
+ <Text mt="lg" size="xs" ta="center">
113
+ {translate(
114
+ "pages.register.buttons.haveAccount",
115
+ "Have an account? ",
116
+ )}{" "}
117
+ {/** biome-ignore lint/suspicious/noExplicitAny: that's fine */}
118
+ <Anchor component={Link as any} to={p.loginLink}>
119
+ {translate("pages.login.signin", "Sign in")}
120
+ </Anchor>
121
+ </Text>
122
+ }
123
+ </form>
124
+ </Card>
125
+ </ScrollArea>
126
+ </Stack>
127
+ );
128
+ };
@@ -0,0 +1,98 @@
1
+ import type { Meta } from "@storybook/react";
2
+ import { IconBrandApple, IconBrandFacebook, IconBrandGoogle } from '@tabler/icons-react';
3
+ import { useOtp } from '@/hooks/useOtp';
4
+ import { LoginPage } from './LoginPage';
5
+
6
+ export default {
7
+ title: 'Auth/LoginPage',
8
+ component: LoginPage,
9
+ } satisfies Meta<typeof LoginPage>;
10
+
11
+ export const WithPassword = () =>
12
+ <LoginPage method='password' />;
13
+
14
+ export const WithProviders = () =>
15
+ <LoginPage
16
+ method='oauth'
17
+ providers={[{
18
+ name: "google",
19
+ label: "Continue with Google",
20
+ icon: <IconBrandGoogle />,
21
+ buttonProps: {
22
+ color: "red",
23
+ }
24
+ }, {
25
+ name: "facebook",
26
+ label: "Continue with the Facebook",
27
+ icon: <IconBrandFacebook />,
28
+ buttonProps: {
29
+ color: "blue",
30
+ }
31
+ }, {
32
+ name: "apple",
33
+ label: "Continue with Apple",
34
+ icon: <IconBrandApple />,
35
+ buttonProps: {
36
+ color: "gray",
37
+ }
38
+ }]}
39
+ />;
40
+
41
+ export const WithProvidersAndPassword = () =>
42
+ <LoginPage
43
+ method='password'
44
+ providers={[{
45
+ name: "google",
46
+ label: "Continue with Google",
47
+ icon: <IconBrandGoogle />,
48
+ buttonProps: {
49
+ color: "red",
50
+ }
51
+ }]}
52
+ />;
53
+
54
+ export const WithProvidersAndOtp = () => {
55
+ const otpHandler = useOtp();
56
+
57
+ return (
58
+ <LoginPage
59
+ method='otp'
60
+ otpHandler={otpHandler}
61
+ providers={[{
62
+ name: "google",
63
+ label: "Continue with Google",
64
+ icon: <IconBrandGoogle />,
65
+ buttonProps: {
66
+ color: "red",
67
+ }
68
+ }]}
69
+ />
70
+ );
71
+ }
72
+
73
+ export const WithProvidersAndMfa = () => {
74
+ const otpHandler = useOtp();
75
+
76
+ return (
77
+ <LoginPage
78
+ method='mfa'
79
+ otpHandler={otpHandler}
80
+ otpInputProps={{ length: 6 }}
81
+ providers={[{
82
+ name: "google",
83
+ label: "Continue with Google",
84
+ icon: <IconBrandGoogle />,
85
+ buttonProps: {
86
+ color: "red",
87
+ }
88
+ }]}
89
+ />
90
+ );
91
+ }
92
+
93
+ export const WithLinks = () =>
94
+ <LoginPage
95
+ method="password"
96
+ registerLink="/register"
97
+ forgotPasswordLink="/forgot-password"
98
+ />;