From 4dc727fcd62eb5e6297e1d1070b25d3c47d32f8d Mon Sep 17 00:00:00 2001 From: denshooter Date: Tue, 6 Jan 2026 20:10:00 +0100 Subject: [PATCH] feat: add activity feed and background effects - Implemented ActivityFeed component to display real-time user activity including coding, music, and chat interactions. - Added GooFilter and BackgroundBlobs components for enhanced visual effects. - Updated layout to include new components and ensure proper stacking context. - Enhanced Tailwind CSS configuration with new color and font settings. - Created API route to mock activity data from n8n. - Refactored main page structure to streamline component rendering. --- app/api/n8n/status/route.ts | 19 + app/components/About.tsx | 206 +++------ app/components/ActivityFeed.tsx | 160 +++++++ app/components/Contact.tsx | 93 ++-- app/components/Footer.tsx | 36 +- app/components/Header.tsx | 79 ++-- app/components/Hero.tsx | 224 ++++------ app/components/Projects.tsx | 207 ++++----- app/globals.css | 734 +++++--------------------------- app/layout.tsx | 8 +- app/page.tsx | 10 +- components/BackgroundBlobs.tsx | 77 ++++ components/GooFilter.tsx | 33 ++ components/LiquidCursor.tsx | 5 + components/LiquidHeading.tsx | 19 + tailwind.config.ts | 63 ++- 16 files changed, 801 insertions(+), 1172 deletions(-) create mode 100644 app/api/n8n/status/route.ts create mode 100644 app/components/ActivityFeed.tsx create mode 100644 components/BackgroundBlobs.tsx create mode 100644 components/GooFilter.tsx create mode 100644 components/LiquidCursor.tsx create mode 100644 components/LiquidHeading.tsx diff --git a/app/api/n8n/status/route.ts b/app/api/n8n/status/route.ts new file mode 100644 index 0000000..06d0fe0 --- /dev/null +++ b/app/api/n8n/status/route.ts @@ -0,0 +1,19 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + // Mock data - in real integration this would fetch from n8n webhook or database + return NextResponse.json({ + activity: { + type: 'coding', // coding, listening, watching + details: 'Portfolio Website', + timestamp: new Date().toISOString(), + }, + music: { + isPlaying: true, + track: 'Midnight City', + artist: 'M83', + platform: 'spotify' + }, + watching: null + }); +} diff --git a/app/components/About.tsx b/app/components/About.tsx index 08abdba..54a59aa 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -2,7 +2,8 @@ import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; -import { Code, Database, Cloud, Smartphone, Globe, Zap, Brain, Rocket } from 'lucide-react'; +import { Code, Terminal, Cpu, Globe } from 'lucide-react'; +import { LiquidHeading } from '@/components/LiquidHeading'; const About = () => { const [mounted, setMounted] = useState(false); @@ -11,180 +12,83 @@ const About = () => { setMounted(true); }, []); - const skills = [ + const techStack = [ { category: 'Frontend', - icon: Code, - technologies: ['React', 'Next.js', 'TypeScript', 'Tailwind CSS', 'Framer Motion'], - color: 'from-blue-500 to-cyan-500' + icon: Globe, + items: ['React', 'TypeScript', 'Tailwind', 'Next.js'] }, { category: 'Backend', - icon: Database, - technologies: ['Node.js', 'PostgreSQL', 'Prisma', 'REST APIs', 'GraphQL'], - color: 'from-purple-500 to-pink-500' + icon: Terminal, + items: ['Node.js', 'PostgreSQL', 'Prisma', 'API Design'] }, { - category: 'DevOps', - icon: Cloud, - technologies: ['Docker', 'CI/CD', 'Nginx', 'Redis', 'AWS'], - color: 'from-green-500 to-emerald-500' - }, - { - category: 'Mobile', - icon: Smartphone, - technologies: ['React Native', 'Expo', 'iOS', 'Android'], - color: 'from-orange-500 to-red-500' - }, + category: 'Tools', + icon: Cpu, + items: ['Git', 'Docker', 'VS Code', 'Figma'] + } ]; - const values = [ - { - icon: Brain, - title: 'Problem Solving', - description: 'I love tackling complex challenges and finding elegant solutions.' - }, - { - icon: Zap, - title: 'Performance', - description: 'Building fast, efficient applications that scale with your needs.' - }, - { - icon: Rocket, - title: 'Innovation', - description: 'Always exploring new technologies and best practices.' - }, - { - icon: Globe, - title: 'User Experience', - description: 'Creating intuitive interfaces that users love to interact with.' - }, - ]; - - if (!mounted) { - return null; - } + if (!mounted) return null; return ( -
-
- {/* Section Header */} - -

- About Me -

-

- I'm a passionate software engineer with a love for creating beautiful, - functional applications. I enjoy working with modern technologies and - turning ideas into reality. -

-
- - {/* About Content */} -
- -

My Journey

-

- I'm a student and software engineer based in Osnabrück, Germany. - My passion for technology started early, and I've been building - applications ever since. -

-

- I specialize in full-stack development, with a focus on creating - modern, performant web applications. I'm always learning new - technologies and improving my skills. -

-

- When I'm not coding, I enjoy exploring new technologies, contributing - to open-source projects, and sharing knowledge with the developer community. -

-
- - -

What I Do

-
- {values.map((value, index) => ( - -
- -
-

{value.title}

-

{value.description}

-
- ))} +
+
+ +
+ {/* Text Content */} +
+ +
+

