Merge branch 'release/v0.9.5'

This commit is contained in:
Amirhossein Mahmoodi
2024-11-24 11:47:38 +03:30
13 changed files with 615 additions and 423 deletions

View File

@@ -1,3 +1,3 @@
NEXT_PUBLIC_VERSION="0.9.4"
NEXT_PUBLIC_VERSION="0.9.5"
NEXT_PUBLIC_API_URL="https://rms.witel.ir"
NEXT_PUBLIC_MAPTILE_ENDPOINT="https://rmsmap.rmto.ir/141map"

View File

@@ -119,6 +119,19 @@ const AzmayeshList = () => {
</Typography>
),
},
{
accessorKey: "updated_at",
header: "تاریخ بروزرسانی",
id: "updated_at",
enableColumnFilter: false,
datatype: "date",
filterFn: "equals",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("HH:mm | YYYY/MM/DD") : "-"}
</Typography>
),
},
],
[]
);

View File

@@ -0,0 +1,198 @@
"use client";
import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
import React, { useState } from "react";
import { useTheme } from "@emotion/react";
import useRequest from "@/lib/hooks/useRequest";
import { object, string } from "yup";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import moment from "jalali-moment";
import { CREATE_AZMAYESH, UPDATE_AZMAYESH } from "@/core/utils/routes";
import StyledForm from "@/core/components/StyledForm";
import TravelExploreIcon from "@mui/icons-material/TravelExplore";
import TextSnippetIcon from "@mui/icons-material/TextSnippet";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
import BeenhereIcon from "@mui/icons-material/Beenhere";
import CandUMapBox from "./CandUMapBox";
import CandUCreateTimeLine from "./CandUTimeLine";
import CandUGeneralInfo from "./CandUGeneralInfo";
function TabPanel(props) {
const { children, value, index } = props;
return (
<div role="tabpanel" hidden={value !== index}>
{value === index && <Box>{children}</Box>}
</div>
);
}
const validationSchema = object({
azmayesh_type_id: string().required("نوع آزمایش را مشخص کنید!"),
province_id: string().required("استان را وارد کنید!"),
project_name: string().required("عنوان پروژه را وارد کنید!"),
employer: string().required("کارفرما را وارد کنید!"),
consultant: string().required("مشاور را وارد کنید!"),
contractor: string().required("پیمانکار را وارد کنید!"),
applicant: string().required("متقاضی را وارد کنید!"),
work_number: string().required("شماره کار را وارد کنید!"),
request_number: string().required("شماره درخواست را وارد کنید!"),
request_date: string().required("تاریخ درخواست را وارد کنید!"),
report_date: string().required("تاریخ گزارش را وارد کنید!"),
});
const CandUAzmayesh = ({ setOpen, mutate, updateInfo, rowId }) => {
const theme = useTheme();
const requestServer = useRequest({ auth: true });
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [tabState, setTabState] = useState(0);
const [mapBoxData, setMapBoxData] = useState(updateInfo ? { lat: updateInfo.lat, lng: updateInfo.lng } : null);
const handleClose = () => {
setOpen(false);
};
const handleChangeTab = (event, newValue) => {
setTabState(newValue);
};
const handlePrev = () => {
if (tabState === 0) {
handleClose();
} else {
setTabState(tabState - 1);
}
};
const defaultValues = {
azmayesh_type_id: updateInfo ? updateInfo.azmayesh_type_id : "",
azmayesh_type_name: updateInfo ? updateInfo.azmayesh_type_name : "",
province_id: updateInfo ? updateInfo.province_id : "",
province_name: updateInfo ? updateInfo.province_name : "",
project_name: updateInfo ? updateInfo.project_name : "",
employer: updateInfo ? updateInfo.employer : "",
consultant: updateInfo ? updateInfo.consultant : "",
contractor: updateInfo ? updateInfo.contractor : "",
applicant: updateInfo ? updateInfo.applicant : "",
work_number: updateInfo ? updateInfo.work_number : "",
request_number: updateInfo ? updateInfo.request_number : "",
request_date: updateInfo ? moment(updateInfo.request_date, "YYYY-MM-DD").toDate() : "",
report_date: updateInfo ? moment(updateInfo.report_date, "YYYY-MM-DD").toDate() : "",
};
const {
control,
register,
handleSubmit,
setValue,
formState: { isSubmitting, errors, touchedFields },
} = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" });
const onSubmit = async (data) => {
const formData = new FormData();
formData.append("lat", mapBoxData.lat);
formData.append("lng", mapBoxData.lng);
formData.append("azmayesh_type_id", data.azmayesh_type_id);
formData.append("azmayesh_type_name", data.azmayesh_type_name);
formData.append("province_id", data.province_id);
formData.append("province_name", data.province_name);
formData.append("project_name", data.project_name);
formData.append("employer", data.employer);
formData.append("consultant", data.consultant);
formData.append("contractor", data.contractor);
formData.append("applicant", data.applicant);
formData.append("work_number", data.work_number);
formData.append("request_number", data.request_number);
formData.append("request_date", moment(new Date(data.request_date)).format("YYYY-MM-DD"));
formData.append("report_date", moment(new Date(data.report_date)).format("YYYY-MM-DD"));
requestServer(updateInfo ? `${UPDATE_AZMAYESH}/${rowId}` : CREATE_AZMAYESH, "post", {
data: formData,
})
.then(() => {
mutate();
handleClose();
})
.catch(() => {});
};
return (
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<Tabs
allowScrollButtonsMobile
value={tabState}
onChange={handleChangeTab}
variant={`${isMobile ? "scrollable" : "fullWidth"}`}
sx={{
display: "flex",
justifyContent: "space-around",
width: "100%",
backgroundColor: "#efefef",
}}
>
<Tab icon={<TravelExploreIcon />} label="انتخاب محل آزمایش"></Tab>
<Tab disabled={!mapBoxData} icon={<TextSnippetIcon />} label="مشخصات آزمایش"></Tab>
</Tabs>
<DialogContent dividers sx={{ display: "flex" }}>
<Box sx={{ flex: 1 }}>
<TabPanel value={tabState} index={0}>
<CandUMapBox mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
</TabPanel>
<TabPanel value={tabState} index={1}>
<CandUGeneralInfo control={control} register={register} setValue={setValue} errors={errors} />
</TabPanel>
</Box>
{!isMobile && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<CandUCreateTimeLine tabState={tabState} />
</Box>
)}
</DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Button
onClick={handlePrev}
variant="outlined"
color="error"
size="large"
startIcon={tabState === 0 ? <ExitToAppIcon /> : <KeyboardDoubleArrowRightIcon />}
>
{tabState === 0 ? "بستن" : "مرحله قبل"}
</Button>
{tabState === 0 ? (
<Button
onClick={(event) => {
event.preventDefault();
setTabState(tabState + 1);
}}
variant="contained"
size="large"
disabled={!mapBoxData}
endIcon={<KeyboardDoubleArrowLeftIcon />}
>
مرحله بعد
</Button>
) : (
<Button
variant="contained"
size="large"
disabled={isSubmitting}
type={"submit"}
endIcon={<BeenhereIcon />}
>
{updateInfo
? isSubmitting
? "در حال ویرایش آزمایش"
: "ویرایش آزمایش"
: isSubmitting
? "در حال ثبت آزمایش"
: "ثبت آزمایش"}
</Button>
)}
</DialogActions>
</StyledForm>
);
};
export default CandUAzmayesh;

