Add WhyRating Templates - brand identity system
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>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { WhyMyRatingLogo, PreviewScaler } from '@/components';
|
||||
|
||||
export function CTATemplateLandscape() {
|
||||
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-landscape',
|
||||
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-landscape-${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 (Landscape)</h3>
|
||||
<p className="text-sm text-slate-500 dark:text-stone-500">1200 × 675px • LinkedIn, Twitter</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={600} height={338}>
|
||||
<div
|
||||
className="rounded-xl p-10 flex flex-col justify-between relative overflow-hidden bg-gradient-to-br from-slate-800 to-slate-900"
|
||||
style={{ width: '600px', height: '338px' }}
|
||||
>
|
||||
{/* Decorative stars */}
|
||||
<div className="absolute top-6 right-8 text-[#FBBC05] text-3xl opacity-20">★</div>
|
||||
<div className="absolute bottom-16 right-12 text-[#FBBC05] text-xl opacity-15">★</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 flex items-center">
|
||||
<div className="flex flex-col gap-4 max-w-[85%]">
|
||||
<div className="text-slate-300 text-xl font-medium leading-tight">{hook}</div>
|
||||
<div className="text-white text-3xl font-bold leading-tight">{valueProp}</div>
|
||||
<div className="mt-2">
|
||||
<span className="inline-block bg-[#F59E0B] text-slate-900 px-5 py-2.5 rounded-lg text-lg font-semibold">
|
||||
{ctaText}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-end justify-end">
|
||||
<WhyMyRatingLogo size={70} variant="horizontal-v2" colorScheme="dark" />
|
||||
</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</label>
|
||||
<input
|
||||
type="text"
|
||||
value={hook}
|
||||
onChange={(e) => setHook(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="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={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="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">CTA Text</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user