From 95a30c5a8d058ab56b14c0eb3ad536eb2841257b Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Wed, 26 Jun 2024 19:06:10 -0400 Subject: [PATCH] Client side picture upload --- Client/src/Components/ProgressBars/index.css | 18 ++ Client/src/Components/ProgressBars/index.jsx | 104 ++++++++++++ .../ProfileSettings/ProfilePanel.jsx | 147 +++++++++++------ .../src/Components/TextFields/Image/index.jsx | 156 ++++++++++-------- Client/src/Pages/Demo/Demo.jsx | 6 + Client/src/Utils/Theme.js | 1 + 6 files changed, 310 insertions(+), 122 deletions(-) create mode 100644 Client/src/Components/ProgressBars/index.css create mode 100644 Client/src/Components/ProgressBars/index.jsx diff --git a/Client/src/Components/ProgressBars/index.css b/Client/src/Components/ProgressBars/index.css new file mode 100644 index 000000000..37317228f --- /dev/null +++ b/Client/src/Components/ProgressBars/index.css @@ -0,0 +1,18 @@ +.progress-bar-container{ + background-color: #fafafa; + padding: var(--env-var-spacing-1-plus); +} +.progress-bar-container h2.MuiTypography-root, +.progress-bar-container p.MuiTypography-root { + color: var(--env-var-color-2); + font-size: var(--env-var-font-size-medium); +} +.progress-bar-container p.MuiTypography-root { + opacity: 0.6; +} +.progress-bar-container p.MuiTypography-root:has(span){ + font-size: 12px; +} +.progress-bar-container p.MuiTypography-root span { + padding-left: 2px; +} \ No newline at end of file diff --git a/Client/src/Components/ProgressBars/index.jsx b/Client/src/Components/ProgressBars/index.jsx new file mode 100644 index 000000000..3bcd31b16 --- /dev/null +++ b/Client/src/Components/ProgressBars/index.jsx @@ -0,0 +1,104 @@ +import React, { useState } from "react"; +import { useTheme } from "@emotion/react"; +import { + Box, + IconButton, + LinearProgress, + Stack, + Typography, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import "./index.css"; + +/** + * @param {Object} props - The component props. + * @returns {JSX.Element} The rendered component. + */ + +const ProgressUpload = ({ + icon, + label, + size, + progress = 0, + onClick, +}) => { + const theme = useTheme(); + return ( + + + {icon ? ( + + {icon} + + ) : ( + "" + )} + + + {label} + + + {size} + + + + + + + + + + + + {progress} + % + + + + ); +}; + +export default ProgressUpload; diff --git a/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx b/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx index 5bf540775..2675aedd9 100644 --- a/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx @@ -1,14 +1,7 @@ import { useTheme } from "@emotion/react"; import { useState } from "react"; import TabPanel from "@mui/lab/TabPanel"; -import { - Box, - Divider, - IconButton, - Modal, - Stack, - Typography, -} from "@mui/material"; +import { Box, Divider, Modal, Stack, Typography } from "@mui/material"; import ButtonSpinner from "../../ButtonSpinner"; import Button from "../../Button"; import EmailTextField from "../../TextFields/Email/EmailTextField"; @@ -18,8 +11,8 @@ import { editProfileValidation } from "../../../Validation/validation"; import { useDispatch, useSelector } from "react-redux"; import { update } from "../../../Features/Auth/authSlice"; import ImageField from "../../TextFields/Image"; -import DoDisturbAltIcon from "@mui/icons-material/DoDisturbAlt"; -import CloseIcon from "@mui/icons-material/Close"; +import ImageIcon from "@mui/icons-material/Image"; +import ProgressUpload from "../../ProgressBars"; /** * ProfilePanel component displays a form for editing user profile information @@ -78,15 +71,64 @@ const ProfilePanel = () => { }); }; - //TODO - implement delete profile picture function + //TODO - add setPicture state to localData + const [picture, setPicture] = useState(); + const [uploadProgress, setUploadProgress] = useState(0); + const [isUploading, setIsUploading] = useState(false); + const handlePicture = (event) => { + const pic = event.target.files[0]; + console.log(pic); + //TODO - add setPicture state to localData + setPicture({ name: pic.name, type: pic.type, size: formatBytes(pic.size) }); + setIsUploading(true); + + setTimeout(() => { + //TODO - add setPicture state to localData + setPicture((prev) => ({ ...prev, src: URL.createObjectURL(pic) })); + }, 1500); + + let progress = 0; + const interval = setInterval(() => { + progress += 10; + setUploadProgress(progress); + if (progress >= 100) { + clearInterval(interval); + } + }, 150); + }; + const formatBytes = (bytes) => { + if (bytes === 0) return "0 Bytes"; + const k = 1024; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " MB"; + }; + const handleCancelUpload = () => { + //TODO - add setPicture state to localData + setPicture(null); + setUploadProgress(0); + setIsUploading(false); + }; + const handleClosePictureModal = () => { + setIsOpen(""); + setUploadProgress(0); + setIsUploading(false); + }; + + //TODO - revisit once localData is set up properly const handleDeletePicture = () => { setLoading(true); setTimeout(() => { + //TODO - add setPicture state to localData + setPicture(null); setLoading(false); }, 2000); }; //TODO - implement update profile function - const handleUpdatePicture = () => {}; + const handleUpdatePicture = () => { + setIsOpen(""); + setUploadProgress(0); + setIsUploading(false); + }; //TODO - implement delete account function const handleDeleteAccount = () => {}; //TODO - implement save profile function @@ -199,7 +241,7 @@ const ProfilePanel = () => { {/* TODO - Update placeholder values with redux data */} { onClick={handleDeletePicture} isLoading={loading} /> - {/* TODO - modal popup for update pfp? */}