Feature/refahi register

This commit is contained in:
2025-02-05 11:50:43 +00:00
committed by AmirHossein Mahmoodi
parent 3bcedb43a2
commit 477c444340
38 changed files with 5407 additions and 232 deletions

View File

@@ -0,0 +1,138 @@
import { FormControl, FormHelperText } from "@mui/material";
import Button from "@mui/material/Button";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { faIR } from "@mui/x-date-pickers/locales";
import moment from "jalali-moment";
import { useState, useMemo } from "react";
function DynamicButtonField(props) {
const {
label,
id,
disabled,
InputProps: { ref } = {},
inputProps: { "aria-label": ariaLabel } = {},
error,
selectedDate,
buttonLabel,
onClick,
} = props;
return (
<Button
sx={{ width: "100%", padding: 0.8 }}
variant="outlined"
color={error ? "error" : "primary"}
id={id}
disabled={disabled}
ref={ref}
aria-label={ariaLabel}
onClick={onClick}
>
{selectedDate ? `${buttonLabel} : ${selectedDate}` : buttonLabel ? buttonLabel : label}
</Button>
);
}
function DynamicDatePicker({
formik,
fieldName,
label,
disabled,
minDate,
maxDate,
errorText,
helperText,
buttonLabel,
onChange,
}) {
const [open, setOpen] = useState(false);
const computedMinMaxDates = useMemo(() => {
const today = moment();
const min = minDate ? moment(minDate, "jYYYY-jMM-jDD").toDate() : null;
const max = maxDate ? moment(maxDate, "jYYYY-jMM-jDD").toDate() : null;
return { min, max };
}, [minDate, maxDate]);
const selectedDate = useMemo(() => {
if (formik.values[fieldName]) {
return moment(formik.values[fieldName]).locale("fa").format("YYYY/MM/DD");
}
return null;
}, [formik.values, fieldName]);
return (
<DatePicker
slots={{ field: DynamicButtonField }}
slotProps={{
field: {
setOpen,
selectedDate,
buttonLabel,
error: formik.touched[fieldName] && Boolean(formik.errors[fieldName]),
helperText: helperText || formik.errors[fieldName],
onClick: () => setOpen((prev) => !prev),
},
}}
label={label}
disabled={disabled}
disableFuture
open={open}
minDate={computedMinMaxDates.min}
maxDate={computedMinMaxDates.max}
value={formik.values[fieldName] ? formik.values[fieldName].toDate() : null}
onChange={(newValue) => {
const formattedValue = moment(newValue);
formik.setFieldValue(fieldName, formattedValue);
onChange?.(formattedValue);
}}
onClose={() => setOpen(false)}
onOpen={() => setOpen(true)}
/>
);
}
export default function PickerWithDynamicField({
formik,
fieldName,
label,
disabled = false,
minDate,
maxDate,
buttonLabel,
errorText,
helperText,
}) {
return (
<FormControl variant="outlined" fullWidth size="small">
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={{
...faIR.components.MuiLocalizationProvider.defaultProps.localeText,
okButtonLabel: "تایید",
}}
>
<DynamicDatePicker
onChange={(newValue) => {
formik.setFieldValue(fieldName, moment(newValue));
}}
formik={formik}
fieldName={fieldName}
label={label}
disabled={disabled}
minDate={minDate}
maxDate={maxDate}
buttonLabel={buttonLabel}
errorText={errorText}
helperText={helperText}
/>
</LocalizationProvider>
<FormHelperText error={Boolean(formik.errors[fieldName])}>
{formik.touched[fieldName] && formik.errors[fieldName]}
</FormHelperText>
</FormControl>
);
}

View File

