feat: Implement dedicated Filament actions for deleting, editing, and pinning assignments, and integrate them into the assignment listing page.
This commit is contained in:
parent
ebe3f6d739
commit
d76fa6ac49
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\Assignments\Actions;
|
||||
|
||||
use App\Filament\Actions\Cheerful\DeleteAction;
|
||||
use App\Models\Assignment;
|
||||
|
||||
class DeleteAssignmentAction extends DeleteAction
|
||||
{
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'deleteAssignment';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this
|
||||
->label('Hapus')
|
||||
->modalHeading(fn (Assignment $record) => "Hapus {$record->title}")
|
||||
->record(fn (array $arguments) => Assignment::find($arguments['record']))
|
||||
->after(function ($livewire) {
|
||||
unset($livewire->assignments);
|
||||
})
|
||||
->icon('heroicon-m-trash')
|
||||
->color('danger')
|
||||
->tooltip('Hapus')
|
||||
->size('sm')
|
||||
->iconButton();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\Assignments\Actions;
|
||||
|
||||
use App\Enums\AssignmentType;
|
||||
use App\Filament\Actions\Cheerful\EditAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Schemas\AssignmentForm;
|
||||
use App\Models\Assignment;
|
||||
use App\Models\AssignmentTarget;
|
||||
use App\Models\StudyGroup;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EditAssignmentAction extends EditAction
|
||||
{
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'editAssignmentAction';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->icon('heroicon-m-pencil-square')
|
||||
->color('warning')
|
||||
->tooltip('Ubah')
|
||||
->size('sm')
|
||||
->iconButton()
|
||||
->record(fn (array $arguments) => Assignment::find($arguments['record']))
|
||||
->modalHeading(fn (Assignment $record) => "Ubah {$record->title}")
|
||||
->modalWidth(Width::FourExtraLarge)
|
||||
->schema(fn (Schema $schema) => AssignmentForm::configure($schema)->getComponents())
|
||||
->fillForm(function (Assignment $record): array {
|
||||
$data = $record->toArray();
|
||||
|
||||
$data['student_ids'] = $record->assignmentTargets()
|
||||
->whereNotNull('student_id')
|
||||
->pluck('student_id')
|
||||
->toArray();
|
||||
|
||||
$data['study_group_ids'] = $record->assignmentTargets()
|
||||
->whereNotNull('study_group_id')
|
||||
->pluck('study_group_id')
|
||||
->toArray();
|
||||
|
||||
$media = $record->getMedia('assignments')->last();
|
||||
$relativePath = $media ? $media->getPathRelativeToRoot() : null;
|
||||
$data['pdf'] = $relativePath ? [$relativePath] : [];
|
||||
|
||||
return $data;
|
||||
})
|
||||
->using(function (Assignment $record, array $data): Assignment {
|
||||
return DB::transaction(function () use ($record, $data) {
|
||||
$currentMedia = $record->getMedia('assignments')->last();
|
||||
$currentPath = $currentMedia ? $currentMedia->getPathRelativeToRoot() : null;
|
||||
|
||||
$studentIds = $data['student_ids'] ?? [];
|
||||
$pdf = $data['pdf'] ?? null;
|
||||
|
||||
unset($data['student_ids'], $data['study_group_ids'], $data['pdf']);
|
||||
|
||||
$record->update($data);
|
||||
|
||||
$record->assignmentTargets()->delete();
|
||||
|
||||
if ($record->type === AssignmentType::Individual) {
|
||||
foreach ($studentIds as $studentId) {
|
||||
AssignmentTarget::create([
|
||||
'assignment_id' => $record->id,
|
||||
'student_id' => $studentId,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$studyGroupIds = StudyGroup::whereHas('courses', function ($q) use ($record) {
|
||||
$q->where('courses.id', $record->course_id);
|
||||
})->pluck('id');
|
||||
|
||||
foreach ($studyGroupIds as $groupId) {
|
||||
AssignmentTarget::create([
|
||||
'assignment_id' => $record->id,
|
||||
'study_group_id' => $groupId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$filePath = is_array($pdf) ? reset($pdf) : $pdf;
|
||||
|
||||
if ($filePath !== $currentPath) {
|
||||
$record->clearMediaCollection('assignments');
|
||||
if (! empty($filePath)) {
|
||||
$record->addMediaFromDisk($filePath, config('filesystems.default'))
|
||||
->preservingOriginal()
|
||||
->withCustomProperties([
|
||||
'feature' => 'assignments',
|
||||
'date' => now()->toDateString(),
|
||||
'doc_type' => 'tasks',
|
||||
])
|
||||
->toMediaCollection('assignments');
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
});
|
||||
})
|
||||
->after(function () {
|
||||
unset($this->assignments);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\Assignments\Actions;
|
||||
|
||||
use App\Filament\Support\SystemNotification;
|
||||
use App\Models\AssignmentPin;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
class PinAction extends Action
|
||||
{
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'pin';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->icon(fn (array $arguments, $livewire) => in_array($arguments['record'], $livewire->pinnedIds) ? 'heroicon-s-bookmark' : 'heroicon-o-bookmark')
|
||||
->color(fn (array $arguments, $livewire) => in_array($arguments['record'], $livewire->pinnedIds) ? 'primary' : 'gray')
|
||||
->tooltip(fn (array $arguments, $livewire) => in_array($arguments['record'], $livewire->pinnedIds) ? 'Lepas pin' : 'Pin tugas ini')
|
||||
->size('sm')
|
||||
->iconButton()
|
||||
->action(function (array $arguments, $livewire) {
|
||||
$assignmentId = $arguments['record'];
|
||||
$studentProfile = auth()->user()->student;
|
||||
|
||||
$existing = AssignmentPin::where('student_id', $studentProfile->id)
|
||||
->where('assignment_id', $assignmentId)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
$existing->delete();
|
||||
SystemNotification::success('Pin Tugas Dilepas 📌', 'Pin pada tugas ini telah berhasil dilepas dari daftar prioritas Anda.')->send();
|
||||
} else {
|
||||
AssignmentPin::create([
|
||||
'student_id' => $studentProfile->id,
|
||||
'assignment_id' => $assignmentId,
|
||||
]);
|
||||
SystemNotification::success('Tugas Berhasil Di-pin 📍', 'Tugas ini sekarang berada di posisi teratas daftar prioritas Anda.')->send();
|
||||
}
|
||||
|
||||
unset($livewire->assignments, $livewire->pinnedIds);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -4,36 +4,53 @@
|
||||
|
||||
use App\Enums\AssignmentType;
|
||||
use App\Filament\Actions\Cheerful\CreateAction;
|
||||
use App\Filament\Actions\Cheerful\DeleteAction;
|
||||
use App\Filament\Actions\Cheerful\EditAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\DeleteAssignmentAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\EditAssignmentAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\PinAction;
|
||||
use App\Filament\Resources\Learning\Assignments\AssignmentResource;
|
||||
use App\Filament\Resources\Learning\Assignments\Schemas\AssignmentForm;
|
||||
use App\Filament\Support\SystemNotification;
|
||||
use App\Models\Assignment;
|
||||
use App\Models\AssignmentPin;
|
||||
use App\Models\AssignmentTarget;
|
||||
use App\Models\StudyGroup;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Attributes\Computed;
|
||||
|
||||
class ListAssignments extends Page implements HasTable
|
||||
class ListAssignments extends Page
|
||||
{
|
||||
use InteractsWithTable;
|
||||
|
||||
protected static string $resource = AssignmentResource::class;
|
||||
|
||||
protected string $view = 'filament.resources.learning.assignments.pages.list-assignments';
|
||||
|
||||
protected static ?string $title = 'Tugas';
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'title';
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
->label('Tambah'),
|
||||
];
|
||||
}
|
||||
|
||||
public function pinAction(): Action
|
||||
{
|
||||
return PinAction::make();
|
||||
}
|
||||
|
||||
public function editAssignmentAction(): Action
|
||||
{
|
||||
return EditAssignmentAction::make()
|
||||
->visible(fn () => auth()->user()->can('Update:Assignment'));
|
||||
}
|
||||
|
||||
public function deleteAssignmentAction(): Action
|
||||
{
|
||||
return DeleteAssignmentAction::make()
|
||||
->visible(fn () => auth()->user()->can('Delete:Assignment'));
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function assignments(): Collection
|
||||
{
|
||||
@ -69,7 +86,7 @@ public function assignmentCards()
|
||||
return $this->assignments()->map(function ($assignment) use ($studentProfile, $pinnedIds) {
|
||||
$submission = $assignment->assignmentSubmissions->first();
|
||||
$isSubmitted = $submission !== null;
|
||||
$isGroup = $assignment->type === \App\Enums\AssignmentType::Group;
|
||||
$isGroup = $assignment->type === AssignmentType::Group;
|
||||
|
||||
$isLeader = false;
|
||||
if ($isGroup) {
|
||||
@ -137,127 +154,4 @@ public function pinnedIds(): array
|
||||
->pluck('assignment_id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function togglePin(int $assignmentId): void
|
||||
{
|
||||
$studentProfile = auth()->user()->student;
|
||||
|
||||
$existing = AssignmentPin::where('student_id', $studentProfile->id)
|
||||
->where('assignment_id', $assignmentId)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
$existing->delete();
|
||||
SystemNotification::success('Pin Tugas Dilepas 📌', 'Pin pada tugas ini telah berhasil dilepas dari daftar prioritas Anda.')->send();
|
||||
} else {
|
||||
AssignmentPin::create([
|
||||
'student_id' => $studentProfile->id,
|
||||
'assignment_id' => $assignmentId,
|
||||
]);
|
||||
SystemNotification::success('Tugas Berhasil Di-pin 📍', 'Tugas ini sekarang berada di posisi teratas daftar prioritas Anda.')->send();
|
||||
}
|
||||
|
||||
unset($this->assignments, $this->pinnedIds);
|
||||
}
|
||||
|
||||
public function editAssignmentAction(): Action
|
||||
{
|
||||
return EditAction::make('editAssignment')
|
||||
->record(fn (array $arguments) => Assignment::find($arguments['record']))
|
||||
->modalHeading(fn (Assignment $record) => "Ubah {$record->title}")
|
||||
->modalWidth(Width::FourExtraLarge)
|
||||
->schema(fn (Schema $schema) => AssignmentForm::configure($schema)->getComponents())
|
||||
->fillForm(function (Assignment $record): array {
|
||||
$data = $record->toArray();
|
||||
|
||||
$data['student_ids'] = $record->assignmentTargets()
|
||||
->whereNotNull('student_id')
|
||||
->pluck('student_id')
|
||||
->toArray();
|
||||
|
||||
$data['study_group_ids'] = $record->assignmentTargets()
|
||||
->whereNotNull('study_group_id')
|
||||
->pluck('study_group_id')
|
||||
->toArray();
|
||||
|
||||
$media = $record->getMedia('assignments')->last();
|
||||
$relativePath = $media ? $media->getPathRelativeToRoot() : null;
|
||||
$data['pdf'] = $relativePath ? [$relativePath] : [];
|
||||
|
||||
return $data;
|
||||
})
|
||||
->using(function (Assignment $record, array $data): Assignment {
|
||||
return DB::transaction(function () use ($record, $data) {
|
||||
$currentMedia = $record->getMedia('assignments')->last();
|
||||
$currentPath = $currentMedia ? $currentMedia->getPathRelativeToRoot() : null;
|
||||
|
||||
$studentIds = $data['student_ids'] ?? [];
|
||||
$pdf = $data['pdf'] ?? null;
|
||||
|
||||
unset($data['student_ids'], $data['study_group_ids'], $data['pdf']);
|
||||
|
||||
$record->update($data);
|
||||
|
||||
$record->assignmentTargets()->delete();
|
||||
|
||||
if ($record->type === AssignmentType::Individual) {
|
||||
foreach ($studentIds as $studentId) {
|
||||
AssignmentTarget::create([
|
||||
'assignment_id' => $record->id,
|
||||
'student_id' => $studentId,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$studyGroupIds = StudyGroup::whereHas('courses', function ($q) use ($record) {
|
||||
$q->where('courses.id', $record->course_id);
|
||||
})->pluck('id');
|
||||
|
||||
foreach ($studyGroupIds as $groupId) {
|
||||
AssignmentTarget::create([
|
||||
'assignment_id' => $record->id,
|
||||
'study_group_id' => $groupId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$filePath = is_array($pdf) ? reset($pdf) : $pdf;
|
||||
|
||||
if ($filePath !== $currentPath) {
|
||||
$record->clearMediaCollection('assignments');
|
||||
if (! empty($filePath)) {
|
||||
$record->addMediaFromDisk($filePath, config('filesystems.default'))
|
||||
->preservingOriginal()
|
||||
->withCustomProperties([
|
||||
'feature' => 'assignments',
|
||||
'date' => now()->toDateString(),
|
||||
'doc_type' => 'tasks',
|
||||
])
|
||||
->toMediaCollection('assignments');
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
});
|
||||
})
|
||||
->after(function () {
|
||||
unset($this->assignments);
|
||||
});
|
||||
}
|
||||
|
||||
public function deleteAssignmentAction(): Action
|
||||
{
|
||||
return DeleteAction::make('deleteAssignment')
|
||||
->record(fn (array $arguments) => Assignment::find($arguments['record']))
|
||||
->after(function () {
|
||||
unset($this->assignments);
|
||||
});
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
->label('Tambah'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,16 +119,13 @@ class="h-4 w-4 text-gray-400 group-hover:text-primary-500 mt-1 transition-colors
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center justify-center border-l border-gray-100 dark:border-gray-700 p-1 gap-3 p-3">
|
||||
<x-filament::icon-button wire:click="togglePin({{ $card->id }})" :icon="$card->is_pinned ? 'heroicon-s-bookmark' : 'heroicon-o-bookmark'"
|
||||
:color="$card->is_pinned ? 'primary' : 'gray'" :tooltip="$card->is_pinned ? 'Lepas pin' : 'Pin tugas ini'" size="sm" />
|
||||
{{ ($this->pinAction)(['record' => $card->id]) }}
|
||||
|
||||
<x-filament::icon-button
|
||||
wire:click="mountAction('editAssignment', { record: {{ $card->id }} })"
|
||||
icon="heroicon-m-pencil-square" color="warning" tooltip="Ubah" size="sm" />
|
||||
@canAny(['Update:Assignment', 'Delete:Assignment'])
|
||||
{{ ($this->editAssignmentAction)(['record' => $card->id]) }}
|
||||
|
||||
<x-filament::icon-button
|
||||
wire:click="mountAction('deleteAssignment', { record: {{ $card->id }} })"
|
||||
icon="heroicon-m-trash" color="danger" tooltip="Hapus" size="sm" />
|
||||
{{ ($this->deleteAssignmentAction)(['record' => $card->id]) }}
|
||||
@endcanAny
|
||||
</div>
|
||||
|
||||
@if ($card->is_group && !$card->is_submitted)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user