"use client"; import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Plus, Search, Edit, Trash2, TestTube } from "lucide-react"; import { toast } from "sonner"; import { showErrorToast } from "@/lib/utils/toastUtils"; import { ExternalCallConfigAPI, ExternalCallConfig, ExternalCallConfigFilter, CALL_TYPE_OPTIONS, API_TYPE_OPTIONS, ACTIVE_STATUS_OPTIONS, } from "@/lib/api/externalCallConfig"; import { ExternalCallConfigModal } from "@/components/admin/ExternalCallConfigModal"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { ResponsiveDataView, RDVColumn, RDVCardField } from "@/components/common/ResponsiveDataView"; import { ScrollToTop } from "@/components/common/ScrollToTop"; // API 응답에 실제로 포함되는 필드를 위한 확장 타입 type ExternalCallConfigWithDate = ExternalCallConfig & { created_date?: string; }; export default function ExternalCallConfigsPage() { const [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(""); const [filter, setFilter] = useState({ is_active: "Y", }); // 모달 상태 const [isModalOpen, setIsModalOpen] = useState(false); const [editingConfig, setEditingConfig] = useState(null); // 삭제 확인 다이얼로그 상태 const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [configToDelete, setConfigToDelete] = useState(null); // 외부 호출 설정 목록 조회 const fetchConfigs = async () => { try { setLoading(true); const filterWithSearch: Record = { ...filter }; const trimmed = searchQuery.trim(); if (trimmed) { filterWithSearch.search = trimmed; } const response = await ExternalCallConfigAPI.getConfigs(filterWithSearch as ExternalCallConfigFilter); if (response.success) { setConfigs((response.data || []) as ExternalCallConfigWithDate[]); } else { showErrorToast("외부 호출 설정 조회에 실패했습니다", response.error, { guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.", }); } } catch (error) { console.error("외부 호출 설정 조회 오류:", error); showErrorToast("외부 호출 설정 조회에 실패했습니다", error, { guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.", }); } finally { setLoading(false); } }; // 초기 로드 및 필터 변경 시 재조회 useEffect(() => { fetchConfigs(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [filter]); // 검색 실행 const handleSearch = () => { fetchConfigs(); }; // 검색 입력 시 엔터키 처리 const handleSearchKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter") { handleSearch(); } }; // 새 설정 추가 const handleAddConfig = () => { setEditingConfig(null); setIsModalOpen(true); }; // 설정 편집 const handleEditConfig = (config: ExternalCallConfig) => { setEditingConfig(config); setIsModalOpen(true); }; // 설정 삭제 확인 const handleDeleteConfig = (config: ExternalCallConfig) => { setConfigToDelete(config); setDeleteDialogOpen(true); }; // 설정 삭제 실행 const confirmDeleteConfig = async () => { if (!configToDelete?.id) return; try { const response = await ExternalCallConfigAPI.deleteConfig(configToDelete.id); if (response.success) { toast.success("외부 호출 설정이 삭제되었습니다."); fetchConfigs(); } else { showErrorToast("외부 호출 설정 삭제에 실패했습니다", response.error, { guidance: "잠시 후 다시 시도해 주세요.", }); } } catch (error) { console.error("외부 호출 설정 삭제 오류:", error); showErrorToast("외부 호출 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요.", }); } finally { setDeleteDialogOpen(false); setConfigToDelete(null); } }; // 설정 테스트 const handleTestConfig = async (config: ExternalCallConfig) => { if (!config.id) return; try { const response = await ExternalCallConfigAPI.testConfig(config.id); if (response.success) { toast.success(`테스트 성공: ${response.message || "정상"}`); } else { toast.error(`테스트 실패: ${response.message || response.error || "알 수 없는 오류"}`); } } catch (error) { console.error("외부 호출 설정 테스트 오류:", error); showErrorToast("외부 호출 테스트 실행에 실패했습니다", error, { guidance: "URL과 설정을 확인해 주세요.", }); } }; // 모달 저장 완료 시 목록 새로고침 const handleModalSave = () => { setIsModalOpen(false); setEditingConfig(null); fetchConfigs(); }; // 호출 타입 라벨 가져오기 const getCallTypeLabel = (callType: string) => { return CALL_TYPE_OPTIONS.find((option) => option.value === callType)?.label || callType; }; // API 타입 라벨 가져오기 const getApiTypeLabel = (apiType?: string) => { if (!apiType) return ""; return API_TYPE_OPTIONS.find((option) => option.value === apiType)?.label || apiType; }; // ResponsiveDataView 컬럼 정의 const columns: RDVColumn[] = [ { key: "config_name", label: "설정명", render: (_v, row) => {row.config_name}, }, { key: "call_type", label: "호출 타입", width: "120px", render: (_v, row) => {getCallTypeLabel(row.call_type)}, }, { key: "api_type", label: "API 타입", width: "120px", render: (_v, row) => row.api_type ? ( {getApiTypeLabel(row.api_type)} ) : ( - ), }, { key: "description", label: "설명", render: (_v, row) => row.description ? ( {row.description} ) : ( - ), }, { key: "is_active", label: "상태", width: "80px", render: (_v, row) => ( {row.is_active === "Y" ? "활성" : "비활성"} ), }, { key: "created_date", label: "생성일", width: "120px", render: (_v, row) => row.created_date ? new Date(row.created_date).toLocaleDateString() : "-", }, ]; // 모바일 카드 필드 정의 const cardFields: RDVCardField[] = [ { label: "호출 타입", render: (c) => {getCallTypeLabel(c.call_type)}, }, { label: "API 타입", render: (c) => c.api_type ? ( {getApiTypeLabel(c.api_type)} ) : ( - ), }, { label: "설명", render: (c) => ( {c.description || "-"} ), }, { label: "생성일", render: (c) => c.created_date ? new Date(c.created_date).toLocaleDateString() : "-", }, ]; return (
{/* 페이지 헤더 */}

외부 호출 관리

Discord, Slack, 카카오톡 등 외부 호출 설정을 관리합니다.

{/* 검색 및 필터 영역 (반응형) */}
setSearchQuery(e.target.value)} onKeyPress={handleSearchKeyPress} className="h-10 pl-10 text-sm" />
{/* 필터 영역 */}
{/* 설정 목록 (ResponsiveDataView) */} data={configs} columns={columns} keyExtractor={(c) => String(c.id || c.config_name)} isLoading={loading} emptyMessage="등록된 외부 호출 설정이 없습니다." skeletonCount={5} cardTitle={(c) => c.config_name} cardSubtitle={(c) => c.description || "설명 없음"} cardHeaderRight={(c) => ( {c.is_active === "Y" ? "활성" : "비활성"} )} cardFields={cardFields} renderActions={(c) => ( <> )} actionsWidth="200px" /> {/* 외부 호출 설정 모달 */} setIsModalOpen(false)} onSave={handleModalSave} editingConfig={editingConfig} /> {/* 삭제 확인 다이얼로그 */} 외부 호출 설정 삭제 "{configToDelete?.config_name}" 설정을 삭제하시겠습니까?
이 작업은 되돌릴 수 없습니다.
취소 삭제
{/* Scroll to Top 버튼 */}
); }