stable changes for adding to product but working on making it better...

This commit is contained in:
2024-11-18 08:50:31 +00:00
committed by AmirHossein Mahmoodi
parent 9cff5ea940
commit dd32aa6a87
22 changed files with 890 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
import TestPage from "@/components/dashboard/azmayesh";
import AzmayeshPage from "@/components/dashboard/azmayesh";
const Page = () => {
return <TestPage />;
return <AzmayeshPage />;
};
export default Page;

View File

@@ -0,0 +1,7 @@
import AzmayeshTypePage from "@/components/dashboard/azmayeshType";
const Page = () => {
return <AzmayeshTypePage />;
};
export default Page;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" width="647.63626" height="632.17383" viewBox="0 0 647.63626 632.17383" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M687.3279,276.08691H512.81813a15.01828,15.01828,0,0,0-15,15v387.85l-2,.61005-42.81006,13.11a8.00676,8.00676,0,0,1-9.98974-5.31L315.678,271.39691a8.00313,8.00313,0,0,1,5.31006-9.99l65.97022-20.2,191.25-58.54,65.96972-20.2a7.98927,7.98927,0,0,1,9.99024,5.3l32.5498,106.32Z" transform="translate(-276.18187 -133.91309)" fill="#f2f2f2"/><path d="M725.408,274.08691l-39.23-128.14a16.99368,16.99368,0,0,0-21.23-11.28l-92.75,28.39L380.95827,221.60693l-92.75,28.4a17.0152,17.0152,0,0,0-11.28028,21.23l134.08008,437.93a17.02661,17.02661,0,0,0,16.26026,12.03,16.78926,16.78926,0,0,0,4.96972-.75l63.58008-19.46,2-.62v-2.09l-2,.61-64.16992,19.65a15.01489,15.01489,0,0,1-18.73-9.95l-134.06983-437.94a14.97935,14.97935,0,0,1,9.94971-18.73l92.75-28.4,191.24024-58.54,92.75-28.4a15.15551,15.15551,0,0,1,4.40966-.66,15.01461,15.01461,0,0,1,14.32032,10.61l39.0498,127.56.62012,2h2.08008Z" transform="translate(-276.18187 -133.91309)" fill="#3f3d56"/><path d="M398.86279,261.73389a9.0157,9.0157,0,0,1-8.61133-6.3667l-12.88037-42.07178a8.99884,8.99884,0,0,1,5.9712-11.24023l175.939-53.86377a9.00867,9.00867,0,0,1,11.24072,5.9707l12.88037,42.07227a9.01029,9.01029,0,0,1-5.9707,11.24072L401.49219,261.33887A8.976,8.976,0,0,1,398.86279,261.73389Z" transform="translate(-276.18187 -133.91309)" fill="#2070af"/><circle cx="190.15351" cy="24.95465" r="20" fill="#2070af"/><circle cx="190.15351" cy="24.95465" r="12.66462" fill="#fff"/><path d="M878.81836,716.08691h-338a8.50981,8.50981,0,0,1-8.5-8.5v-405a8.50951,8.50951,0,0,1,8.5-8.5h338a8.50982,8.50982,0,0,1,8.5,8.5v405A8.51013,8.51013,0,0,1,878.81836,716.08691Z" transform="translate(-276.18187 -133.91309)" fill="#e6e6e6"/><path d="M723.31813,274.08691h-210.5a17.02411,17.02411,0,0,0-17,17v407.8l2-.61v-407.19a15.01828,15.01828,0,0,1,15-15H723.93825Zm183.5,0h-394a17.02411,17.02411,0,0,0-17,17v458a17.0241,17.0241,0,0,0,17,17h394a17.0241,17.0241,0,0,0,17-17v-458A17.02411,17.02411,0,0,0,906.81813,274.08691Zm15,475a15.01828,15.01828,0,0,1-15,15h-394a15.01828,15.01828,0,0,1-15-15v-458a15.01828,15.01828,0,0,1,15-15h394a15.01828,15.01828,0,0,1,15,15Z" transform="translate(-276.18187 -133.91309)" fill="#3f3d56"/><path d="M801.81836,318.08691h-184a9.01015,9.01015,0,0,1-9-9v-44a9.01016,9.01016,0,0,1,9-9h184a9.01016,9.01016,0,0,1,9,9v44A9.01015,9.01015,0,0,1,801.81836,318.08691Z" transform="translate(-276.18187 -133.91309)" fill="#2070af"/><circle cx="433.63626" cy="105.17383" r="20" fill="#2070af"/><circle cx="433.63626" cy="105.17383" r="12.18187" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -18,7 +18,7 @@ const AzmayeshCreate = ({ mutate }) => {
return (
<>
{isMobile ? (
<IconButton aria-label="ایجاد آزمایش جدید" color="primary" onClick={handleOpen}>
<IconButton aria-label="ثبت آزمایش جدید" color="primary" onClick={handleOpen}>
<AddCircle sx={{ fontSize: "25px" }} />
</IconButton>
) : (

View File

@@ -127,7 +127,7 @@ const AzmayeshList = () => {
<>
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
<DataTableWithAuth
table_title={"لیست آزمایش ها"}
table_title={"لیست آزمایشات"}
need_filter={false}
columns={columns}
table_url={GET_AZMAYESH_LIST}

View File

@@ -4,7 +4,7 @@ import PageTitle from "@/core/components/PageTitle";
import { Stack } from "@mui/material";
import AzmayeshList from "./AzmayeshList";
const TestPage = () => {
const AzmayeshPage = () => {
return (
<Stack spacing={1}>
<PageTitle title={"آزمایشات"} />
@@ -12,4 +12,4 @@ const TestPage = () => {
</Stack>
);
};
export default TestPage;
export default AzmayeshPage;

View File

@@ -0,0 +1,33 @@
"use client";
import { Button, IconButton, useMediaQuery } from "@mui/material";
import { useTheme } from "@emotion/react";
import { useState } from "react";
import { AddCircle } from "@mui/icons-material";
import CandUAzmayeshType from "@/components/dashboard/azmayeshType/Forms/CandUAzmayeshType";
const AzmayeshTypeCreate = ({ mutate }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
return (
<>
{isMobile ? (
<IconButton aria-label="ثبت نوع آزمایش جدید" color="primary" onClick={handleOpen}>
<AddCircle sx={{ fontSize: "25px" }} />
</IconButton>
) : (
<Button variant="contained" color="primary" startIcon={<AddCircle />} onClick={handleOpen}>
ثبت نوع آزمایش
</Button>
)}
{open && <CandUAzmayeshType open={open} setOpen={setOpen} mutate={mutate} />}
</>
);
};
export default AzmayeshTypeCreate;

View File

@@ -0,0 +1,65 @@
"use client";
import { useMemo } from "react";
import { Box, Typography } from "@mui/material";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import Toolbar from "./Toolbar";
import { GET_AZMAYESH_TYPE_LIST_TABLE } from "@/core/utils/routes";
import moment from "jalali-moment";
import RowActions from "./RowActions";
const AzmayeshTypeList = () => {
const columns = useMemo(
() => [
{
accessorKey: "id",
header: "کد یکتا",
id: "id",
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
},
{
accessorKey: "name",
header: "عنوان",
id: "name",
enableColumnFilter: false,
datatype: "text",
filterFn: "notEquals",
},
{
accessorKey: "created_at",
header: "تاریخ ثبت",
id: "created_at",
enableColumnFilter: false,
datatype: "date",
filterFn: "equals",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">
{renderedCellValue ? moment(renderedCellValue).locale("fa").format("HH:mm | YYYY/MM/DD") : "-"}
</Typography>
),
},
],
[]
);
return (
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
<DataTableWithAuth
table_title={"لیست انواع آزمایشات"}
need_filter={false}
columns={columns}
sorting={[{ id: "id", desc: true }]}
table_url={GET_AZMAYESH_TYPE_LIST_TABLE}
page_name={"azmayesh_type"}
table_name={"azmayeshTypeList"}
TableToolbar={Toolbar}
enableRowActions
positionActionsColumn={"last"}
RowActions={RowActions}
/>
</Box>
);
};
export default AzmayeshTypeList;

View File

@@ -0,0 +1,181 @@
"use client";
import React, { useEffect, useState } from "react";
import {
Button,
Chip,
FormControl,
FormHelperText,
Grid,
InputAdornment,
InputLabel,
MenuItem,
OutlinedInput,
Select,
Typography,
} from "@mui/material";
import ExtensionIcon from "@mui/icons-material/Extension";
import SelectItemBox from "@/components/dashboard/azmayeshType/Forms/CandUAzmayeshType/SelectItemBox";
import KeyboardIcon from "@mui/icons-material/Keyboard";
import TextFieldsIcon from "@mui/icons-material/TextFields";
import ViewListIcon from "@mui/icons-material/ViewList";
import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
const fieldTypeList = [
{ value: "input", label: "متن", icon: <TextFieldsIcon /> },
{ value: "select", label: "انتخابی", icon: <ViewListIcon /> },
{ value: "date", label: "تاریخ", icon: <CalendarMonthIcon /> },
];
const AzmayeshFields = ({ errors, selectedListItem, register, setValue, fields, watch }) => {
const watchedFields = watch("fields");
const [isSelectType, setIsSelectType] = useState(false);
const [openSelectItem, setOpenSelectItem] = useState(false);
const [typeIcon, setTypeIcon] = useState(<KeyboardIcon />);
useEffect(() => {
const selectedIcon = fieldTypeList.find((option) => option.value === watchedFields?.[selectedListItem]?.type)
?.icon || <KeyboardIcon />;
setTypeIcon(selectedIcon);
setIsSelectType(watchedFields[selectedListItem]?.type === "select");
}, [watchedFields, selectedListItem]);
return (
<Grid container>
<Grid xs={12} sm={6} sx={{ px: { xs: 0, sm: 1 }, py: 1 }}>
<FormControl
error={!!errors.fields?.[selectedListItem]?.type}
size="small"
fullWidth
variant="outlined"
>
<InputLabel htmlFor={`fields.${selectedListItem}.type`}>نوع</InputLabel>
<Select
id={`fields.${selectedListItem}.type`}
label="نوع"
autoComplete="off"
{...register(`fields.${selectedListItem}.type`)}
size="small"
fullWidth
value={watchedFields?.[selectedListItem]?.type || ""}
onChange={(event) => {
const selectedValue = event.target.value;
const selectedIcon = fieldTypeList.find((option) => option.value === selectedValue)
?.icon || <KeyboardIcon />;
setValue(`fields.${selectedListItem}.type`, selectedValue);
setIsSelectType(selectedValue === "select");
setTypeIcon(selectedIcon);
}}
startAdornment={<InputAdornment position="start">{typeIcon}</InputAdornment>}
>
<MenuItem value="">نوع</MenuItem>
{fieldTypeList.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
<FormHelperText>{errors.fields?.[selectedListItem]?.type?.message}</FormHelperText>
</FormControl>
</Grid>
{isSelectType && (
<Grid
xs={12}
sm={6}
sx={{
px: { xs: 0, sm: 1 },
py: 1,
mb: 0.5,
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "space-around",
position: "relative",
}}
>
<Chip
sx={{
position: "absolute",
top: "-6px",
right: "-6px",
zIndex: "1",
}}
color="error"
label={watchedFields?.[selectedListItem].options.length}
/>
<Button
variant="outlined"
color="success"
sx={{ width: "100%", height: "37px" }}
startIcon={<ExtensionIcon />}
onClick={() => setOpenSelectItem(true)}
>
افزودن آیتم نوع انتخابی
</Button>
<Typography variant="caption" color="error">
{errors.fields?.[selectedListItem]?.options?.message}
</Typography>
{openSelectItem && (
<SelectItemBox
open={openSelectItem}
setOpen={setOpenSelectItem}
fields={fields}
setValue={setValue}
index={selectedListItem}
watch={watch}
/>
)}
</Grid>
)}
<Grid xs={12} sm={6} sx={{ px: { xs: 0, sm: 1 }, py: 1 }}>
<FormControl
error={!!errors.fields?.[selectedListItem]?.name}
size="small"
fullWidth
variant="outlined"
>
<InputLabel htmlFor={`fields[${selectedListItem}].name`}>نام فیلد</InputLabel>
<OutlinedInput
id={`fields[${selectedListItem}].name`}
label="نام فیلد"
autoComplete="off"
{...register(`fields[${selectedListItem}].name`)}
value={watchedFields?.[selectedListItem]?.name || ""}
onInput={(event) => setValue(`fields.${selectedListItem}.name`, event.target.value)}
size="small"
fullWidth
type="text"
/>
<FormHelperText>
{errors.fields?.[selectedListItem]?.name ? errors.fields[selectedListItem].name.message : null}
</FormHelperText>
</FormControl>
</Grid>
<Grid xs={12} sm={6} sx={{ px: { xs: 0, sm: 1 }, py: 1 }}>
<FormControl
error={!!errors.fields?.[selectedListItem]?.unit}
size="small"
fullWidth
variant="outlined"
>
<InputLabel htmlFor={`fields[${selectedListItem}].unit`}>واحد اندازه گیری</InputLabel>
<OutlinedInput
id={`fields[${selectedListItem}].unit`}
label="واحد اندازه گیری"
autoComplete="off"
{...register(`fields[${selectedListItem}].unit`)}
value={watchedFields?.[selectedListItem]?.unit || ""}
onChange={(event) => setValue(`fields.${selectedListItem}.unit`, event.target.value)}
size="small"
fullWidth
type="text"
/>
<FormHelperText>
{errors.fields?.[selectedListItem]?.unit ? errors.fields[selectedListItem].unit.message : null}
</FormHelperText>
</FormControl>
</Grid>
</Grid>
);
};
export default AzmayeshFields;

View File

@@ -0,0 +1,77 @@
"use client";
import { Box, IconButton, ListItem, Stack, Typography } from "@mui/material";
import React from "react";
import DeleteIcon from "@mui/icons-material/Delete";
import SettingsIcon from "@mui/icons-material/Settings";
import TextFieldsIcon from "@mui/icons-material/TextFields";
import ViewListIcon from "@mui/icons-material/ViewList";
import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
const fieldsType = {
input: "متن",
date: "تاریخ",
select: "انتخابی",
};
const fieldsTypeIcon = {
input: <TextFieldsIcon sx={{ fontSize: "18px" }} />,
date: <CalendarMonthIcon sx={{ fontSize: "18px" }} />,
select: <ViewListIcon sx={{ fontSize: "18px" }} />,
};
const ListItems = ({ index, handleRemove, selectedListItem, setSelectedListItem, watch }) => {
const watchedFields = watch("fields");
return (
<ListItem
sx={{
transition: "all 0.3s",
boxShadow:
selectedListItem === index
? "rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px"
: "rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px",
borderRadius: 2,
mb: 1.5,
px: 1,
py: 0.7,
backgroundColor: selectedListItem === index ? "#6ea1714d" : "#ebebeb7a",
"&:last-child": {
mb: 0,
},
}}
disablePadding
secondaryAction={
<Box sx={{ display: "flex", alignItems: "center" }}>
<IconButton
edge="start"
aria-label="edit"
color="primary"
onClick={() => setSelectedListItem(index)}
>
<SettingsIcon />
</IconButton>
<IconButton edge="end" aria-label="delete" color="error" onClick={() => handleRemove(index)}>
<DeleteIcon />
</IconButton>
</Box>
}
>
<Stack sx={{ width: "100%" }}>
<Typography
sx={{ color: watchedFields[index].name !== "" ? "" : "error.main" }}
variant="body1"
fontWeight={500}
noWrap
>
{watchedFields[index].name !== "" ? watchedFields[index].name : "نام تعیین نشده"}
</Typography>
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
{fieldsTypeIcon[watchedFields[index].type] || null}
<Typography sx={{ color: watchedFields[index].type !== "" ? "" : "error.main" }} variant="caption">
{watchedFields[index].type !== "" ? fieldsType[watchedFields[index].type] : "واحد تعیین نشده"}
</Typography>
</Box>
</Stack>
</ListItem>
);
};
export default ListItems;

View File

@@ -0,0 +1,108 @@
import {
Box,
Button,
Chip,
Dialog,
DialogActions,
DialogContent,
Divider,
IconButton,
List,
ListItem,
ListItemText,
TextField,
Typography,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import React, { useState } from "react";
const SelectItemBox = ({ open, setOpen, setValue, index, watch }) => {
const watchedFields = watch("fields");
const [itemValue, setItemValue] = useState("");
const [listArr, setListArr] = useState(watchedFields?.[index]?.options);
const addToList = () => {
if (itemValue) {
setListArr([...listArr, itemValue]);
setItemValue("");
}
};
const removeFromList = (index) => {
setListArr(listArr.filter((_, i) => i !== index));
};
const saveAndCloseList = () => {
setValue(`fields.${index}.options`, listArr);
closeModal();
};
const closeModal = () => {
setOpen(false);
};
return (
<Dialog open={open} fullWidth={true} maxWidth={"xs"} onClose={closeModal}>
<DialogContent>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 2 }}>
<TextField
fullWidth
size="small"
id="outlined-controlled"
label="نام آیتم را وارد کنید"
value={itemValue}
onChange={(event) => {
setItemValue(event.target.value);
}}
/>
<Button variant="contained" color="primary" disabled={!itemValue} onClick={addToList}>
افزودن
</Button>
</Box>
<Box sx={{ borderRadius: "5px", mt: 2 }}>
<Divider>
<Chip label="لیست آیتم ها" />
</Divider>
<List>
{listArr.length !== 0 ? (
listArr.map((item, index) => (
<ListItem
key={index}
sx={{
boxShadow:
"rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px",
my: 1,
borderRadius: "5px",
}}
secondaryAction={
<IconButton
edge="end"
aria-label="delete"
color="error"
onClick={() => removeFromList(index)}
>
<DeleteIcon />
</IconButton>
}
>
<ListItemText primary={item} />
</ListItem>
))
) : (
<Typography variant="h6" sx={{ textAlign: "center", letterSpacing: "1px" }}>
هنوز آیتمی ثبت نشده است
</Typography>
)}
</List>
</Box>
</DialogContent>
<DialogActions>
<Button variant="outlined" color="primary" onClick={saveAndCloseList}>
ذخیره لیست
</Button>
</DialogActions>
</Dialog>
);
};
export default SelectItemBox;

View File

@@ -0,0 +1,224 @@
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));
};
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>
);
};
export default CandUAzmayeshType;

View File

@@ -0,0 +1,78 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Tooltip,
Typography,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import useRequest from "@/lib/hooks/useRequest";
import { useState } from "react";
import { DELETE_AZMAYESH_TYPE_LIST } from "@/core/utils/routes";
const DeleteAzmayeshType = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
setIsSubmitting(true);
requestServer(`${DELETE_AZMAYESH_TYPE_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 DeleteAzmayeshType;

View File

@@ -0,0 +1,48 @@
import { IconButton, Tooltip } from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import { GET_AZMAYESH_TYPE_LIST_TABLE } from "@/core/utils/routes";
import { useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import CandUAzmayeshType from "@/components/dashboard/azmayeshType/Forms/CandUAzmayeshType";
const UpdateAzmayeshType = ({ rowId, mutate }) => {
const requestServer = useRequest({ auth: true });
const [openUpdateDialog, setOpenUpdateDialog] = useState(false);
const [updateInfo, setUpdateInfo] = useState(null);
const handleGetInfo = () => {
requestServer(`${GET_AZMAYESH_TYPE_LIST_TABLE}/${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 && (
<CandUAzmayeshType
open={openUpdateDialog}
setOpen={setOpenUpdateDialog}
mutate={mutate}
updateInfo={updateInfo}
rowId={rowId}
/>
)}
</>
);
};
export default UpdateAzmayeshType;

