"use client" import { fetchWithAuth } from "@/lib/apiClient"; import { useEffect, useState } from "react" import { useParams, useRouter } from "next/navigation" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { ArrowLeft, Loader2, Mail, Phone, Target, Award, Calendar, Activity, BookOpen, Flame, TrendingUp, CheckCircle2, AlertCircle, RefreshCw, Edit3, Save, X } from "lucide-react" const TIER_STYLE: Record = { "AI Coach": "bg-[var(--cyan-ai-10)] text-[var(--cyan-ai)] border-[var(--cyan-ai-20)]", "Pro": "bg-yellow-500/10 text-yellow-500 border-yellow-500/20", "Free": "bg-secondary text-foreground border-border", } const TIER_LABEL: Record = { "AI Coach": "AI Coach", "Pro": "Pro", "Free": "Free" } function formatDate(iso?: string | null) { if (!iso) return "—" try { return new Date(iso).toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" }) } catch { return iso } } function formatAmount(paise: number) { return `₹${(paise / 100).toLocaleString("en-IN")}` } export default function StudentKundliPage() { const params = useParams() const router = useRouter() const studentId = params.studentId as string const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [data, setData] = useState(null) const [error, setError] = useState("") const [editing, setEditing] = useState(false) // editable fields const [editName, setEditName] = useState("") const [editTier, setEditTier] = useState("") const [editStreak, setEditStreak] = useState(0) const [editExam, setEditExam] = useState("") const [trialDays, setTrialDays] = useState(15) const [grantingTrial, setGrantingTrial] = useState(false) useEffect(() => { fetchWithAuth(`/api/admin/students/${studentId}`) .then(r => { if (!r.ok) throw new Error("Not found"); return r.json() }) .then(d => { setData(d) setEditName(d.name) setEditTier(d.tier) setEditStreak(d.streak_count) setEditExam(d.target_exam) }) .catch(e => setError(e.message)) .finally(() => setLoading(false)) }, [studentId]) const handleSave = async () => { setSaving(true) try { const res = await fetchWithAuth(`/api/admin/students/${studentId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: editName, tier: editTier, streak_count: editStreak, target_exam: editExam, }), }) if (!res.ok) throw new Error("Failed to save") // Refresh data const fresh = await fetchWithAuth(`/api/admin/students/${studentId}`).then(r => r.json()) setData(fresh) setEditing(false) } catch (e: any) { alert(e.message) } finally { setSaving(false) } } if (loading) return (
) if (error || !data) return (

Student not found or failed to load.

) return (
{/* Header */}
{!editing ? ( ) : (
)}
{/* Profile Header Card */}
{/* Avatar */}
{data.profile_image_url ? ( {data.name} ) : (
{data.name.charAt(0)}
)} {data.name === "Neha Verma" && ( DEMO )}
{/* Name + meta */}
{editing ? ( setEditName(e.target.value)} className="text-2xl font-bold bg-secondary border border-border rounded-lg px-3 py-1.5 w-full max-w-sm focus:outline-none focus:ring-1 focus:ring-primary mb-3" /> ) : (

{data.name}

)}
{editing ? ( ) : ( {TIER_LABEL[data.tier] || data.tier} )} {editing ? ( ) : ( {data.target_exam === "JEE_Main" ? "JEE" : data.target_exam.replace("_", " ")} )}
{data.email && {data.email}} {data.phone && {data.phone}} {data.school && {data.school}} Joined {formatDate(data.created_at)} {data.subscription_end && ( Active till {formatDate(data.subscription_end)} )}
{/* Stats Grid */}
{[ { label: "Tests Attempted", value: data.stats.total_tests, color: "text-[var(--cyan-ai)]", icon: }, { label: "Avg Accuracy", value: data.stats.avg_accuracy ? `${data.stats.avg_accuracy}%` : "—", color: "text-green-500", icon: }, { label: "AI Sessions", value: data.stats.mastered_mistakes, color: "text-purple-400", icon: }, { label: "Current Streak", value: editing ? ( setEditStreak(Number(e.target.value))} className="text-2xl font-black w-20 bg-secondary border border-border rounded px-2 py-0.5 focus:outline-none text-orange-500" /> ) : ( {data.streak_count} ), color: "text-orange-500", icon: }, ].map((stat, i) => (
{stat.icon}
{stat.value}
{stat.label}
))}
{/* Recent Attempts + Weak Topics */}
Recent Activity {data.recent_attempts.length === 0 ? (

No tests attempted yet.

) : (
{data.recent_attempts.map((a: any) => (
{a.name}
{formatDate(a.submitted_at)}
{a.score}/{a.total_questions}
{a.accuracy.toFixed(1)}% acc
))}
)}
Weak Topics {data.weak_topics.length === 0 ? (

No weak topics identified yet.

) : (
    {data.weak_topics.map((topic: string, i: number) => (
  • {topic}
  • ))}
)}
{/* Subscriptions */} {data.subscriptions.length > 0 && ( Subscription History
{data.subscriptions.map((s: any) => ( ))}
Plan Billing Start End Amount Status
{TIER_LABEL[s.tier] || s.tier} {s.billing_cycle} {formatDate(s.start_date)} {formatDate(s.end_date)} {formatAmount(s.amount)} {s.status}
)} {/* Transactions */} {data.transactions.length > 0 && ( Payment History
{data.transactions.map((t: any) => ( ))}
ID Plan Date Amount Status
{t.id.slice(0, 12)}... {TIER_LABEL[t.tier] || t.tier} {formatDate(t.created_at)} ₹{t.amount.toLocaleString("en-IN")} {t.status}
)} {/* Trial Control Card */} Trial Access Control

{data.trial_end ? ( new Date() ? "text-green-500" : "text-red-500"}> {formatDate(data.trial_end)} ({new Date(data.trial_end) > new Date() ? "Active" : "Expired"}) ) : ( No trial set )}

setTrialDays(Number(e.target.value))} className="w-24 bg-background border border-border rounded-lg px-3 py-2 text-sm font-bold focus:outline-none focus:ring-2 focus:ring-amber-500" />

Set 0 to remove trial. Trial starts from now.

) }