"use client"; import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Plus, Search, Trash2, Edit2, } from "lucide-react"; import { getCategoryValues, addCategoryValue, updateCategoryValue, deleteCategoryValue, bulkDeleteCategoryValues, } from "@/lib/api/tableCategoryValue"; import { TableCategoryValue } from "@/types/tableCategoryValue"; import { useToast } from "@/hooks/use-toast"; import { CategoryValueEditDialog } from "./CategoryValueEditDialog"; import { CategoryValueAddDialog } from "./CategoryValueAddDialog"; interface CategoryValueManagerProps { tableName: string; columnName: string; columnLabel: string; onValueCountChange?: (count: number) => void; } export const CategoryValueManager: React.FC = ({ tableName, columnName, columnLabel, onValueCountChange, }) => { const { toast } = useToast(); const [values, setValues] = useState([]); const [filteredValues, setFilteredValues] = useState( [] ); const [selectedValueIds, setSelectedValueIds] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [isLoading, setIsLoading] = useState(false); const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); const [editingValue, setEditingValue] = useState( null ); const [showInactive, setShowInactive] = useState(false); // 비활성 항목 표시 옵션 (기본: 숨김) // 카테고리 값 로드 useEffect(() => { loadCategoryValues(); }, [tableName, columnName]); // 검색 필터링 + 비활성 필터링 useEffect(() => { let filtered = values; // 비활성 항목 필터링 (기본: 활성만 표시, 체크하면 비활성도 표시) if (!showInactive) { filtered = filtered.filter((v) => v.isActive !== false); } // 검색어 필터링 if (searchQuery) { filtered = filtered.filter( (v) => v.valueCode.toLowerCase().includes(searchQuery.toLowerCase()) || v.valueLabel.toLowerCase().includes(searchQuery.toLowerCase()) ); } setFilteredValues(filtered); }, [searchQuery, values, showInactive]); const loadCategoryValues = async () => { setIsLoading(true); try { // includeInactive: true로 비활성 값도 포함 const response = await getCategoryValues(tableName, columnName, true); if (response.success && response.data) { setValues(response.data); setFilteredValues(response.data); onValueCountChange?.(response.data.length); } } catch (error) { console.error("카테고리 값 로드 실패:", error); toast({ title: "오류", description: "카테고리 값을 불러올 수 없습니다", variant: "destructive", }); } finally { setIsLoading(false); } }; const handleAddValue = async (newValue: TableCategoryValue) => { try { const response = await addCategoryValue({ ...newValue, tableName, columnName, }); if (response.success && response.data) { await loadCategoryValues(); setIsAddDialogOpen(false); toast({ title: "성공", description: "카테고리 값이 추가되었습니다", }); } else { console.error("❌ 카테고리 값 추가 실패:", response); toast({ title: "오류", description: response.error || "카테고리 값 추가에 실패했습니다", variant: "destructive", }); } } catch (error: any) { console.error("❌ 카테고리 값 추가 예외:", error); toast({ title: "오류", description: error.message || "카테고리 값 추가에 실패했습니다", variant: "destructive", }); } }; const handleUpdateValue = async ( valueId: number, updates: Partial ) => { try { const response = await updateCategoryValue(valueId, updates); if (response.success) { await loadCategoryValues(); setEditingValue(null); toast({ title: "성공", description: "카테고리 값이 수정되었습니다", }); } } catch (error) { toast({ title: "오류", description: "카테고리 값 수정에 실패했습니다", variant: "destructive", }); } }; const handleDeleteValue = async (valueId: number) => { if (!confirm("정말로 이 카테고리 값을 삭제하시겠습니까?")) { return; } try { const response = await deleteCategoryValue(valueId); if (response.success) { await loadCategoryValues(); toast({ title: "성공", description: "카테고리 값이 삭제되었습니다", }); } } catch (error) { toast({ title: "오류", description: "카테고리 값 삭제에 실패했습니다", variant: "destructive", }); } }; const handleBulkDelete = async () => { if (selectedValueIds.length === 0) { toast({ title: "알림", description: "삭제할 항목을 선택해주세요", variant: "destructive", }); return; } if ( !confirm(`선택한 ${selectedValueIds.length}개 항목을 삭제하시겠습니까?`) ) { return; } try { const response = await bulkDeleteCategoryValues(selectedValueIds); if (response.success) { setSelectedValueIds([]); await loadCategoryValues(); toast({ title: "성공", description: response.message, }); } } catch (error) { toast({ title: "오류", description: "일괄 삭제에 실패했습니다", variant: "destructive", }); } }; const handleSelectAll = () => { if (selectedValueIds.length === filteredValues.length) { setSelectedValueIds([]); } else { setSelectedValueIds(filteredValues.map((v) => v.valueId!)); } }; const handleSelectValue = (valueId: number) => { setSelectedValueIds((prev) => prev.includes(valueId) ? prev.filter((id) => id !== valueId) : [...prev, valueId] ); }; const handleToggleActive = async (valueId: number, currentIsActive: boolean) => { try { const response = await updateCategoryValue(valueId, { isActive: !currentIsActive, }); if (response.success) { await loadCategoryValues(); toast({ title: "성공", description: `카테고리 값이 ${!currentIsActive ? "활성화" : "비활성화"}되었습니다`, }); } else { toast({ title: "오류", description: response.error || "상태 변경에 실패했습니다", variant: "destructive", }); } } catch (error: any) { console.error("❌ 활성 상태 변경 실패:", error); toast({ title: "오류", description: error.message || "상태 변경에 실패했습니다", variant: "destructive", }); } }; return (
{/* 헤더 */}

{columnLabel}

총 {filteredValues.length}개 항목

{/* 비활성 항목 표시 옵션 */}
setShowInactive(checked as boolean)} />
{/* 검색바 */}
setSearchQuery(e.target.value)} className="pl-9" />
{/* 값 목록 */}
{filteredValues.length === 0 ? (

{searchQuery ? "검색 결과가 없습니다" : "카테고리 값을 추가해주세요"}

) : (
{filteredValues.map((value) => { const isInactive = value.isActive === false; return (
handleSelectValue(value.valueId!)} />
{/* 색상 표시 (앞쪽으로 이동) */} {value.color && (
)} {/* 라벨 */} {value.valueLabel} {/* 설명 */} {value.description && ( - {value.description} )} {/* 기본값 배지 */} {value.isDefault && ( 기본값 )} {/* 비활성 배지 */} {isInactive && ( 비활성 )}
handleToggleActive( value.valueId!, value.isActive !== false ) } className="data-[state=checked]:bg-emerald-500" />
); })}
)}
{/* 푸터: 일괄 작업 */} {selectedValueIds.length > 0 && (
0 } onCheckedChange={handleSelectAll} /> {selectedValueIds.length}개 선택됨
)} {/* 추가 다이얼로그 */} {/* 편집 다이얼로그 */} {editingValue && ( !open && setEditingValue(null)} value={editingValue} onUpdate={handleUpdateValue} columnLabel={columnLabel} /> )}
); };