change design and data list in doctor-item & handle component appointment in doctor-item & handle functionality date-picker & fix bug appointment page & add req get appointment list

This commit is contained in:
Ehsan
2025-07-09 03:05:48 +03:30
parent f951128074
commit c6e2657e45
57 changed files with 442 additions and 445 deletions
+21
View File
@@ -0,0 +1,21 @@
import AppointmentPage from "@/components/appointment";
import { getStateInfo } from "@/lib/getStateInfo";
import axios from "axios";
async function Appointment({ params: { slug } }) {
const { matchedCity } = getStateInfo();
let apt = null;
try {
const response = await axios.get(
"https://back-dev.clinic-pro.ir/api/v1/appointment-slots"
);
apt = response.data.data;
} catch (error) {
// console.error("problem with req:", error.message);
}
return <AppointmentPage slug={slug} matchedCity={matchedCity} />;
}
export default Appointment;
-10
View File
@@ -1,10 +0,0 @@
import BookingPage from "@/components/booking";
import { getStateInfo } from "@/lib/getStateInfo";
function Booking({ params: { slug } }) {
const { matchedCity } = getStateInfo();
return <BookingPage slug={slug} matchedCity={matchedCity} />;
}
export default Booking;
+18
View File
@@ -0,0 +1,18 @@
import { CircularProgress } from "@mui/material";
import React from "react";
function LoadingComponent({ children, loading }) {
return (
<div className="relative">
{loading ? (
<div className="absolute min-h-[80px] mt-[160px] w-full h-full flex items-center justify-center">
<CircularProgress />
</div>
) : (
children
)}
</div>
);
}
export default LoadingComponent;
@@ -0,0 +1,16 @@
import { Skeleton } from "@mui/material";
import React from "react";
function LoadingDate({ loading }) {
if (loading) {
return (
<Skeleton
variant="rectangular"
className="!w-[300px] !rounded-[6px] !absolute !top-0 !right-1/2 !translate-x-1/2 !z-10"
height={352}
/>
);
}
}
export default LoadingDate;
@@ -1,22 +0,0 @@
import { Button } from "@mui/material";
import Image from "next/image";
function NextIconDatePicker({ nextIconDatePicker }) {
return (
<Button
className="!min-w-0 !p-0 !rounded-full"
ref={nextIconDatePicker}
onClick={(e) => e.preventDefault()}
>
<Image
src="/assets/icons/arrow-right.svg"
className="icon-left-datepicker"
alt="arrow"
height={24}
width={24}
/>
</Button>
);
}
export default NextIconDatePicker;
@@ -1,22 +0,0 @@
import { Button } from "@mui/material";
import Image from "next/image";
function PrevIconDatePicker({ prevIconDatePicker }) {
return (
<Button
className="!min-w-0 !p-0 !rounded-full !rotate-180"
ref={prevIconDatePicker}
onClick={(e) => e.preventDefault()}
>
<Image
src="/assets/icons/arrow-right.svg"
className="icon-left-datepicker"
alt="arrow"
height={24}
width={24}
/>
</Button>
);
}
export default PrevIconDatePicker;
@@ -0,0 +1,41 @@
import React from "react";
import { DatePicker } from "jalaali-react-date-picker";
import { nextIconData, prevIconData } from "./dataIcon";
import LoadingDate from "../LoadingDate";
import Image from "next/image";
function FirstDatePicker({
startDate,
loading,
handleDisableDate,
nextIconDatePickerF,
prevIconDatePickerF,
prevIconDatePickerS,
handleStartDateChange,
}) {
return (
<div className="w-full first md:w-1/2 lg:w-full xl:w-1/2 relative flex justify-center">
<DatePicker
value={startDate}
className={`w-full ${loading ? "opacity-0" : "opacity-100"}`}
timePicker={false}
nextIcon={
<Image {...nextIconData} ref={nextIconDatePickerF} id="nextIconF" />
}
prevIcon={
<Image
{...prevIconData}
ref={prevIconDatePickerF}
onClick={() => prevIconDatePickerS.current.click()}
/>
}
isFirst={true}
onChange={handleStartDateChange}
disabledDates={(e) => handleDisableDate(e)}
/>
<LoadingDate loading={loading} />
</div>
);
}
export default FirstDatePicker;
@@ -0,0 +1,42 @@
import React from "react";
import { DatePicker } from "jalaali-react-date-picker";
import { nextIconData, prevIconData } from "./dataIcon";
import LoadingDate from "../LoadingDate";
import Image from "next/image";
function SecondDatePicker({
endDate,
loading,
handleDisableDate,
nextIconDatePickerF,
nextIconDatePickerS,
prevIconDatePickerS,
handleEndDateChange,
}) {
return (
<div className="w-1/2 second hidden md:flex lg:hidden xl:flex">
<div className="w-full relative flex justify-center">
<DatePicker
value={endDate}
timePicker={false}
className={`w-full ${loading ? "opacity-0" : "opacity-100"}`}
nextIcon={
<Image
{...nextIconData}
ref={nextIconDatePickerS}
onClick={() => !loading && nextIconDatePickerF.current.click()}
/>
}
prevIcon={
<Image {...prevIconData} ref={prevIconDatePickerS} id="prevIconS" />
}
onChange={handleEndDateChange}
disabledDates={(e) => handleDisableDate(e)}
/>
<LoadingDate loading={loading} />
</div>
</div>
);
}
export default SecondDatePicker;
@@ -0,0 +1,16 @@
export const fixedIconData = {
src: "/assets/icons/arrow-right.svg",
alt: "arrow",
height: 24,
width: 24,
};
export const nextIconData = {
...fixedIconData,
className: "icon-left-datepicker cursor-pointer",
};
export const prevIconData = {
...fixedIconData,
className: "icon-left-datepicker cursor-pointer rotate-180",
};
+31 -61
View File
@@ -1,20 +1,20 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { DatePicker as DatePickerJalali } from "jalaali-react-date-picker";
import { Skeleton } from "@mui/material";
import NextIconDatePicker from "./NextIconDatePicker";
import PrevIconDatePicker from "./PrevIconDatePicker";
import FirstDatePicker from "./date/FirstDatePicker";
import SecondDatePicker from "./date/SecondDatePicker";
function DatePicker({ date, updateDate }) {
const nextIconDatePicker = useRef();
const prevIconDatePicker = useRef();
const nextIconDatePickerF = useRef();
const prevIconDatePickerF = useRef();
const nextIconDatePickerS = useRef();
const prevIconDatePickerS = useRef();
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState();
const [loading, setLoading] = useState(true);
const handleStartDateChange = (date) => {
const handleStartDateChange = (date, e) => {
if (date) {
setStartDate(date);
setEndDate(null);
@@ -34,64 +34,34 @@ function DatePicker({ date, updateDate }) {
};
useEffect(() => {
if (nextIconDatePicker.current) {
nextIconDatePicker.current.click();
if (nextIconDatePickerS.current) {
nextIconDatePickerS.current.click();
console.log(nextIconDatePickerS.current);
setLoading(false);
}
}, [nextIconDatePicker]);
}, [nextIconDatePickerS]);
return (
<div className="flex items-start custom-datepicker justify-evenly w-full">
<div className="w-full md:w-1/2 lg:w-full xl:w-1/2 relative flex justify-center">
<DatePickerJalali
value={startDate}
className={`w-full ${loading ? "opacity-0" : "opacity-100"}`}
timePicker={false}
nextIcon={
<div className="flex md:hidden lg:flex xl:hidden">
<NextIconDatePicker nextIconDatePicker={nextIconDatePicker} />
</div>
}
prevIcon={
<PrevIconDatePicker prevIconDatePicker={prevIconDatePicker} />
}
onChange={handleStartDateChange}
disabledDates={(e) => handleDisableDate(e)}
/>
{loading && (
<Skeleton
variant="rectangular"
className="!w-[300px] !rounded-[6px] !absolute !top-0 !right-1/2 !translate-x-1/2 !z-10"
height={352}
/>
)}
</div>
<div className="w-1/2 hidden md:flex lg:hidden xl:flex">
<div className="w-full relative flex justify-center">
<DatePickerJalali
value={endDate}
timePicker={false}
className={`w-full ${loading ? "opacity-0" : "opacity-100"}`}
nextIcon={
<NextIconDatePicker nextIconDatePicker={nextIconDatePicker} />
}
prevIcon={
<div className="flex md:hidden lg:flex xl:hidden">
<PrevIconDatePicker prevIconDatePicker={prevIconDatePicker} />
</div>
}
onChange={handleEndDateChange}
disabledDates={(e) => handleDisableDate(e)}
/>
{loading && (
<Skeleton
variant="rectangular"
className="!w-[300px] !rounded-[6px] !absolute !top-0 !right-1/2 !translate-x-1/2 !z-10"
height={352}
/>
)}
</div>
</div>
<div className="datePickerApt flex items-start custom-datepicker justify-evenly w-full">
<FirstDatePicker
loading={loading}
startDate={startDate}
handleDisableDate={handleDisableDate}
nextIconDatePickerF={nextIconDatePickerF}
prevIconDatePickerF={prevIconDatePickerF}
prevIconDatePickerS={prevIconDatePickerS}
handleStartDateChange={handleStartDateChange}
/>
<SecondDatePicker
loading={loading}
endDate={endDate}
handleDisableDate={handleDisableDate}
nextIconDatePickerF={nextIconDatePickerF}
nextIconDatePickerS={nextIconDatePickerS}
prevIconDatePickerS={prevIconDatePickerS}
handleEndDateChange={handleEndDateChange}
/>
</div>
);
}
+17 -15
View File
@@ -3,28 +3,30 @@ import { Button } from "@mui/material";
function Hours({ doctor, hour, setHour, value, nameHours }) {
return (
<ul className="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-x-[16px] gap-y-[12px] sm:gap-y-[16px] md:gap-y-[20px] lg:gap-y-[24px] mt-[24px] mb-[40px]">
{doctor[nameHours[value]].map((item, idx) => (
<li key={idx}>
<Button
onClick={() => setHour(idx)}
disabled={!item.active}
className={`
{doctor &&
nameHours &&
doctor[nameHours[value]]?.map((item, idx) => (
<li key={idx}>
<Button
onClick={() => setHour(idx)}
disabled={!item.active}
className={`
!w-full !rounded-[8px] !bg-[#EFEFEF] !flex !justify-center !items-center !py-[10px] !px-[14px] !h-[40px] md:!h-[42px] lg:!h-[44px]
${idx === hour ? "!bg-[#F79463] " : ""}
`}
>
<p
className={`
>
<p
className={`
text-[16px] font-semibold text-[#525252]
${item.active ? "text-[#525252]" : "text-[#D7D7D7]"}
${idx === hour ? "!text-[#FAFAFA] " : ""}
`}
>
{item.hour}
</p>
</Button>
</li>
))}
>
{item.hour}
</p>
</Button>
</li>
))}
</ul>
);
}
+1 -2
View File
@@ -2,9 +2,8 @@ import Content from "@/components/dashboard/Content";
import { defineAbilitiesFor } from "@/lib/ability";
import { getUser } from "@/lib/auth";
import { getStateInfo } from "@/lib/getStateInfo";
import { fetchReq } from "@/lib/req";
import { removeToken } from "@/utils";
import { cookies, headers } from "next/headers";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export default async function Dashboard({ searchParams }) {
+17 -5
View File
@@ -3,14 +3,26 @@ import Layout from "@/components/layout/StLayout";
import axios from "axios";
async function Doctor({ params: { slug } }) {
const response = await axios.get(
`https://back-dev.clinic-pro.ir/api/v1/doctor/${slug}`
);
const doctor = response.data;
let doctors = null;
let doctor = null;
try {
const resDoctors = await axios.get(
"https://back-dev.clinic-pro.ir/api/v1/doctors?active=1"
);
const resDoctor = await axios.get(
`https://back-dev.clinic-pro.ir/api/v1/doctor/${slug}`
);
doctor = resDoctor.data;
doctors = resDoctors.data.data;
} catch (error) {
// console.error("problem with req:", error.message);
// return redirect("/unauthorized");
}
return (
<Layout>
<DoctorPage doctor={doctor} />
<DoctorPage doctor={doctor} doctors={doctors} />
</Layout>
);
}
+1 -2
View File
@@ -11,10 +11,9 @@ async function Doctors({ searchParams }) {
const response = await axios.get(
"https://back-dev.clinic-pro.ir/api/v1/doctors"
);
doctors = response.data.$data;
doctors = response.data.data;
} catch (error) {
// console.error("problem with req:", error.message);
// return redirect("/unauthorized");
}
return (
+53
View File
@@ -885,3 +885,56 @@ html {
.spin {
animation: spin 1s linear infinite;
}
/* date picker appointment */
.datePickerApt
.first
.panel-header-wrapper
.center:first-child
.iconItem:first-child,
.datePickerApt
.first
.panel-header-wrapper
.center:last-child
.iconItem:last-child,
.datePickerApt
.second
.panel-header-wrapper
.center:first-child
.iconItem:first-child,
.datePickerApt
.second
.panel-header-wrapper
.center:last-child
.iconItem:last-child {
display: none;
}
.datePickerApt #nextIconF,
.datePickerApt #prevIconS {
display: flex;
}
@media (min-width: 768px) {
.datePickerApt #nextIconF,
.datePickerApt #prevIconS {
display: none;
}
}
@media (min-width: 1024px) {
.datePickerApt #nextIconF,
.datePickerApt #prevIconS {
display: flex;
}
}
@media (min-width: 1280px) {
.datePickerApt #nextIconF,
.datePickerApt #prevIconS {
display: none;
}
}
/* end of date picker appointment */