Merge branch 'main' into 'feature/dashboard/dark-mode'

# Conflicts:
#   client/src/App.jsx
#   client/src/components/header.jsx
#   client/src/components/sidebar.jsx
This commit is contained in:
2025-07-16 19:35:22 +00:00
10 changed files with 422 additions and 77 deletions
+265 -44
View File
@@ -1,5 +1,5 @@
import { chakra, useRecipe, useSlotRecipe, Box, Button, createIcon, CloseButton, IconButton, Stack } from "@chakra-ui/react";
import { useColorModeValue, ColorModeButton } from "@/components/ui/color-mode";
import { chakra, useRecipe, useSlotRecipe, Box, Button, createIcon, CloseButton, IconButton, Stack, useDisclosure, Tooltip } from "@chakra-ui/react";
import { ColorModeButton } from "@/components/ui/color-mode";
import { FaVolcano } from "react-icons/fa6";
import { RxHamburgerMenu } from "react-icons/rx";
import { useState } from "react";
@@ -57,57 +57,278 @@ export const Users = createIcon({
),
});
export default function Sidebar () {
const variant = useColorModeValue("ghost", "solid")
export default function Sidebar() {
const [isOpen, setIsOpen] = useState(true);
const [hasFinishedClosing, setHasFinishedClosing] = useState(true);
const [activeButton, setActiveButton] = useState(0)
const recipe = useSlotRecipe({ key: "sidebar" });
const styles = recipe();
const toggleSidebar = () => {
if (isOpen) {
setHasFinishedClosing(false);
setTimeout(() => setIsOpen(false), 10); // Kick off transition
setTimeout(() => setHasFinishedClosing(true), 400); // Match sidebar transition duration
} else {
setIsOpen(true);
setHasFinishedClosing(true);
}
};
return (
<chakra.div css={styles.root}>
{isOpen ? (
<>
<CloseButton color="secondary" size="sm" _hover={{bg: "#EDF1F1"}} variant="ghost" onClick={() => setIsOpen(!isOpen)}></CloseButton>
<Button css={styles.buttons} onClick={() => setActiveButton(0)} bg={activeButton === 0 ? "primary":undefined}>
<PushPin fill="iconFill" /> Dashboard
</Button>
<Button css={styles.buttons} onClick={() => setActiveButton(1)} bg={activeButton === 1 ? "primary":undefined}>
<Globe fill="iconFill" stroke="black" /> Global Map
</Button>
<Button css={styles.buttons} onClick={() => setActiveButton(2)} bg={activeButton === 2 ? "primary":undefined}>
<MapMarker fill="iconFill" stroke="black"/> Regional Map
</Button>
<Button css={styles.buttons} onClick={() => setActiveButton(3)} bg={activeButton === 3 ? "primary":undefined}>
<FaVolcano color="color"/> Volcano
</Button>
<Button css={styles.buttons} onClick={() => setActiveButton(4)} bg={activeButton === 4 ? "primary":undefined}>
<Users fill="iconFill" stroke="black"/> Admin
</Button>
</>
) : (
<>
<IconButton color="#19332D" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" onClick={() => setIsOpen(!isOpen)}>
<RxHamburgerMenu />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
<PushPin fill="white"/>
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
<Box
css={styles.root}
w={isOpen ? "220px" : "86px"}
transition="width 0.4s ease"
>
<IconButton
aria-label="Toggle Sidebar"
variant="ghost"
size="md"
onClick={toggleSidebar}
>
<RxHamburgerMenu />
</IconButton>
{/* Dashboard */}
<Button css={styles.buttons} onClick={() => setActiveButton(0)} bg={activeButton === 0 ? "primary":undefined}>
{isOpen ? (
<>
<PushPin fill="white" />
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "250px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
visibility: isOpen || !hasFinishedClosing ? "visible" : "hidden",
}}
>
Dashboard
</span>
</>
) : (
<PushPin fill="white" />
)}
</Button>
{/* Global Map */}
<Button css={styles.buttons} onClick={() => setActiveButton(1)} bg={activeButton === 1 ? "primary":undefined}>
{isOpen ? (
<>
<Globe fill="white" stroke="black" />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
<MapMarker fill="white" stroke="black" />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "250px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
visibility: isOpen || !hasFinishedClosing ? "visible" : "hidden",
}}
>
Global Map
</span>
</>
) : (
<Globe fill="white" stroke="black" />
)}
</Button>
{/* Regional Map */}
<Button css={styles.buttons} onClick={() => setActiveButton(2)} bg={activeButton === 2 ? "primary":undefined}>
{isOpen ? (
<>
<MapMarker fill="white" stroke="black"/>
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "250px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
visibility: isOpen || !hasFinishedClosing ? "visible" : "hidden",
}}
>
Regional Map
</span>
</>
) : (
<MapMarker fill="white" stroke="black" />
)}
</Button>
{/* Volcano */}
<Button css={styles.buttons} onClick={() => setActiveButton(3)} bg={activeButton === 3 ? "primary":undefined}>
{isOpen ? (
<>
{/* <FaVolcano size={20} style={{ marginRight: "1px" }} /> */}
<FaVolcano />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "250px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
visibility: isOpen || !hasFinishedClosing ? "visible" : "hidden",
}}
>
Volcano
</span>
</>
) : (
<FaVolcano />
)}
</Button>
{/* Admin */}
<Button css={styles.buttons} onClick={() => setActiveButton(4)} bg={activeButton === 4 ? "primary":undefined}>
{isOpen ? (
<>
{/* <Users boxSize={5} style={{ marginRight: "8px" }} /> */}
<Users fill="white" stroke="black" />
</IconButton>
</>
)}
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
visibility: isOpen || !hasFinishedClosing ? "visible" : "hidden",
}}
>
Admin
</span>
</>
) : (
<Users fill="white" stroke="black" />
)}
</Button>
<ColorModeButton position="absolute" bottom="2"/>
</chakra.div>
</Box>
);
}
// THIS CODE WORKS, SAVE IN CASE OF EMERGENCY
// Needed to refactor in order allow transitions to work properly
// export default function Sidebar () {
// const [isOpen, setIsOpen] = useState(true);
// const [activeButton, setActiveButton] = useState("Dashboard")
// return (
// <>
// {isOpen && (
// <Box w="100%" h="100%"
// borderRight="1px"
// borderColor="black"
// borderStyle="solid"
// display="flex"
// flexDirection="column"
// justifyContent="start"
// alignItems="start"
// gap={4} paddingTop={4}
// paddingLeft={14} paddingRight={3}
// >
// {/* <CloseButton color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></CloseButton> */}
// {/* <RxHamburgerMenu color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></RxHamburgerMenu> */}
// <Button
// aria-lable="Toggle Sidebar"
// color="secondary"
// _hover={{bg: "#EDF1F1"}}
// size="md" variant="ghost"
// left="-10"
// // ml="auto" // Margin-left: auto, pushes hamburger menu to right side
// onClick={() => setIsOpen(!isOpen)}
// >
// <RxHamburgerMenu />
// </Button>
// <Button color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
// <PushPin fill="white" /> Dashboard
// </Button>
// <Button color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
// <Globe fill="white" stroke="black" /> Global Map
// </Button>
// <Button color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
// <MapMarker fill="white" stroke="black"/> Regional Map
// </Button>
// <Button color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
// <FaVolcano /> Volcano
// </Button>
// <Button color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
// <Users fill="white" stroke="black" /> Admin
// </Button>
// </Box>
// )}
// {!isOpen && (
// <Box w="100%" h="100%"
// borderRight="1px"
// borderColor="black"
// borderStyle="solid"
// display="flex"
// flexDirection="column"
// justifyContent="start"
// alignItems="start"
// gap={4} padding={4}
// >
// <IconButton color="#19332D" _hover={{bg: "#EDF1F1"}} size="md" variant="ghost" onClick={() => setIsOpen(!isOpen)}>
// <RxHamburgerMenu />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
// <PushPin fill="white"/>
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
// <Globe fill="white" stroke="black" />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
// <MapMarker fill="white" stroke="black" />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
// <FaVolcano />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
// <Users fill="white" stroke="black" />
// </IconButton>
// </Box>
// )}
// </>
// );
// }