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