// sections/Portfolio.jsx — project archive · v2 print index
function PortfolioSection({ onOpen }) {
const [filter, setFilter] = React.useState('all');
const filters = [
{ id: 'all', label: 'All' },
{ id: 'fbp', label: 'FBP' },
{ id: 'course', label: 'Courses' },
{ id: 'internship', label: 'Internship' },
];
const matches = (p) => {
if (filter === 'all') return true;
return p.kind === filter;
};
const filtered = PROJECTS.filter(matches);
return (
{/* Filter tray */}
{filters.map(f => (
setFilter(f.id)}>
{f.label}
))}
{filtered.length} {filtered.length === 1 ? 'track' : 'tracks'}
{/* Index table */}
{filtered.map(p => (
onOpen(p)} />
))}
{/* Card grid below */}
D · Featured
Featured
{filtered.slice(0, 3).map(p => (
onOpen(p)} />
))}
);
}
function FilterTab({ active, onClick, children }) {
const [hover, setHover] = React.useState(false);
return (
);
}
function ProjectRow({ project, onClick }) {
const [hover, setHover] = React.useState(false);
return (
setHover(true)}
onMouseLeave={() => setHover(false)}
className="pf-grid"
style={{
display: 'grid',
gridTemplateColumns: '52px 1fr 220px 120px 32px',
gap: 16,
padding: '16px 8px',
alignItems: 'center',
borderBottom: '1px solid var(--c-border)',
background: hover ? '#FAFBFC' : 'transparent',
cursor: 'pointer',
transition: 'background 160ms',
}}>
{project.number}
{project.title} — {project.subtitle}
{project.desc}
{project.venue}
{project.year}
→
);
}
function ProjectCardLocal({ project, onClick }) {
const [hover, setHover] = React.useState(false);
return (
setHover(true)}
onMouseLeave={() => setHover(false)}
style={{
background: '#FFFFFF',
border: `1px solid ${hover ? 'var(--c-smoke)' : 'var(--c-border)'}`,
overflow: 'hidden',
transition: 'transform 240ms var(--ease-spring), border-color 180ms',
transform: hover ? 'translateY(-3px)' : 'translateY(0)',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
}}>
Track {project.number}
{project.number}
{project.year} · {project.venue}
{project.title}
{project.desc}
{project.tags.map((t, i) => {t.label})}
);
}
window.PortfolioSection = PortfolioSection;