View File

@@ -1,204 +1,23 @@
"use client";
import { Box, Button, Dialog, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
import React, { useState } from "react";
import { useTheme } from "@emotion/react";
import useRequest from "@/lib/hooks/useRequest";
import { object, string } from "yup";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import moment from "jalali-moment";
import { CREATE_AZMAYESH, UPDATE_AZMAYESH } from "@/core/utils/routes";
import StyledForm from "@/core/components/StyledForm";
import TravelExploreIcon from "@mui/icons-material/TravelExplore";
import TextSnippetIcon from "@mui/icons-material/TextSnippet";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
import BeenhereIcon from "@mui/icons-material/Beenhere";
import CandUMapBox from "./CandUMapBox";
import CandUCreateTimeLine from "./CandUTimeLine";
import CandUGeneralInfo from "./CandUGeneralInfo";
function TabPanel(props) {
const { children, value, index } = props;
import { Dialog } from "@mui/material";
import FormAndRequest from "./FormAndRequest";
import DialogLoading from "@/core/components/DialogLoading";
const CandUAzmayesh = ({ open, setOpen, mutate, updateInfo, rowId, loadingOpen, fromUpdate = false }) => {
const isUpdateReady = fromUpdate ? updateInfo !== null : true;
return (
<div role="tabpanel" hidden={value !== index}>
{value === index && <Box>{children}</Box>}
</div>
);
}
const validationSchema = object({
azmayesh_type_id: string().required("نوع آزمایش را مشخص کنید!"),
province_id: string().required("استان را وارد کنید!"),
project_name: string().required("عنوان پروژه را وارد کنید!"),
employer: string().required("کارفرما را وارد کنید!"),
consultant: string().required("مشاور را وارد کنید!"),
contractor: string().required("پیمانکار را وارد کنید!"),
applicant: string().required("متقاضی را وارد کنید!"),
work_number: string().required("شماره کار را وارد کنید!"),
request_number: string().required("شماره درخواست را وارد کنید!"),
request_date: string().required("تاریخ درخواست را وارد کنید!"),
report_date: string().required("تاریخ گزارش را وارد کنید!"),
});
const CandUAzmayesh = ({ open, setOpen, mutate, updateInfo, rowId }) => {
const theme = useTheme();
const requestServer = useRequest({ auth: true });
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [tabState, setTabState] = useState(0);
const [mapBoxData, setMapBoxData] = useState(updateInfo ? { lat: updateInfo.lat, lng: updateInfo.lng } : null);
const handleClose = () => {
setOpen(false);
};
const handleChangeTab = (event, newValue) => {
setTabState(newValue);
};
const handlePrev = () => {
if (tabState === 0) {
handleClose();
} else {
setTabState(tabState - 1);
}
};
const defaultValues = {
azmayesh_type_id: updateInfo ? updateInfo.azmayesh_type_id : "",
azmayesh_type_name: updateInfo ? updateInfo.azmayesh_type_name : "",
province_id: updateInfo ? updateInfo.province_id : "",
province_name: updateInfo ? updateInfo.province_name : "",
project_name: updateInfo ? updateInfo.project_name : "",
employer: updateInfo ? updateInfo.employer : "",
consultant: updateInfo ? updateInfo.consultant : "",
contractor: updateInfo ? updateInfo.contractor : "",
applicant: updateInfo ? updateInfo.applicant : "",
work_number: updateInfo ? updateInfo.work_number : "",
request_number: updateInfo ? updateInfo.request_number : "",
request_date: updateInfo ? moment(updateInfo.request_date, "YYYY-MM-DD").toDate() : "",
report_date: updateInfo ? moment(updateInfo.report_date, "YYYY-MM-DD").toDate() : "",
};
const {
control,
register,
handleSubmit,
setValue,
formState: { isSubmitting, errors, touchedFields },
} = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" });
const onSubmit = async (data) => {
const formData = new FormData();
formData.append("lat", mapBoxData.lat);
formData.append("lng", mapBoxData.lng);
formData.append("azmayesh_type_id", data.azmayesh_type_id);
formData.append("azmayesh_type_name", data.azmayesh_type_name);
formData.append("province_id", data.province_id);
formData.append("province_name", data.province_name);
formData.append("project_name", data.project_name);
formData.append("employer", data.employer);
formData.append("consultant", data.consultant);
formData.append("contractor", data.contractor);
formData.append("applicant", data.applicant);
formData.append("work_number", data.work_number);
formData.append("request_number", data.request_number);
formData.append("request_date", moment(new Date(data.request_date)).format("YYYY-MM-DD"));
formData.append("report_date", moment(new Date(data.report_date)).format("YYYY-MM-DD"));
requestServer(updateInfo ? `${UPDATE_AZMAYESH}/${rowId}` : CREATE_AZMAYESH, "post", {
data: formData,
})
.then(() => {
mutate();
handleClose();
})
.catch(() => {});
};
return (
<Dialog open={open} fullWidth={true} maxWidth={"lg"}>
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<Tabs
allowScrollButtonsMobile
value={tabState}
onChange={handleChangeTab}
variant={`${isMobile ? "scrollable" : "fullWidth"}`}
sx={{
display: "flex",
justifyContent: "space-around",
width: "100%",
backgroundColor: "#efefef",
}}
>
<Tab icon={<TravelExploreIcon />} label="انتخاب محل آزمایش"></Tab>
<Tab disabled={!mapBoxData} icon={<TextSnippetIcon />} label="مشخصات آزمایش"></Tab>
</Tabs>
<DialogContent dividers sx={{ display: "flex" }}>
<Box sx={{ flex: 1 }}>
<TabPanel value={tabState} index={0}>
<CandUMapBox mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
</TabPanel>
<TabPanel value={tabState} index={1}>
<CandUGeneralInfo
control={control}
register={register}
setValue={setValue}
errors={errors}
/>
</TabPanel>
</Box>
{!isMobile && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<CandUCreateTimeLine tabState={tabState} />
</Box>
)}
</DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Button
onClick={handlePrev}
variant="outlined"
color="error"
size="large"
startIcon={tabState === 0 ? <ExitToAppIcon /> : <KeyboardDoubleArrowRightIcon />}
>
{tabState === 0 ? "بستن" : "مرحله قبل"}
</Button>
{tabState === 0 ? (
<Button
onClick={(event) => {
event.preventDefault();
setTabState(tabState + 1);
}}
variant="contained"
size="large"
disabled={!mapBoxData}
endIcon={<KeyboardDoubleArrowLeftIcon />}
>
مرحله بعد
</Button>
) : (
<Button
variant="contained"
size="large"
disabled={isSubmitting}
type={"submit"}
endIcon={<BeenhereIcon />}
>
{updateInfo
? isSubmitting
? "در حال ویرایش آزمایش"
: "ویرایش آزمایش"
: isSubmitting
? "در حال ثبت آزمایش"
: "ثبت آزمایش"}
</Button>
)}
</DialogActions>
</StyledForm>
<Dialog open={open} fullWidth maxWidth="lg">
{isUpdateReady ? (
<FormAndRequest
setOpen={setOpen}
mutate={mutate}
rowId={rowId}
{...(fromUpdate ? { updateInfo } : {})}
/>
) : (
<DialogLoading loadingOpen={loadingOpen} />
)}
</Dialog>
);
};

View File

@@ -9,15 +9,18 @@ const AzmayeshUpdate = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openUpdateDialog, setOpenUpdateDialog] = useState(false);
const [updateInfo, setUpdateInfo] = useState(null);
const [loadingOpen, setLoadingOpen] = useState(false);
const handleGetInfo = () => {
setLoadingOpen(true);
setOpenUpdateDialog(true);
requestServer(`${GET_AZMAYESH_LIST}/${rowId}`, "get")
.then((response) => {
setUpdateInfo(response.data.data);
})
.catch(() => {})
.finally(() => {
setOpenUpdateDialog(true);
setLoadingOpen(false);
});
};
@@ -39,6 +42,8 @@ const AzmayeshUpdate = ({ rowId, mutate }) => {
mutate={mutate}
updateInfo={updateInfo}
rowId={rowId}
loadingOpen={loadingOpen}
fromUpdate={true}
/>
)}
</>

