import { useTheme } from "@emotion/react"; import { useRef, useState } from "react"; import TabPanel from "@mui/lab/TabPanel"; import { Box, Divider, Modal, Stack, Typography } from "@mui/material"; import ButtonSpinner from "../../ButtonSpinner"; import Button from "../../Button"; import EmailTextField from "../../TextFields/Email/EmailTextField"; import StringTextField from "../../TextFields/Text/TextField"; import Avatar from "../../Avatar"; import { editProfileValidation, imageValidation, } from "../../../Validation/validation"; import { useDispatch, useSelector } from "react-redux"; import { update } from "../../../Features/Auth/authSlice"; import ImageField from "../../TextFields/Image"; import ImageIcon from "@mui/icons-material/Image"; import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; /** * ProfilePanel component displays a form for editing user profile information * and allows for actions like updating profile picture, credentials, * and deleting account. * * @returns {JSX.Element} */ const ProfilePanel = () => { const theme = useTheme(); const dispatch = useDispatch(); const { user, authToken, isLoading } = useSelector((state) => state.auth); const idToName = { "edit-first-name": "firstname", "edit-last-name": "lastname", // Disabled for now, will revisit in the future // "edit-email": "email", }; const [localData, setLocalData] = useState({ firstname: user.firstname, lastname: user.lastname, // Disabled for now, will revisit in the future // email: user.email, profileImage: user.profileImage, }); const [errors, setErrors] = useState({}); const clearError = (err) => { setErrors((prev) => { const updatedErrors = { ...prev }; if (updatedErrors[err]) delete updatedErrors[err]; return updatedErrors; }); }; const handleChange = (event) => { const { value, id } = event.target; const name = idToName[id]; setLocalData((prev) => ({ ...prev, [name]: value, })); validateField({ [name]: value }, editProfileValidation, name); }; const [file, setFile] = useState(); const intervalRef = useRef(null); const [progress, setProgress] = useState({ value: 0, isLoading: false }); const handlePicture = (event) => { const pic = event.target.files[0]; let error = validateField( { type: pic.type, size: pic.size }, imageValidation ); if (error) return; setProgress((prev) => ({ ...prev, isLoading: true })); setFile({ src: URL.createObjectURL(pic), name: pic.name, size: formatBytes(pic.size), }); //TODO - potentitally remove once image compression functions are implemented above intervalRef.current = setInterval(() => { const buffer = 12; setProgress((prev) => { if (prev.value + buffer >= 100) { clearInterval(intervalRef.current); return { value: 100, isLoading: false }; } return { ...prev, value: prev.value + buffer }; }); }, 120); }; const validateField = (toValidate, schema, name = "picture") => { const { error } = schema.validate(toValidate, { abortEarly: false }); setErrors((prev) => { const prevErrors = { ...prev }; if (error) prevErrors[name] = error.details[0].message; else delete prevErrors[name]; return prevErrors; }); if (error) return true; }; const removePicture = () => { clearError("picture"); setFile({ src: "placeholder" }); //interrupt interval if image upload is canceled prior to completing the process clearInterval(intervalRef.current); setProgress({ value: 0, isLoading: false }); }; const closePictureModal = () => { removePicture(); setIsOpen(""); }; const handleUpdatePicture = () => { setProgress({ value: 0, isLoading: false }); setLocalData((prev) => ({ ...prev, profileImage: null, })); setIsOpen(""); }; const handleDeletePicture = () => { setFile({ src: "placeholder" }); setLocalData((prev) => ({ ...prev, profileImage: null, })); }; //TODO - implement delete account function const handleDeleteAccount = () => {}; const handleSaveProfile = (event) => { event.preventDefault(); if ( localData.firstname === user.firstname && localData.lastname === user.lastname && localData.profileImage === user.profileImage ) { //TODO - add toast(profile data is unchanged) and maybe disable button return; } dispatch(update({ authToken, localData })); //TODO - add toast confirmation }; //modal controllers const [isOpen, setIsOpen] = useState(""); const isModalOpen = (name) => isOpen === name; // console.log(document.querySelector('input[type="file"]')?.files[0]); return (
First name {errors[idToName["edit-first-name"]] ? ( {errors[idToName["edit-first-name"]]} ) : ( "" )}
Last name {errors[idToName["edit-last-name"]] ? ( {errors[idToName["edit-last-name"]]} ) : ( "" )}
Email This is your current email address — it cannot be changed. {errors[idToName["edit-email"]] ? ( {errors[idToName["edit-email"]]} ) : ( "" )}
Your photo This photo will be displayed in your profile page.