feat: introduce detailed viewing actions for assignments and materials in class sessions, enhancing user interaction with modals for comprehensive content display

This commit is contained in:
Yoga Pangestu 2026-03-30 00:43:14 +07:00
parent b7e8f64d8b
commit 63241349cd
12 changed files with 174 additions and 21 deletions

View File

@ -0,0 +1,53 @@
<?php
namespace App\Filament\Resources\Learning\ClassSessions\Actions;
use App\Models\Assignment;
use Filament\Actions\Action;
use Filament\Infolists\Components\TextEntry;
use Filament\Support\Enums\Width;
use Joaopaulolndev\FilamentPdfViewer\Infolists\Components\PdfViewerEntry;
class ViewAssignmentDetailAction extends Action
{
public static function getDefaultName(): ?string
{
return 'viewAssignmentDetail';
}
protected function setUp(): void
{
parent::setUp();
$this->modalHeading(fn (array $arguments) => Assignment::find($arguments['record'], ['*'])?->title ?? 'Detail Tugas')
->modalSubmitAction(false)
->modalCancelActionLabel('Tutup')
->modalWidth(Width::FourExtraLarge)
->infolist(function (array $arguments) {
$record = Assignment::find($arguments['record'], ['*']);
if (! $record) {
return [];
}
$entries = [
TextEntry::make('description')
->label('Deskripsi')
->html()
->columnSpanFull(),
TextEntry::make('due_date')
->label('Batas Waktu')
->dateTime('l, d F Y, H:i')
->columnSpanFull(),
];
$mediaEntries = $record->getMedia('assignments')->map(function ($item, $index) {
return PdfViewerEntry::make('pdf_'.$index)
->label('Lampiran PDF')
->fileUrl($item->getUrl())
->columnSpanFull();
})->toArray();
return array_merge($entries, $mediaEntries);
});
}
}

View File

@ -21,12 +21,20 @@ protected function setUp(): void
->color('info')
->icon('heroicon-o-clipboard-document-list')
->link()
->modalHeading(fn (array $arguments) => 'Tugas Kuliah - Sesi Ke-'.ClassSession::find($arguments['session'], ['*'])->session_number)
->modalHeading(fn (array $arguments) => 'Tugas Kuliah - Sesi Ke-'.(ClassSession::find($arguments['session'] ?? null, ['*'])?->session_number ?? ''))
->modalSubmitAction(false)
->modalCancelActionLabel('Tutup')
->modalWidth(Width::Large)
->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.assignments-modal', [
'assignments' => ClassSession::find($arguments['session'], ['*'])->assignments()->latest()->get(),
'assignments' => ClassSession::find($arguments['session'] ?? null, ['*'])?->assignments()
->latest()
->get()
->map(fn ($a) => (object) [
'id' => $a->id,
'title' => $a->title,
'type_label' => $a->type?->getLabel() ?? 'Tugas',
'due_date_formatted' => $a->due_date?->translatedFormat('d F Y, H:i') ?? '-',
]) ?? collect(),
]));
}
}

View File

@ -21,12 +21,12 @@ protected function setUp(): void
->color('success')
->icon('heroicon-o-check-circle')
->link()
->modalHeading(fn (array $arguments) => 'Daftar Presensi - Sesi Ke-'.ClassSession::find($arguments['session'], ['*'])->session_number)
->modalHeading(fn (array $arguments) => 'Daftar Presensi - Sesi Ke-'.ClassSession::find($arguments['session'] ?? null, ['*'])?->session_number)
->modalSubmitAction(false)
->modalCancelActionLabel('Tutup')
->modalWidth(Width::ExtraLarge)
->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.attendance-modal', [
'attendances' => ClassSession::find($arguments['session'], ['*'])->attendances()->with('student')->latest('attended_at')->get(),
'attendances' => ClassSession::find($arguments['session'] ?? null, ['*'])?->attendances()->with('student')->latest('attended_at')->get() ?? collect(),
]));
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Filament\Resources\Learning\ClassSessions\Actions;
use App\Models\Material;
use Filament\Actions\Action;
use Filament\Support\Enums\Width;
class ViewMaterialDetailAction extends Action
{
public static function getDefaultName(): ?string
{
return 'viewMaterialDetail';
}
protected function setUp(): void
{
parent::setUp();
$this->record(fn (array $arguments) => Material::find($arguments['record'] ?? null, ['*']))
->modalHeading(fn (?Material $record) => $record?->title ?? 'Detail Materi')
->modalSubmitAction(false)
->modalCancelActionLabel('Tutup')
->modalWidth(Width::FourExtraLarge)
->modalContent(fn (?Material $record) => $record ? view('filament.resources.learning.class-sessions.material-detail-modal', [
'record' => $record,
]) : null);
}
}

