itmpwk.ac.id/resources/js/pages/admin/academic-classes/assignments/card.tsx

226 lines
9.4 KiB
TypeScript

import { format } from 'date-fns';
import {
Clock,
ClipboardList,
Pencil,
Trash2,
Upload,
User,
} from 'lucide-react';
import { AttachmentPreviewDialog } from '@/components/attachment-preview-dialog';
import { RowActions } from '@/components/row-actions';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
import { Badge } from '@/components/ui/badge';
import { index as submissionsIndex } from '@/routes/admin/academic-classes/assignments/submissions';
import { formatAcademicTermLabel } from '@/types/academic-term';
import type { Assignment } from '@/types/assignment';
import { AssignmentStatusLabels } from '@/types/assignment';
import { deadlineTextClass, percentageOf } from './utils';
type CreateCardParams = {
handleEdit: (assignment: Assignment) => void;
handleDeleteClick: (assignment: Assignment) => void;
handleSubmitClick: (assignment: Assignment) => void;
canUpdate: boolean;
canDelete: boolean;
canSubmit: boolean;
canViewSubmissions: boolean;
};
export function createAssignmentCard(params: CreateCardParams) {
const {
handleEdit,
handleDeleteClick,
handleSubmitClick,
canUpdate,
canDelete,
canSubmit,
canViewSubmissions,
} = params;
return function AssignmentCard(assignment: Assignment) {
const mySubmission = assignment.submissions?.[0];
const lecturerName =
assignment.course_class?.lecturer?.user?.profile?.full_name ??
null;
return (
<div className="flex flex-col gap-2">
<div className="flex items-start justify-between gap-2">
<div className="flex flex-col gap-1">
<p className="text-base leading-tight font-medium">
{assignment.title}
</p>
{assignment.course_class && (
<span className="text-xs text-muted-foreground">
{assignment.course_class.course?.code ?? ''}{' '}
{assignment.course_class.course?.name ?? ''}
</span>
)}
{lecturerName && (
<span className="flex items-center gap-1 text-xs text-muted-foreground">
<User className="h-3 w-3 shrink-0" />
{lecturerName}
</span>
)}
</div>
<RowActions
wrapperClassName="-mt-1 -mr-2 flex items-center gap-1"
actions={[
{
label:
mySubmission?.status === 'submitted'
? 'Kumpulkan Ulang'
: 'Kumpulkan Tugas',
icon: <Upload className="h-3.5 w-3.5" />,
show:
canSubmit && assignment.status === 'open',
onClick: () => handleSubmitClick(assignment),
},
{
label: 'Pengumpulan',
icon: (
<ClipboardList className="h-3.5 w-3.5" />
),
show: canViewSubmissions,
href: submissionsIndex.url(assignment.id),
},
{
label: 'Edit',
icon: <Pencil className="h-3.5 w-3.5" />,
show: canUpdate,
onClick: () => handleEdit(assignment),
},
{
label: 'Hapus',
icon: (
<Trash2 className="h-3.5 w-3.5 text-destructive" />
),
show: canDelete,
onClick: () => handleDeleteClick(assignment),
},
]}
/>
</div>
<div className="flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
<Badge
variant={
assignment.status === 'open'
? 'secondary'
: 'destructive'
}
className="w-fit text-[10px] font-normal"
>
{AssignmentStatusLabels[assignment.status]}
</Badge>
{assignment.course_class?.academic_term && (
<Badge
variant="outline"
className="w-fit text-[10px] font-normal"
>
{formatAcademicTermLabel(
assignment.course_class.academic_term,
)}
</Badge>
)}
</div>
<span
className={`inline-flex items-center gap-1.5 text-xs ${deadlineTextClass(assignment.deadline)}`}
>
<Clock className="h-3.5 w-3.5 shrink-0" />
{format(new Date(assignment.deadline), 'd MMM yyyy, HH:mm')}
</span>
{assignment.attachment_url && assignment.attachment_name ? (
<div className="text-xs text-muted-foreground">
<AttachmentPreviewDialog
fileUrl={assignment.attachment_url}
fileName={assignment.attachment_name}
/>
</div>
) : null}
<div className="flex flex-wrap items-center gap-1.5 pt-1">
{canSubmit ? (
<Badge
variant={
mySubmission?.status === 'submitted'
? 'default'
: 'secondary'
}
>
{mySubmission?.status === 'submitted'
? 'Sudah Mengumpulkan'
: 'Belum Mengumpulkan'}
</Badge>
) : (
<>
<Badge variant="secondary">
{assignment.submissions_count.toLocaleString(
'id-ID',
)}{' '}
/{' '}
{(
assignment.course_class
?.enrollments_count ?? 0
).toLocaleString('id-ID')}{' '}
Pengumpulan (
{percentageOf(
assignment.submissions_count,
assignment.course_class
?.enrollments_count ?? 0,
)}
%)
</Badge>
<Badge variant="outline">
{assignment.graded_submissions_count.toLocaleString(
'id-ID',
)}{' '}
/{' '}
{assignment.submissions_count.toLocaleString(
'id-ID',
)}{' '}
Dinilai (
{percentageOf(
assignment.graded_submissions_count,
assignment.submissions_count,
)}
%)
</Badge>
</>
)}
</div>
{assignment.description && (
<Accordion
type="single"
collapsible
className="-mx-6 -mb-6 border-t"
>
<AccordionItem
value="description"
className="border-b-0"
>
<AccordionTrigger className="px-6 text-xs font-medium text-foreground hover:no-underline">
Deskripsi
</AccordionTrigger>
<AccordionContent className="px-6">
<p className="text-sm whitespace-pre-line text-foreground">
{assignment.description}
</p>
</AccordionContent>
</AccordionItem>
</Accordion>
)}
</div>
);
};
}