push rasad front on new repo

This commit is contained in:
2026-01-18 14:32:49 +03:30
commit 4fe6e70525
2139 changed files with 303150 additions and 0 deletions

View File

@@ -0,0 +1,574 @@
import React, { useContext, useEffect, useState, useCallback } from "react";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import {
Button,
Box,
Typography,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from "@mui/material";
import { Add as AddIcon } from "@mui/icons-material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { updateGuildByNationalIdNewService } from "../../services/update-guild-by-national-id-new";
import { deactivateGuildService } from "../../services/deactivate-guild";
import { provinceGetCitiesService } from "../../services/province-get-cities";
import { provinceGetFieldOfWorks } from "../../services/ProvinceGetFieldOfWorks";
import { provinceGetTypeActivity } from "../../services/provinceGetTypeActivity";
import { provinceGetRegisterCodeStateService } from "../../services/province-get-register-code-state";
import { mainGetGuildsForUpdateOrCreateService } from "../../services/main-get-guilds-for-update-or-create";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { PersonalInfoSection } from "./components/PersonalInfoSection";
import { InquiryForm } from "./components/InquiryForm";
import { UpdateFromExternalButton } from "./components/UpdateFromExternalButton";
import { ConfirmationDialog } from "./components/ConfirmationDialog";
import { FormActions } from "./components/FormActions";
import { GuildInfoAccordionItem } from "./components/GuildInfoAccordionItem";
import { getValidationSchema, getInitialValues } from "./utils/formUtils";
import {
mapResponseDataToFormFields,
prepareSubmitData,
} from "./utils/dataMapping";
import { handleSubmitSuccess, handleSubmitError } from "./utils/submitHandlers";
const DeleteConfirmationDialog = ({ open, onClose, onConfirm, isDeleting }) => {
return (
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
<DialogTitle>آیا مطمئن هستید؟</DialogTitle>
<DialogContent>
<Typography variant="body2">
آیا از حذف این صنف مطمئن هستید؟ این عمل قابل بازگشت نیست.
</Typography>
</DialogContent>
<DialogActions sx={{ gap: 2, p: 2 }}>
<Button
variant="outlined"
onClick={onClose}
disabled={isDeleting}
sx={{ flex: 1 }}
>
انصراف
</Button>
<Button
variant="contained"
color="error"
onClick={onConfirm}
disabled={isDeleting}
sx={{ flex: 1 }}
>
{isDeleting ? "در حال حذف..." : "حذف"}
</Button>
</DialogActions>
</Dialog>
);
};
export const CreateGuilds = ({ guild, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasRegisterCode, setHasRegisterCode] = useState();
const [inquiryNationalCode, setInquiryNationalCode] = useState("");
const [isInquiryDone, setIsInquiryDone] = useState(false);
const [dbRegister, setDbRegister] = useState(null);
const [hasInquiry, setHasInquiry] = useState(null);
const [guildActive, setGuildActive] = useState(null);
const [guildsList, setGuildsList] = useState(() => (guild ? [guild] : []));
const [expandedAccordion, setExpandedAccordion] = useState(0);
const [guildsFormValues, setGuildsFormValues] = useState(() => {
// Initialize with guild data if editing
if (guild) {
return [getInitialValues(guild)];
}
return [];
});
const [cities, setCities] = useState([]);
const [typeActivities, setTypeActivities] = useState([]);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [deleteDialogIndex, setDeleteDialogIndex] = useState(null);
const [isDeletingGuild, setIsDeletingGuild] = useState(false);
const originalPhoneNumber = guild?.phoneNumber || null;
const currentRole = getRoleFromUrl();
const isAdmin = currentRole === "AdminX";
const isSuperAdmin = currentRole === "SuperAdmin";
const isKillHouse = currentRole === "KillHouse";
const formik = useFormik({
initialValues: getInitialValues(guild),
validationSchema: getValidationSchema(!!guild),
validateOnMount: true,
onSubmit: (values) => {
const guildsDataArray = guildsList.map((guildItem, index) => {
const guildValues = guildsFormValues[index] || values;
const combinedValues = {
...values, // Personal info (shared)
...guildValues, // Guild-specific info (overrides if same keys exist)
};
return prepareSubmitData(
combinedValues,
guildItem,
originalPhoneNumber,
hasInquiry
);
});
dispatch(updateGuildByNationalIdNewService(guildsDataArray)).then(
(result) => {
if (result.payload.error) {
handleSubmitError(openNotif, result.payload.error);
} else {
handleSubmitSuccess(
dispatch,
openNotif,
updateTable,
values,
result.payload?.data
);
}
}
);
},
});
useEffect(() => {
dispatch(provinceGetRegisterCodeStateService()).then((r) => {
const isActive = r.payload.data?.[0]?.active;
setHasRegisterCode(isActive);
if (isActive === false) {
formik.setFieldValue("isAccepted", true);
}
});
dispatch(provinceGetCitiesService()).then((r) => {
setCities(r.payload.data || []);
});
dispatch(provinceGetFieldOfWorks());
dispatch(provinceGetTypeActivity()).then((r) => {
setTypeActivities(r.payload.data || []);
});
}, []);
useEffect(() => {
formik.validateForm();
}, []);
const mapResponseToFormFields = useCallback(
(responseData) => {
const guildsData = Array.isArray(responseData.guilds)
? responseData.guilds
: [];
mapResponseDataToFormFields(responseData, inquiryNationalCode, formik);
if (responseData.dbRegister === false) {
setDbRegister(false);
setHasInquiry(null);
} else {
setHasInquiry(responseData.hasInquiry ?? null);
const firstGuild = guildsData.length > 0 ? guildsData[0] : null;
const activeStatus = firstGuild?.active ?? responseData.active ?? null;
setGuildActive(activeStatus);
formik.setFieldValue("active", activeStatus);
setDbRegister(true);
}
if (guildsData.length > 0) {
setGuildsList(guildsData);
const initialGuildValues = guildsData.map((guildItem) => {
const combinedGuild = {
...guildItem,
user: responseData.user || {},
};
return getInitialValues(combinedGuild);
});
setGuildsFormValues(initialGuildValues);
setExpandedAccordion(0);
} else {
setGuildsList([]);
setGuildsFormValues([]);
}
setTimeout(() => {
formik.validateField("mobile");
formik.validateField("national_id");
}, 0);
},
[formik, inquiryNationalCode]
);
const handleInquiry = useCallback(() => {
if (!inquiryNationalCode) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی را وارد کنید",
severity: "error",
});
return;
}
if (!isAdmin && inquiryNationalCode.length !== 10) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی 10 رقمی معتبر وارد کنید",
severity: "error",
});
return;
}
dispatch(
mainGetGuildsForUpdateOrCreateService({
national_code: inquiryNationalCode,
update: false,
})
).then((r) => {
if (r.payload.error) {
setHasInquiry(false);
if (isAdmin) {
setIsInquiryDone(true);
formik.setFieldValue("national_id", inquiryNationalCode);
}
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
if (!isAdmin) {
return;
}
}
if (r.payload.data) {
mapResponseToFormFields(r.payload.data);
setIsInquiryDone(true);
if (r.payload.data.dbRegister === false) {
setHasInquiry(true);
}
const successMsg =
r.payload.data.dbRegister === false
? "اطلاعات از سامانه خارجی دریافت شد"
: "اطلاعات از پایگاه داده دریافت شد";
openNotif({
vertical: "top",
horizontal: "center",
msg: successMsg,
severity: "success",
});
} else {
setHasInquiry(false);
setIsInquiryDone(true);
if (isAdmin) {
formik.setFieldValue("national_id", inquiryNationalCode);
}
}
});
}, [
dispatch,
inquiryNationalCode,
openNotif,
mapResponseToFormFields,
isAdmin,
formik,
]);
const handleUpdateFromExternal = useCallback(() => {
if (!formik.values.national_id || formik.values.national_id.length !== 10) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی 10 رقمی معتبر وارد کنید",
severity: "error",
});
return;
}
dispatch(
mainGetGuildsForUpdateOrCreateService({
national_code: formik.values.national_id,
update: true,
})
).then((r) => {
if (r.payload.error) {
setHasInquiry(false);
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
return;
}
if (r.payload.data) {
const updateResponse = {
...r.payload.data,
dbRegister: false,
};
mapResponseToFormFields(updateResponse);
setHasInquiry(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "اطلاعات از سامانه خارجی بروزرسانی شد",
severity: "success",
});
} else {
setHasInquiry(false);
}
});
}, [dispatch, formik.values.national_id, openNotif, mapResponseToFormFields]);
const handleAddGuild = () => {
const newIndex = guildsList.length;
setGuildsList([...guildsList, null]);
setGuildsFormValues([...guildsFormValues, getInitialValues(null)]);
setExpandedAccordion(newIndex);
};
const handleDeleteGuild = (index) => {
const guildToDelete = guildsList[index];
if (guildToDelete?.key) {
setDeleteDialogIndex(index);
setDeleteDialogOpen(true);
return;
}
if (guildsList.length > 1) {
setGuildsList(guildsList.filter((_, i) => i !== index));
setGuildsFormValues(guildsFormValues.filter((_, i) => i !== index));
if (expandedAccordion === index) {
setExpandedAccordion(0);
} else if (expandedAccordion > index) {
setExpandedAccordion(expandedAccordion - 1);
}
}
};
const handleConfirmDelete = () => {
if (deleteDialogIndex === null) return;
const guildToDelete = guildsList[deleteDialogIndex];
if (!guildToDelete?.key) return;
setIsDeletingGuild(true);
dispatch(deactivateGuildService(guildToDelete.key)).then((r) => {
setIsDeletingGuild(false);
setDeleteDialogOpen(false);
setDeleteDialogIndex(null);
if (r.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "صنف با موفقیت حذف شد",
severity: "success",
});
// Remove from list after successful deletion
if (guildsList.length > 1) {
setGuildsList(guildsList.filter((_, i) => i !== deleteDialogIndex));
setGuildsFormValues(
guildsFormValues.filter((_, i) => i !== deleteDialogIndex)
);
// If deleted accordion was expanded, expand the first one
if (expandedAccordion === deleteDialogIndex) {
setExpandedAccordion(0);
} else if (expandedAccordion > deleteDialogIndex) {
// Adjust expanded index if deleted item was before it
setExpandedAccordion(expandedAccordion - 1);
}
}
if (updateTable) {
updateTable();
}
}
});
};
const handleCloseDeleteDialog = () => {
if (!isDeletingGuild) {
setDeleteDialogOpen(false);
setDeleteDialogIndex(null);
}
};
const handleGuildValuesChange = useCallback((index, fieldName, value) => {
setGuildsFormValues((prev) => {
const newValues = [...prev];
if (!newValues[index]) {
newValues[index] = getInitialValues(null);
}
newValues[index] = {
...newValues[index],
[fieldName]: value,
};
return newValues;
});
}, []);
const handleAccordionChange = (index) => (event, isExpanded) => {
setExpandedAccordion(isExpanded ? index : false);
};
const shouldShowUpdateButton =
formik?.values?.national_id &&
(isAdmin ||
(dbRegister !== false &&
(guild ||
(!guild &&
((dbRegister === true && hasInquiry === false) ||
(isAdmin && isInquiryDone))))));
const shouldShowInquiryForm = !guild && !isInquiryDone;
const shouldShowFormContent = guild || isInquiryDone;
return (
<form onSubmit={formik.handleSubmit}>
<Grid
container
gap={SPACING.TINY}
maxHeight="80vh"
minWidth={
!guild && !isInquiryDone
? "auto"
: { xs: "96vw", md: "90vw", nlg: "1280px" }
}
overflow="auto"
p={2}
>
{shouldShowUpdateButton && (
<UpdateFromExternalButton
onUpdate={handleUpdateFromExternal}
disabled={!!formik.errors.national_id}
/>
)}
{shouldShowInquiryForm && (
<InquiryForm
inquiryNationalCode={inquiryNationalCode}
setInquiryNationalCode={setInquiryNationalCode}
onInquiry={handleInquiry}
isAdmin={isAdmin}
/>
)}
{shouldShowFormContent && (
<>
<Grid container xs={12}>
<PersonalInfoSection
formik={formik}
guild={guild}
hasInquiry={hasInquiry}
isAdmin={isAdmin}
isSuperAdmin={isSuperAdmin}
isKillHouse={isKillHouse}
/>
<Grid
item
xs={12}
lg={6}
pr={{ xs: 0, md: 2 }}
pl={{ xs: 0, md: 3 }}
>
<Grid container gap={SPACING.TINY} direction="column">
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات صنفی
</Typography>
</Grid>
<Grid item xs={12}>
{guildsList.map((guildItem, index) => (
<Box key={index} sx={{ mb: 2 }}>
<GuildInfoAccordionItem
guildIndex={index}
guildData={guildItem}
guildActive={guildActive}
isAdmin={isAdmin}
cities={cities}
typeActivities={typeActivities}
onDelete={() => handleDeleteGuild(index)}
canDelete={
(guildsList.length > 1 || !guild) && isAdmin
}
guildFormValues={guildsFormValues[index]}
onGuildValuesChange={handleGuildValuesChange}
expanded={expandedAccordion === index}
onChange={handleAccordionChange(index)}
/>
</Box>
))}
</Grid>
{isAdmin && (
<Grid item xs={12} xl={4}>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={handleAddGuild}
fullWidth
sx={{ mb: 2 }}
>
افزودن واحد صنفی
</Button>
</Grid>
)}
</Grid>
</Grid>
</Grid>
<Grid container item xs={12}>
{hasRegisterCode &&
(!(!guild && hasInquiry === true) ||
isAdmin ||
isSuperAdmin ||
isKillHouse) && (
<ConfirmationDialog
isAccepted={formik.values.isAccepted}
onAccept={() => formik.setFieldValue("isAccepted", true)}
onReject={() => formik.setFieldValue("isAccepted", false)}
/>
)}
<FormActions
formik={formik}
onClose={() => dispatch(CLOSE_MODAL())}
showCloseButton={
!guild &&
hasInquiry === true &&
!isAdmin &&
!isSuperAdmin &&
!isKillHouse
}
isKillHouse={isKillHouse}
onSubmit={formik.handleSubmit}
/>
</Grid>
</>
)}
</Grid>
<DeleteConfirmationDialog
open={deleteDialogOpen}
onClose={handleCloseDeleteDialog}
onConfirm={handleConfirmDelete}
isDeleting={isDeletingGuild}
/>
</form>
);
};

