From cb0039d52328dc9f4a9f6809db984f8ea22ec974 Mon Sep 17 00:00:00 2001 From: kwasi Date: Fri, 23 Aug 2024 19:29:13 +0000 Subject: [PATCH 1/8] Update: make api calls and styles to admin service page --- src/admin/components/PageHeader.jsx | 36 ++- src/admin/components/ServiceFormModal.jsx | 124 +++++++++ src/admin/pages/Services.js | 238 ++++++++++-------- src/client/components/MenuItems.jsx | 2 +- src/client/components/ServiceSection.jsx | 2 +- src/client/components/ServicesHomeSection.jsx | 2 +- src/client/data/serviceData.js | 36 +-- src/client/pages/ServicesPage.js | 76 ++++-- 8 files changed, 365 insertions(+), 151 deletions(-) create mode 100644 src/admin/components/ServiceFormModal.jsx diff --git a/src/admin/components/PageHeader.jsx b/src/admin/components/PageHeader.jsx index 09281ea0..1af03e0e 100644 --- a/src/admin/components/PageHeader.jsx +++ b/src/admin/components/PageHeader.jsx @@ -1,18 +1,32 @@ import { Icon } from '@iconify/react'; +import { useLocation } from 'react-router-dom'; + +const PageHeader = ({ onSearch, openModal }) => { + const location = useLocation(); + const isEditService = location.pathname === '/admin/services'; -const PageHeader = ({ onSearch }) => { return (
-

Invoice

-
- - onSearch(e.target.value)} // Trigger onSearch when input changes - /> -
+

{ !isEditService ? 'Invoice' : 'Services' }

+ {!isEditService ? ( +
+ + onSearch(e.target.value)} // Trigger onSearch when input changes + /> +
+ ): ( + + )} +
); } diff --git a/src/admin/components/ServiceFormModal.jsx b/src/admin/components/ServiceFormModal.jsx new file mode 100644 index 00000000..50a77972 --- /dev/null +++ b/src/admin/components/ServiceFormModal.jsx @@ -0,0 +1,124 @@ +import React, { useState, useEffect } from 'react'; +import { Icon } from '@iconify/react/dist/iconify.js'; +import Modal from 'react-modal'; + +const ServiceFormModal = ({ isOpen, onClose, onSaveService, editingService }) => { + // Initialize service state with default values or editingService values if provided + const [service, setService] = useState({ + title: '', + description: '', + features: ['', '', ''], + }); + + // Update service state when editingService changes + useEffect(() => { + if (editingService) { + setService({ + title: editingService.title, + description: editingService.description, + features: editingService.points.map(point => point.title), // Map points to feature titles + }); + } + }, [editingService]); + + // Handle input changes for both service details and features + const handleChange = (e) => { + const { name, value } = e.target; + + if (name.startsWith('feature')) { + // Update the corresponding feature based on index + const index = parseInt(name.replace('feature', '')) - 1; + const updatedFeatures = [...service.features]; + updatedFeatures[index] = value; + setService({ ...service, features: updatedFeatures }); + } else { + // Update title or description + setService({ ...service, [name]: value }); + } + }; + + // Handle form submission to save the service + const handleSubmit = (e) => { + e.preventDefault(); + + // Validate that features are unique and not empty + const featureSet = new Set(service.features.filter(f => f.trim() !== '')); + if (featureSet.size < service.features.length) { + alert("Features must be unique and not empty."); + return; + } + + // Prepare service data for saving + const updatedService = { + title: service.title, + description: service.description, + points: service.features.map(feature => ({ title: feature })), // Convert features back to points + }; + + // Save the service and close the modal + onSaveService(updatedService); + onClose(); + }; + + return ( + +

+ {editingService ? 'Edit' : 'Add A'} Service +

+ +
+
+
+ + +
+
+ + +
+ {service.features.map((feature, index) => ( +
+ + +
+ ))} +
+ +
+
+ ); +} + +export default ServiceFormModal; \ No newline at end of file diff --git a/src/admin/pages/Services.js b/src/admin/pages/Services.js index 90331e78..a9ebe438 100644 --- a/src/admin/pages/Services.js +++ b/src/admin/pages/Services.js @@ -1,121 +1,151 @@ -import React, { useState } from 'react'; -import ReactDOM from 'react-dom'; +import React, { useState, useEffect } from 'react'; +import PageHeader from '../components/PageHeader'; +import { Icon } from '@iconify/react/dist/iconify.js'; +import ServiceFormModal from '../components/ServiceFormModal'; +const Services = () => { + const [services, setServices] = useState([]); + const [isModalOpen, setModalOpen] = useState(false); + const [editingService, setEditingService] = useState(null); + const [error, setError] = useState(null); + const API_URL = 'https://api.example.com/services'; // Replace with your actual API URL -// Service Form Component -function ServiceForm({ onAddService }) { - const [service, setService] = useState({ - name: '', - description: '', - features: '', - }); + useEffect(() => { + const fetchServices = async () => { + try { + const response = await fetch(API_URL); + if (!response.ok) { + throw new Error(`Failed to fetch services: ${response.statusText}`); + } + const data = await response.json(); + setServices(data); // Set the fetched services data + } catch (error) { + setError(error.message); // Handle any errors that occur during fetch + } + }; - const handleChange = (e) => { - setService({ ...service, [e.target.name]: e.target.value }); + fetchServices(); // Fetch services when the component mounts + }, []); + + const saveService = async (newService) => { + try { + if (editingService) { + // Update an existing service + const response = await fetch(`${API_URL}/${editingService.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(newService), + }); + if (!response.ok) { + throw new Error(`Failed to update service: ${response.statusText}`); + } + const updatedService = await response.json(); + setServices(services.map(service => + service.id === editingService.id ? updatedService : service + )); + } else { + // Add a new service + const response = await fetch(API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(newService), + }); + if (!response.ok) { + throw new Error(`Failed to add service: ${response.statusText}`); + } + const addedService = await response.json(); + setServices([...services, addedService]); + } + setEditingService(null); // Reset editing state + setModalOpen(false); // Close the modal after save + } catch (error) { + setError(error.message); // Handle any errors during save + } }; - const handleSubmit = (e) => { - e.preventDefault(); - onAddService({ - ...service, - features: service.features.split(',').map(feature => feature.trim()), - }); - setService({ name: '', description: '', features: '' }); + const deleteService = async (id) => { + try { + const response = await fetch(`${API_URL}/${id}`, { + method: 'DELETE', + }); + if (!response.ok) { + throw new Error(`Failed to delete service: ${response.statusText}`); + } + setServices(services.filter(service => service.id !== id)); // Remove the deleted service from state + } catch (error) { + setError(error.message); // Handle any errors during deletion + } }; - return ( -
-
- - -
-
- - -
-
- - -
- -
- ); -} + const openModal = () => { + setEditingService(null); // Reset editing state for a new service + setModalOpen(true); // Open the modal + }; -// Services Component -function Services() { - const [services, setServices] = useState([ - { - name: 'Auto Resprays', - description: 'Revitalize your car’s appearance with our top-quality respray services...', - features: ['Full Body Resprays', 'Panel Resprays', 'Custom Paint Jobs'], - }, - { - name: 'Car Detailing', - description: 'Keep your car looking brand new with our comprehensive car detailing services...', - features: ['Exterior Detailing', 'Interior Detailing', 'Ceramic Coating'], - }, - ]); + const closeModal = () => { + setModalOpen(false); // Close the modal without saving + }; - const addService = (newService) => { - setServices([...services, newService]); + const handleEdit = (service) => { + setEditingService(service); // Set the service to be edited + setModalOpen(true); // Open the modal for editing }; return ( -
- - - - - - - - - - - - {services.map((service, index) => ( - - - - - +
+ {error && ( +
+

Error: {error}

+ +
+ )} + + + + +
+
NameDescriptionFeaturesActions
{service.name}{service.description} -
    - {service.features.map((feature, i) => ( -
  • {feature}
  • - ))} -
-
- - -
+ + + + + + - ))} - -
NameDescriptionFeaturesActions
+ + + {services.map((service, index) => ( + + {service.title} + {service.description} + +
    + {service.points.map((feature, i) => ( +
  • {feature.title}
  • + ))} +
+ + + + + + + ))} + + +
); } diff --git a/src/client/components/MenuItems.jsx b/src/client/components/MenuItems.jsx index 6ef5b4ce..95650734 100644 --- a/src/client/components/MenuItems.jsx +++ b/src/client/components/MenuItems.jsx @@ -16,7 +16,7 @@ const MenuItems = ({ isHeader = false, className = "" , setIsOpen }) => { - `hover:text-[#DE0000] block w-fit ${isHeader ? 'py-2 md:py-3 lg:py-4 px-4 md:px-0' : '' } transition-colors duration-300 ${(isHeader && isActive) ? 'text-[#DE0000] font-bold' : ''}` + `hover:text-[#DE0000] ${isHeader ? 'block w-fit py-2 md:py-3 lg:py-4 px-4 md:px-0' : '' } transition-colors duration-300 ${(isHeader && isActive) ? 'text-[#DE0000] font-bold' : ''}` } onClick={handleMenuItemClick} > diff --git a/src/client/components/ServiceSection.jsx b/src/client/components/ServiceSection.jsx index 1cd603d6..b0272746 100644 --- a/src/client/components/ServiceSection.jsx +++ b/src/client/components/ServiceSection.jsx @@ -81,7 +81,7 @@ const ServicesSection = ({ whileInView="visible" viewport={{ once: true, amount: 0.3 }} > - {point.title}: {point.text} + {point} ))} diff --git a/src/client/components/ServicesHomeSection.jsx b/src/client/components/ServicesHomeSection.jsx index a703fbe0..91d8f3ce 100644 --- a/src/client/components/ServicesHomeSection.jsx +++ b/src/client/components/ServicesHomeSection.jsx @@ -14,7 +14,7 @@ const ServicesHomeSection = ({ image }) => { {/* Content */}
diff --git a/src/client/data/serviceData.js b/src/client/data/serviceData.js index 1e6968b6..c1cb7de4 100644 --- a/src/client/data/serviceData.js +++ b/src/client/data/serviceData.js @@ -3,9 +3,9 @@ const serviceData = [ title: 'Automobile Resprays', description: 'Revitalize your car’s appearance with our top-quality respray services. Whether you’re looking to restore your car’s original color or want a complete color change, our skilled technicians use the finest automotive paints and precision techniques to deliver a flawless finish.', points: [ - { title: 'Full Body Resprays', text: 'Complete color changes for a fresh new look.' }, - { title: 'Panel Resprays', text: 'Perfect for repairing specific areas or panels.' }, - { title: 'Custom Paint Jobs', text: 'Unique designs and custom colors to make your car stand out.' }, + 'Full Body Resprays: Complete color changes for a fresh new look.', + 'Panel Resprays: Perfect for repairing specific areas or panels.', + 'Custom Paint Jobs: Unique designs and custom colors to make your car stand out.', ], image: require('../assets/image4.png'), icon: 'mdi:spray-bottle', @@ -15,9 +15,9 @@ const serviceData = [ title: 'Car Detailing', description: 'Keep your car looking brand new with our comprehensive car detailing services. Our detailing packages include meticulous cleaning, both inside and out, to ensure your vehicle shines.', points: [ - { title: 'Exterior Detailing', text: 'Includes wash, wax, and polish for a gleaming finish.' }, - { title: 'Interior Detailing', text: 'Deep cleaning of seats, carpets, and upholstery.' }, - { title: 'Ceramic Coating', text: 'Provides long-lasting protection and a brilliant shine.' }, + 'Exterior Detailing: Includes wash, wax, and polish for a gleaming finish.', + 'Interior Detailing: Deep cleaning of seats, carpets, and upholstery.', + 'Ceramic Coating: Provides long-lasting protection and a brilliant shine.', ], image: require('../assets/DetailingBg.jpg'), icon: 'codicon:sparkle-filled', @@ -27,9 +27,9 @@ const serviceData = [ title: 'Electrical Services', description: 'Ensure your vehicle’s electrical systems are in top condition with our expert electrical services. From diagnostics to repairs, our certified technicians handle everything from minor electrical issues to complex system malfunctions.', points: [ - { title: 'Battery Replacement', text: 'Reliable battery testing and replacement services.' }, - { title: 'Alternator and Starter Repairs', text: 'Essential for keeping your car’s electrical system functioning.' }, - { title: 'Electrical Diagnostics', text: 'Advanced tools to identify and fix electrical problems quickly' }, + 'Battery Replacement: Reliable battery testing and replacement services.', + 'Alternator and Starter Repairs: Essential for keeping your car’s electrical system functioning.', + 'Electrical Diagnostics: Advanced tools to identify and fix electrical problems quickly.', ], image: require('../assets/electricalBg.jpg'), icon: 'uil:circuit', @@ -39,9 +39,9 @@ const serviceData = [ title: 'Paint Correction', description: 'Remove imperfections and restore your car’s paint with our advanced paint correction services. Our multi-stage correction process effectively eliminates swirls, scratches, and oxidation, bringing back your car’s original luster.', points: [ - { title: 'Scratch and Swirl Removal', text: ' Enhance your car’s appearance by removing unsightly marks.' }, - { title: 'Oxidation Removal', text: ' Revive dull and faded paint.' }, - { title: 'Gloss Enhancement', text: ' Achieve a mirror-like finish with our polishing techniques' }, + 'Scratch and Swirl Removal: Enhance your car’s appearance by removing unsightly marks.', + 'Oxidation Removal: Revive dull and faded paint.', + 'Gloss Enhancement: Achieve a mirror-like finish with our polishing techniques.', ], image: require('../assets/paint-correctionBg.jpg'), icon: 'fluent:paint-brush-sparkle-24-filled', @@ -51,9 +51,9 @@ const serviceData = [ title: 'Bodyworks', description: 'From minor dents to major collision repairs, our bodywork services ensure your car looks as good as new. Using state-of-the-art equipment and techniques, we handle all aspects of body repair with precision.', points: [ - { title: 'Dent Repair', text: ' Fixes for small dings and dents without repainting.' }, - { title: 'Collision Repair', text: ' Comprehensive repair services for major accidents.' }, - { title: 'Panel Replacement', text: ' Replacing damaged panels to restore structural integrity.' }, + 'Dent Repair: Fixes for small dings and dents without repainting.', + 'Collision Repair: Comprehensive repair services for major accidents.', + 'Panel Replacement: Replacing damaged panels to restore structural integrity.', ], image: require('../assets/bodyworksBg.jpg'), icon: 'mdi:hammer-wrench', @@ -63,9 +63,9 @@ const serviceData = [ title: 'Auto Mechanic', description: 'Trust our skilled mechanics to keep your car running smoothly. From routine maintenance to major repairs, we offer a wide range of mechanical services to meet all your automotive needs.', points: [ - { title: 'Engine Repairs', text: ' Diagnosing and fixing engine issues to maintain performance.' }, - { title: 'Brake Services', text: ' Ensuring your brakes are in optimal condition for safety.' }, - { title: 'Routine Maintenance', text: ' Regular services like oil changes, tire rotations, and inspections.' }, + 'Engine Repairs: Diagnosing and fixing engine issues to maintain performance.', + 'Brake Services: Ensuring your brakes are in optimal condition for safety.', + 'Routine Maintenance: Regular services like oil changes, tire rotations, and inspections.', ], image: require('../assets/mechanicBg.jpg'), icon: 'fluent:wrench-settings-24-filled', diff --git a/src/client/pages/ServicesPage.js b/src/client/pages/ServicesPage.js index 3e296649..a3ae98df 100644 --- a/src/client/pages/ServicesPage.js +++ b/src/client/pages/ServicesPage.js @@ -1,43 +1,89 @@ -import React, { useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import ServicesSection from '../components/ServiceSection'; -import serviceData from '../data/serviceData'; const ServicesPage = () => { + // State for storing services data, loading status, and error messages + const [services, setServices] = useState(() => { + const cachedServices = localStorage.getItem('services'); + return cachedServices ? JSON.parse(cachedServices) : []; + }); + const [loading, setLoading] = useState(!services.length); // Show loading if no cached data + const [error, setError] = useState(null); const location = useLocation(); // Hook to get the current location useEffect(() => { - // Scroll to the element if there's a hash in the URL - const hash = location.hash.substring(1); // Remove the '#' from the hash + // Function to fetch services data with retry logic + const fetchServices = async (retryCount = 3) => { + try { + const response = await fetch('https://api.example.com/services'); // Replace with your API URL + if (!response.ok) { + throw new Error(`Failed to fetch services: ${response.statusText}`); + } + const data = await response.json(); + setServices(data); + localStorage.setItem('services', JSON.stringify(data)); // Cache data in localStorage + } catch (error) { + if (retryCount > 0) { + console.warn(`Retrying fetch services, attempts left: ${retryCount}`); + fetchServices(retryCount - 1); // Retry the fetch request + } else { + setError(error.message); + } + } finally { + setLoading(false); + } + }; + + // Fetch services if no cached data exists + if (!services.length) { + fetchServices(); + } + }, [services.length]); + + useEffect(() => { + // Scroll to the section if a hash is present in the URL + const hash = location.hash.substring(1); if (hash) { - const element = document.getElementById(hash); // Get the element by ID + const element = document.getElementById(hash); if (element) { - element.scrollIntoView({ behavior: 'smooth' }); // Smooth scroll to the element + element.scrollIntoView({ behavior: 'smooth' }); } } - }, [location]); // Re-run the effect when the location changes + }, [location]); + + if (loading) { + return
Loading services...
; + } + + if (error) { + return ( +
+

Error loading services: {error}

+ +
+ ); + } return (
- {/* Page title */}

Our Services

- {/* Map through serviceData to create service sections */} - {serviceData.map((section, index) => ( + {services.map((section, index) => ( ))}
); }; -export default ServicesPage; +export default ServicesPage; \ No newline at end of file From c4e9e9ea1b969eddcf7aac8b7d78fec15d5f155a Mon Sep 17 00:00:00 2001 From: kwasi Date: Fri, 23 Aug 2024 19:38:06 +0000 Subject: [PATCH 2/8] update: make the rating pop-up responsive --- src/client/components/RatingPopup.jsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/client/components/RatingPopup.jsx b/src/client/components/RatingPopup.jsx index e60bab00..dfd0ec35 100644 --- a/src/client/components/RatingPopup.jsx +++ b/src/client/components/RatingPopup.jsx @@ -5,14 +5,17 @@ const RatingPopup = ({ isOpen, onClose, onSubmit }) => { const [comment, setComment] = useState(''); const [isSubmitDisabled, setIsSubmitDisabled] = useState(true); + // Enable or disable the submit button based on rating and comment inputs useEffect(() => { setIsSubmitDisabled(rating === 0 || comment.trim() === ''); }, [rating, comment]); + // Handle star rating change const handleRatingChange = (value) => { setRating(value); }; + // Handle form submission const handleSubmit = async () => { const payload = { rating, comment }; @@ -32,27 +35,27 @@ const RatingPopup = ({ isOpen, onClose, onSubmit }) => { const data = await response.json(); console.log('Rating submitted successfully:', data); - onSubmit(payload); + onSubmit(payload); // Callback to parent component after successful submission } catch (error) { console.error('Error submitting rating:', error); } finally { - onClose(); + onClose(); // Close the popup after submission or error } }; - if (!isOpen) return null; + if (!isOpen) return null; // Do not render the popup if it's not open return ( -
-

Rate Our Service

+
+

Rate Our Service

{[1, 2, 3, 4, 5].map((star) => ( @@ -69,13 +72,13 @@ const RatingPopup = ({ isOpen, onClose, onSubmit }) => {
+
+ )} + + +

+ {action === 'Sign In' ? "Don't" : 'Already'} have an account? + +

- -

- {action === 'Sign In' ? "Don't" : 'Already'} have an account? - -

- - + + setIsResetPasswordOpen(false)} + /> + ); }; diff --git a/src/client/components/PasswordResetModals.jsx b/src/client/components/PasswordResetModals.jsx new file mode 100644 index 00000000..db3adb56 --- /dev/null +++ b/src/client/components/PasswordResetModals.jsx @@ -0,0 +1,214 @@ +import React, { useState, useContext, useEffect } from 'react'; +import Modal from 'react-modal'; +import { UserContext } from '../context/UserContext'; +import { Icon } from '@iconify/react/dist/iconify.js'; + +const InputField = ({ label, type, icon, value, onChange, error, placeholder, PasswordMarginBottom, togglePassword }) => ( +
+ +
+ +
+ + {togglePassword && ( + + )} + {error &&

{error}

} +
+
+); + +const PasswordResetModals = ({ isOpen, onClose }) => { + const [step, setStep] = useState(1); + const [email, setEmail] = useState(''); + const [code, setCode] = useState(Array(6).fill('')); + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + const [isConfirmPasswordVisible, setIsConfirmPasswordVisible] = useState(false); + const { setIsLoggedIn } = useContext(UserContext); + + const handleSendCode = () => { + // Logic to send the verification code to the email + setStep(2); + }; + + const handleInputChange = (e, index) => { + const { value } = e.target; + + // Create a copy of the code array + const newCode = [...code]; + + if (/^[0-9]$/.test(value)) { + newCode[index] = value; + setCode(newCode); + + // Auto-focus on the next input field + if (index < 5 && value) { + document.getElementById(`code-input-${index + 1}`).focus(); + } + + // If all inputs are filled, automatically verify the code + if (index === 5 && newCode.every((digit) => digit !== '')) { + handleVerifyCode(newCode); + } + } + }; + + const handleKeyDown = (e, index) => { + if (e.key === 'Backspace' || e.key === 'ArrowLeft') { + e.preventDefault(); + const newCode = [...code]; + if (code[index] !== '') { + newCode[index] = ''; + setCode(newCode); + } else if (index > 0) { + document.getElementById(`code-input-${index - 1}`).focus(); + } + } else if (e.key === 'ArrowRight' && index < 5) { + document.getElementById(`code-input-${index + 1}`).focus(); + } + }; + + const handleVerifyCode = (enteredCodeArray) => { + const enteredCode = enteredCodeArray.join(''); + // Logic to verify the code + if (enteredCode === '123456') { // Replace with actual verification logic + setStep(3); + } else { + alert('Invalid code'); + } + }; + + const handleResetPassword = () => { + if (newPassword !== confirmPassword) { + alert('Passwords do not match'); + return; + } + + // Logic to reset the password + setIsLoggedIn(true); + onClose(); // Close the modal after resetting the password + }; + + // Disable background scrolling when the modal is open + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + + // Cleanup when component unmounts or modal closes + return () => { + document.body.style.overflow = ''; + }; + }, [isOpen]); + + return ( + + {step === 1 && ( +
+

Forgot Password

+

Enter your email to receive instructions to reset your password

+ setEmail(e.target.value)} + placeholder="onlyspeedstar@gmail.com" + /> + +
+ )} + + {step === 2 && ( +
+

Forgot Password

+

We sent a code to {email.replace(/(.{2})(.*)(@.*)/, "$1******$3")}

+
+ {code.map((digit, index) => ( + handleInputChange(e, index)} + onKeyDown={(e) => handleKeyDown(e, index)} + /> + ))} +
+ +
+ )} + + {step === 3 && ( +
+

Set New Password

+

+ Your password must contain at least 8 characters, and include at least one uppercase letter, one lowercase letter, one number, and one special character. +

+ setNewPassword(e.target.value)} + togglePassword={() => setIsPasswordVisible(!isPasswordVisible)} + PasswordMarginBottom={true} + /> + setConfirmPassword(e.target.value)} + togglePassword={() => setIsConfirmPasswordVisible(!isConfirmPasswordVisible)} + /> + +
+ )} + +
+ ); +}; + +export default PasswordResetModals; From 441e58fc509dbaa77567854780ddd6b3ded04811 Mon Sep 17 00:00:00 2001 From: kwasi Date: Thu, 5 Sep 2024 16:31:44 +0000 Subject: [PATCH 6/8] update: specific minimum and maximum character length for password --- src/client/components/LoginSignupModal.jsx | 10 ++++--- src/client/components/PasswordResetModals.jsx | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/client/components/LoginSignupModal.jsx b/src/client/components/LoginSignupModal.jsx index 363263b6..0701eec6 100644 --- a/src/client/components/LoginSignupModal.jsx +++ b/src/client/components/LoginSignupModal.jsx @@ -85,9 +85,11 @@ const LoginSignupModal = ({ isOpen, onClose, initialAction }) => { if (!state.password) { newErrors.password = 'Password is required'; - } else if (state.password.length < 6) { - newErrors.password = 'Password must be at least 6 characters'; - } + } else if (state.password.length < 8) { + newErrors.password = 'Password must be at least 8 characters'; + } else if(state.password.length > 24) { + newErrors.password = 'Password must be at most 24 characters'; + } if (action === 'Sign Up') { if (!state.firstname) { @@ -263,7 +265,7 @@ const LoginSignupModal = ({ isOpen, onClose, initialAction }) => { )} diff --git a/src/client/components/PasswordResetModals.jsx b/src/client/components/PasswordResetModals.jsx index db3adb56..4e8df413 100644 --- a/src/client/components/PasswordResetModals.jsx +++ b/src/client/components/PasswordResetModals.jsx @@ -31,15 +31,29 @@ const InputField = ({ label, type, icon, value, onChange, error, placeholder, Pa const PasswordResetModals = ({ isOpen, onClose }) => { const [step, setStep] = useState(1); const [email, setEmail] = useState(''); + const [emailError, setEmailError] = useState(''); const [code, setCode] = useState(Array(6).fill('')); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); + const [newPasswordError, setNewPasswordError] = useState(''); const [isPasswordVisible, setIsPasswordVisible] = useState(false); const [isConfirmPasswordVisible, setIsConfirmPasswordVisible] = useState(false); const { setIsLoggedIn } = useContext(UserContext); + const validateEmail = (email) => { + // Simple email validation regex + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + const handleSendCode = () => { + if (!validateEmail(email)) { + setEmailError('Please enter a valid email address'); + return; + } + // Logic to send the verification code to the email + setEmailError(''); setStep(2); }; @@ -91,11 +105,21 @@ const PasswordResetModals = ({ isOpen, onClose }) => { }; const handleResetPassword = () => { + if (newPassword.length < 8) { + setNewPasswordError('Password must be at least 8 characters long'); + return; + } else if (newPassword.length > 24) { + setNewPasswordError('Password must be at most 24 characters long'); + return; + } + if (newPassword !== confirmPassword) { alert('Passwords do not match'); return; } + setNewPasswordError(''); + // Logic to reset the password setIsLoggedIn(true); onClose(); // Close the modal after resetting the password @@ -132,6 +156,7 @@ const PasswordResetModals = ({ isOpen, onClose }) => { icon="heroicons:envelope" value={email} onChange={(e) => setEmail(e.target.value)} + error={emailError} placeholder="onlyspeedstar@gmail.com" />