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

203 lines
No EOL
5.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;