Implemented user creation in frontend

This commit is contained in:
2025-10-25 22:44:36 +02:00
parent 310e05545f
commit 85cb68a6c2
4 changed files with 220 additions and 14 deletions

View File

@@ -15,7 +15,9 @@
<div class="mb-4 d-flex flex-wrap gap-2">
<button class="btn btn-primary">@T["Create user"]</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">
@T["Create user"]
</button>
</div>
@@ -461,3 +463,188 @@
});
</script>
<!-- User Create Modal -->
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="createModalLabel">Create User</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createForm">
<div class="row g-3">
<h6 class="fw-bold">Personal information</h6>
<div class="col-md-6">
<label class="form-label">Title</label>
<input type="text" name="Title" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Name</label>
<input type="text" name="Cn" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Surname</label>
<input type="text" name="Sn" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Birth date</label>
<input type="text" name="Description.BirthDate" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">City</label>
<input type="text" name="Description.Address.City" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Street</label>
<input type="text" name="Description.Address.Street" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Street Nr.</label>
<input type="text" name="Description.Address.StreetNr" class="form-control" />
</div>
<hr class="my-3">
<h6 class="fw-bold">Workplace & account</h6>
<div class="col-md-6">
<label class="form-label">Workplace</label>
<input type="text" name="Description.Workplace" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Groups</label>
<select id="createGroups" name="Description.Groups" class="form-select" multiple></select>
</div>
<div class="col-md-6">
<label class="form-label">Password</label>
<input type="password" name="UserPassword" class="form-control" />
</div>
<div class="col-md-6">
<label class="form-label">Photo</label>
<input type="file" id="createPhotoFile" accept="image/*" class="form-control" />
<input type="hidden" id="createJpegPhoto" name="JpegPhoto" />
<div class="mt-2 text-center">
<img id="createPhotoPreview" src="" alt="Preview" class="img-thumbnail" style="max-height: 150px; display: none;" />
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" form="createForm" class="btn btn-primary">Create</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const createModal = document.getElementById('createModal');
const createForm = document.getElementById('createForm');
const createGroupsSelect = document.getElementById('createGroups');
const photoInput = document.getElementById('createPhotoFile');
const photoPreview = document.getElementById('createPhotoPreview');
const photoHidden = document.getElementById('createJpegPhoto');
// Load available groups when modal is shown
createModal.addEventListener('show.bs.modal', async () => {
await populateGroupsDropdown(createGroupsSelect);
createForm.reset();
photoPreview.style.display = 'none';
});
// Handle photo upload preview
photoInput.addEventListener('change', () => {
const file = photoInput.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = e => {
const base64 = e.target.result.split(',')[1];
photoHidden.value = base64;
photoPreview.src = e.target.result;
photoPreview.style.display = 'block';
};
reader.readAsDataURL(file);
});
// Submit create form
createForm.addEventListener('submit', async e => {
e.preventDefault();
const dataFromEntries = Object.fromEntries(new FormData(createForm).entries());
const data = unflatten(dataFromEntries);
data.Description.Groups = Array.from(createGroupsSelect.selectedOptions).map(o => o.value);
try {
const response = await fetch('/Users/Create', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.Success) {
bootstrap.Modal.getInstance(createModal).hide();
showToast('User created successfully', 'success');
// Add new row to table dynamically
const tbody = document.querySelector('table tbody');
const newRow = document.createElement('tr');
newRow.innerHTML = `
<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>${data.Title || ''}</td>
<td>${data.Cn || ''}</td>
<td>${data.Sn || ''}</td>
<td>${data.Description?.Workplace || ''}</td>
<td>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-warning"
data-user-id="${result.Uid || ''}"
data-user-title="${data.Title || ''}"
data-user-name="${data.Cn || ''}"
data-user-surname="${data.Sn || ''}"
data-user-workplace="${data.Description?.Workplace || ''}"
data-user-groups='${JSON.stringify(data.Description?.Groups || [])}'
data-bs-toggle="modal"
data-bs-target="#updateModal">
Update
</button>
<button class="btn btn-sm btn-danger btn-delete"
data-user-id="${result.NewUid || ''}"
data-user-name="${data.Cn || ''}"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
Delete
</button>
</div>
</td>
`;
tbody.appendChild(newRow);
} else {
showToast(`${result.Exception || 'Create failed'}`, 'danger');
}
} catch (err) {
console.error(err);
showToast('Error creating user', 'danger');
}
});
async function populateGroupsDropdown(selectElement) {
try {
const res = await fetch('/Groups/Get');
const json = await res.json();
if (!json.success) return;
selectElement.innerHTML = '';
json.GroupModels.forEach(group => {
const opt = document.createElement('option');
opt.value = group.Cn;
opt.textContent = group.DisplayName || group.Cn;
selectElement.appendChild(opt);
});
} catch (err) {
console.error('Error fetching groups', err);
}
}
});
</script>