diff --git a/client/src/pages/SurgeryPage.js b/client/src/pages/SurgeryPage.js index 71c26fa..8aecd18 100644 --- a/client/src/pages/SurgeryPage.js +++ b/client/src/pages/SurgeryPage.js @@ -1,6 +1,6 @@ -// src/pages/SurgeryPage.jsx import React, { useState } from 'react'; import axios from 'axios'; +import { FaSpinner, FaCheckCircle, FaExclamationCircle, FaUpload } from 'react-icons/fa'; import styles from './SurgeryPage.module.css'; function SurgeryPage() { @@ -13,28 +13,114 @@ function SurgeryPage() { surgeryType: '', prescription: null, }); + + const [errors, setErrors] = useState({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitStatus, setSubmitStatus] = useState({ success: false, message: '' }); + const [selectedFile, setSelectedFile] = useState(null); + + const validateForm = () => { + const newErrors = {}; + + if (!formData.name.trim()) newErrors.name = 'Name is required'; + + if (!formData.email) { + newErrors.email = 'Email is required'; + } else if (!/\S+@\S+\.\S+/.test(formData.email)) { + newErrors.email = 'Email is invalid'; + } + + if (!formData.phone) { + newErrors.phone = 'Phone number is required'; + } else if (!/^\d{10}$/.test(formData.phone)) { + newErrors.phone = 'Phone number must be 10 digits'; + } + + if (!formData.doctor.trim()) newErrors.doctor = 'Doctor\'s name is required'; + if (!formData.date) newErrors.date = 'Please select a date'; + if (!selectedFile) newErrors.prescription = 'Please upload a prescription'; + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleFileChange = (e) => { + const file = e.target.files[0]; + if (file) { + const validTypes = ['application/pdf', 'image/jpeg', 'image/png']; + const maxSize = 5 * 1024 * 1024; // 5MB + + if (!validTypes.includes(file.type)) { + setErrors({ + ...errors, + prescription: 'Please upload a valid file type (PDF, JPG, or PNG)' + }); + return; + } + + if (file.size > maxSize) { + setErrors({ + ...errors, + prescription: 'File size should be less than 5MB' + }); + return; + } + + setSelectedFile(file); + setFormData({ ...formData, prescription: file }); + setErrors({ ...errors, prescription: null }); + } + }; const handleChange = (e) => { - if (e.target.name === 'prescription') { - setFormData({ ...formData, prescription: e.target.files[0] }); - } else { - setFormData({ ...formData, [e.target.name]: e.target.value }); + const { name, value } = e.target; + setFormData({ + ...formData, + [name]: value + }); + + // Clear error when user types + if (errors[name]) { + setErrors({ + ...errors, + [name]: null + }); } }; const handleSubmit = async (e) => { e.preventDefault(); - + + if (!validateForm()) return; + + setIsSubmitting(true); + setSubmitStatus({ success: false, message: '' }); + const submissionData = new FormData(); - for (let key in formData) { - submissionData.append(key, formData[key]); - } + Object.keys(formData).forEach(key => { + if (formData[key] !== null) { + submissionData.append(key, formData[key]); + } + }); try { - await axios.post(`${process.env.REACT_APP_API_URL}/api/surgery/book`, submissionData, { - headers: { 'Content-Type': 'multipart/form-data' }, + const response = await axios.post( + `${process.env.REACT_APP_API_URL || 'http://localhost:5000'}/api/surgery/book`, + submissionData, + { + headers: { + 'Content-Type': 'multipart/form-data', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + } + ); + + setSubmitStatus({ + success: true, + message: 'Surgery appointment booked successfully! We will contact you soon.' }); - alert('Surgery appointment booked!'); + + // Reset form setFormData({ name: '', email: '', @@ -44,24 +130,139 @@ function SurgeryPage() { surgeryType: '', prescription: null, }); + setSelectedFile(null); + document.getElementById('prescription-upload').value = ''; + } catch (error) { - alert('Booking failed.'); - console.error(error); + console.error('Booking failed:', error); + const errorMessage = error.response?.data?.error || 'Failed to book appointment. Please try again.'; + setSubmitStatus({ + success: false, + message: errorMessage + }); + } finally { + setIsSubmitting(false); } }; return (

Book a Surgery

