import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; const API = process.env.REACT_APP_API_URL || 'http://localhost:8000'; const styles = { app: { maxWidth: '900px', margin: '0 auto', padding: '1.5rem' }, header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }, title: { fontSize: '1.4rem', fontWeight: 700, color: '#f7931a' }, backBtn: { background: 'none', border: '1px solid #555', color: '#aaa', borderRadius: '8px', padding: '0.4rem 1rem', cursor: 'pointer' }, card: { background: '#1a1a1a', padding: '1.5rem', borderRadius: '12px', border: '1px solid #333', marginBottom: '1.5rem' }, sectionTitle: { fontSize: '1rem', fontWeight: 700, color: '#f7931a', marginBottom: '1rem' }, table: { width: '100%', borderCollapse: 'collapse' }, th: { textAlign: 'left', color: '#888', fontSize: '0.8rem', paddingBottom: '0.5rem', borderBottom: '1px solid #333' }, td: { padding: '0.6rem 0', borderBottom: '1px solid #222', color: '#e0e0e0', fontSize: '0.95rem' }, adminBadge: { background: 'rgba(247,147,26,0.15)', color: '#f7931a', borderRadius: '4px', padding: '0.1rem 0.5rem', fontSize: '0.75rem', marginLeft: '0.5rem' }, deleteBtn: { background: 'none', border: '1px solid #555', color: '#ff6b6b', borderRadius: '6px', padding: '0.25rem 0.75rem', cursor: 'pointer', fontSize: '0.85rem' }, form: { display: 'flex', flexDirection: 'column', gap: '0.75rem' }, row: { display: 'flex', gap: '0.75rem', flexWrap: 'wrap' }, input: { flex: 1, minWidth: '140px', padding: '0.6rem 0.75rem', background: '#2a2a2a', border: '1px solid #444', borderRadius: '8px', color: '#e0e0e0', fontSize: '0.95rem' }, checkLabel: { display: 'flex', alignItems: 'center', gap: '0.4rem', color: '#aaa', fontSize: '0.9rem', cursor: 'pointer' }, submitBtn: { alignSelf: 'flex-start', padding: '0.6rem 1.5rem', background: '#f7931a', color: '#000', border: 'none', borderRadius: '8px', fontWeight: 700, cursor: 'pointer' }, error: { color: '#ff6b6b', fontSize: '0.9rem' }, success: { color: '#6bff8e', fontSize: '0.9rem' }, }; export default function AdminPage() { const [users, setUsers] = useState([]); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [isAdmin, setIsAdmin] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const navigate = useNavigate(); const authHeaders = () => ({ Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }); const fetchUsers = useCallback(async () => { const res = await fetch(`${API}/admin/users`, { headers: authHeaders() }); if (res.status === 401 || res.status === 403) { navigate('/'); return; } setUsers(await res.json()); }, [navigate]); useEffect(() => { fetchUsers(); }, [fetchUsers]); const handleCreate = async (e) => { e.preventDefault(); setError(''); setSuccess(''); const res = await fetch(`${API}/admin/users`, { method: 'POST', headers: authHeaders(), body: JSON.stringify({ username, password, is_admin: isAdmin }), }); if (!res.ok) { const data = await res.json(); setError(data.detail || 'Failed to create user'); return; } setSuccess(`User "${username}" created.`); setUsername(''); setPassword(''); setIsAdmin(false); fetchUsers(); }; const handleDelete = async (id, name) => { if (!window.confirm(`Delete user "${name}"? This also deletes all their purchases.`)) return; const res = await fetch(`${API}/admin/users/${id}`, { method: 'DELETE', headers: authHeaders() }); if (!res.ok) { const data = await res.json().catch(() => ({})); setError(data.detail || 'Failed to delete user'); return; } fetchUsers(); }; const currentUsername = (() => { try { const token = localStorage.getItem('token'); return JSON.parse(atob(token.split('.')[1])).sub; } catch { return null; } })(); return (
| Username | Actions |
|---|---|
| {u.username} {u.is_admin && admin} | {u.username !== currentUsername && ( )} |