feat: enhance assignment submission process with improved notifications, form handling, and UI updates
This commit is contained in:
parent
506bfd822f
commit
ba0cadf4f9
@ -34,7 +34,9 @@ protected function setUp(): void
|
||||
'assignment_id' => $assignment->id,
|
||||
]);
|
||||
|
||||
$livewire->js("if (navigator.clipboard) { navigator.clipboard.writeText('{$url}').catch(() => {}); }");
|
||||
$escapedUrl = json_encode($url);
|
||||
|
||||
$livewire->js("if (navigator.clipboard) { navigator.clipboard.writeText({$escapedUrl}).catch(() => {}); }");
|
||||
|
||||
SystemNotification::send('link_copied')
|
||||
->send();
|
||||
|
||||
@ -28,8 +28,9 @@ protected function setUp(): void
|
||||
->modalDescription('Pilih metode untuk membagikan tautan publik status pengumpulan tugas ini.')
|
||||
->modalSubmitActionLabel('Ke WhatsApp')
|
||||
->modalCancelAction(false)
|
||||
->extraModalFooterActions([
|
||||
CopyAssignmentLinkAction::make(),
|
||||
->extraModalFooterActions(fn (Action $action): array => [
|
||||
CopyAssignmentLinkAction::make()
|
||||
->arguments($action->getArguments()),
|
||||
])
|
||||
->action(function (array $arguments, $livewire) {
|
||||
$assignment = Assignment::with(['course'])->find($arguments['record'] ?? null);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Resources\Learning\Assignments\Pages;
|
||||
|
||||
use App\Enums\AssignmentType;
|
||||
use App\Enums\NotifStyle;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\CreateAssignmentAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\DeleteAssignmentAction;
|
||||
use App\Filament\Resources\Learning\Assignments\Actions\EditAssignmentAction;
|
||||
@ -100,6 +101,26 @@ public function description(): string
|
||||
return SystemNotification::getByKey('labels.assignment_list.description');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function emptyStateHeading(): string
|
||||
{
|
||||
return SystemNotification::getByKey('labels.empty_assignment.title');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function emptyStateDescription(): string
|
||||
{
|
||||
return SystemNotification::getByKey('labels.empty_assignment.description');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function icon(): string
|
||||
{
|
||||
return SystemNotification::getNotifStyle() === NotifStyle::Cheerful
|
||||
? 'heroicon-o-clipboard-document-list'
|
||||
: 'heroicon-o-briefcase';
|
||||
}
|
||||
|
||||
public function pinAction(): Action
|
||||
{
|
||||
return PinAction::make();
|
||||
@ -245,20 +266,20 @@ public function assignmentCards()
|
||||
$isNew = $assignment->created_at->diffInDays(now()) <= 3;
|
||||
$isPinned = in_array($assignment->id, $pinnedIds);
|
||||
|
||||
$statusLabel = 'Belum Dikumpulkan';
|
||||
$statusLabel = SystemNotification::getByKey('labels.assignment_status.not_submitted');
|
||||
$statusColor = 'warning';
|
||||
$statusIcon = 'heroicon-o-arrow-up-tray';
|
||||
|
||||
if ($isSubmitted) {
|
||||
$statusLabel = '✓ Sudah Dikumpulkan';
|
||||
$statusLabel = SystemNotification::getByKey('labels.assignment_status.submitted');
|
||||
$statusColor = 'success';
|
||||
$statusIcon = 'heroicon-o-check-circle';
|
||||
} elseif ($isOverdue) {
|
||||
$statusLabel = '⏰ Waktu Habis';
|
||||
$statusLabel = SystemNotification::getByKey('labels.assignment_status.overdue');
|
||||
$statusColor = 'danger';
|
||||
$statusIcon = 'heroicon-o-clock';
|
||||
} elseif ($isGroup && ! $isLeader && ! $isSubmitted) {
|
||||
$statusLabel = 'Menunggu Ketua';
|
||||
$statusLabel = SystemNotification::getByKey('labels.assignment_status.waiting_leader');
|
||||
$statusColor = 'gray';
|
||||
$statusIcon = 'heroicon-o-user-group';
|
||||
}
|
||||
@ -279,6 +300,7 @@ public function assignmentCards()
|
||||
'is_pinned' => $isPinned,
|
||||
'status_label' => $statusLabel,
|
||||
'status_icon' => $statusIcon,
|
||||
'url' => AssignmentResource::getUrl('submit', ['record' => $assignment->id]),
|
||||
// Pre-calculated classes
|
||||
'card_classes' => Arr::toCssClasses([
|
||||
'group flex w-full rounded-xl border transition-all overflow-hidden relative',
|
||||
|
||||
@ -3,23 +3,28 @@
|
||||
namespace App\Filament\Resources\Learning\Assignments\Pages;
|
||||
|
||||
use App\Enums\AssignmentType;
|
||||
use App\Enums\NotifStyle;
|
||||
use App\Filament\Actions\BackAction;
|
||||
use App\Filament\Resources\Learning\Assignments\AssignmentResource;
|
||||
use App\Filament\Resources\Learning\Assignments\Schemas\SubmitAssignmentForm;
|
||||
use App\Filament\Support\SystemNotification;
|
||||
use App\Models\Assignment;
|
||||
use App\Models\AssignmentSubmission;
|
||||
use App\Models\Student;
|
||||
use App\Models\StudyGroup;
|
||||
use App\Models\User;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\WithFileUploads;
|
||||
|
||||
class SubmitAssignmentPage extends Page
|
||||
class SubmitAssignmentPage extends Page implements HasForms
|
||||
{
|
||||
use WithFileUploads;
|
||||
use InteractsWithForms, WithFileUploads;
|
||||
|
||||
protected static string $resource = AssignmentResource::class;
|
||||
|
||||
@ -29,7 +34,24 @@ class SubmitAssignmentPage extends Page
|
||||
|
||||
public Assignment $record;
|
||||
|
||||
public $file = null;
|
||||
public array $data = [];
|
||||
|
||||
public $file;
|
||||
|
||||
public function mount(Assignment $record): void
|
||||
{
|
||||
$this->record = $record;
|
||||
|
||||
$this->form->fill([
|
||||
'is_resubmit' => $this->isResubmit,
|
||||
]);
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return SubmitAssignmentForm::configure($schema)
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function statusCards(): array
|
||||
@ -135,17 +157,75 @@ public function submissionStatus(): object
|
||||
return (object) [
|
||||
'type' => 'submitted',
|
||||
'badge_color' => 'success',
|
||||
'badge_label' => 'Sudah Dikumpulkan',
|
||||
'badge_label' => SystemNotification::getByKey('assignment_status.submitted'),
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'type' => 'none',
|
||||
'badge_color' => 'danger',
|
||||
'badge_label' => 'Tidak Mengumpulkan',
|
||||
'badge_label' => SystemNotification::getByKey('assignment_status.not_submitted'),
|
||||
];
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function overdueHeading(): string
|
||||
{
|
||||
return SystemNotification::getByKey('submission_overdue.title');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function overdueDescription(): string
|
||||
{
|
||||
return SystemNotification::getByKey('submission_overdue.body');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function submissionHeading(): string
|
||||
{
|
||||
return $this->isResubmit
|
||||
? SystemNotification::getByKey('submission_update.title')
|
||||
: SystemNotification::getByKey('submission_create.title');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function submissionDescription(): string
|
||||
{
|
||||
return $this->isResubmit
|
||||
? SystemNotification::getByKey('submission_update.body')
|
||||
: SystemNotification::getByKey('submission_create.body');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function isCheerful(): bool
|
||||
{
|
||||
return SystemNotification::getNotifStyle() === NotifStyle::Cheerful;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function groupHintTitle(): string
|
||||
{
|
||||
return SystemNotification::getByKey('labels.group_submission_hint.title');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function groupHintDescription(): string
|
||||
{
|
||||
return SystemNotification::getByKey('labels.group_submission_hint.description');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function overdueIcon(): string
|
||||
{
|
||||
return $this->isCheerful ? 'heroicon-o-exclamation-triangle' : 'heroicon-o-lock-closed';
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function submissionIcon(): string
|
||||
{
|
||||
return $this->isCheerful ? 'heroicon-o-paper-airplane' : 'heroicon-o-document-arrow-up';
|
||||
}
|
||||
|
||||
public function getSubmissionFileUrl(): ?string
|
||||
{
|
||||
return $this->existingSubmission?->getFirstMediaUrl('submission');
|
||||
@ -171,32 +251,19 @@ public function submit(): void
|
||||
}
|
||||
|
||||
$existingSubmission = $this->existingSubmission;
|
||||
$state = $this->form->getState();
|
||||
|
||||
if (! $existingSubmission && ! $this->file) {
|
||||
if (! $existingSubmission && empty($state['file'])) {
|
||||
SystemNotification::send('submission_file_missing', type: 'warning')
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'file' => [
|
||||
'nullable',
|
||||
'file',
|
||||
'max:'.(1024 * 5),
|
||||
'mimes:pdf',
|
||||
],
|
||||
], [
|
||||
'file.max' => 'Ukuran file maksimal 5MB.',
|
||||
'file.mimes' => 'Hanya file PDF yang diizinkan.',
|
||||
]);
|
||||
|
||||
if ($existingSubmission) {
|
||||
if ($this->file) {
|
||||
if ($state['file']) {
|
||||
$existingSubmission->clearMediaCollection('submission');
|
||||
$existingSubmission->addMedia($this->file->getRealPath())
|
||||
->usingName($this->file->getClientOriginalName())
|
||||
->usingFileName($this->file->getClientOriginalName())
|
||||
$existingSubmission->addMediaFromDisk($state['file'], config('filesystems.default'))
|
||||
->toMediaCollection('submission');
|
||||
}
|
||||
|
||||
@ -214,16 +281,18 @@ public function submit(): void
|
||||
'submitted_at' => now(),
|
||||
]);
|
||||
|
||||
$submission->addMedia($this->file->getRealPath())
|
||||
->usingName($this->file->getClientOriginalName())
|
||||
->usingFileName($this->file->getClientOriginalName())
|
||||
->toMediaCollection('submission');
|
||||
if ($state['file']) {
|
||||
$submission->addMediaFromDisk($state['file'], config('filesystems.default'))
|
||||
->toMediaCollection('submission');
|
||||
}
|
||||
|
||||
SystemNotification::send('submission_success')
|
||||
->send();
|
||||
}
|
||||
|
||||
$this->file = null;
|
||||
$this->form->fill([
|
||||
'is_resubmit' => $this->isResubmit,
|
||||
]);
|
||||
unset($this->existingSubmission, $this->isResubmit);
|
||||
|
||||
$this->dispatch('submission-completed');
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Learning\Assignments\Schemas;
|
||||
|
||||
use Asmit\FilamentUpload\Enums\PdfViewFit;
|
||||
use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class SubmitAssignmentForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
AdvancedFileUpload::make('file')
|
||||
->label(fn ($get) => $get('is_resubmit') ? 'Ganti Berkas PDF (Opsional)' : 'Lampiran Berkas Tugas')
|
||||
->pdfPreviewHeight(400)
|
||||
->pdfDisplayPage(1)
|
||||
->pdfToolbar(true)
|
||||
->pdfZoomLevel(100)
|
||||
->pdfFitType(PdfViewFit::FIT)
|
||||
->pdfNavPanes(true)
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['application/pdf'])
|
||||
->maxSize(1024 * 5)
|
||||
->directory('submissions/'.now()->toDateString())
|
||||
->required(fn ($get) => ! $get('is_resubmit'))
|
||||
->hiddenLabel()
|
||||
->helperText('Hanya file PDF dengan ukuran maksimal 5MB.')
|
||||
->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -121,6 +121,18 @@
|
||||
'title' => 'Yeay! Tugas Terkumpul! 🎉',
|
||||
'body' => 'Berhasil! Tugas Anda telah tercatat dengan aman di sistem. Semangat! 🎈',
|
||||
],
|
||||
'submission_overdue' => [
|
||||
'title' => 'Yah, Batas Waktu Udah Habis! 😭⏳',
|
||||
'body' => 'Maaf ya, kamu udah nggak bisa kumpulin tugas ini lagi karena waktunya udah lewat. Tetap semangat buat tugas selanjutnya! 💪',
|
||||
],
|
||||
'submission_update' => [
|
||||
'title' => 'Kuy Perbarui Tugasmu! 🔄✨',
|
||||
'body' => 'Ada yang mau dirubah? Tenang, kamu masih bisa update file tugasmu selama belum lewat deadline kok! 🚀✨',
|
||||
],
|
||||
'submission_create' => [
|
||||
'title' => 'Kumpulkan Tugas Sekarang! 🚀📚',
|
||||
'body' => 'Ayo, jangan sampai telat ya! Segera upload file tugas terbaikmu biar tenang dan nilai maksimal! 💪🎨',
|
||||
],
|
||||
'user_activated' => [
|
||||
'title' => 'Pengguna Diaktifkan! ✅✨',
|
||||
'body' => 'Status akun pengguna berhasil diaktifkan kembali. Siap beraksi! 🚀',
|
||||
@ -193,6 +205,10 @@
|
||||
'title' => 'Daftar Tugas Bikin Lemes! 🤣📒',
|
||||
'description' => 'Yuk, cek dan kumpulin tugasmu biar tenang hidupnya! Klik aja di tugasnya ya. 🚀👨💻',
|
||||
],
|
||||
'empty_assignment' => [
|
||||
'title' => 'Hore, Belum Ada Tugas! 🎊✨',
|
||||
'description' => 'Belum ada tugas baru yang perlu dikerjakan. Hidup tenang tanpa beban tugas itu asik ya! 😊🌈',
|
||||
],
|
||||
'changelog' => [
|
||||
'title' => 'Catatan Rilis 📜✨',
|
||||
'description' => 'Kepoin apa aja yang baru, fitur kece, dan perbaikan bug biar nggak kudet! 🚀🔥',
|
||||
@ -221,6 +237,16 @@
|
||||
'title' => 'Belum Ada Mata Kuliah Nih! 📚😴',
|
||||
'description' => 'Semester ini kayanya masih kosong. Coba hubungi admin kalau ada yang salah ya! ✨',
|
||||
],
|
||||
'assignment_status' => [
|
||||
'not_submitted' => 'Belum Dikumpulkan',
|
||||
'submitted' => '✓ Sudah Dikumpulkan',
|
||||
'overdue' => '⏰ Waktu Habis',
|
||||
'waiting_leader' => 'Menunggu Ketua',
|
||||
],
|
||||
'group_submission_hint' => [
|
||||
'title' => 'Tugas Kelompok',
|
||||
'description' => 'Hanya <strong>Ketua Kelompok</strong> yang dapat mengumpulkan atau memperbarui tugas ini. Silakan hubungi ketua kelompok Anda.',
|
||||
],
|
||||
],
|
||||
|
||||
// Icons
|
||||
@ -359,6 +385,18 @@
|
||||
'title' => 'Konfirmasi Pengumpulan Berhasil',
|
||||
'body' => 'Seluruh berkas tugas Anda telah berhasil diverifikasi dan disimpan oleh sistem.',
|
||||
],
|
||||
'submission_overdue' => [
|
||||
'title' => 'Batas Waktu Telah Berakhir',
|
||||
'body' => 'Masa pengumpulan tugas ini telah berakhir. Sistem tidak lagi menerima unggahan baru maupun pembaruan untuk penugasan ini.',
|
||||
],
|
||||
'submission_update' => [
|
||||
'title' => 'Perbarui Pengumpulan',
|
||||
'body' => 'Anda diperbolehkan untuk mengunggah ulang atau memperbarui berkas tugas selama masa pengumpulan masih aktif.',
|
||||
],
|
||||
'submission_create' => [
|
||||
'title' => 'Kumpulkan Tugas',
|
||||
'body' => 'Gunakan formulir ini untuk mengunggah berkas tugas Anda sesuai dengan ketentuan yang berlaku.',
|
||||
],
|
||||
'user_activated' => [
|
||||
'title' => 'Aktivasi Akun Berhasil',
|
||||
'body' => 'Status akun pengguna terpilih telah diubah menjadi aktif.',
|
||||
@ -431,6 +469,10 @@
|
||||
'title' => 'Tugas Saya',
|
||||
'description' => 'Klik pada tugas untuk melihat detail dan mengumpulkan file.',
|
||||
],
|
||||
'empty_assignment' => [
|
||||
'title' => 'Tidak Ada Tugas Terdaftar',
|
||||
'description' => 'Belum terdapat tugas baru yang perlu Anda kerjakan untuk saat ini.',
|
||||
],
|
||||
'changelog' => [
|
||||
'title' => 'Catatan Rilis',
|
||||
'description' => 'Lihat riwayat perbaikan bug, pembaruan aplikasi, dan rilis fitur terbaru secara mendalam.',
|
||||
@ -459,6 +501,16 @@
|
||||
'title' => 'Mata Kuliah Belum Terdaftar',
|
||||
'description' => 'Belum ada mata kuliah yang terdaftar untuk semester aktif ini.',
|
||||
],
|
||||
'assignment_status' => [
|
||||
'not_submitted' => 'Belum Dikumpulkan',
|
||||
'submitted' => 'Sudah Dikumpulkan',
|
||||
'overdue' => 'Waktu Habis',
|
||||
'waiting_leader' => 'Menunggu Ketua',
|
||||
],
|
||||
'group_submission_hint' => [
|
||||
'title' => 'Tugas Kelompok',
|
||||
'description' => 'Hanya <strong>Ketua Kelompok</strong> yang memiliki otoritas untuk memperbarui atau mengumpulkan tugas kelompok.',
|
||||
],
|
||||
],
|
||||
|
||||
// Icons
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<x-filament-panels::page>
|
||||
<x-filament::section
|
||||
icon="{{ \App\Filament\Support\SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful ? 'heroicon-o-clipboard-document-list' : 'heroicon-o-briefcase' }}"
|
||||
icon="{{ $this->icon }}"
|
||||
icon-color="primary">
|
||||
<x-slot name="heading">{{ $this->heading }}</x-slot>
|
||||
<x-slot name="description">{{ $this->description }}</x-slot>
|
||||
@ -15,10 +15,9 @@
|
||||
@forelse ($this->assignmentCards as $card)
|
||||
<div class="{{ $card->card_classes }} flex-col">
|
||||
|
||||
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $card->id]) }}"
|
||||
<a href="{{ $card->url }}"
|
||||
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 }} h-12 w-12 mt-1 shrink-0">
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-6 w-6" />
|
||||
</div>
|
||||
@ -32,7 +31,6 @@ class="flex flex-1 items-start gap-4 p-5 text-left min-w-0 hover:bg-primary-500/
|
||||
{{ $card->course_name }}
|
||||
</p>
|
||||
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 mt-3">
|
||||
@if ($card->is_pinned)
|
||||
<span
|
||||
@ -68,7 +66,6 @@ class="inline-flex items-center gap-1.5 rounded-full bg-danger-500 text-white px
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
<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"
|
||||
@ -86,7 +83,6 @@ class="flex items-center gap-1.5 text-success-600 dark:text-success-400 font-bol
|
||||
</div>
|
||||
</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">
|
||||
{{ ($this->pinAction)(['record' => $card->id]) }}
|
||||
@ -98,14 +94,12 @@ class="flex flex-wrap items-center gap-3 px-5 py-4 border-t border-gray-100 dark
|
||||
@endcanAny
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
@empty
|
||||
<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!') }}"
|
||||
heading="{{ $this->emptyStateHeading }}"
|
||||
description="{{ $this->emptyStateDescription }}"
|
||||
iconColor="gray">
|
||||
</x-filament::empty-state>
|
||||
</div>
|
||||
|
||||
@ -32,17 +32,12 @@ class="prose prose-sm dark:prose-invert max-w-none text-gray-600 dark:text-gray-
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($this->record->hasMedia('assignments'))
|
||||
@php
|
||||
$attachmentUrl = $this->record->getFirstMediaUrl('assignments');
|
||||
$googleViewerUrl =
|
||||
'https://docs.google.com/viewer?url=' . urlencode($attachmentUrl) . '&embedded=true';
|
||||
@endphp
|
||||
@if ($attachmentUrl = $this->record->getFirstMediaUrl('assignments'))
|
||||
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
|
||||
<p class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">Lampiran Tugas</p>
|
||||
<div
|
||||
class="rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
|
||||
<iframe src="{{ $this->record->getFirstMediaUrl('assignments') }}" width="100%" height="500"
|
||||
<iframe src="{{ $attachmentUrl }}" width="100%" height="500"
|
||||
class="block border-0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,13 +45,9 @@ class="block border-0"></iframe>
|
||||
</x-filament::section>
|
||||
|
||||
@if ($this->isOverdue)
|
||||
<x-filament::section
|
||||
icon="{{ \App\Filament\Support\SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful ? 'heroicon-o-exclamation-triangle' : 'heroicon-o-lock-closed' }}"
|
||||
icon-color="danger">
|
||||
<x-slot
|
||||
name="heading">{{ \App\Filament\Support\SystemNotification::getMessage('Yah, Batas Waktu Udah Habis! 😭⏳', 'Batas Waktu Telah Berakhir') }}</x-slot>
|
||||
<x-slot
|
||||
name="description">{{ \App\Filament\Support\SystemNotification::getMessage('Maaf ya, kamu udah nggak bisa kumpulin tugas ini lagi karena waktunya udah lewat. Tetap semangat buat tugas selanjutnya! 💪', 'Proses pengumpulan tugas ini telah ditutup karena melewati batas waktu.') }}</x-slot>
|
||||
<x-filament::section :icon="$this->overdueIcon" icon-color="danger">
|
||||
<x-slot name="heading">{{ $this->overdueHeading }}</x-slot>
|
||||
<x-slot name="description">{{ $this->overdueDescription }}</x-slot>
|
||||
|
||||
<div class="flex items-center gap-3 mb-5">
|
||||
<div
|
||||
@ -86,10 +77,9 @@ class="block border-0"></iframe>
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
<x-filament::icon icon="heroicon-o-user-group" class="w-12 h-12 mb-3" />
|
||||
<p class="font-semibold text-gray-700 dark:text-gray-300">Tugas Kelompok</p>
|
||||
<p class="font-semibold text-gray-700 dark:text-gray-300">{{ $this->groupHintTitle }}</p>
|
||||
<p class="text-sm mt-1 max-w-sm">
|
||||
Hanya <strong>Ketua Kelompok</strong> yang dapat mengumpulkan atau memperbarui tugas ini.
|
||||
Silakan hubungi ketua kelompok Anda.
|
||||
{!! $this->groupHintDescription !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -106,11 +96,9 @@ class="block border-0"></iframe>
|
||||
@endif
|
||||
</x-filament::section>
|
||||
@else
|
||||
<x-filament::section
|
||||
icon="{{ \App\Filament\Support\SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful ? 'heroicon-o-paper-airplane' : 'heroicon-o-document-arrow-up' }}"
|
||||
icon-color="primary">
|
||||
<x-slot
|
||||
name="heading">{{ $this->isResubmit ? \App\Filament\Support\SystemNotification::getMessage('Kuy Perbarui Tugasmu! 🔄✨', 'Perbarui Pengumpulan') : \App\Filament\Support\SystemNotification::getMessage('Kumpulkan Tugas Sekarang! 🚀📚', 'Kumpulkan Tugas') }}</x-slot>
|
||||
<x-filament::section :icon="$this->submissionIcon" icon-color="primary">
|
||||
<x-slot name="heading">{{ $this->submissionHeading }}</x-slot>
|
||||
<x-slot name="description">{{ $this->submissionDescription }}</x-slot>
|
||||
|
||||
@if ($this->isResubmit && ($url = $this->getSubmissionFileUrl()))
|
||||
<div class="mb-4">
|
||||
@ -124,87 +112,18 @@ class="block border-0"></iframe>
|
||||
@endif
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
|
||||
{{ $this->isResubmit ? 'Ganti File PDF (opsional)' : 'File Tugas' }}
|
||||
@if (!$this->isResubmit)
|
||||
<span class="text-danger-500">*</span>
|
||||
@endif
|
||||
</label>
|
||||
<input wire:model="file" type="file" id="assignment-file" accept=".pdf"
|
||||
class="block w-full text-sm text-gray-700 dark:text-gray-300
|
||||
file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0
|
||||
file:text-sm file:font-medium file:cursor-pointer
|
||||
file:bg-primary-50 file:text-primary-700
|
||||
dark:file:bg-primary-900/20 dark:file:text-primary-400
|
||||
hover:file:bg-primary-100 dark:hover:file:bg-primary-900/30
|
||||
border border-gray-300 dark:border-gray-600 rounded-lg
|
||||
bg-white dark:bg-gray-800 p-2 transition-colors" />
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Hanya file PDF. Maksimal 5MB.
|
||||
</p>
|
||||
@error('file')
|
||||
<p class="mt-1 text-xs text-danger-600 dark:text-danger-400">
|
||||
{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div id="pdf-submission-preview" class="hidden" wire:ignore>
|
||||
<p class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">Preview</p>
|
||||
<div
|
||||
class="rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
|
||||
<iframe id="pdf-submission-frame" width="100%" height="400"
|
||||
class="block border-0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-5 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<x-filament::button wire:click="submit" wire:loading.attr="disabled" color="primary">
|
||||
<span wire:loading.remove wire:target="submit">
|
||||
{{ $this->isResubmit ? 'Perbarui Pengumpulan' : 'Kumpulkan Sekarang' }}
|
||||
</span>
|
||||
<span wire:loading wire:target="submit">Mengupload...</span>
|
||||
</x-filament::button>
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</x-filament::section>
|
||||
|
||||
<div class="flex justify-start">
|
||||
<x-filament::button wire:click="submit" wire:loading.attr="disabled" color="primary">
|
||||
<span wire:loading.remove wire:target="submit">
|
||||
{{ $this->isResubmit ? 'Perbarui' : 'Kumpulkan' }}
|
||||
</span>
|
||||
<span wire:loading wire:target="submit">Mengupload...</span>
|
||||
</x-filament::button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Function to reset preview
|
||||
function resetPreview() {
|
||||
var preview = document.getElementById('pdf-submission-preview');
|
||||
var frame = document.getElementById('pdf-submission-frame');
|
||||
if (preview) preview.classList.add('hidden');
|
||||
if (frame) frame.src = '';
|
||||
}
|
||||
|
||||
// Delegation for change event
|
||||
document.addEventListener('change', function(e) {
|
||||
if (e.target && e.target.id === 'assignment-file') {
|
||||
var file = e.target.files[0];
|
||||
var preview = document.getElementById('pdf-submission-preview');
|
||||
var frame = document.getElementById('pdf-submission-frame');
|
||||
|
||||
if (file && preview && frame) {
|
||||
frame.src = URL.createObjectURL(file);
|
||||
preview.classList.remove('hidden');
|
||||
} else {
|
||||
resetPreview();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for the Livewire event after submission
|
||||
window.addEventListener('submission-completed', function() {
|
||||
var input = document.getElementById('assignment-file');
|
||||
if (input) input.value = '';
|
||||
resetPreview();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@ -142,9 +142,11 @@
|
||||
$this->actingAs($user);
|
||||
Livewire::test(SubmitAssignmentPage::class, ['record' => $assignment])
|
||||
->assertSuccessful()
|
||||
->set('file', $file)
|
||||
->fillForm([
|
||||
'file' => $file,
|
||||
])
|
||||
->call('submit')
|
||||
->assertHasNoErrors();
|
||||
->assertHasNoFormErrors();
|
||||
|
||||
$this->assertDatabaseHas('assignment_submissions', [
|
||||
'assignment_id' => $assignment->id,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user