feat: add share and copy assignment link actions to enhance assignment sharing capabilities
This commit is contained in:
parent
93085269e9
commit
a4f615ad20
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\ClassSessions\Actions;
|
||||
namespace App\Filament\Resources\Learning\Assignments\Actions;
|
||||
|
||||
use App\Filament\Support\SystemNotification;
|
||||
use App\Models\ClassSession;
|
||||
use App\Models\Assignment;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
@ -22,12 +22,16 @@ protected function setUp(): void
|
||||
->color('gray')
|
||||
->icon('heroicon-o-clipboard-document')
|
||||
->action(function (array $arguments, $livewire) {
|
||||
$course = $livewire->course;
|
||||
$session = ClassSession::find($arguments['session'] ?? null);
|
||||
$assignment = Assignment::find($arguments['record'] ?? null);
|
||||
$course = $assignment ? $assignment->course : null;
|
||||
|
||||
if (! $course || ! $assignment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = URL::temporarySignedRoute('share.assignment', now()->addHour(), [
|
||||
'course' => $course->id,
|
||||
'session_id' => $session ? $session->id : null,
|
||||
'assignment_id' => $assignment->id,
|
||||
]);
|
||||
|
||||
$livewire->js("if (navigator.clipboard) { navigator.clipboard.writeText('{$url}').catch(() => {}); }");
|
||||
@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\ClassSessions\Actions;
|
||||
namespace App\Filament\Resources\Learning\Assignments\Actions;
|
||||
|
||||
use App\Models\ClassSession;
|
||||
use App\Models\Assignment;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
@ -25,22 +25,26 @@ protected function setUp(): void
|
||||
->requiresConfirmation()
|
||||
->modalIcon('heroicon-o-paper-airplane')
|
||||
->modalHeading('Bagikan Info Tugas')
|
||||
->modalDescription('Pilih metode untuk membagikan tautan publik status pengumpulan tugas sesi ini.')
|
||||
->modalDescription('Pilih metode untuk membagikan tautan publik status pengumpulan tugas ini.')
|
||||
->modalSubmitActionLabel('Ke WhatsApp')
|
||||
->modalCancelAction(false)
|
||||
->extraModalFooterActions([
|
||||
CopyAssignmentLinkAction::make(),
|
||||
])
|
||||
->action(function (array $arguments, $livewire) {
|
||||
$course = $livewire->course;
|
||||
$session = ClassSession::find($arguments['session'] ?? null);
|
||||
$assignment = Assignment::with(['course'])->find($arguments['record'] ?? null);
|
||||
$course = $assignment ? $assignment->course : null;
|
||||
|
||||
if (! $course || ! $assignment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = URL::temporarySignedRoute('share.assignment', now()->addHour(), [
|
||||
'course' => $course->id,
|
||||
'session_id' => $session ? $session->id : null,
|
||||
'assignment_id' => $assignment->id,
|
||||
]);
|
||||
|
||||
$text = "*Info Tugas Kelas {$course->name}*\nSesi ke-".($session->session_number ?? '-').' ('.($session->date->translatedFormat('d M Y') ?? '').")\n\nSilakan cek status pengumpulan tugas melalui tautan publik berikut:\n\n{$url}";
|
||||
$text = "*Info Tugas Kelas {$course->name}*\nJudul: {$assignment->title}\n\nSilakan cek status pengumpulan tugas melalui tautan publik berikut:\n\n{$url}";
|
||||
|
||||
$escapedText = json_encode($text);
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
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\Actions\ShareAssignmentAction;
|
||||
use App\Filament\Resources\Learning\Assignments\AssignmentResource;
|
||||
use App\Filament\Support\SystemNotification;
|
||||
use App\Models\Assignment;
|
||||
@ -96,6 +97,11 @@ public function deleteAssignmentAction(): Action
|
||||
->visible(fn () => auth()->user()->can('Delete:Assignment'));
|
||||
}
|
||||
|
||||
public function shareAssignmentAction(): Action
|
||||
{
|
||||
return ShareAssignmentAction::make();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function assignments(): Collection
|
||||
{
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
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\ShareAssignmentAction;
|
||||
use App\Filament\Resources\Learning\ClassSessions\Actions\ShareAttendanceAction;
|
||||
use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource;
|
||||
use App\Models\ClassSession;
|
||||
@ -140,7 +139,6 @@ protected function getActions(): array
|
||||
{
|
||||
return [
|
||||
$this->shareAttendanceAction(),
|
||||
$this->shareAssignmentAction(),
|
||||
$this->viewAttendanceAction(),
|
||||
$this->viewMaterialsAction(),
|
||||
$this->viewAssignmentsAction(),
|
||||
@ -155,11 +153,6 @@ public function shareAttendanceAction(): Action
|
||||
return ShareAttendanceAction::make();
|
||||
}
|
||||
|
||||
public function shareAssignmentAction(): Action
|
||||
{
|
||||
return ShareAssignmentAction::make();
|
||||
}
|
||||
|
||||
public function viewAttendanceAction(): Action
|
||||
{
|
||||
return Action::make('viewAttendance')
|
||||
|
||||
@ -12,74 +12,74 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
@forelse ($this->assignmentCards as $card)
|
||||
|
||||
<div class="{{ $card->card_classes }} flex-col sm:flex-row">
|
||||
<div class="{{ $card->card_classes }} flex-col">
|
||||
|
||||
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $card->id]) }}"
|
||||
class="flex flex-1 items-start gap-3 p-4 sm:p-5 text-left min-w-0 hover:bg-primary-500/5 transition-colors cursor-pointer">
|
||||
class="flex flex-1 items-start gap-4 p-5 text-left min-w-0 hover:bg-primary-500/5 transition-colors cursor-pointer">
|
||||
|
||||
|
||||
<div class="{{ $card->icon_wrapper_classes }} mt-0.5 shrink-0">
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-5 w-5 sm:h-6 sm:w-6" />
|
||||
<div class="{{ $card->icon_wrapper_classes }} h-12 w-12 mt-1 shrink-0">
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-6 w-6" />
|
||||
</div>
|
||||
|
||||
<div class="flex-1 min-w-0">
|
||||
|
||||
<p class="font-semibold text-gray-900 dark:text-white text-sm sm:text-base leading-snug">
|
||||
<p class="font-bold text-gray-900 dark:text-white text-lg leading-tight">
|
||||
{{ $card->title }}
|
||||
</p>
|
||||
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400 mt-0.5 truncate">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 mt-1 truncate">
|
||||
{{ $card->course_name }}
|
||||
</p>
|
||||
|
||||
|
||||
<div class="flex flex-wrap items-center gap-1.5 mt-2">
|
||||
<div class="flex flex-wrap items-center gap-2 mt-3">
|
||||
@if ($card->is_pinned)
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-primary-100 text-primary-700 dark:bg-primary-900/30 dark:text-primary-400 px-2 py-0.5 text-xs font-semibold whitespace-nowrap">
|
||||
class="inline-flex items-center gap-1.5 rounded-full bg-primary-100 text-primary-700 dark:bg-primary-900/30 dark:text-primary-400 px-2.5 py-1 text-xs font-bold whitespace-nowrap border border-primary-200/50 dark:border-primary-500/20">
|
||||
📌 Dipinned
|
||||
</span>
|
||||
@endif
|
||||
@if ($card->is_new)
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400 px-2 py-0.5 text-xs font-semibold whitespace-nowrap">
|
||||
class="inline-flex items-center gap-1.5 rounded-full bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400 px-2.5 py-1 text-xs font-bold whitespace-nowrap border border-violet-200/50 dark:border-violet-500/20">
|
||||
✨ Baru
|
||||
</span>
|
||||
@endif
|
||||
@if ($card->is_group)
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400 px-2 py-0.5 text-xs font-semibold whitespace-nowrap">
|
||||
class="inline-flex items-center gap-1.5 rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400 px-2.5 py-1 text-xs font-bold whitespace-nowrap border border-blue-200/50 dark:border-blue-500/20">
|
||||
👥 Kelompok
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<span class="{{ $card->status_badge_classes }} whitespace-nowrap">
|
||||
<span class="{{ $card->status_badge_classes }} px-2.5 py-1 whitespace-nowrap">
|
||||
@if ($card->status_icon)
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-3 w-3" />
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-4 w-4" />
|
||||
@endif
|
||||
{{ $card->status_label }}
|
||||
</span>
|
||||
|
||||
@if ($card->is_urgent)
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-danger-500 text-white px-2 py-0.5 text-xs font-semibold animate-pulse whitespace-nowrap">
|
||||
class="inline-flex items-center gap-1.5 rounded-full bg-danger-500 text-white px-2.5 py-1 text-xs font-bold animate-pulse whitespace-nowrap shadow-sm shadow-danger-300 dark:shadow-none">
|
||||
🔥 Segera!
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-wrap gap-x-3 gap-y-1 mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
<span class="flex items-center gap-1">
|
||||
<x-filament::icon icon="heroicon-o-clock" class="h-3.5 w-3.5 shrink-0" />
|
||||
<span>Batas: {{ $card->due_date_formatted }}</span>
|
||||
<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">
|
||||
<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>
|
||||
@if ($card->submitted_at_formatted)
|
||||
<span class="flex items-center gap-1 text-success-600 dark:text-success-400">
|
||||
<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">
|
||||
<x-filament::icon icon="heroicon-o-arrow-up-tray"
|
||||
class="h-3.5 w-3.5 shrink-0" />
|
||||
class="h-4 w-4 shrink-0" />
|
||||
<span>Dikumpulkan: {{ $card->submitted_at_formatted }}</span>
|
||||
</span>
|
||||
@endif
|
||||
@ -88,9 +88,9 @@ class="h-3.5 w-3.5 shrink-0" />
|
||||
</a>
|
||||
|
||||
|
||||
<div
|
||||
class="flex items-center justify-start gap-2 px-4 py-2 border-t border-gray-100 dark:border-gray-700 sm:flex-col sm:items-start sm:justify-center sm:border-t-0 sm:border-l sm:px-2 sm:py-3 sm:shrink-0">
|
||||
<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->shareAssignmentAction)(['record' => $card->id]) }}
|
||||
|
||||
@canAny(['Update:Assignment', 'Delete:Assignment'])
|
||||
{{ ($this->editAssignmentAction)(['record' => $card->id]) }}
|
||||
@ -102,11 +102,13 @@ class="flex items-center justify-start gap-2 px-4 py-2 border-t border-gray-100
|
||||
|
||||
</div>
|
||||
@empty
|
||||
<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') }}"
|
||||
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">
|
||||
</x-filament::empty-state>
|
||||
<div class="col-span-full">
|
||||
<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') }}"
|
||||
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">
|
||||
</x-filament::empty-state>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-filament::section>
|
||||
|
||||
@ -55,7 +55,6 @@
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3 px-4 py-3 border-t border-gray-100 dark:border-gray-700">
|
||||
{{ ($this->shareAttendanceAction)(['session' => $session->id]) }}
|
||||
{{ ($this->shareAssignmentAction)(['session' => $session->id]) }}
|
||||
{{ ($this->editSessionAction)(['session' => $session->id]) }}
|
||||
{{ ($this->deleteSessionAction)(['session' => $session->id]) }}
|
||||
</div>
|
||||
|
||||
@ -300,6 +300,27 @@
|
||||
});
|
||||
});
|
||||
|
||||
describe('Assignment Sharing', function () {
|
||||
beforeEach(function () {
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole(RoleEnum::Student);
|
||||
$user->givePermissionTo(['ViewAny:Assignment', 'View:CourseSchedule']);
|
||||
$this->studentProfile = Student::factory()->create(['user_id' => $user->id]);
|
||||
$this->actingAs($user);
|
||||
$this->currentSemester = app(GeneralSettings::class)->current_semester;
|
||||
});
|
||||
|
||||
it('can share assignment information', function () {
|
||||
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||
$assignment = Assignment::factory()->create(['course_id' => $course->id]);
|
||||
$assignment->students()->attach($this->studentProfile->id);
|
||||
|
||||
Livewire::test(ListAssignments::class)
|
||||
->callAction('shareAssignment', [], ['record' => $assignment->id])
|
||||
->assertHasNoActionErrors();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Assignment Details and Preview', function () {
|
||||
beforeEach(function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
@ -158,15 +158,6 @@
|
||||
->assertHasNoActionErrors();
|
||||
});
|
||||
|
||||
it('can share assignment information', function () {
|
||||
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||
$session = ClassSession::factory()->create(['course_id' => $course->id]);
|
||||
|
||||
Livewire::test(ListCourseSessions::class, ['courseId' => $course->id])
|
||||
->callAction('shareAssignment', [], ['session' => $session->id])
|
||||
->assertHasNoActionErrors();
|
||||
});
|
||||
|
||||
it('can show modals for attendance, materials and assignments', function () {
|
||||
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||
$session = ClassSession::factory()->create(['course_id' => $course->id]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user