Redesign college predictor, swap DPP contents, reorder sidebar, fix imports, add sql dump

This commit is contained in:
2026-09-25 12:44:25 +05:30
parent a91abd5a2b
commit c99cfd9d5d
6 changed files with 188 additions and 218 deletions

View File

@@ -1,21 +1,20 @@
"use client"
"use client"
import { fetchWithAuth } from "@/lib/apiClient";
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { BrainCircuit, Play, CheckCircle2, Download, Filter, Lock, FileText, Eye, ShieldAlert, X, Sparkles, Share2, Maximize2, Minimize2, Zap } from "lucide-react"
import { BrainCircuit, Play, CheckCircle2, Download, Filter, Lock, FileText, Eye, ShieldAlert, X, Sparkles, Share2, Maximize2, Minimize2 } from "lucide-react"
import { useEffect, useState } from "react"
import Link from "next/link"
import { useActiveUser } from "@/hooks/useActiveUser"
export default function AIDailyPracticePage() {
export default function DPPPage() {
const { user, userId } = useActiveUser()
const [dpps, setDpps] = useState<any[]>([])
const [attempts, setAttempts] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const [dateFilter, setDateFilter] = useState("")
const [diffFilter, setDiffFilter] = useState("Mixed")
const [selectedPdfDpp, setSelectedPdfDpp] = useState<any | null>(null)
const [isFullScreen, setIsFullScreen] = useState(false)
const [copiedShare, setCopiedShare] = useState(false)
@@ -74,9 +73,10 @@ export default function AIDailyPracticePage() {
const scheduledDate = new Date(dpp.scheduled_date).toISOString().split('T')[0];
if (scheduledDate !== dateFilter) return false;
}
if (diffFilter !== "Mixed" && dpp.difficulty !== diffFilter) {
return false;
}
const isNeet = user?.target_exam === "NEET";
const title = dpp.title.toLowerCase();
if (isNeet && (title.includes("math") || title.includes("mth"))) return false;
if (!isNeet && title.includes("bio")) return false;
return true;
});
@@ -97,26 +97,18 @@ export default function AIDailyPracticePage() {
const cleanTitle = (title: string) => title.replace(/\s*\(ExamEnum\.[^)]+\)/, '');
return (
<div className="p-6 md:p-8 space-y-6 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
{/* Page Header */}
<div className="border-b border-border/50 pb-6">
<div className="flex items-start justify-between gap-4 flex-wrap">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-center justify-between p-8 bg-gradient-to-r from-secondary/50 to-background border border-border rounded-2xl shadow-sm gap-6">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
<span className="p-2.5 rounded-xl bg-cyan-50 dark:bg-[var(--cyan-ai-10)] border border-cyan-300/60 dark:border-[var(--cyan-ai-20)]">
<Zap className="text-cyan-700 dark:text-[var(--cyan-ai)]" size={28} />
</span>
PrepPilot AI
</h1>
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
Launch AI-generated Daily Practice Problems (DPPs) tailored to your exam pattern and weak areas.
</p>
</div>
<div className="flex items-center gap-2 shrink-0">
<span className="text-xs font-bold uppercase tracking-wider text-[var(--cyan-ai)] bg-cyan-50 dark:bg-[var(--cyan-ai-10)] border border-cyan-300/60 dark:border-[var(--cyan-ai-20)] px-3 py-1.5 rounded-full flex items-center gap-1.5">
<BrainCircuit size={12} /> AI Powered
</span>
</div>
<p className="text-muted-foreground font-medium max-w-xl">
Hyper-personalized problem sets generated daily by our AI Evaluator to target your specific weak areas based on past exam performance.
</p>
</div>
</div>
@@ -134,9 +126,21 @@ export default function AIDailyPracticePage() {
{diff}
</button>
))}
<div className="flex items-center gap-1 ml-2 text-xs text-muted-foreground bg-secondary/50 px-2 py-1 rounded-md">
<Lock size={12}/> Pro
</div>
</div>
<Button variant="outline" className="gap-2 rounded-xl text-sm font-bold">
<Download size={16} /> Export as PDF
<Button
variant="outline"
onClick={() => {
navigator.clipboard.writeText(window.location.origin + '/dashboard/dpp');
setCopiedShare(true);
setTimeout(() => setCopiedShare(false), 2000);
}}
className="gap-2 rounded-xl text-sm font-bold border-primary/30 hover:bg-primary/10 text-primary transition-all shadow-sm"
>
{copiedShare ? <CheckCircle2 size={16} className="text-green-500" /> : <Share2 size={16} />}
{copiedShare ? "Share Link Copied!" : "Share DPPs Link"}
</Button>
</div>
@@ -190,12 +194,10 @@ export default function AIDailyPracticePage() {
</div>
<h3 className="font-bold text-lg leading-tight group-hover:text-[var(--cyan-ai)] transition-colors">
{cleanTitle(dpp.title)}
</h3>
<div className="mt-1">
<span className={`text-[10px] uppercase px-2 py-0.5 rounded-md border font-bold tracking-wider ${getDifficultyColor(dpp.difficulty)}`}>
<span className={`ml-2 text-[10px] uppercase px-2 py-0.5 rounded-md border font-bold tracking-wider ${getDifficultyColor(dpp.difficulty)}`}>
{dpp.difficulty}
</span>
</div>
</h3>
<div className="text-sm text-muted-foreground mt-2 font-medium">
{count} Questions • {dpp.total_time} mins
{dpp.scheduled_date && (
@@ -217,19 +219,15 @@ export default function AIDailyPracticePage() {
<Button variant="outline" size="sm" className="font-bold border-border">Result</Button>
</Link>
{!isExpired && (
<Link href={`/cbt/${dpp.id}`}>
<Button variant="secondary" size="sm" className="font-bold bg-[var(--cyan-ai-10)] text-[var(--cyan-ai)] hover:bg-[var(--cyan-ai-20)] gap-2">
<Play size={14} className="fill-current" /> Retake Test
</Button>
</Link>
<Button onClick={() => setSelectedPdfDpp(dpp)} variant="secondary" size="sm" className="font-bold bg-[var(--cyan-ai-10)] text-[var(--cyan-ai)] hover:bg-[var(--cyan-ai-20)] gap-2">
<Eye size={14} /> Read PDF
</Button>
)}
</>
) : (
<Link href={`/cbt/${dpp.id}`}>
<Button variant="secondary" size="sm" className="rounded-xl bg-[var(--cyan-ai)] text-black hover:bg-[var(--cyan-ai-80)] font-extrabold gap-2 text-xs shadow-[0_0_12px_rgba(12,96,96,0.2)]">
<Play size={14} className="fill-black" /> Launch Practice Test
</Button>
</Link>
<Button onClick={() => setSelectedPdfDpp(dpp)} variant="secondary" className="rounded-xl bg-primary/10 text-primary hover:bg-primary/20 font-bold gap-2 text-xs">
<Eye size={14} /> Open PDF
</Button>
)}
</div>
</CardContent>
@@ -249,7 +247,7 @@ export default function AIDailyPracticePage() {
</div>
<div>
<h3 className="font-black text-base sm:text-lg flex items-center gap-2">
{selectedPdfDpp.title}
{cleanTitle(selectedPdfDpp.title)}
<span className="text-[10px] bg-yellow-500/10 text-yellow-500 border border-yellow-500/20 px-2 py-0.5 rounded-full uppercase tracking-wider font-extrabold hidden md:inline-block">
Protected In-App PDF View
</span>
@@ -264,7 +262,7 @@ export default function AIDailyPracticePage() {
size="sm"
variant="outline"
onClick={() => {
navigator.clipboard.writeText(window.location.origin + '/dashboard/ai-coach/cbt-dpp');
navigator.clipboard.writeText(window.location.origin + '/dashboard/dpp');
setCopiedShare(true);
setTimeout(() => setCopiedShare(false), 2000);
}}
@@ -298,7 +296,7 @@ export default function AIDailyPracticePage() {
</span>
<Link href="/upgrade" className="hidden sm:block shrink-0 ml-2">
<Button size="sm" className="bg-amber-500 text-black hover:bg-amber-400 font-extrabold h-7 text-[11px] rounded-lg">
Upgrade for Interactive Practice Mode →
Upgrade for Interactive CBT Mode →
</Button>
</Link>
</div>
@@ -332,7 +330,7 @@ export default function AIDailyPracticePage() {
</Button>
<Link href="/upgrade">
<Button className="bg-[var(--cyan-ai)] text-black hover:bg-[var(--cyan-ai-80)] font-extrabold rounded-xl shadow-[0_0_15px_rgba(12,96,96,0.2)] text-xs h-9">
<Sparkles size={14} className="mr-1.5" /> Upgrade to AI Coach
<Sparkles size={14} className="mr-1.5" /> Upgrade to AI Coach for CBT
</Button>
</Link>
</div>