page add panel/doctor role agent && save data crop img && upload img && just roles agent can access panel && save data doctor && add field phone number && remove two tab

This commit is contained in:
Ehsan
2025-07-16 08:13:29 +03:30
parent 1b2573e460
commit c88a371eda
37 changed files with 196 additions and 1193 deletions
@@ -0,0 +1,54 @@
import { useState } from "react";
import { convertToJalali } from "@/helper";
import EditField from "@/app/component/EditField";
import ButtonDate from "@/app/component/ButtonDate";
import CalendarPD from "@/components/icons/CalendarPD";
import ContentText from "@/app/component/ContentText";
function FieldDate({ changeListData, data }) {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
const updateDate = (newDate) => {
const jalaliDate = newDate
? convertToJalali(newDate)
: "تاریخ مد نظر پیدا نشد...";
changeListData(jalaliDate, "value", "time_work");
};
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
return (
<ContentText text="زمان شروع به کار">
<ButtonDate
setAnchorEl={setAnchorEl}
updateDate={updateDate}
anchorEl={anchorEl}
open={open}
id={id}
>
<div className="relative w-full">
<EditField
placeholder="انتخاب کنید..."
changeData={changeListData}
data={data}
icon={<CalendarPD />}
isTransparent={true}
iconGray={true}
name="time_work"
/>
<span
className="absolute left-0 top-0 w-full h-full bg-transparent z-10 cursor-pointer"
onClick={handleClick}
></span>
</div>
</ButtonDate>
</ContentText>
);
}
export default FieldDate;
@@ -0,0 +1,32 @@
import { FormControlLabel, Radio, RadioGroup } from "@mui/material";
function RadioGender({ changeData, name }) {
return (
<RadioGroup
dir="ltr"
className="!flex radio-mui !flex-row !gap-[36px] !items-center !justify-start"
defaultValue="woman"
onChange={(e) => changeData(e.target.value, "value", name)}
sx={{
"& .muirtl-j204z7-MuiFormControlLabel-root": {
marginLeft: "0 !important",
},
}}
>
<FormControlLabel
label="مرد"
value="man"
control={<Radio />}
className="!text-[#A1A1A1]"
/>
<FormControlLabel
label="زن"
value="woman"
control={<Radio />}
className="!text-[#A1A1A1]"
/>
</RadioGroup>
);
}
export default RadioGender;
@@ -0,0 +1,83 @@
import BottomSelect from "@/components/icons/BottomSelect";
import { Autocomplete, Button, TextField } from "@mui/material";
import { styleTextSelectRight } from "@/mui";
function Selector({ updateData, nameKey, label, name, list }) {
return (
<Autocomplete
disablePortal
options={list}
getOptionLabel={(option) => option[nameKey] || ""}
isOptionEqualToValue={(option, value) => option.id === value.id}
className="!w-full !rounded-lg auto-complete-selector "
onChange={(e, newValue) => {
if (newValue) {
updateData && updateData(newValue.id, "value", name);
}
}}
sx={{
...styleTextSelectRight,
// Hide Icon Number
"& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button":
{
display: "none",
},
"& input[type=number]": {
MozAppearance: "textfield",
},
"& .MuiInputBase-input": { padding: "12.5px 14px !important" },
"& .MuiInputBase-root": {
borderRadius: 2,
padding: "0 !important",
height: {
xs: "43px !important",
sm: "43px !important",
md: "46px !important",
lg: "48px !important",
},
},
"& .MuiOutlinedInput-notchedOutline": {
border: "1px solid #D7D7D7",
},
"& .MuiInput-underline:hover:not(.Mui-disabled):before, .MuiOutlinedInput-notchedOutline":
{
borderColor: "#D7D7D7 !important",
},
"& .muirtl-1kiro5a-MuiInputBase-root-MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline":
{
border: "1px solid #5559CE !important",
},
}}
renderOption={(props, option) => (
<Button
fullWidth
{...props}
key={option.id || props.id}
className="!flex !justify-start !p-[8px] !text-start !text-[#A1A1A1] hover:dark:!bg-[#35343D] !bg-[#FFF] dark:!bg-[#1F1D2B]"
>
{option.name || option.label}
</Button>
)}
renderInput={(params) => (
<TextField
fullWidth
{...params}
InputProps={{
...params.InputProps,
endAdornment: (
<div className="absolute left-4">
<BottomSelect />
</div>
),
}}
placeholder={label}
sx={{
borderRadius: "8px",
}}
/>
)}
/>
);
}
export default Selector;
+133
View File
@@ -0,0 +1,133 @@
import FieldDate from "./fields/FieldDate";
import RadioGender from "./fields/RadioGender";
import SendReq from "./sendReq";
import Selector from "./fields/Selector";
// Components
import EditField from "@/app/component/EditField";
import ContentText from "@/app/component/ContentText";
// Data
import specialties from "@/data/specialties.json";
import education from "@/data/education.json";
const validate_medical_number = {
min: 1,
max: 9999999999,
};
function Form({ image, data, setData, resetData }) {
const changeData = (value, type, name) => {
type !== "isEdit" &&
setData((prev) => {
return {
...prev,
[name]: {
...prev[name],
[type]: value,
},
};
});
};
const updateNum = (value, type, name) => {
const parseValue = +value;
const { max } = validate_medical_number;
const validateValue = parseValue > max ? data[name].value : value;
changeData(validateValue, type, name);
};
const filteredSpe = specialties.filter((item) => item.parent);
return (
<>
<div className="mt-[24px] sm-modal scroll-mui md:mt-[26px] lg:mt-[28px] grid grid-cols-1 md:grid-cols-2 gap-x-[48px] gap-y-[24px]">
<ContentText text="نام و نام خانوادگی">
<EditField
isTransparent={true}
iconGray={true}
changeData={changeData}
data={data?.name}
placeholder={data?.name.placeholder}
name="name"
/>
</ContentText>
<ContentText text="شماره نظام پزشکی">
<EditField
isTransparent={true}
iconGray={true}
changeData={updateNum}
data={data?.medical_system_number}
noDisable={true}
type="number"
placeholder={data?.medical_system_number.placeholder}
name="medical_system_number"
props={{
inputProps: validate_medical_number,
}}
/>
</ContentText>
<ContentText text="تخصص">
<Selector
updateData={changeData}
data={data?.expertise.value}
label="انتخاب کنید..."
name="expertise"
list={filteredSpe}
nameKey="name"
/>
</ContentText>
<ContentText text="مدرک">
<Selector
updateData={changeData}
label="انتخاب کنید..."
name="degree"
data={data?.degree.value}
list={education}
nameKey="label"
/>
</ContentText>
<ContentText text="توضیحات">
<EditField
multiline={5}
placeholder="درباره سوابق خود توضیحاتی بنویسید..."
isTransparent={true}
iconGray={true}
changeData={changeData}
data={data?.description}
name="description"
/>
</ContentText>
<ContentText text="مهارت ها">
<EditField
multiline={5}
placeholder="مهارت های خود را بنویسید..."
isTransparent={true}
iconGray={true}
changeData={changeData}
data={data?.skills}
name="skills"
/>
</ContentText>
<ContentText text="شماره موبایل">
<EditField
isTransparent={true}
iconGray={true}
changeData={changeData}
data={data?.phone}
type="number"
placeholder={data?.phone.placeholder}
name="phone"
/>
</ContentText>
<FieldDate changeListData={changeData} data={data?.time_work} />
<ContentText text="جنسیت">
<RadioGender changeData={changeData} name="gender" />
</ContentText>
</div>
<SendReq img={image} data={data} resetData={resetData} />
</>
);
}
export default Form;
@@ -0,0 +1,13 @@
export const degree = [
{ label: "مدرک 1", id: 10 },
{ label: "مدرک 2", id: 20 },
{ label: "مدرک 3", id: 30 },
{ label: "مدرک 4", id: 40 },
];
export const expertise = [
{ label: "فوق تخصص", id: 10 },
{ label: "دکترای تخصصی", id: 20 },
{ label: "متخصص", id: 30 },
{ label: "دکتری", id: 40 },
];
@@ -0,0 +1,18 @@
import moment from "jalali-moment";
export const dateToTimestamp = (date) => {
const gregorianMoment = moment(date, "jYYYY/jM/jD").locale("fa");
const unixTimestamp = gregorianMoment.valueOf();
return unixTimestamp / 1000;
};
export const transformData = (data) => {
return {
name: data.name.value,
activity_time: dateToTimestamp(data.time_work.value),
doctor_mobile_number: data.phone.value,
degree: data.degree.value,
specialty: [Number(data.expertise.value)],
info: data.description.value,
};
};
@@ -0,0 +1,66 @@
import ArrowLeftPA from "@/components/icons/ArrowLeftPA";
import { checkAllValues } from "@/helper";
import { request } from "@/services/response";
import { Button } from "@mui/material";
import { useState } from "react";
import { transformData } from "./function";
import { defaultState } from "../../defaultState";
function SendReq({ img, data, resetData }) {
const [loading, setLoading] = useState(false);
const saveData = (img) => {
let transformed = transformData(data);
if (img) {
transformed.image = [img];
}
request
.postDoctor(transformed)
.then(() => {
resetData();
setLoading(false);
})
.catch(() => {
setLoading(false);
});
};
const saveImg = () => {
setLoading(true);
if (img) {
request
.postImageUpload(img.file)
.then((res) => {
const imgId = res.fid[0].value;
saveData(imgId);
})
.catch((err) => {
setLoading(false);
});
} else {
saveData();
}
};
return (
<div className="mt-[40px] w-full flex justify-end">
<Button
className="!w-full md:!w-fit !shadow-none !rounded-[4px] !gap-[4px] !text-[#EFEFEF] !text-[14px] md:!text-[16px] !font-medium"
// onClick={() => setValue((prev) => prev + 1)}
disabled={!checkAllValues(data)}
variant="contained"
onClick={saveImg}
loading={loading}
>
<p className={loading ? "opacity-0" : ""}>ثبت اطلاعات</p>
<div className={!checkAllValues(data) ? "opacity-40" : "opacity-100"}>
<ArrowLeftPA />
</div>
</Button>
</div>
);
}
export default SendReq;