diff --git a/backend/routers/users.py b/backend/routers/users.py index 0225081..056f485 100644 --- a/backend/routers/users.py +++ b/backend/routers/users.py @@ -84,3 +84,17 @@ async def assign_app_to_user(user_id: int, app_id: int, db: Session = Depends(da db.add(new_assignment) db.commit() return {"message": "Assigned successfully"} + +@router.delete("/{user_id}/assign/{app_id}") +async def remove_app_assignment(user_id: int, app_id: int, db: Session = Depends(database.get_db)): + assignment = db.query(models.UserApplication).filter( + models.UserApplication.user_id == user_id, + models.UserApplication.application_id == app_id + ).first() + + if not assignment: + raise HTTPException(status_code=404, detail="Assignment not found") + + db.delete(assignment) + db.commit() + return {"message": "Assignment removed"} diff --git a/frontend/index.html b/frontend/index.html index c557fa1..03ddea6 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -48,19 +48,6 @@
- - - - - - - - - - - - -
IDUsernameEmailAdminActiveActions
diff --git a/frontend/static/js/app.js b/frontend/static/js/app.js index 55c2b1f..16fdd50 100644 --- a/frontend/static/js/app.js +++ b/frontend/static/js/app.js @@ -89,7 +89,7 @@ async function authFetch(url, options = {}) { ...options.headers, "Authorization": `Bearer ${token}` }; - + const res = await fetch(url, { ...options, headers }); if (res.status === 401) { logoutBtn.click(); @@ -102,21 +102,46 @@ async function authFetch(url, options = {}) { async function loadUsers() { const res = await authFetch(`${API_URL}/users/`); const users = await res.json(); - usersTableBody.innerHTML = users.map(user => ` + usersTableBody.innerHTML = users.map(user => { + const appsList = user.applications.map(ua => + ` + ${ua.application.name} + + ` + ).join(""); + + return ` ${user.id} ${user.username} ${user.email} ${user.is_admin ? "Yes" : "No"} ${user.is_active ? "Yes" : "No"} + ${appsList} - `).join(""); + `}).join(""); } +window.removeAppAssignment = async (userId, appId) => { + if (!confirm("Are you sure you want to remove this app assignment?")) return; + + try { + const res = await authFetch(`${API_URL}/users/${userId}/assign/${appId}`, { + method: "DELETE" + }); + + if (!res.ok) throw new Error("Failed to remove assignment"); + + loadUsers(); + } catch (err) { + alert(err.message); + } +}; + // Apps async function loadApps() { const res = await authFetch(`${API_URL}/apps/`); @@ -170,12 +195,12 @@ userForm.addEventListener("submit", async (e) => { body: JSON.stringify(data) }); } - + if (!res.ok) { const err = await res.json(); throw new Error(err.detail); } - + closeModal("user-modal"); loadUsers(); } catch (err) { @@ -222,12 +247,12 @@ appForm.addEventListener("submit", async (e) => { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, url }) }); - + if (!res.ok) { const err = await res.json(); throw new Error(err.detail); } - + closeModal("app-modal"); loadApps(); } catch (err) { @@ -238,14 +263,14 @@ appForm.addEventListener("submit", async (e) => { // Assign App window.assignAppModal = async (userId) => { document.getElementById("assign-user-id").value = userId; - + // Load apps for select const res = await authFetch(`${API_URL}/apps/`); const apps = await res.json(); const select = document.getElementById("assign-app-select"); - select.innerHTML = '' + + select.innerHTML = '' + apps.map(app => ``).join(""); - + openModal("assign-modal"); }; @@ -259,9 +284,9 @@ assignForm.addEventListener("submit", async (e) => { const res = await authFetch(`${API_URL}/users/${userId}/assign/${appId}`, { method: "POST" }); - + if (!res.ok) throw new Error("Failed to assign"); - + closeModal("assign-modal"); alert("Assigned successfully"); } catch (err) {