Update frontend/src/App.jsx
This commit is contained in:
parent
6db713e0c4
commit
8d2a030e2c
1 changed files with 128 additions and 7 deletions
|
|
@ -14,16 +14,20 @@ const App = () => {
|
|||
enabled: true
|
||||
});
|
||||
const [systemInfo, setSystemInfo] = useState(null);
|
||||
const [usageStats, setUsageStats] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [activeView, setActiveView] = useState('tasks'); // 'tasks' | 'dashboard'
|
||||
|
||||
// Fetch tasks on component mount
|
||||
useEffect(() => {
|
||||
fetchTasks();
|
||||
fetchSystemInfo();
|
||||
fetchUsageStats();
|
||||
const interval = setInterval(() => {
|
||||
fetchTasks();
|
||||
fetchSystemInfo();
|
||||
}, 5000); // Refresh every 5 seconds
|
||||
fetchUsageStats();
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
|
|
@ -47,6 +51,16 @@ const App = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const fetchUsageStats = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/system/usage');
|
||||
const data = await response.json();
|
||||
setUsageStats(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching usage stats:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateTask = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
|
|
@ -118,15 +132,121 @@ const App = () => {
|
|||
<h1>Claude Persistent Agent</h1>
|
||||
<p>Scheduled task management & Claude Code runner</p>
|
||||
</div>
|
||||
{systemInfo && (
|
||||
<div className="system-status">
|
||||
<span className={`status-indicator ${systemInfo.scheduler_running ? 'running' : 'stopped'}`}></span>
|
||||
<span>{systemInfo.task_count} tasks</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="header-right">
|
||||
<nav className="header-nav">
|
||||
<button className={`nav-btn ${activeView === 'tasks' ? 'active' : ''}`} onClick={() => setActiveView('tasks')}>Tasks</button>
|
||||
<button className={`nav-btn ${activeView === 'dashboard' ? 'active' : ''}`} onClick={() => setActiveView('dashboard')}>Dashboard</button>
|
||||
</nav>
|
||||
{systemInfo && (
|
||||
<div className="system-status">
|
||||
<span className={`status-indicator ${systemInfo.scheduler_running ? 'running' : 'stopped'}`}></span>
|
||||
<span>{systemInfo.task_count} tasks · {systemInfo.total_runs || 0} runs</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="app-main">
|
||||
{activeView === 'dashboard' && (
|
||||
<div className="dashboard-view">
|
||||
<h2 className="dashboard-title">System Dashboard</h2>
|
||||
<div className="dashboard-grid">
|
||||
{systemInfo && (
|
||||
<>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">📋</div>
|
||||
<div className="dash-card-value">{systemInfo.task_count}</div>
|
||||
<div className="dash-card-label">Total Tasks</div>
|
||||
</div>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">✅</div>
|
||||
<div className="dash-card-value">{systemInfo.completed_runs || 0}</div>
|
||||
<div className="dash-card-label">Completed Runs</div>
|
||||
</div>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">❌</div>
|
||||
<div className="dash-card-value">{systemInfo.failed_runs || 0}</div>
|
||||
<div className="dash-card-label">Failed Runs</div>
|
||||
</div>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">⚡</div>
|
||||
<div className="dash-card-value">{systemInfo.running_runs || 0}</div>
|
||||
<div className="dash-card-label">Currently Running</div>
|
||||
</div>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">🔄</div>
|
||||
<div className="dash-card-value">{systemInfo.total_runs || 0}</div>
|
||||
<div className="dash-card-label">Total Runs Ever</div>
|
||||
</div>
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-icon">{systemInfo.scheduler_running ? '🟢' : '🔴'}</div>
|
||||
<div className="dash-card-value">{systemInfo.scheduler_running ? 'Active' : 'Stopped'}</div>
|
||||
<div className="dash-card-label">Scheduler</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="dashboard-section">
|
||||
<h3>Claude API Usage</h3>
|
||||
<div className="usage-grid">
|
||||
{usageStats ? (
|
||||
<>
|
||||
<div className="usage-row">
|
||||
<span className="usage-label">Claude Runs (all time)</span>
|
||||
<span className="usage-value">{usageStats.claude_runs_total ?? '—'}</span>
|
||||
</div>
|
||||
<div className="usage-row">
|
||||
<span className="usage-label">Active Sessions</span>
|
||||
<span className="usage-value">{usageStats.session_count ?? '—'}</span>
|
||||
</div>
|
||||
<div className="usage-row">
|
||||
<span className="usage-label">First Run</span>
|
||||
<span className="usage-value">{usageStats.first_run ? new Date(usageStats.first_run).toLocaleString() : '—'}</span>
|
||||
</div>
|
||||
<div className="usage-row">
|
||||
<span className="usage-label">Last Run</span>
|
||||
<span className="usage-value">{usageStats.last_run ? new Date(usageStats.last_run).toLocaleString() : '—'}</span>
|
||||
</div>
|
||||
<div className="usage-row highlight">
|
||||
<span className="usage-label">🔄 Next Monthly Reset</span>
|
||||
<span className="usage-value">{usageStats.next_reset ? new Date(usageStats.next_reset).toLocaleDateString() : '—'}</span>
|
||||
</div>
|
||||
<div className="usage-row highlight">
|
||||
<span className="usage-label">⏳ Days Until Reset</span>
|
||||
<span className="usage-value">{usageStats.days_until_reset ?? '—'}</span>
|
||||
</div>
|
||||
{usageStats.note && (
|
||||
<div className="usage-note">{usageStats.note}</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="usage-loading">Loading usage stats…</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-section">
|
||||
<h3>Task Breakdown</h3>
|
||||
<div className="task-breakdown">
|
||||
{tasks.length === 0 ? (
|
||||
<p className="empty-msg">No tasks yet. Create one in the Tasks view.</p>
|
||||
) : (
|
||||
tasks.map(t => (
|
||||
<div key={t.id} className="breakdown-row">
|
||||
<span className="breakdown-name">{t.name}</span>
|
||||
<span className="breakdown-type">{t.schedule_type}</span>
|
||||
<span className={`breakdown-status status-${t.status}`}>{t.status}</span>
|
||||
<span className="breakdown-last">{t.last_run ? new Date(t.last_run).toLocaleString() : 'never'}</span>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeView === 'tasks' && (
|
||||
<aside className="sidebar">
|
||||
<button
|
||||
className="btn btn-primary btn-create"
|
||||
|
|
@ -341,6 +461,7 @@ const App = () => {
|
|||
</div>
|
||||
)}
|
||||
</section>
|
||||
)} {/* end activeView === 'tasks' */}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Reference in a new issue