AV: precise recolor to spec — warm dark backgrounds (#1c1815/#231d18/#26201a/#15110e) + gold #F0A623 / orange #D85A30 accents + warm text (#FBEFE0/#C9BBA8/#8a7e6e). 138 component files + 36 token swaps.

This commit is contained in:
2026-06-02 23:46:00 +00:00
parent 01c1abf99d
commit 2876ac6ecd
141 changed files with 2020 additions and 2020 deletions

View File

@@ -42,7 +42,7 @@ const STYLE_OPTIONS = [
];
const ACCENT_COLORS = [
'#ffb800', '#ef4444', '#10b981', '#8b5cf6', '#f59e0b', '#ec4899', '#06b6d4', '#f97316',
'#F0A623', '#ef4444', '#10b981', '#8b5cf6', '#f59e0b', '#ec4899', '#06b6d4', '#f97316',
];
const LAYOUT_ICONS: Record<string, React.ReactNode> = {
@@ -83,7 +83,7 @@ const emptyForm = (): Omit<NewsletterTemplate, 'id' | 'is_preset' | 'created_at'
subject_prefix: '',
header_text: '',
footer_text: '',
accent_color: '#ffb800',
accent_color: '#F0A623',
article_blocks: [{ title: '', excerpt: '', url: '', category: '' }],
});
@@ -226,7 +226,7 @@ export default function NewsletterTemplatesPage() {
if (loading) {
return (
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center">
<div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" />
<div className="w-8 h-8 border-2 border-[#F0A623] border-t-transparent rounded-full animate-spin" />
</div>
);
}
@@ -242,7 +242,7 @@ export default function NewsletterTemplatesPage() {
)}
{/* Header */}
<div className="bg-[#111] border-b border-[#252525] px-6 py-4">
<div className="bg-[#111] border-b border-[#3a322b] px-6 py-4">
<div className="max-w-6xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-3">
<Link href="/admin/newsletter" className="text-[#555] hover:text-[#888] transition-colors">
@@ -272,11 +272,11 @@ export default function NewsletterTemplatesPage() {
<div className="mb-8">
<div className="flex items-center gap-2 mb-4">
<h2 className="font-display font-bold text-white text-base">Preset Templates</h2>
<span className="text-xs bg-[#1a1208] text-[#ffb800] px-2 py-0.5 rounded-full font-body">{presets.length}</span>
<span className="text-xs bg-[#231d18] text-[#F0A623] px-2 py-0.5 rounded-full font-body">{presets.length}</span>
</div>
{loadingData ? (
<div className="flex items-center justify-center py-12">
<div className="w-6 h-6 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" />
<div className="w-6 h-6 border-2 border-[#F0A623] border-t-transparent rounded-full animate-spin" />
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@@ -298,15 +298,15 @@ export default function NewsletterTemplatesPage() {
<div>
<div className="flex items-center gap-2 mb-4">
<h2 className="font-display font-bold text-white text-base">Custom Templates</h2>
<span className="text-xs bg-[#1a1208] text-[#ffb800] px-2 py-0.5 rounded-full font-body">{custom.length}</span>
<span className="text-xs bg-[#231d18] text-[#F0A623] px-2 py-0.5 rounded-full font-body">{custom.length}</span>
</div>
{!loadingData && custom.length === 0 ? (
<div className="bg-[#111] border border-[#252525] rounded-lg p-10 text-center">
<div className="bg-[#111] border border-[#3a322b] rounded-lg p-10 text-center">
<svg className="w-10 h-10 text-[#333] mx-auto mb-3" fill="none" stroke="currentColor" strokeWidth="1.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
<p className="text-[#555] font-body text-sm mb-3">No custom templates yet</p>
<button onClick={openNew} className="text-[#ffb800] text-sm font-body font-semibold hover:text-blue-400 transition-colors">
<button onClick={openNew} className="text-[#F0A623] text-sm font-body font-semibold hover:text-blue-400 transition-colors">
Create your first template
</button>
</div>
@@ -330,8 +330,8 @@ export default function NewsletterTemplatesPage() {
{/* Template Form Modal */}
{showForm && (
<div className="fixed inset-0 z-50 bg-black/70 flex items-start justify-center overflow-y-auto py-8 px-4">
<div className="bg-[#111] border border-[#252525] rounded-xl w-full max-w-2xl">
<div className="flex items-center justify-between px-6 py-4 border-b border-[#252525]">
<div className="bg-[#111] border border-[#3a322b] rounded-xl w-full max-w-2xl">
<div className="flex items-center justify-between px-6 py-4 border-b border-[#3a322b]">
<h2 className="font-display font-bold text-white text-base">
{editingId ? 'Edit Template' : 'New Template'}
</h2>
@@ -376,8 +376,8 @@ export default function NewsletterTemplatesPage() {
key={opt.value}
type="button"
onClick={() => setForm((f) => ({ ...f, layout: opt.value as NewsletterTemplate['layout'] }))}
className={`flex items-start gap-3 p-3 rounded-lg border text-left transition-colors ${form.layout === opt.value ? 'border-[#ffb800] bg-[#1a1208]' : 'border-[#252525] bg-[#0d0d0d] hover:border-[#333]'}`}>
<span className={`mt-0.5 ${form.layout === opt.value ? 'text-[#ffb800]' : 'text-[#555]'}`}>
className={`flex items-start gap-3 p-3 rounded-lg border text-left transition-colors ${form.layout === opt.value ? 'border-[#F0A623] bg-[#231d18]' : 'border-[#3a322b] bg-[#1c1815] hover:border-[#333]'}`}>
<span className={`mt-0.5 ${form.layout === opt.value ? 'text-[#F0A623]' : 'text-[#555]'}`}>
{LAYOUT_ICONS[opt.value]}
</span>
<div>
@@ -398,7 +398,7 @@ export default function NewsletterTemplatesPage() {
key={opt.value}
type="button"
onClick={() => setForm((f) => ({ ...f, style: opt.value as NewsletterTemplate['style'] }))}
className={`flex-1 py-2.5 px-3 rounded-lg border text-sm font-body font-semibold transition-colors ${form.style === opt.value ? 'border-[#ffb800] bg-[#1a1208] text-white' : 'border-[#252525] bg-[#0d0d0d] text-[#888] hover:border-[#333]'}`}>
className={`flex-1 py-2.5 px-3 rounded-lg border text-sm font-body font-semibold transition-colors ${form.style === opt.value ? 'border-[#F0A623] bg-[#231d18] text-white' : 'border-[#3a322b] bg-[#1c1815] text-[#888] hover:border-[#333]'}`}>
{opt.label}
</button>
))}
@@ -469,14 +469,14 @@ export default function NewsletterTemplatesPage() {
<div>
<div className="flex items-center justify-between mb-2">
<label className="text-xs font-body font-bold text-[#888] uppercase tracking-wider">Article Block Slots</label>
<button type="button" onClick={addBlock} className="text-xs text-[#ffb800] hover:text-blue-400 font-body font-semibold transition-colors">
<button type="button" onClick={addBlock} className="text-xs text-[#F0A623] hover:text-blue-400 font-body font-semibold transition-colors">
+ Add Slot
</button>
</div>
<p className="text-xs text-[#555] font-body mb-3">Define placeholder slots for articles. These will be pre-filled when using the template in Send Digest.</p>
<div className="space-y-2">
{form.article_blocks.map((block, i) => (
<div key={i} className="bg-[#0d0d0d] border border-[#252525] rounded-lg p-3">
<div key={i} className="bg-[#1c1815] border border-[#3a322b] rounded-lg p-3">
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-body text-[#555]">Slot {i + 1}</span>
<div className="flex items-center gap-3">
@@ -486,7 +486,7 @@ export default function NewsletterTemplatesPage() {
type="checkbox"
checked={!!block.featured}
onChange={(e) => updateBlock(i, 'featured', e.target.checked)}
className="w-3 h-3 accent-[#ffb800]"
className="w-3 h-3 accent-[#F0A623]"
/>
<span className="text-xs font-body text-[#888]">Featured</span>
</label>
@@ -527,7 +527,7 @@ export default function NewsletterTemplatesPage() {
</div>
</div>
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-[#252525]">
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-[#3a322b]">
<button onClick={() => setShowForm(false)} className="px-4 py-2 text-sm font-body text-[#888] hover:text-white transition-colors">
Cancel
</button>
@@ -545,8 +545,8 @@ export default function NewsletterTemplatesPage() {
{/* Preview Modal */}
{previewTemplate && (
<div className="fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4" onClick={() => setPreviewTemplate(null)}>
<div className="bg-[#111] border border-[#252525] rounded-xl w-full max-w-lg" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#252525]">
<div className="bg-[#111] border border-[#3a322b] rounded-xl w-full max-w-lg" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#3a322b]">
<h2 className="font-display font-bold text-white text-base">Template Preview</h2>
<button onClick={() => setPreviewTemplate(null)} className="text-[#555] hover:text-white transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
@@ -565,27 +565,27 @@ export default function NewsletterTemplatesPage() {
</div>
</div>
<div className="grid grid-cols-3 gap-3 text-center">
<div className="bg-[#0d0d0d] rounded-lg p-3">
<div className="bg-[#1c1815] rounded-lg p-3">
<p className="text-xs text-[#555] font-body mb-1">Layout</p>
<p className="text-sm font-body font-semibold text-white capitalize">{previewTemplate.layout}</p>
</div>
<div className="bg-[#0d0d0d] rounded-lg p-3">
<div className="bg-[#1c1815] rounded-lg p-3">
<p className="text-xs text-[#555] font-body mb-1">Style</p>
<p className="text-sm font-body font-semibold text-white capitalize">{previewTemplate.style}</p>
</div>
<div className="bg-[#0d0d0d] rounded-lg p-3">
<div className="bg-[#1c1815] rounded-lg p-3">
<p className="text-xs text-[#555] font-body mb-1">Slots</p>
<p className="text-sm font-body font-semibold text-white">{previewTemplate.article_blocks?.length || 0}</p>
</div>
</div>
{previewTemplate.subject_prefix && (
<div className="bg-[#0d0d0d] rounded-lg p-3">
<div className="bg-[#1c1815] rounded-lg p-3">
<p className="text-xs text-[#555] font-body mb-1">Subject Prefix</p>
<p className="text-sm font-body text-[#e0e0e0]">{previewTemplate.subject_prefix}</p>
</div>
)}
{previewTemplate.header_text && (
<div className="bg-[#0d0d0d] rounded-lg p-3">
<div className="bg-[#1c1815] rounded-lg p-3">
<p className="text-xs text-[#555] font-body mb-1">Header Text</p>
<p className="text-sm font-body text-[#e0e0e0]">{previewTemplate.header_text}</p>
</div>
@@ -595,9 +595,9 @@ export default function NewsletterTemplatesPage() {
<p className="text-xs text-[#555] font-body mb-2">Article Slots ({previewTemplate.article_blocks.length})</p>
<div className="space-y-1.5">
{previewTemplate.article_blocks.map((b, i) => (
<div key={i} className="flex items-center gap-2 bg-[#0d0d0d] rounded px-3 py-2">
<div key={i} className="flex items-center gap-2 bg-[#1c1815] rounded px-3 py-2">
<span className="text-xs text-[#555] font-body w-12 flex-shrink-0">Slot {i + 1}</span>
{b.featured && <span className="text-xs text-[#ffb800] font-body bg-[#1a1208] px-1.5 py-0.5 rounded">Featured</span>}
{b.featured && <span className="text-xs text-[#F0A623] font-body bg-[#231d18] px-1.5 py-0.5 rounded">Featured</span>}
{b.category && <span className="text-xs text-[#888] font-body">{b.category}</span>}
{b.title && <span className="text-xs text-[#e0e0e0] font-body truncate">{b.title}</span>}
</div>
@@ -614,7 +614,7 @@ export default function NewsletterTemplatesPage() {
{!previewTemplate.is_preset && (
<button
onClick={() => { setPreviewTemplate(null); openEdit(previewTemplate); }}
className="px-4 py-2 text-sm font-body text-[#888] hover:text-white border border-[#252525] rounded-lg transition-colors">
className="px-4 py-2 text-sm font-body text-[#888] hover:text-white border border-[#3a322b] rounded-lg transition-colors">
Edit
</button>
)}
@@ -637,7 +637,7 @@ interface TemplateCardProps {
function TemplateCard({ template, onEdit, onDelete, onPreview, deletingId }: TemplateCardProps) {
return (
<div className="bg-[#111] border border-[#252525] rounded-xl overflow-hidden hover:border-[#333] transition-colors group">
<div className="bg-[#111] border border-[#3a322b] rounded-xl overflow-hidden hover:border-[#333] transition-colors group">
{/* Color bar */}
<div className="h-1" style={{ backgroundColor: template.accent_color }} />
<div className="p-4">
@@ -660,27 +660,27 @@ function TemplateCard({ template, onEdit, onDelete, onPreview, deletingId }: Tem
)}
<div className="flex flex-wrap gap-1.5 mb-4">
<span className="text-xs bg-[#1a1a1a] text-[#888] px-2 py-0.5 rounded-full font-body capitalize">{template.layout}</span>
<span className="text-xs bg-[#1a1a1a] text-[#888] px-2 py-0.5 rounded-full font-body capitalize">{template.style}</span>
<span className="text-xs bg-[#1a1a1a] text-[#888] px-2 py-0.5 rounded-full font-body">{template.article_blocks?.length || 0} slots</span>
<span className="text-xs bg-[#231d18] text-[#888] px-2 py-0.5 rounded-full font-body capitalize">{template.layout}</span>
<span className="text-xs bg-[#231d18] text-[#888] px-2 py-0.5 rounded-full font-body capitalize">{template.style}</span>
<span className="text-xs bg-[#231d18] text-[#888] px-2 py-0.5 rounded-full font-body">{template.article_blocks?.length || 0} slots</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => onPreview(template)}
className="flex-1 py-1.5 text-xs font-body font-semibold text-[#ffb800] border border-[#4a3500] rounded-lg hover:bg-[#1a1208] transition-colors">
className="flex-1 py-1.5 text-xs font-body font-semibold text-[#F0A623] border border-[#412402] rounded-lg hover:bg-[#231d18] transition-colors">
Preview
</button>
<Link
href={`/admin/newsletter?template=${template.id}`}
className="flex-1 py-1.5 text-xs font-body font-semibold text-center text-white bg-[#1a1208] border border-[#4a3500] rounded-lg hover:bg-[#4a3500] transition-colors">
className="flex-1 py-1.5 text-xs font-body font-semibold text-center text-white bg-[#231d18] border border-[#412402] rounded-lg hover:bg-[#412402] transition-colors">
Use
</Link>
{!template.is_preset && (
<>
<button
onClick={() => onEdit(template)}
className="p-1.5 text-[#555] hover:text-white border border-[#252525] rounded-lg transition-colors">
className="p-1.5 text-[#555] hover:text-white border border-[#3a322b] rounded-lg transition-colors">
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125" />
</svg>
@@ -688,7 +688,7 @@ function TemplateCard({ template, onEdit, onDelete, onPreview, deletingId }: Tem
<button
onClick={() => onDelete(template.id)}
disabled={deletingId === template.id}
className="p-1.5 text-[#555] hover:text-red-400 border border-[#252525] rounded-lg transition-colors disabled:opacity-50">
className="p-1.5 text-[#555] hover:text-red-400 border border-[#3a322b] rounded-lg transition-colors disabled:opacity-50">
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>