Move settings nav into SideNav, matching WebsiteNav pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mike Cao 2026-02-07 22:00:47 -08:00
parent 04c7216928
commit 08e4da27c1
4 changed files with 79 additions and 29 deletions

View file

@ -10,6 +10,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from '@umami/react-zen'; } from '@umami/react-zen';
import Link from 'next/link'; import Link from 'next/link';
import { SettingsNav } from '@/app/(main)/settings/SettingsNav';
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav'; import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
import { IconLabel } from '@/components/common/IconLabel'; import { IconLabel } from '@/components/common/IconLabel';
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks'; import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
@ -83,6 +84,8 @@ export function SideNav(props: any) {
</Row> </Row>
{websiteId ? ( {websiteId ? (
<WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} /> <WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} />
) : pathname.includes('/settings') ? (
<SettingsNav isCollapsed={isCollapsed} />
) : ( ) : (
<Column gap="2"> <Column gap="2">
{links.map(({ id, path, label, icon }) => { {links.map(({ id, path, label, icon }) => {

View file

@ -1,25 +1,12 @@
'use client'; 'use client';
import { Column, Grid } from '@umami/react-zen'; import { Column } from '@umami/react-zen';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { PageBody } from '@/components/common/PageBody'; import { PageBody } from '@/components/common/PageBody';
import { SettingsNav } from './SettingsNav';
export function SettingsLayout({ children }: { children: ReactNode }) { export function SettingsLayout({ children }: { children: ReactNode }) {
return ( return (
<Grid columns={{ base: '1fr', lg: 'auto 1fr' }} width="100%" height="100%"> <Column gap="6" margin="2" width="100%">
<Column <PageBody>{children}</PageBody>
display={{ base: 'none', lg: 'flex' }} </Column>
width="240px"
height="100%"
border="right"
marginRight="2"
padding="3"
>
<SettingsNav />
</Column>
<Column gap="6" margin="2">
<PageBody>{children}</PageBody>
</Column>
</Grid>
); );
} }

View file

@ -1,8 +1,16 @@
import { NavMenu } from '@/components/common/NavMenu'; import { Column, Focusable, Row, Text, Tooltip, TooltipTrigger } from '@umami/react-zen';
import Link from 'next/link';
import { IconLabel } from '@/components/common/IconLabel';
import { useMessages, useNavigation } from '@/components/hooks'; import { useMessages, useNavigation } from '@/components/hooks';
import { Settings2, UserCircle, Users } from '@/components/icons'; import { ArrowLeft, Settings2, UserCircle, Users } from '@/components/icons';
export function SettingsNav({ onItemClick }: { onItemClick?: () => void }) { export function SettingsNav({
isCollapsed,
onItemClick,
}: {
isCollapsed?: boolean;
onItemClick?: () => void;
}) {
const { t, labels } = useMessages(); const { t, labels } = useMessages();
const { renderUrl, pathname } = useNavigation(); const { renderUrl, pathname } = useNavigation();
@ -42,12 +50,57 @@ export function SettingsNav({ onItemClick }: { onItemClick?: () => void }) {
.find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id; .find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id;
return ( return (
<NavMenu <Column gap="2">
items={items} <Link href={renderUrl('/boards', false)} role="button" onClick={onItemClick}>
title={t(labels.settings)} <TooltipTrigger isDisabled={!isCollapsed} delay={0}>
selectedKey={selectedKey} <Focusable>
allowMinimize={false} <Row
onItemClick={onItemClick} alignItems="center"
/> hover={{ backgroundColor: 'surface-sunken' }}
borderRadius
minHeight="40px"
>
<IconLabel icon={<ArrowLeft />} label={isCollapsed ? '' : t(labels.back)} padding />
</Row>
</Focusable>
<Tooltip placement="right">{t(labels.back)}</Tooltip>
</TooltipTrigger>
</Link>
{items.map(({ label: sectionLabel, items: sectionItems }, index) => (
<Column key={`${sectionLabel}${index}`} gap="1" marginBottom="1">
{!isCollapsed && (
<Row padding>
<Text weight="bold">{sectionLabel}</Text>
</Row>
)}
{sectionItems.map(({ id, path, label, icon }) => {
const isSelected = selectedKey === id;
return (
<Link key={id} href={path} role="button" onClick={onItemClick}>
<TooltipTrigger isDisabled={!isCollapsed} delay={0}>
<Focusable>
<Row
alignItems="center"
hover={{ backgroundColor: 'surface-sunken' }}
backgroundColor={isSelected ? 'surface-sunken' : undefined}
borderRadius
minHeight="40px"
>
<IconLabel
icon={icon}
label={isCollapsed ? '' : label}
weight={isSelected ? 'bold' : undefined}
padding
/>
</Row>
</Focusable>
<Tooltip placement="right">{label}</Tooltip>
</TooltipTrigger>
</Link>
);
})}
</Column>
))}
</Column>
); );
} }

View file

@ -47,9 +47,16 @@ export function TeamsButton() {
return ( return (
<MenuTrigger> <MenuTrigger>
<Button variant="quiet"> <Button variant="quiet">
<Row alignItems="center" position="relative" gap maxHeight="40px"> <Row
alignItems="center"
position="relative"
gap
maxHeight="40px"
minWidth="200px"
maxWidth="200px"
>
<Icon>{teamId ? <Users /> : <User />}</Icon> <Icon>{teamId ? <Users /> : <User />}</Icon>
<Text>{label}</Text> <Text truncate>{label}</Text>
</Row> </Row>
<Icon rotate={90} size="sm"> <Icon rotate={90} size="sm">
<ChevronRight /> <ChevronRight />