fix filter gender, req and set new data when delete all filters, show date time with timestamp in doctors list, fix send data in home page list specialty, change data of params for request to doctors in doctors page

This commit is contained in:
Ehsan
2025-09-13 15:05:50 +03:30
parent 6414293d1c
commit f0e8a753c3
11 changed files with 227 additions and 114 deletions
+4 -1
View File
@@ -10,6 +10,9 @@ import Link from "next/link";
import CustomLoading from "./loading/Custom"; 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";
moment.loadPersian({ dialect: "persian-modern" });
function ItemDoctor({ doctor }) { function ItemDoctor({ doctor }) {
return ( return (
@@ -78,7 +81,7 @@ function ItemDoctor({ doctor }) {
}`} }`}
> >
{Number(doctor?.active) {Number(doctor?.active)
? `اولین نوبت آزاد: ${Number(doctor?.active)}` ? `اولین نوبت آزاد: ${moment(doctor?.free_turn * 1000)?.format("dddd jD jMMMM")}`
: "نوبت ندارد"} : "نوبت ندارد"}
</p> </p>
</CustomLoading> </CustomLoading>
-2
View File
@@ -21,8 +21,6 @@ function Content({
const handleClick = () => { const handleClick = () => {
setLoading(true); setLoading(true);
sendReq().finally(() => { sendReq().finally(() => {
console.log("asd");
setLoading(false); setLoading(false);
handleClose(); handleClose();
}); });
+5 -20
View File
@@ -1,39 +1,24 @@
import DoctorsPage from "@/components/doctors"; import DoctorsPage from "@/components/doctors";
import Layout from "@/components/layout/StLayout"; import Layout from "@/components/layout/StLayout";
import { buildDoctorParams } from "@/helper";
import { getStateInfo } from "@/lib/getStateInfo"; import { getStateInfo } from "@/lib/getStateInfo";
import axios from "axios"; import axios from "axios";
// Data
import states from "@/data/state.json";
import cities from "@/data/city.json";
async function Doctors({ searchParams }) { async function Doctors({ searchParams }) {
const { matchedCity, matchedState } = getStateInfo(); const { matchedCity, matchedState } = getStateInfo();
const API_URL = process.env.NEXT_PUBLIC_API_URL; const API_URL = process.env.NEXT_PUBLIC_API_URL;
let doctors = null; let doctors = null;
try { try {
const params = {}; const params = buildDoctorParams(searchParams);
const state = searchParams.state;
const city = searchParams.city;
console.log(state);
console.log(city);
if (matchedCity)
params.city = city
? cities.find((item) => item.name === city)?.id
: matchedCity?.id || null;
if (matchedState)
params.state = state
? states.find((item) => item.name === state)?.id
: matchedState?.id || null;
if (searchParams.search) params.name = searchParams.search;
const response = await axios.get(`${API_URL}/api/v1/doctors`, { const response = await axios.get(`${API_URL}/api/v1/doctors`, {
params, params,
}); });
doctors = response.data.data; doctors = response.data.data;
} catch (error) {} } catch (error) {
console.error("Error fetching doctors:", error);
}
return ( return (
<Layout> <Layout>
-9
View File
@@ -1,9 +0,0 @@
import React from 'react'
function Content() {
return (
<div>Content</div>
)
}
export default Content
+34
View File
@@ -0,0 +1,34 @@
import LocationD from "../../icons/LocationD";
import { Button } from "@mui/material";
import ModalSearchCity from "@/app/component/modalSearchCity";
import { useState } from "react";
function FilterLocate({ filter, sendReq, setFilter, setDataInURL }) {
const [open, setOpen] = useState(false);
return (
<ModalSearchCity
open={open}
filter={filter}
sendReq={sendReq}
setOpen={setOpen}
setFilter={setFilter}
setDataInURL={setDataInURL}
>
<Button
className="!flex !ml-auto !w-fit !px-3 !py-1 !items-center !justify-start !gap-2 !cursor-pointer"
onClick={() => setOpen(true)}
>
<LocationD />
<h1 className="text-[#525252] text-[16px] font-semibold">
{" "}
{filter?.state?.name || "استان"}
{" / "}
{filter?.city?.name || "شهر"}
</h1>
</Button>
</ModalSearchCity>
);
}
export default FilterLocate;
+7 -34
View File
@@ -2,38 +2,26 @@ import { useState } from "react";
import SearchBar from "../search"; import SearchBar from "../search";
import SortList from "../sortlist"; import SortList from "../sortlist";
import { Button } from "@mui/material"; import { useRouter, useSearchParams } from "next/navigation";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import LocationD from "../../icons/LocationD";
import specialties from "@/data/specialties.json"; import specialties from "@/data/specialties.json";
import ModalSearchCity from "@/app/component/modalSearchCity";
// Data // Data
import states from "@/data/state.json"; import states from "@/data/state.json";
import cities from "@/data/city.json"; import cities from "@/data/city.json";
import { QueryForDoctorsFilter, QueryForDoctorsReq } from "@/helper"; import { QueryForDoctorsFilter, QueryForDoctorsReq } from "@/helper";
import { request } from "@/services/response"; import { request } from "@/services/response";
import FilterLocate from "./FilterLocate";
function Head({ matchedCity, matchedState, setDoctors }) { function Head({ matchedCity, matchedState, setDoctors }) {
const params = useParams();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const router = useRouter(); const router = useRouter();
const fieldName = searchParams.get("specialty"); const fieldName = searchParams.get("specialty");
const [open, setOpen] = useState(false);
const findItem = (key, name) => const findItem = (key, name) =>
specialties.find((item) => item[key] === name); specialties.find((item) => item[key] === name);
const isParent = const isParent =
findItem("name", fieldName) && findItem("name", fieldName).parent; findItem("name", fieldName) && findItem("name", fieldName).parent;
const defaultData = {
category: isParent ? findItem("id", isParent) : findItem("name", fieldName),
specialties: isParent && findItem("name", fieldName),
turn: params && params.hasOwnProperty("turn") ? JSON.parse(params.turn) : 1,
gender: params.gender || "هر دو",
degree: params.degree || "همه موارد",
};
const selectedState = searchParams.get("state") || matchedState?.name; const selectedState = searchParams.get("state") || matchedState?.name;
const selectedCity = const selectedCity =
searchParams.get("city") || searchParams.get("city") ||
@@ -51,7 +39,6 @@ function Head({ matchedCity, matchedState, setDoctors }) {
name: searchParams.get("name") || "", name: searchParams.get("name") || "",
}; };
const [data, setData] = useState(defaultData);
const [filter, setFilter] = useState(defaultFilter); const [filter, setFilter] = useState(defaultFilter);
const setDataInURL = (newFilter) => { const setDataInURL = (newFilter) => {
@@ -83,27 +70,12 @@ function Head({ matchedCity, matchedState, setDoctors }) {
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]">
<ModalSearchCity <FilterLocate
open={open}
filter={filter} filter={filter}
sendReq={sendReq} sendReq={sendReq}
setOpen={setOpen}
setFilter={setFilter} setFilter={setFilter}
setDataInURL={setDataInURL} setDataInURL={setDataInURL}
> />
<Button
className="!flex !ml-auto !w-fit !px-3 !py-1 !items-center !justify-start !gap-2 !cursor-pointer"
onClick={() => setOpen(true)}
>
<LocationD />
<h1 className="text-[#525252] text-[16px] font-semibold">
{" "}
{filter?.state?.name || "استان"}
{" / "}
{filter?.city?.name || "شهر"}
</h1>
</Button>
</ModalSearchCity>
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-center gap-[24px] lg:gap-[32px] w-full"> <div className="flex flex-col lg:flex-row items-start lg:items-center justify-center gap-[24px] lg:gap-[32px] w-full">
<SearchBar <SearchBar
filter={filter} filter={filter}
@@ -113,10 +85,11 @@ function Head({ matchedCity, matchedState, setDoctors }) {
setDataInURL={setDataInURL} setDataInURL={setDataInURL}
/> />
<SortList <SortList
data={data}
filter={filter} filter={filter}
setData={setData} sendReq={sendReq}
setFilter={setFilter}
updateData={updateData} updateData={updateData}
setDataInURL={setDataInURL}
/> />
</div> </div>
</div> </div>
@@ -0,0 +1,58 @@
import CloseOrangeModal from "@/components/icons/CloseOrangeModal";
import { Button, CircularProgress } from "@mui/material";
import { useRouter } from "next/navigation";
import { clearFilterParams } from "@/helper";
import { useState } from "react";
function DeleteAllButton({ filter, sendReq, setFilter, handleClose }) {
const [loading, setLoading] = useState(false);
const router = useRouter();
const resetFilter = () => {
setLoading(true);
clearFilterParams(router, [
"category",
"specialty",
"active",
"gender",
"degree",
]);
const newFilter = {
...filter,
...{
category: undefined,
specialty: undefined,
active: 0,
gender: "هر دو",
degree: "همه موارد",
},
};
sendReq(newFilter).finally(() => {
setLoading(false);
handleClose();
setFilter(newFilter);
});
};
return (
<div className="flex justify-between w-full">
<p className="text-[#3B3B3B] text-[20px] font-bold">فیلترها</p>
<Button
variant="text"
disabled={loading}
onClick={resetFilter}
className="cursor-pointer !py-0 !px-2 flex items-center justify-center gap-1"
>
{loading ? (
<CircularProgress className="!max-w-[17px] !max-h-[17px] !w-[17px] !h-[17px] !text-[#F17732]" />
) : (
<CloseOrangeModal />
)}
<p className="text-[#F17732] text-[14px] font-normal">حذف همه</p>
</Button>
</div>
);
}
export default DeleteAllButton;
+8 -42
View File
@@ -1,13 +1,11 @@
"use client"; "use client";
import CloseOrangeModal from "@/components/icons/CloseOrangeModal";
import { styleDefault } from "@/mui"; import { styleDefault } from "@/mui";
import { Button, Modal } from "@mui/material"; import { Modal } from "@mui/material";
import { useState } from "react"; import { useState } from "react";
import Content from "./Content"; import Content from "./Content";
import { useRouter } from "next/navigation";
import { clearFilterParams } from "@/helper";
import ButtonApply from "./ButtonApply"; import ButtonApply from "./ButtonApply";
import DeleteAllButton from "./DeleteAllButton";
function FilterModal({ function FilterModal({
filter, filter,
@@ -17,7 +15,6 @@ function FilterModal({
updateData, updateData,
setDataInURL, setDataInURL,
}) { }) {
const router = useRouter();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true); const handleOpen = () => setOpen(true);
@@ -25,30 +22,6 @@ function FilterModal({
setOpen(false); setOpen(false);
}; };
const resetFilter = () => {
clearFilterParams(router, [
"category",
"specialty",
"active",
"gender",
"degree",
]);
const newFilter = {
category: undefined,
specialty: undefined,
active: 0,
gender: "هر دو",
degree: "همه موارد",
};
setFilter({
...filter,
...newFilter,
});
handleClose();
};
return ( return (
<> <>
{/* Button Modal */} {/* Button Modal */}
@@ -62,19 +35,12 @@ function FilterModal({
!w-full md:!w-fit !h-full md:!h-fit !flex !flex-col !justify-between !max-h-screen md:!max-h-[calc(100%_-_32px)]" !w-full md:!w-fit !h-full md:!h-fit !flex !flex-col !justify-between !max-h-screen md:!max-h-[calc(100%_-_32px)]"
> >
<div> <div>
<div className="flex justify-between w-full"> <DeleteAllButton
<p className="text-[#3B3B3B] text-[20px] font-bold">فیلترها</p> filter={filter}
<Button sendReq={sendReq}
className="cursor-pointer !p-0 flex items-center justify-center gap-1" setFilter={setFilter}
onClick={resetFilter} handleClose={handleClose}
variant="text" />
>
<CloseOrangeModal />
<p className="text-[#F17732] text-[14px] font-normal">
حذف همه
</p>
</Button>
</div>
<Content <Content
filter={filter} filter={filter}
setFilter={setFilter} setFilter={setFilter}
+14 -2
View File
@@ -4,10 +4,22 @@ import FilterModal from "../modal/FilterModal";
import FilterBtn from "./FilterBtn"; import FilterBtn from "./FilterBtn";
import SelectSort from "./SelectSort"; import SelectSort from "./SelectSort";
function SortList({ data, filter, setData, updateData }) { function SortList({
filter,
updateData,
sendReq,
setFilter,
setDataInURL,
}) {
return ( return (
<div className="flex flex-wrap items-center lg:!w-[25%] gap-[20px] gap-y-[12px] lg:gap-0"> <div className="flex flex-wrap items-center lg:!w-[25%] gap-[20px] gap-y-[12px] lg:gap-0">
<FilterModal data={data} setData={setData}> <FilterModal
filter={filter}
sendReq={sendReq}
setFilter={setFilter}
updateData={updateData}
setDataInURL={setDataInURL}
>
<FilterBtn /> <FilterBtn />
</FilterModal> </FilterModal>
<SelectSort filter={filter} updateData={updateData} /> <SelectSort filter={filter} updateData={updateData} />
+1 -1
View File
@@ -17,7 +17,7 @@ function FrequentSearches() {
href={{ href={{
pathname: "/doctors", pathname: "/doctors",
query: { query: {
field: item.id, specialty: item.name,
}, },
}} }}
> >
+96 -3
View File
@@ -1,8 +1,12 @@
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import specialties from "@/data/specialties.json";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import moment from "moment-jalaali"; import moment from "moment-jalaali";
// Data
import specialties from "@/data/specialties.json";
import states from "@/data/state.json";
import cities from "@/data/city.json";
export const numberToArStyle = (num) => num?.toLocaleString("ar-AE") || ""; export const numberToArStyle = (num) => num?.toLocaleString("ar-AE") || "";
export const isActiveURL = (link, name) => link === name; export const isActiveURL = (link, name) => link === name;
@@ -329,9 +333,25 @@ export const QueryForDoctorsReq = (newFilter) => {
if (newFilter.state?.id) params.state = newFilter.state.id; if (newFilter.state?.id) params.state = newFilter.state.id;
if (newFilter.degree && newFilter.degree !== "همه موارد") if (newFilter.degree && newFilter.degree !== "همه موارد")
params.degree = newFilter.degree; switch (newFilter.degree) {
case "کارشناس":
params.degree = "expert";
break;
case "پزشک عمومی":
params.degree = "general";
break;
case "پزشک متخصص":
params.degree = "specialist";
break;
case "پزشک فوق تخصص":
params.degree = "subspecialistplus";
break;
default:
break;
}
if (newFilter.gender && newFilter.gender !== "هر دو") if (newFilter.gender && newFilter.gender !== "هر دو")
params.gender = newFilter.gender; params.gender = newFilter.gender === "مرد" ? "man" : "woman";
if (newFilter.name) params.name = newFilter.name; if (newFilter.name) params.name = newFilter.name;
@@ -342,3 +362,76 @@ export const QueryForDoctorsReq = (newFilter) => {
return params; return params;
}; };
export const buildDoctorParams = (searchParams) => {
const params = {};
// 🔹 active
if (searchParams.active && Number(searchParams.active) === 1) {
params.active = 1;
}
// 🔹 specialty (find by name in specialties.json)
if (searchParams.specialty) {
const spec = specialties.find((s) => s.name === searchParams.specialty);
if (spec) {
params.specialty = spec.id;
}
}
// 🔹 city (find by name in city.json)
if (searchParams.city) {
const cityObj = cities.find((c) => c.name === searchParams.city);
if (cityObj) {
params.city = cityObj.id;
}
}
// 🔹 state (find by name in state.json)
if (searchParams.state) {
const stateObj = states.find((s) => s.name === searchParams.state);
if (stateObj) {
params.state = stateObj.id;
}
}
// 🔹 degree → convert fa → en
if (searchParams.degree) {
switch (searchParams.degree) {
case "کارشناس":
params.degree = "expert";
break;
case "پزشک عمومی":
params.degree = "general";
break;
case "پزشک متخصص":
params.degree = "specialist";
break;
case "پزشک فوق تخصص":
params.degree = "subspecialistplus";
break;
default:
break;
}
}
// 🔹 gender
if (searchParams.gender) {
if (searchParams.gender === "مرد") params.gender = "man";
if (searchParams.gender === "زن") params.gender = "woman";
}
// 🔹 name
if (searchParams.name) {
params.name = searchParams.name;
}
// 🔹 sort (3 = ASC, 4|5 = DESC)
if (searchParams.sort) {
const sortNum = Number(searchParams.sort);
if (sortNum === 3) params.sort = "ASC";
else if ([4, 5].includes(sortNum)) params.sort = "DESC";
}
return params;
};