Implement active user tiers, class-based Tailwind theme switcher, 3-tiled static dashboard layout, Minio object storage integration, school & state settings database sync, and streak wishes onboarding modal

This commit is contained in:
2026-07-07 13:43:30 +05:30
parent 426b98ddab
commit 04db17ea0e
24 changed files with 913 additions and 236 deletions

View File

@@ -4,16 +4,19 @@ import { useEffect, useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { GraduationCap, MapPin, RefreshCw } from "lucide-react"
const USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"
import { useActiveUser } from "@/hooks/useActiveUser"
export function CollegePredictorCard() {
const [data, setData] = useState<any>(null)
const [loading, setLoading] = useState(true)
const { userId, loading: userLoading } = useActiveUser();
useEffect(() => {
if (userLoading || !userId) return;
async function fetchReadiness() {
try {
const res = await fetch(`http://localhost:8000/api/ai-coach/readiness/${USER_ID}`)
const res = await fetch(`/api/ai-coach/readiness/${userId}`)
if (!res.ok) throw new Error("Failed to fetch")
const json = await res.json()
setData(json)
@@ -28,7 +31,7 @@ export function CollegePredictorCard() {
}
}
fetchReadiness()
}, [])
}, [userId, userLoading])
if (loading) {
return (

View File

@@ -4,16 +4,19 @@ import { useEffect, useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Activity, RefreshCw } from "lucide-react"
const USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"
import { useActiveUser } from "@/hooks/useActiveUser"
export function ReadinessGauge() {
const [data, setData] = useState<any>(null)
const [loading, setLoading] = useState(true)
const { userId, loading: userLoading } = useActiveUser();
useEffect(() => {
if (userLoading || !userId) return;
async function fetchReadiness() {
try {
const res = await fetch(`http://localhost:8000/api/ai-coach/readiness/${USER_ID}`)
const res = await fetch(`/api/ai-coach/readiness/${userId}`)
if (!res.ok) throw new Error("Failed to fetch")
const json = await res.json()
setData(json)
@@ -34,7 +37,7 @@ export function ReadinessGauge() {
}
}
fetchReadiness()
}, [])
}, [userId, userLoading])
if (loading) {
return (

View File

@@ -5,16 +5,18 @@ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/com
import { Button } from "@/components/ui/button"
import { Link as LinkIcon, Share2, Check, Gift } from "lucide-react"
const USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"
import { useActiveUser } from "@/hooks/useActiveUser"
export function ReferralCard() {
const { userId, loading: userLoading } = useActiveUser();
const [data, setData] = useState<any>(null)
const [copied, setCopied] = useState(false)
useEffect(() => {
if (userLoading || !userId) return;
async function fetchReferral() {
try {
const res = await fetch(`http://localhost:8000/api/growth/referral/${USER_ID}`)
const res = await fetch(`/api/growth/referral/${userId}`)
if (res.ok) {
const json = await res.json()
setData(json)
@@ -24,9 +26,9 @@ export function ReferralCard() {
}
}
fetchReferral()
}, [])
}, [userId, userLoading])
const link = data?.referral_link || "https://pragativeer.com/join/demo"
const link = data?.referral_link || `https://pragativeer.com/join/${userId || "demo"}`
const message = data?.share_message || "Join me on PragatiVeer! Use my link to get 7 days of the AI Coach for free."
const copyToClipboard = () => {

View File

@@ -5,16 +5,19 @@ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/com
import { Badge } from "@/components/ui/badge"
import { CalendarClock, CheckCircle2, Circle, Clock, Loader2, PlayCircle, RefreshCw, XCircle } from "lucide-react"
const USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"
import { useActiveUser } from "@/hooks/useActiveUser"
export function StudyPlanCard() {
const [tasks, setTasks] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const { userId, loading: userLoading } = useActiveUser();
useEffect(() => {
if (userLoading || !userId) return;
async function fetchPlan() {
try {
const res = await fetch(`http://localhost:8000/api/ai-coach/study-plan/today?user_id=${USER_ID}`)
const res = await fetch(`/api/ai-coach/study-plan/today?user_id=${userId}`)
if (!res.ok) throw new Error("No plan")
const data = await res.json()
setTasks(data.tasks || [])
@@ -31,7 +34,7 @@ export function StudyPlanCard() {
}
}
fetchPlan()
}, [])
}, [userId, userLoading])
const toggleTask = (idx: number, newStatus: string) => {
setTasks(prev => {

View File

@@ -10,7 +10,7 @@ import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import "katex/dist/katex.min.css"
const USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"
import { useActiveUser } from "@/hooks/useActiveUser"
interface Message {
role: "user" | "assistant"
@@ -24,6 +24,8 @@ export function TutorChat() {
const [input, setInput] = useState("")
const [streaming, setStreaming] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
const { userId, loading: userLoading } = useActiveUser();
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
@@ -35,7 +37,7 @@ export function TutorChat() {
const handleSend = async (e?: React.FormEvent) => {
e?.preventDefault()
if (!input.trim() || streaming) return
if (!input.trim() || streaming || userLoading || !userId) return
const userMsg = input.trim()
setInput("")
@@ -46,10 +48,10 @@ export function TutorChat() {
setStreaming(true)
try {
const res = await fetch("http://localhost:8000/api/ai-coach/chat/stream", {
const res = await fetch("/api/ai-coach/chat/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user_id: USER_ID, message: userMsg })
body: JSON.stringify({ user_id: userId, message: userMsg })
})
if (!res.ok) {