feat(client): check in
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -1,31 +1,9 @@
|
|||||||
import { Badge } from '@/components/ui/badge';
|
import { CheckinCard } from './workbenchCards/checkin';
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardAction,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from '@/components/ui/card';
|
|
||||||
import { Button } from './ui/button';
|
|
||||||
|
|
||||||
export function SectionCards() {
|
export function SectionCards() {
|
||||||
return (
|
return (
|
||||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||||
<Card className="@container/card">
|
<CheckinCard />
|
||||||
<CardHeader>
|
|
||||||
<CardDescription>签到状态</CardDescription>
|
|
||||||
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
|
||||||
未签到
|
|
||||||
</CardTitle>
|
|
||||||
<CardAction>
|
|
||||||
<Badge variant="outline">Day 1</Badge>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
|
||||||
<Button className="w-20">签到</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
38
client/src/components/workbenchCards/checkin.tsx
Normal file
38
client/src/components/workbenchCards/checkin.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
export function CheckinCard() {
|
||||||
|
const { mutateAsync, isPending } = useCheckin();
|
||||||
|
return (
|
||||||
|
<Card className="@container/card">
|
||||||
|
<CardHeader>
|
||||||
|
<CardDescription>签到状态</CardDescription>
|
||||||
|
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
||||||
|
未签到
|
||||||
|
</CardTitle>
|
||||||
|
<CardAction>
|
||||||
|
<Badge variant="outline">Day 1</Badge>
|
||||||
|
</CardAction>
|
||||||
|
</CardHeader>
|
||||||
|
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
||||||
|
<Button
|
||||||
|
className="w-20"
|
||||||
|
onClick={() => {
|
||||||
|
mutateAsync().then(() => {
|
||||||
|
toast('签到成功');
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast('签到失败');
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
签到
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
10
client/src/hooks/data/useCheckin.ts
Normal file
10
client/src/hooks/data/useCheckin.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
import { axiosClient } from '@/lib/axios';
|
||||||
|
|
||||||
|
export function useCheckin() {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
return axiosClient.post<object>('/checkin');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -22,9 +22,7 @@ axiosClient.interceptors.response.use(undefined, async (error: AxiosError) => {
|
|||||||
await router.navigate({ to: '/login' });
|
await router.navigate({ to: '/login' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (error.config) {
|
else { return Promise.reject(error); }
|
||||||
return axiosClient(error.config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,13 +4,10 @@ import { hasToken } from '@/lib/token';
|
|||||||
|
|
||||||
export const Route = createFileRoute('/_sidebarLayout/')({
|
export const Route = createFileRoute('/_sidebarLayout/')({
|
||||||
component: Index,
|
component: Index,
|
||||||
beforeLoad: async ({ location }) => {
|
beforeLoad: async () => {
|
||||||
if (!hasToken()) {
|
if (!hasToken()) {
|
||||||
throw redirect({
|
throw redirect({
|
||||||
to: '/login',
|
to: '/login',
|
||||||
search: {
|
|
||||||
redirect: location.href,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user