WIP: Nav merge

This commit is contained in:
Mike Cao 2025-09-25 15:58:25 -07:00
parent dfc6161a53
commit 805b3ec853
4 changed files with 98 additions and 29 deletions

View file

@ -82,7 +82,7 @@
"@react-spring/web": "^10.0.1",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.85.5",
"@umami/react-zen": "^0.186.0",
"@umami/react-zen": "^0.187.0",
"@umami/redis-client": "^0.29.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.6.0",

10
pnpm-lock.yaml generated
View file

@ -45,8 +45,8 @@ importers:
specifier: ^5.85.5
version: 5.85.5(react@19.1.1)
'@umami/react-zen':
specifier: ^0.186.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))
specifier: ^0.187.0
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':
specifier: ^0.29.0
version: 0.29.0
@ -2738,8 +2738,8 @@ packages:
'@prisma/client': ^6.1.0
'@prisma/extension-read-replicas': ^0.4.1
'@umami/react-zen@0.186.0':
resolution: {integrity: sha512-s+x4cJK5UTHQ0l2TTUb3zX8P2U6bMw35NRjIqG+OJvljJf5NNdRo6WChZOvnh/08XxGI30jntFhUYdup255rFg==}
'@umami/react-zen@0.187.0':
resolution: {integrity: sha512-CiTGBqEvN/dcZ1Tq4R+mj9ynN1opZF81iukUzElChJ5XF/Ec9HhPR+KM2r8PXt+uWeVVe1aZtjyVOdwUR/ndXg==}
'@umami/redis-client@0.29.0':
resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==}
@ -10343,7 +10343,7 @@ snapshots:
transitivePeerDependencies:
- 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:
'@fontsource/jetbrains-mono': 5.2.8
'@internationalized/date': 3.9.0

View file

@ -36,9 +36,21 @@ function MessagesProvider({ children }) {
export function Providers({ children }) {
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 (
<ZenProvider>
<RouterProvider navigate={router.push}>
<RouterProvider navigate={navigate}>
<MessagesProvider>
<QueryClientProvider client={client}>
<ErrorBoundary>{children}</ErrorBoundary>

View file

@ -6,27 +6,48 @@ import {
MenuTrigger,
MenuSection,
MenuSeparator,
SubmenuTrigger,
Popover,
Row,
Column,
Pressable,
IconLabel,
} from '@umami/react-zen';
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
import { ChevronRight, User, Users } from '@/components/icons';
import { useConfig, useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
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 {
showText?: boolean;
onAction?: (id: any) => void;
}
export function NavButton({ showText = true, onAction }: TeamsButtonProps) {
export function NavButton({ showText = true }: TeamsButtonProps) {
const { user } = useLoginQuery();
const { cloudMode } = useConfig();
const { formatMessage, labels } = useMessages();
const { teamId } = useNavigation();
const team = user?.teams?.find(({ id }) => id === teamId);
const selectedKeys = new Set([teamId || 'user']);
const label = teamId ? team?.name : user.username;
const getUrl = (url: string) => {
return cloudMode ? `${process.env.cloudUrl}/${url}` : url;
};
const handleAction = async () => {};
return (
<MenuTrigger>
<Pressable>
@ -58,19 +79,21 @@ export function NavButton({ showText = true, onAction }: TeamsButtonProps) {
selectionMode="single"
selectedKeys={selectedKeys}
autoFocus="last"
onAction={onAction}
onAction={handleAction}
>
<MenuSection title={formatMessage(labels.myAccount)}>
<MenuItem id={'user'}>
<Row alignItems="center" gap>
<Icon>
<User />
</Icon>
<Text wrap="nowrap">{user.username}</Text>
</Row>
<MenuItem id="user">
<IconLabel icon={<User />} label={user.username} />
</MenuItem>
</MenuSection>
<MenuSeparator />
<SubmenuTrigger>
<MenuItem id="teams" showChecked={false} showSubMenuIcon>
<IconLabel icon={<Users />} label={formatMessage(labels.teams)} />
</MenuItem>
<Popover placement="right top">
<Column minWidth="300px">
<Menu>
<MenuSection title={formatMessage(labels.teams)}>
{user?.teams?.map(({ id, name }) => (
<MenuItem key={id} id={id}>
@ -86,6 +109,40 @@ export function NavButton({ showText = true, onAction }: TeamsButtonProps) {
</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
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>
</Column>
</Popover>
</MenuTrigger>
);
}