diff --git a/client/src/App.jsx b/client/src/App.jsx
index e507f47..c8a52e0 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -1,7 +1,7 @@
import 'leaflet/dist/leaflet.css';
-import { useEffect } from 'react';
-import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
-import { useRecipe, Grid, GridItem, useDialog, Flex, Heading } from "@chakra-ui/react";
+import {useEffect} from 'react';
+import {Routes, Route, Navigate, Outlet} from 'react-router-dom';
+import {useRecipe, Grid, GridItem, useDialog, Flex, Heading} from "@chakra-ui/react";
// --- NEW AUTH IMPORTS ---
import useAuthStore from './store/authStore';
@@ -19,80 +19,75 @@ import GlobalMap from "./pages/maps/GlobalMap.jsx";
import Admin from "./pages/admin/Admin.jsx";
import MyDashboard from "./pages/home/tabs/MyDashboard.jsx";
+export default function App() {
+ // 1. Pull the initialization function from Zustand
+ const checkAuth = useAuthStore((state) => state.checkAuth);
+
+ // 2. Ping Django to verify the HttpOnly cookie the moment the app boots
+ useEffect(() => {
+ checkAuth();
+ }, [checkAuth]);
+
+ return (
+
+ {/* --- Public Routes --- */}
+ }/>
+
+ {/* --- Secure Routes --- */}
+ }>
+
+ {/* --- Non-Public Routes (Now completely locked down) --- */}
+ }>
+
+ }>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+
+ }/>
+ }/>
+ }/>
+ }/>
+
+
+
+
+ {/* --- Redirect Routes --- */}
+ }/>
+
+ );
+}
+
+// The fundamental layout of the webpage
function AppFoundation() {
- const recipe = useRecipe({ key: "dashboard" });
+ const recipe = useRecipe({key: "dashboard"});
const styles = recipe();
const dialog = useDialog();
return (
<>
-
-
+
+
-
+
-
+
-
-
+
+
>
)
}
-const Placeholder = ({ name }) => (
-
- {name} Dashboard - Coming Soon
-
-);
-
-function App() {
- // 1. Pull the initialization function from Zustand
- const checkAuth = useAuthStore((state) => state.checkAuth);
-
- // 2. Ping Django to verify the HttpOnly cookie the moment the app boots
- useEffect(() => {
- checkAuth();
- }, [checkAuth]);
-
- return (
-
- {/* --- Public Routes --- */}
- } />
-
- {/* --- SECURE ROUTES --- */}
- }>
-
- {/* --- Non-Public Routes (Now completely locked down) --- */}
- }>
-
- }>
- } />
- } />
- } />
- } />
- } />
-
-
- } />
- } />
- } />
- } />
-
-
-
-
- {/* --- Redirect Routes --- */}
- } />
-
- );
-}
-
-export default App;
\ No newline at end of file
+// A dummy page that acts as a placeholder for pages that aren't developed yet but are navigatable to
+const Placeholder = ({name}) => (
+
+ {name} Dashboard - Coming Soon
+
+);
\ No newline at end of file
diff --git a/client/src/components/layout/Header.jsx b/client/src/components/layout/Header.jsx
index 82db471..e2bfa3a 100644
--- a/client/src/components/layout/Header.jsx
+++ b/client/src/components/layout/Header.jsx
@@ -1,6 +1,5 @@
-import { useRecipe, Button, Stack, Text, Flex } from "@chakra-ui/react"
+import {useSlotRecipe, Text, Flex} from "@chakra-ui/react"
import UserAvatarMenu from "./UserAvatarMenu.jsx";
-import { NavLink } from "react-router-dom";
import useAuthStore from "@/store/authStore";
import {SearchBar} from "@/components/dashboard/SearchBar.jsx";
@@ -9,27 +8,30 @@ import {SearchBar} from "@/components/dashboard/SearchBar.jsx";
// Accept a 'user' prop now, fallback to placeholder if empty
export default function Header() {
- const recipe = useRecipe({ key: "header" });
- const styles = recipe();
+ const recipe = useSlotRecipe({key: "header"});
+ const styles = recipe();
- const { user } = useAuthStore();
- console.log(user);
- const displayName = user ? (user.firstName || user.username) : "Loading...";
- const avatarname = user ? (user.fullName || user.username) : "Loading...";
+ const {user} = useAuthStore();
+ console.log(user);
+ const displayName = user ? (user.firstName || user.username) : "Loading...";
+ const avatarname = user ? (user.fullName || user.username) : "Loading...";
- const displayEmail = user?.email || "LDAP User";
+ const displayEmail = user?.email || "LDAP User";
- return (
-
-
- {/* User Profile Section */}
-
- {displayName}
-
-
-
- );
+ return (
+
+
+
+
+
+ {/* User Profile Section */}
+
+ {displayName}
+
+
+
+ );
}
\ No newline at end of file
diff --git a/client/src/pages/home/Home.jsx b/client/src/pages/home/Home.jsx
index 4f34457..0afeeec 100644
--- a/client/src/pages/home/Home.jsx
+++ b/client/src/pages/home/Home.jsx
@@ -49,7 +49,6 @@ export default function Home() {
{/* The Settings button is now inline with the tabs and only renders on the personal dashboard */}
{isPersonalDashboard && }
-
{/* EDIT MODE BUTTONS & CONTROLS */}
@@ -57,9 +56,9 @@ export default function Home() {
{/* Future global controls go here */}
-
+ {/* TABS CONTENT */}
diff --git a/client/src/pages/home/tabs/MyDashboard.jsx b/client/src/pages/home/tabs/MyDashboard.jsx
index aed386f..14a9502 100644
--- a/client/src/pages/home/tabs/MyDashboard.jsx
+++ b/client/src/pages/home/tabs/MyDashboard.jsx
@@ -13,8 +13,8 @@ import {WIDGET_REGISTRY} from '@/constants/widgetRegistry.jsx';
// React-Grid-Layout requires this wrapper to automatically calculate screen width
const ResponsiveGridLayout = WidthProvider(Responsive);
-export default function MyDashboard() {
+export default function MyDashboard() {
// βοΈ Database Connection
const {user} = useAuthStore();
@@ -28,25 +28,23 @@ export default function MyDashboard() {
// π Safety lock to prevent infinite re-renders
const hasHydrated = useRef(false);
- // 1. Pull the new global slot recipe
+ // 1. Pull the global slot recipes
const recipe = useSlotRecipe({key: "glassPanel"});
const styles = recipe({ layout: "widget" });
const popoverStyles = recipe({ layout: "popover" });
+ // 2. Populate widgets
useEffect(() => {
if (user?.preferences?.dashboard_layout && !hasHydrated.current) {
const savedLayout = user.preferences.dashboard_layout;
// Safety check: Only inject if it's actually an array of widgets.
// (Prevents a crash if MariaDB returns a default empty object {})
- if (Array.isArray(savedLayout)) {
- setWidgets(savedLayout);
- }
+ if (Array.isArray(savedLayout)) setWidgets(savedLayout);
hasHydrated.current = true;
}
}, [user, setWidgets]);
-
// π οΈ Format the data for RGL
const rglLayout = widgets.map(w => ({
i: w.id,
@@ -54,10 +52,10 @@ export default function MyDashboard() {
y: w.y,
w: w.w,
h: w.h,
- isResizable: false // π« The magic lock! No resizing allowed globally.
+ isResizable: false // The magic lock! No resizing allowed globally.
}));
- // 3. THE COMPONENT MAPPER
+ // 3. The component mapper
const renderWidget = (widget) => {
const registryEntry = WIDGET_REGISTRY[widget.type];
@@ -74,7 +72,7 @@ export default function MyDashboard() {
{registryEntry.name}
- {/* THE SETTINGS ELLIPSIS */}
+ {/* WIDGET SETTINGS BUTTON (ELLIPSES) */}
{SettingsUI ? (
@@ -136,20 +134,20 @@ export default function MyDashboard() {
onLayoutChange={(newLayout) => updateLayoutPositions(newLayout)} // Fires when user drops a widget
compactType="vertical" // Auto-packs widgets to the top
>
- {/* 3. π¨ Paint the Widgets */}
+ {/* π¨ Paint the Widgets */}
{widgets.map(widget => (
- {/* 4. THE DELETE BUTTON */}
+ {/* THE DELETE BUTTON */}
{isEditing && (
)}
- {/* 4. SUSPENSE BOUNDARY wrapping the render function */}
+ {/* SUSPENSE BOUNDARY wrapping the render function */}
diff --git a/client/src/theme.js b/client/src/theme.js
index fcb7ae1..c44160e 100644
--- a/client/src/theme.js
+++ b/client/src/theme.js
@@ -1,17 +1,41 @@
import {defineSlotRecipe, createSystem, defaultConfig, defineConfig, defineRecipe} from "@chakra-ui/react";
-//βββββRECIPESβββββ//
-const headerRecipe = defineRecipe({
+// CONSOLIDATED β
+const headerRecipe = defineSlotRecipe({
+ slots: ["root", "search_bar", "user_info"],
base: {
- bg: "{transparent}",
- color: "text",
- width: "100%",
- height: "100%",
- display: "flex",
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
- }
+ root: {
+ bg: "{transparent}",
+ color: "text",
+ width: "100%",
+ height: "100%",
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ pl: 3,
+ },
+ search_bar: {
+ width: "100%",
+ // input element in search bar component
+ "& input": {
+ width: "410px",
+ }
+ },
+ user_info: {
+ flexDirection: "row",
+ justifyContent: "flex-end",
+ alignItems: "center",
+ gap: 3,
+ pr: 6,
+
+ // The Text component
+ "& p": {
+ fontWeight: "bold",
+ fontSize:"16px" // equivalent to chakra 'md'
+ }
+ }
+ },
});
// CONSOLIDATED β
@@ -208,9 +232,18 @@ const sidebarRecipe = defineSlotRecipe({
}
});
+// CONSOLIDATED β
const dashboardRecipe = defineRecipe({
base: {
- bg: "{MainBackground}"
+ height: "100vh",
+ width: "100vw",
+ bg: "{MainBackground}",
+
+ // selects the third div in the grid layout (the canvas)
+ "& > div:nth-of-type(3)": {
+ width: "100%",
+ overflow: "auto",
+ }
}
});
@@ -484,12 +517,12 @@ const config= defineConfig({
},
},
recipes: {
- header: headerRecipe,
dashboard: dashboardRecipe,
canvas: canvasRecipe,
glassTab: glassTabRecipe
},
slotRecipes: {
+ header: headerRecipe,
sidebar: sidebarRecipe,
// widget: widgetRecipe,
glassPanel: glassPanelRecipe,
@@ -497,4 +530,6 @@ const config= defineConfig({
},
});
+
+// Define semantic tokens and export recipes
export const system = createSystem(defaultConfig, config);
\ No newline at end of file