View File

@@ -0,0 +1,73 @@
import { Button, DialogActions, DialogContent, DialogTitle } from "@mui/material";
import useRequest from "@/lib/hooks/useRequest";
import React, { useState } from "react";
import { ADD_SAMPLE_TO_AZMAYESH, UPDATE_SAMPLE_OF_AZMAYESH } from "@/core/utils/routes";
import FormMaker from "@/core/components/FormMaker";
import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog";
const CandUSampleOfAzmayesh = ({
sampleInfo,
mutate,
rowId,
setOpenSampleDialog,
defaultValues,
setDefaultValues,
isUpdate,
azmayesh_id,
}) => {
const requestServer = useRequest({ auth: true });
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
const data = {};
setIsSubmitting(true);
const formData = new FormData();
formData.append("azmayesh_id", isUpdate ? azmayesh_id : rowId);
Object.entries(defaultValues).forEach(([key, value]) => {
data[key] = value;
});
formData.append("data", JSON.stringify(data));
requestServer(isUpdate ? `${UPDATE_SAMPLE_OF_AZMAYESH}/${rowId}` : `${ADD_SAMPLE_TO_AZMAYESH}`, "post", {
data: formData,
})
.then(() => {
mutate();
})
.catch(() => {})
.finally(() => {
setOpenSampleDialog(false);
setIsSubmitting(false);
});
};
return (
<>
<DialogHeader>
<DialogTitle>{isUpdate ? "ویرایش نمونه" : "افزودن نمونه"}</DialogTitle>
</DialogHeader>
<DialogContent>
<FormMaker formInfo={sampleInfo} defaultValues={defaultValues} setDefaultValues={setDefaultValues} />
</DialogContent>
<DialogActions>
<Button
onClick={() => setOpenSampleDialog(false)}
variant="outlined"
color="error"
disabled={isSubmitting}
>
بستن
</Button>
<Button onClick={handleSubmit} variant="contained" color="primary" disabled={isSubmitting}>
{isUpdate
? isSubmitting
? "در حال ویرایش نمونه"
: "ویرایش نمونه"
: isSubmitting
? "در حال ثبت نمونه"
: "ثبت نمونه"}
</Button>
</DialogActions>
</>
);
};
export default CandUSampleOfAzmayesh;

