ProjectDetail.jsx
vibe_coding/src/pages/ProjectDetail.jsx
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { getProjectBySlug, getNeighbours } from '../data/projects.js';
export default function ProjectDetail() {
const { slug } = useParams();
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const project = getProjectBySlug(slug);
useEffect(() => {
if (!project) navigate('/proyectos', { replace: true });
}, [project, navigate]);
if (!project) return null;
const lang = (i18n.resolvedLanguage || 'es').slice(0, 2);
const fallback = (obj) => (obj?.[lang] ?? obj?.es ?? '');
const { prev, next } = getNeighbours(project.slug);
const gallery = project.gallery || [];
return (
<article>
<section className="proj-hero">
{project.cover ? (
<img
src={project.cover}
alt={fallback(project.title)}
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
/>
) : (
<div className="placeholder">{fallback(project.title)}</div>
)}
<div className="proj-hero__inner">
<span className="hero__tag">{t(`projects.tags.${project.type}`)}</span>
<h1>{fallback(project.title)}</h1>
<p>{fallback(project.summary)}</p>
</div>
</section>
<div className="container--narrow">
<dl className="proj-meta">
<div>
<dt>{t('projects.detail.type')}</dt>
<dd>{fallback(project.meta?.type)}</dd>
</div>
<div>
<dt>{t('projects.detail.area')}</dt>
<dd>{fallback(project.meta?.area)}</dd>
</div>
<div>
<dt>{t('projects.detail.year')}</dt>
<dd>{fallback(project.meta?.year)}</dd>
</div>
<div>
<dt>{t('projects.detail.location')}</dt>
<dd>{fallback(project.meta?.location)}</dd>
</div>
</dl>
<blockquote className="proj-question">
{t('projects.detail.question')}
<br />
<span style={{ color: 'var(--color-text)', fontSize: '0.9em' }}>
— {fallback(project.question)}
</span>
</blockquote>
<section className="proj-section">
<span className="eyebrow">{t('projects.detail.process')}</span>
<h3>{t('projects.detail.process')}</h3>
<div className="proj-gallery">
{gallery.length === 0 ? (
<>
<div className="placeholder">Sketch</div>
<div className="placeholder">Plan</div>
<div className="placeholder">Render</div>
<div className="placeholder">Detail</div>
</>
) : (
gallery.map((g, i) => (
<div
key={i}
className={`placeholder ${g.kind === 'tall' ? 'tall' : ''}`}
>
{g.label || `0${i + 1}`}
</div>
))
)}
</div>
</section>
<section className="proj-section">
<span className="eyebrow">{t('projects.detail.description')}</span>
<h3>{t('projects.detail.description')}</h3>
<p>{fallback(project.description)}</p>
</section>
<section className="proj-section">
<span className="eyebrow">{t('projects.detail.materials')}</span>
<h3>{t('projects.detail.materials')}</h3>
<p>{fallback(project.materials)}</p>
</section>
<nav className="proj-nav">
{prev ? (
<Link to={`/proyectos/${prev.slug}`}>← {t('projects.detail.prev')}</Link>
) : (
<span />
)}
<Link to="/proyectos">{t('projects.detail.back')}</Link>
{next ? (
<Link to={`/proyectos/${next.slug}`}>{t('projects.detail.next')} →</Link>
) : (
<span />
)}
</nav>
</div>
</article>
);
}
Articoli correlati
App.jsx
App.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/App.jsx).
Leggi l'articolo →Footer.jsx
Footer.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/Footer.jsx).
Leggi l'articolo →LanguageSwitcher.jsx
LanguageSwitcher.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/LanguageSwitcher.jsx).
Leggi l'articolo →Navbar.jsx
Navbar.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/Navbar.jsx).
Leggi l'articolo →ProjectCard.jsx
ProjectCard.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/ProjectCard.jsx).
Leggi l'articolo →ScrollToTop.jsx
ScrollToTop.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/ScrollToTop.jsx).
Leggi l'articolo →