@@ -24,7 +24,8 @@ function ButtonField(props) {
sx={{ width: "100%", padding: 0.8 }}
variant="outlined"
color={
props.slotProps.formik.touched.company_date && Boolean(props.slotProps.formik.errors.company_date)
props.slotProps.formik.touched.company_register_date &&
Boolean(props.slotProps.formik.errors.company_register_date)
? "error"
: "primary"
}
@@ -50,7 +51,7 @@ function ButtonDatePicker(props) {
formik: props.formik,
field: { setOpen },
textField: {
helperText: props.formik.errors.company_date ? `${t("company_date")}` : null,
helperText: props.formik.errors.company_register_date ? `${t("company_date")}` : null,
},
}}
{...props}
@@ -74,18 +75,24 @@ const LegalPersonDatePicker = ({ formik, disabled }) => {
>
<ButtonDatePicker
label={
formik.values.company_date !== "" &&
formik.values.company_date?.locale("fa").format("YYYY/MM/DD")
formik.values.company_register_date !== "" &&
formik.values.company_register_date?.locale("fa").format("YYYY/MM/DD")
}
value={
formik.values.company_register_date !== ""
? formik.values.company_register_date?.toDate()
: null
}
value={formik.values.company_date !== "" ? formik.values.company_date?.toDate() : null}
formik={formik}
disabled={disabled}
onChange={(newValue) => {
formik.setFieldValue("company_date", moment(newValue));
formik.setFieldValue("company_register_date", moment(newValue));
}}
/>
</LocalizationProvider>
<FormHelperText>{formik.touched.company_date && formik.errors.company_date}</FormHelperText>
<FormHelperText>
{formik.touched.company_register_date && formik.errors.company_register_date}
</FormHelperText>
</FormControl>
);
};

View File

@@ -1,156 +1,95 @@
import { Box, Button, FormHelperText, TextField, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import Image from "next/image";
import { Box, Button, Paper, Typography } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
import { useRef, useState } from "react";
import { useRef } from "react";
const UploadSystem = ({
fieldName,
default_image,
helperText,
error,
setFieldValue,
imageAlt,
label,
enableDelete,
}) => {
const t = useTranslations();
const UploadSystem = ({ selectedImage, handleUploadChange, imageSize, fileType, showAddIcon }) => {
const fileInputRef = useRef(null);
const [selectedImage, setSelectedImage] = useState(default_image || "/images/upload-image.svg");
const [fileType, setFileType] = useState(null);
const [fileName, setFileName] = useState("");
// upload files
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
setSelectedImage(URL.createObjectURL(uploadedFile));
setFileType(uploadedFile.type);
setFileName(uploadedFile.name);
setFieldValue(fieldName, uploadedFile);
}
};
// end upload files
const uploadBoxStyle = error
? {
cursor: "pointer",
objectFit: "contain",
border: "1px dashed #d32f2f",
borderRadius: "5px",
padding: "5px",
}
: {
cursor: "pointer",
objectFit: "contain",
border: "1px dashed #e1e1e1",
borderRadius: "5px",
padding: "5px",
};
const handleClick = () => {
fileInputRef.current.click();
};
const handleDeleteImage = () => {
setselectedImage("/images/upload-image.svg");
setFieldValue(fieldname, null);
};
return (
<Box sx={{ my: 2 }}>
<Typography
margin={1}
sx={{
fontWeight: 500,
fontSize: "0.9rem",
color: "#a19d9d",
}}
textAlign="center"
>
{label}
</Typography>
<Box sx={{ position: "relative" }}></Box>
{selectedImage === "/images/upload-image.svg" || selectedImage === default_image ? (
<Image
src={selectedImage}
priority
alt={imageAlt}
width={200}
height={200}
onClick={handleClick}
style={uploadBoxStyle}
/>
) : fileType && fileType.startsWith("image/") ? (
<Image
src={selectedImage}
priority
alt={imageAlt}
width={200}
height={200}
onClick={handleClick}
style={uploadBoxStyle}
/>
) : (
<Box sx={{ width: "100%" }}>
{showAddIcon ? (
<Box
sx={{
display: "flex",
border: "1px dashed #e1e1e1",
cursor: "pointer",
borderRadius: "5px",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "200px",
height: "200px",
border: 1,
borderColor: "divider",
borderRadius: 2,
cursor: "pointer",
padding: "5px",
height: imageSize[1],
}}
onClick={handleClick}
>
<AddIcon sx={{ fontSize: "2rem", color: "#a19d9d" }} />
<Typography
margin={2}
variant="subtitle2"
sx={{
fontWeight: 600,
fontSize: "1rem",
fontSize: "0.8rem",
color: "#a19d9d",
textAlign: "center",
display: "flex",
justifyContent: "end",
overflow: "hidden",
mt: 1,
}}
textAlign="center"
>
{fileName}
فرمت قابل قبول : png, jpg
<br />
حداکثر 2Mb
</Typography>
</Box>
) : (
<>
{fileType && fileType.startsWith("image/") && (
<Box
width="100%"
height={imageSize[1]}
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
objectFit: "contain",
border: "1px solid #b3b3b3",
borderRadius: "10px",
padding: "5px",
overflow: "hidden",
}}
onClick={handleClick}
>
<Paper
elevation={0}
sx={{
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundSize: "contain",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundImage: `url(${selectedImage})`,
}}
/>
</Box>
)}
</>
)}
<TextField
id="avatar-upload"
<input
type="file"
accept="image/*"
style={{ display: "none" }}
onChange={(e) => handleUploadChange(e)}
inputRef={fileInputRef}
onChange={handleUploadChange}
ref={fileInputRef}
/>
{enableDelete ? (
<Button
sx={{
width: "100%",
borderTopLeftRadius: "unset",
borderTopRightRadius: "unset",
}}
color="error"
disabled={selectedImage === "/images/upload-image.svg"}
endIcon={<DeleteForeverIcon />}
variant="contained"
onClick={handleDeleteImage}
>
{t("delete")}
</Button>
) : (
""
)}
<FormHelperText sx={{ color: "#d32f2f" }}>{helperText}</FormHelperText>
</Box>
);
};
export default UploadSystem;

