trip-planner/frontend/src/components/plannables/PlannableForm.jsx

203 lines
5.7 KiB
React
Raw Normal View History

import { useState, useEffect } from 'react';
import ModalErrorDisplay from '../common/ModalErrorDisplay';
import './PlannableForm.css';
const PlannableForm = ({ item, tripId, calendarSlots, onSubmit, onCancel }) => {
const [formData, setFormData] = useState({
name: '',
type: 'attraction',
address: '',
notes: '',
calendar_slot_id: null
});
const [errors, setErrors] = useState({});
const [submitting, setSubmitting] = useState(false);
const [submitError, setSubmitError] = useState(null);
useEffect(() => {
if (item) {
setFormData({
name: item.name || '',
type: item.type || 'attraction',
address: item.address || '',
notes: item.notes || '',
calendar_slot_id: item.calendar_slot_id || null
});
}
}, [item]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value === '' ? null : value
}));
// Clear error for this field
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: null
}));
}
// Clear submit error when user starts typing
if (submitError) {
setSubmitError(null);
}
};
const validate = () => {
const newErrors = {};
if (!formData.name || formData.name.trim() === '') {
newErrors.name = 'Name is required';
}
if (!formData.type) {
newErrors.type = 'Type is required';
}
return newErrors;
};
const handleSubmit = async (e) => {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setSubmitting(true);
setSubmitError(null);
try {
await onSubmit(formData);
} catch (err) {
console.error('Form submission error:', err);
setSubmitError(err.message || 'Failed to save item. Please try again.');
} finally {
setSubmitting(false);
}
};
return (
<div className="plannable-form-overlay">
<div className="plannable-form-modal">
<div className="form-header">
<h2>{item ? 'Edit Item' : 'Add New Item'}</h2>
<button className="btn-close" onClick={onCancel}>×</button>
</div>
<form onSubmit={handleSubmit} className="plannable-form">
<ModalErrorDisplay
error={submitError}
onDismiss={() => setSubmitError(null)}
/>
<div className="form-group">
<label>Name *</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
className={`form-control ${errors.name ? 'error' : ''}`}
placeholder="Enter item name"
autoFocus
/>
{errors.name && <span className="error-message">{errors.name}</span>}
</div>
<div className="form-group">
<label>Type *</label>
<select
name="type"
value={formData.type}
onChange={handleChange}
className={`form-control ${errors.type ? 'error' : ''}`}
>
<option value="hotel">🏨 Hotel</option>
<option value="restaurant">🍽 Restaurant</option>
<option value="attraction">🎯 Attraction</option>
<option value="transport"> Transport</option>
<option value="activity">🎭 Activity</option>
</select>
{errors.type && <span className="error-message">{errors.type}</span>}
</div>
<div className="form-group">
<label>Address</label>
<input
type="text"
name="address"
value={formData.address}
onChange={handleChange}
className="form-control"
placeholder="Enter address (optional)"
/>
</div>
<div className="form-group">
<label>Assign to Day</label>
<select
name="calendar_slot_id"
value={formData.calendar_slot_id || ''}
onChange={handleChange}
className="form-control"
>
<option value="">Unplanned</option>
{calendarSlots.map(slot => {
const slotDate = new Date(slot.slot_date);
const dateStr = slotDate.toLocaleDateString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric'
});
return (
<option key={slot.id} value={slot.id}>
{slot.name} - {dateStr}
</option>
);
})}
</select>
</div>
<div className="form-group">
<label>Notes</label>
<textarea
name="notes"
value={formData.notes}
onChange={handleChange}
className="form-control"
rows="3"
placeholder="Add any additional notes (optional)"
/>
</div>
<div className="form-actions">
<button
type="button"
className="btn-secondary"
onClick={onCancel}
disabled={submitting}
>
Cancel
</button>
<button
type="submit"
className="btn-primary"
disabled={submitting}
>
{submitting ? 'Saving...' : (item ? 'Update' : 'Add')} Item
</button>
</div>
</form>
</div>
</div>
);
};
export default PlannableForm;