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.
This commit is contained in:
@@ -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<string, any>;
|
||||
}
|
||||
|
||||
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<StatusData | null>(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 (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="h-full flex flex-col justify-center space-y-6"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full bg-liquid-mint/10 flex items-center justify-center">
|
||||
<Quote size={18} className="text-liquid-mint" />
|
||||
</div>
|
||||
<p className="text-xl md:text-2xl font-light leading-tight text-stone-300 italic">
|
||||
“{idleQuote || "Gerade am Planen des nächsten großen Projekts."}”
|
||||
</p>
|
||||
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-stone-500">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-stone-700" /> Currently Idle
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* CODING */}
|
||||
{data.coding?.isActive && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-green-500/10 border border-green-500/20 rounded-2xl p-4">
|
||||
{data?.coding?.isActive && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-green-500/10 border border-green-500/20 rounded-2xl p-5">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<Zap size={14} className="text-green-400 animate-pulse" />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-green-400">Coding Now</span>
|
||||
</div>
|
||||
<p className="font-bold text-white text-sm truncate">{data.coding.project}</p>
|
||||
<p className="font-bold text-white text-lg truncate">{data.coding.project}</p>
|
||||
<p className="text-xs text-white/50 truncate">{data.coding.file}</p>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* GAMING */}
|
||||
{data.gaming?.isPlaying && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-indigo-500/10 border border-indigo-500/20 rounded-2xl p-4">
|
||||
{data?.gaming?.isPlaying && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-indigo-500/10 border border-indigo-500/20 rounded-2xl p-5">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Gamepad2 size={14} className="text-indigo-400" />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-indigo-400">Gaming</span>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
{data.gaming.image && <div className="w-10 h-10 rounded-lg overflow-hidden shrink-0"><img src={data.gaming.image} className="w-full h-full object-cover" /></div>}
|
||||
<div className="min-w-0">
|
||||
<p className="font-bold text-white text-sm truncate">{data.gaming.name}</p>
|
||||
<p className="text-xs text-white/50 truncate">{getSafeGamingText(data.gaming.details, data.gaming.state, "In Game")}</p>
|
||||
<div className="flex gap-4">
|
||||
{data.gaming.image && <div className="w-12 h-12 rounded-xl overflow-hidden shrink-0 shadow-lg"><img src={data.gaming.image} className="w-full h-full object-cover" /></div>}
|
||||
<div className="min-w-0 flex flex-col justify-center">
|
||||
<p className="font-bold text-white text-base truncate">{data.gaming.name}</p>
|
||||
<p className="text-xs text-white/50 truncate">In Game</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* MUSIC */}
|
||||
{data.music?.isPlaying && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-white/5 border border-white/10 rounded-2xl p-4">
|
||||
{data?.music?.isPlaying && (
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="bg-white/5 border border-white/10 rounded-2xl p-5">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Disc3 size={14} className="text-green-400 animate-spin-slow" />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-white/40">Spotify</span>
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-white/40">Listening</span>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<div className="w-10 h-10 rounded-lg overflow-hidden shrink-0 shadow-lg"><img src={data.music.albumArt} className="w-full h-full object-cover" /></div>
|
||||
<div className="min-w-0">
|
||||
<p className="font-bold text-white text-sm truncate">{data.music.track}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="w-12 h-12 rounded-xl overflow-hidden shrink-0 shadow-2xl"><img src={data.music.albumArt} className="w-full h-full object-cover" /></div>
|
||||
<div className="min-w-0 flex flex-col justify-center">
|
||||
<p className="font-bold text-white text-base truncate">{data.music.track}</p>
|
||||
<p className="text-xs text-white/50 truncate">{data.music.artist}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user