View File

@ -21,12 +21,19 @@ protected function setUp(): void
->color('warning')
->icon('heroicon-o-book-open')
->link()
->modalHeading(fn (array $arguments) => 'Materi Kuliah - Sesi Ke-'.ClassSession::find($arguments['session'], ['*'])->session_number)
->modalHeading(fn (array $arguments) => 'Materi Kuliah - Sesi Ke-'.(ClassSession::find($arguments['session'] ?? null, ['*'])?->session_number ?? ''))
->modalSubmitAction(false)
->modalCancelActionLabel('Tutup')
->modalWidth(Width::Large)
->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.materials-modal', [
'materials' => ClassSession::find($arguments['session'], ['*'])->materials()->latest()->get(),
'materials' => ClassSession::find($arguments['session'] ?? null, ['*'])?->materials()
->latest()
->get()
->map(fn ($m) => (object) [
'id' => $m->id,
'title' => $m->title,
'created_at_formatted' => $m->created_at?->translatedFormat('d F Y') ?? '-',
]) ?? collect(),
]));
}
}

View File

@ -7,8 +7,10 @@
use App\Filament\Resources\Learning\ClassSessions\Actions\DeleteSessionAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\EditSessionAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\GenerateSessionsAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentDetailAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentsAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAttendanceAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewMaterialDetailAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewMaterialsAction;
use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource;
use App\Models\Course;
@ -151,6 +153,19 @@ protected function getHeaderActions(): array
];
}
protected function getActions(): array
{
return [
$this->viewAttendanceAction(),
$this->viewMaterialsAction(),
$this->viewAssignmentsAction(),
$this->editSessionAction(),
$this->deleteSessionAction(),
$this->viewMaterialDetailAction(),
$this->viewAssignmentDetailAction(),
];
}
public function viewAttendanceAction(): Action
{
return ViewAttendanceAction::make();
@ -175,4 +190,14 @@ public function deleteSessionAction(): Action
{
return DeleteSessionAction::make();
}
public function viewMaterialDetailAction(): Action
{
return ViewMaterialDetailAction::make();
}
public function viewAssignmentDetailAction(): Action
{
return ViewAssignmentDetailAction::make();
}
}

View File

@ -2,8 +2,10 @@
namespace App\Filament\Resources\Learning\ClassSessions\Pages;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentDetailAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentsAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAttendanceAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewMaterialDetailAction;
use App\Filament\Resources\Learning\ClassSessions\Actions\ViewMaterialsAction;
use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource;
use App\Models\ClassSession;
@ -134,6 +136,17 @@ public function courses(): Collection
});
}
protected function getActions(): array
{
return [
$this->viewAttendanceAction(),
$this->viewMaterialsAction(),
$this->viewAssignmentsAction(),
$this->viewMaterialDetailAction(),
$this->viewAssignmentDetailAction(),
];
}
public function viewAttendanceAction(): Action
{
return ViewAttendanceAction::make();
@ -148,4 +161,14 @@ public function viewAssignmentsAction(): Action
{
return ViewAssignmentsAction::make();
}
public function viewMaterialDetailAction(): Action
{
return ViewMaterialDetailAction::make();
}
public function viewAssignmentDetailAction(): Action
{
return ViewAssignmentDetailAction::make();
}
}

View File