-
- - - - - - - - + + {submitStatus.message && ( +
+ {submitStatus.success ? ( + + ) : ( + + )} + {submitStatus.message} +
+ )} + + +
+ + {errors.name && {errors.name}} +
+ +
+ + {errors.email && {errors.email}} +
+ +
+ + {errors.phone && {errors.phone}} +
+ +
+ + {errors.doctor && {errors.doctor}} +
+ +
+ +
+ +
+ + {errors.date && {errors.date}} +
+ +
+ + + {errors.prescription && ( + {errors.prescription} + )} +
+ +
); diff --git a/client/src/pages/SurgeryPage.module.css b/client/src/pages/SurgeryPage.module.css index aef413d..7638b9a 100644 --- a/client/src/pages/SurgeryPage.module.css +++ b/client/src/pages/SurgeryPage.module.css @@ -1,22 +1,45 @@ .surgeryContainer { - max-width: 600px; - margin: 40px auto; - padding: 30px; - background-color: #f9f9f9; - border-radius: 12px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + max-width: 800px; + margin: 2rem auto; + padding: 2.5rem; + background: #ffffff; + border-radius: 16px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .surgeryContainer h1 { text-align: center; - margin-bottom: 25px; - color: #2c3e50; + margin: 0 0 2rem 0; + color: #2d3748; + font-size: 2.2rem; + font-weight: 700; + position: relative; + padding-bottom: 1rem; +} + +.surgeryContainer h1::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 80px; + height: 4px; + background: linear-gradient(90deg, #4f46e5, #7c3aed); + border-radius: 2px; } .surgeryForm { display: flex; flex-direction: column; - gap: 15px; + gap: 1.5rem; +} + +.formGroup { + display: flex; + flex-direction: column; + gap: 0.5rem; } .surgeryForm input[type="text"], @@ -24,29 +47,180 @@ .surgeryForm input[type="tel"], .surgeryForm input[type="date"], .surgeryForm input[type="file"] { - padding: 10px 15px; - font-size: 16px; - border: 1px solid #ccc; - border-radius: 8px; + padding: 0.8rem 1.2rem; + font-size: 1rem; + border: 2px solid #e2e8f0; + border-radius: 10px; outline: none; - transition: border 0.3s ease; + transition: all 0.3s ease; + background-color: #f8fafc; + color: #1a202c; } .surgeryForm input:focus { - border-color: #3498db; + border-color: #4f46e5; + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2); +} + +.errorInput { + border-color: #ef4444 !important; + background-color: #fef2f2 !important; +} + +.errorText { + color: #ef4444; + font-size: 0.85rem; + margin-top: 0.25rem; + margin-left: 0.25rem; +} + +.fileUpload { + position: relative; + margin: 1rem 0; +} + +.fileInput { + position: absolute; + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + z-index: -1; +} + +.uploadLabel { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + padding: 1rem 1.5rem; + background-color: #f8fafc; + border: 2px dashed #cbd5e0; + border-radius: 10px; + color: #4a5568; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; +} + +.uploadLabel:hover { + background-color: #f1f5f9; + border-color: #93c5fd; +} + +.uploadIcon { + font-size: 1.25rem; + color: #4f46e5; } -.surgeryForm button { - padding: 12px 20px; - font-size: 16px; - background-color: #3498db; +.errorUpload .uploadLabel { + border-color: #ef4444; + background-color: #fef2f2; +} + +.submitButton { + margin-top: 1rem; + padding: 1rem 2rem; + font-size: 1.1rem; + font-weight: 600; color: white; + background: linear-gradient(90deg, #4f46e5, #7c3aed); border: none; - border-radius: 8px; + border-radius: 10px; cursor: pointer; - transition: background-color 0.3s ease; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.submitButton:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); +} + +.submitButton:disabled { + background: #cbd5e0; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.spinner { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.alert { + padding: 1rem 1.5rem; + margin-bottom: 1.5rem; + border-radius: 10px; + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 0.95rem; + line-height: 1.5; +} + +.success { + background-color: #f0fdf4; + color: #166534; + border-left: 4px solid #22c55e; +} + +.error { + background-color: #fef2f2; + color: #991b1b; + border-left: 4px solid #ef4444; +} + +.icon { + font-size: 1.25rem; + flex-shrink: 0; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .surgeryContainer { + margin: 1rem; + padding: 1.5rem; + } + + .surgeryContainer h1 { + font-size: 1.75rem; + } + + .surgeryForm { + gap: 1.25rem; + } + + .submitButton { + padding: 0.9rem 1.5rem; + font-size: 1rem; + } } -.surgeryForm button:hover { - background-color: #2980b9; +@media (max-width: 480px) { + .surgeryContainer { + padding: 1.25rem; + } + + .surgeryContainer h1 { + font-size: 1.5rem; + } + + .uploadLabel { + padding: 0.75rem 1rem; + font-size: 0.9rem; + } + + .alert { + padding: 0.75rem 1rem; + font-size: 0.9rem; + } }