View File

@@ -1,16 +1,18 @@
import { useMemo } from "react";
import { Box } from "@mui/material";
import { Box, Typography } from "@mui/material";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import { GET_SAMPLE_LIST } from "@/core/utils/routes";
import RowActions from "./RowActions";
import AzmayeshInfo from "@/components/dashboard/azmayesh/RowActions/ShowSampleOfAzmayesh/AzmayeshInfo";
import Toolbar from "./Toolbar";
import moment from "jalali-moment";
const ShowSampleList = ({ sampleInfo, rowData }) => {
const parsedData = (data) => {
return data.data.map((item) => ({
...JSON.parse(item.data),
id: item.id,
updated_at: item.updated_at,
}));
};
@@ -22,7 +24,6 @@ const ShowSampleList = ({ sampleInfo, rowData }) => {
id: "id",
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
},
...sampleInfo.map((item) => ({
accessorKey: item.id.toString(),
@@ -30,8 +31,19 @@ const ShowSampleList = ({ sampleInfo, rowData }) => {
id: item.id.toString(),
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
})),
{
accessorKey: "updated_at",
header: "تاریخ بروزرسانی",
id: "updated_at",
enableColumnFilter: false,
datatype: "date",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("HH:mm | YYYY/MM/DD") : "-"}
</Typography>
),
},
],
[sampleInfo]
);

