markout n shit
This commit is contained in:
@@ -1,39 +1,47 @@
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
// Remove server-specific imports to avoid issues in client bundle
|
||||
// import { createSelectSchema, students } from '@workspace/db';
|
||||
// import * as z from 'zod/v4';
|
||||
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';
|
||||
|
||||
// Define the Student interface locally to avoid importing server-side code
|
||||
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;
|
||||
}
|
||||
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>[] = [
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
@@ -23,9 +22,10 @@ import { columns } from './columns';
|
||||
|
||||
interface DataTableProps {
|
||||
data: Student[];
|
||||
markoutAction: (id: number, state: boolean) => void;
|
||||
}
|
||||
|
||||
export function DataTable({ data }: DataTableProps) {
|
||||
export function DataTable({ data, markoutAction }: DataTableProps) {
|
||||
const [selectedStudent, setSelectedStudent] = useState<Student | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
@@ -122,6 +122,9 @@ export function DataTable({ data }: DataTableProps) {
|
||||
student={selectedStudent}
|
||||
isOpen={isModalOpen}
|
||||
onClose={handleCloseModal}
|
||||
markoutAction={(state: boolean) => {
|
||||
markoutAction(selectedStudent.id, state);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -6,30 +6,25 @@ import { Button } from '@workspace/ui/components/button';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { eq } from '@workspace/db/drizzle';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Separator } from '@workspace/ui/components/separator';
|
||||
import {
|
||||
Users,
|
||||
Plus,
|
||||
Search,
|
||||
Filter,
|
||||
Download,
|
||||
Mail,
|
||||
Phone,
|
||||
MapPin,
|
||||
Calendar,
|
||||
GraduationCap,
|
||||
User,
|
||||
Linkedin,
|
||||
Github,
|
||||
FileText,
|
||||
Award,
|
||||
BookOpen
|
||||
} from 'lucide-react';
|
||||
|
||||
async function getData(): Promise<Student[]> {
|
||||
try {
|
||||
const data = await db.select().from(students);
|
||||
const data = await db.query.students.findMany({
|
||||
with: {
|
||||
internships: true,
|
||||
resumes: true,
|
||||
grades: true,
|
||||
},
|
||||
orderBy: (students, { asc }) => [asc(students.createdAt)],
|
||||
});
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Database error:', error);
|
||||
@@ -49,6 +44,16 @@ async function addStudent(formData: FormData) {
|
||||
revalidatePath('/students');
|
||||
}
|
||||
|
||||
async function markoutAction(id: number, state: boolean) {
|
||||
'use server';
|
||||
try {
|
||||
await db.update(students).set({ markedOut: state }).where(eq(students.id, id));
|
||||
revalidatePath('/students');
|
||||
} catch (error) {
|
||||
console.error('Error marking student:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function StudentsTable() {
|
||||
const data = await getData();
|
||||
|
||||
@@ -194,7 +199,7 @@ async function StudentsTable() {
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<DataTable data={data} />
|
||||
<DataTable data={data} markoutAction={markoutAction}/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Student } from './columns';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogClose,
|
||||
} from '@workspace/ui/components/dialog';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar';
|
||||
@@ -20,7 +22,6 @@ import {
|
||||
User,
|
||||
Linkedin,
|
||||
Github,
|
||||
FileText,
|
||||
Award,
|
||||
BookOpen,
|
||||
ExternalLink,
|
||||
@@ -29,14 +30,16 @@ import {
|
||||
Download,
|
||||
Share2
|
||||
} from 'lucide-react';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
|
||||
interface StudentDetailsModalProps {
|
||||
student: Student;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
markoutAction: (state: boolean) => void;
|
||||
}
|
||||
|
||||
export function StudentDetailsModal({ student, isOpen, onClose }: StudentDetailsModalProps) {
|
||||
export function StudentDetailsModal({ student, isOpen, onClose, markoutAction }: StudentDetailsModalProps) {
|
||||
const fullName = [
|
||||
student.firstName,
|
||||
student.middleName,
|
||||
@@ -57,6 +60,17 @@ export function StudentDetailsModal({ student, isOpen, onClose }: StudentDetails
|
||||
return `${value}%`;
|
||||
};
|
||||
|
||||
const [markedOut, setMarkedOut] = React.useState(student.markedOut ?? false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setMarkedOut(student.markedOut ?? false);
|
||||
}, [student.markedOut]);
|
||||
|
||||
const handleMarkoutChange = (checked: boolean) => {
|
||||
setMarkedOut(checked);
|
||||
markoutAction(checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||
@@ -65,14 +79,28 @@ export function StudentDetailsModal({ student, isOpen, onClose }: StudentDetails
|
||||
<DialogTitle className="text-2xl font-bold text-gray-800">
|
||||
Student Details
|
||||
</DialogTitle>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</Button>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={markedOut}
|
||||
onCheckedChange={handleMarkoutChange}
|
||||
className={
|
||||
markedOut
|
||||
? 'bg-red-600 border-red-600 focus-visible:ring-red-600/50'
|
||||
: 'bg-white border-gray-300 focus-visible:ring-gray-300/50'
|
||||
}
|
||||
/>
|
||||
<span className={markedOut ? 'text-red-600 font-semibold' : 'text-gray-700'}>
|
||||
Marked Out
|
||||
</span>
|
||||
</div>
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline" size="icon" className="text-gray-500 hover:text-gray-700">
|
||||
<X className="w-5 h-5" />
|
||||
<span className="sr-only">Close</span>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -319,13 +347,9 @@ export function StudentDetailsModal({ student, isOpen, onClose }: StudentDetails
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button className="flex items-center gap-2">
|
||||
<Share2 className="w-4 h-4" />
|
||||
Share Profile
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user