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 }) => (
|
||||
<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) */}
|
||||
|
||||
@@ -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?
|
||||
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 */}
|
||||
|
||||
Reference in New Issue
Block a user