loading ,page
This commit is contained in:
@@ -2,15 +2,16 @@ import ClinicPage from "@/components/clinic";
|
||||
import Layout from "@/components/layout/StLayout";
|
||||
import { fetchReq } from "@/lib/req";
|
||||
|
||||
async function Clinic({ params: { slug } }) {
|
||||
async function Clinic({ params: { slug },searchParams }) {
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
const page = Number(searchParams?.page) || 1;
|
||||
const reqClinics = await fetchReq(
|
||||
`${API_URL}/api/v1/clinic/${slug}`
|
||||
);
|
||||
const limit=50
|
||||
|
||||
const reqDoctors = await fetchReq(
|
||||
`${API_URL}/api/v1/clinic/doctor-list/${slug}?page=1&limit=${limit}`
|
||||
`${API_URL}/api/v1/clinic/doctor-list/${slug}?page=${page}&limit=${limit}`
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -20,34 +20,50 @@ function List({
|
||||
const router = useRouter();
|
||||
const changePage = async (_, num) => {
|
||||
setLoading(true);
|
||||
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("page", num);
|
||||
router.push(`/clinic/${slug}?${searchParams.toString()}`);
|
||||
|
||||
setPage(num);
|
||||
|
||||
let params = {
|
||||
...QueryForDoctorsClinicReq(filter),
|
||||
current: num,
|
||||
limit,
|
||||
};
|
||||
try {
|
||||
const doctors = await getClinicDoctors(slug, params);
|
||||
setDoctors(doctors.data);
|
||||
setTotalPage(doctors?.page?.total_pages || 1);
|
||||
let params = {
|
||||
...QueryForDoctorsClinicReq(filter),
|
||||
page: num,
|
||||
limit,
|
||||
};
|
||||
|
||||
const res = await getClinicDoctors(slug, params);
|
||||
console.log("📦 پاسخ API:", res);
|
||||
|
||||
// اگر سرور داده را در data برمیگرداند
|
||||
if (res?.data) {
|
||||
setDoctors([...res.data]);
|
||||
setTotalPage(res?.page?.total_pages || 1);
|
||||
}
|
||||
// اگر فقط آرایه برمیگرداند
|
||||
else if (Array.isArray(res)) {
|
||||
setDoctors([...res]);
|
||||
setTotalPage(1);
|
||||
}
|
||||
|
||||
setPage(num);
|
||||
} catch (error) {
|
||||
console.error("❌ خطا در دریافت داده:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const doctorList = Array.isArray(doctors?.data) ? doctors.data : doctors;
|
||||
const doctorList = Array.isArray(doctors)
|
||||
? doctors
|
||||
: Array.isArray(doctors?.data)
|
||||
? doctors.data
|
||||
: [];
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center py-20">
|
||||
<div className="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
|
||||
</div>
|
||||
) : doctorList?.length === 0 ? (
|
||||
{ doctorList?.length === 0 ? (
|
||||
<div className="text-center text-gray-500 mt-10">
|
||||
دکتری مطابق فیلتر شما یافت نشد
|
||||
</div>
|
||||
@@ -55,7 +71,12 @@ function List({
|
||||
<>
|
||||
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 mt-[40px] gap-[16px] md:gap-[20px] lg:gap-[24px]">
|
||||
{doctorList?.map((doctor) => (
|
||||
<ItemDoctor key={doctor.id} doctor={doctor} />
|
||||
<ItemDoctor
|
||||
loading={loading}
|
||||
setDoctors={setDoctors}
|
||||
key={doctor.id}
|
||||
doctor={doctor}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<div className="mt-[56px] flex justify-center">
|
||||
|
||||
@@ -57,9 +57,10 @@ function Head({ setDoctors, slug, filter, setFilter,setPage,setTotalPage }) {
|
||||
let params = QueryForDoctorsClinicReq(data);
|
||||
|
||||
|
||||
if (
|
||||
data?.name === data.specialty?.name ||
|
||||
data?.name === data.category?.name
|
||||
if (
|
||||
data?.specialty &&
|
||||
(data?.name === data.specialty?.name ||
|
||||
data?.name === data.category?.name)
|
||||
) {
|
||||
delete params.name;
|
||||
}
|
||||
@@ -71,7 +72,7 @@ function Head({ setDoctors, slug, filter, setFilter,setPage,setTotalPage }) {
|
||||
const doctors = await getClinicDoctors(slug, params);
|
||||
|
||||
// تنظیم دادهها در state
|
||||
setDoctors(doctors);
|
||||
setDoctors(doctors.data);
|
||||
|
||||
// ✅ حالا که جواب API اومده، مقدار صفحات مشخصه
|
||||
if (doctors?.page) {
|
||||
|
||||
@@ -2,71 +2,61 @@ import { Autocomplete, TextField } from "@mui/material";
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
|
||||
function AutoComplete({
|
||||
data,
|
||||
noneBg,
|
||||
data = [], // همیشه آرایه ایمن
|
||||
noneBg = false,
|
||||
clearText,
|
||||
inputValue,
|
||||
placeholder,
|
||||
inputValue = "",
|
||||
placeholder = "",
|
||||
handleSearch,
|
||||
setInputValue,
|
||||
setSelectedOption,
|
||||
debounce = 500,
|
||||
debounce = 400,
|
||||
}) {
|
||||
const typingTimeoutRef = useRef(null);
|
||||
const [localInput, setLocalInput] = useState(inputValue || "");
|
||||
const [localInput, setLocalInput] = useState(inputValue);
|
||||
|
||||
// sync بین inputValue بیرونی و مقدار محلی
|
||||
useEffect(() => {
|
||||
setLocalInput(inputValue || "");
|
||||
if (inputValue !== localInput) setLocalInput(inputValue || "");
|
||||
}, [inputValue]);
|
||||
|
||||
// پاکسازی تایمر هنگام خروج
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onInputChange = (_, newInputValue) => {
|
||||
setLocalInput(newInputValue);
|
||||
|
||||
if (setInputValue) setInputValue(newInputValue);
|
||||
|
||||
// تایپ در input
|
||||
const onInputChange = (_, newValue) => {
|
||||
setLocalInput(newValue);
|
||||
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
|
||||
typingTimeoutRef.current = setTimeout(() => {
|
||||
handleSearch(newInputValue);
|
||||
handleSearch(newValue);
|
||||
}, debounce);
|
||||
};
|
||||
|
||||
// انتخاب از بین گزینهها
|
||||
const onChange = (_, newValue) => {
|
||||
if (typingTimeoutRef.current) {
|
||||
clearTimeout(typingTimeoutRef.current);
|
||||
typingTimeoutRef.current = null;
|
||||
}
|
||||
|
||||
const val = newValue
|
||||
? typeof newValue === "string"
|
||||
? newValue
|
||||
: newValue.name
|
||||
: "";
|
||||
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
|
||||
const val = typeof newValue === "string" ? newValue : newValue?.name || "";
|
||||
setLocalInput(val);
|
||||
setSelectedOption && setSelectedOption(newValue);
|
||||
handleSearch(val);
|
||||
if (setInputValue) setInputValue(val);
|
||||
};
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
freeSolo
|
||||
options={data}
|
||||
getOptionLabel={(option) =>
|
||||
typeof option === "string" ? option : option.name
|
||||
}
|
||||
disableClearable
|
||||
options={Array.isArray(data) ? data : []}
|
||||
getOptionLabel={(option) =>
|
||||
typeof option === "string" ? option : option.name || ""
|
||||
}
|
||||
inputValue={localInput}
|
||||
className="!w-full"
|
||||
onInputChange={onInputChange}
|
||||
onChange={onChange}
|
||||
noOptionsText="پزشکی یافت نشد"
|
||||
className="!w-full"
|
||||
renderOption={(props, option) => (
|
||||
<li {...props} key={option.id}>
|
||||
<li {...props} key={option.id || option.name}>
|
||||
{option.name}
|
||||
</li>
|
||||
)}
|
||||
|
||||
@@ -1,79 +1,59 @@
|
||||
"use client";
|
||||
|
||||
import { clearFilterParams } from "@/helper";
|
||||
import { useRouter } from "next/navigation";
|
||||
import specialties from "@/data/specialties.json";
|
||||
import { useRef } from "react";
|
||||
import AutoComplete from "./AutoComplete";
|
||||
|
||||
function SearchField({ filter, setFilter, setDataInURL }) {
|
||||
function SearchField({ filter, setFilter, setDataInURL, sendReq }) {
|
||||
const router = useRouter();
|
||||
const typingTimeoutRef = useRef(null);
|
||||
|
||||
// ✅ پاک کردن فیلد سرچ
|
||||
const clearText = () => {
|
||||
clearFilterParams(router, ["name", "category", "specialty"]);
|
||||
const newFilter = {
|
||||
...filter,
|
||||
...{
|
||||
name: "",
|
||||
category: undefined,
|
||||
specialty: undefined,
|
||||
},
|
||||
};
|
||||
clearFilterParams(router, ["name"]);
|
||||
const newFilter = { ...filter, name: "" };
|
||||
setFilter(newFilter);
|
||||
setDataInURL(newFilter);
|
||||
sendReq(newFilter); // جستجو مجدد بدون نام پزشک
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const findName = specialties.find((item) => item.name === e);
|
||||
// ✅ فقط جستجوی پزشک بر اساس name
|
||||
const handleSearch = (value) => {
|
||||
const newFilter = { ...filter, name: value };
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
let dataUrl = {};
|
||||
|
||||
if (findName) {
|
||||
let newFilter = { ...filter };
|
||||
if (findName.parent) {
|
||||
const itemParent = specialties.find(
|
||||
(item) => item.id === findName.parent
|
||||
);
|
||||
dataUrl.specialty = findName.name;
|
||||
newFilter.category = itemParent;
|
||||
newFilter.specialty = findName;
|
||||
} else {
|
||||
dataUrl.specialty = findName.name;
|
||||
newFilter.category = findName;
|
||||
newFilter.specialty = "";
|
||||
}
|
||||
|
||||
// اگر کاربر پاک کرد
|
||||
if (!value.trim()) {
|
||||
searchParams.delete("name");
|
||||
Object.entries(dataUrl).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
searchParams.set(key, value);
|
||||
}
|
||||
});
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
newFilter.name = e;
|
||||
setFilter(newFilter);
|
||||
} else {
|
||||
let newFilter = filter;
|
||||
|
||||
if (
|
||||
filter.name === filter.specialty?.name ||
|
||||
filter.name === filter.category?.name
|
||||
) {
|
||||
searchParams.delete("specialty");
|
||||
newFilter.specialty = "";
|
||||
newFilter.category = "";
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
}
|
||||
newFilter.name = e;
|
||||
setFilter(newFilter);
|
||||
setDataInURL(newFilter);
|
||||
sendReq(newFilter);
|
||||
return;
|
||||
}
|
||||
|
||||
// مقدار name در URL قرار میگیرد
|
||||
searchParams.set("name", value);
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
setFilter(newFilter);
|
||||
setDataInURL(newFilter);
|
||||
|
||||
// جلوگیری از درخواست زیاد
|
||||
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
|
||||
typingTimeoutRef.current = setTimeout(() => {
|
||||
sendReq(newFilter); // ارسال درخواست به بکاند
|
||||
}, 400);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="!min-w-[50%] !w-full">
|
||||
<AutoComplete
|
||||
data={specialties}
|
||||
data={[]} // داده از بکاند میاد، نیازی به local data نیست
|
||||
clearText={clearText}
|
||||
inputValue={filter.name}
|
||||
handleSearch={handleSearch}
|
||||
placeholder="جستجوی نام پزشک، تخصص، بیماری ..."
|
||||
placeholder="جستوجوی نام پزشک..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,39 +5,28 @@ import specialties from "@/data/specialties.json";
|
||||
|
||||
function SendReq({ filter, setFilter, setDataInURL, sendReq }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
// const checkName = () => {
|
||||
// let newFilter = filter;
|
||||
// const specialtyItem =
|
||||
// filter.name &&
|
||||
// specialties.find((item) => item.name.includes(filter.name));
|
||||
|
||||
// if (specialtyItem) {
|
||||
// if (specialtyItem.parent) {
|
||||
// newFilter.specialty = specialtyItem;
|
||||
// newFilter.category = specialties.find(
|
||||
// (item) => item.id === specialtyItem.parent
|
||||
// );
|
||||
// } else {
|
||||
// newFilter.specialty = specialtyItem;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return newFilter;
|
||||
// };
|
||||
|
||||
const filterData = () => {
|
||||
if (!filter?.name?.trim()) {
|
||||
// اگر کاربر چیزی ننوشته، فقط کل لیست رو بگیر
|
||||
setDataInURL(filter);
|
||||
sendReq(filter);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
// setLoading(true);
|
||||
// // const newFilter = checkName();
|
||||
// // setFilter(filter);
|
||||
// // setDataInURL(filter);
|
||||
|
||||
sendReq(filter).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
// ✅ 1. مقدار در URL ست بشه
|
||||
if (setDataInURL) {
|
||||
setDataInURL(filter);
|
||||
}
|
||||
|
||||
// ✅ 2. درخواست به بکاند بره
|
||||
sendReq(filter)
|
||||
.catch((err) => console.error("❌ خطا در جستجو:", err))
|
||||
.finally(() => {
|
||||
setLoading(false); // ✅ 3. بعد از پاسخ، loading خاموش
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
loading={loading}
|
||||
|
||||
@@ -44,6 +44,7 @@ function SearchBar({ slug,filter, sendReq, setFilter, updateData, setDataInURL,s
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
setDataInURL={setDataInURL}
|
||||
sendReq={sendReq}
|
||||
/>
|
||||
<SendReq
|
||||
filter={filter}
|
||||
|
||||
@@ -17,8 +17,14 @@ function ListDoctors({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const changePage = (_, num) => {
|
||||
const changePage =async (_, num) => {
|
||||
setLoading(true);
|
||||
// 👇 اسکرول به بالا با انیمیشن
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
||||
// 👇 کمی تاخیر برای نمایش نرمتر
|
||||
await new Promise((res) => setTimeout(res, 300));
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("page", num);
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
|
||||
+10
-3
@@ -16,12 +16,19 @@ export async function getClinicDoctors(slug, params = {}) {
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
return {
|
||||
|
||||
// ✅ خروجی همیشه ساختار ثابت دارد
|
||||
return {
|
||||
data: json?.data || [],
|
||||
page: json?.page || json?.pages || { total_pages: 1, current: 1 },
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("خطا در دریافت دکترهای کلینیک:", error);
|
||||
return [];
|
||||
console.error("❌ خطا در دریافت دکترهای کلینیک:", error);
|
||||
|
||||
// ✅ ساختار خروجی در حالت خطا هم مثل حالت عادی
|
||||
return {
|
||||
data: [],
|
||||
page: { total_pages: 1, current: 1 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user