mirror of
https://github.com/umami-software/umami.git
synced 2025-12-06 01:18:00 +01:00
Updated nav.
This commit is contained in:
parent
25f96f6b6b
commit
a025fc9552
11 changed files with 971 additions and 1005 deletions
|
|
@ -82,7 +82,7 @@
|
|||
"@react-spring/web": "^9.7.3",
|
||||
"@svgr/cli": "^8.1.0",
|
||||
"@tanstack/react-query": "^5.80.10",
|
||||
"@umami/react-zen": "^0.150.0",
|
||||
"@umami/react-zen": "^0.153.0",
|
||||
"@umami/redis-client": "^0.27.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"chalk": "^4.1.1",
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
"lucide-react": "^0.517.0",
|
||||
"maxmind": "^4.3.27",
|
||||
"md5": "^2.3.0",
|
||||
"next": "15.4.1",
|
||||
"next": "15.4.3",
|
||||
"node-fetch": "^3.2.8",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pg": "^8.16.3",
|
||||
|
|
|
|||
1839
pnpm-lock.yaml
generated
1839
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -18,6 +18,8 @@ import {
|
|||
} from '@/components/icons';
|
||||
import { useMessages, useNavigation, useGlobalState } from '@/components/hooks';
|
||||
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
||||
import { TeamsButton } from '@/components/input/TeamsButton';
|
||||
import { PanelButton } from '@/components/input/PanelButton';
|
||||
|
||||
export function SideNav(props: SidebarProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
|
@ -73,7 +75,6 @@ export function SideNav(props: SidebarProps) {
|
|||
{...props}
|
||||
isCollapsed={isCollapsed || isWebsite}
|
||||
muteItems={false}
|
||||
variant="quiet"
|
||||
showBorder={false}
|
||||
>
|
||||
<SidebarSection>
|
||||
|
|
@ -98,6 +99,12 @@ export function SideNav(props: SidebarProps) {
|
|||
);
|
||||
})}
|
||||
</SidebarSection>
|
||||
<SidebarSection>
|
||||
<TeamsButton showText={!isCollapsed} />
|
||||
<Row>
|
||||
<PanelButton isDisabled={!!isWebsite} />
|
||||
</Row>
|
||||
</SidebarSection>
|
||||
</Sidebar>
|
||||
{isWebsite && <WebsiteNav websiteId={websiteId} />}
|
||||
</Row>
|
||||
|
|
|
|||
|
|
@ -1,44 +1,22 @@
|
|||
import { ThemeButton, Row, Icon } from '@umami/react-zen';
|
||||
import { ThemeButton, Row } from '@umami/react-zen';
|
||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
||||
import { ProfileButton } from '@/components/input/ProfileButton';
|
||||
import { TeamsButton } from '@/components/input/TeamsButton';
|
||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||
import { Slash } from '@/components/icons';
|
||||
import { useNavigation } from '@/components/hooks';
|
||||
import { PanelButton } from '@/components/input/PanelButton';
|
||||
|
||||
export function TopNav() {
|
||||
const { teamId, websiteId, pathname } = useNavigation();
|
||||
const isWebsite = websiteId && !pathname.includes('/settings');
|
||||
|
||||
return (
|
||||
<Row
|
||||
justifyContent="space-between"
|
||||
position="absolute"
|
||||
top="0"
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
paddingY="2"
|
||||
paddingX="3"
|
||||
paddingRight="5"
|
||||
width="100%"
|
||||
style={{ position: 'sticky', top: 0 }}
|
||||
backgroundColor="2"
|
||||
zIndex={1}
|
||||
>
|
||||
<Row alignItems="center">
|
||||
<PanelButton isDisabled={!!isWebsite} />
|
||||
<Seperator />
|
||||
<TeamsButton />
|
||||
{isWebsite && (
|
||||
<>
|
||||
<Seperator />
|
||||
<WebsiteSelect
|
||||
buttonProps={{ variant: 'quiet' }}
|
||||
websiteId={websiteId}
|
||||
teamId={teamId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
<Row alignItems="center" justifyContent="flex-end">
|
||||
<Row alignItems="center" justifyContent="flex-end" backgroundColor="2" borderRadius>
|
||||
<ThemeButton />
|
||||
<LanguageButton />
|
||||
<ProfileButton />
|
||||
|
|
@ -46,11 +24,3 @@ export function TopNav() {
|
|||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const Seperator = () => {
|
||||
return (
|
||||
<Icon strokeColor="7" rotate={-25}>
|
||||
<Slash />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@ export function SettingsLayout({ children }: { children: ReactNode }) {
|
|||
label: formatMessage(labels.profile),
|
||||
url: '/settings/profile',
|
||||
},
|
||||
{
|
||||
id: 'websites',
|
||||
label: formatMessage(labels.websites),
|
||||
url: '/settings/websites',
|
||||
},
|
||||
{ id: 'teams', label: formatMessage(labels.teams), url: '/settings/teams' },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,4 @@
|
|||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Text,
|
||||
useToast,
|
||||
DialogTrigger,
|
||||
Dialog,
|
||||
Modal,
|
||||
Column,
|
||||
} from '@umami/react-zen';
|
||||
import { Button, Icon, Text, useToast, DialogTrigger, Dialog, Modal } from '@umami/react-zen';
|
||||
import { PasswordEditForm } from './PasswordEditForm';
|
||||
import { LockKeyhole } from '@/components/icons';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
|
@ -30,11 +21,7 @@ export function PasswordChangeButton() {
|
|||
</Button>
|
||||
<Modal>
|
||||
<Dialog title={formatMessage(labels.changePassword)} style={{ width: 400 }}>
|
||||
{({ close }) => (
|
||||
<Column width="300px">
|
||||
<PasswordEditForm onSave={handleSave} onClose={close} />
|
||||
</Column>
|
||||
)}
|
||||
{({ close }) => <PasswordEditForm onSave={handleSave} onClose={close} />}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
</DialogTrigger>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ import {
|
|||
} from '@/components/icons';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import Link from 'next/link';
|
||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||
|
||||
export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { pathname, renderUrl } = useNavigation();
|
||||
const { pathname, renderUrl, teamId } = useNavigation();
|
||||
|
||||
const links = [
|
||||
{
|
||||
|
|
@ -135,7 +136,8 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
|||
|
||||
return (
|
||||
<Column gap padding width="240px" border="left" overflowY="auto">
|
||||
<NavMenu highlightColor="2">
|
||||
<WebsiteSelect buttonProps={{ variant: 'quiet' }} websiteId={websiteId} teamId={teamId} />
|
||||
<NavMenu muteItems={false}>
|
||||
{links.map(({ label, items }) => {
|
||||
return (
|
||||
<NavMenuGroup title={label} key={label} gap="1">
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ export {
|
|||
Trash,
|
||||
Upload,
|
||||
User,
|
||||
CircleUserRound as UserCircle,
|
||||
Users,
|
||||
UserPlus,
|
||||
X as Close,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import {
|
|||
Text,
|
||||
} from '@umami/react-zen';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { User, LogOut, CircleUserRound } from 'lucide-react';
|
||||
import { useMessages, useLoginQuery } from '@/components/hooks';
|
||||
import { LogOut, Settings, UserCircle, LockKeyhole } from '@/components/icons';
|
||||
|
||||
export function ProfileButton() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
|
@ -21,31 +21,34 @@ export function ProfileButton() {
|
|||
const cloudMode = !!process.env.cloudMode;
|
||||
|
||||
const handleSelect = (key: Key) => {
|
||||
if (key === 'profile') {
|
||||
router.push('/settings/profile');
|
||||
}
|
||||
if (key === 'logout') {
|
||||
router.push('/logout');
|
||||
}
|
||||
router.push(`/${key}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuTrigger>
|
||||
<Button data-test="button-profile" variant="quiet">
|
||||
<Icon>
|
||||
<CircleUserRound />
|
||||
<UserCircle />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover placement="bottom end">
|
||||
<Menu autoFocus="last" onAction={handleSelect}>
|
||||
<MenuSection title={user.username}>
|
||||
<MenuSeparator />
|
||||
<MenuItem id="profile">
|
||||
<MenuItem id="settings">
|
||||
<Icon>
|
||||
<User />
|
||||
<Settings />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.profile)}</Text>
|
||||
<Text>{formatMessage(labels.settings)}</Text>
|
||||
</MenuItem>
|
||||
{user.isAdmin && (
|
||||
<MenuItem id="admin">
|
||||
<Icon>
|
||||
<LockKeyhole />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.admin)}</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
{!cloudMode && (
|
||||
<MenuItem data-test="item-logout" id="logout">
|
||||
<Icon>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { useRouter } from 'next/navigation';
|
|||
import {
|
||||
Text,
|
||||
Icon,
|
||||
Button,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuTrigger,
|
||||
|
|
@ -12,6 +11,8 @@ import {
|
|||
Popover,
|
||||
Row,
|
||||
Box,
|
||||
SidebarItem,
|
||||
Pressable,
|
||||
} from '@umami/react-zen';
|
||||
import { useLoginQuery, useMessages, useUserTeamsQuery, useNavigation } from '@/components/hooks';
|
||||
import { Chevron, User, Users } from '@/components/icons';
|
||||
|
|
@ -35,19 +36,20 @@ export function TeamsButton({ showText = true }: { showText?: boolean }) {
|
|||
|
||||
return (
|
||||
<MenuTrigger>
|
||||
<Button variant="quiet">
|
||||
<Pressable>
|
||||
<SidebarItem
|
||||
label={teamId ? team?.name : user.username}
|
||||
icon={teamId ? <Users /> : <User />}
|
||||
>
|
||||
<Row alignItems="center" justifyContent="space-between" width="100%" gap>
|
||||
<Row alignItems="center" gap>
|
||||
<Icon>{teamId ? <Users /> : <User />}</Icon>
|
||||
{showText && <Text truncate>{teamId ? team?.name : user.username}</Text>}
|
||||
</Row>
|
||||
{showText && (
|
||||
<Icon rotate={90} size="sm">
|
||||
<Chevron />
|
||||
</Icon>
|
||||
)}
|
||||
</Row>
|
||||
</Button>
|
||||
</SidebarItem>
|
||||
</Pressable>
|
||||
<Popover placement="bottom start">
|
||||
<Box minWidth="300px">
|
||||
<Menu
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function Legend({
|
|||
return (
|
||||
<Row key={text} onClick={() => onClick(item)}>
|
||||
<StatusLight color={color.alpha(color.alpha() + 0.2).toHex()}>
|
||||
<Text size="1" color={hidden ? 'disabled' : undefined} wrap="nowrap">
|
||||
<Text size="2" color={hidden ? 'disabled' : undefined} wrap="nowrap">
|
||||
{text}
|
||||
</Text>
|
||||
</StatusLight>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue