Contact.jsx
vibe_coding/src/pages/Contact.jsx
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
const FORMSPREE_ENDPOINT = import.meta.env.VITE_FORMSPREE_ENDPOINT || '';
export default function Contact() {
const { t } = useTranslation();
const [status, setStatus] = useState('idle');
const [error, setError] = useState('');
const onSubmit = async (e) => {
e.preventDefault();
setStatus('sending');
setError('');
const data = new FormData(e.currentTarget);
if (!FORMSPREE_ENDPOINT) {
console.info('Formspree endpoint not configured. Submission:', Object.fromEntries(data));
setTimeout(() => setStatus('ok'), 600);
e.currentTarget.reset();
return;
}
try {
const res = await fetch(FORMSPREE_ENDPOINT, {
method: 'POST',
body: data,
headers: { Accept: 'application/json' }
});
if (res.ok) {
setStatus('ok');
e.currentTarget.reset();
} else {
setStatus('err');
setError(t('contact.error'));
}
} catch (err) {
setStatus('err');
setError(t('contact.error'));
}
};
return (
<>
<header className="page-head">
<span className="eyebrow">{t('nav.contact')}</span>
<h1>{t('contact.title')}</h1>
<p>{t('contact.subtitle')}</p>
</header>
<section className="section">
<div className="container">
<div className="contact">
<aside className="contact__info">
<h3>{t('contact.infoTitle')}</h3>
<ul>
<li>
<small>Email</small>
<a href="mailto:hola@chijia.design">hola@chijia.design</a>
</li>
<li>
<small>LinkedIn</small>
<a href="https://www.linkedin.com/" target="_blank" rel="noopener noreferrer">
linkedin.com/in/chijia
</a>
</li>
<li>
<small>{t('contact.city')}</small>
<span>Madrid, España</span>
</li>
</ul>
</aside>
<form className="contact__form" onSubmit={onSubmit} noValidate>
<h3 style={{ marginTop: 0 }}>{t('contact.formTitle')}</h3>
<div className="field">
<label htmlFor="name">{t('contact.name')}</label>
<input id="name" name="name" type="text" required />
</div>
<div className="field">
<label htmlFor="email">{t('contact.email')}</label>
<input id="email" name="email" type="email" required />
</div>
<div className="field">
<label htmlFor="message">{t('contact.message')}</label>
<textarea id="message" name="message" required />
</div>
<button type="submit" className="btn btn--primary" disabled={status === 'sending'}>
{status === 'sending' ? t('contact.sending') : t('contact.send')}
</button>
{status === 'ok' && (
<p className="form-status form-status--ok">{t('contact.success')}</p>
)}
{status === 'err' && (
<p className="form-status form-status--err">{error}</p>
)}
</form>
</div>
<p className="contact__closing">{t('contact.closing')}</p>
</div>
</section>
</>
);
}
Artículos relacionados
App.jsx
App.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/App.jsx).
Leer artículo →Footer.jsx
Footer.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/Footer.jsx).
Leer artículo →LanguageSwitcher.jsx
LanguageSwitcher.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/LanguageSwitcher.jsx).
Leer artículo →Navbar.jsx
Navbar.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/Navbar.jsx).
Leer artículo →ProjectCard.jsx
ProjectCard.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/ProjectCard.jsx).
Leer artículo →ScrollToTop.jsx
ScrollToTop.jsx — javascript source code from the vibe coding learning materials (vibe_coding/src/components/ScrollToTop.jsx).
Leer artículo →