feat: Add LEVIS Holzbau — Kinder-Holzwerk-Website (Port 3013)
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 39s
CI / test-python-voice (push) Successful in 37s
CI / test-bqas (push) Successful in 37s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 39s
CI / test-python-voice (push) Successful in 37s
CI / test-bqas (push) Successful in 37s
Neue statische Website fuer Kinder (6-12 Jahre) mit 8 Holzprojekten, SVG-Illustrationen, Sicherheitshinweisen und kindgerechtem Design. Next.js 15 + Tailwind + Framer Motion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
120
levis-holzbau/app/projekte/[slug]/page.tsx
Normal file
120
levis-holzbau/app/projekte/[slug]/page.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft, Clock, Wrench, Package } from 'lucide-react'
|
||||
import { projects, getProject, getRelatedProjects } from '@/lib/projects'
|
||||
import { DifficultyBadge } from '@/components/DifficultyBadge'
|
||||
import { AgeBadge } from '@/components/AgeBadge'
|
||||
import { StepCard } from '@/components/StepCard'
|
||||
import { SafetyTip } from '@/components/SafetyTip'
|
||||
import { ToolIcon } from '@/components/ToolIcon'
|
||||
import { ProjectIllustration } from '@/components/ProjectIllustration'
|
||||
import { ProjectCard } from '@/components/ProjectCard'
|
||||
|
||||
export function generateStaticParams() {
|
||||
return projects.map((p) => ({ slug: p.slug }))
|
||||
}
|
||||
|
||||
export default async function ProjectPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params
|
||||
const project = getProject(slug)
|
||||
if (!project) notFound()
|
||||
|
||||
const related = getRelatedProjects(slug)
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Back */}
|
||||
<Link href="/projekte" className="inline-flex items-center gap-1 text-accent hover:underline mb-6 text-sm font-semibold">
|
||||
<ArrowLeft className="w-4 h-4" /> Alle Projekte
|
||||
</Link>
|
||||
|
||||
{/* Hero */}
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-primary/5 overflow-hidden mb-8">
|
||||
<div className="bg-cream p-10 flex items-center justify-center">
|
||||
<ProjectIllustration slug={project.slug} size={180} />
|
||||
</div>
|
||||
<div className="p-6 sm:p-8">
|
||||
<div className="flex flex-wrap items-center gap-3 mb-3">
|
||||
<AgeBadge range={project.ageRange} />
|
||||
<DifficultyBadge level={project.difficulty} />
|
||||
<span className="flex items-center gap-1 text-sm text-dark/50">
|
||||
<Clock className="w-4 h-4" /> {project.duration}
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-heading font-bold text-3xl sm:text-4xl mb-3">{project.name}</h1>
|
||||
<p className="text-dark/70 text-lg leading-relaxed">{project.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tools & Materials */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
|
||||
<div className="bg-white rounded-2xl p-6 border border-primary/5">
|
||||
<h2 className="font-heading font-bold text-lg flex items-center gap-2 mb-4">
|
||||
<Wrench className="w-5 h-5 text-primary" /> Werkzeuge
|
||||
</h2>
|
||||
<ul className="space-y-2">
|
||||
{project.tools.map((t) => (
|
||||
<li key={t} className="flex items-center gap-2 text-sm">
|
||||
<ToolIcon name={t} />
|
||||
{t}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl p-6 border border-primary/5">
|
||||
<h2 className="font-heading font-bold text-lg flex items-center gap-2 mb-4">
|
||||
<Package className="w-5 h-5 text-secondary" /> Material
|
||||
</h2>
|
||||
<ul className="space-y-2">
|
||||
{project.materials.map((m) => (
|
||||
<li key={m} className="flex items-center gap-2 text-sm">
|
||||
<span className="w-2 h-2 rounded-full bg-secondary flex-shrink-0" />
|
||||
{m}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Safety */}
|
||||
<div className="space-y-3 mb-10">
|
||||
<h2 className="font-heading font-bold text-xl mb-2">Sicherheitshinweise</h2>
|
||||
{project.safetyTips.map((tip) => (
|
||||
<SafetyTip key={tip}>{tip}</SafetyTip>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Steps */}
|
||||
<div className="mb-10">
|
||||
<h2 className="font-heading font-bold text-xl mb-6">Schritt fuer Schritt</h2>
|
||||
<div className="space-y-0">
|
||||
{project.steps.map((step, i) => (
|
||||
<StepCard key={i} step={step} index={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skills */}
|
||||
<div className="bg-secondary/5 rounded-2xl p-6 mb-12">
|
||||
<h2 className="font-heading font-bold text-xl mb-3">Was du lernst</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{project.skills.map((s) => (
|
||||
<span key={s} className="px-3 py-1.5 bg-secondary/10 text-secondary rounded-full text-sm font-semibold">
|
||||
{s}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Related */}
|
||||
<div>
|
||||
<h2 className="font-heading font-bold text-xl mb-6">Aehnliche Projekte</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{related.map((p) => (
|
||||
<ProjectCard key={p.slug} project={p} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
59
levis-holzbau/app/projekte/page.tsx
Normal file
59
levis-holzbau/app/projekte/page.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ProjectCard } from '@/components/ProjectCard'
|
||||
import { projects } from '@/lib/projects'
|
||||
|
||||
const filters = [
|
||||
{ label: 'Alle', value: 0 },
|
||||
{ label: 'Anfaenger', value: 1 },
|
||||
{ label: 'Fortgeschritten', value: 2 },
|
||||
{ label: 'Profi', value: 3 },
|
||||
]
|
||||
|
||||
export default function ProjektePage() {
|
||||
const [filter, setFilter] = useState(0)
|
||||
const filtered = filter === 0 ? projects : projects.filter((p) => p.difficulty === filter)
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-center mb-10"
|
||||
>
|
||||
<h1 className="font-heading font-bold text-4xl mb-3">Alle Projekte</h1>
|
||||
<p className="text-dark/60 text-lg">Waehle ein Projekt und leg los!</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Filter */}
|
||||
<div className="flex justify-center gap-2 mb-10">
|
||||
{filters.map((f) => (
|
||||
<button
|
||||
key={f.value}
|
||||
onClick={() => setFilter(f.value as 0 | 1 | 2 | 3)}
|
||||
className={`px-4 py-2 rounded-xl font-semibold text-sm transition-colors ${
|
||||
filter === f.value
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-white text-dark/60 hover:bg-primary/5'
|
||||
}`}
|
||||
>
|
||||
{f.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filtered.map((p) => (
|
||||
<ProjectCard key={p.slug} project={p} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<p className="text-center text-dark/40 mt-12">Keine Projekte in dieser Kategorie.</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user