Merge pull request #177 from LD-Reborn/176-feature-add-user-selection-drop-down-for-assets-owner-field

176 feature add user selection drop down for assets owner field
This commit is contained in:
LD50
2025-10-26 11:45:14 +01:00
committed by GitHub
5 changed files with 164 additions and 22 deletions

View File

@@ -157,4 +157,13 @@
<data name="Add Barcode to print batch" xml:space="preserve"> <data name="Add Barcode to print batch" xml:space="preserve">
<value>Barcode zum Druckauftragsstapel hinzufügen</value> <value>Barcode zum Druckauftragsstapel hinzufügen</value>
</data> </data>
<data name="Select location" xml:space="preserve">
<value>Ort auswählen</value>
</data>
<data name="Select owner" xml:space="preserve">
<value>Besitzer auswählen</value>
</data>
<data name="Select user" xml:space="preserve">
<value>Benutzer auswählen</value>
</data>
</root> </root>

View File

@@ -5,6 +5,8 @@
@{ @{
ViewData["Title"] = T["Assets"]; ViewData["Title"] = T["Assets"];
} }
<link href="https://cdn.jsdelivr.net/npm/tom-select/dist/css/tom-select.bootstrap5.min.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'"/>
<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js" defer></script>
<partial name="_BatchButton"/> <partial name="_BatchButton"/>
@@ -177,9 +179,10 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">@T["Owner"]</label> <label class="form-label">@T["Owner"]</label>
<input type="text" class="form-control" name="Owner" /> <select class="form-select" name="Owner" id="createUsersSelect">
<option value="">@T["Select owner"]</option>
</select>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">@T["Serial Number"]</label> <label class="form-label">@T["Serial Number"]</label>
<input type="text" class="form-control" name="SerialNumber" /> <input type="text" class="form-control" name="SerialNumber" />
@@ -388,9 +391,10 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">@T["Owner"]</label> <label class="form-label">@T["Owner"]</label>
<input type="text" class="form-control" name="Owner" /> <select class="form-select" name="Owner" id="updateUsersSelect">
<option value="">@T["Select owner"]</option>
</select>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">@T["Serial Number"]</label> <label class="form-label">@T["Serial Number"]</label>
<input type="text" class="form-control" name="SerialNumber" /> <input type="text" class="form-control" name="SerialNumber" />
@@ -485,13 +489,16 @@ document.addEventListener('DOMContentLoaded', () => {
updateAttributesContainer.innerHTML = ''; updateAttributesContainer.innerHTML = '';
updateForm.reset(); updateForm.reset();
const locationSelect = updateForm.querySelector('#updateLocationSelect');
await loadLocationsIntoSelect(locationSelect);
try { try {
const response = await fetch(`/Assets/Get?cn=${assetId}`); const response = await fetch(`/Assets/Get?cn=${assetId}`);
const responseJson = await response.json(); const responseJson = await response.json();
const asset = responseJson.assetsModel; const asset = responseJson.assetsModel;
const locationSelect = updateForm.querySelector('#updateLocationSelect');
await loadLocationsIntoSelect(locationSelect, asset.Location);
const usersSelect = updateForm.querySelector('#updateUsersSelect');
await loadUsersIntoSelect(usersSelect, asset.Owner);
for (const [key, value] of Object.entries(asset)) { for (const [key, value] of Object.entries(asset)) {
const input = updateForm.querySelector(`[name="${key}"]`); const input = updateForm.querySelector(`[name="${key}"]`);
if (input) input.value = value; if (input) input.value = value;
@@ -762,11 +769,67 @@ document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const createModal = document.getElementById('createAssetModal'); const createModal = document.getElementById('createAssetModal');
createModal.addEventListener('show.bs.modal', async () => { createModal.addEventListener('show.bs.modal', async () => {
const select = createModal.querySelector('#createLocationSelect'); const selectLocations = createModal.querySelector('#createLocationSelect');
await loadLocationsIntoSelect(select); await loadLocationsIntoSelect(selectLocations);
const selectUsers = createModal.querySelector('#createUsersSelect');
await loadUsersIntoSelect(selectUsers);
}); });
}); });
</script> </script>
<!-- TomSelect dropdowns -->
<script>
// Locations dropdowns
document.addEventListener('DOMContentLoaded', () => {
const createLocationSelect = document.getElementById('createLocationSelect');
const updateLocationSelect = document.getElementById('updateLocationSelect');
async function initLocationSelect(selectElement) {
if (!selectElement) return;
await loadLocationsIntoSelect(selectElement);
new TomSelect(selectElement, {
plugins: ['clear_button'],
create: false,
sortField: { field: 'text', direction: 'asc' },
placeholder: '@T["Select location"]',
maxOptions: 500, // avoid performance hit if there are many
render: {
no_results: function(data, escape) {
return `<div class="no-results">@T["No locations found"]</div>`;
}
}
});
}
initLocationSelect(createLocationSelect);
initLocationSelect(updateLocationSelect);
// Users dropdowns
const createUsersSelect = document.getElementById('createUsersSelect');
const updateUsersSelect = document.getElementById('updateUsersSelect');
async function initUsersSelect(selectElement) {
if (!selectElement) return;
await loadUsersIntoSelect(selectElement);
new TomSelect(selectElement, {
plugins: ['clear_button'],
create: false,
sortField: { field: 'text', direction: 'asc' },
placeholder: '@T["Select user"]',
maxOptions: 500, // avoid performance hit if there are many
render: {
no_results: function(data, escape) {
return `<div class="no-results">@T["No users found"]</div>`;
}
}
});
}
initUsersSelect(createUsersSelect);
initUsersSelect(updateUsersSelect);
});
</script>
<partial name="_Batch"/> <partial name="_Batch"/>

View File

@@ -20,7 +20,9 @@
<script> <script>
window.appTranslations = { window.appTranslations = {
selectLocation: '@T["Select location"]', selectLocation: '@T["Select location"]',
errorLoading: '@T["Error loading locations"]' errorLoadingLocations: '@T["Error loading locations"]',
selectUser: '@T["Select user"]',
errorLoadingUsers: '@T["Error loading users"]'
}; };
</script> </script>
</head> </head>

View File

@@ -40,4 +40,27 @@ body {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
float: right; float: right;
} }
/* Tomselect dark mode theme */
[data-bs-theme="dark"] .ts-control,
[data-bs-theme="dark"] .ts-dropdown {
color: #f8f9fa !important;
border-color: #444 !important;
}
[data-bs-theme="dark"] .ts-control input,
[data-bs-theme="dark"] .ts-dropdown .option,
[data-bs-theme="dark"] .ts-dropdown .item {
color: #f8f9fa !important;
}
[data-bs-theme="dark"] .ts-dropdown .option:hover,
[data-bs-theme="dark"] .ts-dropdown .active {
color: #ffffff !important;
}
[data-bs-theme="dark"] .ts-control .item {
color: #f8f9fa !important;
border-color: #666 !important;
}

View File

@@ -123,20 +123,65 @@ async function loadLocationsIntoSelect(selectElement, selectedValue = null) {
const response = await fetch('/Locations/Index'); const response = await fetch('/Locations/Index');
const locations = await response.json(); const locations = await response.json();
selectElement.innerHTML = `<option value="">${appTranslations.selectLocation}</option>`; const ts = selectElement.tomselect;
if (!ts) {
selectElement.innerHTML = `<option value="">${appTranslations.selectLocation}</option>`;
locations.forEach(loc => {
const option = document.createElement('option');
option.value = loc.location;
option.textContent = loc.location;
if (selectedValue && selectedValue === loc.location) {
option.selected = true;
}
selectElement.appendChild(option);
});
return;
}
ts.clearOptions();
ts.addOption(locations.map(loc => ({
value: loc.location,
text: loc.location
})));
ts.refreshOptions(false);
if (selectedValue) {
ts.setValue(selectedValue);
} else {
ts.clear(true);
}
locations.forEach(loc => {
const text = `${loc.description.Location}, Room ${loc.description.RoomNumber}, Seat ${loc.description.Seat}`;
const option = document.createElement('option');
option.value = loc.location;
option.textContent = text;
if (selectedValue && selectedValue === loc.location) {
option.selected = true;
}
selectElement.appendChild(option);
});
} catch (err) { } catch (err) {
console.error('Error loading locations:', err); console.error('Error loading locations:', err);
showToast(appTranslations.errorLoading, 'danger'); showToast(appTranslations.errorLoadingLocations, 'danger');
}
}
async function loadUsersIntoSelect(selectElement, selectedValue = null) {
try {
const response = await fetch('/Users/Index?Cn=false&Sn=false&Title=false&Description=false&JpegPhoto=false&UserPassword=false');
const users = await response.json();
const ts = selectElement.tomselect;
if (!ts) {
selectElement.innerHTML = `<option value="">${appTranslations.selectUser}</option>`;
users.forEach(u => {
const opt = document.createElement('option');
opt.value = u.uid;
opt.textContent = u.uid;
selectElement.appendChild(opt);
});
return;
}
ts.clearOptions();
ts.addOption(users.map(u => ({ value: u.uid, text: u.uid })));
ts.refreshOptions(false);
if (selectedValue) ts.setValue(selectedValue);
} catch (err) {
console.error('Error loading users:', err);
showToast(appTranslations.errorLoadingUsers, 'danger');
} }
} }