Merge pull request #199 from LD-Reborn/192-bug-newly-created-assets-do-not-get-asset-row-class

192 bug newly created assets do not get asset row class
This commit is contained in:
LD50
2025-10-26 21:43:39 +01:00
committed by GitHub
4 changed files with 76 additions and 44 deletions

View File

@@ -63,6 +63,10 @@ public class UsersController : Controller
[HttpPost("Create")] [HttpPost("Create")]
public async Task<UsersCreateResponseModel> Create([FromBody] UsersCreateRequestModel requestModel) public async Task<UsersCreateResponseModel> Create([FromBody] UsersCreateRequestModel requestModel)
{ {
if (requestModel is null)
{
return new() { Success = false, Exception = "The request model is null" };
}
try try
{ {
string? jpegPhoto = requestModel.JpegPhoto; string? jpegPhoto = requestModel.JpegPhoto;

View File

@@ -127,4 +127,7 @@
<data name="Confirm Delete" xml:space="preserve"> <data name="Confirm Delete" xml:space="preserve">
<value>Löschen bestätigen</value> <value>Löschen bestätigen</value>
</data> </data>
<data name="Password must be at least 8 characters long and include upper, lower, number, and special character" xml:space="preserve">
<value>Passwörter müssen mindestens 8 Zeichen lang sein und Groß- und Kleinbuchstaben, sowie mindestens eine Zahl und mindestens ein Sonderzeichen enthalten</value>
</data>
</root> </root>

View File

@@ -112,10 +112,8 @@
deleteModal.addEventListener('show.bs.modal', event => { deleteModal.addEventListener('show.bs.modal', event => {
currentButton = event.relatedTarget; // Button that triggered the modal currentButton = event.relatedTarget; // Button that triggered the modal
const userId = currentButton.getAttribute('data-user-id'); const userId = currentButton.getAttribute('data-user-id');
const userName = currentButton.getAttribute('data-user-name');
deleteModal.querySelector('#userId').textContent = userId; deleteModal.querySelector('#userId').textContent = userId;
deleteModal.querySelector('#userName').textContent = userName;
}); });
@@ -123,7 +121,6 @@
const deleteForm = document.getElementById('deleteForm'); const deleteForm = document.getElementById('deleteForm');
deleteForm.addEventListener('submit', async e => { deleteForm.addEventListener('submit', async e => {
e.preventDefault(); e.preventDefault();
console.log(deleteForm);
const userId = deleteModal.querySelector('#userId').textContent; const userId = deleteModal.querySelector('#userId').textContent;
const url = `/Users/Delete?uid=${userId}`; const url = `/Users/Delete?uid=${userId}`;
@@ -298,6 +295,12 @@
updateForm.addEventListener('submit', async e => { updateForm.addEventListener('submit', async e => {
e.preventDefault(); e.preventDefault();
const password = updateForm.querySelector('input[name="UserPassword"]').value;
if (password.length > 0 && !validatePassword(password)) {
showToast('@T["Password must be at least 8 characters long and include upper, lower, number, and special character"]', 'danger');
return;
}
var dataFromEntries = Object.fromEntries(new FormData(updateForm).entries()); var dataFromEntries = Object.fromEntries(new FormData(updateForm).entries());
var data = unflatten(dataFromEntries); var data = unflatten(dataFromEntries);
data.Description.Groups = Array.from(updateForm.querySelector('#updateGroups').selectedOptions).map(option => option.value); data.Description.Groups = Array.from(updateForm.querySelector('#updateGroups').selectedOptions).map(option => option.value);
@@ -543,6 +546,13 @@
// Submit create form // Submit create form
createForm.addEventListener('submit', async e => { createForm.addEventListener('submit', async e => {
e.preventDefault(); e.preventDefault();
const password = createForm.querySelector('input[name="UserPassword"]').value;
if (password == null || !validatePassword(password)) {
showToast('@T["Password must be at least 8 characters long and include upper, lower, number, and special character"]', 'danger');
return;
}
const dataFromEntries = Object.fromEntries(new FormData(createForm).entries()); const dataFromEntries = Object.fromEntries(new FormData(createForm).entries());
const data = unflatten(dataFromEntries); const data = unflatten(dataFromEntries);
data.Description.Groups = Array.from(createGroupsSelect.selectedOptions).map(o => o.value); data.Description.Groups = Array.from(createGroupsSelect.selectedOptions).map(o => o.value);
@@ -564,7 +574,7 @@
const newRow = document.createElement('tr'); const newRow = document.createElement('tr');
newRow.innerHTML = ` newRow.innerHTML = `
<td><img class="rounded-circle user-icon" src="data:image/jpeg;base64,${data.JpegPhoto || ''}" alt="Photo" style="max-width:300px;" /></td> <td><img class="rounded-circle user-icon" src="data:image/jpeg;base64,${data.JpegPhoto || ''}" alt="Photo" style="max-width:300px;" /></td>
<td>${result.NewUid || ''}</td> <td>${result.Uid || ''}</td>
<td>${data.Title || ''}</td> <td>${data.Title || ''}</td>
<td>${data.Cn || ''}</td> <td>${data.Cn || ''}</td>
<td>${data.Sn || ''}</td> <td>${data.Sn || ''}</td>
@@ -576,22 +586,29 @@
data-user-title="${data.Title || ''}" data-user-title="${data.Title || ''}"
data-user-name="${data.Cn || ''}" data-user-name="${data.Cn || ''}"
data-user-surname="${data.Sn || ''}" data-user-surname="${data.Sn || ''}"
data-user-birthdate="${data.BirthDate}"
data-user-address-city="${data.Description.Address.City}"
data-user-address-street="${data.Description.Address.Street}"
data-user-address-streetnr="${data.Description.Address.StreetNr}"
data-user-workplace="${data.Description?.Workplace || ''}" data-user-workplace="${data.Description?.Workplace || ''}"
data-user-groups='${JSON.stringify(data.Description?.Groups || [])}' data-user-groups='${JSON.stringify(data.Description?.Groups || [])}'
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#updateModal"> data-bs-target="#updateModal">
Update @T["Update"]
</button> </button>
<button class="btn btn-sm btn-danger btn-delete" <button class="btn btn-sm btn-danger btn-delete"
data-user-id="${result.NewUid || ''}" data-user-id="${result.NewUid || ''}"
data-user-name="${data.Cn || ''}" data-user-name="${data.Cn || ''}"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#deleteModal"> data-bs-target="#deleteModal">
Delete @T["Delete"]
</button> </button>
</div> </div>
</td> </td>
`; `;
newRow.setAttribute("data-asset-id", result.Uid);
newRow.classList.toggle("user-row");
registerRowDetailviewClick(newRow);
tbody.appendChild(newRow); tbody.appendChild(newRow);
} else { } else {
showToast(`${result.Exception || '@T["Create failed"]'}`, 'danger'); showToast(`${result.Exception || '@T["Create failed"]'}`, 'danger');
@@ -689,52 +706,53 @@
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const rows = document.querySelectorAll('table tbody tr'); const rows = document.querySelectorAll('table tbody tr');
rows.forEach(row => registerRowDetailviewClick(row));
});
function registerRowDetailviewClick(row) {
const detailModalEl = document.getElementById('detailModal'); const detailModalEl = document.getElementById('detailModal');
const detailModal = new bootstrap.Modal(detailModalEl); const detailModal = new bootstrap.Modal(detailModalEl);
row.addEventListener('click', (e) => {
// Dont trigger when clicking inside the action buttons
if (e.target.closest('button')) return;
rows.forEach(row => { const updateBtn = row.querySelector('.btn-warning[data-user-id]');
row.addEventListener('click', (e) => { if (!updateBtn) return;
// Dont trigger when clicking inside the action buttons
if (e.target.closest('button')) return;
const updateBtn = row.querySelector('.btn-warning[data-user-id]'); // Extract data from update button
if (!updateBtn) return; const data = {
uid: updateBtn.dataset.userId,
// Extract data from update button title: updateBtn.dataset.userTitle,
const data = { name: updateBtn.dataset.userName,
uid: updateBtn.dataset.userId, surname: updateBtn.dataset.userSurname,
title: updateBtn.dataset.userTitle, workplace: updateBtn.dataset.userWorkplace,
name: updateBtn.dataset.userName, birthdate: updateBtn.dataset.userBirthdate,
surname: updateBtn.dataset.userSurname, city: updateBtn.dataset.userAddressCity,
workplace: updateBtn.dataset.userWorkplace, street: updateBtn.dataset.userAddressStreet,
birthdate: updateBtn.dataset.userBirthdate, streetNr: updateBtn.dataset.userAddressStreetnr,
city: updateBtn.dataset.userAddressCity, groups: JSON.parse(updateBtn.dataset.userGroups || '[]'),
street: updateBtn.dataset.userAddressStreet, };
streetNr: updateBtn.dataset.userAddressStreetnr,
groups: JSON.parse(updateBtn.dataset.userGroups || '[]'),
};
// Fill modal fields // Fill modal fields
document.getElementById('detailUid').value = data.uid || ''; document.getElementById('detailUid').value = data.uid || '';
document.getElementById('detailTitle').value = data.title || ''; document.getElementById('detailTitle').value = data.title || '';
document.getElementById('detailName').value = data.name || ''; document.getElementById('detailName').value = data.name || '';
document.getElementById('detailSurname').value = data.surname || ''; document.getElementById('detailSurname').value = data.surname || '';
document.getElementById('detailBirthdate').value = data.birthdate || ''; document.getElementById('detailBirthdate').value = data.birthdate || '';
document.getElementById('detailCity').value = data.city || ''; document.getElementById('detailCity').value = data.city || '';
document.getElementById('detailStreet').value = data.street || ''; document.getElementById('detailStreet').value = data.street || '';
document.getElementById('detailStreetNr').value = data.streetNr || ''; document.getElementById('detailStreetNr').value = data.streetNr || '';
document.getElementById('detailWorkplace').value = data.workplace || ''; document.getElementById('detailWorkplace').value = data.workplace || '';
document.getElementById('detailGroups').value = data.groups.join(', ') || ''; document.getElementById('detailGroups').value = data.groups.join(', ') || '';
// Photo // Photo
const imgEl = row.querySelector('td:first-child img'); const imgEl = row.querySelector('td:first-child img');
const detailPhoto = document.getElementById('detailPhoto'); const detailPhoto = document.getElementById('detailPhoto');
detailPhoto.src = `/Home/UserPhoto?uid=${data.uid}&size=256`; detailPhoto.src = `/Home/UserPhoto?uid=${data.uid}&size=256`;
detailModal.show(); detailModal.show();
});
}); });
}); }
</script> </script>
<style> <style>

View File

@@ -187,4 +187,11 @@ async function loadUsersIntoSelect(selectElement, selectedValue = null) {
console.error('Error loading users:', err); console.error('Error loading users:', err);
showToast(appTranslations.errorLoadingUsers, 'danger'); showToast(appTranslations.errorLoadingUsers, 'danger');
} }
}
function validatePassword(password) {
// Regex: min 8 chars, one uppercase, one lowercase, one number, one special char
const strongPasswordRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_\-+=\[{\]};:'",<.>/?\\|`~]).{8,}$/;
return strongPasswordRegex.test(password);
} }