Improve notifications

This commit is contained in:
DariusIII
2025-06-06 16:05:15 +02:00
parent b17a0a3a7a
commit d29d54bb27
3 changed files with 169 additions and 84 deletions
@@ -216,10 +216,12 @@ class AdminUserController extends BasePageController
{
if ($request->has('id')) {
$user = User::find($request->input('id'));
$username = $user->username; // Store username before deletion
$user->delete();
return redirect()->to('admin/user-list');
// Redirect with username to display in notification
return redirect()->to('admin/user-list?deleted=1&username='.urlencode($username));
}
if ($request->has('redir')) {
+47 -2
View File
@@ -94,9 +94,12 @@
<td>{$user->deleted_at|date_format:"%Y-%m-%d %H:%M"}</td>
<td>{if $user->lastlogin != ""}{$user->lastlogin|date_format:"%Y-%m-%d %H:%M"}{else}Never{/if}</td>
<td>
<a href="{$smarty.const.WWW_TOP}/admin/deleted-users/restore/{$user->id}" class="btn btn-success btn-sm me-2" title="Restore User">
<button class="btn btn-success btn-sm me-2 restore-user"
data-user-id="{$user->id}"
data-username="{$user->username}"
title="Restore User">
<i class="fas fa-user-check"></i> Restore
</a>
</button>
<button class="btn btn-danger btn-sm delete-permanently"
data-user-id="{$user->id}"
data-username="{$user->username}"
@@ -158,6 +161,32 @@
</div>
</div>
<!-- Restore User Confirmation Modal -->
<div class="modal fade" id="restoreUserModal" tabindex="-1" aria-labelledby="restoreUserModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title" id="restoreUserModalLabel">Confirm User Restoration</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> Please confirm this action
</div>
<p>You are about to restore user <strong id="restoreUserName"></strong>.</p>
<p>This will reactivate the user account and grant them access to the system again.</p>
<p>Are you sure you want to continue?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<a href="#" id="confirmRestoreBtn" class="btn btn-success">
<i class="fas fa-user-check"></i> Restore User
</a>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Set up delete confirmation modal
@@ -175,5 +204,21 @@ document.addEventListener('DOMContentLoaded', function() {
deleteUserModal.show();
});
});
// Set up restore confirmation modal
const restoreUserModal = new bootstrap.Modal(document.getElementById('restoreUserModal'));
const restoreButtons = document.querySelectorAll('.restore-user');
restoreButtons.forEach(button => {
button.addEventListener('click', function() {
const userId = this.getAttribute('data-user-id');
const username = this.getAttribute('data-username');
document.getElementById('restoreUserName').textContent = username;
document.getElementById('confirmRestoreBtn').href = '{$smarty.const.WWW_TOP}/admin/deleted-users/restore/' + userId;
restoreUserModal.show();
});
});
});
</script>
+119 -81
View File
@@ -233,7 +233,7 @@
</a>
{/if}
{if $user->roles_id != "2"}
<a href="{{url("/admin/user-delete?id={$user->id}")}}" class="btn btn-sm btn-outline-danger confirm_action" data-bs-toggle="tooltip" data-bs-placement="top" title="Delete user">
<a href="{{url("/admin/user-delete?id={$user->id}")}}" class="btn btn-sm btn-outline-danger custom_delete_action" data-bs-toggle="tooltip" data-bs-placement="top" title="Delete user">
<i class="fa fa-trash"></i>
</a>
{/if}
@@ -265,94 +265,132 @@
{/if}
</div>
<script>
{literal}
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
<!-- Delete User Confirmation Modal -->
<div class="modal fade" id="deleteUserModal" tabindex="-1" aria-labelledby="deleteUserModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteUserModalLabel">
<i class="fa fa-exclamation-triangle me-2"></i>Delete User Confirmation
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="fw-bold mb-2">You are about to delete the following user:</p>
<div class="alert alert-info d-flex align-items-center">
<i class="fa fa-user me-2 fs-5"></i>
<span id="deleteUserName" class="fw-bold fs-5"></span>
</div>
<p>This action will soft-delete the user account. The user will no longer be able to log in, but their data will remain in the database.</p>
<p class="text-danger fw-bold">Are you absolutely sure you want to proceed?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<a href="#" id="confirmDeleteBtn" class="btn btn-danger">
<i class="fa fa-trash me-1"></i>Delete User
</a>
</div>
</div>
</div>
</div>
// Confirm action for delete
document.querySelectorAll('.confirm_action').forEach(function(element) {
element.addEventListener('click', function(e) {
if (!confirm('Are you sure you want to delete this user? This cannot be undone.')) {
e.preventDefault();
return false;
}
});
<script>
{literal}
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Enhanced delete confirmation
document.querySelectorAll('.custom_delete_action').forEach(function(element) {
element.addEventListener('click', function(e) {
e.preventDefault();
// Get the user details
const row = this.closest('tr');
const username = row.querySelector('td:first-child a').textContent.trim();
const deleteUrl = this.getAttribute('href');
// Set the modal content
document.getElementById('deleteUserName').textContent = username;
document.getElementById('confirmDeleteBtn').setAttribute('href', deleteUrl);
// Show the modal
const deleteModal = new bootstrap.Modal(document.getElementById('deleteUserModal'));
deleteModal.show();
});
});
{/literal}
</script>
<style>
{literal}
/* Sort controls styling */
.sort-controls {
display: flex;
flex-direction: column;
font-size: 0.8rem;
margin-left: 0.3rem;
// Toast notification system
function showToast(message, type) {
const toastContainer = document.getElementById('toast-container');
const toastElement = document.createElement('div');
toastElement.className = `toast align-items-center text-white bg-${type} border-0`;
toastElement.setAttribute('role', 'alert');
toastElement.setAttribute('aria-live', 'assertive');
toastElement.setAttribute('aria-atomic', 'true');
const toastFlex = document.createElement('div');
toastFlex.className = 'd-flex';
const toastBody = document.createElement('div');
toastBody.className = 'toast-body';
toastBody.innerHTML = message;
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.className = 'btn-close btn-close-white me-2 m-auto';
closeButton.setAttribute('data-bs-dismiss', 'toast');
closeButton.setAttribute('aria-label', 'Close');
toastFlex.appendChild(toastBody);
toastFlex.appendChild(closeButton);
toastElement.appendChild(toastFlex);
toastContainer.appendChild(toastElement);
const toast = new bootstrap.Toast(toastElement, {
delay: 5000
});
toast.show();
toastElement.addEventListener('hidden.bs.toast', function () {
toastElement.remove();
});
}
.sort-icon {
color: #6c757d;
opacity: 0.6;
transition: opacity 0.2s;
// If there's a success parameter in URL (will be added by our controller)
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('deleted')) {
const username = urlParams.get('username');
showToast(`User "${username}" has been successfully deleted.`, 'success');
}
});
{/literal}
</script>
.sort-icon:hover, .sort-icon.active {
color: #0d6efd;
opacity: 1;
}
<!-- Toast container for notifications -->
<div id="toast-container" class="toast-container position-fixed bottom-0 end-0 p-3"></div>
/* Table styling improvements */
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
<style>
{literal}
/* Sort controls styling */
.sort-controls {
display: flex;
flex-direction: column;
font-size: 0.8rem;
margin-left: 0.3rem;
}
/* Badge styling */
.badge {
font-weight: 500;
padding: 0.4em 0.6em;
}
.sort-icon {
color: #6c757d;
}
/* Pagination container */
.pagination-container {
max-width: 100%;
}
/* Improve buttons spacing in small screens */
@media (max-width: 767.98px) {
.btn-group .btn {
padding: 0.25rem 0.5rem;
}
.card-footer .d-flex {
flex-direction: column;
gap: 0.5rem;
align-items: center !important;
}
.pagination-container {
justify-content: center !important;
}
}
/* Improve search form on small screens */
@media (max-width: 767.98px) {
#user-search-form .row {
margin-right: 0;
margin-left: 0;
}
#user-search-form .col-md-12 {
padding-right: 0;
padding-left: 0;
}
}
{/literal}
</style>
/* Toast styling */
.toast {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
{/literal}
</style>