"use client"; import React, { useState, useMemo } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { toast } from "sonner"; import { showErrorToast } from "@/lib/utils/toastUtils"; import { Plus, Search, Edit, Trash2, Eye, RotateCcw, SortAsc, SortDesc } from "lucide-react"; import { useWebTypes } from "@/hooks/admin/useWebTypes"; import Link from "next/link"; import { ResponsiveDataView, RDVColumn, RDVCardField } from "@/components/common/ResponsiveDataView"; import type { WebTypeStandard } from "@/hooks/admin/useWebTypes"; export default function WebTypesManagePage() { const [searchTerm, setSearchTerm] = useState(""); const [categoryFilter, setCategoryFilter] = useState("all"); const [activeFilter, setActiveFilter] = useState("Y"); const [sortField, setSortField] = useState("sort_order"); const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc"); const { webTypes, isLoading, error, deleteWebType, isDeleting, deleteError, refetch } = useWebTypes({ active: activeFilter === "all" ? undefined : activeFilter, search: searchTerm || undefined, category: categoryFilter === "all" ? undefined : categoryFilter, }); const categories = useMemo(() => { const uniqueCategories = Array.from(new Set(webTypes.map((wt) => wt.category).filter(Boolean))); return uniqueCategories.sort(); }, [webTypes]); const filteredAndSortedWebTypes = useMemo(() => { let filtered = [...webTypes]; filtered.sort((a, b) => { let aValue: any = a[sortField as keyof typeof a]; let bValue: any = b[sortField as keyof typeof b]; if (sortField === "sort_order") { aValue = aValue || 0; bValue = bValue || 0; } if (typeof aValue === "string") { aValue = aValue.toLowerCase(); } if (typeof bValue === "string") { bValue = bValue.toLowerCase(); } if (aValue < bValue) return sortDirection === "asc" ? -1 : 1; if (aValue > bValue) return sortDirection === "asc" ? 1 : -1; return 0; }); return filtered; }, [webTypes, sortField, sortDirection]); const handleDelete = async (webType: string, typeName: string) => { try { await deleteWebType(webType); toast.success(`웹타입 '${typeName}'이 삭제되었습니다.`); } catch (error) { showErrorToast("웹타입 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." }); } }; const resetFilters = () => { setSearchTerm(""); setCategoryFilter("all"); setActiveFilter("Y"); setSortField("sort_order"); setSortDirection("asc"); }; // 삭제 AlertDialog 렌더 헬퍼 const renderDeleteDialog = (wt: WebTypeStandard) => ( 웹타입 삭제 '{wt.type_name}' 웹타입을 삭제하시겠습니까?
이 작업은 되돌릴 수 없습니다.
취소 handleDelete(wt.web_type, wt.type_name)} disabled={isDeleting} className="bg-destructive hover:bg-destructive/90" > {isDeleting ? "삭제 중..." : "삭제"}
); const columns: RDVColumn[] = [ { key: "sort_order", label: "순서", width: "80px", render: (_val, wt) => {wt.sort_order || 0}, }, { key: "web_type", label: "웹타입 코드", hideOnMobile: true, render: (_val, wt) => {wt.web_type}, }, { key: "type_name", label: "웹타입명", render: (_val, wt) => (
{wt.type_name}
{wt.type_name_eng && (
{wt.type_name_eng}
)}
), }, { key: "category", label: "카테고리", render: (_val, wt) => {wt.category}, }, { key: "description", label: "설명", hideOnMobile: true, render: (_val, wt) => ( {wt.description || "-"} ), }, { key: "is_active", label: "상태", render: (_val, wt) => ( {wt.is_active === "Y" ? "활성화" : "비활성화"} ), }, { key: "updated_date", label: "최종 수정일", hideOnMobile: true, render: (_val, wt) => ( {wt.updated_date ? new Date(wt.updated_date).toLocaleDateString("ko-KR") : "-"} ), }, ]; const cardFields: RDVCardField[] = [ { label: "카테고리", render: (wt) => {wt.category}, }, { label: "순서", render: (wt) => String(wt.sort_order || 0), }, { label: "설명", render: (wt) => wt.description || "-", hideEmpty: true, }, { label: "수정일", render: (wt) => wt.updated_date ? new Date(wt.updated_date).toLocaleDateString("ko-KR") : "-", }, ]; return (
{/* 페이지 헤더 */}

웹타입 관리

화면관리에서 사용할 웹타입들을 관리합니다

{/* 에러 상태 */} {error && (

웹타입 목록을 불러오는데 실패했습니다.

)} {/* 검색 툴바 */}
setSearchTerm(e.target.value)} className="h-10 pl-10 text-sm" />
{/* 정렬 선택 */}
{/* 결과 수 */}
{filteredAndSortedWebTypes.length}개의 웹타입
{/* 삭제 에러 */} {deleteError && (

삭제 중 오류가 발생했습니다: {deleteError instanceof Error ? deleteError.message : "알 수 없는 오류"}

)} data={filteredAndSortedWebTypes} columns={columns} keyExtractor={(wt) => wt.web_type} isLoading={isLoading} emptyMessage="조건에 맞는 웹타입이 없습니다." skeletonCount={6} cardTitle={(wt) => wt.type_name} cardSubtitle={(wt) => ( <> {wt.type_name_eng && ( {wt.type_name_eng} / )} {wt.web_type} )} cardHeaderRight={(wt) => ( {wt.is_active === "Y" ? "활성화" : "비활성화"} )} cardFields={cardFields} actionsLabel="작업" actionsWidth="140px" renderActions={(wt) => ( <> {renderDeleteDialog(wt)} )} />
); }