All materials
BookingForm.jsx
jsxBookingForm.jsx
import { useState, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { fetchTrek, createBooking } from '../api';
export default function BookingForm() {
const { trekId } = useParams();
const navigate = useNavigate();
const [trek, setTrek] = useState(null);
const [form, setForm] = useState({
customer_name: '',
customer_email: '',
group_size: 1,
booking_date: ''
});
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
fetchTrek(trekId).then(setTrek).catch(err => setError(err.message));
}, [trekId]);
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitting(true);
setError(null);
try {
await createBooking({ ...form, trek_id: parseInt(trekId) });
navigate('/bookings');
} catch (err) {
setError(err.message);
} finally {
setSubmitting(false);
}
};
if (!trek) return <p className="text-stone-500">Loading...</p>;
return (
<div className="max-w-lg">
<Link to={`/treks/${trekId}`} className="text-sm text-amber-700 hover:text-amber-800">← Back to trek</Link>
<h1 className="text-2xl font-bold text-stone-800 mt-4">Book: {trek.name}</h1>
<p className="text-stone-500 mt-1">{trek.duration_days} days · NPR {trek.price_npr?.toLocaleString()}</p>
{error && <p className="mt-4 text-red-600 text-sm">{error}</p>}
<form onSubmit={handleSubmit} className="mt-6 space-y-4">
<div>
<label className="block text-sm font-medium text-stone-700">Name</label>
<input
type="text"
required
value={form.customer_name}
onChange={e => setForm({ ...form, customer_name: e.target.value })}
className="mt-1 w-full border border-stone-300 rounded px-3 py-2 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-stone-700">Email</label>
<input
type="email"
required
value={form.customer_email}
onChange={e => setForm({ ...form, customer_email: e.target.value })}
className="mt-1 w-full border border-stone-300 rounded px-3 py-2 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-stone-700">
Group size (max {trek.max_group_size})
</label>
<input
type="number"
min="1"
max={trek.max_group_size}
required
value={form.group_size}
onChange={e => setForm({ ...form, group_size: parseInt(e.target.value) })}
className="mt-1 w-full border border-stone-300 rounded px-3 py-2 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-stone-700">Preferred date</label>
<input
type="date"
required
value={form.booking_date}
onChange={e => setForm({ ...form, booking_date: e.target.value })}
className="mt-1 w-full border border-stone-300 rounded px-3 py-2 text-sm"
/>
</div>
<button
type="submit"
disabled={submitting}
className="w-full bg-amber-600 text-white py-2 rounded hover:bg-amber-700 transition-colors disabled:opacity-50"
>
{submitting ? 'Booking...' : 'Confirm Booking'}
</button>
</form>
</div>
);
}