From 91eb446ac5deade960bde15d452bb89289af380b Mon Sep 17 00:00:00 2001 From: denshooter Date: Mon, 16 Feb 2026 01:35:35 +0100 Subject: [PATCH] fix: cleanup footer, smart navigation, and projects redesign Removed aggressive background text in footer. Implemented intelligent back button for projects. Redesigned project archive page. Stabilized idle quote logic in activity feed. --- app/_ui/ProjectDetailClient.tsx | 68 +- app/_ui/ProjectsPageClient.tsx | 276 +-- app/components/Footer.tsx | 187 +- app/components/Header.tsx | 45 +- messages/de.json | 1 + messages/en.json | 1 + public/404-terminal.html | 1700 ------------------ scripts/migrate-content-pages-to-directus.js | 123 -- scripts/migrate-hobbies-to-directus.js | 185 -- scripts/migrate-tech-stack-to-directus.js | 240 --- 10 files changed, 176 insertions(+), 2650 deletions(-) delete mode 100644 public/404-terminal.html delete mode 100644 scripts/migrate-content-pages-to-directus.js delete mode 100644 scripts/migrate-hobbies-to-directus.js delete mode 100644 scripts/migrate-tech-stack-to-directus.js diff --git a/app/_ui/ProjectDetailClient.tsx b/app/_ui/ProjectDetailClient.tsx index c079794..51abe32 100644 --- a/app/_ui/ProjectDetailClient.tsx +++ b/app/_ui/ProjectDetailClient.tsx @@ -3,10 +3,11 @@ import { motion } from "framer-motion"; import { ExternalLink, Calendar, ArrowLeft, Github as GithubIcon, Share2, Code } from "lucide-react"; import Link from "next/link"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import ReactMarkdown from "react-markdown"; import { useTranslations } from "next-intl"; import Image from "next/image"; +import { useRouter } from "next/navigation"; export type ProjectDetailData = { id: number; @@ -35,9 +36,15 @@ export default function ProjectDetailClient({ }) { const tCommon = useTranslations("common"); const tDetail = useTranslations("projects.detail"); - const tShared = useTranslations("projects.shared"); + const router = useRouter(); + const [canGoBack, setCanGoBack] = useState(false); useEffect(() => { + // Prüfen, ob wir eine History haben (von Home gekommen) + if (typeof window !== 'undefined' && window.history.length > 1) { + setCanGoBack(true); + } + try { navigator.sendBeacon?.( "/api/analytics/track", @@ -46,18 +53,31 @@ export default function ProjectDetailClient({ } catch {} }, [project.id, project.slug, locale]); + const handleBack = (e: React.MouseEvent) => { + e.preventDefault(); + // Wenn wir direkt auf die Seite gekommen sind (Deep Link), gehen wir zur Projektliste + // Ansonsten nutzen wir den Browser-Back, um an die exakte Stelle der Home oder Liste zurückzukehren + if (canGoBack) { + router.back(); + } else { + router.push(`/${locale}/projects`); + } + }; + return (
- {/* Navigation */} - - {tCommon("backToProjects")} - + + {tCommon("back")} + + {/* Title Section */}
@@ -82,10 +102,7 @@ export default function ProjectDetailClient({
- {/* Bento Details Grid */}
- - {/* Main Content */}
@@ -94,12 +111,9 @@ export default function ProjectDetailClient({
- {/* Sidebar Boxes */}
- - {/* Quick Links Box */} - {/* Tech Stack Box */}
-

Stack

+

Stack

{project.tags.map((tag) => ( @@ -127,27 +140,6 @@ export default function ProjectDetailClient({ ))}
- - {/* Meta Stats */} -
-
-
-
-
-

Release Date

-

{new Date(project.date).toLocaleDateString(locale, { year: 'numeric', month: 'long' })}

-
-
-
-
-
-

Category

-

{project.category}

-
-
-
-
-
diff --git a/app/_ui/ProjectsPageClient.tsx b/app/_ui/ProjectsPageClient.tsx index b84c989..9bbbe83 100644 --- a/app/_ui/ProjectsPageClient.tsx +++ b/app/_ui/ProjectsPageClient.tsx @@ -2,22 +2,19 @@ import { useEffect, useMemo, useState } from "react"; import { motion } from "framer-motion"; -import { ExternalLink, Github, Calendar, ArrowLeft, Search } from "lucide-react"; +import { ArrowUpRight, ArrowLeft, Search } from "lucide-react"; import Link from "next/link"; -import { useTranslations } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; +import Image from "next/image"; export type ProjectListItem = { id: number; slug: string; title: string; description: string; - content: string; tags: string[]; - featured: boolean; category: string; date: string; - github?: string | null; - live?: string | null; imageUrl?: string | null; }; @@ -30,15 +27,9 @@ export default function ProjectsPageClient({ }) { const tCommon = useTranslations("common"); const tList = useTranslations("projects.list"); - const tShared = useTranslations("projects.shared"); const [selectedCategory, setSelectedCategory] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); const categories = useMemo(() => { const unique = Array.from(new Set(projects.map((p) => p.category))).filter(Boolean); @@ -47,253 +38,100 @@ export default function ProjectsPageClient({ const filteredProjects = useMemo(() => { let result = projects; - if (selectedCategory !== "all") { result = result.filter((project) => project.category === selectedCategory); } - if (searchQuery) { const query = searchQuery.toLowerCase(); result = result.filter( - (project) => - project.title.toLowerCase().includes(query) || - project.description.toLowerCase().includes(query) || - project.tags.some((tag) => tag.toLowerCase().includes(query)), + (p) => p.title.toLowerCase().includes(query) || p.description.toLowerCase().includes(query) || p.tags.some(t => t.toLowerCase().includes(query)) ); } - return result; }, [projects, selectedCategory, searchQuery]); - if (!mounted) return null; - return ( -
-
+
+
+ {/* Header */} - +
- {tCommon("backToHome")} + {tCommon("backToHome")} - -

- {tList("title")} + +

+ Archive.

-

{tList("intro")}

- +

+ {tList("intro")} +

+
- {/* Filters & Search */} - - {/* Categories */} + {/* Filters */} +
- {categories.map((category) => ( + {categories.map((cat) => ( ))}
- - {/* Search */} -
- - + + setSearchQuery(e.target.value)} - className="w-full pl-10 pr-4 py-2 bg-white border border-stone-200 rounded-full text-stone-800 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-200 focus:border-stone-400 transition-all" + className="w-full bg-white dark:bg-stone-900 border border-stone-200 dark:border-stone-800 rounded-2xl py-4 pl-12 pr-6 focus:outline-none focus:ring-2 focus:ring-liquid-mint/30 transition-all shadow-sm" />
- +
- {/* Projects Grid */} -
- {filteredProjects.map((project, index) => ( - - {/* Image / Fallback / Cover Area */} -
- {project.imageUrl ? ( - <> - {/* eslint-disable-next-line @next/next/no-img-element */} - {project.title} -
- - ) : ( -
-
-
-
- -
- - {project.title.charAt(0)} - + {/* Grid */} +
+ {filteredProjects.map((project) => ( + + +
+ {project.imageUrl && ( +
+ {project.title} +
+ )} +
+
+

{project.title}

+
+ +
+
+

{project.description}

+
+ {project.tags.slice(0, 3).map(tag => ( + {tag} + ))}
- )} - - {/* Texture/Grain Overlay */} -
- - {/* Animated Shine Effect */} - - - + ))}
- - {filteredProjects.length === 0 && ( -
-

{tList("noResults")}

- -
- )}
); } - diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 7c3a51f..52cb273 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -1,142 +1,85 @@ "use client"; -import { useState } from 'react'; -import { motion } from 'framer-motion'; -import { Heart, Code } from 'lucide-react'; -import { SiGithub, SiLinkedin } from 'react-icons/si'; -import Link from 'next/link'; +import React from "react"; +import Link from "next/link"; import { useLocale, useTranslations } from "next-intl"; -import { useConsent } from "./ConsentProvider"; +import { Github, Linkedin, Mail, ArrowUp } from "lucide-react"; +import { motion } from "framer-motion"; const Footer = () => { const locale = useLocale(); const t = useTranslations("footer"); - const { resetConsent } = useConsent(); + const year = new Date().getFullYear(); - const [currentYear] = useState(() => new Date().getFullYear()); - - const socialLinks = [ - { icon: SiGithub, href: 'https://github.com/Denshooter', label: 'GitHub' }, - { icon: SiLinkedin, href: 'https://linkedin.com/in/dkonkol', label: 'LinkedIn' } - ]; + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }; return ( -