feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
import { Link } from 'react-router-dom';
|
|
|
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
2026-01-22 22:04:37 +09:00
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
2026-01-23 10:21:06 +09:00
|
|
|
import { Home, ChevronRight, Bot, CheckCircle, XCircle, RefreshCw } from 'lucide-react';
|
|
|
|
|
import { Toast, Tooltip, AnimatedNumber } from '@/components/common';
|
|
|
|
|
import { AdminLayout, BotCard } from '@/components/pc/admin';
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
import { useAdminAuth } from '@/hooks/pc/admin';
|
|
|
|
|
import { useToast } from '@/hooks/common';
|
2026-01-22 21:39:01 +09:00
|
|
|
import * as botsApi from '@/api/admin/bots';
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
|
2026-01-22 22:04:37 +09:00
|
|
|
// 애니메이션 variants
|
|
|
|
|
const containerVariants = {
|
|
|
|
|
hidden: { opacity: 0 },
|
|
|
|
|
visible: {
|
|
|
|
|
opacity: 1,
|
|
|
|
|
transition: { staggerChildren: 0.1 },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const itemVariants = {
|
|
|
|
|
hidden: { opacity: 0, y: 20 },
|
|
|
|
|
visible: {
|
|
|
|
|
opacity: 1,
|
|
|
|
|
y: 0,
|
|
|
|
|
transition: { duration: 0.4, ease: 'easeOut' },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
function ScheduleBots() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { user, isAuthenticated } = useAdminAuth();
|
|
|
|
|
const { toast, setToast } = useToast();
|
|
|
|
|
const [isInitialLoad, setIsInitialLoad] = useState(true); // 첫 로드 여부 (애니메이션용)
|
|
|
|
|
const [syncing, setSyncing] = useState(null); // 동기화 중인 봇 ID
|
|
|
|
|
const [quotaWarning, setQuotaWarning] = useState(null); // 할당량 경고 상태
|
|
|
|
|
|
|
|
|
|
// 봇 목록 조회
|
|
|
|
|
const {
|
|
|
|
|
data: bots = [],
|
|
|
|
|
isLoading: loading,
|
|
|
|
|
isError,
|
|
|
|
|
refetch: fetchBots,
|
|
|
|
|
} = useQuery({
|
|
|
|
|
queryKey: ['admin', 'bots'],
|
|
|
|
|
queryFn: botsApi.getBots,
|
|
|
|
|
enabled: isAuthenticated,
|
|
|
|
|
staleTime: 30000,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 할당량 경고 상태 조회
|
|
|
|
|
const { data: quotaData } = useQuery({
|
|
|
|
|
queryKey: ['admin', 'bots', 'quota'],
|
|
|
|
|
queryFn: botsApi.getQuotaWarning,
|
|
|
|
|
enabled: isAuthenticated,
|
|
|
|
|
staleTime: 60000,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 에러 처리
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isError) {
|
|
|
|
|
setToast({ type: 'error', message: '봇 목록을 불러올 수 없습니다.' });
|
|
|
|
|
}
|
|
|
|
|
}, [isError, setToast]);
|
|
|
|
|
|
|
|
|
|
// 할당량 경고 상태 업데이트
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (quotaData?.active) {
|
|
|
|
|
setQuotaWarning(quotaData);
|
|
|
|
|
}
|
|
|
|
|
}, [quotaData]);
|
|
|
|
|
|
|
|
|
|
// 할당량 경고 해제
|
|
|
|
|
const handleDismissQuotaWarning = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await botsApi.dismissQuotaWarning();
|
|
|
|
|
setQuotaWarning(null);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('할당량 경고 해제 오류:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 봇 시작/정지 토글
|
|
|
|
|
const toggleBot = async (botId, currentStatus, botName) => {
|
|
|
|
|
try {
|
|
|
|
|
const action = currentStatus === 'running' ? 'stop' : 'start';
|
|
|
|
|
|
|
|
|
|
if (action === 'start') {
|
|
|
|
|
await botsApi.startBot(botId);
|
|
|
|
|
} else {
|
|
|
|
|
await botsApi.stopBot(botId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 캐시 업데이트 (전체 목록 새로고침 대신)
|
|
|
|
|
queryClient.setQueryData(['admin', 'bots'], (prev) =>
|
|
|
|
|
prev?.map((bot) =>
|
|
|
|
|
bot.id === botId ? { ...bot, status: action === 'start' ? 'running' : 'stopped' } : bot
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
setToast({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message:
|
|
|
|
|
action === 'start' ? `${botName} 봇이 시작되었습니다.` : `${botName} 봇이 정지되었습니다.`,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('봇 토글 오류:', error);
|
|
|
|
|
setToast({ type: 'error', message: error.message || '작업 중 오류가 발생했습니다.' });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 전체 동기화
|
|
|
|
|
const handleSyncAllVideos = async (botId) => {
|
|
|
|
|
setSyncing(botId);
|
|
|
|
|
try {
|
|
|
|
|
const data = await botsApi.syncAllVideos(botId);
|
|
|
|
|
setToast({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: `${data.addedCount}개 일정이 추가되었습니다. (전체 ${data.total}개)`,
|
|
|
|
|
});
|
|
|
|
|
fetchBots();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('전체 동기화 오류:', error);
|
|
|
|
|
setToast({ type: 'error', message: error.message || '동기화 중 오류가 발생했습니다.' });
|
|
|
|
|
fetchBots();
|
|
|
|
|
} finally {
|
|
|
|
|
setSyncing(null);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 상태 아이콘 및 색상
|
|
|
|
|
const getStatusInfo = (status) => {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 'running':
|
|
|
|
|
return {
|
|
|
|
|
icon: <CheckCircle size={16} />,
|
|
|
|
|
text: '실행 중',
|
|
|
|
|
color: 'text-green-500',
|
|
|
|
|
bg: 'bg-green-50',
|
|
|
|
|
dot: 'bg-green-500',
|
|
|
|
|
};
|
|
|
|
|
case 'stopped':
|
|
|
|
|
return {
|
|
|
|
|
icon: <XCircle size={16} />,
|
|
|
|
|
text: '정지됨',
|
|
|
|
|
color: 'text-gray-400',
|
|
|
|
|
bg: 'bg-gray-50',
|
|
|
|
|
dot: 'bg-gray-400',
|
|
|
|
|
};
|
|
|
|
|
case 'error':
|
|
|
|
|
return {
|
|
|
|
|
icon: <XCircle size={16} />,
|
|
|
|
|
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',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 시간 포맷 (UTC → KST 변환)
|
|
|
|
|
const formatTime = (dateString) => {
|
|
|
|
|
if (!dateString) return '-';
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
return date.toLocaleString('ko-KR', {
|
|
|
|
|
month: 'short',
|
|
|
|
|
day: 'numeric',
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 간격 포맷 (분 → 분/시간/일)
|
|
|
|
|
const formatInterval = (minutes) => {
|
|
|
|
|
if (!minutes) return '-';
|
|
|
|
|
if (minutes >= 1440) {
|
|
|
|
|
const days = Math.floor(minutes / 1440);
|
|
|
|
|
return `${days}일`;
|
|
|
|
|
} else if (minutes >= 60) {
|
|
|
|
|
const hours = Math.floor(minutes / 60);
|
|
|
|
|
return `${hours}시간`;
|
|
|
|
|
}
|
|
|
|
|
return `${minutes}분`;
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-22 22:04:37 +09:00
|
|
|
// 통계 계산
|
|
|
|
|
const runningCount = bots.filter((b) => b.status === 'running').length;
|
|
|
|
|
const stoppedCount = bots.filter((b) => b.status === 'stopped').length;
|
|
|
|
|
const errorCount = bots.filter((b) => b.status === 'error').length;
|
|
|
|
|
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
return (
|
|
|
|
|
<AdminLayout user={user}>
|
|
|
|
|
<Toast toast={toast} onClose={() => setToast(null)} />
|
|
|
|
|
|
|
|
|
|
{/* 메인 콘텐츠 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<motion.div
|
|
|
|
|
className="max-w-7xl mx-auto px-6 py-8"
|
|
|
|
|
variants={containerVariants}
|
|
|
|
|
initial="hidden"
|
|
|
|
|
animate="visible"
|
|
|
|
|
>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
{/* 브레드크럼 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<motion.div variants={itemVariants} className="flex items-center gap-2 text-sm text-gray-400 mb-8">
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
<Link to="/admin/dashboard" className="hover:text-primary transition-colors">
|
|
|
|
|
<Home size={16} />
|
|
|
|
|
</Link>
|
|
|
|
|
<ChevronRight size={14} />
|
|
|
|
|
<Link to="/admin/schedule" className="hover:text-primary transition-colors">
|
|
|
|
|
일정 관리
|
|
|
|
|
</Link>
|
|
|
|
|
<ChevronRight size={14} />
|
|
|
|
|
<span className="text-gray-700">봇 관리</span>
|
2026-01-22 22:04:37 +09:00
|
|
|
</motion.div>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
|
|
|
|
|
{/* 타이틀 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<motion.div variants={itemVariants} className="mb-8">
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">봇 관리</h1>
|
|
|
|
|
<p className="text-gray-500">일정 자동화 봇을 관리합니다</p>
|
2026-01-22 22:04:37 +09:00
|
|
|
</motion.div>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
|
|
|
|
|
{/* 봇 통계 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<motion.div variants={itemVariants} className="grid grid-cols-4 gap-4 mb-8">
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
<div className="bg-white rounded-xl p-5 border border-gray-100">
|
|
|
|
|
<div className="text-sm text-gray-500 mb-1">전체 봇</div>
|
2026-01-22 22:04:37 +09:00
|
|
|
<div className="text-2xl font-bold text-gray-900">
|
|
|
|
|
<AnimatedNumber value={bots.length} />
|
|
|
|
|
</div>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 border border-gray-100">
|
|
|
|
|
<div className="text-sm text-gray-500 mb-1">실행 중</div>
|
|
|
|
|
<div className="text-2xl font-bold text-green-500">
|
2026-01-22 22:04:37 +09:00
|
|
|
<AnimatedNumber value={runningCount} />
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 border border-gray-100">
|
|
|
|
|
<div className="text-sm text-gray-500 mb-1">정지됨</div>
|
|
|
|
|
<div className="text-2xl font-bold text-gray-400">
|
2026-01-22 22:04:37 +09:00
|
|
|
<AnimatedNumber value={stoppedCount} />
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 border border-gray-100">
|
|
|
|
|
<div className="text-sm text-gray-500 mb-1">오류</div>
|
|
|
|
|
<div className="text-2xl font-bold text-red-500">
|
2026-01-22 22:04:37 +09:00
|
|
|
<AnimatedNumber value={errorCount} />
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-01-22 22:04:37 +09:00
|
|
|
</motion.div>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
|
|
|
|
|
{/* API 할당량 경고 배너 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<AnimatePresence>
|
|
|
|
|
{quotaWarning && (
|
|
|
|
|
<motion.div
|
|
|
|
|
initial={{ opacity: 0, height: 0 }}
|
|
|
|
|
animate={{ opacity: 1, height: 'auto' }}
|
|
|
|
|
exit={{ opacity: 0, height: 0 }}
|
|
|
|
|
className="bg-red-50 border border-red-200 rounded-xl p-4 mb-8 flex items-start justify-between overflow-hidden"
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
>
|
2026-01-22 22:04:37 +09:00
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
<div className="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center flex-shrink-0">
|
|
|
|
|
<XCircle size={18} className="text-red-500" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<h3 className="font-bold text-red-700">YouTube API 할당량 경고</h3>
|
|
|
|
|
<p className="text-sm text-red-600 mt-0.5">{quotaWarning.message}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleDismissQuotaWarning}
|
|
|
|
|
className="text-red-400 hover:text-red-600 transition-colors text-sm px-2 py-1"
|
|
|
|
|
>
|
|
|
|
|
닫기
|
|
|
|
|
</button>
|
|
|
|
|
</motion.div>
|
|
|
|
|
)}
|
|
|
|
|
</AnimatePresence>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
|
|
|
|
|
{/* 봇 목록 */}
|
2026-01-22 22:04:37 +09:00
|
|
|
<motion.div variants={itemVariants} className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
|
|
|
|
|
<h2 className="font-bold text-gray-900">봇 목록</h2>
|
|
|
|
|
<Tooltip text="새로고침">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setIsInitialLoad(true);
|
|
|
|
|
fetchBots();
|
|
|
|
|
}}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
className="p-2 hover:bg-gray-100 rounded-lg transition-colors text-gray-500 hover:text-gray-700 disabled:opacity-50"
|
|
|
|
|
>
|
|
|
|
|
<RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
|
|
|
|
|
</button>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{loading ? (
|
|
|
|
|
<div className="flex justify-center items-center py-20">
|
|
|
|
|
<div className="animate-spin rounded-full h-10 w-10 border-4 border-primary border-t-transparent"></div>
|
|
|
|
|
</div>
|
|
|
|
|
) : bots.length === 0 ? (
|
|
|
|
|
<div className="text-center py-20 text-gray-400">
|
|
|
|
|
<Bot size={48} className="mx-auto mb-4 opacity-30" />
|
|
|
|
|
<p>등록된 봇이 없습니다</p>
|
|
|
|
|
<p className="text-sm mt-1">위의 버튼을 클릭하여 봇을 추가하세요</p>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="p-6 grid grid-cols-1 lg:grid-cols-2 gap-4">
|
2026-01-23 10:21:06 +09:00
|
|
|
{bots.map((bot, index) => (
|
|
|
|
|
<BotCard
|
|
|
|
|
key={bot.id}
|
|
|
|
|
bot={bot}
|
|
|
|
|
index={index}
|
|
|
|
|
isInitialLoad={isInitialLoad}
|
|
|
|
|
syncing={syncing}
|
|
|
|
|
statusInfo={getStatusInfo(bot.status)}
|
|
|
|
|
onSync={handleSyncAllVideos}
|
|
|
|
|
onToggle={toggleBot}
|
|
|
|
|
onAnimationComplete={() =>
|
|
|
|
|
isInitialLoad && index === bots.length - 1 && setIsInitialLoad(false)
|
|
|
|
|
}
|
|
|
|
|
formatTime={formatTime}
|
|
|
|
|
formatInterval={formatInterval}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-22 22:04:37 +09:00
|
|
|
</motion.div>
|
|
|
|
|
</motion.div>
|
feat: 관리자 페이지 마이그레이션 완료 (Phase 4-5)
- 관리자 페이지 폴더 구조 재구성 (pages/pc/admin/)
- login/, dashboard/, members/, albums/, schedules/
- 앨범 관리 페이지 마이그레이션 (Albums, AlbumForm, AlbumPhotos, AlbumTeasers)
- 일정 관리 페이지 마이그레이션 (Schedules, ScheduleForm, ScheduleCategory, ScheduleDict, ScheduleBots)
- DatePicker 컴포넌트 버그 수정 (월 이동 및 연도 선택)
- 일정 관리 라우트 경로 수정 (/admin/schedule)
- 마이그레이션 문서 업데이트
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:35:05 +09:00
|
|
|
</AdminLayout>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ScheduleBots;
|