Feat: Bulk file downloads

This commit is contained in:
Sallu9007
2025-05-11 14:59:00 +05:30
parent 6b84de9472
commit c224497740
3 changed files with 529 additions and 312 deletions

View File

@@ -39,7 +39,7 @@ class ActivitiesAttendedController extends Controller
'activity_type' => 'required|string', 'activity_type' => 'required|string',
'category' => 'required|string', 'category' => 'required|string',
'level' => 'required|string', 'level' => 'required|string',
'proof' => 'nullable|mimes:jpg,jpeg,png,pdf,doc,docx,zip', 'proof' => 'nullable|mimes:jpg,jpeg,png,pdf,doc,docx,zip,ppt,pptx|max:5120', // 5MB max
]); ]);
// Combine start date and time // Combine start date and time
@@ -113,6 +113,73 @@ class ActivitiesAttendedController extends Controller
return response()->json(['success' => 'Record deleted successfully']); return response()->json(['success' => 'Record deleted successfully']);
} }
public function downloadProofs(Request $request)
{
// Validate the request
$request->validate([
'ids' => 'required|string',
]);
$ids = json_decode($request->input('ids'));
if (empty($ids)) {
return back()->with('error', 'No items selected');
}
// Get the records with proofs
$responses = ActivitiesAttended::whereIn('id', $ids)
->whereNotNull('proof')
->get();
if ($responses->isEmpty()) {
return back()->with('error', 'No valid proofs found');
}
// Create a temporary zip file
$zipFileName = 'proofs_' . time() . '.zip';
$zipFilePath = storage_path('app/public/temp/' . $zipFileName);
// Ensure the temp directory exists
if (!Storage::disk('public')->exists('temp')) {
Storage::disk('public')->makeDirectory('temp');
}
$zip = new \ZipArchive();
if ($zip->open($zipFilePath, \ZipArchive::CREATE) !== TRUE) {
return back()->with('error', 'Could not create zip file');
}
$fileCount = 0;
foreach ($responses as $response) {
if (Storage::disk('public')->exists($response->proof)) {
$fileContent = Storage::disk('public')->get($response->proof);
// Get original filename from path
$originalName = basename($response->proof);
// Add file to the zip with a prefix to avoid duplicate names
$fileNameInZip = $response->id . '_' . $originalName;
$zip->addFromString($fileNameInZip, $fileContent);
$fileCount++;
}
}
$zip->close();
if ($fileCount === 0) {
// Delete the empty zip file
if (file_exists($zipFilePath)) {
unlink($zipFilePath);
}
return back()->with('error', 'No files found to download');
}
// Return the zip file as a download
return response()->download($zipFilePath, $zipFileName)->deleteFileAfterSend(true);
}
public function getActivitiesAttendedResponses(Request $request) public function getActivitiesAttendedResponses(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();

View File

@@ -61,67 +61,67 @@
<div class="row g-2"> <div class="row g-2">
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="1" id="column-title" checked> <input class="form-check-input column-checkbox" type="checkbox" value="2" id="column-title" checked>
<label class="form-check-label" for="column-title">Title</label> <label class="form-check-label" for="column-title">Title</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="2" id="column-institute" checked> <input class="form-check-input column-checkbox" type="checkbox" value="3" id="column-institute" checked>
<label class="form-check-label" for="column-institute">Organising Institute</label> <label class="form-check-label" for="column-institute">Organising Institute</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="3" id="column-address" checked> <input class="form-check-input column-checkbox" type="checkbox" value="4" id="column-address" checked>
<label class="form-check-label" for="column-address">Address</label> <label class="form-check-label" for="column-address">Address</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="4" id="column-department" checked> <input class="form-check-input column-checkbox" type="checkbox" value="5" id="column-department" checked>
<label class="form-check-label" for="column-department">Department</label> <label class="form-check-label" for="column-department">Department</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="5" id="column-faculty" checked> <input class="form-check-input column-checkbox" type="checkbox" value="6" id="column-faculty" checked>
<label class="form-check-label" for="column-faculty">Faculty</label> <label class="form-check-label" for="column-faculty">Faculty</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="6" id="column-start-date" checked> <input class="form-check-input column-checkbox" type="checkbox" value="7" id="column-start-date" checked>
<label class="form-check-label" for="column-start-date">Start Date</label> <label class="form-check-label" for="column-start-date">Start Date</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="7" id="column-end-date" checked> <input class="form-check-input column-checkbox" type="checkbox" value="8" id="column-end-date" checked>
<label class="form-check-label" for="column-end-date">End Date</label> <label class="form-check-label" for="column-end-date">End Date</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="8" id="column-days" checked> <input class="form-check-input column-checkbox" type="checkbox" value="9" id="column-days" unchecked>
<label class="form-check-label" for="column-days">Days</label> <label class="form-check-label" for="column-days">Days</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="9" id="column-activity-type" checked> <input class="form-check-input column-checkbox" type="checkbox" value="10" id="column-activity-type" checked>
<label class="form-check-label" for="column-activity-type">Activity Type</label> <label class="form-check-label" for="column-activity-type">Activity Type</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="10" id="column-category" checked> <input class="form-check-input column-checkbox" type="checkbox" value="11" id="column-category" checked>
<label class="form-check-label" for="column-category">Category</label> <label class="form-check-label" for="column-category">Category</label>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input column-checkbox" type="checkbox" value="11" id="column-level" checked> <input class="form-check-input column-checkbox" type="checkbox" value="12" id="column-level" checked>
<label class="form-check-label" for="column-level">Level</label> <label class="form-check-label" for="column-level">Level</label>
</div> </div>
</div> </div>
@@ -139,12 +139,18 @@
</div> </div>
</div> </div>
</div> </div>
<button id="download-proofs" class="btn btn-success ms-2" disabled>
<i class="fas fa-download me-1"></i> Download Proofs
</button>
</div> </div>
<!-- Table --> <!-- Table -->
<div class="table-responsive"> <div class="table-responsive">
<table id="responses-table" class="table table-striped table-hover"> <table id="responses-table" class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th class="select-checkbox"></th>
<th>ID</th> <th>ID</th>
<th>Title</th> <th>Title</th>
<th>Organising Institute</th> <th>Organising Institute</th>
@@ -263,321 +269,421 @@
@endsection @endsection
@section('scripts') @section('scripts')
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js" integrity="sha512-Tn2m0TIpgVyTzzvmxLNuqbSJH3JP8jm+Cy3hvHrW7ndTDcJ1w5mBiksqDBb8GpE2ksktFvDB/ykZ0mDpsZj20w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script> <script>
const downloadProofsRoute = "{{ route('activitiesAttended.downloadProofs') }}";
const csrf_token = "{{ csrf_token() }}";
$(document).ready(function() { $(document).ready(function() {
// Handle "Select All" checkbox // Handle "Select All" checkbox
$('#selectAll').change(function() { $('#selectAll').change(function() {
const isChecked = $(this).prop('checked'); const isChecked = $(this).prop('checked');
$('input[name="categories[]"]').prop('checked', isChecked); $('input[name="categories[]"]').prop('checked', isChecked);
});
// Update "Select All" when individual checkboxes change
$('input[name="categories[]"]').change(function() {
const totalCheckboxes = $('input[name="categories[]"]').length;
const checkedCheckboxes = $('input[name="categories[]"]:checked').length;
$('#selectAll').prop('checked', totalCheckboxes === checkedCheckboxes);
});
// Form validation for the modal
const form = document.getElementById('missingProofsForm');
if (form) {
form.addEventListener('submit', function(e) {
const checkboxes = form.querySelectorAll('input[type="checkbox"]:checked');
if (checkboxes.length === 0) {
e.preventDefault();
alert('Please select at least one category before sending emails.');
}
}); });
}
// Update "Select All" when individual checkboxes change const sheetName = "Responses Report";
$('input[name="categories[]"]').change(function() { let table; // Declare table variable in the outer scope
const totalCheckboxes = $('input[name="categories[]"]').length;
const checkedCheckboxes = $('input[name="categories[]"]:checked').length;
$('#selectAll').prop('checked', totalCheckboxes === checkedCheckboxes);
});
function exportOptions() {
// Form validation for the modal return {
const form = document.getElementById('missingProofsForm'); columns: ':visible',
if (form) { format: {
form.addEventListener('submit', function(e) { body: function(data, row, column, node) {
const checkboxes = form.querySelectorAll('input[type="checkbox"]:checked'); if ($(node).find('select').length) {
return $(node).find("select option:selected").text();
if (checkboxes.length === 0) { }
e.preventDefault(); return $(node).text();
alert('Please select at least one category before sending emails.');
} }
}); }
}
const sheetName = "Responses Report";
var initAjaxRoute = function(route) {
table = $("#responses-table").DataTable({
fnDestroy: true,
processing: true,
serverSide: true,
responsive: true,
ajax: {
url: route,
data: function(d) {
d.department = $('#department-filter').val();
d.category = $('#category-filter').val();
d.dateFrom = $('#date-from').val();
d.dateTo = $('#date-to').val();
}
},
columns: [{
data: 'id',
name: 'id',
searchable: false
},
{
data: 'title',
name: 'title',
orderable: false
},
{
data: 'organising_institute',
name: 'organising_institute',
orderable: false
},
{
data: 'address',
name: 'address',
orderable: false
},
{
data: 'department_name',
name: 'department_name',
orderable: false,
searchable: false
},
{
data: 'user_name',
name: 'user_name',
orderable: false
},
{
data: 'start_date',
name: 'start_date',
orderable: false
},
// { data: 'start_time', name: 'start_time', orderable: false },
{
data: 'end_date',
name: 'end_date',
orderable: false
},
// { data: 'end_time', name: 'end_time', orderable: false },
{
data: 'num_days',
name: 'num_days',
orderable: false
},
{
data: 'activity_type',
name: 'activity_type',
orderable: false
},
{
data: 'category',
name: 'category',
orderable: false,
searchable: false
},
{
data: 'level',
name: 'level',
orderable: false
},
{
data: 'action',
name: 'proof',
orderable: false,
searchable: false
}
],
columnDefs: [{
targets: '_all',
className: 'text-center wrap-text'
},
{
targets: 12,
render: function(data, type, row) {
return '<div class="btn-group" role="group">' +
data +
'</div>';
}
}
],
dom: '<"d-flex justify-content-between align-items-center mb-3"<"d-flex align-items-center"l><"d-flex"f<"ms-2"B>>>rtip',
buttons: [{
extend: 'copy',
text: '<i class="fas fa-copy me-1"></i> Copy',
className: 'btn btn-sm btn-outline-white',
title: sheetName,
exportOptions: exportOptions()
},
{
extend: 'csv',
text: '<i class="fas fa-file-csv me-1"></i> CSV',
className: 'btn btn-sm btn-outline-white',
title: sheetName,
exportOptions: exportOptions()
},
{
extend: 'excel',
text: '<i class="fas fa-file-excel me-1"></i> Excel',
className: 'btn btn-sm btn-outline-white',
title: sheetName,
exportOptions: exportOptions()
},
{
extend: 'pdf',
text: '<i class="fas fa-file-pdf me-1"></i> PDF',
className: 'btn btn-sm btn-outline-white',
title: sheetName,
exportOptions: exportOptions()
},
{
extend: 'print',
text: '<i class="fas fa-print me-1"></i> Print',
className: 'btn btn-sm btn-outline-white',
title: sheetName,
exportOptions: exportOptions()
}
],
language: {
search: "<i class='fas fa-search'></i> _INPUT_",
searchPlaceholder: "Search records...",
lengthMenu: "<i class='fas fa-list me-1'></i> _MENU_ records per page",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
paginate: {
first: "<i class='fas fa-angle-double-left'></i>",
last: "<i class='fas fa-angle-double-right'></i>",
next: "<i class='fas fa-angle-right'></i>",
previous: "<i class='fas fa-angle-left'></i>"
}
}
});
// Apply filters when they change
$('#department-filter, #category-filter, #date-from, #date-to').change(function() {
table.ajax.reload();
});
}; };
}
// Delete button handler // Function to toggle column visibility - FIXED to check if responsive is initialized
$('#responses-table').on('click', '.delete-btn', function() { function toggleColumnVisibility() {
if (confirm('Are you sure you want to delete this record?')) { $('.column-checkbox').each(function() {
const id = $(this).data('id'); const columnIndex = $(this).val();
const url = $(this).data('url'); const isChecked = $(this).is(':checked');
$.ajax({ // Show or hide the column based on checkbox state
url: url, if (table) {
type: 'DELETE', table.column(columnIndex).visible(isChecked);
data: {
"_token": "{{ csrf_token() }}"
},
success: function(result) {
table.ajax.reload();
showToast('Record deleted successfully', 'success');
},
error: function(error) {
console.error(error);
showToast('Error deleting record', 'danger');
}
});
} }
}); });
function exportOptions() { // Adjust table layout only if table and responsive are initialized
return { if (table && table.responsive) {
columns: ':visible', table.columns.adjust().responsive.recalc();
format: { } else if (table) {
body: function(data, row, column, node) { table.columns.adjust();
if ($(node).find('select').length) { }
return $(node).find("select option:selected").text(); }
}
return $(node).text(); var initAjaxRoute = function(route) {
// If table already exists, destroy it before re-initializing
if ($.fn.DataTable.isDataTable('#responses-table')) {
$('#responses-table').DataTable().destroy();
}
table = $("#responses-table").DataTable({
processing: true,
serverSide: true,
responsive: true,
ajax: {
url: route,
data: function(d) {
d.department = $('#department-filter').val();
d.category = $('#category-filter').val();
d.dateFrom = $('#date-from').val();
d.dateTo = $('#date-to').val();
}
},
columns: [{
data: null,
defaultContent: '',
orderable: false,
searchable: false
},
{
data: 'id',
name: 'id',
searchable: false
},
{
data: 'title',
name: 'title',
orderable: false
},
{
data: 'organising_institute',
name: 'organising_institute',
orderable: false
},
{
data: 'address',
name: 'address',
orderable: false
},
{
data: 'department_name',
name: 'department_name',
orderable: false,
searchable: false
},
{
data: 'user_name',
name: 'user_name',
orderable: false
},
{
data: 'start_date',
name: 'start_date',
orderable: false
},
{
data: 'end_date',
name: 'end_date',
orderable: false
},
{
data: 'num_days',
name: 'num_days',
orderable: false
},
{
data: 'activity_type',
name: 'activity_type',
orderable: false
},
{
data: 'category',
name: 'category',
orderable: false,
searchable: false
},
{
data: 'level',
name: 'level',
orderable: false
},
{
data: 'action',
name: 'proof',
orderable: false,
searchable: false
}
],
columnDefs: [{
targets: 0,
orderable: false,
className: 'select-checkbox',
render: function(data, type, row) {
return row.proof ?
'<input type="checkbox" class="row-checkbox" data-id="' + row.id + '">' :
'<input type="checkbox" disabled>';
}
},
{
targets: '_all',
className: 'text-center wrap-text'
},
{
targets: 13,
render: function(data, type, row) {
return '<div class="btn-group" role="group">' +
data +
'</div>';
} }
} }
}; ],
} dom: '<"d-flex justify-content-between align-items-center mb-3"<"d-flex align-items-center"l><"d-flex"f<"ms-2"B>>>rtip',
buttons: [{
// Toast notification function extend: 'copy',
function showToast(message, type) { text: '<i class="fas fa-copy me-1"></i> Copy',
const toastContainer = document.createElement('div'); className: 'btn btn-sm btn-outline-white',
toastContainer.className = 'position-fixed bottom-0 start-0 p-3'; title: sheetName,
toastContainer.style.zIndex = '1050'; exportOptions: exportOptions()
},
const toastEl = document.createElement('div'); {
toastEl.className = `toast align-items-center text-white bg-${type} border-0`; extend: 'csv',
toastEl.setAttribute('role', 'alert'); text: '<i class="fas fa-file-csv me-1"></i> CSV',
toastEl.setAttribute('aria-live', 'assertive'); className: 'btn btn-sm btn-outline-white',
toastEl.setAttribute('aria-atomic', 'true'); title: sheetName,
exportOptions: exportOptions()
toastEl.innerHTML = ` },
<div class="d-flex"> {
<div class="toast-body"> extend: 'excel',
${message} text: '<i class="fas fa-file-excel me-1"></i> Excel',
</div> className: 'btn btn-sm btn-outline-white',
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button> title: sheetName,
</div> exportOptions: exportOptions()
`; },
{
toastContainer.appendChild(toastEl); extend: 'pdf',
document.body.appendChild(toastContainer); text: '<i class="fas fa-file-pdf me-1"></i> PDF',
className: 'btn btn-sm btn-outline-white',
const toast = new bootstrap.Toast(toastEl, { title: sheetName,
autohide: true, exportOptions: exportOptions()
delay: 3000 },
}); {
extend: 'print',
toast.show(); text: '<i class="fas fa-print me-1"></i> Print',
className: 'btn btn-sm btn-outline-white',
toastEl.addEventListener('hidden.bs.toast', function() { title: sheetName,
document.body.removeChild(toastContainer); exportOptions: exportOptions()
}); }
} ],
language: {
// Set the appropriate data route based on user role search: "<i class='fas fa-search'></i> _INPUT_",
const userRole = "{{ auth()->user()->role->name }}"; searchPlaceholder: "Search records...",
let dataRoute = "{{ route('admin.ActivitiesAttendedResponses.data') }}"; lengthMenu: "<i class='fas fa-list me-1'></i> _MENU_ records per page",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
if (userRole === 'Coordinator') { paginate: {
dataRoute = "{{ route('coordinator.ActivitiesAttendedResponses.data') }}"; first: "<i class='fas fa-angle-double-left'></i>",
} last: "<i class='fas fa-angle-double-right'></i>",
if (userRole === 'Faculty') { next: "<i class='fas fa-angle-right'></i>",
dataRoute = "{{ route('faculty.ActivitiesAttendedResponses.data') }}"; previous: "<i class='fas fa-angle-left'></i>"
} }
},
// Initialize the data table // Important: Initialize the column visibility after table is drawn
initAjaxRoute(dataRoute); initComplete: function() {
// Now it's safe to set column visibility
// Function to toggle column visibility toggleColumnVisibility();
function toggleColumnVisibility() {
$('.column-checkbox').each(function() { // Add select-all checkbox
const columnIndex = $(this).val(); addSelectAllCheckbox();
const isChecked = $(this).is(':checked'); },
drawCallback: function() {
// Show or hide the column based on checkbox state // Ensure select-all checkbox is present on redraw
table.column(columnIndex).visible(isChecked); addSelectAllCheckbox();
}); }
// Adjust table layout to prevent horizontal scrolling
table.columns.adjust().responsive.recalc();
}
// Attach change event listener to checkboxes
$('.column-checkbox').off('change').on('change', function() {
toggleColumnVisibility();
}); });
// Ensure "Actions" column is always visible // Apply filters when they change
$('#column-actions').prop('disabled', true); $('#department-filter, #category-filter, #date-from, #date-to').change(function() {
table.ajax.reload();
});
// Initial call to set column visibility return table;
};
// Delete button handler
$('#responses-table').on('click', '.delete-btn', function() {
if (confirm('Are you sure you want to delete this record?')) {
const id = $(this).data('id');
const url = $(this).data('url');
$.ajax({
url: url,
type: 'DELETE',
data: {
"_token": "{{ csrf_token() }}"
},
success: function(result) {
table.ajax.reload();
showToast('Record deleted successfully', 'success');
},
error: function(error) {
console.error(error);
showToast('Error deleting record', 'danger');
}
});
}
});
// Handle row selection and Download button state
$('#responses-table').on('change', '.row-checkbox', function() {
const selectedRows = $('.row-checkbox:checked').length;
$('#download-proofs').prop('disabled', selectedRows === 0);
});
// Download proofs button handler
$('#download-proofs').on('click', function() {
const selectedIds = [];
$('.row-checkbox:checked').each(function() {
const id = $(this).data('id');
if (id) selectedIds.push(id);
});
if (selectedIds.length > 0) {
// Create a form to submit the request
const form = $('<form></form>')
.attr('method', 'POST')
.attr('action', downloadProofsRoute)
.css('display', 'none');
// Add CSRF token
$('<input>')
.attr('type', 'hidden')
.attr('name', '_token')
.attr('value', csrf_token)
.appendTo(form);
// Add selected IDs
$('<input>')
.attr('type', 'hidden')
.attr('name', 'ids')
.attr('value', JSON.stringify(selectedIds))
.appendTo(form);
$('body').append(form);
form.submit();
}
});
// Function to add select-all checkbox to the table header
function addSelectAllCheckbox() {
// Only add if it doesn't exist yet
if ($('#select-all-checkbox').length === 0) {
const selectAllCheckbox = $('<input>', {
type: 'checkbox',
id: 'select-all-checkbox',
class: 'form-check-input'
});
// Add to the first header column
$('#responses-table thead th.select-checkbox').html(selectAllCheckbox);
// Handle the select all functionality
$('#select-all-checkbox').on('change', function() {
const isChecked = $(this).prop('checked');
$('.row-checkbox:not(:disabled)').prop('checked', isChecked);
// Update download button state
const selectedRows = $('.row-checkbox:checked').length;
$('#download-proofs').prop('disabled', selectedRows === 0);
});
}
}
// Toast notification function
function showToast(message, type) {
const toastContainer = document.createElement('div');
toastContainer.className = 'position-fixed bottom-0 start-0 p-3';
toastContainer.style.zIndex = '1050';
const toastEl = document.createElement('div');
toastEl.className = `toast align-items-center text-white bg-${type} border-0`;
toastEl.setAttribute('role', 'alert');
toastEl.setAttribute('aria-live', 'assertive');
toastEl.setAttribute('aria-atomic', 'true');
toastEl.innerHTML = `
<div class="d-flex">
<div class="toast-body">
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
toastContainer.appendChild(toastEl);
document.body.appendChild(toastContainer);
const toast = new bootstrap.Toast(toastEl, {
autohide: true,
delay: 3000
});
toast.show();
toastEl.addEventListener('hidden.bs.toast', function() {
document.body.removeChild(toastContainer);
});
}
// Set the appropriate data route based on user role
const userRole = "{{ auth()->user()->role->name }}";
let dataRoute = "{{ route('admin.ActivitiesAttendedResponses.data') }}";
if (userRole === 'Coordinator') {
dataRoute = "{{ route('coordinator.ActivitiesAttendedResponses.data') }}";
}
if (userRole === 'Faculty') {
dataRoute = "{{ route('faculty.ActivitiesAttendedResponses.data') }}";
}
// Initialize the data table
table = initAjaxRoute(dataRoute);
// Attach change event listener to column visibility checkboxes AFTER table is initialized
$('.column-checkbox').on('change', function() {
toggleColumnVisibility(); toggleColumnVisibility();
}); });
</script>
<script> // Ensure "Actions" column is always visible
$(document).ready(function() { $('#column-actions').prop('disabled', true);
// Select all columns button // Select all columns button
$('#select-all-columns').click(function() { $('#select-all-columns').click(function() {
$('.column-checkbox').prop('checked', true).trigger('change'); $('.column-checkbox').prop('checked', true).trigger('change');
}); });
// Deselect all columns button // Deselect all columns button
$('#deselect-all-columns').click(function() { $('#deselect-all-columns').click(function() {
$('.column-checkbox').prop('checked', false).trigger('change'); $('.column-checkbox').prop('checked', false).trigger('change');
}); });
// Prevent dropdown from closing when clicking inside it // Prevent dropdown from closing when clicking inside it
$('.dropdown-menu').on('click', function(e) { $('.dropdown-menu').on('click', function(e) {
e.stopPropagation(); e.stopPropagation();
@@ -590,84 +696,93 @@ $(document).ready(function() {
border-color: #ed1c24; border-color: #ed1c24;
} }
.dropdown-menu { .dropdown-menu {
background-color: #fff; background-color: #fff;
border-radius: 0.5rem; border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
} }
.dropdown-menu .form-check-input:checked { .dropdown-menu .form-check-input:checked {
background-color: #b7202e; background-color: #b7202e;
border-color: #ed1c24; border-color: #ed1c24;
} }
.dropdown-menu .form-check-input:focus { .dropdown-menu .form-check-input:focus {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:checked:focus { .dropdown-menu .form-check-input:checked:focus {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible { .dropdown-menu .form-check-input:focus-visible {
outline: none; outline: none;
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:checked:focus-visible { .dropdown-menu .form-check-input:checked:focus-visible {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:checked { .dropdown-menu .form-check-input:focus-visible:checked {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked) { .dropdown-menu .form-check-input:focus-visible:not(:checked) {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):checked { .dropdown-menu .form-check-input:focus-visible:not(:checked):checked {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked) { .dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked) {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):checked { .dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):checked {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked) { .dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked) {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked):checked { .dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked):checked {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked):not(:checked) { .dropdown-menu .form-check-input:focus-visible:not(:checked):not(:checked):not(:checked):not(:checked) {
box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25); box-shadow: 0 0 0 0.2rem rgba(189, 72, 46, 0.25);
} }
.form-switch .form-check-input { .form-switch .form-check-input {
width: 2.5em; width: 2.5em;
margin-left: -2.8em; margin-left: -2.8em;
position: relative; position: relative;
} }
.form-switch .form-check-input:focus { .form-switch .form-check-input:focus {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");
} }
.form-switch .form-check-input:checked { .form-switch .form-check-input:checked {
background-position: right center; background-position: right center;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
transition: background-position 0.15s ease-in-out; transition: background-position 0.15s ease-in-out;
} }
/* Enhance the toggle appearance */ /* Enhance the toggle appearance */
.form-switch .form-check-input { .form-switch .form-check-input {
background-size: contain; background-size: contain;
transition: 0.2s; transition: 0.2s;
} }
.dropdown-menu { .dropdown-menu {
max-height: 400px; max-height: 400px;
overflow-y: auto; overflow-y: auto;
} }
</style>
<style>
/* Ensure the table container is responsive */ /* Ensure the table container is responsive */
.table-responsive { .table-responsive {
overflow-x: auto; overflow-x: auto;
@@ -678,5 +793,38 @@ $(document).ready(function() {
#responses-table { #responses-table {
width: 100% !important; width: 100% !important;
} }
.select-checkbox {
width: 30px;
text-align: center;
}
.select-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.select-checkbox input[type="checkbox"]:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Style the Download button */
#download-proofs {
background-color: #28a745;
border-color: #28a745;
}
#download-proofs:hover:not(:disabled) {
background-color: #218838;
border-color: #218838;
}
#download-proofs:disabled {
opacity: 0.65;
cursor: not-allowed;
}
</style> </style>
@endsection @endsection

View File

@@ -288,6 +288,8 @@ Route::prefix('admin')->name('admin.')->group(function () {
Route::get('analytics/contribution', [AdminController::class, 'analyticsContribution'])->name('analytics.contribution'); Route::get('analytics/contribution', [AdminController::class, 'analyticsContribution'])->name('analytics.contribution');
}); });
Route::post('/activities-attended/download-proofs', [ActivitiesAttendedController::class, 'downloadProofs'])->name('activitiesAttended.downloadProofs');
// Google Login Route // Google Login Route
Route::get('/auth/google', [App\Http\Controllers\Auth\GoogleController::class, 'redirectToGoogle'])->name('google.login'); Route::get('/auth/google', [App\Http\Controllers\Auth\GoogleController::class, 'redirectToGoogle'])->name('google.login');
Route::get('/auth/google/callback', [App\Http\Controllers\Auth\GoogleController::class, 'handleGoogleCallback'])->name('google.callback'); Route::get('/auth/google/callback', [App\Http\Controllers\Auth\GoogleController::class, 'handleGoogleCallback'])->name('google.callback');