form-4-add-row #54
7
web/src/api/expense-item.js
Normal file
7
web/src/api/expense-item.js
Normal file
@ -0,0 +1,7 @@
|
||||
import api from "./client";
|
||||
|
||||
export const ExpenseItemApi = {
|
||||
getAll(params) {
|
||||
return api.get(`/expense-item/`, { params }).then((r) => r.data);
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,158 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Stack,
|
||||
Select,
|
||||
MenuItem,
|
||||
Autocomplete,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
ModalBackdrop,
|
||||
ModalContainer,
|
||||
HeaderContainer,
|
||||
ModalTitle,
|
||||
} from '../../common/Modal/ModalStyled';
|
||||
import { Cancel } from '../../common/icons/icons';
|
||||
import { DictVspApi } from '../../../api/dict-vsp';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ExpenseItemApi } from '../../../api/expense-item';
|
||||
|
||||
export const SelectExpenseItemModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSelect,
|
||||
formId,
|
||||
title = '',
|
||||
isSaving = false,
|
||||
sheet,
|
||||
}) => {
|
||||
const [selectedValue, setSelectedValue] = useState('');
|
||||
const [expenseItemOptions, setExpenseItemOption] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
if (formId === null) return;
|
||||
ExpenseItemApi.getAll({ r_start: true, sheet: sheet }).then(data => {
|
||||
if (data.success) {
|
||||
setExpenseItemOption(data.result)
|
||||
return;
|
||||
}
|
||||
toast.error('Ошибка получения статья расходов')
|
||||
})
|
||||
}, [formId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setSelectedValue('');
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const handleCancel = () => {
|
||||
setSelectedValue('');
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleBackdropClick = (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
handleCancel();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onSelect?.(selectedValue);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const isFormValid = selectedValue;
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<ModalBackdrop isOpen={isOpen} onClick={handleBackdropClick}>
|
||||
<ModalContainer height={'max-content'} >
|
||||
<HeaderContainer>
|
||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
|
||||
<ModalTitle>{title}</ModalTitle>
|
||||
</Stack>
|
||||
<IconButton size="small" onClick={handleCancel}>
|
||||
<Cancel />
|
||||
</IconButton>
|
||||
</HeaderContainer>
|
||||
|
||||
<Box>
|
||||
<Divider sx={{ marginBottom: '0.75rem' }} />
|
||||
|
||||
{/* Выбор ВСП */}
|
||||
<Stack sx={{ marginBottom: '0.75rem', width: '100%' }}>
|
||||
<FormControl fullWidth>
|
||||
<Autocomplete
|
||||
size="small"
|
||||
options={expenseItemOptions}
|
||||
noOptionsText="Не найдено"
|
||||
getOptionLabel={(option) => option.name || ''}
|
||||
getOptionKey={(option) => option.id}
|
||||
value={selectedValue}
|
||||
onChange={(event, newValue) => {
|
||||
setSelectedValue(newValue);
|
||||
}}
|
||||
slotProps={{
|
||||
popper: {
|
||||
style: { zIndex: 60 },
|
||||
},
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
placeholder="Не выбрана"
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
borderColor: 'rgba(0,0,0,0.1)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
</FormControl>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={0.75}
|
||||
sx={{
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '1.5rem',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="default"
|
||||
onClick={handleCancel}
|
||||
style={{ height: '2.5rem' }}
|
||||
disabled={isSaving}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
style={{ height: '2.5rem' }}
|
||||
disabled={!isFormValid || isSaving}
|
||||
loading={isSaving}
|
||||
>
|
||||
Выбрать
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
</ModalContainer>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
};
|
||||
@ -1,4 +1,3 @@
|
||||
// components/RealtimeTable/index.js
|
||||
import React, { useState, useMemo, useRef, useEffect, useCallback, useTransition } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
@ -25,9 +24,11 @@ import {
|
||||
import { useRealtime } from './contexts/RealtimeContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
import { additionVspRowTable } from './constants/addingRowConfig';
|
||||
import { additionExpenseRowTable, additionVspRowTable } from './constants/addingRowConfig';
|
||||
import { SelectVspModal } from './Modals/SelectVspModal';
|
||||
import { SelectExpenseItemModal } from './Modals/SelectExpenseItemModal';
|
||||
import { getRowId } from './utils/rowUtils';
|
||||
import { FORM_TYPE_OPTIONS } from '../../constants/constants';
|
||||
|
||||
const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction, formType, year);
|
||||
@ -40,6 +41,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
const [editingCell, setEditingCell] = useState(null);
|
||||
|
||||
const [isOpenModalSelectVsp, setIsOpenModalSelectVsp] = useState(false);
|
||||
const [isOpenModalSelectExpenseItem, setIsOpenModalSelectExpenseItem] = useState(false);
|
||||
|
||||
const isVspAdditionRow = useMemo(() => {
|
||||
if (!formType || !sheetName) return false;
|
||||
@ -47,6 +49,12 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
return additionVspRowTable[formType].includes(sheetName);
|
||||
}, [formType, sheetName]);
|
||||
|
||||
const isAdditionExpenseItem = useMemo(() => {
|
||||
if (!formType || !sheetName) return false;
|
||||
if (!additionExpenseRowTable[formType]) return false;
|
||||
return additionExpenseRowTable[formType].includes(sheetName);
|
||||
}, [formType, sheetName]);
|
||||
|
||||
const handleGlobalFilterChange = (value) => {
|
||||
startTransition(() => {
|
||||
setGlobalFilter(value);
|
||||
@ -359,11 +367,19 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
|
||||
const handleAddVspRow = useCallback((vsp_id) => {
|
||||
contextAddRow({ vsp_id: vsp_id });
|
||||
}, [rowSelection, table]);
|
||||
}, []);
|
||||
|
||||
const handleAddExpenseItemRow = useCallback((expense_item) => {
|
||||
contextAddRow({ expense_item_id: expense_item.id });
|
||||
}, []);
|
||||
|
||||
const handleOpenModalSelectVsp = useCallback(() => {
|
||||
setIsOpenModalSelectVsp(true);
|
||||
}, [rowSelection, table])
|
||||
}, [])
|
||||
|
||||
const handleOpenModalSelectExpenseItem = useCallback(() => {
|
||||
setIsOpenModalSelectExpenseItem(true);
|
||||
}, [])
|
||||
|
||||
const handleDeleteRow = useCallback(() => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
@ -379,6 +395,17 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
setRowSelection({});
|
||||
}, [rowSelection, table])
|
||||
|
||||
const addRow = useCallback(() => {
|
||||
|
||||
if (isVspAdditionRow) {
|
||||
return handleOpenModalSelectVsp();
|
||||
}
|
||||
if (isAdditionExpenseItem) {
|
||||
return handleOpenModalSelectExpenseItem();
|
||||
}
|
||||
return handleAddRow();
|
||||
}, [isVspAdditionRow, isAdditionExpenseItem, handleOpenModalSelectVsp, handleAddRow]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setData([]);
|
||||
@ -405,7 +432,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
onGlobalFilterChange={handleGlobalFilterChange}
|
||||
sizeMult={sizeMult}
|
||||
onChangeShowColumnFilters={setShowColumnFilters}
|
||||
onAddRow={isVspAdditionRow ? handleOpenModalSelectVsp : handleAddRow}
|
||||
onAddRow={addRow}
|
||||
onDeleteRow={handleDeleteRow}
|
||||
isLoadingData={isLoadingData}
|
||||
formId={formId}
|
||||
@ -475,6 +502,14 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
onSelect={handleAddVspRow}
|
||||
title="Выбор ВСП"
|
||||
/>
|
||||
<SelectExpenseItemModal
|
||||
isOpen={isOpenModalSelectExpenseItem}
|
||||
onClose={() => setIsOpenModalSelectExpenseItem(false)}
|
||||
formId={formId}
|
||||
onSelect={handleAddExpenseItemRow}
|
||||
title="Добавление строки"
|
||||
sheet={sheetName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
export const additionVspRowTable = {
|
||||
FORM_2: ["AHR_SECURITY", "AHR_RENT", "AHR_UTILITY"],
|
||||
};
|
||||
|
||||
export const additionExpenseRowTable = {
|
||||
FORM_4: ["AHR", "CAP", "OPER"],
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user