feat: enhance assignment sharing view to support group assignments and update submission statistics display

This commit is contained in:
Yoga Pangestu 2026-03-30 17:04:12 +07:00
parent 9d05e4b594
commit b05e5212b3
3 changed files with 89 additions and 24 deletions

View File

@ -55,10 +55,9 @@ public function deleteAssignmentAction(): Action
public function assignments(): Collection
{
$studentProfile = auth()->user()->student;
$pinnedIds = $this->pinnedIds;
return Assignment::with(['course', 'assignmentSubmissions' => function ($q) use ($studentProfile) {
$assignments = Assignment::with(['course', 'assignmentSubmissions' => function ($q) use ($studentProfile) {
$q->where('student_id', $studentProfile->id)
->orWhereHas('studyGroup', fn ($sq) => $sq->where('leader_id', $studentProfile->id)->orWhereHas('students', fn ($ssq) => $ssq->whereKey($studentProfile->id)));
}, 'studyGroups.students'])
@ -69,12 +68,49 @@ public function assignments(): Collection
$query->whereHas('students', fn ($q) => $q->whereKey($studentProfile->id))
->orWhereHas('studyGroups', fn ($q) => $q->where('leader_id', $studentProfile->id)->orWhereHas('students', fn ($sq) => $sq->whereKey($studentProfile->id)));
})
->when(! empty($pinnedIds), function ($query) use ($pinnedIds) {
$ids = implode(',', $pinnedIds);
$query->orderByRaw("FIELD(id, $ids) DESC");
})
->orderBy('due_date')
->get();
return $assignments->sort(function ($a, $b) use ($pinnedIds) {
if (! empty($pinnedIds)) {
$aIndex = array_search($a->id, $pinnedIds);
$bIndex = array_search($b->id, $pinnedIds);
$aPinned = $aIndex !== false;
$bPinned = $bIndex !== false;
if ($aPinned && $bPinned) {
return $bIndex <=> $aIndex;
}
if ($aPinned) {
return -1;
}
if ($bPinned) {
return 1;
}
}
$aPriority = $this->getAssignmentPriority($a);
$bPriority = $this->getAssignmentPriority($b);
if ($aPriority !== $bPriority) {
return $aPriority <=> $bPriority;
}
return $a->due_date <=> $b->due_date;
});
}
private function getAssignmentPriority($assignment): int
{
$isSubmitted = $assignment->assignmentSubmissions->isNotEmpty();
$isOverdue = now()->isAfter($assignment->due_date);
if (! $isOverdue) {
return $isSubmitted ? 2 : 1;
}
// Overdue
return $isSubmitted ? 3 : 4;
}
#[Computed]

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Enums\AssignmentType;
use App\Models\Assignment;
use App\Models\AssignmentSubmission;
use App\Models\Course;
@ -15,13 +16,11 @@ class ShareAssignmentController extends Controller
public function show(Request $request, Course $course): View
{
// Retrieve available assignments for the dropdown (from the whole course)
$availableAssignments = Assignment::where('course_id', $course->id)
->with('classSession')
->orderBy('created_at', 'desc')
->get();
// Default: If session_id is provided, try to find an assignment for that session, or fallback to latest assignment.
$assignmentId = $request->query('assignment_id');
$sessionId = $request->query('session_id');
@ -36,17 +35,28 @@ public function show(Request $request, Course $course): View
$sessionInfo = $assignment ? $assignment->classSession : null;
// Get submissions and calculate stats
$submissions = collect();
$isGroup = false;
$totalTargets = 0;
if ($assignment) {
$submissions = AssignmentSubmission::with(['student', 'media'])
$isGroup = $assignment->type === AssignmentType::Group;
$submissions = AssignmentSubmission::with(['student', 'media', 'studyGroup.students'])
->where('assignment_id', $assignment->id)
->get();
if ($isGroup) {
$totalTargets = $assignment->studyGroups()->count();
} else {
$totalTargets = $assignment->students()->count();
if ($totalTargets === 0) {
$totalTargets = Student::whereHas('user', fn ($q) => $q->active())->count();
}
}
}
$totalStudents = Student::whereHas('user', fn ($q) => $q->active())->count();
$submittedCount = $submissions->count();
$submissionPercentage = $totalStudents > 0 ? round(($submittedCount / $totalStudents) * 100) : 0;
$submissionPercentage = $totalTargets > 0 ? round(($submittedCount / $totalTargets) * 100) : 0;
$formattedDeadline = $assignment && $assignment->due_date
? Carbon::parse($assignment->due_date)->translatedFormat('l, d F Y H:i')
@ -58,10 +68,11 @@ public function show(Request $request, Course $course): View
'availableAssignments',
'sessionInfo',
'submissions',
'totalStudents',
'totalTargets',
'submittedCount',
'submissionPercentage',
'formattedDeadline'
'formattedDeadline',
'isGroup'
));
}
}

