feat: apply various dashboard and auth fixes including demo trials, settings UI, and signup options

This commit is contained in:
2026-09-23 16:38:49 +05:30
parent 86a2749360
commit 1d9cdd3f33
18 changed files with 435 additions and 147 deletions

View File

@@ -3,20 +3,48 @@ import { NextRequest } from 'next/server';
import { join } from 'path';
import { readFileSync } from 'fs';
export async function GET(req: NextRequest) {
export async function GET(req: NextRequest, { params }: { params: Promise<{ userId: string }> }) {
try {
const { searchParams } = new URL(req.url);
const { userId } = await params;
// Read metrics
const score = searchParams.get('score') || '0 / 0';
const accuracy = searchParams.get('accuracy') || '0%';
const qs = searchParams.get('qs') || '0';
const rank = searchParams.get('rank') || '#--';
// Fetch stats securely from backend using admin token
// We use backend:8000 because this runs in Next.js docker container
const backendUrl = process.env.NEXT_PUBLIC_API_URL?.includes('localhost')
? 'http://backend:8000'
: process.env.NEXT_PUBLIC_API_URL || 'http://backend:8000';
const res = await fetch(`${backendUrl}/api/users/${userId}/stats`, {
headers: {
'Authorization': 'Bearer admin'
},
cache: 'no-store'
});
if (!res.ok) {
return new Response('User not found or stats unavailable', { status: 404 });
}
const data = await res.json();
// Read metrics from backend response
const score = `${data.score} / ${data.total_marks || 300}`;
const accuracy = data.accuracy || '0%';
const qs = (data.total_questions || 0).toString();
const rank = data.city_rank ? `#${data.city_rank}` : '#--';
// Read user info
const name = searchParams.get('name') || 'Student';
const target = searchParams.get('target') || 'JEE';
const plan = searchParams.get('plan') || 'Free';
const name = data.name || 'Student';
// Format target correctly (e.g. JEE_MAIN -> JEE)
let target = data.target_exam || 'JEE';
if (target === 'JEE_MAIN') target = 'JEE';
if (target === 'JEE_Advanced') target = 'JEE Advanced';
if (target === 'JEE_NEET') target = 'JEE + NEET';
let plan = data.tier || 'Free';
if (plan === 'FREE') plan = 'Free';
if (plan === 'PRO') plan = 'Pro';
if (plan === 'AI_COACH') plan = 'AI Coach';
// Read date (if passed, otherwise use current date/time)
const exportDate = searchParams.get('date') || new Intl.DateTimeFormat('en-IN', {

View File

@@ -1,4 +1,4 @@
"use client"
"use client"
import { useEffect, useState } from "react"
import { useRouter, useSearchParams } from "next/navigation"

View File

@@ -1,11 +1,21 @@
"use client"
"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 { 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 },
@@ -16,8 +26,13 @@ const CUTOFF_TRENDS = [
]
export default function CollegePredictorPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
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))
return (

View File

@@ -0,0 +1,14 @@
import React from "react"
import { PremiumGate } from "@/components/PremiumGate"
export default function AICoachLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<PremiumGate featureKey="ai-tutor-chat">
{children}
</PremiumGate>
)
}

View File

@@ -1,4 +1,4 @@
"use client"
"use client"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { LineChart as LineChartIcon, Activity, BrainCircuit } from "lucide-react"
@@ -41,6 +41,8 @@ const ZERO_HEATMAP_DATA = [
{ topic: 'Inorganic', accuracy: 0 },
]
import { PremiumGate } from "@/components/PremiumGate"
export default function AnalyticsPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
@@ -55,13 +57,14 @@ export default function AnalyticsPage() {
}
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<span className="p-2.5 rounded-xl bg-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
<LineChartIcon className="text-yellow-600 dark:text-[var(--cyan-ai)]" size={28} />
</span>
<PremiumGate featureKey="detailed-performance-analytics">
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<span className="p-2.5 rounded-xl bg-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
<LineChartIcon className="text-yellow-600 dark:text-[var(--cyan-ai)]" size={28} />
</span>
Performance Analytics
</h1>
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
@@ -136,5 +139,6 @@ export default function AnalyticsPage() {
</div>
</div>
</div>
</PremiumGate>
)
}

View File

@@ -8,6 +8,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Activity, Clock, PlayCircle, Search, Lock, CheckSquare } from "lucide-react";
import { useActiveUser } from "@/hooks/useActiveUser";
import { PremiumGate } from "@/components/PremiumGate";
interface TestData {
id: string;
@@ -128,8 +129,9 @@ export default function CBTPage() {
}
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<PremiumGate featureKey="weekly-test-series">
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<span className="p-2.5 rounded-xl bg-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
@@ -358,5 +360,6 @@ export default function CBTPage() {
</div>
)}
</div>
</PremiumGate>
);
}

