diff --git a/seeds.sql b/seeds.sql new file mode 100644 index 0000000..e69de29 diff --git a/server_tests.sql b/server_tests.sql new file mode 100644 index 0000000..11faa4f Binary files /dev/null and b/server_tests.sql differ diff --git a/src/app/dashboard/ai-coach/cbt-dpp/page.tsx b/src/app/dashboard/ai-coach/cbt-dpp/page.tsx index 2824658..5d2ff5c 100644 --- a/src/app/dashboard/ai-coach/cbt-dpp/page.tsx +++ b/src/app/dashboard/ai-coach/cbt-dpp/page.tsx @@ -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([]) const [attempts, setAttempts] = useState([]) const [loading, setLoading] = useState(true) const [dateFilter, setDateFilter] = useState("") - const [diffFilter, setDiffFilter] = useState("Mixed") const [selectedPdfDpp, setSelectedPdfDpp] = useState(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 ( -
- {/* Page Header */} -
-
-
-

+
+
+
+

PrepPilot AI

-

- Launch AI-generated Daily Practice Problems (DPPs) tailored to your exam pattern and weak areas. -

-
-
- - AI Powered - -
+

+ Hyper-personalized problem sets generated daily by our AI Evaluator to target your specific weak areas based on past exam performance. +

@@ -134,9 +126,21 @@ export default function AIDailyPracticePage() { {diff} ))} +
+ Pro +

