mirror of
https://github.com/hex248/ob248.com.git
synced 2026-02-08 10:43:38 +00:00
project system
This commit is contained in:
39
src/components/ProjectListItem.astro
Normal file
39
src/components/ProjectListItem.astro
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
const { title, description, date, image, slug } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.project-item {
|
||||||
|
border-color: var(--ayu-gutter-dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.project-item:hover {
|
||||||
|
border-color: var(--ayu-gutter);
|
||||||
|
}
|
||||||
|
.project-title {
|
||||||
|
color: var(--ayu-accent);
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
.project-item:hover .project-title {
|
||||||
|
color: var(--ayu-red-500);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<a href={`/projects/${slug}`} class="project-item block border p-4 mb-4">
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<div class="w-16 h-16 flex-shrink-0">
|
||||||
|
{image ? (
|
||||||
|
<img src={image} alt={`${title} icon`} class="w-full h-full object-cover rounded" />
|
||||||
|
) : (
|
||||||
|
<div class="w-full h-full border border-ayu-gutter rounded"></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h3 class="project-title text-lg mb-2">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-ayu-fg mb-2">{description}</p>
|
||||||
|
<p class="text-xs text-ayu-gutter">{date}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
40
src/components/ProjectPage.astro
Normal file
40
src/components/ProjectPage.astro
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
metadata: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
date: string;
|
||||||
|
slug: string;
|
||||||
|
image?: string | null;
|
||||||
|
hidden: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { metadata } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout currentPage="projects">
|
||||||
|
<div class="text-md">
|
||||||
|
<h1 class="text-xl text-ayu-accent mb-2">
|
||||||
|
{metadata.title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{
|
||||||
|
metadata.image ? (
|
||||||
|
<img
|
||||||
|
src={metadata.image}
|
||||||
|
alt={`${metadata.title} project icon`}
|
||||||
|
class="w-32 h-32 mb-4 rounded"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div class="w-32 h-32 mb-4 border border-ayu-gutter rounded" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<p class="text-sm text-ayu-gutter mt-4">{metadata.date}</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
@@ -1,7 +1,84 @@
|
|||||||
---
|
---
|
||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
import ProjectListItem from "../components/ProjectListItem.astro";
|
||||||
|
|
||||||
|
interface ProjectMetadata {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
date: string;
|
||||||
|
slug: string;
|
||||||
|
hidden: boolean;
|
||||||
|
image?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AstroModule {
|
||||||
|
metadata?: ProjectMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDate(dateStr: string): Date {
|
||||||
|
// formats like "February 2024", "January - June 2024", "Q1 2023", etc
|
||||||
|
const lower = dateStr.toLowerCase();
|
||||||
|
|
||||||
|
if (lower.includes("q1")) return new Date("2023-01-01");
|
||||||
|
if (lower.includes("q2")) return new Date("2023-04-01");
|
||||||
|
if (lower.includes("q3")) return new Date("2023-07-01");
|
||||||
|
if (lower.includes("q4")) return new Date("2023-10-01");
|
||||||
|
|
||||||
|
const months: Record<string, number> = {
|
||||||
|
january: 0,
|
||||||
|
february: 1,
|
||||||
|
march: 2,
|
||||||
|
april: 3,
|
||||||
|
may: 4,
|
||||||
|
june: 5,
|
||||||
|
july: 6,
|
||||||
|
august: 7,
|
||||||
|
september: 8,
|
||||||
|
october: 9,
|
||||||
|
november: 10,
|
||||||
|
december: 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
// month and year
|
||||||
|
for (const [monthName, monthIndex] of Object.entries(months)) {
|
||||||
|
if (lower.includes(monthName)) {
|
||||||
|
const yearMatch = dateStr.match(/\b(20\d{2})\b/);
|
||||||
|
if (yearMatch) {
|
||||||
|
return new Date(parseInt(yearMatch[1]), monthIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback: try to extract any year
|
||||||
|
const yearMatch = dateStr.match(/\b(20\d{2})\b/);
|
||||||
|
if (yearMatch) {
|
||||||
|
return new Date(parseInt(yearMatch[1]), 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projects: ProjectMetadata[] = Object.values(
|
||||||
|
import.meta.glob<AstroModule>("./projects/*.astro", { eager: true })
|
||||||
|
)
|
||||||
|
.map((module) => module.metadata)
|
||||||
|
.filter((metadata): metadata is ProjectMetadata => metadata !== undefined)
|
||||||
|
.filter((project) => !project.hidden)
|
||||||
|
.sort((a, b) => parseDate(b.date).getTime() - parseDate(a.date).getTime());
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout currentPage="home">
|
<Layout currentPage="home">
|
||||||
<h1 class="text-md">home page</h1>
|
<h1 class="text-md mb-4">projects</h1>
|
||||||
|
|
||||||
|
{
|
||||||
|
projects.map((project) => (
|
||||||
|
<ProjectListItem
|
||||||
|
title={project.title}
|
||||||
|
description={project.description}
|
||||||
|
date={project.date}
|
||||||
|
image={project.image}
|
||||||
|
slug={project.slug}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
</Layout>
|
</Layout>
|
||||||
Reference in New Issue
Block a user