diff --git a/frontend/app/(main)/admin/dashboard/page.tsx b/frontend/app/(main)/admin/dashboard/page.tsx index d1ca6125..97c1036c 100644 --- a/frontend/app/(main)/admin/dashboard/page.tsx +++ b/frontend/app/(main)/admin/dashboard/page.tsx @@ -1,20 +1,14 @@ "use client"; -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { dashboardApi } from "@/lib/api/dashboard"; import { Dashboard } from "@/lib/api/dashboard"; import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { AlertDialog, AlertDialogAction, @@ -25,8 +19,8 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Plus, Search, MoreVertical, Edit, Trash2, Copy, CheckCircle2 } from "lucide-react"; +import { useToast } from "@/hooks/use-toast"; +import { Plus, Search, Edit, Trash2, Copy, LayoutDashboard, MoreVertical } from "lucide-react"; /** * 대시보드 관리 페이지 @@ -35,27 +29,28 @@ import { Plus, Search, MoreVertical, Edit, Trash2, Copy, CheckCircle2 } from "lu */ export default function DashboardListPage() { const router = useRouter(); + const { toast } = useToast(); const [dashboards, setDashboards] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(""); - const [error, setError] = useState(null); // 모달 상태 const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState<{ id: string; title: string } | null>(null); - const [successDialogOpen, setSuccessDialogOpen] = useState(false); - const [successMessage, setSuccessMessage] = useState(""); // 대시보드 목록 로드 const loadDashboards = async () => { try { setLoading(true); - setError(null); const result = await dashboardApi.getMyDashboards({ search: searchTerm }); setDashboards(result.dashboards); } catch (err) { console.error("Failed to load dashboards:", err); - setError("대시보드 목록을 불러오는데 실패했습니다."); + toast({ + title: "오류", + description: "대시보드 목록을 불러오는데 실패했습니다.", + variant: "destructive", + }); } finally { setLoading(false); } @@ -63,6 +58,7 @@ export default function DashboardListPage() { useEffect(() => { loadDashboards(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchTerm]); // 대시보드 삭제 확인 모달 열기 @@ -79,37 +75,48 @@ export default function DashboardListPage() { await dashboardApi.deleteDashboard(deleteTarget.id); setDeleteDialogOpen(false); setDeleteTarget(null); - setSuccessMessage("대시보드가 삭제되었습니다."); - setSuccessDialogOpen(true); + toast({ + title: "성공", + description: "대시보드가 삭제되었습니다.", + }); loadDashboards(); } catch (err) { console.error("Failed to delete dashboard:", err); setDeleteDialogOpen(false); - setError("대시보드 삭제에 실패했습니다."); + toast({ + title: "오류", + description: "대시보드 삭제에 실패했습니다.", + variant: "destructive", + }); } }; // 대시보드 복사 const handleCopy = async (dashboard: Dashboard) => { try { - // 전체 대시보드 정보(요소 포함)를 가져오기 const fullDashboard = await dashboardApi.getDashboard(dashboard.id); - const newDashboard = await dashboardApi.createDashboard({ + await dashboardApi.createDashboard({ title: `${fullDashboard.title} (복사본)`, description: fullDashboard.description, elements: fullDashboard.elements || [], isPublic: false, tags: fullDashboard.tags, category: fullDashboard.category, - settings: (fullDashboard as any).settings, // 해상도와 배경색 설정도 복사 + settings: fullDashboard.settings as { resolution?: string; backgroundColor?: string }, + }); + toast({ + title: "성공", + description: "대시보드가 복사되었습니다.", }); - setSuccessMessage("대시보드가 복사되었습니다."); - setSuccessDialogOpen(true); loadDashboards(); } catch (err) { console.error("Failed to copy dashboard:", err); - setError("대시보드 복사에 실패했습니다."); + toast({ + title: "오류", + description: "대시보드 복사에 실패했습니다.", + variant: "destructive", + }); } }; @@ -119,120 +126,125 @@ export default function DashboardListPage() { year: "numeric", month: "2-digit", day: "2-digit", - hour: "2-digit", - minute: "2-digit", }); }; - if (loading) { - return ( -
-
-
로딩 중...
-
대시보드 목록을 불러오고 있습니다
-
-
- ); - } - return ( -
-
- {/* 헤더 */} -
-

대시보드 관리

-

대시보드를 생성하고 관리할 수 있습니다

-
- - {/* 액션 바 */} -
-
- - setSearchTerm(e.target.value)} - className="pl-9" - /> +
+
+ {/* 페이지 제목 */} +
+
+

대시보드 관리

+

대시보드를 생성하고 관리할 수 있습니다

-
- {/* 에러 메시지 */} - {error && ( - -

{error}

-
- )} + {/* 검색 및 필터 */} + + +
+
+ + setSearchTerm(e.target.value)} + className="w-64 pl-10" + /> +
+ +
+
+
{/* 대시보드 목록 */} - {dashboards.length === 0 ? ( - -
- -
-

대시보드가 없습니다

-

첫 번째 대시보드를 생성하여 데이터 시각화를 시작하세요

- + {loading ? ( +
+
로딩 중...
+
+ ) : dashboards.length === 0 ? ( + + +
+ +

등록된 대시보드가 없습니다

+

첫 번째 대시보드를 생성하여 데이터 시각화를 시작하세요.

+ +
+
) : ( - - - - - 제목 - 설명 - 생성일 - 수정일 - 작업 - - - - {dashboards.map((dashboard) => ( - - {dashboard.title} - - {dashboard.description || "-"} - - {formatDate(dashboard.createdAt)} - {formatDate(dashboard.updatedAt)} - - - - - - - router.push(`/admin/dashboard/edit/${dashboard.id}`)} - className="gap-2" - > - - 편집 - - handleCopy(dashboard)} className="gap-2"> - - 복사 - - handleDeleteClick(dashboard.id, dashboard.title)} - className="gap-2 text-red-600 focus:text-red-600" - > - - 삭제 - - - - + + +
+ + + 제목 + 설명 + 생성일 + 작업 - ))} - -
+ + + {dashboards.map((dashboard) => ( + + +
{dashboard.title}
+
+ + {dashboard.description || "-"} + + {formatDate(dashboard.createdAt)} + + + + + + +
+ + + +
+
+
+
+
+ ))} +
+ +
)}
@@ -241,36 +253,24 @@ export default function DashboardListPage() { - 대시보드 삭제 + 대시보드 삭제 확인 "{deleteTarget?.title}" 대시보드를 삭제하시겠습니까? -
이 작업은 되돌릴 수 없습니다. +
+ 이 작업은 되돌릴 수 없습니다.
취소 - + 삭제
- - {/* 성공 모달 */} - - - -
- -
- 완료 - {successMessage} -
-
- -
-
-
); }