mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-20 06:51:55 +00:00
346 lines
15 KiB
Plaintext
346 lines
15 KiB
Plaintext
@using Microsoft.AspNetCore.Mvc.Localization
|
||
@using Berufsschule_HAM.Models
|
||
@model HomeIndexViewModel
|
||
@inject IViewLocalizer T
|
||
@{
|
||
ViewData["Title"] = T["Assets"];
|
||
}
|
||
|
||
|
||
<div class="container py-4">
|
||
<h2 class="mb-3">@T["Assets"]</h2>
|
||
|
||
|
||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||
<button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#createAssetModal">
|
||
@T["Create asset"]
|
||
</button>
|
||
</div>
|
||
|
||
|
||
<div class="table-responsive">
|
||
<table class="table table-striped align-middle">
|
||
<thead>
|
||
<tr>
|
||
<th>@T["Owner"]</th>
|
||
<th>@T["Asset ID"]</th>
|
||
<th>@T["Asset Name"]</th>
|
||
<th>@T["Location"]</th>
|
||
<th>@T["Action"]</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@{
|
||
foreach (AssetsTableViewModel assetsTableViewModel in Model.AssetsTableViewModels)
|
||
{
|
||
<tr>
|
||
<td>@assetsTableViewModel.UserUID</td>
|
||
<td>@assetsTableViewModel.AssetCn</td>
|
||
<td>@assetsTableViewModel.AssetName</td>
|
||
<td>@assetsTableViewModel.LocationName</td>
|
||
<td>
|
||
<div class="d-flex gap-2">
|
||
<button class="btn btn-sm btn-primary">@T["Update"]</button>
|
||
<button class="btn btn-sm btn-danger btn-delete"
|
||
data-asset-id="@assetsTableViewModel.AssetCn"
|
||
data-bs-toggle="modal"
|
||
data-bs-target="#deleteModal">
|
||
🗑️ @T["Delete"]
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
}
|
||
}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Asset 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">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>Are you sure you want to delete the asset <strong id="assetName"></strong> (ID: <span id="assetId"></span>)?</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||
<form id="deleteForm" method="post" action="">
|
||
<button type="submit" class="btn btn-danger">Yes, Delete</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>
|
||
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;
|
||
}
|
||
|
||
// 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());
|
||
}
|
||
|
||
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 assetId = currentButton.getAttribute('data-asset-id');
|
||
const assetName = currentButton.getAttribute('data-asset-name');
|
||
|
||
deleteModal.querySelector('#assetId').textContent = assetId;
|
||
deleteModal.querySelector('#assetName').textContent = assetName;
|
||
|
||
// Store the delete URL for later use
|
||
deleteModal.querySelector('#deleteForm').dataset.url = `/Assets/Delete?cn=${assetId}`;
|
||
});
|
||
|
||
// Handle submit of deleteForm via fetch()
|
||
const deleteForm = document.getElementById('deleteForm');
|
||
deleteForm.addEventListener('submit', async e => {
|
||
e.preventDefault();
|
||
|
||
const url = deleteForm.dataset.url;
|
||
const assetId = deleteModal.querySelector('#assetId').textContent;
|
||
|
||
try {
|
||
const response = await fetch(url, {
|
||
method: 'DELETE',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json'
|
||
}//,
|
||
//body: JSON.stringify({ id: assetId }) // 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('Asset deleted successfully', 'success');
|
||
} else {
|
||
showToast(`${result.reason}: ${result.exception || 'Unknown error'}`, 'danger');
|
||
}
|
||
} catch (error) {
|
||
console.error(error);
|
||
showToast('Error contacting server', 'danger');
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- Asset Create Modal -->
|
||
<div class="modal fade" id="createAssetModal" tabindex="-1" aria-labelledby="createAssetModalLabel" 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="createAssetModalLabel">@T["Create Asset"]</h5>
|
||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
|
||
<form id="createAssetForm">
|
||
<div class="modal-body">
|
||
<div class="row g-3">
|
||
<!-- Basic Info -->
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Asset ID (Cn)"] *</label>
|
||
<input type="text" class="form-control" name="Cn" required />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Name"]</label>
|
||
<input type="text" class="form-control" name="Name" />
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Location"]</label>
|
||
<input type="text" class="form-control" name="Location" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Owner"]</label>
|
||
<input type="text" class="form-control" name="Owner" />
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Serial Number"]</label>
|
||
<input type="text" class="form-control" name="SerialNumber" />
|
||
</div>
|
||
|
||
<hr class="my-3" />
|
||
|
||
<!-- Description Section -->
|
||
<h6 class="fw-bold">@T["Description"]</h6>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Type"]</label>
|
||
<input type="text" class="form-control" name="Description.Type" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Make"]</label>
|
||
<input type="text" class="form-control" name="Description.Make" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Model"]</label>
|
||
<input type="text" class="form-control" name="Description.Model" />
|
||
</div>
|
||
|
||
|
||
<!-- Attributes Section -->
|
||
<div class="col-12 mt-3">
|
||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||
<h6 class="fw-bold mb-0">@T["Attributes"]</h6>
|
||
</div>
|
||
<div id="attributesContainer" class="d-flex flex-column gap-2">
|
||
<!-- Dynamic attribute rows will appear here -->
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-outline-success mt-3" id="addAttributeBtn">
|
||
➕ @T["Add Attribute"]
|
||
</button>
|
||
</div>
|
||
|
||
<hr class="my-3" />
|
||
|
||
<!-- Purchase Info -->
|
||
<h6 class="fw-bold">@T["Purchase Information"]</h6>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Purchase Date"]</label>
|
||
<input type="date" class="form-control" name="Description.Purchase.PurchaseDate" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Purchase Value"]</label>
|
||
<input type="text" class="form-control" name="Description.Purchase.PurchaseValue" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Purchased At"]</label>
|
||
<input type="text" class="form-control" name="Description.Purchase.PurchasedAt" />
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">@T["Purchased By"]</label>
|
||
<input type="text" class="form-control" name="Description.Purchase.PurchasedBy" />
|
||
</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 attributesContainer = document.getElementById('attributesContainer');
|
||
const addAttributeBtn = document.getElementById('addAttributeBtn');
|
||
|
||
addAttributeBtn.addEventListener('click', () => {
|
||
const row = document.createElement('div');
|
||
row.className = 'd-flex gap-2 align-items-center attribute-row';
|
||
row.innerHTML = `
|
||
<input type="text" class="form-control" placeholder="Attribute name" data-attr-name />
|
||
<input type="text" class="form-control" placeholder="Attribute value" data-attr-value />
|
||
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-attribute">✖</button>
|
||
`;
|
||
attributesContainer.appendChild(row);
|
||
});
|
||
|
||
attributesContainer.addEventListener('click', (e) => {
|
||
if (e.target.classList.contains('btn-remove-attribute')) {
|
||
e.target.closest('.attribute-row').remove();
|
||
}
|
||
});
|
||
|
||
const createForm = document.getElementById('createAssetForm');
|
||
const originalHandler = createForm.onsubmit;
|
||
createForm.addEventListener('submit', (e) => {
|
||
const formData = new FormData(createForm);
|
||
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;
|
||
}
|
||
|
||
const attributes = {};
|
||
document.querySelectorAll('#attributesContainer .attribute-row').forEach(row => {
|
||
const name = row.querySelector('[data-attr-name]').value.trim();
|
||
const value = row.querySelector('[data-attr-value]').value.trim();
|
||
if (name) attributes[name] = value;
|
||
});
|
||
|
||
if (Object.keys(attributes).length > 0) {
|
||
jsonData.Description = jsonData.Description || {};
|
||
jsonData.Description.Attributes = attributes;
|
||
}
|
||
|
||
e.preventDefault();
|
||
|
||
fetch('/Assets/Create', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: JSON.stringify(jsonData)
|
||
})
|
||
.then(res => res.json())
|
||
.then(result => {
|
||
const createModalEl = document.getElementById('createAssetModal');
|
||
if (result.success) {
|
||
bootstrap.Modal.getInstance(createModalEl).hide();
|
||
createForm.reset();
|
||
attributesContainer.innerHTML = '';
|
||
showToast('Asset created successfully', 'success');
|
||
} else {
|
||
showToast(`${result.reason || 'Error creating asset'}`, 'danger');
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
showToast('Error contacting server', 'danger');
|
||
});
|
||
});
|
||
});
|
||
</script> |