update website
This commit is contained in:
+26
-295
@@ -1,298 +1,29 @@
|
||||
"use client"
|
||||
import {useRef, useState, useEffect} from "react";
|
||||
import Link from "next/link";
|
||||
import FadeInSection from "@/app/components/FadeInSection";
|
||||
|
||||
export const metadata = {title: "About"};
|
||||
const timeline = [
|
||||
{year: "2018", title: "The first spark", text: "I discovered programming through Scratch, turning small ideas into simple, playable games.", skills: ["Scratch", "Game logic"]},
|
||||
{year: "2022", title: "Into the web", text: "I began building websites and learned the foundations of HTML, CSS, and accessible interface design.", skills: ["HTML", "CSS", "JavaScript"]},
|
||||
{year: "2022", title: "From screen to hardware", text: "A Raspberry Pi opened the door to Python scripting, Linux, and the fun of connecting software with real hardware.", skills: ["Python", "Linux", "Raspberry Pi"]},
|
||||
{year: "2024", title: "Full stack & beyond", text: "I expanded into databases, backend systems, Java, and larger projects—while continuing to sharpen my eye for frontend craft.", skills: ["SQL", "Python", "Java", "JavaScript"]},
|
||||
];
|
||||
export default function About() {
|
||||
const timelineData = [
|
||||
{
|
||||
date: "2018",
|
||||
title: "First Contact with Coding",
|
||||
content:
|
||||
"I discovered my passion for technology by experimenting with Scratch and creating some simple Games.",
|
||||
},
|
||||
{
|
||||
date: "2022",
|
||||
title: "Exploring Web & Scripting",
|
||||
content:
|
||||
"Started creating small websites and got into HTML, CSS",
|
||||
},
|
||||
{
|
||||
date: "2022",
|
||||
title: "Raspberry Pi & Python",
|
||||
content:
|
||||
"Created some simple Python scripts for my Raspberry Pi, experimenting with the Raspberry Pi OS",
|
||||
},
|
||||
{
|
||||
date: "2024",
|
||||
title: "Full Stack & Beyond",
|
||||
content:
|
||||
"Ventured into backend (SQL, Python, Javascript, Java) and continued refining my frontend abilities.",
|
||||
},
|
||||
];
|
||||
|
||||
function getSkillEmoji(name) {
|
||||
switch (name) {
|
||||
case "JavaScript": return "🟨";
|
||||
case "Python": return "🐍";
|
||||
case "Java": return "☕";
|
||||
case "SQL": return "💾";
|
||||
case "HTML & CSS": return "🎨";
|
||||
default: return "💡";
|
||||
}
|
||||
}
|
||||
|
||||
const skills = [
|
||||
{
|
||||
name: "JavaScript",
|
||||
level: 80,
|
||||
waveSpeed: "2s",
|
||||
progression: [0, 30, 60, 80],
|
||||
},
|
||||
{
|
||||
name: "Python",
|
||||
level: 75,
|
||||
waveSpeed: "2.5s",
|
||||
progression: [10, 25, 50, 75],
|
||||
},
|
||||
{
|
||||
name: "Java",
|
||||
level: 50,
|
||||
waveSpeed: "3s",
|
||||
progression: [0, 0, 0, 60],
|
||||
},
|
||||
{
|
||||
name: "SQL",
|
||||
level: 85,
|
||||
waveSpeed: "2.2s",
|
||||
progression: [0, 0, 0, 85],
|
||||
},
|
||||
{
|
||||
name: "HTML & CSS",
|
||||
level: 70,
|
||||
waveSpeed: "2.7s",
|
||||
progression: [0, 30, 50, 80],
|
||||
},
|
||||
];
|
||||
|
||||
const getSkillLabel = (level) => {
|
||||
if (level < 25) return "Beginner";
|
||||
if (level < 50) return "Intermediate";
|
||||
if (level < 75) return "Advanced";
|
||||
return "Expert";
|
||||
};
|
||||
|
||||
const sectionRefs = useRef([]);
|
||||
const aboutMeRef = useRef(null);
|
||||
const buttonSectionRef = useRef(null);
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [currentSection, setCurrentSection] = useState(0);
|
||||
const [timelineFinished, setTimelineFinished] = useState(false);
|
||||
const [showScrollPrompt, setShowScrollPrompt] = useState(false);
|
||||
useEffect(() => {
|
||||
setCurrentSection(0);
|
||||
setTimelineFinished(false);
|
||||
setTimeout(() => {
|
||||
sectionRefs.current[0]?.scrollIntoView({behavior: "auto", inline: "start"});
|
||||
window.scrollTo({top: 0, behavior: "smooth"});
|
||||
}, 100);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let scrollListener;
|
||||
|
||||
if (timelineFinished) {
|
||||
setShowScrollPrompt(true);
|
||||
|
||||
scrollListener = () => {
|
||||
setShowScrollPrompt(false);
|
||||
window.removeEventListener("scroll", scrollListener);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", scrollListener);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (scrollListener) {
|
||||
window.removeEventListener("scroll", scrollListener);
|
||||
}
|
||||
};
|
||||
}, [timelineFinished]);
|
||||
const [isNavigating, setIsNavigating] = useState(false);
|
||||
const handleButtonClick = (direction) => {
|
||||
if (isNavigating) return;
|
||||
setIsNavigating(true);
|
||||
setCurrentSection((prev) => {
|
||||
const newSection = Math.max(0, Math.min(prev + direction, timelineData.length - 1));
|
||||
sectionRefs.current[newSection]?.scrollIntoView({behavior: "smooth", inline: "start"});
|
||||
return newSection;
|
||||
});
|
||||
setTimeout(() => {
|
||||
setIsNavigating(false);
|
||||
}, 600);
|
||||
};
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const index = sectionRefs.current.findIndex((el) => el === entry.target);
|
||||
if (entry.isIntersecting && index !== -1) {
|
||||
setCurrentSection(index);
|
||||
}
|
||||
if (entry.isIntersecting && index === timelineData.length - 1) {
|
||||
setTimelineFinished(true);
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
{threshold: 0.6}
|
||||
);
|
||||
sectionRefs.current.forEach((el) => {
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
return () => observer.disconnect();
|
||||
}, [timelineData.length]);
|
||||
useEffect(() => {
|
||||
setTimelineFinished(currentSection === timelineData.length - 1);
|
||||
}, [currentSection, timelineData.length]);
|
||||
return (
|
||||
<div style={{
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
overflowX: "hidden"
|
||||
}}>
|
||||
<FadeInSection>
|
||||
<section
|
||||
ref={aboutMeRef}
|
||||
style={{
|
||||
minHeight: "100vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "2rem",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<h1 style={{fontSize: "3.5rem", marginBottom: "1rem"}}>About Me</h1>
|
||||
<p style={{maxWidth: "600px", textAlign: "center", fontWeight: "300", fontSize: "1.5rem"}}>
|
||||
I’m a passionate developer always excited about new challenges.
|
||||
Scroll down to learn more about me!
|
||||
</p>
|
||||
</section>
|
||||
</FadeInSection>
|
||||
{/* Timeline Section */}
|
||||
<section style={{position: "relative", height: "100vh", overflowY: "hidden"}}>
|
||||
{/* Navigation buttons and timeline content here */}
|
||||
<div ref={scrollContainerRef} style={{
|
||||
display: "flex",
|
||||
overflowX: "auto",
|
||||
scrollSnapType: "x mandatory",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
scrollBehavior: "smooth",
|
||||
scrollbarWidth: "none",
|
||||
msOverflowStyle: "none"
|
||||
}}>
|
||||
{timelineData.map((item, index) => {
|
||||
const adjustedSkills = skills.map((s) => {
|
||||
const stepLevel = s.progression?.[index] ?? 0;
|
||||
return {
|
||||
...s,
|
||||
level: Math.floor((s.level * stepLevel) / 100),
|
||||
};
|
||||
});
|
||||
return (
|
||||
<section
|
||||
key={index}
|
||||
ref={(el) => (sectionRefs.current[index] = el)}
|
||||
className="timeline-page"
|
||||
>
|
||||
<div className="timeline-card">
|
||||
<div className="timeline-card-header">
|
||||
<span className="timeline-date">{item.date}</span>
|
||||
<h2>{item.title}</h2>
|
||||
</div>
|
||||
<p className="timeline-content">{item.content}</p>
|
||||
<div className="timeline-skills">
|
||||
{adjustedSkills.filter((s) => s.level > 0).map((skill, sIndex) => (
|
||||
<span
|
||||
key={`${skill.name}-${index}`}
|
||||
className={`skill-badge skill-${getSkillLabel(skill.level).toLowerCase()}`}
|
||||
style={{ animationDelay: `${sIndex * 80}ms` }}
|
||||
>
|
||||
{getSkillEmoji(skill.name)} {skill.name}
|
||||
<span className="skill-level">{getSkillLabel(skill.level)}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{currentSection > 0 && (
|
||||
<button
|
||||
onClick={() => handleButtonClick(-1)}
|
||||
className="timeline-arrow-btn left"
|
||||
aria-label="Previous"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
)}
|
||||
{currentSection < timelineData.length - 1 && (
|
||||
<button
|
||||
onClick={() => handleButtonClick(1)}
|
||||
className="timeline-arrow-btn right"
|
||||
aria-label="Next"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
{timelineFinished && showScrollPrompt && (
|
||||
<div className="scrollPrompt">
|
||||
⬇ Scroll down to continue ⬇
|
||||
</div>
|
||||
)}
|
||||
{/* FINAL BUTTON SECTION - MOVED OUTSIDE */}
|
||||
{timelineFinished && (
|
||||
// Fügen Sie dem Button-Section einen z-Index hinzu
|
||||
<section
|
||||
ref={buttonSectionRef}
|
||||
style={{
|
||||
minHeight: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
padding: "2rem",
|
||||
boxSizing: "border-box",
|
||||
position: "relative", // Hinzugefügt
|
||||
zIndex: 1 // Hinzugefügt
|
||||
}}
|
||||
>
|
||||
<Link href="/projects">
|
||||
<button
|
||||
className="learnMoreButton"
|
||||
style={{
|
||||
padding: "1rem 2rem",
|
||||
fontSize: "1.25rem",
|
||||
backgroundColor: "#3d003d",
|
||||
border: "none",
|
||||
borderRadius: "5px",
|
||||
color: "#fff",
|
||||
cursor: "pointer",
|
||||
transition: "background-color 0.3s ease",
|
||||
}}
|
||||
onMouseOver={(e) => (e.currentTarget.style.backgroundColor = "#2e002e")}
|
||||
onMouseOut={(e) => (e.currentTarget.style.backgroundColor = "#3d003d")}
|
||||
>
|
||||
View Projects
|
||||
</button>
|
||||
</Link>
|
||||
</section>
|
||||
)}
|
||||
return (<>
|
||||
<header className="page-head"><div className="shell page-head-row">
|
||||
<div><p className="eyebrow">About me</p><h1 className="page-title">Always<br/>learning.</h1></div>
|
||||
<p className="page-intro">I’m a developer driven by curiosity, thoughtful details, and the satisfying moment when a complex problem finally clicks.</p>
|
||||
</div></header>
|
||||
<div className="shell about-grid">
|
||||
<aside className="about-sticky">
|
||||
<p className="eyebrow">My path so far</p><h2>From playful experiments to complete products.</h2>
|
||||
<p>Every project has added a new tool, a sharper instinct, or a better question to ask.</p>
|
||||
<Link href="/projects" className="button">View projects</Link>
|
||||
</aside>
|
||||
<div className="timeline">{timeline.map(item => (
|
||||
<article className="timeline-item" key={`${item.year}-${item.title}`}>
|
||||
<div className="timeline-year">{item.year}</div>
|
||||
<div><h3>{item.title}</h3><p>{item.text}</p><div className="skills">{item.skills.map(skill => <span className="skill" key={skill}>{skill}</span>)}</div></div>
|
||||
</article>
|
||||
))}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user