mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
WIP: Nav merge
This commit is contained in:
parent
dfc6161a53
commit
805b3ec853
4 changed files with 98 additions and 29 deletions
|
|
@ -82,7 +82,7 @@
|
||||||
"@react-spring/web": "^10.0.1",
|
"@react-spring/web": "^10.0.1",
|
||||||
"@svgr/cli": "^8.1.0",
|
"@svgr/cli": "^8.1.0",
|
||||||
"@tanstack/react-query": "^5.85.5",
|
"@tanstack/react-query": "^5.85.5",
|
||||||
"@umami/react-zen": "^0.186.0",
|
"@umami/react-zen": "^0.187.0",
|
||||||
"@umami/redis-client": "^0.29.0",
|
"@umami/redis-client": "^0.29.0",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"chalk": "^5.6.0",
|
"chalk": "^5.6.0",
|
||||||
|
|
|
||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
|
@ -45,8 +45,8 @@ importers:
|
||||||
specifier: ^5.85.5
|
specifier: ^5.85.5
|
||||||
version: 5.85.5(react@19.1.1)
|
version: 5.85.5(react@19.1.1)
|
||||||
'@umami/react-zen':
|
'@umami/react-zen':
|
||||||
specifier: ^0.186.0
|
specifier: ^0.187.0
|
||||||
version: 0.186.0(@babel/core@7.28.3)(@types/react@19.1.12)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.1)(use-sync-external-store@1.5.0(react@19.1.1))
|
version: 0.187.0(@babel/core@7.28.3)(@types/react@19.1.12)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.1)(use-sync-external-store@1.5.0(react@19.1.1))
|
||||||
'@umami/redis-client':
|
'@umami/redis-client':
|
||||||
specifier: ^0.29.0
|
specifier: ^0.29.0
|
||||||
version: 0.29.0
|
version: 0.29.0
|
||||||
|
|
@ -2738,8 +2738,8 @@ packages:
|
||||||
'@prisma/client': ^6.1.0
|
'@prisma/client': ^6.1.0
|
||||||
'@prisma/extension-read-replicas': ^0.4.1
|
'@prisma/extension-read-replicas': ^0.4.1
|
||||||
|
|
||||||
'@umami/react-zen@0.186.0':
|
'@umami/react-zen@0.187.0':
|
||||||
resolution: {integrity: sha512-s+x4cJK5UTHQ0l2TTUb3zX8P2U6bMw35NRjIqG+OJvljJf5NNdRo6WChZOvnh/08XxGI30jntFhUYdup255rFg==}
|
resolution: {integrity: sha512-CiTGBqEvN/dcZ1Tq4R+mj9ynN1opZF81iukUzElChJ5XF/Ec9HhPR+KM2r8PXt+uWeVVe1aZtjyVOdwUR/ndXg==}
|
||||||
|
|
||||||
'@umami/redis-client@0.29.0':
|
'@umami/redis-client@0.29.0':
|
||||||
resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==}
|
resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==}
|
||||||
|
|
@ -10343,7 +10343,7 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umami/react-zen@0.186.0(@babel/core@7.28.3)(@types/react@19.1.12)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.1)(use-sync-external-store@1.5.0(react@19.1.1))':
|
'@umami/react-zen@0.187.0(@babel/core@7.28.3)(@types/react@19.1.12)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.1)(use-sync-external-store@1.5.0(react@19.1.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fontsource/jetbrains-mono': 5.2.8
|
'@fontsource/jetbrains-mono': 5.2.8
|
||||||
'@internationalized/date': 3.9.0
|
'@internationalized/date': 3.9.0
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,21 @@ function MessagesProvider({ children }) {
|
||||||
export function Providers({ children }) {
|
export function Providers({ children }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
function navigate(url: string) {
|
||||||
|
if (shouldUseNativeLink(url)) {
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
router.push(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldUseNativeLink(url: string) {
|
||||||
|
return url.startsWith('http');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZenProvider>
|
<ZenProvider>
|
||||||
<RouterProvider navigate={router.push}>
|
<RouterProvider navigate={navigate}>
|
||||||
<MessagesProvider>
|
<MessagesProvider>
|
||||||
<QueryClientProvider client={client}>
|
<QueryClientProvider client={client}>
|
||||||
<ErrorBoundary>{children}</ErrorBoundary>
|
<ErrorBoundary>{children}</ErrorBoundary>
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,48 @@ import {
|
||||||
MenuTrigger,
|
MenuTrigger,
|
||||||
MenuSection,
|
MenuSection,
|
||||||
MenuSeparator,
|
MenuSeparator,
|
||||||
|
SubmenuTrigger,
|
||||||
Popover,
|
Popover,
|
||||||
Row,
|
Row,
|
||||||
Column,
|
Column,
|
||||||
Pressable,
|
Pressable,
|
||||||
|
IconLabel,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
import { useConfig, useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { ChevronRight, User, Users } from '@/components/icons';
|
import {
|
||||||
|
BookText,
|
||||||
|
ChevronRight,
|
||||||
|
ExternalLink,
|
||||||
|
LifeBuoy,
|
||||||
|
LockKeyhole,
|
||||||
|
LogOut,
|
||||||
|
Settings,
|
||||||
|
User,
|
||||||
|
Users,
|
||||||
|
} from '@/components/icons';
|
||||||
|
import { DOCS_URL } from '@/lib/constants';
|
||||||
|
import * as url from 'node:url';
|
||||||
|
|
||||||
export interface TeamsButtonProps {
|
export interface TeamsButtonProps {
|
||||||
showText?: boolean;
|
showText?: boolean;
|
||||||
onAction?: (id: any) => void;
|
onAction?: (id: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NavButton({ showText = true, onAction }: TeamsButtonProps) {
|
export function NavButton({ showText = true }: TeamsButtonProps) {
|
||||||
const { user } = useLoginQuery();
|
const { user } = useLoginQuery();
|
||||||
|
const { cloudMode } = useConfig();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { teamId } = useNavigation();
|
const { teamId } = useNavigation();
|
||||||
const team = user?.teams?.find(({ id }) => id === teamId);
|
const team = user?.teams?.find(({ id }) => id === teamId);
|
||||||
const selectedKeys = new Set([teamId || 'user']);
|
const selectedKeys = new Set([teamId || 'user']);
|
||||||
const label = teamId ? team?.name : user.username;
|
const label = teamId ? team?.name : user.username;
|
||||||
|
|
||||||
|
const getUrl = (url: string) => {
|
||||||
|
return cloudMode ? `${process.env.cloudUrl}/${url}` : url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAction = async () => {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuTrigger>
|
<MenuTrigger>
|
||||||
<Pressable>
|
<Pressable>
|
||||||
|
|
@ -58,31 +79,67 @@ export function NavButton({ showText = true, onAction }: TeamsButtonProps) {
|
||||||
selectionMode="single"
|
selectionMode="single"
|
||||||
selectedKeys={selectedKeys}
|
selectedKeys={selectedKeys}
|
||||||
autoFocus="last"
|
autoFocus="last"
|
||||||
onAction={onAction}
|
onAction={handleAction}
|
||||||
>
|
>
|
||||||
<MenuSection title={formatMessage(labels.myAccount)}>
|
<MenuSection title={formatMessage(labels.myAccount)}>
|
||||||
<MenuItem id={'user'}>
|
<MenuItem id="user">
|
||||||
<Row alignItems="center" gap>
|
<IconLabel icon={<User />} label={user.username} />
|
||||||
<Icon>
|
|
||||||
<User />
|
|
||||||
</Icon>
|
|
||||||
<Text wrap="nowrap">{user.username}</Text>
|
|
||||||
</Row>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<MenuSeparator />
|
<MenuSeparator />
|
||||||
<MenuSection title={formatMessage(labels.teams)}>
|
<SubmenuTrigger>
|
||||||
{user?.teams?.map(({ id, name }) => (
|
<MenuItem id="teams" showChecked={false} showSubMenuIcon>
|
||||||
<MenuItem key={id} id={id}>
|
<IconLabel icon={<Users />} label={formatMessage(labels.teams)} />
|
||||||
<Row alignItems="center" gap>
|
</MenuItem>
|
||||||
<Icon size="sm">
|
<Popover placement="right top">
|
||||||
<Users />
|
<Column minWidth="300px">
|
||||||
</Icon>
|
<Menu>
|
||||||
<Text wrap="nowrap">{name}</Text>
|
<MenuSection title={formatMessage(labels.teams)}>
|
||||||
</Row>
|
{user?.teams?.map(({ id, name }) => (
|
||||||
|
<MenuItem key={id} id={id}>
|
||||||
|
<Row alignItems="center" gap>
|
||||||
|
<Icon size="sm">
|
||||||
|
<Users />
|
||||||
|
</Icon>
|
||||||
|
<Text wrap="nowrap">{name}</Text>
|
||||||
|
</Row>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</MenuSection>
|
||||||
|
</Menu>
|
||||||
|
</Column>
|
||||||
|
</Popover>
|
||||||
|
</SubmenuTrigger>
|
||||||
|
<MenuItem id="settings" icon={<Settings />} label={formatMessage(labels.settings)} />
|
||||||
|
{cloudMode && (
|
||||||
|
<>
|
||||||
|
<MenuItem
|
||||||
|
id="docs"
|
||||||
|
href={DOCS_URL}
|
||||||
|
target="_blank"
|
||||||
|
icon={<BookText />}
|
||||||
|
label={formatMessage(labels.documentation)}
|
||||||
|
>
|
||||||
|
<Icon color="muted">
|
||||||
|
<ExternalLink />
|
||||||
|
</Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
<MenuItem
|
||||||
</MenuSection>
|
id="support"
|
||||||
|
href={getUrl('/settings/support')}
|
||||||
|
icon={<LifeBuoy />}
|
||||||
|
label={formatMessage(labels.support)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{user.isAdmin && (
|
||||||
|
<>
|
||||||
|
<MenuSeparator />
|
||||||
|
<MenuItem id="/admin" icon={<LockKeyhole />} label={formatMessage(labels.admin)} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<MenuSeparator />
|
||||||
|
<MenuItem id="/logout" icon={<LogOut />} label={formatMessage(labels.logout)} />
|
||||||
</Menu>
|
</Menu>
|
||||||
</Column>
|
</Column>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue