diff --git a/app/components/About.tsx b/app/components/About.tsx index f0a69ef..faf2279 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -3,7 +3,7 @@ import { motion, Variants } from "framer-motion"; import { Globe, Server, Wrench, Shield, Gamepad2, Code, Activity, Lightbulb } from "lucide-react"; import { useEffect, useState } from "react"; -import { useLocale } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import type { JSONContent } from "@tiptap/react"; import RichTextClient from "./RichTextClient"; @@ -32,6 +32,7 @@ const fadeInUp: Variants = { const About = () => { const locale = useLocale(); + const t = useTranslations("home.about"); const [cmsDoc, setCmsDoc] = useState(null); useEffect(() => { @@ -99,7 +100,7 @@ const About = () => { variants={fadeInUp} className="text-4xl md:text-5xl font-bold text-stone-900" > - About Me + {t("title")} { ) : ( <> -

- Hi, I'm Dennis – a student and passionate self-hoster based - in Osnabrück, Germany. -

-

- I love building full-stack web applications with{" "} - Next.js and mobile apps with{" "} - Flutter. But what really excites me is{" "} - DevOps: I run my own infrastructure on{" "} - IONOS and OVHcloud, managing - everything with Docker Swarm,{" "} - Traefik, and automated CI/CD pipelines with my - own runners. -

-

- When I'm not coding or tinkering with servers, you'll - find me gaming, jogging, or - experimenting with new tech like game servers or automation - workflows with n8n. -

+

{t("p1")}

+

{t("p2")}

+

{t("p3")}

)} {

- Fun Fact + {t("funFactTitle")}

- Even though I automate a lot, I still use pen and paper - for my calendar and notes – it helps me clear my head and - stay focused. + {t("funFactBody")}

diff --git a/app/components/ActivityFeed.tsx b/app/components/ActivityFeed.tsx index a8653b3..335e6ab 100644 --- a/app/components/ActivityFeed.tsx +++ b/app/components/ActivityFeed.tsx @@ -1457,7 +1457,26 @@ export default function ActivityFeed() { }; // Don't render if tracking is disabled and no data - if (!isTrackingEnabled && !data) return null; + if (!isTrackingEnabled && !data) { + return ( +
+ + + +
+ ); + } // If tracking disabled but we have data, show a disabled state if (!isTrackingEnabled && data) { diff --git a/app/components/ConsentProvider.tsx b/app/components/ConsentProvider.tsx index 69e3c8d..7a3cea0 100644 --- a/app/components/ConsentProvider.tsx +++ b/app/components/ConsentProvider.tsx @@ -38,9 +38,11 @@ function writeConsentCookie(value: ConsentState) { const ConsentContext = createContext<{ consent: ConsentState | null; setConsent: (next: ConsentState) => void; + resetConsent: () => void; }>({ consent: null, setConsent: () => {}, + resetConsent: () => {}, }); export function ConsentProvider({ children }: { children: React.ReactNode }) { @@ -55,7 +57,16 @@ export function ConsentProvider({ children }: { children: React.ReactNode }) { writeConsentCookie(next); }, []); - const value = useMemo(() => ({ consent, setConsent }), [consent, setConsent]); + const resetConsent = useCallback(() => { + setConsentState(null); + // expire cookie + document.cookie = `${COOKIE_NAME}=; path=/; max-age=0; samesite=lax`; + }, []); + + const value = useMemo( + () => ({ consent, setConsent, resetConsent }), + [consent, setConsent, resetConsent], + ); return {children}; } diff --git a/app/components/Contact.tsx b/app/components/Contact.tsx index bd01ded..fb26a7e 100644 --- a/app/components/Contact.tsx +++ b/app/components/Contact.tsx @@ -4,20 +4,16 @@ import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { Mail, MapPin, Send } from "lucide-react"; import { useToast } from "@/components/Toast"; -import { useLocale } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import type { JSONContent } from "@tiptap/react"; import RichTextClient from "./RichTextClient"; const Contact = () => { - const [mounted, setMounted] = useState(false); const { showEmailSent, showEmailError } = useToast(); const locale = useLocale(); + const t = useTranslations("home.contact"); const [cmsDoc, setCmsDoc] = useState(null); - useEffect(() => { - setMounted(true); - }, []); - useEffect(() => { (async () => { try { @@ -164,10 +160,6 @@ const Contact = () => { }, ]; - if (!mounted) { - return null; - } - return (
{ className="text-center mb-16" >

- Contact Me + {t("title")}

{cmsDoc ? ( ) : (

- Interested in working together or have questions about my projects? - Feel free to reach out! + {t("subtitle")}

)} @@ -206,12 +197,10 @@ const Contact = () => { >

- Get In Touch + {t("getInTouch")}

- I'm always available to discuss new opportunities, - interesting projects, or simply chat about technology and - innovation. + {t("getInTouchBody")}

diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 83bd0c4..4fa4433 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -6,15 +6,15 @@ import { Heart, Code } from 'lucide-react'; import { SiGithub, SiLinkedin } from 'react-icons/si'; import Link from 'next/link'; import { useLocale } from "next-intl"; +import { useConsent } from "./ConsentProvider"; const Footer = () => { const [currentYear, setCurrentYear] = useState(2024); - const [mounted, setMounted] = useState(false); const locale = useLocale(); + const { resetConsent } = useConsent(); useEffect(() => { setCurrentYear(new Date().getFullYear()); - setMounted(true); }, []); const socialLinks = [ @@ -22,10 +22,6 @@ const Footer = () => { { icon: SiLinkedin, href: 'https://linkedin.com/in/dkonkol', label: 'LinkedIn' } ]; - if (!mounted) { - return null; - } - return (