diff --git a/src/pages/ProjectDetailPage.tsx b/src/pages/ProjectDetailPage.tsx index af30090..8e2d8c6 100644 --- a/src/pages/ProjectDetailPage.tsx +++ b/src/pages/ProjectDetailPage.tsx @@ -26,7 +26,7 @@ export default function ProjectDetailPage() { const fetchProject = () => { api.get(`/projects/${id}`).then(({ data }) => setProject(data)) api.get(`/projects/${id}/members`).then(({ data }) => setMembers(data)) - api.get(`/milestones?project_id=${id}`).then(({ data }) => setMilestones(data)) + api.get(`/projects/${id}/milestones`).then(({ data }) => setMilestones(data)) } useEffect(() => { diff --git a/src/pages/UsersPage.tsx b/src/pages/UsersPage.tsx index 182cd97..251a78d 100644 --- a/src/pages/UsersPage.tsx +++ b/src/pages/UsersPage.tsx @@ -9,6 +9,11 @@ interface RoleOption { description?: string | null } +interface ApiKeyPerms { + can_reset_self: boolean + can_reset_any: boolean +} + export default function UsersPage() { const { user } = useAuth() const isAdmin = user?.is_admin === true @@ -19,6 +24,8 @@ export default function UsersPage() { const [saving, setSaving] = useState(false) const [message, setMessage] = useState('') const [selectedId, setSelectedId] = useState(null) + const [apikeyPerms, setApikeyPerms] = useState({ can_reset_self: false, can_reset_any: false }) + const [generatedApiKey, setGeneratedApiKey] = useState(null) const [createForm, setCreateForm] = useState({ username: '', @@ -51,6 +58,7 @@ export default function UsersPage() { useEffect(() => { if (!selectedUser) return + setGeneratedApiKey(null) setEditForm({ email: selectedUser.email, full_name: selectedUser.full_name || '', @@ -71,10 +79,12 @@ export default function UsersPage() { const fetchData = async () => { try { - const [usersRes, rolesRes] = await Promise.all([ + const [usersRes, rolesRes, apikeyRes] = await Promise.all([ api.get('/users'), api.get('/roles'), + api.get('/auth/me/apikey-permissions').catch(() => ({ data: { can_reset_self: false, can_reset_any: false } })), ]) + setApikeyPerms(apikeyRes.data) const assignableRoles = rolesRes.data .filter((role) => role.name !== 'admin') .sort((a, b) => a.name.localeCompare(b.name)) @@ -152,6 +162,29 @@ export default function UsersPage() { } } + const canResetApiKey = (targetUser: User) => { + if (apikeyPerms.can_reset_any) return true + if (apikeyPerms.can_reset_self && targetUser.id === user?.id) return true + return false + } + + const handleResetApiKey = async () => { + if (!selectedUser) return + if (!confirm(`Reset API key for ${selectedUser.username}? The old key will be deactivated.`)) return + setSaving(true) + setMessage('') + setGeneratedApiKey(null) + try { + const { data } = await api.post(`/users/${selectedUser.id}/reset-apikey`) + setGeneratedApiKey(data.api_key) + setMessage('API key reset successfully. Copy it now — it will not be shown again.') + } catch (err: any) { + setMessage(err.response?.data?.detail || 'Failed to reset API key') + } finally { + setSaving(false) + } + } + const handleDeleteUser = async () => { if (!selectedUser) return if (!confirm(`Delete user ${selectedUser.username}? This cannot be undone.`)) return @@ -274,7 +307,7 @@ export default function UsersPage() { - @@ -326,6 +359,28 @@ export default function UsersPage() { Active + + {canResetApiKey(selectedUser) && ( +
+
API Key
+ + {generatedApiKey && ( +
+
New API Key (copy now!):
+ {generatedApiKey} + +
+ )} +
+ )} ) : (