Switch navbar to #001321, admin can update logo/favicon, logo links home

This commit is contained in:
2026-06-27 16:44:17 -05:00
parent 8952bbf555
commit 1148f7a72f
3 changed files with 192 additions and 7 deletions
+120 -1
View File
@@ -11,7 +11,7 @@ interface ImageRecord {
}
const gold = '#E8A849';
const navy = '#010D1E';
const navy = '#001321';
const warm = '#a6683c';
export default function AdminPage() {
@@ -20,6 +20,9 @@ export default function AdminPage() {
const [count, setCount] = useState(0);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [logoUrl, setLogoUrl] = useState('');
const [faviconUrl, setFaviconUrl] = useState('');
const [message, setMessage] = useState<string | null>(null);
useEffect(() => {
fetch('/api/admin/images', { credentials: 'same-origin' })
@@ -37,8 +40,50 @@ export default function AdminPage() {
})
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
fetch('/api/site-assets?key=logo')
.then(async (res) => {
if (!res.ok) return;
const data = await res.json();
if (data.data) setLogoUrl(data.data);
})
.catch(() => {});
fetch('/api/site-assets?key=favicon')
.then(async (res) => {
if (!res.ok) return;
const data = await res.json();
if (data.data) setFaviconUrl(data.data);
})
.catch(() => {});
}, [router]);
useEffect(() => {
if (!faviconUrl) return;
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement | null;
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
link.href = faviconUrl;
}, [faviconUrl]);
const updateAsset = async (key: string, value: string) => {
const res = await fetch('/api/site-assets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key, value }),
credentials: 'same-origin',
});
if (!res.ok) {
setError('Failed to save ' + key);
return;
}
setMessage(`${key} updated. Refresh the page to see the change.`);
setTimeout(() => setMessage(null), 4000);
};
const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (!files) return;
@@ -94,6 +139,80 @@ export default function AdminPage() {
Total uploaded images: <strong style={{ color: warm }}>{count}</strong>
</p>
{error && <p style={{ color: '#c23b22', marginBottom: '1rem' }}>{error}</p>}
{message && <p style={{ color: '#3b7a22', marginBottom: '1rem' }}>{message}</p>}
<section
style={{
marginBottom: '2rem',
padding: '1.5rem',
background: '#f5f0e8',
borderRadius: '16px',
display: 'flex',
flexDirection: 'column',
gap: '1rem',
}}
>
<h2 style={{ fontSize: '18px', color: navy, margin: 0 }}>Brand Assets</h2>
<div style={{ display: 'grid', gap: '1rem', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))' }}>
<label style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem', fontSize: '13px', color: '#555' }}>
Navbar logo
<input
type="text"
value={logoUrl}
onChange={(e) => setLogoUrl(e.target.value)}
placeholder="Paste image data URL or upload a small image below"
style={{ padding: '0.6rem 0.8rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.18)' }}
/>
</label>
<button
onClick={() => updateAsset('logo', logoUrl)}
disabled={!logoUrl}
style={{
padding: '0.7rem 1.2rem',
background: gold,
color: navy,
border: 'none',
borderRadius: '999px',
cursor: logoUrl ? 'pointer' : 'not-allowed',
fontWeight: 600,
alignSelf: 'end',
}}
>
Save logo
</button>
<label style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem', fontSize: '13px', color: '#555' }}>
Favicon
<input
type="text"
value={faviconUrl}
onChange={(e) => setFaviconUrl(e.target.value)}
placeholder="Paste image data URL or upload a small image below"
style={{ padding: '0.6rem 0.8rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.18)' }}
/>
</label>
<button
onClick={() => updateAsset('favicon', faviconUrl)}
disabled={!faviconUrl}
style={{
padding: '0.7rem 1.2rem',
background: gold,
color: navy,
border: 'none',
borderRadius: '999px',
cursor: faviconUrl ? 'pointer' : 'not-allowed',
fontWeight: 600,
alignSelf: 'end',
}}
>
Save favicon
</button>
</div>
<p style={{ margin: 0, fontSize: '13px', color: '#777' }}>
Tip: click an uploaded gallery image to preview, copy its data URL, and paste it above.
</p>
</section>
<label
style={{
display: 'inline-flex',