Update share card with fb, whatsapp, download and export date; update CBT and Doubt Forum UI

This commit is contained in:
2026-09-22 20:53:15 +05:30
parent 9705ee96e6
commit 18006c97ac
4 changed files with 73 additions and 24 deletions

View File

@@ -18,6 +18,13 @@ export async function GET(req: NextRequest) {
const target = searchParams.get('target') || 'JEE';
const plan = searchParams.get('plan') || 'Free';
// Read date (if passed, otherwise use current date/time)
const exportDate = searchParams.get('date') || new Intl.DateTimeFormat('en-IN', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Asia/Kolkata',
}).format(new Date());
// Load logo from disk and convert to base64
const logoPath = join(process.cwd(), 'public', 'PragatiVeer_Logo', 'PNG_Logos', 'Rectangular_dark_background.png');
let logoUrl = '';
@@ -110,6 +117,7 @@ export async function GET(req: NextRequest) {
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ fontSize: 44, fontWeight: 900, color: '#ffffff', letterSpacing: '-0.02em' }}>{name}</div>
<div style={{ fontSize: 20, color: 'rgba(255,255,255,0.5)', marginTop: 4 }}>Generated: {exportDate}</div>
<div style={{ display: 'flex', marginTop: 8 }}>
<div style={{ fontSize: 22, color: '#06b6d4', fontWeight: 700, marginRight: 16 }}>{`${target} Aspirant`}</div>
<div style={{ fontSize: 22, color: 'rgba(255,255,255,0.5)', fontWeight: 600 }}>•</div>
@@ -118,10 +126,10 @@ export async function GET(req: NextRequest) {
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: 22, color: 'rgba(255,255,255,0.4)', fontWeight: 600, letterSpacing: '0.05em' }}>
pragativeer.com
</div>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: '4px' }}>
<div style={{ fontSize: 22, color: 'rgba(255,255,255,0.7)', fontWeight: 600 }}>pragativeer.com</div>
<div style={{ fontSize: 22, color: 'rgba(255,255,255,0.5)', fontWeight: 600 }}>contact@pragativeer.com</div>
<div style={{ fontSize: 22, color: 'rgba(255,255,255,0.5)', fontWeight: 600 }}>+91 9079337961</div>
</div>
</div>
</div>

View File

@@ -134,7 +134,7 @@ export default function CBTPage() {
<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)]">
<CheckSquare className="text-yellow-600 dark:text-[var(--cyan-ai)]" size={28} />
</span>
Computer Based Tests (CBT)
Mock Tests (CBT)
</h1>
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
Real exam environment simulations to conquer {selectedPattern === "NEET" ? "NEET" : (targetExam || "JEE")}.

View File

@@ -1,7 +1,7 @@
"use client"
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 } from "lucide-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 { LanguageToggle } from "@/components/LanguageToggle"
@@ -145,7 +145,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
<NavItem
section="pro"
href="/dashboard/community"
icon={<School size={17} />}
icon={<MessageSquare size={17} />}
label="Doubt Forum"
active={pathname.startsWith("/dashboard/community")}
isLocked={!hasAccessTo("doubt-resolution-community")}

View File