View File

@@ -28,9 +28,9 @@ const AzmayeshTypeList = () => {
filterFn: "notEquals",
},
{
accessorKey: "created_at",
header: "تاریخ ثبت",
id: "created_at",
accessorKey: "updated_at",
header: "تاریخ بروزرسانی",
id: "updated_at",
enableColumnFilter: false,
datatype: "date",
filterFn: "equals",

View File

@@ -0,0 +1,222 @@
import {
Box,
Button,
Chip,
DialogActions,
DialogContent,
Divider,
FormControl,
FormHelperText,
Grid,
InputLabel,
List,
OutlinedInput,
Typography,
} from "@mui/material";
import StyledForm from "@/core/components/StyledForm";
import useRequest from "@/lib/hooks/useRequest";
import { useState } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { array, object, string } from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import ListItems from "./ListItems";
import AzmayeshFields from "./AzmayeshFields";
import { ADD_AZMAYESH_TYPE, UPDATE_AZMAYESH_TYPE } from "@/core/utils/routes";
const validationSchema = object({
name: string().required("عنوان نوع آزمایش را مشخص کنید!"),
fields: array()
.of(
object({
name: string().required("نام فیلد الزامی است"),
type: string().required("نوع فیلد الزامی است"),
options: array().when("type", {
is: "select",
then: (schema) => schema.min(1, "حداقل باید یک آیتم برای نوع انتخابی ایجاد کنید."),
otherwise: (schema) => schema.notRequired(),
}),
})
)
.min(1, "حداقل باید یک فیلد ایجاد نمایید."),
});
const FormAndRequest = ({ setOpen, mutate, rowId, updateInfo }) => {
const requestServer = useRequest({ auth: true });
const [selectedListItem, setSelectedListItem] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleClose = () => {
setOpen(false);
};
const defaultValues = {
name: updateInfo?.name || "",
fields: Array.isArray(updateInfo?.azmayesh_fields)
? updateInfo.azmayesh_fields.map((field) => ({
name: field.name || "",
type: field.type || "",
unit: field.unit || "",
options: field.option ? field.option : [],
}))
: [],
};
const {
control,
register,
handleSubmit,
setValue,
watch,
formState: { errors },
} = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" });
const { fields, append, remove } = useFieldArray({
control,
name: "fields",
});
const handleRemove = (index) => {
remove(index);
if (selectedListItem === index) {
setSelectedListItem(null);
} else if (selectedListItem > index) {
setSelectedListItem((prev) => prev - 1);
}
};
const handleAddField = () => {
append({ name: "", type: "", unit: "", options: [] });
};
const onSubmit = async (data) => {
setIsSubmitting(true);
const formData = new FormData();
formData.append("name", data.name);
data.fields.map((field, index) => {
formData.append(`fields[${index}][name]`, field.name);
formData.append(`fields[${index}][unit]`, field.unit);
formData.append(`fields[${index}][type]`, field.type);
if (field.options.length !== 0) formData.append(`fields[${index}][option]`, JSON.stringify(field.options));
});
requestServer(updateInfo ? `${UPDATE_AZMAYESH_TYPE}/${rowId}` : ADD_AZMAYESH_TYPE, "post", {
data: formData,
})
.then(() => {
mutate();
handleClose();
})
.catch(() => {})
.finally(() => setIsSubmitting(false));
};
return (
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<DialogContent dividers>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", my: 2 }}>
<FormControl error={!!errors.name} size="small" variant="outlined" sx={{ width: "250px" }}>
<InputLabel htmlFor="name">عنوان نوع آزمایش</InputLabel>
<OutlinedInput
id="name"
label={"عنوان نوع آزمایش"}
autoComplete="off"
{...register("name")}
size="small"
type="text"
/>
<FormHelperText id="name">{errors.name ? errors.name.message : null}</FormHelperText>
</FormControl>
</Box>
<Grid container spacing={2} sx={{ justifyContent: "space-between" }}>
<Grid xs={12} md={4} lg={3} sx={{ px: 1 }}>
<Divider sx={{ my: 2 }}>
<Chip label="لیست فیلد ها" />
</Divider>
<Button
variant="contained"
color="success"
fullWidth
startIcon={<DataSaverOnIcon />}
sx={{ mb: 2 }}
onClick={handleAddField}
>
ایجاد فیلد جدید
</Button>
<List
sx={{
border: "1px dashed #e1e1e1",
borderRadius: "5px",
p: 1,
maxHeight: "300px",
overflowY: "auto",
}}
>
{fields.length !== 0 ? (
fields.map((item, index) => (
<ListItems
key={index}
index={index}
selectedListItem={selectedListItem}
fields={fields}
handleRemove={handleRemove}
setSelectedListItem={setSelectedListItem}
watch={watch}
/>
))
) : (
<Typography
variant="h6"
sx={{ color: "#a2a2a2", textAlign: "center", letterSpacing: "1px" }}
>
فیلدی وجود ندارد
</Typography>
)}
</List>
<Typography variant="caption" color="error">
{errors.fields ? errors.fields.message : null}
</Typography>
</Grid>
<Divider orientation="vertical" variant="middle" flexItem />
<Grid xs={12} md={7} lg={8} sx={{ pl: 2 }}>
<Divider sx={{ my: 2 }}>
<Chip label="اطلاعات فیلد" />
</Divider>
{selectedListItem !== null ? (
<AzmayeshFields
errors={errors}
selectedListItem={selectedListItem}
register={register}
setValue={setValue}
fields={fields}
watch={watch}
/>
) : (
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", py: 2 }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#a2a2a2" }}>
از بخش لیست فیلد ها یک آیتم را انتخاب نمایید
</Typography>
</Box>
)}
</Grid>
</Grid>
</DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center", py: 2 }}>
<Button variant="outlined" color="error" onClick={handleClose}>
بستن
</Button>
<Button
type={"submit"}
variant="contained"
color="primary"
disabled={isSubmitting}
sx={{ width: "200px" }}
>
{isSubmitting ? "در حال ثبت..." : "ثبت نوع آزمایش"}
</Button>
</DialogActions>
</StyledForm>
);
};
export default FormAndRequest;

