update: comm coor widget settings ui

wip: themes for checkboxes
This commit is contained in:
2026-11-09 08:00:02 -08:00
parent 7b3e7d3d0c
commit 5798b90ab7
5 changed files with 164 additions and 107 deletions
@@ -114,23 +114,26 @@ export default function CommCoorReport({ settings, widgetID }) {
} }
}, [settings, widgetID, updateSettings, apiData]); }, [settings, widgetID, updateSettings, apiData]);
if (isLoading) return ( if (isLoading)
<Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="transparent"> return (
<Spinner size="xl" color="blue.400" thickness="4px"/> <Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="transparent">
</Flex> <Spinner size="xl" color="blue.400" thickness="4px"/>
); </Flex>
);
if (error) return ( if (error)
<Flex w="100%" h="100%" align="center" justify="center" textAlign="center" borderRadius="lg" bg="transparent"> return (
<Text color="red.400">{error}</Text> <Flex w="100%" h="100%" align="center" justify="center" textAlign="center" borderRadius="lg" bg="transparent">
</Flex> <Text color="red.400">{error}</Text>
); </Flex>
);
if (!apiData) return ( if (!apiData)
<Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="transparent"> return (
<Text textAlign="center" color="gray.400">No data</Text> <Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="transparent">
</Flex> <Text textAlign="center" color="gray.400">No data</Text>
); </Flex>
);
return ( return (
<Flex direction="row" w="100%" h="100%" p={1} overflowY="auto"> <Flex direction="row" w="100%" h="100%" p={1} overflowY="auto">
@@ -117,16 +117,14 @@ export default function IvansAlerts({ settings, widgetID }) {
</Flex> </Flex>
{item?.files?.map((file, index) => { {item?.files?.map((file, index) => {
return ( return (
<Box w="100%" h="100%" key={index} p={1}> <Box w="100%" h="100%" key={index}>
<Flex direction="row" mb={2} gap={1} justify="space-between"> <Flex direction="row" w="100%">
<Text w="100%" p={1} fontWeight="semibold" bg="widgetBadgeBg" borderLeftRadius="lg" fontSize="sm" color="text">File: </Text>
<a href={`${file?.url}#view=FitV&toolbar=0`} target="_blank" rel="noopener noreferrer"> <a href={`${file?.url}#view=FitV&toolbar=0`} target="_blank" rel="noopener noreferrer">
<Text mr={1} truncate p={1} bg="widgetBadgeBg" borderRadius="lg" fontSize="sm" color="text"> <Text truncate w="100%" p={1} bg="widgetBadgeBg" borderRightRadius="lg" fontSize="sm" color="text">
File: {file?.name} {file?.name}
</Text> </Text>
</a> </a>
{/*<Box borderRadius="lg" bg="widgetBadgeBg" color="text" p={1} fontSize="sm" asChild>*/}
{/* Open*/}
{/*</Box>*/}
</Flex> </Flex>
<object <object
data={`${file?.url}#view=FitH&toolbar=1`} data={`${file?.url}#view=FitH&toolbar=1`}
@@ -1,4 +1,4 @@
import {Button, Field, Flex, Input, Switch, Text} from "@chakra-ui/react"; import {Button, Field, Flex, Input, Switch, Text, Fieldset, Separator, HStack} from "@chakra-ui/react";
import {useDashboardStore} from "@/store/useDashboardStore.jsx"; import {useDashboardStore} from "@/store/useDashboardStore.jsx";
import {useState} from "react"; import {useState} from "react";
@@ -17,67 +17,80 @@ export default function CommCoorReportSettings({ widgetId, currentSettings }) {
return ( return (
<Flex flexDirection="column" justifyContent="space-between" gap={2} overflowY="auto"> <Flex flexDirection="column" justifyContent="space-between" gap={2} overflowY="auto">
{/* COPY TO CLIPBOARD */} <Fieldset.Root>
<Button w="fit" size="xs" onClick={handleCopyToClipboard}>Copy Report</Button> <Fieldset.Content>
<HStack>
<Text flexShrink="0" fontWeight="semibold">Date Range Selection</Text>
<Separator flex="1" size="sm"/> {/*line seperator*/}
</HStack>
{/* SELECTING DATE RANGE */}
<Flex direction="column" w="100%" h="100%" justify="center" gap="2">
<Field.Root invalid={!!startError}>
<Flex direction="row" w="100%" h="100%" align="center" justify="center">
<Text w="50%" align="center" justify="center" mt={2} color="text">
Start Date
</Text>
<Flex direction="column" w="100%">
<Input
w="100%"
type="date"
value={currentSettings?.startDate || ""}
onChange={(e) => {
const value = e.target.value;
updateSettings(widgetId, {...currentSettings, startDate: value});
{/* DOWNLOAD REPORT */} const dateSelected = new Date(value);
<Button w="fit" size="xs" onClick={handleDownloadReport}>Download Report</Button> const today = new Date();
today.setHours(0, 0, 0, 0);
{/* SELECTING DATE RANGE */} if (value && (dateSelected > today)) setStartError("Can't Choose a Future Date");
<Flex direction="column" w="100%" h="100%" align="center" justify="center" gap="2"> else setStartError("");
<Field.Root invalid={!!startError}> }}
<Flex direction="row" w="100%" h="100%" align="center" justify="center"> />
<Text w="50%" align="center" justify="center" mt={2} color="gray.700"> {startError && <Field.ErrorText>{startError}</Field.ErrorText>}
Start Date </Flex>
</Text> </Flex>
<Flex direction="column" w="100%"> </Field.Root>
<Input <Field.Root invalid={!!endError}>
w="100%" <Flex direction="row" w="100%" h="100%" align="center" justify="center">
type="date" <Text w="50%" align="center" justify="center" mt={2} color="text">
value={currentSettings?.startDate || ""} End Date
onChange={(e) => { </Text>
const value = e.target.value; <Flex direction="column" w="100%">
updateSettings(widgetId, {...currentSettings, startDate: value}); <Input
w="100%"
type="date"
value={currentSettings?.endDate || ""}
onChange={(e) => {
const value = e.target.value;
updateSettings(widgetId, {...currentSettings, endDate: value});
const dateSelected = new Date(value); const dateSelected = new Date(value);
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
if (value && (dateSelected > today)) setStartError("Can't Choose a Future Date"); if (value && (dateSelected > today)) setEndError("Can't Choose a Future Date");
else setStartError(""); else setEndError("");
}} }}
/> />
{startError && <Field.ErrorText>{startError}</Field.ErrorText>} {endError && <Field.ErrorText>{endError}</Field.ErrorText>}
</Flex> </Flex>
</Flex>
</Field.Root>
</Flex> </Flex>
</Field.Root>
<Field.Root invalid={!!endError}>
<Flex direction="row" w="100%" h="100%" align="center" justify="center">
<Text w="50%" align="center" justify="center" mt={2} color="gray.700">
End Date
</Text>
<Flex direction="column" w="100%">
<Input
w="100%"
type="date"
value={currentSettings?.endDate || ""}
onChange={(e) => {
const value = e.target.value;
updateSettings(widgetId, {...currentSettings, endDate: value});
const dateSelected = new Date(value); <HStack>
const today = new Date(); <Text flexShrink="0" fontWeight="semibold">Tools</Text>
today.setHours(0, 0, 0, 0); <Separator flex="1" size="sm"/> {/*line seperator*/}
</HStack>
if (value && (dateSelected > today)) setEndError("Can't Choose a Future Date"); <Flex direction="column" w="100%" h="100%" justify="center" gap={2}>
else setEndError(""); {/* COPY TO CLIPBOARD */}
}} <Button w="100%" size="xs" borderRadius="lg" variant="subtle" onClick={handleCopyToClipboard}>Copy Report</Button>
/> {/* DOWNLOAD REPORT */}
{endError && <Field.ErrorText>{endError}</Field.ErrorText>} <Button w="100%" size="xs" borderRadius="lg" variant="subtle" onClick={handleDownloadReport}>Download Report</Button>
</Flex>
</Flex> </Flex>
</Field.Root> </Fieldset.Content>
</Flex> </Fieldset.Root>
</Flex> </Flex>
) )
} }
@@ -1,4 +1,16 @@
import {Button, Field, Flex, Input, Switch, Text, Fieldset, CheckboxCard} from "@chakra-ui/react"; import {
Button,
Field,
Flex,
Input,
Switch,
Text,
Fieldset,
CheckboxCard,
Separator,
HStack,
useSlotRecipe
} from "@chakra-ui/react";
import {useDashboardStore} from "../../../store/useDashboardStore.jsx"; import {useDashboardStore} from "../../../store/useDashboardStore.jsx";
import {useState} from "react"; import {useState} from "react";
@@ -7,6 +19,10 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
const [startError, setStartError] = useState(null); const [startError, setStartError] = useState(null);
const [endError, setEndError] = useState(null); const [endError, setEndError] = useState(null);
const popoverRecipe = useSlotRecipe({ key: "glassPopover" });
const popoverStyles = popoverRecipe();
const handleCopyToClipboard = () => { const handleCopyToClipboard = () => {
updateSettings(widgetId, {copyToClipboard: true}); updateSettings(widgetId, {copyToClipboard: true});
} }
@@ -25,12 +41,11 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
<Fieldset.Content> <Fieldset.Content>
{/* SELECTING DATE RANGE */} {/* SELECTING DATE RANGE */}
<Flex direction="column" w="100%" h="100%" justify="center" gap={2}> <Flex direction="column" w="100%" h="100%" justify="center" gap={2}>
<Flex direction="column"> <HStack>
<Text w="100%" fontWeight="bold"> <Separator flex="1" size="md"/> {/*line seperator*/}
Date Range Selection <Text flexShrink="0" fontWeight="semibold">Date Range Selection</Text>
</Text> <Separator flex="1" size="md"/> {/*line seperator*/}
<hr style={{borderColor: "red.700"}} /> {/*line seperator*/} </HStack>
</Flex>
{/* start date */} {/* start date */}
<Field.Root invalid={!!startError}> <Field.Root invalid={!!startError}>
@@ -49,6 +64,7 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
type="date" type="date"
w="100%" w="100%"
borderRadius="lg" borderRadius="lg"
bg="widgetBadgeBg"
value={currentSettings?.startDate || ""} value={currentSettings?.startDate || ""}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
@@ -90,6 +106,7 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
type="date" type="date"
w="100%" w="100%"
borderRadius="lg" borderRadius="lg"
bg="widgetBadgeBg"
value={currentSettings?.endDate || ""} value={currentSettings?.endDate || ""}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
@@ -114,18 +131,17 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
</Flex> </Flex>
{/* SORTS */} {/* SORTS */}
<Flex direction="column"> <HStack>
<Text w="100%" fontWeight="bold"> <Separator flex="1" size="md"/> {/*line seperator*/}
Sorts <Text flexShrink="0" fontWeight="semibold">Sorts</Text>
</Text> <Separator flex="1" size="md"/> {/*line seperator*/}
<hr /> {/*line seperator*/} </HStack>
</Flex>
{/* time sort */} {/* time sort */}
<Switch.Root <Switch.Root
checked={currentSettings?.activeSorts?.timeSort === "asc"} checked={currentSettings?.activeSorts?.timeSort === "asc"}
onCheckedChange={handleTimeSort} onCheckedChange={handleTimeSort}
colorPalette="blue" colorPalette="orange"
> >
<Switch.HiddenInput /> <Switch.HiddenInput />
<Switch.Control> <Switch.Control>
@@ -138,7 +154,7 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
<Switch.Root <Switch.Root
checked={currentSettings?.activeSorts?.volcanoSort} checked={currentSettings?.activeSorts?.volcanoSort}
onCheckedChange={handleVolcanoSort} onCheckedChange={handleVolcanoSort}
colorPalette="blue" colorPalette="orange"
> >
<Switch.HiddenInput /> <Switch.HiddenInput />
<Switch.Control> <Switch.Control>
@@ -147,18 +163,19 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
<Switch.Label fontWeight={400}>Alphabetically by Volcano</Switch.Label> <Switch.Label fontWeight={400}>Alphabetically by Volcano</Switch.Label>
</Switch.Root> </Switch.Root>
<Flex direction="column"> <HStack>
<Text w="100%" fontWeight="bold"> <Separator flex="1" size="md"/> {/*line seperator*/}
Filters <Text flexShrink="0" fontWeight="semibold">Filters</Text>
</Text> <Separator flex="1" size="md"/> {/*line seperator*/}
<hr /> {/*line seperator*/} </HStack>
</Flex>
{/* elevated levels */} {/* elevated levels */}
<CheckboxCard.Root <CheckboxCard.Root
size="sm" size="sm"
variant="surface" variant="surface"
borderRadius="lg" borderRadius="lg"
css={popoverStyles.checkboxcard}
defaultChecked={currentSettings?.filters?.elevatedLevels} defaultChecked={currentSettings?.filters?.elevatedLevels}
onCheckedChange={(e) => { onCheckedChange={(e) => {
updateSettings(widgetId, { updateSettings(widgetId, {
@@ -177,18 +194,17 @@ export default function IvansAlertsSettings({ widgetId, currentSettings }) {
Only Elevated Levels Only Elevated Levels
</CheckboxCard.Label> </CheckboxCard.Label>
</CheckboxCard.Content> </CheckboxCard.Content>
<CheckboxCard.Indicator /> <CheckboxCard.Indicator css={popoverStyles.checkboxcard} />
</CheckboxCard.Control> </CheckboxCard.Control>
</CheckboxCard.Root> </CheckboxCard.Root>
{/* COPY TO CLIPBOARD */} {/* COPY TO CLIPBOARD */}
<Flex direction="column"> <HStack>
<Text w="100%" fontWeight="bold"> <Separator flex="1" size="md"/> {/*line seperator*/}
Tools <Text flexShrink="0" fontWeight="semibold">Tools</Text>
</Text> <Separator flex="1" size="md"/> {/*line seperator*/}
<hr /> {/*line seperator*/} </HStack>
<Button mt={2} w="100%" size="xs" borderRadius="lg" variant="subtle" onClick={handleCopyToClipboard}>Copy All IVANS</Button> <Button mt={-2} colorPalette="orange" w="100%" size="xs" borderRadius="lg" variant="subtle" onClick={handleCopyToClipboard}>Copy All IVANS</Button>
</Flex>
</Fieldset.Content> </Fieldset.Content>
</Fieldset.Root> </Fieldset.Root>
</Flex> </Flex>
+28 -1
View File
@@ -346,7 +346,7 @@ const widgetRecipe = defineSlotRecipe({
// 6. TRUE POPOVER: Compact floating card for 3-dots menus // 6. TRUE POPOVER: Compact floating card for 3-dots menus
const glassPopoverRecipe = defineSlotRecipe({ const glassPopoverRecipe = defineSlotRecipe({
slots: ["content", "arrow", "positioner"], slots: ["content", "arrow", "positioner", "checkboxcard", "checkbox"],
base: { base: {
content: { content: {
...glassSurface, ...glassSurface,
@@ -357,6 +357,33 @@ const glassPopoverRecipe = defineSlotRecipe({
backdropFilter: "blur(15px)", backdropFilter: "blur(15px)",
WebkitBackdropFilter: "blur(15px)", WebkitBackdropFilter: "blur(15px)",
}, },
checkboxcard: {
bg: "widgetItemBg",
_hover: {
bg: "widgetBadgeBg"
},
_checked: {
bg: "orange.700"
},
_dark: {
bg: "widgetBadgeBg",
_hover: {
bg: "widgetInnerBadgeBg"
},
_checked: {
bg: "orange.700",
_hover: {
bg: "orange.700/50"
}
}
}
},
checkbox: {
bg: "widgetInnerBadgeBg",
_checked: {
bg: "orange"
}
}
}, },
}); });