Feat: Bulk file downloads
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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,311 +269,411 @@
|
|||||||
@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: [{
|
||||||
|
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>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Important: Initialize the column visibility after table is drawn
|
||||||
|
initComplete: function() {
|
||||||
|
// Now it's safe to set column visibility
|
||||||
|
toggleColumnVisibility();
|
||||||
|
|
||||||
// Toast notification function
|
// Add select-all checkbox
|
||||||
function showToast(message, type) {
|
addSelectAllCheckbox();
|
||||||
const toastContainer = document.createElement('div');
|
},
|
||||||
toastContainer.className = 'position-fixed bottom-0 start-0 p-3';
|
drawCallback: function() {
|
||||||
toastContainer.style.zIndex = '1050';
|
// Ensure select-all checkbox is present on redraw
|
||||||
|
addSelectAllCheckbox();
|
||||||
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
|
|
||||||
initAjaxRoute(dataRoute);
|
|
||||||
|
|
||||||
// Function to toggle column visibility
|
|
||||||
function toggleColumnVisibility() {
|
|
||||||
$('.column-checkbox').each(function() {
|
|
||||||
const columnIndex = $(this).val();
|
|
||||||
const isChecked = $(this).is(':checked');
|
|
||||||
|
|
||||||
// Show or hide the column based on checkbox state
|
|
||||||
table.column(columnIndex).visible(isChecked);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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');
|
||||||
@@ -596,44 +702,57 @@ $(document).ready(function() {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -664,10 +783,6 @@ $(document).ready(function() {
|
|||||||
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
|
||||||
@@ -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');
|
||||||
|
|||||||
Reference in New Issue
Block a user