169 lines
9.1 KiB
TypeScript
169 lines
9.1 KiB
TypeScript
import { ImageResponse } from 'next/og';
|
|
import { NextRequest } from 'next/server';
|
|
import { join } from 'path';
|
|
import { readFileSync } from 'fs';
|
|
|
|
export async function GET(req: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(req.url);
|
|
|
|
// 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') || '#--';
|
|
|
|
// Read user info
|
|
const name = searchParams.get('name') || 'Student';
|
|
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());
|
|
|
|
// Read theme
|
|
const theme = searchParams.get('theme') || 'dark';
|
|
const isDark = theme === 'dark';
|
|
|
|
// Theme Variables
|
|
const bgColor = isDark
|
|
? 'linear-gradient(145deg, #050505 0%, #111111 50%, #0a0a0a 100%)'
|
|
: 'linear-gradient(145deg, #f8fafc 0%, #ffffff 50%, #f1f5f9 100%)';
|
|
const textColor = isDark ? '#ffffff' : '#0f172a';
|
|
const gridColor = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)';
|
|
const cardBg = isDark
|
|
? 'linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.01) 100%)'
|
|
: 'linear-gradient(145deg, #ffffff 0%, #f8fafc 100%)';
|
|
const cardBorder = isDark ? '1px solid rgba(255,255,255,0.1)' : '1px solid rgba(0,0,0,0.05)';
|
|
const textSec = isDark ? 'rgba(255,255,255,0.5)' : 'rgba(15,23,42,0.5)';
|
|
const textTert = isDark ? 'rgba(255,255,255,0.7)' : 'rgba(15,23,42,0.7)';
|
|
const dividerColor = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)';
|
|
const logoName = isDark ? 'Rectangular_dark_background.png' : 'Rectangular_light_background.png';
|
|
|
|
// Load logo from disk and convert to base64
|
|
const logoPath = join(process.cwd(), 'public', 'PragatiVeer_Logo', 'PNG_Logos', logoName);
|
|
let logoUrl = '';
|
|
try {
|
|
const logoData = readFileSync(logoPath);
|
|
logoUrl = `data:image/png;base64,${logoData.toString('base64')}`;
|
|
} catch (e) {
|
|
console.error('Failed to load logo', e);
|
|
}
|
|
|
|
return new ImageResponse(
|
|
(
|
|
<div
|
|
style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
background: bgColor,
|
|
color: textColor,
|
|
fontFamily: '"Inter", sans-serif',
|
|
padding: '70px 0',
|
|
}}
|
|
>
|
|
{/* Subtle grid background overlay */}
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundImage: `radial-gradient(circle at 2px 2px, ${gridColor} 1px, transparent 0)`,
|
|
backgroundSize: '40px 40px',
|
|
zIndex: 0,
|
|
}}
|
|
/>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', width: '880px', height: '100%', justifyContent: 'space-between', zIndex: 10 }}>
|
|
{/* Header */}
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
|
<img src={logoUrl} width={450} height={110} style={{ objectFit: 'contain', marginLeft: '-64px' }} />
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', background: 'linear-gradient(90deg, rgba(6,182,212,0.15) 0%, rgba(6,182,212,0.05) 100%)', padding: '12px 28px', borderRadius: 100, border: '1px solid rgba(6,182,212,0.3)' }}>
|
|
<div style={{ width: 12, height: 12, borderRadius: 6, backgroundColor: '#06b6d4', marginRight: 12 }} />
|
|
<span style={{ fontSize: 22, fontWeight: 700, color: '#22d3ee', letterSpacing: '0.05em', textTransform: 'uppercase' }}>
|
|
Performance Report
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content - 2x2 Grid */}
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', width: '880px', justifyContent: 'space-between', gap: '32px', marginTop: 20 }}>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingLeft: 48, width: '424px', height: '240px', background: cardBg, borderRadius: 36, border: cardBorder, boxShadow: isDark ? 'none' : '0 10px 25px rgba(0,0,0,0.02)' }}>
|
|
<span style={{ fontSize: 20, color: textSec, textTransform: 'uppercase', fontWeight: 800, letterSpacing: '0.15em', marginBottom: 12 }}>Today's Score</span>
|
|
<span style={{ fontSize: 64, fontWeight: 900, color: '#facc15', letterSpacing: '-0.02em' }}>{score}</span>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingLeft: 48, width: '424px', height: '240px', background: cardBg, borderRadius: 36, border: cardBorder, boxShadow: isDark ? 'none' : '0 10px 25px rgba(0,0,0,0.02)' }}>
|
|
<span style={{ fontSize: 20, color: textSec, textTransform: 'uppercase', fontWeight: 800, letterSpacing: '0.15em', marginBottom: 12 }}>Accuracy</span>
|
|
<span style={{ fontSize: 64, fontWeight: 900, color: '#34d399', letterSpacing: '-0.02em' }}>{accuracy}</span>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingLeft: 48, width: '424px', height: '240px', background: cardBg, borderRadius: 36, border: cardBorder, boxShadow: isDark ? 'none' : '0 10px 25px rgba(0,0,0,0.02)' }}>
|
|
<span style={{ fontSize: 20, color: textSec, textTransform: 'uppercase', fontWeight: 800, letterSpacing: '0.15em', marginBottom: 12 }}>Qs Attempted</span>
|
|
<span style={{ fontSize: 64, fontWeight: 900, color: '#38bdf8', letterSpacing: '-0.02em' }}>{qs}</span>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingLeft: 48, width: '424px', height: '240px', background: cardBg, borderRadius: 36, border: cardBorder, boxShadow: isDark ? 'none' : '0 10px 25px rgba(0,0,0,0.02)' }}>
|
|
<span style={{ fontSize: 20, color: textSec, textTransform: 'uppercase', fontWeight: 800, letterSpacing: '0.15em', marginBottom: 12 }}>City Rank</span>
|
|
<span style={{ fontSize: 64, fontWeight: 900, color: '#a78bfa', letterSpacing: '-0.02em' }}>{rank}</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', marginTop: 20 }}>
|
|
{/* Divider */}
|
|
<div style={{ width: '100%', height: '1px', backgroundColor: dividerColor, marginBottom: 40 }} />
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
{/* User Avatar Placeholder */}
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 80, height: 80, borderRadius: 40, background: 'linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%)', marginRight: 24 }}>
|
|
<span style={{ fontSize: 36, fontWeight: 800, color: '#ffffff' }}>{name.charAt(0).toUpperCase()}</span>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
<div style={{ fontSize: 44, fontWeight: 900, color: textColor, letterSpacing: '-0.02em' }}>{name}</div>
|
|
<div style={{ fontSize: 20, color: textSec, 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: textSec, fontWeight: 600 }}>•</div>
|
|
<div style={{ fontSize: 22, color: textTert, fontWeight: 600, marginLeft: 16 }}>{`${plan} Plan`}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: '4px' }}>
|
|
<div style={{ fontSize: 22, color: textTert, fontWeight: 600 }}>pragativeer.com</div>
|
|
<div style={{ fontSize: 22, color: textSec, fontWeight: 600 }}>contact@pragativeer.com</div>
|
|
<div style={{ fontSize: 22, color: textSec, fontWeight: 600 }}>+91 9079337961</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
),
|
|
{
|
|
width: 1080,
|
|
height: 1080,
|
|
}
|
|
);
|
|
} catch (e: any) {
|
|
return new Response(`Failed to generate image`, {
|
|
status: 500,
|
|
});
|
|
}
|
|
}
|