-
@@ -190,12 +194,10 @@ export default function AIDailyPracticePage() {

{cleanTitle(dpp.title)} -

-
- + {dpp.difficulty} -
+
{count} Questions • {dpp.total_time} mins {dpp.scheduled_date && ( @@ -217,19 +219,15 @@ export default function AIDailyPracticePage() { {!isExpired && ( - - - + )} ) : ( - - - + )}
@@ -249,7 +247,7 @@ export default function AIDailyPracticePage() {

- {selectedPdfDpp.title} + {cleanTitle(selectedPdfDpp.title)} Protected In-App PDF View @@ -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() {

@@ -332,7 +330,7 @@ export default function AIDailyPracticePage() { diff --git a/src/app/dashboard/ai-coach/college-predictor/page.tsx b/src/app/dashboard/ai-coach/college-predictor/page.tsx index 998b583..ab6a25c 100644 --- a/src/app/dashboard/ai-coach/college-predictor/page.tsx +++ b/src/app/dashboard/ai-coach/college-predictor/page.tsx @@ -1,39 +1,45 @@ "use client" import { useState, useEffect } from "react" -import { CollegePredictorCard } from "@/components/AI/CollegePredictorCard" -import { Card, CardContent } from "@/components/ui/card" -import { GraduationCap, Building2, Award, CheckCircle2, TrendingUp, MapPin, Users, BarChart2 } from "lucide-react" -import Link from "next/link" +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" +import { GraduationCap, BrainCircuit, LineChart as LineChartIcon } from "lucide-react" +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' +import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser" -const DEMO_IDS = new Set([ - "11111111-1111-1111-1111-111111111111", - "22222222-2222-2222-2222-222222222222", - "33333333-3333-3333-3333-333333333333", - "44444444-4444-4444-4444-444444444444" -]); - -function isDemoUser(id: string): boolean { - return !!id && DEMO_IDS.has(id); -} - -const CUTOFF_TRENDS = [ - { year: "2021", rank: 3800 }, - { year: "2022", rank: 3500 }, - { year: "2023", rank: 3200 }, - { year: "2024", rank: 2950 }, - { year: "2025", rank: 2750 }, +const DEMO_TREND_DATA = [ + { name: 'Week 1', rank: 4500, label: 'NIT Trichy CSE' }, + { name: 'Week 2', rank: 4100 }, + { name: 'Week 3', rank: 3800 }, + { name: 'Week 4', rank: 3300 }, + { name: 'Week 5', rank: 2900 }, + { name: 'Week 6', rank: 2750, label: 'IIT Roorkee' }, ] +const ZERO_TREND_DATA = [ + { name: 'Week 1', rank: 0 }, + { name: 'Week 2', rank: 0 }, + { name: 'Week 3', rank: 0 }, + { name: 'Week 4', rank: 0 }, + { name: 'Week 5', rank: 0 }, + { name: 'Week 6', rank: 0 }, +] + +// Custom Label to permanently show text on the graph (not on hover) +const CustomLabel = (props: any) => { + const { x, y, stroke, value, index, data } = props; + const point = data[index]; + if (!point || !point.label) return null; + return ( + + {point.label} + + ); +}; + export default function CollegePredictorPage() { - const [isDemo, setIsDemo] = useState(false); - useEffect(() => { - if (typeof window !== 'undefined') { - const id = localStorage.getItem('activeUserId'); - setIsDemo(isDemoUser(id ?? '')); - } - }, []); - const maxRank = Math.max(...CUTOFF_TRENDS.map(d => d.rank)) + const { userId } = useActiveUser() + const isDemo = isDemoUser(userId) + const trendData = isDemo ? DEMO_TREND_DATA : ZERO_TREND_DATA return (
@@ -45,10 +51,10 @@ export default function CollegePredictorPage() { - AI College & Cutoff Predictor + College Predictor

- Discover your probable target medical & engineering colleges based on current mock exam scores and historical cutoffs. + Discover your probable target medical & engineering colleges based on current mock exam scores and historical cutoffs.

@@ -57,97 +63,69 @@ export default function CollegePredictorPage() { - {/* Summary KPI Strip */} -
- {[ - { label: "Predicted Rank", value: isDemo ? "~4,200" : "--", icon: , color: "border-[var(--cyan-ai-20)] bg-[var(--cyan-ai-10)]" }, - { label: "Target Colleges", value: isDemo ? "12" : "0", icon: , color: "border-amber-400/20 bg-amber-400/5" }, - { label: "Reach Colleges", value: isDemo ? "5" : "0", icon: , color: "border-purple-400/20 bg-purple-400/5" }, - { label: "Safe Colleges", value: isDemo ? "18" : "0", icon: , color: "border-green-400/20 bg-green-400/5" }, - ].map((kpi) => ( - - - {kpi.icon} -
{kpi.value}
-
{kpi.label}
-
-
- ))} -
- -
-
- {/* College Predictor */} - - - {/* Cutoff Trend Chart */} - -
- -

NIT Trichy CSE — Closing Rank Trend

- - Improving Cutoffs - -
-
- {CUTOFF_TRENDS.map((d, i) => ( -
- {d.rank.toLocaleString()} -
- {d.year} -
- ))} -
-

Closing rank trend: cutoffs are improving, meaning competition is easing. Your predicted rank of ~4,200 is within Target range.

- -
+
+ {/* Left Box: Graph */} + + + College & Branch Legacy Prediction + + {isDemo ? "Your projected admission chances mapped over your prep weeks." : "Your college trajectory will plot here as you complete tests."} + + + + + + {/* User said static line, not dotted. So removed strokeDasharray from grid? Let's make it solid if they mean grid, or just the line. Line is monotone. */} + + + + [`Rank: ${value}`, 'Prediction']} + /> + } + /> + + + + + {/* Right Box: AI Analysis (Text Only) */}
- -

- Cutoff Modelling Parameters -

-
    -
  • - - Trained on 100,000+ candidate rank trends across 2021–2025 counselling rounds. -
  • -
  • - - Supports All India Quota (AIQ) & State Quota seat adjustments. -
  • -
  • - - Simulates category reservation cutoffs: General, OBC, SC, ST, EWS. -
  • -
  • - - Score updated daily as you complete more mock tests. -
  • -
-
- - -

- Improve Your Rank -

-
    -
  • - +15 marks - in Maths → upgrades you from NIT Trichy to IIT Roorkee -
  • -
  • - Fix Optics - errors to recover ~8 marks in Physics alone -
  • -
  • - Boost Speed - from 45s → 38s/Q gives you ~12 extra attempt opportunities -
  • -
+ + + AI Analysis + + + {isDemo ? ( +
    +
  • + +15 marks + in Maths → upgrades you from NIT Trichy to IIT Roorkee +
  • +
  • + Fix Optics + errors to recover ~8 marks in Physics alone +
  • +
  • + Boost Speed + from 45s ? 38s/Q gives you ~12 extra attempt opportunities +
  • +
+ ) : ( +

+ Complete your first diagnostic test or DPP to unlock personalized AI college predictions and target recommendations. +

+ )} +
diff --git a/src/app/dashboard/dpp/page.tsx b/src/app/dashboard/dpp/page.tsx index d909703..935fbe3 100644 --- a/src/app/dashboard/dpp/page.tsx +++ b/src/app/dashboard/dpp/page.tsx @@ -1,20 +1,21 @@ -"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 } from "lucide-react" +import { BrainCircuit, Play, CheckCircle2, Download, Filter, Lock, FileText, Eye, ShieldAlert, X, Sparkles, Share2, Maximize2, Minimize2, Zap } from "lucide-react" import { useEffect, useState } from "react" import Link from "next/link" import { useActiveUser } from "@/hooks/useActiveUser" -export default function DPPPage() { +export default function AIDailyPracticePage() { const { user, userId } = useActiveUser() const [dpps, setDpps] = useState([]) const [attempts, setAttempts] = useState([]) const [loading, setLoading] = useState(true) const [dateFilter, setDateFilter] = useState("") + const [selectedPdfDpp, setSelectedPdfDpp] = useState(null) const [isFullScreen, setIsFullScreen] = useState(false) const [copiedShare, setCopiedShare] = useState(false) @@ -73,10 +74,7 @@ export default function DPPPage() { const scheduledDate = new Date(dpp.scheduled_date).toISOString().split('T')[0]; if (scheduledDate !== dateFilter) 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,13 +95,21 @@ export default function DPPPage() { const cleanTitle = (title: string) => title.replace(/\s*\(ExamEnum\.[^)]+\)/, ''); return ( -
-
-
-

Daily Practice Problems (DPP)

-

- Hyper-personalized problem sets generated daily by our AI Evaluator to target your specific weak areas based on past exam performance. -

+
+ {/* Page Header */} +
+
+
+

+ + + Daily Practice (DPP) +

+

+ Launch Daily Practice Problems (DPPs) tailored to your exam pattern. +

+
+
@@ -115,27 +121,10 @@ export default function DPPPage() { onChange={(e) => setDateFilter(e.target.value)} className="px-3 py-1.5 bg-background border border-border rounded-lg text-xs font-bold focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none text-muted-foreground mr-2" /> - Difficulty: - {['Mixed', 'Easy', 'Medium', 'Hard'].map((diff) => ( - - ))} -
- Pro -
+
-
@@ -189,10 +178,12 @@ export default function DPPPage() {

{cleanTitle(dpp.title)} - +

+
+ {dpp.difficulty} - +
{count} Questions • {dpp.total_time} mins {dpp.scheduled_date && ( @@ -214,15 +205,19 @@ export default function DPPPage() { {!isExpired && ( - + + + )} ) : ( - + + + )}
@@ -242,7 +237,7 @@ export default function DPPPage() {

- {cleanTitle(selectedPdfDpp.title)} + {selectedPdfDpp.title} Protected In-App PDF View @@ -257,7 +252,7 @@ export default function DPPPage() { size="sm" variant="outline" onClick={() => { - navigator.clipboard.writeText(window.location.origin + '/dashboard/dpp'); + navigator.clipboard.writeText(window.location.origin + '/dashboard/ai-coach/cbt-dpp'); setCopiedShare(true); setTimeout(() => setCopiedShare(false), 2000); }} @@ -291,7 +286,7 @@ export default function DPPPage() {

@@ -325,7 +320,7 @@ export default function DPPPage() {
diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index fa80736..dc40dc6 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -144,7 +144,14 @@ export default function DashboardLayout({ children }: { children: ReactNode }) { )} } label="Overview" active={pathname === "/dashboard"} /> - } label="Leaderboard" active={pathname.startsWith("/dashboard/leaderboard")} isLocked={!hasAccessTo("chapter-wise-progress-tracking")} /> + } + label="Daily Practice (DPP)" + active={pathname.startsWith("/dashboard/dpp")} + isLocked={!hasAccessTo("chapter-wise-progress-tracking")} + /> - {/* } - label="Daily Practice (DPP)" - active={pathname.startsWith("/dashboard/dpp")} - isLocked={!hasAccessTo("chapter-wise-progress-tracking")} - /> */} - + } label="Leaderboard" active={pathname.startsWith("/dashboard/leaderboard")} isLocked={!hasAccessTo("chapter-wise-progress-tracking")} />