feat: add GenderBadge and StudentStatusBadge components; update user columns to utilize new badges
Some checks failed
tests / ci (pull_request) Has been cancelled

This commit is contained in:
Yoga Pangestu 2026-08-26 00:51:22 +07:00
parent 5c0cadcebf
commit 1012127196
5 changed files with 144 additions and 32 deletions

View File

@ -0,0 +1,25 @@
import { Badge } from '@/components/ui/badge';
type GenderBadgeProps = {
gender: string | null | undefined;
};
export function GenderBadge({ gender }: GenderBadgeProps) {
if (gender === 'male') {
return (
<Badge className="bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300">
Laki-laki
</Badge>
);
}
if (gender === 'female') {
return (
<Badge className="bg-pink-100 text-pink-700 dark:bg-pink-500/20 dark:text-pink-300">
Perempuan
</Badge>
);
}
return <span className="text-muted-foreground">-</span>;
}

View File

@ -0,0 +1,34 @@
import { Badge } from '@/components/ui/badge';
type StudentStatusBadgeProps = {
status: string | null | undefined;
};
const StudentStatusLabels: Record<string, string> = {
active: 'Aktif',
on_leave: 'Cuti',
graduated: 'Lulus',
dropped_out: 'Drop Out',
};
const StudentStatusVariants: Record<
string,
'default' | 'secondary' | 'destructive' | 'outline'
> = {
active: 'default',
on_leave: 'secondary',
graduated: 'outline',
dropped_out: 'destructive',
};
export function StudentStatusBadge({ status }: StudentStatusBadgeProps) {
if (!status) {
return <span className="text-muted-foreground">-</span>;
}
return (
<Badge variant={StudentStatusVariants[status] ?? 'outline'}>
{StudentStatusLabels[status] ?? status}
</Badge>
);
}

View File

@ -1,6 +1,7 @@
import { RowActions } from '@/components/row-actions';
import type { ColumnDef } from '@tanstack/react-table'; import type { ColumnDef } from '@tanstack/react-table';
import { Key, Pencil, Trash2 } from 'lucide-react'; import { Key, Pencil, Trash2 } from 'lucide-react';
import { GenderBadge } from '@/components/gender-badge';
import { RowActions } from '@/components/row-actions';
export type Administrator = { export type Administrator = {
id: number; id: number;
@ -33,7 +34,9 @@ export function createAdministratorColumns(
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex flex-col"> <div className="flex flex-col">
<span>{row.original.username}</span> <span>{row.original.username}</span>
<span className="text-sm text-muted-foreground">{row.original.email}</span> <span className="text-sm text-muted-foreground">
{row.original.email}
</span>
</div> </div>
), ),
}, },
@ -50,10 +53,9 @@ export function createAdministratorColumns(
{ {
accessorKey: 'profile.gender', accessorKey: 'profile.gender',
header: () => <span>Jenis Kelamin</span>, header: () => <span>Jenis Kelamin</span>,
cell: ({ row }) => { cell: ({ row }) => (
const gender = row.original.profile?.gender; <GenderBadge gender={row.original.profile?.gender} />
return gender === 'male' ? 'Laki-laki' : gender === 'female' ? 'Perempuan' : '-'; ),
},
}, },
{ {
id: 'actions', id: 'actions',
@ -80,7 +82,9 @@ export function createAdministratorColumns(
}, },
{ {
label: 'Hapus', label: 'Hapus',
icon: <Trash2 className="h-4 w-4 text-destructive" />, icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
onClick: () => handleDeleteClick(admin), onClick: () => handleDeleteClick(admin),
}, },
]} ]}

View File

