feat(client): user info

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2025-12-25 02:50:23 +08:00
parent 70ae704008
commit 2fb7ce8346
5 changed files with 37 additions and 18 deletions

View File

@@ -63,7 +63,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<NavSecondary items={data.navSecondary} className="mt-auto" />
</SidebarContent>
<SidebarFooter>
<NavUser user={data.user} />
<NavUser />
</SidebarFooter>
</Sidebar>
);

View File

@@ -22,17 +22,11 @@ import {
SidebarMenuItem,
useSidebar,
} from '@/components/ui/sidebar';
import { useUserInfo } from '@/hooks/data/useUserInfo';
export function NavUser({
user,
}: {
user: {
name: string;
email: string;
avatar: string;
};
}) {
export function NavUser() {
const { isMobile } = useSidebar();
const { data: user } = useUserInfo();
return (
<SidebarMenu>
@@ -44,11 +38,11 @@ export function NavUser({
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg grayscale">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarImage src={user.avatar} alt={user.nickname} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate font-medium">{user.nickname}</span>
<span className="text-muted-foreground truncate text-xs">
{user.email}
</span>
@@ -65,11 +59,11 @@ export function NavUser({
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarImage src={user.avatar} alt={user.nickname} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate font-medium">{user.nickname}</span>
<span className="text-muted-foreground truncate text-xs">
{user.email}
</span>

View File

@@ -1,17 +1,22 @@
import { useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { useCheckin } from '@/hooks/data/useCheckin';
import { useUserInfo } from '@/hooks/data/useUserInfo';
export function CheckinCard() {
const { mutateAsync, isPending } = useCheckin();
const { data } = useUserInfo();
const queryClient = useQueryClient();
return (
<Card className="@container/card">
<CardHeader>
<CardDescription></CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
{data.checkin !== null ? '已签到' : '未签到'}
{data.checkin !== null && <span className="text-sm font-medium ml-2">{`${new Date(data.checkin).toLocaleString()}`}</span>}
</CardTitle>
<CardAction>
<Badge variant="outline">Day 1</Badge>
@@ -23,12 +28,13 @@ export function CheckinCard() {
onClick={() => {
mutateAsync().then(() => {
toast('签到成功');
void queryClient.invalidateQueries({ queryKey: ['userInfo'] });
}).catch((error) => {
console.error(error);
toast('签到失败');
});
}}
disabled={isPending}
disabled={isPending || data.checkin !== null}
>
</Button>

View File

@@ -0,0 +1,21 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { axiosClient } from '@/lib/axios';
export function useUserInfo() {
return useSuspenseQuery({
queryKey: ['userInfo'],
queryFn: async () => {
const response = await axiosClient.get<{
user_id: string;
email: string;
type: string;
nickname: string;
subtitle: string;
avatar: string;
checkin: string | null;
}
>('/user/info');
return response.data;
},
});
}

View File

@@ -1,6 +1,5 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createRootRoute, Outlet } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools';
import { ThemeProvider } from '@/components/theme-provider';
import { Toaster } from '@/components/ui/sonner';
import '@/index.css';
@@ -15,7 +14,6 @@ function RootLayout() {
<Outlet />
</QueryClientProvider>
</ThemeProvider>
<TanStackRouterDevtools />
<Toaster position="top-right" />
</>
);