feat: apply various dashboard and auth fixes including demo trials, settings UI, and signup options
This commit is contained in:
@@ -4,7 +4,6 @@ import { fetchWithAuth } from "@/lib/apiClient";
|
||||
import { useEffect, useState } from "react"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { RefreshCw, TrendingUp, MapPin, Star } from "lucide-react"
|
||||
import { useActiveUser } from "@/hooks/useActiveUser"
|
||||
|
||||
const MOCK_COLLEGES_JEE: Record<string, Array<{ rank: number; name: string; branch: string; match: number; location: string; type: string; starred?: boolean }>> = {
|
||||
Reach: [
|
||||
@@ -56,22 +55,28 @@ const MATCH_COLOR = (match: number) => {
|
||||
export function CollegePredictorCard() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [activeTab, setActiveTab] = useState("Target")
|
||||
const { userId, user, loading: userLoading } = useActiveUser()
|
||||
const [isNeet, setIsNeet] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (userLoading || !userId) return
|
||||
// Read userId and target exam from localStorage to avoid SSR issues
|
||||
const userId = typeof window !== 'undefined' ? localStorage.getItem('activeUserId') : null;
|
||||
if (!userId) { setLoading(false); return; }
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
const res = await fetchWithAuth(`/api/ai-coach/readiness/${userId}`)
|
||||
if (!res.ok) throw new Error("fallback")
|
||||
const res = await fetchWithAuth(`/api/users/${userId}`);
|
||||
if (res.ok) {
|
||||
const userData = await res.json();
|
||||
setIsNeet(userData?.target_exam === 'NEET');
|
||||
}
|
||||
} catch {
|
||||
// Use mock data — no-op
|
||||
// Use default (JEE) on error
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [userId, userLoading])
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -81,7 +86,6 @@ export function CollegePredictorCard() {
|
||||
)
|
||||
}
|
||||
|
||||
const isNeet = user?.target_exam === "NEET";
|
||||
const colleges = (isNeet ? MOCK_COLLEGES_NEET[activeTab] : MOCK_COLLEGES_JEE[activeTab]) || []
|
||||
|
||||
const TAB_COLORS: Record<string, string> = {
|
||||
|
||||
55
src/components/PremiumGate.tsx
Normal file
55
src/components/PremiumGate.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useActiveUser } from "@/hooks/useActiveUser"
|
||||
import { Lock, Sparkles, ArrowRight } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
|
||||
interface PremiumGateProps {
|
||||
featureKey: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function PremiumGate({ featureKey, children }: PremiumGateProps) {
|
||||
const { hasAccessTo, loading, user } = useActiveUser();
|
||||
|
||||
if (loading || !user) {
|
||||
return (
|
||||
<div className="flex-1 min-h-[400px] flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-10 h-10 border-4 border-[var(--cyan-ai-20)] border-t-[var(--cyan-ai)] rounded-full animate-spin"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasAccessTo(featureKey)) {
|
||||
return (
|
||||
<div className="flex-1 min-h-[60vh] flex items-center justify-center p-6">
|
||||
<div className="max-w-md w-full p-8 rounded-3xl bg-secondary/30 border border-border/50 text-center space-y-6 shadow-2xl backdrop-blur-xl relative overflow-hidden group">
|
||||
{/* Background Glow */}
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-1/2 bg-[var(--cyan-ai)] opacity-5 blur-[80px] pointer-events-none group-hover:opacity-10 transition-opacity duration-700"></div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 mx-auto rounded-2xl bg-[var(--cyan-ai-10)] flex items-center justify-center shadow-[0_0_20px_-5px_var(--cyan-ai)] border border-[var(--cyan-ai-20)] mb-4 animate-in zoom-in duration-500">
|
||||
<Lock className="text-[var(--cyan-ai)] w-8 h-8" />
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-black tracking-tight text-foreground">Premium Feature</h2>
|
||||
<p className="text-muted-foreground mt-2 font-medium">
|
||||
You need an active plan to access this exclusive tool.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link href="/upgrade" className="inline-flex items-center justify-center gap-2 w-full bg-[var(--cyan-ai)] text-black font-bold py-3.5 px-6 rounded-xl hover:bg-cyan-400 transition-all shadow-[0_0_20px_-5px_var(--cyan-ai)] hover:shadow-[0_0_30px_-5px_var(--cyan-ai)] group/btn">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
Upgrade Plan to Unlock
|
||||
<ArrowRight className="w-4 h-4 group-hover/btn:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
Reference in New Issue
Block a user