stable state
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import SafeImage from "./SafeImage";
|
||||
|
||||
interface EducationItem {
|
||||
@@ -11,33 +10,21 @@ interface EducationItem {
|
||||
logo?: string;
|
||||
}
|
||||
|
||||
interface EducationProps {
|
||||
education: EducationItem[];
|
||||
interface CertificateItem {
|
||||
name: string;
|
||||
issuer: string;
|
||||
date: string;
|
||||
credentialId: string;
|
||||
}
|
||||
|
||||
function EduCard({ item, index }: { item: EducationItem; index: number }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setTimeout(() => el.classList.add("visible"), index * 100);
|
||||
obs.disconnect();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, [index]);
|
||||
interface EducationProps {
|
||||
education: EducationItem[];
|
||||
certificates: CertificateItem[];
|
||||
}
|
||||
|
||||
function EduCard({ item }: { item: EducationItem }) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="reveal"
|
||||
style={{
|
||||
border: "1px solid #1c1f26",
|
||||
padding: "1.75rem",
|
||||
@@ -45,6 +32,9 @@ function EduCard({ item, index }: { item: EducationItem; index: number }) {
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
transition: "border-color 0.3s",
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.borderColor = "#6b5730")}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.borderColor = "#1c1f26")}
|
||||
@@ -113,26 +103,80 @@ function EduCard({ item, index }: { item: EducationItem; index: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function Education({ education }: EducationProps) {
|
||||
const headingRef = useRef<HTMLDivElement>(null);
|
||||
function CertCard({ item }: { item: CertificateItem }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #1c1f26",
|
||||
padding: "1.75rem",
|
||||
background: "#0e1014",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
transition: "border-color 0.3s",
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.borderColor = "#c8a96e")}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.borderColor = "#1c1f26")}
|
||||
>
|
||||
<div
|
||||
className="font-mono"
|
||||
style={{
|
||||
fontFamily: "var(--font-jetbrains), monospace",
|
||||
fontSize: "0.62rem",
|
||||
letterSpacing: "0.12em",
|
||||
color: "#c8a96e",
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
{item.date}
|
||||
</div>
|
||||
|
||||
useEffect(() => {
|
||||
const el = headingRef.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) { el.classList.add("visible"); obs.disconnect(); }
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, []);
|
||||
<h3
|
||||
className="font-display"
|
||||
style={{
|
||||
fontFamily: "var(--font-bebas), sans-serif",
|
||||
fontSize: "1.15rem",
|
||||
letterSpacing: "0.04em",
|
||||
color: "#e2e4e9",
|
||||
lineHeight: 1.3,
|
||||
marginBottom: "0.5rem",
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "var(--font-lora), serif",
|
||||
fontSize: "0.85rem",
|
||||
color: "#6b7280",
|
||||
lineHeight: 1.5,
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
{item.issuer}
|
||||
</p>
|
||||
|
||||
<div style={{
|
||||
fontFamily: "var(--font-jetbrains), monospace",
|
||||
fontSize: "0.55rem",
|
||||
color: "#4a5060",
|
||||
letterSpacing: "0.12em",
|
||||
textTransform: "uppercase",
|
||||
marginTop: "auto"
|
||||
}}>
|
||||
ID: {item.credentialId}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Education({ education, certificates }: EducationProps) {
|
||||
return (
|
||||
<section id="education" style={{ padding: "8rem 2rem" }}>
|
||||
<div style={{ maxWidth: "1200px", margin: "0 auto" }}>
|
||||
<div ref={headingRef} className="reveal" style={{ marginBottom: "4rem" }}>
|
||||
<div style={{ marginBottom: "4rem" }}>
|
||||
<div className="section-label" style={{ marginBottom: "0.75rem" }}>Academic Background</div>
|
||||
<h2
|
||||
className="font-display"
|
||||
@@ -152,18 +196,55 @@ export default function Education({ education }: EducationProps) {
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(min(100%, 340px), 1fr))",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 340px), 1fr))",
|
||||
gap: "1px",
|
||||
background: "#1c1f26",
|
||||
border: "1px solid #1c1f26",
|
||||
}}
|
||||
>
|
||||
{education.map((item, i) => (
|
||||
<div key={item.degree} style={{ background: "#07080a" }}>
|
||||
<EduCard item={item} index={i} />
|
||||
{education.map((item) => (
|
||||
<div key={item.degree} style={{ background: "#07080a", display: "flex" }}>
|
||||
<EduCard item={item} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{certificates && certificates.length > 0 && (
|
||||
<div style={{ marginTop: "6rem" }}>
|
||||
<div style={{ marginBottom: "3rem" }}>
|
||||
<div className="section-label" style={{ marginBottom: "0.75rem" }}>Licenses & Certifications</div>
|
||||
<h3
|
||||
className="font-display"
|
||||
style={{
|
||||
fontFamily: "var(--font-bebas), sans-serif",
|
||||
fontSize: "clamp(2rem, 4vw, 3.5rem)",
|
||||
letterSpacing: "0.04em",
|
||||
color: "#e2e4e9",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
Certificates
|
||||
</h3>
|
||||
<div style={{ width: "32px", height: "1px", background: "#c8a96e", marginTop: "1rem" }} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 340px), 1fr))",
|
||||
gap: "1px",
|
||||
background: "#1c1f26",
|
||||
border: "1px solid #1c1f26",
|
||||
}}
|
||||
>
|
||||
{certificates.map((cert) => (
|
||||
<div key={cert.name} style={{ background: "#07080a", display: "flex" }}>
|
||||
<CertCard item={cert} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user