init
This commit is contained in:
77
frontend/src/pages/index.astro
Normal file
77
frontend/src/pages/index.astro
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
|
||||
const API_URL = import.meta.env.PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
interface Post {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
let posts: Post[] = [];
|
||||
let error = '';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/posts`);
|
||||
if (response.ok) {
|
||||
posts = await response.json();
|
||||
} else {
|
||||
error = 'Failed to fetch posts';
|
||||
}
|
||||
} catch (e) {
|
||||
error = 'Could not connect to backend';
|
||||
}
|
||||
|
||||
function formatSlug(slug: string) {
|
||||
return slug
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title="Home">
|
||||
<div class="space-y-8">
|
||||
<section class="text-center py-12">
|
||||
<h1 class="text-5xl font-extrabold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-mauve via-blue to-teal">
|
||||
Welcome to my blog
|
||||
</h1>
|
||||
<p class="text-subtext1 text-lg max-w-2xl mx-auto">
|
||||
Thoughts on software, design, and building things with Rust and Astro.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="grid gap-6">
|
||||
{error && (
|
||||
<div class="glass p-6 text-red text-center border-red/20">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.length === 0 && !error && (
|
||||
<div class="glass p-12 text-center text-subtext0">
|
||||
<p>No posts found yet. Add some .md files to the data/posts directory!</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{posts.map((post) => (
|
||||
<a href={`/posts/${post.slug}`} class="group">
|
||||
<article class="glass p-8 transition-all hover:scale-[1.01] hover:bg-surface0/80 active:scale-[0.99]">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-lavender group-hover:text-mauve transition-colors mb-2">
|
||||
{formatSlug(post.slug)}
|
||||
</h2>
|
||||
<p class="text-subtext0">
|
||||
Read more about {formatSlug(post.slug)}...
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-mauve opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
64
frontend/src/pages/posts/[slug].astro
Normal file
64
frontend/src/pages/posts/[slug].astro
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
import { marked } from 'marked';
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const API_URL = import.meta.env.PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
interface PostDetail {
|
||||
slug: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
let post: PostDetail | null = null;
|
||||
let html = '';
|
||||
let error = '';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/posts/${slug}`);
|
||||
if (response.ok) {
|
||||
post = await response.json();
|
||||
if (post) {
|
||||
html = await marked.parse(post.content);
|
||||
}
|
||||
} else {
|
||||
error = 'Post not found';
|
||||
}
|
||||
} catch (e) {
|
||||
error = 'Could not connect to backend';
|
||||
}
|
||||
|
||||
function formatSlug(slug: string) {
|
||||
return slug
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title={post ? formatSlug(post.slug) : 'Post'}>
|
||||
<article class="glass p-12 mb-12 animate-in fade-in slide-in-from-bottom-4 duration-700">
|
||||
<header class="mb-12 border-b border-white/5 pb-12">
|
||||
<a href="/" class="text-blue hover:text-sky transition-colors mb-8 inline-flex items-center gap-2 group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="transition-transform group-hover:-translate-x-1"><path d="m15 18-6-6 6-6"/></svg>
|
||||
Back to list
|
||||
</a>
|
||||
{post && (
|
||||
<h1 class="text-5xl font-extrabold text-mauve mt-4">
|
||||
{formatSlug(post.slug)}
|
||||
</h1>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{error && (
|
||||
<div class="text-red text-center py-12">
|
||||
<h2 class="text-2xl font-bold mb-4">{error}</h2>
|
||||
<a href="/" class="text-blue underline">Return home</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{post && (
|
||||
<div class="prose prose-invert max-w-none" set:html={html} />
|
||||
)}
|
||||
</article>
|
||||
</Layout>
|
||||
Reference in New Issue
Block a user