'use client'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import StatusBadge from '@/components/common/StatusBadge'; import ConfirmModal from '@/components/admin/ConfirmModal'; import { API_URL } from '@/lib/config'; import { adminFetch } from '@/lib/adminFetch'; import { SISTER_EMOJIS, SISTER_DISPLAY_NAMES } from '@/lib/sisters'; type Status = 'online' | 'offline' | 'working' | 'unknown'; interface ActionResult { success: boolean; output: string; error: string | null; } const Table = styled.div` display: flex; flex-direction: column; gap: 8px; `; const Row = styled.div` background: var(--bg-surface); border: 1px solid var(--border-color); border-radius: 10px; padding: 16px 20px; display: flex; align-items: center; gap: 16px; `; const SisterInfo = styled.div` display: flex; align-items: center; gap: 10px; flex: 1; `; const Emoji = styled.span`font-size: 22px;`; const Name = styled.span` font-size: 15px; font-weight: 600; color: var(--text-primary); min-width: 70px; `; const Actions = styled.div` display: flex; gap: 8px; `; const ActionBtn = styled.button<{ $danger?: boolean }>` padding: 6px 14px; border-radius: 7px; font-size: 13px; font-weight: 600; cursor: pointer; border: 1px solid; transition: all 0.15s; ${({ $danger }) => $danger ? `background: rgba(255,23,68,0.08); border-color: rgba(255,23,68,0.3); color: #FF1744; &:hover { background: rgba(255,23,68,0.18); }` : `background: rgba(88,166,255,0.08); border-color: rgba(88,166,255,0.3); color: #58A6FF; &:hover { background: rgba(88,166,255,0.18); }`} `; const ResultBanner = styled.div<{ $success: boolean }>` margin-top: 8px; padding: 8px 12px; background: ${({ $success }) => $success ? 'rgba(0,230,118,0.08)' : 'rgba(255,23,68,0.08)'}; border: 1px solid ${({ $success }) => $success ? 'rgba(0,230,118,0.3)' : 'rgba(255,23,68,0.3)'}; border-radius: 6px; font-size: 12px; color: ${({ $success }) => $success ? '#00FF00' : '#FF1744'}; font-family: monospace; white-space: pre-wrap; `; export default function AdminSistersPage() { const [sisters, setSisters] = useState([]); const [modal, setModal] = useState<{ action: 'restart' | 'reset'; name: string } | null>(null); const [results, setResults] = useState>({}); const [loading, setLoading] = useState>({}); useEffect(() => { fetch(`${API_URL}/api/sisters`).then((r) => r.json()).then(setSisters).catch(() => {}); const iv = setInterval(() => { fetch(`${API_URL}/api/sisters`).then((r) => r.json()).then(setSisters).catch(() => {}); }, 15000); return () => clearInterval(iv); }, []); const doAction = async (action: 'restart' | 'reset', name: string) => { setModal(null); setLoading((p) => ({ ...p, [`${action}:${name}`]: true })); try { const res = await adminFetch(`/api/admin/sisters/${name}/${action}`, { method: 'POST' }); const data: ActionResult = await res.json(); setResults((p) => ({ ...p, [`${action}:${name}`]: data })); } catch (e) { setResults((p) => ({ ...p, [`${action}:${name}`]: { success: false, output: '', error: (e as Error).message }, })); } finally { setLoading((p) => ({ ...p, [`${action}:${name}`]: false })); } }; return ( <> {sisters.map((s) => { const restartResult = results[`restart:${s.name}`]; const resetResult = results[`reset:${s.name}`]; return (
{SISTER_EMOJIS[s.name] ?? 'πŸ€–'} {SISTER_DISPLAY_NAMES[s.name] ?? s.name} setModal({ action: 'restart', name: s.name })} disabled={loading[`restart:${s.name}`]} > {loading[`restart:${s.name}`] ? '...' : 'πŸ”„ μž¬μ‹œμž‘'} setModal({ action: 'reset', name: s.name })} disabled={loading[`reset:${s.name}`]} > {loading[`reset:${s.name}`] ? '...' : 'πŸ—‘οΈ 리셋'} {restartResult && ( μž¬μ‹œμž‘: {restartResult.success ? 'βœ… 성곡' : `❌ ${restartResult.error}`} {restartResult.output && `\n${restartResult.output}`} )} {resetResult && ( 리셋: {resetResult.success ? 'βœ… 성곡' : `❌ ${resetResult.error}`} )}
); })}
{modal && ( doAction(modal.action, modal.name)} onCancel={() => setModal(null)} /> )} ); }