Can copy invitation code to clipboard
This commit is contained in:
parent
3a0f9c6e48
commit
e3dea1512c
16980
geneit_app/package-lock.json
generated
16980
geneit_app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@emotion/react": "^11.11.0",
|
"@emotion/react": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@fontsource/roboto": "^5.0.2",
|
"@fontsource/roboto": "^5.0.2",
|
||||||
@ -21,7 +22,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
|
@ -14,6 +14,7 @@ import { BrowserRouter } from "react-router-dom";
|
|||||||
import { ConfirmDialogProvider } from "./widgets/ConfirmDialogProvider";
|
import { ConfirmDialogProvider } from "./widgets/ConfirmDialogProvider";
|
||||||
import { AlertDialogProvider } from "./widgets/AlertDialogProvider";
|
import { AlertDialogProvider } from "./widgets/AlertDialogProvider";
|
||||||
import { AsyncWidget } from "./widgets/AsyncWidget";
|
import { AsyncWidget } from "./widgets/AsyncWidget";
|
||||||
|
import { SnackbarProvider } from "./widgets/SnackbarProvider";
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
@ -26,14 +27,16 @@ async function init() {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AlertDialogProvider>
|
<AlertDialogProvider>
|
||||||
<ConfirmDialogProvider>
|
<ConfirmDialogProvider>
|
||||||
<div style={{ height: "100vh" }}>
|
<SnackbarProvider>
|
||||||
<AsyncWidget
|
<div style={{ height: "100vh" }}>
|
||||||
loadKey={1}
|
<AsyncWidget
|
||||||
load={async () => await ServerApi.LoadConfig()}
|
loadKey={1}
|
||||||
errMsg="Echec de la connexion au serveur pour la récupération de la configuration statique !"
|
load={async () => await ServerApi.LoadConfig()}
|
||||||
build={() => <App />}
|
errMsg="Echec de la connexion au serveur pour la récupération de la configuration statique !"
|
||||||
/>
|
build={() => <App />}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</SnackbarProvider>
|
||||||
</ConfirmDialogProvider>
|
</ConfirmDialogProvider>
|
||||||
</AlertDialogProvider>
|
</AlertDialogProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { mdiFamilyTree } from "@mdi/js";
|
import { mdiFamilyTree } from "@mdi/js";
|
||||||
import Icon from "@mdi/react";
|
import Icon from "@mdi/react";
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
import { Button } from "@mui/material";
|
import { Box, Button } from "@mui/material";
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from "@mui/material/AppBar";
|
||||||
import Menu from "@mui/material/Menu";
|
import Menu from "@mui/material/Menu";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
@ -61,11 +61,20 @@ export function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
reloadUserInfo: load,
|
reloadUserInfo: load,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<Box
|
||||||
style={{
|
component="div"
|
||||||
|
sx={{
|
||||||
minHeight: "100vh",
|
minHeight: "100vh",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.mode === "light"
|
||||||
|
? theme.palette.grey[100]
|
||||||
|
: theme.palette.grey[900],
|
||||||
|
color: (theme) =>
|
||||||
|
theme.palette.mode === "light"
|
||||||
|
? theme.palette.grey[900]
|
||||||
|
: theme.palette.grey[100],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppBar position="sticky">
|
<AppBar position="sticky">
|
||||||
@ -122,7 +131,7 @@ export function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</Box>
|
||||||
</UserContextK.Provider>
|
</UserContextK.Provider>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
mdiCrowd,
|
mdiCrowd,
|
||||||
mdiFamilyTree,
|
mdiFamilyTree,
|
||||||
mdiHumanMaleFemale,
|
mdiHumanMaleFemale,
|
||||||
|
mdiLockCheck,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import Icon from "@mdi/react";
|
import Icon from "@mdi/react";
|
||||||
import HomeIcon from "@mui/icons-material/Home";
|
import HomeIcon from "@mui/icons-material/Home";
|
||||||
@ -15,12 +16,14 @@ import {
|
|||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
ListSubheader,
|
ListSubheader,
|
||||||
|
Tooltip,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Outlet, useLocation, useParams } from "react-router-dom";
|
import { Outlet, useLocation, useParams } from "react-router-dom";
|
||||||
import { Family, FamilyApi } from "../api/FamilyApi";
|
import { Family, FamilyApi } from "../api/FamilyApi";
|
||||||
import { AsyncWidget } from "./AsyncWidget";
|
import { AsyncWidget } from "./AsyncWidget";
|
||||||
import { RouterLink } from "./RouterLink";
|
import { RouterLink } from "./RouterLink";
|
||||||
|
import { useSnackbar } from "./SnackbarProvider";
|
||||||
|
|
||||||
interface FamilyContext {
|
interface FamilyContext {
|
||||||
family: Family;
|
family: Family;
|
||||||
@ -31,6 +34,7 @@ const FamilyContextK = React.createContext<FamilyContext | null>(null);
|
|||||||
|
|
||||||
export function BaseFamilyRoute(): React.ReactElement {
|
export function BaseFamilyRoute(): React.ReactElement {
|
||||||
const { familyId } = useParams();
|
const { familyId } = useParams();
|
||||||
|
const snackbar = useSnackbar();
|
||||||
|
|
||||||
const [family, setFamily] = React.useState<null | Family>(null);
|
const [family, setFamily] = React.useState<null | Family>(null);
|
||||||
|
|
||||||
@ -45,6 +49,11 @@ export function BaseFamilyRoute(): React.ReactElement {
|
|||||||
setFamily(null);
|
setFamily(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copyInvitationCode = async () => {
|
||||||
|
navigator.clipboard.writeText(family!.invitation_code);
|
||||||
|
snackbar("Le code d'invitation a été copié dans le presse papier !");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncWidget
|
<AsyncWidget
|
||||||
ready={family != null}
|
ready={family != null}
|
||||||
@ -58,8 +67,21 @@ export function BaseFamilyRoute(): React.ReactElement {
|
|||||||
reloadFamilyInfo: onReload,
|
reloadFamilyInfo: onReload,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", flex: "2" }}>
|
<Box
|
||||||
<List component="nav">
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flex: "2",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<List
|
||||||
|
component="nav"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.mode === "light"
|
||||||
|
? theme.palette.grey[100]
|
||||||
|
: "background.paper",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<FamilyLink icon={<HomeIcon />} label="Accueil" uri="" />
|
<FamilyLink icon={<HomeIcon />} label="Accueil" uri="" />
|
||||||
|
|
||||||
<FamilyLink
|
<FamilyLink
|
||||||
@ -81,9 +103,7 @@ export function BaseFamilyRoute(): React.ReactElement {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Divider sx={{ my: 1 }} />
|
<Divider sx={{ my: 1 }} />
|
||||||
<ListSubheader component="div" inset>
|
<ListSubheader component="div">Administration</ListSubheader>
|
||||||
Administration
|
|
||||||
</ListSubheader>
|
|
||||||
|
|
||||||
<FamilyLink
|
<FamilyLink
|
||||||
icon={<Icon path={mdiAccountMultiple} size={1} />}
|
icon={<Icon path={mdiAccountMultiple} size={1} />}
|
||||||
@ -96,15 +116,24 @@ export function BaseFamilyRoute(): React.ReactElement {
|
|||||||
label="Paramètres"
|
label="Paramètres"
|
||||||
uri="settings"
|
uri="settings"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Invitation code */}
|
||||||
|
<Tooltip title="Copier le code d'invitation dans le presse papier">
|
||||||
|
<ListItemButton onClick={copyInvitationCode}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Icon path={mdiLockCheck} size={1} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary="Code d'invitation"
|
||||||
|
secondary={family?.invitation_code}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
component="main"
|
component="main"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: (theme) =>
|
|
||||||
theme.palette.mode === "light"
|
|
||||||
? theme.palette.grey[100]
|
|
||||||
: theme.palette.grey[900],
|
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
padding: "20px",
|
padding: "20px",
|
||||||
|
43
geneit_app/src/widgets/SnackbarProvider.tsx
Normal file
43
geneit_app/src/widgets/SnackbarProvider.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Snackbar } from "@mui/material";
|
||||||
|
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
type SnackbarContext = (message: string, duration?: number) => void;
|
||||||
|
|
||||||
|
const SnackbarContextK = React.createContext<SnackbarContext | null>(null);
|
||||||
|
|
||||||
|
export function SnackbarProvider(p: PropsWithChildren): React.ReactElement {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const [message, setMessage] = React.useState("");
|
||||||
|
const [duration, setDuration] = React.useState(0);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hook: SnackbarContext = (message, duration) => {
|
||||||
|
setMessage(message);
|
||||||
|
setDuration(duration ?? 6000);
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SnackbarContextK.Provider value={hook}>
|
||||||
|
{p.children}
|
||||||
|
</SnackbarContextK.Provider>
|
||||||
|
|
||||||
|
<Snackbar
|
||||||
|
open={open}
|
||||||
|
autoHideDuration={duration}
|
||||||
|
onClose={handleClose}
|
||||||
|
message={message}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSnackbar(): SnackbarContext {
|
||||||
|
return React.useContext(SnackbarContextK)!;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user