fix: ограничить управление строками и этапами по правам доступа
This commit is contained in:
parent
d66e4fe375
commit
ee3b87c69a
@ -8,7 +8,6 @@ import DictsNavigatorPage from '../pages/DictsNavigatorPage/DictsNavigatorPage.j
|
|||||||
import FormsPage from '../pages/FormsPage';
|
import FormsPage from '../pages/FormsPage';
|
||||||
import LoginPage from '../pages/LoginPage';
|
import LoginPage from '../pages/LoginPage';
|
||||||
import NewFormTablePage from '../pages/NewFormTablePage.jsx';
|
import NewFormTablePage from '../pages/NewFormTablePage.jsx';
|
||||||
import NewTablePage from '../pages/NewTablePage.jsx';
|
|
||||||
import NavigationProjectPage from '../pages/ProjectPages/NavigationProjectPage.jsx';
|
import NavigationProjectPage from '../pages/ProjectPages/NavigationProjectPage.jsx';
|
||||||
import Svod2Page from '../pages/Svod2Page/Svod2Page.jsx';
|
import Svod2Page from '../pages/Svod2Page/Svod2Page.jsx';
|
||||||
import { SvodProvider } from '../pages/SvodPage/SvodContext.jsx';
|
import { SvodProvider } from '../pages/SvodPage/SvodContext.jsx';
|
||||||
@ -52,7 +51,6 @@ export const AppRoutes = () => {
|
|||||||
<Route path='/admin_panel/audit-log' element={<AuditLogsPage />} />
|
<Route path='/admin_panel/audit-log' element={<AuditLogsPage />} />
|
||||||
<Route path='/dicts/dict-vsp' element={<DictVspPage />} />
|
<Route path='/dicts/dict-vsp' element={<DictVspPage />} />
|
||||||
<Route path='/dicts-navigator' element={<DictsNavigatorPage />} />
|
<Route path='/dicts-navigator' element={<DictsNavigatorPage />} />
|
||||||
<Route path='/new-table' element={<NewTablePage />} />
|
|
||||||
<Route path='/tables-new' element={<TablesTest />} />
|
<Route path='/tables-new' element={<TablesTest />} />
|
||||||
<Route path='/tables/:form/:sheetName' element={<NewFormTablePage />} />
|
<Route path='/tables/:form/:sheetName' element={<NewFormTablePage />} />
|
||||||
<Route path='/table/form/:formId/form-type/:formType/:sheetName/:direction/:year' element={<NewFormTablePage />} />
|
<Route path='/table/form/:formId/form-type/:formType/:sheetName/:direction/:year' element={<NewFormTablePage />} />
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { getRowId } from './utils/rowUtils';
|
|||||||
|
|
||||||
import { DictVspApi } from '../../api/dict-vsp';
|
import { DictVspApi } from '../../api/dict-vsp';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
|
import { ROLES_NAME_ID } from '../../constants/constants';
|
||||||
import { FormsNoCanAddRow } from './constants/formConfig';
|
import { FormsNoCanAddRow } from './constants/formConfig';
|
||||||
|
|
||||||
const CONFIG_CACHE = new Map();
|
const CONFIG_CACHE = new Map();
|
||||||
@ -121,6 +122,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
const [createModalType, setCreateModalType] = useState(null);
|
const [createModalType, setCreateModalType] = useState(null);
|
||||||
|
|
||||||
const isFormNoCanAddRow = useMemo(() => FormsNoCanAddRow[formType]?.includes(sheetName) ?? false, [formType, sheetName]);
|
const isFormNoCanAddRow = useMemo(() => FormsNoCanAddRow[formType]?.includes(sheetName) ?? false, [formType, sheetName]);
|
||||||
|
const canModifyRows = userRoleId === ROLES_NAME_ID.admin || columnsCurStage.length > 0;
|
||||||
|
|
||||||
const isVspAdditionRow = useMemo(() => {
|
const isVspAdditionRow = useMemo(() => {
|
||||||
if (!formType || !sheetName) return false;
|
if (!formType || !sheetName) return false;
|
||||||
@ -664,6 +666,10 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
}, [getSelectedRow]);
|
}, [getSelectedRow]);
|
||||||
|
|
||||||
const handleDeleteRow = useCallback(() => {
|
const handleDeleteRow = useCallback(() => {
|
||||||
|
if (!canModifyRows) {
|
||||||
|
toast.error('Удаление строк недоступно: нет доступных для редактирования столбцов');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const row = getSelectedRow();
|
const row = getSelectedRow();
|
||||||
if (!row) {
|
if (!row) {
|
||||||
toast.error('Выделите строку для удаления');
|
toast.error('Выделите строку для удаления');
|
||||||
@ -671,9 +677,13 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
}
|
}
|
||||||
contextDeleteRow(getRowId(row));
|
contextDeleteRow(getRowId(row));
|
||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
}, [contextDeleteRow, getSelectedRow]);
|
}, [canModifyRows, contextDeleteRow, getSelectedRow]);
|
||||||
|
|
||||||
const addRow = useCallback(() => {
|
const addRow = useCallback(() => {
|
||||||
|
if (!canModifyRows) {
|
||||||
|
toast.error('Добавление строк недоступно: нет доступных для редактирования столбцов');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isVspAdditionRow) {
|
if (isVspAdditionRow) {
|
||||||
return handleOpenModalSelectVsp();
|
return handleOpenModalSelectVsp();
|
||||||
}
|
}
|
||||||
@ -681,13 +691,21 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
return handleOpenModalSelectExpenseItem();
|
return handleOpenModalSelectExpenseItem();
|
||||||
}
|
}
|
||||||
return handleAddRow();
|
return handleAddRow();
|
||||||
}, [isVspAdditionRow, isAdditionExpenseItem, handleOpenModalSelectVsp, handleAddRow, handleOpenModalSelectExpenseItem]);
|
}, [canModifyRows, isVspAdditionRow, isAdditionExpenseItem, handleOpenModalSelectVsp, handleAddRow, handleOpenModalSelectExpenseItem]);
|
||||||
|
|
||||||
const addProgram = useCallback(() => {
|
const addProgram = useCallback(() => {
|
||||||
|
if (!canModifyRows) {
|
||||||
|
toast.error('Добавление строк недоступно: нет доступных для редактирования столбцов');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setCreateModalType('program');
|
setCreateModalType('program');
|
||||||
}, []);
|
}, [canModifyRows]);
|
||||||
|
|
||||||
const addProject = useCallback(() => {
|
const addProject = useCallback(() => {
|
||||||
|
if (!canModifyRows) {
|
||||||
|
toast.error('Добавление строк недоступно: нет доступных для редактирования столбцов');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const row = getSelectedRow();
|
const row = getSelectedRow();
|
||||||
if (!row) {
|
if (!row) {
|
||||||
toast.error('Выделите строку для вставки');
|
toast.error('Выделите строку для вставки');
|
||||||
@ -698,7 +716,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCreateModalType('project');
|
setCreateModalType('project');
|
||||||
}, [getSelectedRow]);
|
}, [canModifyRows, getSelectedRow]);
|
||||||
|
|
||||||
const closeSelectVspModal = useCallback(() => setIsOpenModalSelectVsp(false), []);
|
const closeSelectVspModal = useCallback(() => setIsOpenModalSelectVsp(false), []);
|
||||||
const closeSelectExpenseItemModal = useCallback(() => setIsOpenModalSelectExpenseItem(false), []);
|
const closeSelectExpenseItemModal = useCallback(() => setIsOpenModalSelectExpenseItem(false), []);
|
||||||
@ -777,6 +795,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
direction={direction}
|
direction={direction}
|
||||||
year={year}
|
year={year}
|
||||||
isFormNoCanAddRow={isFormNoCanAddRow}
|
isFormNoCanAddRow={isFormNoCanAddRow}
|
||||||
|
canModifyRows={canModifyRows}
|
||||||
formType={formType}
|
formType={formType}
|
||||||
validationErrors={validationErrors}
|
validationErrors={validationErrors}
|
||||||
onNavigateToColumn={handleNavigateToColumn}
|
onNavigateToColumn={handleNavigateToColumn}
|
||||||
|
|||||||
@ -90,6 +90,7 @@ const SettingPanel = ({
|
|||||||
direction,
|
direction,
|
||||||
year,
|
year,
|
||||||
isFormNoCanAddRow,
|
isFormNoCanAddRow,
|
||||||
|
canModifyRows,
|
||||||
formType,
|
formType,
|
||||||
validationErrors = [],
|
validationErrors = [],
|
||||||
onNavigateToColumn,
|
onNavigateToColumn,
|
||||||
@ -178,7 +179,7 @@ const SettingPanel = ({
|
|||||||
|
|
||||||
<Divider orientation='vertical' flexItem />
|
<Divider orientation='vertical' flexItem />
|
||||||
<GroupByObject title='Строки'>
|
<GroupByObject title='Строки'>
|
||||||
{!isFormNoCanAddRow && (
|
{!isFormNoCanAddRow && canModifyRows && (
|
||||||
<>
|
<>
|
||||||
<Tooltip title='Добавить строку'>
|
<Tooltip title='Добавить строку'>
|
||||||
<IconButton onClick={onAddRow} variant='outlined' sx={controlSx}>
|
<IconButton onClick={onAddRow} variant='outlined' sx={controlSx}>
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Box, CircularProgress, Stack } from '@mui/material';
|
import { Box, CircularProgress, Stack } from '@mui/material';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { useTaskStages } from '../../hooks/useTaskStages';
|
import { useTaskStages } from '../../hooks/useTaskStages';
|
||||||
import { useProjectStages } from '../../hooks/useProjectStages';
|
import { useProjectStages } from '../../hooks/useProjectStages';
|
||||||
import { SHEET_NAME } from '../../constants/constants';
|
import { ROLES_NAME_ID, SHEET_NAME } from '../../constants/constants';
|
||||||
import { PrimaryButton } from '../common/Buttons/Buttons';
|
import { PrimaryButton } from '../common/Buttons/Buttons';
|
||||||
import Modal from '../common/Modal/Modal';
|
import Modal from '../common/Modal/Modal';
|
||||||
import { EditContainer } from '../common/Modal/ModalStyled';
|
import { EditContainer } from '../common/Modal/ModalStyled';
|
||||||
@ -23,6 +24,8 @@ export const SettingModal = ({
|
|||||||
formTypeCode = '',
|
formTypeCode = '',
|
||||||
sheets,
|
sheets,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const isAdmin = user?.role_id === ROLES_NAME_ID.admin;
|
||||||
const taskStages = useTaskStages(taskId);
|
const taskStages = useTaskStages(taskId);
|
||||||
const projectStages = useProjectStages(projectId, year, reportType);
|
const projectStages = useProjectStages(projectId, year, reportType);
|
||||||
const stages = mode === 'project' ? projectStages : taskStages;
|
const stages = mode === 'project' ? projectStages : taskStages;
|
||||||
@ -137,11 +140,13 @@ export const SettingModal = ({
|
|||||||
<>
|
<>
|
||||||
<Modal open={isOpen} onClose={onClose} title={modalTitle} customSize={50}>
|
<Modal open={isOpen} onClose={onClose} title={modalTitle} customSize={50}>
|
||||||
<EditContainer style={{ margin: 0 }}>
|
<EditContainer style={{ margin: 0 }}>
|
||||||
|
{isAdmin && (
|
||||||
<Stack direction='row' sx={{ width: '100%', justifyContent: 'flex-end' }}>
|
<Stack direction='row' sx={{ width: '100%', justifyContent: 'flex-end' }}>
|
||||||
<PrimaryButton onClick={handleAddStage} startIcon={<WhitePlus />}>
|
<PrimaryButton onClick={handleAddStage} startIcon={<WhitePlus />}>
|
||||||
Добавить этап
|
Добавить этап
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
{stagesInfo.isLoading ? (
|
{stagesInfo.isLoading ? (
|
||||||
<Box
|
<Box
|
||||||
@ -159,6 +164,7 @@ export const SettingModal = ({
|
|||||||
onDelete={!isTaskMode ? handleDeleteStage : undefined}
|
onDelete={!isTaskMode ? handleDeleteStage : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isAdmin && (
|
||||||
<AddStagesModal
|
<AddStagesModal
|
||||||
isOpen={isAddEditModalOpen}
|
isOpen={isAddEditModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@ -173,6 +179,7 @@ export const SettingModal = ({
|
|||||||
formType={formTypeCode}
|
formType={formTypeCode}
|
||||||
defaultSheet={mode === 'project' ? reportType : ''}
|
defaultSheet={mode === 'project' ? reportType : ''}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{!isTaskMode && (
|
{!isTaskMode && (
|
||||||
<DeleteStageModal
|
<DeleteStageModal
|
||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const TableCard = ({ table, onNavigate, action }) => {
|
|||||||
</div>
|
</div>
|
||||||
<SectionTitle>
|
<SectionTitle>
|
||||||
<h2>{table.name}</h2>
|
<h2>{table.name}</h2>
|
||||||
<h2>{DIRECTION_TRANSLATE[table?.direction]}</h2>
|
<h2>{DIRECTION_TRANSLATE[table?.direction] || ''}</h2>
|
||||||
{/* <UsersList>
|
{/* <UsersList>
|
||||||
<div className="img-user"></div>
|
<div className="img-user"></div>
|
||||||
</UsersList> */}
|
</UsersList> */}
|
||||||
|
|||||||
@ -167,7 +167,8 @@ export const EditUserModal = ({ isOpen, onClose, onConfirm, user, onEditSsp }) =
|
|||||||
variant='outlined'
|
variant='outlined'
|
||||||
size='small'
|
size='small'
|
||||||
autoFocus
|
autoFocus
|
||||||
InputProps={{
|
slotProps={{
|
||||||
|
input: {
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<InputAdornment position='end'>
|
<InputAdornment position='end'>
|
||||||
<IconButton edge='end' onClick={() => toggleEdit(field)} size='small' sx={{ padding: '4px' }}>
|
<IconButton edge='end' onClick={() => toggleEdit(field)} size='small' sx={{ padding: '4px' }}>
|
||||||
@ -175,6 +176,7 @@ export const EditUserModal = ({ isOpen, onClose, onConfirm, user, onEditSsp }) =
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { DIRECTION_TRANSLATE, SHEET_NAME } from '../constants/constants';
|
|||||||
const TableInfo = ({ formId, sheetName, direction, isProject = false }) => {
|
const TableInfo = ({ formId, sheetName, direction, isProject = false }) => {
|
||||||
const path = (isProject ? '/project/' : '/task/') + formId;
|
const path = (isProject ? '/project/' : '/task/') + formId;
|
||||||
const tableName = SHEET_NAME[sheetName] || sheetName;
|
const tableName = SHEET_NAME[sheetName] || sheetName;
|
||||||
const directionName = DIRECTION_TRANSLATE[direction] || direction;
|
const directionName = DIRECTION_TRANSLATE[direction] || '';
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
<TaskInfoContainer>
|
<TaskInfoContainer>
|
||||||
<BackButton to={path} />
|
<BackButton to={path} />
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { TaskInfoContainer } from '../components/common/SwitchFormTask/SwitchFormTask.style';
|
|
||||||
|
|
||||||
import RealtimeTable from '../components/RealtimeTable';
|
|
||||||
|
|
||||||
const TableInfo = () => {
|
|
||||||
const portalContent = (
|
|
||||||
<TaskInfoContainer>
|
|
||||||
{/* <BackButton to={`/task/${table.task_id}`} />
|
|
||||||
<NameTask>{table.name}</NameTask> */}
|
|
||||||
</TaskInfoContainer>
|
|
||||||
);
|
|
||||||
|
|
||||||
const targetElement = document.getElementById('TableInfo');
|
|
||||||
|
|
||||||
if (targetElement) {
|
|
||||||
return createPortal(portalContent, targetElement);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function NewTablePage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RealtimeTable />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -93,21 +93,23 @@ const TableFilters = ({
|
|||||||
onBranchChange(newValue || '');
|
onBranchChange(newValue || '');
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => {
|
renderInput={(params) => {
|
||||||
const { InputProps, ...rest } = params;
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
{...rest}
|
{...params}
|
||||||
placeholder='Региональные филиалы'
|
placeholder='Региональные филиалы'
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
size='small'
|
size='small'
|
||||||
InputProps={{
|
slotProps={{
|
||||||
...InputProps,
|
...params.slotProps,
|
||||||
|
input: {
|
||||||
|
...params.slotProps.input,
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<>
|
<>
|
||||||
{isLoading && <CircularProgress color='inherit' size={20} />}
|
{isLoading && <CircularProgress color='inherit' size={20} />}
|
||||||
{InputProps?.endAdornment}
|
{params.slotProps.input.endAdornment}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -85,12 +85,12 @@ export function SvodFilters({
|
|||||||
onChange({ selectedOrganizations: selectedAll ? [SELECT_ALL_OPTION] : value });
|
onChange({ selectedOrganizations: selectedAll ? [SELECT_ALL_OPTION] : value });
|
||||||
}}
|
}}
|
||||||
sx={{ minWidth: '18.75rem', flex: '1 1 22.5rem', maxWidth: '32.5rem' }}
|
sx={{ minWidth: '18.75rem', flex: '1 1 22.5rem', maxWidth: '32.5rem' }}
|
||||||
renderTags={(value, getTagProps) =>
|
renderValue={(value, getItemProps) =>
|
||||||
value
|
value
|
||||||
.slice(0, 2)
|
.slice(0, 2)
|
||||||
.map((option, index) => {
|
.map((option, index) => {
|
||||||
const tagProps = getTagProps({ index });
|
const { key, ...itemProps } = getItemProps({ index });
|
||||||
return <Chip {...tagProps} key={option.id} label={option.title} size='small' />;
|
return <Chip {...itemProps} key={option.id || key} label={option.title} size='small' />;
|
||||||
})
|
})
|
||||||
.concat(value.length > 2 ? [<Chip key='more' label={`+${value.length - 2}`} size='small' />] : [])
|
.concat(value.length > 2 ? [<Chip key='more' label={`+${value.length - 2}`} size='small' />] : [])
|
||||||
}
|
}
|
||||||
@ -99,14 +99,17 @@ export function SvodFilters({
|
|||||||
{...params}
|
{...params}
|
||||||
label='ССП/РФ'
|
label='ССП/РФ'
|
||||||
placeholder={selectedOrganizations.length ? '' : emptyOrganizationsPlaceholder}
|
placeholder={selectedOrganizations.length ? '' : emptyOrganizationsPlaceholder}
|
||||||
InputProps={{
|
slotProps={{
|
||||||
...(params.InputProps || {}),
|
...params.slotProps,
|
||||||
|
input: {
|
||||||
|
...params.slotProps.input,
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<>
|
<>
|
||||||
{organizationsLoading && <CircularProgress size='1.125rem' />}
|
{organizationsLoading && <CircularProgress size='1.125rem' />}
|
||||||
{params.InputProps?.endAdornment}
|
{params.slotProps.input.endAdornment}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -117,12 +120,14 @@ export function SvodFilters({
|
|||||||
onChange={(event) => onChange({ search: event.target.value })}
|
onChange={(event) => onChange({ search: event.target.value })}
|
||||||
placeholder={searchPlaceholder}
|
placeholder={searchPlaceholder}
|
||||||
sx={{ minWidth: '16.25rem', flex: '1 1 17.5rem', maxWidth: '23.75rem' }}
|
sx={{ minWidth: '16.25rem', flex: '1 1 17.5rem', maxWidth: '23.75rem' }}
|
||||||
InputProps={{
|
slotProps={{
|
||||||
|
input: {
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position='start'>
|
<InputAdornment position='start'>
|
||||||
<SearchRoundedIcon sx={{ color: '#99a1af' }} />
|
<SearchRoundedIcon sx={{ color: '#99a1af' }} />
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const formatValue = (value) => {
|
|||||||
|
|
||||||
const getShapeForSheet = (sheet) => {
|
const getShapeForSheet = (sheet) => {
|
||||||
const directionShape = { q1: 0, q2: 0, q3: 0, q4: 0, year: 0 };
|
const directionShape = { q1: 0, q2: 0, q3: 0, q4: 0, year: 0 };
|
||||||
const correctedShape = sheet === 'FORM_4' ? directionShape : { q2: 0, q3: 0, q4: 0 };
|
const correctedShape = { q2: 0, q3: 0, q4: 0 };
|
||||||
return {
|
return {
|
||||||
plan: { support: directionShape, development: directionShape, ...(['FORM_1', 'MAIN'].includes(sheet) ? { total_year: 0 } : {}) },
|
plan: { support: directionShape, development: directionShape, ...(['FORM_1', 'MAIN'].includes(sheet) ? { total_year: 0 } : {}) },
|
||||||
approved: { support: directionShape, development: directionShape, ...(['FORM_1', 'MAIN'].includes(sheet) ? { total_year: 0 } : {}) },
|
approved: { support: directionShape, development: directionShape, ...(['FORM_1', 'MAIN'].includes(sheet) ? { total_year: 0 } : {}) },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user