@ -1,13 +1,17 @@
import { RowActions } from '@/components/row-actions';
import type { ColumnDef } from '@tanstack/react-table'; import type { ColumnDef } from '@tanstack/react-table';
import { Key, Pencil, Trash2 } from 'lucide-react'; import { Key, Pencil, Trash2 } from 'lucide-react';
import { GenderBadge } from '@/components/gender-badge';
import { RowActions } from '@/components/row-actions';
export type Lecturer = { export type Lecturer = {
id: number; id: number;
username: string; username: string;
email: string; email: string;
profile: { full_name: string } | null; profile: { full_name: string; phone_number: string; gender: string } | null;
lecturer: { lecturer_number: string; department: { name: string } | null } | null; lecturer: {
lecturer_number: string;
department: { name: string } | null;
} | null;
}; };
type CreateColumnsParams = { type CreateColumnsParams = {
@ -23,14 +27,16 @@ export function createLecturerColumns(
return [ return [
{ {
accessorKey: 'lecturer.lecturer_number', id: 'identity',
header: () => <span>NIDN</span>, header: () => <span>NIDN</span>,
cell: ({ row }) => row.original.lecturer?.lecturer_number ?? '-', cell: ({ row }) => (
}, <div className="flex flex-col">
{ <span>{row.original.lecturer?.lecturer_number ?? '-'}</span>
accessorKey: 'profile.full_name', <span className="text-sm text-muted-foreground">
header: () => <span>Nama Lengkap</span>, {row.original.profile?.full_name ?? '-'}
cell: ({ row }) => row.original.profile?.full_name ?? '-', </span>
</div>
),
}, },
{ {
id: 'account', id: 'account',
@ -38,7 +44,9 @@ export function createLecturerColumns(
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex flex-col"> <div className="flex flex-col">
<span>{row.original.username}</span> <span>{row.original.username}</span>
<span className="text-sm text-muted-foreground">{row.original.email}</span> <span className="text-sm text-muted-foreground">
{row.original.email}
</span>
</div> </div>
), ),
}, },
@ -47,6 +55,18 @@ export function createLecturerColumns(
header: () => <span>Jurusan</span>, header: () => <span>Jurusan</span>,
cell: ({ row }) => row.original.lecturer?.department?.name ?? '-', cell: ({ row }) => row.original.lecturer?.department?.name ?? '-',
}, },
{
accessorKey: 'profile.phone_number',
header: () => <span>Nomor Telepon</span>,
cell: ({ row }) => row.original.profile?.phone_number ?? '-',
},
{
accessorKey: 'profile.gender',
header: () => <span>Jenis Kelamin</span>,
cell: ({ row }) => (
<GenderBadge gender={row.original.profile?.gender} />
),
},
{ {
id: 'actions', id: 'actions',
header: () => <span className="block text-center">Aksi</span>, header: () => <span className="block text-center">Aksi</span>,
@ -72,7 +92,9 @@ export function createLecturerColumns(
}, },
{ {
label: 'Hapus', label: 'Hapus',
icon: <Trash2 className="h-4 w-4 text-destructive" />, icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
onClick: () => handleDeleteClick(lecturer), onClick: () => handleDeleteClick(lecturer),
}, },
]} ]}

View File

@ -1,13 +1,20 @@
import { RowActions } from '@/components/row-actions';
import type { ColumnDef } from '@tanstack/react-table'; import type { ColumnDef } from '@tanstack/react-table';
import { Key, Pencil, Trash2 } from 'lucide-react'; import { Key, Pencil, Trash2 } from 'lucide-react';
import { GenderBadge } from '@/components/gender-badge';
import { RowActions } from '@/components/row-actions';
import { StudentStatusBadge } from '@/components/student-status-badge';
export type Student = { export type Student = {
id: number; id: number;
username: string; username: string;
email: string; email: string;
profile: { full_name: string } | null; profile: { full_name: string; phone_number: string; gender: string } | null;
student: { student_number: string; department: { name: string } | null; enrollment_year: number; status: string | null } | null; student: {
student_number: string;
department: { name: string } | null;
enrollment_year: number;
status: string | null;
} | null;
}; };
type CreateColumnsParams = { type CreateColumnsParams = {
@ -23,14 +30,16 @@ export function createStudentColumns(
return [ return [
{ {
accessorKey: 'student.student_number', id: 'identity',
header: () => <span>NIM</span>, header: () => <span>NIM</span>,
cell: ({ row }) => row.original.student?.student_number ?? '-', cell: ({ row }) => (
}, <div className="flex flex-col">
{ <span>{row.original.student?.student_number ?? '-'}</span>
accessorKey: 'profile.full_name', <span className="text-sm text-muted-foreground">
header: () => <span>Nama Lengkap</span>, {row.original.profile?.full_name ?? '-'}
cell: ({ row }) => row.original.profile?.full_name ?? '-', </span>
</div>
),
}, },
{ {
id: 'account', id: 'account',
@ -38,7 +47,9 @@ export function createStudentColumns(
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex flex-col"> <div className="flex flex-col">
<span>{row.original.username}</span> <span>{row.original.username}</span>
<span className="text-sm text-muted-foreground">{row.original.email}</span> <span className="text-sm text-muted-foreground">
{row.original.email}
</span>
</div> </div>
), ),
}, },
@ -55,7 +66,21 @@ export function createStudentColumns(
{ {
accessorKey: 'student.status', accessorKey: 'student.status',
header: () => <span>Status</span>, header: () => <span>Status</span>,
cell: ({ row }) => row.original.student?.status ?? '-', cell: ({ row }) => (
<StudentStatusBadge status={row.original.student?.status} />
),
},
{
accessorKey: 'profile.phone_number',
header: () => <span>Nomor Telepon</span>,
cell: ({ row }) => row.original.profile?.phone_number ?? '-',
},
{
accessorKey: 'profile.gender',
header: () => <span>Jenis Kelamin</span>,
cell: ({ row }) => (
<GenderBadge gender={row.original.profile?.gender} />
),
}, },
{ {
id: 'actions', id: 'actions',
@ -82,7 +107,9 @@ export function createStudentColumns(
}, },
{ {
label: 'Hapus', label: 'Hapus',
icon: <Trash2 className="h-4 w-4 text-destructive" />, icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
onClick: () => handleDeleteClick(student), onClick: () => handleDeleteClick(student),
}, },
]} ]}