Can request account deletion
This commit is contained in:
		@@ -79,4 +79,14 @@ export class UserApi {
 | 
			
		||||
        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/700.css";
 | 
			
		||||
import { BrowserRouter } from "react-router-dom";
 | 
			
		||||
import { ConfirmDialogProvider } from "./widgets/ConfirmDialogProvider";
 | 
			
		||||
import { AlertDialogProvider } from "./widgets/AlertDialogProvider";
 | 
			
		||||
 | 
			
		||||
async function init() {
 | 
			
		||||
  try {
 | 
			
		||||
@@ -23,9 +25,13 @@ async function init() {
 | 
			
		||||
    root.render(
 | 
			
		||||
      <React.StrictMode>
 | 
			
		||||
        <BrowserRouter>
 | 
			
		||||
          <div style={{ height: "100vh" }}>
 | 
			
		||||
            <App />
 | 
			
		||||
          </div>
 | 
			
		||||
          <AlertDialogProvider>
 | 
			
		||||
            <ConfirmDialogProvider>
 | 
			
		||||
              <div style={{ height: "100vh" }}>
 | 
			
		||||
                <App />
 | 
			
		||||
              </div>
 | 
			
		||||
            </ConfirmDialogProvider>
 | 
			
		||||
          </AlertDialogProvider>
 | 
			
		||||
        </BrowserRouter>
 | 
			
		||||
      </React.StrictMode>
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,8 @@ import { ReplacePasswordResponse, User, UserApi } from "../api/UserApi";
 | 
			
		||||
import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
			
		||||
import { PasswordInput } from "../widgets/PasswordInput";
 | 
			
		||||
import { formatDate } from "../widgets/TimeWidget";
 | 
			
		||||
import { useConfirm } from "../widgets/ConfirmDialogProvider";
 | 
			
		||||
import { useAlert } from "../widgets/AlertDialogProvider";
 | 
			
		||||
 | 
			
		||||
export function ProfileRoute(): React.ReactElement {
 | 
			
		||||
  const [user, setUser] = React.useState<null | User>(null);
 | 
			
		||||
@@ -41,6 +43,7 @@ export function ProfileRoute(): React.ReactElement {
 | 
			
		||||
            onUpdate={() => (counter.current += 1)}
 | 
			
		||||
          />
 | 
			
		||||
          {user?.has_password && <ChangePasswordCard />}
 | 
			
		||||
          <DeleteAccountButton />
 | 
			
		||||
        </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)!;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user