diff --git a/components/AppointmentTable/AppointmentPage.jsx b/components/AppointmentTable/AppointmentPage.jsx new file mode 100644 index 0000000..1288a33 --- /dev/null +++ b/components/AppointmentTable/AppointmentPage.jsx @@ -0,0 +1,98 @@ +import Image from "next/image"; +import dayjs from "dayjs"; +import { TableRow, TableCell } from "@mui/material"; +import { capitalize, formateDate } from "@/utils/heplerFunction"; + +export default function AppointmentPage({ + appointments, + page, + rowsPerPage, + order, + searchTerm, + userType, +}) { + // Function to sort appointments based on booking date and time + const sortedAppointments = () => { + const comparator = (a, b) => { + const dateA = dayjs(a.bookingDate + " " + a.time); + const dateB = dayjs(b.bookingDate + " " + b.time); + + if (order === "asc") { + return dateA - dateB; + } else { + return dateB - dateA; + } + }; + + return appointments.sort(comparator); + }; + + // Function to filter appointments based on search term + const filteredAppointments = () => { + return sortedAppointments().filter((appointment) => + appointment.doctor.name.toLowerCase().includes(searchTerm?.toLowerCase()) + ); + }; + + const startIndex = page * rowsPerPage; + const endIndex = startIndex + rowsPerPage; + + let filteredAppointment; + if (userType === "doctor") { + filteredAppointment = sortedAppointments(); + } else { + filteredAppointment = filteredAppointments(); + } + + // If search term is not empty and no appointments are found, display a message + if (searchTerm !== "" && filteredAppointment.length === 0) { + return ( +

+ Docter not found +

+ ); + } + + // Render the filtered appointments within the specified range + return filteredAppointment.slice(startIndex, endIndex).map((item) => ( + + +
+ +
+
+ {capitalize( + userType === "doctor" ? item.user?.name : item.doctor?.name + )} +
+
+ {userType === "doctor" ? item.user?.email : item.doctor?.email} +
+
+
+
+ + {item.isPaid ? ( +
+
+ Paid +
+ ) : ( +
+
+ Unpaid +
+ )} +
+ {item.fees} + {item.time} + {formateDate(item.bookingDate)} +
+ )); +} diff --git a/components/AppointmentTable/TablePagination.jsx b/components/AppointmentTable/TablePagination.jsx new file mode 100644 index 0000000..fe9da1e --- /dev/null +++ b/components/AppointmentTable/TablePagination.jsx @@ -0,0 +1,121 @@ +import { + Table, + TableHead, + TableBody, + TableRow, + TableCell, + TablePagination, + TableSortLabel, + TextField, +} from "@mui/material"; +import Head from "next/head"; +import AppointmentPage from "./AppointmentPage"; +import { useState } from "react"; +import { formateDate } from "@/utils/heplerFunction"; + +export default function AppointmentTablePagination({ type, appointments }) { + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + const [order, setOrder] = useState("asc"); + const [orderBy, setOrderBy] = useState("bookingDate"); + const [searchTerm, setSearchTerm] = useState(""); + + // Function to handle page change + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; + + // Function to handle rows per page change + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + // Function to handle sorting request + const handleRequestSort = (property) => { + const isAsc = orderBy === property && order === "asc"; + setOrder(isAsc ? "desc" : "asc"); + setOrderBy(property); + }; + + // Function to handle search term change + const handleSearchChange = (event) => { + setSearchTerm(event.target.value); + setPage(0); + }; + + return ( +
+ {/* + Appointment page + + */} + + {appointments.length === 0 && ( +

+ {`You did not ${ + type === "user" ? " book " : " have" + } any appointment yet!`} +

+ )} + + {appointments.length > 0 && ( + <> + {type === "user" && ( + + )} + + + + Doctor Name + Status + Fees + Time + + handleRequestSort("bookingDate")} + > + Booked For + + + + + + + +
+ + + )} +
+ ); +} diff --git a/components/DFL/Card.jsx b/components/DFL/Card.jsx new file mode 100644 index 0000000..4779698 --- /dev/null +++ b/components/DFL/Card.jsx @@ -0,0 +1,17 @@ +import Image from "next/image"; +import style from "../../styles/DFL/HomeDfl.module.scss"; + +export default function Card({ title, text, img, alt = "" }) { + return ( +
+
+ {alt} +
+ +
+

{title}

+

{text}

+
+
+ ); +} diff --git a/components/DFL/Donate/MedicalInfoForm.js b/components/DFL/Donate/MedicalInfoForm.js new file mode 100644 index 0000000..6d0b5e8 --- /dev/null +++ b/components/DFL/Donate/MedicalInfoForm.js @@ -0,0 +1,131 @@ +import { HashLoader } from "react-spinners"; +import style from "../../../styles/DFL/donate.module.scss"; + +export default function MedicalInfoForm({ + formData, + loading, + onSubmit, + onChange, + errors, + onBlur, +}) { + const handleChange = (e) => { + const { name, value } = e.target; + onChange(name, value); + }; + return ( +
+
+

Medical Info

+
+
+
+
+ + + {errors.lastDonationDate &&

{errors.lastDonationDate}

} +
+
+ + + {errors.bloodType &&

{errors.bloodType}

} +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ + {errors.weight &&

{errors.weight}

} +
+
+ +
+
+
+ +
+
+
+ ); +} diff --git a/components/DFL/Donate/PersonalInfoForm.js b/components/DFL/Donate/PersonalInfoForm.js new file mode 100644 index 0000000..6076137 --- /dev/null +++ b/components/DFL/Donate/PersonalInfoForm.js @@ -0,0 +1,180 @@ +import Image from "next/image"; +import donateLogo from "../../../public/assets/images/dfl/donate logo.png"; + +import style from "../../../styles/DFL/donate.module.scss"; + +export default function PersonalInfoForm({ + formData, + onNextClick, + onChange, + onBlur, + errors, +}) { + const handleChange = (e) => { + const { name, value } = e.target; + onChange(name, value); + }; + + return ( + <> +
+
+
+ +
+
+

Blood Donation Form

+

+ Please answer the following questions correctly. This will help to + protect you and the patient who receives your blood. +

+
+
+
+
+
+
+ + + {errors.name &&

{errors.name}

} +
+
+ + + {errors.addharCard &&

{errors.addharCard}

} +
+
+ +
+
+ + + {errors.email &&

{errors.email}

} +
+
+ + + {errors.phone &&

{errors.phone}

} +
+
+ +
+
+ + + {errors.dob &&

{errors.dob}

} +
+
+ + + {errors.address &&

{errors.address}

} +
+
+ +
+
+ + + {errors.gender &&

{errors.gender}

} +
+
+ + + {errors.city &&

{errors.city}

} +
+
+
+ +
+
+ + ); +} diff --git a/components/DFL/Request/DonorCard.jsx b/components/DFL/Request/DonorCard.jsx new file mode 100644 index 0000000..b9c3adb --- /dev/null +++ b/components/DFL/Request/DonorCard.jsx @@ -0,0 +1,160 @@ +import Model from "../../Timeslots/Model"; +import axios from "axios"; +import toast from "react-hot-toast"; +import { capitalize } from "@/utils/heplerFunction"; +import { useState } from "react"; +import { FaTimes } from "react-icons/fa"; +import { BASE_URL } from "@/utils/config"; + +import style from "../../../styles/DFL/request.module.scss"; + +export default function DonorCard({ donor }) { + const [dialogOpen, setDialogOpen] = useState(false); + const [phone, setPhone] = useState(""); + const initalForm = { + name: "", + phone: "", + address: "", + message: "", + }; + + const [formData, setFormData] = useState(initalForm); + + const handleChange = (e) => { + const { name, value } = e.target; + + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + // Function for set Phone number of selected donor + const handleContactDonor = (phone) => { + setDialogOpen(true); + setPhone(phone); + }; + + // Function for close model and set form data empty + const handleCloseDialog = () => { + setDialogOpen(false); + setFormData(initalForm); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + const data = { + ...formData, + donorPhone: phone, + }; + + try { + await axios.post(`${BASE_URL}/donor/sendSMS`, data); + setDialogOpen(false); + setFormData(initalForm); + toast.success("Message send Succefully!"); + } catch (error) { + const err = error?.response?.data?.message || error?.message; + toast.error(err); + return null; + } + }; + + return ( +
+
+ {capitalize(donor.bloodType)} +

{donor.name}

+

+ Address: {donor.address}. +

+ + +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + {errors.about && ( +

{errors.about}

+ )} +
+
+ {formData.photo && ( +
+ +
+ )} + +
+ + +
+
+
+ +
+
+
+ ); +} diff --git a/components/Doctors/Dashboard/Tabs.jsx b/components/Doctors/Dashboard/Tabs.jsx new file mode 100644 index 0000000..d1a7ef2 --- /dev/null +++ b/components/Doctors/Dashboard/Tabs.jsx @@ -0,0 +1,75 @@ +import { logout } from "@/store/slices/userSlice"; +import { useRouter } from "next/router"; +import React from "react"; +import { BiMenu } from "react-icons/bi"; +import { useDispatch } from "react-redux"; + +export default function Tabs({ tab, setTab }) { + const dispatch = useDispatch(); + const router = useRouter(); + + const handleLogout = () => { + dispatch(logout()); + router.replace("/"); + }; + return ( +
+ + + +
+ + + + +
+ + +
+
+
+ ); +} diff --git a/components/Doctors/DoctorAbout.jsx b/components/Doctors/DoctorAbout.jsx index 27dc4d5..725980b 100644 --- a/components/Doctors/DoctorAbout.jsx +++ b/components/Doctors/DoctorAbout.jsx @@ -1,22 +1,21 @@ import { formateDate } from "@/utils/heplerFunction"; -import React from "react"; -export default function DoctorAbout() { +export default function DoctorAbout({ + name, + about, + qualifications, + experiences, +}) { return ( -
+

About of - Jay Zadafiya + {name}

-

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Fuga placeat - ducimus facere minus facilis totam praesentium. Asperiores optio - blanditiis quae mollitia corporis neque eveniet cum rerum molestias. - Hic, fuga expedita. -

+

{about}

@@ -25,33 +24,25 @@ export default function DoctorAbout() {
    -
  • -
    - - {formateDate("09-04-2016")}- {formateDate("12-04-2020")} - -

    - PHD in Surgon -

    -
    -

    - New Apollo Hospital, New Your -

    -
  • - -
  • -
    - - {formateDate("12-04-2010")}- {formateDate("12-04-2010")} - -

    - PHD in Surgon + {qualifications?.map((item, index) => ( +

  • +
    + + {formateDate(item.startingDate)} -{" "} + {formateDate(item.endingDate)} + +

    + {item.degree} +

    +
    +

    + {item.university}

    -
-

- New Apollo Hospital, New Your -

- + + ))}
@@ -61,30 +52,22 @@ export default function DoctorAbout() {
- + ); } diff --git a/components/Doctors/DoctorCard.jsx b/components/Doctors/DoctorCard.jsx index 40e8775..75876cc 100644 --- a/components/Doctors/DoctorCard.jsx +++ b/components/Doctors/DoctorCard.jsx @@ -2,20 +2,22 @@ import Image from "next/image"; import Link from "next/link"; import { BsArrowRight } from "react-icons/bs"; import starIcon from "../../public/assets/images/Star.png"; + export default function DoctorCard({ doctor }) { const { + _id, name, avgRating, totalRating, photo, specialization, totalPatients, - hospital, + experiences, } = doctor; return (
- +

@@ -33,7 +35,7 @@ export default function DoctorCard({ doctor }) { {avgRating} - ( {totalRating}) + ( {totalRating} )

@@ -44,11 +46,11 @@ export default function DoctorCard({ doctor }) { +{totalPatients} patients

- At {hospital} + At {experiences && experiences[0]?.place}

diff --git a/components/Doctors/DoctorList.jsx b/components/Doctors/DoctorList.jsx index 8a05d0a..5912177 100644 --- a/components/Doctors/DoctorList.jsx +++ b/components/Doctors/DoctorList.jsx @@ -1,11 +1,10 @@ -import { doctors } from "@/public/assets/data/doctors"; import DoctorCard from "./DoctorCard"; -export default function DoctorList() { +export default function DoctorList({ doctors }) { return (
- {doctors.map((doctor) => ( - + {doctors.map((doctor, index) => ( + ))}
); diff --git a/components/Doctors/FeedBack.jsx b/components/Doctors/FeedBack.jsx index 25ac3c2..daff1f8 100644 --- a/components/Doctors/FeedBack.jsx +++ b/components/Doctors/FeedBack.jsx @@ -1,47 +1,97 @@ +import { + List, + AutoSizer, + CellMeasurer, + CellMeasurerCache, +} from "react-virtualized"; import Image from "next/image"; -import avater from "../../public/assets/images/avatar-icon.png"; +import FeedbackForm from "./FeedbackForm"; import { formateDate } from "@/utils/heplerFunction"; import { AiFillStar } from "react-icons/ai"; import { useState } from "react"; -import FeedbackForm from "./FeedbackForm"; +import { useSelector } from "react-redux"; -export default function FeedBack() { +export default function FeedBack({ reviews, totalRating }) { + const { role } = useSelector((state) => state.user); const [showFeedbackForm, setShowFeedbackForm] = useState(false); + + const cache = new CellMeasurerCache({ + fixedWidth: true, + defaultHeight: 100, + }); return (
-
+ +

- All reviews (272) + All reviews ({totalRating})

+
+ + {({ width, height }) => ( + { + const review = reviews[index]; -
-
-
- -
+ return ( + + {({ registerChild }) => ( +
+
+
+ +
-
-
- Ali ahmed -
-

- {formateDate("02-14-2023")} -

+
+
+ {review?.user?.name} +
+

+ {formateDate(review?.createdAt)} +

-

- Good services, Enjoy tretement -

-
-
-
- {[...Array(5).keys()].map((_, index) => ( - - ))} -
+

+ {review?.reviewText} +

+
+
+
+ {[...Array(review?.rating).keys()].map((_, idx) => ( + + ))} +
+
+ )} + + ); + }} + /> + )} +
- {!showFeedbackForm && ( + {!showFeedbackForm && role === "patient" && (
- ); - })} +

+ How would you rate the overall experience +

+
+ {[...Array(5).keys()].map((_, index) => { + index += 1; + + return ( + + ); + })} +
+
+ +
+

+ share your feedback or suggestions +

+
-
- -
-

- share your feedback or suggestions -

- -
- - - + + + + ); } diff --git a/components/Doctors/SidePanel.jsx b/components/Doctors/SidePanel.jsx index a2bcc55..2e5f67e 100644 --- a/components/Doctors/SidePanel.jsx +++ b/components/Doctors/SidePanel.jsx @@ -1,47 +1,78 @@ -export default function SidePanel() { +import Model from "../Timeslots/Model"; +import Timeslot from "../Timeslots/Timeslot"; + +import { useMemo, useState } from "react"; +import { FaTimes } from "react-icons/fa"; +import { capitalize, convertTime } from "@/utils/heplerFunction"; + +export default function SidePanel({ address, timeslots, timeslotsData, fees }) { + const [open, setOpen] = useState(false); + + const handelModel = () => { + setOpen((prev) => !prev); + }; + + const sortedTimeslots = useMemo(() => { + return timeslotsData.slice().sort((a, b) => { + const order = ["morning", "afternoon", "evening"]; + return ( + order.indexOf(Object.values(a)[0]) - order.indexOf(Object.values(b)[0]) + ); + }); + }, [timeslotsData]); + return ( -
-
-

Ticket Price

- - 500 - -
+ <> +
+
+

Fees

+ + {fees}₹ + +
-
-

- Available Time Slots -

- -
    -
  • -

    - Sunday -

    -

    - 4:00 PM - 9:30 PM -

    -
  • -
  • -

    - Tuseday -

    -

    - 4:00 PM - 9:30 PM -

    -
  • -
  • -

    - Wedensday -

    -

    - 4:00 PM - 9:30 PM -

    -
  • -
-
+
+

+ Address:{" "} + + {address} + +

- -
+
    + {sortedTimeslots.map((slot, index) => ( +
  • +

    + {capitalize(slot.slot)} +

    +

    + {convertTime(slot.startingTime)} -{" "} + {convertTime(slot.endingTime)} +

    +
  • + ))} +
+
+ + +
+ + +
+ +
+
+ ); } diff --git a/components/ProtectedRoute/ProtectedRoute.jsx b/components/ProtectedRoute/ProtectedRoute.jsx deleted file mode 100644 index b21a0ff..0000000 --- a/components/ProtectedRoute/ProtectedRoute.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; - -export default function ProtectedRoute({ children, allowedRoles }) { - const user = useSelector((state) => state.user.user); // Assuming user role is stored in Redux state - const router = useRouter(); - - useEffect(() => { - if (!user) { - router.push("/login"); // Redirect to login if user not logged in - } - }, [user, router]); - - return children; -} diff --git a/components/TImeslots/Calander.jsx b/components/TImeslots/Calander.jsx new file mode 100644 index 0000000..04e9e85 --- /dev/null +++ b/components/TImeslots/Calander.jsx @@ -0,0 +1,18 @@ +import * as React from "react"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { DateCalendar } from "@mui/x-date-pickers/DateCalendar"; +import dayjs, { Dayjs } from "dayjs"; + +export default function Calander({ onChange }) { + return ( + + + + ); +} diff --git a/components/TImeslots/Model.jsx b/components/TImeslots/Model.jsx new file mode 100644 index 0000000..d475e02 --- /dev/null +++ b/components/TImeslots/Model.jsx @@ -0,0 +1,34 @@ +import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; + +function Modal({ open, children }) { + const [isClient, setIsClient] = useState(false); + const dialog = useRef(); + + useEffect(() => { + setIsClient(true); + }, []); + + useEffect(() => { + if (isClient) { + if (open) { + dialog.current.showModal(); + } else { + dialog.current.close(); + } + } + }, [open, isClient]); + + if (!isClient) return null; + + return createPortal( + +
+
{open ? children : null}
+
+
, + document.getElementById("modal") + ); +} + +export default Modal; diff --git a/components/TImeslots/Slot.jsx b/components/TImeslots/Slot.jsx new file mode 100644 index 0000000..c8e2ffb --- /dev/null +++ b/components/TImeslots/Slot.jsx @@ -0,0 +1,23 @@ +import { capitalize } from "@mui/material"; +import React from "react"; + +export default function Slot({ slot, period, openDetails }) { + return ( + <> +

+ {capitalize(period)} +

+
+ {slot[period]?.map(({ time }, timeIndex) => ( +
openDetails(time, period)} + > +

{time}

+
+ ))} +
+ + ); +} diff --git a/components/TImeslots/Timeslot.jsx b/components/TImeslots/Timeslot.jsx new file mode 100644 index 0000000..8cae28f --- /dev/null +++ b/components/TImeslots/Timeslot.jsx @@ -0,0 +1,150 @@ +import Head from "next/head"; +import axios from "axios"; +import Slot from "./Slot"; +import Calander from "./Calander"; +import toast from "react-hot-toast"; +import React, { useMemo, useState } from "react"; +import { useSelector } from "react-redux"; +import { useRouter } from "next/router"; +import { FaTimes } from "react-icons/fa"; +import { BASE_URL } from "@/utils/config"; +import { dateToString, timeslotByDate } from "@/utils/heplerFunction"; + +//Memoize the component to prevent unnecessary re-renders +const Timeslot = React.memo(({ timeslots, fees }) => { + const sortedTimeslots = useMemo(() => { + return timeslots.slice().sort((a, b) => { + const order = ["morning", "afternoon", "evening"]; + + return ( + order.indexOf(Object.keys(a)[0]) - order.indexOf(Object.keys(b)[0]) + ); + }); + }, [timeslots]); + + const router = useRouter(); + const { slug } = router.query; + + const [dialogOpen, setDialogOpen] = useState(false); + const [selectedTime, setSelectedTime] = useState(""); + const [selectedSlot, setSelectedSlot] = useState(""); + const [selectedDate, setSelectedDate] = useState(dateToString(new Date())); + const [newTimeslots, setNewTimeslots] = useState(sortedTimeslots); + + const { accessToken } = useSelector((state) => state.user); + + const openDetails = (time, period) => { + setSelectedTime(time); + setSelectedSlot(period); + setDialogOpen(true); + }; + + const onChangeDate = (newDateStr) => { + const formattedDate = dateToString(newDateStr); + + setSelectedDate(formattedDate); + setNewTimeslots(timeslotByDate(sortedTimeslots, formattedDate)); + }; + + const confirmBooking = async () => { + setDialogOpen(false); + try { + const data = { + bookingDate: selectedDate, + time: selectedTime, + slotPhase: selectedSlot, + }; + + const session = await axios.post( + `${BASE_URL}/booking/checkout-session/${slug}`, + data, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + if (session.data.url) { + router.push(session.data.url); + } + + // Reset selectedTime and selectedSlot if needed + setSelectedTime(""); + setSelectedSlot(""); + } catch (error) { + const err = error?.response?.data?.message || error?.message; + toast.error(err); + + return null; + } + }; + + return ( + <> + + {selectedDate} timeslots + + +
+
+ +
+
+ {newTimeslots.map((slot, index) => + Object.keys(slot).map((period, periodIndex) => ( + + )) + )} +
+
+ + {dialogOpen && ( +
+
+

+ Selected Date:{" "} + {selectedDate} +

+

+ Selected Slot:{" "} + {selectedSlot} +

+

+ Selected Time:{" "} + {selectedTime} +

+

+ Appointment Fees: {fees} +

+ + +
+ +
setDialogOpen(false)} + > + +
+
+ )} + + ); +}); + +export default Timeslot; diff --git a/components/User/MyBookings.jsx b/components/User/MyBookings.jsx deleted file mode 100644 index a9ad4cf..0000000 --- a/components/User/MyBookings.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import DoctorCard from "../Doctors/DoctorCard"; - -export default function MyBookings({ appointentments }) { - return ( -
- {appointentments.length === 0 && ( -

- You did not book any doctro yet! -

- )} - - {appointentments.length > 0 && ( -
- {appointentments.map((doctor) => ( - - ))} -
- )} -
- ); -} diff --git a/components/User/Profile.jsx b/components/User/Profile.jsx index a8afa5d..87a09d9 100644 --- a/components/User/Profile.jsx +++ b/components/User/Profile.jsx @@ -1,21 +1,19 @@ -import { useState } from "react"; - import Image from "next/image"; -import axios from "axios"; import HashLoader from "react-spinners/HashLoader"; +import Error from "../Error/Error"; +import { useState } from "react"; import { useRouter } from "next/router"; -import { uploadImageToCloudinary } from "@/utils/uploadCloudinary"; -import { BASE_URL, token } from "@/utils/config"; -import { useDispatch, useSelector } from "react-redux"; import { updateUser } from "@/store/slices/userSlice"; -import Error from "../Error/Error"; +import { useDispatch, useSelector } from "react-redux"; +import { uploadImageToCloudinary } from "@/utils/uploadCloudinary"; +import { patientFormValidation } from "@/utils/formValidation"; export default function Profile({ user }) { - const [selectFile, setSelectFile] = useState(user?.photo); const dispatch = useDispatch(); const router = useRouter(); const { error, loading } = useSelector((state) => state.user); + const [errors, setErrors] = useState({}); const [formData, setFormDate] = useState({ name: user?.name || "", @@ -44,18 +42,38 @@ export default function Profile({ user }) { setFormDate({ ...formData, photo: url }); }; + const handleBlur = (e) => { + const { name, value } = e.target; + + const formErrors = patientFormValidation({ ...formData, [name]: value }); + setErrors((prevErrors) => ({ + ...prevErrors, + [name]: formErrors[name] || "", // Clear previous errors for this field + })); + }; + const submitHandler = async (e) => { e.preventDefault(); - try { - dispatch(updateUser({ formData, userId: user._id })).then((result) => { - if (result.payload && result.payload.data) { - router.push("/users/profile"); - } - }); - } catch (error) { - return ; + + const formError = patientFormValidation(formData); + setErrors(formError); + + if (Object.keys(formError).length === 0) { + try { + dispatch(updateUser(formData)).then((result) => { + if (result.payload && result.payload.data) { + router.push("/users/profile"); + } + }); + } catch (error) { + const err = error?.response?.data?.message || error?.message; + toast.error(err); + + return null; + } } }; + return (
@@ -64,11 +82,15 @@ export default function Profile({ user }) { type="text" placeholder="Full Name" name="name" + onBlur={handleBlur} value={formData.name} onChange={handelInputChange} className="w-full pr-4 py-3 border-b border-solid border-[#8066ff61] focus:outline-none focus:border-b-primaryColor text-[16 px] leading-7 text-headingColor cursor-pointer " /> + {errors.name && ( +

{errors.name}

+ )}
@@ -76,10 +98,12 @@ export default function Profile({ user }) { type="email" placeholder="Enter Your Email" name="email" + onBlur={handleBlur} value={formData.email} - onChange={handelInputChange} className="w-full pr-4 py-3 border-b border-solid border-[#8066ff61] focus:outline-none focus:border-b-primaryColor text-[16 px] leading-7 text-headingColor cursor-pointer " + aria-readonly + readOnly />
@@ -87,28 +111,38 @@ export default function Profile({ user }) { type="test" placeholder="Blood Type" name="bloodType" + onBlur={handleBlur} value={formData.bloodType} onChange={handelInputChange} className="w-full pr-4 py-3 border-b border-solid border-[#8066ff61] focus:outline-none focus:border-b-primaryColor text-[16 px] leading-7 text-headingColor cursor-pointer " /> + {errors.bloodType && ( +

{errors.bloodType}

+ )}
-
- +
+
+ +
+ {errors.gender && ( +

{errors.gender}

+ )}
diff --git a/components/layout/footer.jsx b/components/layout/footer.jsx index 195c405..28e79db 100644 --- a/components/layout/footer.jsx +++ b/components/layout/footer.jsx @@ -1,10 +1,11 @@ -import React from "react"; +import Image from "next/image"; import Link from "next/link"; import Logo from "../../public/assets/images/logo.png"; +import dolLogo from "../../public/assets/images/dfl/logo.png"; import { RiLinkedinFill } from "react-icons/ri"; import { AiFillYoutube, AiFillGithub, AiFillInstagram } from "react-icons/ai"; -import Image from "next/image"; +import { useRouter } from "next/router"; const socialLink = [ { @@ -75,14 +76,17 @@ const quickLinks03 = [ ]; export default function Footer() { + const router = useRouter(); const year = new Date().getFullYear(); + const isDFL = router.pathname.startsWith("/drop-for-life"); return (