From 159e7768bb465559c5c1caaf8080d11eb5f7a221 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Mon, 9 Mar 2026 21:04:33 +0900 Subject: [PATCH] [agent-pipeline] pipe-20260309112447-f5iu round-3 --- .../automaticMng/exCallConfList/page.tsx | 519 ++++++++++-------- .../admin/automaticMng/flowMgmtList/page.tsx | 10 +- 2 files changed, 287 insertions(+), 242 deletions(-) diff --git a/frontend/app/(main)/admin/automaticMng/exCallConfList/page.tsx b/frontend/app/(main)/admin/automaticMng/exCallConfList/page.tsx index f98a98d1..94a83225 100644 --- a/frontend/app/(main)/admin/automaticMng/exCallConfList/page.tsx +++ b/frontend/app/(main)/admin/automaticMng/exCallConfList/page.tsx @@ -3,10 +3,8 @@ import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { Plus, Search, Edit, Trash2, TestTube, Filter } from "lucide-react"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Plus, Search, Edit, Trash2, TestTube } from "lucide-react"; import { toast } from "sonner"; import { showErrorToast } from "@/lib/utils/toastUtils"; import { @@ -29,9 +27,16 @@ import { 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 [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(""); const [filter, setFilter] = useState({ @@ -50,15 +55,17 @@ export default function ExternalCallConfigsPage() { const fetchConfigs = async () => { try { setLoading(true); - const response = await ExternalCallConfigAPI.getConfigs({ - ...filter, - search: searchQuery.trim() || undefined, - }); + 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 || []); + setConfigs((response.data || []) as ExternalCallConfigWithDate[]); } else { - showErrorToast("외부 호출 설정 조회에 실패했습니다", response.message, { + showErrorToast("외부 호출 설정 조회에 실패했습니다", response.error, { guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.", }); } @@ -72,9 +79,10 @@ export default function ExternalCallConfigsPage() { } }; - // 초기 로드 및 필터/검색 변경 시 재조회 + // 초기 로드 및 필터 변경 시 재조회 useEffect(() => { fetchConfigs(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filter]); // 검색 실행 @@ -118,7 +126,7 @@ export default function ExternalCallConfigsPage() { toast.success("외부 호출 설정이 삭제되었습니다."); fetchConfigs(); } else { - showErrorToast("외부 호출 설정 삭제에 실패했습니다", response.message, { + showErrorToast("외부 호출 설정 삭제에 실패했습니다", response.error, { guidance: "잠시 후 다시 시도해 주세요.", }); } @@ -140,10 +148,10 @@ export default function ExternalCallConfigsPage() { try { const response = await ExternalCallConfigAPI.testConfig(config.id); - if (response.success && response.data?.success) { - toast.success(`테스트 성공: ${response.data.message}`); + if (response.success) { + toast.success(`테스트 성공: ${response.message || "정상"}`); } else { - toast.error(`테스트 실패: ${response.data?.message || response.message}`); + toast.error(`테스트 실패: ${response.message || response.error || "알 수 없는 오류"}`); } } catch (error) { console.error("외부 호출 설정 테스트 오류:", error); @@ -171,244 +179,283 @@ export default function ExternalCallConfigsPage() { 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" - /> -
-
- + {/* 검색 및 필터 영역 (반응형) */} +
+
+
+ + setSearchQuery(e.target.value)} + onKeyPress={handleSearchKeyPress} + className="h-10 pl-10 text-sm" + />
-
- - {/* 두 번째 줄: 필터 */} -
- - - - - -
+
- {/* 설정 목록 */} -
- {loading ? ( - // 로딩 상태 -
-
로딩 중...
-
- ) : configs.length === 0 ? ( - // 빈 상태 -
-
-

등록된 외부 호출 설정이 없습니다.

-

새 외부 호출을 추가해보세요.

-
-
- ) : ( - // 설정 테이블 목록 - - - - 설정명 - 호출 타입 - API 타입 - 설명 - 상태 - 생성일 - 작업 - - - - {configs.map((config) => ( - - {config.config_name} - - {getCallTypeLabel(config.call_type)} - - - {config.api_type ? ( - {getApiTypeLabel(config.api_type)} - ) : ( - - - )} - - -
- {config.description ? ( - - {config.description} - - ) : ( - - - )} -
-
- - - {config.is_active === "Y" ? "활성" : "비활성"} - - - - {config.created_date ? new Date(config.created_date).toLocaleDateString() : "-"} - - -
- - - -
-
-
- ))} -
-
+ {/* 필터 영역 */} +
+ + + + + +
+ + {/* 설정 목록 (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} - /> + {/* 외부 호출 설정 모달 */} + setIsModalOpen(false)} + onSave={handleModalSave} + editingConfig={editingConfig} + /> - {/* 삭제 확인 다이얼로그 */} - - - - 외부 호출 설정 삭제 - - "{configToDelete?.config_name}" 설정을 삭제하시겠습니까? -
이 작업은 되돌릴 수 없습니다. -
-
- - - 취소 - - - 삭제 - - -
-
+ {/* 삭제 확인 다이얼로그 */} + + + + 외부 호출 설정 삭제 + + "{configToDelete?.config_name}" 설정을 삭제하시겠습니까? +
이 작업은 되돌릴 수 없습니다. +
+
+ + + 취소 + + + 삭제 + + +
+
+ + {/* Scroll to Top 버튼 */} +
); } diff --git a/frontend/app/(main)/admin/automaticMng/flowMgmtList/page.tsx b/frontend/app/(main)/admin/automaticMng/flowMgmtList/page.tsx index b690bd30..9ade5843 100644 --- a/frontend/app/(main)/admin/automaticMng/flowMgmtList/page.tsx +++ b/frontend/app/(main)/admin/automaticMng/flowMgmtList/page.tsx @@ -33,7 +33,7 @@ import { cn } from "@/lib/utils"; import { formatErrorMessage } from "@/lib/utils/errorUtils"; import { tableManagementApi } from "@/lib/api/tableManagement"; import { ScrollToTop } from "@/components/common/ScrollToTop"; -import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection"; +import { ExternalDbConnectionAPI, ExternalDbConnection } from "@/lib/api/externalDbConnection"; import { ExternalRestApiConnectionAPI, ExternalRestApiConnection } from "@/lib/api/externalRestApiConnection"; import { ResponsiveDataView, RDVColumn, RDVCardField } from "@/components/common/ResponsiveDataView"; @@ -56,9 +56,7 @@ export default function FlowManagementPage() { const [openTableCombobox, setOpenTableCombobox] = useState(false); // 데이터 소스 타입: "internal" (내부 DB), "external_db_숫자" (외부 DB), "restapi_숫자" (REST API) const [selectedDbSource, setSelectedDbSource] = useState("internal"); - const [externalConnections, setExternalConnections] = useState< - Array<{ id: number; connection_name: string; db_type: string }> - >([]); + const [externalConnections, setExternalConnections] = useState([]); const [externalTableList, setExternalTableList] = useState([]); const [loadingExternalTables, setLoadingExternalTables] = useState(false); @@ -255,7 +253,7 @@ export default function FlowManagementPage() { .map((t: string | { tableName?: string; table_name?: string; tablename?: string; name?: string }) => typeof t === "string" ? t : t.tableName || t.table_name || t.tablename || t.name, ) - .filter(Boolean); + .filter((v): v is string => Boolean(v)); setMultiDbTableLists(prev => ({ ...prev, [connectionId]: tableNames })); } } catch (error) { @@ -447,7 +445,7 @@ export default function FlowManagementPage() { } console.log("✅ Calling createFlowDefinition with:", requestData); - const response = await createFlowDefinition(requestData as Parameters[0]); + const response = await createFlowDefinition(requestData as unknown as Parameters[0]); if (response.success && response.data) { toast({ title: "생성 완료",