init elas atelier
This commit is contained in:
+112
-98
@@ -6,125 +6,139 @@ import AssetsButton from '../components/react/AssetsButton';
|
||||
|
||||
const API_URL = process.env.PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
interface CoverImage {
|
||||
url: string;
|
||||
alt: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
slug: string;
|
||||
date: string;
|
||||
title?: string;
|
||||
excerpt?: string;
|
||||
tags: string[];
|
||||
draft: boolean;
|
||||
reading_time: number;
|
||||
slug: string;
|
||||
date: string;
|
||||
title?: string;
|
||||
summary?: string;
|
||||
excerpt?: string;
|
||||
tags: string[];
|
||||
draft: boolean;
|
||||
reading_time: number;
|
||||
cover_image?: CoverImage;
|
||||
image_count: number;
|
||||
}
|
||||
|
||||
let posts: Post[] = [];
|
||||
let error = '';
|
||||
let siteConfig = {
|
||||
welcome_title: "Welcome to my blog",
|
||||
welcome_subtitle: "Thoughts on software, design, and building things with Rust and Astro."
|
||||
welcome_title: "Works on view",
|
||||
welcome_subtitle: "An ongoing arrangement of pieces, sketches, and stray observations."
|
||||
};
|
||||
|
||||
try {
|
||||
const [postsRes, configRes] = await Promise.all([
|
||||
fetch(`${API_URL}/api/posts`),
|
||||
fetch(`${API_URL}/api/config`)
|
||||
]);
|
||||
const [postsRes, configRes] = await Promise.all([
|
||||
fetch(`${API_URL}/api/posts`),
|
||||
fetch(`${API_URL}/api/config`)
|
||||
]);
|
||||
|
||||
if (postsRes.ok) {
|
||||
posts = await postsRes.json();
|
||||
} else {
|
||||
error = 'Failed to fetch posts';
|
||||
}
|
||||
if (postsRes.ok) {
|
||||
posts = await postsRes.json();
|
||||
} else {
|
||||
error = 'Failed to fetch works';
|
||||
}
|
||||
|
||||
if (configRes.ok) {
|
||||
siteConfig = await configRes.json();
|
||||
}
|
||||
if (configRes.ok) {
|
||||
siteConfig = await configRes.json();
|
||||
}
|
||||
} catch (e) {
|
||||
const cause = (e as any)?.cause;
|
||||
error = `Could not connect to backend at ${API_URL}: ${e instanceof Error ? e.message : String(e)}${cause ? ' (Cause: ' + (cause.message || cause.code || JSON.stringify(cause)) + ')' : ''}`;
|
||||
console.error(error);
|
||||
const cause = (e as any)?.cause;
|
||||
error = `Could not connect to backend at ${API_URL}: ${e instanceof Error ? e.message : String(e)}${cause ? ' (Cause: ' + (cause.message || cause.code || JSON.stringify(cause)) + ')' : ''}`;
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
|
||||
const total = posts.length;
|
||||
---
|
||||
|
||||
<Layout title="Home" description={siteConfig.welcome_subtitle}>
|
||||
<div class="space-y-6 md:space-y-8">
|
||||
<section class="text-center py-6 md:py-12">
|
||||
<h1 class="text-3xl md:text-5xl font-extrabold mb-3 md:mb-4 pb-2 md:pb-4 leading-tight">
|
||||
{isAdmin ? (
|
||||
<EditableText
|
||||
client:load
|
||||
initial={siteConfig.welcome_title}
|
||||
fieldKey="welcome_title"
|
||||
isAdmin
|
||||
ariaLabel="welcome title"
|
||||
className="bg-clip-text text-transparent bg-gradient-to-r from-mauve via-blue to-teal"
|
||||
/>
|
||||
) : (
|
||||
<span class="bg-clip-text text-transparent bg-gradient-to-r from-mauve via-blue to-teal">
|
||||
{siteConfig.welcome_title}
|
||||
</span>
|
||||
<Layout title="Catalogue" description={siteConfig.welcome_subtitle}>
|
||||
<section class="relative mb-16 md:mb-24">
|
||||
<div class="flex flex-col md:flex-row md:items-end gap-8 md:gap-12">
|
||||
<div class="flex-1 max-w-2xl">
|
||||
<div class="font-display italic text-[var(--subtext0)] text-sm tracking-[0.3em] uppercase mb-4">
|
||||
Currently arranged — {new Date().toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
|
||||
</div>
|
||||
<h1 class="font-display italic font-semibold text-[var(--text)] text-5xl md:text-7xl lg:text-8xl leading-[0.95] tracking-tight mb-6">
|
||||
{isAdmin ? (
|
||||
<EditableText
|
||||
client:load
|
||||
initial={siteConfig.welcome_title}
|
||||
fieldKey="welcome_title"
|
||||
isAdmin
|
||||
ariaLabel="welcome title"
|
||||
className="inline"
|
||||
/>
|
||||
) : siteConfig.welcome_title}
|
||||
</h1>
|
||||
<p class="font-sans text-lg md:text-xl text-[var(--subtext1)] leading-relaxed max-w-xl">
|
||||
{isAdmin ? (
|
||||
<EditableText
|
||||
client:load
|
||||
initial={siteConfig.welcome_subtitle}
|
||||
fieldKey="welcome_subtitle"
|
||||
isAdmin
|
||||
ariaLabel="welcome subtitle"
|
||||
multiline
|
||||
className="inline"
|
||||
/>
|
||||
) : siteConfig.welcome_subtitle}
|
||||
</p>
|
||||
|
||||
{isAdmin && (
|
||||
<div class="mt-8 flex flex-wrap items-center gap-3">
|
||||
<a href="/admin/editor" class="btn-stamp">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
Hang new work
|
||||
</a>
|
||||
<AssetsButton client:load />
|
||||
<a href="/admin/settings" class="btn-ghost">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/></svg>
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</h1>
|
||||
<p class="text-subtext1 text-base md:text-lg max-w-2xl mx-auto px-4 md:px-0">
|
||||
{isAdmin ? (
|
||||
<EditableText
|
||||
client:load
|
||||
initial={siteConfig.welcome_subtitle}
|
||||
fieldKey="welcome_subtitle"
|
||||
isAdmin
|
||||
ariaLabel="welcome subtitle"
|
||||
multiline
|
||||
className="inline"
|
||||
/>
|
||||
) : siteConfig.welcome_subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{isAdmin && (
|
||||
<div class="mt-6 md:mt-8 flex flex-wrap items-center justify-center gap-2 md:gap-3">
|
||||
<a
|
||||
href="/admin/editor"
|
||||
class="inline-flex items-center gap-2 bg-mauve text-crust font-semibold px-4 py-2 rounded-lg hover:bg-pink transition-colors text-sm shadow-lg shadow-mauve/20"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
New Post
|
||||
</a>
|
||||
<AssetsButton client:load />
|
||||
<a
|
||||
href="/admin/settings"
|
||||
class="inline-flex items-center gap-2 bg-surface0 hover:bg-surface1 text-subtext1 hover:text-text px-3 py-2 rounded-lg border border-surface1 transition-colors text-sm"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
Settings
|
||||
</a>
|
||||
<aside class="md:w-64 lg:w-80 shrink-0 md:pb-2">
|
||||
<div class="font-display italic text-[var(--subtext0)] text-xs tracking-[0.3em] uppercase mb-2">Index</div>
|
||||
<div class="numeral text-5xl md:text-6xl text-[var(--mauve)] leading-none mb-3">
|
||||
{String(total).padStart(2, '0')}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<div class="flex flex-col space-y-6">
|
||||
{error && (
|
||||
<div class="glass p-4 md:p-6 text-red text-center border-red/20 text-sm md:text-base">
|
||||
{error}
|
||||
<div class="font-display italic text-[var(--subtext1)] text-base leading-snug">
|
||||
{total === 1 ? 'work hanging' : 'works hanging'},
|
||||
<span class="font-hand text-[var(--mauve)] text-xl ml-1">arranged below</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length === 0 && !error && !isAdmin && (
|
||||
<div class="glass p-8 md:p-12 text-center text-sm md:text-base text-subtext1">
|
||||
<p>No posts yet — check back soon.</p>
|
||||
<div class="mt-4 h-px bg-[var(--surface2)]"></div>
|
||||
<div class="mt-3 text-[var(--subtext0)] font-display italic text-sm">
|
||||
Scroll the room — no two visits the same.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length === 0 && !error && isAdmin && (
|
||||
<div class="glass p-8 md:p-12 text-center text-sm md:text-base">
|
||||
<p class="text-subtext1 mb-4">No posts yet. Write your first one.</p>
|
||||
<a href="/admin/editor" class="inline-flex items-center gap-2 bg-mauve text-crust font-semibold px-4 py-2 rounded-lg hover:bg-pink transition-colors text-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
New Post
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length > 0 && <PostList posts={posts} isAdmin={isAdmin} client:load />}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{error && (
|
||||
<div class="glass p-6 md:p-8 text-center mb-12 border-[var(--red)]/40">
|
||||
<p class="font-display italic text-[var(--red)] text-lg">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length === 0 && !error && (
|
||||
<div class="glass p-12 md:p-20 text-center max-w-2xl mx-auto">
|
||||
<div class="font-display italic text-[var(--subtext0)] text-sm tracking-[0.3em] uppercase mb-4">Notice</div>
|
||||
<p class="font-display italic text-[var(--text)] text-2xl md:text-3xl leading-snug mb-2">
|
||||
The exhibition is currently being arranged.
|
||||
</p>
|
||||
<p class="font-sans text-[var(--subtext1)] mt-4">Please return shortly.</p>
|
||||
{isAdmin && (
|
||||
<a href="/admin/editor" class="btn-stamp mt-8">Hang the first work</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length > 0 && <PostList posts={posts} isAdmin={isAdmin} client:load />}
|
||||
</Layout>
|
||||
|
||||
Reference in New Issue
Block a user