change function and design page doctors & signin & signup & home & specialties & helper

This commit is contained in:
Ehsan
2025-05-21 16:44:34 +03:30
parent c8c217e286
commit 6c7709c342
13 changed files with 96 additions and 86 deletions
+6 -4
View File
@@ -7,7 +7,6 @@ function AutoCompleteSelect({
value, value,
isError, isError,
updateData, updateData,
name,
label, label,
disabled = false, disabled = false,
listboxProps, listboxProps,
@@ -20,14 +19,17 @@ function AutoCompleteSelect({
disabled={disabled} disabled={disabled}
ListboxProps={listboxProps} ListboxProps={listboxProps}
value={value || null} value={value || null}
getOptionLabel={(option) => {
if (typeof option === "string") return option;
return option?.name || option?.label || "";
}}
className="!w-full !rounded-lg auto-complete-selector" className="!w-full !rounded-lg auto-complete-selector"
onChange={(e, newValue) => { onChange={(e, newValue) => {
updateData && updateData &&
updateData( updateData(
newValue && sendAll newValue && sendAll
? newValue ? newValue
: (newValue && !sendAll && newValue.label) || "", : (newValue && !sendAll && newValue.label) || ""
name,
); );
}} }}
sx={{ sx={{
@@ -73,7 +75,7 @@ function AutoCompleteSelect({
key={option.id || props.id} key={option.id || props.id}
className="!flex !justify-start !p-[8px] !text-start !text-[#A1A1A1] hover:dark:!bg-[#35343D] !bg-[#FFF] dark:!bg-[#1F1D2B]" className="!flex !justify-start !p-[8px] !text-start !text-[#A1A1A1] hover:dark:!bg-[#35343D] !bg-[#FFF] dark:!bg-[#1F1D2B]"
> >
{option.label} {option.name || option.label}
</Button> </Button>
)} )}
renderInput={(params) => ( renderInput={(params) => (
+9 -1
View File
@@ -295,7 +295,7 @@ html {
} }
.MuiModal-root div:nth-child(3) { .MuiModal-root div:nth-child(3) {
border-radius: 0 !important; /* border-radius: 0 !important; */
} }
} }
@@ -850,3 +850,11 @@ html {
.text-item-head-white:hover .item-head { .text-item-head-white:hover .item-head {
color: #d7d8ed; color: #d7d8ed;
} }
.bg-poster {
background-image: url(/public/assets/images/poster.png);
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
}
+15
View File
@@ -0,0 +1,15 @@
import Image from "next/image";
import Logo from "@/public/assets/images/logo.png";
function Poster() {
return (
<div className="bg-poster !pt-[32px] p-[24px]">
<div className="flex items-center justify-start gap-[5px]">
<Image src={Logo} width={30} height={30} alt="logo" />
<p className="text-[#E7EEF6] text-[16px] font-bold">nobat724.com</p>
</div>
</div>
);
}
export default Poster;
+1 -1
View File
@@ -41,7 +41,7 @@ function Share() {
sx={styleDefault} sx={styleDefault}
className="!pt-[12px] md:!pt-[14px] lg:!py-[16px] className="!pt-[12px] md:!pt-[14px] lg:!py-[16px]
!px-[12px] sm:!px-[16px] md:!px-[20px] lg:!px-[24px] !px-[12px] sm:!px-[16px] md:!px-[20px] lg:!px-[24px]
!pb-[12px] sm:!pb-[16px] lg:!pb-[20px]" !pb-[12px] sm:!pb-[16px] lg:!pb-[20px] !rounded-[8px] overflow-hidden"
> >
<div className="flex items-center justify-between w-full"> <div className="flex items-center justify-between w-full">
<p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium"> <p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium">
+2
View File
@@ -5,6 +5,7 @@ import AppointmentList from "./appointmentList";
import DetailDoctor from "./detailDoctor"; import DetailDoctor from "./detailDoctor";
import Share from "./detailDoctor/Share"; import Share from "./detailDoctor/Share";
import CustomLoading from "@/app/component/loading/Custom"; import CustomLoading from "@/app/component/loading/Custom";
import Poster from "./Poster";
function DoctorPage({ doctor }) { function DoctorPage({ doctor }) {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -30,6 +31,7 @@ function DoctorPage({ doctor }) {
<Share /> <Share />
</div> </div>
</div> </div>
<Poster />
<div className="flex items-start justify-center gap-6 mt-[30px]"> <div className="flex items-start justify-center gap-6 mt-[30px]">
<DetailDoctor doctor={doctor} loading={loading} /> <DetailDoctor doctor={doctor} loading={loading} />
<AppointmentList doctor={doctor} loading={loading} /> <AppointmentList doctor={doctor} loading={loading} />
+7 -8
View File
@@ -18,19 +18,18 @@ function DoctorsPage({ params, matchedCity, matchedState }) {
province: matchedState?.name || params?.province, province: matchedState?.name || params?.province,
city: matchedCity?.name || params?.city, city: matchedCity?.name || params?.city,
}); });
const findItem = (name, value) => const findSpecial = (id) => specialties.find((item) => item.id === id);
specialties.find((item) => item[name] === value);
const itemCategory = findItem("id", params.category);
const itemSpecialties = findItem("id", params.specialties);
const [data, setData] = useState({ const [data, setData] = useState({
category: itemCategory || "", category: findSpecial(params.specialties)?.parent
specialties: itemSpecialties ? findSpecial(findSpecial(params.specialties)?.parent)
? { ...itemSpecialties, label: itemSpecialties.name } : findSpecial(params.specialties) || "",
specialties: findSpecial(params.specialties)?.parent
? findSpecial(params.specialties)
: "", : "",
turn: params && params.hasOwnProperty("turn") ? JSON.parse(params.turn) : 1, turn: params && params.hasOwnProperty("turn") ? JSON.parse(params.turn) : 1,
gender: params.gender || "هر دو", gender: params.gender || "هر دو",
degree: params.degree || "متخصص", degree: params.degree || "همه موارد",
}); });
useEffect(() => { useEffect(() => {
+11 -16
View File
@@ -3,25 +3,22 @@ import RadioContent from "./RadioContent";
import { FormControlLabel, Switch } from "@mui/material"; import { FormControlLabel, Switch } from "@mui/material";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { filterList } from "@/helper"; import { filterList } from "@/helper";
const group1 = ["خانم", "آقا", "هر دو"]; const gender = ["زن", "مرد", "هر دو"];
const group2 = ["فوق تخصص", "دکترای تخصصی", "متخصص", "دکتری"]; const degree = ["پزشک عمومی", "پزشک متخصص", "پزشک فوق تخصص", "همه موارد"];
function Content({ data, setData }) { function Content({ data, setData }) {
const router = useRouter(); const router = useRouter();
const { parentList, childrenList } = filterList(data); const { parentList, childrenList } = filterList(data);
const changeUrl = (value, name, removeSpecialties) => { const changeUrl = (value) => {
const current = new URLSearchParams(window.location.search); const current = new URLSearchParams(window.location.search);
current.set(name, value.id || value); current.set("specialties", value.id || value);
if (removeSpecialties) {
current.delete("specialties");
}
const queryString = current.toString(); const queryString = current.toString();
router.push(`/doctors?${queryString}`); router.push(`/doctors?${queryString}`);
}; };
const changeData = (value, name, removeSpecialties) => { const changeData = (value, name, removeSpecialties) => {
changeUrl(value, name, removeSpecialties); changeUrl(value);
const newData = data; const newData = data;
newData[name] = value; newData[name] = value;
if (removeSpecialties) { if (removeSpecialties) {
@@ -34,20 +31,18 @@ function Content({ data, setData }) {
<div className="mt-[50px] px-[0px] md:px-[19px] lg:px-[38px] mb-[32px]"> <div className="mt-[50px] px-[0px] md:px-[19px] lg:px-[38px] mb-[32px]">
<AutoCompleteSelect <AutoCompleteSelect
list={parentList} list={parentList}
updateData={(value, name) => changeData(value, name, true)} updateData={(value) => changeData(value, "category", true)}
sendAll={true} sendAll={true}
value={data.category.name} value={data.category?.name}
label="دسته بندی" label="دسته بندی"
name="category"
/> />
{!!childrenList.length && ( {!!childrenList.length && (
<div className="mt-[24px]"> <div className="mt-[24px]">
<AutoCompleteSelect <AutoCompleteSelect
list={childrenList} list={childrenList}
updateData={changeData} updateData={(value) => changeData(value, "specialties")}
sendAll={true} sendAll={true}
value={data.specialties} value={data.specialties?.name}
name="specialties"
label="تخصص" label="تخصص"
/> />
</div> </div>
@@ -57,7 +52,7 @@ function Content({ data, setData }) {
changeData={changeData} changeData={changeData}
value={data.gender} value={data.gender}
text="جنسیت پزشک" text="جنسیت پزشک"
group={group1} group={gender}
name="gender" name="gender"
/> />
<span className="block mt-[16px] mb-[24px] w-full h-px bg-[#EFEFEF]"></span> <span className="block mt-[16px] mb-[24px] w-full h-px bg-[#EFEFEF]"></span>
@@ -65,7 +60,7 @@ function Content({ data, setData }) {
changeData={changeData} changeData={changeData}
value={data.degree} value={data.degree}
text="درجه علمی" text="درجه علمی"
group={group2} group={degree}
name="degree" name="degree"
/> />
<span className="block mt-[16px] mb-[24px] w-full h-px bg-[#EFEFEF]"></span> <span className="block mt-[16px] mb-[24px] w-full h-px bg-[#EFEFEF]"></span>
-4
View File
@@ -1,12 +1,9 @@
import { frequentSearches } from "@/constans";
import { filterList } from "@/helper";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import specialties from "@/data/specialties.json"; import specialties from "@/data/specialties.json";
import Link from "next/link"; import Link from "next/link";
function FrequentSearches() { function FrequentSearches() {
const childrenList = specialties.filter((item) => item.parent); const childrenList = specialties.filter((item) => item.parent);
console.log(childrenList);
return ( return (
<div className="flex items-center justify-center gap-[8px] sm:gap-[10px] md:gap-[13px] lg:gap-[16px] w-full"> <div className="flex items-center justify-center gap-[8px] sm:gap-[10px] md:gap-[13px] lg:gap-[16px] w-full">
@@ -20,7 +17,6 @@ function FrequentSearches() {
href={{ href={{
pathname: "/doctors", pathname: "/doctors",
query: { query: {
category: item.parent,
specialties: item.id, specialties: item.id,
}, },
}} }}
+14 -2
View File
@@ -1,5 +1,6 @@
import Field from "@/app/component/element/Field"; import Field from "@/app/component/element/Field";
import { formatPhoneNumber, validatePhoneNumber } from "@/helper"; import { formatPhoneNumber, validatePhoneNumber } from "@/helper";
import { request } from "@/services/response";
import { Button, InputAdornment } from "@mui/material"; import { Button, InputAdornment } from "@mui/material";
import Link from "next/link"; import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
@@ -15,6 +16,18 @@ function LogInPage({ setIsSendMsg, setStep, matchedCity }) {
setNum(formatted); setNum(formatted);
}; };
const handleReq = () => {
request
.auth("/")
.then(() => {
setIsSendMsg && setIsSendMsg(true);
setStep && setStep((prev) => prev + 1);
})
.catch(() => {
setLoading(false);
});
};
return ( return (
<div <div
className="rounded-[16px] mt-[28px] opacity-page sm:mt-[14px] border border-solid sm:border-[#EFEFEF] bg-transparent sm:bg-[#FFF] className="rounded-[16px] mt-[28px] opacity-page sm:mt-[14px] border border-solid sm:border-[#EFEFEF] bg-transparent sm:bg-[#FFF]
@@ -73,8 +86,7 @@ function LogInPage({ setIsSendMsg, setStep, matchedCity }) {
setError(checkedNum); setError(checkedNum);
return; return;
} }
setIsSendMsg && setIsSendMsg(true); handleReq();
setStep && setStep((prev) => prev + 1);
}} }}
className="!rounded-[8px] !bg-[#5559CE] !py-[7.5px] md:!py-[8.5px] lg:!py-[10px]" className="!rounded-[8px] !bg-[#5559CE] !py-[7.5px] md:!py-[8.5px] lg:!py-[10px]"
> >
+9 -23
View File
@@ -105,11 +105,11 @@ export const addLabelToList = (list) =>
}); });
export const formatPhoneNumber = (input) => { export const formatPhoneNumber = (input) => {
if (typeof input !== 'string') input = String(input); if (typeof input !== "string") input = String(input);
const normalized = input const normalized = input
.replace(/[\u06F0-\u06F9]/g, d => String(d.charCodeAt(0) - 0x06F0)) .replace(/[\u06F0-\u06F9]/g, (d) => String(d.charCodeAt(0) - 0x06f0))
.replace(/[\u0660-\u0669]/g, d => String(d.charCodeAt(0) - 0x0660)); .replace(/[\u0660-\u0669]/g, (d) => String(d.charCodeAt(0) - 0x0660));
const digits = normalized.replace(/\D/g, ""); const digits = normalized.replace(/\D/g, "");
@@ -146,17 +146,6 @@ export const searchOnList = (list, value, name) => {
return newList; return newList;
}; };
export const addKeyToList = (list, nameKey, nameValue) => {
const newList = list.map((item) => {
return {
...item,
[nameKey]: item[nameValue],
};
});
return newList;
};
export function clearFilterParams(router, listRemove) { export function clearFilterParams(router, listRemove) {
const currentParams = new URLSearchParams(window.location.search); const currentParams = new URLSearchParams(window.location.search);
const keysToRemove = listRemove; const keysToRemove = listRemove;
@@ -171,30 +160,27 @@ export const filterList = (data) => {
const parentList = specialties.filter((item) => !item.parent); const parentList = specialties.filter((item) => !item.parent);
const childrenList = specialties.filter((item) => item.parent); const childrenList = specialties.filter((item) => item.parent);
const changedParentList = addKeyToList(parentList, "label", "name") || [];
const changedChildrenList = addKeyToList(childrenList, "label", "name") || [];
const filteredChildrenList = const filteredChildrenList =
data && data.category data && data.category
? changedChildrenList.filter((item) => item.parent === data.category.id) ? childrenList.filter((item) => item.parent === data.category.id)
: []; : [];
return { return {
parentList: changedParentList, parentList: parentList,
childrenList: filteredChildrenList, childrenList: filteredChildrenList,
}; };
}; };
export function getCleanNumberValue(value, defaultValue = 0) { export function getCleanNumberValue(value, defaultValue = 0) {
if (typeof value !== 'string') value = String(value); if (typeof value !== "string") value = String(value);
// Convert Persian and Arabic digits to English digits // Convert Persian and Arabic digits to English digits
const persianArabicToEnglish = value const persianArabicToEnglish = value
.replace(/[\u06F0-\u06F9]/g, d => String(d.charCodeAt(0) - 0x06F0)) .replace(/[\u06F0-\u06F9]/g, (d) => String(d.charCodeAt(0) - 0x06f0))
.replace(/[\u0660-\u0669]/g, d => String(d.charCodeAt(0) - 0x0660)); .replace(/[\u0660-\u0669]/g, (d) => String(d.charCodeAt(0) - 0x0660));
// Remove non-numeric characters except dots (for decimal support) // Remove non-numeric characters except dots (for decimal support)
const cleaned = persianArabicToEnglish.replace(/[^0-9.]/g, ''); const cleaned = persianArabicToEnglish.replace(/[^0-9.]/g, "");
// Return number or default if invalid // Return number or default if invalid
const number = parseFloat(cleaned); const number = parseFloat(cleaned);
Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

+4 -26
View File
@@ -1,46 +1,24 @@
import axios from "axios"; import axios from "axios";
import Cookies from "universal-cookie"; import Cookies from "universal-cookie";
import "react-toastify/dist/ReactToastify.css";
const cookies = new Cookies(); const cookies = new Cookies();
const BASE_URL = "https://nobat724.com/"; const BASE_URL = process.env.NEXT_PUBLIC_API_URL;
const api = axios.create({ const api = axios.create({
baseURL: BASE_URL, baseURL: BASE_URL,
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("access")}`,
}, },
}); });
api.interceptors.request.use(
(request) => {
const token = cookies.get("token");
const isLoginPage = window.location.pathname === "/login";
if (!token) {
if (!isLoginPage) {
window.location.pathname = "/login";
}
} else {
if (isLoginPage) {
window.location.pathname = "/";
}
request.headers.authorization = `Bearer ${token}`;
}
return request;
},
(err) => Promise.reject(err)
);
api.interceptors.response.use( api.interceptors.response.use(
(response) => { (response) => {
return response.data; return response.data;
}, },
(error) => { (error) => {
if (error.data.status === 400) { return error;
cookies.remove("token", { path: "/" });
window.location.pathname = "/login";
} else {
}
} }
); );
+18 -1
View File
@@ -1,5 +1,22 @@
import api from "./api"; import api from "./api";
import Cookies from "universal-cookie";
export const request = { export const request = {
auth: (phone) => api.post("sign-up/", { phone }), auth: (grant_type, client_id, client_secret, username, password) =>
api.post(
"oauth/token",
{
grant_type,
client_id,
client_secret,
username,
password,
},
{
headers: {
Authorization: "",
"Content-Type": "application/x-www-form-urlencoded",
},
}
),
}; };