Compare commits
2 Commits
653ede59c1
...
8c978b4e33
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c978b4e33 | ||
|
|
d257c3f1c1 |
5
web/public/images/Program.svg
Normal file
5
web/public/images/Program.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M8 21V7C8 6.07003 8 5.60504 8.10222 5.22354C8.37962 4.18827 9.18827 3.37962 10.2235 3.10222C10.605 3 11.07 3 12 3C12.93 3 13.395 3 13.7765 3.10222C14.8117 3.37962 15.6204 4.18827 15.8978 5.22354C16 5.60504 16 6.07003 16 7V21M5.2 21H18.8C19.9201 21 20.4802 21 20.908 20.782C21.2843 20.5903 21.5903 20.2843 21.782 19.908C22 19.4802 22 18.9201 22 17.8V10.2C22 9.07989 22 8.51984 21.782 8.09202C21.5903 7.71569 21.2843 7.40973 20.908 7.21799C20.4802 7 19.9201 7 18.8 7H5.2C4.07989 7 3.51984 7 3.09202 7.21799C2.71569 7.40973 2.40973 7.71569 2.21799 8.09202C2 8.51984 2 9.07989 2 10.2V17.8C2 18.9201 2 19.4802 2.21799 19.908C2.40973 20.2843 2.71569 20.5903 3.09202 20.782C3.51984 21 4.0799 21 5.2 21Z"
|
||||||
|
stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 900 B |
5
web/public/images/Project.svg
Normal file
5
web/public/images/Project.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M14 11H8M10 15H8M16 7H8M20 6.8V17.2C20 18.8802 20 19.7202 19.673 20.362C19.3854 20.9265 18.9265 21.3854 18.362 21.673C17.7202 22 16.8802 22 15.2 22H8.8C7.11984 22 6.27976 22 5.63803 21.673C5.07354 21.3854 4.6146 20.9265 4.32698 20.362C4 19.7202 4 18.8802 4 17.2V6.8C4 5.11984 4 4.27976 4.32698 3.63803C4.6146 3.07354 5.07354 2.6146 5.63803 2.32698C6.27976 2 7.11984 2 8.8 2H15.2C16.8802 2 17.7202 2 18.362 2.32698C18.9265 2.6146 19.3854 3.07354 19.673 3.63803C20 4.27976 20 5.11984 20 6.8Z"
|
||||||
|
stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 704 B |
@ -0,0 +1,110 @@
|
|||||||
|
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';
|
||||||
|
import { DangerOutlinedButton, PrimaryButton } from '../../common/Buttons/Buttons';
|
||||||
|
import { StyledTextField } from '../../common/StyledTextField';
|
||||||
|
|
||||||
|
export const CreateProgramModal = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onCreate,
|
||||||
|
title = '',
|
||||||
|
isSaving = false,
|
||||||
|
sheet,
|
||||||
|
}) => {
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setName('');
|
||||||
|
}
|
||||||
|
}, [isOpen])
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setName('');
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBackdropClick = (e) => {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
onCreate?.(name);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFormValid = name !== '';
|
||||||
|
|
||||||
|
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' }} />
|
||||||
|
|
||||||
|
|
||||||
|
{/* <Input */}
|
||||||
|
<StyledTextField label={'Название программы'} onChange={(e) => setName(e.target.value)}></StyledTextField>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
spacing={0.75}
|
||||||
|
sx={{
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
marginTop: '1.5rem',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DangerOutlinedButton
|
||||||
|
onClick={handleCancel}
|
||||||
|
disabled={isSaving}
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</DangerOutlinedButton>
|
||||||
|
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={!isFormValid || isSaving}
|
||||||
|
loading={isSaving}>
|
||||||
|
Создать
|
||||||
|
</PrimaryButton>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</ModalContainer>
|
||||||
|
</ModalBackdrop>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -22,6 +22,7 @@ import { Cancel } from '../../common/icons/icons';
|
|||||||
import { DictVspApi } from '../../../api/dict-vsp';
|
import { DictVspApi } from '../../../api/dict-vsp';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { ExpenseItemApi } from '../../../api/expense-item';
|
import { ExpenseItemApi } from '../../../api/expense-item';
|
||||||
|
import { DangerOutlinedButton, PrimaryButton } from '../../common/Buttons/Buttons';
|
||||||
|
|
||||||
export const SelectExpenseItemModal = ({
|
export const SelectExpenseItemModal = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
@ -132,24 +133,19 @@ export const SelectExpenseItemModal = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<DangerOutlinedButton
|
||||||
variant="outlined"
|
|
||||||
color="default"
|
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
style={{ height: '2.5rem' }}
|
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
>
|
>
|
||||||
Отмена
|
Отмена
|
||||||
</Button>
|
</DangerOutlinedButton>
|
||||||
<Button
|
|
||||||
variant="contained"
|
<PrimaryButton
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
style={{ height: '2.5rem' }}
|
|
||||||
disabled={!isFormValid || isSaving}
|
disabled={!isFormValid || isSaving}
|
||||||
loading={isSaving}
|
loading={isSaving}>
|
||||||
>
|
|
||||||
Выбрать
|
Выбрать
|
||||||
</Button>
|
</PrimaryButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
import { Cancel } from '../../common/icons/icons';
|
import { Cancel } from '../../common/icons/icons';
|
||||||
import { DictVspApi } from '../../../api/dict-vsp';
|
import { DictVspApi } from '../../../api/dict-vsp';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import { DangerOutlinedButton, PrimaryButton } from '../../common/Buttons/Buttons';
|
||||||
|
|
||||||
export const SelectVspModal = ({
|
export const SelectVspModal = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
@ -113,24 +114,19 @@ export const SelectVspModal = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<DangerOutlinedButton
|
||||||
variant="outlined"
|
|
||||||
color="default"
|
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
style={{ height: '2.5rem' }}
|
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
>
|
>
|
||||||
Отмена
|
Отмена
|
||||||
</Button>
|
</DangerOutlinedButton>
|
||||||
<Button
|
|
||||||
variant="contained"
|
<PrimaryButton
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
style={{ height: '2.5rem' }}
|
|
||||||
disabled={!isFormValid || isSaving}
|
disabled={!isFormValid || isSaving}
|
||||||
loading={isSaving}
|
loading={isSaving}>
|
||||||
>
|
|
||||||
Выбрать
|
Выбрать
|
||||||
</Button>
|
</PrimaryButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
|
|||||||
@ -24,10 +24,13 @@ import {
|
|||||||
import { useRealtime } from './contexts/RealtimeContext';
|
import { useRealtime } from './contexts/RealtimeContext';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { CircularProgress } from '@mui/material';
|
import { CircularProgress } from '@mui/material';
|
||||||
import { additionVspRowTable } from './constants/addingRowConfig';
|
import { additionExpenseRowTable, additionVspRowTable } from './constants/addingRowConfig';
|
||||||
import { SelectVspModal } from './Modals/SelectVspModal';
|
import { SelectVspModal } from './Modals/SelectVspModal';
|
||||||
|
import { SelectExpenseItemModal } from './Modals/SelectExpenseItemModal';
|
||||||
import { getRowId } from './utils/rowUtils';
|
import { getRowId } from './utils/rowUtils';
|
||||||
|
import { FORM_TYPE_OPTIONS } from '../../constants/constants';
|
||||||
import { debounce } from '@mui/material';
|
import { debounce } from '@mui/material';
|
||||||
|
import { CreateProgramModal } from './Modals/CreateProgramProjectModal';
|
||||||
|
|
||||||
const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||||
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction, formType, year);
|
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction, formType, year);
|
||||||
@ -40,6 +43,10 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
const [editingCell, setEditingCell] = useState(null);
|
const [editingCell, setEditingCell] = useState(null);
|
||||||
|
|
||||||
const [isOpenModalSelectVsp, setIsOpenModalSelectVsp] = useState(false);
|
const [isOpenModalSelectVsp, setIsOpenModalSelectVsp] = useState(false);
|
||||||
|
const [isOpenModalSelectExpenseItem, setIsOpenModalSelectExpenseItem] = useState(false);
|
||||||
|
const [isOpenModalCreateProgram, setIsOpenModalCreateProgram] = useState(false);
|
||||||
|
const [isOpenModalCreateProject, setIsOpenModalCreateProject] = useState(false);
|
||||||
|
const [isProgramCreate, setIsProgramCreate] = useState(false)
|
||||||
|
|
||||||
const isVspAdditionRow = useMemo(() => {
|
const isVspAdditionRow = useMemo(() => {
|
||||||
if (!formType || !sheetName) return false;
|
if (!formType || !sheetName) return false;
|
||||||
@ -47,6 +54,12 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
return additionVspRowTable[formType].includes(sheetName);
|
return additionVspRowTable[formType].includes(sheetName);
|
||||||
}, [formType, 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 = useCallback(
|
const handleGlobalFilterChange = useCallback(
|
||||||
debounce((value) => {
|
debounce((value) => {
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
@ -64,8 +77,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
error: wsError,
|
error: wsError,
|
||||||
updateCell: contextUpdateCell,
|
updateCell: contextUpdateCell,
|
||||||
addRow: contextAddRow,
|
addRow: contextAddRow,
|
||||||
deleteRow: contextDeleteRow,
|
|
||||||
startEditing: contextStartEditing,
|
startEditing: contextStartEditing,
|
||||||
|
addProgram: contextAddProgram,
|
||||||
|
addProject: contextAddProject,
|
||||||
} = useRealtime();
|
} = useRealtime();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -113,6 +127,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
setColumnsConfig({ columns: [], colors: {} });
|
setColumnsConfig({ columns: [], colors: {} });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadConfig();
|
loadConfig();
|
||||||
}, [formType, sheetName]);
|
}, [formType, sheetName]);
|
||||||
|
|
||||||
@ -363,11 +378,45 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
|
|
||||||
const handleAddVspRow = useCallback((vsp_id) => {
|
const handleAddVspRow = useCallback((vsp_id) => {
|
||||||
contextAddRow({ vsp_id: vsp_id });
|
contextAddRow({ vsp_id: vsp_id });
|
||||||
}, [rowSelection, table]);
|
}, []);
|
||||||
|
|
||||||
|
const handleAddExpenseItemRow = useCallback((expense_item) => {
|
||||||
|
const allRows = table.getRowModel().rows;
|
||||||
|
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||||
|
const row = selectedRows[0];
|
||||||
|
contextAddRow({ expense_item_id: expense_item.id, project_id: row.original.data.header.project_id });
|
||||||
|
}, [rowSelection]);
|
||||||
|
|
||||||
|
const handleAddProgramRow = useCallback((name) => {
|
||||||
|
contextAddProgram({ name: name });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddProjectRow = useCallback((name) => {
|
||||||
|
const allRows = table.getRowModel().rows;
|
||||||
|
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||||
|
const row = selectedRows[0];
|
||||||
|
contextAddProject({ name: name, program_id: row.original.data.header.program_id });
|
||||||
|
}, [rowSelection]);
|
||||||
|
|
||||||
const handleOpenModalSelectVsp = useCallback(() => {
|
const handleOpenModalSelectVsp = useCallback(() => {
|
||||||
setIsOpenModalSelectVsp(true);
|
setIsOpenModalSelectVsp(true);
|
||||||
}, [rowSelection, table])
|
}, [])
|
||||||
|
|
||||||
|
const handleOpenModalSelectExpenseItem = useCallback(() => {
|
||||||
|
const allRows = table.getRowModel().rows;
|
||||||
|
|
||||||
|
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||||
|
if (selectedRows.length === 0) {
|
||||||
|
toast.error('Выделите строку для вставки');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const row = selectedRows[0];
|
||||||
|
if (row.original.row_type !== 'ITEM') {
|
||||||
|
toast.error('Выделите строку проекта');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsOpenModalSelectExpenseItem(true);
|
||||||
|
}, [rowSelection])
|
||||||
|
|
||||||
const handleDeleteRow = useCallback(() => {
|
const handleDeleteRow = useCallback(() => {
|
||||||
const allRows = table.getRowModel().rows;
|
const allRows = table.getRowModel().rows;
|
||||||
@ -383,6 +432,39 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
}, [rowSelection, table])
|
}, [rowSelection, table])
|
||||||
|
|
||||||
|
const addRow = useCallback(() => {
|
||||||
|
|
||||||
|
if (isVspAdditionRow) {
|
||||||
|
return handleOpenModalSelectVsp();
|
||||||
|
}
|
||||||
|
if (isAdditionExpenseItem) {
|
||||||
|
return handleOpenModalSelectExpenseItem();
|
||||||
|
}
|
||||||
|
return handleAddRow();
|
||||||
|
}, [isVspAdditionRow, isAdditionExpenseItem, handleOpenModalSelectVsp, handleAddRow]);
|
||||||
|
|
||||||
|
const addProgram = useCallback(() => {
|
||||||
|
setIsProgramCreate(true);
|
||||||
|
setIsOpenModalCreateProgram(true);
|
||||||
|
}, [setIsOpenModalCreateProgram])
|
||||||
|
|
||||||
|
const addProject = useCallback(() => {
|
||||||
|
setIsProgramCreate(false);
|
||||||
|
const allRows = table.getRowModel().rows;
|
||||||
|
|
||||||
|
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||||
|
if (selectedRows.length === 0) {
|
||||||
|
toast.error('Выделите строку для вставки');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const row = selectedRows[0];
|
||||||
|
if (row.original.row_type !== 'GROUP') {
|
||||||
|
toast.error('Выделите строку программы');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsOpenModalCreateProgram(true);
|
||||||
|
}, [setIsOpenModalCreateProgram, rowSelection])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
setData([]);
|
setData([]);
|
||||||
@ -409,7 +491,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
onGlobalFilterChange={handleGlobalFilterChange}
|
onGlobalFilterChange={handleGlobalFilterChange}
|
||||||
sizeMult={sizeMult}
|
sizeMult={sizeMult}
|
||||||
onChangeShowColumnFilters={setShowColumnFilters}
|
onChangeShowColumnFilters={setShowColumnFilters}
|
||||||
onAddRow={isVspAdditionRow ? handleOpenModalSelectVsp : handleAddRow}
|
onAddRow={addRow}
|
||||||
|
onAddProgram={addProgram}
|
||||||
|
onAddProject={addProject}
|
||||||
onDeleteRow={handleDeleteRow}
|
onDeleteRow={handleDeleteRow}
|
||||||
isLoadingData={isLoadingData}
|
isLoadingData={isLoadingData}
|
||||||
formId={formId}
|
formId={formId}
|
||||||
@ -417,6 +501,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
direction={direction}
|
direction={direction}
|
||||||
year={year}
|
year={year}
|
||||||
isProject={formType == 'PROJECT'}
|
isProject={formType == 'PROJECT'}
|
||||||
|
formType={formType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={tableWrapperStyle}>
|
<div style={tableWrapperStyle}>
|
||||||
@ -479,6 +564,20 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
onSelect={handleAddVspRow}
|
onSelect={handleAddVspRow}
|
||||||
title="Выбор ВСП"
|
title="Выбор ВСП"
|
||||||
/>
|
/>
|
||||||
|
<SelectExpenseItemModal
|
||||||
|
isOpen={isOpenModalSelectExpenseItem}
|
||||||
|
onClose={() => setIsOpenModalSelectExpenseItem(false)}
|
||||||
|
formId={formId}
|
||||||
|
onSelect={handleAddExpenseItemRow}
|
||||||
|
title="Добавление строки"
|
||||||
|
sheet={sheetName}
|
||||||
|
/>
|
||||||
|
<CreateProgramModal
|
||||||
|
isOpen={isOpenModalCreateProgram}
|
||||||
|
onClose={() => setIsOpenModalCreateProgram(false)}
|
||||||
|
title={isProgramCreate ? "Создание программы" : "Создание проекта"}
|
||||||
|
onCreate={isProgramCreate ? handleAddProgramRow : handleAddProjectRow}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import {
|
|||||||
Minus,
|
Minus,
|
||||||
Pin,
|
Pin,
|
||||||
Plus,
|
Plus,
|
||||||
|
Program,
|
||||||
|
Project,
|
||||||
RemoveLine,
|
RemoveLine,
|
||||||
Search,
|
Search,
|
||||||
} from '../../common/icons/icons';
|
} from '../../common/icons/icons';
|
||||||
@ -19,6 +21,7 @@ import ZoomSlider from './ZoomSlider';
|
|||||||
import SearchComponent from '../../common/SearchComponent';
|
import SearchComponent from '../../common/SearchComponent';
|
||||||
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
|
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
|
||||||
import { exportSheet, exportSheetProject } from '../../../utils/exportFile';
|
import { exportSheet, exportSheetProject } from '../../../utils/exportFile';
|
||||||
|
import { FORM_TYPE_OPTIONS } from '../../../constants/constants';
|
||||||
|
|
||||||
const SettingPanel = ({
|
const SettingPanel = ({
|
||||||
selectedColumnId,
|
selectedColumnId,
|
||||||
@ -34,6 +37,8 @@ const SettingPanel = ({
|
|||||||
sizeMult,
|
sizeMult,
|
||||||
onChangeShowColumnFilters,
|
onChangeShowColumnFilters,
|
||||||
onAddRow,
|
onAddRow,
|
||||||
|
onAddProgram,
|
||||||
|
onAddProject,
|
||||||
onDeleteRow,
|
onDeleteRow,
|
||||||
isLoadingData,
|
isLoadingData,
|
||||||
formId,
|
formId,
|
||||||
@ -41,13 +46,13 @@ const SettingPanel = ({
|
|||||||
direction,
|
direction,
|
||||||
year,
|
year,
|
||||||
isProject,
|
isProject,
|
||||||
|
formType,
|
||||||
}) => {
|
}) => {
|
||||||
const [isPinned, setIsPinned] = useState(false);
|
const [isPinned, setIsPinned] = useState(false);
|
||||||
const [isExporting, setIsExporting] = useState(false);
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
const [selectedColumnIds, setSelectedColumnIds] = useState();
|
const [selectedColumnIds, setSelectedColumnIds] = useState();
|
||||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||||
|
|
||||||
// TODO: console.log(!!!) везде посомтреть
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedColumnId) {
|
if (!selectedColumnId) {
|
||||||
setIsPinned(false);
|
setIsPinned(false);
|
||||||
@ -65,7 +70,7 @@ const SettingPanel = ({
|
|||||||
}, [columnVisibility]);
|
}, [columnVisibility]);
|
||||||
|
|
||||||
//TO DO: сделать
|
//TO DO: сделать
|
||||||
const { addColorForCells, addFormulaForCells, deleteFormula } = {};
|
const { addColorForCells,} = {};
|
||||||
|
|
||||||
const handleChangeShowColumnFilters = () => {
|
const handleChangeShowColumnFilters = () => {
|
||||||
const show = table.getState().showColumnFilters;
|
const show = table.getState().showColumnFilters;
|
||||||
@ -73,10 +78,6 @@ const SettingPanel = ({
|
|||||||
onChangeShowColumnFilters(!show);
|
onChangeShowColumnFilters(!show);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickAddFormula = () => { };
|
|
||||||
|
|
||||||
const handleClickDeleteFormula = () => { };
|
|
||||||
|
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
if (isExporting) return;
|
if (isExporting) return;
|
||||||
if (isProject) {
|
if (isProject) {
|
||||||
@ -111,11 +112,6 @@ const SettingPanel = ({
|
|||||||
console.warn('[ColumnPin] клик Pin: selectedColumnId пустой');
|
console.warn('[ColumnPin] клик Pin: selectedColumnId пустой');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[ColumnPin] клик Pin:', {
|
|
||||||
selectedColumnId,
|
|
||||||
isPinned,
|
|
||||||
action: isPinned ? 'unpin' : 'pin',
|
|
||||||
});
|
|
||||||
isPinned
|
isPinned
|
||||||
? onUnpinColumn?.(selectedColumnId)
|
? onUnpinColumn?.(selectedColumnId)
|
||||||
: onPinColumn?.(selectedColumnId);
|
: onPinColumn?.(selectedColumnId);
|
||||||
@ -143,6 +139,23 @@ const SettingPanel = ({
|
|||||||
<RemoveLine />
|
<RemoveLine />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{/* проверяем на 4 форму */}
|
||||||
|
{formType == FORM_TYPE_OPTIONS[3] && <>
|
||||||
|
|
||||||
|
<Tooltip title="Добавить программу">
|
||||||
|
<IconButton onClick={onAddProgram} variant="outlined">
|
||||||
|
<Plus />
|
||||||
|
<Program />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="Добавить проект">
|
||||||
|
<IconButton onClick={onAddProject} variant="outlined">
|
||||||
|
<Plus />
|
||||||
|
<Project />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</>}
|
||||||
</GroupByObject>
|
</GroupByObject>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,19 +88,33 @@ export const RealtimeProvider = ({
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "program_added":
|
||||||
|
console.log("Program added:", data);
|
||||||
|
if (data.result && onProgramAddRef.current) {
|
||||||
|
onProgramAddRef.current(data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "project_added":
|
||||||
|
console.log("Project added:", data);
|
||||||
|
if (data.result && onProjectAddRef.current) {
|
||||||
|
onProjectAddRef.current(data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Unknown event:", data.event);
|
console.log("Unknown event:", data.event);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { isConnectedRef, isConnected, sendMessage, disconnect, lastError } = useWebSocket(
|
const { isConnectedRef, isConnected, sendMessage, disconnect, lastError } =
|
||||||
wsUrl,
|
useWebSocket(wsUrl, handleMessage);
|
||||||
handleMessage,
|
|
||||||
);
|
|
||||||
|
|
||||||
const onCellUpdateRef = useRef(null);
|
const onCellUpdateRef = useRef(null);
|
||||||
const onRowAddRef = useRef(null);
|
const onRowAddRef = useRef(null);
|
||||||
const onRowDeleteRef = useRef(null);
|
const onRowDeleteRef = useRef(null);
|
||||||
|
const onProgramAddRef = useRef(null);
|
||||||
|
const onProjectAddRef = useRef(null);
|
||||||
const onErrorRef = useRef(null);
|
const onErrorRef = useRef(null);
|
||||||
const onAuthSuccessRef = useRef(null);
|
const onAuthSuccessRef = useRef(null);
|
||||||
const onCellEditStartRef = useRef(null);
|
const onCellEditStartRef = useRef(null);
|
||||||
@ -283,6 +297,42 @@ export const RealtimeProvider = ({
|
|||||||
[isConnected, sendMessage, sheetName, formId, direction],
|
[isConnected, sendMessage, sheetName, formId, direction],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addProject = useCallback(
|
||||||
|
async (rowData) => {
|
||||||
|
if (!isConnectedRef) {
|
||||||
|
onErrorRef.current?.("Нет подключения или авторизации");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
event: "project_added",
|
||||||
|
data: rowData,
|
||||||
|
};
|
||||||
|
addCommonField(message);
|
||||||
|
|
||||||
|
return sendMessage(message);
|
||||||
|
},
|
||||||
|
[isConnected, sendMessage, sheetName, formId, direction],
|
||||||
|
);
|
||||||
|
|
||||||
|
const addProgram = useCallback(
|
||||||
|
async (rowData) => {
|
||||||
|
if (!isConnectedRef) {
|
||||||
|
onErrorRef.current?.("Нет подключения или авторизации");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
event: "program_added",
|
||||||
|
data: rowData,
|
||||||
|
};
|
||||||
|
addCommonField(message);
|
||||||
|
|
||||||
|
return sendMessage(message);
|
||||||
|
},
|
||||||
|
[isConnected, sendMessage, sheetName, formId, direction],
|
||||||
|
);
|
||||||
|
|
||||||
const deleteRow = useCallback(
|
const deleteRow = useCallback(
|
||||||
async (rowId) => {
|
async (rowId) => {
|
||||||
if (!isConnectedRef) {
|
if (!isConnectedRef) {
|
||||||
@ -322,6 +372,20 @@ export const RealtimeProvider = ({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const subscribeToProgramAdds = useCallback((callback) => {
|
||||||
|
onProgramAddRef.current = callback;
|
||||||
|
return () => {
|
||||||
|
onProgramAddRef.current = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const subscribeToProjectAdds = useCallback((callback) => {
|
||||||
|
onProjectAddRef.current = callback;
|
||||||
|
return () => {
|
||||||
|
onProjectAddRef.current = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const subscribeToErrors = useCallback((callback) => {
|
const subscribeToErrors = useCallback((callback) => {
|
||||||
onErrorRef.current = callback;
|
onErrorRef.current = callback;
|
||||||
return () => {
|
return () => {
|
||||||
@ -366,9 +430,13 @@ export const RealtimeProvider = ({
|
|||||||
endEditing,
|
endEditing,
|
||||||
updateCell,
|
updateCell,
|
||||||
addRow,
|
addRow,
|
||||||
|
addProgram,
|
||||||
|
addProject,
|
||||||
deleteRow,
|
deleteRow,
|
||||||
subscribeToRowAdds,
|
subscribeToRowAdds,
|
||||||
subscribeToRowDeletes,
|
subscribeToRowDeletes,
|
||||||
|
subscribeToProgramAdds,
|
||||||
|
subscribeToProjectAdds,
|
||||||
subscribeToErrors,
|
subscribeToErrors,
|
||||||
subscribeToAuthSuccess,
|
subscribeToAuthSuccess,
|
||||||
subscribeToCellEditStart,
|
subscribeToCellEditStart,
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { FormsSheetApi } from "../../../api/form_sheet";
|
import { FormsSheetApi } from "../../../api/form_sheet";
|
||||||
import { useRealtime } from "../contexts/RealtimeContext";
|
import { useRealtime } from "../contexts/RealtimeContext";
|
||||||
import { deleteRow, insertCell, updateCells } from "../utils/cellUtils";
|
import {
|
||||||
|
deleteRow,
|
||||||
|
insertCell,
|
||||||
|
insertProjectRow,
|
||||||
|
updateCells,
|
||||||
|
} from "../utils/cellUtils";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { isVspNewRow, isVspRow } from "../utils/rowUtils";
|
import { getParentId, isVspNewRow, isVspRow } from "../utils/rowUtils";
|
||||||
import { ProjectsApi } from "../../../api/projects";
|
import { ProjectsApi } from "../../../api/projects";
|
||||||
|
|
||||||
const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
||||||
@ -19,6 +24,8 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
|||||||
error: wsError,
|
error: wsError,
|
||||||
subscribeToCellUpdates,
|
subscribeToCellUpdates,
|
||||||
subscribeToRowAdds,
|
subscribeToRowAdds,
|
||||||
|
subscribeToProgramAdds,
|
||||||
|
subscribeToProjectAdds,
|
||||||
subscribeToRowDeletes,
|
subscribeToRowDeletes,
|
||||||
subscribeToErrors,
|
subscribeToErrors,
|
||||||
subscribeToCellEditStart,
|
subscribeToCellEditStart,
|
||||||
@ -182,7 +189,7 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
|||||||
|
|
||||||
const unsubscribeRowAdds = subscribeToRowAdds((data) => {
|
const unsubscribeRowAdds = subscribeToRowAdds((data) => {
|
||||||
const updates = data.result.data;
|
const updates = data.result.data;
|
||||||
const expense_item_id = data.data.expense_item_id || null;
|
const parent_id = getParentId(data.data);
|
||||||
const newLineId = data.result.new_line_id;
|
const newLineId = data.result.new_line_id;
|
||||||
const [updatedCells, newRow] = updates.reduce(
|
const [updatedCells, newRow] = updates.reduce(
|
||||||
(res, row) => {
|
(res, row) => {
|
||||||
@ -201,7 +208,49 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
|||||||
}
|
}
|
||||||
setData((prevData) => {
|
setData((prevData) => {
|
||||||
const updateData = updateCells(prevData, updatedCells);
|
const updateData = updateCells(prevData, updatedCells);
|
||||||
const newData = insertCell(updateData, newRow, expense_item_id);
|
const newData = insertCell(updateData, newRow, parent_id);
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeProjectAdds = subscribeToProjectAdds((data) => {
|
||||||
|
const updates = data.result[1];
|
||||||
|
const program_id = data.data.program_id;
|
||||||
|
const [updatedCells, newRow] = updates.reduce(
|
||||||
|
(res, row) => {
|
||||||
|
if (row[0] === "ITEM") {
|
||||||
|
res[1] = row;
|
||||||
|
} else {
|
||||||
|
res[0].push(row);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
[[], null],
|
||||||
|
);
|
||||||
|
if (newRow === null) {
|
||||||
|
toast.error("Ошибка получения данных");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setData((prevData) => {
|
||||||
|
const updateData = updateCells(prevData, updatedCells);
|
||||||
|
const newData = insertProjectRow(updateData, newRow, program_id);
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeProgramAdds = subscribeToProgramAdds((data) => {
|
||||||
|
const newRow = data.result[1][0];
|
||||||
|
setData((prevData) => {
|
||||||
|
const updateData = structuredClone(prevData);
|
||||||
|
const [row_type, depth, sort_order, dataRow] = newRow;
|
||||||
|
const newRowObject = {
|
||||||
|
row_type: row_type,
|
||||||
|
depth: depth,
|
||||||
|
sort_order: sort_order,
|
||||||
|
subRows: [],
|
||||||
|
data: dataRow,
|
||||||
|
};
|
||||||
|
const newData = [...updateData, newRowObject];
|
||||||
return newData;
|
return newData;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -255,6 +304,8 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribeCellUpdates();
|
unsubscribeCellUpdates();
|
||||||
unsubscribeRowAdds();
|
unsubscribeRowAdds();
|
||||||
|
unsubscribeProgramAdds();
|
||||||
|
unsubscribeProjectAdds();
|
||||||
unsubscribeRowDeletes();
|
unsubscribeRowDeletes();
|
||||||
unsubscribeErrors();
|
unsubscribeErrors();
|
||||||
unsubscribeCellEditStart();
|
unsubscribeCellEditStart();
|
||||||
@ -268,6 +319,8 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
|||||||
subscribeToErrors,
|
subscribeToErrors,
|
||||||
subscribeToCellEditStart,
|
subscribeToCellEditStart,
|
||||||
subscribeToCellEditEnd,
|
subscribeToCellEditEnd,
|
||||||
|
subscribeToProgramAdds,
|
||||||
|
subscribeToProjectAdds,
|
||||||
formatedToSubRows,
|
formatedToSubRows,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { getParentId } from "./rowUtils";
|
||||||
|
|
||||||
export function setNewValueToData(data, colId, rowId, value) {
|
export function setNewValueToData(data, colId, rowId, value) {
|
||||||
let targetObject = data;
|
let targetObject = data;
|
||||||
if (rowId && rowId.length) {
|
if (rowId && rowId.length) {
|
||||||
@ -114,7 +116,7 @@ export function updateCells(data, newRows) {
|
|||||||
return updatedRows;
|
return updatedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertCell(data, newRow, expense_item_id) {
|
export function insertCell(data, newRow, parent_id) {
|
||||||
const updatedRows = JSON.parse(JSON.stringify(data));
|
const updatedRows = JSON.parse(JSON.stringify(data));
|
||||||
const [row_type, depth, sort_order, dataRow] = newRow;
|
const [row_type, depth, sort_order, dataRow] = newRow;
|
||||||
const newRowObject = {
|
const newRowObject = {
|
||||||
@ -126,7 +128,7 @@ export function insertCell(data, newRow, expense_item_id) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Если expense_item_id === null, добавляем в конец
|
// Если expense_item_id === null, добавляем в конец
|
||||||
if (expense_item_id === null) {
|
if (parent_id === null) {
|
||||||
updatedRows.push(newRowObject);
|
updatedRows.push(newRowObject);
|
||||||
return updatedRows;
|
return updatedRows;
|
||||||
}
|
}
|
||||||
@ -143,7 +145,47 @@ export function insertCell(data, newRow, expense_item_id) {
|
|||||||
row.sort_order = row.sort_order + 1;
|
row.sort_order = row.sort_order + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!adding & (row.data.header.expense_item_id === expense_item_id)) {
|
if (!adding & (getParentId(row.data.header) === parent_id)) {
|
||||||
|
if (!("subRows" in row)) {
|
||||||
|
row.subRows = [];
|
||||||
|
}
|
||||||
|
row.subRows.push(newRowObject);
|
||||||
|
adding = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.subRows && Array.isArray(row.subRows) && row.subRows.length > 0) {
|
||||||
|
const found = findAndUpdate(row.subRows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
findAndUpdate(updatedRows);
|
||||||
|
return updatedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function insertProjectRow(data, newRow, program_id) {
|
||||||
|
const updatedRows = structuredClone(data);
|
||||||
|
const [row_type, depth, sort_order, dataRow] = newRow;
|
||||||
|
const newRowObject = {
|
||||||
|
row_type: row_type,
|
||||||
|
depth: depth,
|
||||||
|
sort_order: sort_order,
|
||||||
|
subRows: [],
|
||||||
|
data: dataRow,
|
||||||
|
};
|
||||||
|
|
||||||
|
let adding = false;
|
||||||
|
function findAndUpdate(rows) {
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const row = rows[i];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(row.sort_order >= newRowObject.sort_order) &
|
||||||
|
(row.data.line_id != newRowObject.data.line_id)
|
||||||
|
) {
|
||||||
|
row.sort_order = row.sort_order + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!adding & (row.data.header.program_id === program_id)) {
|
||||||
if (!("subRows" in row)) {
|
if (!("subRows" in row)) {
|
||||||
row.subRows = [];
|
row.subRows = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,3 +13,7 @@ export function isVspNewRow(rowData) {
|
|||||||
const newRow = rowData[3];
|
const newRow = rowData[3];
|
||||||
return !!newRow.id;
|
return !!newRow.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getParentId(data){
|
||||||
|
return data.project_id || data.expense_item_id || null;
|
||||||
|
}
|
||||||
|
|||||||
20
web/src/components/common/StyledTextField.jsx
Normal file
20
web/src/components/common/StyledTextField.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
TextField,
|
||||||
|
} from '@mui/material';
|
||||||
|
export const StyledTextField = ({ error, helperText, disabled, ...props }) => (
|
||||||
|
<TextField
|
||||||
|
className="fullWidth"
|
||||||
|
size="small"
|
||||||
|
error={!!error}
|
||||||
|
helperText={helperText || error}
|
||||||
|
disabled={disabled}
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
height: '2.69rem',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
@ -1,117 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import Tooltip from './Tooltip';
|
|
||||||
|
|
||||||
const TextInput = ({
|
|
||||||
label,
|
|
||||||
name,
|
|
||||||
type = 'text',
|
|
||||||
onChange,
|
|
||||||
onChangeWithName,
|
|
||||||
tooltipText = '',
|
|
||||||
value = '',
|
|
||||||
placeholder = '',
|
|
||||||
autocomplete = 'new-password',
|
|
||||||
}) => {
|
|
||||||
const [cur_value, setCurValue] = useState(value);
|
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
|
||||||
|
|
||||||
const handleChange = (inputValue) => {
|
|
||||||
if (onChangeWithName) {
|
|
||||||
onChangeWithName(name, inputValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onChange) {
|
|
||||||
onChange(inputValue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TextInputDiv>
|
|
||||||
<label>
|
|
||||||
{label}
|
|
||||||
<input
|
|
||||||
type={type}
|
|
||||||
name={name}
|
|
||||||
onChange={(e) => {
|
|
||||||
setCurValue(e.target.value);
|
|
||||||
if (!isFocused) {
|
|
||||||
handleChange(e.target.value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onBlur={() => {
|
|
||||||
handleChange(cur_value);
|
|
||||||
setIsFocused(false);
|
|
||||||
}}
|
|
||||||
onFocus={() => setIsFocused(true)}
|
|
||||||
value={cur_value}
|
|
||||||
autoComplete={autocomplete}
|
|
||||||
placeholder={placeholder}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
{tooltipText !== '' && <Tooltip tooltipText={tooltipText} />}
|
|
||||||
</TextInputDiv>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const TextInputDiv = styled.div`
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
label {
|
|
||||||
color: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.2rem; /* Уменьшено еще на 10% (0.225rem * 0.9) */
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 1.6rem; /* Уменьшено еще на 10% (1.8rem * 0.9) */
|
|
||||||
min-height: 36px; /* Уменьшено еще на 10% (40px * 0.9) */
|
|
||||||
background-color: rgb(240, 242, 246);
|
|
||||||
padding: 0 0.4rem; /* Уменьшено еще на 10% (0.45rem * 0.9) */
|
|
||||||
font-size: inherit;
|
|
||||||
transition: border-color 0.2s ease;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #258141;
|
|
||||||
box-shadow: 0 0 0 2px rgba(37, 129, 65, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Медиа-запросы для адаптивности */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
input {
|
|
||||||
height: 2rem; /* Уменьшено еще на 10% (2.25rem * 0.9) */
|
|
||||||
min-height: 39px; /* Уменьшено еще на 10% (43px * 0.9) */
|
|
||||||
font-size: 13px; /* Уменьшено еще на 10% (14.4px * 0.9) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
label {
|
|
||||||
font-size: 0.73rem; /* Уменьшено еще на 10% (0.81rem * 0.9) */
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
height: 2.3rem; /* Уменьшено еще на 10% (2.52rem * 0.9) */
|
|
||||||
min-height: 42px; /* Уменьшено еще на 10% (47px * 0.9) */
|
|
||||||
padding: 0 0.6rem; /* Уменьшено еще на 10% (0.675rem * 0.9) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default TextInput;
|
|
||||||
@ -108,6 +108,23 @@ export const AddColumn = styled.div`
|
|||||||
background-position: center;
|
background-position: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const Project = styled.div`
|
||||||
|
background-image: url('images/Project.svg');
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
`;
|
||||||
|
export const Program = styled.div`
|
||||||
|
background-image: url('images/Program.svg');
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
`;
|
||||||
|
|
||||||
export const DeleteColumn = styled.div`
|
export const DeleteColumn = styled.div`
|
||||||
background-image: url('images/RemoveColumn.svg');
|
background-image: url('images/RemoveColumn.svg');
|
||||||
width: 1.25rem;
|
width: 1.25rem;
|
||||||
|
|||||||
@ -11,6 +11,7 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
background: #fcfcfd;
|
background: #fcfcfd;
|
||||||
color: #171a1c;
|
color: #171a1c;
|
||||||
|
/* color: green; */
|
||||||
font-family:
|
font-family:
|
||||||
-apple-system,
|
-apple-system,
|
||||||
Segoe UI,
|
Segoe UI,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user