feat: add course filtering to assignments list and enhance search functionality
This commit is contained in:
parent
a4f615ad20
commit
b097168c09
@ -12,8 +12,10 @@
|
|||||||
use App\Filament\Support\SystemNotification;
|
use App\Filament\Support\SystemNotification;
|
||||||
use App\Models\Assignment;
|
use App\Models\Assignment;
|
||||||
use App\Models\AssignmentPin;
|
use App\Models\AssignmentPin;
|
||||||
|
use App\Models\Course;
|
||||||
use App\Settings\GeneralSettings;
|
use App\Settings\GeneralSettings;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Resources\Pages\Page;
|
use Filament\Resources\Pages\Page;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
@ -27,6 +29,9 @@ class ListAssignments extends Page
|
|||||||
#[Url]
|
#[Url]
|
||||||
public ?string $search = '';
|
public ?string $search = '';
|
||||||
|
|
||||||
|
#[Url]
|
||||||
|
public ?int $course_id = null;
|
||||||
|
|
||||||
protected static string $resource = AssignmentResource::class;
|
protected static string $resource = AssignmentResource::class;
|
||||||
|
|
||||||
protected string $view = 'filament.resources.learning.assignments.index';
|
protected string $view = 'filament.resources.learning.assignments.index';
|
||||||
@ -42,7 +47,10 @@ public static function getPagePermission(): string
|
|||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
$this->form->fill();
|
$this->form->fill([
|
||||||
|
'search' => $this->search,
|
||||||
|
'course_id' => $this->course_id,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function form(Schema $schema): Schema
|
public function form(Schema $schema): Schema
|
||||||
@ -51,13 +59,27 @@ public function form(Schema $schema): Schema
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('search')
|
TextInput::make('search')
|
||||||
->label('Cari')
|
->label('Cari')
|
||||||
->placeholder('Cari Judul Tugas atau Mata Kuliah...')
|
->placeholder('Cari Judul Tugas...')
|
||||||
->autocomplete(false)
|
->autocomplete(false)
|
||||||
->live(debounce: 500)
|
->live(debounce: 500)
|
||||||
->afterStateUpdated(fn ($state) => $this->search = $state),
|
->afterStateUpdated(fn ($state) => $this->search = $state),
|
||||||
|
|
||||||
|
Select::make('course_id')
|
||||||
|
->label('Mata Kuliah')
|
||||||
|
->placeholder('Semua Mata Kuliah')
|
||||||
|
->options(function () {
|
||||||
|
return Course::query()
|
||||||
|
->where('semester', app(GeneralSettings::class)->current_semester)
|
||||||
|
->whereHas('courseSchedules')
|
||||||
|
->pluck('name', 'id')
|
||||||
|
->toArray();
|
||||||
|
})
|
||||||
|
->searchable()
|
||||||
|
->live()
|
||||||
|
->afterStateUpdated(fn ($state) => $this->course_id = $state),
|
||||||
])
|
])
|
||||||
->columns([
|
->columns([
|
||||||
'sm' => 1,
|
'sm' => 2,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +134,15 @@ public function assignments(): Collection
|
|||||||
->whereHas('course', function ($q) {
|
->whereHas('course', function ($q) {
|
||||||
$q->where('semester', app(GeneralSettings::class)->current_semester);
|
$q->where('semester', app(GeneralSettings::class)->current_semester);
|
||||||
})
|
})
|
||||||
|
->when($this->search, function ($query) {
|
||||||
|
$query->where(function ($q) {
|
||||||
|
$q->where('title', 'like', "%{$this->search}%")
|
||||||
|
->orWhereHas('course', fn ($cq) => $cq->where('name', 'like', "%{$this->search}%"));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->when($this->course_id, function ($query) {
|
||||||
|
$query->where('course_id', $this->course_id);
|
||||||
|
})
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +165,9 @@ public function assignments(): Collection
|
|||||||
->orWhereHas('course', fn ($cq) => $cq->where('name', 'like', "%{$this->search}%"));
|
->orWhereHas('course', fn ($cq) => $cq->where('name', 'like', "%{$this->search}%"));
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
->when($this->course_id, function ($query) {
|
||||||
|
$query->where('course_id', $this->course_id);
|
||||||
|
})
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
return $assignments->sort(function ($a, $b) use ($pinnedIds) {
|
return $assignments->sort(function ($a, $b) use ($pinnedIds) {
|
||||||
|
|||||||
@ -1,20 +1,18 @@
|
|||||||
<x-filament-panels::page>
|
<x-filament-panels::page>
|
||||||
<x-filament::section
|
<x-filament::section
|
||||||
icon="{{ \App\Filament\Support\SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful ? 'heroicon-o-clipboard-document-list' : 'heroicon-o-briefcase' }}"
|
icon="{{ \App\Filament\Support\SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful ? 'heroicon-o-clipboard-document-list' : 'heroicon-o-briefcase' }}"
|
||||||
icon-color="primary"
|
icon-color="primary">
|
||||||
>
|
|
||||||
<x-slot name="heading">{{ $this->heading }}</x-slot>
|
<x-slot name="heading">{{ $this->heading }}</x-slot>
|
||||||
<x-slot name="description">{{ $this->description }}</x-slot>
|
<x-slot name="description">{{ $this->description }}</x-slot>
|
||||||
|
|
||||||
<div class="flex items-center justify-between gap-4 mb-6">
|
<div class="flex items-center justify-between gap-4 mb-6">
|
||||||
<div class="w-full max-w-xl">
|
<div class="w-full">
|
||||||
{{ $this->form }}
|
{{ $this->form }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
@forelse ($this->assignmentCards as $card)
|
@forelse ($this->assignmentCards as $card)
|
||||||
|
|
||||||
<div class="{{ $card->card_classes }} flex-col">
|
<div class="{{ $card->card_classes }} flex-col">
|
||||||
|
|
||||||
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $card->id]) }}"
|
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $card->id]) }}"
|
||||||
@ -73,13 +71,14 @@ class="inline-flex items-center gap-1.5 rounded-full bg-danger-500 text-white px
|
|||||||
|
|
||||||
<div class="flex flex-wrap gap-x-4 gap-y-2 mt-4 text-xs text-gray-500 dark:text-gray-400">
|
<div class="flex flex-wrap gap-x-4 gap-y-2 mt-4 text-xs text-gray-500 dark:text-gray-400">
|
||||||
<span class="flex items-center gap-1.5">
|
<span class="flex items-center gap-1.5">
|
||||||
<x-filament::icon icon="heroicon-o-clock" class="h-4 w-4 shrink-0 text-primary-500" />
|
<x-filament::icon icon="heroicon-o-clock"
|
||||||
|
class="h-4 w-4 shrink-0 text-primary-500" />
|
||||||
<span class="font-medium">Batas: {{ $card->due_date_formatted }}</span>
|
<span class="font-medium">Batas: {{ $card->due_date_formatted }}</span>
|
||||||
</span>
|
</span>
|
||||||
@if ($card->submitted_at_formatted)
|
@if ($card->submitted_at_formatted)
|
||||||
<span class="flex items-center gap-1.5 text-success-600 dark:text-success-400 font-bold bg-success-50 dark:bg-success-900/25 px-2 py-0.5 rounded-lg">
|
<span
|
||||||
<x-filament::icon icon="heroicon-o-arrow-up-tray"
|
class="flex items-center gap-1.5 text-success-600 dark:text-success-400 font-bold bg-success-50 dark:bg-success-900/25 px-2 py-0.5 rounded-lg">
|
||||||
class="h-4 w-4 shrink-0" />
|
<x-filament::icon icon="heroicon-o-arrow-up-tray" class="h-4 w-4 shrink-0" />
|
||||||
<span>Dikumpulkan: {{ $card->submitted_at_formatted }}</span>
|
<span>Dikumpulkan: {{ $card->submitted_at_formatted }}</span>
|
||||||
</span>
|
</span>
|
||||||
@endif
|
@endif
|
||||||
@ -88,7 +87,8 @@ class="h-4 w-4 shrink-0" />
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-3 px-5 py-4 border-t border-gray-100 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-800/20">
|
<div
|
||||||
|
class="flex flex-wrap items-center gap-3 px-5 py-4 border-t border-gray-100 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-800/20">
|
||||||
{{ ($this->pinAction)(['record' => $card->id]) }}
|
{{ ($this->pinAction)(['record' => $card->id]) }}
|
||||||
{{ ($this->shareAssignmentAction)(['record' => $card->id]) }}
|
{{ ($this->shareAssignmentAction)(['record' => $card->id]) }}
|
||||||
|
|
||||||
@ -101,9 +101,9 @@ class="h-4 w-4 shrink-0" />
|
|||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@empty
|
@empty
|
||||||
<div class="col-span-full">
|
<div class="col-span-full">
|
||||||
<x-filament::empty-state icon="heroicon-o-clipboard-document-list"
|
<x-filament::empty-state icon="heroicon-o-clipboard-document-list"
|
||||||
heading="{{ \App\Filament\Support\SystemNotification::getMessage('Hore, Belum Ada Tugas! 🎊✨', 'Tidak ada data yang ditemukan') }}"
|
heading="{{ \App\Filament\Support\SystemNotification::getMessage('Hore, Belum Ada Tugas! 🎊✨', 'Tidak ada data yang ditemukan') }}"
|
||||||
description="{{ \App\Filament\Support\SystemNotification::getMessage('Belum ada tugas baru yang perlu dikerjakan. Hidup tenang tanpa beban tugas itu asik ya! 😊🌈', 'Belum ada tugas baru yang perlu dikerjakan untuk saat ini. Tetap semangat!') }}"
|
description="{{ \App\Filament\Support\SystemNotification::getMessage('Belum ada tugas baru yang perlu dikerjakan. Hidup tenang tanpa beban tugas itu asik ya! 😊🌈', 'Belum ada tugas baru yang perlu dikerjakan untuk saat ini. Tetap semangat!') }}"
|
||||||
iconColor="gray">
|
iconColor="gray">
|
||||||
|
|||||||
@ -298,6 +298,30 @@
|
|||||||
->assertDontSee('Tugas Wajib')
|
->assertDontSee('Tugas Wajib')
|
||||||
->assertSee('Hore, Belum Ada Tugas!');
|
->assertSee('Hore, Belum Ada Tugas!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can filter assignments by course', function () {
|
||||||
|
$course1 = Course::factory()->create(['name' => 'Matematika', 'semester' => $this->currentSemester]);
|
||||||
|
$course2 = Course::factory()->create(['name' => 'Fisika', 'semester' => $this->currentSemester]);
|
||||||
|
|
||||||
|
$assignment1 = Assignment::factory()->create([
|
||||||
|
'title' => 'Tugas Mat',
|
||||||
|
'course_id' => $course1->id,
|
||||||
|
'due_date' => now()->addDays(1),
|
||||||
|
]);
|
||||||
|
$assignment2 = Assignment::factory()->create([
|
||||||
|
'title' => 'Tugas Fis',
|
||||||
|
'course_id' => $course2->id,
|
||||||
|
'due_date' => now()->addDays(1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$assignment1->students()->attach($this->studentProfile->id);
|
||||||
|
$assignment2->students()->attach($this->studentProfile->id);
|
||||||
|
|
||||||
|
Livewire::test(ListAssignments::class)
|
||||||
|
->set('course_id', $course1->id)
|
||||||
|
->assertSee('Tugas Mat')
|
||||||
|
->assertDontSee('Tugas Fis');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Assignment Sharing', function () {
|
describe('Assignment Sharing', function () {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user