diff --git a/src/view/atoms/ConfirmDialog.tsx b/src/view/atoms/ConfirmDialog.tsx index 083c210..4761d98 100644 --- a/src/view/atoms/ConfirmDialog.tsx +++ b/src/view/atoms/ConfirmDialog.tsx @@ -40,11 +40,15 @@ const ConfirmDialog = ({ return ( - {title} + + {title} + - {message} + + {message} + - + diff --git a/src/view/moles/Header.tsx b/src/view/moles/Header.tsx index 4266722..25353b4 100644 --- a/src/view/moles/Header.tsx +++ b/src/view/moles/Header.tsx @@ -10,9 +10,6 @@ import { Avatar, Tooltip, } from "@mui/material"; -import { useColorScheme } from "@mui/material/styles"; -import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined"; -import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined"; import { useNavigate } from "react-router-dom"; import { useAccount } from "../../nonview/core/AccountContext"; import { getInitials } from "../_constants/avatarUtils"; @@ -31,8 +28,6 @@ const Header = ({ const isMobile = useMediaQuery(theme.breakpoints.down("md")); const navigate = useNavigate(); const { activeAccount } = useAccount(); - const { mode, systemMode, setMode } = useColorScheme(); - const resolvedMode = (mode === "system" ? systemMode : mode) || "light"; const TitleIcon = titleIcon; @@ -124,20 +119,6 @@ const Header = ({ ); })} - - setMode(resolvedMode === "dark" ? "light" : "dark")} - aria-label="toggle color scheme" - sx={{ color: "text.secondary" }} - > - {resolvedMode === "dark" ? ( - - ) : ( - - )} - - - navigate("/profile")} aria-label="profile" diff --git a/src/view/moles/ManageAccountDialog.tsx b/src/view/moles/ManageAccountDialog.tsx new file mode 100644 index 0000000..e677585 --- /dev/null +++ b/src/view/moles/ManageAccountDialog.tsx @@ -0,0 +1,314 @@ +import React, { useState } from "react"; +import { + Dialog, + DialogTitle, + DialogContent, + IconButton, + List, + ListItemButton, + ListItemIcon, + ListItemText, + Box, + Avatar, + TextField, + Button, + Divider, + Typography, + Link, + useMediaQuery, + useTheme, +} from "@mui/material"; +import { useColorScheme } from "@mui/material/styles"; +import CloseIcon from "@mui/icons-material/Close"; +import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; +import PaletteOutlinedIcon from "@mui/icons-material/PaletteOutlined"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import { useAccount } from "../../nonview/core/AccountContext"; +import { getInitials } from "../_constants/avatarUtils"; +import ErrorMessage from "../atoms/ErrorMessage"; +import ConfirmDialog from "../atoms/ConfirmDialog"; + +interface ManageAccountDialogProps { + open: boolean; + onClose: () => void; +} + +const THEME_OPTIONS = ["light", "dark"] as const; +const NAV_ITEMS = [ + { key: "profile", label: "Profile", icon: PersonOutlineIcon }, + { key: "appearance", label: "Appearance", icon: PaletteOutlinedIcon }, +] as const; + +const ManageAccountDialog = ({ open, onClose }: ManageAccountDialogProps) => { + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down("md")); + const { activeAccount, updateAccount, removeAccount } = useAccount(); + const { mode, systemMode, setMode } = useColorScheme(); + const resolvedMode = (mode === "system" ? systemMode : mode) || "light"; + + const [section, setSection] = useState<(typeof NAV_ITEMS)[number]["key"]>("profile"); + const [name, setName] = useState(activeAccount?.name || ""); + const [nameError, setNameError] = useState(""); + const [saved, setSaved] = useState(false); + const [confirmOpen, setConfirmOpen] = useState(false); + const [removing, setRemoving] = useState(false); + + if (!activeAccount) return null; + + const handleSave = () => { + if (!name.trim()) { + setNameError("Name is required"); + return; + } + updateAccount(activeAccount.id, { name: name.trim() }); + setSaved(true); + }; + + const handleRemove = async () => { + setRemoving(true); + await removeAccount(activeAccount.id); + setRemoving(false); + setConfirmOpen(false); + onClose(); + }; + + const filledFieldSx = { + "& .MuiFilledInput-root": { + borderRadius: 2, + bgcolor: "action.hover", + "&::before, &::after": { display: "none" }, + }, + }; + + return ( + <> + + + + {getInitials(activeAccount.name)} + + + + {activeAccount.name} + + + {activeAccount.email} + + + + + + + + + + + + {NAV_ITEMS.map(({ key, label, icon: Icon }) => ( + setSection(key)} + sx={fullScreen ? { flex: 1, justifyContent: "center", mx: 0.5, borderRadius: 1.5 } : { mx: 1, borderRadius: 1.5 }} + > + + + + + + ))} + + + + {section === "profile" && ( + + + + {getInitials(activeAccount.name)} + + + + Profile photo + + {/* No avatar-upload backend yet — inert placeholder. */} + + Change photo + + + + + {saved && ( + + setSaved(false)} + /> + + )} + + { + setName(e.target.value); + setNameError(""); + setSaved(false); + }} + error={!!nameError} + helperText={nameError} + sx={{ mb: 2, ...filledFieldSx }} + /> + + + + + + Account management + + setConfirmOpen(true)} + sx={{ + border: 1, + borderColor: "divider", + borderRadius: 2, + }} + > + + + + + + + + )} + + {section === "appearance" && ( + + + Theme + + + {THEME_OPTIONS.map((option) => ( + setMode(option)} + sx={{ + flex: 1, + cursor: "pointer", + border: 2, + borderColor: resolvedMode === option ? "primary.main" : "divider", + borderRadius: 2, + p: 2, + textAlign: "center", + bgcolor: option === "dark" ? "grey.900" : "grey.50", + color: option === "dark" ? "grey.50" : "grey.900", + }} + > + + {option} + + + ))} + + + )} + + + + + setConfirmOpen(false)} + /> + + ); +}; + +export default ManageAccountDialog;