loading ,page

This commit is contained in:
Arezoo
2025-10-27 10:58:26 +03:30
parent 8ab33b4e93
commit 79ff139779
9 changed files with 136 additions and 140 deletions
+4 -3
View File
@@ -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}`
);
+37 -16
View File
@@ -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}
+7 -1
View File
@@ -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
View File
@@ -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 },
};
}
}