Files
biiproject-kit-v1/resources/views/pages/system_settings/notification-center.blade.php.bak.20260515085714

318 lines
15 KiB
Plaintext

<x-app-layout>
@push('styles')
<style>
.ck-editor__editable {
min-height: 200px;
}
</style>
@endpush
<div class="container-fluid py-4">
<div class="row align-items-center mb-4">
<div class="col">
<h4 class="fw-bold mb-1">{{ __('Notification Center') }}</h4>
<p class="text-secondary small mb-0">{{ __('Manage system notifications and activity feed.') }}</p>
</div>
@hasanyrole('Developer|Administrator')
<div class="col-auto">
<div class="btn-group me-2">
<button type="button" class="btn btn-outline-dark btn-sm rounded-pill px-3" id="page-mark-all-read">
<i class="bi bi-check-all me-1"></i>{{ __('Mark all read') }}
</button>
<button type="button" class="btn btn-outline-danger btn-sm rounded-pill px-3 ms-2" id="page-clear-read">
<i class="bi bi-trash3 me-1"></i>{{ __('Clear read') }}
</button>
</div>
<button type="button" class="btn btn-dark btn-sm rounded-pill px-3" data-bs-toggle="modal" data-bs-target="#sendNotificationModal">
<i class="bi bi-plus-lg me-1"></i>{{ __('Send') }}
</button>
</div>
@else
<div class="col-auto">
<button type="button" class="btn btn-outline-dark btn-sm rounded-pill px-3" id="page-mark-all-read">
<i class="bi bi-check-all me-1"></i>{{ __('Mark all read') }}
</button>
</div>
@endhasanyrole
</div>
<div class="row justify-content-center">
<div class="col-12 col-xl-10 col-lg-11">
{{-- Standardized Notification Feed --}}
<div id="notification-feed">
<div class="card adminuiux-card border-0 shadow-sm rounded-3">
<div class="card-body text-center py-5">
<div class="spinner-border text-primary spinner-border-sm" role="status"></div>
<p class="text-secondary small mt-2">{{ __('Loading...') }}</p>
</div>
</div>
</div>
{{-- Pagination --}}
<div id="feed-pagination" class="d-flex justify-content-center mt-5"></div>
</div>
</div>
</div>
{{-- MODAL --}}
<div class="modal fade" id="sendNotificationModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-3 border-0 shadow-lg">
<div class="modal-header">
<h5 class="modal-title">{{ __('Send Notification') }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" action="{{ rout('notification-center.store') }}" id="manualNotificationForm" class="ajax-form" data-reset="true">
@csrf
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">{{ __('Title') }} <span class="text-danger">*</span></label>
<input type="text" name="title" class="form-control" placeholder="{{ __('Notification Title') }}" required maxlength="100">
</div>
<div class="mb-3">
<label class="form-label fw-semibold">{{ __('Message') }} <span class="text-danger">*</span></label>
<textarea id="notificationMessage" name="message" class="form-control" rows="4"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">{{ __('Recipient') }} <span class="text-danger">*</span></label>
<select name="recipient" class="form-select">
<option value="all">{{ __('All Users (Public)') }}</option>
@foreach($roles as $roleName)
<option value="{{ $roleName }}" {{ $roleName === 'Developer' ? 'selected' : '' }}>{{ ucfirst($roleName) }}</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">{{ __('Type') }} <span class="text-danger">*</span></label>
<select name="type" class="form-select">
<option value="info">{{ __('Information') }}</option>
<option value="warning">{{ __('Warning') }}</option>
<option value="system">{{ __('System Alert') }}</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark rounded-pill px-4" data-bs-dismiss="modal">
{{ __('Close') }}
</button>
<button type="submit" class="btn btn-dark rounded-pill px-4">
<i class="bi bi-send me-1"></i> {{ __('Send Notification') }}
</button>
</div>
</form>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.ckeditor.com/ckeditor5/41.1.0/classic/ckeditor.js" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
let notificationEditor;
let currentPage = 1;
const editorEl = document.querySelector('#notificationMessage');
if (editorEl && typeof ClassicEditor !== 'undefined') {
ClassicEditor
.create(editorEl, {
ckfinder: { uploadUrl: "{{ route('editor.upload') }}?_token={{ csrf_token() }}" }
})
.then(newEditor => {
notificationEditor = newEditor;
editorEl.ckeditorInstance = newEditor;
})
.catch(error => console.error('CKEditor Error:', error));
}
// Load feed — this MUST always run
loadFeed(1);
function loadFeed(page = 1) {
currentPage = page;
const $container = $('#notification-feed');
$.ajax({
url: "{{ route('notification-center.index') }}",
data: {
start: (page - 1) * 10,
length: 10,
draw: 1
},
success: function(response) {
console.log('Notification Response:', response);
if (!response.data || response.data.length === 0) {
$container.html(`
<div class="text-center py-5 opacity-50">
<i class="bi bi-inbox h1 display-1"></i>
<p class="small mt-2">{{ __('Inbox is empty') }}</p>
</div>
`);
} else {
let html = '';
response.data.forEach(n => {
try {
html += window.renderNotificationCard(n);
} catch (e) {
console.error('Render Error:', e, n);
}
});
$container.html(html);
}
renderPagination(response.recordsTotal, page);
},
error: function(xhr, status, error) {
console.error('AJAX Error:', status, error, xhr.responseText);
let message = 'Failed to load notifications.';
try {
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
} catch(e) {}
$container.html(`
<div class="card adminuiux-card border-0 shadow-sm rounded-3">
<div class="card-body text-center py-5 text-danger">
<i class="bi bi-exclamation-circle h3"></i>
<p class="small mt-2">${message}</p>
<p class="text-muted smallest">Status: ${xhr.status}</p>
<button class="btn btn-sm btn-outline-danger mt-2" onclick="location.reload()">Refresh Page</button>
</div>
</div>
`);
}
});
}
window.reloadFeed = () => loadFeed(currentPage);
function renderPagination(total, current) {
const pages = Math.ceil(total / 10);
if (pages <= 1) { $('#feed-pagination').html(''); return; }
let html = '<ul class="pagination pagination-sm m-0">';
// Previous button
html += `<li class="page-item ${current === 1 ? 'disabled' : ''}"><a class="page-link rounded-circle mx-1 border-0 shadow-sm" href="#" data-page="${current - 1}">&laquo;</a></li>`;
// Page numbers with sliding window (current page +/- 2)
const delta = 2;
const left = current - delta;
const right = current + delta;
const range = [];
for (let i = 1; i <= pages; i++) {
if (i === 1 || i === pages || (i >= left && i <= right)) {
range.push(i);
}
}
let last = 0;
for (let i of range) {
if (last) {
if (i - last === 2) {
html += `<li class="page-item"><a class="page-link rounded-circle mx-1 border-0 shadow-sm" href="#" data-page="${last + 1}">${last + 1}</a></li>`;
} else if (i - last !== 1) {
html += `<li class="page-item disabled"><span class="page-link rounded-circle mx-1 border-0 shadow-sm">...</span></li>`;
}
}
html += `<li class="page-item ${i === current ? 'active' : ''}"><a class="page-link rounded-circle mx-1 border-0 shadow-sm" href="#" data-page="${i}">${i}</a></li>`;
last = i;
}
// Next button
html += `<li class="page-item ${current === pages ? 'disabled' : ''}"><a class="page-link rounded-circle mx-1 border-0 shadow-sm" href="#" data-page="${current + 1}">&raquo;</a></li>`;
html += '</ul>';
$('#feed-pagination').html(html);
}
$(document).on('click', '.page-link', function(e) {
e.preventDefault();
loadFeed($(this).data('page'));
});
$(document).on('click', '.btn-delete', function() {
const url = $(this).data('url');
StandardSwal.fire({
title: 'Delete this notification?',
text: 'This notification will be permanently removed from your history.',
icon: 'warning',
showCancelButton: true,
customClass: {
confirmButton: 'btn-pill-danger',
cancelButton: 'btn-pill-cancel'
},
confirmButtonText: 'Yes, Delete',
cancelButtonText: "Cancel",
}).then(result => {
if (result.isConfirmed) {
$.ajax({
url: url,
method: 'DELETE',
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
success: (res) => {
window.reloadNotificationUI();
window.showNotificationToast('success', res.message || 'Notification deleted');
},
error: (xhr) => window.showNotificationToast('error', 'Delete failed')
});
}
});
});
$('#page-mark-all-read').on('click', function() {
$.ajax({
url: "{{ route('notification-center.read-all') }}",
method: 'PATCH',
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
success: (res) => {
window.reloadNotificationUI();
window.showNotificationToast('success', res.message || 'All marked as read');
},
error: () => window.showNotificationToast('error', 'Process failed')
});
});
$('#page-clear-read').on('click', function() {
StandardSwal.fire({
title: "Clear all read notifications?",
text: "All read updates will be permanently purged from your feed.",
icon: 'warning',
showCancelButton: true,
customClass: {
confirmButton: 'btn-pill-danger',
cancelButton: 'btn-pill-cancel'
},
confirmButtonText: "Yes, Clear",
cancelButtonText: "Cancel",
}).then(result => {
if (result.isConfirmed) {
$.ajax({
url: "{{ route('notification-center.clear-read') }}",
method: 'DELETE',
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
success: (res) => {
window.reloadNotificationUI();
window.showNotificationToast('success', res.message || 'Read notifications cleared');
},
error: () => window.showNotificationToast('error', 'Clear failed')
});
}
});
});
// Listen for standard AJAX success to reload feed
$('#manualNotificationForm').on('ajaxForm:success', function() {
window.reloadNotificationUI();
if (notificationEditor) notificationEditor.setData('');
});
});
</script>
@endpush
</x-app-layout>