From e13f608d88787e0b32e8ee19c2766ba108db1797 Mon Sep 17 00:00:00 2001 From: caadiq Date: Sat, 27 Dec 2025 16:29:03 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=B0=8F=20=EC=9D=BC=EA=B4=84=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - alert 대신 다이얼로그로 삭제 확인 - 체크박스로 파일 선택 기능 - 전체 선택/해제 및 일괄 삭제 지원 - 삭제 중 로딩 상태 표시 --- frontend/src/pages/Admin.jsx | 188 +++++++++++++++++++++++++++++++---- 1 file changed, 166 insertions(+), 22 deletions(-) diff --git a/frontend/src/pages/Admin.jsx b/frontend/src/pages/Admin.jsx index 8bba6cb..4af3f42 100644 --- a/frontend/src/pages/Admin.jsx +++ b/frontend/src/pages/Admin.jsx @@ -89,6 +89,8 @@ export default function Admin({ isMobile = false }) { const [logLoading, setLogLoading] = useState(false); // 로그 로딩 const [serverDropdownOpen, setServerDropdownOpen] = useState(false); // 서버 드롭다운 const [typeDropdownOpen, setTypeDropdownOpen] = useState(false); // 타입 드롭다운 + const [deleteLogDialog, setDeleteLogDialog] = useState({ show: false, files: [], loading: false }); // 로그 삭제 다이얼로그 + const [selectedLogFiles, setSelectedLogFiles] = useState(new Set()); // 선택된 로그 파일 ID const logEndRef = useRef(null); const logContainerRef = useRef(null); const isInitialLoad = useRef(true); @@ -832,23 +834,66 @@ export default function Admin({ isMobile = false }) { } }; - // 로그 파일 삭제 - const deleteLogFile = async (file, e) => { + // 로그 파일 삭제 다이얼로그 열기 + const openDeleteLogDialog = (files, e) => { if (e) e.stopPropagation(); - if (!confirm(`${file.fileName} 파일을 삭제하시겠습니까?`)) return; + const fileArray = Array.isArray(files) ? files : [files]; + setDeleteLogDialog({ show: true, files: fileArray, loading: false }); + }; + + // 로그 파일 삭제 실행 + const executeDeleteLog = async () => { + setDeleteLogDialog(prev => ({ ...prev, loading: true })); try { const token = localStorage.getItem('token'); - const response = await fetch(`/api/admin/logfile?id=${file.id}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${token}` } - }); - if (response.ok) { - fetchLogFiles(); // 목록 새로고침 + for (const file of deleteLogDialog.files) { + await fetch(`/api/admin/logfile?id=${file.id}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${token}` } + }); } + + setToast(`${deleteLogDialog.files.length}개 로그 파일 삭제 완료`); + fetchLogFiles(); + setSelectedLogFiles(new Set()); } catch (error) { console.error('로그 파일 삭제 실패:', error); + setToast('삭제 실패', true); + } finally { + setDeleteLogDialog({ show: false, files: [], loading: false }); + } + }; + + // 로그 파일 선택 토글 + const toggleLogFileSelect = (fileId, e) => { + e.stopPropagation(); + setSelectedLogFiles(prev => { + const next = new Set(prev); + if (next.has(fileId)) { + next.delete(fileId); + } else { + next.add(fileId); + } + return next; + }); + }; + + // 전체 선택/해제 + const toggleSelectAllLogs = () => { + if (selectedLogFiles.size === logFiles.length) { + setSelectedLogFiles(new Set()); + } else { + setSelectedLogFiles(new Set(logFiles.map(f => f.id))); + } + }; + + // 선택된 파일 일괄 삭제 + const deleteSelectedLogs = () => { + const selectedFiles = logFiles.filter(f => selectedLogFiles.has(f.id)); + if (selectedFiles.length > 0) { + openDeleteLogDialog(selectedFiles); } }; @@ -1542,6 +1587,34 @@ export default function Admin({ isMobile = false }) { + {/* 일괄 삭제 컨트롤 */} + {logFiles.length > 0 && ( +
+
+ + {selectedLogFiles.size > 0 && ( + + {selectedLogFiles.size}개 선택됨 + + )} +
+ {selectedLogFiles.size > 0 && ( + + )} +
+ )} +
{logFiles.length === 0 ? (

로그 파일이 없습니다

@@ -1549,25 +1622,36 @@ export default function Admin({ isMobile = false }) { logFiles.map((file) => (
viewLogContent(file)} > -
-

{file.fileName}

-

- {file.fileSize} • {file.serverId} • - - {file.fileType} - -

+
+ toggleLogFileSelect(file.id, e)} + onClick={(e) => e.stopPropagation()} + className="w-4 h-4 rounded border-zinc-600 bg-zinc-700 text-emerald-500 focus:ring-emerald-500 focus:ring-offset-0" + /> +
+

{file.fileName}

+

+ {file.fileSize} • {file.serverId} • + + {file.fileType} + +

+
+ +
+ + + )} + {/* 로그 뷰어 다이얼로그 */} {logViewerOpen && (