Files
Berufsschule_HAM/src/Views/Home/Groups.cshtml

520 lines
26 KiB
Plaintext

@using Microsoft.AspNetCore.Mvc.Localization
@using Berufsschule_HAM.Models
@using System.Buffers.Text
@model GroupsIndexViewModel
@inject IViewLocalizer T
@{
ViewData["Title"] = T["Groups"];
}
<div class="container py-4">
<h2 class="mb-3">@T["Groups"]</h2>
<div class="mb-4 d-flex flex-wrap gap-2">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createGroupModal">
@T["Create group"]
</button>
</div>
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th class="text-center">Group</th>
<th class="text-center">@T["Can"]:<br/>@T["inventorize"]</th>
<th class="text-center">@T["Can"]:<br/>@T["manage users"]</th>
<th class="text-center">@T["Can"]:<br/>@T["manage locations"]</th>
<th class="text-center">@T["Can"]:<br/>@T["manage assets"]</th>
<th class="text-center">@T["Can"]:<br/>@T["manage groups"]</th>
<th class="text-center">@T["Action"]</th>
</tr>
</thead>
<tbody>
@{
foreach (GroupsTableViewModel groupTableViewModel in Model.GroupsTableViewModels)
{
<tr>
<td class="text-center">@groupTableViewModel.Group</td>
<td class="text-center @(groupTableViewModel.CanInventorize ? "text-success" : "text-danger")">@(groupTableViewModel.CanInventorize ? "✓" : "✗")</td>
<td class="text-center @(groupTableViewModel.CanManageUsers ? "text-success" : "text-danger")">@(groupTableViewModel.CanManageUsers ? "✓" : "✗")</td>
<td class="text-center @(groupTableViewModel.CanManageLocations ? "text-success" : "text-danger")">@(groupTableViewModel.CanManageLocations ? "✓" : "✗")</td>
<td class="text-center @(groupTableViewModel.CanManageAssets ? "text-success" : "text-danger")">@(groupTableViewModel.CanManageAssets ? "✓" : "✗")</td>
<td class="text-center @(groupTableViewModel.CanManageGroups ? "text-success" : "text-danger")">@(groupTableViewModel.CanManageGroups ? "✓" : "✗")</td>
<td class="text-center">
<div class="d-flex gap-2 justify-content-center">
<button class="btn btn-sm btn-warning btn-update"
data-group-id="@groupTableViewModel.Cn"
data-bs-toggle="modal"
data-bs-target="#updateGroupModal">
@T["Update"]
</button>
<button class="btn btn-sm btn-danger btn-delete"
data-group-id="@groupTableViewModel.Cn"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
@T["Delete"]
</button>
</div>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
<!-- Group Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteModalLabel">@T["Confirm Delete"]</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>@T["GroupDeleteConfirmation1"] <strong id="groupName"></strong> (ID: <span id="groupId"></span>)@T["GroupDeleteConfirmation2"]</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@T["Cancel"]</button>
<form id="deleteForm" method="post" action="">
<button type="submit" class="btn btn-danger">@T["Yes, Delete"]</button>
</form>
</div>
</div>
</div>
</div>
<script>
// Simple toast helper
function showToast(message, type) {
const toastContainer = document.getElementById('toastContainer') || createToastContainer();
const toast = document.createElement('div');
toast.className = `toast align-items-center text-white bg-${type} border-0`;
toast.role = 'alert';
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
toastContainer.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: 3000 });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
function createToastContainer() {
const container = document.createElement('div');
container.id = 'toastContainer';
container.className = 'toast-container position-fixed bottom-0 end-0 p-3';
document.body.appendChild(container);
return container;
}
document.addEventListener('DOMContentLoaded', () => {
const deleteModal = document.getElementById('deleteModal');
let currentButton = null; // The delete button that opened the modal
deleteModal.addEventListener('show.bs.modal', event => {
currentButton = event.relatedTarget; // Button that triggered the modal
const groupId = currentButton.getAttribute('data-group-id');
const groupName = currentButton.getAttribute('data-group-name');
deleteModal.querySelector('#groupId').textContent = groupId;
deleteModal.querySelector('#groupName').textContent = groupName;
// Store the delete URL for later use
deleteModal.querySelector('#deleteForm').dataset.url = `/Groups/Delete?uid=${groupId}`;
});
// Handle submit of deleteForm via fetch()
const deleteForm = document.getElementById('deleteForm');
deleteForm.addEventListener('submit', async e => {
e.preventDefault();
const url = deleteForm.dataset.url;
const groupId = deleteModal.querySelector('#groupId').textContent;
try {
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}//,
//body: JSON.stringify({ id: groupId }) // Use this for Post requests with [FromBody] parameters like in /Groups/Update
});
const result = await response.json();
if (result.success) {
// Close the modal
const modal = bootstrap.Modal.getInstance(deleteModal);
modal.hide();
// Remove the row from the table
const row = currentButton.closest('tr');
row.classList.add('table-danger');
setTimeout(() => row.remove(), 300);
showToast('@T["Group deleted successfully"]', 'success');
} else {
let exception = result.exception;
switch (exception) {
case "None":
exception = "@T["Unknown error"]";
default:
break;
}
showToast(`${@T["Error"] + ": " + exception}`, 'danger');
}
} catch (error) {
console.error(error);
showToast('@T["Error contacting server"]', 'danger');
}
});
});
</script>
<!-- Groups Create Modal -->
<div class="modal fade" id="createGroupModal" tabindex="-1" aria-labelledby="createGroupModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="createGroupModalLabel">@T["Create group"]</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="createGroupForm">
<div class="modal-body">
<div class="row g-3 justify-content-center">
<!-- Basic Info -->
<div class="col-md-6">
<label class="form-label">@T["Group ID"] *</label>
<input type="text" class="form-control" name="Cn" required />
</div>
<div class="col-md-6">
<label class="form-label">@T["Display Name"] *</label>
<input type="text" class="form-control" name="DisplayName" />
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Permissions.CanInventorize" id="canInventorize" />
<label class="form-check-label" for="canInventorize">@T["Can inventorize"]</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Permissions.CanManageAssets" id="canManageAssets" />
<label class="form-check-label" for="canManageAssets">@T["Can manage assets"]</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Permissions.CanManageUsers" id="canManageUsers" />
<label class="form-check-label" for="canManageUsers">@T["Can manage users"]</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Permissions.CanManageGroups" id="canManageGroups" />
<label class="form-check-label" for="canManageGroups">@T["Can manage groups"]</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Permissions.CanManageLocations" id="canManageLocations" />
<label class="form-check-label" for="canManageLocations">@T["Can manage locations"]</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@T["Cancel"]</button>
<button type="submit" class="btn btn-primary">@T["Create"]</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const createForm = document.getElementById('createGroupForm');
createForm.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(createForm);
const jsonData = {};
// Basic fields
jsonData.Cn = formData.get('Cn');
jsonData.DisplayName = formData.get('DisplayName');
// Permissions
jsonData.Permissions = [];
if (createForm.querySelector('[name="Permissions.CanInventorize"]').checked) jsonData.Permissions.push("CanInventorize");
if (createForm.querySelector('[name="Permissions.CanManageAssets"]').checked) jsonData.Permissions.push("CanManageAssets");
if (createForm.querySelector('[name="Permissions.CanManageUsers"]').checked) jsonData.Permissions.push("CanManageUsers");
if (createForm.querySelector('[name="Permissions.CanManageGroups"]').checked) jsonData.Permissions.push("CanManageGroups");
if (createForm.querySelector('[name="Permissions.CanManageLocations"]').checked) jsonData.Permissions.push("CanManageLocations");
try {
const response = await fetch('/Groups/Create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(jsonData)
});
const result = await response.json();
const createModalEl = document.getElementById('createGroupModal');
if (result.success) {
bootstrap.Modal.getInstance(createModalEl).hide();
createForm.reset();
// Add the new group to the table
const tableBody = document.querySelector('tbody');
const newRow = document.createElement('tr');
newRow.innerHTML = `
<td style="text-align: center">${jsonData.DisplayName}</td>
<td class="text-center ${jsonData.Permissions.includes("CanInventorize") ? "text-success" : "text-danger"}">${jsonData.Permissions.includes("CanInventorize") ? "✓" : "✗"}</td>
<td class="text-center ${jsonData.Permissions.includes("CanManageUsers") ? "text-success" : "text-danger"}">${jsonData.Permissions.includes("CanManageUsers") ? "✓" : "✗"}</td>
<td class="text-center ${jsonData.Permissions.includes("CanManageLocations") ? "text-success" : "text-danger"}">${jsonData.Permissions.includes("CanManageLocations") ? "✓" : "✗"}</td>
<td class="text-center ${jsonData.Permissions.includes("CanManageAssets") ? "text-success" : "text-danger"}">${jsonData.Permissions.includes("CanManageAssets") ? "✓" : "✗"}</td>
<td class="text-center ${jsonData.Permissions.includes("CanManageGroups") ? "text-success" : "text-danger"}">${jsonData.Permissions.includes("CanManageGroups") ? "✓" : "✗"}</td>
<td style="text-align: center">
<div class="d-flex gap-2 justify-content-center">
<button class="btn btn-sm btn-warning btn-update"
data-group-id="${jsonData.Cn}"
data-bs-toggle="modal"
data-bs-target="#updateGroupModal">
@T["Update"]
</button>
<button class="btn btn-sm btn-danger btn-delete"
data-group-id="${jsonData.Cn}"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
@T["Delete"]
</button>
</div>
</td>
`;
tableBody.appendChild(newRow);
showToast('@T["Group created successfully"]', 'success');
} else {
let exception = result.exception;
switch (exception) {
case "Entry Already Exists":
exception = "@T["Entry Already Exists"]";
break;
case "None":
exception = "@T["Unknown error"]";
default:
break;
}
showToast("@T["Error"]: " + result.exception || '@T["Error creating group"]', 'danger');
}
} catch (err) {
console.error(err);
showToast('@T["Error contacting server"]', 'danger');
}
});
});
</script>
<!-- Groups Update Modal -->
<div class="modal fade" id="updateGroupModal" tabindex="-1" aria-labelledby="updateGroupModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="updateGroupModalLabel">@T["Update Group"]</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="updateGroupForm">
<div class="modal-body">
<div class="row g-3 justify-content-center">
<!-- Basic Info -->
<div class="col-md-6">
<label class="form-label">@T["Group ID"] *</label>
<input type="text" class="form-control" name="Cn" required />
</div>
<div class="col-md-6">
<label class="form-label">@T["Display Name"] *</label>
<input type="text" class="form-control" name="DisplayName" />
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Description.Permissions.CanInventorize" id="canInventorize" />
<label class="form-check-label" for="canInventorize">@T["Can inventorize"]</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Description.Permissions.CanManageAssets" id="canManageAssets" />
<label class="form-check-label" for="canManageAssets">@T["Can manage assets"]</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Description.Permissions.CanManageUsers" id="canManageUsers" />
<label class="form-check-label" for="canManageUsers">@T["Can manage users"]</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Description.Permissions.CanManageGroups" id="canManageGroups" />
<label class="form-check-label" for="canManageGroups">@T["Can manage groups"]</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="Description.Permissions.CanManageLocations" id="canManageLocations" />
<label class="form-check-label" for="canManageLocations">@T["Can manage locations"]</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@T["Cancel"]</button>
<button type="submit" class="btn btn-warning">@T["Save Changes"]</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const updateButtons = document.querySelectorAll('.btn-update');
const updateModal = document.getElementById('updateGroupModal');
const updateForm = document.getElementById('updateGroupForm');
updateModal.addEventListener('show.bs.modal', async event => {
const button = event.relatedTarget;
const groupId = button.getAttribute('data-group-id');
updateForm.reset();
try {
const response = await fetch(`/Groups/Get?cn=${groupId}`);
const responseJson = await response.json();
const groups = responseJson.GroupModels[0];
for (const [key, value] of Object.entries(groups)) {
const input = updateForm.querySelector(`[name="${key}"]`);
if (input) input.value = value;
}
if (groups.Permissions) {
for (const permission of groups.Permissions)
{
updateForm.querySelector(`[name="Description.Permissions.${permission}"]`).checked = true;
}
}
} catch (err) {
console.error(err);
showToast('Error loading group data', 'danger');
}
});
updateForm.addEventListener('submit', async e => {
e.preventDefault();
const formData = new FormData(updateForm);
const jsonData = {};
for (const [key, value] of formData.entries()) {
if (!value) continue;
const keys = key.split('.');
let target = jsonData;
for (let i = 0; i < keys.length - 1; i++) {
target[keys[i]] = target[keys[i]] || {};
target = target[keys[i]];
}
target[keys[keys.length - 1]] = value;
}
let permissions = [];
const elements = document.querySelectorAll('[name^="Description.Permissions"]');
for (const element of elements)
{
if (element.checked)
{
permissions.push(element.name.substr(element.name.lastIndexOf(".") + 1));
}
}
if (!jsonData.Description) { // Necessary for when no permissions are selected.
jsonData.Description = {};
}
jsonData.Description.Permissions = permissions;
jsonData.Description.DisplayName = jsonData.DisplayName;
jsonData.DisplayName = null;
try {
const response = await fetch('/Groups/Update', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(jsonData)
});
const result = await response.json();
if (result.success) {
bootstrap.Modal.getInstance(updateModal).hide();
showToast('Group updated successfully', 'success');
// Optionally refresh the row
const row = [...document.querySelectorAll('tr')]
.find(r => r.querySelector(`[data-group-id="${jsonData.Cn}"]`));
if (row) {
row.children[0].textContent = jsonData.Description.DisplayName || '';
row.children[1].textContent = jsonData.Description.Permissions.includes("CanInventorize") ? "✓" : "✗" || '';
row.children[2].textContent = jsonData.Description.Permissions.includes("CanManageUsers") ? "✓" : "✗" || '';
row.children[3].textContent = jsonData.Description.Permissions.includes("CanManageLocations") ? "✓" : "✗" || '';
row.children[4].textContent = jsonData.Description.Permissions.includes("CanManageAssets") ? "✓" : "✗" || '';
row.children[5].textContent = jsonData.Description.Permissions.includes("CanManageGroups") ? "✓" : "✗" || '';
if (jsonData.Description.Permissions.includes("CanInventorize")) {
row.children[1].className = "text-center text-success";
} else {
row.children[1].className = "text-center text-danger";
}
if (jsonData.Description.Permissions.includes("CanManageUsers")) {
row.children[2].className = "text-center text-success";
} else {
row.children[2].className = "text-center text-danger";
}
if (jsonData.Description.Permissions.includes("CanManageLocations")) {
row.children[3].className = "text-center text-success";
} else {
row.children[3].className = "text-center text-danger";
}
if (jsonData.Description.Permissions.includes("CanManageAssets")) {
row.children[4].className = "text-center text-success";
} else {
row.children[4].className = "text-center text-danger";
}
if (jsonData.Description.Permissions.includes("CanManageGroups")) {
row.children[5].className = "text-center text-success";
} else {
row.children[5].className = "text-center text-danger";
}
}
} else {
showToast(result.reason || 'Error updating group', 'danger');
}
} catch (err) {
console.error(err);
showToast('Error contacting server', 'danger');
}
});
});
</script>