Merge pull request 'updating access for projects' (#107) from fix-sticky-columns into test
Reviewed-on: #107
This commit is contained in:
commit
36a534f06f
@ -12,14 +12,14 @@ export const DEFAULT_PROJECT_CONFIG = {
|
|||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
min: 0,
|
min: 0,
|
||||||
step: 1,
|
step: 1,
|
||||||
required_field: false,
|
required_field: true,
|
||||||
},
|
},
|
||||||
'Тип проекта развития': {
|
'Тип проекта развития': {
|
||||||
type: 'multiselect',
|
type: 'multiselect',
|
||||||
options: ['Открытие ВСП', 'Закрытие ВСП', 'Переезд ВСП', 'Реновация РФ', 'Открытие УРМ'],
|
options: ['Открытие ВСП', 'Закрытие ВСП', 'Переезд ВСП', 'Реновация РФ', 'Открытие УРМ'],
|
||||||
placeholder: 'Выберите тип проекта',
|
placeholder: 'Выберите тип проекта',
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
required_field: false,
|
required_field: true,
|
||||||
},
|
},
|
||||||
'Название проекта': {
|
'Название проекта': {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -29,16 +29,17 @@ export const DEFAULT_PROJECT_CONFIG = {
|
|||||||
},
|
},
|
||||||
'Формат ВСП': {
|
'Формат ВСП': {
|
||||||
type: 'multiselect',
|
type: 'multiselect',
|
||||||
options: ['Фланговый', 'Типовой', 'Розничный', 'МСБ', 'Лёгкий', 'Мини', 'Розничный-киоск', 'МБО', 'Офис самообслуживания', 'Другое'],
|
options: ['Флагманский', 'Типовой', 'Розничный', 'МСБ', 'Лёгкий', 'Мини', 'Розничный-киоск', 'МБО', 'Офис самообслуживания', 'Другое'],
|
||||||
placeholder: 'Выберите формат ВСП',
|
placeholder: 'Выберите формат ВСП',
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
required_field: false,
|
required_field: true,
|
||||||
},
|
},
|
||||||
'Адрес объекта': {
|
'Адрес объекта': {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
placeholder: 'Введите адрес объекта',
|
placeholder: 'Введите адрес объекта',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
required_field: false,
|
required_field: true,
|
||||||
|
|
||||||
},
|
},
|
||||||
'Целевая численность, чел.': {
|
'Целевая численность, чел.': {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
@ -53,7 +54,8 @@ export const DEFAULT_PROJECT_CONFIG = {
|
|||||||
options: ['Собственность', 'Аренда', 'Субаренда'],
|
options: ['Собственность', 'Аренда', 'Субаренда'],
|
||||||
placeholder: 'Выберите тип размещения',
|
placeholder: 'Выберите тип размещения',
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
required_field: false,
|
required_field: true,
|
||||||
|
|
||||||
},
|
},
|
||||||
'Площадь размещения, м2': {
|
'Площадь размещения, м2': {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
|||||||
@ -135,7 +135,7 @@ export default function FormPage() {
|
|||||||
'Формат ВСП': {
|
'Формат ВСП': {
|
||||||
type: 'multiselect',
|
type: 'multiselect',
|
||||||
options: [
|
options: [
|
||||||
'Фланговый',
|
'Флагманский',
|
||||||
'Типовой',
|
'Типовой',
|
||||||
'Розничный',
|
'Розничный',
|
||||||
'МСБ',
|
'МСБ',
|
||||||
|
|||||||
@ -202,11 +202,13 @@ const SummaryPage = () => {
|
|||||||
|
|
||||||
const onEdit = (row) => {
|
const onEdit = (row) => {
|
||||||
const isSmeta = row.depth > 0;
|
const isSmeta = row.depth > 0;
|
||||||
const projectId = isSmeta ? row.getParentRow()?.original?.id : row.original.id;
|
const parentProject = isSmeta ? row.getParentRow()?.original : row.original;
|
||||||
|
const projectId = parentProject?.id;
|
||||||
setSelectedRowData({
|
setSelectedRowData({
|
||||||
...row.original,
|
...row.original,
|
||||||
_rowType: isSmeta ? 'smeta' : 'project',
|
_rowType: isSmeta ? 'smeta' : 'project',
|
||||||
_projectId: projectId,
|
_projectId: projectId,
|
||||||
|
_projectFundingByKoDecision: parentProject?.funding_by_ko_decision,
|
||||||
});
|
});
|
||||||
setEditModalOpen(true);
|
setEditModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Box, IconButton, Tooltip } from '@mui/material';
|
import { Box, IconButton, Tooltip } from '@mui/material';
|
||||||
|
import { differenceInMonths, isValid, parseISO } from 'date-fns';
|
||||||
import { Back, EditPenSvg, TrashSvg } from '../../components/common/icons/icons';
|
import { Back, EditPenSvg, TrashSvg } from '../../components/common/icons/icons';
|
||||||
import StatusBadge from './components/StatusBadge/StatusBadge';
|
import StatusBadge from './components/StatusBadge/StatusBadge';
|
||||||
import { DEVELOMENT_BLOCK, FUNDING_BY_DECISION } from './constants';
|
import { DEVELOMENT_BLOCK, FUNDING_BY_DECISION } from './constants';
|
||||||
@ -63,6 +64,19 @@ export const createFirstColumns = (handlers) => {
|
|||||||
header: 'Дата открытия / переезда / закрытия',
|
header: 'Дата открытия / переезда / закрытия',
|
||||||
size: 160,
|
size: 160,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'project_implementation_months',
|
||||||
|
accessorFn: (row) => {
|
||||||
|
if (!Array.isArray(row.sub_rows) || !row.board_decision_date) return null;
|
||||||
|
|
||||||
|
const boardDecisionDate = parseISO(row.board_decision_date);
|
||||||
|
if (!isValid(boardDecisionDate)) return null;
|
||||||
|
|
||||||
|
return differenceInMonths(new Date(), boardDecisionDate);
|
||||||
|
},
|
||||||
|
header: 'Время реализации проекта в месяцах',
|
||||||
|
size: 180,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'project_type',
|
accessorKey: 'project_type',
|
||||||
header: 'Тип проекта',
|
header: 'Тип проекта',
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import React from 'react';
|
|||||||
import { useAuth } from '../../../../app/context/AuthProvider';
|
import { useAuth } from '../../../../app/context/AuthProvider';
|
||||||
import { DangerOutlinedButton, PrimaryButton } from '../../../../components/common/Buttons/Buttons';
|
import { DangerOutlinedButton, PrimaryButton } from '../../../../components/common/Buttons/Buttons';
|
||||||
import { DEVELOMENT_BLOCK, FUNDING_BY_DECISION } from '../../constants';
|
import { DEVELOMENT_BLOCK, FUNDING_BY_DECISION } from '../../constants';
|
||||||
import { canEditFirstTableField, isFirstTableFieldDependencySatisfied } from '../../constants/fieldAccess';
|
import { canEditFirstTableField, hasReserveValuesInChildSmetas, isFirstTableFieldDependencySatisfied } from '../../constants/fieldAccess';
|
||||||
|
|
||||||
const EditModal = ({ open, onClose, rowData, onSave, isSaving = false, orgUnitNames = {} }) => {
|
const EditModal = ({ open, onClose, rowData, onSave, isSaving = false, orgUnitNames = {} }) => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@ -80,8 +80,10 @@ const EditModal = ({ open, onClose, rowData, onSave, isSaving = false, orgUnitNa
|
|||||||
// Компонент для поля ввода
|
// Компонент для поля ввода
|
||||||
const renderField = (field) => {
|
const renderField = (field) => {
|
||||||
const rawValue = formData[field.key];
|
const rawValue = formData[field.key];
|
||||||
|
const isFundingByDecisionLocked = field.key === 'funding_by_ko_decision' && hasReserveValuesInChildSmetas(formData);
|
||||||
const isFieldDisabled =
|
const isFieldDisabled =
|
||||||
field.disabled ||
|
field.disabled ||
|
||||||
|
isFundingByDecisionLocked ||
|
||||||
!canEditFirstTableField(field.key, user?.role_id) ||
|
!canEditFirstTableField(field.key, user?.role_id) ||
|
||||||
!isFirstTableFieldDependencySatisfied(field.key, formData);
|
!isFirstTableFieldDependencySatisfied(field.key, formData);
|
||||||
const fieldValue = field.valueType === 'boolean' && rawValue !== null && rawValue !== undefined
|
const fieldValue = field.valueType === 'boolean' && rawValue !== null && rawValue !== undefined
|
||||||
@ -143,6 +145,11 @@ const EditModal = ({ open, onClose, rowData, onSave, isSaving = false, orgUnitNa
|
|||||||
return <MenuItem key={value} value={value}>{label}</MenuItem>;
|
return <MenuItem key={value} value={value}>{label}</MenuItem>;
|
||||||
})]}
|
})]}
|
||||||
</TextField>
|
</TextField>
|
||||||
|
{isFundingByDecisionLocked && (
|
||||||
|
<Typography sx={{ mt: 0.5, color: 'warning.main', fontSize: '0.75rem', lineHeight: 1.4 }}>
|
||||||
|
Поле нельзя изменить: в одной из дочерних смет заполнено значение «Из резерва Банка в смету ПРРС АХР» или «Из резерва Банка в смету ПРРС КВ».
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,14 +11,14 @@ const ALL_ROLES = [
|
|||||||
// Матрица доступа к полям первой таблицы.
|
// Матрица доступа к полям первой таблицы.
|
||||||
export const FIRST_TABLE_FIELD_ACCESS = {
|
export const FIRST_TABLE_FIELD_ACCESS = {
|
||||||
name: ADMIN_ROLES,
|
name: ADMIN_ROLES,
|
||||||
org_unit_id: ADMIN_ROLES,
|
org_unit_id: ALL_ROLES,
|
||||||
status: DFIP_ROLES,
|
status: DFIP_ROLES,
|
||||||
technical_number: [],
|
technical_number: [],
|
||||||
object_address: ALL_ROLES,
|
object_address: ALL_ROLES,
|
||||||
vsp_format: ALL_ROLES,
|
vsp_format: ALL_ROLES,
|
||||||
placement_type: ADMIN_ROLES,
|
placement_type: ALL_ROLES,
|
||||||
staff_count: ADMIN_ROLES,
|
staff_count: ALL_ROLES,
|
||||||
total_area: ADMIN_ROLES,
|
total_area: ALL_ROLES,
|
||||||
project_type: ALL_ROLES,
|
project_type: ALL_ROLES,
|
||||||
krf_decision_date: ALL_ROLES,
|
krf_decision_date: ALL_ROLES,
|
||||||
fk_decision_date: ALL_ROLES,
|
fk_decision_date: ALL_ROLES,
|
||||||
@ -41,10 +41,33 @@ export const FIRST_TABLE_FIELD_DEPENDENCIES = {
|
|||||||
board_decision_date: "fk_decision_date",
|
board_decision_date: "fk_decision_date",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FIRST_TABLE_FIELD_VALUE_REQUIREMENTS = {
|
||||||
|
reserve_to_prrs_ahr: {
|
||||||
|
fieldKey: "_projectFundingByKoDecision",
|
||||||
|
value: "bank_reserve",
|
||||||
|
},
|
||||||
|
reserve_to_prrs_kv: {
|
||||||
|
fieldKey: "_projectFundingByKoDecision",
|
||||||
|
value: "bank_reserve",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const canEditFirstTableField = (fieldKey, roleId) =>
|
export const canEditFirstTableField = (fieldKey, roleId) =>
|
||||||
FIRST_TABLE_FIELD_ACCESS[fieldKey]?.includes(roleId) ?? false;
|
FIRST_TABLE_FIELD_ACCESS[fieldKey]?.includes(roleId) ?? false;
|
||||||
|
|
||||||
|
const isFilled = (value) => value !== null && value !== undefined && value !== "";
|
||||||
|
|
||||||
|
export const hasReserveValuesInChildSmetas = (project) =>
|
||||||
|
(project?.sub_rows || []).some(
|
||||||
|
(smeta) => isFilled(smeta.reserve_to_prrs_ahr) || isFilled(smeta.reserve_to_prrs_kv),
|
||||||
|
);
|
||||||
|
|
||||||
export const isFirstTableFieldDependencySatisfied = (fieldKey, rowData) => {
|
export const isFirstTableFieldDependencySatisfied = (fieldKey, rowData) => {
|
||||||
const dependencyKey = FIRST_TABLE_FIELD_DEPENDENCIES[fieldKey];
|
const dependencyKey = FIRST_TABLE_FIELD_DEPENDENCIES[fieldKey];
|
||||||
return !dependencyKey || Boolean(rowData?.[dependencyKey]);
|
if (dependencyKey && !rowData?.[dependencyKey]) return false;
|
||||||
|
|
||||||
|
const valueRequirement = FIRST_TABLE_FIELD_VALUE_REQUIREMENTS[fieldKey];
|
||||||
|
if (valueRequirement && rowData?.[valueRequirement.fieldKey] !== valueRequirement.value) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -120,7 +120,7 @@ export const ModalCreateProject = ({
|
|||||||
switch (config.type) {
|
switch (config.type) {
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
return (
|
return (
|
||||||
<Box key={fieldName}>
|
<Box key={fieldName} sx={{ mb: hasError ? '0.5rem' : 0 }}>
|
||||||
<Typography
|
<Typography
|
||||||
className='gray-color'
|
className='gray-color'
|
||||||
sx={{
|
sx={{
|
||||||
@ -159,7 +159,7 @@ export const ModalCreateProject = ({
|
|||||||
|
|
||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<Box key={fieldName}>
|
<Box key={fieldName} sx={{ mb: hasError ? '0.5rem' : 0 }}>
|
||||||
<Typography
|
<Typography
|
||||||
className='gray-color'
|
className='gray-color'
|
||||||
sx={{
|
sx={{
|
||||||
@ -191,7 +191,7 @@ export const ModalCreateProject = ({
|
|||||||
|
|
||||||
case 'number':
|
case 'number':
|
||||||
return (
|
return (
|
||||||
<Box key={fieldName}>
|
<Box key={fieldName} sx={{ mb: hasError ? '0.5rem' : 0 }}>
|
||||||
<Typography
|
<Typography
|
||||||
className='gray-color'
|
className='gray-color'
|
||||||
sx={{
|
sx={{
|
||||||
@ -264,9 +264,11 @@ export const ModalCreateProject = ({
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '1.25rem',
|
gap: '1.25rem',
|
||||||
'& .sameSize': {
|
'& .sameSize': {
|
||||||
height: '2.69rem',
|
|
||||||
minWidth: '39.75rem',
|
minWidth: '39.75rem',
|
||||||
},
|
},
|
||||||
|
'& .sameSize.MuiInputBase-root, & .sameSize .MuiInputBase-root': {
|
||||||
|
height: '2.69rem',
|
||||||
|
},
|
||||||
'& .gray-color': { color: '#4A5565' },
|
'& .gray-color': { color: '#4A5565' },
|
||||||
}}>
|
}}>
|
||||||
{Object.entries(config).map(([fieldName, config]) => renderField(fieldName, config))}
|
{Object.entries(config).map(([fieldName, config]) => renderField(fieldName, config))}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user