mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-20 06:51:55 +00:00
Fixed Asset delete wrong HTTP method, Added Asset creation in /Home/Assets
This commit is contained in:
@@ -38,7 +38,7 @@ public class AssetsController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("Create")]
|
||||
public async Task<AssetsCreateResponseModel> Create(AssetsCreateRequestModel assetModel)
|
||||
public async Task<AssetsCreateResponseModel> Create([FromBody]AssetsCreateRequestModel assetModel)
|
||||
{
|
||||
AssetsCreateResponseModel result;
|
||||
if (assetModel is null)
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
|
||||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-outline-primary">@T["Create asset"]</button>
|
||||
<button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#createAssetModal">
|
||||
@T["Create asset"]
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,11 +22,11 @@
|
||||
<table class="table table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Asset ID</th>
|
||||
<th>Asset Name</th>
|
||||
<th>Location</th>
|
||||
<th>Action</th>
|
||||
<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>
|
||||
@@ -104,7 +106,7 @@
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
@@ -161,45 +163,244 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@* <script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
alert("DEBUG@1");
|
||||
const deleteModal = document.getElementById('deleteModal');
|
||||
deleteModal.addEventListener('show.bs.modal', event => {
|
||||
alert("DEBUG@2");
|
||||
const button = event.relatedTarget; // Button that triggered the modal
|
||||
const assetId = button.getAttribute('data-asset-id');
|
||||
const assetName = button.getAttribute('data-asset-name');
|
||||
|
||||
// Update modal content
|
||||
deleteModal.querySelector('#assetId').textContent = assetId;
|
||||
deleteModal.querySelector('#assetName').textContent = assetName;
|
||||
<!-- 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>
|
||||
|
||||
// Set the form action dynamically
|
||||
const form = deleteModal.querySelector('#deleteForm');
|
||||
form.action = `/Assets/Delete/${assetId}`; // <-- adjust controller/action as needed
|
||||
});
|
||||
});
|
||||
</script> *@
|
||||
<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>
|
||||
|
||||
In case someone wants to reread this:
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">@T["Serial Number"]</label>
|
||||
<input type="text" class="form-control" name="SerialNumber" />
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Crashcourse zu "MVC"</h1>
|
||||
<p>Die HTML, die grad gerendert wird, liegt in /Views/Home/ - dort sollte auch das Login später rein (bspw. als Login.cshtml)</p>
|
||||
<p>Die Web-App folgt der MVC Struktur. D.h. man hat ein Model, ein View und einen Controller. (Wobei dieses View hier hat kein Model. Ist also mehr oder weniger optional.)</p>
|
||||
<h2>McDonalds Vergleich:</h2>
|
||||
<p>Der Controller ist der Kassierer; der nimmt die Informationen entgegen, verarbeitet diese und setzt eine Bestellnotiz zusammen.</p>
|
||||
<p>Das Model ist die Bestellnotiz, die zur Küche weitergegeben wird mit den Informationen, wie es zubereitet werden muss.</p>
|
||||
<p>Der View ist der Angestellte, der die Burger zusammensetzt; er kocht nichts und macht nichts groß kompliziertes, sondern tut nur die Teile in den Burger und gibt das Ergebnis zum Kassierer (d.h. Controller).</p>
|
||||
<h2>Normale Erklärung:</h2>
|
||||
<p>Ein Controller ist zwingend erforderlich und stellt den Endpoint unter einer Route bereit (d.h. das, was man im Browser eingibt, bspw. "/Home/Index")</p>
|
||||
<p>Im Controller von diesem View ("/Controllers/HomeControllers.cs") findet man ein simples "return View();", was dafür sorgt, dass einfach diese .cshtml returned wird.</p>
|
||||
<p>("Warum diese .cshtml?" - Bestimmt er selber anhand der Controller Route. Die Controller Route setzt er zusammen aus dem Namen der Klasse (wenn oberhalb der Klasse "[Route("[controller]")]" steht) und dem, was man vor eine Methode schreibt, bspw. "[HttpGet("Index")]" -> "/Home/Index" und "[HttpGet("/")]" -> "/".)</p>
|
||||
<p>Wenn man Informationen vom Controller an das View übergeben will, tut man das mit einem Model; bspw. "return View(new AssetViewModel { cn = assetName, id = assetId });" </p>
|
||||
<p>Ein Model ist ganz einfach eine Klasse, wie man sie kennt.</p>
|
||||
<p>Logik kommt in den Controller und das HTML kommt in das View (... in der Praxis kommt ins View öfters mal auch bissl mehr Logik rein. Lässt sich nicht verhindern.)</p>
|
||||
<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>
|
||||
<button type="button" class="btn btn-sm btn-outline-success" id="addAttributeBtn">
|
||||
➕ @T["Add Attribute"]
|
||||
</button>
|
||||
</div>
|
||||
<div id="attributesContainer" class="d-flex flex-column gap-2">
|
||||
<!-- Dynamic attribute rows will appear here -->
|
||||
</div>
|
||||
</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 createForm = document.getElementById('createAssetForm');
|
||||
const createModalEl = document.getElementById('createAssetModal');
|
||||
|
||||
createForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(createForm);
|
||||
const jsonData = {};
|
||||
|
||||
// Convert form data into nested JSON for AssetsCreateRequestModel
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/Assets/Create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(jsonData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
const modal = bootstrap.Modal.getInstance(createModalEl);
|
||||
modal.hide();
|
||||
createForm.reset();
|
||||
|
||||
showToast('✅ Asset created successfully', 'success');
|
||||
// Optional: reload page or dynamically add new row
|
||||
// location.reload();
|
||||
} else {
|
||||
showToast(`❌ ${result.reason || 'Error creating asset'}`, 'danger');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showToast('Error contacting server', 'danger');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
/* Handle the attributes list for asset creation*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const attributesContainer = document.getElementById('attributesContainer');
|
||||
const addAttributeBtn = document.getElementById('addAttributeBtn');
|
||||
|
||||
// Add a new attribute row
|
||||
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);
|
||||
});
|
||||
|
||||
// Delegate delete button click
|
||||
attributesContainer.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('btn-remove-attribute')) {
|
||||
e.target.closest('.attribute-row').remove();
|
||||
}
|
||||
});
|
||||
|
||||
// 🔧 Hook into the existing submit handler to include attributes
|
||||
const createForm = document.getElementById('createAssetForm');
|
||||
const originalHandler = createForm.onsubmit;
|
||||
createForm.addEventListener('submit', (e) => {
|
||||
// Let’s patch the JSON generation just before sending
|
||||
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;
|
||||
}
|
||||
|
||||
// Add Attributes as Dictionary<string,string>
|
||||
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;
|
||||
}
|
||||
|
||||
// Replace the body before sending
|
||||
e.preventDefault(); // stop default form submit
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user