Merge remote-tracking branch 'origin/20260721_0845_Venessa_addingWidgets' into 20260715_UI_update
This commit is contained in:
+2
-2
@@ -48,8 +48,8 @@ function AppFoundation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Placeholder = ({ name }) => (
|
const Placeholder = ({ name }) => (
|
||||||
<Flex width="100%" height="100%" align="center" justify="center" bg="gray.50" _dark={{ bg: "gray.900" }}>
|
<Flex width="100%" height="100%" align="center" justify="center">
|
||||||
<Heading color="gray.400" _dark={{ bg: "gray.600" }} size="md">{name} Dashboard - Coming Soon</Heading>
|
<Heading color="gray.500" _dark={{ color: "gray.200" }} size="lg">{name} Dashboard - Coming Soon</Heading>
|
||||||
</Flex>
|
</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 {
|
import {
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
@@ -46,12 +46,20 @@ export function SettingsButton() {
|
|||||||
|
|
||||||
const categories = Object.keys(categorizedWidgets);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* THE STATIC DROPDOWN MENU */}
|
{/* THE STATIC DROPDOWN MENU */}
|
||||||
<Menu.Root>
|
<Menu.Root positioning={{ getAnchorRect }}>
|
||||||
<Menu.Trigger asChild>
|
<Menu.Trigger asChild>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
ref={menuRef}
|
||||||
aria-label="Settings"
|
aria-label="Settings"
|
||||||
bg="vdap.darkGreen"
|
bg="vdap.darkGreen"
|
||||||
color="white"
|
color="white"
|
||||||
@@ -62,7 +70,7 @@ export function SettingsButton() {
|
|||||||
<Settings size={18} />
|
<Settings size={18} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Menu.Trigger>
|
</Menu.Trigger>
|
||||||
|
<Menu.Positioner>
|
||||||
<Menu.Content bg="white" boxShadow="lg" borderRadius="md" p={1} zIndex="dropdown">
|
<Menu.Content bg="white" boxShadow="lg" borderRadius="md" p={1} zIndex="dropdown">
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<>
|
<>
|
||||||
@@ -87,6 +95,7 @@ export function SettingsButton() {
|
|||||||
<LayoutGrid size={16} style={{ marginRight: '8px' }} /> Widget Library
|
<LayoutGrid size={16} style={{ marginRight: '8px' }} /> Widget Library
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.Content>
|
</Menu.Content>
|
||||||
|
</Menu.Positioner>
|
||||||
</Menu.Root>
|
</Menu.Root>
|
||||||
|
|
||||||
{/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
|
{/* THE WIDGET LIBRARY MODAL (Controlled by isLibraryOpen) */}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -10,17 +10,17 @@ export default function Home() {
|
|||||||
// 1. CSS making this look like how we want it, TODO turn to theme?
|
// 1. CSS making this look like how we want it, TODO turn to theme?
|
||||||
const tabStyles = {
|
const tabStyles = {
|
||||||
fontSize: "lg",
|
fontSize: "lg",
|
||||||
fontWeight: "normal",
|
fontWeight: "bold",
|
||||||
color: "fg.muted",
|
color: "fg.muted",
|
||||||
px: 4, // Horizontal padding
|
px: 2, // Horizontal padding
|
||||||
py: 2, // Vertical padding
|
// py: 2, // Vertical padding
|
||||||
borderBottom: "2px solid transparent",
|
borderBottom: "1px solid transparent",
|
||||||
mb: "-2px", // 🔥 The trick: Pulls the active border down to overlap the container's line
|
|
||||||
transition: "all 0.2s",
|
transition: "all 0.2s",
|
||||||
_hover: { color: "fg" },
|
_hover: { color: "fg" },
|
||||||
_activeLink: {
|
|
||||||
color: "color", // Or whatever your primary text color is
|
"&.active": { // Target the active class applied by React Router NavLink
|
||||||
borderColor: "currentColor", // The active underline
|
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%">
|
<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 */}
|
{/* 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" 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/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>
|
</Flex>
|
||||||
|
|
||||||
{/* EDIT MODE BUTTONS & CONTROLS */}
|
{/* EDIT MODE BUTTONS & CONTROLS */}
|
||||||
|
|||||||
Reference in New Issue
Block a user