chore: remove parent dashboard UI and update PRD tags

This commit is contained in:
2026-08-13 11:41:52 +05:30
parent ba583836e3
commit b152f73c3d
2 changed files with 18 additions and 246 deletions

42
PRD.md
View File

@@ -78,40 +78,34 @@ The stack is orchestrated using `docker-compose.yml` and consists of 8 distinct
The backend exposes the following RESTful APIs powered by FastAPI:
**Authentication**
- `POST /api/auth/register` - Create new student account
- `POST /api/auth/login` - Authenticate & receive JWT session
- `POST /api/auth/forgot-password` - Request password reset link
- `POST /api/auth/otp-request` / `otp-verify` - Phone number authentication
- `[Built] POST /api/auth/register` - Create new student account
- `[Built] POST /api/auth/login` - Authenticate & receive JWT session
- `[Built] POST /api/auth/forgot-password` - Request password reset link
- `[Planned] POST /api/auth/otp-request` / `otp-verify` - Phone number authentication
**Users & Profiles**
- `GET/PUT /api/users/{id}` - Fetch/update user profiles and settings
- `POST /api/users/{id}/avatar` - Upload profile picture to MinIO
- `GET /api/users/{id}/attempts` - Retrieve past test history
- `[Built] GET/PUT /api/users/{id}` - Fetch/update user profiles and settings
- `[Planned] POST /api/users/{id}/avatar` - Upload profile picture to MinIO
- `[Built] GET /api/users/{id}/attempts` - Retrieve past test history
**CBT Engine**
- `GET /api/tests` - List active Mock Tests, PYQs, and Weekly Tests
- `GET /api/tests/{id}/questions` - Fetch test sections and questions
- `POST /api/attempts/submit` - Auto-grade and record a CBT session
- `[Built] GET /api/tests` - List active Mock Tests, PYQs, and Weekly Tests
- `[Built] GET /api/tests/{id}/questions` - Fetch test sections and questions
- `[Built] POST /api/attempts/submit` - Auto-grade and record a CBT session
**AI Coach**
- `GET /api/ai-coach/readiness/{id}` - Predict JEE/NEET rank and college
- `GET /api/ai-coach/study-plan/today` - Generate daily tasks
- `GET /api/mistakes/today` - Fetch spaced repetition flashcards (SM-2 algorithm)
- `POST /api/mistakes/review` - Update mistake mastery based on recall
- `POST /api/ai-coach/chat/stream` - AI Tutor socket connection
- `[Planned] GET /api/ai-coach/readiness/{id}` - Predict JEE/NEET rank and college
- `[Planned] GET /api/ai-coach/study-plan/today` - Generate daily tasks
- `[Planned] GET /api/mistakes/today` - Fetch spaced repetition flashcards (SM-2 algorithm)
- `[Planned] POST /api/mistakes/review` - Update mistake mastery based on recall
- `[Built] POST /api/ai-coach/chat/stream` - AI Tutor socket connection
**Subscription & Billing (Gateway Agnostic)**
- `POST /api/billing/create-order` - Generate payment gateway order IDs
- `POST /api/billing/verify-payment` - Cryptographically verify webhooks and upgrade tier
- `[Built] POST /api/billing/create-order` - Generate payment gateway order IDs
- `[Built] POST /api/billing/verify-payment` - Cryptographically verify webhooks and upgrade tier
**Admin System**
- `GET/POST/PUT/DELETE /api/admin/*` - Full CRUD for Tests, Users, Coupons, and System Settings
**Parent System (Planned / UI Mocked)**
- *Note: The UI exists at `/parent/dashboard` but currently uses fallback mock data. Backend APIs are not yet built.*
- `[Planned] POST /api/parent/register` - Parent account creation
- `[Planned] POST /api/parent/link-student` - Link student to parent dashboard
- `[Planned] GET /api/parent/{parent_id}/dashboard/{student_id}` - Fetch real-time student analytics
- `[Built] GET/POST/PUT/DELETE /api/admin/*` - Full CRUD for Tests, Users, Coupons, and System Settings
## 5. Technology Stack Summary
- **Frontend:** Next.js 14/15, TailwindCSS, Framer Motion, Lucide Icons, Shadcn UI.

