Update frontend/src/App.jsx
This commit is contained in:
parent
a4094f13cf
commit
11e72ebeec
1 changed files with 81 additions and 0 deletions
|
|
@ -15,6 +15,7 @@ const App = () => {
|
||||||
});
|
});
|
||||||
const [systemInfo, setSystemInfo] = useState(null);
|
const [systemInfo, setSystemInfo] = useState(null);
|
||||||
const [usageStats, setUsageStats] = useState(null);
|
const [usageStats, setUsageStats] = useState(null);
|
||||||
|
const [authStatus, setAuthStatus] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [activeView, setActiveView] = useState('tasks'); // 'tasks' | 'dashboard'
|
const [activeView, setActiveView] = useState('tasks'); // 'tasks' | 'dashboard'
|
||||||
|
|
||||||
|
|
@ -23,10 +24,12 @@ const App = () => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
fetchSystemInfo();
|
fetchSystemInfo();
|
||||||
fetchUsageStats();
|
fetchUsageStats();
|
||||||
|
fetchAuthStatus();
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
fetchSystemInfo();
|
fetchSystemInfo();
|
||||||
fetchUsageStats();
|
fetchUsageStats();
|
||||||
|
fetchAuthStatus();
|
||||||
}, 10000);
|
}, 10000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -61,6 +64,38 @@ const App = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchAuthStatus = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/auth/status');
|
||||||
|
const data = await response.json();
|
||||||
|
setAuthStatus(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching auth status:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/auth/login', { method: 'POST' });
|
||||||
|
const data = await response.json();
|
||||||
|
setAuthStatus(prev => ({ ...prev, ...data }));
|
||||||
|
if (data.auth_url) {
|
||||||
|
window.open(data.auth_url, '_blank');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initiating login:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
try {
|
||||||
|
await fetch('/api/auth/logout', { method: 'POST' });
|
||||||
|
fetchAuthStatus();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error logging out:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateTask = async (e) => {
|
const handleCreateTask = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -137,6 +172,15 @@ const App = () => {
|
||||||
<button className={`nav-btn ${activeView === 'tasks' ? 'active' : ''}`} onClick={() => setActiveView('tasks')}>Tasks</button>
|
<button className={`nav-btn ${activeView === 'tasks' ? 'active' : ''}`} onClick={() => setActiveView('tasks')}>Tasks</button>
|
||||||
<button className={`nav-btn ${activeView === 'dashboard' ? 'active' : ''}`} onClick={() => setActiveView('dashboard')}>Dashboard</button>
|
<button className={`nav-btn ${activeView === 'dashboard' ? 'active' : ''}`} onClick={() => setActiveView('dashboard')}>Dashboard</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div className="auth-badge">
|
||||||
|
{authStatus?.status === 'logged_in' ? (
|
||||||
|
<span className="auth-ok" title={authStatus.account}>● {authStatus.account || 'Logged in'}</span>
|
||||||
|
) : authStatus?.status === 'pending' ? (
|
||||||
|
<span className="auth-pending">⏳ Awaiting login…</span>
|
||||||
|
) : (
|
||||||
|
<button className="auth-login-btn" onClick={handleLogin}>🔐 Login with Claude Max</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{systemInfo && (
|
{systemInfo && (
|
||||||
<div className="system-status">
|
<div className="system-status">
|
||||||
<span className={`status-indicator ${systemInfo.scheduler_running ? 'running' : 'stopped'}`}></span>
|
<span className={`status-indicator ${systemInfo.scheduler_running ? 'running' : 'stopped'}`}></span>
|
||||||
|
|
@ -187,6 +231,43 @@ const App = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="dashboard-section">
|
||||||
|
<h3>Claude Authentication</h3>
|
||||||
|
<div className="auth-panel">
|
||||||
|
{authStatus?.status === 'logged_in' ? (
|
||||||
|
<div className="auth-connected">
|
||||||
|
<span className="auth-icon">✅</span>
|
||||||
|
<div>
|
||||||
|
<div className="auth-title">Connected to Claude Max</div>
|
||||||
|
<div className="auth-sub">{authStatus.account}</div>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-secondary btn-sm" onClick={handleLogout}>Log Out</button>
|
||||||
|
</div>
|
||||||
|
) : authStatus?.status === 'pending' ? (
|
||||||
|
<div className="auth-pending-panel">
|
||||||
|
<span className="auth-icon">⏳</span>
|
||||||
|
<div>
|
||||||
|
<div className="auth-title">Waiting for browser login…</div>
|
||||||
|
{authStatus.auth_url && (
|
||||||
|
<a className="auth-url-link" href={authStatus.auth_url} target="_blank" rel="noreferrer">
|
||||||
|
Open login page →
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="auth-disconnected">
|
||||||
|
<span className="auth-icon">🔐</span>
|
||||||
|
<div>
|
||||||
|
<div className="auth-title">Not logged in</div>
|
||||||
|
<div className="auth-sub">Log in with your Claude Max account to run tasks</div>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-primary btn-sm" onClick={handleLogin}>Login with Claude Max</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="dashboard-section">
|
<div className="dashboard-section">
|
||||||
<h3>Claude API Usage</h3>
|
<h3>Claude API Usage</h3>
|
||||||
<div className="usage-grid">
|
<div className="usage-grid">
|
||||||
|
|
|
||||||
Reference in a new issue