This commit is contained in:
2026-01-25 17:00:35 +01:00
parent 751e1a6347
commit b626b682b5
3 changed files with 51 additions and 25 deletions

View File

@@ -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.add(new_assignment)
db.commit() db.commit()
return {"message": "Assigned successfully"} 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"}

View File

@@ -48,19 +48,6 @@
</div> </div>
<div class="card"> <div class="card">
<table id="users-table"> <table id="users-table">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Admin</th>
<th>Active</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Users will be populated here -->
</tbody>
</table> </table>
</div> </div>
</section> </section>

View File

@@ -89,7 +89,7 @@ async function authFetch(url, options = {}) {
...options.headers, ...options.headers,
"Authorization": `Bearer ${token}` "Authorization": `Bearer ${token}`
}; };
const res = await fetch(url, { ...options, headers }); const res = await fetch(url, { ...options, headers });
if (res.status === 401) { if (res.status === 401) {
logoutBtn.click(); logoutBtn.click();
@@ -102,21 +102,46 @@ async function authFetch(url, options = {}) {
async function loadUsers() { async function loadUsers() {
const res = await authFetch(`${API_URL}/users/`); const res = await authFetch(`${API_URL}/users/`);
const users = await res.json(); const users = await res.json();
usersTableBody.innerHTML = users.map(user => ` usersTableBody.innerHTML = users.map(user => {
const appsList = user.applications.map(ua =>
`<span class="badge badge-info" style="margin-right: 5px; display: inline-flex; align-items: center;">
${ua.application.name}
<button onclick="removeAppAssignment(${user.id}, ${ua.application.id})" style="margin-left: 5px; color: red; font-weight: bold; border: none; background: none; cursor: pointer;">&times;</button>
</span>`
).join("");
return `
<tr> <tr>
<td>${user.id}</td> <td>${user.id}</td>
<td>${user.username}</td> <td>${user.username}</td>
<td>${user.email}</td> <td>${user.email}</td>
<td>${user.is_admin ? "Yes" : "No"}</td> <td>${user.is_admin ? "Yes" : "No"}</td>
<td>${user.is_active ? "Yes" : "No"}</td> <td>${user.is_active ? "Yes" : "No"}</td>
<td>${appsList}</td>
<td> <td>
<button class="btn btn-secondary" onclick='editUser(${JSON.stringify(user)})'>Edit</button> <button class="btn btn-secondary" onclick='editUser(${JSON.stringify(user)})'>Edit</button>
<button class="btn btn-primary" onclick='assignAppModal(${user.id})'>Assign App</button> <button class="btn btn-primary" onclick='assignAppModal(${user.id})'>Assign App</button>
</td> </td>
</tr> </tr>
`).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 // Apps
async function loadApps() { async function loadApps() {
const res = await authFetch(`${API_URL}/apps/`); const res = await authFetch(`${API_URL}/apps/`);
@@ -170,12 +195,12 @@ userForm.addEventListener("submit", async (e) => {
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
} }
if (!res.ok) { if (!res.ok) {
const err = await res.json(); const err = await res.json();
throw new Error(err.detail); throw new Error(err.detail);
} }
closeModal("user-modal"); closeModal("user-modal");
loadUsers(); loadUsers();
} catch (err) { } catch (err) {
@@ -222,12 +247,12 @@ appForm.addEventListener("submit", async (e) => {
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, url }) body: JSON.stringify({ name, url })
}); });
if (!res.ok) { if (!res.ok) {
const err = await res.json(); const err = await res.json();
throw new Error(err.detail); throw new Error(err.detail);
} }
closeModal("app-modal"); closeModal("app-modal");
loadApps(); loadApps();
} catch (err) { } catch (err) {
@@ -238,14 +263,14 @@ appForm.addEventListener("submit", async (e) => {
// Assign App // Assign App
window.assignAppModal = async (userId) => { window.assignAppModal = async (userId) => {
document.getElementById("assign-user-id").value = userId; document.getElementById("assign-user-id").value = userId;
// Load apps for select // Load apps for select
const res = await authFetch(`${API_URL}/apps/`); const res = await authFetch(`${API_URL}/apps/`);
const apps = await res.json(); const apps = await res.json();
const select = document.getElementById("assign-app-select"); const select = document.getElementById("assign-app-select");
select.innerHTML = '<option value="">Select Application</option>' + select.innerHTML = '<option value="">Select Application</option>' +
apps.map(app => `<option value="${app.id}">${app.name}</option>`).join(""); apps.map(app => `<option value="${app.id}">${app.name}</option>`).join("");
openModal("assign-modal"); openModal("assign-modal");
}; };
@@ -259,9 +284,9 @@ assignForm.addEventListener("submit", async (e) => {
const res = await authFetch(`${API_URL}/users/${userId}/assign/${appId}`, { const res = await authFetch(`${API_URL}/users/${userId}/assign/${appId}`, {
method: "POST" method: "POST"
}); });
if (!res.ok) throw new Error("Failed to assign"); if (!res.ok) throw new Error("Failed to assign");
closeModal("assign-modal"); closeModal("assign-modal");
alert("Assigned successfully"); alert("Assigned successfully");
} catch (err) { } catch (err) {