198 lines
6.3 KiB
TypeScript
198 lines
6.3 KiB
TypeScript
import { ColumnDef } from '@tanstack/react-table';
|
|
import { students, internships, resumes, grades } from '@workspace/db/schema';
|
|
import { Badge } from '@workspace/ui/components/badge';
|
|
// import { Button } from '@workspace/ui/components/button';
|
|
import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar';
|
|
import { Eye, Mail, Phone, MapPin, Calendar, GraduationCap } from 'lucide-react';
|
|
|
|
type SelectStudent = typeof students.$inferSelect
|
|
type SelectInternships = typeof internships.$inferSelect
|
|
type SelectResume = typeof resumes.$inferSelect
|
|
type SelectGrades = typeof grades.$inferSelect
|
|
|
|
export type Student = SelectStudent & {
|
|
internships: SelectInternships[];
|
|
resumes: SelectResume[];
|
|
grades: SelectGrades[];
|
|
};
|
|
|
|
// export interface Student {
|
|
// id: number;
|
|
// email: string;
|
|
// rollNumber: string | null;
|
|
// verified: boolean;
|
|
// firstName: string | null;
|
|
// middleName: string | null;
|
|
// lastName: string | null;
|
|
// mothersName?: string | null;
|
|
// gender?: string | null;
|
|
// dob?: Date | null;
|
|
// personalGmail?: string | null;
|
|
// phoneNumber?: string | null;
|
|
// address?: string | null;
|
|
// profilePicture?: string | null;
|
|
// degree?: string | null;
|
|
// branch?: string | null;
|
|
// year?: string | null;
|
|
// skills?: string[] | null;
|
|
// ssc?: number | null;
|
|
// hsc?: number | null;
|
|
// isDiploma?: boolean | null;
|
|
// linkedin?: string | null;
|
|
// github?: string | null;
|
|
// createdAt?: Date;
|
|
// }
|
|
|
|
export const columns: ColumnDef<Student>[] = [
|
|
{
|
|
accessorKey: 'id',
|
|
header: 'ID',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
return (
|
|
<div className="flex items-center gap-3">
|
|
<Avatar className="w-10 h-10">
|
|
<AvatarImage src={student.profilePicture || undefined} />
|
|
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-purple-600 text-white font-semibold">
|
|
{student.firstName ? student.firstName.charAt(0).toUpperCase() :
|
|
student.email ? student.email.charAt(0).toUpperCase() : 'S'}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-medium text-gray-900">#{student.id}</span>
|
|
<Badge
|
|
variant={student.verified ? "default" : "secondary"}
|
|
className={`text-xs ${student.verified ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'}`}
|
|
>
|
|
{student.verified ? 'Verified' : 'Pending'}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'name',
|
|
header: 'Student Name',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
const fullName = [
|
|
student.firstName,
|
|
student.middleName,
|
|
student.lastName
|
|
].filter(Boolean).join(' ') || 'Not provided';
|
|
|
|
return (
|
|
<div className="flex flex-col">
|
|
<span className="font-medium text-gray-900">{fullName}</span>
|
|
<span className="text-sm text-gray-500">{student.rollNumber || 'No Roll Number'}</span>
|
|
</div>
|
|
);
|
|
},
|
|
filterFn: 'includesString',
|
|
},
|
|
{
|
|
accessorKey: 'email',
|
|
header: 'Contact Information',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
return (
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<Mail className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-900">{student.email}</span>
|
|
</div>
|
|
{student.phoneNumber && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<Phone className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-600">{student.phoneNumber}</span>
|
|
</div>
|
|
)}
|
|
{student.personalGmail && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<Mail className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-600">{student.personalGmail}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
filterFn: 'includesString',
|
|
},
|
|
{
|
|
accessorKey: 'academic',
|
|
header: 'Academic Details',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
return (
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<GraduationCap className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-900">{student.degree || 'Not specified'}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<span className="text-gray-600">{student.branch || 'Branch not specified'}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<Calendar className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-600">{student.year || 'Year not specified'}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'location',
|
|
header: 'Location',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
return (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<MapPin className="w-3 h-3 text-gray-400" />
|
|
<span className="text-gray-600">{student.address || 'Address not provided'}</span>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'skills',
|
|
header: 'Skills',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
const skills = student.skills || [];
|
|
|
|
if (skills.length === 0) {
|
|
return <span className="text-sm text-gray-500">No skills listed</span>;
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-wrap gap-1">
|
|
{skills.slice(0, 3).map((skill, index) => (
|
|
<Badge key={index} variant="outline" className="text-xs">
|
|
{skill}
|
|
</Badge>
|
|
))}
|
|
{skills.length > 3 && (
|
|
<Badge variant="outline" className="text-xs">
|
|
+{skills.length - 3} more
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
id: 'actions',
|
|
header: 'Actions',
|
|
cell: ({ row }) => {
|
|
const student = row.original;
|
|
return (
|
|
<div className="flex items-center gap-2 text-blue-600">
|
|
<Eye className="w-4 h-4" />
|
|
<span className="text-sm font-medium">View Details</span>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
];
|