View File

@@ -1,222 +0,0 @@
"use client"
import { useState, useEffect } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Switch } from "@/components/ui/switch"
import { Label } from "@/components/ui/label"
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
import { Activity, Clock, Flame, AlertTriangle, RefreshCw } from "lucide-react"
import { useActiveUser } from "@/hooks/useActiveUser"
// Hardcoded mock UUIDs for this sprint
const PARENT_ID = "parent-123"
const DICTIONARY = {
en: {
title: "Parent Monitoring Portal",
subtitle: "Real-time analytics for your child's learning journey.",
lang_toggle: "हिंदी",
time_spent: "Weekly Time Spent",
minutes: "Minutes",
streak: "Active Streak",
days: "Days",
tests_attempted: "Tests Attempted",
tests: "This week",
accuracy_trend: "7-Day Accuracy Trend",
areas_of_concern: "Areas of Concern",
attention_needed: "Attention Needed",
pending_errors: "pending errors",
loading: "Loading child data...",
no_concerns: "No current areas of concern. Great job!"
},
hi: {
title: "अभिभावक निगरानी पोर्टल",
subtitle: "आपके बच्चे की सीखने की यात्रा के लिए वास्तविक समय विश्लेषिकी।",
lang_toggle: "English",
time_spent: "साप्ताहिक समय व्यतीत",
minutes: "मिनट",
streak: "सक्रिय लकीर",
days: "दिन",
tests_attempted: "परीक्षण का प्रयास",
tests: "इस सप्ताह",
accuracy_trend: "7-दिवसीय सटीकता प्रवृत्ति",
areas_of_concern: "चिंता के विषय",
attention_needed: "ध्यान देने की आवश्यकता है",
pending_errors: "लंबित त्रुटियां",
loading: "बच्चे का डेटा लोड हो रहा है...",
no_concerns: "चिंता का कोई विषय नहीं है। बहुत बढ़िया!"
}
}
export default function ParentDashboard() {
const { userId, loading: userLoading } = useActiveUser()
const [lang, setLang] = useState<'en' | 'hi'>('en')
const [data, setData] = useState<any>(null)
const [loading, setLoading] = useState(true)
const t = DICTIONARY[lang]
useEffect(() => {
if (userLoading || !userId) return;
async function fetchDashboard() {
try {
const res = await fetch(`/api/parent/${PARENT_ID}/dashboard/${userId}`)
if (!res.ok) throw new Error("Failed to fetch")
const payload = await res.json()
setData(payload)
} catch (err) {
console.error(err)
// Fallback mock data if server fails
setData({
student_name: "Rahul",
streak_count: 5,
weekly_minutes_spent: 124.5,
total_tests_attempted_this_week: 3,
accuracy_trend: [
{ date: "Jul 01", accuracy: 40 },
{ date: "Jul 02", accuracy: 55 },
{ date: "Jul 03", accuracy: 50 },
{ date: "Jul 04", accuracy: 70 },
{ date: "Jul 05", accuracy: 65 },
{ date: "Jul 06", accuracy: 80 },
{ date: "Jul 07", accuracy: 85 }
],
areas_of_concern: [
{ topic: "Thermodynamics", pending_errors: 4 },
{ topic: "Integration", pending_errors: 2 }
]
})
} finally {
setLoading(false)
}
}
fetchDashboard()
}, [userId, userLoading])
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<RefreshCw className="animate-spin text-muted-foreground mr-3" /> {t.loading}
</div>
)
}
return (
<div className="p-8 max-w-6xl mx-auto space-y-10 animate-in fade-in duration-500">
{/* Header & Language Toggle */}
<header className="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-border pb-6">
<div>
<h1 className="text-3xl font-bold tracking-tight text-primary">
{t.title}
</h1>
<p className="text-muted-foreground mt-1 font-medium">{t.subtitle} — {data?.student_name}</p>
</div>
{/*
<div className="flex items-center space-x-3">
<Label htmlFor="lang-switch" className="font-bold text-muted-foreground">{lang === 'hi' ? 'English' : 'हिंदी'}</Label>
<Switch
id="lang-switch"
checked={lang === 'hi'}
onCheckedChange={(c) => setLang(c ? 'hi' : 'en')}
/>
</div>
*/}
</header>
{/* Metric Blocks */}
<section className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card className="border-border shadow-sm bg-secondary/10">
<CardContent className="p-6 flex items-center gap-5">
<div className="p-4 bg-primary/10 rounded-2xl text-primary"><Clock size={28}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">{t.time_spent}</p>
<h3 className="text-3xl font-black mt-1">{data?.weekly_minutes_spent} <span className="text-sm font-semibold text-muted-foreground lowercase">{t.minutes}</span></h3>
</div>
</CardContent>
</Card>
<Card className="border-border shadow-sm bg-secondary/10">
<CardContent className="p-6 flex items-center gap-5">
<div className="p-4 bg-amber-500/10 rounded-2xl text-amber-500"><Flame size={28}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">{t.streak}</p>
<h3 className="text-3xl font-black mt-1">{data?.streak_count} <span className="text-sm font-semibold text-muted-foreground lowercase">{t.days}</span></h3>
</div>
</CardContent>
</Card>
<Card className="border-border shadow-sm bg-secondary/10">
<CardContent className="p-6 flex items-center gap-5">
<div className="p-4 bg-emerald-500/10 rounded-2xl text-emerald-500"><Activity size={28}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">{t.tests_attempted}</p>
<h3 className="text-3xl font-black mt-1">{data?.total_tests_attempted_this_week} <span className="text-sm font-semibold text-muted-foreground lowercase">{t.tests}</span></h3>
</div>
</CardContent>
</Card>
</section>
{/* Main Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Chart */}
<Card className="lg:col-span-2 border-border shadow-sm">
<CardHeader>
<CardTitle className="text-lg">{t.accuracy_trend}</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data?.accuracy_trend}>
<CartesianGrid strokeDasharray="3 3" stroke="#333" opacity={0.2} />
<XAxis dataKey="date" stroke="#888" fontSize={12} tickLine={false} axisLine={false} />
<YAxis stroke="#888" fontSize={12} tickLine={false} axisLine={false} domain={[0, 100]} tickFormatter={(val) => `${val}%`} />
<Tooltip
contentStyle={{ backgroundColor: 'hsl(var(--background))', borderColor: 'hsl(var(--border))', borderRadius: '8px' }}
itemStyle={{ color: 'hsl(var(--primary))' }}
/>
<Line
type="monotone"
dataKey="accuracy"
stroke="hsl(var(--primary))"
strokeWidth={4}
dot={{ r: 4, fill: 'hsl(var(--primary))' }}
activeDot={{ r: 8 }}
/>
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
{/* Areas of Concern */}
<Card className="border-border shadow-sm">
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<AlertTriangle className="text-red-500" size={20} />
{t.areas_of_concern}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{!data?.areas_of_concern || data.areas_of_concern.length === 0 ? (
<div className="text-center p-6 bg-secondary/20 rounded-xl">
<p className="text-muted-foreground font-medium">{t.no_concerns}</p>
</div>
) : (
data.areas_of_concern.map((concern: any, idx: number) => (
<div key={idx} className="p-4 rounded-xl border border-red-500/20 bg-red-500/5 relative overflow-hidden group">
<div className="absolute left-0 top-0 bottom-0 w-1 bg-red-500 rounded-l-xl"></div>
<h4 className="font-bold text-foreground mb-1">{concern.topic}</h4>
<p className="text-xs font-semibold text-red-500 uppercase tracking-wider">
{t.attention_needed} • {concern.pending_errors} {t.pending_errors}
</p>
</div>
))
)}
</CardContent>
</Card>
</div>
</div>
)
}