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 @@
-
-
- | ID |
- Username |
- Email |
- Admin |
- Active |
- Actions |
-
-
-
-
-
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) {