siakad-itm/resources/js/pages/admin/services/letter-requests/columns.tsx
Yoga Pangestu 5994f38f01 feat: implement permission checks for academic terms, courses, departments, and user management
- Added permission checks for creating, updating, and deleting academic terms, courses, and departments.
- Updated the routes to enforce permissions for various actions in the admin panel.
- Enhanced user management by adding permissions for administrators, lecturers, and students.
- Refactored components to conditionally render actions based on user permissions.
- Updated the auth type to include optional permissions array for user roles.
2026-08-30 23:27:31 +07:00

165 lines
5.8 KiB
TypeScript

import type { ColumnDef } from '@tanstack/react-table';
import { format } from 'date-fns';
import { Paperclip, Pencil, Trash2 } from 'lucide-react';
import { RowActions } from '@/components/row-actions';
import { Badge } from '@/components/ui/badge';
import type { LetterRequest, LetterStatus } from '@/types/letter-request';
import { LetterStatusLabels } from '@/types/letter-request';
export type { LetterRequest } from '@/types/letter-request';
type CreateColumnsParams = {
handleEdit: (letterRequest: LetterRequest) => void;
handleDeleteClick: (letterRequest: LetterRequest) => void;
canUpdate: boolean;
canDelete: boolean;
};
export function createLetterRequestColumns(
params: CreateColumnsParams,
): ColumnDef<LetterRequest>[] {
const { handleEdit, handleDeleteClick, canUpdate, canDelete } = params;
return [
{
accessorKey: 'student.student_number',
header: () => <span>Mahasiswa</span>,
cell: ({ row }) => {
const student = row.original.student;
return (
<div>
<p className="font-medium">
{student?.user?.profile?.full_name ?? 'N/A'}
</p>
<p className="text-xs text-muted-foreground">
{student?.student_number} &middot;{' '}
{student?.department?.name ?? '-'}
</p>
</div>
);
},
},
{
accessorKey: 'letter_type',
header: () => <span>Jenis Surat</span>,
cell: ({ row }) => {
const letterRequest = row.original;
return (
<div>
<p className="font-medium">
{letterRequest.letter_type}
</p>
{letterRequest.purpose && (
<p className="line-clamp-1 max-w-xs text-xs text-muted-foreground">
{letterRequest.purpose}
</p>
)}
</div>
);
},
},
{
accessorKey: 'status',
header: () => <span className="block text-center">Status</span>,
meta: {
className: 'w-[120px] text-center',
headerClassName: 'w-[120px] text-center',
},
cell: ({ row }) => {
const status = row.getValue('status') as LetterStatus | null;
const variant =
status === 'completed'
? 'default'
: status === 'rejected'
? 'destructive'
: status === 'in_process'
? 'secondary'
: 'outline';
return (
<div className="flex justify-center">
{status ? (
<Badge variant={variant}>
{LetterStatusLabels[status]}
</Badge>
) : (
<span className="text-muted-foreground">-</span>
)}
</div>
);
},
},
{
accessorKey: 'processor.profile.full_name',
header: () => <span>Diproses Oleh</span>,
cell: ({ row }) =>
row.original.processor?.profile?.full_name ?? '-',
},
{
accessorKey: 'submitted_at',
header: () => <span>Diajukan</span>,
cell: ({ row }) => {
const submittedAt = row.original.submitted_at;
return submittedAt
? format(new Date(submittedAt), 'd MMM yyyy, HH:mm')
: '-';
},
},
{
accessorKey: 'result_name',
header: () => <span>Dokumen</span>,
cell: ({ row }) => {
const letterRequest = row.original;
if (!letterRequest.result_url) {
return <span className="text-muted-foreground">-</span>;
}
return (
<a
href={letterRequest.result_url}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1 text-primary underline underline-offset-4 hover:text-primary/80"
>
<Paperclip className="h-3.5 w-3.5" />
{letterRequest.result_name}
</a>
);
},
},
{
id: 'actions',
header: () => <span className="block text-center">Aksi</span>,
meta: {
className: 'w-[100px] text-center',
headerClassName: 'w-[100px] text-center',
},
cell: ({ row }) => (
<RowActions
actions={[
{
label: 'Edit',
icon: <Pencil className="h-4 w-4" />,
show: canUpdate,
onClick: () => handleEdit(row.original),
},
{
label: 'Hapus',
icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
show: canDelete,
onClick: () => handleDeleteClick(row.original),
},
]}
/>
),
},
];
}