Files
Rasadyar_FrontEnd/src/features/province/components/create-guilds/CreateGuilds.js

756 lines
24 KiB
JavaScript

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 { cityGetProvinces } from "../../../city/services/CityGetProvinces";
import { cityGetCity } from "../../../city/services/city-get-city";
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 [provinces, setProvinces] = useState([]);
const [provinceCities, setProvinceCities] = 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)
national_id: values?.national_id,
gender: values?.gender,
first_name: values?.first_name,
last_name: values?.last_name,
national_code: values?.last_name,
is_alive: values?.is_alive,
birth_date: values?.birth_date,
father_name: values?.father_name,
mobile: values?.mobile,
};
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 || []);
});
// Fetch provinces for province/city selection
dispatch(cityGetProvinces()).then((r) => {
if (r?.payload?.data) {
setProvinces(r.payload.data);
}
});
}, []);
useEffect(() => {
// Initialize Formik's guilds array if we have initial guild data
if (guild && guildsList.length > 0) {
const guildsForFormik = guildsList.map((guildItem) => {
const combinedGuild = {
...guildItem,
user: guild?.user || {},
};
const initialValues = getInitialValues(combinedGuild);
return {
steward: initialValues.steward || false,
guild: initialValues.guild || false,
license_number: initialValues.license_number ?? "",
};
});
formik.setFieldValue("guilds", guildsForFormik, false).then(() => {
formik.validateField("guilds");
});
}
formik.validateForm();
}, []);
// Set province ID from state name when provinces are loaded
useEffect(() => {
if (
formik.values.state &&
provinces.length > 0 &&
!formik.values.province
) {
const province = provinces.find((p) => p.name === formik.values.state);
if (province) {
formik.setFieldValue("province", province.key);
}
}
}, [provinces, formik.values.state, formik.values.province]);
// Fetch cities when province is selected
useEffect(() => {
if (formik.values.province) {
dispatch(cityGetCity(formik.values.province)).then((r) => {
if (r?.payload?.data) {
setProvinceCities(r.payload.data);
}
});
} else {
setProvinceCities([]);
}
}, [formik.values.province, dispatch]);
// Set city ID from person_city name when provinceCities are loaded
useEffect(() => {
if (
formik.values.person_city &&
!formik.values.city &&
provinceCities.length > 0
) {
const city = provinceCities.find(
(c) => c.name === formik.values.person_city
);
if (city) {
formik.setFieldValue("city", city.key);
}
}
}, [provinceCities, formik.values.person_city, formik.values.city]);
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);
// Update Formik's guilds array for validation (include validated fields)
const guildsForFormik = initialGuildValues.map((g) => ({
steward: g.steward ?? false,
guild: g.guild ?? false,
license_number: g.license_number ?? "",
}));
formik.setFieldValue("guilds", guildsForFormik, true).then(() => {
formik.validateField("guilds");
});
setExpandedAccordion(0);
} else {
setGuildsList([]);
setGuildsFormValues([]);
formik.setFieldValue("guilds", [], true).then(() => {
formik.validateField("guilds");
});
}
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)]);
// Add to Formik's guilds array for validation
const currentGuilds = formik.values.guilds || [];
formik
.setFieldValue(
"guilds",
[...currentGuilds, { steward: false, guild: false }],
true
)
.then(() => {
formik.validateField("guilds");
});
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));
// Remove from Formik's guilds array
const currentGuilds = formik.values.guilds || [];
formik
.setFieldValue(
"guilds",
currentGuilds.filter((_, i) => i !== index),
true
)
.then(() => {
formik.validateField("guilds");
});
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)
);
// Remove from Formik's guilds array
const currentGuilds = formik.values.guilds || [];
formik
.setFieldValue(
"guilds",
currentGuilds.filter((_, i) => i !== deleteDialogIndex),
true
)
.then(() => {
formik.validateField("guilds");
});
// 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;
});
// Sync validated guild fields to Formik's guilds array (schema: license_number, steward, guild)
if (
fieldName === "steward" ||
fieldName === "guild" ||
fieldName === "license_number"
) {
const currentGuilds = formik.values.guilds || [];
const updatedGuilds = [...currentGuilds];
if (!updatedGuilds[index]) {
updatedGuilds[index] = { steward: false, guild: false };
}
updatedGuilds[index] = {
...updatedGuilds[index],
[fieldName]: value,
};
formik.setFieldValue("guilds", updatedGuilds, true).then(() => {
formik.validateField("guilds");
});
}
},
[formik]
);
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}
sx={{
maxHeight: "80vh",
overflow: "auto",
p: 2,
minWidth:
!guild && !isInquiryDone
? "auto"
: {
xs: "96vw",
md: "90vw",
nlg: "1280px",
},
}}
>
{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}
provinces={provinces}
provinceCities={provinceCities}
guildsList={guildsList}
/>
<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}
isSuperAdmin={isSuperAdmin}
cities={
provinceCities.length > 0 ? provinceCities : cities
}
typeActivities={typeActivities}
onDelete={() => handleDeleteGuild(index)}
canDelete={
(guildsList.length > 1 || !guild) && isAdmin
}
guildFormValues={guildsFormValues[index]}
onGuildValuesChange={handleGuildValuesChange}
expanded={expandedAccordion === index}
onChange={handleAccordionChange(index)}
mainFormik={formik}
/>
</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
}
formData={guildsList.map((guildItem, index) => {
const guildValues = guildsFormValues[index];
const combinedValues = {
...guildValues, // Guild-specific info (overrides if same keys exist)
};
return prepareSubmitData(
combinedValues,
guildItem,
originalPhoneNumber,
hasInquiry
);
})}
isKillHouse={isKillHouse}
onSubmit={formik.handleSubmit}
/>
</Grid>
</>
)}
</Grid>
<DeleteConfirmationDialog
open={deleteDialogOpen}
onClose={handleCloseDeleteDialog}
onConfirm={handleConfirmDelete}
isDeleting={isDeletingGuild}
/>
</form>
);
};