import { useTheme } from "@emotion/react"; import { useState } from "react"; import TabPanel from "@mui/lab/TabPanel"; import { Box, Button, Divider, Stack, Typography } from "@mui/material"; import Avatar from "@/Components/v1/Avatar/index.jsx"; import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx"; import ImageUpload from "@/Components/v1/Inputs/ImageUpload/index.jsx"; import { newOrChangedCredentials } from "../../../Validation/validation.js"; import { useDispatch, useSelector } from "react-redux"; import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice.js"; import { createToast } from "../../../Utils/toastUtils.jsx"; import { logger } from "../../../Utils/Logger.js"; import { GenericDialog } from "@/Components/v1/Dialog/genericDialog.jsx"; import Dialog from "@/Components/v1/Dialog/index.jsx"; import { useTranslation } from "react-i18next"; /** * 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 { t } = useTranslation(); const SPACING_GAP = theme.spacing(12); //redux state const { user, 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", }; // Local state for form data, errors, and file handling const [localData, setLocalData] = useState({ firstName: user.firstName, lastName: user.lastName, // email: user.email, // Disabled for now }); const [errors, setErrors] = useState({}); const [file, setFile] = useState(); // Handles input field changes and performs validation const handleChange = (event) => { errors["unchanged"] && clearError("unchanged"); const { value, id } = event.target; const name = idToName[id]; setLocalData((prev) => ({ ...prev, [name]: value, })); validateField({ [name]: value }, newOrChangedCredentials, name); }; // Validates input against provided schema and updates error state 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; }; // Clears specific error from errors state const clearError = (err) => { setErrors((prev) => { const updatedErrors = { ...prev }; if (updatedErrors[err]) delete updatedErrors[err]; return updatedErrors; }); }; // Resets picture-related states and clears interval const removePicture = () => { errors["picture"] && clearError("picture"); setFile(undefined); setLocalData((prev) => ({ ...prev, file: undefined, deleteProfileImage: true, })); }; // Opens the picture update modal const openPictureModal = () => { setIsOpen("picture"); setFile(undefined); }; // Closes the picture update modal and resets related states const closePictureModal = () => { if (errors["picture"]) clearError("picture"); setFile(undefined); setIsOpen(""); }; // Updates profile image displayed on UI const handleUpdatePicture = () => { setLocalData((prev) => ({ ...prev, file: file?.src, deleteProfileImage: false, })); setIsOpen(""); if (errors["unchanged"]) clearError("unchanged"); }; // Handles form submission to update user profile const handleSaveProfile = async (event) => { event.preventDefault(); const nameChanged = localData.firstName !== user.firstName || localData.lastName !== user.lastName; const avatarChanged = localData.deleteProfileImage === true || (localData.file && localData.file !== `data:image/png;base64,${user.avatarImage}`); if (!nameChanged && !avatarChanged) { createToast({ body: "Unable to update profile — no changes detected.", }); setErrors({ unchanged: "unable to update profile" }); return; } const action = await dispatch(update({ localData })); if (action.payload.success) { createToast({ body: "Your profile data was changed successfully.", }); } else { createToast({ body: "There was an error updating your profile data.", }); } }; // Removes current profile image from UI const handleDeletePicture = () => { setLocalData((prev) => ({ ...prev, deleteProfileImage: true, })); setFile(undefined); errors["unchanged"] && clearError("unchanged"); }; // Initiates the account deletion process const handleDeleteAccount = async () => { const action = await dispatch(deleteUser()); if (action.payload.success) { dispatch(clearAuthState()); } else { if (action.payload) { // dispatch errors createToast({ body: action.payload.msg, }); } else { // unknown errors createToast({ body: "Unknown error.", }); } } }; // Modal state and control functions const [isOpen, setIsOpen] = useState(""); const isModalOpen = (name) => isOpen === name; return ( {/* This 0.9 is a bit magic numbering, refactor */} {t("FirstName")} {t("LastName")} {t("auth.common.inputs.email.label")} {t("EmailDescriptionText")} logger.warn("disabled")} disabled={true} flex={1} /> {t("YourPhoto")} {t("PhotoDescriptionText")} ); }; ProfilePanel.propTypes = { // No props are being passed to this component, hence no specific PropTypes are defined. }; export default ProfilePanel;