Skip to content
Snippets Groups Projects
Select Git revision
  • 04c931c493adc79c4363b9be4362b97865c31a37
  • main default protected
2 results

List.tsx

Blame
  • List.tsx 8.64 KiB
    import { useEffect, useState } from 'react';
    import { Link } from 'react-router';
    import { Filter, Plus, Check, X, Pencil, Trash } from 'lucide-react';
    import { toast } from 'sonner';
    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 { Badge } from '@/components/ui/badge';
    import { Skeleton } from '@/components/ui/skeleton';
    import { useAuthStore } from '@/stores/authStore';
    import { Routing } from '@/types/routing';
    import {
      AlertDialog,
      AlertDialogContent,
      AlertDialogAction,
      AlertDialogCancel,
      AlertDialogHeader,
      AlertDialogTitle,
      AlertDialogDescription,
      AlertDialogFooter,
    } from '@/components/ui/alert-dialog';
    
    export default function RoutingList() {
      const { authFetch, selectedProject } = useAuthStore();
      const [routings, setRoutings] = useState<Routing[] | null>(null);
      const [selectedRouting, setSelectedRouting] = useState<Routing | null>(null);
    
      useEffect(() => {
        authFetch(`/api/routings?projectId=${selectedProject?.id}`)
          .then((response) => {
            if (!response.ok) throw Error(`라우팅 목록 조회 실패: ${response.status}`);
    
            return response.json();
          })
          .then(({ contents }) => {
            setRoutings(contents);
          })
          .catch((error) => {
            console.error(error);
            toast.error('라우팅 정보를 조회할 수 없습니다.');
          });
      }, [authFetch, selectedProject]);
    
      const handleDelete = () => {
        if (selectedRouting === null) throw Error('selectedRouting is null');
    
        authFetch(`/api/routing?routingId=${selectedRouting.id}`, {
          method: 'DELETE',
        }).then((response) => {
          if (!response.ok) {
            console.error(response);
            toast.error('라우팅 설정 삭제에 실패했습니다');
          } else {
            toast.warning('라우팅 설정이 삭제되었습니다');
            setRoutings((prev) => prev!.filter((routing) => routing.id !== selectedRouting.id));
          }
        });
    
        setSelectedRouting(null);
      };
    
      return (
        <div className="flex flex-1 flex-col gap-4 p-6">
          <div className="flex flex-col sm:flex-row gap-4 justify-between mb-2">
            <div>
              <h1 className="scroll-m-20 text-3xl font-semibold first:mt-0">웹 라우팅 설정</h1>
              {routings === null ? (
                <Skeleton className="w-[18rem] h-[1rem] mt-2 rounded-full" />
              ) : (
                <p className="mt-1 text-base text-gray-500">
                  <p className="mt-1 text-base text-gray-500">현재 {routings.length}개의 웹 프록시가 설정되어 있습니다.</p>
                </p>
              )}
            </div>
            <Button 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="이름, 도메인, 인스턴스 IP로 검색..." />
                <Button variant="secondary">검색</Button>
              </div>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead className="w-48">이름</TableHead>
                    <TableHead>도메인</TableHead>
                    <TableHead>인스턴스 IP</TableHead>
                    <TableHead>포트</TableHead>
                    <TableHead className="w-32 min-w-32 text-center">기타 설정</TableHead>
                    <TableHead className="w-32 min-w-32 text-center">작업</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {routings === null ? (
                    <>
                      <TableRow>
                        <TableCell colSpan={6}>
                          <Skeleton className="w-full h-[1rem] my-2 rounded-full" />
                        </TableCell>
                      </TableRow>
                      <TableRow>
                        <TableCell colSpan={6}>
                          <Skeleton className="w-full h-[1rem] my-2 rounded-full" />
                        </TableCell>
                      </TableRow>
                      <TableRow>
                        <TableCell colSpan={6}>
                          <Skeleton className="w-full h-[1rem] my-2 rounded-full" />
                        </TableCell>
                      </TableRow>
                    </>
                  ) : routings.length === 0 ? (
                    <TableRow>
                      <TableCell colSpan={6} className="text-center text-muted-foreground">
                        현재 프로젝트에 등록된 웹 프록시 설정이 없습니다.
                      </TableCell>
                    </TableRow>
                  ) : (
                    routings.map((routing) => (
                      <TableRow key={routing.id}>
                        <TableCell className="truncate max-w-48">
                          <HoverCard>
                            <HoverCardTrigger>{routing.name}</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">{routing.name}</p>
                                  <p className="text-sm">
                                    {routing.domain} ({routing.ip}:{routing.port})
                                  </p>
                                  <p className="text-xs text-muted-foreground mt-2">{routing.createdAt} 생성</p>
                                  <p className="text-xs text-muted-foreground">{routing.updatedAt} 수정</p>
                                </div>
                              </div>
                            </HoverCardContent>
                          </HoverCard>
                        </TableCell>
                        <TableCell>{routing.domain}</TableCell>
                        <TableCell>{routing.ip}</TableCell>
                        <TableCell>{routing.port}</TableCell>
                        <TableCell>
                          <div className="flex justify-center items-center gap-1">
                            {routing.certificateId !== undefined ? (
                              <Badge variant="secondary">
                                <Check className="h-3 w-3" />
                                SSL
                              </Badge>
                            ) : (
                              <Badge variant="outline" className="text-gray-500">
                                <X className="h-3 w-3" />
                                SSL
                              </Badge>
                            )}
                            {routing.caching ? (
                              <Badge variant="secondary">
                                <Check className="h-3 w-3" />
                                캐시
                              </Badge>
                            ) : (
                              <Badge variant="outline" className="text-gray-500">
                                <X className="h-3 w-3" />
                                캐시
                              </Badge>
                            )}
                          </div>
                        </TableCell>
                        <TableCell>
                          <div className="flex justify-center items-center gap-2">
                            <Button variant="secondary" className="size-8">
                              <Link to={`./edit/${routing.id}`}>
                                <Pencil />
                              </Link>
                            </Button>
                            <Button variant="secondary" className="size-8" onClick={() => setSelectedRouting(routing)}>
                              <Trash />
                            </Button>
                          </div>
                        </TableCell>
                      </TableRow>
                    ))
                  )}
                </TableBody>
              </Table>
            </CardContent>
          </Card>
    
          <AlertDialog open={selectedRouting !== null} onOpenChange={(open) => open || setSelectedRouting(null)}>
            <AlertDialogContent>
              <AlertDialogHeader>
                <AlertDialogTitle>웹 라우팅 설정 삭제</AlertDialogTitle>
                <AlertDialogDescription>
                  정말 '{selectedRouting?.name}' 라우팅 설정을 삭제하시겠습니까?
                </AlertDialogDescription>
              </AlertDialogHeader>
              <AlertDialogFooter>
                <AlertDialogCancel>취소</AlertDialogCancel>
                <AlertDialogAction onClick={handleDelete}>삭제</AlertDialogAction>
              </AlertDialogFooter>
            </AlertDialogContent>
          </AlertDialog>
        </div>
      );
    }