add state of page in url, add location and working_days on list in clinics page, fix update list clinic with page and limit value, hide pagination when list of clinic is empty, round image of doctor and appo in doctor item
This commit is contained in:
+1
-5
@@ -24,11 +24,7 @@ export default async function Clinics({ searchParams }) {
|
||||
else if (matchedCity && matchedCity.id !== "600")
|
||||
newSearchParams.city = matchedCity.name;
|
||||
|
||||
const params = {
|
||||
...buildClinicParams(newSearchParams),
|
||||
page: 1,
|
||||
limit: 12,
|
||||
};
|
||||
const params = buildClinicParams(newSearchParams);
|
||||
|
||||
// Req
|
||||
clinics = await fetchReq(`${API_URL}/api/v1/clinics`, {
|
||||
|
||||
@@ -11,7 +11,7 @@ import ArrowLeftD from "@/components/icons/ArrowLeftD";
|
||||
import ClockD from "@/components/icons/ClockD";
|
||||
import LikeD from "@/components/icons/LikeD";
|
||||
import PointD from "@/components/icons/PointD";
|
||||
import { convertTimestampToPersianDate, convertTimestampToPersianDateSimple } from "@/components/doctors/listDoctors/timestamp_to_persian_date";
|
||||
import { convertTimestampToPersianDateSimple } from "@/helper";
|
||||
|
||||
moment.loadPersian({ dialect: "persian-modern" });
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ function PaginationContent({ page, pageDetail, changePage, limit = 12 }) {
|
||||
page={page || 1}
|
||||
onChange={changePage}
|
||||
color="primary"
|
||||
className={count < 2 && "!hidden"}
|
||||
className={(!count || count < 2) && "!hidden"}
|
||||
sx={{
|
||||
"& .mui-8q7g72-MuiButtonBase-root-MuiPaginationItem-root": {
|
||||
color: "#616161",
|
||||
|
||||
@@ -3,21 +3,14 @@ import Layout from "@/components/layout/StLayout";
|
||||
import axios from "axios";
|
||||
|
||||
async function Doctor({ params: { slug } }) {
|
||||
let doctors = null;
|
||||
let doctor = null;
|
||||
let comments = null;
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
try {
|
||||
const resDoctors = await axios.get(
|
||||
`${API_URL}/api/v1/doctors?active=1`
|
||||
);
|
||||
const resDoctor = await axios.get(
|
||||
`${API_URL}/api/v1/doctor/${slug}`
|
||||
);
|
||||
const resDoctor = await axios.get(`${API_URL}/api/v1/doctor/${slug}`);
|
||||
|
||||
doctor = resDoctor.data;
|
||||
doctors = resDoctors.data.data;
|
||||
if (doctor) {
|
||||
const resComments = await axios.get(
|
||||
`${API_URL}/api/v1/clinicpro/comments/${doctor.id}`
|
||||
@@ -31,7 +24,7 @@ async function Doctor({ params: { slug } }) {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<DoctorPage doctor={doctor} doctors={doctors} comments={comments} />
|
||||
<DoctorPage doctor={doctor} comments={comments} />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { useState } from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import SearchD from "@/components/icons/SearchD";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
function FilterButton({ filterData }) {
|
||||
function FilterButton({ limit, selected, filterData }) {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const sendReq = () => {
|
||||
setLoading(true);
|
||||
filterData().finally(() => setLoading(false));
|
||||
const newSelected = { ...selected, page: 1, limit };
|
||||
filterData(newSelected).finally(() => {
|
||||
setLoading(false);
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("page", 1);
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -12,6 +12,7 @@ import { request } from "@/services/response";
|
||||
|
||||
function SearchBar({
|
||||
state,
|
||||
limit,
|
||||
selected,
|
||||
specialties,
|
||||
setSelected,
|
||||
@@ -59,6 +60,7 @@ function SearchBar({
|
||||
>
|
||||
<ModalSearchCity
|
||||
open={open}
|
||||
limit={limit}
|
||||
state={state}
|
||||
setOpen={setOpen}
|
||||
selected={selected}
|
||||
@@ -73,7 +75,7 @@ function SearchBar({
|
||||
placeholder="جستجوی تخصص"
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
<FilterButton filterData={filterData} />
|
||||
<FilterButton limit={limit} selected={selected} filterData={filterData} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Button } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
|
||||
// Icon
|
||||
import { Button } from "@mui/material";
|
||||
import CloseModalD from "@/components/icons/CloseModalD";
|
||||
import ArrowLeftM from "@/components/icons/ArrowLeftM";
|
||||
import AutoComplete from "./AutoComplete";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
function Content({
|
||||
state,
|
||||
limit,
|
||||
states,
|
||||
selected,
|
||||
updateData,
|
||||
@@ -16,6 +17,7 @@ function Content({
|
||||
filteredCities,
|
||||
handleSearch,
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const listboxProps = {
|
||||
style: {
|
||||
@@ -27,8 +29,12 @@ function Content({
|
||||
const handleFilter = () => {
|
||||
handleSearch && handleSearch("");
|
||||
setLoading(true);
|
||||
filterData().finally(() => {
|
||||
const newSelected = { ...selected, page: 1, limit };
|
||||
filterData(newSelected).finally(() => {
|
||||
handleClose();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("page", 1);
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useRouter } from "next/navigation";
|
||||
function ModalSearchCity({
|
||||
open,
|
||||
state,
|
||||
limit,
|
||||
setOpen,
|
||||
children,
|
||||
selected,
|
||||
@@ -67,6 +68,7 @@ function ModalSearchCity({
|
||||
<Modal open={open} onClose={handleClose}>
|
||||
<Box sx={styleDefault} className="!w-full !h-full md:!w-fit md:!h-fit">
|
||||
<Content
|
||||
limit={limit}
|
||||
state={state}
|
||||
states={states}
|
||||
selected={selected}
|
||||
|
||||
@@ -14,7 +14,7 @@ import cities from "@/data/city.json";
|
||||
|
||||
function ClinicsPage({ clinics, matchedCity, matchedState }) {
|
||||
const searchParams = useSearchParams();
|
||||
const [filteredClinics, setFilteredClinics] = useState(clinics.data);
|
||||
const [filteredClinics, setFilteredClinics] = useState(clinics?.data);
|
||||
|
||||
const selectedState = searchParams.get("state") || matchedState?.name;
|
||||
const selectedCity =
|
||||
@@ -37,6 +37,7 @@ function ClinicsPage({ clinics, matchedCity, matchedState }) {
|
||||
detail="در قسمت زیر می توانید کلینیک مورد نظر خود را جستجو نمایید"
|
||||
>
|
||||
<SearchBar
|
||||
limit={12}
|
||||
selected={selected}
|
||||
state={matchedState}
|
||||
setSelected={setSelected}
|
||||
@@ -47,7 +48,7 @@ function ClinicsPage({ clinics, matchedCity, matchedState }) {
|
||||
<List
|
||||
limit={12}
|
||||
selected={selected}
|
||||
pages={clinics.page}
|
||||
pages={clinics?.page || []}
|
||||
clinics={filteredClinics}
|
||||
setFilteredClinics={setFilteredClinics}
|
||||
/>
|
||||
|
||||
@@ -48,7 +48,7 @@ function ItemClinic({ setFilteredClinics, data, loading }) {
|
||||
<LocationClinic />
|
||||
<TextLoading loading={loading} width={150} height={18}>
|
||||
<p className="text-[#7E7E7E] text-[12px] font-normal">
|
||||
{data.location}
|
||||
{data.address}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@ function ItemClinic({ setFilteredClinics, data, loading }) {
|
||||
<ClockClinic />
|
||||
<TextLoading loading={loading} width={120} height={18}>
|
||||
<p className="text-[#7E7E7E] text-[12px] font-normal">
|
||||
ساعت کاری: {data.hours_of_work}
|
||||
ساعت کاری: {data.field_working_days}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</div>
|
||||
|
||||
@@ -4,14 +4,19 @@ import { QueryForClinicsFilter } from "@/helper";
|
||||
import Pageguide from "@/app/component/Pageguide";
|
||||
import PaginationContent from "@/app/component/PaginationContent";
|
||||
import { request } from "@/services/response";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
function List({ limit, selected, clinics, pages, setFilteredClinics }) {
|
||||
const listPage = ["کلینیک ها", selected?.city?.name || ""];
|
||||
const [page, setPage] = useState(pages?.currentPage);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const changePage = (_, num) => {
|
||||
setLoading(true);
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("page", num);
|
||||
router.push(`?${searchParams.toString()}`);
|
||||
|
||||
setPage(num);
|
||||
const params = {
|
||||
|
||||
@@ -19,7 +19,13 @@ 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 width={56} height={56} alt="doctor" src={turn?.img[0]?.url} />
|
||||
<Image
|
||||
className="rounded-full"
|
||||
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}>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useRef } from "react";
|
||||
|
||||
// Icons
|
||||
import CopyBlueD from "@/components/icons/CopyBlueD";
|
||||
import DownloadBlue from "@/components/icons/DownloadBlue";
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import { styleDefault } from "@/mui";
|
||||
import ShareDI from "@/components/icons/ShareDI";
|
||||
import { Box, Button, Modal } from "@mui/material";
|
||||
|
||||
// Icons
|
||||
import CloseModalD from "@/components/icons/CloseModalD";
|
||||
import List from "./List";
|
||||
import Poster from "../poster";
|
||||
@@ -28,7 +25,8 @@ function Share({ data }) {
|
||||
<ShareDI />
|
||||
<p className="hidden lg:flex">اشتراک گذاری</p>
|
||||
</Button>
|
||||
<div className="fixed top-0 left-0 opacity-0 pointer-events-none -z-10">
|
||||
{/* <div className="fixed top-0 left-0 opacity-0 pointer-events-none -z-10"> */}
|
||||
<div className="">
|
||||
<div ref={hiddenRef}>
|
||||
<Poster data={data} />
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ function Title({ doctor }) {
|
||||
<CircularLoading width={100} height={100}>
|
||||
<Image
|
||||
className="
|
||||
object-contain
|
||||
object-contain rounded-full
|
||||
w-[40px] sm:w-[81px] md:w-[123px] lg:w-[164px]
|
||||
h-[40px] sm:h-[81px] md:h-[123px] lg:h-[164px]
|
||||
"
|
||||
|
||||
@@ -3,7 +3,7 @@ import DetailDoctor from "./detailDoctor";
|
||||
import Share from "./detailDoctor/Share";
|
||||
import CustomLoading from "@/app/component/loading/Custom";
|
||||
|
||||
function DoctorPage({ doctor, doctors, comments }) {
|
||||
function DoctorPage({ doctor, 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">
|
||||
|
||||
@@ -7,8 +7,8 @@ function Address({ data }) {
|
||||
<ClipBoard />
|
||||
<p className="text-[#E7EEF6] text-[20px] font-semibold">آدرس:</p>
|
||||
</div>
|
||||
<p className="text-[#E7EEF6] text-[16px] !font-normal mt-[16px]">
|
||||
{data?.location}
|
||||
<p className="text-[#E7EEF6] text-[16px] !font-normal mt-[16px] text-right">
|
||||
{data?.address[0]?.address}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,13 @@ import Image from "next/image";
|
||||
function ImageProfile({ data }) {
|
||||
return (
|
||||
<div className="bg-stroke-circle ml-[10px] mt-[57px] p-[9px] rounded-full overflow-hidden">
|
||||
<Image src={data?.img} width={173} height={173} alt="profile-user" />
|
||||
<Image
|
||||
src={data?.img[0]?.url}
|
||||
width={173}
|
||||
height={173}
|
||||
alt="profile-user"
|
||||
className="rounded-full"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@ import TelefonPoster from "@/components/icons/TelefonPoster";
|
||||
|
||||
function Telefon({ data }) {
|
||||
return (
|
||||
<div className="flex flex-col items-start justify-start mt-[40px]">
|
||||
<div className={`flex-col items-start justify-start mt-[40px] ${data?.address[0]?.phone ? 'flex' : 'hidden'}`}>
|
||||
<div className="flex items-center justify-start gap-[6px]">
|
||||
<TelefonPoster />
|
||||
<p className="text-[#E7EEF6] text-[20px] font-semibold">تلفن:</p>
|
||||
</div>
|
||||
<p className="text-[#E7EEF6] text-[16px] font-normal mt-[16px]">
|
||||
{data?.phone}
|
||||
</p>
|
||||
<p className="text-[#E7EEF6] text-[16px] font-normal mt-[12px]">
|
||||
{data?.phone} - {data?.phone}
|
||||
{data?.address[0]?.phone}
|
||||
</p>
|
||||
{/* <p className="text-[#E7EEF6] text-[16px] font-normal mt-[12px]">
|
||||
{data?.address[0]?.phone} - {data?.address[0]?.phone}
|
||||
</p> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ function Poster({ data }) {
|
||||
<Image src={Logo} width={30} height={30} alt="logo" />
|
||||
<p className="text-[#E7EEF6] text-[16px] font-bold">nobat724.com</p>
|
||||
</div>
|
||||
<p className="mt-[40px] text-[#E7EEF6] text-[20px] font-bold">
|
||||
<p className="mt-[40px] text-[#E7EEF6] text-[20px] font-bold text-right">
|
||||
{data?.name}
|
||||
</p>
|
||||
<p className="text-[24px] mt-[20px] text-[#E7EEF6] font-bold">
|
||||
<p className="text-[24px] mt-[20px] text-[#E7EEF6] font-bold text-right">
|
||||
تخصص:
|
||||
{data?.expertise?.map(
|
||||
(item, idx) =>
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
export function convertTimestampToPersianDate(timestamp) {
|
||||
// Convert timestamp to milliseconds (JavaScript uses milliseconds)
|
||||
const date = new Date(timestamp * 1000);
|
||||
|
||||
// Format with Persian calendar and Tehran timezone
|
||||
const options = {
|
||||
timeZone: "Asia/Tehran",
|
||||
calendar: "persian",
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
weekday: "long",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true,
|
||||
};
|
||||
|
||||
const formatter = new Intl.DateTimeFormat("fa-IR", options);
|
||||
const parts = formatter.formatToParts(date);
|
||||
|
||||
// Extract parts
|
||||
const weekday = parts.find((part) => part.type === "weekday").value;
|
||||
const year = parts.find((part) => part.type === "year").value;
|
||||
const month = parts.find((part) => part.type === "month").value;
|
||||
const day = parts.find((part) => part.type === "day").value;
|
||||
const hour = parts.find((part) => part.type === "hour").value;
|
||||
const minute = parts.find((part) => part.type === "minute").value;
|
||||
const second = parts.find((part) => part.type === "second").value;
|
||||
const dayPeriod = parts.find((part) => part.type === "dayPeriod").value;
|
||||
|
||||
// Format the final string
|
||||
return `${weekday}, ${year}-${month}-${day} ${hour}:${minute}:${second} ${dayPeriod}`;
|
||||
}
|
||||
|
||||
// Alternative simpler approach using toLocaleString
|
||||
export function convertTimestampToPersianDateSimple(timestamp) {
|
||||
const date = new Date(timestamp * 1000);
|
||||
|
||||
const options = {
|
||||
timeZone: "Asia/Tehran",
|
||||
calendar: "persian",
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
weekday: "long",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true,
|
||||
};
|
||||
|
||||
return date.toLocaleString("fa-IR", options);
|
||||
}
|
||||
|
||||
// Example usage
|
||||
const timestamp = 1759132200;
|
||||
|
||||
console.log("Method 1 (formatToParts):");
|
||||
console.log(convertTimestampToPersianDate(timestamp));
|
||||
|
||||
console.log("\nMethod 2 (toLocaleString):");
|
||||
console.log(convertTimestampToPersianDateSimple(timestamp));
|
||||
|
||||
// For more control over the format, you can create a custom formatter
|
||||
export function convertTimestampCustomFormat(timestamp) {
|
||||
const date = new Date(timestamp * 1000);
|
||||
|
||||
const options = {
|
||||
timeZone: "Asia/Tehran",
|
||||
calendar: "persian",
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
weekday: "long",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true,
|
||||
};
|
||||
|
||||
const formatter = new Intl.DateTimeFormat("fa-IR", options);
|
||||
const formatted = formatter.format(date);
|
||||
|
||||
// Add bullet point if needed
|
||||
return `* ${formatted}`;
|
||||
}
|
||||
|
||||
console.log("\nMethod 3 (with bullet point):");
|
||||
console.log(convertTimestampCustomFormat(timestamp));
|
||||
@@ -390,6 +390,12 @@ export const QueryForClinicsFilter = (searchParams) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 page
|
||||
if (searchParams.page) params.page = searchParams.page;
|
||||
|
||||
// 🔹 limit
|
||||
if (searchParams.limit) params.limit = searchParams.limit;
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
@@ -493,6 +499,12 @@ export const buildClinicParams = (searchParams) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 page
|
||||
if (searchParams.page) {
|
||||
params.page = searchParams.page;
|
||||
params.limit = 12;
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
@@ -525,3 +537,22 @@ export function isUserLoggedIn() {
|
||||
const userInfo = Cookies.get("userInfo");
|
||||
return !!userInfo;
|
||||
}
|
||||
|
||||
export function convertTimestampToPersianDateSimple(timestamp) {
|
||||
const date = new Date(timestamp * 1000);
|
||||
|
||||
const options = {
|
||||
timeZone: "Asia/Tehran",
|
||||
calendar: "persian",
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
weekday: "long",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true,
|
||||
};
|
||||
|
||||
return date.toLocaleString("fa-IR", options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user