View File

@@ -0,0 +1,45 @@
import React from "react";
import {
Button,
Typography,
ListItem,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { DialogAlert } from "../../../../../components/dialog-alert/DialogAlert";
import { Done } from "@mui/icons-material";
export const ConfirmationDialog = ({ isAccepted, onAccept, onReject }) => {
return (
<Grid item pb={2} mt={2}>
<DialogAlert
title="تعهد نامه"
content={
<>
<ListItem>
<ListItemIcon>
<Done />
</ListItemIcon>
<ListItemText primary="لطفا صحت اطلاعات وارد شده را بررسی نمایید. پس از تکمیل و ثبت درخواست، یک کد احراز هویت از طریق پیامک برای صنف یا مباشر به شماره تلفن اعلامی ارسال میگردد." />
</ListItem>
<Typography></Typography>
</>
}
actions={
<Grid container gap={SPACING.TINY}>
<Button variant="outlined" color="error" onClick={onReject}>
رد
</Button>
<Button variant="contained" color="success" onClick={onAccept}>
موافقم
</Button>
</Grid>
}
btnTitle="تایید صحت اطلاعات"
isAccepted={isAccepted}
/>
</Grid>
);
};

View File

@@ -0,0 +1,69 @@
import React from "react";
import { Button, Typography, Checkbox, FormControlLabel } from "@mui/material";
import { Grid } from "../../../../../components/grid/Grid";
export const FormActions = ({
formik,
onClose,
showCloseButton,
isKillHouse,
onSubmit,
}) => {
if (showCloseButton) {
return (
<Grid item xs={12} mt={4}>
<Button color="primary" fullWidth variant="contained" onClick={onClose}>
متوجه شدم
</Button>
</Grid>
);
}
// For KillHouse: check if area_activity contains "مرغ"
const isAreaActivityValid = isKillHouse
? formik.values.area_activity && formik.values.area_activity.includes("مرغ")
: true;
return (
<>
<Grid item xs={12}>
<FormControlLabel
control={
<Checkbox
checked={formik.values.verify_mobile}
onChange={formik.handleChange}
name="verify_mobile"
color="primary"
/>
}
label="احراز شماره موبایل"
/>
</Grid>
<Grid item xs={12}>
<Button
disabled={
formik.errors.isAccepted ||
Boolean(formik.errors.national_id) ||
!isAreaActivityValid
}
color="primary"
fullWidth
variant="contained"
onClick={onSubmit}
type="button"
>
ثبت
</Button>
{isKillHouse && !isAreaActivityValid && (
<Typography
variant="caption"
color="error"
sx={{ mt: 1, display: "block" }}
>
رسته واحد صنفی باید شامل کلمه &quot;مرغ&quot; باشد
</Typography>
)}
</Grid>
</>
);
};

View File

@@ -0,0 +1,97 @@
import React from "react";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
IconButton,
} from "@mui/material";
import {
ExpandMore as ExpandMoreIcon,
Delete as DeleteIcon,
} from "@mui/icons-material";
import { GuildInfoSection } from "./GuildInfoSection";
export const GuildInfoAccordionItem = ({
guildIndex,
guildData,
guildActive,
isAdmin,
cities,
typeActivities,
onDelete,
canDelete,
guildFormValues,
onGuildValuesChange,
expanded,
onChange,
}) => {
// Create a formik-like object for this guild's values
const guildFormik = {
values: guildFormValues || {},
setFieldValue: (fieldName, value) => {
onGuildValuesChange(guildIndex, fieldName, value);
},
handleChange: (e) => {
onGuildValuesChange(guildIndex, e.target.name, e.target.value);
},
handleBlur: () => {},
errors: {},
touched: {},
};
const getGuildTitle = () => {
if (guildData?.guildsName) {
return guildData.guildsName;
}
if (guildData?.title) {
return guildData.title;
}
if (guildFormik.values.guild_name) {
return guildFormik.values.guild_name;
}
return `واحد صنفی ${guildIndex + 1}`;
};
return (
<Accordion expanded={expanded} onChange={onChange}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
"& .MuiAccordionSummary-content": {
alignItems: "center",
justifyContent: "space-between",
},
}}
>
<Typography variant="h6" sx={{ flexGrow: 1, fontSize: 18 }}>
{getGuildTitle()}
</Typography>
{canDelete && (
<IconButton
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
color="error"
size="small"
sx={{ mr: 1 }}
>
<DeleteIcon />
</IconButton>
)}
</AccordionSummary>
<AccordionDetails>
<GuildInfoSection
formik={guildFormik}
guild={guildData}
guildActive={guildActive}
isAdmin={isAdmin}
cities={cities}
typeActivities={typeActivities}
hideTitle={true}
noGridWrapper={true}
/>
</AccordionDetails>
</Accordion>
);
};

