Student Gradebook Table
blockeducationAcademic gradebook ledger with course header, weighted assignment categories (homework, midterm, labs, final project), class GPA & enrollment metric cards, overall percentage, letter grade badges, attendance tags, and student action menus.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/student-gradebook-table.json$npx shadcn@latest add https://uipkge.dev/r/react/student-gradebook-table.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/student-gradebook-table.json$bunx shadcn@latest add https://uipkge.dev/r/react/student-gradebook-table.jsonNamed registry:
npx shadcn@latest add @uipkge-react/student-gradebook-tableInstalls to:components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
StudentGradeRecordinterface StudentGradeRecord {
id: string
name: string
studentId: string
homework: string
midterm: string
labProjects: string
finalProject: string
overallGrade: string
letterGrade: LetterGrade
attendanceRate: string
absences: number
}npm dependencies
Files installed (1)
components/blocks/StudentGradebookTable.tsx11.9 kB
'use client' import { CalendarCheck, ClipboardCheck, Download, GraduationCap, Mail, MoreHorizontal, Pencil, Plus, User, Users, } from 'lucide-react' import { cn } from '@/lib/utils' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' export type LetterGrade = 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' export interface StudentGradeRecord { id: string name: string studentId: string homework: string midterm: string labProjects: string finalProject: string overallGrade: string letterGrade: LetterGrade attendanceRate: string absences: number } const metrics = [ { title: 'Class GPA', value: '3.62 / 4.0', description: '+0.18 vs departmental target', icon: GraduationCap, iconClass: 'bg-success/10 text-success', valueClass: 'text-success', }, { title: 'Total Enrolled Students', value: '28 students', description: '100% active roster', icon: Users, iconClass: 'bg-info/10 text-info', valueClass: 'text-foreground', }, { title: 'Assignments Graded', value: '12 / 14 assignments', description: '2 pending final submissions', icon: ClipboardCheck, iconClass: 'bg-chart-1/10 text-chart-1', valueClass: 'text-foreground', }, { title: 'Class Attendance Rate', value: '96.2% average', description: '36 lectures & labs logged', icon: CalendarCheck, iconClass: 'bg-warning/10 text-warning', valueClass: 'text-foreground', }, ] const gradeBadgeConfig: Record<LetterGrade, { className: string }> = { 'A+': { className: 'bg-success/10 text-success border-success/20 font-semibold', }, A: { className: 'bg-success/10 text-success border-success/20 font-semibold', }, 'A-': { className: 'bg-success/10 text-success border-success/20 font-semibold', }, 'B+': { className: 'bg-info/10 text-info border-info/20 font-semibold', }, B: { className: 'bg-warning/10 text-warning border-warning/20 font-semibold', }, 'B-': { className: 'bg-warning/10 text-warning border-warning/20 font-semibold', }, } const students: StudentGradeRecord[] = [ { id: 's1', name: 'Elena Rostova', studentId: '#STU-84920', homework: '95.0%', midterm: '92.0%', labProjects: '98.0%', finalProject: '94.0%', overallGrade: '94.5%', letterGrade: 'A', attendanceRate: '100%', absences: 0, }, { id: 's2', name: 'David Chen', studentId: '#STU-84921', homework: '88.0%', midterm: '91.0%', labProjects: '86.0%', finalProject: '89.0%', overallGrade: '88.6%', letterGrade: 'B+', attendanceRate: '96%', absences: 1, }, { id: 's3', name: 'Sarah Jenkins', studentId: '#STU-84922', homework: '98.0%', midterm: '96.0%', labProjects: '100.0%', finalProject: '95.0%', overallGrade: '97.1%', letterGrade: 'A', attendanceRate: '100%', absences: 0, }, { id: 's4', name: 'Marcus Vance', studentId: '#STU-84923', homework: '82.0%', midterm: '79.0%', labProjects: '85.0%', finalProject: '80.0%', overallGrade: '81.4%', letterGrade: 'B', attendanceRate: '92%', absences: 2, }, { id: 's5', name: 'Sofia Rossi', studentId: '#STU-84924', homework: '91.0%', midterm: '88.0%', labProjects: '94.0%', finalProject: '90.0%', overallGrade: '90.6%', letterGrade: 'A-', attendanceRate: '98%', absences: 1, }, { id: 's6', name: 'Alex Rivera', studentId: '#STU-84925', homework: '84.0%', midterm: '86.0%', labProjects: '82.0%', finalProject: '85.0%', overallGrade: '84.3%', letterGrade: 'B', attendanceRate: '94%', absences: 2, }, ] function initials(name: string) { return name .split(' ') .map((part) => part[0]) .slice(0, 2) .join('') .toUpperCase() } export function StudentGradebookTable({ className }: { className?: string }) { return ( <div data-slot="student-gradebook-table" className={cn('w-full space-y-6', className)}> {/* Header */} <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <div> <div className="flex flex-wrap items-center gap-2.5"> <h2 className="text-2xl font-bold tracking-tight">CS 301: Advanced Software Systems & Compilers</h2> <Badge variant="outline" className="font-mono text-xs"> CS-301-B </Badge> </div> <div className="text-muted-foreground mt-1.5 flex flex-wrap items-center gap-2 text-sm"> <span>Fall 2026 · Section B</span> <span className="text-foreground inline-flex items-center gap-1 font-medium"> Class Average: <span className="tabular-nums">88.4%</span> · Grade B+ </span> </div> </div> <div className="flex flex-wrap items-center gap-2.5"> <Button aria-label="Download attachment" variant="outline"> <Download aria-hidden="true" /> Export Grades CSV </Button> <Button> <Plus aria-hidden="true" /> New Grade Entry </Button> </div> </div> {/* 4 Class Overview Metric Cards */} <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> {metrics.map((metric) => { const Icon = metric.icon return ( <Card key={metric.title} className="shadow-xs"> <CardContent className="space-y-2 p-5"> <div className="flex items-center justify-between"> <p className="text-muted-foreground text-sm font-medium">{metric.title}</p> <div className={cn('flex size-8 items-center justify-center rounded-md', metric.iconClass)}> <Icon aria-hidden="true" className="size-4" /> </div> </div> <div> <p className={cn('text-2xl font-bold tracking-tight tabular-nums', metric.valueClass)}> {metric.value} </p> <p className="text-muted-foreground mt-1 text-xs">{metric.description}</p> </div> </CardContent> </Card> ) })} </div> {/* Gradebook Table */} <div className="bg-card overflow-x-auto rounded-lg border shadow-xs"> <Table> <TableHeader> <TableRow> <TableHead>Student</TableHead> <TableHead className="text-right">Homework (25%)</TableHead> <TableHead className="text-right">Midterm (30%)</TableHead> <TableHead className="text-right">Lab Projects (25%)</TableHead> <TableHead className="text-right">Final Project (20%)</TableHead> <TableHead className="text-right font-semibold">Overall Grade</TableHead> <TableHead className="text-center">Letter Grade</TableHead> <TableHead>Attendance</TableHead> <TableHead className="w-12 text-right"> <span className="sr-only">Actions</span> </TableHead> </TableRow> </TableHeader> <TableBody> {students.map((student) => ( <TableRow key={student.id} className="hover:bg-muted/50"> <TableCell> <div className="flex items-center gap-3"> <Avatar className="size-8"> <AvatarFallback className="text-xs font-medium">{initials(student.name)}</AvatarFallback> </Avatar> <div className="min-w-0"> <p className="truncate text-sm font-semibold">{student.name}</p> <p className="text-muted-foreground font-mono text-xs">{student.studentId}</p> </div> </div> </TableCell> <TableCell className="text-right font-medium tabular-nums">{student.homework}</TableCell> <TableCell className="text-right font-medium tabular-nums">{student.midterm}</TableCell> <TableCell className="text-right font-medium tabular-nums">{student.labProjects}</TableCell> <TableCell className="text-right font-medium tabular-nums">{student.finalProject}</TableCell> <TableCell className="text-foreground text-right font-bold tabular-nums"> {student.overallGrade} </TableCell> <TableCell className="text-center"> <Badge variant="outline" className={cn('font-semibold', gradeBadgeConfig[student.letterGrade].className)} > {student.letterGrade} </Badge> </TableCell> <TableCell> <div className="inline-flex items-center gap-1.5 text-xs"> <span className={cn( 'inline-block size-1.5 rounded-full', student.absences === 0 ? 'bg-success' : 'bg-warning', )} aria-hidden="true" /> <span className="text-foreground font-medium tabular-nums">{student.attendanceRate}</span> <span className="text-muted-foreground"> ·{' '} {student.absences === 0 ? '0 absences' : `${student.absences} absence${student.absences > 1 ? 's' : ''}`} </span> </div> </TableCell> <TableCell className="text-right"> <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" size="icon-sm" className="text-muted-foreground"> <MoreHorizontal aria-hidden="true" /> <span className="sr-only">Open actions for {student.name}</span> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-48"> <DropdownMenuItem> <Pencil aria-hidden="true" /> Edit Grades </DropdownMenuItem> <DropdownMenuItem> <User aria-hidden="true" /> View Student Profile </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem> <Mail aria-hidden="true" /> Send Email Alert </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </TableCell> </TableRow> ))} </TableBody> </Table> </div> {/* Table Footer / Pagination */} <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <p className="text-muted-foreground text-sm"> Showing <span className="text-foreground font-medium">6</span> of{' '} <span className="text-foreground font-medium">28</span> enrolled students </p> <div className="flex items-center gap-2"> <Button variant="outline" size="sm" disabled> Previous </Button> <Button variant="outline" size="sm"> Next </Button> </div> </div> </div> ) }
Raw manifest:https://uipkge.dev/r/react/student-gradebook-table.json