Add some features to the admin panel

This commit is contained in:
2025-06-07 14:06:11 +09:00
parent 9b18963041
commit bc2be914df
5 changed files with 512 additions and 49 deletions

View File

@ -15,6 +15,14 @@ interface Stats {
activeFeeds: number;
inactiveFeeds: number;
totalEpisodes: number;
pendingRequests: number;
totalRequests: number;
batchScheduler: {
enabled: boolean;
isRunning: boolean;
lastRun?: string;
nextRun?: string;
};
lastUpdated: string;
adminPort: number;
authEnabled: boolean;
@ -32,7 +40,7 @@ function App() {
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
const [newFeedUrl, setNewFeedUrl] = useState('');
const [activeTab, setActiveTab] = useState<'dashboard' | 'feeds' | 'env'>('dashboard');
const [activeTab, setActiveTab] = useState<'dashboard' | 'feeds' | 'env' | 'batch'>('dashboard');
useEffect(() => {
loadData();
@ -151,6 +159,7 @@ function App() {
if (res.ok) {
setSuccess(data.message);
loadData(); // Refresh data to update batch status
} else {
setError(data.error || 'バッチ処理開始に失敗しました');
}
@ -160,6 +169,26 @@ function App() {
}
};
const toggleBatchScheduler = async (enable: boolean) => {
try {
const res = await fetch(`/api/admin/batch/${enable ? 'enable' : 'disable'}`, {
method: 'POST'
});
const data = await res.json();
if (res.ok) {
setSuccess(data.message);
loadData(); // Refresh data to update batch status
} else {
setError(data.error || 'バッチスケジューラーの状態変更に失敗しました');
}
} catch (err) {
setError('バッチスケジューラーの状態変更に失敗しました');
console.error('Error toggling batch scheduler:', err);
}
};
if (loading) {
return <div className="container"><div className="loading">...</div></div>;
}
@ -189,6 +218,12 @@ function App() {
>
</button>
<button
className={`btn ${activeTab === 'batch' ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => setActiveTab('batch')}
>
</button>
<button
className={`btn ${activeTab === 'env' ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => setActiveTab('env')}
@ -218,6 +253,16 @@ function App() {
<div className="value">{stats?.totalEpisodes || 0}</div>
<div className="label"></div>
</div>
<div className="stat-card">
<div className="value">{stats?.pendingRequests || 0}</div>
<div className="label"></div>
</div>
<div className="stat-card">
<div className="value" style={{ color: stats?.batchScheduler?.enabled ? '#28a745' : '#dc3545' }}>
{stats?.batchScheduler?.enabled ? 'ON' : 'OFF'}
</div>
<div className="label"></div>
</div>
</div>
<div style={{ marginBottom: '20px' }}>
@ -296,6 +341,93 @@ function App() {
</>
)}
{activeTab === 'batch' && (
<>
<h3></h3>
<p style={{ marginBottom: '20px', color: '#7f8c8d' }}>
</p>
<div className="stats-grid" style={{ marginBottom: '24px' }}>
<div className="stat-card">
<div className="value" style={{ color: stats?.batchScheduler?.enabled ? '#28a745' : '#dc3545' }}>
{stats?.batchScheduler?.enabled ? '有効' : '無効'}
</div>
<div className="label"></div>
</div>
<div className="stat-card">
<div className="value" style={{ color: stats?.batchScheduler?.isRunning ? '#ffc107' : '#6c757d' }}>
{stats?.batchScheduler?.isRunning ? '実行中' : '待機中'}
</div>
<div className="label"></div>
</div>
<div className="stat-card">
<div className="value" style={{ fontSize: '12px' }}>
{stats?.batchScheduler?.lastRun
? new Date(stats.batchScheduler.lastRun).toLocaleString('ja-JP')
: '未実行'}
</div>
<div className="label"></div>
</div>
<div className="stat-card">
<div className="value" style={{ fontSize: '12px' }}>
{stats?.batchScheduler?.nextRun
? new Date(stats.batchScheduler.nextRun).toLocaleString('ja-JP')
: '未予定'}
</div>
<div className="label"></div>
</div>
</div>
<div style={{ marginBottom: '24px' }}>
<h4></h4>
<div style={{ display: 'flex', gap: '12px', marginTop: '12px' }}>
<button
className="btn btn-success"
onClick={() => toggleBatchScheduler(true)}
disabled={stats?.batchScheduler?.enabled}
>
</button>
<button
className="btn btn-warning"
onClick={() => toggleBatchScheduler(false)}
disabled={!stats?.batchScheduler?.enabled}
>
</button>
</div>
<p style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px' }}>
</p>
</div>
<div style={{ marginBottom: '24px' }}>
<h4></h4>
<button
className="btn btn-primary"
onClick={triggerBatch}
disabled={stats?.batchScheduler?.isRunning}
>
{stats?.batchScheduler?.isRunning ? 'バッチ処理実行中...' : 'バッチ処理を手動実行'}
</button>
<p style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px' }}>
</p>
</div>
<div style={{ padding: '16px', background: '#f8f9fa', borderRadius: '4px' }}>
<h4></h4>
<ul style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px', paddingLeft: '20px' }}>
<li>6</li>
<li>RSS記事の取得</li>
<li></li>
<li></li>
</ul>
</div>
</>
)}
{activeTab === 'env' && (
<>
<h3></h3>