Skip to content
Snippets Groups Projects
Commit 36d0ca64 authored by 한동현's avatar 한동현
Browse files

feat: 대시보드 페이지 API 연동

parent 95266fab
No related branches found
No related tags found
No related merge requests found
import { useEffect, useState } from 'react';
import { Link } from 'react-router';
import { Split, ShieldCheck, Server, ArrowRight } from 'lucide-react'; import { Split, ShieldCheck, Server, ArrowRight } from 'lucide-react';
import { toast } from 'sonner';
import { useAuthStore } from '@/stores/authStore';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { Badge } from '@/components/ui/badge';
import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardDescription } from '@/components/ui/card'; import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardDescription } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Log, LogListResponse } from '@/types/log';
const logs = [ interface ProjectInfo {
{ routing: number;
id: 1, forwarding: number;
timestamp: '2021-09-01 12:00:00', certificate: number;
action: '웹 라우팅 설정 변경 (console.ajou.app)', }
username: 'admin',
}, const ACTION = {
{ CREATE: '생성',
id: 2, UPDATE: '수정',
timestamp: '2021-09-01 12:01:00', DELETE: '삭제',
action: 'SSL 인증서 발급 (*.ajou.app)', };
username: 'admin',
}, const TYPE = {
{ ROUTING: '라우팅',
id: 3, CERTIFICATE: '인증서',
timestamp: '2021-09-01 13:00:00', FORWARDING: '포워딩',
action: 'SSH 포트포워딩 설정 변경 (10.16.1.10)', };
username: 'admin',
},
{
id: 4,
timestamp: '2021-09-02 12:00:00',
action: '웹 라우팅 설정 변경 (blog.ajou.app)',
username: 'hando1220',
},
];
export default function Home() { export default function Home() {
const { authFetch, selectedProject } = useAuthStore();
const [logs, setLogs] = useState<Log[] | null>(null);
const [projectInfo, setProjectInfo] = useState<ProjectInfo | null>(null);
useEffect(() => {
authFetch(`/api/main?projectId=${selectedProject?.id}`)
.then((response) => {
if (!response.ok) throw new Error(`프로젝트 정보 조회 실패: (${response.status})`);
return response.json();
})
.then((data) => {
setProjectInfo(data);
})
.catch((error) => {
console.error(error);
toast.error('프로젝트 정보를 조회할 수 없습니다.');
});
}, [authFetch, selectedProject]);
useEffect(() => {
authFetch(`/api/logs?projectId=${selectedProject?.id}&size=5`)
.then((response): Promise<LogListResponse> => {
if (!response.ok) throw new Error(`로그 목록 조회 실패: (${response.status})`);
return response.json();
})
.then(({ contents }) => {
setLogs(contents);
})
.catch((error) => {
console.error(error);
toast.error('로그 정보를 조회할 수 없습니다.');
});
}, [authFetch, selectedProject]);
return ( return (
<div className="flex flex-1 flex-col gap-4 p-6"> <div className="flex flex-1 flex-col gap-4 p-6">
<h1 className="scroll-m-20 text-3xl font-semibold first:mt-0 mb-2">Aolda Proxy Manager</h1> <h1 className="scroll-m-20 text-3xl font-semibold first:mt-0 mb-2">Aolda Proxy Manager</h1>
...@@ -40,14 +74,22 @@ export default function Home() { ...@@ -40,14 +74,22 @@ export default function Home() {
<CardTitle className="text-xl">웹 라우팅</CardTitle> <CardTitle className="text-xl">웹 라우팅</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> <div className="flex flex-row justify-center items-center gap-4 font-bold text-4xl">
<Split className="size-8" /> 0 {projectInfo === null ? (
<Skeleton className="w-22 h-10 rounded-full" />
) : (
<>
<Split className="size-8" /> {projectInfo.routing}
</>
)}
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button variant="ghost" className="ml-auto"> <Link className="ml-auto" to="/routing">
<Button variant="ghost">
관리하기 <ArrowRight /> 관리하기 <ArrowRight />
</Button> </Button>
</Link>
</CardFooter> </CardFooter>
</Card> </Card>
<Card> <Card>
...@@ -55,14 +97,22 @@ export default function Home() { ...@@ -55,14 +97,22 @@ export default function Home() {
<CardTitle className="text-xl">SSL 인증서</CardTitle> <CardTitle className="text-xl">SSL 인증서</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> <div className="flex flex-row justify-center items-center gap-4 font-bold text-4xl">
<ShieldCheck className="size-8" /> 0 {projectInfo === null ? (
<Skeleton className="w-22 h-10 rounded-full" />
) : (
<>
<ShieldCheck className="size-8" /> {projectInfo.certificate}
</>
)}
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button variant="ghost" className="ml-auto"> <Link className="ml-auto" to="/certificate">
<Button variant="ghost">
관리하기 <ArrowRight /> 관리하기 <ArrowRight />
</Button> </Button>
</Link>
</CardFooter> </CardFooter>
</Card> </Card>
<Card> <Card>
...@@ -70,14 +120,22 @@ export default function Home() { ...@@ -70,14 +120,22 @@ export default function Home() {
<CardTitle className="text-xl">SSH 포트포워딩</CardTitle> <CardTitle className="text-xl">SSH 포트포워딩</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> <div className="flex flex-row justify-center items-center gap-4 font-bold text-4xl">
<Server className="size-8" /> 0 {projectInfo === null ? (
<Skeleton className="w-22 h-10 rounded-full" />
) : (
<>
<Server className="size-8" /> {projectInfo.certificate}
</>
)}
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button variant="ghost" className="ml-auto"> <Link className="ml-auto" to="/forwarding">
<Button variant="ghost">
관리하기 <ArrowRight /> 관리하기 <ArrowRight />
</Button> </Button>
</Link>
</CardFooter> </CardFooter>
</Card> </Card>
</div> </div>
...@@ -96,20 +154,54 @@ export default function Home() { ...@@ -96,20 +154,54 @@ export default function Home() {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{logs.map((log) => ( {logs === null ? (
<>
<TableRow>
<TableCell colSpan={3}>
<Skeleton className="w-full h-[1rem] my-2 rounded-full" />
</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={3}>
<Skeleton className="w-full h-[1rem] my-2 rounded-full" />
</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={3}>
<Skeleton className="w-full h-[1rem] my-2 rounded-full" />
</TableCell>
</TableRow>
</>
) : logs.length === 0 ? (
<TableRow>
<TableCell colSpan={3} className="text-center text-muted-foreground">
조회된 로그가 없습니다.
</TableCell>
</TableRow>
) : (
logs.map((log) => (
<TableRow key={log.id}> <TableRow key={log.id}>
<TableCell className="font-medium">{log.timestamp}</TableCell> <TableCell>{log.createdAt}</TableCell>
<TableCell>{log.action}</TableCell> <TableCell>
<TableCell className="text-center">{log.username}</TableCell> <div className="flex items-center gap-2">
<Badge variant="default">{TYPE[log.type]}</Badge>
<Badge variant="secondary">{ACTION[log.action]}</Badge>
<div>{log.description.split('\n').join(' / ')}</div>
</div>
</TableCell>
<TableCell className="truncate max-w-32 text-center">{log.user.name}</TableCell>
</TableRow> </TableRow>
))} ))
)}
</TableBody> </TableBody>
</Table> </Table>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button variant="ghost" className="ml-auto"> <Link className="ml-auto" to="/log">
<Button variant="ghost">
모든 기록 보기 <ArrowRight /> 모든 기록 보기 <ArrowRight />
</Button> </Button>
</Link>
</CardFooter> </CardFooter>
</Card> </Card>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment