Initial design

This commit is contained in:
Daniel Cojocea committed 2024-06-25 17:38:40 -04:00
1 parent 91f2cac438
commit db66774ca6
5 files changed
+211 -16

No files matched your search

@@ -1,7 +1,14 @@
import { useTheme } from "@emotion/react";
import { useState } from "react";
import TabPanel from "@mui/lab/TabPanel";
import { Box, Divider, Modal, Stack, Typography } from "@mui/material";
import {
Box,
Divider,
IconButton,
Modal,
Stack,
Typography,
} from "@mui/material";
import ButtonSpinner from "../../ButtonSpinner";
import Button from "../../Button";
import EmailTextField from "../../TextFields/Email/EmailTextField";
@@ -10,6 +17,9 @@ import Avatar from "../../Avatar";
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";
/**
* ProfilePanel component displays a form for editing user profile information
@@ -39,8 +49,9 @@ const ProfilePanel = () => {
profilePicUrl: "placeholder",
});
const [errors, setErrors] = useState({});
const [isOpen, setIsOpen] = useState(false);
const [loadingPfp, setLoadingPfp] = useState(false);
const [isOpen, setIsOpen] = useState("");
const isModalOpen = (name) => isOpen === name;
const [loading, setLoading] = useState(false);
const handleChange = (event) => {
const { value, id } = event.target;
@@ -69,16 +80,13 @@ const ProfilePanel = () => {
//TODO - implement delete profile picture function
const handleDeletePicture = () => {
setLoadingPfp(true);
setLoading(true);
setTimeout(() => {
setLoadingPfp(false);
setLoading(false);
}, 2000);
};
//TODO - implement update profile function
const handleUpdatePicture = () => {
setLocalData((prev) => ({ ...prev, profilePicUrl: file }));
console.log(localData);
};
const handleUpdatePicture = () => {};
//TODO - implement delete account function
const handleDeleteAccount = () => {};
//TODO - implement save profile function
@@ -94,6 +102,7 @@ const ProfilePanel = () => {
}
dispatch(update({ authToken, localData }));
//TODO - add toast confirmation
};
return (
@@ -204,13 +213,13 @@ const ProfilePanel = () => {
level="tertiary"
label="Delete"
onClick={handleDeletePicture}
isLoading={loadingPfp}
isLoading={loading}
/>
{/* TODO - modal popup for update pfp? */}
<Button
level="tertiary"
label="Update"
onClick={handleUpdatePicture}
onClick={() => setIsOpen("picture")}
sx={{
color: theme.palette.primary.main,
}}
@@ -254,7 +263,7 @@ const ProfilePanel = () => {
<Button
level="error"
label="Delete account"
onClick={() => setIsOpen(true)}
onClick={() => setIsOpen("delete")}
/>
</Box>
</Stack>
@@ -264,8 +273,8 @@ const ProfilePanel = () => {
<Modal
aria-labelledby="modal-delete-account"
aria-describedby="delete-account-confirmation"
open={isOpen}
onClose={() => setIsOpen(false)}
open={isModalOpen("delete")}
onClose={() => setIsOpen("")}
disablePortal
>
<Stack
@@ -302,7 +311,7 @@ const ProfilePanel = () => {
<Button
level="tertiary"
label="Cancel"
onClick={() => setIsOpen(false)}
onClick={() => setIsOpen("")}
/>
<ButtonSpinner
level="error"
@@ -313,6 +322,71 @@ const ProfilePanel = () => {
</Stack>
</Stack>
</Modal>
<Modal
aria-labelledby="modal-update-picture"
aria-describedby="update-profile-picture"
open={isModalOpen("picture")}
onClose={() => setIsOpen("")}
disablePortal
>
<Stack
gap="20px"
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "white",
border: "solid 1px #f2f2f2",
borderRadius: `${theme.shape.borderRadius}px`,
boxShadow: 24,
p: "30px",
"&:focus": {
outline: "none",
},
}}
>
<Stack
direction="row"
mt="-10px"
justifyContent="space-between"
alignItems="center"
>
<Typography id="modal-update-picture" variant="h4" component="h1">
Upload Image
</Typography>
<IconButton
onClick={() => setIsOpen("")}
sx={{
"&:focus": {
outline: "none",
},
}}
>
<CloseIcon />
</IconButton>
</Stack>
<ImageField id="update-profile-picture" />
<Stack direction="row" mt="10px" justifyContent="space-between">
<Button level="secondary" label="Edit" disabled />
<Stack direction="row" gap="10px" justifyContent="flex-end">
<ButtonSpinner
level="tertiary"
label="Remove"
onClick={handleDeletePicture}
isLoading={loading}
/>
<ButtonSpinner
level="primary"
label="Update"
onClick={handleUpdatePicture}
isLoading={isLoading}
/>
</Stack>
</Stack>
</Stack>
</Modal>
</TabPanel>
);
};
@@ -0,0 +1,15 @@
.image-field-wrapper h2.MuiTypography-root,
.image-field-wrapper p.MuiTypography-root {
color: var(--env-var-color-2);
font-size: var(--env-var-font-size-medium);
}
.image-field-wrapper h2.MuiTypography-root {
margin-top: 10px;
}
.image-field-wrapper p.MuiTypography-root {
opacity: 0.8;
}
.image-field-wrapper h2.MuiTypography-root span {
color: var(--env-var-color-3);
font-weight: 500;
}
@@ -0,0 +1,97 @@
import React, { useState } from "react";
import { Box, IconButton, Stack, TextField, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import "./index.css";
/**
* @param {Object} props - The component props.
* @returns {JSX.Element} The rendered component.
*/
const ImageField = ({ id, onChange }) => {
const theme = useTheme();
const [isDragging, setIsDragging] = useState(false);
const handleDragEnter = () => {
setIsDragging(true);
};
const handleDragLeave = () => {
setIsDragging(false);
};
return (
<Box
className="image-field-wrapper"
sx={{
position: "relative",
width: "fit-content",
height: "fit-content",
border: "dashed",
borderRadius: theme.shape.borderRadius / 2,
borderSpacing: "10px",
borderColor: isDragging
? theme.palette.primary.main
: theme.palette.otherColors.graishWhite,
borderWidth: "2px",
transition: "0.2s",
"&:hover": {
borderColor: theme.palette.primary.main,
backgroundColor: "hsl(215, 87%, 51%, 0.05)",
},
}}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDrop={handleDragLeave}
>
<TextField
id={id}
type="file"
sx={{
"& .MuiInputBase-input[type='file']": {
opacity: 0,
cursor: "pointer",
padding: 10,
maxHeight: "300px",
height: "100%",
},
"& fieldset": {
padding: 0,
border: "none",
},
}}
/>
<Stack
className="custom-file-text"
alignItems="center"
gap="10px"
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: "-1",
width: "100%",
}}
>
<IconButton
sx={{
pointerEvents: "none",
borderRadius: "2px",
border: `solid 2px ${theme.palette.otherColors.graishWhite}`,
boxShadow: theme.shape.boxShadow,
}}
>
<CloudUploadIcon />
</IconButton>
<Typography variant="h4" component="h2">
<span>Click to upload</span> or drag and drop
</Typography>
<Typography variant="h4" component="p">
(max. 800x400px)
</Typography>
</Stack>
</Box>
);
};
export default ImageField;
+5
View File
@@ -35,6 +35,7 @@ import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { getMonitors } from "../../Features/Monitors/monitorsSlice";
import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice";
import ImageField from "../../Components/TextFields/Image";
const cols = [
{
@@ -415,6 +416,10 @@ const Demo = () => {
/>
</Stack>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<Stack justifyContent="center" alignItems="center">
<ImageField />
</Stack>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
</div>
);
};
+5 -1
View File
@@ -27,6 +27,9 @@ const otherColorsGraishWhiteLight = "#f2f2f2";
const otherColorsStrongBlue = "#f2f2f2";
const otherColorsSlateGray = "#667085";
//box shadow
const shadow =
"0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)";
// Global Font Family
const fontFamilyDefault = '"Roboto", "Helvetica", "Arial", sans-serif';
@@ -73,13 +76,14 @@ const theme = createTheme({
slateGray: otherColorsSlateGray,
},
},
font :{
font: {
default: {
font: fontFamilyDefault,
},
},
shape: {
borderRadius: 4,
boxShadow: shadow,
},
});