From 7955dfbabbb4451fd0ce33600a0b09b07fca1c2a Mon Sep 17 00:00:00 2001 From: denshooter Date: Mon, 16 Feb 2026 01:30:04 +0100 Subject: [PATCH] style: unified bento design across all sub-pages Applied the editorial look to legal notice and privacy policy pages. Created consistent grid-based layouts for easier reading and a premium feel. --- app/components/About.tsx | 126 ++++++++++++++++++-------------- app/components/ActivityFeed.tsx | 84 +++++++++++---------- app/legal-notice/page.tsx | 122 +++++++++++++++++++++---------- app/privacy-policy/page.tsx | 118 ++++++++++++++++++++++-------- 4 files changed, 289 insertions(+), 161 deletions(-) diff --git a/app/components/About.tsx b/app/components/About.tsx index cda6d92..b3c5825 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -1,14 +1,13 @@ "use client"; import { useState, useEffect } from "react"; -import { Globe, Server, Wrench, Shield, Gamepad2, Code, Activity, Lightbulb, BookOpen, MessageSquare, ArrowRight } from "lucide-react"; +import { Globe, Server, Wrench, Shield, Gamepad2, Code, Activity, Lightbulb, BookOpen, MessageSquare, ArrowUpRight, Book } from "lucide-react"; import { useLocale, useTranslations } from "next-intl"; import type { JSONContent } from "@tiptap/react"; import RichTextClient from "./RichTextClient"; import CurrentlyReading from "./CurrentlyReading"; -import ReadBooks from "./ReadBooks"; import { motion } from "framer-motion"; -import { TechStackCategory, Hobby } from "@/lib/directus"; +import { TechStackCategory, Hobby, BookReview } from "@/lib/directus"; import Link from "next/link"; import ActivityFeed from "./ActivityFeed"; import BentoChat from "./BentoChat"; @@ -23,17 +22,18 @@ const About = () => { const [cmsDoc, setCmsDoc] = useState(null); const [techStack, setTechStack] = useState([]); const [hobbies, setHobbies] = useState([]); - const [isActivityActive, setIsActivityActive] = useState(false); + const [reviewsCount, setReviewsCount] = useState(0); const [cmsMessages, setCmsMessages] = useState>({}); useEffect(() => { const fetchData = async () => { try { - const [cmsRes, techRes, hobbiesRes, msgRes] = await Promise.all([ + const [cmsRes, techRes, hobbiesRes, msgRes, booksRes] = await Promise.all([ fetch(`/api/content/page?key=home-about&locale=${locale}`), fetch(`/api/tech-stack?locale=${locale}`), fetch(`/api/hobbies?locale=${locale}`), - fetch(`/api/messages?locale=${locale}`) + fetch(`/api/messages?locale=${locale}`), + fetch(`/api/book-reviews?locale=${locale}`) ]); const cmsData = await cmsRes.json(); @@ -47,6 +47,9 @@ const About = () => { const msgData = await msgRes.json(); if (msgData?.messages) setCmsMessages(msgData.messages); + + const booksData = await booksRes.json(); + if (booksData?.bookReviews) setReviewsCount(booksData.bookReviews.length); } catch (error) {} }; fetchData(); @@ -58,7 +61,7 @@ const About = () => {
- {/* 1. Large Bio Text */} + {/* 1. Bio Box */} { {cmsDoc ? :

{t("p1")} {t("p2")}

}
-
+

{t("funFactTitle")}

{t("funFactBody")}

@@ -81,7 +84,7 @@ const About = () => {
- {/* 2. Activity / Status Box */} + {/* 2. Status Box (Currently) */} { transition={{ delay: 0.1 }} className="md:col-span-4 bg-stone-900 rounded-[3rem] p-10 border border-stone-800 shadow-2xl text-white overflow-hidden relative flex flex-col" > -
+

- Currently + Status

- -
- setIsActivityActive(active)} /> - - {!isActivityActive && ( - -

- “{cmsMessages["about.quote.idle"] || "Gerade am Planen des nächsten großen Projekts."}” -

-
- )} -
+
-
+
- {/* 3. AI Chat Box (Integrated) */} + {/* 3. AI Chat Box */}
-

AI Twin

+

AI Assistant

@@ -138,13 +126,13 @@ const About = () => { transition={{ delay: 0.3 }} className="md:col-span-12 lg:col-span-8 bg-white dark:bg-stone-900 rounded-[3rem] p-10 md:p-16 border border-stone-200/60 dark:border-stone-800/60 shadow-sm" > -
+
{techStack.map((cat) => (

{cat.name}

{cat.items?.map((item: any) => ( - + {item.name} ))} @@ -154,43 +142,69 @@ const About = () => {
- {/* 5. Library & Hobbies */} + {/* 5. Library (Visual Teaser) */} -
-
-
-

- Library +
+
+
+

+ Library

- - View All - +

ARCHIVE OF KNOWLEDGE

+ + + +
+ +
+
+
+ +
+
+

{reviewsCount}+ Books

+

Read and summarized in my personal collection.

+
+
+
+ -
-
- {hobbies.map((hobby) => { - const Icon = iconMap[hobby.icon] || Lightbulb; - return ( -
+ {/* 6. Hobbies (Clean Editorial Look) */} + +

+ Beyond Dev +

+
+ {hobbies.map((hobby) => { + const Icon = iconMap[hobby.icon] || Lightbulb; + return ( +
+
- ) - })} -
-
-

{t("hobbiesTitle")}

-

Curiosity beyond software engineering.

-
+
+

{hobby.title}

+

Passion & Mindset

+
+
+ ) + })}
diff --git a/app/components/ActivityFeed.tsx b/app/components/ActivityFeed.tsx index 2c59803..ac31fbb 100644 --- a/app/components/ActivityFeed.tsx +++ b/app/components/ActivityFeed.tsx @@ -2,15 +2,8 @@ import React, { useEffect, useState } from "react"; import Image from "next/image"; -import { motion, AnimatePresence } from "framer-motion"; -import { - Code2, - Disc3, - Gamepad2, - Zap, - Clock, - Activity, -} from "lucide-react"; +import { motion } from "framer-motion"; +import { Code2, Disc3, Gamepad2, Zap, BookOpen, Quote } from "lucide-react"; interface StatusData { status: { text: string; color: string; }; @@ -20,15 +13,13 @@ interface StatusData { customActivities?: Record; } -function getSafeGamingText(details: string | number | undefined, state: string | number | undefined, fallback: string): string { - if (typeof details === 'string' && details.trim().length > 0) return details; - if (typeof state === 'string' && state.trim().length > 0) return state; - if (typeof details === 'number' && !isNaN(details)) return String(details); - if (typeof state === 'number' && !isNaN(state)) return String(state); - return fallback; -} - -export default function ActivityFeed({ onActivityChange }: { onActivityChange?: (active: boolean) => void }) { +export default function ActivityFeed({ + onActivityChange, + idleQuote +}: { + onActivityChange?: (active: boolean) => void; + idleQuote?: string; +}) { const [data, setData] = useState(null); const [hasActivity, setHasActivity] = useState(false); @@ -60,50 +51,65 @@ export default function ActivityFeed({ onActivityChange }: { onActivityChange?: return () => clearInterval(interval); }, [onActivityChange]); - if (!data || !hasActivity) return null; + if (!hasActivity) { + return ( + +
+ +
+

+ “{idleQuote || "Gerade am Planen des nächsten großen Projekts."}” +

+
+ Currently Idle +
+
+ ); + } return (
- {/* CODING */} - {data.coding?.isActive && ( - + {data?.coding?.isActive && ( +
Coding Now
-

{data.coding.project}

+

{data.coding.project}

{data.coding.file}

)} - {/* GAMING */} - {data.gaming?.isPlaying && ( - + {data?.gaming?.isPlaying && ( +
Gaming
-
- {data.gaming.image &&
} -
-

{data.gaming.name}

-

{getSafeGamingText(data.gaming.details, data.gaming.state, "In Game")}

+
+ {data.gaming.image &&
} +
+

{data.gaming.name}

+

In Game

)} - {/* MUSIC */} - {data.music?.isPlaying && ( - + {data?.music?.isPlaying && ( +
- Spotify + Listening
-
-
-
-

{data.music.track}

+
+
+
+

{data.music.track}

{data.music.artist}

diff --git a/app/legal-notice/page.tsx b/app/legal-notice/page.tsx index 124e687..9278a9f 100644 --- a/app/legal-notice/page.tsx +++ b/app/legal-notice/page.tsx @@ -2,7 +2,7 @@ import React from "react"; import { motion } from 'framer-motion'; -import { ArrowLeft } from 'lucide-react'; +import { ArrowLeft, Mail, MapPin, Scale, ShieldCheck, Clock } from 'lucide-react'; import Header from "../components/Header"; import Footer from "../components/Footer"; import Link from "next/link"; @@ -15,7 +15,6 @@ export default function LegalNotice() { const locale = useLocale(); const t = useTranslations("common"); const [cmsDoc, setCmsDoc] = useState(null); - const [cmsTitle, setCmsTitle] = useState(null); useEffect(() => { (async () => { @@ -26,7 +25,6 @@ export default function LegalNotice() { const data = await res.json(); if (data?.content?.content && data?.content?.locale === locale) { setCmsDoc(data.content.content as JSONContent); - setCmsTitle((data.content.title as string | null) ?? null); } } catch {} })(); @@ -36,11 +34,12 @@ export default function LegalNotice() {
+ + {/* Editorial Header */} {t("backToHome")} -

+

Legal.

- - {cmsDoc ? ( -
- -
- ) : ( -
-
-

Angaben gemäß § 5 TMG

-
-

Dennis Konkol

-

Auf dem Ziegenbrink 2B, 49082 Osnabrück, Deutschland

-

E-Mail: info@dk0.dev

+ {/* Bento Content Grid */} +
+ + {/* Main Legal Content (Large Box) */} + + {cmsDoc ? ( +
+ +
+ ) : ( +
+
+

+ Angaben gemäß § 5 TMG +

+
+

Dennis Konkol

+

Auf dem Ziegenbrink 2B

+

49082 Osnabrück, Deutschland

+
+
+ +
+

+ Haftungsausschluss +

+

+ Die Inhalte dieser Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte kann ich jedoch keine Gewähr übernehmen. +

+
+
+ )} +
+ + {/* Sidebar Widgets */} +
+ + {/* Quick Contact Box */} +
+

Direct Contact

+
+
+
+ +
+
+

Email

+ info@dk0.dev +
+
+
+
+ +
+
+

Location

+

Osnabrück, GER

+
- -
-

Haftung für Inhalte

-

- Als Diensteanbieter bin ich gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG bin ich als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen. -

-
- -
-

Last updated: 12.02.2025

-
- )} - + + {/* Meta Info Box */} +
+
+ +
+

Last Review

+

February 15, 2025

+
+
+

+ This legal notice applies to all contents on dk0.dev and related social media profiles. +

+
+ +
+
diff --git a/app/privacy-policy/page.tsx b/app/privacy-policy/page.tsx index 1ffebb8..8579224 100644 --- a/app/privacy-policy/page.tsx +++ b/app/privacy-policy/page.tsx @@ -2,7 +2,7 @@ import React from "react"; import { motion } from 'framer-motion'; -import { ArrowLeft } from 'lucide-react'; +import { ArrowLeft, Shield, Lock, Eye, Database, Globe } from 'lucide-react'; import Header from "../components/Header"; import Footer from "../components/Footer"; import Link from "next/link"; @@ -34,11 +34,12 @@ export default function PrivacyPolicy() {
+ + {/* Editorial Header */} {t("backToHome")} -

+

Privacy.

- - {cmsDoc ? ( -
- -
- ) : ( -
-
-

Datenschutz

-

- Der Schutz Ihrer persönlichen Daten ist mir ein besonderes Anliegen. Ich verarbeite Ihre Daten daher ausschließlich auf Grundlage der gesetzlichen Bestimmungen (DSGVO, TMG). -

-

Kontakt mit mir

-

- Wenn Sie per Formular auf der Website oder per E-Mail Kontakt mit mir aufnehmen, werden Ihre angegebenen Daten zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei mir gespeichert. Diese Daten gebe ich nicht ohne Ihre Einwilligung weiter. -

+ {/* Bento Content Grid */} +
+ + {/* Main Privacy Text (Large) */} + + {cmsDoc ? ( +
+
+ ) : ( +
+
+

+ Allgemeiner Überblick +

+

+ Der Schutz Ihrer persönlichen Daten ist mir ein besonderes Anliegen. Ich verarbeite Ihre Daten daher ausschließlich auf Grundlage der gesetzlichen Bestimmungen (DSGVO, TMG). +

+
-
-

Last updated: 12.02.2025

+
+

+ Datenerfassung +

+

+ Wenn Sie per Formular auf der Website oder per E-Mail Kontakt mit mir aufnehmen, werden Ihre angegebenen Daten zwecks Bearbeitung der Anfrage bei mir gespeichert. +

+
+
+ )} + + + {/* Quick Info Cards */} +
+ + {/* Core Values Box */} +
+

Principles

+
+
+ +
+

Encryption

+

SSL/TLS secured data transfer.

+
+
+
+ +
+

Transparency

+

No hidden tracking algorithms.

+
+
+
+ +
+

Compliance

+

GDPR / DSGVO optimized.

+
+
- )} - + + {/* Cookie Status Indicator */} +
+
+
+

Security Check

+
+

Your connection is private and secure.

+ +
+ +
+