mirror of
https://github.com/umami-software/umami.git
synced 2026-02-15 18:15:35 +01:00
Add UserButton to SideNav, refactor NavButton into TeamsButton
- Create UserButton component at bottom of SideNav with Settings, Language, Theme, Admin, and Logout menu items - Move Settings/Logout/Admin/Docs/Support out of NavButton into UserButton - Remove LanguageButton and ThemeButton from SideNav bottom - Refactor NavButton into TeamsButton with simplified team switching - Simplify WebsiteNav and move TeamsButton to App top bar Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f1b314e90b
commit
04c7216928
9 changed files with 382 additions and 186 deletions
|
|
@ -5,6 +5,7 @@ import { useEffect } from 'react';
|
|||
import { MobileNav } from '@/app/(main)/MobileNav';
|
||||
import { SideNav } from '@/app/(main)/SideNav';
|
||||
import { useConfig, useLoginQuery, useNavigation } from '@/components/hooks';
|
||||
import { TeamsButton } from '@/components/input/TeamsButton';
|
||||
import { LAST_TEAM_CONFIG } from '@/lib/constants';
|
||||
import { removeItem, setItem } from '@/lib/storage';
|
||||
import { UpdateNotice } from './UpdateNotice';
|
||||
|
|
@ -50,6 +51,18 @@ export function App({ children }) {
|
|||
<SideNav />
|
||||
</Column>
|
||||
<Column overflowX="hidden" position="relative">
|
||||
<Row
|
||||
position="sticky"
|
||||
top="0"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
paddingY="2"
|
||||
zIndex={100}
|
||||
>
|
||||
<Row backgroundColor="surface-raised">
|
||||
<TeamsButton />
|
||||
</Row>
|
||||
</Row>
|
||||
<Column alignItems="center">{children}</Column>
|
||||
</Column>
|
||||
<UpdateNotice user={user} config={config} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { IconLabel } from '@/components/common/IconLabel';
|
|||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { Globe, Grid2x2, LinkIcon } from '@/components/icons';
|
||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||
import { NavButton } from '@/components/input/NavButton';
|
||||
import { TeamsButton } from '@/components/input/TeamsButton';
|
||||
import { Logo } from '@/components/svg';
|
||||
import { AdminNav } from './admin/AdminNav';
|
||||
import { SettingsNav } from './settings/SettingsNav';
|
||||
|
|
@ -44,7 +44,7 @@ export function MobileNav() {
|
|||
return (
|
||||
<>
|
||||
<Row padding="3" onClick={close} border="bottom">
|
||||
<NavButton />
|
||||
<TeamsButton />
|
||||
{links.map(link => {
|
||||
return (
|
||||
<Link key={link.id} href={renderUrl(link.path)}>
|
||||
|
|
|
|||
|
|
@ -6,26 +6,21 @@ import {
|
|||
Icon,
|
||||
Row,
|
||||
Text,
|
||||
ThemeButton,
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
} from '@umami/react-zen';
|
||||
import Link from 'next/link';
|
||||
import type { Key } from 'react';
|
||||
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
||||
import { IconLabel } from '@/components/common/IconLabel';
|
||||
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { Globe, Grid2x2, LayoutDashboard, LinkIcon, PanelLeft } from '@/components/icons';
|
||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
||||
import { NavButton } from '@/components/input/NavButton';
|
||||
import { UserButton } from '@/components/input/UserButton';
|
||||
import { Logo } from '@/components/svg';
|
||||
|
||||
export function SideNav(props: any) {
|
||||
const { t, labels } = useMessages();
|
||||
const { pathname, renderUrl, websiteId, router } = useNavigation();
|
||||
const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed', false);
|
||||
|
||||
const hasNav = !!(websiteId || pathname.startsWith('/admin') || pathname.includes('/settings'));
|
||||
const { pathname, renderUrl, websiteId } = useNavigation();
|
||||
const [isCollapsed] = useGlobalState('sidenav-collapsed', false);
|
||||
|
||||
const links = [
|
||||
{
|
||||
|
|
@ -54,10 +49,6 @@ export function SideNav(props: any) {
|
|||
},
|
||||
];
|
||||
|
||||
const handleSelect = (id: Key) => {
|
||||
router.push(id === 'user' ? '/websites' : `/teams/${id}/websites`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column
|
||||
{...props}
|
||||
|
|
@ -90,9 +81,6 @@ export function SideNav(props: any) {
|
|||
<PanelButton />
|
||||
</Row>
|
||||
</Row>
|
||||
<Row marginBottom="4" style={{ flexShrink: 0 }}>
|
||||
<NavButton showText={!isCollapsed} onAction={handleSelect} />
|
||||
</Row>
|
||||
{websiteId ? (
|
||||
<WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} />
|
||||
) : (
|
||||
|
|
@ -126,9 +114,8 @@ export function SideNav(props: any) {
|
|||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
<Row alignItems="center" justifyContent="center" wrap="wrap" marginBottom="4" gap>
|
||||
<LanguageButton />
|
||||
<ThemeButton />
|
||||
<Row marginBottom="4">
|
||||
<UserButton showText={!isCollapsed} />
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,97 +32,66 @@ export function WebsiteNav({
|
|||
router.push(renderUrl(`/websites/${value}`));
|
||||
};
|
||||
|
||||
const renderValue = (value: any) => {
|
||||
return (
|
||||
<Text truncate style={{ maxWidth: 160, lineHeight: 1 }}>
|
||||
{value?.selectedItem?.name}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
if (isCollapsed !== undefined) {
|
||||
return (
|
||||
<Column gap="2">
|
||||
<Link href={renderUrl('/websites', false)} role="button">
|
||||
<TooltipTrigger isDisabled={!isCollapsed} delay={0}>
|
||||
<Focusable>
|
||||
<Row
|
||||
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>
|
||||
{!isCollapsed && (
|
||||
<Box marginBottom="2">
|
||||
<WebsiteSelect
|
||||
websiteId={websiteId}
|
||||
teamId={teamId}
|
||||
onChange={handleChange}
|
||||
renderValue={renderValue}
|
||||
buttonProps={{ style: { outline: 'none' } }}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{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">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Column padding="2" position="sticky" top="0" gap>
|
||||
<WebsiteSelect
|
||||
websiteId={websiteId}
|
||||
teamId={teamId}
|
||||
onChange={handleChange}
|
||||
renderValue={renderValue}
|
||||
buttonProps={{ style: { outline: 'none' } }}
|
||||
/>
|
||||
<NavMenu
|
||||
items={items}
|
||||
selectedKey={selectedKey}
|
||||
allowMinimize={false}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
<Column gap="2">
|
||||
<Link href={renderUrl('/websites', false)} role="button">
|
||||
<TooltipTrigger isDisabled={!isCollapsed} delay={0}>
|
||||
<Focusable>
|
||||
<Row
|
||||
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>
|
||||
<Box marginBottom="2">
|
||||
<WebsiteSelect
|
||||
websiteId={websiteId}
|
||||
teamId={teamId}
|
||||
onChange={handleChange}
|
||||
buttonProps={{ style: { outline: 'none' } }}
|
||||
/>
|
||||
</Box>
|
||||
{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">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue