feat: display student assignment submissions in the session modal and add submission factory

This commit is contained in:
Yoga Pangestu 2026-04-01 13:43:22 +07:00
parent 86bc954edb
commit e994adf5dc
4 changed files with 126 additions and 25 deletions

View File

@ -212,13 +212,20 @@ public function viewAssignmentsAction(): Action
->modalWidth(Width::TwoExtraLarge) ->modalWidth(Width::TwoExtraLarge)
->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.assignments-modal', [ ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.assignments-modal', [
'assignments' => ClassSession::find($arguments['session'] ?? null, ['*'])?->assignments() 'assignments' => ClassSession::find($arguments['session'] ?? null, ['*'])?->assignments()
->with(['assignmentSubmissions.student'])
->latest() ->latest()
->get() ->get()
->map(fn ($a) => (object) [ ->map(fn ($a) => (object) [
'id' => $a->id, 'id' => $a->id,
'title' => $a->title, 'title' => $a->title,
'type_label' => $a->type?->getLabel() ?? 'Tugas', 'type_label' => $a->type?->getLabel() ?? 'Tugas',
'due_date_formatted' => $a->due_date?->translatedFormat('d F Y, H:i') ?? '-', 'due_date_formatted' => $a->due_date?->translatedFormat('d F Y H:i') ?? '-',
'submissions' => $a->assignmentSubmissions->map(fn ($s) => (object) [
'student_name' => $s->student->full_name,
'student_number' => $s->student->student_number,
'submitted_at_formatted' => $s->submitted_at?->translatedFormat('d F Y H:i') ?? '-',
'id' => $s->id,
]),
]) ?? collect(), ]) ?? collect(),
])); ]));
} }

View File

@ -0,0 +1,28 @@
<?php
namespace Database\Factories;
use App\Models\Assignment;
use App\Models\AssignmentSubmission;
use App\Models\Student;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<AssignmentSubmission>
*/
class AssignmentSubmissionFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'assignment_id' => Assignment::factory(),
'student_id' => Student::factory(),
'submitted_at' => now(),
];
}
}

View File

