Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
133
frontend/src/components/BookingForm.jsx
Normal file
133
frontend/src/components/BookingForm.jsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { useState } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import './BookingForm.css';
|
||||
|
||||
const BookingForm = ({ service, resources, onSubmit, onCancel, loading }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
resource: resources?.[0]?.id || '',
|
||||
date: '',
|
||||
time: '',
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
// Clear error for this field
|
||||
if (errors[name]) {
|
||||
setErrors(prev => ({ ...prev, [name]: '' }));
|
||||
}
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
const newErrors = {};
|
||||
|
||||
if (!formData.resource) {
|
||||
newErrors.resource = 'Please select a resource';
|
||||
}
|
||||
if (!formData.date) {
|
||||
newErrors.date = 'Please select a date';
|
||||
}
|
||||
if (!formData.time) {
|
||||
newErrors.time = 'Please select a time';
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine date and time into ISO format
|
||||
const startDateTime = new Date(`${formData.date}T${formData.time}`);
|
||||
const endDateTime = new Date(startDateTime.getTime() + service.duration * 60000);
|
||||
|
||||
const appointmentData = {
|
||||
service: service.id,
|
||||
resource: parseInt(formData.resource),
|
||||
start_time: startDateTime.toISOString(),
|
||||
end_time: endDateTime.toISOString(),
|
||||
};
|
||||
|
||||
onSubmit(appointmentData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="booking-form-container">
|
||||
<div className="booking-form-header">
|
||||
<h2>Book: {service.name}</h2>
|
||||
<button onClick={onCancel} className="close-btn">×</button>
|
||||
</div>
|
||||
|
||||
<div className="service-summary">
|
||||
<p><strong>Duration:</strong> {service.duration} minutes</p>
|
||||
<p><strong>Price:</strong> ${service.price}</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="booking-form">
|
||||
<div className="form-group">
|
||||
<label htmlFor="resource">Select Provider</label>
|
||||
<select
|
||||
id="resource"
|
||||
name="resource"
|
||||
value={formData.resource}
|
||||
onChange={handleChange}
|
||||
className={errors.resource ? 'error' : ''}
|
||||
>
|
||||
<option value="">Choose a provider...</option>
|
||||
{resources?.map((resource) => (
|
||||
<option key={resource.id} value={resource.id}>
|
||||
{resource.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.resource && <span className="error-message">{errors.resource}</span>}
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="date">Date</label>
|
||||
<input
|
||||
type="date"
|
||||
id="date"
|
||||
name="date"
|
||||
value={formData.date}
|
||||
onChange={handleChange}
|
||||
min={format(new Date(), 'yyyy-MM-dd')}
|
||||
className={errors.date ? 'error' : ''}
|
||||
/>
|
||||
{errors.date && <span className="error-message">{errors.date}</span>}
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="time">Time</label>
|
||||
<input
|
||||
type="time"
|
||||
id="time"
|
||||
name="time"
|
||||
value={formData.time}
|
||||
onChange={handleChange}
|
||||
className={errors.time ? 'error' : ''}
|
||||
/>
|
||||
{errors.time && <span className="error-message">{errors.time}</span>}
|
||||
</div>
|
||||
|
||||
<div className="form-actions">
|
||||
<button type="button" onClick={onCancel} className="btn-cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" className="btn-submit" disabled={loading}>
|
||||
{loading ? 'Booking...' : 'Confirm Booking'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingForm;
|
||||
Reference in New Issue
Block a user