View File

@@ -1,9 +1,10 @@
"use client"
"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
import { MessageSquare, ThumbsUp, ChevronUp, Search, Image as ImageIcon, CheckCircle, Lock, X, Send } from "lucide-react"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
import { PremiumGate } from "@/components/PremiumGate"
interface Doubt {
id: string
@@ -113,8 +114,9 @@ export default function CommunityPage() {
}
return (
<div className="p-8 space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4">
<PremiumGate featureKey="doubt-resolution-community">
<div className="p-8 space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<span className="p-2.5 rounded-xl bg-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
@@ -328,8 +330,8 @@ export default function CommunityPage() {
</div>
</div>
)}
</div>
</PremiumGate>
)
}

View File

@@ -3,7 +3,7 @@
import { ReactNode, useState, useEffect, useRef } from "react"
import { BookOpen, LayoutDashboard, Settings, FileText, Activity, LineChart, Trophy, User, ChevronUp, Lock, ShieldAlert, Flame, Target, Gauge, Zap, Calendar, Clock, BrainCircuit, BarChart3, School, CheckSquare, MessageSquare } from "lucide-react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import { LanguageToggle } from "@/components/LanguageToggle"
import { Logo } from "@/components/Logo"
import { ThemeToggle } from "@/components/ThemeToggle"
@@ -15,6 +15,8 @@ import { StreakOnboardingModal } from "@/components/Dashboard/StreakOnboardingMo
import { Badge } from "@/components/ui/badge"
import MaintenanceScreen from "@/components/MaintenanceScreen"
export const dynamic = 'force-dynamic';
export default function DashboardLayout({ children }: { children: ReactNode }) {
const pathname = usePathname()
const [dropdownOpen, setDropdownOpen] = useState(false)
@@ -54,14 +56,27 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
const cleanDisplayName = (name: string) => name.replace(/\s*\([^)]*\)\s*$/, '').trim();
const userInitials = user ? cleanDisplayName(user.name).split(" ").filter(n => /^[a-zA-Z]/.test(n)).slice(0, 2).map(n => n[0]).join("").toUpperCase() : "DS"
const targetExamLabel = user?.target_exam === "JEE_Main" ? "JEE" : user?.target_exam === "JEE_Advanced" ? "JEE Advanced" : user?.target_exam === "NEET" ? "NEET" : "NEET/JEE"
const targetExamLabel = user?.target_exam === "JEE_Main" ? "JEE" : user?.target_exam === "JEE_Advanced" ? "JEE Advanced" : user?.target_exam === "NEET" ? "NEET" : "JEE + NEET"
// Trial logic removed as requested
const router = useRouter();
if (maintenance) {
return <MaintenanceScreen />;
}
// Redirect to login (or admin) if no user is found after loading
if (!loading && !switching && !user) {
if (typeof window !== 'undefined') {
const activeId = localStorage.getItem("activeUserId");
if (activeId === "admin") {
router.replace("/admin");
} else {
router.replace("/login");
}
}
return null;
}
if (loading || !user) {
return (
<div className="h-screen w-screen flex flex-col items-center justify-center bg-background">
@@ -103,20 +118,32 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
<nav className="flex-1 min-h-0 px-3 py-3 overflow-y-auto custom-scrollbar space-y-0.5">
{/* PRO TIER Section */}
<div className="px-2 pt-2 pb-2">
<div className="flex items-center gap-2">
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-yellow-500/40 to-transparent" />
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-yellow-600 dark:text-yellow-400 flex items-center gap-1.5 shrink-0">
<span className="w-1 h-1 rounded-full bg-yellow-500 shadow-[0_0_4px_#EAB308]"></span>
Pro Tier
</span>
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-yellow-500/40 to-transparent" />
{/* Section divider — only shows 'Pro Tier' label for users who have Pro or above */}
{hasAccessTo("chapter-wise-progress-tracking") ? (
<div className="px-2 pt-2 pb-2">
<div className="flex items-center gap-2">
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-yellow-500/40 to-transparent" />
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-yellow-600 dark:text-yellow-400 flex items-center gap-1.5 shrink-0">
<span className="w-1 h-1 rounded-full bg-yellow-500 shadow-[0_0_4px_#EAB308]"></span>
Pro Tier
</span>
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-yellow-500/40 to-transparent" />
</div>
</div>
</div>
) : (
<div className="px-2 pt-2 pb-2">
<div className="flex items-center gap-2">
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-border to-transparent" />
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-muted-foreground flex items-center gap-1.5 shrink-0">
Free Plan
</span>
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-border to-transparent" />
</div>
</div>
)}
<NavItem section="pro" href="/dashboard" icon={<LayoutDashboard size={17} />} label="Overview" active={pathname === "/dashboard"} />
<NavItem section="pro" href="/dashboard/leaderboard" icon={<Trophy size={17} />} label="Leaderboard" active={pathname.startsWith("/dashboard/leaderboard")} />
<NavItem section="pro" href="/dashboard/leaderboard" icon={<Trophy size={17} />} label="Leaderboard" active={pathname.startsWith("/dashboard/leaderboard")} isLocked={!hasAccessTo("chapter-wise-progress-tracking")} />
<NavItem
section="pro"
href="/dashboard/progress"
@@ -133,14 +160,6 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
active={pathname.startsWith("/dashboard/cbt")}
isLocked={!hasAccessTo("weekly-test-series")}
/>
{/* <NavItem
section="pro"
href="/dashboard/materials"
icon={<BookOpen size={17} />}
label="Quick Notes"
active={pathname.startsWith("/dashboard/materials")}
isLocked={!hasAccessTo("detailed-performance-analytics")}
/> */}
<NavItem
section="pro"
@@ -151,7 +170,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
isLocked={!hasAccessTo("doubt-resolution-community")}
/>
{/* AI COACH Section */}
{/* AI COACH Section always visible */}
<div className="px-2 pt-4 pb-2">
<div className="flex items-center gap-2">
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-cyan-500/50 to-transparent" />
@@ -244,6 +263,26 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
isLocked={!hasAccessTo("detailed-performance-analytics")}
/>
{(!hasAccessTo("ai-tutor-chat") || !hasAccessTo("chapter-wise-progress-tracking")) && (
<div className="px-3 pt-6 pb-2">
<div className="rounded-xl bg-gradient-to-br from-cyan-500/10 to-yellow-500/10 border border-border/50 p-4 text-center">
<div className="w-10 h-10 mx-auto bg-background rounded-full shadow-sm flex items-center justify-center mb-3">
<Lock size={18} className="text-muted-foreground" />
</div>
<h4 className="text-sm font-bold text-foreground mb-1">More Features Locked</h4>
<p className="text-xs text-muted-foreground mb-3">
Upgrade your plan to unlock the AI Coach and advanced Pro tools.
</p>
<Link
href="/dashboard/settings"
className="block w-full py-2 px-3 bg-primary text-primary-foreground text-xs font-bold rounded-lg shadow-sm hover:bg-primary/90 transition-colors"
>
Upgrade Now
</Link>
</div>
</div>
)}
</nav>
{/* Bottom Settings + Profile */}
@@ -275,10 +314,10 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
</Link>
{isDemoUser(userId) && (
<button
onClick={() => switchUser(DEMO_USERS.AICOACH_JEE.id)}
onClick={() => switchUser(userId, true)}
className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-secondary rounded-lg transition-colors"
>
Reset Demo
Reset Current Demo
</button>
)}
<div className="h-px bg-border my-1" />
@@ -303,7 +342,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
<div className="flex-1 min-w-0">
<p className="text-sm font-bold leading-none truncate">{user ? cleanDisplayName(user.name).split(" ")[0] : "Loading..."}</p>
<div className="mt-1">
<TierBadge tier={user ? (user.tier === "Al_Coach" ? "AI Coach" : user.tier) : "Free"} />
<TierBadge tier={user ? (user.tier === "Al_Coach" ? "AI Coach" : user.tier === "Pro" ? "Pro" : "Free") : "Free"} />
</div>
</div>
</div>
@@ -326,7 +365,6 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
{targetExamLabel} Target
</Badge>
)}
{/* Trial countdown badge removed from top header */}
</div>
<div className="flex items-center gap-4">
@@ -396,6 +434,8 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
}
function NavItem({ href, icon, label, active, isLocked, section }: { href: string, icon: React.ReactNode, label: string, active?: boolean, isLocked?: boolean, section?: "pro" | "ai" }) {
// We no longer return null for isLocked. We render it with a disabled appearance.
// Section-aware active color tokens (light mode only, dark mode falls back to cyan)
const activeClasses = active
? section === "pro"
@@ -403,8 +443,6 @@ function NavItem({ href, icon, label, active, isLocked, section }: { href: strin
: section === "ai"
? "bg-cyan-50 dark:bg-[var(--cyan-ai-10)] text-cyan-800 dark:text-[var(--cyan-ai)] font-semibold border border-cyan-300/60 dark:border-[var(--cyan-ai-20)] shadow-[0_0_12px_-4px_rgba(6,182,212,0.2)] dark:shadow-[0_0_12px_-4px_rgba(12,96,96,0.3)]"
: "bg-[var(--cyan-ai-10)] text-[var(--cyan-ai)] font-semibold border border-[var(--cyan-ai-20)] shadow-[0_0_12px_-4px_rgba(12,96,96,0.3)]"
: isLocked
? "text-muted-foreground/40 cursor-not-allowed border border-transparent"
: "text-muted-foreground/80 hover:bg-secondary/60 hover:text-foreground border border-transparent hover:border-border/40"
const iconActiveClass = active
@@ -413,8 +451,6 @@ function NavItem({ href, icon, label, active, isLocked, section }: { href: strin
: section === "ai"
? "text-cyan-700 dark:text-[var(--cyan-ai)]"
: "text-[var(--cyan-ai)]"
: isLocked
? "text-muted-foreground/30"
: "text-muted-foreground/60 group-hover:text-foreground/80"
const activeBarClass = active
@@ -427,8 +463,8 @@ function NavItem({ href, icon, label, active, isLocked, section }: { href: strin
return (
<Link
href={isLocked ? "#" : href}
className={`group flex items-center justify-between px-3 py-2 rounded-xl transition-all duration-200 ${activeClasses}`}
href={isLocked ? "/dashboard/settings" : href}
className={`group flex items-center justify-between px-3 py-2 rounded-xl transition-all duration-200 ${isLocked ? 'opacity-60 grayscale-[0.5]' : ''} ${activeClasses}`}
>
<div className="flex items-center gap-2.5">
<span className={`transition-all duration-200 ${iconActiveClass}`}>
@@ -436,8 +472,10 @@ function NavItem({ href, icon, label, active, isLocked, section }: { href: strin
</span>
<span className="text-[13px] font-medium tracking-[-0.01em]">{label}</span>
</div>
{isLocked && <Lock size={11} className="text-muted-foreground/30 shrink-0" />}
{active && <span className={activeBarClass}></span>}
<div className="flex items-center gap-1.5">
{isLocked && <Lock size={12} className="text-muted-foreground/70" />}
{!isLocked && active && <span className={activeBarClass}></span>}
</div>
</Link>
)
}

View File

@@ -59,7 +59,8 @@ export default function DashboardPage() {
const isDark = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
const themeParam = isDark ? 'dark' : 'light';
return `/api/og/dashboard?score=${encodeURIComponent(score)}&accuracy=${encodeURIComponent(accuracy)}&qs=${encodeURIComponent(qs)}&rank=${encodeURIComponent(rank)}&name=${encodeURIComponent(name)}&target=${target}&plan=${encodeURIComponent(plan)}&date=${encodeURIComponent(date)}&theme=${themeParam}`
if (!user) return "";
return `/api/og/dashboard/${user.id}?theme=${themeParam}&date=${encodeURIComponent(date)}`
}
const handleDownload = async () => {

View File

@@ -4,6 +4,7 @@ import { useState, useEffect } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { ChevronDown, ChevronRight, CheckCircle2, Circle, Clock, Activity, BarChart3 } from "lucide-react"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
import { PremiumGate } from "@/components/PremiumGate"
type ChapterStatus = 'Mastered' | 'Practiced' | 'In Progress' | 'Not Started'
@@ -215,9 +216,10 @@ export default function ProgressTrackerPage() {
}
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<PremiumGate featureKey="chapter-wise-progress-tracking">
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
<span className="p-2.5 rounded-xl bg-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
<BarChart3 className="text-yellow-600 dark:text-[var(--cyan-ai)]" size={28} />
@@ -305,5 +307,6 @@ export default function ProgressTrackerPage() {
))}
</div>
</div>
</PremiumGate>
)
}

View File

@@ -9,7 +9,7 @@ import { User as UserIcon, Upload, CheckCircle2, Settings } from "lucide-react";
export default function SettingsPage() {
const { userId, user, refreshUser, loading: userLoading } = useActiveUser();
const [targetExam, setTargetExam] = useState<"JEE_Main" | "JEE_Advanced" | "NEET">("JEE_Main");
const [targetExam, setTargetExam] = useState<"JEE_Main" | "JEE_Advanced" | "NEET" | "JEE_NEET">("JEE_Main");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [school, setSchool] = useState("");
@@ -263,45 +263,63 @@ export default function SettingsPage() {
<div className="flex gap-4">
<button
type="button"
onClick={() => setTargetExam("NEET")}
className={`flex items-center gap-2 cursor-pointer p-4 border rounded-xl flex-1 text-left transition-all ${
disabled
className={`flex items-center justify-center gap-2 p-4 border rounded-xl flex-1 text-center transition-all ${
targetExam === "NEET"
? "border-[var(--cyan-ai)] bg-[var(--cyan-ai-10)]"
: "border-border bg-secondary/20 hover:bg-secondary/50"
: "border-border bg-secondary/20 opacity-50 cursor-not-allowed"
}`}
>
<input
type="radio"
name="target"
checked={targetExam === "NEET"}
onChange={() => {}}
className="accent-[var(--cyan-ai)] w-4 h-4"
readOnly
className="accent-[var(--cyan-ai)] w-4 h-4 hidden"
/>
<span className="font-bold">NEET (UG)</span>
<span className="font-bold">NEET</span>
</button>
<button
type="button"
onClick={() => setTargetExam("JEE_Main")}
className={`flex items-center gap-2 cursor-pointer p-4 border rounded-xl flex-1 text-left transition-all ${
disabled
className={`flex items-center justify-center gap-2 p-4 border rounded-xl flex-1 text-center transition-all ${
targetExam === "JEE_Main"
? "border-[var(--cyan-ai)] bg-[var(--cyan-ai-10)]"
: "border-border bg-secondary/20 hover:bg-secondary/50"
: "border-border bg-secondary/20 opacity-50 cursor-not-allowed"
}`}
>
<input
type="radio"
name="target"
checked={targetExam === "JEE_Main"}
onChange={() => {}}
className="accent-[var(--cyan-ai)] w-4 h-4"
readOnly
className="accent-[var(--cyan-ai)] w-4 h-4 hidden"
/>
<span className="font-bold">JEE Main & Adv</span>
<span className="font-bold">JEE</span>
</button>
<button
type="button"
disabled
className={`flex items-center justify-center gap-2 p-4 border rounded-xl flex-1 text-center transition-all ${
targetExam === "JEE_NEET"
? "border-[var(--cyan-ai)] bg-[var(--cyan-ai-10)]"
: "border-border bg-secondary/20 opacity-50 cursor-not-allowed"
}`}
>
<input
type="radio"
name="target"
checked={targetExam === "JEE_NEET"}
readOnly
className="accent-[var(--cyan-ai)] w-4 h-4 hidden"
/>
<span className="font-bold">JEE + NEET</span>
</button>
</div>
</div>
<Button onClick={handleSave} disabled={saving} className="bg-foreground text-background hover:bg-foreground/90 font-bold mt-2 rounded-xl px-6">
{/* <Button onClick={handleSave} disabled={saving} className="bg-foreground text-background hover:bg-foreground/90 font-bold mt-2 rounded-xl px-6">
{saving ? "Saving..." : "Save Preferences"}
</Button>
</Button> */}
</CardContent>
</Card>
</div>

View File

@@ -231,9 +231,9 @@ export default function SignupPage() {
value={targetExam}
onChange={(e) => setTargetExam(e.target.value)}
className="w-full bg-background border border-border rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all cursor-pointer">
<option value="JEE_Main" className="bg-background text-foreground font-medium">JEE Main</option>
<option value="JEE_Advanced" className="bg-background text-foreground font-medium">JEE Advanced</option>
<option value="JEE_Main" className="bg-background text-foreground font-medium">JEE</option>
<option value="NEET" className="bg-background text-foreground font-medium">NEET</option>
<option value="JEE_NEET" className="bg-background text-foreground font-medium">JEE + NEET</option>
</select>
</div>
<div className="space-y-2">
@@ -259,7 +259,7 @@ export default function SignupPage() {
</select>
</div>
<div className="space-y-2">
<label className="text-sm font-bold text-foreground">School / Coaching Name <span className="text-muted-foreground font-normal">(Optional)</span></label>
<label className="text-sm font-bold text-foreground">Institute Name <span className="text-muted-foreground font-normal">(Optional)</span></label>
<input
type="text"
value={schoolName}