itmpwk.ac.id/resources/js/pages/admin/services/letter-requests/columns.tsx
Yoga Pangestu 93c5514c22 Refactor Letter Request Management
- Created LetterRequestRequest and LetterRequestStatusRequest for handling form requests.
- Removed the old LetterRequestRequest class and updated the authorization logic.
- Changed the student relationship to user in the LetterRequest model.
- Updated LetterRequestService to reflect changes in the model and request handling.
- Modified permission catalog to include new permissions for letter request status updates.
- Updated database migrations and seeders to use user_id instead of student_id.
- Removed student-specific letter request pages and components, consolidating functionality under admin routes.
- Adjusted frontend components to accommodate new data structure and permissions.
- Enhanced the letter request columns to support status updates directly from the table.
2026-09-01 00:03:13 +07:00

187 lines
6.3 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 { StatusBadge } from '@/components/status-badge';
import type { LetterRequest } from '@/types/letter-request';
import { LetterStatuses, LetterStatusLabels } from '@/types/letter-request';
export type { LetterRequest } from '@/types/letter-request';
const LetterStatusVariants: Record<
string,
'default' | 'secondary' | 'destructive' | 'outline'
> = {
submitted: 'outline',
in_process: 'secondary',
completed: 'default',
rejected: 'destructive',
};
const letterStatusOptions = LetterStatuses.map((status) => ({
value: status,
label: LetterStatusLabels[status],
}));
type CreateColumnsParams = {
handleEdit: (letterRequest: LetterRequest) => void;
handleDeleteClick: (letterRequest: LetterRequest) => void;
handleStatusChange: (letterRequest: LetterRequest, status: string) => void;
canUpdate: boolean;
canDelete: boolean;
canProcess: boolean;
};
export function createLetterRequestColumns(
params: CreateColumnsParams,
): ColumnDef<LetterRequest>[] {
const {
handleEdit,
handleDeleteClick,
handleStatusChange,
canUpdate,
canDelete,
canProcess,
} = params;
const columns: ColumnDef<LetterRequest>[] = [
{
accessorKey: 'user.student.student_number',
header: () => <span>Mahasiswa</span>,
cell: ({ row }) => {
const user = row.original.user;
return (
<div>
<p className="font-medium">
{user?.profile?.full_name ?? 'N/A'}
</p>
<p className="text-xs text-muted-foreground">
{user?.student?.student_number} &middot;{' '}
{user?.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 }) => (
<div className="flex justify-center">
<StatusBadge
status={row.original.status}
statuses={letterStatusOptions}
variants={LetterStatusVariants}
onChange={(status) =>
handleStatusChange(row.original, status)
}
disabled={!canProcess}
/>
</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>
);
},
},
];
if (canUpdate || canDelete) {
columns.push({
id: 'actions',
header: () => <span className="block text-center">Aksi</span>,
meta: {
className: 'w-[100px] text-center',
headerClassName: 'w-[100px] text-center',
},
cell: ({ row }) => {
const isLocked = row.original.status !== 'submitted';
return (
<RowActions
actions={[
{
label: 'Edit',
icon: <Pencil className="h-4 w-4" />,
show: canUpdate && !isLocked,
onClick: () => handleEdit(row.original),
},
{
label: 'Hapus',
icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
show: canDelete && !isLocked,
onClick: () => handleDeleteClick(row.original),
},
]}
/>
);
},
});
}
return columns;
}