View File

@@ -1,222 +1,21 @@
import {
Box,
Button,
Chip,
Dialog,
DialogActions,
DialogContent,
Divider,
FormControl,
FormHelperText,
Grid,
InputLabel,
List,
OutlinedInput,
Typography,
} from "@mui/material";
import StyledForm from "@/core/components/StyledForm";
import useRequest from "@/lib/hooks/useRequest";
import { useState } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { array, object, string } from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import ListItems from "./ListItems";
import AzmayeshFields from "./AzmayeshFields";
import { ADD_AZMAYESH_TYPE, UPDATE_AZMAYESH_TYPE } from "@/core/utils/routes";
const validationSchema = object({
name: string().required("عنوان نوع آزمایش را مشخص کنید!"),
fields: array()
.of(
object({
name: string().required("نام فیلد الزامی است"),
type: string().required("نوع فیلد الزامی است"),
options: array().when("type", {
is: "select",
then: (schema) => schema.min(1, "حداقل باید یک آیتم برای نوع انتخابی ایجاد کنید."),
otherwise: (schema) => schema.notRequired(),
}),
})
)
.min(1, "حداقل باید یک فیلد ایجاد نمایید."),
});
const CandUAzmayeshType = ({ open, setOpen, mutate, rowId, updateInfo }) => {
const requestServer = useRequest({ auth: true });
const [selectedListItem, setSelectedListItem] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleClose = () => {
setOpen(false);
};
const defaultValues = {
name: updateInfo?.name || "",
fields: Array.isArray(updateInfo?.azmayesh_fields)
? updateInfo.azmayesh_fields.map((field) => ({
name: field.name || "",
type: field.type || "",
unit: field.unit || "",
options: field.option ? field.option : [],
}))
: [],
};
const {
control,
register,
handleSubmit,
setValue,
watch,
formState: { errors },
} = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" });
const { fields, append, remove } = useFieldArray({
control,
name: "fields",
});
const handleRemove = (index) => {
remove(index);
if (selectedListItem === index) {
setSelectedListItem(null);
} else if (selectedListItem > index) {
setSelectedListItem((prev) => prev - 1);
}
};
const handleAddField = () => {
append({ name: "", type: "", unit: "", options: [] });
};
const onSubmit = async (data) => {
setIsSubmitting(true);
const formData = new FormData();
formData.append("name", data.name);
data.fields.map((field, index) => {
formData.append(`fields[${index}][name]`, field.name);
formData.append(`fields[${index}][unit]`, field.unit);
formData.append(`fields[${index}][type]`, field.type);
if (field.options.length !== 0) formData.append(`fields[${index}][option]`, JSON.stringify(field.options));
});
requestServer(updateInfo ? `${UPDATE_AZMAYESH_TYPE}/${rowId}` : ADD_AZMAYESH_TYPE, "post", {
data: formData,
})
.then(() => {
mutate();
handleClose();
})
.catch(() => {})
.finally(() => setIsSubmitting(false));
};
import { Dialog } from "@mui/material";
import FormAndRequest from "./FormAndRequest";
import DialogLoading from "@/core/components/DialogLoading";
const CandUAzmayeshType = ({ open, setOpen, mutate, rowId, updateInfo, loadingOpen, fromUpdate = false }) => {
const isUpdateReady = fromUpdate ? updateInfo !== null : true;
return (
<Dialog open={open} fullWidth={true} maxWidth={"lg"}>
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<DialogContent dividers>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", my: 2 }}>
<FormControl error={!!errors.name} size="small" variant="outlined" sx={{ width: "250px" }}>
<InputLabel htmlFor="name">عنوان نوع آزمایش</InputLabel>
<OutlinedInput
id="name"
label={"عنوان نوع آزمایش"}
autoComplete="off"
{...register("name")}
size="small"
type="text"
/>
<FormHelperText id="name">{errors.name ? errors.name.message : null}</FormHelperText>
</FormControl>
</Box>
<Grid container spacing={2} sx={{ justifyContent: "space-between" }}>
<Grid xs={12} md={4} lg={3} sx={{ px: 1 }}>
<Divider sx={{ my: 2 }}>
<Chip label="لیست فیلد ها" />
</Divider>
<Button
variant="contained"
color="success"
fullWidth
startIcon={<DataSaverOnIcon />}
sx={{ mb: 2 }}
onClick={handleAddField}
>
ایجاد فیلد جدید
</Button>
<List
sx={{
border: "1px dashed #e1e1e1",
borderRadius: "5px",
p: 1,
maxHeight: "300px",
overflowY: "auto",
}}
>
{fields.length !== 0 ? (
fields.map((item, index) => (
<ListItems
key={index}
index={index}
selectedListItem={selectedListItem}
fields={fields}
handleRemove={handleRemove}
setSelectedListItem={setSelectedListItem}
watch={watch}
/>
))
) : (
<Typography
variant="h6"
sx={{ color: "#a2a2a2", textAlign: "center", letterSpacing: "1px" }}
>
فیلدی وجود ندارد
</Typography>
)}
</List>
<Typography variant="caption" color="error">
{errors.fields ? errors.fields.message : null}
</Typography>
</Grid>
<Divider orientation="vertical" variant="middle" flexItem />
<Grid xs={12} md={7} lg={8} sx={{ pl: 2 }}>
<Divider sx={{ my: 2 }}>
<Chip label="اطلاعات فیلد" />
</Divider>
{selectedListItem !== null ? (
<AzmayeshFields
errors={errors}
selectedListItem={selectedListItem}
register={register}
setValue={setValue}
fields={fields}
watch={watch}
/>
) : (
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", py: 2 }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#a2a2a2" }}>
از بخش لیست فیلد ها یک آیتم را انتخاب نمایید
</Typography>
</Box>
)}
</Grid>
</Grid>
</DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center", py: 2 }}>
<Button variant="outlined" color="error" onClick={handleClose}>
بستن
</Button>
<Button
type={"submit"}
variant="contained"
color="primary"
disabled={isSubmitting}
sx={{ width: "200px" }}
>
{isSubmitting ? "در حال ثبت..." : "ثبت نوع آزمایش"}
</Button>
</DialogActions>
</StyledForm>
<Dialog open={open} fullWidth maxWidth="lg">
{isUpdateReady ? (
<FormAndRequest
setOpen={setOpen}
mutate={mutate}
rowId={rowId}
{...(fromUpdate ? { updateInfo } : {})}
/>
) : (
<DialogLoading loadingOpen={loadingOpen} />
)}
</Dialog>
);
};

