feat: add attendance export functionality for class sessions
This commit is contained in:
parent
400a0e3001
commit
9d1000a484
105
app/Filament/Exports/AttendanceExport.php
Normal file
105
app/Filament/Exports/AttendanceExport.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Exports;
|
||||||
|
|
||||||
|
use App\Models\Attendance;
|
||||||
|
use App\Models\ClassSession;
|
||||||
|
use App\Models\Student;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithEvents;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||||
|
use Maatwebsite\Excel\Events\AfterSheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
|
class AttendanceExport implements FromCollection, WithEvents, WithHeadings, WithMapping, WithStyles
|
||||||
|
{
|
||||||
|
protected ClassSession $session;
|
||||||
|
|
||||||
|
protected Collection $activeStudents;
|
||||||
|
|
||||||
|
protected Collection $attendanceRecords;
|
||||||
|
|
||||||
|
public function __construct(ClassSession $session)
|
||||||
|
{
|
||||||
|
$session->load('course');
|
||||||
|
$this->session = $session;
|
||||||
|
$this->activeStudents = Student::whereHas('user', fn ($q) => $q->active())
|
||||||
|
->orderBy('full_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$this->attendanceRecords = Attendance::query()
|
||||||
|
->whereHas('courseSchedule', function ($query) {
|
||||||
|
$query->where('course_id', $this->session->course_id);
|
||||||
|
})
|
||||||
|
->whereDate('date', $this->session->date)
|
||||||
|
->get()
|
||||||
|
->keyBy('student_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
return $this->activeStudents->map(fn ($student) => (object) [
|
||||||
|
'full_name' => $student->full_name,
|
||||||
|
'student_number' => $student->student_number,
|
||||||
|
'status' => $this->attendanceRecords->has($student->id) ? 'Hadir' : 'Alpa',
|
||||||
|
'attended_at' => $this->attendanceRecords->get($student->id)?->attended_at,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['Data Presensi '.($this->session->course?->name ?? '').' Sesi Ke-'.$this->session->session_number.' ('.$this->session->date?->translatedFormat('d F Y').')'],
|
||||||
|
['Mahasiswa', 'NIM', 'Status', 'Waktu Presensi'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map($row): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$row->full_name,
|
||||||
|
$row->student_number,
|
||||||
|
$row->status,
|
||||||
|
$row->attended_at ? $row->attended_at->translatedFormat('d F Y H:i') : '-',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function styles(Worksheet $sheet)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
1 => [
|
||||||
|
'font' => ['bold' => true, 'size' => 14],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
|
||||||
|
],
|
||||||
|
2 => [
|
||||||
|
'font' => ['bold' => true],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
AfterSheet::class => function (AfterSheet $event) {
|
||||||
|
$sheet = $event->sheet->getDelegate();
|
||||||
|
$lastColumn = $sheet->getHighestColumn();
|
||||||
|
|
||||||
|
$sheet->mergeCells("A1:{$lastColumn}1");
|
||||||
|
|
||||||
|
$sheet->getStyle("A2:{$lastColumn}".$sheet->getHighestRow())
|
||||||
|
->getAlignment()
|
||||||
|
->setVertical(Alignment::VERTICAL_TOP)
|
||||||
|
->setWrapText(true);
|
||||||
|
|
||||||
|
foreach (range('A', $lastColumn) as $columnID) {
|
||||||
|
$sheet->getColumnDimension($columnID)->setAutoSize(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Learning\ClassSessions\Actions;
|
||||||
|
|
||||||
|
use App\Filament\Exports\AttendanceExport;
|
||||||
|
use App\Models\ClassSession;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
|
class ExportSessionAttendanceAction extends Action
|
||||||
|
{
|
||||||
|
public static function getDefaultName(): ?string
|
||||||
|
{
|
||||||
|
return 'exportAttendance';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->label('Export Presensi')
|
||||||
|
->color('success')
|
||||||
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
|
->link()
|
||||||
|
->action(function (array $arguments) {
|
||||||
|
$session = ClassSession::findOrFail($arguments['session']);
|
||||||
|
$courseName = $session->course?->name ?? '';
|
||||||
|
|
||||||
|
$filename = 'rekap-presensi-'.Str::slug($courseName).'-sesi-'.$session->session_number.'-'.$session->date?->toDateString().'.xlsx';
|
||||||
|
|
||||||
|
return Excel::download(new AttendanceExport($session), $filename);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
use App\Filament\Actions\BackAction;
|
use App\Filament\Actions\BackAction;
|
||||||
use App\Filament\Resources\Learning\ClassSessions\Actions\DeleteSessionAction;
|
use App\Filament\Resources\Learning\ClassSessions\Actions\DeleteSessionAction;
|
||||||
use App\Filament\Resources\Learning\ClassSessions\Actions\EditSessionAction;
|
use App\Filament\Resources\Learning\ClassSessions\Actions\EditSessionAction;
|
||||||
|
use App\Filament\Resources\Learning\ClassSessions\Actions\ExportSessionAttendanceAction;
|
||||||
use App\Filament\Resources\Learning\ClassSessions\Actions\GenerateSessionsAction;
|
use App\Filament\Resources\Learning\ClassSessions\Actions\GenerateSessionsAction;
|
||||||
use App\Filament\Resources\Learning\ClassSessions\Actions\MarkAsSentAction;
|
use App\Filament\Resources\Learning\ClassSessions\Actions\MarkAsSentAction;
|
||||||
use App\Filament\Resources\Learning\ClassSessions\Actions\ShareAttendanceAction;
|
use App\Filament\Resources\Learning\ClassSessions\Actions\ShareAttendanceAction;
|
||||||
@ -126,6 +127,11 @@ public function deleteSessionAction(): DeleteSessionAction
|
|||||||
return DeleteSessionAction::make();
|
return DeleteSessionAction::make();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function exportAttendanceAction(): ExportSessionAttendanceAction
|
||||||
|
{
|
||||||
|
return ExportSessionAttendanceAction::make();
|
||||||
|
}
|
||||||
|
|
||||||
#[Computed]
|
#[Computed]
|
||||||
public function emptyStateHeading(): string
|
public function emptyStateHeading(): string
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"keywords": ["laravel", "framework"],
|
"keywords": ["laravel", "framework"],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.2",
|
||||||
"achyutn/filament-log-viewer": "^2.1",
|
"achyutn/filament-log-viewer": "^2.1",
|
||||||
"asmit/filament-upload": "^2.0",
|
"asmit/filament-upload": "^2.0",
|
||||||
"bezhansalleh/filament-shield": "^4.1",
|
"bezhansalleh/filament-shield": "^4.1",
|
||||||
@ -18,6 +18,8 @@
|
|||||||
"joaopaulolndev/filament-pdf-viewer": "^3.0",
|
"joaopaulolndev/filament-pdf-viewer": "^3.0",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
|
"maatwebsite/excel": "^3.1",
|
||||||
|
"pxlrbt/filament-excel": "^3.6",
|
||||||
"saade/filament-facehash": "^1.0"
|
"saade/filament-facehash": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
944
composer.lock
generated
944
composer.lock
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -66,6 +66,7 @@ class="text-[10px] font-bold text-gray-500">{{ $session->attendance_percentage }
|
|||||||
class="flex flex-wrap items-center gap-3 px-4 py-3 border-t border-gray-100 dark:border-gray-700">
|
class="flex flex-wrap items-center gap-3 px-4 py-3 border-t border-gray-100 dark:border-gray-700">
|
||||||
{{ ($this->viewAttendanceAction)(['session' => $session->id]) }}
|
{{ ($this->viewAttendanceAction)(['session' => $session->id]) }}
|
||||||
{{ ($this->shareAttendanceAction)(['session' => $session->id]) }}
|
{{ ($this->shareAttendanceAction)(['session' => $session->id]) }}
|
||||||
|
{{ ($this->exportAttendanceAction)(['session' => $session->id]) }}
|
||||||
{{ ($this->markAsSentAction)(['record' => $session->id]) }}
|
{{ ($this->markAsSentAction)(['record' => $session->id]) }}
|
||||||
{{ ($this->editSessionAction)(['session' => $session->id]) }}
|
{{ ($this->editSessionAction)(['session' => $session->id]) }}
|
||||||
{{ ($this->deleteSessionAction)(['session' => $session->id]) }}
|
{{ ($this->deleteSessionAction)(['session' => $session->id]) }}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user