refine-mantine 1.6.1 → 1.7.0-dev.10
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/README.md +12 -0
- package/dist/index.d.ts +41 -3
- package/dist/index.js +346 -11
- package/package.json +26 -26
- package/src/components/crud/Show.story.tsx +14 -0
- package/src/components/crud/Show.tsx +4 -4
- package/src/components/layout/LayoutMinimal.story.tsx +42 -0
- package/src/components/layout/LayoutMinimal.tsx +365 -0
- package/src/index.ts +3 -0
- package/src/pages/auth/DefaultTitle.story.tsx +15 -0
- package/src/pages/auth/LoginPage.tsx +13 -7
- package/src/pages/auth/RegistrationVerificationPage.story.tsx +12 -0
- package/src/pages/auth/RegistrationVerificationPage.tsx +72 -0
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://discord.gg/BCGmvSSJBk)
|
|
2
|
+
|
|
1
3
|
# Refine-Mantine
|
|
2
4
|
|
|
3
5
|
## Refine Mantine Components
|
|
@@ -25,3 +27,13 @@ npm i refine-mantine
|
|
|
25
27
|
|
|
26
28
|
**Early development stage:** The project is actively being developed, and APIs or component structures may change frequently.
|
|
27
29
|
Feedback, suggestions, and contributions are welcome as the project evolves.
|
|
30
|
+
|
|
31
|
+
## How to Contribute
|
|
32
|
+
|
|
33
|
+
- leave a star ⭐
|
|
34
|
+
- report a bug 🐞
|
|
35
|
+
- open a pull request 🏗️
|
|
36
|
+
- help others ❤️
|
|
37
|
+
- [buy me a coffee ☕](https://www.buymeacoffee.com/kruschid)
|
|
38
|
+
|
|
39
|
+
<a href="https://www.buymeacoffee.com/kruschid" target="_blank"><img width="200px" src="https://cdn.buymeacoffee.com/buttons/v2/default-orange.png" alt="Buy Me A Coffee" ></a>
|
package/dist/index.d.ts
CHANGED
|
@@ -194,7 +194,7 @@ declare const FileField: React.FC<UrlFieldProps>;
|
|
|
194
194
|
declare const PhoneField: React.FC<UrlFieldProps>;
|
|
195
195
|
//#endregion
|
|
196
196
|
//#region src/components/layout/Layout.d.ts
|
|
197
|
-
interface LayoutProps {
|
|
197
|
+
interface LayoutProps$1 {
|
|
198
198
|
children: ReactNode$1;
|
|
199
199
|
shellProps?: AppShellProps;
|
|
200
200
|
headerProps?: AppShellHeaderProps;
|
|
@@ -216,7 +216,27 @@ interface LayoutLocale {
|
|
|
216
216
|
label: string;
|
|
217
217
|
icon?: ReactNode$1;
|
|
218
218
|
}
|
|
219
|
-
declare const Layout: React.FC<LayoutProps>;
|
|
219
|
+
declare const Layout: React.FC<LayoutProps$1>;
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/components/layout/LayoutMinimal.d.ts
|
|
222
|
+
interface LayoutProps {
|
|
223
|
+
children: ReactNode$1;
|
|
224
|
+
shellProps?: AppShellProps;
|
|
225
|
+
headerProps?: AppShellHeaderProps;
|
|
226
|
+
navbarProps?: AppShellNavbarProps;
|
|
227
|
+
navbarConfiguration?: Partial<AppShellNavbarConfiguration>;
|
|
228
|
+
navbarMenuProps?: AppShellSectionProps;
|
|
229
|
+
navbarFooterProps?: AppShellSectionProps;
|
|
230
|
+
mainProps?: AppShellMainProps;
|
|
231
|
+
hideNavbar?: boolean;
|
|
232
|
+
footer?: ReactNode$1;
|
|
233
|
+
footerProps?: AppShellFooterProps;
|
|
234
|
+
locales?: LayoutLocale[];
|
|
235
|
+
renderHeader?: (toggle: () => void) => ReactNode$1;
|
|
236
|
+
renderMenu?: (params: ReturnType<typeof useMenu>) => ReactNode$1;
|
|
237
|
+
renderIdentity?: <T extends BaseRecord>(identity: T, logout: () => void) => ReactNode$1;
|
|
238
|
+
}
|
|
239
|
+
declare const LayoutMinimal: React.FC<LayoutProps>;
|
|
220
240
|
//#endregion
|
|
221
241
|
//#region src/components/notification/AutoSaveIndicator.d.ts
|
|
222
242
|
interface AutoSaveIndicatorProps {
|
|
@@ -332,6 +352,8 @@ type LoginPageProps = {
|
|
|
332
352
|
registerLink?: string;
|
|
333
353
|
forgotPasswordLink?: string;
|
|
334
354
|
validate?: FormValidateInput<LoginForm>;
|
|
355
|
+
onBeforeLogin?: (args: LoginArgs) => void | Promise<void>;
|
|
356
|
+
onBeforeProviderLogin?: (args: LoginArgs) => void | Promise<void>;
|
|
335
357
|
wrapperProps?: StackProps;
|
|
336
358
|
scrollAreaProps?: ScrollAreaProps;
|
|
337
359
|
emailFieldProps?: TextInputProps;
|
|
@@ -382,6 +404,19 @@ interface RegisterForm {
|
|
|
382
404
|
}
|
|
383
405
|
declare const RegisterPage: React.FC<RegisterPageProps>;
|
|
384
406
|
//#endregion
|
|
407
|
+
//#region src/pages/auth/RegistrationVerificationPage.d.ts
|
|
408
|
+
interface RegistrationVerificationPageProps {
|
|
409
|
+
loginLink?: string;
|
|
410
|
+
wrapperProps?: StackProps;
|
|
411
|
+
scrollAreaProps?: ScrollAreaProps;
|
|
412
|
+
cardProps?: CardProps;
|
|
413
|
+
buttonProps?: ButtonProps;
|
|
414
|
+
icon?: ReactNode$1;
|
|
415
|
+
title?: ReactNode$1;
|
|
416
|
+
description?: ReactNode$1;
|
|
417
|
+
}
|
|
418
|
+
declare const RegistrationVerificationPage: React.FC<RegistrationVerificationPageProps>;
|
|
419
|
+
//#endregion
|
|
385
420
|
//#region src/pages/auth/UpdatePasswordPage.d.ts
|
|
386
421
|
interface UpdatePasswordPageProps {
|
|
387
422
|
mutationVariables?: UpdatePasswordFormTypes;
|
|
@@ -398,6 +433,9 @@ interface UpdatePasswordPageProps {
|
|
|
398
433
|
}
|
|
399
434
|
declare const UpdatePasswordPage: React.FC<UpdatePasswordPageProps>;
|
|
400
435
|
//#endregion
|
|
436
|
+
//#region src/pages/auth/DefaultTitle.d.ts
|
|
437
|
+
declare const DefaultTitle: () => react_jsx_runtime0.JSX.Element;
|
|
438
|
+
//#endregion
|
|
401
439
|
//#region src/pages/NotFound.d.ts
|
|
402
440
|
interface NotFoundProps {
|
|
403
441
|
returnTo: string;
|
|
@@ -410,4 +448,4 @@ declare const authProvider: AuthProvider;
|
|
|
410
448
|
//#region src/providers/notificationProvider.d.ts
|
|
411
449
|
declare const notificationProvider: () => NotificationProvider;
|
|
412
450
|
//#endregion
|
|
413
|
-
export { AutoSaveIndicator, AutoSaveIndicatorProps, BooleanField, BooleanFieldProps, Breadcrumb, BreadcrumbProps, CloneButton, CloneButtonProps, ColorSchemeToggle, ColumnFilter, ColumnSorter, Create, CreateButton, CreateButtonProps, CreateProps, DateField, DateFieldProps, DeleteButton, DeleteButtonProps, Edit, EditButton, EditButtonProps, EditProps, EmailField, Empty, ExportButton, ExportButtonProps, FileField, ForgotPasswordForm, ForgotPasswordPage, ForgotPasswordPageProps, ImportButton, ImportButtonProps, Layout, LayoutLocale, List, ListButton, ListButtonProps, ListProps, LoginArgs, LoginPage, LoginPageProps, Message, MessageProps, NotFound, OAuthProviderMantine, OtpHandler, PhoneField, RefreshButton, RefreshButtonProps, RegisterForm, RegisterPage, RegisterPageProps, SaveButton, SaveButtonProps, Show, ShowButton, ShowButtonProps, ShowProps, Table, TranslateFn, UpdatePasswordPage, UpdatePasswordPageProps, UrlField, UrlFieldProps, UseFormProps, UseFormReturnType, authProvider, notificationProvider, useForm, useOtp };
|
|
451
|
+
export { AutoSaveIndicator, AutoSaveIndicatorProps, BooleanField, BooleanFieldProps, Breadcrumb, BreadcrumbProps, CloneButton, CloneButtonProps, ColorSchemeToggle, ColumnFilter, ColumnSorter, Create, CreateButton, CreateButtonProps, CreateProps, DateField, DateFieldProps, DefaultTitle, DeleteButton, DeleteButtonProps, Edit, EditButton, EditButtonProps, EditProps, EmailField, Empty, ExportButton, ExportButtonProps, FileField, ForgotPasswordForm, ForgotPasswordPage, ForgotPasswordPageProps, ImportButton, ImportButtonProps, Layout, LayoutLocale, LayoutMinimal, List, ListButton, ListButtonProps, ListProps, LoginArgs, LoginPage, LoginPageProps, Message, MessageProps, NotFound, OAuthProviderMantine, OtpHandler, PhoneField, RefreshButton, RefreshButtonProps, RegisterForm, RegisterPage, RegisterPageProps, RegistrationVerificationPage, RegistrationVerificationPageProps, SaveButton, SaveButtonProps, Show, ShowButton, ShowButtonProps, ShowProps, Table, TranslateFn, UpdatePasswordPage, UpdatePasswordPageProps, UrlField, UrlFieldProps, UseFormProps, UseFormReturnType, authProvider, notificationProvider, useForm, useOtp };
|
package/dist/index.js
CHANGED
|
@@ -952,18 +952,23 @@ const Show = (props) => {
|
|
|
952
952
|
/* @__PURE__ */ jsxs(Group, {
|
|
953
953
|
justify: "space-between",
|
|
954
954
|
align: "center",
|
|
955
|
+
wrap: "nowrap",
|
|
955
956
|
...headerProps,
|
|
956
957
|
children: [/* @__PURE__ */ jsxs(Stack, {
|
|
957
958
|
gap: "xs",
|
|
958
959
|
children: [breadcrumbComponent, /* @__PURE__ */ jsxs(Group, {
|
|
959
960
|
gap: "xs",
|
|
961
|
+
wrap: "nowrap",
|
|
960
962
|
children: [buttonBack, /* @__PURE__ */ jsx(Title, {
|
|
961
963
|
order: 3,
|
|
964
|
+
lineClamp: 2,
|
|
965
|
+
textWrap: "balance",
|
|
962
966
|
children: title ?? translate(`${identifier}.titles.show`, `Show ${getUserFriendlyName(resource?.meta?.label ?? identifier, "singular")}`)
|
|
963
967
|
})]
|
|
964
968
|
})]
|
|
965
969
|
}), /* @__PURE__ */ jsx(Group, {
|
|
966
970
|
...headerButtonProps,
|
|
971
|
+
wrap: "nowrap",
|
|
967
972
|
children: headerButtons
|
|
968
973
|
})]
|
|
969
974
|
}),
|
|
@@ -1103,7 +1108,7 @@ const Layout = (p) => {
|
|
|
1103
1108
|
to: "/",
|
|
1104
1109
|
style: { all: "unset" },
|
|
1105
1110
|
children: /* @__PURE__ */ jsxs(Group, { children: [defaultIcon, /* @__PURE__ */ jsx(Text, { children: defaultText })] })
|
|
1106
|
-
})] }), /* @__PURE__ */ jsxs(Group, { children: [p.locales && /* @__PURE__ */ jsx(Locales, { locales: p.locales }), /* @__PURE__ */ jsx(Tooltip, {
|
|
1111
|
+
})] }), /* @__PURE__ */ jsxs(Group, { children: [p.locales && /* @__PURE__ */ jsx(Locales$1, { locales: p.locales }), /* @__PURE__ */ jsx(Tooltip, {
|
|
1107
1112
|
label: computedColorScheme === "dark" ? translate("layout.header.lightMode", "Light mode") : translate("layout.header.darkMode", "Dark mode"),
|
|
1108
1113
|
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
1109
1114
|
onClick: () => setColorScheme(computedColorScheme === "light" ? "dark" : "light"),
|
|
@@ -1181,7 +1186,7 @@ const MenuItem = (p) => {
|
|
|
1181
1186
|
}, p.item.key)
|
|
1182
1187
|
}, p.item.key);
|
|
1183
1188
|
};
|
|
1184
|
-
const Locales = (p) => {
|
|
1189
|
+
const Locales$1 = (p) => {
|
|
1185
1190
|
const { changeLocale, getLocale } = useTranslation();
|
|
1186
1191
|
const locale = getLocale();
|
|
1187
1192
|
return /* @__PURE__ */ jsxs(Menu, {
|
|
@@ -1196,6 +1201,284 @@ const Locales = (p) => {
|
|
|
1196
1201
|
});
|
|
1197
1202
|
};
|
|
1198
1203
|
|
|
1204
|
+
//#endregion
|
|
1205
|
+
//#region src/components/layout/LayoutMinimal.tsx
|
|
1206
|
+
const LayoutMinimal = (p) => {
|
|
1207
|
+
const [opened, { toggle, close }] = useDisclosure();
|
|
1208
|
+
const { title: { icon: defaultIcon, text: defaultText } = {} } = useRefineOptions();
|
|
1209
|
+
const { data: identity } = useGetIdentity();
|
|
1210
|
+
const menu = useMenu();
|
|
1211
|
+
const { mutate: logout } = useLogout();
|
|
1212
|
+
const { setColorScheme } = useMantineColorScheme();
|
|
1213
|
+
const computedColorScheme = useComputedColorScheme("light", { getInitialValueInEffect: true });
|
|
1214
|
+
const translate = useTranslate();
|
|
1215
|
+
const languageLabel = translate("layout.navbar.languageLabel", "Language");
|
|
1216
|
+
const colorSchemeLabel = computedColorScheme === "dark" ? translate("layout.header.lightMode", "Light mode") : translate("layout.header.darkMode", "Dark mode");
|
|
1217
|
+
const handleLogout = useCallback(() => {
|
|
1218
|
+
logout();
|
|
1219
|
+
}, [logout]);
|
|
1220
|
+
return /* @__PURE__ */ jsxs(AppShell, {
|
|
1221
|
+
header: { height: {
|
|
1222
|
+
base: 60,
|
|
1223
|
+
sm: 0
|
|
1224
|
+
} },
|
|
1225
|
+
navbar: {
|
|
1226
|
+
width: {
|
|
1227
|
+
base: 260,
|
|
1228
|
+
sm: 80
|
|
1229
|
+
},
|
|
1230
|
+
breakpoint: "sm",
|
|
1231
|
+
collapsed: {
|
|
1232
|
+
mobile: !opened || p.hideNavbar,
|
|
1233
|
+
desktop: p.hideNavbar
|
|
1234
|
+
},
|
|
1235
|
+
...p.navbarConfiguration
|
|
1236
|
+
},
|
|
1237
|
+
padding: "md",
|
|
1238
|
+
...p.shellProps,
|
|
1239
|
+
children: [
|
|
1240
|
+
/* @__PURE__ */ jsx(AppShell.Header, {
|
|
1241
|
+
p: "md",
|
|
1242
|
+
...p.headerProps,
|
|
1243
|
+
hiddenFrom: "sm",
|
|
1244
|
+
children: p.renderHeader ? p.renderHeader(toggle) : /* @__PURE__ */ jsx(Group, {
|
|
1245
|
+
justify: "space-between",
|
|
1246
|
+
children: /* @__PURE__ */ jsxs(Group, { children: [/* @__PURE__ */ jsx(Burger, {
|
|
1247
|
+
opened,
|
|
1248
|
+
onClick: toggle,
|
|
1249
|
+
size: "sm",
|
|
1250
|
+
hidden: p.hideNavbar
|
|
1251
|
+
}), /* @__PURE__ */ jsx(Anchor, {
|
|
1252
|
+
component: Link,
|
|
1253
|
+
to: "/",
|
|
1254
|
+
style: { all: "unset" },
|
|
1255
|
+
children: /* @__PURE__ */ jsxs(Group, { children: [defaultIcon, defaultText ? /* @__PURE__ */ jsx(Text, { children: defaultText }) : null] })
|
|
1256
|
+
})] })
|
|
1257
|
+
})
|
|
1258
|
+
}),
|
|
1259
|
+
/* @__PURE__ */ jsxs(AppShell.Navbar, {
|
|
1260
|
+
...p.navbarProps,
|
|
1261
|
+
children: [
|
|
1262
|
+
/* @__PURE__ */ jsx(AppShell.Section, {
|
|
1263
|
+
visibleFrom: "sm",
|
|
1264
|
+
p: "md",
|
|
1265
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
1266
|
+
align: "center",
|
|
1267
|
+
gap: "xs",
|
|
1268
|
+
children: /* @__PURE__ */ jsx(Tooltip, {
|
|
1269
|
+
label: defaultText ?? translate("layout.navbar.homeLabel", "Home"),
|
|
1270
|
+
position: "right",
|
|
1271
|
+
transitionProps: { duration: 0 },
|
|
1272
|
+
children: /* @__PURE__ */ jsx(Anchor, {
|
|
1273
|
+
component: Link,
|
|
1274
|
+
to: "/",
|
|
1275
|
+
style: { all: "unset" },
|
|
1276
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
1277
|
+
align: "center",
|
|
1278
|
+
gap: 2,
|
|
1279
|
+
children: defaultIcon
|
|
1280
|
+
})
|
|
1281
|
+
})
|
|
1282
|
+
})
|
|
1283
|
+
})
|
|
1284
|
+
}),
|
|
1285
|
+
/* @__PURE__ */ jsx(AppShell.Section, {
|
|
1286
|
+
component: ScrollArea,
|
|
1287
|
+
grow: true,
|
|
1288
|
+
mt: "xs",
|
|
1289
|
+
...p.navbarMenuProps,
|
|
1290
|
+
visibleFrom: "sm",
|
|
1291
|
+
children: p.renderMenu ? p.renderMenu(menu) : /* @__PURE__ */ jsx(Stack, {
|
|
1292
|
+
align: "center",
|
|
1293
|
+
gap: 0,
|
|
1294
|
+
children: menu.menuItems.map((item) => /* @__PURE__ */ jsx(MenuItemIcon, {
|
|
1295
|
+
item,
|
|
1296
|
+
selectedKey: menu.selectedKey,
|
|
1297
|
+
onClick: close
|
|
1298
|
+
}, item.key))
|
|
1299
|
+
})
|
|
1300
|
+
}),
|
|
1301
|
+
/* @__PURE__ */ jsx(AppShell.Section, {
|
|
1302
|
+
component: ScrollArea,
|
|
1303
|
+
grow: true,
|
|
1304
|
+
mt: "xs",
|
|
1305
|
+
...p.navbarMenuProps,
|
|
1306
|
+
hiddenFrom: "sm",
|
|
1307
|
+
children: p.renderMenu ? p.renderMenu(menu) : /* @__PURE__ */ jsx(Stack, {
|
|
1308
|
+
gap: "xs",
|
|
1309
|
+
children: menu.menuItems.map((item) => /* @__PURE__ */ jsx(MenuItemFull, {
|
|
1310
|
+
item,
|
|
1311
|
+
selectedKey: menu.selectedKey,
|
|
1312
|
+
onClick: close
|
|
1313
|
+
}, item.key))
|
|
1314
|
+
})
|
|
1315
|
+
}),
|
|
1316
|
+
/* @__PURE__ */ jsx(AppShell.Section, {
|
|
1317
|
+
pb: "sm",
|
|
1318
|
+
...p.navbarFooterProps,
|
|
1319
|
+
visibleFrom: "sm",
|
|
1320
|
+
children: p.renderIdentity ? p.renderIdentity(identity, handleLogout) : /* @__PURE__ */ jsxs(Stack, {
|
|
1321
|
+
align: "center",
|
|
1322
|
+
gap: "xs",
|
|
1323
|
+
children: [
|
|
1324
|
+
p.locales && /* @__PURE__ */ jsx(Locales, {
|
|
1325
|
+
locales: p.locales,
|
|
1326
|
+
variant: "icon",
|
|
1327
|
+
label: languageLabel
|
|
1328
|
+
}),
|
|
1329
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
1330
|
+
label: colorSchemeLabel,
|
|
1331
|
+
position: "right",
|
|
1332
|
+
transitionProps: { duration: 0 },
|
|
1333
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
1334
|
+
onClick: () => setColorScheme(computedColorScheme === "light" ? "dark" : "light"),
|
|
1335
|
+
"aria-label": translate("layout.header.toggleColorScheme", "Toggle color scheme"),
|
|
1336
|
+
variant: "subtle",
|
|
1337
|
+
size: "xl",
|
|
1338
|
+
children: computedColorScheme === "light" ? /* @__PURE__ */ jsx(IconSun, {
|
|
1339
|
+
stroke: 1.5,
|
|
1340
|
+
size: 22
|
|
1341
|
+
}) : /* @__PURE__ */ jsx(IconMoon, {
|
|
1342
|
+
stroke: 1.5,
|
|
1343
|
+
size: 22
|
|
1344
|
+
})
|
|
1345
|
+
})
|
|
1346
|
+
}),
|
|
1347
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
1348
|
+
label: translate("layout.navbar.signOutLabel", "Sign out"),
|
|
1349
|
+
position: "right",
|
|
1350
|
+
transitionProps: { duration: 0 },
|
|
1351
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
1352
|
+
onClick: handleLogout,
|
|
1353
|
+
"aria-label": translate("layout.navbar.signOutLabel", "Sign out"),
|
|
1354
|
+
variant: "subtle",
|
|
1355
|
+
size: "xl",
|
|
1356
|
+
children: /* @__PURE__ */ jsx(IconLogout, { size: 20 })
|
|
1357
|
+
})
|
|
1358
|
+
})
|
|
1359
|
+
]
|
|
1360
|
+
})
|
|
1361
|
+
}),
|
|
1362
|
+
/* @__PURE__ */ jsx(AppShell.Section, {
|
|
1363
|
+
pb: "sm",
|
|
1364
|
+
...p.navbarFooterProps,
|
|
1365
|
+
hiddenFrom: "sm",
|
|
1366
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1367
|
+
gap: "xs",
|
|
1368
|
+
children: [
|
|
1369
|
+
p.locales && /* @__PURE__ */ jsx(Locales, {
|
|
1370
|
+
locales: p.locales,
|
|
1371
|
+
variant: "full",
|
|
1372
|
+
label: languageLabel
|
|
1373
|
+
}),
|
|
1374
|
+
/* @__PURE__ */ jsx(NavLink, {
|
|
1375
|
+
onClick: () => setColorScheme(computedColorScheme === "light" ? "dark" : "light"),
|
|
1376
|
+
leftSection: computedColorScheme === "light" ? /* @__PURE__ */ jsx(IconSun, {
|
|
1377
|
+
stroke: 1.5,
|
|
1378
|
+
size: 18
|
|
1379
|
+
}) : /* @__PURE__ */ jsx(IconMoon, {
|
|
1380
|
+
stroke: 1.5,
|
|
1381
|
+
size: 18
|
|
1382
|
+
}),
|
|
1383
|
+
label: colorSchemeLabel,
|
|
1384
|
+
variant: "subtle"
|
|
1385
|
+
}),
|
|
1386
|
+
p.renderIdentity ? p.renderIdentity(identity, handleLogout) : /* @__PURE__ */ jsx(NavLink, {
|
|
1387
|
+
onClick: handleLogout,
|
|
1388
|
+
leftSection: identity?.avatar ? /* @__PURE__ */ jsx(Avatar, { src: identity.avatar }) : /* @__PURE__ */ jsx(IconLogout, { size: 18 }),
|
|
1389
|
+
label: translate("layout.navbar.signOutLabel", "Sign out"),
|
|
1390
|
+
description: identity?.email ? translate("layout.navbar.signOutDescription", { email: identity.email }, `Signed in as ${identity.email}`) : void 0,
|
|
1391
|
+
variant: "filled",
|
|
1392
|
+
active: true
|
|
1393
|
+
})
|
|
1394
|
+
]
|
|
1395
|
+
})
|
|
1396
|
+
})
|
|
1397
|
+
]
|
|
1398
|
+
}),
|
|
1399
|
+
/* @__PURE__ */ jsx(AppShell.Main, {
|
|
1400
|
+
...p.mainProps,
|
|
1401
|
+
children: p.children
|
|
1402
|
+
}),
|
|
1403
|
+
/* @__PURE__ */ jsx(AppShell.Footer, {
|
|
1404
|
+
...p.footerProps,
|
|
1405
|
+
children: p.footer
|
|
1406
|
+
})
|
|
1407
|
+
]
|
|
1408
|
+
});
|
|
1409
|
+
};
|
|
1410
|
+
const MenuItemIcon = (p) => {
|
|
1411
|
+
const { listUrl } = useNavigation();
|
|
1412
|
+
const isSelected = p.item.key === p.selectedKey;
|
|
1413
|
+
const label = p.item.meta?.label ?? p.item.label ?? p.item.name;
|
|
1414
|
+
return /* @__PURE__ */ jsx(CanAccess, {
|
|
1415
|
+
resource: p.item.name,
|
|
1416
|
+
action: "list",
|
|
1417
|
+
params: { resource: p.item },
|
|
1418
|
+
children: /* @__PURE__ */ jsx(Tooltip, {
|
|
1419
|
+
label,
|
|
1420
|
+
position: "right",
|
|
1421
|
+
transitionProps: { duration: 0 },
|
|
1422
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
1423
|
+
component: Link,
|
|
1424
|
+
to: listUrl(p.item.name),
|
|
1425
|
+
variant: isSelected ? "filled" : "subtle",
|
|
1426
|
+
size: "xl",
|
|
1427
|
+
mb: "xs",
|
|
1428
|
+
bd: 0,
|
|
1429
|
+
onClick: p.onClick,
|
|
1430
|
+
children: p.item.meta?.icon ?? /* @__PURE__ */ jsx(IconList, { size: 20 })
|
|
1431
|
+
}, p.item.key)
|
|
1432
|
+
})
|
|
1433
|
+
}, p.item.key);
|
|
1434
|
+
};
|
|
1435
|
+
const MenuItemFull = (p) => {
|
|
1436
|
+
const { listUrl } = useNavigation();
|
|
1437
|
+
const isSelected = p.item.key === p.selectedKey;
|
|
1438
|
+
const label = p.item.meta?.label ?? p.item.label ?? p.item.name;
|
|
1439
|
+
return /* @__PURE__ */ jsx(CanAccess, {
|
|
1440
|
+
resource: p.item.name,
|
|
1441
|
+
action: "list",
|
|
1442
|
+
params: { resource: p.item },
|
|
1443
|
+
children: /* @__PURE__ */ jsx(NavLink, {
|
|
1444
|
+
label,
|
|
1445
|
+
leftSection: p.item.meta?.icon ?? /* @__PURE__ */ jsx(IconList, { size: 18 }),
|
|
1446
|
+
active: isSelected,
|
|
1447
|
+
component: Link,
|
|
1448
|
+
to: listUrl(p.item.name),
|
|
1449
|
+
onClick: p.onClick
|
|
1450
|
+
}, p.item.key)
|
|
1451
|
+
}, p.item.key);
|
|
1452
|
+
};
|
|
1453
|
+
const Locales = (p) => {
|
|
1454
|
+
const { changeLocale, getLocale } = useTranslation();
|
|
1455
|
+
const locale = getLocale();
|
|
1456
|
+
return /* @__PURE__ */ jsxs(Menu, {
|
|
1457
|
+
shadow: "md",
|
|
1458
|
+
width: 200,
|
|
1459
|
+
children: [/* @__PURE__ */ jsx(Menu.Target, { children: p.variant === "icon" ? /* @__PURE__ */ jsx(Tooltip, {
|
|
1460
|
+
label: p.label,
|
|
1461
|
+
position: "right",
|
|
1462
|
+
transitionProps: { duration: 0 },
|
|
1463
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
1464
|
+
"aria-label": p.label,
|
|
1465
|
+
variant: "subtle",
|
|
1466
|
+
size: "xl",
|
|
1467
|
+
children: /* @__PURE__ */ jsx(IconLanguage, {})
|
|
1468
|
+
})
|
|
1469
|
+
}) : /* @__PURE__ */ jsx(NavLink, {
|
|
1470
|
+
label: p.label,
|
|
1471
|
+
leftSection: /* @__PURE__ */ jsx(IconLanguage, { size: 18 }),
|
|
1472
|
+
component: "button"
|
|
1473
|
+
}) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: p.locales.map(({ label, lang, icon }) => /* @__PURE__ */ jsx(Menu.Item, {
|
|
1474
|
+
leftSection: icon,
|
|
1475
|
+
onClick: () => changeLocale(lang),
|
|
1476
|
+
rightSection: lang === locale ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : void 0,
|
|
1477
|
+
children: label
|
|
1478
|
+
}, lang)) })]
|
|
1479
|
+
});
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1199
1482
|
//#endregion
|
|
1200
1483
|
//#region src/components/notification/Message.tsx
|
|
1201
1484
|
const Message = ({ message, description, undoLabel, undoableTime = 5, undoableTimeout = 0, notificationProps, actioniconProps, buttonProps, onUndo }) => /* @__PURE__ */ jsx(Notification, {
|
|
@@ -1408,7 +1691,7 @@ const set = (obj, path, value) => {
|
|
|
1408
1691
|
|
|
1409
1692
|
//#endregion
|
|
1410
1693
|
//#region src/hooks/useForm.ts
|
|
1411
|
-
const useForm = ({ refineCoreProps, disableServerSideValidation: disableServerSideValidationProp = false
|
|
1694
|
+
const useForm = ({ refineCoreProps, disableServerSideValidation: disableServerSideValidationProp = false, ...rest } = {}) => {
|
|
1412
1695
|
const { options } = useRefineContext();
|
|
1413
1696
|
const disableServerSideValidation = options?.disableServerSideValidation || disableServerSideValidationProp;
|
|
1414
1697
|
const translate = useTranslate();
|
|
@@ -1529,6 +1812,7 @@ const DefaultTitle = () => {
|
|
|
1529
1812
|
|
|
1530
1813
|
//#endregion
|
|
1531
1814
|
//#region src/pages/auth/ForgotPasswordPage.tsx
|
|
1815
|
+
/** biome-ignore-all lint/correctness/useUniqueElementIds: test ids for playwright */
|
|
1532
1816
|
const ForgotPasswordPage = (p) => {
|
|
1533
1817
|
const translate = useTranslate();
|
|
1534
1818
|
const { mutate: forgotPassword, isPending } = useForgotPassword();
|
|
@@ -1625,25 +1909,30 @@ const LoginPage = (p) => {
|
|
|
1625
1909
|
...p.validate
|
|
1626
1910
|
}
|
|
1627
1911
|
});
|
|
1628
|
-
const handleProviderLogin = useCallback((provider) => {
|
|
1629
|
-
|
|
1912
|
+
const handleProviderLogin = useCallback(async (provider) => {
|
|
1913
|
+
const loginArgs = {
|
|
1630
1914
|
providerName: provider.name,
|
|
1631
1915
|
translate,
|
|
1632
1916
|
...p.mutationVariables
|
|
1633
|
-
}
|
|
1917
|
+
};
|
|
1918
|
+
await p.onBeforeProviderLogin?.(loginArgs);
|
|
1919
|
+
login.mutate(loginArgs);
|
|
1634
1920
|
}, [
|
|
1635
1921
|
login,
|
|
1636
1922
|
translate,
|
|
1637
|
-
p.mutationVariables
|
|
1923
|
+
p.mutationVariables,
|
|
1924
|
+
p.onBeforeProviderLogin
|
|
1638
1925
|
]);
|
|
1639
|
-
const handleLogin = onSubmit(({ email, password }) => {
|
|
1640
|
-
|
|
1926
|
+
const handleLogin = onSubmit(async ({ email, password }) => {
|
|
1927
|
+
const loginArgs = {
|
|
1641
1928
|
email,
|
|
1642
1929
|
password,
|
|
1643
1930
|
otpHandler: p.otpHandler,
|
|
1644
1931
|
translate,
|
|
1645
1932
|
...p.mutationVariables
|
|
1646
|
-
}
|
|
1933
|
+
};
|
|
1934
|
+
await p.onBeforeLogin?.(loginArgs);
|
|
1935
|
+
login.mutate(loginArgs);
|
|
1647
1936
|
});
|
|
1648
1937
|
return /* @__PURE__ */ jsx(Stack, {
|
|
1649
1938
|
h: "100vh",
|
|
@@ -1886,6 +2175,52 @@ const RegisterPage = (p) => {
|
|
|
1886
2175
|
});
|
|
1887
2176
|
};
|
|
1888
2177
|
|
|
2178
|
+
//#endregion
|
|
2179
|
+
//#region src/pages/auth/RegistrationVerificationPage.tsx
|
|
2180
|
+
const RegistrationVerificationPage = (p) => {
|
|
2181
|
+
const translate = useTranslate();
|
|
2182
|
+
return /* @__PURE__ */ jsx(Stack, {
|
|
2183
|
+
h: "100vh",
|
|
2184
|
+
align: "center",
|
|
2185
|
+
justify: "center",
|
|
2186
|
+
...p.wrapperProps,
|
|
2187
|
+
children: /* @__PURE__ */ jsxs(ScrollArea, {
|
|
2188
|
+
type: "never",
|
|
2189
|
+
...p.scrollAreaProps,
|
|
2190
|
+
children: [p.icon ?? /* @__PURE__ */ jsx(DefaultTitle, {}), /* @__PURE__ */ jsxs(Card, {
|
|
2191
|
+
shadow: "sm",
|
|
2192
|
+
padding: "lg",
|
|
2193
|
+
radius: "md",
|
|
2194
|
+
withBorder: true,
|
|
2195
|
+
...p.cardProps,
|
|
2196
|
+
children: [
|
|
2197
|
+
p.title ?? /* @__PURE__ */ jsx(Title, {
|
|
2198
|
+
order: 5,
|
|
2199
|
+
mb: "sm",
|
|
2200
|
+
ta: "center",
|
|
2201
|
+
children: translate("pages.registerVerification.title", "Thank you for your registration")
|
|
2202
|
+
}),
|
|
2203
|
+
p.description ?? /* @__PURE__ */ jsx(Text, {
|
|
2204
|
+
size: "sm",
|
|
2205
|
+
ta: "center",
|
|
2206
|
+
c: "dimmed",
|
|
2207
|
+
children: translate("pages.registerVerification.description", "You will get an email where you can confirm your registration.")
|
|
2208
|
+
}),
|
|
2209
|
+
p.loginLink && /* @__PURE__ */ jsx(Button, {
|
|
2210
|
+
component: Link,
|
|
2211
|
+
to: p.loginLink,
|
|
2212
|
+
fullWidth: true,
|
|
2213
|
+
mt: "lg",
|
|
2214
|
+
variant: "light",
|
|
2215
|
+
...p.buttonProps,
|
|
2216
|
+
children: translate("pages.registerVerification.login", "Back to login")
|
|
2217
|
+
})
|
|
2218
|
+
]
|
|
2219
|
+
})]
|
|
2220
|
+
})
|
|
2221
|
+
});
|
|
2222
|
+
};
|
|
2223
|
+
|
|
1889
2224
|
//#endregion
|
|
1890
2225
|
//#region src/pages/auth/UpdatePasswordPage.tsx
|
|
1891
2226
|
const UpdatePasswordPage = (p) => {
|
|
@@ -2093,4 +2428,4 @@ const notificationProvider = () => {
|
|
|
2093
2428
|
};
|
|
2094
2429
|
|
|
2095
2430
|
//#endregion
|
|
2096
|
-
export { AutoSaveIndicator, BooleanField, Breadcrumb, CloneButton, ColorSchemeToggle, ColumnFilter, ColumnSorter, Create, CreateButton, DateField, DeleteButton, Edit, EditButton, EmailField, Empty, ExportButton, FileField, ForgotPasswordPage, ImportButton, Layout, List, ListButton, LoginPage, Message, NotFound, PhoneField, RefreshButton, RegisterPage, SaveButton, Show, ShowButton, Table, UpdatePasswordPage, UrlField, notificationProvider, useForm, useOtp };
|
|
2431
|
+
export { AutoSaveIndicator, BooleanField, Breadcrumb, CloneButton, ColorSchemeToggle, ColumnFilter, ColumnSorter, Create, CreateButton, DateField, DefaultTitle, DeleteButton, Edit, EditButton, EmailField, Empty, ExportButton, FileField, ForgotPasswordPage, ImportButton, Layout, LayoutMinimal, List, ListButton, LoginPage, Message, NotFound, PhoneField, RefreshButton, RegisterPage, RegistrationVerificationPage, SaveButton, Show, ShowButton, Table, UpdatePasswordPage, UrlField, notificationProvider, useForm, useOtp };
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "refine-mantine",
|
|
3
|
+
"version": "1.7.0-dev.10",
|
|
3
4
|
"type": "module",
|
|
4
5
|
"exports": "./dist/index.js",
|
|
5
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,6 +19,9 @@
|
|
|
18
19
|
"prepare": "husky",
|
|
19
20
|
"commitlint": "commitlint --edit"
|
|
20
21
|
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@refinedev/ui-types": "^2.0.1"
|
|
24
|
+
},
|
|
21
25
|
"peerDependencies": {
|
|
22
26
|
"@mantine/core": "^8.3.1",
|
|
23
27
|
"@mantine/form": "^8.3.1",
|
|
@@ -36,44 +40,44 @@
|
|
|
36
40
|
"react-router-dom": "^7.0.2"
|
|
37
41
|
},
|
|
38
42
|
"devDependencies": {
|
|
39
|
-
"@commitlint/cli": "^20.
|
|
40
|
-
"@commitlint/config-conventional": "^20.
|
|
43
|
+
"@commitlint/cli": "^20.2.0",
|
|
44
|
+
"@commitlint/config-conventional": "^20.2.0",
|
|
41
45
|
"@mantine/core": "^8.3.10",
|
|
42
46
|
"@mantine/form": "^8.3.10",
|
|
43
47
|
"@mantine/hooks": "^8.3.10",
|
|
44
48
|
"@mantine/notifications": "^8.3.10",
|
|
45
|
-
"@refinedev/core": "^5.0.
|
|
49
|
+
"@refinedev/core": "^5.0.7",
|
|
46
50
|
"@refinedev/react-router": "^2.0.3",
|
|
47
51
|
"@refinedev/react-table": "^6.0.1",
|
|
48
52
|
"@refinedev/simple-rest": "^6.0.1",
|
|
49
|
-
"@storybook/addon-
|
|
50
|
-
"@storybook/addon-themes": "^
|
|
51
|
-
"@storybook/react": "^
|
|
52
|
-
"@storybook/react-vite": "^
|
|
53
|
-
"@tabler/icons-react": "^3.
|
|
54
|
-
"@tanstack/react-query": "^5.90.
|
|
53
|
+
"@storybook/addon-docs": "^10.1.10",
|
|
54
|
+
"@storybook/addon-themes": "^10.1.10",
|
|
55
|
+
"@storybook/react": "^10.1.10",
|
|
56
|
+
"@storybook/react-vite": "^10.1.10",
|
|
57
|
+
"@tabler/icons-react": "^3.36.0",
|
|
58
|
+
"@tanstack/react-query": "^5.90.12",
|
|
55
59
|
"@tanstack/react-table": "^8.21.3",
|
|
56
60
|
"@types/node": "^24.9.1",
|
|
57
|
-
"@types/react": "^19.2.
|
|
58
|
-
"@types/react-dom": "^19.2.
|
|
59
|
-
"@vitejs/plugin-react": "^5.1.
|
|
61
|
+
"@types/react": "^19.2.7",
|
|
62
|
+
"@types/react-dom": "^19.2.3",
|
|
63
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
60
64
|
"dayjs": "^1.11.19",
|
|
61
65
|
"husky": "^9.1.7",
|
|
62
66
|
"postcss": "^8.5.6",
|
|
63
67
|
"postcss-preset-mantine": "1.18.0",
|
|
64
68
|
"postcss-simple-vars": "^7.0.1",
|
|
65
|
-
"react": "^19.2.
|
|
66
|
-
"react-dom": "^19.2.
|
|
67
|
-
"react-router": "^7.
|
|
68
|
-
"react-router-dom": "^7.
|
|
69
|
+
"react": "^19.2.3",
|
|
70
|
+
"react-dom": "^19.2.3",
|
|
71
|
+
"react-router": "^7.11.0",
|
|
72
|
+
"react-router-dom": "^7.11.0",
|
|
69
73
|
"semantic-release": "^25.0.2",
|
|
70
|
-
"storybook": "^
|
|
71
|
-
"tsdown": "^0.
|
|
74
|
+
"storybook": "^10.1.10",
|
|
75
|
+
"tsdown": "^0.18.1",
|
|
72
76
|
"typescript": "^5.9.3",
|
|
73
|
-
"vite": "^7.
|
|
74
|
-
"vite-tsconfig-paths": "^
|
|
77
|
+
"vite": "^7.3.0",
|
|
78
|
+
"vite-tsconfig-paths": "^6.0.2"
|
|
75
79
|
},
|
|
76
|
-
"packageManager": "yarn@4.
|
|
80
|
+
"packageManager": "yarn@4.12.0",
|
|
77
81
|
"files": [
|
|
78
82
|
"dist",
|
|
79
83
|
"src",
|
|
@@ -89,9 +93,5 @@
|
|
|
89
93
|
"prerelease": true
|
|
90
94
|
}
|
|
91
95
|
]
|
|
92
|
-
}
|
|
93
|
-
"dependencies": {
|
|
94
|
-
"@refinedev/ui-types": "^2.0.1"
|
|
95
|
-
},
|
|
96
|
-
"version": "1.6.1"
|
|
96
|
+
}
|
|
97
97
|
}
|
|
@@ -82,3 +82,17 @@ export const Default = () => {
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
export const LongTitle = () => (
|
|
86
|
+
<Show
|
|
87
|
+
title="Show user profile details with a very long resource title that should stay on one line without pushing the layout out of place"
|
|
88
|
+
headerButtons={() =>
|
|
89
|
+
<ButtonGroup>
|
|
90
|
+
<RefreshButton />
|
|
91
|
+
<CloneButton />
|
|
92
|
+
<EditButton />
|
|
93
|
+
<DeleteButton />
|
|
94
|
+
</ButtonGroup>
|
|
95
|
+
}
|
|
96
|
+
>
|
|
97
|
+
</Show>
|
|
98
|
+
);
|
|
@@ -186,12 +186,12 @@ export const Show: React.FC<ShowProps> = (props) => {
|
|
|
186
186
|
return (
|
|
187
187
|
<Card p="md" {...wrapperProps}>
|
|
188
188
|
<LoadingOverlay visible={loadingOverlayVisible} />
|
|
189
|
-
<Group justify="space-between" align="center" {...headerProps}>
|
|
189
|
+
<Group justify="space-between" align="center" wrap="nowrap" {...headerProps}>
|
|
190
190
|
<Stack gap="xs">
|
|
191
191
|
{breadcrumbComponent}
|
|
192
|
-
<Group gap="xs">
|
|
192
|
+
<Group gap="xs" wrap="nowrap">
|
|
193
193
|
{buttonBack}
|
|
194
|
-
<Title order={3}>
|
|
194
|
+
<Title order={3} lineClamp={2} textWrap="balance">
|
|
195
195
|
{title ??
|
|
196
196
|
translate(
|
|
197
197
|
`${identifier}.titles.show`,
|
|
@@ -203,7 +203,7 @@ export const Show: React.FC<ShowProps> = (props) => {
|
|
|
203
203
|
</Title>
|
|
204
204
|
</Group>
|
|
205
205
|
</Stack>
|
|
206
|
-
<Group {...headerButtonProps}>{headerButtons}</Group>
|
|
206
|
+
<Group {...headerButtonProps} wrap="nowrap">{headerButtons}</Group>
|
|
207
207
|
</Group>
|
|
208
208
|
<Box pt="sm" {...contentProps}>
|
|
209
209
|
{children}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Image } from "@mantine/core";
|
|
2
|
+
import type { Meta } from "@storybook/react";
|
|
3
|
+
import { LayoutMinimal } from "./LayoutMinimal";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Components/LayoutMinimal",
|
|
7
|
+
component: LayoutMinimal,
|
|
8
|
+
} satisfies Meta<typeof LayoutMinimal>;
|
|
9
|
+
|
|
10
|
+
export const FullLayout = () => <LayoutMinimal>Hello World</LayoutMinimal>;
|
|
11
|
+
|
|
12
|
+
export const WithLocaleChange = () => (
|
|
13
|
+
<LayoutMinimal
|
|
14
|
+
locales={[
|
|
15
|
+
{
|
|
16
|
+
label: "English",
|
|
17
|
+
lang: "en",
|
|
18
|
+
icon: <Image h={18} src="https://flagsapi.com/US/flat/64.png" />,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Deutsch",
|
|
22
|
+
lang: "de",
|
|
23
|
+
icon: <Image h={18} src="https://flagsapi.com/DE/flat/64.png" />,
|
|
24
|
+
},
|
|
25
|
+
]}
|
|
26
|
+
>
|
|
27
|
+
Hello World
|
|
28
|
+
</LayoutMinimal>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export const WithFooter = () => (
|
|
32
|
+
<LayoutMinimal
|
|
33
|
+
footer={
|
|
34
|
+
<div style={{ padding: 12, textAlign: "center" }}>
|
|
35
|
+
Footer content
|
|
36
|
+
</div>
|
|
37
|
+
}
|
|
38
|
+
footerProps={{ height: 48, withBorder: true }}
|
|
39
|
+
>
|
|
40
|
+
Hello World
|
|
41
|
+
</LayoutMinimal>
|
|
42
|
+
);
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionIcon,
|
|
3
|
+
Anchor,
|
|
4
|
+
AppShell,
|
|
5
|
+
type AppShellFooterProps,
|
|
6
|
+
type AppShellHeaderProps,
|
|
7
|
+
type AppShellMainProps,
|
|
8
|
+
type AppShellNavbarConfiguration,
|
|
9
|
+
type AppShellNavbarProps,
|
|
10
|
+
type AppShellProps,
|
|
11
|
+
type AppShellSectionProps,
|
|
12
|
+
Avatar,
|
|
13
|
+
Burger,
|
|
14
|
+
Group,
|
|
15
|
+
Menu,
|
|
16
|
+
NavLink,
|
|
17
|
+
ScrollArea,
|
|
18
|
+
Stack,
|
|
19
|
+
Text,
|
|
20
|
+
Tooltip,
|
|
21
|
+
useComputedColorScheme,
|
|
22
|
+
useMantineColorScheme
|
|
23
|
+
} from "@mantine/core";
|
|
24
|
+
import { useDisclosure } from "@mantine/hooks";
|
|
25
|
+
import {
|
|
26
|
+
type BaseRecord,
|
|
27
|
+
CanAccess,
|
|
28
|
+
Link,
|
|
29
|
+
type TreeMenuItem,
|
|
30
|
+
useGetIdentity,
|
|
31
|
+
useLogout,
|
|
32
|
+
useMenu,
|
|
33
|
+
useNavigation,
|
|
34
|
+
useRefineOptions,
|
|
35
|
+
useTranslate,
|
|
36
|
+
useTranslation,
|
|
37
|
+
} from "@refinedev/core";
|
|
38
|
+
import { IconCheck, IconLanguage, IconList, IconLogout, IconMoon, IconSun } from "@tabler/icons-react";
|
|
39
|
+
import { type ReactNode, useCallback } from "react";
|
|
40
|
+
import type { LayoutLocale } from "./Layout";
|
|
41
|
+
|
|
42
|
+
interface LayoutProps {
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
shellProps?: AppShellProps;
|
|
45
|
+
headerProps?: AppShellHeaderProps;
|
|
46
|
+
navbarProps?: AppShellNavbarProps;
|
|
47
|
+
navbarConfiguration?: Partial<AppShellNavbarConfiguration>;
|
|
48
|
+
navbarMenuProps?: AppShellSectionProps;
|
|
49
|
+
navbarFooterProps?: AppShellSectionProps;
|
|
50
|
+
mainProps?: AppShellMainProps;
|
|
51
|
+
hideNavbar?: boolean;
|
|
52
|
+
footer?: ReactNode;
|
|
53
|
+
footerProps?: AppShellFooterProps;
|
|
54
|
+
locales?: LayoutLocale[];
|
|
55
|
+
renderHeader?: (toggle: ()=> void) => ReactNode;
|
|
56
|
+
renderMenu?: (params: ReturnType<typeof useMenu>) => ReactNode;
|
|
57
|
+
renderIdentity?: <T extends BaseRecord>(identity: T, logout: () => void) => ReactNode;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const LayoutMinimal: React.FC<LayoutProps> = (p) => {
|
|
61
|
+
const [opened, { toggle, close }] = useDisclosure();
|
|
62
|
+
const { title: { icon: defaultIcon, text: defaultText } = {} } =
|
|
63
|
+
useRefineOptions();
|
|
64
|
+
const { data: identity } = useGetIdentity();
|
|
65
|
+
const menu = useMenu();
|
|
66
|
+
const { mutate: logout } = useLogout();
|
|
67
|
+
const { setColorScheme } = useMantineColorScheme();
|
|
68
|
+
const computedColorScheme = useComputedColorScheme('light', { getInitialValueInEffect: true });
|
|
69
|
+
const translate = useTranslate();
|
|
70
|
+
const languageLabel = translate("layout.navbar.languageLabel", "Language");
|
|
71
|
+
const colorSchemeLabel = computedColorScheme === 'dark'
|
|
72
|
+
? translate("layout.header.lightMode", 'Light mode')
|
|
73
|
+
: translate("layout.header.darkMode", 'Dark mode');
|
|
74
|
+
|
|
75
|
+
const handleLogout = useCallback(() => {
|
|
76
|
+
logout();
|
|
77
|
+
}, [logout]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<AppShell
|
|
81
|
+
header={{
|
|
82
|
+
height: { base: 60, sm: 0 },
|
|
83
|
+
}}
|
|
84
|
+
navbar={{
|
|
85
|
+
width: { base: 260, sm: 80 },
|
|
86
|
+
breakpoint: "sm",
|
|
87
|
+
collapsed: {
|
|
88
|
+
mobile: !opened || p.hideNavbar,
|
|
89
|
+
desktop: p.hideNavbar,
|
|
90
|
+
},
|
|
91
|
+
...p.navbarConfiguration,
|
|
92
|
+
}}
|
|
93
|
+
padding="md"
|
|
94
|
+
{...p.shellProps}
|
|
95
|
+
>
|
|
96
|
+
<AppShell.Header p="md" {...p.headerProps} hiddenFrom="sm">
|
|
97
|
+
{p.renderHeader ? (
|
|
98
|
+
p.renderHeader(toggle)
|
|
99
|
+
) : (
|
|
100
|
+
<Group justify="space-between">
|
|
101
|
+
<Group>
|
|
102
|
+
<Burger opened={opened} onClick={toggle} size="sm" hidden={p.hideNavbar} />
|
|
103
|
+
<Anchor
|
|
104
|
+
component={Link as React.FC<{ to: string, children: ReactNode }>}
|
|
105
|
+
to="/"
|
|
106
|
+
style={{all: "unset"}}
|
|
107
|
+
>
|
|
108
|
+
<Group>
|
|
109
|
+
{defaultIcon}
|
|
110
|
+
{defaultText ? <Text>{defaultText}</Text> : null}
|
|
111
|
+
</Group>
|
|
112
|
+
</Anchor>
|
|
113
|
+
</Group>
|
|
114
|
+
</Group>
|
|
115
|
+
)}
|
|
116
|
+
</AppShell.Header>
|
|
117
|
+
|
|
118
|
+
<AppShell.Navbar {...p.navbarProps}>
|
|
119
|
+
<AppShell.Section visibleFrom="sm" p="md">
|
|
120
|
+
<Stack align="center" gap="xs">
|
|
121
|
+
<Tooltip label={defaultText ?? translate("layout.navbar.homeLabel", "Home")} position="right" transitionProps={{ duration: 0 }}>
|
|
122
|
+
<Anchor
|
|
123
|
+
component={Link as React.FC<{ to: string, children: ReactNode }>}
|
|
124
|
+
to="/"
|
|
125
|
+
style={{all: "unset"}}
|
|
126
|
+
>
|
|
127
|
+
<Stack align="center" gap={2}>
|
|
128
|
+
{defaultIcon}
|
|
129
|
+
</Stack>
|
|
130
|
+
</Anchor>
|
|
131
|
+
</Tooltip>
|
|
132
|
+
</Stack>
|
|
133
|
+
</AppShell.Section>
|
|
134
|
+
|
|
135
|
+
<AppShell.Section
|
|
136
|
+
component={ScrollArea}
|
|
137
|
+
grow
|
|
138
|
+
mt="xs"
|
|
139
|
+
{...p.navbarMenuProps}
|
|
140
|
+
visibleFrom="sm"
|
|
141
|
+
>
|
|
142
|
+
{p.renderMenu ? (
|
|
143
|
+
p.renderMenu(menu)
|
|
144
|
+
) : (
|
|
145
|
+
<Stack align="center" gap={0}>
|
|
146
|
+
{menu.menuItems.map((item) => (
|
|
147
|
+
<MenuItemIcon
|
|
148
|
+
item={item}
|
|
149
|
+
key={item.key}
|
|
150
|
+
selectedKey={menu.selectedKey}
|
|
151
|
+
onClick={close}
|
|
152
|
+
/>
|
|
153
|
+
))}
|
|
154
|
+
</Stack>
|
|
155
|
+
)}
|
|
156
|
+
</AppShell.Section>
|
|
157
|
+
|
|
158
|
+
<AppShell.Section
|
|
159
|
+
component={ScrollArea}
|
|
160
|
+
grow
|
|
161
|
+
mt="xs"
|
|
162
|
+
{...p.navbarMenuProps}
|
|
163
|
+
hiddenFrom="sm"
|
|
164
|
+
>
|
|
165
|
+
{p.renderMenu ? (
|
|
166
|
+
p.renderMenu(menu)
|
|
167
|
+
) : (
|
|
168
|
+
<Stack gap="xs">
|
|
169
|
+
{menu.menuItems.map((item) => (
|
|
170
|
+
<MenuItemFull
|
|
171
|
+
item={item}
|
|
172
|
+
key={item.key}
|
|
173
|
+
selectedKey={menu.selectedKey}
|
|
174
|
+
onClick={close}
|
|
175
|
+
/>
|
|
176
|
+
))}
|
|
177
|
+
</Stack>
|
|
178
|
+
)}
|
|
179
|
+
</AppShell.Section>
|
|
180
|
+
|
|
181
|
+
<AppShell.Section pb="sm" {...p.navbarFooterProps} visibleFrom="sm">
|
|
182
|
+
{p.renderIdentity ? (
|
|
183
|
+
p.renderIdentity(identity, handleLogout)
|
|
184
|
+
) : (
|
|
185
|
+
<Stack align="center" gap="xs">
|
|
186
|
+
{p.locales && (
|
|
187
|
+
<Locales locales={p.locales} variant="icon" label={languageLabel} />
|
|
188
|
+
)}
|
|
189
|
+
<Tooltip label={colorSchemeLabel} position="right" transitionProps={{ duration: 0 }}>
|
|
190
|
+
<ActionIcon
|
|
191
|
+
onClick={() => setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')}
|
|
192
|
+
aria-label={translate("layout.header.toggleColorScheme", "Toggle color scheme")}
|
|
193
|
+
variant="subtle"
|
|
194
|
+
size="xl"
|
|
195
|
+
>
|
|
196
|
+
{computedColorScheme === "light"
|
|
197
|
+
? <IconSun stroke={1.5} size={22} />
|
|
198
|
+
: <IconMoon stroke={1.5} size={22} />}
|
|
199
|
+
</ActionIcon>
|
|
200
|
+
</Tooltip>
|
|
201
|
+
<Tooltip label={translate("layout.navbar.signOutLabel", "Sign out")} position="right" transitionProps={{ duration: 0 }}>
|
|
202
|
+
<ActionIcon
|
|
203
|
+
onClick={handleLogout}
|
|
204
|
+
aria-label={translate("layout.navbar.signOutLabel", "Sign out")}
|
|
205
|
+
variant="subtle"
|
|
206
|
+
size="xl"
|
|
207
|
+
>
|
|
208
|
+
<IconLogout size={20} />
|
|
209
|
+
</ActionIcon>
|
|
210
|
+
</Tooltip>
|
|
211
|
+
</Stack>
|
|
212
|
+
)}
|
|
213
|
+
</AppShell.Section>
|
|
214
|
+
|
|
215
|
+
<AppShell.Section pb="sm" {...p.navbarFooterProps} hiddenFrom="sm">
|
|
216
|
+
<Stack gap="xs">
|
|
217
|
+
{p.locales && (
|
|
218
|
+
<Locales locales={p.locales} variant="full" label={languageLabel} />
|
|
219
|
+
)}
|
|
220
|
+
<NavLink
|
|
221
|
+
onClick={() => setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')}
|
|
222
|
+
leftSection={computedColorScheme === "light"
|
|
223
|
+
? <IconSun stroke={1.5} size={18} />
|
|
224
|
+
: <IconMoon stroke={1.5} size={18} />}
|
|
225
|
+
label={colorSchemeLabel}
|
|
226
|
+
variant="subtle"
|
|
227
|
+
/>
|
|
228
|
+
{p.renderIdentity ? (
|
|
229
|
+
p.renderIdentity(identity, handleLogout)
|
|
230
|
+
) : (
|
|
231
|
+
<NavLink
|
|
232
|
+
onClick={handleLogout}
|
|
233
|
+
leftSection={identity?.avatar ? <Avatar src={identity.avatar} /> : <IconLogout size={18} />}
|
|
234
|
+
label={translate("layout.navbar.signOutLabel", "Sign out")}
|
|
235
|
+
description={
|
|
236
|
+
identity?.email
|
|
237
|
+
? translate("layout.navbar.signOutDescription", { email: identity.email }, `Signed in as ${identity.email}`)
|
|
238
|
+
: undefined
|
|
239
|
+
}
|
|
240
|
+
variant="filled"
|
|
241
|
+
active
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
</Stack>
|
|
245
|
+
</AppShell.Section>
|
|
246
|
+
</AppShell.Navbar>
|
|
247
|
+
|
|
248
|
+
<AppShell.Main {...p.mainProps}>
|
|
249
|
+
{p.children}
|
|
250
|
+
</AppShell.Main>
|
|
251
|
+
|
|
252
|
+
<AppShell.Footer {...p.footerProps}>
|
|
253
|
+
{p.footer}
|
|
254
|
+
</AppShell.Footer>
|
|
255
|
+
</AppShell>
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const MenuItemIcon = (p: {
|
|
260
|
+
item: TreeMenuItem;
|
|
261
|
+
selectedKey?: string;
|
|
262
|
+
onClick: () => void;
|
|
263
|
+
}) => {
|
|
264
|
+
const { listUrl } = useNavigation();
|
|
265
|
+
const isSelected = p.item.key === p.selectedKey;
|
|
266
|
+
const label = p.item.meta?.label ?? p.item.label ?? p.item.name;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<CanAccess
|
|
270
|
+
key={p.item.key}
|
|
271
|
+
resource={p.item.name}
|
|
272
|
+
action="list"
|
|
273
|
+
params={{
|
|
274
|
+
resource: p.item,
|
|
275
|
+
}}
|
|
276
|
+
>
|
|
277
|
+
<Tooltip label={label} position="right" transitionProps={{ duration: 0 }}>
|
|
278
|
+
<ActionIcon
|
|
279
|
+
key={p.item.key}
|
|
280
|
+
component={Link as React.FC<{ to: string, onClick: () => void }>}
|
|
281
|
+
to={listUrl(p.item.name)}
|
|
282
|
+
variant={isSelected ? "filled" : "subtle"}
|
|
283
|
+
size="xl"
|
|
284
|
+
mb="xs"
|
|
285
|
+
bd={0}
|
|
286
|
+
onClick={p.onClick}
|
|
287
|
+
>
|
|
288
|
+
{p.item.meta?.icon ?? <IconList size={20} />}
|
|
289
|
+
</ActionIcon>
|
|
290
|
+
</Tooltip>
|
|
291
|
+
</CanAccess>
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const MenuItemFull = (p: {
|
|
296
|
+
item: TreeMenuItem;
|
|
297
|
+
selectedKey?: string;
|
|
298
|
+
onClick: () => void;
|
|
299
|
+
}) => {
|
|
300
|
+
const { listUrl } = useNavigation();
|
|
301
|
+
const isSelected = p.item.key === p.selectedKey;
|
|
302
|
+
const label = p.item.meta?.label ?? p.item.label ?? p.item.name;
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<CanAccess
|
|
306
|
+
key={p.item.key}
|
|
307
|
+
resource={p.item.name}
|
|
308
|
+
action="list"
|
|
309
|
+
params={{
|
|
310
|
+
resource: p.item,
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
<NavLink
|
|
314
|
+
key={p.item.key}
|
|
315
|
+
label={label}
|
|
316
|
+
leftSection={p.item.meta?.icon ?? <IconList size={18} />}
|
|
317
|
+
active={isSelected}
|
|
318
|
+
component={Link as React.FC<{ to: string, onClick: () => void }>}
|
|
319
|
+
to={listUrl(p.item.name)}
|
|
320
|
+
onClick={p.onClick}
|
|
321
|
+
/>
|
|
322
|
+
</CanAccess>
|
|
323
|
+
);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const Locales = (p: {
|
|
327
|
+
locales: LayoutLocale[];
|
|
328
|
+
variant: "icon" | "full";
|
|
329
|
+
label: string;
|
|
330
|
+
}) => {
|
|
331
|
+
const { changeLocale, getLocale } = useTranslation();
|
|
332
|
+
const locale = getLocale();
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<Menu shadow="md" width={200}>
|
|
336
|
+
<Menu.Target>
|
|
337
|
+
{p.variant === "icon" ? (
|
|
338
|
+
<Tooltip label={p.label} position="right" transitionProps={{ duration: 0 }}>
|
|
339
|
+
<ActionIcon aria-label={p.label} variant="subtle" size="xl">
|
|
340
|
+
<IconLanguage />
|
|
341
|
+
</ActionIcon>
|
|
342
|
+
</Tooltip>
|
|
343
|
+
) : (
|
|
344
|
+
<NavLink
|
|
345
|
+
label={p.label}
|
|
346
|
+
leftSection={<IconLanguage size={18} />}
|
|
347
|
+
component="button"
|
|
348
|
+
/>
|
|
349
|
+
)}
|
|
350
|
+
</Menu.Target>
|
|
351
|
+
<Menu.Dropdown>
|
|
352
|
+
{p.locales.map(({label, lang, icon}) => (
|
|
353
|
+
<Menu.Item
|
|
354
|
+
key={lang}
|
|
355
|
+
leftSection={icon}
|
|
356
|
+
onClick={() => changeLocale(lang)}
|
|
357
|
+
rightSection={lang === locale ? <IconCheck size={14} /> : undefined}
|
|
358
|
+
>
|
|
359
|
+
{label}
|
|
360
|
+
</Menu.Item>
|
|
361
|
+
))}
|
|
362
|
+
</Menu.Dropdown>
|
|
363
|
+
</Menu>
|
|
364
|
+
);
|
|
365
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export * from "@/components/fields/PhoneField";
|
|
|
28
28
|
export * from "@/components/fields/UrlField";
|
|
29
29
|
// layout
|
|
30
30
|
export * from "@/components/layout/Layout";
|
|
31
|
+
export * from "@/components/layout/LayoutMinimal";
|
|
31
32
|
// notification
|
|
32
33
|
export * from "@/components/notification/AutoSaveIndicator";
|
|
33
34
|
export * from "@/components/notification/Message";
|
|
@@ -42,7 +43,9 @@ export * from "@/hooks/useOtp";
|
|
|
42
43
|
export * from "@/pages/auth/ForgotPasswordPage";
|
|
43
44
|
export * from "@/pages/auth/LoginPage";
|
|
44
45
|
export * from "@/pages/auth/RegisterPage";
|
|
46
|
+
export * from "@/pages/auth/RegistrationVerificationPage";
|
|
45
47
|
export * from "@/pages/auth/UpdatePasswordPage";
|
|
48
|
+
export * from "@/pages/auth/DefaultTitle";
|
|
46
49
|
export * from "@/pages/NotFound";
|
|
47
50
|
// providers
|
|
48
51
|
export type * from "@/providers/authProvider";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Stack } from "@mantine/core";
|
|
2
|
+
import type { Meta } from "@storybook/react";
|
|
3
|
+
import { DefaultTitle } from "./DefaultTitle";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Auth/DefaultTitle",
|
|
7
|
+
component: DefaultTitle,
|
|
8
|
+
decorators: (Story) => (
|
|
9
|
+
<Stack h="100vh" align="center" justify="center">
|
|
10
|
+
<Story />
|
|
11
|
+
</Stack>
|
|
12
|
+
),
|
|
13
|
+
} satisfies Meta<typeof DefaultTitle>;
|
|
14
|
+
|
|
15
|
+
export const Basic = () => <DefaultTitle />;
|
|
@@ -49,6 +49,8 @@ export type LoginPageProps = {
|
|
|
49
49
|
registerLink?: string;
|
|
50
50
|
forgotPasswordLink?: string;
|
|
51
51
|
validate?: FormValidateInput<LoginForm>;
|
|
52
|
+
onBeforeLogin?: (args: LoginArgs) => void | Promise<void>;
|
|
53
|
+
onBeforeProviderLogin?: (args: LoginArgs) => void | Promise<void>;
|
|
52
54
|
// customization
|
|
53
55
|
wrapperProps?: StackProps;
|
|
54
56
|
scrollAreaProps?: ScrollAreaProps;
|
|
@@ -98,22 +100,26 @@ export const LoginPage: React.FC<LoginPageProps> = (p) => {
|
|
|
98
100
|
},
|
|
99
101
|
});
|
|
100
102
|
|
|
101
|
-
const handleProviderLogin = useCallback((provider: OAuthProvider) => {
|
|
102
|
-
|
|
103
|
+
const handleProviderLogin = useCallback(async (provider: OAuthProvider) => {
|
|
104
|
+
const loginArgs = {
|
|
103
105
|
providerName: provider.name,
|
|
104
106
|
translate,
|
|
105
107
|
...p.mutationVariables
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
+
};
|
|
109
|
+
await p.onBeforeProviderLogin?.(loginArgs);
|
|
110
|
+
login.mutate(loginArgs);
|
|
111
|
+
}, [login, translate, p.mutationVariables, p.onBeforeProviderLogin]);
|
|
108
112
|
|
|
109
|
-
const handleLogin = onSubmit(({ email, password }) => {
|
|
110
|
-
|
|
113
|
+
const handleLogin = onSubmit(async ({ email, password }) => {
|
|
114
|
+
const loginArgs = {
|
|
111
115
|
email,
|
|
112
116
|
password,
|
|
113
117
|
otpHandler: p.otpHandler,
|
|
114
118
|
translate,
|
|
115
119
|
...p.mutationVariables,
|
|
116
|
-
}
|
|
120
|
+
};
|
|
121
|
+
await p.onBeforeLogin?.(loginArgs);
|
|
122
|
+
login.mutate(loginArgs);
|
|
117
123
|
});
|
|
118
124
|
|
|
119
125
|
return (
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RegistrationVerificationPage } from "./RegistrationVerificationPage";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: "Auth/RegistrationVerificationPage",
|
|
5
|
+
component: RegistrationVerificationPage,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Default = () => <RegistrationVerificationPage />;
|
|
9
|
+
|
|
10
|
+
export const WithLoginLink = () => (
|
|
11
|
+
<RegistrationVerificationPage loginLink="/login" />
|
|
12
|
+
);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
type ButtonProps,
|
|
4
|
+
Card,
|
|
5
|
+
type CardProps,
|
|
6
|
+
ScrollArea,
|
|
7
|
+
type ScrollAreaProps,
|
|
8
|
+
Stack,
|
|
9
|
+
type StackProps,
|
|
10
|
+
Text,
|
|
11
|
+
Title,
|
|
12
|
+
} from "@mantine/core";
|
|
13
|
+
import { Link, useTranslate } from "@refinedev/core";
|
|
14
|
+
import type { ReactNode } from "react";
|
|
15
|
+
import { DefaultTitle } from "./DefaultTitle";
|
|
16
|
+
|
|
17
|
+
export interface RegistrationVerificationPageProps {
|
|
18
|
+
loginLink?: string;
|
|
19
|
+
// props
|
|
20
|
+
wrapperProps?: StackProps;
|
|
21
|
+
scrollAreaProps?: ScrollAreaProps;
|
|
22
|
+
cardProps?: CardProps;
|
|
23
|
+
buttonProps?: ButtonProps;
|
|
24
|
+
// components
|
|
25
|
+
icon?: ReactNode;
|
|
26
|
+
title?: ReactNode;
|
|
27
|
+
description?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const RegistrationVerificationPage: React.FC<
|
|
31
|
+
RegistrationVerificationPageProps
|
|
32
|
+
> = (p) => {
|
|
33
|
+
const translate = useTranslate();
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Stack h="100vh" align="center" justify="center" {...p.wrapperProps}>
|
|
37
|
+
<ScrollArea type="never" {...p.scrollAreaProps}>
|
|
38
|
+
{p.icon ?? <DefaultTitle />}
|
|
39
|
+
<Card shadow="sm" padding="lg" radius="md" withBorder {...p.cardProps}>
|
|
40
|
+
{p.title ?? (
|
|
41
|
+
<Title order={5} mb="sm" ta="center">
|
|
42
|
+
{translate(
|
|
43
|
+
"pages.registerVerification.title",
|
|
44
|
+
"Thank you for your registration",
|
|
45
|
+
)}
|
|
46
|
+
</Title>
|
|
47
|
+
)}
|
|
48
|
+
{p.description ?? (
|
|
49
|
+
<Text size="sm" ta="center" c="dimmed">
|
|
50
|
+
{translate(
|
|
51
|
+
"pages.registerVerification.description",
|
|
52
|
+
"You will get an email where you can confirm your registration.",
|
|
53
|
+
)}
|
|
54
|
+
</Text>
|
|
55
|
+
)}
|
|
56
|
+
{p.loginLink && (
|
|
57
|
+
<Button
|
|
58
|
+
component={Link as React.FC<{ to: string }>}
|
|
59
|
+
to={p.loginLink}
|
|
60
|
+
fullWidth
|
|
61
|
+
mt="lg"
|
|
62
|
+
variant="light"
|
|
63
|
+
{...p.buttonProps}
|
|
64
|
+
>
|
|
65
|
+
{translate("pages.registerVerification.login", "Back to login")}
|
|
66
|
+
</Button>
|
|
67
|
+
)}
|
|
68
|
+
</Card>
|
|
69
|
+
</ScrollArea>
|
|
70
|
+
</Stack>
|
|
71
|
+
);
|
|
72
|
+
};
|