View File

@ -73,8 +73,8 @@ class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white d
<thead
class="border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
<tr>
<th class="px-5 py-4 font-semibold text-gray-950 dark:text-white">Mahasiswa</th>
<th class="px-5 py-4 font-semibold text-gray-950 dark:text-white">Nomor Induk
<th class="px-5 py-4 font-semibold text-gray-950 dark:text-white">{{ $isGroup ? 'Kelompok' : 'Mahasiswa' }}</th>
<th class="px-5 py-4 font-semibold text-gray-950 dark:text-white">{{ $isGroup ? 'Anggota' : 'Nomor Induk' }}
</th>
<th class="px-5 py-4 font-semibold text-gray-950 dark:text-white">Hasil Tugas
</th>
@ -88,15 +88,29 @@ class="border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800
<td class="px-5 py-4">
<div class="flex items-center gap-3">
<div class="flex flex-col gap-0.5">
<span
class="font-bold text-gray-950 dark:text-white text-sm">{{ $submission->student->full_name }}</span>
@if($isGroup && $submission->studyGroup)
<span class="font-bold text-gray-950 dark:text-white text-sm">{{ $submission->studyGroup->name }}</span>
<span class="text-[10px] text-gray-500 dark:text-gray-400">Pengumpul: {{ $submission->student->full_name }}</span>
@else
<span class="font-bold text-gray-950 dark:text-white text-sm">{{ $submission->student->full_name }}</span>
@endif
</div>
</div>
</td>
<td class="px-5 py-4">
<span class="text-gray-500 dark:text-gray-400 font-medium text-sm">
{{ $submission->student->student_number }}
</span>
@if($isGroup && $submission->studyGroup)
<div class="flex flex-wrap gap-1 max-w-[200px]">
@foreach($submission->studyGroup->students as $member)
<span class="inline-flex items-center rounded-md bg-gray-50 dark:bg-gray-800/50 px-1.5 py-0.5 text-[10px] font-medium text-gray-600 dark:text-gray-400 ring-1 ring-inset ring-gray-500/10 whitespace-nowrap">
{{ $member->full_name }}
</span>
@endforeach
</div>
@else
<span class="text-gray-500 dark:text-gray-400 font-medium text-sm">
{{ $submission->student->student_number }}
</span>
@endif
</td>
<td class="px-5 py-4">
<div class="flex flex-col gap-2 max-w-sm">
@ -182,6 +196,10 @@ class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white d
Sesi Ke-{{ $assignment->classSession->session_number }}
</span>
@endif
<span class="inline-flex items-center rounded-md {{ $isGroup ? 'bg-indigo-50 dark:bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 ring-indigo-500/20' : 'bg-emerald-50 dark:bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 ring-emerald-500/20' }} px-2 py-0.5 mt-2 text-[10px] font-bold uppercase tracking-wider ring-1 ring-inset w-fit ml-1">
{{ $isGroup ? 'Tugas Kelompok' : 'Tugas Individu' }}
</span>
</div>
<div class="border-t border-gray-100 dark:border-gray-800"></div>
@ -192,8 +210,8 @@ class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white d
class="text-[10px] font-bold text-gray-400 dark:text-gray-500 uppercase tracking-widest mb-0.5">Terkumpul</span>
<span
class="text-sm font-bold text-gray-950 dark:text-white">{{ $submittedCount }}<span
class="text-[10px] text-gray-400 font-normal ml-0.5">/{{ $totalStudents }}</span>
Orang</span>
class="text-[10px] text-gray-400 font-normal ml-0.5">/{{ $totalTargets }}</span>
{{ $isGroup ? 'Kelompok' : 'Orang' }}</span>
<div class="mt-2 flex items-center gap-2">
<div