@ -1,32 +1,72 @@
<div class="space-y-4 py-2"> <div class="space-y-6 py-2">
@forelse($assignments as $assignment) @forelse($assignments as $assignment)
<div <div class="space-y-3">
class="flex items-center justify-between p-3 rounded-lg border border-gray-100 dark:border-white/5 bg-gray-50/50 dark:bg-white/5 hover:bg-white dark:hover:bg-white/10 transition-colors shadow-sm"> <div
<div class="flex items-center gap-3"> class="flex items-center justify-between p-3 rounded-lg border border-gray-100 dark:border-white/5 bg-gray-50/50 dark:bg-white/5 shadow-sm">
<div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400"> <div class="flex items-center gap-3">
<x-heroicon-o-pencil-square class="w-5 h-5" /> <div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400">
</div> <x-heroicon-o-pencil-square class="w-5 h-5" />
<div class="flex flex-col"> </div>
<span <div class="flex flex-col">
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"> <span
{{ $assignment->type_label }} 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">
</span> {{ $assignment->type_label }}
<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_formatted }}
</span> </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_formatted }}
</span>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="flex items-center gap-2">
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $assignment->id]) }}" @if ($assignment->submissions->count() > 0)
class="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-white/10 text-gray-500 transition-colors" <div
title="Lihat Detail"> class="fi-ta-content overflow-x-auto overflow-y-auto max-h-60 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 ring-1 ring-gray-950/5 dark:ring-white/10">
<x-heroicon-o-chevron-right class="w-5 h-5" /> <table class="w-full text-sm text-left divide-y divide-gray-200 dark:divide-white/5">
</a> <thead class="bg-gray-50 dark:bg-gray-800 sticky top-0 z-10">
</div> <tr>
<th class="px-4 py-2.5 font-bold text-gray-900 dark:text-white">Mahasiswa</th>
<th class="px-4 py-2.5 font-bold text-gray-900 dark:text-white text-right">Waktu Unggah</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-white/5">
@foreach ($assignment->submissions as $submission)
<tr class="hover:bg-gray-50 dark:hover:bg-white/5 transition-colors">
<td class="px-4 py-2.5 text-gray-950 dark:text-white font-medium">
<div class="flex flex-col">
<span class="text-sm">{{ $submission->student_name }}</span>
<span class="text-[10px] text-gray-500 dark:text-gray-400 font-normal uppercase tabular-nums">
NIM: {{ $submission->student_number }}
</span>
</div>
</td>
<td class="px-4 py-2.5 text-right">
<span
class="inline-flex items-center rounded-md bg-success-50 dark:bg-success-400/10 px-2 py-0.5 text-xs font-bold text-success-700 dark:text-success-400 ring-1 ring-inset ring-success-600/20 tabular-nums">
{{ $submission->submitted_at_formatted }}
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="px-2">
<p class="text-[10px] text-gray-500 font-medium uppercase tracking-wider">
Total Pengumpulan: <span class="text-primary-600 dark:text-primary-400 font-bold font-mono">{{ $assignment->submissions->count() }}
Mahasiswa</span>
</p>
</div>
@else
<div class="flex flex-col items-center justify-center p-6 border border-dashed rounded-xl border-gray-200 dark:border-white/10 bg-gray-50/50 dark:bg-white/5">
<x-heroicon-o-document-minus class="w-8 h-8 text-gray-400 dark:text-gray-600 mb-2" />
<p class="text-xs text-gray-500 dark:text-gray-400 font-medium">Belum ada pengumpulan untuk tugas ini.</p>
</div>
@endif
</div> </div>
@empty @empty
<x-filament::empty-state icon="heroicon-o-clipboard-document" heading="Tidak ada data yang ditemukan" <x-filament::empty-state icon="heroicon-o-clipboard-document" heading="Tidak ada data yang ditemukan"
@ -34,3 +74,4 @@ class="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-white/10 text-gray-500
</x-filament::empty-state> </x-filament::empty-state>
@endforelse @endforelse
</div> </div>

View File

@ -3,9 +3,12 @@
use App\Enums\RoleEnum; use App\Enums\RoleEnum;
use App\Filament\Resources\Learning\ClassSessions\Pages\ListCourseSessions; use App\Filament\Resources\Learning\ClassSessions\Pages\ListCourseSessions;
use App\Filament\Resources\Learning\ClassSessions\Pages\ManageClassSessions; use App\Filament\Resources\Learning\ClassSessions\Pages\ManageClassSessions;
use App\Models\Assignment;
use App\Models\AssignmentSubmission;
use App\Models\ClassSession; use App\Models\ClassSession;
use App\Models\Course; use App\Models\Course;
use App\Models\CourseSchedule; use App\Models\CourseSchedule;
use App\Models\Student;
use App\Models\User; use App\Models\User;
use App\Settings\GeneralSettings; use App\Settings\GeneralSettings;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -173,6 +176,28 @@
->assertActionVisible('viewMaterials') ->assertActionVisible('viewMaterials')
->assertActionVisible('viewAssignments'); ->assertActionVisible('viewAssignments');
}); });
it('shows student submissions in the assignment modal', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
$session = ClassSession::factory()->create(['course_id' => $course->id]);
$assignment = Assignment::factory()->create([
'title' => 'Test Assignment Label',
'class_session_id' => $session->id,
'course_id' => $course->id,
]);
$student = Student::factory()->create(['full_name' => 'John Doe', 'student_number' => '1234567890']);
AssignmentSubmission::factory()->create([
'assignment_id' => $assignment->id,
'student_id' => $student->id,
'submitted_at' => now(),
]);
Livewire::test(ListCourseSessions::class, ['courseId' => $course->id])
->mountAction('viewAssignments', ['session' => $session->id])
->assertActionMounted('viewAssignments')
->assertSuccessful();
});
}); });
describe('Search and Dashboard Filter', function () { describe('Search and Dashboard Filter', function () {