154 lines
5 KiB
TypeScript
154 lines
5 KiB
TypeScript
'use client'
|
|
|
|
import { motion } from 'framer-motion'
|
|
import { ArrowUpRight, ExternalLink } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
import Link from 'next/link'
|
|
|
|
const projects = [
|
|
{
|
|
title: 'Hacker News Daily AI Reports',
|
|
slug: 'hackernews',
|
|
description: 'KI Tools werten zweimal täglich die Hacker News aus und beurteilen deren Relevanz in Bezug auf KI.',
|
|
tech: ['Python', 'FastAPI', 'Openrouter', 'deepseek-reasoner', 'qwen3-max'],
|
|
status: 'Aktiv',
|
|
url: 'https://jamulix.de/hackernews/',
|
|
featured: true,
|
|
},
|
|
{
|
|
title: 'Sichtbare Sortier-Algorithmen (Demo)',
|
|
slug: 'sorting',
|
|
description: 'Interaktive Visualisierung verschiedener Sortieralgorithmen. Didaktisches Werkzeug für Algorithmen-Verständnis.',
|
|
tech: ['Rust', 'WASM', 'Canvas'],
|
|
status: 'Aktiv',
|
|
url: 'https://jamulix.de/sorting',
|
|
featured: true,
|
|
},
|
|
{
|
|
title: 'Digitalisierung einer "Mundorgel"',
|
|
slug: 'upcoming-1',
|
|
description: 'KI-gestützte Digitalisierung eines Liederheftes von 1960.',
|
|
tech: ['Python', 'LLM', 'AST'],
|
|
status: 'In Entwicklung',
|
|
url: null,
|
|
featured: false,
|
|
},
|
|
{
|
|
title: 'Geplant',
|
|
slug: 'upcoming-2',
|
|
description: 'Self-hosted Infrastruktur-Monitoring. Lightweight, ohne Cloud-Abhängigkeiten.',
|
|
tech: ['Rust', 'Linux', 'SQLite'],
|
|
status: 'Geplant',
|
|
url: null,
|
|
featured: false,
|
|
},
|
|
]
|
|
|
|
function ProjectCard({ project, index }: { project: typeof projects[0]; index: number }) {
|
|
const isClickable = project.url !== null
|
|
|
|
return (
|
|
<motion.article
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true, margin: '-50px' }}
|
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
|
className={`group relative ${project.featured ? 'lg:col-span-1' : ''}`}
|
|
>
|
|
<div
|
|
className={`
|
|
relative h-full p-6 lg:p-8 rounded-lg border border-border
|
|
bg-card transition-all duration-300
|
|
${isClickable ? 'hover:border-accent/50 hover:bg-card/80' : 'opacity-70'}
|
|
`}
|
|
>
|
|
{/* Status badge */}
|
|
<div className="flex items-center justify-between mb-4">
|
|
<span
|
|
className={`
|
|
font-mono text-xs px-2 py-1 rounded
|
|
${project.status === 'Aktiv'
|
|
? 'bg-accent/10 text-accent'
|
|
: project.status === 'In Entwicklung'
|
|
? 'bg-secondary text-muted-foreground'
|
|
: 'bg-secondary/50 text-muted-foreground/70'
|
|
}
|
|
`}
|
|
>
|
|
{project.status}
|
|
</span>
|
|
{isClickable && (
|
|
<ArrowUpRight className="size-5 text-muted-foreground group-hover:text-accent transition-colors" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Title */}
|
|
<h3 className="font-serif text-xl lg:text-2xl mb-3 group-hover:text-accent transition-colors">
|
|
{project.title}
|
|
</h3>
|
|
|
|
{/* Description */}
|
|
<p className="text-muted-foreground leading-relaxed mb-6">
|
|
{project.description}
|
|
</p>
|
|
|
|
{/* Tech stack */}
|
|
<div className="flex flex-wrap gap-2 mb-6">
|
|
{project.tech.map((tech) => (
|
|
<span
|
|
key={tech}
|
|
className="font-mono text-xs px-2 py-1 bg-secondary text-secondary-foreground rounded"
|
|
>
|
|
{tech}
|
|
</span>
|
|
))}
|
|
</div>
|
|
|
|
{/* Link */}
|
|
{isClickable && (
|
|
<Button asChild variant="outline" size="sm" className="group/btn">
|
|
<Link href={project.url} target="_blank" rel="noopener noreferrer">
|
|
Ansehen
|
|
<ExternalLink className="ml-2 size-3 transition-transform group-hover/btn:translate-x-0.5" />
|
|
</Link>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</motion.article>
|
|
)
|
|
}
|
|
|
|
export function ProjectsSection() {
|
|
return (
|
|
<section id="projekte" className="py-24 lg:py-32 bg-secondary/30">
|
|
<div className="max-w-6xl mx-auto px-6 lg:px-8">
|
|
{/* Section Header */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.6 }}
|
|
className="mb-16"
|
|
>
|
|
<span className="font-mono text-xs tracking-wider text-accent uppercase">
|
|
Experimente
|
|
</span>
|
|
<h2 className="font-serif text-3xl sm:text-4xl lg:text-5xl mt-4 text-balance">
|
|
Ausgewählte junge Projekte
|
|
</h2>
|
|
<p className="mt-6 text-lg text-muted-foreground max-w-2xl">
|
|
Technische Experimente und Werkzeuge. Weniger Portfolio,
|
|
mehr Labor für Ideen.
|
|
</p>
|
|
</motion.div>
|
|
|
|
{/* Projects Grid */}
|
|
<div className="grid md:grid-cols-2 gap-6 lg:gap-8">
|
|
{projects.map((project, index) => (
|
|
<ProjectCard key={project.slug} project={project} index={index} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|