@ -133,7 +133,8 @@ public static function table(Table $table): Table
TextColumn::make('course.name')
->label('Mata Kuliah')
->searchable()
->sortable(),
->sortable()
->description(fn (Material $record) => $record->classSession?->session_number ? "Sesi Ke-{$record->classSession->session_number}" : ''),
TextColumn::make('title')
->label('Judul')
@ -145,13 +146,6 @@ public static function table(Table $table): Table
->date('l, d F Y')
->sortable(),
TextColumn::make('classSession.session_number')
->label('Pertemuan')
->formatStateUsing(fn ($state) => $state ? "Sesi Ke-$state" : '-')
->sortable()
->badge()
->color('gray'),
...TimestampColumns::make(),
])
->filters([

View File

@ -7,19 +7,20 @@
</div>
<div class="flex flex-col">
<span class="font-bold text-gray-900 dark:text-white leading-tight uppercase font-mono text-[11px] mb-0.5 tracking-tight text-primary-600/60 dark:text-primary-400/50 block">
{{ $assignment->type->label() ?? 'TUGAS' }}
{{ $assignment->type_label }}
</span>
<span class="font-bold text-gray-900 dark:text-white leading-tight">{{ $assignment->title }}</span>
<div class="flex items-center gap-1.5 mt-0.5">
<x-heroicon-o-clock class="w-3.5 h-3.5 text-gray-400 dark:text-gray-500" />
<span class="text-[10px] text-gray-500 dark:text-gray-400 font-medium uppercase tracking-tight">
Deadline: {{ $assignment->due_date?->translatedFormat('d F Y, H:i') ?? '-' }}
Deadline: {{ $assignment->due_date_formatted }}
</span>
</div>
</div>
</div>
<div class="flex items-center gap-2">
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('index') . '?tableFilters[course_id][value]=' . $assignment->course_id . '&tableSearch=' . urlencode($assignment->title) }}"
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $assignment->id]) }}"
wire:navigate
class="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-white/10 text-gray-500 transition-colors"
title="Lihat Detail">
<x-heroicon-o-chevron-right class="w-5 h-5" />

View File

@ -0,0 +1,12 @@
<div class="space-y-4 py-2">
@foreach($record->getMedia('materials') as $item)
<div class="rounded-xl border border-gray-100 dark:border-white/5 overflow-hidden shadow-sm">
<div class="p-3 bg-gray-50/50 dark:bg-white/5 border-b border-gray-100 dark:border-white/5">
<span class="text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-tight">Lampiran PDF</span>
</div>
<div class="h-[600px] w-full bg-white dark:bg-gray-900 overflow-hidden">
<iframe src="{{ $item->getUrl() }}" class="w-full h-full border-0" frameborder="0"></iframe>
</div>
</div>
@endforeach
</div>

View File

@ -8,16 +8,17 @@
<div class="flex flex-col">
<span class="font-bold text-gray-900 dark:text-white leading-tight">{{ $material->title }}</span>
<span class="text-[10px] text-gray-500 dark:text-gray-400 font-medium uppercase mt-0.5">
Dibuat: {{ $material->created_at?->translatedFormat('d F Y') ?? '-' }}
Dibuat: {{ $material->created_at_formatted }}
</span>
</div>
</div>
<div class="flex items-center gap-2">
<a href="{{ \App\Filament\Resources\Learning\Materials\MaterialResource::getUrl('index') . '?tableFilters[course_id][value]=' . $material->course_id . '&tableSearch=' . urlencode($material->title) }}"
<button type="button"
wire:click="mountAction('viewMaterialDetail', { record: {{ $material->id }} })"
class="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-white/10 text-gray-500 transition-colors"
title="Lihat Detail">
<x-heroicon-o-chevron-right class="w-5 h-5" />
</a>
</button>
</div>
</div>
@empty

View File

@ -50,7 +50,7 @@ class="flex items-center gap-1.5 font-bold text-success-600 dark:text-success-40
</div>
</div>
<div class="flex items-center gap-1">
<div class="flex items-center gap-3">
{{ ($this->viewAttendanceAction)(['session' => $session->id]) }}
{{ ($this->viewMaterialsAction)(['session' => $session->id]) }}
{{ ($this->viewAssignmentsAction)(['session' => $session->id]) }}