add first open time for appo item and change component data, remove work time in location, add loading on links in page doctor list, fix bug search city and state in clinics page

This commit is contained in:
ehsan
2025-09-18 14:30:49 +03:30
parent a6386e693f
commit 2f078eba3c
17 changed files with 115 additions and 65 deletions
+17 -1
View File
@@ -11,10 +11,22 @@ import CustomLoading from "./loading/Custom";
import TextLoading from "./loading/Text"; import TextLoading from "./loading/Text";
import CircularLoading from "./loading/Circular"; import CircularLoading from "./loading/Circular";
import moment from "moment-jalaali"; import moment from "moment-jalaali";
import { useState } from "react";
moment.loadPersian({ dialect: "persian-modern" }); moment.loadPersian({ dialect: "persian-modern" });
function ItemDoctor({ doctor }) { function ItemDoctor({ doctor, doctors, setDoctors }) {
const [loading, setLoading] = useState(false);
const handleLoading = () => {
setDoctors((prevDoctors) =>
prevDoctors.map((item) =>
item.id === doctor.id
? { ...item, loading: true }
: { ...item, loading: false }
)
);
};
return ( return (
<li className="p-4 relative rounded-lg bg-[#FFF] border border-[#EFEFEF]"> <li className="p-4 relative rounded-lg bg-[#FFF] border border-[#EFEFEF]">
{/* Detail Doctor */} {/* Detail Doctor */}
@@ -93,9 +105,13 @@ function ItemDoctor({ doctor }) {
<Link href={`/doctor/${doctor?.uuid}`}> <Link href={`/doctor/${doctor?.uuid}`}>
<Button <Button
variant="contained" variant="contained"
onClick={handleLoading}
loading={doctor?.loading}
className="!p-[14px] !absolute !left-4 !bottom-4 !rounded-full !w-fit" className="!p-[14px] !absolute !left-4 !bottom-4 !rounded-full !w-fit"
> >
<div className={doctor && doctor.loading ? "opacity-0" : ""}>
<ArrowLeftD /> <ArrowLeftD />
</div>
</Button> </Button>
</Link> </Link>
</li> </li>
+4 -4
View File
@@ -60,16 +60,16 @@ function Content({
list={states} list={states}
label="استان" label="استان"
disabled={matchedState} disabled={matchedState}
value={filter.state} value={filter?.state}
updateData={updateData} updateData={updateData}
/> />
<AddressSelector <AddressSelector
name="city" name="city"
label="شهر" label="شهر"
value={filter.city} value={filter?.city}
list={filteredCities} list={filteredCities}
updateData={updateData} updateData={updateData}
disabled={!filter.state} disabled={!filter?.state}
/> />
</div> </div>
<div className="w-full flex justify-end"> <div className="w-full flex justify-end">
@@ -79,7 +79,7 @@ function Content({
onClick={handleClick} onClick={handleClick}
variant="contained" variant="contained"
color="primary" color="primary"
disabled={!filter.city || !filter.state} disabled={!filter?.city || !filter?.state}
> >
تایید و ادامه تایید و ادامه
<ArrowLeftM /> <ArrowLeftM />
+33 -9
View File
@@ -1,22 +1,44 @@
import CloseModalD from "@/components/icons/CloseModalD"; import CloseModalD from "@/components/icons/CloseModalD";
import Image from "next/image"; import Image from "next/image";
const maps = [ function Content({ data, onClose }) {
{ src: "/assets/images/snapp.png", name: "snapp" }, const { latitude, longitude } = data && data.map;
{ src: "/assets/images/tapsi.png", name: "tapsi" },
{ src: "/assets/images/maps.png", name: "maps" }, const maps = [
{ src: "/assets/images/balad.png", name: "balad" }, {
{ src: "/assets/images/waze.png", name: "waze" }, src: "/assets/images/snapp.png",
]; name: "snapp",
url: `https://snapp.ir/route?lat=${latitude}&lng=${longitude}`,
},
{
src: "/assets/images/tapsi.png",
name: "tapsi",
url: `https://tapsi.ir/route?lat=${latitude}&lng=${longitude}`,
},
{
src: "/assets/images/maps.png",
name: "maps",
url: `https://www.google.com/maps/dir/?api=1&destination=${latitude},${longitude}`,
},
{
src: "/assets/images/balad.png",
name: "balad",
url: `https://balad.ir/map?lat=${latitude}&lng=${longitude}`,
},
{
src: "/assets/images/waze.png",
name: "waze",
url: `https://waze.com/ul?ll=${latitude},${longitude}&navigate=yes`,
},
];
function Content({ handleClose }) {
return ( return (
<> <>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium"> <p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium">
باز شدن با باز شدن با
</p> </p>
<div className="cursor-pointer" onClick={handleClose}> <div className="cursor-pointer" onClick={onClose}>
<CloseModalD /> <CloseModalD />
</div> </div>
</div> </div>
@@ -27,6 +49,7 @@ function Content({ handleClose }) {
key={idx} key={idx}
className="flex items-center flex-col justify-center gap-1" className="flex items-center flex-col justify-center gap-1"
> >
<a href={item.url} target="_blank" rel="noopener noreferrer">
<Image <Image
className=" className="
w-[32px] h-[32px] lg:w-[40px] lg:h-[40px] w-[32px] h-[32px] lg:w-[40px] lg:h-[40px]
@@ -41,6 +64,7 @@ function Content({ handleClose }) {
<p className="text-[#3B3B3B] font-normal text-[14px]"> <p className="text-[#3B3B3B] font-normal text-[14px]">
{item.name} {item.name}
</p> </p>
</a>
</li> </li>
))} ))}
</ul> </ul>
+3 -3
View File
@@ -4,7 +4,7 @@ import { Drawer, Modal } from "@mui/material";
import { styleDefault } from "@/mui"; import { styleDefault } from "@/mui";
import Content from "./Content"; import Content from "./Content";
function ModalOpenLocation({ open, setOpen, handleClose, children }) { function ModalOpenLocation({ open, data, setOpen, handleClose, children }) {
const toggleDrawer = (newOpen) => () => { const toggleDrawer = (newOpen) => () => {
setOpen(newOpen); setOpen(newOpen);
}; };
@@ -19,7 +19,7 @@ function ModalOpenLocation({ open, setOpen, handleClose, children }) {
style={styleDefault} style={styleDefault}
className="!pt-[12px] !pb-[28px] !px-[24px] !bg-[#FAFAFA] !rounded-[8px] !overflow-hidden" className="!pt-[12px] !pb-[28px] !px-[24px] !bg-[#FAFAFA] !rounded-[8px] !overflow-hidden"
> >
<Content onClose={handleClose} /> <Content data={data} onClose={handleClose} />
</div> </div>
</Modal> </Modal>
<Drawer <Drawer
@@ -35,7 +35,7 @@ function ModalOpenLocation({ open, setOpen, handleClose, children }) {
}} }}
> >
<div className="!pt-[12px] !pb-[28px] !px-[24px] !bg-[#FAFAFA] !rounded-[8px] !overflow-hidden !shadow-[0px_1px_24.8px_0px_rgba(155,_155,_155,_0.27)]"> <div className="!pt-[12px] !pb-[28px] !px-[24px] !bg-[#FAFAFA] !rounded-[8px] !overflow-hidden !shadow-[0px_1px_24.8px_0px_rgba(155,_155,_155,_0.27)]">
<Content handleClose={handleClose} /> <Content data={data} handleClose={handleClose} />
</div> </div>
</Drawer> </Drawer>
</> </>
+2 -2
View File
@@ -1,9 +1,9 @@
import { Button, ButtonGroup, TextField } from "@mui/material"; import { Button, ButtonGroup, TextField } from "@mui/material";
import SearchD from "@/components/icons/SearchD"; import SearchD from "@/components/icons/SearchD";
import ModalSearchCity from "@/app/component/modalSearchCity";
import { useState } from "react"; import { useState } from "react";
import ButtonFilter from "@/app/component/modalSearchCity/ButtonFilter"; import ButtonFilter from "@/app/component/modalSearchCity/ButtonFilter";
import AutoCompleteSearch from "@/components/searchHead/smSearch/AutoCompleteSearch"; import AutoCompleteSearch from "@/components/searchHead/smSearch/AutoCompleteSearch";
import ModalSearchCity from "@/components/home/search/modal";
function SearchBar({ function SearchBar({
selected, selected,
@@ -28,7 +28,7 @@ function SearchBar({
setOpen={setOpen} setOpen={setOpen}
selected={selected} selected={selected}
setSelected={setSelected} setSelected={setSelected}
handleSearch={handleSearch} // handleSearch={handleSearch}
> >
<ButtonFilter setOpen={setOpen} selected={selected} /> <ButtonFilter setOpen={setOpen} selected={selected} />
</ModalSearchCity> </ModalSearchCity>
+1 -1
View File
@@ -32,8 +32,8 @@ function ClinicsPage({ clinics, matchedCity, matchedState }) {
> >
<SearchBar <SearchBar
selected={selected} selected={selected}
state={matchedState}
setSelected={setSelected} setSelected={setSelected}
state={matchedState?.name}
handleSearch={handleSearch} handleSearch={handleSearch}
specialties={specialtiesData} specialties={specialtiesData}
/> />
@@ -5,7 +5,9 @@ import GreenCalendarTurn from "@/components/icons/GreenCalendarTurn";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import React from "react"; import moment from "moment-jalaali";
moment.loadPersian({ dialect: "persian-modern" });
function ItemAppointment({ turn, loading }) { function ItemAppointment({ turn, loading }) {
return ( return (
@@ -17,7 +19,7 @@ function ItemAppointment({ turn, loading }) {
<span className="block w-full h-px bg-[#EFEFEF] my-4"></span> <span className="block w-full h-px bg-[#EFEFEF] my-4"></span>
<div className="flex items-center justify-start gap-3"> <div className="flex items-center justify-start gap-3">
<CircularLoading width={56} height={56} loading={loading}> <CircularLoading width={56} height={56} loading={loading}>
<Image src={turn?.img || ""} width={56} height={56} alt="doctor" /> <Image width={56} height={56} alt="doctor" src={turn?.img[0]?.url} />
</CircularLoading> </CircularLoading>
<div className="flex flex-col items-start justify-center gap-2"> <div className="flex flex-col items-start justify-center gap-2">
<TextLoading width={60} height={15} loading={loading}> <TextLoading width={60} height={15} loading={loading}>
@@ -26,9 +28,9 @@ function ItemAppointment({ turn, loading }) {
<TextLoading width={65} height={15} loading={loading}> <TextLoading width={65} height={15} loading={loading}>
<p className="text-[#525252] text-[14px] font-medium"> <p className="text-[#525252] text-[14px] font-medium">
تخصص: تخصص:
{turn?.expertise?.map( {turn?.specialties?.map(
(item, idx) => (item, idx) =>
`${item.name} ${turn.expertise.length === idx + 1 ? "" : "|"} ` `${item.name} ${turn.specialties.length === idx + 1 ? "" : "|"} `
)} )}
</p> </p>
</TextLoading> </TextLoading>
@@ -39,7 +41,11 @@ function ItemAppointment({ turn, loading }) {
<GreenCalendarTurn /> <GreenCalendarTurn />
<TextLoading width={100} height={15} loading={loading}> <TextLoading width={100} height={15} loading={loading}>
<p className="text-[#05BA58] text-[12px] font-medium"> <p className="text-[#05BA58] text-[12px] font-medium">
اولین نوبت آزاد: {turn.first_free_turn || "یکشنبه 24 اردیبهشت"} {Number(turn?.active)
? `اولین نوبت آزاد: ${moment(turn?.free_turn * 1000).format(
"dddd jD jMMMM [ساعت] HH:mm"
)}`
: "نوبت ندارد"}
</p> </p>
</TextLoading> </TextLoading>
</div> </div>
+2 -2
View File
@@ -22,9 +22,9 @@ function AppointmentList({ doctors, loading }) {
border-solid bottom-0 right-0 w-full p-4 flex items-center justify-between" border-solid bottom-0 right-0 w-full p-4 flex items-center justify-between"
> >
<p className="text-[#05BA58] text-[11px] font-normal"> <p className="text-[#05BA58] text-[11px] font-normal">
اولین نوبت آزاد: {doctors[0]?.free_turn} اولین نوبت آزاد: {doctors && doctors[0]?.free_turn}
</p> </p>
<Link href={`/appointment/${doctors[0]?.id}`}> <Link href={`/appointment/${doctors && doctors[0]?.id}`}>
<Button <Button
variant="contained" variant="contained"
className="!py-2 !px-4 gap-1 !text-[14px] !font-medium" className="!py-2 !px-4 gap-1 !text-[14px] !font-medium"
@@ -8,7 +8,7 @@ function ProgressAll({ doctor }) {
<CircularLoading width={120} height={120}> <CircularLoading width={120} height={120}>
<CircularProgress <CircularProgress
variant="determinate" variant="determinate"
value={doctor?.total_user_satisfaction} value={doctor?.satisfaction}
sx={{ sx={{
"& .MuiCircularProgress-circle": { "& .MuiCircularProgress-circle": {
stroke: "#05BA58", stroke: "#05BA58",
@@ -28,7 +28,7 @@ function ProgressAll({ doctor }) {
رضایت کاربران رضایت کاربران
</p> </p>
<p className="text-[#3B3B3B]"> <p className="text-[#3B3B3B]">
{numberToArStyle(doctor?.total_user_satisfaction)}% {numberToArStyle(doctor?.satisfaction)}%
</p> </p>
</div> </div>
</CircularLoading> </CircularLoading>
@@ -6,7 +6,10 @@ import ProgressDetail from "@/app/component/ProfressDetail";
import TextLoading from "@/app/component/loading/Text"; import TextLoading from "@/app/component/loading/Text";
import CustomLoading from "@/app/component/loading/Custom"; import CustomLoading from "@/app/component/loading/Custom";
function Chart({ doctor }) { function Chart({ comments, doctor }) {
console.log(doctor);
console.log(comments);
return ( return (
<div <div
className=" className="
@@ -17,10 +20,10 @@ function Chart({ doctor }) {
<TextLoading width={130} height={20}> <TextLoading width={130} height={20}>
<div className="flex items-center justify-start gap-2.5"> <div className="flex items-center justify-start gap-2.5">
<p className="text-[#616161] text-[14px] font-medium"> <p className="text-[#616161] text-[14px] font-medium">
امتیاز کلی کاربران: {doctor?.overall_score} از 5 امتیاز کلی کاربران: {doctor?.point} از 5
</p> </p>
<p className="text-[#7E7E7E] text-[14px] font-normal"> <p className="text-[#7E7E7E] text-[14px] font-normal">
(از مجموع {doctor?.comments?.length} نظر) (از مجموع {comments?.length} نظر)
</p> </p>
</div> </div>
</TextLoading> </TextLoading>
@@ -28,7 +31,7 @@ function Chart({ doctor }) {
<TextLoading width={160} height={20}> <TextLoading width={160} height={20}>
<Rating <Rating
readOnly readOnly
value={4} value={3.5}
size="small" size="small"
name="read-only" name="read-only"
className="!mt-4 !mb-6 !hidden lg:!flex" className="!mt-4 !mb-6 !hidden lg:!flex"
@@ -18,7 +18,7 @@ function Comments({ doctor, comments }) {
</p> </p>
<ModalAnswer doctor={doctor} /> <ModalAnswer doctor={doctor} />
</div> </div>
<Chart doctor={doctor} /> <Chart comments={comments} doctor={doctor} />
<Comment data={doctor} comments={comments} /> <Comment data={doctor} comments={comments} />
</div> </div>
); );
@@ -7,7 +7,7 @@ import ModalOpenLocation from "@/app/component/openLocation";
import RoutingWhiteC from "@/components/icons/RoutingWhiteC"; import RoutingWhiteC from "@/components/icons/RoutingWhiteC";
import RoutingC from "@/components/icons/RoutingC"; import RoutingC from "@/components/icons/RoutingC";
function Item({ doctor, data }) { function Item({ data }) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -45,15 +45,13 @@ function Item({ doctor, data }) {
<p className="text-[#525252] text-[16px] font-normal"> <p className="text-[#525252] text-[16px] font-normal">
آدرس: {data.address} آدرس: {data.address}
</p> </p>
<div className="flex w-full items-center justify-between">
<p className="text-[#525252] text-[16px] font-normal"> <p className="text-[#525252] text-[16px] font-normal">
تلفن: {data.telephone} تلفن: {data.telephone}
</p> </p>
<div className="flex w-full items-center justify-between">
<p className="text-[#525252] text-[16px] font-normal">
روزهای کاری: {doctor.hours_of_work}
</p>
<ModalOpenLocation <ModalOpenLocation
open={open} open={open}
data={data}
setOpen={setOpen} setOpen={setOpen}
handleClose={handleClose} handleClose={handleClose}
> >
@@ -79,6 +77,7 @@ function Item({ doctor, data }) {
<ModalOpenLocation <ModalOpenLocation
open={open} open={open}
data={data}
setOpen={setOpen} setOpen={setOpen}
handleClose={handleClose} handleClose={handleClose}
> >
@@ -11,7 +11,7 @@ function Locations({ doctor }) {
{doctor?.address?.map((item, idx) => ( {doctor?.address?.map((item, idx) => (
<CustomLoading key={idx} width="full" height={40}> <CustomLoading key={idx} width="full" height={40}>
<li className="w-full"> <li className="w-full">
<Item doctor={doctor} data={item} /> <Item data={item} />
{doctor?.address.length > idx + 1 && ( {doctor?.address.length > idx + 1 && (
<span className="w-full h-px bg-[#EFEFEF] block my-[12px] md:my-[14px] lg:my-[16px]"></span> <span className="w-full h-px bg-[#EFEFEF] block my-[12px] md:my-[14px] lg:my-[16px]"></span>
)} )}
-2
View File
@@ -4,8 +4,6 @@ import Share from "./detailDoctor/Share";
import CustomLoading from "@/app/component/loading/Custom"; import CustomLoading from "@/app/component/loading/Custom";
function DoctorPage({ doctor, doctors, comments }) { function DoctorPage({ doctor, doctors, comments }) {
comments;
return ( return (
<div className="pt-[92px] sm:pt-[120px] mt:pt-[148px] lg:pt-[176px] mt-[px] padding-responsive"> <div className="pt-[92px] sm:pt-[120px] mt:pt-[148px] lg:pt-[176px] mt-[px] padding-responsive">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
-1
View File
@@ -67,7 +67,6 @@ function Head({ matchedCity, matchedState, setDoctors }) {
throw err; throw err;
}); });
}; };
console.log(filter);
return ( return (
<div className="flex justify-center gap-[16px] sm:gap-[19px] md:gap-[22px] lg:gap-[24px] flex-col items-center mt-0 md:mt-[40px] lg:mt-[90px]"> <div className="flex justify-center gap-[16px] sm:gap-[19px] md:gap-[22px] lg:gap-[24px] flex-col items-center mt-0 md:mt-[40px] lg:mt-[90px]">
+1 -1
View File
@@ -16,7 +16,7 @@ function DoctorsPage({ matchedCity, matchedState, list }) {
matchedCity={matchedCity} matchedCity={matchedCity}
matchedState={matchedState} matchedState={matchedState}
/> />
<ListDoctors list={doctors} /> <ListDoctors doctors={doctors} setDoctors={setDoctors} />
</div> </div>
); );
} }
+11 -6
View File
@@ -1,15 +1,20 @@
import doctors from "@/data/doctors.json"; import doctorList from "@/data/doctors.json";
import Pagination from "@/app/component/Pagination"; import Pagination from "@/app/component/Pagination";
import ItemDoctor from "@/app/component/ItemDoctor"; import ItemDoctor from "@/app/component/ItemDoctor";
function ListDoctors({ list }) { function ListDoctors({ doctors, setDoctors }) {
return ( return (
<> <>
<div className="mt-[24px] sm:mt-[37px] md:mt-[50px] lg:mt-[64px]"> <div className="mt-[24px] sm:mt-[37px] md:mt-[50px] lg:mt-[64px]">
{list && list.length ? ( {doctors && doctors.length ? (
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[24px]"> <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[24px]">
{list?.map((doctor) => ( {doctors?.map((doctor) => (
<ItemDoctor key={doctor.id} doctor={doctor} /> <ItemDoctor
key={doctor.id}
doctor={doctor}
doctors={doctors}
setDoctors={setDoctors}
/>
))} ))}
</ul> </ul>
) : ( ) : (
@@ -19,7 +24,7 @@ function ListDoctors({ list }) {
)} )}
</div> </div>
<div className="mt-[56px] flex justify-center"> <div className="mt-[56px] flex justify-center">
<Pagination list={doctors} itemsPerPage={12} /> <Pagination list={doctorList} itemsPerPage={12} />
</div> </div>
</> </>
); );