+ Hi, I'm Dennis. I'm a software engineer who likes building things that work well and look good. +

+

+ I'm currently based in Osnabrück, Germany. My journey in tech is driven by curiosity—I love figuring out how things work and how to make them better. +

+

+ When I'm not in front of a screen, you can find me listening to music, exploring new ideas, or just relaxing. +

- -
+
- {/* Skills Section */} - -

Skills & Technologies

-
- {skills.map((skill, index) => ( + {/* Simplified Skills / Tech Stack */} +
+

My Toolbox

+ {techStack.map((stack, idx) => ( -
- +
+
+ +
+

{stack.category}

-

{skill.category}

-
- {skill.technologies.map((tech) => ( -
- {tech} -
+
+ {stack.items.map(item => ( + + {item} + ))}
))}
- +
); }; -export default About; - - +export default About; \ No newline at end of file diff --git a/app/components/ActivityFeed.tsx b/app/components/ActivityFeed.tsx new file mode 100644 index 0000000..9a2f3ec --- /dev/null +++ b/app/components/ActivityFeed.tsx @@ -0,0 +1,160 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Music, Code, Monitor, MessageSquare, Send, X } from 'lucide-react'; + +interface ActivityData { + activity: { + type: 'coding' | 'listening' | 'watching'; + details: string; + timestamp: string; + } | null; + music: { + isPlaying: boolean; + track: string; + artist: string; + platform: 'spotify' | 'apple'; + } | null; + watching: { + title: string; + platform: 'youtube' | 'netflix'; + } | null; +} + +export const ActivityFeed = () => { + const [data, setData] = useState(null); + const [showChat, setShowChat] = useState(false); + const [chatMessage, setChatMessage] = useState(''); + const [chatHistory, setChatHistory] = useState<{ + role: 'user' | 'ai'; + text: string; + }[]>([ + { role: 'ai', text: 'Hi! I am Dennis\'s AI assistant. Ask me anything about him!' } + ]); + + useEffect(() => { + const fetchData = async () => { + try { + const res = await fetch('/api/n8n/status'); + if (res.ok) { + const json = await res.json(); + setData(json); + } + } catch (e) { + console.error('Failed to fetch activity', e); + } + }; + fetchData(); + const interval = setInterval(fetchData, 30000); // Poll every 30s + return () => clearInterval(interval); + }, []); + + const handleSendMessage = (e: React.FormEvent) => { + e.preventDefault(); + if (!chatMessage.trim()) return; + + const userMsg = chatMessage; + setChatHistory(prev => [...prev, { role: 'user', text: userMsg }]); + setChatMessage(''); + + // Mock AI response - would connect to n8n webhook + setTimeout(() => { + setChatHistory(prev => [...prev, { role: 'ai', text: `That's a great question about "${userMsg}"! I'll ask Dennis to add more info about that.` }]); + }, 1000); + }; + + if (!data) return null; + + return ( +
+ + {/* Chat Window */} + + {showChat && ( + +
+ + + Ask me anything + + +
+
+ {chatHistory.map((msg, i) => ( +
+
+ {msg.text} +
+
+ ))} +
+
+ setChatMessage(e.target.value)} + placeholder="Type a message..." + className="flex-1 bg-white/60 border border-white/60 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-stone-400" + /> + +
+
+ )} +
+ + {/* Activity Bubbles */} +
+ {data.activity?.type === 'coding' && ( + + + + + + + Working on {data.activity.details} + + )} + + {data.music?.isPlaying && ( + + + Listening to {data.music.track} + + )} + + {/* Chat Toggle Button */} + setShowChat(!showChat)} + className="bg-stone-900 text-white rounded-full p-4 shadow-xl hover:bg-black transition-all" + > + + +
+
+ ); +}; diff --git a/app/components/Contact.tsx b/app/components/Contact.tsx index 9494326..560b68a 100644 --- a/app/components/Contact.tsx +++ b/app/components/Contact.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Mail, MapPin, Send } from 'lucide-react'; import { useToast } from '@/components/Toast'; +import { LiquidHeading } from '@/components/LiquidHeading'; const Contact = () => { const [mounted, setMounted] = useState(false); @@ -139,23 +140,19 @@ const Contact = () => { } return ( -
+
{/* Section Header */} - -

- Contact Me -

-

+

+ +

Interested in working together or have questions about my projects? Feel free to reach out!

- +
{/* Contact Information */} @@ -167,10 +164,10 @@ const Contact = () => { className="space-y-8" >
-

+

Get In Touch

-

+

I'm always available to discuss new opportunities, interesting projects, or simply chat about technology and innovation.

@@ -187,14 +184,14 @@ const Contact = () => { viewport={{ once: true }} transition={{ duration: 0.6, delay: index * 0.1 }} whileHover={{ x: 5 }} - className="flex items-center space-x-4 p-4 rounded-lg glass-card hover:bg-gray-800/30 transition-colors group" + className="flex items-center space-x-4 p-4 rounded-2xl glass-card hover:bg-white/60 transition-colors group border-transparent hover:border-white/60" > -
- +
+
-

{info.title}

-

{info.value}

+

{info.title}

+

{info.value}

))} @@ -208,15 +205,15 @@ const Contact = () => { whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.8 }} - className="glass-card p-8 rounded-2xl" + className="glass-card p-8 rounded-3xl bg-white/40 border border-white/60" > -

Send Message

+

Send Message

-
-
-
-