View File

@@ -0,0 +1,13 @@
import { Box } from "@mui/material";
import UpdateAzmayeshType from "./UpdateAzmayeshType";
import DeleteAzmayeshType from "./DeleteAzmayeshType";
const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
<UpdateAzmayeshType rowId={row.getValue("id")} mutate={mutate} />
<DeleteAzmayeshType rowId={row.getValue("id")} mutate={mutate} />
</Box>
);
};
export default RowActions;

View File

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

View File

@@ -0,0 +1,15 @@
"use client";
import PageTitle from "@/core/components/PageTitle";
import { Stack } from "@mui/material";
import AzmayeshTypeList from "./AzmayeshTypeList";
const AzmayeshTypePage = () => {
return (
<Stack spacing={1}>
<PageTitle title={"نوع آزمایشات"} />
<AzmayeshTypeList />
</Stack>
);
};
export default AzmayeshTypePage;

View File

@@ -26,6 +26,7 @@ const DataTable_Main = (props) => {
} = props;
const flatColumns = flattenArrayOfObjects(columns, "columns");
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
const [totalRowCount, setTotalRowCount] = useState(0);
const flattenHideData = flattenObjectOfObjects(hideData);
const onSortingChange = (event) => {
setSortData(event);
@@ -51,6 +52,7 @@ const DataTable_Main = (props) => {
const fetcher = async (url) => {
try {
const response = await request(url);
setTotalRowCount(response.data?.meta?.totalRowCount);
if (specific_data) {
return specific_data(response.data);
} else {
@@ -112,6 +114,7 @@ const DataTable_Main = (props) => {
manualSorting: true,
onPaginationChange: setPagination,
onSortingChange: onSortingChange,
rowCount: totalRowCount,
renderTopToolbarCustomActions: ({ table }) => <>{TableToolbar && <TableToolbar mutate={mutate} />}</>,
renderRowActions: ({ row }) => <RowActions row={row} mutate={mutate} />,
...props,

View File

@@ -8,6 +8,7 @@ const DataTable = (props) => {
page_name={props.page_name}
table_name={props.table_name}
columns={props.columns}
initialSort={props.sorting}
>
<DataTable_Main {...props} />
</DataTableProvider>

View File

@@ -19,6 +19,7 @@ import ReportIcon from "@mui/icons-material/Report";
import AssignmentLateIcon from "@mui/icons-material/AssignmentLate";
import LineAxisIcon from "@mui/icons-material/LineAxis";
import ScienceIcon from "@mui/icons-material/Science";
import BiotechIcon from "@mui/icons-material/Biotech";
export const pageMenu = [
{
@@ -482,6 +483,14 @@ export const pageMenu = [
type: "page",
route: "/dashboard/azmayesh",
icon: <ScienceIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"],
permissions: ["azmayesh-management"],
},
{
id: "azmayeshType",
label: "نوع آزمایشات",
type: "page",
route: "/dashboard/azmayesh_type",
icon: <BiotechIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["azmayesh-type-management"],
},
];

View File

@@ -14,7 +14,8 @@ export const GET_PREV_STATE_OPINION = "/v3/api/fake-prev-state-opinion";
export const SUBMIT_ROAD_SAFETY_FORM = "/v3/api/fake-submit";
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_TYPE_LIST = api + "/api/v3/azmayesh_types/list";
export const GET_AZMAYESH_TYPE_LIST_TABLE = 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";
@@ -22,3 +23,6 @@ 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";
export const DELETE_AZMAYESH_TYPE_LIST = api + "/api/v3/azmayesh_types/delete";
export const ADD_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/store";
export const UPDATE_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/update";

View File

@@ -7,14 +7,14 @@ import { LinearProgress } from "@mui/material";
export const DataTableContext = createContext();
const DataTableProvider = ({ children, user_id, page_name, table_name, columns }) => {
const DataTableProvider = ({ children, user_id, page_name, table_name, columns, initialSort = [] }) => {
const { settingStore } = useTableSetting();
const [isInitStates, setInitStates] = useState(false);
const flatColumns = flattenArrayOfObjects(columns, "columns");
const [initFilter, setInitFilter] = useState({});
const [filterData, setFilterData] = useState({});
const [initSort, setInitSort] = useState([]);
const [sortData, setSortData] = useState([]);
const [initSort, setInitSort] = useState(initialSort || []);
const [sortData, setSortData] = useState(initialSort || []);
const [initHide, setInitHide] = useState({});
const [hideData, setHideData] = useState({});
@@ -51,8 +51,8 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns }
}, {});
setFilterData(filterValues);
setInitFilter(filterValues);
setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []);
setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []);
setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || initialSort);
setInitStates(true);
}, [settingStore, isInitStates]);