diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index e641857b9f16bec46204e2a6a585c3e5c6a0d152..765d57c588a8b973c43ee75713f79c4fc6b2ad42 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Link } from 'react-router'; import { useAuthStore } from '@/stores/authStore'; -import { Router, ShieldCheck, HardDrive, CircleHelp } from 'lucide-react'; +import { ArrowLeftRight, ShieldCheck, Server, TextSearch, CircleHelp } from 'lucide-react'; import { Sidebar, SidebarContent, @@ -20,10 +20,10 @@ const data = { projects: ['aolda_edu', 'proxy_manager', 'blog'], menus: [ { - title: '웹 프록시 설정', + title: '웹 프록시 서버', items: [ { - icon: Router, + icon: ArrowLeftRight, title: '라우팅 설정', url: '#', isActive: true, @@ -40,13 +40,24 @@ const data = { title: 'SSH 포트포워딩', items: [ { - icon: HardDrive, + icon: Server, title: 'SSH 설정', url: '#', isActive: false, }, ], }, + { + title: '로그', + items: [ + { + icon: TextSearch, + title: '설정 변경 내역', + url: '#', + isActive: false, + }, + ], + }, ], }; diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7b81be9c22e5da6cd859a187cfa2ddfed2d43cbd --- /dev/null +++ b/src/components/ui/table.tsx @@ -0,0 +1,114 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Table({ className, ...props }: React.ComponentProps<"table">) { + return ( + <div + data-slot="table-container" + className="relative w-full overflow-x-auto" + > + <table + data-slot="table" + className={cn("w-full caption-bottom text-sm", className)} + {...props} + /> + </div> + ) +} + +function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { + return ( + <thead + data-slot="table-header" + className={cn("[&_tr]:border-b", className)} + {...props} + /> + ) +} + +function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { + return ( + <tbody + data-slot="table-body" + className={cn("[&_tr:last-child]:border-0", className)} + {...props} + /> + ) +} + +function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { + return ( + <tfoot + data-slot="table-footer" + className={cn( + "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", + className + )} + {...props} + /> + ) +} + +function TableRow({ className, ...props }: React.ComponentProps<"tr">) { + return ( + <tr + data-slot="table-row" + className={cn( + "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", + className + )} + {...props} + /> + ) +} + +function TableHead({ className, ...props }: React.ComponentProps<"th">) { + return ( + <th + data-slot="table-head" + className={cn( + "text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCell({ className, ...props }: React.ComponentProps<"td">) { + return ( + <td + data-slot="table-cell" + className={cn( + "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCaption({ + className, + ...props +}: React.ComponentProps<"caption">) { + return ( + <caption + data-slot="table-caption" + className={cn("text-muted-foreground mt-4 text-sm", className)} + {...props} + /> + ) +} + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index c1d2b3f06e4be20c59ce23e5857a4f905f7633ed..f186395cd28e0e54b7156a1b58693b1cc5b6ce68 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,13 +1,117 @@ +import { Split, ShieldCheck, Server, ArrowRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardDescription } from '@/components/ui/card'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; + +const logs = [ + { + id: 1, + timestamp: '2021-09-01 12:00:00', + action: '웹 라우팅 설정 변경 (console.ajou.app)', + username: 'admin', + }, + { + id: 2, + timestamp: '2021-09-01 12:01:00', + action: 'SSL 인증서 발급 (*.ajou.app)', + username: 'admin', + }, + { + id: 3, + timestamp: '2021-09-01 13:00:00', + 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() { return ( - <div className="flex flex-1 flex-col gap-4 p-4"> + <div className="flex flex-1 flex-col gap-4 p-6"> <h1 className="scroll-m-20 text-3xl font-semibold first:mt-0">Aolda Proxy Manager</h1> <div className="grid auto-rows-min gap-4 md:grid-cols-3"> - <div className="aspect-video rounded-xl bg-muted/50" /> - <div className="aspect-video rounded-xl bg-muted/50" /> - <div className="aspect-video rounded-xl bg-muted/50" /> + <Card> + <CardHeader> + <CardTitle className="text-xl">웹 라우팅</CardTitle> + </CardHeader> + <CardContent> + <div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> + <Split className="size-8" /> 0 + </div> + </CardContent> + <CardFooter> + <Button variant="ghost" className="ml-auto"> + 관리하기 <ArrowRight /> + </Button> + </CardFooter> + </Card> + <Card> + <CardHeader> + <CardTitle className="text-xl">SSL 인증서</CardTitle> + </CardHeader> + <CardContent> + <div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> + <ShieldCheck className="size-8" /> 0 + </div> + </CardContent> + <CardFooter> + <Button variant="ghost" className="ml-auto"> + 관리하기 <ArrowRight /> + </Button> + </CardFooter> + </Card> + <Card> + <CardHeader> + <CardTitle className="text-xl">SSH 포트포워딩</CardTitle> + </CardHeader> + <CardContent> + <div className="flex flex-row justify-center items-center gap-2 font-bold text-4xl"> + <Server className="size-8" /> 0 + </div> + </CardContent> + <CardFooter> + <Button variant="ghost" className="ml-auto"> + 관리하기 <ArrowRight /> + </Button> + </CardFooter> + </Card> </div> - <div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" /> + <Card> + <CardHeader> + <CardTitle className="text-xl">최근 활동</CardTitle> + <CardDescription>최근 설정 내역을 확인합니다</CardDescription> + </CardHeader> + <CardContent> + <Table> + <TableHeader> + <TableRow> + <TableHead className="w-40">시간</TableHead> + <TableHead>설정 내용</TableHead> + <TableHead className="w-32 text-center">사용자명</TableHead> + </TableRow> + </TableHeader> + <TableBody> + {logs.map((log) => ( + <TableRow key={log.id}> + <TableCell className="font-medium">{log.timestamp}</TableCell> + <TableCell>{log.action}</TableCell> + <TableCell className="text-center">{log.username}</TableCell> + </TableRow> + ))} + </TableBody> + </Table> + </CardContent> + <CardFooter> + <Button variant="ghost" className="ml-auto"> + 모든 기록 보기 <ArrowRight /> + </Button> + </CardFooter> + </Card> </div> ); }