Refactor made changes to file structures

This commit is contained in:
2026-06-10 14:00:37 -07:00
parent 305ee4e757
commit baaf162df9
46 changed files with 42 additions and 177 deletions
+66
View File
@@ -0,0 +1,66 @@
import { Flex, Tabs, HStack } from '@chakra-ui/react';
import { MEDataEntry } from './tabs/MEDataEntry.jsx';
import { ProjectCollector } from './tabs/ProjectCollector.jsx';
import { NASApplication } from './tabs/NASApplication.jsx';
import { IVANS } from './tabs/IVANS.jsx'
import { SearchBar } from "../../components/dashboard/SearchBar.jsx";
import { SettingsButton } from "../../components/dashboard/SettingsButton.jsx";
import { CanvasHeaderTabs } from '../../components/dashboard/CanvasHeaderTabs.jsx';
import { ADMIN_TABS } from '@/constants/viewKeys.js';
// TODO - refactor this. Currently Admin doesn't use onEditWidgets or appendWidget
export default function Admin({ view, onChangeView, onEditWidgets, appendWidget }) {
const settingsButtonProps = { onEditWidgets, appendWidget };
return (
/* HEADER */
<Tabs.Root
width="100%"
height="100%"
lazyMount
value={ view }
variant="line"
// Prevent navigation away from 'My Dashboard' while in Edit mode
onValueChange={(e) => {
const nextView = e.value;
onChangeView(nextView);
}}
>
<Flex
as="header"
position="sticky"
top="0"
zIndex="dropdown" // zIndex: 1100
bg="base200"
pl={5}
pr={2}
pt={3}
pb={2}
align="center"
justify="space-between"
width="100%"
>
{/* HEADER */}
<CanvasHeaderTabs tabs={ADMIN_TABS} />
<HStack width="258px" justifyContent="flex-end">
<SearchBar />
<SettingsButton />
</HStack>
</Flex>
{/* CANVAS */}
<Tabs.Content value="medataentry">
<MEDataEntry />
</Tabs.Content>
<Tabs.Content value="projectcollector">
<ProjectCollector />
</Tabs.Content>
<Tabs.Content value="nasapplication">
<NASApplication />
</Tabs.Content>
<Tabs.Content value="ivans">
<IVANS />
</Tabs.Content>
</Tabs.Root>
);
}
+22
View File
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function IVANS() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">IVANS dashboard coming soon.</Text>
</Box>
</Box>
);
}
+331
View File
@@ -0,0 +1,331 @@
import { useState } from 'react';
import { Box, Button, ButtonGroup, Text, Input, List, Field, Stack, SimpleGrid, HStack, Table, Checkbox, createListCollection } from "@chakra-ui/react"
import DropDownMenu from "../../../components/dashboard/DropDownMenu.jsx"
import { useForm } from 'react-hook-form';
const startYear = 1970;
const currentYear = new Date().getFullYear();
const yearItems = Array.from(
{ length: currentYear - startYear + 1 },
(_, i) => {
const year = startYear + i;
return { label: String(year), value: String(year) };
}
);
const years = createListCollection({
items: yearItems.reverse(),
});
const quarters = createListCollection({
items: [
{ label: "Q1", value: "quarter_1" },
{ label: "Q2", value: "quarter_2" },
{ label: "Q3", value: "quarter_3" },
{ label: "Q4", value: "quarter_4" },
],
})
const sections = createListCollection({
items: [
{ label: "All", value: "all" },
{ label: "Com", value: "com" },
{ label: "Eng", value: "eng" },
{ label: "Gas", value: "gas" },
{ label: "Geophys", value: "geophys" },
{ label: "GRSP", value: "grsp" },
{ label: "IT", value: "it" },
],
})
// Sample entries for visual reference - remove before flight
const items = [
{
id: "r1",
section: "Com",
volcano: "Nevado del Ruiz",
country: "COL",
start: "2024-01-15",
end: "2024-01-30",
type: "Training",
vpi10: "Yes",
downstream: 1200,
male: 600,
female: 600,
total: 1200,
},
{
id: "r2",
section: "Gas",
volcano: "Etna",
country: "ITA",
start: "2024-02-01",
end: "2024-02-20",
type: "Assistance",
vpi10: "No",
downstream: 900,
male: 450,
female: 450,
total: 900,
},
{
id: "r3",
section: "Eng",
volcano: "Kilauea",
country: "USA",
start: "2024-03-10",
end: "2024-03-25",
type: "Support",
vpi10: "Yes",
downstream: 2000,
male: 1000,
female: 1000,
total: 2000,
},
{
id: "r4",
section: "Geophys",
volcano: "Fuji",
country: "JPN",
start: "2024-04-05",
end: "2024-04-18",
type: "Monitoring",
vpi10: "Yes",
downstream: 1500,
male: 700,
female: 800,
total: 1500,
},
{
id: "r5",
section: "IT",
volcano: "Merapi",
country: "IDN",
start: "2024-05-02",
end: "2024-05-12",
type: "Deployment",
vpi10: "No",
downstream: 1100,
male: 550,
female: 550,
total: 1100,
},
{
id: "r6",
section: "Gas",
volcano: "Ranier",
country: "US",
start: "2024-07-02",
end: "2027-05-12",
type: "Deployment",
vpi10: "Yes",
downstream: 1200,
male: 570,
female: 570,
total: 1400,
},
];
export function MEDataEntry() {
const [value, setValue] = useState("");
const [selection, setSelection] = useState([]);
const {
register,
handleSubmit,
reset,
watch,
formState: { errors },
} = useForm();
const onSubmit = (data) => {
console.log("Form submitted:", data);
}
const rows = items.map((item) => (
<Table.Row
key={item.id}
data-selected={selection.includes(item.id) ? "" : undefined }
>
<Table.Cell>
<Checkbox.Root
size="sm"
mt="0.5"
aria-label="Select row"
checked={selection.includes(item.id)}
onCheckedChange={(changes) => {
setSelection((prev) =>
changes.checked
? [...prev, item.id]
: prev.filter((id) => id !== item.id ),
);
}}
>
<Checkbox.HiddenInput />
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Root>
</Table.Cell>
<Table.Cell>{item.section}</Table.Cell>
<Table.Cell>{item.volcano}</Table.Cell>
<Table.Cell>{item.country}</Table.Cell>
<Table.Cell>{item.start}</Table.Cell>
<Table.Cell>{item.end}</Table.Cell>
<Table.Cell>{item.type}</Table.Cell>
<Table.Cell>{item.vpi10}</Table.Cell>
<Table.Cell isNumeric>{item.downstream}</Table.Cell>
<Table.Cell isNumeric>{item.male}</Table.Cell>
<Table.Cell isNumeric>{item.female}</Table.Cell>
<Table.Cell isNumeric>{item.total}</Table.Cell>
</Table.Row>
))
return(
<Box>
<HStack spacing={6} m={2}>
<DropDownMenu collection={years} placeholder="Year" width="130px" />
<DropDownMenu collection={quarters} placeholder="Quarter" width="130px" />
<DropDownMenu collection={sections} placeholder="Section" width="130px" />
</HStack>
<Text fontSize="xs" pl={2} pt={2}>
Use Volcano names as found in GVP and dashboard. (e.g. Nevado del Ruiz, Chiles-Cerro Negro).
</Text>
<List.Root as="ul" styleType="circle" fontSize="xs" pl={6}>
<List.Item>Edit entry.</List.Item>
<List.Item>Delete entry.</List.Item>
<List.Item>Copy entry to form. Then hitting 'Save' will create a new entry.</List.Item>
</List.Root>
<Box m={4} mt={1} border="1px solid" borderRadius="sm" borderColor="gray.500">
<Box m={4} border="1px solid">
<Box pl={1} bg="gray.200" border="1px solid" borderColor="gray.400">
Number of people benefitting from VDAP activities, disaggregate by sex
</Box>
{/* <Box overflowX="auto"> */}
<Table.ScrollArea height="240px">
<Table.Root variant="outline" overflow="initial" size="sm" stickyHeader interactive>
<Table.Header>
<Table.Row bg="bg.subtle">
<Table.ColumnHeader w="40px"/>
<Table.ColumnHeader>Section</Table.ColumnHeader>
<Table.ColumnHeader>Volcano</Table.ColumnHeader>
<Table.ColumnHeader>Country</Table.ColumnHeader>
<Table.ColumnHeader>Start Date of Assistance</Table.ColumnHeader>
<Table.ColumnHeader>End Date of Assistance</Table.ColumnHeader>
<Table.ColumnHeader>Assistance Type</Table.ColumnHeader>
<Table.ColumnHeader>VPI10</Table.ColumnHeader>
<Table.ColumnHeader>Downstream Population</Table.ColumnHeader>
<Table.ColumnHeader>Male</Table.ColumnHeader>
<Table.ColumnHeader>Female</Table.ColumnHeader>
<Table.ColumnHeader>Total</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{rows}
</Table.Body>
</Table.Root>
</Table.ScrollArea>
{/* </Box> */}
</Box>
{/* Row actions aligned right */}
<HStack justify="flex-end" p={3} pt={0} borderBottom="1px solid" borderColor="gray.300" spacing={3} >
<ButtonGroup variant="surface" size="sm">
<Button >EDIT</Button>
<Button>COPY</Button>
<Button variant="solid" colorPalette="red">DELETE</Button>
</ButtonGroup>
</HStack>
{/* Form Section */}
<Box
as="form"
onSubmit={handleSubmit(onSubmit)}
m={4}
border="1px solid"
>
<Box pl={1} bg="gray.200" border="1px solid" borderColor="gray.400" overflow="hidden">
Fill in Form
</Box>
<Stack p={2} gap="2" css={{ "--field-label-width": "80px" }}>
<HStack spacing={6} align="flex-start" w="100%" flexWrap="wrap">
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>Country:</Field.Label>
<Input {...register("country")} placeholder="US" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>Start Date of Assistance:</Field.Label>
<Input {...register("startdate", { valueAsDate: true })} placeholder="9/6/1987" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1">
<Field.Label>VPI10 (LandScan 2014):</Field.Label>
<Input {...register("lastname")} placeholder="CatPic.jpg" />
</Field.Root>
</HStack>
<HStack spacing={6} align="flex-start" w="100%" flexWrap="wrap">
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>Volcano:</Field.Label>
<Input {...register("volcano")} placeholder="St Helens" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>End Date of Assistance:</Field.Label>
<Input {...register("enddate", { valueAsDate: true })} placeholder="7/14/2054" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1">
<Field.Label>Downstream Affected Population:</Field.Label>
<Input {...register("downstream", { valueAsNumber: true })} placeholder="1337" />
</Field.Root>
</HStack>
<HStack spacing={6} align="flex-start" w="100%" flexWrap="wrap">
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>Male Population:</Field.Label>
<Input {...register("male", { valueAsNumber: true })} placeholder="1776" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1" minW="260px">
<Field.Label>Female Population:</Field.Label>
<Input {...register("female", { valueAsNumber: true })} placeholder="1892" />
</Field.Root>
<Field.Root orientation="horizontal" flex="1">
<Field.Label>Total Population:</Field.Label>
<Input {...register("total", { valueAsNumber: true })} placeholder="3668" />
</Field.Root>
</HStack>
<Field.Root orientation="horizontal" flex="1" pt={2}>
<Field.Label>Assistance Type:</Field.Label>
<Input {...register("assistance")} placeholder="Monetary" flex="1" />
</Field.Root>
<HStack alignSelf="flex-start" pt={2}>
<ButtonGroup variant="surface" size="sm">
<Button type="submit" variant="solid" colorPalette="green" >
Add
</Button>
<Button onClick={() => reset()}>
Reset
</Button>
</ButtonGroup>
</HStack>
</Stack>
</Box>
</Box>
</Box>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function NASApplication() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">NAS Application dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function ProjectCollector() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Project Collector dashboard coming soon.</Text>
</Box>
</Box>
);
}
+75
View File
@@ -0,0 +1,75 @@
import { Box, Flex, Field, Input, Stack, Heading, Button } from "@chakra-ui/react"
import { useForm } from 'react-hook-form';
export default function Account() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm();
const onSubmit = (data) => {
console.log("Form submitted:", data);
}
console.log(watch("example")); // watch input value by passing the name of it
return (
<Flex
height="100vh"
direction="column"
align="start"
justify="start"
bg="gray.50"
pt={6}
pl={6}
>
<Heading as="h1" mb={2}>Account</Heading>
<Box
as="form"
onSubmit={handleSubmit(onSubmit)}
border="1px solid"
borderColor="gray.300"
borderRadius="md"
padding={4}
bg="white"
boxShadow="md"
width="100%"
maxW="sm"
>
{/* <Text fontSize="xl" fontWeight="bold" mb={6}>
Account
</Text> */}
<Stack gap="6" maxW="sm" css={{ "--field-label-width": "96px" }}>
<Field.Root orientation="horizontal">
<Field.Label>User Name</Field.Label>
<Input {...register("username")} placeholder="David Johnston" flex="1" />
</Field.Root>
<Field.Root orientation="horizontal">
<Field.Label> First Name</Field.Label>
<Input {...register("firstname")} placeholder="David" flex="1" />
</Field.Root>
<Field.Root orientation="horizontal">
<Field.Label>Last Name</Field.Label>
<Input {...register("lastname")} placeholder="Johnston" flex="1" />
</Field.Root>
<Field.Root orientation="horizontal">
<Field.Label>Email</Field.Label>
<Input {...register("email")} placeholder="djohntson@usgs.gov" flex="1" />
</Field.Root>
<Button type="submit">
Save
</Button>
</Stack>
</Box>
</Flex>
)
}
+91
View File
@@ -0,0 +1,91 @@
import { useContext } from 'react';
import { useForm } from 'react-hook-form';
import {
Button,
Field,
Input,
Stack,
useRecipe,
Center,
Text,
Flex,
Heading,
Highlight,
GridItem,
Grid
} from "@chakra-ui/react"
import { PasswordInput } from "@/components/ui/password-input.jsx";
export default function Login( signedIn, setSignedIn) {
const recipe = useRecipe({ key: "dashboard" }); // styling recipes option
const styles = recipe(); // use style
const {
register,
handleSubmit,
formState: { errors, isSubmitSuccessful },
} = useForm({
defaultValues: {
username: "",
password: "",
}
});
const onSubmit = (data) => {
console.log(data);
// setUser(data.username);
};
return (
<Flex css={styles} width="100%" height="100%">
<Grid
templateRows="1fr 11fr"
height="100vh"
width="100vw"
>
<GridItem rowSpan={1} >
<Flex width="100%" height="100%" align="center" >
<Heading size="4xl" fontWeight="bold" pl="1rem" cursor="default" >
<Highlight query="A" styles={{ color: 'red.600'}}>
VDAP
</Highlight>
</Heading>
</Flex>
</GridItem>
<GridItem rowSpan={11} >
<Flex width="100%" height="100%" justify="center" align="center" direction="column" >
<Center background="white" width="40%" height="50%" borderRadius="1rem">
<form onSubmit={handleSubmit(onSubmit)}>
<Stack gap="4" align="center" maxW="sm">
<Text textStyle="2xl" fontWeight="bold" cursor="default">Sign In</Text>
<Field.Root invalid={!!errors.username}>
<Input
placeholder="username"
{...register("username", {required: "Required"})}
/>
<Field.ErrorText>{errors.username?.message}</Field.ErrorText>
</Field.Root>
<Field.Root invalid={!!errors.password}>
<PasswordInput
placeholder="password"
{...register("password", {required: "Required"})}
/>
<Field.ErrorText>{errors.password?.message}</Field.ErrorText>
</Field.Root>
<Button
type="submit"
disabled={isSubmitSuccessful}
>
Submit
</Button>
</Stack>
</form>
</Center>
</Flex>
</GridItem>
</Grid>
</Flex>
);
}
+24
View File
@@ -0,0 +1,24 @@
import { Box, Text } from "@chakra-ui/react";
export default function Personal() {
return (
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">
Personal Info Canvas coming soon!
</Text>
</Box>
</Box>
);
}
+24
View File
@@ -0,0 +1,24 @@
import { Box, Text } from "@chakra-ui/react";
export default function Security() {
return (
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">
Security Canvas coming soon!
</Text>
</Box>
</Box>
);
}
+64
View File
@@ -0,0 +1,64 @@
import { HStack, Flex, Box } from "@chakra-ui/react";
import { NavLink, Outlet, useLocation } from "react-router-dom";
import { SearchBar } from "../../components/dashboard/SearchBar.jsx";
import { SettingsButton } from "../../components/dashboard/SettingsButton.jsx";
export default function Home() {
const location = useLocation();
const isPersonalDashboard = location.pathname === "/home" || location.pathname === "/home/";
// 1. CSS making this look like how we want it, TODO turn to theme?
const tabStyles = {
fontSize: "lg",
fontWeight: "normal",
color: "fg.muted",
px: 4, // Horizontal padding
py: 2, // Vertical padding
borderBottom: "2px solid transparent",
mb: "-2px", // 🔥 The trick: Pulls the active border down to overlap the container's line
transition: "all 0.2s",
_hover: { color: "fg" },
_activeLink: {
color: "color", // Or whatever your primary text color is
borderColor: "currentColor", // The active underline
}
};
return (
<Flex direction="column" height="100%" width="100%">
{/* 2. 🛠️ SUB HEADER: Matches your original padding and background */}
<Flex as="header" position="sticky" top="0" zIndex="dropdown" bg="base200" p={5} align="center" justify="space-between" width="100%">
{/* TABS CONTAINER - Replicates the Tabs.List line container */}
<Flex borderBottom="2px solid" borderColor="border.muted" gap={2}>
<Box as={NavLink} to="/home" end css={tabStyles}>My Dashboard</Box>
<Box as={NavLink} to="/home/gas" css={tabStyles}>Gas</Box>
<Box as={NavLink} to="/home/seismic" css={tabStyles}>Seismic</Box>
<Box as={NavLink} to="/home/remote" css={tabStyles}>Remote Sensing</Box>
<Box as={NavLink} to="/home/daily" css={tabStyles}>Daily Activity</Box>
</Flex>
{/* EDIT MODE BUTTONS & CONTROLS */}
<HStack width="258px" justifyContent="flex-end">
{isPersonalDashboard ? (
<>
<SearchBar placeholder="Enter a volcano to search" size="sm" width="210px" />
{/* This will eventually trigger your Zustand store's edit mode! */}
<SettingsButton />
</>
) : (
<SearchBar placeholder="Enter a volcano to search" size="sm" width="210px" />
)}
</HStack>
</Flex>
{/* 3. 🛠️ CANVAS AREA */}
<Flex flex="1" overflow="auto">
<Outlet />
</Flex>
</Flex>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function DailyActivity() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Daily Activity dashboard coming soon.</Text>
</Box>
</Box>
);
}
+22
View File
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function Gas() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Gas dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,70 @@
import { Box, Text, Flex } from "@chakra-ui/react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { useDashboardStore } from "@/store/useDashboardStore.jsx";
// React-Grid-Layout requires this wrapper to automatically calculate screen width
const ResponsiveGridLayout = WidthProvider(Responsive);
export default function MyDashboard() {
// 1. ☁️ Connect to the Cloud (Zustand)
const widgets = useDashboardStore(state => state.widgets);
const isEditing = useDashboardStore(state => state.isEditing);
const updateLayoutPositions = useDashboardStore(state => state.updateLayoutPositions);
// 2. 🛠️ Format the data for RGL
// RGL expects a very specific array format for its layout prop
const rglLayout = widgets.map(w => ({
i: w.id,
x: w.x,
y: w.y,
w: w.w,
h: w.h,
isResizable: false // 🚫 The magic lock! No resizing allowed globally.
}));
return (
<Box width="100%" height="100%">
<ResponsiveGridLayout
className="layout"
layouts={{ lg: rglLayout }} // We feed it the formatted layout map
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }} // Our 12-column master grid
rowHeight={30}
isDraggable={isEditing} // Only allows dragging when your Settings tools toggle this
isResizable={false} // Double lock-down
onLayoutChange={(newLayout) => updateLayoutPositions(newLayout)} // Fires when user drops a widget
compactType="vertical" // Auto-packs widgets to the top
>
{/* 3. 🎨 Paint the Widgets */}
{widgets.map(widget => (
<Box
key={widget.id}
bg="white"
borderRadius="md"
boxShadow={isEditing ? "outline" : "sm"} // Visual cue when editing
border={isEditing ? "2px dashed gray" : "1px solid"}
borderColor={isEditing ? "gray.400" : "gray.200"}
overflow="hidden"
cursor={isEditing ? "grab" : "default"}
>
{/* 🚧 Temporary Placeholder!
Eventually, we will swap this block out for a dynamic component loader
that looks at widget.type and loads the correct graph or table.
*/}
<Flex width="100%" height="100%" align="center" justify="center" direction="column">
<Text fontWeight="bold" color="gray.600">{widget.type}</Text>
<Text fontSize="sm" color="gray.400">Size: {widget.size}</Text>
</Flex>
</Box>
))}
</ResponsiveGridLayout>
{/* Empty State Helper */}
{widgets.length === 0 && (
<Flex width="100%" height="200px" align="center" justify="center">
<Text color="gray.400">Your dashboard is empty. Click Settings to add widgets.</Text>
</Flex>
)}
</Box>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function RemoteSensing() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Remote Sensing dashboard coming soon.</Text>
</Box>
</Box>
);
}
+22
View File
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function Seismic() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Seismic dashboard coming soon.</Text>
</Box>
</Box>
);
}
+23
View File
@@ -0,0 +1,23 @@
import React from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import MapButtonControls from "../../components/maps/MapButtonControls.jsx";
import MapSettingsButton from "../../components/maps/MapSettingsButton.jsx";
export default function GlobalMap() {
const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
return (
<MapContainer center={position} zoom={3} style={{ height: "100%", width: "100%" }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; OpenStreetMap contributors'
/>
<MapButtonControls settingsBtn = {<MapSettingsButton />} position="topright" />
<Marker position={position}>
<Popup>
CVO Home of the volcanoligists
</Popup>
</Marker>
</MapContainer>
);
}
+19
View File
@@ -0,0 +1,19 @@
import React from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
export default function RegionalMap() {
const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
return (
<MapContainer center={position} zoom={13} style={{ height: "100%", width: "100%" }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; OpenStreetMap contributors'
/>
<Marker position={position}>
<Popup>
CVO Home of the volcanoligists
</Popup>
</Marker>
</MapContainer>
);
}
+58
View File
@@ -0,0 +1,58 @@
import { Box, Text, Heading, DataList, Flex, HStack, Tabs } from '@chakra-ui/react';
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import { CanvasHeaderTabs } from '../../components/dashboard/CanvasHeaderTabs.jsx';
import { VOLCANO_TABS } from '@/constants/viewKeys.js';
import { VolcanoHome } from './tabs/VolcanoHome.jsx';
import { PartnerData } from './tabs/PartnerData.jsx';
import { Reports } from './tabs/Reports.jsx';
import { OfficialInstitutions } from './tabs/OfficialInstitutions.jsx';
import { SocialMedia } from './tabs/SocialMedia.jsx';
import { SearchBar } from "../../components/dashboard/SearchBar.jsx";
import { SettingsButton } from "../../components/dashboard/SettingsButton.jsx";
export default function Volcano() {
return (
<Tabs.Root
variant="line"
width="100%"
height="100%"
lazyMount
>
<Flex
as="header"
position="sticky"
top="0"
zIndex="dropdown" // zIndex: 1100
bg="base200"
p={5}
align="center"
justify="space-between"
width="100%"
>
{/* SUB HEADER */}
<CanvasHeaderTabs tabs={VOLCANO_TABS} />
<HStack width="258px" justifyContent="flex-end">
<SearchBar />
<SettingsButton />
</HStack>
</Flex>
{/* SUB CONTENT */}
<Tabs.Content value="volcanohome">
<VolcanoHome />
</Tabs.Content>
<Tabs.Content value="partner">
<PartnerData />
</Tabs.Content>
<Tabs.Content value="reports">
<Reports />
</Tabs.Content>
<Tabs.Content value="official">
<OfficialInstitutions />
</Tabs.Content>
<Tabs.Content value="social">
<SocialMedia />
</Tabs.Content>
</Tabs.Root>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function OfficialInstitutions() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Official Institutions dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function PartnerData() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Partner Data dashboard coming soon.</Text>
</Box>
</Box>
);
}
+22
View File
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function Reports() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Reports dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,22 @@
import { Box, Text } from "@chakra-ui/react";
export function SocialMedia() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Social Media dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,70 @@
import { Box, Text, Heading, DataList, HStack } from '@chakra-ui/react';
import React from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
export function VolcanoHome() {
const position = [46.2, -122.18]; // Mt. St. Helens
const stats = [
{ label: "Elevation", value: "2,539 (m) 8,330 (ft)", helpText: "Volcano Elevation" },
{ label: "Volcano Type", value: "Stratovolcano", helpText: "Volcano Type" },
{ label: "Composition", value: "Basalt to Ryodacite", helpText: "Volcano Composition" },
{ label: "Recent Eruptions", value: "1980, 2004-2008", helpText: "Last Eruption" },
{ label: "Threat Potential", value: "Very High", helpText: "Threat Potential" },
]
const historyText = `
Mount St. Helens, located in the Cascade Range of Washington State, is one of the most
active and well-known volcanoes in the United States. It famously erupted on May 18, 1980,
in one of the most significant volcanic events in U.S. history, which caused widespread
devastation and loss of life. The eruption reduced the mountains summit and created a large
crater. Subsequent dome-building eruptions continued through the 2000s, and the volcano
remains closely monitored today.
`;
return (
<Box width="100%" px={6} py={2}>
<Heading textAlign="center" mb={6} size="xl" fontWeight="bold">
Mount St. Helens - United States CVO
</Heading>
<Box height="550px" borderRadius="md" overflow="hidden" boxShadow="md" mb={6}>
<MapContainer center={position} zoom={10} style={{ height: "100%", width: "100%" }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; OpenStreetMap contributors'
/>
<Marker position={position}>
<Popup>
CVO Home of the volcanoligists
</Popup>
</Marker>
</MapContainer>
</Box>
<HStack align="start" spacing={10} mt={4}>
<Box width="300px" height="200px" overflowY="auto" p={4} border="1px solid" borderColor="gray.200" borderRadius="md" bg="gray.50" boxShadow="sm">
<DataList.Root orientation="horizontal">
{stats.map((item) => (
<DataList.Item key={item.label}>
<DataList.ItemLabel>
{item.label}
</DataList.ItemLabel>
<DataList.ItemValue>
{item.value}
</DataList.ItemValue>
</DataList.Item>
))}
</DataList.Root>
</Box>
<Box flex="1" height="200px" overflowY="auto" p={4} border="1px solid" borderColor="gray.200" borderRadius="md" bg="gray.50" boxShadow="sm">
<Heading textAlign="center" pb={2}>Historical Summary</Heading>
<Text>
{ historyText}
</Text>
</Box>
</HStack>
</Box>
);
}