Merge remote-tracking branch 'origin/20260721_0845_Venessa_addingWidgets' into 20260715_UI_update

This commit is contained in:
2026-07-22 10:37:09 -07:00
6 changed files with 214 additions and 37 deletions
+2 -2
View File
@@ -48,8 +48,8 @@ function AppFoundation() {
}
const Placeholder = ({ name }) => (
<Flex width="100%" height="100%" align="center" justify="center" bg="gray.50" _dark={{ bg: "gray.900" }}>
<Heading color="gray.400" _dark={{ bg: "gray.600" }} size="md">{name} Dashboard - Coming Soon</Heading>
<Flex width="100%" height="100%" align="center" justify="center">
<Heading color="gray.500" _dark={{ color: "gray.200" }} size="lg">{name} Dashboard - Coming Soon</Heading>
</Flex>
);
@@ -0,0 +1,84 @@
import VdapApi from '../../hooks/useVdapApi.js';
import {Flex, Spinner, Text, Box, Center} from "@chakra-ui/react"; // Adjust path as needed
export default function LlmAlChangesQuadPlot(props) {
console.log("3. Quad Plot loaded");
let tp_percent = 0;
let tn_percent = 0;
let fp_percent = 0;
let fn_percent = 0;
// Clean, single-line data fetching
const { data: apiData, isLoading, error } = VdapApi('/api/v2/llm_quadrant_plot');
if (isLoading) return (
<Flex w="100%" h="100%" bg="gray.800" borderRadius="md" align="center" justify="center">
<Spinner size="xl" color="blue.400" thickness="4px" />
</Flex>
);
if (error) return (
<Flex w="100%" h="100%" bg="gray.800" borderRadius="md" align="center" justify="center" p={4}>
<Text color="red.400">{error}</Text>
</Flex>
);
if (apiData.length === 0) return (
<Flex height="100%" width="100%" align="center" justify="center" bg="gray.800" borderRadius="md">
<Text color="gray.400">No alert level quad plot data available.</Text>
</Flex>
);
if (apiData) {
console.log(apiData);
tp_percent = ((apiData[0]['tp_count']/apiData[0]['tot_count'])*100).toFixed(2);
tn_percent = ((apiData[0]['tn_count']/apiData[0]['tot_count'])*100).toFixed(2);
fp_percent = ((apiData[0]['fp_count']/apiData[0]['tot_count'])*100).toFixed(2);
fn_percent = ((apiData[0]['fn_count']/apiData[0]['tot_count'])*100).toFixed(2);
}
return (
<Flex direction="column" w="100%" h="100%" borderRadius="lg">
<Flex direction="row" w="100%" bg="gray.800" borderTopRadius="lg" justifyItems="center" alignItems="start" p={2} pb={0} pl={4} justifyContent="end">
{/*<Text fontWeight="bold" fontSize="lg" color="white">Alert Level Changes Quad Plot</Text>*/}
<Text bg="cyan.200/5" borderRadius="sm" padding="1" fontWeight="normal" fontSize="sm" color="white">All time</Text>
</Flex>
<Flex direction="column" w="100%" h="100%" bg="gray.800" gap="2" padding="2" borderBottomRadius="lg">
<Flex direction="row" w="100%" h="100%" gap="2">
<Center bg={`blue.200/${tp_percent % 50}`} w="100%" h="100%" borderRadius="lg" color="white" fontSize="sm" fontWeight="bold">
<Flex direction="column" w="100%" h="100%" justifyContent="center" alignItems="center">
<Text>True Positive</Text>
<Text fontSize="lg"> {tp_percent}% </Text>
<Text color="gray.400" fontSize="xs" fontWeight="normal"> {apiData[0]['tp_count']}/{apiData[0]['tot_count']} </Text>
</Flex>
</Center>
<Center bg={`blue.200/${tn_percent % 50}`} w="100%" h="100%" borderRadius="lg" color="white" fontSize="sm" fontWeight="bold">
<Flex direction="column" w="100%" h="100%" justifyContent="center" alignItems="center">
<Text>True Negative</Text>
<Text fontSize="lg"> {tn_percent}% </Text>
<Text color="gray.400" fontSize="xs" fontWeight="normal"> {apiData[0]['tn_count']}/{apiData[0]['tot_count']} </Text>
</Flex>
</Center>
</Flex>
<Flex direction="row" w="100%" h="100%" gap="2">
<Center bg={`blue.200/${fp_percent % 50}`} w="100%" h="100%" borderRadius="lg" color="white" fontSize="sm" fontWeight="bold">
<Flex direction="column" w="100%" h="100%" justifyContent="center" alignItems="center">
<Text>False Positive</Text>
<Text fontSize="lg"> {fp_percent}% </Text>
<Text color="gray.400" fontSize="xs" fontWeight="normal"> {apiData[0]['fp_count']}/{apiData[0]['tot_count']} </Text>
</Flex>
</Center>
<Center bg={`blue.200/${fn_percent % 50}`} w="100%" h="100%" borderRadius="lg" color="white" fontSize="sm" fontWeight="bold">
<Flex direction="column" w="100%" h="100%" justifyContent="center" alignItems="center">
<Text>False Negative</Text>
<Text fontSize="lg"> {fn_percent}% </Text>
<Text color="gray.400" fontSize="xs" fontWeight="normal"> {apiData[0]['fn_count']}/{apiData[0]['tot_count']} </Text>
</Flex>
</Center>
</Flex>
</Flex>
</Flex>
)
}
@@ -0,0 +1,73 @@
import React from 'react';
import { Box } from '@chakra-ui/react';
import { http, HttpResponse } from 'msw'; // Import MSW
import LlmAlChangesQuadPlot from './LlmAlChangesQuadPlot';
export default {
title: 'Dashboard Widgets/LLM AL Changes Quad Plot',
component: LlmAlChangesQuadPlot,
decorators: [
(Story) => (
<Box
w="100%" maxW="1800px" h="400px"
border="2px dashed" borderColor="gray.600" p={2}
resize="both" overflow="auto"
>
<Story />
</Box>
),
],
};
const mockData = [
{ tp: 50 },
{ tn: 250 },
{ fp: 10 },
{ fn: 5 },
{ tot_count: 315}
];
// Scenario LIVE: call our API for real
export const LivePopulated = {};
// Scenario 1: Successful API Call
export const Populated = {
parameters: {
msw: {
handlers: [
// Intercept the exact URL your component is trying to fetch
http.get('*/api/v2/llm_quadrant_plot', () => {
// Return a 200 OK with the mock JSON
return HttpResponse.json(mockData);
}),
],
},
},
};
// Scenario 2: The 500 Server Crash
export const ServerCrash = {
parameters: {
msw: {
handlers: [
http.get('*/api/v2/llm_quad_plot', () => {
// Return a 500 Error
return new HttpResponse(null, { status: 500 });
}),
],
},
},
};
// Scenario 3: Empty State (API works, but no data returned)
export const EmptyData = {
parameters: {
msw: {
handlers: [
http.get('*/api/v2/llm_quad_plot', () => {
return HttpResponse.json([]);
}),
],
},
},
};
@@ -1,4 +1,4 @@
import { useState, useMemo } from "react";
import { useState, useMemo, useRef } from "react";
import {
Button,
IconButton,
@@ -46,12 +46,20 @@ export function SettingsButton() {
const categories = Object.keys(categorizedWidgets);
// use ref to capture html node for placing menu accordingly
const menuRef = useRef(null);
const getAnchorRect = () => {
if (!menuRef.current) return null;
return menuRef.current.getBoundingClientRect();
};
return (
<>
{/* THE STATIC DROPDOWN MENU */}
<Menu.Root>
<Menu.Root positioning={{ getAnchorRect }}>
<Menu.Trigger asChild>
<IconButton
ref={menuRef}
aria-label="Settings"
bg="vdap.darkGreen"
color="white"
@@ -62,7 +70,7 @@ export function SettingsButton() {
<Settings size={18} />
</IconButton>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content bg="white" boxShadow="lg" borderRadius="md" p={1} zIndex="dropdown">
{isEditing ? (
<>
@@ -87,6 +95,7 @@ export function SettingsButton() {
<LayoutGrid size={16} style={{ marginRight: '8px' }} /> Widget Library
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
{/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
+11
View File
@@ -85,4 +85,15 @@ export const WIDGET_REGISTRY = {
}
},
"llm_al_changes_quad_plot": {
name: "Alert Level Changes Quad Plot",
category: "Computer Science",
description: "Percentage of True-Positive, True-Negative, False-Positive, and False-Negative changes in all " +
"alert levels.",
component: lazy(() => import('../components/Widgets/LlmAlChangesQuadPlot.jsx')),
allowedSizes: {
small: { w: 3, h: 12 }
}
}
};
+12 -12
View File
@@ -10,17 +10,17 @@ export default function Home() {
// 1. CSS making this look like how we want it, TODO turn to theme?
const tabStyles = {
fontSize: "lg",
fontWeight: "normal",
fontWeight: "bold",
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
px: 2, // Horizontal padding
// py: 2, // Vertical padding
borderBottom: "1px solid transparent",
transition: "all 0.2s",
_hover: { color: "fg" },
_activeLink: {
color: "color", // Or whatever your primary text color is
borderColor: "currentColor", // The active underline
"&.active": { // Target the active class applied by React Router NavLink
color: "fg",
borderColor: "fg",
}
};
@@ -31,12 +31,12 @@ export default function Home() {
<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}>
<Flex borderBottom="1px solid" borderColor="fg.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>
<Box as={NavLink} to="/home/gas" css={tabStyles}>Gas</Box>
<Box as={NavLink} to="/home/remote" css={tabStyles}>Remote Sensing</Box>
<Box as={NavLink} to="/home/seismic" css={tabStyles}>Seismic</Box>
</Flex>
{/* EDIT MODE BUTTONS & CONTROLS */}