import { useState, useEffect } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { LogOut, Home, ChevronRight, Bot, Play, Square, Youtube, Calendar, Clock, CheckCircle, XCircle, RefreshCw, Download } from 'lucide-react'; import Toast from '../../../components/Toast'; function AdminScheduleBots() { const navigate = useNavigate(); const [user, setUser] = useState(null); const [toast, setToast] = useState(null); const [bots, setBots] = useState([]); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(null); // 동기화 중인 봇 ID // Toast 자동 숨김 useEffect(() => { if (toast) { const timer = setTimeout(() => setToast(null), 3000); return () => clearTimeout(timer); } }, [toast]); useEffect(() => { const token = localStorage.getItem('adminToken'); const userData = localStorage.getItem('adminUser'); if (!token || !userData) { navigate('/admin'); return; } setUser(JSON.parse(userData)); fetchBots(); }, [navigate]); // 봇 목록 조회 const fetchBots = async () => { try { const token = localStorage.getItem('adminToken'); const response = await fetch('/api/admin/bots', { headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { const data = await response.json(); setBots(data); } } catch (error) { console.error('봇 목록 조회 오류:', error); setToast({ type: 'error', message: '봇 목록을 불러올 수 없습니다.' }); } finally { setLoading(false); } }; const handleLogout = () => { localStorage.removeItem('adminToken'); localStorage.removeItem('adminUser'); navigate('/admin'); }; // 봇 시작/정지 토글 const toggleBot = async (botId, currentStatus) => { try { const token = localStorage.getItem('adminToken'); const action = currentStatus === 'running' ? 'stop' : 'start'; const response = await fetch(`/api/admin/bots/${botId}/${action}`, { method: 'POST', headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { setToast({ type: 'success', message: action === 'start' ? '봇이 시작되었습니다.' : '봇이 정지되었습니다.' }); fetchBots(); // 목록 새로고침 } else { const data = await response.json(); setToast({ type: 'error', message: data.error || '작업 실패' }); } } catch (error) { console.error('봇 토글 오류:', error); setToast({ type: 'error', message: '작업 중 오류가 발생했습니다.' }); } }; // 전체 동기화 const syncAllVideos = async (botId) => { setSyncing(botId); try { const token = localStorage.getItem('adminToken'); const response = await fetch(`/api/admin/bots/${botId}/sync-all`, { method: 'POST', headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { const data = await response.json(); setToast({ type: 'success', message: `${data.addedCount}개 일정이 추가되었습니다. (전체 ${data.total}개)` }); fetchBots(); } else { const data = await response.json(); setToast({ type: 'error', message: data.error || '동기화 실패' }); } } catch (error) { console.error('전체 동기화 오류:', error); setToast({ type: 'error', message: '동기화 중 오류가 발생했습니다.' }); } finally { setSyncing(null); } }; // 상태 아이콘 및 색상 const getStatusInfo = (status) => { switch (status) { case 'running': return { icon: , text: '실행 중', color: 'text-green-500', bg: 'bg-green-50', dot: 'bg-green-500', }; case 'stopped': return { icon: , text: '정지됨', color: 'text-gray-400', bg: 'bg-gray-50', dot: 'bg-gray-400', }; case 'error': return { icon: , text: '오류', color: 'text-red-500', bg: 'bg-red-50', dot: 'bg-red-500', }; default: return { icon: null, text: '알 수 없음', color: 'text-gray-400', bg: 'bg-gray-50', dot: 'bg-gray-400', }; } }; // 시간 포맷 (DB에 KST로 저장되어 있으므로 그대로 표시) const formatTime = (dateString) => { if (!dateString) return '-'; // DB의 KST 시간을 UTC로 재해석하지 않도록 Z 접미사 제거 const cleanDateString = dateString.replace('Z', '').replace('T', ' '); const date = new Date(cleanDateString); return date.toLocaleString('ko-KR', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; return (
setToast(null)} /> {/* 헤더 */}
fromis_9 Admin
안녕하세요, {user?.username}
{/* 메인 콘텐츠 */}
{/* 브레드크럼 */}
일정 관리 봇 관리
{/* 타이틀 */}

봇 관리

일정 자동화 봇을 관리합니다

{/* 봇 통계 */}
전체 봇
{bots.length}
실행 중
{bots.filter(b => b.status === 'running').length}
정지됨
{bots.filter(b => b.status === 'stopped').length}
오류
{bots.filter(b => b.status === 'error').length}
{/* 봇 목록 */}

봇 목록

{loading ? (
) : bots.length === 0 ? (

등록된 봇이 없습니다

위의 버튼을 클릭하여 봇을 추가하세요

) : (
{bots.map((bot, index) => { const statusInfo = getStatusInfo(bot.status); return (
{/* 아이콘 */}
{/* 정보 */}

{bot.name}

{statusInfo.text} YouTube

채널: {bot.channel_name || bot.channel_id} | {bot.include_shorts ? ' Shorts 포함' : ' Shorts 제외'} | {bot.check_interval}분 간격

{/* 메타 정보 */}
마지막 체크: {formatTime(bot.last_check_at)} 추가된 일정: {bot.schedules_added}개
{/* 오류 메시지 */} {bot.status === 'error' && bot.error_message && (
⚠️ {bot.error_message}
)}
{/* 액션 버튼 */}
); })}
)}
); } export default AdminScheduleBots;