Some checks failed
tests / ci (pull_request) Has been cancelled
- Implemented CourseRegistrationShow component for admin to view registration details. - Added course registration creation and listing for students. - Enhanced student registration edit and create forms with improved error handling. - Introduced new columns for course registration submissions in student view. - Updated routes for course registration management in admin and student contexts. - Added logging and status handling for course registration submissions. - Improved type definitions for course registration and user roles.
181 lines
7.3 KiB
TypeScript
181 lines
7.3 KiB
TypeScript
import { Head, Link } from '@inertiajs/react';
|
|
import { format } from 'date-fns';
|
|
import { ArrowLeft } from 'lucide-react';
|
|
import { PageHeader } from '@/components/page-header';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { index } from '@/routes/student/course-registrations';
|
|
import { formatAcademicTermLabel } from '@/types/academic-term';
|
|
import type { CourseRegistrationSubmission } from '@/types/course-registration';
|
|
import { RegistrationStatusLabels } from '@/types/course-registration';
|
|
|
|
type Props = {
|
|
submission: CourseRegistrationSubmission;
|
|
};
|
|
|
|
export default function CourseRegistrationShow({ submission }: Props) {
|
|
return (
|
|
<>
|
|
<Head title="Detail KRS" />
|
|
|
|
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
|
|
<PageHeader
|
|
title="Detail Kartu Rencana Studi"
|
|
actions={
|
|
<Button variant="outline" asChild>
|
|
<Link href={index.url()}>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
Kembali
|
|
</Link>
|
|
</Button>
|
|
}
|
|
/>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-start justify-between gap-4">
|
|
<div>
|
|
<CardTitle>
|
|
{submission.academic_term
|
|
? formatAcademicTermLabel(
|
|
submission.academic_term,
|
|
)
|
|
: '-'}
|
|
</CardTitle>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
Ditandatangani pada{' '}
|
|
{format(
|
|
new Date(submission.signed_at),
|
|
'd MMM yyyy, HH:mm',
|
|
)}
|
|
</p>
|
|
</div>
|
|
<Badge
|
|
variant={
|
|
submission.status === 'approved'
|
|
? 'default'
|
|
: submission.status === 'rejected'
|
|
? 'destructive'
|
|
: 'outline'
|
|
}
|
|
>
|
|
{RegistrationStatusLabels[submission.status]}
|
|
</Badge>
|
|
</CardHeader>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">
|
|
Tanda Tangan
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{submission.signature_url ? (
|
|
<div className="w-fit rounded-md border bg-white p-2">
|
|
<img
|
|
src={submission.signature_url}
|
|
alt="Tanda tangan"
|
|
className="h-24"
|
|
/>
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">
|
|
Tidak ada tanda tangan.
|
|
</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">
|
|
Mata Kuliah (
|
|
{submission.course_registrations.length})
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-2">
|
|
{submission.course_registrations.map((registration) => (
|
|
<div
|
|
key={registration.id}
|
|
className="rounded-md border p-3"
|
|
>
|
|
<p className="font-medium">
|
|
{registration.course_class?.course?.code ??
|
|
'-'}{' '}
|
|
-{' '}
|
|
{registration.course_class?.course?.name ??
|
|
'N/A'}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
{registration.course_class?.course
|
|
?.credits ?? '-'}{' '}
|
|
SKS ·{' '}
|
|
{registration.course_class?.lecturer?.user
|
|
?.profile?.full_name ??
|
|
'Dosen belum ditentukan'}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Riwayat</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-2">
|
|
{submission.logs.map((log, index) => (
|
|
<LogBubble
|
|
key={log.id}
|
|
log={log}
|
|
isResubmission={
|
|
log.status === 'submitted' &&
|
|
submission.logs
|
|
.slice(0, index)
|
|
.some((l) => l.status === 'submitted')
|
|
}
|
|
/>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function LogBubble({
|
|
log,
|
|
isResubmission,
|
|
}: {
|
|
log: CourseRegistrationSubmission['logs'][number];
|
|
isResubmission: boolean;
|
|
}) {
|
|
const actorName = log.actor?.profile?.full_name ?? 'Sistem';
|
|
|
|
const message =
|
|
log.status === 'approved'
|
|
? `${actorName} menyetujui KRS ini.`
|
|
: log.status === 'rejected'
|
|
? `${actorName} menolak KRS ini dengan alasan: "${log.reason}"`
|
|
: isResubmission
|
|
? `${actorName} mengajukan ulang KRS.`
|
|
: `${actorName} mengajukan KRS.`;
|
|
|
|
const colorClasses =
|
|
log.status === 'approved'
|
|
? 'border-primary/30 bg-primary/10'
|
|
: log.status === 'rejected'
|
|
? 'border-destructive/30 bg-destructive/10'
|
|
: 'border-border bg-muted';
|
|
|
|
return (
|
|
<div className={`rounded-lg border px-3 py-2 ${colorClasses}`}>
|
|
<p className="text-sm">{message}</p>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
{format(new Date(log.created_at), 'd MMM yyyy, HH:mm')}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|