fix image

fix menu, links
add drawer menu
This commit is contained in:
NlightN22 2024-02-26 03:05:19 +07:00
parent 5cfd9155c3
commit fecd30df70
20 changed files with 140 additions and 1187 deletions

View File

@ -1,7 +1,7 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { AppShell, useMantineTheme, } from "@mantine/core" import { AppShell, useMantineTheme, } from "@mantine/core"
import { HeaderAction } from './widgets/header/HeaderAction'; import { HeaderAction } from './widgets/header/HeaderAction';
import { testHeaderLinks } from './widgets/header/header.links'; import { headerLinks } from './widgets/header/header.links';
import AppRouter from './router/AppRouter'; import AppRouter from './router/AppRouter';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Context } from '.'; import { Context } from '.';
@ -38,7 +38,7 @@ const AppBody = () => {
<AppShell <AppShell
styles={{ styles={{
main: { main: {
paddingLeft: !leftSideBar ? "3em" : '', paddingLeft: !leftSideBar ? "1em" : '',
paddingRight: !rightSideBar ? '3em' : '', paddingRight: !rightSideBar ? '3em' : '',
background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : undefined, background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : undefined,
}, },
@ -47,7 +47,7 @@ const AppBody = () => {
asideOffsetBreakpoint="sm" asideOffsetBreakpoint="sm"
header={ header={
<HeaderAction links={testHeaderLinks.links} /> <HeaderAction links={headerLinks} />
} }
aside={ aside={
<SideBar isHidden={rightSideBarIsHidden} side="right" /> <SideBar isHidden={rightSideBarIsHidden} side="right" />

View File

@ -10,9 +10,13 @@ import { useQuery } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api'; import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import RetryErrorPage from './RetryErrorPage'; import RetryErrorPage from './RetryErrorPage';
import CameraCard from '../shared/components/CameraCard'; import CameraCard from '../shared/components/CameraCard';
import { useMediaQuery } from '@mantine/hooks';
import { dimensions } from '../shared/dimensions/dimensions';
const MainPage = () => { const MainPage = () => {
const { sideBarsStore } = useContext(Context) const { sideBarsStore } = useContext(Context)
const isMobile = useMediaQuery(dimensions.mobileSize)
useEffect(() => { useEffect(() => {
sideBarsStore.rightVisible = false sideBarsStore.rightVisible = false
@ -36,8 +40,8 @@ const MainPage = () => {
if (isError) return <RetryErrorPage onRetry={refetch} /> if (isError) return <RetryErrorPage onRetry={refetch} />
const cards = () => { const cards = () => {
return cameras.filter(cam => cam.frigateHost?.host.includes('5000')).slice(0,25).map(camera => ( return cameras.filter(cam => cam.frigateHost?.host.includes('5000')).slice(0, 25).map(camera => (
// return cameras.map(camera => ( // return cameras.map(camera => (
<CameraCard <CameraCard
key={camera.id} key={camera.id}
camera={camera} camera={camera}
@ -46,26 +50,17 @@ const MainPage = () => {
} }
return ( return (
<Flex direction='column' h='100%' > <Flex direction='column' h='100%' w='100%' >
<Flex justify='space-between' align='center' w='100%'> <Flex justify='space-between' align='center' w='100%'>
<Group <Flex w='100%'
w='25%'
>
</Group>
<Group
w='50%'
style={{ style={{
justifyContent: 'center', justifyContent: 'center',
}} }}
><HeadSearch /></Group> ><HeadSearch /></Flex>
<Group {/* <ViewSelector state={viewState} onChange={handleToggleState} /> */}
w='25%'
position="right">
<ViewSelector state={viewState} onChange={handleToggleState} />
</Group>
</Flex> </Flex>
<Flex justify='center' h='100%' direction='column' > <Flex justify='center' h='100%' direction='column' w='100%' >
<Grid mt='sm' justify="center" mb='sm' align='stretch'> <Grid mt='sm' justify="center" mb='sm' align='stretch' mr='0.5rem'>
{cards()} {cards()}
</Grid> </Grid>
</Flex> </Flex>

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { CameraConfig } from "../../types/frigateConfig"; import { CameraConfig } from "../../types/frigateConfig";
import { Flex, Text } from "@mantine/core"; import { Flex, Text, Image } from "@mantine/core";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { frigateApi, proxyApi } from "../../services/frigate.proxy/frigate.api"; import { frigateApi, proxyApi } from "../../services/frigate.proxy/frigate.api";
import { useIntersection } from "@mantine/hooks"; import { useIntersection } from "@mantine/hooks";
@ -19,7 +19,7 @@ const AutoUpdatedImage = ({
enabled, enabled,
...rest ...rest
}: AutoUpdatedImageProps) => { }: AutoUpdatedImageProps) => {
const { ref, entry } = useIntersection({threshold: 0.1,}) const { ref, entry } = useIntersection({ threshold: 0.1, })
const isVisible = entry?.isIntersecting const isVisible = entry?.isIntersecting
const { data: imageBlob, refetch, isPending, isError } = useQuery({ const { data: imageBlob, refetch, isPending, isError } = useQuery({
@ -52,14 +52,12 @@ const AutoUpdatedImage = ({
const image = URL.createObjectURL(imageBlob!) const image = URL.createObjectURL(imageBlob!)
return ( return (
<> <Flex direction="column" justify="center" h="100%">
{enabled ? <img ref={ref} src={image} alt="Dynamic Content" {...rest}/> {enabled ? <Image ref={ref} src={image} alt="Dynamic Content" {...rest} />
: :
<Flex direction="column" justify="center" h="100%"> <Text align="center">Camera is disabled in config, no stream or snapshot available!</Text>
<Text align="center">Camera is disabled in config, no stream or snapshot available!</Text>
</Flex>
} }
</>) </Flex>)
}; };
export default AutoUpdatedImage export default AutoUpdatedImage

View File

@ -57,9 +57,7 @@ const CameraCard = ({
<Grid.Col md={6} lg={3} p='0.2rem'> <Grid.Col md={6} lg={3} p='0.2rem'>
<Card h='100%' radius="lg" padding='0.5rem' className={classes.mainCard}> <Card h='100%' radius="lg" padding='0.5rem' className={classes.mainCard}>
<Text align='center' size='md' className={classes.headText} >{camera.name} / {camera.frigateHost?.name}</Text> <Text align='center' size='md' className={classes.headText} >{camera.name} / {camera.frigateHost?.name}</Text>
<Flex direction='column' className={classes.cameraImage}> <AutoUpdatedImage onClick={handleOpenLiveView} enabled={camera.config?.enabled} imageUrl={imageUrl} />
<AutoUpdatedImage onClick={handleOpenLiveView} enabled={camera.config?.enabled} imageUrl={imageUrl} />
</Flex>
<Group <Group
className={classes.bottomGroup}> className={classes.bottomGroup}>
<Flex justify='space-evenly' mt='0.5rem' w='100%'> <Flex justify='space-evenly' mt='0.5rem' w='100%'>

View File

@ -0,0 +1,85 @@
import { Box, Burger, Button, Center, Collapse, Divider, Drawer, Flex, Group, Menu, ScrollArea, UnstyledButton, createStyles, rem, useMantineTheme } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconChevronDown } from '@tabler/icons-react';
import React from 'react';
import { LinkItem } from '../../widgets/header/HeaderAction';
import { useNavigate } from 'react-router-dom';
const useStyles = createStyles((theme) => ({
burger: {
[theme.fn.largerThan('sm')]: {
display: 'none',
},
},
drawerButton: {
color: theme.colorScheme === 'dark' ? '#a5d8ff' : '#228be6',
fontWeight: 600,
fontSize: '1.2rem',
margin: '0.3rem',
height: '2.5rem',
textAlign: 'center',
borderRadius: '0.5rem',
transition: 'background-color 0.5s',
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.fn.darken(theme.colors.cyan[9], 0.5) : theme.colors.cyan[1],
},
'&:active': {
backgroundColor: theme.colorScheme === 'dark' ?
theme.fn.darken(theme.colors.cyan[9], 0.6) :
theme.fn.darken(theme.colors.cyan[1], 0.1),
}
},
}))
interface DrawerMenuProps {
links: LinkItem[],
}
const DrawerMenu = ({
links
}: DrawerMenuProps) => {
const navigate = useNavigate()
const { classes } = useStyles();
const [drawerOpened, { toggle: toggleDrawer, close: closeDrawer }] = useDisclosure(false)
const theme = useMantineTheme();
const handleNavigate = (link: string) => {
navigate(link)
closeDrawer()
}
const items = links.map(item => (
<UnstyledButton
className={classes.drawerButton}
key={item.link}
onClick={() => handleNavigate(item.link)}
>
{item.label}
</UnstyledButton>
))
return (
<>
<Burger opened={drawerOpened} onClick={toggleDrawer} className={classes.burger} size="sm" />
<Drawer
opened={drawerOpened}
onClose={closeDrawer}
size="100%"
padding="md"
title="Menu"
zIndex={1000000}
>
<Flex direction='column' w='100%'>
{items}
</Flex>
</Drawer>
</>
);
};
export default DrawerMenu;

View File

@ -1,99 +0,0 @@
// import { useCallback, useEffect, useMemo, useState } from "react";
// import CameraImage from "./CameraImage";
// import { CameraConfig } from "../../../types/frigateConfig";
// import { useDocumentVisibility } from "@mantine/hooks";
// import { AspectRatio, Flex } from "@mantine/core";
export {}
// interface AutoUpdatingCameraImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
// cameraConfig?: CameraConfig
// searchParams?: {};
// showFps?: boolean;
// className?: string;
// url: string
// };
// // TODO Delete
// export default function AutoUpdatingCameraImage({
// cameraConfig,
// searchParams = "",
// showFps = true,
// className,
// url,
// ...rest
// }: AutoUpdatingCameraImageProps) {
// const [key, setKey] = useState(Date.now());
// const [fps, setFps] = useState<string>("0");
// const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout>();
// const windowVisible = useDocumentVisibility()
// const reloadInterval = useMemo(() => {
// if (windowVisible === "hidden") {
// return -1; // no reason to update the image when the window is not visible
// }
// // if (liveReady) {
// // return 60000;
// // }
// // if (cameraActive) {
// // return 200;
// // }
// return 30000;
// }, [windowVisible]);
// useEffect(() => {
// if (reloadInterval == -1) {
// return;
// }
// setKey(Date.now());
// return () => {
// if (timeoutId) {
// clearTimeout(timeoutId);
// setTimeoutId(undefined);
// }
// };
// }, [reloadInterval]);
// const handleLoad = useCallback(() => {
// if (reloadInterval == -1) {
// return;
// }
// const loadTime = Date.now() - key;
// if (showFps) {
// setFps((1000 / Math.max(loadTime, reloadInterval)).toFixed(1));
// }
// setTimeoutId(
// setTimeout(
// () => {
// setKey(Date.now());
// },
// loadTime > reloadInterval ? 1 : reloadInterval
// )
// );
// }, [key, setFps]);
// return (
// // <AspectRatio ratio={1}>
// <Flex direction='column' h='100%'>
// {/* <CameraImage
// cameraConfig={cameraConfig}
// onload={handleLoad}
// enabled={cameraConfig?.enabled}
// url={url}
// {...rest}
// /> */}
// {showFps ? <span className="text-xs">Displaying at {fps}fps</span> : null}
// </Flex>
// // </AspectRatio >
// );
// }

View File

@ -1,117 +0,0 @@
// import Tooltip from './Tooltip';
// import { Fragment, useCallback, useRef, useState } from 'react';
export {}
// const ButtonColors = {
// blue: {
// contained: 'bg-blue-500 focus:bg-blue-400 active:bg-blue-600 ring-blue-300',
// outlined:
// 'text-blue-500 border-2 border-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
// text: 'text-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
// iconOnly: 'text-blue-500 hover:text-blue-200',
// },
// red: {
// contained: 'bg-red-500 focus:bg-red-400 active:bg-red-600 ring-red-300',
// outlined:
// 'text-red-500 border-2 border-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
// text: 'text-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
// iconOnly: 'text-red-500 hover:text-red-200',
// },
// yellow: {
// contained: 'bg-yellow-500 focus:bg-yellow-400 active:bg-yellow-600 ring-yellow-300',
// outlined:
// 'text-yellow-500 border-2 border-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
// text: 'text-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
// iconOnly: 'text-yellow-500 hover:text-yellow-200',
// },
// green: {
// contained: 'bg-green-500 focus:bg-green-400 active:bg-green-600 ring-green-300',
// outlined:
// 'text-green-500 border-2 border-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
// text: 'text-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
// iconOnly: 'text-green-500 hover:text-green-200',
// },
// gray: {
// contained: 'bg-gray-500 focus:bg-gray-400 active:bg-gray-600 ring-gray-300',
// outlined:
// 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
// text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
// iconOnly: 'text-gray-500 hover:text-gray-200',
// },
// disabled: {
// contained: 'bg-gray-400',
// outlined:
// 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
// text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
// iconOnly: 'text-gray-500 hover:text-gray-200',
// },
// black: {
// contained: '',
// outlined: '',
// text: 'text-black dark:text-white',
// iconOnly: '',
// },
// };
// const ButtonTypes = {
// contained: 'text-white shadow focus:shadow-xl hover:shadow-md',
// outlined: '',
// text: 'transition-opacity',
// iconOnly: 'transition-opacity',
// };
// export default function Button({
// children,
// className = '',
// color = 'blue',
// disabled = false,
// ariaCapitalize = false,
// href,
// target,
// type = 'contained',
// ...attrs
// }) {
// const [hovered, setHovered] = useState(false);
// const ref = useRef();
// let classes = `whitespace-nowrap flex items-center space-x-1 ${className} ${ButtonTypes[type]} ${
// ButtonColors[disabled ? 'disabled' : color][type]
// } font-sans inline-flex font-bold uppercase text-xs px-1.5 md:px-2 py-2 rounded outline-none focus:outline-none ring-opacity-50 transition-shadow transition-colors ${
// disabled ? 'cursor-not-allowed' : `${type == 'iconOnly' ? '' : 'focus:ring-2'} cursor-pointer`
// }`;
// if (disabled) {
// classes = classes.replace(/(?:focus|active|hover):[^ ]+/g, '');
// }
// const handleMousenter = useCallback(() => {
// setHovered(true);
// }, []);
// const handleMouseleave = useCallback(() => {
// setHovered(false);
// }, []);
// const Element = href ? 'a' : 'div';
// return (
// <Fragment>
// <Element
// role="button"
// aria-disabled={disabled ? 'true' : 'false'}
// tabindex="0"
// className={classes}
// href={href}
// target={target}
// ref={ref}
// onmouseenter={handleMousenter}
// onmouseleave={handleMouseleave}
// {...attrs}
// >
// {children}
// </Element>
// {hovered && attrs['aria-label'] ? <Tooltip text={attrs['aria-label']} relativeTo={ref} capitalize={ariaCapitalize} /> : null}
// </Fragment>
// );
// }

View File

@ -1,161 +0,0 @@
// import { useCallback, useMemo, useState } from "react";
// import AutoUpdatingCameraImage from "./AutoUpdatingCameraImage";
// import { CameraConfig } from "../../../types/frigateConfig";
// import { usePersistence } from "../../../hooks/use-persistence";
// import { Button, Switch, Text } from "@mantine/core";
// import { Card, CardContent, CardHeader, CardTitle } from "./card";
// import { IconSettings } from "@tabler/icons-react";
export {}
// type Options = { [key: string]: boolean };
// const emptyObject = Object.freeze({});
// type DebugCameraImageProps = {
// className?: string;
// cameraConfig: CameraConfig
// url: string
// };
// export default function DebugCameraImage({
// className,
// cameraConfig,
// url,
// }: DebugCameraImageProps) {
// const [showSettings, setShowSettings] = useState(false);
// const [options, setOptions] = usePersistence(
// `${cameraConfig?.name}-feed`,
// emptyObject
// );
// const handleSetOption = useCallback(
// (id: string, value: boolean) => {
// const newOptions = { ...options, [id]: value };
// setOptions(newOptions);
// },
// [options]
// );
// const searchParams = useMemo(
// () =>
// new URLSearchParams(
// Object.keys(options).reduce((memo, key) => {
// //@ts-ignore we know this is correct
// memo.push([key, options[key] === true ? "1" : "0"]);
// return memo;
// }, [])
// ),
// [options]
// );
// const handleToggleSettings = useCallback(() => {
// setShowSettings(!showSettings);
// }, [showSettings]);
// return (
// <div className={className}>
// <AutoUpdatingCameraImage
// cameraConfig={cameraConfig}
// searchParams={searchParams}
// url={url}
// />
// <Button onClick={handleToggleSettings} variant="link" size="sm">
// <span className="w-5 h-5">
// <IconSettings />
// </span>{" "}
// <span>{showSettings ? "Hide" : "Show"} Options</span>
// </Button>
// {showSettings ? (
// <Card>
// <CardHeader>
// <CardTitle>Options</CardTitle>
// </CardHeader>
// <CardContent>
// <DebugSettings
// handleSetOption={handleSetOption}
// options={options}
// />
// </CardContent>
// </Card>
// ) : null}
// </div>
// );
// }
// type DebugSettingsProps = {
// handleSetOption: (id: string, value: boolean) => void;
// options: Options;
// };
// function DebugSettings({ handleSetOption, options }: DebugSettingsProps) {
// return (
// <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
// <div className="flex items-center space-x-2">
// <Switch
// id="bbox"
// checked={options["bbox"]}
// onChange={() => { }}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("bbox", isChecked);
// // }}
// />
// {/* <Label htmlFor="bbox">Bounding Box</Label> */}
// <Text>Bounding Box</Text>
// </div>
// <div className="flex items-center space-x-2">
// <Switch
// id="timestamp"
// checked={options["timestamp"]}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("timestamp", isChecked);
// // }}
// />
// {/* <Label htmlFor="timestamp">Timestamp</Label> */}
// <Text>Timestamp</Text>
// </div>
// <div className="flex items-center space-x-2">
// <Switch
// id="zones"
// checked={options["zones"]}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("zones", isChecked);
// // }}
// />
// {/* <Label htmlFor="zones">Zones</Label> */}
// <Text>Zones</Text>
// </div>
// <div className="flex items-center space-x-2">
// <Switch
// id="mask"
// checked={options["mask"]}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("mask", isChecked);
// // }}
// />
// {/* <Label htmlFor="mask">Mask</Label> */}
// <Text>Mask</Text>
// </div>
// <div className="flex items-center space-x-2">
// <Switch
// id="motion"
// checked={options["motion"]}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("motion", isChecked);
// // }}
// />
// {/* <Label htmlFor="motion">Motion</Label> */}
// <Text>Motion</Text>
// </div>
// <div className="flex items-center space-x-2">
// <Switch
// id="regions"
// checked={options["regions"]}
// // onCheckedChange={(isChecked) => {
// // handleSetOption("regions", isChecked);
// // }}
// />
// {/* <Label htmlFor="regions">Regions</Label> */}
// <Text>Regions</Text>
// </div>
// </div>
// );
// }

View File

@ -1,37 +0,0 @@
// import { h, Fragment } from 'preact';
// import { createPortal } from 'preact/compat';
// import { useState, useEffect } from 'preact/hooks';
export {}
// export default function Dialog({ children, portalRootID = 'dialogs' }) {
// const portalRoot = portalRootID && document.getElementById(portalRootID);
// const [show, setShow] = useState(false);
// useEffect(() => {
// window.requestAnimationFrame(() => {
// setShow(true);
// });
// }, []);
// const dialog = (
// <Fragment>
// <div
// data-testid="scrim"
// key="scrim"
// className="fixed bg-fixed inset-0 z-10 flex justify-center items-center bg-black bg-opacity-40"
// >
// <div
// role="modal"
// className={`absolute rounded shadow-2xl bg-white dark:bg-gray-700 sm:max-w-sm md:max-w-md lg:max-w-lg text-gray-900 dark:text-white transition-transform transition-opacity duration-75 transform scale-90 opacity-0 ${
// show ? 'scale-100 opacity-100' : ''
// }`}
// >
// {children}
// </div>
// </div>
// </Fragment>
// );
// return portalRoot ? createPortal(dialog, portalRoot) : dialog;
// }

View File

@ -1,18 +0,0 @@
// import { h } from 'preact';
// import { Link as RouterLink } from 'preact-router/match';
export {}
// export default function Link({
// activeClassName = '',
// className = 'text-blue-500 hover:underline',
// children,
// href,
// ...props
// }) {
// return (
// <RouterLink activeClassName={activeClassName} className={className} href={href} {...props}>
// {children}
// </RouterLink>
// );
// }

View File

@ -1,50 +0,0 @@
// import { h } from 'preact';
// import RelativeModal from './RelativeModal';
// import { useCallback } from 'preact/hooks';
export {}
// export default function Menu({ className, children, onDismiss, relativeTo, widthRelative }) {
// return relativeTo ? (
// <RelativeModal
// children={children}
// className={`${className || ''} py-2`}
// role="listbox"
// onDismiss={onDismiss}
// portalRootID="menus"
// relativeTo={relativeTo}
// widthRelative={widthRelative}
// />
// ) : null;
// }
// export function MenuItem({ focus, icon: Icon, label, href, onSelect, value, ...attrs }) {
// const handleClick = useCallback(() => {
// onSelect && onSelect(value, label);
// }, [onSelect, value, label]);
// const Element = href ? 'a' : 'div';
// return (
// <Element
// className={`flex space-x-2 p-2 px-5 hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white cursor-pointer ${
// focus ? 'bg-gray-200 dark:bg-gray-800 dark:text-white' : ''
// }`}
// href={href}
// onClick={handleClick}
// role="option"
// {...attrs}
// >
// {Icon ? (
// <div className="w-6 h-6 self-center mr-4 text-gray-500 flex-shrink-0">
// <Icon />
// </div>
// ) : null}
// <div className="whitespace-nowrap">{label}</div>
// </Element>
// );
// }
// export function MenuSeparator() {
// return <div className="border-b border-gray-200 dark:border-gray-800 my-2" />;
// }

View File

@ -1,72 +0,0 @@
// import { h } from 'preact';
// import { useRef, useState } from 'preact/hooks';
// import Menu from './Menu';
// import { ArrowDropdown } from '../icons/ArrowDropdown';
// import Heading from './Heading';
// import Button from './Button';
// import SelectOnlyIcon from '../icons/SelectOnly';
export {}
// export default function MultiSelect({ className, title, options, selection, onToggle, onShowAll, onSelectSingle }) {
// const popupRef = useRef(null);
// const [state, setState] = useState({
// showMenu: false,
// });
// const isOptionSelected = (item) => {
// return selection == 'all' || selection.split(',').indexOf(item) > -1;
// };
// const menuHeight = Math.round(window.innerHeight * 0.55);
// return (
// <div className={`${className} p-2`} ref={popupRef}>
// <div className="flex justify-between min-w-[120px]" onClick={() => setState({ showMenu: true })}>
// <label>{title}</label>
// <ArrowDropdown className="w-6" />
// </div>
// {state.showMenu ? (
// <Menu
// className={`max-h-[${menuHeight}px] overflow-auto`}
// relativeTo={popupRef}
// onDismiss={() => setState({ showMenu: false })}
// >
// <div className="flex flex-wrap justify-between items-center">
// <Heading className="p-4 justify-center" size="md">
// {title}
// </Heading>
// <Button tabindex="false" className="mx-4" onClick={() => onShowAll()}>
// Show All
// </Button>
// </div>
// {options.map((item) => (
// <div className="flex flex-grow" key={item}>
// <label
// className={`flex flex-shrink space-x-2 p-1 my-1 min-w-[176px] hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white cursor-pointer capitalize text-sm`}
// >
// <input
// className="mx-4 m-0 align-middle"
// type="checkbox"
// checked={isOptionSelected(item)}
// onChange={() => onToggle(item)}
// />
// {item.replaceAll('_', ' ')}
// </label>
// <div className="justify-right">
// <Button
// color={isOptionSelected(item) ? 'blue' : 'black'}
// type="text"
// className="max-h-[35px] mx-2"
// onClick={() => onSelectSingle(item)}
// >
// { ( <SelectOnlyIcon /> ) }
// </Button>
// </div>
// </div>
// ))}
// </Menu>
// ) : null}
// </div>
// );
// }

View File

@ -1,43 +0,0 @@
// import { h } from 'preact';
// import { useCallback, useState } from 'preact/hooks';
export {}
// export function Tabs({ children, selectedIndex: selectedIndexProp, onChange, className }) {
// const [selectedIndex, setSelectedIndex] = useState(selectedIndexProp);
// const handleSelected = useCallback(
// (index) => () => {
// setSelectedIndex(index);
// onChange && onChange(index);
// },
// [onChange]
// );
// const RenderChildren = useCallback(() => {
// return children.map((child, i) => {
// child.props.selected = i === selectedIndex;
// child.props.onClick = handleSelected(i);
// return child;
// });
// }, [selectedIndex, children, handleSelected]);
// return (
// <div className={`flex ${className}`}>
// <RenderChildren />
// </div>
// );
// }
// export function TextTab({ selected, text, onClick, disabled }) {
// const selectedStyle = disabled
// ? 'text-gray-400 dark:text-gray-600 bg-transparent'
// : selected
// ? 'text-white bg-blue-500 dark:text-black dark:bg-white'
// : 'text-black dark:text-white bg-transparent';
// return (
// <button onClick={onClick} disabled={disabled} className={`rounded-full px-4 py-2 ${selectedStyle}`}>
// <span>{text}</span>
// </button>
// );
// }

View File

@ -1,85 +0,0 @@
// import { FunctionComponent, useEffect, useMemo, useState } from 'react';
// interface IProp {
// /** The time to calculate time-ago from */
// time: Date;
// /** OPTIONAL: overwrite current time */
// currentTime?: Date;
// /** OPTIONAL: boolean that determines whether to show the time-ago text in dense format */
// dense?: boolean;
// /** OPTIONAL: set custom refresh interval in milliseconds, default 1000 (1 sec) */
// refreshInterval?: number;
// }
export {}
// type TimeUnit = {
// unit: string;
// full: string;
// value: number;
// };
// const timeAgo = ({ time, currentTime = new Date(), dense = false }: IProp): string => {
// if (typeof time !== 'number' || time < 0) return 'Invalid Time Provided';
// const pastTime: Date = new Date(time);
// const elapsedTime: number = currentTime.getTime() - pastTime.getTime();
// const timeUnits: TimeUnit[] = [
// { unit: 'yr', full: 'year', value: 31536000 },
// { unit: 'mo', full: 'month', value: 0 },
// { unit: 'd', full: 'day', value: 86400 },
// { unit: 'h', full: 'hour', value: 3600 },
// { unit: 'm', full: 'minute', value: 60 },
// { unit: 's', full: 'second', value: 1 },
// ];
// const elapsed: number = elapsedTime / 1000;
// if (elapsed < 10) {
// return 'just now';
// }
// for (let i = 0; i < timeUnits.length; i++) {
// // if months
// if (i === 1) {
// // Get the month and year for the time provided
// const pastMonth = pastTime.getUTCMonth();
// const pastYear = pastTime.getUTCFullYear();
// // get current month and year
// const currentMonth = currentTime.getUTCMonth();
// const currentYear = currentTime.getUTCFullYear();
// let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
// // check if the time provided is the previous month but not exceeded 1 month ago.
// if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
// monthDiff--;
// }
// if (monthDiff > 0) {
// const unitAmount = monthDiff;
// return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
// }
// } else if (elapsed >= timeUnits[i].value) {
// const unitAmount: number = Math.floor(elapsed / timeUnits[i].value);
// return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
// }
// }
// return 'Invalid Time';
// };
// const TimeAgo: FunctionComponent<IProp> = ({ refreshInterval = 1000, ...rest }): JSX.Element => {
// const [currentTime, setCurrentTime] = useState<Date>(new Date());
// useEffect(() => {
// const intervalId: NodeJS.Timeout = setInterval(() => {
// setCurrentTime(new Date());
// }, refreshInterval);
// return () => clearInterval(intervalId);
// }, [refreshInterval]);
// const timeAgoValue = useMemo(() => timeAgo({ currentTime, ...rest }), [currentTime, rest]);
// return <span>{timeAgoValue}</span>;
// };
// export default TimeAgo;

View File

@ -1,67 +0,0 @@
// import { Fragment, h } from 'preact';
// import { useState } from 'preact/hooks';
export {}
// export default function TimelineEventOverlay({ eventOverlay, cameraConfig }) {
// const boxLeftEdge = Math.round(eventOverlay.data.box[0] * 100);
// const boxTopEdge = Math.round(eventOverlay.data.box[1] * 100);
// const boxRightEdge = Math.round((1 - eventOverlay.data.box[2] - eventOverlay.data.box[0]) * 100);
// const boxBottomEdge = Math.round((1 - eventOverlay.data.box[3] - eventOverlay.data.box[1]) * 100);
// const [isHovering, setIsHovering] = useState(false);
// const getHoverStyle = () => {
// if (boxLeftEdge < 15) {
// // show object stats on right side
// return {
// left: `${boxLeftEdge + eventOverlay.data.box[2] * 100 + 1}%`,
// top: `${boxTopEdge}%`,
// };
// }
// return {
// right: `${boxRightEdge + eventOverlay.data.box[2] * 100 + 1}%`,
// top: `${boxTopEdge}%`,
// };
// };
// const getObjectArea = () => {
// const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
// const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
// return Math.round(width * height);
// };
// const getObjectRatio = () => {
// const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
// const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
// return Math.round(100 * (width / height)) / 100;
// };
// return (
// <Fragment>
// <div
// className="absolute border-4 border-red-600"
// onMouseEnter={() => setIsHovering(true)}
// onMouseLeave={() => setIsHovering(false)}
// onTouchStart={() => setIsHovering(true)}
// onTouchEnd={() => setIsHovering(false)}
// style={{
// left: `${boxLeftEdge}%`,
// top: `${boxTopEdge}%`,
// right: `${boxRightEdge}%`,
// bottom: `${boxBottomEdge}%`,
// }}
// >
// {eventOverlay.class_type == 'entered_zone' ? (
// <div className="absolute w-2 h-2 bg-yellow-500 left-[50%] -translate-x-1/2 translate-y-3/4 bottom-0" />
// ) : null}
// </div>
// {isHovering && (
// <div className="absolute bg-white dark:bg-slate-800 p-4 block text-black dark:text-white text-lg" style={getHoverStyle()}>
// <div>{`Area: ${getObjectArea()} px`}</div>
// <div>{`Ratio: ${getObjectRatio()}`}</div>
// </div>
// )}
// </Fragment>
// );
// }

View File

@ -1,220 +0,0 @@
// import { h } from 'preact';
// import useSWR from 'swr';
// import ActivityIndicator from './ActivityIndicator';
// import { formatUnixTimestampToDateTime } from '../utils/dateUtil';
// import About from '../icons/About';
// import ActiveObjectIcon from '../icons/ActiveObject';
// import PlayIcon from '../icons/Play';
// import ExitIcon from '../icons/Exit';
// import StationaryObjectIcon from '../icons/StationaryObject';
// import FaceIcon from '../icons/Face';
// import LicensePlateIcon from '../icons/LicensePlate';
// import DeliveryTruckIcon from '../icons/DeliveryTruck';
// import ZoneIcon from '../icons/Zone';
// import { useMemo, useState } from 'preact/hooks';
// import Button from './Button';
export {}
// export default function TimelineSummary({ event, onFrameSelected }) {
// const { data: eventTimeline } = useSWR([
// 'timeline',
// {
// source_id: event.id,
// },
// ]);
// const { data: config } = useSWR('config');
// const annotationOffset = useMemo(() => {
// if (!config) {
// return 0;
// }
// return (config.cameras[event.camera]?.detect?.annotation_offset || 0) / 1000;
// }, [config, event]);
// const [timeIndex, setTimeIndex] = useState(-1);
// const recordingParams = useMemo(() => {
// if (!event.end_time) {
// return {
// after: event.start_time,
// };
// }
// return {
// before: event.end_time,
// after: event.start_time,
// };
// }, [event]);
// const { data: recordings } = useSWR([`${event.camera}/recordings`, recordingParams], { revalidateOnFocus: false });
// // calculates the seek seconds by adding up all the seconds in the segments prior to the playback time
// const getSeekSeconds = (seekUnix) => {
// if (!recordings) {
// return 0;
// }
// let seekSeconds = 0;
// recordings.every((segment) => {
// // if the next segment is past the desired time, stop calculating
// if (segment.start_time > seekUnix) {
// return false;
// }
// if (segment.end_time < seekUnix) {
// seekSeconds += segment.end_time - segment.start_time;
// return true;
// }
// seekSeconds += segment.end_time - segment.start_time - (segment.end_time - seekUnix);
// return true;
// });
// return seekSeconds;
// };
// const onSelectMoment = async (index) => {
// setTimeIndex(index);
// onFrameSelected(eventTimeline[index], getSeekSeconds(eventTimeline[index].timestamp + annotationOffset));
// };
// if (!eventTimeline || !config) {
// return <ActivityIndicator />;
// }
// if (eventTimeline.length == 0) {
// return <div />;
// }
// return (
// <div className="flex flex-col">
// <div className="h-14 flex justify-center">
// <div className="flex flex-row flex-nowrap justify-between overflow-auto">
// {eventTimeline.map((item, index) => (
// <Button
// key={index}
// className="rounded-full"
// type="iconOnly"
// color={index == timeIndex ? 'blue' : 'gray'}
// aria-label={window.innerWidth > 640 ? getTimelineItemDescription(config, item, event) : ''}
// onClick={() => onSelectMoment(index)}
// >
// {getTimelineIcon(item)}
// </Button>
// ))}
// </div>
// </div>
// {timeIndex >= 0 ? (
// <div className="m-2 max-w-md self-center">
// <div className="flex justify-start">
// <div className="text-lg flex justify-between py-4">Bounding boxes may not align</div>
// <Button
// className="rounded-full"
// type="text"
// color="gray"
// aria-label=" Disclaimer: This data comes from the detect feed but is shown on the recordings, it is unlikely that the
// streams are perfectly in sync so the bounding box and the footage will not line up perfectly. The annotation_offset field can be used to adjust this."
// >
// <About className="w-4" />
// </Button>
// </div>
// </div>
// ) : null}
// </div>
// );
// }
// function getTimelineIcon(timelineItem) {
// switch (timelineItem.class_type) {
// case 'visible':
// return <PlayIcon className="w-8" />;
// case 'gone':
// return <ExitIcon className="w-8" />;
// case 'active':
// return <ActiveObjectIcon className="w-8" />;
// case 'stationary':
// return <StationaryObjectIcon className="w-8" />;
// case 'entered_zone':
// return <ZoneIcon className="w-8" />;
// case 'attribute':
// switch (timelineItem.data.attribute) {
// case 'face':
// return <FaceIcon className="w-8" />;
// case 'license_plate':
// return <LicensePlateIcon className="w-8" />;
// default:
// return <DeliveryTruckIcon className="w-8" />;
// }
// case 'sub_label':
// switch (timelineItem.data.label) {
// case 'person':
// return <FaceIcon className="w-8" />;
// case 'car':
// return <LicensePlateIcon className="w-8" />;
// }
// }
// }
// function getTimelineItemDescription(config, timelineItem, event) {
// switch (timelineItem.class_type) {
// case 'visible':
// return `${event.label} detected at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// })}`;
// case 'entered_zone':
// return `${event.label.replaceAll('_', ' ')} entered ${timelineItem.data.zones
// .join(' and ')
// .replaceAll('_', ' ')} at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// })}`;
// case 'active':
// return `${event.label} became active at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// })}`;
// case 'stationary':
// return `${event.label} became stationary at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// })}`;
// case 'attribute': {
// let title = "";
// if (timelineItem.data.attribute == 'face' || timelineItem.data.attribute == 'license_plate') {
// title = `${timelineItem.data.attribute.replaceAll("_", " ")} detected for ${event.label}`;
// } else {
// title = `${event.label} recognized as ${timelineItem.data.attribute.replaceAll("_", " ")}`
// }
// return `${title} at ${formatUnixTimestampToDateTime(
// timelineItem.timestamp,
// {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// }
// )}`;
// }
// case 'sub_label':
// return `${event.label} recognized as ${timelineItem.data.sub_label} at ${formatUnixTimestampToDateTime(
// timelineItem.timestamp,
// {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// }
// )}`;
// case 'gone':
// return `${event.label} left at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
// date_style: 'short',
// time_style: 'medium',
// time_format: config.ui.time_format,
// })}`;
// }
// }

View File

@ -1,64 +0,0 @@
// import { createPortal } from 'react'; // TODO implement
// import { useLayoutEffect, useRef, useState } from 'react';
// const TIP_SPACE = 20;
export {}
// export default function Tooltip({ relativeTo, text, capitalize }) {
// const [position, setPosition] = useState({ top: -9999, left: -9999 });
// const portalRoot = document.getElementById('tooltips');
// const ref = useRef();
// useLayoutEffect(() => {
// if (ref && ref.current && relativeTo && relativeTo.current) {
// const windowWidth = window.innerWidth;
// const {
// x: relativeToX,
// y: relativeToY,
// width: relativeToWidth,
// height: relativeToHeight,
// } = relativeTo.current.getBoundingClientRect();
// const { width: _tipWidth, height: _tipHeight } = ref.current.getBoundingClientRect();
// const tipWidth = _tipWidth * 1.1;
// const tipHeight = _tipHeight * 1.1;
// const left = relativeToX + Math.round(relativeToWidth / 2) + window.scrollX;
// const top = relativeToY + Math.round(relativeToHeight / 2) + window.scrollY;
// let newTop = top - TIP_SPACE - tipHeight;
// let newLeft = left - Math.round(tipWidth / 2);
// // too far right
// if (newLeft + tipWidth + TIP_SPACE > windowWidth - window.scrollX) {
// newLeft = Math.max(0, left - tipWidth - TIP_SPACE);
// newTop = top - Math.round(tipHeight / 2);
// }
// // too far left
// else if (newLeft < TIP_SPACE + window.scrollX) {
// newLeft = left + TIP_SPACE;
// newTop = top - Math.round(tipHeight / 2);
// }
// // too close to top
// else if (newTop <= TIP_SPACE + window.scrollY) {
// newTop = top + tipHeight + TIP_SPACE;
// }
// setPosition({ left: newLeft, top: newTop });
// }
// }, [relativeTo, ref]);
// const tooltip = (
// <div
// role="tooltip"
// className={`shadow max-w-lg absolute pointer-events-none bg-gray-900 dark:bg-gray-200 bg-opacity-80 rounded px-2 py-1 transition-transform transition-opacity duration-75 transform scale-90 opacity-0 text-gray-100 dark:text-gray-900 text-sm ${
// capitalize ? 'capitalize' : ''
// } ${position.top >= 0 ? 'opacity-100 scale-100' : ''}`}
// ref={ref}
// style={position}
// >
// {text}
// </div>
// );
// return portalRoot ? createPortal(tooltip, portalRoot) : tooltip;
// }

View File

@ -1,81 +0,0 @@
// import * as React from "react"
export {}
// const Card = React.forwardRef<
// HTMLDivElement,
// React.HTMLAttributes<HTMLDivElement>
// >(({ className, ...props }, ref) => (
// <div
// ref={ref}
// // className={cn(
// // "rounded-lg border bg-card text-card-foreground shadow-sm",
// // className
// // )}
// {...props}
// />
// ))
// Card.displayName = "Card"
// const CardHeader = React.forwardRef<
// HTMLDivElement,
// React.HTMLAttributes<HTMLDivElement>
// >(({ className, ...props }, ref) => (
// <div
// ref={ref}
// // className={cn("flex flex-col space-y-1.5 p-6", className)}
// {...props}
// />
// ))
// CardHeader.displayName = "CardHeader"
// const CardTitle = React.forwardRef<
// HTMLParagraphElement,
// React.HTMLAttributes<HTMLHeadingElement>
// >(({ className, ...props }, ref) => (
// <h3
// ref={ref}
// // className={cn(
// // "text-2xl font-semibold leading-none tracking-tight",
// // className
// // )}
// {...props}
// />
// ))
// CardTitle.displayName = "CardTitle"
// const CardDescription = React.forwardRef<
// HTMLParagraphElement,
// React.HTMLAttributes<HTMLParagraphElement>
// >(({ className, ...props }, ref) => (
// <p
// ref={ref}
// // className={cn("text-sm text-muted-foreground", className)}
// {...props}
// />
// ))
// CardDescription.displayName = "CardDescription"
// const CardContent = React.forwardRef<
// HTMLDivElement,
// React.HTMLAttributes<HTMLDivElement>
// >(({ className, ...props }, ref) => (
// <div ref={ref}
// // className= {cn("p-6 pt-0", className)}
// {...props} />
// ))
// CardContent.displayName = "CardContent"
// const CardFooter = React.forwardRef<
// HTMLDivElement,
// React.HTMLAttributes<HTMLDivElement>
// >(({ className, ...props }, ref) => (
// <div
// ref={ref}
// // className={cn("flex items-center p-6 pt-0", className)}
// {...props}
// />
// ))
// CardFooter.displayName = "CardFooter"
// export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
import ColorSchemeToggle from "../../shared/components/ColorSchemeToggle"; import ColorSchemeToggle from "../../shared/components/ColorSchemeToggle";
import Logo from "../../shared/components/Logo"; import Logo from "../../shared/components/Logo";
import { routesPath } from "../../router/routes.path"; import { routesPath } from "../../router/routes.path";
import DrawerMenu from "../../shared/components/DrawerMenu";
const HEADER_HEIGHT = rem(60) const HEADER_HEIGHT = rem(60)
@ -39,6 +40,7 @@ const useStyles = createStyles((theme) => ({
}, },
}, },
// TODO delete
burger: { burger: {
[theme.fn.largerThan('sm')]: { [theme.fn.largerThan('sm')]: {
display: 'none', display: 'none',
@ -46,62 +48,54 @@ const useStyles = createStyles((theme) => ({
}, },
colorToggle: { colorToggle: {
[theme.fn.smallerThan('md')]: {display:'none'} [theme.fn.smallerThan('md')]: { display: 'none' }
} }
})) }))
export interface LinkItem {
label: string
link: string
}
export interface HeaderActionProps { export interface HeaderActionProps {
links: { link: string; label: string; links: { link: string; label: string }[] }[], links: LinkItem[],
logo?: JSX.Element logo?: JSX.Element
} }
export const HeaderAction = ({ links }: HeaderActionProps) => { export const HeaderAction = ({ links }: HeaderActionProps) => {
const { classes } = useStyles(); const { classes } = useStyles();
const navigate = useNavigate() const navigate = useNavigate()
const [opened, { toggle }] = useDisclosure(false)
const isMiddleScreen = useMediaQuery('md')
const auth = useAuth() const auth = useAuth()
const handleNavigate = (link: string) => { const handleNavigate = (link: string) => {
navigate(link) navigate(link)
} }
const items = links.map((link) => { const items = links.map(item =>
const menuItems = link.links?.map((item) => ( <Menu key={item.label} trigger="hover" transitionProps={{ exitDuration: 0 }} withinPortal>
<Menu.Item key={item.link}>{item.label}</Menu.Item> <Menu.Target>
)) <Button variant="subtle" uppercase onClick={() => handleNavigate(item.link)}>
{item.label}
if (menuItems) { </Button>
return ( </Menu.Target>
<Menu key={link.label} trigger="hover" transitionProps={{ exitDuration: 0 }} withinPortal> </Menu>
<Menu.Target> )
<Button variant="subtle" uppercase onClick={() => handleNavigate(link.link)}>
{link.label}
</Button>
</Menu.Target>
</Menu>
)
}
return (
null
)
})
return ( return (
<Header height={HEADER_HEIGHT} sx={{ borderBottom: 0 }}> <Header height={HEADER_HEIGHT} sx={{ borderBottom: 0 }}>
<Container className={classes.inner} fluid> <Container className={classes.inner} fluid>
<Flex wrap='nowrap' > <Flex wrap='nowrap' >
<Logo onClick={() => handleNavigate(routesPath.MAIN_PATH)} /> <Logo onClick={() => handleNavigate(routesPath.MAIN_PATH)} />
<Burger opened={opened} onClick={toggle} className={classes.burger} size="sm" /> {/* <Burger opened={opened} onClick={toggle} className={classes.burger} size="sm" /> */}
<DrawerMenu links={links} />
<Flex className={classes.leftLinksMenu}> <Flex className={classes.leftLinksMenu}>
{ items } {items}
</Flex> </Flex>
</Flex> </Flex>
<Container className={classes.centerLinksMenu}> <Container className={classes.centerLinksMenu}>
{items} {items}
</Container> </Container>
<Group position="right"> <Group position="right">
<ColorSchemeToggle className={classes.colorToggle} /> <ColorSchemeToggle className={classes.colorToggle} />
<UserMenu user={{ name: auth.user?.profile.preferred_username || "", image: "" }} /> <UserMenu user={{ name: auth.user?.profile.preferred_username || "", image: "" }} />

View File

@ -1,15 +1,12 @@
import { routesPath } from "../../router/routes.path"; import { routesPath } from "../../router/routes.path";
import { headerMenu } from "../../shared/strings/header.menu.strings"; import { headerMenu } from "../../shared/strings/header.menu.strings";
import { HeaderActionProps } from "./HeaderAction"; import { HeaderActionProps, LinkItem } from "./HeaderAction";
export const testHeaderLinks: HeaderActionProps = export const headerLinks: LinkItem[] = [
{ { link: routesPath.MAIN_PATH, label: headerMenu.home },
links: [ { link: routesPath.TEST_PATH, label: headerMenu.test },
{link: routesPath.MAIN_PATH, label: headerMenu.home, links: []}, { link: routesPath.SETTINGS_PATH, label: headerMenu.settings },
{link: routesPath.TEST_PATH, label: headerMenu.test, links: []}, { link: routesPath.RECORDINGS_PATH, label: headerMenu.recordings },
{link: routesPath.SETTINGS_PATH, label: headerMenu.settings, links: []}, { link: routesPath.HOSTS_PATH, label: headerMenu.hostsConfig },
{link: routesPath.RECORDINGS_PATH, label: headerMenu.recordings, links: []}, { link: routesPath.ACCESS_PATH, label: headerMenu.acessSettings },
{link: routesPath.HOSTS_PATH, label: headerMenu.hostsConfig, links: []}, ]
{link: routesPath.ACCESS_PATH, label: headerMenu.acessSettings, links: []},
]
}