Next.js app showcasing WhyRating brand guidelines with interactive tabs for colors, typography, proportions, logos, voice, downloads, and AI context. Includes email templates (headers, signatures, CTAs) and presentation component. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
5.3 KiB
TypeScript
127 lines
5.3 KiB
TypeScript
'use client';
|
||
|
||
import { useState } from 'react';
|
||
import { WhyMyRatingLogo, PreviewScaler } from '@/components';
|
||
|
||
export function CTATemplateSquare() {
|
||
const [hook, setHook] = useState('Still reading reviews one by one?');
|
||
const [valueProp, setValueProp] = useState('Get AI-powered insights in 45 seconds');
|
||
const [ctaText, setCtaText] = useState('Try it free at whyrating.com');
|
||
const [isDownloading, setIsDownloading] = useState(false);
|
||
|
||
const downloadPng = async () => {
|
||
setIsDownloading(true);
|
||
try {
|
||
const response = await fetch('/api/screenshot', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
template: 'cta-square',
|
||
params: { hook, valueProp, ctaText }
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) throw new Error('Screenshot failed');
|
||
|
||
const blob = await response.blob();
|
||
const url = URL.createObjectURL(blob);
|
||
const link = document.createElement('a');
|
||
link.download = `whyrating-cta-square-${Date.now()}.png`;
|
||
link.href = url;
|
||
link.click();
|
||
URL.revokeObjectURL(url);
|
||
} catch (error) {
|
||
console.error('Download failed:', error);
|
||
alert('Download failed. Please try again.');
|
||
} finally {
|
||
setIsDownloading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="bg-white dark:bg-stone-900 rounded-xl border border-slate-100 shadow-sm dark:border-stone-700 p-6">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<div>
|
||
<h3 className="text-lg font-semibold text-slate-900 dark:text-stone-50">CTA Post (Square)</h3>
|
||
<p className="text-sm text-slate-500 dark:text-stone-500">1080 × 1080px • Instagram, Facebook</p>
|
||
</div>
|
||
<button
|
||
onClick={downloadPng}
|
||
disabled={isDownloading}
|
||
className="px-4 py-2 bg-[var(--ui-primary)] text-white rounded-lg text-sm font-medium hover:bg-[var(--ui-primary-hover)] transition-colors disabled:opacity-50"
|
||
>
|
||
{isDownloading ? 'Generating...' : 'Download PNG'}
|
||
</button>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{/* Preview */}
|
||
<PreviewScaler width={540} height={540}>
|
||
<div
|
||
className="rounded-xl p-12 flex flex-col justify-between relative overflow-hidden bg-gradient-to-br from-slate-800 to-slate-900"
|
||
style={{ width: '540px', height: '540px' }}
|
||
>
|
||
{/* Decorative stars */}
|
||
<div className="absolute top-8 right-8 text-[#FBBC05] text-4xl opacity-20">★</div>
|
||
<div className="absolute bottom-32 left-10 text-[#FBBC05] text-2xl opacity-15">★</div>
|
||
|
||
{/* Hook */}
|
||
<div className="pt-4">
|
||
<div className="text-white text-3xl font-bold leading-tight max-w-md">{hook}</div>
|
||
</div>
|
||
|
||
{/* Value Prop */}
|
||
<div className="flex-1 flex items-center justify-center">
|
||
<div className="text-white text-2xl font-medium leading-snug text-center max-w-lg">{valueProp}</div>
|
||
</div>
|
||
|
||
{/* CTA */}
|
||
<div className="pb-4">
|
||
<div className="bg-[#F59E0B] text-slate-900 text-xl font-bold px-6 py-4 rounded-xl text-center mb-6">
|
||
{ctaText}
|
||
</div>
|
||
<div className="flex justify-center">
|
||
<WhyMyRatingLogo size={100} variant="horizontal-v2" colorScheme="dark" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</PreviewScaler>
|
||
|
||
{/* Editor */}
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 dark:text-stone-300 mb-1">Hook Question</label>
|
||
<textarea
|
||
value={hook}
|
||
onChange={(e) => setHook(e.target.value)}
|
||
rows={2}
|
||
className="w-full px-3 py-2 border border-slate-300 dark:border-stone-600 rounded-lg bg-white dark:bg-stone-800 text-slate-900 dark:text-stone-50 focus:ring-2 focus:ring-[var(--ui-primary)] focus:border-transparent resize-none"
|
||
placeholder="Still reading reviews one by one?"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 dark:text-stone-300 mb-1">Value Proposition</label>
|
||
<textarea
|
||
value={valueProp}
|
||
onChange={(e) => setValueProp(e.target.value)}
|
||
rows={3}
|
||
className="w-full px-3 py-2 border border-slate-300 dark:border-stone-600 rounded-lg bg-white dark:bg-stone-800 text-slate-900 dark:text-stone-50 focus:ring-2 focus:ring-[var(--ui-primary)] focus:border-transparent resize-none"
|
||
placeholder="Get AI-powered insights in 45 seconds"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 dark:text-stone-300 mb-1">Call to Action</label>
|
||
<input
|
||
type="text"
|
||
value={ctaText}
|
||
onChange={(e) => setCtaText(e.target.value)}
|
||
className="w-full px-3 py-2 border border-slate-300 dark:border-stone-600 rounded-lg bg-white dark:bg-stone-800 text-slate-900 dark:text-stone-50 focus:ring-2 focus:ring-[var(--ui-primary)] focus:border-transparent"
|
||
placeholder="Try it free at whyrating.com"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|