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
|
enabled: true
|
||||||
});
|
});
|
||||||
const [systemInfo, setSystemInfo] = useState(null);
|
const [systemInfo, setSystemInfo] = useState(null);
|
||||||
|
const [usageStats, setUsageStats] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [activeView, setActiveView] = useState('tasks'); // 'tasks' | 'dashboard'
|
||||||
|
|
||||||
// Fetch tasks on component mount
|
// Fetch tasks on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
fetchSystemInfo();
|
fetchSystemInfo();
|
||||||
|
fetchUsageStats();
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
fetchSystemInfo();
|
fetchSystemInfo();
|
||||||
}, 5000); // Refresh every 5 seconds
|
fetchUsageStats();
|
||||||
|
}, 10000);
|
||||||
return () => clearInterval(interval);
|
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) => {
|
const handleCreateTask = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -118,15 +132,121 @@ const App = () => {
|
||||||
<h1>Claude Persistent Agent</h1>
|
<h1>Claude Persistent Agent</h1>
|
||||||
<p>Scheduled task management & Claude Code runner</p>
|
<p>Scheduled task management & Claude Code runner</p>
|
||||||
</div>
|
</div>
|
||||||
{systemInfo && (
|
<div className="header-right">
|
||||||
<div className="system-status">
|
<nav className="header-nav">
|
||||||
<span className={`status-indicator ${systemInfo.scheduler_running ? 'running' : 'stopped'}`}></span>
|
<button className={`nav-btn ${activeView === 'tasks' ? 'active' : ''}`} onClick={() => setActiveView('tasks')}>Tasks</button>
|
||||||
<span>{systemInfo.task_count} tasks</span>
|
<button className={`nav-btn ${activeView === 'dashboard' ? 'active' : ''}`} onClick={() => setActiveView('dashboard')}>Dashboard</button>
|
||||||
</div>
|
</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>
|
</header>
|
||||||
|
|
||||||
<main className="app-main">
|
<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">
|
<aside className="sidebar">
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-create"
|
className="btn btn-primary btn-create"
|
||||||
|
|
@ -341,6 +461,7 @@ const App = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
)} {/* end activeView === 'tasks' */}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Reference in a new issue