refactor(ui): Remove non-mobile sidebar and ad header links
Some checks are pending
Publish Docker image to Dockerhub / test (push) Waiting to run
Publish Docker image to Dockerhub / Build OCI Images (push) Blocked by required conditions
Publish Docker image to Dockerhub / Build OCI Images-1 (push) Blocked by required conditions
Publish Docker image to Dockerhub / Merge OCI Images and Push (push) Blocked by required conditions

* Remove non-mobile sidebar to free up some more screen real estate
* Add quick Links menu to header that hides on mobile breakpoint
This commit is contained in:
FoxxMD 2026-07-08 16:49:37 +00:00
parent be872d5ce1
commit f431f4d5af
6 changed files with 78 additions and 31 deletions

View file

@ -13,10 +13,8 @@ import { Provider } from './components/Provider';
import { Container, Box, Center, Splitter, useSplitter } from '@chakra-ui/react'; import { Container, Box, Center, Splitter, useSplitter } from '@chakra-ui/react';
import { MsSseEvent } from '../core/Api'; import { MsSseEvent } from '../core/Api';
import { SSEProvider } from "@flamefrontend/sse-runtime-react"; import { SSEProvider } from "@flamefrontend/sse-runtime-react";
import { AppHeader, RightHeaderFloatingLogs, RightHeaderSwitchLogs } from './components/AppHeader'; import { AppHeader } from './components/AppHeader';
import { NAV_LINKS, SideNavItems } from './components/SideNav'; import { NAV_LINKS, SideNavItems } from './components/SideNav';
import { LogsFetchable } from './components/LogsNext';
import { SplitLayout } from './components/layouts/SplitLayout';
import { ComponentDetailedRoutable } from './components/msComponent/MSComponentDetailed'; import { ComponentDetailedRoutable } from './components/msComponent/MSComponentDetailed';
import { MSErrorBoundary } from './components/ErrorBoundary'; import { MSErrorBoundary } from './components/ErrorBoundary';
@ -60,9 +58,11 @@ const Layout = () => {
//const [logsEnabled, setLogsEnabled] = useState(true); //const [logsEnabled, setLogsEnabled] = useState(true);
const location = useLocation(); const location = useLocation();
return (<> return (<>
<Box px="4" py="2" mb="4" pb="4" position="sticky" top="0" zIndex="1" bg="bg" borderBottomWidth="1px"><AppHeader fetchable><RightHeaderFloatingLogs streamable/></AppHeader></Box> <Box px="4" py="2" mb="4" pb="4" position="sticky" top="0" zIndex="1" bg="bg" borderBottomWidth="1px">
<AppHeader fetchable/>
</Box>
<Container display="flex"> <Container display="flex">
<Box hideBelow="md" display="flex" flexDir="column" pr="2" gap="6" flexShrink="1"><SideNavItems items={NAV_LINKS} currentUrl={location.pathname}/></Box> <Box hideBelow="md" display="flex" flexDir="column" pr="2" gap="6" flexShrink="1"></Box>
<Outlet/> <Outlet/>
</Container> </Container>
</>); </>);

View file

@ -17,6 +17,7 @@ import { ToggleTip } from "./ToggleTip";
import { ErrorLike } from "../../core/Atomic"; import { ErrorLike } from "../../core/Atomic";
import { ErrorAlert } from "./ErrorAlert"; import { ErrorAlert } from "./ErrorAlert";
import { MSErrorBoundary } from "./ErrorBoundary"; import { MSErrorBoundary } from "./ErrorBoundary";
import { ExternaLinksMenu } from "./ExternaLinksMenu";
export const AppTitle = (props: { fetchable?: boolean } = {}) => { export const AppTitle = (props: { fetchable?: boolean } = {}) => {
const { const {
@ -121,8 +122,7 @@ export const SSEStatus = (props: {live?: boolean, status?: ReturnType<typeof use
export const RightHeaderFloatingLogs = (props: {streamable?: boolean}) => { export const RightHeaderFloatingLogs = (props: {streamable?: boolean}) => {
const [width, height] = useWindowSize(); const [width, height] = useWindowSize();
return <HStack gap="2"> return (
<Box marginRight="2"><SSEStatus live={props.streamable}/></Box>
<FloatingPanel.Root <FloatingPanel.Root
defaultPosition={{x: width * 0.03, y: height * 0.65}} defaultPosition={{x: width * 0.03, y: height * 0.65}}
defaultSize={{ width: width * 0.95, height: height * 0.3 }} defaultSize={{ width: width * 0.95, height: height * 0.3 }}
@ -160,14 +160,18 @@ export const RightHeaderFloatingLogs = (props: {streamable?: boolean}) => {
</FloatingPanel.Positioner> </FloatingPanel.Positioner>
</Portal> </Portal>
</FloatingPanel.Root> </FloatingPanel.Root>
</HStack> );
} }
export const AppHeader = (props: PropsWithChildren<{ fetchable?: boolean }>) => { export const AppHeader = (props: PropsWithChildren<{ fetchable?: boolean }>) => {
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<AppTitle fetchable={props.fetchable} /> <AppTitle fetchable={props.fetchable} />
<Flex justify="flex-start" alignItems="flex-end">{props.children}</Flex> <Flex justify="flex-start" gap="1" alignItems="flex-end">
<ExternaLinksMenu hideBelow="sm"/>
<Box marginRight="2"><SSEStatus live={props.fetchable}/></Box>
<RightHeaderFloatingLogs streamable={props.fetchable}/>
</Flex>
</Flex> </Flex>
) )
} }

View file

@ -0,0 +1,30 @@
import { Button, Menu, Portal, Box, ChakraComponent } from "@chakra-ui/react"
import { ExternalLinkIcon } from "./icons/ChakraIcons"
import { EXTERNAL_LINKS } from "./SideNav"
import { ComponentProps } from "react"
export const ExternaLinksMenu = (props: ComponentProps<typeof Box> = {}) => (
<Box {...props}>
<Menu.Root>
<Menu.Trigger asChild>
<Button size="sm" variant="ghost">
Links
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
{EXTERNAL_LINKS.items.map((link) => (
<Menu.Item key={link.url} asChild value={link.title} cursor="pointer">
<a href={link.url} target="_blank" rel="noreferrer">
{link.title} <ExternalLinkIcon />
</a>
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
</Box>
)

View file

@ -12,9 +12,6 @@ import { NAV_LINKS, SideNavItems } from "./SideNav";
const MobileMenuButton = MenuButton; const MobileMenuButton = MenuButton;
export const MobileSidebarNav = (props: { hideFrom?: BreakpointName | false } = {}) => { export const MobileSidebarNav = (props: { hideFrom?: BreakpointName | false } = {}) => {
const {
hideFrom = 'md'
} = props;
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
let location = useLocation(); let location = useLocation();
@ -22,11 +19,11 @@ export const MobileSidebarNav = (props: { hideFrom?: BreakpointName | false } =
const closeMenu = () => setIsOpen(false) const closeMenu = () => setIsOpen(false)
const menuButtonProps: ComponentProps<typeof MobileMenuButton> = { const menuButtonProps: ComponentProps<typeof MobileMenuButton> = {
variant: 'ghost' variant: 'ghost',
iconProps: {
size: 'lg'
}
}; };
if (hideFrom !== false) {
menuButtonProps.hideFrom = hideFrom;
}
useEffect(() => { useEffect(() => {
setIsOpen(false); setIsOpen(false);

View file

@ -104,18 +104,7 @@ const StatusBadge = (props: BadgeProps) => (
/> />
) )
export const NAV_LINKS: SideNavProps[] = [ export const EXTERNAL_LINKS: SideNavProps = {
{
title: 'Main',
id: 'Main',
items: [
{
title: 'Dashboard',
url: '/next/'
}
]
},
{
title: 'Links', title: 'Links',
id: 'Links', id: 'Links',
items: [ items: [
@ -135,5 +124,18 @@ export const NAV_LINKS: SideNavProps[] = [
external: true external: true
} }
] ]
} }
]
export const NAV_LINKS: SideNavProps[] = [
{
title: 'Main',
id: 'Main',
items: [
{
title: 'Dashboard',
url: '/next/'
}
]
},
EXTERNAL_LINKS
];

View file

@ -48,6 +48,20 @@ export const makeIconButton = (Icon: IconType) => (props: PropsWithChildren<Comp
</IconButton> </IconButton>
); );
} }
export const makeChakraIconButton = (IconBase: IconType) => (props: PropsWithChildren<ComponentProps<typeof IconButton>> & { iconProps?: ComponentProps<typeof Icon>, loading?: boolean }) => {
const {
iconProps = {},
children,
loading = false,
size = 'xs',
...rest
} = props;
return (
<IconButton variant="surface" disabled={loading} size={size} {...rest}>
{loading ? <Spinner/> : <Icon {...iconProps}><IconBase/></Icon>}{children}
</IconButton>
);
}
export const makeChakraIcon = (IconComponent: IconType) => (props: ComponentProps<typeof Icon> & { iconProps?: IconBaseProps }) => { export const makeChakraIcon = (IconComponent: IconType) => (props: ComponentProps<typeof Icon> & { iconProps?: IconBaseProps }) => {
const { const {
iconProps, iconProps,
@ -95,7 +109,7 @@ export const TerminalButton = (props: ComponentProps<typeof IconButton>) => (
); );
export const MenuIcon = LuAlignJustify; export const MenuIcon = LuAlignJustify;
export const MenuButton = makeIconButton(MenuIcon); export const MenuButton = makeChakraIconButton(MenuIcon);
export const XIcon = LuX; export const XIcon = LuX;
export const XButton = makeIconButton(XIcon); export const XButton = makeIconButton(XIcon);