116 lines
2.9 KiB
TypeScript
116 lines
2.9 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
import styled from 'styled-components';
|
|
import LogTerminal from '@/components/admin/LogTerminal';
|
|
import { adminFetch } from '@/lib/adminFetch';
|
|
|
|
const SISTERS = [
|
|
{ value: 'harang', label: '하랑이' },
|
|
{ value: 'narang', label: '나랑이' },
|
|
{ value: 'darang', label: '다랑이' },
|
|
{ value: 'erang', label: '이랑이' },
|
|
];
|
|
|
|
const Controls = styled.div`
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
margin-bottom: 16px;
|
|
`;
|
|
|
|
const Select = styled.select`
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 7px;
|
|
color: var(--text-primary);
|
|
padding: 8px 12px;
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
outline: none;
|
|
&:focus { border-color: #58A6FF; }
|
|
option { background: #1a1f2a; }
|
|
`;
|
|
|
|
const LinesInput = styled.input`
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 7px;
|
|
color: var(--text-primary);
|
|
padding: 8px 12px;
|
|
font-size: 13px;
|
|
width: 80px;
|
|
outline: none;
|
|
&:focus { border-color: #58A6FF; }
|
|
`;
|
|
|
|
const FetchBtn = styled.button`
|
|
padding: 8px 18px;
|
|
border-radius: 7px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
background: rgba(88,166,255,0.1);
|
|
border: 1px solid rgba(88,166,255,0.35);
|
|
color: #58A6FF;
|
|
cursor: pointer;
|
|
transition: background 0.15s;
|
|
&:hover { background: rgba(88,166,255,0.2); }
|
|
&:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
`;
|
|
|
|
const Label = styled.span`
|
|
font-size: 12px;
|
|
color: var(--text-secondary);
|
|
`;
|
|
|
|
export default function LogsPage() {
|
|
const [sister, setSister] = useState('narang');
|
|
const [lines, setLines] = useState(100);
|
|
const [logLines, setLogLines] = useState<string[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const fetchLogs = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await adminFetch(`/api/admin/logs/${sister}?lines=${lines}`);
|
|
const d = await res.json();
|
|
setLogLines(d.lines ?? []);
|
|
} catch {
|
|
setLogLines(['(로그 로드 실패)']);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [sister, lines]);
|
|
|
|
// 마운트 시 자동 로드
|
|
useEffect(() => {
|
|
fetchLogs();
|
|
}, [fetchLogs]);
|
|
|
|
return (
|
|
<>
|
|
<Controls>
|
|
<Select value={sister} onChange={(e) => setSister(e.target.value)}>
|
|
{SISTERS.map((s) => (
|
|
<option key={s.value} value={s.value}>{s.label}</option>
|
|
))}
|
|
</Select>
|
|
<Label>최근</Label>
|
|
<LinesInput
|
|
type="number"
|
|
value={lines}
|
|
onChange={(e) => setLines(parseInt(e.target.value, 10) || 100)}
|
|
min={10}
|
|
max={500}
|
|
/>
|
|
<Label>줄</Label>
|
|
<FetchBtn onClick={fetchLogs} disabled={loading}>
|
|
{loading ? '로딩 중...' : '📋 로그 가져오기'}
|
|
</FetchBtn>
|
|
</Controls>
|
|
|
|
<LogTerminal lines={logLines} />
|
|
</>
|
|
);
|
|
}
|