From bc9fbaae6e3395e7ac374381eb98a6c0df78e483 Mon Sep 17 00:00:00 2001 From: vkuchenik Date: Mon, 5 Oct 2026 15:21:51 -0700 Subject: [PATCH 01/10] wip - committing to check out another branch --- .../Widgets/settings/AlertChangeSettings.jsx | 12 +++++---- client/src/pages/home/tabs/MyDashboard.jsx | 26 ++++++++++--------- client/src/theme.js | 13 +++++++++- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/client/src/components/Widgets/settings/AlertChangeSettings.jsx b/client/src/components/Widgets/settings/AlertChangeSettings.jsx index bc1103b..b4b041f 100644 --- a/client/src/components/Widgets/settings/AlertChangeSettings.jsx +++ b/client/src/components/Widgets/settings/AlertChangeSettings.jsx @@ -4,21 +4,23 @@ import {useDashboardStore} from "@/store/useDashboardStore.jsx"; export default function IvansAlertsSettings({ widgetId, currentSettings }) { const updateSettings = useDashboardStore(state => state.updateWidgetSettings); + console.log("currentSettings:", currentSettings); + return ( - + {/* SLIDER DATE RANGE SELECTION */} { + console.log("change end", e); const day = new Date(Date.now() - (e.value[0])*86400000).toLocaleDateString('en-CA'); - console.log("new start: ", day); updateSettings(widgetId, {...currentSettings, startDate: day}); }} > - {currentSettings?.endDate} to {currentSettings?.startDate} + {currentSettings?.startDate} to {currentSettings?.endDate} diff --git a/client/src/pages/home/tabs/MyDashboard.jsx b/client/src/pages/home/tabs/MyDashboard.jsx index 8fcc8f8..3dc9f98 100644 --- a/client/src/pages/home/tabs/MyDashboard.jsx +++ b/client/src/pages/home/tabs/MyDashboard.jsx @@ -29,9 +29,11 @@ export default function MyDashboard() { const hasHydrated = useRef(false); // 1. Pull the global slot recipes - const recipe = useSlotRecipe({key: "glassPanel"}); - const styles = recipe({ layout: "widget" }); - const popoverStyles = recipe({ layout: "popover" }); + const glassPanelRecipe = useSlotRecipe({key: "glassPanel"}); + const glassPanelStyles = glassPanelRecipe(); + const widgetStyles = glassPanelRecipe({ layout: "widget" }); + const widgetSettingsRecipe = useSlotRecipe({ key: "widgetSettings" }); + const widgetSettingsStyles = widgetSettingsRecipe(); // 2. Populate widgets useEffect(() => { @@ -67,7 +69,7 @@ export default function MyDashboard() { return ( <> - + {registryEntry.name} @@ -82,8 +84,8 @@ export default function MyDashboard() { {/* NEW: The Positioner lifts the content out of the Flexbox flow */} - - + + {/*className={`${glassPanelStyles.root} ${widgetSettingsStyles.root}`}>*/} }> - + @@ -105,7 +107,7 @@ export default function MyDashboard() { // Fallback if someone asks for a widget that doesn't exist return ( - + Error Widget '{widget.type}' not found. @@ -129,12 +131,12 @@ export default function MyDashboard() { {widgets.map(widget => ( @@ -163,7 +165,7 @@ export default function MyDashboard() { {/* SUSPENSE BOUNDARY wrapping the render function */} + }> diff --git a/client/src/theme.js b/client/src/theme.js index 53edb4f..f530bb5 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -304,6 +304,16 @@ const homeRecipe = defineSlotRecipe({ }, }) +const widgetSettingsRecipe = defineSlotRecipe({ + slots: ["root"], + base: { + root: { + w: "100%", + h: "500px", + bg: "red", + } + } +}) // const canvasRecipe = defineRecipe({ // base: { // bg: "{transparent}", @@ -428,7 +438,7 @@ const glassPanelRecipe = defineSlotRecipe({ popover: { root: { borderRadius: "2xl", - p: 0, + p: 2, boxShadow: "xl", minW: "240px", maxW: "60%", @@ -775,6 +785,7 @@ const config = defineConfig({ header: headerRecipe, sidebar: sidebarRecipe, // widget: widgetRecipe, + widgetSettings: widgetSettingsRecipe, glassPanel: glassPanelRecipe, home: homeRecipe, }, From 64c78cea7072532c7011a615e86efebd0dfb557d Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 10:43:28 -0700 Subject: [PATCH 02/10] Refactor starting to update theme.js --- client/src/theme.js | 63 +++++++++------------------------------------ 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/client/src/theme.js b/client/src/theme.js index f530bb5..16ee1fe 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -1,5 +1,16 @@ import {defineSlotRecipe, createSystem, defaultConfig, defineConfig, defineRecipe} from "@chakra-ui/react"; + +// REUSABLE GLASS SURFACE +const glassSurface = { + bg: {base: "rgba(255, 255, 255, 0.4)", _dark: "rgba(255, 255, 255, 0.04)"}, + backdropFilter: "blur(12px)", + WebkitBackdropFilter: "blur(12px)", + border: "1px solid", + borderColor: {base: "gray.200", _dark: "whiteAlpha.200"}, + boxShadow: "sm", +}; + // CONSOLIDATED ✔ const headerRecipe = defineSlotRecipe({ slots: ["root", "search_bar", "user_info"], @@ -314,51 +325,6 @@ const widgetSettingsRecipe = defineSlotRecipe({ } } }) -// const canvasRecipe = defineRecipe({ -// base: { -// bg: "{transparent}", -// zIndex: "-1", -// color: "{text}" -// } -// }); - -// const widgetRecipe = defineSlotRecipe({ -// slots: ["root", "header", "canvas"], -// base: { -// root: { -// flexDirection: "column", -// gap: "1", -// position: "relative", -// bg: "base100", -// borderRadius: "md", -// p: "1", -// paddingBottom: "2", -// border: "1px solid", -// borderColor: "{base300}", -// textAlign: "center", -// height: "100%", -// // position: "fixed" -// }, -// header: { -// flexShrink: "0", -// bg: "{base300}", -// color: "{text}", -// cursor: "move", -// borderRadius: "sm", -// whiteSpace: "nowrap", -// textAlign: "left", -// pl: "2" -// }, -// canvas: { -// flex: "1 1 auto", -// bg: "transparent", -// color: "{text}", -// cursor: "default", -// borderRadius: "md", -// overflow: "auto", -// } -// } -// }); // USED IN COMPONENTS: // * Settings Button @@ -373,12 +339,7 @@ const glassPanelRecipe = defineSlotRecipe({ flexDirection: "column", position: "relative", overflow: "hidden", - bg: {base: "rgba(255, 255, 255, 0.4)", _dark: "rgba(255, 255, 255, 0.04)"}, - backdropFilter: "blur(12px)", - WebkitBackdropFilter: "blur(12px)", - border: "1px solid", - borderColor: {base: "gray.200", _dark: "whiteAlpha.200"}, - boxShadow: "sm", + } }, From 91af70dbb5ddcf01fa939249f0f8d4e352b15788 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 13:44:59 -0700 Subject: [PATCH 03/10] Refactor lots of changes to normaliz theme.js a bit more --- client/src/theme.js | 242 ++++++++++++++++++-------------------------- 1 file changed, 97 insertions(+), 145 deletions(-) diff --git a/client/src/theme.js b/client/src/theme.js index 16ee1fe..bbf701a 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -11,12 +11,16 @@ const glassSurface = { boxShadow: "sm", }; -// CONSOLIDATED ✔ +/* ========================================================================== + RECIPES + ========================================================================== */ + +// 1. Header const headerRecipe = defineSlotRecipe({ slots: ["root", "search_bar", "user_info"], base: { root: { - bg: "{transparent}", + bg: "transparent", color: "text", width: "100%", height: "100%", @@ -31,7 +35,7 @@ const headerRecipe = defineSlotRecipe({ // input element in search bar component "& input": { width: "410px", - } + }, }, user_info: { flexDirection: "row", @@ -43,15 +47,24 @@ const headerRecipe = defineSlotRecipe({ // The Text component "& p": { fontWeight: "600", - fontSize: "md" // equivalent to 16px - } - } + fontSize: "md", + }, + }, }, }); -// CONSOLIDATED ✔ +// 2. Sidebar const sidebarRecipe = defineSlotRecipe({ - slots: ["root", "logo", "logo_text", "footer", "button_text", "buttons", "collapse_button", "color_mode_button"], + slots: [ + "root", + "logo", + "logo_text", + "footer", + "button_text", + "buttons", + "collapse_button", + "color_mode_button", + ], base: { root: { // 1. Semi-transparent backgrounds @@ -73,8 +86,6 @@ const sidebarRecipe = defineSlotRecipe({ borderRight: "none", borderRadius: "2xl", - // boxShadow: "0 4px 30px rgba(0, 0, 0, 0.1)", - // boxShadow: "-10px 4px 30px rgba(0, 0, 0, 0.1)", width: "100%", height: "calc(100% - 16px)", margin: "8px", @@ -103,18 +114,18 @@ const sidebarRecipe = defineSlotRecipe({ "& svg": { transition: "stroke-width 0.4s ease", - flexShrink: "0" + flexShrink: "0", }, _hover: { bg: "rgba(255, 255, 255, 0.2)", color: "{text}", "& svg": { - strokeWidth: "3" + strokeWidth: "3", }, "& span": { fontWeight: "700", - } + }, }, fontSize: "l", @@ -133,12 +144,8 @@ const sidebarRecipe = defineSlotRecipe({ _hover: { bg: "{MainBackground}", color: "{textInverted}", - "& svg": { - strokeWidth: "2" // Keeps the active icon at normal thickness on hover - }, - "& span": { - fontWeight: "500", - } + "& svg": { strokeWidth: "2" }, + "& span": { fontWeight: "500" }, }, // The Top Inverted Curve @@ -167,7 +174,7 @@ const sidebarRecipe = defineSlotRecipe({ WebkitMaskImage: "radial-gradient(circle at bottom left, transparent 25px, black 25.5px)", maskImage: "radial-gradient(circle at bottom left, transparent 25px, black 25.5px)", pointerEvents: "none", - } + }, }, }, logo: { @@ -208,30 +215,8 @@ const sidebarRecipe = defineSlotRecipe({ _hover: { bg: "transparent", }, - } - } - // opened_theme_button: { - // width: "100%", - // align: "center", - // justifyContent: "right", - // mr: 6, - // // "& button": { - // // alignSelf: "center", - // // transition: "opacity 0.3s ease, visibility 0.3s ease, margin 0.3s ease", - // // _hover: { - // // bg: "transparent", - // // } - // // } - // }, - // closed_theme_button: { - // // ml: 0.9, - // // "& button": { - // // transition: "opacity 0.3s ease, visibility 0.3s ease, margin 0.3s ease", - // // _hover: { - // // bg: "transparent", - // // } - // // } - // }, + }, + }, }, variants: { state: { @@ -261,7 +246,7 @@ const sidebarRecipe = defineSlotRecipe({ } }); -// CONSOLIDATED ✔ +// 3. Dashboard Shell const dashboardRecipe = defineRecipe({ base: { height: "100vh", @@ -276,7 +261,7 @@ const dashboardRecipe = defineRecipe({ } }); -// CONSOLIDATED ✔ +// 4. Home Page Tabs const homeRecipe = defineSlotRecipe({ slots: ["root", "header", "body"], base: { @@ -301,34 +286,16 @@ const homeRecipe = defineSlotRecipe({ "& > div": { alignItems: "center", gap: 2, - - // icon in each tab button - "& svg": { - // TODO: bring over svg styling - } - } + }, }, body: { flex: 1, overflow: "auto", - } + }, }, -}) +}); -const widgetSettingsRecipe = defineSlotRecipe({ - slots: ["root"], - base: { - root: { - w: "100%", - h: "500px", - bg: "red", - } - } -}) - -// USED IN COMPONENTS: -// * Settings Button -// * My Dashboard +// 5. Glass Panel Surfaces (Separated: Widget, Dropdown, Popover, Dialog) const glassPanelRecipe = defineSlotRecipe({ slots: ["root", "header", "body"], @@ -338,19 +305,20 @@ const glassPanelRecipe = defineSlotRecipe({ display: "flex", flexDirection: "column", position: "relative", - overflow: "hidden", - - } + ...glassSurface, + }, }, // 📏 THE LAYOUTS (Applies selectively) variants: { layout: { + // Dashboard Widget Card widget: { root: { width: "100%", height: "100%", borderRadius: "3xl", + overflow: "hidden", }, header: { display: "flex", @@ -378,7 +346,7 @@ const glassPanelRecipe = defineSlotRecipe({ color: "white", bg: "gray.500", }, - } + }, }, body: { flex: "1", @@ -386,17 +354,33 @@ const glassPanelRecipe = defineSlotRecipe({ position: "relative", px: 1, pb: 1, - pt: 0 - } + pt: 0, + }, }, dropdown: { root: { borderRadius: "xl", - p: 1 - } - // (Header and body slots are ignored here because dropdowns don't use them) + p: 1, + }, }, + + // 🎯 TRUE POPOVER: Compact floating card for Widget Settings (3-dots menu) popover: { + root: { + borderRadius: "xl", + p: 3, + boxShadow: "lg", + bg: { + base: "rgba(255, 255, 255, 0.85)", + _dark: "rgba(20, 22, 25, 0.85)", + }, + backdropFilter: "blur(15px)", + WebkitBackdropFilter: "blur(15px)", + }, + }, + + // 🎯 MODAL DIALOG: Large view for the Widget Shop + dialog: { root: { borderRadius: "2xl", p: 2, @@ -437,7 +421,7 @@ const glassPanelRecipe = defineSlotRecipe({ }, }, - // dialog body + // Dialog Body (Category Tabs & Masked Navigators) "& > div.chakra-dialog__body": { p: 0, @@ -517,23 +501,17 @@ const glassPanelRecipe = defineSlotRecipe({ cursor: "pointer", bg: { base: "rgba(255, 255, 255, 0.7)", - _dark: "rgba(30, 30, 30, 0.8)" - }, - color: { - base: "gray.600", - _dark: "gray.300" + _dark: "rgba(30, 30, 30, 0.8)", }, + color: { base: "gray.600", _dark: "gray.300" }, backdropFilter: "blur(1px)", transition: "background-color 0.2s", _hover: { - color: { - base: "blue.500", - _dark: "blue.300" - }, + color: { base: "blue.500", _dark: "blue.300" }, bg: { base: "rgba(255, 255, 255, 0.9)", - _dark: "rgba(40, 40, 40, 0.95)" + _dark: "rgba(40, 40, 40, 0.95)", }, }, @@ -553,12 +531,11 @@ const glassPanelRecipe = defineSlotRecipe({ // THE MAGIC: Bites a transparent circle out of the flat left side! WebkitMaskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 19px)", maskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 35px)", - } - } - - } - } - } + }, + }, + }, + }, + }, }, mode: { editing: { @@ -568,16 +545,17 @@ const glassPanelRecipe = defineSlotRecipe({ borderWidth: "2px", borderColor: "orange.300", cursor: "grab", - } - } - } + }, + }, + }, }, defaultVariants: { - layout: "widget" - } + layout: "widget", + }, }); +// 6. Pill/Glass Expanding Tab Button const glassTabRecipe = defineRecipe({ base: { display: "flex", @@ -634,11 +612,14 @@ const glassTabRecipe = defineRecipe({ bg: "{gooeyButtonBackground}", cursor: "default", textDecoration: "none", - } - } - } + }, + }, + }, }); +/* ========================================================================== + CONFIG & SYSTEM + ========================================================================== */ const config = defineConfig({ globalCss: { ".react-grid-placeholder": { @@ -677,36 +658,14 @@ const config = defineConfig({ "::-webkit-scrollbar-track": { background: "transparent", }, - // '.leaflet-tile': { - // _dark: { - // filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", - // } - // }, - '.leaflet-control-zoom-in, .leaflet-control-zoom-out, .leaflet-control-attribution, .leaflet-container': { + ".leaflet-control-zoom-in, .leaflet-control-zoom-out, .leaflet-control-attribution, .leaflet-container": { _dark: { filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", - } + }, }, - '.leaflet-control-attribution': { - _dark: { - filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", - } - }, - '.leaflet-container': { + ".leaflet-container": { fontFamily: "system-ui, sans-serif !important", - _dark: { - filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", - } }, - // ".leaflet-popup-content": { - // fontFamily: "georgia, system-ui, sans-serif !important" - // }, - // ".leaflet-control a": { - // fontFamily: "georgia, system-ui, sans-serif !important" - // }, - // ".leaflet-tooltip": { - // fontFamily: "georgia, system-ui, sans-serif !important" - // }, ".react-grid-item": { transition: "none !important" }, @@ -714,17 +673,15 @@ const config = defineConfig({ theme: { semanticTokens: { colors: { - // base100: {value: {base: "#f6f9faff", _dark: "#1617199e"}}, - // base200: {value: {base: "#e6ecf1ff", _dark: "#242424"}}, - // base300: {value: {base: "#d3dae4ff", _dark: "#0a0b0bff"}}, - // primary: {value: {base: "#2B98F8", _dark: "#2B98F8"}}, - // primaryLt: {value: {base: "#98cdffff", _dark: "#98cdffff"}}, - // secondaryBG: {value: {base: "#EDF1F1", _dark: "#121212"}}, - // border: {value: {base: "#000000", _dark: "#cbcbcb84"}}, - text: {value: {base: "#000000", _dark: "#FFFFFF"}}, - textInverted: {value: {base: "#000000", _dark: "#FFFFFF"}}, - // icon: {value: {base: "#000000", _dark: "#FFFFFF"}}, - + text: { value: { base: "#000000", _dark: "#FFFFFF" } }, + textInverted: { value: { base: "#FFFFFF", _dark: "#000000" } }, // Fixed: Properly inverted + gooeyButtonBackground: { + value: { base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)" }, + }, + MainBackground: { value: { base: "#F9F9F9", _dark: "#141414" } }, + titleColor: { value: { base: "gray.800", _dark: "#FFFFFF" } }, + iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode + glassSeperator: { value: { base: "#0000001A", _dark: "#FFFFFF3F" } }, gooeyButtonBackground: {value: {base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)"}}, MainBackground: {value: {base: "#F9F9F9", _dark: "#141414"}}, titleColor: {value: {base: "gray.800", _dark: "#FFFFFF"}}, @@ -739,20 +696,15 @@ const config = defineConfig({ }, recipes: { dashboard: dashboardRecipe, - // canvas: canvasRecipe, - glassTab: glassTabRecipe + glassTab: glassTabRecipe, }, slotRecipes: { header: headerRecipe, sidebar: sidebarRecipe, - // widget: widgetRecipe, - widgetSettings: widgetSettingsRecipe, glassPanel: glassPanelRecipe, home: homeRecipe, }, }, }); - -// Define semantic tokens and export recipes -export const system = createSystem(defaultConfig, config); \ No newline at end of file +export const system = createSystem(defaultConfig, config); From f43cfb649ed38de72699feebbefad3637efa25fb Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 14:06:34 -0700 Subject: [PATCH 04/10] Bug Fix copy paste errors repaired --- client/src/theme.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/src/theme.js b/client/src/theme.js index bbf701a..807bcf7 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -675,18 +675,11 @@ const config = defineConfig({ colors: { text: { value: { base: "#000000", _dark: "#FFFFFF" } }, textInverted: { value: { base: "#FFFFFF", _dark: "#000000" } }, // Fixed: Properly inverted - gooeyButtonBackground: { - value: { base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)" }, - }, MainBackground: { value: { base: "#F9F9F9", _dark: "#141414" } }, titleColor: { value: { base: "gray.800", _dark: "#FFFFFF" } }, iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode glassSeperator: { value: { base: "#0000001A", _dark: "#FFFFFF3F" } }, gooeyButtonBackground: {value: {base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)"}}, - MainBackground: {value: {base: "#F9F9F9", _dark: "#141414"}}, - titleColor: {value: {base: "gray.800", _dark: "#FFFFFF"}}, - iconColor: {value: {base: "gray.600", _dark: "#000000"}}, - glassSeperator: {value: {base: "#0000001A", _dark: "#FFFFFF3F"}}, }, }, tokens: { From 56ae6d2e3fd5d521fbda4964faae66161df045f7 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 14:24:43 -0700 Subject: [PATCH 05/10] Refactor theme.js to split components out more. --- client/src/theme.js | 372 ++++++++++++++++---------------------------- 1 file changed, 133 insertions(+), 239 deletions(-) diff --git a/client/src/theme.js b/client/src/theme.js index 807bcf7..19e3904 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -1,6 +1,5 @@ import {defineSlotRecipe, createSystem, defaultConfig, defineConfig, defineRecipe} from "@chakra-ui/react"; - // REUSABLE GLASS SURFACE const glassSurface = { bg: {base: "rgba(255, 255, 255, 0.4)", _dark: "rgba(255, 255, 255, 0.04)"}, @@ -69,8 +68,7 @@ const sidebarRecipe = defineSlotRecipe({ root: { // 1. Semi-transparent backgrounds bg: { - // base: "linear-gradient(to bottom right, #99000040 20%, #FF660040 60%, #EEEE0040 100%)", // this is too pale for light mode imo - base: "linear-gradient(to bottom right, #97282880 20%, #EB7F3160 60%, #FCAD3840 100%)", // colors from: https://colorhunt.co/palette/972828e45742eb7f31fcad38 + base: "linear-gradient(to bottom right, #97282880 20%, #EB7F3160 60%, #FCAD3840 100%)", _dark: "linear-gradient(to bottom right, #9900003C 20%, #FF66003C 60%, #FFC0003C 100%)" }, @@ -295,248 +293,43 @@ const homeRecipe = defineSlotRecipe({ }, }); -// 5. Glass Panel Surfaces (Separated: Widget, Dropdown, Popover, Dialog) -const glassPanelRecipe = defineSlotRecipe({ +// 5. Dashboard Widget Card +const widgetRecipe = defineSlotRecipe({ slots: ["root", "header", "body"], - - // 🧊 THE GLASS MATH (Applies to everything) base: { root: { display: "flex", flexDirection: "column", position: "relative", ...glassSurface, + width: "100%", + height: "100%", + borderRadius: "3xl", + overflow: "hidden", }, + header: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + px: 3, + pt: 2, + pb: 0, + "& > p": { + color: "titleColor", + fontWeight: "500", + fontSize: "16px", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "clip", + }, + "& > button": { + borderRadius: "xl", + _hover: {color: "white", bg: "gray.500"}, + }, + }, + body: {flex: "1", overflow: "hidden", position: "relative", px: 1, pb: 1, pt: 0}, }, - - // 📏 THE LAYOUTS (Applies selectively) variants: { - layout: { - // Dashboard Widget Card - widget: { - root: { - width: "100%", - height: "100%", - borderRadius: "3xl", - overflow: "hidden", - }, - header: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - px: 3, - pt: 2, - pb: 0, - - "& > p": { - color: "titleColor", - fontWeight: "500", - fontSize: "16px", - - // the following three specifications are equivalent to Chakra's 'truncate' prop - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "clip", - }, - - "& > button": { - borderRadius: "xl", - - _hover: { - color: "white", - bg: "gray.500", - }, - }, - }, - body: { - flex: "1", - overflow: "hidden", - position: "relative", - px: 1, - pb: 1, - pt: 0, - }, - }, - dropdown: { - root: { - borderRadius: "xl", - p: 1, - }, - }, - - // 🎯 TRUE POPOVER: Compact floating card for Widget Settings (3-dots menu) - popover: { - root: { - borderRadius: "xl", - p: 3, - boxShadow: "lg", - bg: { - base: "rgba(255, 255, 255, 0.85)", - _dark: "rgba(20, 22, 25, 0.85)", - }, - backdropFilter: "blur(15px)", - WebkitBackdropFilter: "blur(15px)", - }, - }, - - // 🎯 MODAL DIALOG: Large view for the Widget Shop - dialog: { - root: { - borderRadius: "2xl", - p: 2, - boxShadow: "xl", - minW: "240px", - maxW: "60%", - minH: "80%", - overflow: "hidden", - // OVERRIDES: Make the background 85-90% opaque instead of 40% - bg: { - base: "rgba(255, 255, 255, 0.75)", - _dark: "rgba(20, 22, 25, 0.8)" - }, - - // OVERRIDES: Increase the blur just to smooth out any stubborn gridlines - backdropFilter: "blur(15px)", - WebkitBackdropFilter: "blur(15px)", - - - // IMPORTANT: everything below is specific to the widget menu found in the settingsButton component - // dialog header - "& > div.chakra-dialog__header": { - bg: "transparent", - color: { - base: "gray.800", - _dark: "white" - }, - py: 4, - borderColor: { - base: "gray.200", - _dark: "whiteAlpha.200" - }, - - // title - "& h2": { - fontSize: "xl", - fontWeight: "bold" - }, - }, - - // Dialog Body (Category Tabs & Masked Navigators) - "& > div.chakra-dialog__body": { - p: 0, - - // box container of the widget category menu - "& div.chakra-tabs__root > div:first-child": { - position: "relative", - display: "flex", - maxW: "100%", - mx: 5, - mt: 4, - mb: 6 - }, - - // the horizontal widget categories menu - "& div.chakra-tabs__list": { - bg: { - base: "rgba(0, 0, 0, 0.04)", - _dark: "rgba(255, 255, 255, 0.05)" - }, - p: 1, - borderRadius: "full", - display: "flex", - flexWrap: "nowrap", - overflowX: "auto", - maxW: "100%", - border: "1px solid", - borderColor: { - base: "blackAlpha.50", - _dark: "whiteAlpha.100" - }, - scrollbarWidth: "none", - "&::-webkit-scrollbar": {display: "none"}, - - // the pill shaped menu options - "& button.chakra-tabs__trigger": { - px: 5, - py: 2, - borderRadius: "full", - whiteSpace: "nowrap", - flexShrink: 0, - color: { - base: "gray.500", - _dark: "gray.400" - }, - fontWeight: "medium", - transition: "background-color 0.2s, color 0.2s, box-shadow 0.2s", - _hover: { - color: { - base: "gray.800", - _dark: "gray.200" - }, - }, - _selected: { - bg: { - base: "white", - _dark: "rgba(255, 255, 255, 0.12)" - }, - color: { - base: "blue.600", - _dark: "blue.200" - }, - boxShadow: "sm", - fontWeight: "bold", - } - } - }, - - // base styling applied to both chevron navigators - "& div.chakra-tabs__root > div:first-child div:has(svg)": { - position: "absolute", - top: "0", - bottom: "0", - w: "50px", - alignItems: "center", - justifyContent: "center", - zIndex: 2, - cursor: "pointer", - bg: { - base: "rgba(255, 255, 255, 0.7)", - _dark: "rgba(30, 30, 30, 0.8)", - }, - color: { base: "gray.600", _dark: "gray.300" }, - backdropFilter: "blur(1px)", - transition: "background-color 0.2s", - - _hover: { - color: { base: "blue.500", _dark: "blue.300" }, - bg: { - base: "rgba(255, 255, 255, 0.9)", - _dark: "rgba(40, 40, 40, 0.95)", - }, - }, - - "&:has(.lucide-chevron-left)": { - left: 0, - borderTopLeftRadius: "full", - borderBottomLeftRadius: "full", - // Bites a transparent circle out of the flat right side! - WebkitMaskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 19px)", - maskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 35px)", - }, - - "&:has(.lucide-chevron-right)": { - right: 0, - borderTopRightRadius: "full", - borderBottomRightRadius: "full", - // THE MAGIC: Bites a transparent circle out of the flat left side! - WebkitMaskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 19px)", - maskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 35px)", - }, - }, - }, - }, - }, - }, mode: { editing: { root: { @@ -549,9 +342,108 @@ const glassPanelRecipe = defineSlotRecipe({ }, }, }, +}); - defaultVariants: { - layout: "widget", +// 6. TRUE POPOVER: Compact floating card for 3-dots menus +const glassPopoverRecipe = defineSlotRecipe({ + slots: ["content", "arrow", "positioner"], + base: { + content: { + ...glassSurface, + borderRadius: "xl", + p: 3, + boxShadow: "lg", + bg: {base: "rgba(255, 255, 255, 0.85)", _dark: "rgba(20, 22, 25, 0.85)"}, + backdropFilter: "blur(15px)", + WebkitBackdropFilter: "blur(15px)", + }, + }, +}); + +// 7. MODAL DIALOG: Large view for the Widget Shop +const glassDialogRecipe = defineSlotRecipe({ + slots: ["content", "header", "body", "backdrop"], + base: { + backdrop: { + bg: "rgba(0, 0, 0, 0.4)", + backdropFilter: "blur(4px)", + WebkitBackdropFilter: "blur(4px)", + }, + content: { + display: "flex", + flexDirection: "column", + position: "relative", + ...glassSurface, + borderRadius: "2xl", + p: 2, + boxShadow: "xl", + minW: "240px", + maxW: "60%", + minH: "80%", + overflow: "hidden", + bg: {base: "rgba(255, 255, 255, 0.75)", _dark: "rgba(20, 22, 25, 0.8)"}, + backdropFilter: "blur(15px)", + WebkitBackdropFilter: "blur(15px)", + }, + header: { + bg: "transparent", + color: {base: "gray.800", _dark: "white"}, + py: 4, + borderColor: {base: "gray.200", _dark: "whiteAlpha.200"}, + "& h2": {fontSize: "xl", fontWeight: "bold"}, + }, + body: { + p: 0, + "& div.chakra-tabs__root > div:first-child": { + position: "relative", + display: "flex", + maxW: "100%", + mx: 5, + mt: 4, + mb: 6 + }, + "& div.chakra-tabs__list": { + bg: {base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)"}, + p: 1, borderRadius: "full", display: "flex", flexWrap: "nowrap", overflowX: "auto", maxW: "100%", + border: "1px solid", borderColor: {base: "blackAlpha.50", _dark: "whiteAlpha.100"}, + scrollbarWidth: "none", "&::-webkit-scrollbar": {display: "none"}, + "& button.chakra-tabs__trigger": { + px: 5, py: 2, borderRadius: "full", whiteSpace: "nowrap", flexShrink: 0, + color: {base: "gray.500", _dark: "gray.400"}, + fontWeight: "medium", transition: "background-color 0.2s, color 0.2s, box-shadow 0.2s", + _hover: {color: {base: "gray.800", _dark: "gray.200"}}, + _selected: { + bg: {base: "white", _dark: "rgba(255, 255, 255, 0.12)"}, + color: {base: "blue.600", _dark: "blue.200"}, + boxShadow: "sm", + fontWeight: "bold" + } + } + }, + "& div.chakra-tabs__root > div:first-child div:has(svg)": { + position: "absolute", top: "0", bottom: "0", w: "50px", alignItems: "center", justifyContent: "center", + zIndex: 2, cursor: "pointer", bg: {base: "rgba(255, 255, 255, 0.7)", _dark: "rgba(30, 30, 30, 0.8)"}, + color: {base: "gray.600", _dark: "gray.300"}, backdropFilter: "blur(1px)", transition: "background-color 0.2s", + _hover: { + color: {base: "blue.500", _dark: "blue.300"}, + bg: {base: "rgba(255, 255, 255, 0.9)", _dark: "rgba(40, 40, 40, 0.95)"} + }, + "&:has(.lucide-chevron-left)": { + left: 0, + borderTopLeftRadius: "full", + borderBottomLeftRadius: "full", + WebkitMaskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 19px)", + maskImage: "radial-gradient(circle at 100% 50%, transparent 30px, black 35px)" + }, + "&:has(.lucide-chevron-right)": { + right: 0, + borderTopRightRadius: "full", + borderBottomRightRadius: "full", + WebkitMaskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 19px)", + maskImage: "radial-gradient(circle at 0% 50%, transparent 30px, black 35px)" + }, + }, + }, }, }); @@ -694,10 +586,12 @@ const config = defineConfig({ slotRecipes: { header: headerRecipe, sidebar: sidebarRecipe, - glassPanel: glassPanelRecipe, home: homeRecipe, + widget: widgetRecipe, + glassPopover: glassPopoverRecipe, + glassDialog: glassDialogRecipe, }, }, }); -export const system = createSystem(defaultConfig, config); +export const system = createSystem(defaultConfig, config); \ No newline at end of file From c810056d67a0d46b3983c4c2159be0357cbd9e54 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 14:34:40 -0700 Subject: [PATCH 06/10] Refactor dashboard to use new theme.js --- client/src/pages/home/tabs/MyDashboard.jsx | 25 +++++++++------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/client/src/pages/home/tabs/MyDashboard.jsx b/client/src/pages/home/tabs/MyDashboard.jsx index 3dc9f98..3836ecf 100644 --- a/client/src/pages/home/tabs/MyDashboard.jsx +++ b/client/src/pages/home/tabs/MyDashboard.jsx @@ -29,13 +29,14 @@ export default function MyDashboard() { const hasHydrated = useRef(false); // 1. Pull the global slot recipes - const glassPanelRecipe = useSlotRecipe({key: "glassPanel"}); - const glassPanelStyles = glassPanelRecipe(); - const widgetStyles = glassPanelRecipe({ layout: "widget" }); - const widgetSettingsRecipe = useSlotRecipe({ key: "widgetSettings" }); - const widgetSettingsStyles = widgetSettingsRecipe(); + const widgetRecipe = useSlotRecipe({ key: "widget" }); + const popoverRecipe = useSlotRecipe({ key: "glassPopover" }); - // 2. Populate widgets + // 2. Pass the editing state directly into the widget recipe! + const widgetStyles = widgetRecipe({ mode: isEditing ? "editing" : undefined }); + const popoverStyles = popoverRecipe(); + + // 3. Populate widgets useEffect(() => { if (user?.preferences?.dashboard_layout && !hasHydrated.current) { const savedLayout = user.preferences.dashboard_layout; @@ -57,7 +58,7 @@ export default function MyDashboard() { isResizable: false // The magic lock! No resizing allowed globally. })); - // 3. The component mapper + // 4. The component mapper const renderWidget = (widget) => { const registryEntry = WIDGET_REGISTRY[widget.type]; @@ -84,8 +85,8 @@ export default function MyDashboard() { {/* NEW: The Positioner lifts the content out of the Flexbox flow */} - - {/*className={`${glassPanelStyles.root} ${widgetSettingsStyles.root}`}>*/} + + }> {/* THE DELETE BUTTON */} From bd5e02ac1b0e74ddbf57521988799670e68a66a6 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Mon, 19 Oct 2026 14:59:01 -0700 Subject: [PATCH 07/10] Refactor connecting more of theme.js --- .../components/dashboard/SettingsButton.jsx | 18 ++++++++++-------- client/src/theme.js | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/client/src/components/dashboard/SettingsButton.jsx b/client/src/components/dashboard/SettingsButton.jsx index f49d6ae..0adec2e 100644 --- a/client/src/components/dashboard/SettingsButton.jsx +++ b/client/src/components/dashboard/SettingsButton.jsx @@ -38,9 +38,11 @@ export function SettingsButton() { const [canScrollRight, setCanScrollRight] = useState(false); // Pull the glass container styles for the dropdown and modal - const panelRecipe = useSlotRecipe({key: "glassPanel"}); - const panelStyles = panelRecipe({layout: "dropdown"}); - const modalStyles = panelRecipe({layout: "popover"}); + const popoverRecipe = useSlotRecipe({ key: "glassPopover" }); + const popoverStyles = popoverRecipe(); + + const dialogRecipe = useSlotRecipe({ key: "glassDialog" }); + const dialogStyles = dialogRecipe(); const [isLibraryOpen, setIsLibraryOpen] = useState(false); @@ -130,7 +132,7 @@ export function SettingsButton() { - + */} - - + + + Widgets Shop - + {/* HORIZONTAL SCROLLABLE MENU OF WIDGET CATEGORIES */} @@ -246,7 +249,6 @@ export function SettingsButton() { h="24px" flexShrink={0} borderRadius="full" - // bg={{base: "blue.50", _dark: "whiteAlpha.200"}} fontWeight="black" fontSize="xs" bg='transparent' diff --git a/client/src/theme.js b/client/src/theme.js index 19e3904..2865f1f 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -566,7 +566,7 @@ const config = defineConfig({ semanticTokens: { colors: { text: { value: { base: "#000000", _dark: "#FFFFFF" } }, - textInverted: { value: { base: "#FFFFFF", _dark: "#000000" } }, // Fixed: Properly inverted + textInverted: { value: { base: "#000000", _dark: "#FFFFFF" } }, // Fixed: Properly inverted MainBackground: { value: { base: "#F9F9F9", _dark: "#141414" } }, titleColor: { value: { base: "gray.800", _dark: "#FFFFFF" } }, iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode From c027a30b3f6b9f12720a4169a08b3784f053009a Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Tue, 20 Oct 2026 14:05:52 -0700 Subject: [PATCH 08/10] Refactor widgets to use theme better. Alert change and 7day vaa --- .../src/components/Widgets/AlertChanges.jsx | 75 +++++++++--------- .../components/Widgets/VaaActivity7Day.jsx | 79 ++++++++----------- client/src/theme.js | 12 +++ 3 files changed, 83 insertions(+), 83 deletions(-) diff --git a/client/src/components/Widgets/AlertChanges.jsx b/client/src/components/Widgets/AlertChanges.jsx index 9f6dbd2..42eea0d 100644 --- a/client/src/components/Widgets/AlertChanges.jsx +++ b/client/src/components/Widgets/AlertChanges.jsx @@ -60,38 +60,38 @@ export default function AlertChanges({ settings, widgetID}) { return sorted_entries; }, [apiData, thisWidget]); - // list of alert colors used for coloring text below + // Alert colors are now light/dark mode responsive const alertColors = { - Red: "red.400", - Orange: "orange.400", - Yellow: "yellow.400", - Green: "green.400", + Red: "alertRed", + Orange: "alertOrange", + Yellow: "alertYellow", + Green: "alertGreen", }; if (isLoading) return ( - + ); if (error) return ( - + {error} ); if (data.length === 0) return ( - - No flight level data available for the selected dates. + + No flight level data available for the selected dates. ); return ( - + // bg="transparent" to let the glass panel show through + {data.map((volcano, index) => { if (thisWidget?.size === "large") return ( - {/* Using context to dynamically change the border radii of the triggers */} {(collapsible) => ( <> @@ -102,57 +102,58 @@ export default function AlertChanges({ settings, widgetID}) { p={1} pl={3} pr={3} borderTopRadius="lg" borderBottomRadius={collapsible.open ? "0" : "lg"} - bg="gray.600" + bg="widgetItemBg" > - - {volcano[0]} {/* volcano name */} - {volcano[1][0]?.alert_start_date_local} + + {volcano[0]} + {volcano[1][0]?.alert_start_date_local} - {volcano[1][0]?.alert_level_name} + + {volcano[1][0]?.alert_level_name} + - + + {volcano[1]?.map((object, index) => ( - {/**/} - + {object?.alert_scheme_domain === "terrestrial" && } {object?.alert_scheme_domain === "aviation" && } {object?.alert_scheme_domain === "maritime" && } - + {object?.change_type === "increase" ? : } - {/*{object?.volcano}*/} - {object?.alert_start_date_local} + {object?.alert_start_date_local} {object?.alert_level_name} - Alert Scale: + Alert Scale: {(Array.from({ length: Number(object?.alert_scheme_highest) - Number(object?.alert_scheme_lowest) + 1}, (v, i) => Number(object?.alert_scheme_lowest) + i)) .map((num) => (
{num}
@@ -181,30 +182,30 @@ export default function AlertChanges({ settings, widgetID}) { justify="space-between" align="center" p={1} pl={3} pr={3} borderRadius="lg" - bg="gray.600" + bg="widgetItemBg" > - + - + {domain[0] === "aviation" && } {domain[0] === "terrestrial" && } {domain[0] === "maritime" && } - {volcano[0] || ""} {/* volcano name */} + {volcano[0] || ""} - {domain[1]?.[0]?.alert_start_date_local || ""} + {domain[1]?.[0]?.alert_start_date_local || ""} - - + + {domain[1]?.[0]?.change_type === "increase" ? : } {domain[1]?.[0]?.alert_level_name} diff --git a/client/src/components/Widgets/VaaActivity7Day.jsx b/client/src/components/Widgets/VaaActivity7Day.jsx index a6bb48c..d33b667 100644 --- a/client/src/components/Widgets/VaaActivity7Day.jsx +++ b/client/src/components/Widgets/VaaActivity7Day.jsx @@ -23,7 +23,6 @@ export default function VaaActivity7Day({ settings, widgetID }) { 'hexagon', 'octagon', 'cross', 'x' ]; - // Sort data descending by flight level (matching Observable behavior) const sortedData = [...apiData].sort((a, b) => b.obs_fl - a.obs_fl); // Efficiently group into Plotly traces by volcano @@ -44,102 +43,90 @@ export default function VaaActivity7Day({ settings, widgetID }) { mode: 'markers', marker: { size: 7, - line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' }, + line: { width: 0.5, color: 'var(--chakra-colors-textInverted)' }, symbol: Symbolway[index % Symbolway.length], }, // Replicating the clean Observable tooltip hovertemplate: - `${volcanoName}
` + - 'Date: %{x|%b %d %Y %H:%M}
' + - 'Flight Level: %{y}' + `${volcanoName}
` + + 'Date: %{x|%b %d %Y %H:%M}
' + + 'Flight Level: %{y}' })); }, [apiData]); // 2. Build the Layout (Styling and Grid) const layout = useMemo(() => { - return { autosize: true, paper_bgcolor: 'transparent', // Let Chakra UI background show through plot_bgcolor: 'transparent', - font: { color: '#d1d5db' }, // gray.300 + font: { color: 'var(--chakra-colors-widgetTextMuted)' }, margin: { t: 40, r: 20, l: 20, b: 20 }, - // title: { - // text: '7 Day Consolidated Flight Levels', - // font: { size: 16, color: 'white' }, - // x: 0.05 - // }, xaxis: { title: { text: 'Date (UTC)', - font: { color: 'white', size: 12 } + font: { color: 'var(--chakra-colors-text)', size: 12 } }, type: 'date', - tickformat: '%b %d
%Y %H:%M', // Multiline ticks from Observable - gridcolor: '#4b5563', - zerolinecolor: '#4b5563', + tickformat: '%b %d
%Y %H:%M', + gridcolor: 'var(--chakra-colors-glassSeperator)', + zerolinecolor: 'var(--chakra-colors-glassSeperator)', automargin: true, }, yaxis: { title: { text: 'Flight Level', - font: { color: 'white', size: 12 } + font: { color: 'var(--chakra-colors-text)', size: 12 } }, type: plotYAxisType, - gridcolor: '#4b5563', - zerolinecolor: '#ffffff', + gridcolor: 'var(--chakra-colors-glassSeperator)', + zerolinecolor: 'var(--chakra-colors-text)', automargin: true, }, - // legend: { - // orientation: 'h', - // y: -0.2, - // x: 0.5, - // xanchor: 'center' - // }, hovermode: 'closest', shapes: [ { type: 'line', x0: 0, x1: 1, - xref: 'paper', // Spans entire chart width + xref: 'paper', y0: 0, y1: 0, yref: 'y', - line: { color: 'white', width: 1 } + line: { color: 'var(--chakra-colors-text)', width: 1 } } ], }; }, [apiData, plotYAxisType]); if (isLoading) return ( - - - + + + ); if (error) return ( - - {error} - + + {error} + ); if (plotTraces.length === 0) return ( - - No flight level data available. - + + No flight level data available. + ); return ( - - - + + + ); } \ No newline at end of file diff --git a/client/src/theme.js b/client/src/theme.js index 2865f1f..052037b 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -572,6 +572,18 @@ const config = defineConfig({ iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode glassSeperator: { value: { base: "#0000001A", _dark: "#FFFFFF3F" } }, gooeyButtonBackground: {value: {base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)"}}, + + // WIDGET SPECIFIC VALUES + widgetItemBg: { value: { base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" } }, + widgetItemExpandedBg: { value: { base: "rgba(0, 0, 0, 0.08)", _dark: "rgba(0, 0, 0, 0.3)" } }, + widgetBadgeBg: { value: { base: "rgba(255, 255, 255, 0.6)", _dark: "rgba(0, 0, 0, 0.5)" } }, + widgetTextMuted: { value: { base: "gray.600", _dark: "gray.400" } }, + + // ALERT COLORS + alertRed: { value: { base: "red.600", _dark: "red.400" } }, + alertOrange: { value: { base: "orange.600", _dark: "orange.400" } }, + alertYellow: { value: { base: "yellow.600", _dark: "yellow.400" } }, + alertGreen: { value: { base: "green.600", _dark: "green.400" } }, }, }, tokens: { From 0ec4f27640c7968c36bb9b56e41b06dbfe33154f Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Tue, 20 Oct 2026 14:59:03 -0700 Subject: [PATCH 09/10] Refactor CSS for scatter plots --- .../components/Widgets/VaaActivity7Day.jsx | 45 ++++++++++--------- client/src/theme.js | 16 +++++++ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/client/src/components/Widgets/VaaActivity7Day.jsx b/client/src/components/Widgets/VaaActivity7Day.jsx index d33b667..afbe219 100644 --- a/client/src/components/Widgets/VaaActivity7Day.jsx +++ b/client/src/components/Widgets/VaaActivity7Day.jsx @@ -1,11 +1,18 @@ import { useMemo } from 'react'; import { Box, Flex, Spinner, Text } from '@chakra-ui/react'; import Plot from 'react-plotly.js'; -import VdapApi from '../../hooks/useVdapApi.js'; // Adjust path as needed +import VdapApi from '../../hooks/useVdapApi.js'; +import { useColorMode } from "@/components/ui/color-mode.jsx"; + +// Import central color dictionary +import { rawWidgetColors } from '../../theme.js'; // Adjust path if needed export default function VaaActivity7Day({ settings, widgetID }) { - console.log("3. Chart received settings:", settings); - // Plotly expects 'linear' for normal axes + const { colorMode } = useColorMode(); + + // 2. Assign the active palette based on the current mode + const plotColors = colorMode === 'dark' ? rawWidgetColors.dark : rawWidgetColors.light; + const plotYAxisType = settings?.yAxisScale === 'symlog' ? 'symlog' : 'linear'; // Clean, single-line data fetching @@ -17,10 +24,8 @@ export default function VaaActivity7Day({ settings, widgetID }) { // Array of distinct Plotly shapes const Symbolway = [ - 'circle', 'square', 'diamond', - 'triangle-up', 'triangle-down', - 'star', 'hexagram', 'pentagon', - 'hexagon', 'octagon', 'cross', 'x' + 'circle', 'square', 'diamond', 'triangle-up', 'triangle-down', + 'star', 'hexagram', 'pentagon', 'hexagon', 'octagon', 'cross', 'x' ]; const sortedData = [...apiData].sort((a, b) => b.obs_fl - a.obs_fl); @@ -43,7 +48,7 @@ export default function VaaActivity7Day({ settings, widgetID }) { mode: 'markers', marker: { size: 7, - line: { width: 0.5, color: 'var(--chakra-colors-textInverted)' }, + line: { width: 0.5, color: plotColors.text }, symbol: Symbolway[index % Symbolway.length], }, // Replicating the clean Observable tooltip @@ -52,35 +57,35 @@ export default function VaaActivity7Day({ settings, widgetID }) { 'Date: %{x|%b %d %Y %H:%M}
' + 'Flight Level: %{y}' })); - }, [apiData]); + }, [apiData, plotColors]); // 2. Build the Layout (Styling and Grid) const layout = useMemo(() => { return { autosize: true, - paper_bgcolor: 'transparent', // Let Chakra UI background show through + paper_bgcolor: 'transparent', plot_bgcolor: 'transparent', - font: { color: 'var(--chakra-colors-widgetTextMuted)' }, + font: { color: plotColors.muted }, margin: { t: 40, r: 20, l: 20, b: 20 }, xaxis: { title: { text: 'Date (UTC)', - font: { color: 'var(--chakra-colors-text)', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'date', tickformat: '%b %d
%Y %H:%M', - gridcolor: 'var(--chakra-colors-glassSeperator)', - zerolinecolor: 'var(--chakra-colors-glassSeperator)', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.grid, automargin: true, }, yaxis: { title: { text: 'Flight Level', - font: { color: 'var(--chakra-colors-text)', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: plotYAxisType, - gridcolor: 'var(--chakra-colors-glassSeperator)', - zerolinecolor: 'var(--chakra-colors-text)', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.text, automargin: true, }, hovermode: 'closest', @@ -93,11 +98,11 @@ export default function VaaActivity7Day({ settings, widgetID }) { y0: 0, y1: 0, yref: 'y', - line: { color: 'var(--chakra-colors-text)', width: 1 } + line: { color: plotColors.text, width: 1 } } ], }; - }, [apiData, plotYAxisType]); + }, [plotYAxisType, plotColors]); if (isLoading) return ( @@ -120,7 +125,7 @@ export default function VaaActivity7Day({ settings, widgetID }) { return ( Date: Tue, 20 Oct 2026 16:19:45 -0700 Subject: [PATCH 10/10] Refactor changed widgets to match themes. --- .../components/Widgets/ExplosionEvents.jsx | 88 +++++++++---------- client/src/components/Widgets/IvansAlerts.jsx | 48 +++++----- client/src/components/Widgets/PlumeEvents.jsx | 63 +++++++------ .../src/components/Widgets/RemoteSensing.jsx | 47 +++++----- .../src/components/Widgets/SeismicEvents.jsx | 55 ++++++------ 5 files changed, 144 insertions(+), 157 deletions(-) diff --git a/client/src/components/Widgets/ExplosionEvents.jsx b/client/src/components/Widgets/ExplosionEvents.jsx index 2ddbf51..0f1e742 100644 --- a/client/src/components/Widgets/ExplosionEvents.jsx +++ b/client/src/components/Widgets/ExplosionEvents.jsx @@ -5,13 +5,17 @@ import Plot from 'react-plotly.js'; import VdapApi from '../../hooks/useVdapApi.js'; import useBuildUrl from "@/hooks/useBuildUrl.js"; +// 1. Import your central color dictionary +import { rawWidgetColors } from '../../theme.js'; + export default function ExplosionEvents({ settings, widgetID }) { // Plotly expects 'linear' for normal axes const plotYAxisType = settings?.yAxisScale === 'log' ? 'log' : 'linear'; // 🎨 Let's check color mode so we can make people happy with light and dark const { colorMode } = useColorMode(); - const isDark = colorMode === 'dark'; + // Assign the active palette based on the current mode + const plotColors = colorMode === 'dark' ? rawWidgetColors.dark : rawWidgetColors.light; // Generate url with input dates, default to 7 day range const url = useBuildUrl('/api/v2/explosion_events', settings?.startDate, settings?.endDate); @@ -53,58 +57,46 @@ export default function ExplosionEvents({ settings, widgetID }) { mode: 'markers', marker: { size: 7, - line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' }, + line: { width: .5, color: plotColors.text }, symbol: Symbolway[index % Symbolway.length], }, // Replicating the clean D3/Observable tooltip hovertemplate: - `${volcanoName}
` + - 'Date (UTC): %{x|%b %d %Y %H:%M}
' + - 'Number of Explosions: %{y}' + `${volcanoName}
` + + 'Date (UTC): %{x|%b %d %Y %H:%M}
' + + 'Number of Explosions: %{y}' })); - }, [apiData]); + }, [apiData, plotColors]); // 2. Build the Layout (Styling and Grid) const layout = useMemo(() => { - return { autosize: true, - paper_bgcolor: 'transparent', // Let Chakra UI background show through + paper_bgcolor: 'transparent', plot_bgcolor: 'transparent', - font: { color: '#d1d5db' }, // gray.300 + font: { color: plotColors.muted }, margin: { t: 40, r: 20, l: 20, b: 20 }, - // title: { - // text: '7 Day Consolidated Flight Levels', - // font: { size: 16, color: 'white' }, - // x: 0.05 - // }, xaxis: { title: { text: 'Date (UTC)', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'date', - tickformat: '%b %d
%Y', // D3 requested %b %d \n %Y formatting - gridcolor: '#4b5563', - zerolinecolor: '#4b5563', + tickformat: '%b %d
%Y', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.grid, automargin: true, }, yaxis: { title: { text: 'Number of Explosions', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: plotYAxisType, - gridcolor: '#4b5563', - zerolinecolor: '#ffffff', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.text, automargin: true, }, - // legend: { - // orientation: 'h', - // y: -0.2, - // x: 0.5, - // xanchor: 'center' - // }, hovermode: 'closest', shapes: [ { @@ -115,40 +107,40 @@ export default function ExplosionEvents({ settings, widgetID }) { y0: 0, y1: 0, yref: 'y', - line: { color: 'white', width: 1 } + line: { color: plotColors.text, width: 1 } } ], }; - }, [apiData, plotYAxisType]); + }, [plotYAxisType, plotColors]); if (isLoading) return ( - - - + + + ); if (error) return ( - - {error} - + + {error} + ); if (plotTraces.length === 0) return ( - - No data available for current filter. - + + No data available for current filter. + ); return ( - - - + + + ); } \ No newline at end of file diff --git a/client/src/components/Widgets/IvansAlerts.jsx b/client/src/components/Widgets/IvansAlerts.jsx index 48451a7..f65db1f 100644 --- a/client/src/components/Widgets/IvansAlerts.jsx +++ b/client/src/components/Widgets/IvansAlerts.jsx @@ -43,30 +43,31 @@ export default function IvansAlerts({ settings, widgetID }) { }).join('\n\n'); navigator.clipboard.writeText(dataText) .then(() => { - alert('Copied to clipboard!'); + alert('Copied to clipboard!'); }) .catch((err => { - console.error('Failed to copy: ', err); + console.error('Failed to copy: ', err); })); updateSettings(widgetID, {copyToClipboard: false}); } }, [settings, some_data, widgetID, updateSettings]); + // Cleaned hardcoded grays for transparent so glass shell shows through if (isLoading) return ( - + ); if (error) return ( - + {error} ); if (some_data.length === 0) return ( - - No flight level data available for the selected dates. + + No flight level data available for the selected dates. ); @@ -78,7 +79,7 @@ export default function IvansAlerts({ settings, widgetID }) { p={2} gap={2} overflow="auto" borderRadius="xl" - bg="gray.800" + bg="transparent" // Let the global widgetRecipe glass shell show through > {some_data.map((item, index) => { if (thisWidget?.size === "large") return ( @@ -96,31 +97,31 @@ export default function IvansAlerts({ settings, widgetID }) { gap={2} borderTopRadius="lg" borderBottomRadius={collapsible.open ? "0" : "lg"} - bg="gray.600" + bg="widgetItemBg" > - + {item.volcano_name} - {item.send_time} + {item.send_time} - {item.report_num} + {item.report_num} {/* This box needs to be at h="100%" for the pdf toolbar to appear */} - - - {item?.summary} - Preparer: {item?.preparer} + + + {item?.summary} + Preparer: {item?.preparer} {item?.files?.map((file, index) => { return (
- {file?.name} - + {file?.name} + Open @@ -131,8 +132,8 @@ export default function IvansAlerts({ settings, widgetID }) { type="application/pdf" style={{ width: '100%', height: '100%', display: 'block' }} > - - + + Your browser cannot display this PDF directly. @@ -151,19 +152,20 @@ export default function IvansAlerts({ settings, widgetID }) { ) else if (thisWidget?.size === "small") return ( - + {item.volcano_name} - {item.send_time} + {item.send_time} - {item.report_num} + {item.report_num} ) diff --git a/client/src/components/Widgets/PlumeEvents.jsx b/client/src/components/Widgets/PlumeEvents.jsx index 925a96d..8b5c6a4 100644 --- a/client/src/components/Widgets/PlumeEvents.jsx +++ b/client/src/components/Widgets/PlumeEvents.jsx @@ -1,10 +1,18 @@ -import VdapApi from '../../hooks/useVdapApi.js'; import { useMemo } from 'react'; +import { Box, Flex, Spinner, Text } from "@chakra-ui/react"; +import { useColorMode } from "@/components/ui/color-mode.jsx"; import Plot from 'react-plotly.js'; -import {Box, Flex, Spinner, Text} from "@chakra-ui/react"; +import VdapApi from '../../hooks/useVdapApi.js'; import useBuildUrl from "@/hooks/useBuildUrl.js"; +// 1. Import your central color dictionary +import { rawWidgetColors } from '../../theme.js'; + export default function PlumeEvents({ settings, widgetID}) { + const { colorMode } = useColorMode(); + // 2. Assign the active palette based on the current mode + const plotColors = colorMode === 'dark' ? rawWidgetColors.dark : rawWidgetColors.light; + // Build url, defaults to 7 days past const url = useBuildUrl('/api/v2/plume_events', settings?.startDate, settings?.endDate); @@ -44,7 +52,7 @@ export default function PlumeEvents({ settings, widgetID}) { mode: 'markers', marker: { size: 7, - line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' }, + line: { width: .5, color: plotColors.text }, symbol: Symbolway[index % Symbolway.length], }, // Replicating the clean D3/Observable tooltip @@ -53,88 +61,77 @@ export default function PlumeEvents({ settings, widgetID}) { 'Date (UTC): %{x|%b %d %Y %H:%M}
' + 'Plume Height (m): %{y}' })); - }, [apiData]); + }, [apiData, plotColors]); const layout = useMemo(() => { - return { autosize: true, paper_bgcolor: 'transparent', // Let Chakra UI background show through plot_bgcolor: 'transparent', - font: { color: '#d1d5db' }, // gray.300 + font: { color: plotColors.muted }, margin: { t: 40, r: 20, l: 20, b: 20 }, - // title: { - // text: '7 Day Consolidated Flight Levels', - // font: { size: 16, color: 'white' }, - // x: 0.05 - // }, xaxis: { title: { text: 'Date (UTC)', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'date', - tickformat: '%b %d
%Y', // D3 requested %b %d \n %Y formatting - gridcolor: '#4b5563', - zerolinecolor: '#4b5563', + tickformat: '%b %d
%Y', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.grid, automargin: true, }, yaxis: { title: { text: 'Plume Height', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'linear', - gridcolor: '#4b5563', - zerolinecolor: '#ffffff', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.text, automargin: true, }, - // legend: { - // orientation: 'h', - // y: -0.2, - // x: 0.5, - // xanchor: 'center' - // }, hovermode: 'closest', shapes: [ { type: 'line', x0: 0, x1: 1, - xref: 'paper', // Spans entire chart width + xref: 'paper', y0: 0, y1: 0, yref: 'y', - line: { color: 'white', width: 1 } + line: { color: plotColors.text, width: 1 } } ], }; - }, []); + }, [plotColors]); if (isLoading) return ( - + ); if (error) return ( - + {error} ); if (plotTraces.length === 0) return ( - - No data available for current filter. + + No data available for current filter. ); return ( - + diff --git a/client/src/components/Widgets/RemoteSensing.jsx b/client/src/components/Widgets/RemoteSensing.jsx index 3adbb27..38ed592 100644 --- a/client/src/components/Widgets/RemoteSensing.jsx +++ b/client/src/components/Widgets/RemoteSensing.jsx @@ -52,25 +52,25 @@ export default function RemoteSensing({ settings, widgetID }) { }, [settings, data, widgetID, updateSettings]); if (isLoading) return ( - + ); if (error) return ( - + {error} ); if (data.length === 0) return ( - - No Remote Sensing data available this week. + + No Remote Sensing data available this week. ); return ( - + {data.map((item, index) => { if (thisWidget?.size === "large" || thisWidget?.size === "medium") return ( @@ -80,14 +80,14 @@ export default function RemoteSensing({ settings, widgetID }) { <> - + {item?.volcano_name} - {item?.country} + {item?.country} - + {item?.report_time} @@ -95,10 +95,10 @@ export default function RemoteSensing({ settings, widgetID }) { {/* This box needs to be at h="100%" for the pdf toolbar to appear */} - - - {item?.summary} - Source: {item?.sender} + + + {item?.summary} + Source: {item?.sender} {item?.files?.map((file, index) => { @@ -107,15 +107,15 @@ export default function RemoteSensing({ settings, widgetID }) {
- {file?.name} - PDF + {file?.name} + PDF - + Your browser cannot display this PDF directly. @@ -132,8 +132,8 @@ export default function RemoteSensing({ settings, widgetID }) {
- {file?.name} - PNG + {file?.name} + PNG {file?.name} @@ -146,7 +146,7 @@ export default function RemoteSensing({ settings, widgetID }) { <>
- {file?.name} + {file?.name}
@@ -162,17 +162,17 @@ export default function RemoteSensing({ settings, widgetID }) { ) else if (thisWidget?.size === "small") return ( - - + + {item?.volcano_name} - {item?.country} + {item?.country} {item?.report_time} @@ -185,7 +185,6 @@ export default function RemoteSensing({ settings, widgetID }) { ); } - function ascTimeVolcanoSort(a, b) { if (a.volcano_name < b.volcano_name) return -1 else if (a.volcano_name > b.volcano_name) return 1 diff --git a/client/src/components/Widgets/SeismicEvents.jsx b/client/src/components/Widgets/SeismicEvents.jsx index d01dd75..3e3259d 100644 --- a/client/src/components/Widgets/SeismicEvents.jsx +++ b/client/src/components/Widgets/SeismicEvents.jsx @@ -1,10 +1,18 @@ import VdapApi from '../../hooks/useVdapApi.js'; import { useMemo } from 'react'; import Plot from 'react-plotly.js'; -import {Box, Flex, Spinner, Text} from "@chakra-ui/react"; +import { Box, Flex, Spinner, Text } from "@chakra-ui/react"; import useBuildUrl from "@/hooks/useBuildUrl.js"; +// 1. Import Chakra color mode and your central color dictionary +import { useColorMode } from "@/components/ui/color-mode.jsx"; +import { rawWidgetColors } from '../../theme.js'; + export default function SeismicEvents({ settings, widgetID}) { + const { colorMode } = useColorMode(); + // 2. Assign the active palette based on the current mode + const plotColors = colorMode === 'dark' ? rawWidgetColors.dark : rawWidgetColors.light; + // build url, defaults to 7 days past const url = useBuildUrl('/api/v2/seismic_events', settings?.startDate, settings?.endDate); @@ -44,7 +52,7 @@ export default function SeismicEvents({ settings, widgetID}) { mode: 'markers', marker: { size: 7, - line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' }, + line: { width: .5, color: plotColors.text }, symbol: Symbolway[index % Symbolway.length], }, // Replicating the clean D3/Observable tooltip @@ -53,48 +61,36 @@ export default function SeismicEvents({ settings, widgetID}) { 'Date (UTC): %{x|%b %d %Y %H:%M}
' + 'Number of Seismic Events: %{y}' })); - }, [apiData]); + }, [apiData, plotColors]); const layout = useMemo(() => { - return { autosize: true, paper_bgcolor: 'transparent', // Let Chakra UI background show through plot_bgcolor: 'transparent', - font: { color: '#d1d5db' }, // gray.300 + font: { color: plotColors.muted }, margin: { t: 40, r: 20, l: 20, b: 20 }, - // title: { - // text: '', - // font: { size: 16, color: 'white' }, - // x: 0.05 - // }, xaxis: { title: { text: 'Date (UTC)', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'date', tickformat: '%b %d
%Y', // D3 requested %b %d \n %Y formatting - gridcolor: '#4b5563', - zerolinecolor: '#4b5563', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.grid, automargin: true, }, yaxis: { title: { text: 'Seismic Count', - font: { color: 'white', size: 12 } + font: { color: plotColors.text, size: 12 } }, type: 'linear', - gridcolor: '#4b5563', - zerolinecolor: '#ffffff', + gridcolor: plotColors.grid, + zerolinecolor: plotColors.text, automargin: true, }, - // legend: { - // orientation: 'h', - // y: -0.2, - // x: 0.5, - // xanchor: 'center' - // }, hovermode: 'closest', shapes: [ { @@ -105,33 +101,34 @@ export default function SeismicEvents({ settings, widgetID}) { y0: 0, y1: 0, yref: 'y', - line: { color: 'white', width: 1 } + line: { color: plotColors.text, width: 1 } } ], }; - }, []); + }, [plotColors]); if (isLoading) return ( - + ); if (error) return ( - + {error} ); if (plotTraces.length === 0) return ( - - No data available for current filter. + + No data available for current filter. ); return ( - +