From b46c04562f8bdec6d5c04378ad9f46a0c7db8afc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EB=8F=99=ED=98=84?= <hando1220@ajou.ac.kr>
Date: Sun, 2 Mar 2025 13:47:39 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EC=84=9C=20=EB=AA=A9?=
 =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/pages/certificate/List.tsx | 143 +++++++++++++++++++++++++++++++++
 src/routes.tsx                 |   4 +
 2 files changed, 147 insertions(+)
 create mode 100644 src/pages/certificate/List.tsx

diff --git a/src/pages/certificate/List.tsx b/src/pages/certificate/List.tsx
new file mode 100644
index 0000000..db5a5f5
--- /dev/null
+++ b/src/pages/certificate/List.tsx
@@ -0,0 +1,143 @@
+import { Filter, Plus, Trash, HardDrive, Globe } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent } from '@/components/ui/card';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
+import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
+import { Input } from '@/components/ui/input';
+import { Link } from 'react-router';
+import { Badge } from '@/components/ui/badge';
+
+const certificates = [
+  {
+    id: 1,
+    domain: '*.aoldacloud.com',
+    created_at: '2021-09-01 11:43:00',
+    updated_at: '2021-09-01 12:00:00',
+    email: 'admin@aoldacloud.com',
+    dns_challenge: 'cloudflare:gFMEHqxje9DPRHgyYgIVlRCkp2btVFTOkkcL1HlC1PlCsgjZK0sk9IgXgeLtvg0M',
+    expires_at: '2022-09-01 12:00:00',
+  },
+  {
+    id: 2,
+    domain: '*.ajou.app',
+    created_at: '2021-09-01 12:00:00',
+    updated_at: '2021-09-01 12:01:00',
+    email: 'admin@aoldacloud.com',
+    dns_challenge: 'cloudflare:gFMEHqxje9DPRHgyYgIVlRCkp2btVFTOkkcL1HlC1PlCsgjZK0sk9IgXgeLtvg0M',
+    expires_at: '2022-09-01 12:01:00',
+  },
+  {
+    id: 3,
+    domain: 'blog.username.blog',
+    created_at: '2021-09-01 12:01:00',
+    updated_at: '2021-09-01 13:00:00',
+    email: 'username@example.com',
+    dns_challenge: 'cloudflare:DFrPF3Rp70aUJOekkXobvx5yTzYGxo5cGvHJsPX3fKxVJLQsA78GcOL2v9I6Bw9s',
+    expires_at: '2022-09-01 13:00:00',
+  },
+  {
+    id: 4,
+    domain: 'test.aolda.app',
+    created_at: '2021-09-01 13:00:00',
+    updated_at: '2021-09-02 12:00:00',
+    email: 'aolda@example.com',
+    dns_challenge: null,
+    expires_at: '2022-09-02 12:00:00',
+  },
+];
+
+export default function CertificateList() {
+  return (
+    <div className="flex flex-1 flex-col gap-4 p-6">
+      <div className="flex justify-between mb-2">
+        <div>
+          <h1 className="scroll-m-20 text-3xl font-semibold first:mt-0">SSL 인증서 설정</h1>
+          <p className="mt-1 text-base text-gray-500">현재 {certificates.length}개의 SSL 인증서가 등록되어 있습니다.</p>
+        </div>
+        <Button className="ml-2" asChild>
+          <Link to="./create">
+            <Plus className="h-4 w-4" /> 새 인증서 추가
+          </Link>
+        </Button>
+      </div>
+      <Card>
+        <CardContent>
+          <div className="flex w-full items-center space-x-2 mb-4">
+            <Filter className="mr-3" />
+            <Input placeholder="도메인, 관리자 이메일로 검색..." />
+            <Button variant="secondary">검색</Button>
+          </div>
+          <Table>
+            <TableHeader>
+              <TableRow>
+                <TableHead>도메인</TableHead>
+                <TableHead>관리자 메일</TableHead>
+                <TableHead>인증서 만료일</TableHead>
+                <TableHead className="w-24 min-w-24 text-center">인증 방식</TableHead>
+                <TableHead className="w-16 min-w-16 text-center">작업</TableHead>
+              </TableRow>
+            </TableHeader>
+            <TableBody>
+              {certificates.map((certificate) => (
+                <TableRow key={certificate.id}>
+                  <TableCell>
+                    <HoverCard>
+                      <HoverCardTrigger>{certificate.domain}</HoverCardTrigger>
+                      <HoverCardContent className="w-80 whitespace-normal">
+                        <div className="flex justify-between space-x-4">
+                          <div className="space-y-1">
+                            <p className="text-sm font-semibold">
+                              {certificate.dns_challenge === null ? (
+                                <Badge variant="secondary" className="mr-2">
+                                  HTTP
+                                </Badge>
+                              ) : (
+                                <Badge variant="secondary" className="mr-2">
+                                  DNS
+                                </Badge>
+                              )}
+                              {certificate.domain}
+                            </p>
+                            <p className="text-sm">{certificate.email}</p>
+                            <p className="text-xs text-muted-foreground mt-2">{certificate.created_at} 생성</p>
+                            <p className="text-xs text-muted-foreground">{certificate.updated_at} 수정</p>
+                          </div>
+                        </div>
+                      </HoverCardContent>
+                    </HoverCard>
+                  </TableCell>
+                  <TableCell>{certificate.email}</TableCell>
+                  <TableCell>{certificate.expires_at}</TableCell>
+                  <TableCell>
+                    <div className="flex justify-center items-center gap-1">
+                      {certificate.dns_challenge === null ? (
+                        <Badge variant="secondary">
+                          <Globe className="h-3 w-3" />
+                          HTTP
+                        </Badge>
+                      ) : (
+                        <Badge variant="secondary">
+                          <HardDrive className="h-3 w-3" />
+                          DNS
+                        </Badge>
+                      )}
+                    </div>
+                  </TableCell>
+                  <TableCell>
+                    <div className="flex justify-center items-center gap-2">
+                      <Button variant="secondary" className="size-8">
+                        <Link to={`./delete/${certificate.id}`}>
+                          <Trash />
+                        </Link>
+                      </Button>
+                    </div>
+                  </TableCell>
+                </TableRow>
+              ))}
+            </TableBody>
+          </Table>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
diff --git a/src/routes.tsx b/src/routes.tsx
index 0d17aee..0fc068b 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -5,6 +5,7 @@ import Home from '@/pages/Home';
 import Login from '@/pages/Login';
 import RoutingList from '@/pages/routing/List';
 import RoutingCreate from '@/pages/routing/Create';
+import CertificateList from './pages/certificate/List';
 import ForwardingList from '@/pages/forwarding/List';
 import ForwardingCreate from './pages/forwarding/Create';
 
@@ -18,6 +19,9 @@ export default function AppRoutes() {
           <Route index element={<RoutingList />} />
           <Route path="create" element={<RoutingCreate />} />
         </Route>
+        <Route path="certificate">
+          <Route index element={<CertificateList />} />
+        </Route>
         <Route path="forwarding">
           <Route index element={<ForwardingList />} />
           <Route path="create" element={<ForwardingCreate />} />
-- 
GitLab