Merge branch 'feature/azmayesh_sample' into 'develop'

complete up to request for storing azmayesh_sample and complete design of...

See merge request witel-front-end/rms!30
This commit is contained in:
AmirHossein Mahmoodi
2024-11-04 07:37:46 +00:00
30 changed files with 924 additions and 85 deletions

View File

@@ -4,7 +4,7 @@ import { Button, IconButton, useMediaQuery } from "@mui/material";
import { useTheme } from "@emotion/react";
import { useState } from "react";
import { AddCircle } from "@mui/icons-material";
import CreateDialog from "./CreateDialog";
import CandUAzmayesh from "@/components/dashboard/azmayesh/Forms/CandUAzmayesh";
const AzmayeshCreate = ({ mutate }) => {
const theme = useTheme();
@@ -26,7 +26,7 @@ const AzmayeshCreate = ({ mutate }) => {
ثبت آزمایش
</Button>
)}
{open && <CreateDialog open={open} setOpen={setOpen} mutate={mutate} />}
{open && <CandUAzmayesh open={open} setOpen={setOpen} mutate={mutate} />}
</>
);
};

View File

@@ -5,8 +5,9 @@ import { Box, Typography } from "@mui/material";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import Toolbar from "./Toolbar";
import { GET_AZMAYESH_LIST } from "@/core/utils/routes";
import moment from "jalali-moment";
import ShowLocation from "./ShowLocation";
import moment from "jalali-moment";
import RowActions from "./RowActions";
const AzmayeshList = () => {
const columns = useMemo(
@@ -101,7 +102,7 @@ const AzmayeshList = () => {
filterFn: "equals",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">
{moment(renderedCellValue).locale("fa").format("YYYY/MM/DD")}
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("YYYY/MM/DD") : "-"}
</Typography>
),
},
@@ -114,7 +115,7 @@ const AzmayeshList = () => {
filterFn: "equals",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">
{moment(renderedCellValue).locale("fa").format("YYYY/MM/DD")}
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("YYYY/MM/DD") : "-"}
</Typography>
),
},
@@ -133,6 +134,9 @@ const AzmayeshList = () => {
page_name={"azmayesh"}
table_name={"azmayeshList"}
TableToolbar={Toolbar}
enableRowActions
positionActionsColumn={"last"}
RowActions={RowActions}
/>
</Box>
</>

View File

@@ -21,7 +21,7 @@ import VerifiedIcon from "@mui/icons-material/Verified";
import EditIcon from "@mui/icons-material/Edit";
import { useTheme } from "@emotion/react";
const ChooseLocation = ({ mapBoxData, setMapBoxData }) => {
const CandUChooseLocation = ({ mapBoxData, setMapBoxData }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
@@ -177,4 +177,4 @@ const ChooseLocation = ({ mapBoxData, setMapBoxData }) => {
);
};
export default ChooseLocation;
export default CandUChooseLocation;

View File

@@ -22,7 +22,7 @@ import { faIR } from "@mui/x-date-pickers/locales";
import ClearIcon from "@mui/icons-material/Clear";
import React from "react";
const AzmayeshGeneralInfo = ({ control, register, setValue, errors }) => {
const CandUGeneralInfo = ({ control, register, setValue, errors }) => {
const { azmayeshes, errorAzmayeshes, loadingAzmayeshes } = useAzmayesh();
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
@@ -98,11 +98,11 @@ const AzmayeshGeneralInfo = ({ control, register, setValue, errors }) => {
value={provinces.find((province) => province.id === value) || null}
disablePortal
options={provinces}
getOptionLabel={(province) => province.name}
getOptionLabel={(province) => province.name_fa}
isOptionEqualToValue={(option, value) => option.id === value?.id}
onChange={(event, newValue) => {
onChange(newValue ? newValue.id : "");
setValue("province_name", newValue ? newValue.name : "");
setValue("province_name", newValue ? newValue.name_fa : "");
}}
renderInput={(params) => (
<TextField
@@ -241,7 +241,6 @@ const AzmayeshGeneralInfo = ({ control, register, setValue, errors }) => {
<Controller
name="request_date"
control={control}
defaultValue={new Date()}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl error={!!errors.request_date} fullWidth size="small" variant="outlined">
<LocalizationProvider
@@ -295,7 +294,6 @@ const AzmayeshGeneralInfo = ({ control, register, setValue, errors }) => {
<Controller
name="report_date"
control={control}
defaultValue={new Date()}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl error={!!errors.report_date} fullWidth size="small" variant="outlined">
<LocalizationProvider
@@ -348,4 +346,4 @@ const AzmayeshGeneralInfo = ({ control, register, setValue, errors }) => {
</Grid>
);
};
export default AzmayeshGeneralInfo;
export default CandUGeneralInfo;

View File

@@ -3,14 +3,14 @@
import { Box, Stack } from "@mui/material";
import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading";
import ChooseLocation from "./ChooseLocation";
import CandUChooseLocation from "./CandUChooseLocation";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />,
ssr: false,
});
const MapBox = ({ mapBoxData, setMapBoxData }) => {
const CandUMapBox = ({ mapBoxData, setMapBoxData }) => {
return (
<Stack spacing={1} sx={{ height: "500px" }}>
<Box
@@ -31,11 +31,11 @@ const MapBox = ({ mapBoxData, setMapBoxData }) => {
}}
>
<MapLayer>
<ChooseLocation mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
<CandUChooseLocation mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
</MapLayer>
</Box>
</Box>
</Stack>
);
};
export default MapBox;
export default CandUMapBox;

View File

@@ -13,7 +13,7 @@ import {
TimelineSeparator,
} from "@mui/lab";
const CreateTimeLine = ({ tabState }) => {
const CandUCreateTimeLine = ({ tabState }) => {
return (
<Box sx={{ display: "flex", alignItems: "center" }}>
<Timeline
@@ -62,4 +62,4 @@ const CreateTimeLine = ({ tabState }) => {
</Box>
);
};
export default CreateTimeLine;
export default CandUCreateTimeLine;

View File

@@ -1,24 +1,24 @@
"use client";
import { Box, Button, Dialog, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
import { useTheme } from "@emotion/react";
import React, { useState } from "react";
import MapBox from "./MapBox";
import TravelExploreIcon from "@mui/icons-material/TravelExplore";
import TextSnippetIcon from "@mui/icons-material/TextSnippet";
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import BeenhereIcon from "@mui/icons-material/Beenhere";
import CreateTimeLine from "./CreateTimeLine";
import { useForm } from "react-hook-form";
import StyledForm from "@/core/components/StyledForm";
import { object, string } from "yup";
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 { POST_AZMAYESH } from "@/core/utils/routes";
import AzmayeshGeneralInfo from "./AzmayeshGeneralInfo";
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;
@@ -30,11 +30,26 @@ function TabPanel(props) {
);
}
const CreateDialog = ({ open, setOpen, mutate }) => {
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(null);
const [mapBoxData, setMapBoxData] = useState(updateInfo ? { lat: updateInfo.lat, lng: updateInfo.lng } : null);
const handleClose = () => {
setOpen(false);
@@ -52,35 +67,20 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
}
};
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 requestServer = useRequest({ auth: true });
const defaultValues = {
azmayesh_type_id: "",
azmayesh_type_name: "",
province_id: "",
province_name: "",
project_name: "",
employer: "",
consultant: "",
contractor: "",
applicant: "",
work_number: "",
request_number: "",
request_date: "",
report_date: "",
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 {
@@ -106,10 +106,10 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
formData.append("applicant", data.applicant);
formData.append("work_number", data.work_number);
formData.append("request_number", data.request_number);
formData.append("request_date", moment(data.request_date).format("YYYY-MM-DD"));
formData.append("report_date", moment(data.report_date).format("YYYY-MM-DD"));
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(POST_AZMAYESH, "post", {
requestServer(updateInfo ? `${UPDATE_AZMAYESH}/${rowId}` : CREATE_AZMAYESH, "post", {
data: formData,
})
.then(() => {
@@ -140,10 +140,10 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
<DialogContent dividers sx={{ display: "flex" }}>
<Box sx={{ flex: 1 }}>
<TabPanel value={tabState} index={0}>
<MapBox mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
<CandUMapBox mapBoxData={mapBoxData} setMapBoxData={setMapBoxData} />
</TabPanel>
<TabPanel value={tabState} index={1}>
<AzmayeshGeneralInfo
<CandUGeneralInfo
control={control}
register={register}
setValue={setValue}
@@ -153,7 +153,7 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
</Box>
{!isMobile && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<CreateTimeLine tabState={tabState} />
<CandUCreateTimeLine tabState={tabState} />
</Box>
)}
</DialogContent>
@@ -169,7 +169,10 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
</Button>
{tabState === 0 ? (
<Button
onClick={() => setTabState(tabState + 1)}
onClick={(event) => {
event.preventDefault();
setTabState(tabState + 1);
}}
variant="contained"
size="large"
disabled={!mapBoxData}
@@ -185,7 +188,13 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
type={"submit"}
endIcon={<BeenhereIcon />}
>
{isSubmitting ? "در حال ثبت آزمایش" : "ثبت آزمایش"}
{updateInfo
? isSubmitting
? "در حال ویرایش آزمایش"
: "ویرایش آزمایش"
: isSubmitting
? "در حال ثبت آزمایش"
: "ثبت آزمایش"}
</Button>
)}
</DialogActions>
@@ -193,4 +202,4 @@ const CreateDialog = ({ open, setOpen, mutate }) => {
</Dialog>
);
};
export default CreateDialog;
export default CandUAzmayesh;

View File

@@ -0,0 +1,47 @@
import { IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import EditIcon from "@mui/icons-material/Edit";
import { GET_AZMAYESH_LIST } from "@/core/utils/routes";
import CandUAzmayesh from "@/components/dashboard/azmayesh/Forms/CandUAzmayesh";
const AzmayeshUpdate = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openUpdateDialog, setOpenUpdateDialog] = useState(false);
const [updateInfo, setUpdateInfo] = useState(null);
const handleGetInfo = () => {
requestServer(`${GET_AZMAYESH_LIST}/${rowId}`, "get")
.then((response) => {
setUpdateInfo(response.data.data);
})
.catch(() => {})
.finally(() => {
setOpenUpdateDialog(true);
});
};
return (
<>
<Tooltip title="ویرایش آزمایش" arrow placement="right">
<IconButton
color="success"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={handleGetInfo}
>
<EditIcon />
</IconButton>
</Tooltip>
{openUpdateDialog && (
<CandUAzmayesh
open={openUpdateDialog}
setOpen={setOpenUpdateDialog}
mutate={mutate}
updateInfo={updateInfo}
rowId={rowId}
/>
)}
</>
);
};
export default AzmayeshUpdate;

View File

@@ -0,0 +1,78 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Tooltip,
Typography,
} from "@mui/material";
import { useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import DeleteIcon from "@mui/icons-material/Delete";
import { DELETE_AZMAYESH } from "@/core/utils/routes";
const DeleteAzmayesh = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
setIsSubmitting(true);
requestServer(`${DELETE_AZMAYESH}/${rowId}`, "post")
.then(() => {
setOpenDeleteDialog(false);
mutate();
})
.catch(() => {})
.finally(() => {
setIsSubmitting(false);
});
};
return (
<>
<Tooltip title="حذف آزمایش" arrow placement="right">
<IconButton
color="error"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={() => {
setOpenDeleteDialog(true);
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Dialog
maxWidth="sm"
open={openDeleteDialog}
PaperProps={{
sx: {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
},
}}
>
<DialogTitle>حذف آزمایش</DialogTitle>
<DialogContent>
<Typography>آیا از حذف این آزمایش مطمئن هستید؟</Typography>
</DialogContent>
<DialogActions>
<Button
onClick={() => setOpenDeleteDialog(false)}
variant="outlined"
color="primary"
disabled={isSubmitting}
autoFocus
>
بستن
</Button>
<Button onClick={handleSubmit} variant="contained" color="error" disabled={isSubmitting}>
حذف
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default DeleteAzmayesh;

View File

@@ -0,0 +1,59 @@
import { Button, IconButton, useMediaQuery } from "@mui/material";
import useRequest from "@/lib/hooks/useRequest";
import { useEffect, useState } from "react";
import { GET_AZMAYESH_SAMPLE_FIELDS } from "@/core/utils/routes";
import { AddCircle } from "@mui/icons-material";
import { useTheme } from "@emotion/react";
import CandUSampleOfAzmayesh from "@/components/dashboard/azmayesh/RowActions/ShowSampleOfAzmayesh/Forms/CandUSampleOfAzmayesh";
const AddSampleToAzmayesh = ({ azmayeshTypeId, mutate, rowId }) => {
const requestServer = useRequest({ auth: true });
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [sampleInfo, setSampleInfo] = useState(null);
const [openAddSampleDialog, setOpenAddSampleDialog] = useState(false);
const [defaultValues, setDefaultValues] = useState({});
const handleGetFields = () => {
requestServer(`${GET_AZMAYESH_SAMPLE_FIELDS}/${azmayeshTypeId}`, "get")
.then((response) => {
setSampleInfo(response.data.data);
})
.catch(() => {})
.finally(() => {
setOpenAddSampleDialog(true);
});
};
useEffect(() => {
const initialValues = sampleInfo?.reduce((acc, item) => {
acc[item.id] = "";
return acc;
}, {});
setDefaultValues(initialValues);
}, [sampleInfo]);
return (
<>
{isMobile ? (
<IconButton aria-label="ایجاد نمونه جدید" color="primary" onClick={handleGetFields}>
<AddCircle sx={{ fontSize: "25px" }} />
</IconButton>
) : (
<Button variant="contained" color="primary" startIcon={<AddCircle />} onClick={handleGetFields}>
ثبت نمونه
</Button>
)}
<CandUSampleOfAzmayesh
openSampleDialog={openAddSampleDialog}
setOpenSampleDialog={setOpenAddSampleDialog}
sampleInfo={sampleInfo}
mutate={mutate}
rowId={rowId}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues}
/>
</>
);
};
export default AddSampleToAzmayesh;

View File

@@ -0,0 +1,82 @@
"use client";
import { Box, Chip, Divider, Grid, Stack, Typography } from "@mui/material";
import ScienceIcon from "@mui/icons-material/Science";
import LocationCityIcon from "@mui/icons-material/LocationCity";
const AzmayeshInfo = ({ rowData }) => {
return (
<Stack sx={{ p: 2, alignItems: "center" }}>
<Typography variant="body1" sx={{ fontWeight: 600, pb: 4 }}>
{rowData.project_name}
</Typography>
<Grid container spacing={2} sx={{ alignItems: "center", justifyContent: "space-around" }}>
<Grid item xs={12} sm={6} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<ScienceIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
نوع آزمایش
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{rowData.azmayesh_type_name}
</Typography>
</Grid>
<Grid item xs={12} sm={6} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<LocationCityIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
نام استان
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{rowData.province_name}
</Typography>
</Grid>
<Grid item xs={12} sm={6} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<LocationCityIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
شماره کار
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{rowData.work_number}
</Typography>
</Grid>
<Grid item xs={12} sm={6} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<LocationCityIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
شماره درخواست
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{rowData.request_number}
</Typography>
</Grid>
</Grid>
</Stack>
);
};
export default AzmayeshInfo;

View File

@@ -0,0 +1,74 @@
import { Button, Dialog, 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,
openSampleDialog,
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 (
<Dialog maxWidth="md" fullWidth open={openSampleDialog}>
<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>
</Dialog>
);
};
export default CandUSampleOfAzmayesh;

View File

@@ -0,0 +1,78 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Tooltip,
Typography,
} from "@mui/material";
import { useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import DeleteIcon from "@mui/icons-material/Delete";
import { DELETE_SAMPLE_LIST } from "@/core/utils/routes";
const DeleteSample = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
setIsSubmitting(true);
requestServer(`${DELETE_SAMPLE_LIST}/${rowId}`, "post")
.then(() => {
setOpenDeleteDialog(false);
mutate();
})
.catch(() => {})
.finally(() => {
setIsSubmitting(false);
});
};
return (
<>
<Tooltip title="حذف نمونه" arrow placement="right">
<IconButton
color="error"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={() => {
setOpenDeleteDialog(true);
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Dialog
maxWidth="sm"
open={openDeleteDialog}
PaperProps={{
sx: {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
},
}}
>
<DialogTitle>حذف نمونه</DialogTitle>
<DialogContent>
<Typography>آیا از حذف این نمونه مطمئن هستید؟</Typography>
</DialogContent>
<DialogActions>
<Button
onClick={() => setOpenDeleteDialog(false)}
variant="outlined"
color="primary"
disabled={isSubmitting}
autoFocus
>
بستن
</Button>
<Button onClick={handleSubmit} variant="contained" color="error" disabled={isSubmitting}>
حذف
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default DeleteSample;

View File

@@ -0,0 +1,60 @@
import { IconButton, Tooltip } from "@mui/material";
import useRequest from "@/lib/hooks/useRequest";
import { useEffect, useState } from "react";
import { GET_AZMAYESH_SAMPLE_FIELDS, GET_SAMPLE_LIST } from "@/core/utils/routes";
import CandUSampleOfAzmayesh from "@/components/dashboard/azmayesh/RowActions/ShowSampleOfAzmayesh/Forms/CandUSampleOfAzmayesh";
import EditIcon from "@mui/icons-material/Edit";
const UpdateSample = ({ azmayeshTypeId, mutate, rowId, azmayesh_id }) => {
const requestServer = useRequest({ auth: true });
const [sampleInfo, setSampleInfo] = useState(null);
const [openUpdateSampleDialog, setOpenUpdateSampleDialog] = useState(false);
const [defaultValues, setDefaultValues] = useState({});
const [sampleDetail, setSampleDetail] = useState({});
const handleGetFields = () => {
requestServer(`${GET_AZMAYESH_SAMPLE_FIELDS}/${azmayeshTypeId}`, "get")
.then((response) => {
setSampleInfo(response.data.data);
})
.catch(() => {});
requestServer(`${GET_SAMPLE_LIST}/${rowId}`, "get")
.then((response) => {
setSampleDetail(JSON.parse(response.data.data.data));
})
.catch(() => {})
.finally(() => {
setOpenUpdateSampleDialog(true);
});
};
useEffect(() => {
setDefaultValues(sampleDetail);
}, [sampleDetail]);
return (
<>
<Tooltip title="ویرایش نمونه" arrow placement="right">
<IconButton
color="success"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={handleGetFields}
>
<EditIcon />
</IconButton>
</Tooltip>
<CandUSampleOfAzmayesh
openSampleDialog={openUpdateSampleDialog}
setOpenSampleDialog={setOpenUpdateSampleDialog}
sampleInfo={sampleInfo}
mutate={mutate}
rowId={rowId}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues}
isUpdate={true}
azmayesh_id={azmayesh_id}
/>
</>
);
};
export default UpdateSample;

View File

@@ -0,0 +1,18 @@
import { Box } from "@mui/material";
import DeleteSample from "./DeleteSample";
import UpdateSample from "./UpdateSample";
const RowActions = ({ rowData, row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
<UpdateSample
mutate={mutate}
rowId={row.getValue("id")}
azmayeshTypeId={rowData.azmayesh_type_id}
azmayesh_id={rowData.id}
/>
<DeleteSample mutate={mutate} rowId={row.getValue("id")} />
</Box>
);
};
export default RowActions;

View File

@@ -0,0 +1,64 @@
import { useMemo } from "react";
import { Box } 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";
const ShowSampleList = ({ sampleInfo, rowData }) => {
const parsedData = (data) => {
return data.data.map((item) => ({
...JSON.parse(item.data),
id: item.id,
}));
};
const columns = useMemo(
() => [
{
accessorKey: "id",
header: "کد یکتا",
id: "id",
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
},
...sampleInfo.map((item) => ({
accessorKey: item.id.toString(),
header: item.name,
id: item.id.toString(),
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
})),
],
[sampleInfo]
);
const ToolbarWithRowData = (props) => <Toolbar rowData={rowData} {...props} />;
const RowActionsWithRowData = (props) => <RowActions rowData={rowData} {...props} />;
return (
<Box>
<AzmayeshInfo rowData={rowData} />
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
<DataTableWithAuth
table_title={"نمونه های ثبت شده"}
need_filter={false}
columns={columns}
table_url={`${GET_SAMPLE_LIST}/index/${rowData.id}`}
page_name={`azmayesh_${rowData.azmayesh_type_id}`}
table_name={"sample_list"}
TableToolbar={ToolbarWithRowData}
specific_data={parsedData}
enableRowActions
positionActionsColumn={"last"}
RowActions={RowActionsWithRowData}
/>
</Box>
</Box>
);
};
export default ShowSampleList;

View File

@@ -0,0 +1,10 @@
import AddSampleToAzmayesh from "./Actions/Create";
const Toolbar = ({ mutate, rowData }) => {
return (
<>
<AddSampleToAzmayesh mutate={mutate} azmayeshTypeId={rowData.azmayesh_type_id} rowId={rowData.id} />
</>
);
};
export default Toolbar;

View File

@@ -0,0 +1,46 @@
import { Button, Dialog, DialogActions, DialogContent, IconButton, Tooltip } from "@mui/material";
import AssignmentIcon from "@mui/icons-material/Assignment";
import { useState } from "react";
import ShowSampleList from "./ShowSampleList";
import { GET_AZMAYESH_SAMPLE_FIELDS } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
const ShowSampleOfAzmayesh = ({ rowData }) => {
const requestServer = useRequest({ auth: true });
const [openShowSampleDialog, setOpenShowSampleDialog] = useState(false);
const [sampleInfo, setSampleInfo] = useState(null);
const handleGetSampleFields = () => {
requestServer(`${GET_AZMAYESH_SAMPLE_FIELDS}/${rowData.azmayesh_type_id}`, "get")
.then((response) => {
setSampleInfo(response.data.data);
})
.catch(() => {})
.finally(() => {
setOpenShowSampleDialog(true);
});
};
return (
<>
<Tooltip title="نمایش نمونه های آزمایش" arrow placement="right">
<IconButton
color="primary"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={handleGetSampleFields}
>
<AssignmentIcon />
</IconButton>
</Tooltip>
<Dialog maxWidth="md" fullWidth open={openShowSampleDialog}>
<DialogContent>
<ShowSampleList rowData={rowData} sampleInfo={sampleInfo} />
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenShowSampleDialog(false)} variant="outlined" color="error">
بستن
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default ShowSampleOfAzmayesh;

View File

@@ -0,0 +1,15 @@
import { Box } from "@mui/material";
import DeleteAzmayesh from "./DeleteAzmayesh";
import ShowSampleOfAzmayesh from "@/components/dashboard/azmayesh/RowActions/ShowSampleOfAzmayesh";
import AzmayeshUpdate from "@/components/dashboard/azmayesh/RowActions/AzmayeshUpdate";
const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
<AzmayeshUpdate mutate={mutate} rowId={row.getValue("id")} />
<ShowSampleOfAzmayesh rowData={row.original} />
<DeleteAzmayesh mutate={mutate} rowId={row.getValue("id")} />
</Box>
);
};
export default RowActions;

View File

@@ -100,7 +100,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => {
value={provinces.find((province) => province.id === value) || null}
disablePortal
options={provinces}
getOptionLabel={(province) => province.name}
getOptionLabel={(province) => province.name_fa}
isOptionEqualToValue={(option, value) => option.id === value.id}
onChange={(event, newValue) => {
onChange(newValue ? newValue.id : "");

View File

@@ -0,0 +1,53 @@
import React from "react";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { faIR } from "@mui/x-date-pickers/locales";
import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers";
import { IconButton, InputAdornment } from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
const CustomDatePicker = ({ dateValue, setDateValue, placeholder = "انتخاب تاریخ", size = "small" }) => {
const handleDateChange = (newValue) => {
setDateValue(newValue);
};
return (
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
>
<MobileDatePicker
value={dateValue || null}
onChange={handleDateChange}
slotProps={{
textField: {
size: size,
placeholder: placeholder,
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size={size}
onClick={(event) => {
event.stopPropagation();
setDateValue(null);
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
},
}}
></MobileDatePicker>
</LocalizationProvider>
);
};
export default CustomDatePicker;

View File

@@ -22,6 +22,7 @@ const DataTable_Main = (props) => {
TableToolbar,
table_title,
RowActions,
specific_data,
} = props;
const flatColumns = flattenArrayOfObjects(columns, "columns");
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
@@ -50,7 +51,11 @@ const DataTable_Main = (props) => {
const fetcher = async (url) => {
try {
const response = await request(url);
return response.data;
if (specific_data) {
return specific_data(response.data);
} else {
return response.data?.data;
}
} catch (error) {
throw error;
}
@@ -66,7 +71,7 @@ const DataTable_Main = (props) => {
const table = useMaterialReactTable({
localization: FA_DATATABLE_LOCALIZATION,
columns,
data: data?.data ?? [],
data: data ?? [],
initialState: {
density: "compact",
columnVisibility: flattenHideData,

View File

@@ -81,7 +81,7 @@ function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name,
<Drawer
open={drawerState}
onClose={() => setDrawerState(false)}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%" }}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }}
>
<FilterHeader setDrawerState={setDrawerState} />
{Object.keys(initialValues).length > 0 && (

View File

@@ -30,7 +30,7 @@ function HideBody({ columns, drawerState, setDrawerState }) {
<Drawer
open={drawerState}
onClose={() => setDrawerState(false)}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%" }}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }}
>
<HideHeader setDrawerState={setDrawerState} />
<HideOrShowAll hideData={hideData} setHideData={setHideData} />

View File

@@ -0,0 +1,35 @@
import { FormControl } from "@mui/material";
import CustomDatePicker from "@/core/components/CustomDatePicker";
import moment from "jalali-moment";
import { useEffect, useState } from "react";
const DateType = ({ itemInfo, defaultValues, setDefaultValues }) => {
const [dateValue, setDateValue] = useState(null);
const handleDateChange = (newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date).locale("fa").format("YYYY/MM/DD");
setDefaultValues((prev) => ({
...prev,
[itemInfo.id]: newValue ? formattedDate : "",
}));
setDateValue(moment(date).format("YYYY/MM/DD"));
};
useEffect(() => {
if (defaultValues[itemInfo.id]) {
setDateValue(moment.from(defaultValues[itemInfo.id], "fa", "YYYY/MM/DD"));
}
}, []);
return (
<FormControl fullWidth size="small" variant="outlined">
<CustomDatePicker
dateValue={dateValue ? new Date(dateValue) : null}
setDateValue={handleDateChange}
placeholder={itemInfo.name}
/>
</FormControl>
);
};
export default DateType;

View File

@@ -0,0 +1,30 @@
import { FormControl, InputLabel, OutlinedInput } from "@mui/material";
import React from "react";
const InputType = ({ itemInfo, defaultValues, setDefaultValues }) => {
const handleChange = (event) => {
const { value } = event.target;
setDefaultValues((prevValues) => ({
...prevValues,
[itemInfo.id]: value,
}));
};
return (
<FormControl size="small" fullWidth variant="outlined">
<InputLabel htmlFor={itemInfo.id}>{itemInfo.name}</InputLabel>
<OutlinedInput
id={itemInfo.id}
label={itemInfo.name}
autoComplete="off"
size="small"
fullWidth
type="text"
onChange={handleChange}
value={defaultValues[itemInfo.id] || ""}
/>
</FormControl>
);
};
export default InputType;

View File

@@ -0,0 +1,34 @@
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import React from "react";
const SelectType = ({ itemInfo, defaultValues, setDefaultValues }) => {
const handleChange = (event) => {
const newValue = event.target.value;
setDefaultValues((prev) => ({
...prev,
[itemInfo.id]: newValue,
}));
};
return (
<FormControl size="small" fullWidth variant="outlined">
<InputLabel id={itemInfo.id}>{itemInfo.name}</InputLabel>
<Select
labelId={itemInfo.id}
id={itemInfo.id}
value={defaultValues[itemInfo.id] || ""}
label={itemInfo.name}
onChange={handleChange}
>
<MenuItem value="">{itemInfo.name}</MenuItem>
{itemInfo.option?.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
</FormControl>
);
};
export default SelectType;

View File

@@ -0,0 +1,33 @@
import InputType from "./InputType";
import SelectType from "./SelectType";
import DateType from "./DateType";
import { Grid } from "@mui/material";
const componentMap = {
input: InputType,
select: SelectType,
date: DateType,
};
const FormMaker = ({ formInfo, defaultValues, setDefaultValues }) => {
return (
<Grid container spacing={1}>
{formInfo?.map((itemInfo) => {
const Component = componentMap[itemInfo.type] || null;
return (
<Grid item xs={12} sm={6} md={4} key={itemInfo.id}>
{Component ? (
<Component
itemInfo={itemInfo}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues}
/>
) : null}
</Grid>
);
})}
</Grid>
);
};
export default FormMaker;

View File

@@ -9,9 +9,16 @@ export const GET_SIDEBAR_BADGE_ROUTE = api + "/v2/activity_statistics";
export const REFER_ADMIN_PROVINCE = "/v3/api/fake-submit";
export const REFER_ADMIN_CITY = "/v3/api/fake-submit";
export const GET_CITY_LISTS = "/v3/api/fake-cities";
export const GET_PROVINCE_LISTS = "/v3/api/fake-provinces";
export const GET_PROVINCE_LISTS = api + "/public/contents/provinces";
export const GET_PREV_STATE_OPINION = "/v3/api/fake-prev-state-opinion";
export const SUBMIT_ROAD_SAFETY_FORM = "/v3/api/fake-submit";
export const POST_AZMAYESH = api + "/api/v3/azmayeshes/store";
export const CREATE_AZMAYESH = api + "/api/v3/azmayeshes/store";
export const UPDATE_AZMAYESH = api + "/api/v3/azmayeshes/update";
export const GET_AZMAYESH_TYPE_LIST = api + "/api/v3/azmayesh_types";
export const GET_AZMAYESH_LIST = api + "/api/v3/azmayeshes";
export const DELETE_AZMAYESH = api + "/api/v3/azmayeshes/delete";
export const GET_AZMAYESH_SAMPLE_FIELDS = api + "/api/v3/azmayesh_types/fields";
export const ADD_SAMPLE_TO_AZMAYESH = api + "/api/v3/azmayesh_samples/store";
export const UPDATE_SAMPLE_OF_AZMAYESH = api + "/api/v3/azmayesh_samples/update";
export const GET_SAMPLE_LIST = api + "/api/v3/azmayesh_samples";
export const DELETE_SAMPLE_LIST = api + "/api/v3/azmayesh_samples/delete";

View File

@@ -11,18 +11,18 @@ const useProvinces = () => {
useEffect(() => {
const fetchCities = async () => {
try {
const response = await requestServer(`${GET_PROVINCE_LISTS}`); // Fetch the cities from the API
setProvinces(response.data.data); // Set the cities data
setLoadingProvinces(false); // Set loading to false after successful fetch
const response = await requestServer(`${GET_PROVINCE_LISTS}`);
setProvinces(response.data.data);
setLoadingProvinces(false);
} catch (e) {
console.error("Error fetching cities:", e);
console.error("Error fetching provinces:", e);
setErrorProvinces(e);
setLoadingProvinces(false);
}
};
fetchCities(); // Call the fetch function
}, []); // Empty dependency array to ensure it only runs once
fetchCities();
}, []);
return { provinces, loadingProvinces, errorProvinces };
};