View File

@@ -9,15 +9,18 @@ const UpdateAzmayeshType = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openUpdateDialog, setOpenUpdateDialog] = useState(false);
const [updateInfo, setUpdateInfo] = useState(null);
const [loadingOpen, setLoadingOpen] = useState(false);
const handleGetInfo = () => {
setLoadingOpen(true);
setOpenUpdateDialog(true);
requestServer(`${GET_AZMAYESH_TYPE_LIST_TABLE}/${rowId}`, "get")
.then((response) => {
setUpdateInfo(response.data.data);
})
.catch(() => {})
.finally(() => {
setOpenUpdateDialog(true);
setLoadingOpen(false);
});
};
@@ -39,6 +42,8 @@ const UpdateAzmayeshType = ({ rowId, mutate }) => {
mutate={mutate}
updateInfo={updateInfo}
rowId={rowId}
loadingOpen={loadingOpen}
fromUpdate={true}
/>
)}
</>

View File

@@ -0,0 +1,36 @@
import { Box, Skeleton } from "@mui/material";
const DialogLoading = ({ loadingOpen }) => {
return (
<Box sx={{ display: "flex", flexDirection: "column", gap: 2, p: 2 }}>
<Skeleton variant="text" sx={{ fontSize: "1.5rem", width: "70%" }} />
<Skeleton variant="text" sx={{ fontSize: "1rem", width: "90%" }} />
<Skeleton variant="rectangular" sx={{ borderRadius: "8px", height: "180px", width: "100%" }} />
<Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Skeleton variant="text" sx={{ fontSize: "1rem", width: "80%" }} />
<Skeleton variant="text" sx={{ fontSize: "1rem", width: "60%" }} />
<Skeleton variant="text" sx={{ fontSize: "1rem", width: "70%" }} />
</Box>
<Box sx={{ display: "flex", gap: 2, mt: 2 }}>
<Skeleton
variant="rectangular"
sx={{
borderRadius: "8px",
height: "40px",
width: "30%",
}}
/>
<Skeleton
variant="rectangular"
sx={{
borderRadius: "8px",
height: "40px",
width: "20%",
}}
/>
</Box>
</Box>
);
};
export default DialogLoading;

View File

@@ -17,6 +17,16 @@ const theme = createTheme({
contrastText: "#fff",
},
},
components: {
MuiBackdrop: {
styleOverrides: {
root: {
backdropFilter: "blur(2px)",
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
},
},
},
});
export default theme;