View File

@@ -0,0 +1,40 @@
import DangerousIcon from "@mui/icons-material/Dangerous";
import { Box, Typography } from "@mui/material";
import { toast } from "react-toastify";
const UploadFileNotification = (t) => {
toast(
({ closeToast }) => (
<>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<DangerousIcon color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography color="error" variant="button">
{t("UploadSystem.uploadfile_error")}
</Typography>
</Box>
</Box>
</Box>
</>
),
{
containerId: "validation",
toastId: "upload",
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);
};
export default UploadFileNotification;

View File

@@ -27,3 +27,4 @@ export const UPDATE_LOAN = BASE_URL + "/navgan/loan/update/";
export const GET_PROJECT_TITLE = BASE_URL + "/navgan_plans";
export const GET_ACTIVITY_LIST = BASE_URL + "/activity_types";
export const GET_PRIORITY_LIST = BASE_URL + "/activity_priorities";
export const SEND_LOAN_REQUEST_REFAHI = BASE_URL + "/refahi/loan/store";

View File

@@ -1,6 +1,7 @@
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import BookmarkAddedIcon from "@mui/icons-material/BookmarkAdded";
import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
import VolunteerActivismIcon from "@mui/icons-material/VolunteerActivism";
import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
const sidebarMenu = [
[
@@ -14,19 +15,30 @@ const sidebarMenu = [
permission: "all",
},
{
key: "sidebar.add-request-loan",
name: "sidebar.add-request-loan",
key: "sidebar.add-request-navgan-loan",
name: "sidebar.add-request-navgan-loan",
secondary: "secondary.navgan",
type: "page",
route: "/dashboard/navgan/add-request-loan",
icon: <DataSaverOnIcon sx={{ width: "inherit", height: "inherit" }} />,
icon: <DirectionsCarIcon sx={{ width: "inherit", height: "inherit" }} />,
selected: false,
permission: "can_request_navgan_loan",
},
{
key: "sidebar.add-request-refahi-loan",
name: "sidebar.add-request-refahi-loan",
secondary: "secondary.refahi",
type: "page",
route: "/dashboard/refahi/add-request-loan",
icon: <VolunteerActivismIcon sx={{ width: "inherit", height: "inherit" }} />,
selected: false,
permission: "can_request_refahi_loan",
},
{
key: "sidebar.followUp-loan",
name: "sidebar.followUp-loan",
type: "page",
route: "/dashboard/navgan/followUp-loan",
route: "/dashboard/followUp-loan",
icon: <BookmarkAddedIcon sx={{ width: "inherit", height: "inherit" }} />,
selected: false,
permission: "all",