Can request account deletion
This commit is contained in:
parent
6f58e767a2
commit
10e5c124fd
@ -79,4 +79,14 @@ export class UserApi {
|
|||||||
return ReplacePasswordResponse.Error;
|
return ReplacePasswordResponse.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request account deletion
|
||||||
|
*/
|
||||||
|
static async RequestAccountDeletion(): Promise<void> {
|
||||||
|
await APIClient.exec({
|
||||||
|
uri: "/user/request_delete",
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import "@fontsource/roboto/400.css";
|
|||||||
import "@fontsource/roboto/500.css";
|
import "@fontsource/roboto/500.css";
|
||||||
import "@fontsource/roboto/700.css";
|
import "@fontsource/roboto/700.css";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { ConfirmDialogProvider } from "./widgets/ConfirmDialogProvider";
|
||||||
|
import { AlertDialogProvider } from "./widgets/AlertDialogProvider";
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
@ -23,9 +25,13 @@ async function init() {
|
|||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<div style={{ height: "100vh" }}>
|
<AlertDialogProvider>
|
||||||
<App />
|
<ConfirmDialogProvider>
|
||||||
</div>
|
<div style={{ height: "100vh" }}>
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</ConfirmDialogProvider>
|
||||||
|
</AlertDialogProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
@ -16,6 +16,8 @@ import { ReplacePasswordResponse, User, UserApi } from "../api/UserApi";
|
|||||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
import { PasswordInput } from "../widgets/PasswordInput";
|
import { PasswordInput } from "../widgets/PasswordInput";
|
||||||
import { formatDate } from "../widgets/TimeWidget";
|
import { formatDate } from "../widgets/TimeWidget";
|
||||||
|
import { useConfirm } from "../widgets/ConfirmDialogProvider";
|
||||||
|
import { useAlert } from "../widgets/AlertDialogProvider";
|
||||||
|
|
||||||
export function ProfileRoute(): React.ReactElement {
|
export function ProfileRoute(): React.ReactElement {
|
||||||
const [user, setUser] = React.useState<null | User>(null);
|
const [user, setUser] = React.useState<null | User>(null);
|
||||||
@ -41,6 +43,7 @@ export function ProfileRoute(): React.ReactElement {
|
|||||||
onUpdate={() => (counter.current += 1)}
|
onUpdate={() => (counter.current += 1)}
|
||||||
/>
|
/>
|
||||||
{user?.has_password && <ChangePasswordCard />}
|
{user?.has_password && <ChangePasswordCard />}
|
||||||
|
<DeleteAccountButton />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -258,3 +261,37 @@ function ChangePasswordCard(): React.ReactElement {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DeleteAccountButton(): React.ReactElement {
|
||||||
|
const alert = useAlert();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
|
const requestDelete = async () => {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!(await confirm.confirm(
|
||||||
|
"Voulez-vous initier la suppression de votre compte ?",
|
||||||
|
"Suppression de compte"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await UserApi.RequestAccountDeletion();
|
||||||
|
|
||||||
|
await alert.alert(
|
||||||
|
"Demande de suppression de compte enregistrée avec succès. Veuillez consulter votre boîte mail."
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert.alert("Echec de la demande de suppression de compte !");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<Button onClick={requestDelete} color="error">
|
||||||
|
Supprimer mon compte
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
77
geneit_app/src/widgets/AlertDialogProvider.tsx
Normal file
77
geneit_app/src/widgets/AlertDialogProvider.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@mui/material";
|
||||||
|
import React, {
|
||||||
|
PropsWithChildren,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useRef,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
interface AlertHook {
|
||||||
|
alert: (message: string, title?: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AlertContext = createContext<AlertHook | null>(null);
|
||||||
|
|
||||||
|
export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const [title, setTitle] = React.useState<string | undefined>(undefined);
|
||||||
|
const [message, setMessage] = React.useState("");
|
||||||
|
|
||||||
|
const cb = React.useRef<null | (() => void)>(null);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
|
||||||
|
if (cb.current !== null) cb.current();
|
||||||
|
cb.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hook: AlertHook = {
|
||||||
|
alert: (message, title) => {
|
||||||
|
setTitle(title);
|
||||||
|
setMessage(message);
|
||||||
|
setOpen(true);
|
||||||
|
|
||||||
|
return new Promise((res) => {
|
||||||
|
cb.current = res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AlertContext.Provider value={hook}>{p.children}</AlertContext.Provider>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
{title && <DialogTitle id="alert-dialog-title">{title}</DialogTitle>}
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
{message}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose} autoFocus>
|
||||||
|
Ok
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAlert(): AlertHook {
|
||||||
|
return useContext(AlertContext)!;
|
||||||
|
}
|
92
geneit_app/src/widgets/ConfirmDialogProvider.tsx
Normal file
92
geneit_app/src/widgets/ConfirmDialogProvider.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@mui/material";
|
||||||
|
import React, {
|
||||||
|
PropsWithChildren,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useRef,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
interface ConfirmHook {
|
||||||
|
confirm: (
|
||||||
|
message: string,
|
||||||
|
title?: string,
|
||||||
|
confirmButton?: string
|
||||||
|
) => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmContext = createContext<ConfirmHook | null>(null);
|
||||||
|
|
||||||
|
export function ConfirmDialogProvider(
|
||||||
|
p: PropsWithChildren
|
||||||
|
): React.ReactElement {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const [title, setTitle] = React.useState<string | undefined>(undefined);
|
||||||
|
const [message, setMessage] = React.useState("");
|
||||||
|
const [confirmButton, setConfirmButton] = React.useState<string | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const cb = React.useRef<null | ((a: boolean) => void)>(null);
|
||||||
|
|
||||||
|
const handleClose = (confirm: boolean) => {
|
||||||
|
setOpen(false);
|
||||||
|
|
||||||
|
if (cb.current !== null) cb.current(confirm);
|
||||||
|
cb.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hook: ConfirmHook = {
|
||||||
|
confirm: (message, title, confirmButton) => {
|
||||||
|
setTitle(title);
|
||||||
|
setMessage(message);
|
||||||
|
setConfirmButton(confirmButton);
|
||||||
|
setOpen(true);
|
||||||
|
|
||||||
|
return new Promise((res) => {
|
||||||
|
cb.current = res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ConfirmContext.Provider value={hook}>
|
||||||
|
{p.children}
|
||||||
|
</ConfirmContext.Provider>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={() => handleClose(false)}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
{title && <DialogTitle id="alert-dialog-title">{title}</DialogTitle>}
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
{message}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => handleClose(false)} autoFocus>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => handleClose(true)} color="error">
|
||||||
|
{confirmButton ?? "Confirmer"}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useConfirm(): ConfirmHook {
|
||||||
|
return useContext(ConfirmContext)!;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user