@@ -4,7 +4,7 @@ import { useRef, useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { BrainCircuit, Play, ChevronRight, Activity, Target, CheckCircle2, AlertTriangle, TrendingUp, Calendar, Zap, LayoutDashboard, Share2, Trophy, Lock, ArrowRight, Clock } from "lucide-react"
import { BrainCircuit, Play, ChevronRight, Activity, Target, CheckCircle2, AlertTriangle, TrendingUp, Calendar, Zap, LayoutDashboard, Share2, Trophy, Lock, ArrowRight, Clock, Facebook, Download, MessageCircle } from "lucide-react"
import { MistakeHeatmap } from "@/components/AI/MistakeHeatmap"
import { AdaptiveStudyPlan } from "@/components/AI/AdaptiveStudyPlan"
import { useActiveUser } from "@/hooks/useActiveUser"
@@ -14,6 +14,7 @@ export default function DashboardPage() {
const [userStats, setUserStats] = useState<any>(null)
const [loading, setLoading] = useState(true)
const [downloading, setDownloading] = useState(false)
const [shareMenuOpen, setShareMenuOpen] = useState(false)
const progressRef = useRef<HTMLDivElement>(null)
useEffect(() => {
@@ -39,9 +40,7 @@ export default function DashboardPage() {
fetchStats()
}, [userLoading, user?.id, userId])
const handleExport = async () => {
try {
setDownloading(true)
const getShareUrl = () => {
const score = isNew ? "0 / 0" : `${userStats?.today_score || 0} / ${userStats?.today_max_score || 60}`
const accuracy = isNew ? "0%" : `${userStats?.overall_accuracy || 0}%`
const qs = isNew ? "0" : (userStats?.total_questions_attempted || 0).toLocaleString()
@@ -50,9 +49,19 @@ export default function DashboardPage() {
const target = user?.target_exam === "NEET" ? "NEET" : "JEE"
const plan = user?.tier === "Al_Coach" ? "AI Coach" : user?.tier || "Free"
const name = user?.name || "Student"
const date = new Intl.DateTimeFormat('en-IN', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Asia/Kolkata',
}).format(new Date());
const url = `/api/og/dashboard?score=${encodeURIComponent(score)}&accuracy=${encodeURIComponent(accuracy)}&qs=${encodeURIComponent(qs)}&rank=${encodeURIComponent(rank)}&name=${encodeURIComponent(name)}&target=${target}&plan=${encodeURIComponent(plan)}`
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)}`
}
const handleDownload = async () => {
try {
setDownloading(true)
const url = getShareUrl()
const res = await fetch(url)
const blob = await res.blob()
const blobUrl = window.URL.createObjectURL(blob)
@@ -68,9 +77,23 @@ export default function DashboardPage() {
console.error("Failed to export image", err)
} finally {
setDownloading(false)
setShareMenuOpen(false)
}
}
const handleShareFacebook = () => {
const fullUrl = window.location.origin + getShareUrl()
window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(fullUrl)}`, '_blank')
setShareMenuOpen(false)
}
const handleShareWhatsApp = () => {
const fullUrl = window.location.origin + getShareUrl()
const text = `Check out my performance on PragatiVeer!\n\n${fullUrl}`
window.open(`https://wa.me/?text=${encodeURIComponent(text)}`, '_blank')
setShareMenuOpen(false)
}
if (loading) {
return (
<div className="h-[70vh] flex flex-col items-center justify-center p-8">
@@ -100,10 +123,28 @@ export default function DashboardPage() {
Your overall progress, scores, and active study plan at a glance.
</p>
</div>
<Button onClick={handleExport} variant="outline" size="sm" className="gap-2 font-medium shrink-0" disabled={downloading}>
<div className="relative">
<Button onClick={() => setShareMenuOpen(!shareMenuOpen)} variant="outline" size="sm" className="gap-2 font-medium shrink-0" disabled={downloading}>
{downloading ? <div className="h-4 w-4 border-2 border-current border-t-transparent rounded-full animate-spin"></div> : <Share2 size={16} />}
{downloading ? 'Generating...' : 'Share Card'}
</Button>
{shareMenuOpen && (
<div className="absolute right-0 top-full mt-2 w-48 bg-card border border-border rounded-xl shadow-xl z-50 overflow-hidden animate-in fade-in zoom-in-95 duration-200">
<button onClick={handleShareFacebook} className="flex items-center w-full px-4 py-3 text-sm hover:bg-secondary text-left transition-colors">
<Facebook size={16} className="mr-3 text-blue-600" />
Share on Facebook
</button>
<button onClick={handleShareWhatsApp} className="flex items-center w-full px-4 py-3 text-sm hover:bg-secondary text-left transition-colors">
<MessageCircle size={16} className="mr-3 text-green-500" />
Share on WhatsApp
</button>
<button onClick={handleDownload} className="flex items-center w-full px-4 py-3 text-sm hover:bg-secondary text-left transition-colors border-t border-border">
<Download size={16} className="mr-3 text-primary" />
Download Image
</button>
</div>
)}
</div>
</div>
<div ref={progressRef} className="grid grid-cols-2 md:grid-cols-4 gap-4 p-4 -mx-4 bg-background rounded-2xl">
{/* Today's Score */}