import React, { useState, useEffect, useRef, useMemo } from 'react'; import { X, Edit3, Trash2, Shield, ShieldCheck, ShieldAlert, Skull, Newspaper, Download, Upload, Database, Save, History, Plus, Globe, User, ShieldX, UserMinus, UserCheck } from 'lucide-react'; import { useCTF } from './CTFContext'; import { Challenge, Team, BlogPost, Difficulty, ChallengeFile } from './types'; import { Button, Countdown, CategoryIcon } from './UIComponents'; import { CATEGORIES, DIFFICULTIES } from './constants'; export const Admin: React.FC = () => { const { state, toggleCtf, resetScores, upsertChallenge, deleteChallenge, deleteAllChallenges, updateTeam, deleteTeam, createBlogPost, updateBlogPost, deleteBlogPost, updateConfig, exportChallenges, importChallenges, backupDatabase, restoreDatabase, refreshState } = useCTF(); const [editingChallenge, setEditingChallenge] = useState | null>(null); const [editingTeam, setEditingTeam] = useState & { newPassword?: string } | null>(null); const [editingBlogPost, setEditingBlogPost] = useState | null>(null); const [newFiles, setNewFiles] = useState([]); const [currentFiles, setCurrentFiles] = useState([]); const [localConf, setLocalConf] = useState>({}); const initialLoadDone = useRef(false); // Sorted data for display const sortedChallenges = useMemo(() => { return [...state.challenges].sort((a, b) => a.title.localeCompare(b.title)); }, [state.challenges]); const sortedOperators = useMemo(() => { return state.teams .filter(t => t.id !== 'admin-0') .sort((a, b) => a.name.localeCompare(b.name)); }, [state.teams]); useEffect(() => { if (Object.keys(state.config).length > 0 && !initialLoadDone.current) { setLocalConf({ ...state.config }); initialLoadDone.current = true; } }, [state.config]); const importChalRef = useRef(null); const restoreDbRef = useRef(null); const handleConfigSubmit = async (e: React.FormEvent) => { e.preventDefault(); const fd = new FormData(); Object.entries(localConf).forEach(([k, v]) => { if (v !== undefined && v !== null && v !== 'NaN') fd.append(k, String(v)); }); const logoInput = (e.target as any).logo; const bgInput = (e.target as any).bgImage; if (logoInput && logoInput.files[0]) fd.append('logo', logoInput.files[0]); if (bgInput && bgInput.files[0]) fd.append('bgImage', bgInput.files[0]); try { await updateConfig(fd); await refreshState(); initialLoadDone.current = false; alert('CONFIGURATION_SAVED_SUCCESSFULLY'); } catch (err) { alert('SAVE_FAILED_CHECK_CONSOLE'); } }; const toUTCDisplay = (msStr: string) => { if (!msStr || msStr === 'undefined' || msStr === 'NaN') return ""; const ms = parseInt(msStr); if (isNaN(ms)) return ""; const d = new Date(ms); return d.toISOString().slice(0, 16); }; const fromUTCDisplay = (isoStr: string) => { if (!isoStr) return ""; const d = new Date(isoStr + ":00Z"); const ms = d.getTime(); return isNaN(ms) ? "" : ms.toString(); }; const eventStartTime = parseInt(state.config.eventStartTime || "0"); const isBeforeStart = Date.now() < eventStartTime; const handleDifficultyChange = (val: Difficulty) => { if (!editingChallenge) return; const points = val === 'Low' ? 100 : val === 'Medium' ? 200 : 300; setEditingChallenge({ ...editingChallenge, difficulty: val, initialPoints: points, minimumPoints: Math.floor(points / 2) }); }; const handleInitialPointsChange = (p: number) => { if (!editingChallenge) return; setEditingChallenge({ ...editingChallenge, initialPoints: p, minimumPoints: Math.floor(p / 2) }); }; const quickToggleAdmin = async (team: Team) => { if (team.id === 'admin-0') return; await updateTeam(team.id, { ...team, isAdmin: !team.isAdmin }); }; const quickToggleStatus = async (team: Team) => { if (team.id === 'admin-0') return; await updateTeam(team.id, { ...team, isDisabled: !team.isDisabled }); }; return (

ADMIN_CONSOLE

{isBeforeStart && (
)}

GENERAL_CONFIG

setLocalConf({...localConf, conferenceName: e.target.value})} />
{ const val = fromUTCDisplay(e.target.value); if (val) setLocalConf({...localConf, eventStartTime: val}); }} />
{ const val = fromUTCDisplay(e.target.value); if (val) setLocalConf({...localConf, eventEndTime: val}); }} />
setLocalConf({...localConf, dockerIp: e.target.value})} />