feat(classes): optimize teacher dashboard ui and implement grade management
This commit is contained in:
@@ -1,77 +1,113 @@
|
||||
import Link from "next/link";
|
||||
import { GraduationCap, Building2, BookOpen } from "lucide-react";
|
||||
import { Book, MoreVertical, Edit, Trash2, BookOpen, GraduationCap, Building2 } from "lucide-react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/shared/components/ui/card";
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/shared/components/ui/dropdown-menu";
|
||||
import { cn, formatDate } from "@/shared/lib/utils";
|
||||
import { Textbook } from "../types";
|
||||
|
||||
interface TextbookCardProps {
|
||||
textbook: Textbook;
|
||||
hrefBase?: string;
|
||||
hideActions?: boolean;
|
||||
}
|
||||
|
||||
export function TextbookCard({ textbook, hrefBase }: TextbookCardProps) {
|
||||
const subjectColorMap: Record<string, string> = {
|
||||
Mathematics: "from-blue-500/20 to-blue-600/20 text-blue-700 dark:text-blue-300 border-blue-200 dark:border-blue-800",
|
||||
Physics: "from-purple-500/20 to-purple-600/20 text-purple-700 dark:text-purple-300 border-purple-200 dark:border-purple-800",
|
||||
Chemistry: "from-teal-500/20 to-teal-600/20 text-teal-700 dark:text-teal-300 border-teal-200 dark:border-teal-800",
|
||||
English: "from-orange-500/20 to-orange-600/20 text-orange-700 dark:text-orange-300 border-orange-200 dark:border-orange-800",
|
||||
History: "from-amber-500/20 to-amber-600/20 text-amber-700 dark:text-amber-300 border-amber-200 dark:border-amber-800",
|
||||
Biology: "from-emerald-500/20 to-emerald-600/20 text-emerald-700 dark:text-emerald-300 border-emerald-200 dark:border-emerald-800",
|
||||
Geography: "from-sky-500/20 to-sky-600/20 text-sky-700 dark:text-sky-300 border-sky-200 dark:border-sky-800",
|
||||
};
|
||||
|
||||
export function TextbookCard({ textbook, hrefBase, hideActions }: TextbookCardProps) {
|
||||
const base = hrefBase || "/teacher/textbooks";
|
||||
const colorClass = subjectColorMap[textbook.subject] || "from-zinc-500/20 to-zinc-600/20 text-zinc-700 dark:text-zinc-300 border-zinc-200 dark:border-zinc-800";
|
||||
|
||||
return (
|
||||
<Link href={`${base}/${textbook.id}`} className="block h-full">
|
||||
<Card
|
||||
className={cn(
|
||||
"group h-full overflow-hidden transition-all duration-300 ease-out",
|
||||
"hover:-translate-y-1 hover:shadow-md hover:border-primary/50"
|
||||
)}
|
||||
>
|
||||
<div className="relative aspect-[4/3] w-full overflow-hidden bg-muted/30 p-6 flex items-center justify-center">
|
||||
{/* Fallback Cover Visualization */}
|
||||
<div className="relative z-10 flex h-24 w-20 flex-col items-center justify-center rounded-sm bg-background shadow-sm border transition-transform duration-300 group-hover:scale-110">
|
||||
<div className="h-full w-full bg-gradient-to-br from-primary/10 to-primary/5 p-2">
|
||||
<div className="h-1 w-full rounded-full bg-primary/20 mb-1" />
|
||||
<div className="h-1 w-2/3 rounded-full bg-primary/20" />
|
||||
</div>
|
||||
<Card className="group flex flex-col h-full overflow-hidden transition-all duration-300 hover:shadow-md hover:border-primary/50">
|
||||
<Link href={`${base}/${textbook.id}`} className="flex-1">
|
||||
<div className={cn("relative h-32 w-full overflow-hidden bg-gradient-to-br p-6 transition-all group-hover:scale-105", colorClass)}>
|
||||
<div className="absolute inset-0 bg-grid-black/[0.05] dark:bg-grid-white/[0.05]" />
|
||||
<div className="relative z-10 flex h-full flex-col justify-between">
|
||||
<Badge variant="secondary" className="w-fit bg-background/50 backdrop-blur-sm border-transparent shadow-none">
|
||||
{textbook.subject}
|
||||
</Badge>
|
||||
<Book className="h-8 w-8 opacity-50" />
|
||||
</div>
|
||||
|
||||
{/* Decorative Background Pattern */}
|
||||
<div className="absolute inset-0 bg-grid-black/[0.02] dark:bg-grid-white/[0.02]" />
|
||||
</div>
|
||||
|
||||
<CardHeader className="p-4 pb-2">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="space-y-1">
|
||||
<Badge variant="outline" className="w-fit text-[10px] h-5 px-1.5 font-normal border-primary/20 text-primary bg-primary/5">
|
||||
{textbook.subject}
|
||||
</Badge>
|
||||
<CardTitle className="line-clamp-2 text-base leading-tight">
|
||||
{textbook.title}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<h3 className="font-semibold text-lg leading-tight line-clamp-2 group-hover:text-primary transition-colors">
|
||||
{textbook.title}
|
||||
</h3>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="p-4 pt-0 text-sm text-muted-foreground">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<GraduationCap className="h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
<span>{textbook.grade}</span>
|
||||
<CardContent className="p-4 pt-1 pb-2">
|
||||
<div className="flex flex-wrap gap-y-1 gap-x-4 text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<GraduationCap className="h-3.5 w-3.5" />
|
||||
<span>{textbook.grade || "Grade N/A"}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Building2 className="h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
<span className="line-clamp-1">{textbook.publisher || "Unknown Publisher"}</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Building2 className="h-3.5 w-3.5" />
|
||||
<span className="truncate max-w-[120px]" title={textbook.publisher || ""}>
|
||||
{textbook.publisher || "Publisher N/A"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Link>
|
||||
|
||||
<CardFooter className="p-4 pt-0 mt-auto">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground/80 bg-muted/30 px-2 py-1 rounded-md w-full">
|
||||
<BookOpen className="h-3.5 w-3.5" />
|
||||
<span>{textbook._count?.chapters || 0} Chapters</span>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Link>
|
||||
<CardFooter className="p-4 pt-2 mt-auto border-t bg-muted/20 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground tabular-nums">
|
||||
<BookOpen className="h-3.5 w-3.5" />
|
||||
<span>{textbook._count?.chapters || 0} Chapters</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-[10px] text-muted-foreground/60 mr-2">
|
||||
Updated {formatDate(textbook.updatedAt)}
|
||||
</span>
|
||||
{!hideActions && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-6 w-6 -mr-2">
|
||||
<MoreVertical className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">More options</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`${base}/${textbook.id}`}>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit Content
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="text-destructive focus:text-destructive">
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user