convert setting modals + associate function
This commit is contained in:
@@ -1,16 +0,0 @@
|
|||||||
import styles from "./button.module.css";
|
|
||||||
|
|
||||||
const Button = ({ children, variant = "primary", onClick, className }) => {
|
|
||||||
let btnStyle;
|
|
||||||
|
|
||||||
if (variant === "primary") btnStyle = styles.primary;
|
|
||||||
else if (variant === "danger") btnStyle = styles.danger;
|
|
||||||
else if(variant === "transparent") btnStyle = styles.transparent;
|
|
||||||
else btnStyle = styles.default;
|
|
||||||
|
|
||||||
return <div className={`${styles.btn} ${btnStyle} ${className}`} onClick={onClick}>
|
|
||||||
{children}
|
|
||||||
</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Button;
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*import styles from "./button.module.css";
|
||||||
|
|
||||||
|
const Button = ({ children, variant = "primary", onClick, className }) => {
|
||||||
|
let btnStyle;
|
||||||
|
|
||||||
|
if (variant === "primary") btnStyle = styles.primary;
|
||||||
|
else if (variant === "danger") btnStyle = styles.danger;
|
||||||
|
else if(variant === "transparent") btnStyle = styles.transparent;
|
||||||
|
else btnStyle = styles.default;
|
||||||
|
|
||||||
|
return <div className={`${styles.btn} ${btnStyle} ${className}`} onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styles from "./button.module.css";
|
||||||
|
import { ReactNode, MouseEvent } from "react";
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
children: ReactNode;
|
||||||
|
variant?: "primary" | "danger" | "transparent" | "default";
|
||||||
|
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = ({
|
||||||
|
children,
|
||||||
|
variant = "primary",
|
||||||
|
onClick,
|
||||||
|
className = "",
|
||||||
|
}: ButtonProps) => {
|
||||||
|
const variantStyles = {
|
||||||
|
primary: styles.primary,
|
||||||
|
danger: styles.danger,
|
||||||
|
transparent: styles.transparent,
|
||||||
|
default: styles.default,
|
||||||
|
};
|
||||||
|
const btnStyle = variantStyles[variant in variantStyles ? variant : "default"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${styles.btn} ${btnStyle} ${className}`}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import styles from "./input.module.css";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const TextInput = ({ placeholder, onChange, value, borderStyle, password, className, ...props }) => {
|
|
||||||
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
|
||||||
|
|
||||||
let inputBorderStyle = styles.square;
|
|
||||||
if (borderStyle === "square") inputBorderStyle = styles.square;
|
|
||||||
if(borderStyle === "rounded") inputBorderStyle = styles.rounded;
|
|
||||||
|
|
||||||
const toggleShowPassword = () => {
|
|
||||||
setShowPassword(!showPassword);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(password) return (
|
|
||||||
<div className={styles.passwordContainer}>
|
|
||||||
<TextInput
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
value={value}
|
|
||||||
placeholder={placeholder}
|
|
||||||
onChange={onChange}
|
|
||||||
className={styles.passwordInput}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={toggleShowPassword}
|
|
||||||
className={styles.passwordBtn}
|
|
||||||
>
|
|
||||||
{showPassword ? '👁️' : '🔒'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return <input className={`${styles.input} ${inputBorderStyle} ${className}`} onChange={onChange} placeholder={placeholder} value={value} {...props} />
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TextInput;
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*import styles from "./input.module.css";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const TextInput = ({ placeholder, onChange, value, borderStyle, password, className, ...props }) => {
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
let inputBorderStyle = styles.square;
|
||||||
|
if (borderStyle === "square") inputBorderStyle = styles.square;
|
||||||
|
if(borderStyle === "rounded") inputBorderStyle = styles.rounded;
|
||||||
|
|
||||||
|
const toggleShowPassword = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(password) return (
|
||||||
|
<div className={styles.passwordContainer}>
|
||||||
|
<TextInput
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={onChange}
|
||||||
|
className={styles.passwordInput}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={toggleShowPassword}
|
||||||
|
className={styles.passwordBtn}
|
||||||
|
>
|
||||||
|
{showPassword ? '👁️' : '🔒'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <input className={`${styles.input} ${inputBorderStyle} ${className}`} onChange={onChange} placeholder={placeholder} value={value} {...props} />
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextInput;*/
|
||||||
|
|
||||||
|
|
||||||
|
import styles from "./input.module.css";
|
||||||
|
import { useState, InputHTMLAttributes, ChangeEvent } from "react";
|
||||||
|
|
||||||
|
interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||||
|
placeholder?: string;
|
||||||
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
value: string;
|
||||||
|
borderStyle?: "square" | "rounded";
|
||||||
|
password?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextInput = ({
|
||||||
|
placeholder,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
borderStyle = "square",
|
||||||
|
password = false,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: TextInputProps) => {
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const borderStyles = {
|
||||||
|
square: styles.square,
|
||||||
|
rounded: styles.rounded,
|
||||||
|
};
|
||||||
|
|
||||||
|
const inputBorderStyle = borderStyles[borderStyle] || styles.square;
|
||||||
|
|
||||||
|
const toggleShowPassword = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
return (
|
||||||
|
<div className={styles.passwordContainer}>
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={onChange}
|
||||||
|
className={`${styles.input} ${styles.passwordInput} ${inputBorderStyle} ${className}`}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={toggleShowPassword}
|
||||||
|
className={styles.passwordBtn}
|
||||||
|
>
|
||||||
|
{showPassword ? "👁️" : "🔒"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
className={`${styles.input} ${inputBorderStyle} ${className}`}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={value}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextInput;
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { createPortal } from "react-dom";
|
|
||||||
import styles from "./modal.module.css";
|
|
||||||
import Button from "../button/button.jsx";
|
|
||||||
|
|
||||||
const Modal = ({ open, onClose, children, title }) => {
|
|
||||||
|
|
||||||
if (!open) return null;
|
|
||||||
|
|
||||||
const handleOverlayClick = () => {
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalClick = (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
return createPortal(
|
|
||||||
<div className={styles.overlay} onClick={handleOverlayClick}>
|
|
||||||
<div className={styles.modal} onClick={handleModalClick}>
|
|
||||||
{title && <h2>{title}</h2>}
|
|
||||||
{children}
|
|
||||||
<div className={styles.modalFooter}>
|
|
||||||
<Button onClick={onClose}>Fermer</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>,
|
|
||||||
document.body
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Modal;
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*import { createPortal } from "react-dom";
|
||||||
|
import styles from "./modal.module.css";
|
||||||
|
import Button from "../button/button.jsx";
|
||||||
|
|
||||||
|
const Modal = ({ open, onClose, children, title }) => {
|
||||||
|
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className={styles.overlay} onClick={handleOverlayClick}>
|
||||||
|
<div className={styles.modal} onClick={handleModalClick}>
|
||||||
|
{title && <h2>{title}</h2>}
|
||||||
|
{children}
|
||||||
|
<div className={styles.modalFooter}>
|
||||||
|
<Button onClick={onClose}>Fermer</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;*/
|
||||||
|
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import styles from "./modal.module.css";
|
||||||
|
import Button from "../button/button";
|
||||||
|
import { ReactNode, MouseEvent } from "react";
|
||||||
|
|
||||||
|
interface ModalProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
children: ReactNode;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modal = ({ open, onClose, children, title }: ModalProps) => {
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className={styles.overlay} onClick={handleOverlayClick}>
|
||||||
|
<div className={styles.modal} onClick={handleModalClick}>
|
||||||
|
{title && <h2>{title}</h2>}
|
||||||
|
{children}
|
||||||
|
<div className={styles.modalFooter}>
|
||||||
|
<Button onClick={onClose}>Fermer</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import Button from "../button/button.jsx";
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import styles from "./ThemeSwitcher.module.css";
|
|
||||||
|
|
||||||
|
|
||||||
export default function ThemeSwitcher() {
|
|
||||||
|
|
||||||
const [theme, setTheme] = useState("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const t = localStorage.getItem("theme");
|
|
||||||
if(t) setTheme(t);
|
|
||||||
else setTheme("light");
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const switchTheme = () => {
|
|
||||||
|
|
||||||
let newTheme = theme === "light" ? "dark" : "light";
|
|
||||||
|
|
||||||
if(theme === null || theme === "") newTheme = "dark";
|
|
||||||
|
|
||||||
localStorage.setItem("theme", newTheme);
|
|
||||||
setTheme(newTheme);
|
|
||||||
|
|
||||||
window.dispatchEvent(new Event("theme-changed"));
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Button variant={"default"} onClick={switchTheme} className={styles.themeBtn}>
|
|
||||||
<img src={`/icons/theme/${theme}.svg`} alt={`${theme} icon`} className={styles.themeIcon} />
|
|
||||||
<p>Changer de thème </p>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*import Button from "../button/button.jsx";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import styles from "./ThemeSwitcher.module.css";
|
||||||
|
|
||||||
|
|
||||||
|
export default function ThemeSwitcher() {
|
||||||
|
|
||||||
|
const [theme, setTheme] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const t = localStorage.getItem("theme");
|
||||||
|
if(t) setTheme(t);
|
||||||
|
else setTheme("light");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchTheme = () => {
|
||||||
|
|
||||||
|
let newTheme = theme === "light" ? "dark" : "light";
|
||||||
|
|
||||||
|
if(theme === null || theme === "") newTheme = "dark";
|
||||||
|
|
||||||
|
localStorage.setItem("theme", newTheme);
|
||||||
|
setTheme(newTheme);
|
||||||
|
|
||||||
|
window.dispatchEvent(new Event("theme-changed"));
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Button variant={"default"} onClick={switchTheme} className={styles.themeBtn}>
|
||||||
|
<img src={`/icons/theme/${theme}.svg`} alt={`${theme} icon`} className={styles.themeIcon} />
|
||||||
|
<p>Changer de thème </p>
|
||||||
|
</Button>
|
||||||
|
}*/
|
||||||
|
|
||||||
|
import Button from "../button/button";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import styles from "./ThemeSwitcher.module.css";
|
||||||
|
|
||||||
|
type Theme = "light" | "dark";
|
||||||
|
|
||||||
|
export default function ThemeSwitcher() {
|
||||||
|
const [theme, setTheme] = useState<Theme | "">("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const savedTheme = localStorage.getItem("theme") as Theme | null;
|
||||||
|
if (savedTheme) {
|
||||||
|
setTheme(savedTheme);
|
||||||
|
} else {
|
||||||
|
setTheme("light");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchTheme = () => {
|
||||||
|
const newTheme: Theme = theme === "light" ? "dark" : "light";
|
||||||
|
localStorage.setItem("theme", newTheme);
|
||||||
|
setTheme(newTheme);
|
||||||
|
window.dispatchEvent(new Event("theme-changed"));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
onClick={switchTheme}
|
||||||
|
className={styles.themeBtn}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`/icons/theme/${theme}.svg`}
|
||||||
|
alt={`${theme} icon`}
|
||||||
|
className={styles.themeIcon}
|
||||||
|
/>
|
||||||
|
<p>Changer de thème</p>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import Modal from "../../../../components/ui/modal/modal.jsx";
|
|
||||||
import Button from "../../../../components/ui/button/button.jsx";
|
|
||||||
import styles from "./SettingsModal.module.css"
|
|
||||||
import { useContext, useState } from "react";
|
|
||||||
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher.jsx";
|
|
||||||
import { AuthContext } from "../../../../contexts/auth/AuthContext.js";
|
|
||||||
import TextInput from "../../../../components/ui/input/input.jsx";
|
|
||||||
import updateUser from "../../../../utils/users/updateUser.js";
|
|
||||||
import deleteUser from "../../../../utils/users/deleteUser.js";
|
|
||||||
|
|
||||||
|
|
||||||
export default function SettingsModal() {
|
|
||||||
|
|
||||||
const { user, logout, update } = useContext(AuthContext);
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [isDelete, setIsDelete] = useState(false);
|
|
||||||
|
|
||||||
const [name, setName] = useState(user?.name);
|
|
||||||
const [lastname, setLastName] = useState(user?.lastname);
|
|
||||||
const [phone, setPhone] = useState(user?.phone);
|
|
||||||
|
|
||||||
const handleLogout = () => {
|
|
||||||
logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
await updateUser(name, lastname, phone);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = async () => {
|
|
||||||
await deleteUser();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button variant={"transparent"} onClick={() => setOpen(!open)}>
|
|
||||||
<img src={"/icons/settings-wheel.svg"} alt={"Modify btn"} className={styles.modifyIcon}/>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Modal title={"Paramètres"} open={open} onClose={() => setOpen(false)}>
|
|
||||||
<div className={styles.content}>
|
|
||||||
|
|
||||||
<div className={styles.section}>
|
|
||||||
<h3>Compte</h3>
|
|
||||||
<Button variant={"danger"} onClick={handleLogout}> Déconnexion </Button>
|
|
||||||
<Button variant={"danger"} onClick={() => setIsDelete(true)}> Supprimer le compte </Button>
|
|
||||||
|
|
||||||
<div className={styles.section}>
|
|
||||||
<h4>Changer de Prénom</h4>
|
|
||||||
<TextInput value={name} onChange={e => setName(e.target.value)} type={"text"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.section}>
|
|
||||||
<h4>Changer de nom</h4>
|
|
||||||
<TextInput value={lastname} onChange={e => setLastName(e.target.value)} type={"text"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.section}>
|
|
||||||
<h4>Changer de numéro de téléphone</h4>
|
|
||||||
<TextInput value={phone} onChange={e => setPhone(e.target.value)} type={"text"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button className={styles.submitBtn} onClick={handleSubmit}>Confirmer</Button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.section}>
|
|
||||||
<h3>Apparence</h3>
|
|
||||||
<ThemeSwitcher />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
|
|
||||||
<Modal title={"Validation"} open={isDelete} onClose={() => setIsDelete(false)}>
|
|
||||||
<div className={styles.section}>
|
|
||||||
<p className={styles.alignText}> Êtes vous vraiment sûr de vouloir supprimer votre compte ?</p>
|
|
||||||
<Button variant={"danger"} className={styles.submitBtn} onClick={handleDelete}>Confirmer</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
/*import Modal from "../../../../components/ui/modal/modal.jsx";
|
||||||
|
import Button from "../../../../components/ui/button/button.jsx";
|
||||||
|
import styles from "./SettingsModal.module.css"
|
||||||
|
import { useContext, useState } from "react";
|
||||||
|
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher.jsx";
|
||||||
|
import { AuthContext } from "../../../../contexts/auth/AuthContext.js";
|
||||||
|
import TextInput from "../../../../components/ui/input/input.jsx";
|
||||||
|
import updateUser from "../../../../utils/users/updateUser.js";
|
||||||
|
import deleteUser from "../../../../utils/users/deleteUser.js";
|
||||||
|
|
||||||
|
|
||||||
|
export default function SettingsModal() {
|
||||||
|
|
||||||
|
const { user, logout, update } = useContext(AuthContext);
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [isDelete, setIsDelete] = useState(false);
|
||||||
|
|
||||||
|
const [name, setName] = useState(user?.name);
|
||||||
|
const [lastname, setLastName] = useState(user?.lastname);
|
||||||
|
const [phone, setPhone] = useState(user?.phone);
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await updateUser(name, lastname, phone);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await deleteUser();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant={"transparent"} onClick={() => setOpen(!open)}>
|
||||||
|
<img src={"/icons/settings-wheel.svg"} alt={"Modify btn"} className={styles.modifyIcon}/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Modal title={"Paramètres"} open={open} onClose={() => setOpen(false)}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Compte</h3>
|
||||||
|
<Button variant={"danger"} onClick={handleLogout}> Déconnexion </Button>
|
||||||
|
<Button variant={"danger"} onClick={() => setIsDelete(true)}> Supprimer le compte </Button>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de Prénom</h4>
|
||||||
|
<TextInput value={name} onChange={e => setName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de nom</h4>
|
||||||
|
<TextInput value={lastname} onChange={e => setLastName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de numéro de téléphone</h4>
|
||||||
|
<TextInput value={phone} onChange={e => setPhone(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button className={styles.submitBtn} onClick={handleSubmit}>Confirmer</Button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Apparence</h3>
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
<Modal title={"Validation"} open={isDelete} onClose={() => setIsDelete(false)}>
|
||||||
|
<div className={styles.section}>
|
||||||
|
<p className={styles.alignText}> Êtes vous vraiment sûr de vouloir supprimer votre compte ?</p>
|
||||||
|
<Button variant={"danger"} className={styles.submitBtn} onClick={handleDelete}>Confirmer</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
import Modal from "../../../../components/ui/modal/modal";
|
||||||
|
import Button from "../../../../components/ui/button/button";
|
||||||
|
import styles from "./SettingsModal.module.css";
|
||||||
|
import { useContext, useState } from "react";
|
||||||
|
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher";
|
||||||
|
import { AuthContext } from "../../../../contexts/auth/AuthContext";
|
||||||
|
import TextInput from "../../../../components/ui/input/input";
|
||||||
|
import updateUser from "../../../../utils/users/updateUser";
|
||||||
|
import deleteUser from "../../../../utils/users/deleteUser";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
phone: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuthContextType {
|
||||||
|
user: User | null;
|
||||||
|
logout: () => void;
|
||||||
|
update: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsModal() {
|
||||||
|
const { user, logout, update } = useContext(AuthContext) as AuthContextType;
|
||||||
|
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||||
|
const [name, setName] = useState<string>(user?.name || "");
|
||||||
|
const [lastname, setLastName] = useState<string>(user?.lastname || "");
|
||||||
|
const [phone, setPhone] = useState<string | null>(user?.phone || null);
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
logout();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await updateUser(name, lastname, phone);
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await deleteUser();
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant={"transparent"} onClick={() => setOpen(!open)}>
|
||||||
|
<img src={"/icons/settings-wheel.svg"} alt={"Modify btn"} className={styles.modifyIcon} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Modal title={"Paramètres"} open={open} onClose={() => setOpen(false)}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Compte</h3>
|
||||||
|
<Button variant={"danger"} onClick={handleLogout}>
|
||||||
|
Déconnexion
|
||||||
|
</Button>
|
||||||
|
<Button variant={"danger"} onClick={() => setIsDelete(true)}>
|
||||||
|
Supprimer le compte
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de Prénom</h4>
|
||||||
|
<TextInput value={name} onChange={(e) => setName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de nom</h4>
|
||||||
|
<TextInput value={lastname} onChange={(e) => setLastName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de numéro de téléphone</h4>
|
||||||
|
<TextInput value={phone || ""} onChange={(e) => setPhone(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button className={styles.submitBtn} onClick={handleSubmit}>
|
||||||
|
Confirmer
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Apparence</h3>
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal title={"Validation"} open={isDelete} onClose={() => setIsDelete(false)}>
|
||||||
|
<div className={styles.section}>
|
||||||
|
<p className={styles.alignText}>Êtes-vous vraiment sûr de vouloir supprimer votre compte ?</p>
|
||||||
|
<Button variant={"danger"} className={styles.submitBtn} onClick={handleDelete}>
|
||||||
|
Confirmer
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
export default async function fetchWrapper(
|
|
||||||
path,
|
|
||||||
data = null,
|
|
||||||
method = "GET",
|
|
||||||
headers = {}
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const res = await fetch(
|
|
||||||
`${import.meta.env.VITE_API_URL}${path}`,
|
|
||||||
{
|
|
||||||
method,
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...headers,
|
|
||||||
},
|
|
||||||
body: data ? JSON.stringify(data) : null,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let responseData = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
responseData = await res.json();
|
|
||||||
} catch (_) {
|
|
||||||
responseData = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: res.status,
|
|
||||||
data: responseData,
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error("❌ Erreur réseau :", err);
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 0,
|
|
||||||
data: {
|
|
||||||
message: "Network error",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*export default async function fetchWrapper(path,data = null,method = "GET",headers = {}) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`${import.meta.env.VITE_API_URL}${path}`,
|
||||||
|
{
|
||||||
|
method,
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
body: data ? JSON.stringify(data) : null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let responseData = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
responseData = await res.json();
|
||||||
|
} catch (_) {
|
||||||
|
responseData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: res.status,
|
||||||
|
data: responseData,
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Erreur réseau :", err);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 0,
|
||||||
|
data: {
|
||||||
|
message: "Network error",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
interface FetchResponse<T = any> {
|
||||||
|
status: number;
|
||||||
|
data: T | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function fetchWrapper(
|
||||||
|
path: string,
|
||||||
|
data: any = null,
|
||||||
|
method: string = "GET",
|
||||||
|
headers: Record<string, string> = {}
|
||||||
|
): Promise<FetchResponse> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`${import.meta.env.VITE_API_URL}${path}`,
|
||||||
|
{
|
||||||
|
method,
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
body: data ? JSON.stringify(data) : null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let responseData = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
responseData = await res.json();
|
||||||
|
} catch (_) {
|
||||||
|
responseData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: res.status,
|
||||||
|
data: responseData,
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Erreur réseau :", err);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 0,
|
||||||
|
data: {
|
||||||
|
message: "Network error",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
export default async function getXSRFToken() {
|
|
||||||
const response = await fetch(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'include'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok && response.status !== 204) {
|
|
||||||
throw new Error(`Error : ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = 'XSRF-TOKEN';
|
|
||||||
const value = `; ${document.cookie}`;
|
|
||||||
const parts = value.split(`; ${name}=`);
|
|
||||||
if (parts.length === 2) {
|
|
||||||
return decodeURIComponent(parts.pop().split(';').shift());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Error: Invalid XSRF-TOKEN');
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*export default async function getXSRFToken() {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok && response.status !== 204) {
|
||||||
|
throw new Error(`Error : ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = 'XSRF-TOKEN';
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return decodeURIComponent(parts.pop().split(';').shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Error: Invalid XSRF-TOKEN');
|
||||||
|
}*/
|
||||||
|
|
||||||
|
export default async function getXSRFToken(): Promise<string> {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok && response.status !== 204) {
|
||||||
|
throw new Error(`Error : ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = 'XSRF-TOKEN';
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return decodeURIComponent(parts.pop()!.split(';').shift()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Error: Invalid XSRF-TOKEN');
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import fetchWrapper from "../fetchWrapper.js";
|
|
||||||
import getXSRFToken from "../getXSRF.js";
|
|
||||||
|
|
||||||
export default async function deleteUser() {
|
|
||||||
const csrfToken = await getXSRFToken();
|
|
||||||
|
|
||||||
return fetchWrapper(
|
|
||||||
"/api/users",
|
|
||||||
null,
|
|
||||||
"DELETE",
|
|
||||||
{ "X-XSRF-TOKEN": csrfToken }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import fetchWrapper from "../fetchWrapper";
|
||||||
|
import getXSRFToken from "../getXSRF";
|
||||||
|
|
||||||
|
export default async function deleteUser(): Promise<{status: number;data: any;}> {
|
||||||
|
const csrfToken: string = await getXSRFToken();
|
||||||
|
|
||||||
|
return fetchWrapper(
|
||||||
|
"/api/users",
|
||||||
|
null,
|
||||||
|
"DELETE",
|
||||||
|
{ "X-XSRF-TOKEN": csrfToken }
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import fetchWrapper from "../fetchWrapper.js";
|
|
||||||
import getXSRFToken from "../getXSRF.js";
|
|
||||||
|
|
||||||
export default async function updateUser(name, lastname, phone) {
|
|
||||||
const csrfToken = await getXSRFToken();
|
|
||||||
|
|
||||||
return fetchWrapper(
|
|
||||||
"/api/users/update",
|
|
||||||
{ name, lastname, phone },
|
|
||||||
"POST",
|
|
||||||
{ "X-XSRF-TOKEN": csrfToken }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*import fetchWrapper from "../fetchWrapper.js";
|
||||||
|
import getXSRFToken from "../getXSRF.js";
|
||||||
|
|
||||||
|
export default async function updateUser(name, lastname, phone) {
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
return fetchWrapper(
|
||||||
|
"/api/users/update",
|
||||||
|
{ name, lastname, phone },
|
||||||
|
"POST",
|
||||||
|
{ "X-XSRF-TOKEN": csrfToken }
|
||||||
|
);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
import fetchWrapper from "../fetchWrapper";
|
||||||
|
import getXSRFToken from "../getXSRF";
|
||||||
|
|
||||||
|
interface UserUpdateData {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
phone: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestHeaders {
|
||||||
|
"X-XSRF-TOKEN": string;
|
||||||
|
}
|
||||||
|
export default async function updateUser(name: string,lastname: string,phone: string | null): Promise<Response> {
|
||||||
|
const csrfToken: string = await getXSRFToken();
|
||||||
|
const userData: UserUpdateData = {name,lastname,phone,};
|
||||||
|
const headers: RequestHeaders = {"X-XSRF-TOKEN": csrfToken,};
|
||||||
|
return fetchWrapper("/api/users/update",userData,"POST",headers);
|
||||||
|
}
|
||||||
|
|
||||||
+1
-1
@@ -54,7 +54,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": false,
|
||||||
|
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
|
|||||||
Reference in New Issue
Block a user