View File

@@ -0,0 +1,566 @@
import React from "react";
import {
TextField,
Typography,
RadioGroup,
FormControl,
Radio,
FormControlLabel,
Select,
MenuItem,
InputLabel,
Checkbox,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import BusinessIcon from "@mui/icons-material/Business";
import PublicIcon from "@mui/icons-material/Public";
import LocationCityIcon from "@mui/icons-material/LocationCity";
import DateRangeIcon from "@mui/icons-material/DateRange";
import ConfirmationNumberIcon from "@mui/icons-material/ConfirmationNumber";
import AccountBalanceIcon from "@mui/icons-material/AccountBalance";
import LocalPostOfficeIcon from "@mui/icons-material/LocalPostOffice";
import PhoneIcon from "@mui/icons-material/Phone";
import BadgeIcon from "@mui/icons-material/Badge";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import HomeIcon from "@mui/icons-material/Home";
import CorporateFareIcon from "@mui/icons-material/CorporateFare";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { LabelField } from "../../../../../components/label-field/LabelField";
import { InfoBox } from "./InfoBox";
import { STATUS_VALUES } from "../constants";
import {
convertPersianToGregorian,
convertGregorianToPersian,
} from "../utils/dateUtils";
export const GuildInfoSection = ({
formik,
guild,
guildActive,
isAdmin,
cities,
typeActivities,
hideTitle = false,
noGridWrapper = false,
}) => {
const getForeignerDisplay = (isForeigner) => {
if (isForeigner === STATUS_VALUES.NO || isForeigner === false)
return STATUS_VALUES.NO;
if (isForeigner === STATUS_VALUES.YES || isForeigner === true)
return STATUS_VALUES.YES;
return "-";
};
const getLicenseExpireDateDisplay = () => {
return formik.values.license_expire_date || "-";
};
const getActiveStatusDisplay = () => {
const activeValue =
formik.values.active !== null
? formik.values.active
: guild?.active === true || guildActive === true;
return activeValue === true ? "فعال" : "غیر فعال";
};
const content = (
<Grid container gap={SPACING.TINY} direction="column">
{!hideTitle && (
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات صنفی
</Typography>
</Grid>
)}
<Grid container xs={12}>
<Grid
container
direction="column"
xs={12}
md={6}
px={SPACING.TINY}
gap={SPACING.TINY}
>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام واحد"
variant="outlined"
fullWidth
id="guild_name"
name="guild_name"
value={formik.values.guild_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BusinessIcon}
label="نام واحد"
value={formik.values.guild_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<FormControl fullWidth>
<InputLabel id="area-activity-select-label">
رسته واحد صنفی
</InputLabel>
<Select
labelId="area-activity-select-label"
id="area_activity"
name="area_activity"
value={formik.values.area_activity || ""}
label="رسته واحد صنفی"
onChange={(e) => {
formik.setFieldValue("area_activity", e.target.value);
}}
onBlur={formik.handleBlur}
>
{typeActivities.map((activity) => (
<MenuItem key={activity.key} value={activity.title}>
{activity.title}
</MenuItem>
))}
{/* Show current value if it doesn't exist in options */}
{formik.values.area_activity &&
!typeActivities.some(
(activity) =>
activity.title === formik.values.area_activity
) && (
<MenuItem
key="current-value"
value={formik.values.area_activity}
>
{formik.values.area_activity}
</MenuItem>
)}
</Select>
</FormControl>
) : (
<InfoBox
icon={BusinessIcon}
label="رسته واحد صنفی"
value={formik.values.area_activity}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="استان"
variant="outlined"
fullWidth
id="state"
name="state"
value={formik.values.state}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PublicIcon}
label="استان"
value={formik.values.state}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<FormControl fullWidth>
<InputLabel id="city-select-label">شهرستان</InputLabel>
<Select
labelId="city-select-label"
id="city"
name="city"
value={formik.values.city || ""}
label="شهرستان"
onChange={(e) => {
formik.setFieldValue("city", e.target.value);
}}
onBlur={formik.handleBlur}
>
{cities.map((city) => (
<MenuItem key={city.key} value={city.name}>
{city.name}
</MenuItem>
))}
{/* Show current value if it doesn't exist in options */}
{formik.values.city &&
!cities.some(
(city) => city.name === formik.values.city
) && (
<MenuItem key="current-value" value={formik.values.city}>
{formik.values.city}
</MenuItem>
)}
</Select>
</FormControl>
) : (
<InfoBox
icon={LocationCityIcon}
label="شهرستان"
value={formik.values.city}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<DatePicker
label="تاریخ انقضا مجوز"
value={
formik.values.license_expire_date
? moment(
convertPersianToGregorian(
formik.values.license_expire_date
)
)
: null
}
onChange={(newValue) => {
if (newValue) {
const gregorianDate = moment(newValue).format("YYYY-MM-DD");
const persianDate =
convertGregorianToPersian(gregorianDate);
formik.setFieldValue("license_expire_date", persianDate);
} else {
formik.setFieldValue("license_expire_date", "");
}
}}
renderInput={(params) => (
<TextField {...params} fullWidth variant="outlined" />
)}
/>
) : (
<InfoBox
icon={DateRangeIcon}
label="تاریخ انقضا مجوز"
value={getLicenseExpireDateDisplay()}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره مجوز"
variant="outlined"
fullWidth
id="license_number"
name="license_number"
value={formik.values.license_number}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={ConfirmationNumberIcon}
label="شماره مجوز"
value={formik.values.license_number}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام اتحادیه"
variant="outlined"
fullWidth
id="union_name"
name="union_name"
value={formik.values.union_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={AccountBalanceIcon}
label="نام اتحادیه"
value={formik.values.union_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="وضعیت">
<FormControl fullWidth>
<RadioGroup
row
name="active"
value={
formik.values.active === true
? "true"
: formik.values.active === false
? "false"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"active",
e.target.value === "true" ? true : false
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="true"
control={<Radio />}
label="فعال"
/>
<FormControlLabel
value="false"
control={<Radio />}
label="غیر فعال"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={CheckCircleIcon}
label="وضعیت"
value={getActiveStatusDisplay()}
/>
)}
</Grid>
</Grid>
<Grid
container
xs={12}
md={6}
px={SPACING.TINY}
direction="column"
gap={SPACING.TINY}
>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="کد پستی"
variant="outlined"
fullWidth
id="postal_code"
name="postal_code"
value={formik.values.postal_code}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={LocalPostOfficeIcon}
label="کد پستی"
value={formik.values.postal_code}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره تلفن"
variant="outlined"
fullWidth
id="phone_number"
name="phone_number"
value={formik.values.phone_number}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PhoneIcon}
label="شماره تلفن"
value={formik.values.phone_number}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="آیا اتباع است؟">
<FormControl fullWidth>
<RadioGroup
row
name="is_foreigner"
value={
formik.values.is_foreigner === "بلی" ||
formik.values.is_foreigner === true
? "بلی"
: formik.values.is_foreigner === "خیر" ||
formik.values.is_foreigner === false
? "خیر"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"is_foreigner",
e.target.value === "بلی" ? "بلی" : "خیر"
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="بلی"
control={<Radio />}
label="بلی"
/>
<FormControlLabel
value="خیر"
control={<Radio />}
label="خیر"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={PublicIcon}
label="آیا اتباع است؟"
value={getForeignerDisplay(formik.values.is_foreigner)}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام شرکت"
variant="outlined"
fullWidth
id="corporation_name"
name="corporation_name"
value={formik.values.corporation_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={CorporateFareIcon}
label="نام شرکت"
value={formik.values.corporation_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شناسه ملی شرکت"
variant="outlined"
fullWidth
id="guild_national_id"
name="guild_national_id"
value={formik.values.guild_national_id}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="شناسه ملی شرکت"
value={formik.values.guild_national_id}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="وضعیت مجوز"
variant="outlined"
fullWidth
id="license_status"
name="license_status"
value={formik.values.license_status}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={CheckCircleIcon}
label="وضعیت مجوز"
value={formik.values.license_status}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="آدرس"
variant="outlined"
fullWidth
multiline
rows={3}
id="address"
name="address"
value={formik.values.address}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={HomeIcon}
label="آدرس"
value={formik.values.address}
iconSx={{ mt: 0.5 }}
/>
)}
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sx={{ display: "flex", mt: 2, pl: 2, mb: 2 }}>
{isAdmin ? (
<>
<FormControlLabel
control={
<Checkbox
checked={formik.values.steward || false}
onChange={(e) =>
formik.setFieldValue("steward", e.target.checked)
}
name="steward"
color="primary"
/>
}
label="مباشر"
/>
<FormControlLabel
sx={{ ml: 3 }}
control={
<Checkbox
checked={formik.values.guild || false}
onChange={(e) =>
formik.setFieldValue("guild", e.target.checked)
}
name="guild"
color="primary"
/>
}
label="صنف"
/>
</>
) : (
<>
<InfoBox
icon={CheckCircleIcon}
label="مباشر"
value={formik.values.steward ? "بلی" : "خیر"}
/>
<InfoBox
icon={CheckCircleIcon}
label="صنف"
value={formik.values.guild ? "بلی" : "خیر"}
sx={{ ml: 3 }}
/>
</>
)}
</Grid>
</Grid>
);
if (noGridWrapper) {
return <>{content}</>;
}
return (
<Grid item xs={12} lg={6} pr={{ xs: 0, md: 2 }} pl={{ xs: 0, md: 3 }}>
{content}
</Grid>
);
};

View File

@@ -0,0 +1,23 @@
import React from "react";
import { Box, Typography } from "@mui/material";
export const InfoBox = ({ icon: Icon, label, value, iconSx }) => (
<Box
display="flex"
alignItems={iconSx ? "flex-start" : "center"}
gap={1}
px={1.5}
py={0.5}
bgcolor="#f5f5f5"
borderRadius={1}
>
<Icon color="action" sx={iconSx} />
<Box>
<Typography variant="caption" color="text.secondary">
{label}
</Typography>
<Typography variant="body1">{value || "-"}</Typography>
</Box>
</Box>
);

View File

@@ -0,0 +1,34 @@
import React from "react";
import { TextField, Button } from "@mui/material";
export const InquiryForm = ({
inquiryNationalCode,
setInquiryNationalCode,
onInquiry,
isAdmin,
}) => {
return (
<>
<TextField
label="کد ملی برای استعلام"
variant="outlined"
fullWidth
value={inquiryNationalCode}
onChange={(e) => setInquiryNationalCode(e.target.value)}
placeholder={
isAdmin ? "کد ملی را وارد کنید" : "کد ملی 10 رقمی را وارد کنید"
}
inputProps={isAdmin ? {} : { maxLength: 10 }}
/>
<Button
color="primary"
fullWidth
variant="contained"
onClick={onInquiry}
disabled={!isAdmin && inquiryNationalCode.length !== 10}
>
استعلام
</Button>
</>
);
};

View File

@@ -0,0 +1,369 @@
import React from "react";
import {
TextField,
Typography,
RadioGroup,
FormControl,
Radio,
FormControlLabel,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import PersonIcon from "@mui/icons-material/Person";
import PhoneIcon from "@mui/icons-material/Phone";
import BadgeIcon from "@mui/icons-material/Badge";
import CakeIcon from "@mui/icons-material/Cake";
import FaceIcon from "@mui/icons-material/Face";
import LocationCityIcon from "@mui/icons-material/LocationCity";
import FavoriteIcon from "@mui/icons-material/Favorite";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { LabelField } from "../../../../../components/label-field/LabelField";
import { InfoBox } from "./InfoBox";
import { GENDER_VALUES, ALIVE_STATUS } from "../constants";
import {
convertPersianToGregorian,
convertGregorianToPersian,
} from "../utils/dateUtils";
export const PersonalInfoSection = ({
formik,
guild,
hasInquiry,
isAdmin,
isSuperAdmin,
isKillHouse,
}) => {
const getGenderDisplay = (gender) => {
if (gender === "True" || gender === true) return GENDER_VALUES.MALE;
if (gender === "False" || gender === false) return GENDER_VALUES.FEMALE;
return "-";
};
const getAliveStatusDisplay = (isAlive) => {
if (isAlive === ALIVE_STATUS.YES || isAlive === true)
return ALIVE_STATUS.YES;
if (isAlive === ALIVE_STATUS.NO || isAlive === false)
return ALIVE_STATUS.NO;
return "-";
};
const getBirthDateDisplay = () => {
return formik.values.birth_date || "-";
};
return (
<Grid
item
xs={12}
lg={6}
pl={{ xs: 0, md: 2 }}
pr={{ xs: 0, md: 3 }}
mb={2}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات شخصی
</Typography>
</Grid>
<Grid container xs={12}>
<Grid container xs={12} md={6} gap={SPACING.TINY} px={SPACING.TINY}>
<Grid item xs={12}>
{guild || isAdmin ? (
<TextField
label="کد ملی"
variant="outlined"
fullWidth
id="national_id"
name="national_id"
value={formik.values.national_id}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={Boolean(formik.errors.national_id)}
helperText={formik.errors.national_id}
inputProps={{ maxLength: 10 }}
disabled={!isAdmin || isKillHouse}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="کد ملی"
value={formik.values.national_id}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام"
variant="outlined"
fullWidth
id="first_name"
name="first_name"
value={formik.values.first_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام"
value={formik.values.first_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام خانوادگی"
variant="outlined"
fullWidth
id="last_name"
name="last_name"
value={formik.values.last_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام خانوادگی"
value={formik.values.last_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره شناسنامه"
variant="outlined"
fullWidth
id="national_code"
name="national_code"
value={formik.values.national_code}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="شماره شناسنامه"
value={formik.values.national_code}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="در قید حیات">
<FormControl fullWidth>
<RadioGroup
row
name="is_alive"
value={
formik.values.is_alive === "بلی" ||
formik.values.is_alive === true
? "بلی"
: formik.values.is_alive === "خیر" ||
formik.values.is_alive === false
? "خیر"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"is_alive",
e.target.value === "بلی" ? "بلی" : "خیر"
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="بلی"
control={<Radio />}
label="بلی"
/>
<FormControlLabel
value="خیر"
control={<Radio />}
label="خیر"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={FavoriteIcon}
label="در قید حیات"
value={getAliveStatusDisplay(formik.values.is_alive)}
/>
)}
</Grid>
</Grid>
<Grid
container
xs={12}
md={6}
gap={SPACING.TINY}
px={SPACING.TINY}
mt={{ xs: 1, md: 0 }}
>
<Grid item xs={12}>
{isAdmin ? (
<DatePicker
label="تاریخ تولد"
value={
formik.values.birth_date
? moment(
convertPersianToGregorian(formik.values.birth_date)
)
: null
}
onChange={(newValue) => {
if (newValue) {
const gregorianDate =
moment(newValue).format("YYYY-MM-DD");
const persianDate =
convertGregorianToPersian(gregorianDate);
formik.setFieldValue("birth_date", persianDate);
} else {
formik.setFieldValue("birth_date", "");
}
}}
renderInput={(params) => (
<TextField {...params} fullWidth variant="outlined" />
)}
/>
) : (
<InfoBox
icon={CakeIcon}
label="تاریخ تولد"
value={getBirthDateDisplay()}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام پدر"
variant="outlined"
fullWidth
id="father_name"
name="father_name"
value={formik.values.father_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام پدر"
value={formik.values.father_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="جنسیت">
<FormControl fullWidth>
<RadioGroup
row
name="gender"
value={
formik.values.gender === "True" ||
formik.values.gender === true
? "True"
: formik.values.gender === "False" ||
formik.values.gender === false
? "False"
: ""
}
onChange={(e) => {
formik.setFieldValue("gender", e.target.value);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="True"
control={<Radio />}
label="مرد"
/>
<FormControlLabel
value="False"
control={<Radio />}
label="زن"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={FaceIcon}
label="جنسیت"
value={getGenderDisplay(formik.values.gender)}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شهر"
variant="outlined"
fullWidth
id="person_city"
name="person_city"
value={formik.values.person_city}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={LocationCityIcon}
label="شهر"
value={formik.values.person_city}
/>
)}
</Grid>
<Grid item xs={12} mt={{ xs: 1, md: 0 }}>
{!guild &&
hasInquiry &&
!isAdmin &&
!isSuperAdmin &&
!isKillHouse ? (
<InfoBox
icon={PhoneIcon}
label="شماره همراه"
value={formik.values.mobile}
/>
) : isKillHouse &&
formik.values.mobile &&
/^[0-9]{11}$/.test(formik.values.mobile) ? (
<InfoBox
icon={PhoneIcon}
label="شماره همراه"
value={formik.values.mobile}
/>
) : (
<TextField
label="شماره همراه"
variant="outlined"
fullWidth
id="mobile"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
error={formik.touched.mobile && Boolean(formik.errors.mobile)}
helperText={formik.touched.mobile && formik.errors.mobile}
disabled={
isKillHouse &&
formik.values.mobile &&
/^[0-9]{11}$/.test(formik.values.mobile)
}
/>
)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,19 @@
import React from "react";
import { Button } from "@mui/material";
import { Refresh } from "@mui/icons-material";
import { Grid } from "../../../../../components/grid/Grid";
export const UpdateFromExternalButton = ({ onUpdate, disabled }) => {
return (
<Grid container xs={12} justifyContent="flex-end">
<Button
onClick={onUpdate}
disabled={disabled}
color="primary"
size="small"
>
بروزرسانی از سامانه واحد <Refresh />
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,17 @@
export const STATUS_VALUES = {
YES: "بلی",
NO: "خیر",
TRUE: "True",
FALSE: "False",
};
export const GENDER_VALUES = {
MALE: "مرد",
FEMALE: "زن",
};
export const ALIVE_STATUS = {
YES: "بلی",
NO: "خیر",
};

View File

@@ -0,0 +1,232 @@
import { getRoleFromUrl } from "../../../../../utils/getRoleFromUrl";
import { normalizeExternalApiDate, normalizeDatabaseDate } from "./dateUtils";
import { formatDateForSubmit } from "./dateUtils";
export const prepareSubmitData = (
values,
guild,
originalPhoneNumber,
hasInquiry
) => {
const baseData = {
national_id: values.national_id,
mobile: values.mobile,
mobilenumber: values.mobile,
steward: !!values.steward,
guild: !!values.guild,
active_register_code: !!values.verify_mobile,
firstName: values.first_name || "",
lastName: values.last_name || "",
fatherName: values.father_name || "",
gender: values.gender || "",
identityNo: values.national_code || "",
isLive: values.is_alive === "بلی" || values.is_alive === true,
birthDate: formatDateForSubmit(
values.birth_date || "",
hasInquiry === true
),
city: values.city || "",
address: values.address || "",
postalcode: values.postal_code || "",
licenseNumber: values.license_number || "",
licenseExpireDate: formatDateForSubmit(
values.license_expire_date || "",
hasInquiry === true
),
licenseIssueDate: formatDateForSubmit(
values.license_issue_date || "",
hasInquiry === true
),
licenseType: values.license_type || "",
licenseStatus: values.license_status || "",
isicname: values.area_activity || "",
corporationName: values.corporation_name || "",
nationalId: values.guild_national_id || "",
unionName: values.union_name || "",
phonenumber: values.phone_number || "",
hasPartner: values.has_partner === true || values.has_partner === "بلی",
isForeigner: values.is_foreigner === true || values.is_foreigner === "بلی",
title: values.guild_name || "",
role: getRoleFromUrl(),
has_inquiry: hasInquiry !== null ? hasInquiry : false,
...(values.active !== null && { active: values.active }),
};
if (guild) {
baseData.guilds_key = guild.key;
if (values.mobile !== originalPhoneNumber) {
baseData.mobile = values.mobile;
baseData.mobilenumber = values.mobile;
}
}
return baseData;
};
export const mapResponseDataToFormFields = (
responseData,
inquiryNationalCode,
formik
) => {
const isExternalApi = responseData.dbRegister === false;
// New structure: user is at top level, guilds is an array
const userData = responseData.user || {};
// For personal info, we use the first guild's data if available, or empty
const firstGuild =
Array.isArray(responseData.guilds) && responseData.guilds.length > 0
? responseData.guilds[0]
: {};
const guildData = firstGuild || {};
const layerTwo = guildData.layerTwo || {};
const addressData = firstGuild?.address || guildData.address || {};
const provinceData = addressData.province || {};
const cityData = addressData.city || {};
const nationalIdValue = isExternalApi
? String(
layerTwo.nationalcode || userData.nationalCode || inquiryNationalCode
).trim()
: String(userData.nationalId || inquiryNationalCode).trim();
const birthDatePersian = isExternalApi
? normalizeExternalApiDate(userData.birthDate || "")
: normalizeDatabaseDate(userData.birthday || "");
const licenseExpireDatePersian = isExternalApi
? normalizeExternalApiDate(guildData.licenseExpireDate || "")
: normalizeDatabaseDate(firstGuild.licenseExpireDate || "");
const licenseIssueDatePersian = isExternalApi
? normalizeExternalApiDate(layerTwo.licenseIssueDate || "")
: normalizeDatabaseDate(responseData.licenseIssueDate || "");
const genderValue = isExternalApi
? userData.gender === true
? "True"
: userData.gender === false
? "False"
: ""
: userData.gender || "";
const isAliveValue = isExternalApi
? userData.isLive === true
? "بلی"
: userData.isLive === false
? "خیر"
: ""
: userData.isAlive === false
? "خیر"
: userData.isAlive === true
? "بلی"
: "";
const isForeignerValue = isExternalApi
? layerTwo.isForeigner === "خیر"
? false
: layerTwo.isForeigner === "بلی"
? true
: ""
: responseData.isForeignNational === false
? false
: responseData.isForeignNational === true
? true
: "";
const hasStewardValue = isExternalApi
? layerTwo.hasSteward === "خیر"
? false
: layerTwo.hasSteward === "بلی"
? true
: ""
: responseData.steward === false
? false
: responseData.steward === true
? true
: "";
const hasPartnerValue = isExternalApi
? layerTwo.hasPartner === "خیر"
? false
: layerTwo.hasPartner === "بلی"
? true
: ""
: responseData.hasPartner === false
? false
: responseData.hasPartner === true
? true
: "";
const values = {
first_name: userData.firstName || "",
last_name: userData.lastName || "",
national_id: nationalIdValue,
national_code: isExternalApi
? userData.identityNo || ""
: userData.nationalCode || "",
birth_date: birthDatePersian,
father_name: userData.fatherName || "",
gender: genderValue,
person_city: userData.city || "",
is_alive: isAliveValue,
// Guild fields - will be set per guild in accordion, so we set empty or first guild's data
guild_name: isExternalApi
? guildData.title || ""
: firstGuild.guildsName || "",
area_activity: isExternalApi
? guildData.isicname || ""
: firstGuild.areaActivity || "",
state: isExternalApi ? guildData.state || "" : provinceData.name || "",
city: isExternalApi ? guildData.city || "" : cityData.name || "",
address: isExternalApi
? guildData.address || ""
: addressData.address || "",
license_expire_date: licenseExpireDatePersian,
license_status: isExternalApi
? guildData.licenseStatus || ""
: firstGuild.licenseStatus || "",
license_type: isExternalApi
? guildData.licenseType || ""
: firstGuild.licenseType || "",
license_number: isExternalApi
? guildData.licenseNumber || ""
: firstGuild.licenseNumber || "",
union_name: isExternalApi
? layerTwo.unionName || ""
: firstGuild.unionName || "",
postal_code: isExternalApi
? layerTwo.postalcode || ""
: addressData.postalCode || "",
phone_number: isExternalApi
? layerTwo.phonenumber || ""
: firstGuild.phoneNumber || "",
mobile: isExternalApi ? layerTwo.mobilenumber || "" : userData.mobile || "",
guild_national_id: isExternalApi
? layerTwo.nationalId || ""
: firstGuild.nationalCode || "",
is_foreigner: isForeignerValue,
corporation_name: isExternalApi
? layerTwo.corporationName || ""
: firstGuild.companyName || "",
has_steward: hasStewardValue,
has_partner: hasPartnerValue,
steward: isExternalApi ? false : firstGuild.isSteward || false,
guild: isExternalApi
? typeof guildData.guild === "boolean"
? guildData.guild
: false
: typeof firstGuild.guild === "boolean"
? firstGuild.guild
: false,
license_issue_date: licenseIssueDatePersian,
...(isExternalApi
? {}
: {
company_name: firstGuild.companyName || "",
company_identifier: firstGuild.companyIdentifier || "",
type_activity_name: firstGuild.typeActivity || "",
}),
};
formik.setValues({ ...formik.values, ...values });
};

View File

@@ -0,0 +1,158 @@
import {
convertToIranianTime,
convertPersianToEnglishNumerals,
} from "../../../../../utils/formatTime";
import { fromJalali } from "../../../../../utils/jalali";
export const convertGregorianToPersian = (gregorianDateString) => {
if (!gregorianDateString || typeof gregorianDateString !== "string") {
return "";
}
// Check if the date is already in Persian format (YYYY/MM/DD with year < 1500)
const persianPattern = /^\d{4}\/\d{2}\/\d{2}$/;
if (persianPattern.test(gregorianDateString)) {
const year = parseInt(gregorianDateString.split("/")[0]);
// If year is < 1500, it's likely Persian, return as is
if (year < 1500) {
return gregorianDateString;
}
}
// Try to convert using convertToIranianTime
try {
return convertToIranianTime(gregorianDateString);
} catch (error) {
console.error("Error converting Gregorian date to Persian:", error);
return gregorianDateString; // Return original on error
}
};
export const convertPersianToGregorian = (persianDateString) => {
if (!persianDateString || typeof persianDateString !== "string") {
return "";
}
const normalizedDateString =
convertPersianToEnglishNumerals(persianDateString);
const gregorianPattern = /^\d{4}[-/]\d{2}[-/]\d{2}$/;
if (gregorianPattern.test(normalizedDateString)) {
const year = parseInt(normalizedDateString.split(/[-/]/)[0]);
if (year > 1500) {
return normalizedDateString.replace(/\//g, "-");
}
}
const parts = normalizedDateString.split("/");
if (parts.length !== 3) {
return persianDateString;
}
const py = parseInt(parts[0]);
const pm = parseInt(parts[1]);
const pd = parseInt(parts[2]);
if (isNaN(py) || isNaN(pm) || isNaN(pd)) {
return persianDateString;
}
try {
const gregorianDate = fromJalali(py, pm, pd);
const year = gregorianDate.getFullYear();
const month = String(gregorianDate.getMonth() + 1).padStart(2, "0");
const day = String(gregorianDate.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
} catch (error) {
console.error("Error converting Persian date to Gregorian:", error);
return persianDateString;
}
};
export const normalizeExternalApiDate = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
return convertPersianToEnglishNumerals(dateString);
};
export const normalizeDatabaseDate = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
const first10Chars = dateString.substring(0, 10);
const normalizedDate = first10Chars.replace(/-/g, "/");
return convertToIranianTime(normalizedDate);
};
export const formatDateForSubmitExternal = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
return convertPersianToEnglishNumerals(dateString);
};
export const formatDateForSubmitDatabase = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
const normalizedDate = convertPersianToEnglishNumerals(dateString);
const persianPattern = /^\d{4}\/\d{2}\/\d{2}$/;
if (persianPattern.test(normalizedDate)) {
const year = parseInt(normalizedDate.split("/")[0]);
if (year < 1500) {
const parts = normalizedDate.split("/");
const py = parseInt(parts[0]);
const pm = parseInt(parts[1]);
const pd = parseInt(parts[2]);
if (!isNaN(py) && !isNaN(pm) && !isNaN(pd)) {
try {
const gregorianDate = fromJalali(py, pm, pd);
const gy = gregorianDate.getFullYear();
const gm = String(gregorianDate.getMonth() + 1).padStart(2, "0");
const gd = String(gregorianDate.getDate()).padStart(2, "0");
return `${gy}/${gm}/${gd}`;
} catch (error) {
console.error("Error converting Persian to Gregorian:", error);
}
}
}
}
const gregorianPattern = /^\d{4}[-/]\d{2}[-/]\d{2}$/;
if (gregorianPattern.test(normalizedDate)) {
const year = parseInt(normalizedDate.split(/[-/]/)[0]);
if (year > 1900) {
return normalizedDate.replace(/-/g, "/");
}
}
try {
const date = new Date(normalizedDate);
if (!isNaN(date.getTime())) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}/${month}/${day}`;
}
} catch (error) {
console.error("Error formatting database date:", error);
}
return normalizedDate.replace(/-/g, "/");
};
export const formatDateForSubmit = (dateString, isExternalApi = false) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
if (isExternalApi) {
return formatDateForSubmitExternal(dateString);
} else {
return formatDateForSubmitDatabase(dateString);
}
};

View File

@@ -0,0 +1,95 @@
import * as yup from "yup";
import { normalizeDatabaseDate } from "./dateUtils";
export const getValidationSchema = (isEditMode) =>
yup.object({
national_id: yup
.string()
.required("کد ملی الزامی است")
.matches(/^[0-9]{10}$/, "کد ملی باید 10 رقم باشد"),
mobile: isEditMode
? yup
.string()
.nullable()
.test(
"mobile-format",
"شماره تلفن باید 11 رقم باشد",
(value) => !value || /^[0-9]{11}$/.test(value)
)
: yup
.string()
.required("شماره تلفن الزامی است")
.matches(/^[0-9]{11}$/, "شماره تلفن باید 11 رقم باشد"),
first_name: yup.string(),
last_name: yup.string(),
guild_name: yup.string(),
guild_category: yup.string(),
state: yup.string(),
city: yup.string(),
address: yup.string(),
license_expire_date: yup.string(),
license_status: yup.string(),
union_name: yup.string(),
postal_code: yup.string(),
guild_national_id: yup.string(),
is_foreigner: yup.string(),
national_code: yup.string(),
has_steward: yup.string(),
has_partner: yup.string(),
license_number: yup.string(),
isAccepted: yup
.boolean()
.test("req", "باید تعهد نامه را بپذیرید!", (val) => {
return val === true;
})
.required("این فیلد اجباری است!"),
});
export const getInitialValues = (guild) => ({
first_name: guild?.user?.firstName || "",
last_name: guild?.user?.lastName || "",
corporation_name: guild?.companyName || "",
national_id: guild?.user?.nationalId || "",
national_code: guild?.user?.nationalCode || "",
birth_date: normalizeDatabaseDate(guild?.user?.birthday || ""),
father_name: guild?.user?.fatherName || "",
gender: guild?.user?.gender || "",
person_city: guild?.user?.city || "",
is_alive: guild?.user?.isAlive || "",
guild_name: guild?.guildsName || "",
area_activity: guild?.areaActivity || "",
state: guild?.address?.province?.name || "",
city: guild?.address?.city?.name || "",
address: guild?.address?.address || "",
license_expire_date: normalizeDatabaseDate(guild?.licenseExpireDate || ""),
license_status: guild?.licenseStatus || "",
license_type: guild?.licenseType || "",
union_name: guild?.unionName || "",
postal_code: guild?.address?.postalCode || "",
phone_number: guild?.phoneNumber || "",
mobile: guild?.user?.mobile || "",
is_foreigner: guild?.is_foreign_national || "",
has_steward: guild?.hasSteward || "",
has_partner: guild?.hasPartner || "",
license_number: guild?.licenseNumber || "",
isAccepted: guild?.provinceAcceptState === "accepted" || false,
steward:
typeof guild?.steward === "boolean"
? guild.steward
: typeof guild?.isSteward === "boolean"
? guild.isSteward
: false,
guild:
typeof guild?.guild === "boolean"
? guild.guild
: typeof guild?.isGuild === "boolean"
? guild.isGuild
: false,
verify_mobile: guild?.verifyMobile || false,
guild_national_id: guild?.nationalId || "",
license_issue_date: normalizeDatabaseDate(guild?.licenseIssueDate || ""),
company_name: guild?.companyName || "",
company_identifier: guild?.companyIdentifier || "",
type_activity_name: guild?.typeActivityName || "",
active: guild?.active ?? null,
});

View File

@@ -0,0 +1,45 @@
import {
CLOSE_MODAL,
OPEN_MODAL,
} from "../../../../../lib/redux/slices/appSlice";
import { ProvinceManageGuildsSubmitRegisterCode } from "../../province-manage-guilds-submit-register-code/ProvinceManageGuildsSubmitRegisterCode";
export const handleSubmitSuccess = (
dispatch,
openNotif,
updateTable,
values,
responseData
) => {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
if (values.verify_mobile && responseData) {
dispatch(
OPEN_MODAL({
title: "ثبت کد احراز",
content: (
<ProvinceManageGuildsSubmitRegisterCode
item={responseData}
updateTable={updateTable}
/>
),
})
);
}
};
export const handleSubmitError = (openNotif, error) => {
openNotif({
vertical: "top",
horizontal: "center",
msg: error,
severity: "error",
});
};