556 lines
16 KiB
JavaScript
556 lines
16 KiB
JavaScript
import { useState, useEffect } from 'react';
|
||
import {
|
||
Box,
|
||
Button,
|
||
Divider,
|
||
MenuItem,
|
||
Autocomplete,
|
||
Stack,
|
||
TextField,
|
||
} from '@mui/material';
|
||
import Modal from '../../../../components/common/Modal/Modal';
|
||
import {
|
||
DangerOutlinedButton,
|
||
PrimaryButton,
|
||
} from '../../../../components/common/Buttons/Buttons';
|
||
import { useAuth } from '../../../../app/context/AuthProvider';
|
||
import { ROLES_NAME_ID } from '../../../../constants';
|
||
import {
|
||
ModalContainer,
|
||
FieldContainer,
|
||
FieldLabel,
|
||
StyledTextField,
|
||
StyledMultilineTextField,
|
||
StyledDatePicker,
|
||
} from './Modal.style';
|
||
|
||
const ModalEditAddVsp = ({
|
||
open,
|
||
onClose,
|
||
onSave,
|
||
onDelete,
|
||
initialData = null,
|
||
ssps = [],
|
||
loading = false,
|
||
}) => {
|
||
const { user } = useAuth();
|
||
const [formData, setFormData] = useState({
|
||
registration_number: '',
|
||
ssp_id: null,
|
||
vsp_type: '',
|
||
address: '',
|
||
approved_format: '',
|
||
open_date: null,
|
||
close_date: null,
|
||
notes: '',
|
||
location_form: '',
|
||
numbers: '',
|
||
area: null,
|
||
rent_contract_num: '',
|
||
rent_end_date: null,
|
||
system_code: '',
|
||
});
|
||
|
||
const [errors, setErrors] = useState({});
|
||
|
||
const isExecutor = user?.role_id !== ROLES_NAME_ID.admin;
|
||
const canSelectSsp = !isExecutor;
|
||
const readOnlyFields = [
|
||
'system_code',
|
||
'registration_number',
|
||
'ssp_id',
|
||
'vsp_type',
|
||
'address',
|
||
'approved_format',
|
||
'open_date',
|
||
'close_date',
|
||
'notes',
|
||
];
|
||
// Только эти поля readOnly для executor_rf
|
||
const isReadOnlyField = (fieldName) => {
|
||
if (!isExecutor) return false;
|
||
|
||
return readOnlyFields.includes(fieldName);
|
||
};
|
||
|
||
const locationFormOptions = [
|
||
{ value: 'аренда', label: 'Аренда' },
|
||
{ value: 'субаренда', label: 'Субаренда' },
|
||
{ value: 'встроенное помещение', label: 'Встроенное помещение' },
|
||
];
|
||
|
||
useEffect(() => {
|
||
if (initialData) {
|
||
setFormData({
|
||
registration_number: initialData.registration_number || '',
|
||
ssp_id: initialData.ssp_id || null,
|
||
vsp_type: initialData.vsp_type || '',
|
||
address: initialData.address || '',
|
||
approved_format: initialData.approved_format || '',
|
||
open_date: initialData.open_date
|
||
? new Date(initialData.open_date)
|
||
: null,
|
||
close_date: initialData.close_date
|
||
? new Date(initialData.close_date)
|
||
: null,
|
||
notes: initialData.notes || '',
|
||
location_form: initialData.location_form || '',
|
||
numbers: initialData.staff_count?.toString() || '',
|
||
area: initialData.area || null,
|
||
rent_contract_num: initialData.rent_contract_num || '',
|
||
rent_end_date: initialData.rent_end_date
|
||
? new Date(initialData.rent_end_date)
|
||
: null,
|
||
system_code: initialData.system_code || '',
|
||
});
|
||
} else {
|
||
setFormData({
|
||
registration_number: '',
|
||
ssp_id: isExecutor ? user?.ssp_id : null,
|
||
address: '',
|
||
vsp_type: '',
|
||
location: '',
|
||
approved_format: '',
|
||
open_date: null,
|
||
close_date: null,
|
||
notes: '',
|
||
location_form: '',
|
||
numbers: '',
|
||
area: null,
|
||
rent_contract_num: '',
|
||
rent_end_date: null,
|
||
system_code: '',
|
||
});
|
||
}
|
||
setErrors({});
|
||
}, [initialData, open, user, isExecutor]);
|
||
|
||
// Валидация только для registration_number и ssp_id
|
||
const validateForm = () => {
|
||
const newErrors = {};
|
||
|
||
if (!formData.registration_number || !formData.registration_number.trim()) {
|
||
newErrors.registration_number = 'Регистрационный номер ВСП обязателен';
|
||
}
|
||
|
||
if (canSelectSsp && !formData.ssp_id) {
|
||
newErrors.ssp_id = 'Региональный филиал обязателен';
|
||
}
|
||
|
||
if (isExecutor && !formData.ssp_id) {
|
||
newErrors.ssp_id = 'Региональный филиал не найден';
|
||
}
|
||
|
||
setErrors(newErrors);
|
||
return Object.keys(newErrors).length === 0;
|
||
};
|
||
|
||
const handleChange = (field) => (event) => {
|
||
if (isReadOnlyField(field)) return;
|
||
|
||
setFormData({
|
||
...formData,
|
||
[field]: event.target.value,
|
||
});
|
||
if (errors[field]) {
|
||
setErrors({
|
||
...errors,
|
||
[field]: '',
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleNumberChange = (field) => (event) => {
|
||
if (isReadOnlyField(field)) return;
|
||
|
||
const inputValue = event.target.value;
|
||
const isValidInput = /^\d*\.?\d*$/.test(inputValue) || inputValue === '';
|
||
if (isValidInput) {
|
||
setFormData({
|
||
...formData,
|
||
[field]: inputValue,
|
||
});
|
||
if (errors[field]) {
|
||
setErrors({
|
||
...errors,
|
||
[field]: '',
|
||
});
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleSspChange = (event, newValue) => {
|
||
if (isReadOnlyField('ssp_id')) return;
|
||
|
||
setFormData({
|
||
...formData,
|
||
ssp_id: newValue?.id || null,
|
||
});
|
||
if (errors.ssp_id) {
|
||
setErrors({
|
||
...errors,
|
||
ssp_id: '',
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleDateChange = (field) => (date) => {
|
||
if (isReadOnlyField(field)) return;
|
||
|
||
setFormData({
|
||
...formData,
|
||
[field]: date,
|
||
});
|
||
if (errors[field]) {
|
||
setErrors({
|
||
...errors,
|
||
[field]: '',
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleSubmit = async () => {
|
||
// Выполняем валидацию перед отправкой
|
||
if (!validateForm()) return;
|
||
|
||
const submitData = {
|
||
...formData,
|
||
numbers: formData.staff_count ? parseInt(formData.staff_count, 10) : null,
|
||
area: formData.area ? parseFloat(formData.area) : null,
|
||
open_date: formData.open_date
|
||
? formData.open_date.toLocaleDateString('en-CA')
|
||
: null,
|
||
close_date: formData.close_date
|
||
? formData.close_date.toLocaleDateString('en-CA')
|
||
: null,
|
||
rent_end_date: formData.rent_end_date
|
||
? formData.rent_end_date.toLocaleDateString('en-CA')
|
||
: null,
|
||
};
|
||
|
||
if (user.role_id !== ROLES_NAME_ID.admin) {
|
||
const submitDataForExecutor = Object.fromEntries(
|
||
Object.entries(submitData).filter(
|
||
([key]) => !readOnlyFields.includes(key),
|
||
),
|
||
);
|
||
await onSave(submitDataForExecutor);
|
||
} else {
|
||
await onSave(submitData);
|
||
}
|
||
|
||
if (!loading) {
|
||
handleClose();
|
||
}
|
||
};
|
||
|
||
const handleClose = () => {
|
||
if (!loading) {
|
||
setFormData({
|
||
registration_number: '',
|
||
ssp_id: null,
|
||
vsp_type: '',
|
||
location: '',
|
||
approved_format: '',
|
||
open_date: null,
|
||
close_date: null,
|
||
notes: '',
|
||
location_form: '',
|
||
numbers: '',
|
||
area: '',
|
||
rent_contract_num: '',
|
||
rent_end_date: null,
|
||
system_code: '',
|
||
});
|
||
setErrors({});
|
||
onClose();
|
||
}
|
||
};
|
||
|
||
const handleDelete = () => {
|
||
onDelete?.(initialData.id);
|
||
handleClose();
|
||
};
|
||
|
||
const selectedSsp = ssps.find((ssp) => ssp.id === formData.ssp_id) || null;
|
||
|
||
const showActions = true;
|
||
const showDeleteButton = !isExecutor;
|
||
|
||
return (
|
||
<Modal
|
||
title={initialData ? 'Редактирование ВСП' : 'Добавление ВСП'}
|
||
open={open}
|
||
onClose={handleClose}
|
||
customSize={'60'}
|
||
actions={
|
||
showActions && (
|
||
<Stack
|
||
direction={'row'}
|
||
justifyContent={'space-between'}
|
||
width={'100%'}
|
||
>
|
||
{showDeleteButton && (
|
||
<DangerOutlinedButton
|
||
onClick={handleDelete}
|
||
style={{ height: '2.5rem' }}
|
||
>
|
||
Удалить
|
||
</DangerOutlinedButton>
|
||
)}
|
||
{!showDeleteButton && <Box />} {/* Спейсер для выравнивания */}
|
||
<Stack spacing={'.5rem'} direction={'row'}>
|
||
<Button
|
||
variant="grey"
|
||
onClick={handleClose}
|
||
style={{ height: '2.5rem' }}
|
||
disabled={loading}
|
||
>
|
||
Отменить
|
||
</Button>
|
||
<PrimaryButton
|
||
variant="contained"
|
||
onClick={handleSubmit}
|
||
style={{ height: '2.5rem' }}
|
||
disabled={loading}
|
||
>
|
||
{loading
|
||
? 'Сохранение...'
|
||
: initialData
|
||
? 'Сохранить'
|
||
: 'Создать'}
|
||
</PrimaryButton>
|
||
</Stack>
|
||
</Stack>
|
||
)
|
||
}
|
||
>
|
||
<Divider sx={{ mb: '1.5rem' }} />
|
||
|
||
<ModalContainer maxHeight="60vh">
|
||
<FieldContainer>
|
||
<FieldLabel>Код РФ</FieldLabel>
|
||
<StyledTextField
|
||
value={formData.system_code}
|
||
onChange={handleChange('system_code')}
|
||
placeholder="Введите код РФ"
|
||
error={!!errors.system_code}
|
||
helperText={errors.system_code}
|
||
disabled={loading || isReadOnlyField('system_code')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>
|
||
Регистрационный номер ВСП <span style={{ color: 'red' }}>*</span>
|
||
</FieldLabel>
|
||
<StyledTextField
|
||
value={formData.registration_number}
|
||
onChange={handleChange('registration_number')}
|
||
placeholder="Введите регистрационный номер ВСП"
|
||
error={!!errors.registration_number}
|
||
helperText={errors.registration_number}
|
||
disabled={loading || isReadOnlyField('registration_number')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
{canSelectSsp && (
|
||
<FieldContainer>
|
||
<FieldLabel>
|
||
Региональный филиал <span style={{ color: 'red' }}>*</span>
|
||
</FieldLabel>
|
||
<Autocomplete
|
||
options={ssps}
|
||
getOptionLabel={(option) => option.title || option.name || ''}
|
||
value={selectedSsp}
|
||
onChange={handleSspChange}
|
||
disabled={loading || isReadOnlyField('ssp_id')}
|
||
getOptionKey={(option) =>
|
||
option.id || option.name || option.title
|
||
}
|
||
renderInput={(params) => (
|
||
<TextField
|
||
{...params}
|
||
placeholder="Выберите региональный филиал"
|
||
size="small"
|
||
error={!!errors.ssp_id}
|
||
helperText={errors.ssp_id}
|
||
disabled={loading || isReadOnlyField('ssp_id')}
|
||
sx={{
|
||
'& .MuiInputBase-root': {
|
||
height: '2.69rem',
|
||
},
|
||
}}
|
||
/>
|
||
)}
|
||
noOptionsText="Не найдено"
|
||
sx={{ width: '100%' }}
|
||
/>
|
||
</FieldContainer>
|
||
)}
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Вид ВСП</FieldLabel>
|
||
<StyledTextField
|
||
value={formData.vsp_type}
|
||
onChange={handleChange('vsp_type')}
|
||
placeholder="Введите вид ВСП"
|
||
disabled={loading || isReadOnlyField('vsp_type')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Адрес ВСП</FieldLabel>
|
||
<StyledMultilineTextField
|
||
value={formData.address}
|
||
onChange={handleChange('address')}
|
||
placeholder="Введите адрес ВСП"
|
||
error={!!errors.address}
|
||
helperText={errors.address}
|
||
disabled={loading || isReadOnlyField('address')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Утвержденный формат</FieldLabel>
|
||
<StyledTextField
|
||
value={formData.approved_format}
|
||
onChange={handleChange('approved_format')}
|
||
placeholder="Введите утвержденный формат"
|
||
error={!!errors.approved_format}
|
||
helperText={errors.approved_format}
|
||
disabled={loading || isReadOnlyField('approved_format')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Дата открытия ВСП</FieldLabel>
|
||
<StyledDatePicker
|
||
value={formData.open_date}
|
||
onChange={handleDateChange('open_date')}
|
||
disabled={loading || isReadOnlyField('open_date')}
|
||
error={!!errors.open_date}
|
||
helperText={errors.open_date}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Дата закрытия ВСП</FieldLabel>
|
||
<StyledDatePicker
|
||
value={formData.close_date}
|
||
onChange={handleDateChange('close_date')}
|
||
disabled={loading || isReadOnlyField('close_date')}
|
||
error={!!errors.close_date}
|
||
helperText={errors.close_date}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Примечания</FieldLabel>
|
||
<StyledMultilineTextField
|
||
value={formData.notes}
|
||
onChange={handleChange('notes')}
|
||
placeholder="Введите примечания"
|
||
error={!!errors.notes}
|
||
helperText={errors.notes}
|
||
disabled={loading || isReadOnlyField('notes')}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Форма расположения ВСП</FieldLabel>
|
||
<StyledTextField
|
||
select
|
||
value={formData.location_form}
|
||
onChange={handleChange('location_form')}
|
||
placeholder="Выберите форму расположения"
|
||
error={!!errors.location_form}
|
||
helperText={errors.location_form}
|
||
disabled={loading}
|
||
slotProps={{
|
||
select: {
|
||
MenuProps: {
|
||
sx: { zIndex: 1301 },
|
||
style: { zIndex: 1301 },
|
||
PaperProps: {
|
||
sx: {
|
||
zIndex: 1301,
|
||
backgroundColor: '#fff',
|
||
color: '#171a1c',
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}}
|
||
>
|
||
{locationFormOptions.map((option) => (
|
||
<MenuItem key={option.value} value={option.value}>
|
||
{option.label}
|
||
</MenuItem>
|
||
))}
|
||
</StyledTextField>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Штатная численность ВСП</FieldLabel>
|
||
<StyledTextField
|
||
type="text"
|
||
value={formData.staff_count}
|
||
onChange={handleNumberChange('numbers')}
|
||
placeholder="Введите численность"
|
||
error={!!errors.staff_count}
|
||
helperText={errors.staff_count}
|
||
disabled={loading}
|
||
slotProps={{
|
||
htmlInput: { min: 1, step: 1 },
|
||
}}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Площадь помещений ВСП</FieldLabel>
|
||
<StyledTextField
|
||
type="text"
|
||
value={formData.area?.toString() || ''}
|
||
onChange={handleNumberChange('area')}
|
||
placeholder="Введите площадь помещений"
|
||
error={!!errors.area}
|
||
helperText={errors.area}
|
||
disabled={loading}
|
||
slotProps={{
|
||
htmlInput: { min: 1, step: 1 },
|
||
}}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Номер договора аренды</FieldLabel>
|
||
<StyledTextField
|
||
value={formData.rent_contract_num}
|
||
onChange={handleChange('rent_contract_num')}
|
||
placeholder="Введите номер договора аренды"
|
||
error={!!errors.rent_contract_num}
|
||
helperText={errors.rent_contract_num}
|
||
disabled={loading}
|
||
/>
|
||
</FieldContainer>
|
||
|
||
<FieldContainer>
|
||
<FieldLabel>Срок окончания договора аренды</FieldLabel>
|
||
<StyledDatePicker
|
||
value={formData.rent_end_date}
|
||
onChange={handleDateChange('rent_end_date')}
|
||
disabled={loading}
|
||
error={!!errors.rent_end_date}
|
||
helperText={errors.rent_end_date}
|
||
/>
|
||
</FieldContainer>
|
||
</ModalContainer>
|
||
|
||
<Divider sx={{ mt: '1.5rem' }} />
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
export default ModalEditAddVsp;
|