327 lines
10 KiB
JavaScript

import { Divider, IconButton, Stack, Tooltip } from '@mui/material';
import { memo, useCallback, useMemo, useState } from 'react';
import { FORM_TYPE_OPTIONS } from '../../../constants/constants';
import { exportSheet, exportSheetProject } from '../../../utils/exportFile';
import { stageColumnsHiddenFromPicker } from '../../Stages/constants';
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
import CollapsibleTree from '../../common/CollapsibleTree';
import SearchComponent from '../../common/SearchComponent';
import {
AddLine,
AllStageColumns,
CurrentStageColumns,
HiddenExpenseItems,
Minus,
Pin,
Plus,
Program,
Project,
RemoveLine,
Search,
VisibleExpenseItems,
} from '../../common/icons/icons';
import { collectLeafColumns } from '../utils/columnUtils';
import { GroupByObject } from './GroupByObject/GroupByObject';
import { Panel } from './SettingPanel.style';
import ValidationErrorsAlert from './ValidationErrorsAlert';
import ZoomSlider from './ZoomSlider';
const controlSx = {
minHeight: '2.5rem',
flex: '0 0 2.5rem',
};
const searchSx = { height: '2.5rem' };
const searchNavigationSx = {
display: 'inline-flex',
alignItems: 'center',
height: '2rem',
border: '1px solid rgba(0, 0, 0, 0.12)',
borderRadius: '0.4rem',
overflow: 'hidden',
backgroundColor: '#fff',
};
const searchNavigationButtonSx = {
width: '1.65rem',
height: '2rem',
minWidth: '1.65rem',
padding: 0,
borderRadius: 0,
fontSize: '0.95rem',
};
const searchMatchCounterSx = {
minWidth: '2.7rem',
padding: '0 0.3rem',
fontSize: '0.7rem',
lineHeight: 1,
textAlign: 'center',
whiteSpace: 'nowrap',
color: 'rgba(0, 0, 0, 0.65)',
};
const exportButtonSx = { height: '2.5rem', minHeight: '2.5rem' };
const SettingPanel = ({
selectedColumnId,
columnPinning,
columns,
onPinColumn,
onUnpinColumn,
onToggleColumnVisibility,
onSetColumnsVisibility,
columnVisibility,
columnsCurStage,
onChangeSizeMult,
onGlobalFilterChange,
globalFilter,
onSearchKeyDown,
onSearchNext,
onSearchPrevious,
searchMatchCount,
searchMatchIndex,
sizeMult,
onChangeShowColumnFilters,
showColumnFilters,
onAddRow,
onAddProgram,
onAddProject,
onDeleteRow,
formId,
sheetName,
direction,
year,
isFormNoCanAddRow,
canModifyRows,
formType,
validationErrors = [],
onNavigateToColumn,
expenseItemsVisibilityAction,
onToggleDepthVisibility,
}) => {
const [isExporting, setIsExporting] = useState(false);
const isPinned = useMemo(
() => Boolean(selectedColumnId && columnPinning?.left?.includes(selectedColumnId)),
[columnPinning, selectedColumnId],
);
const selectedColumnIds = useMemo(() => Object.keys(columnVisibility).filter((key) => !columnVisibility[key]), [columnVisibility]);
const handleChangeShowColumnFilters = useCallback(() => {
onChangeShowColumnFilters(!showColumnFilters);
}, [onChangeShowColumnFilters, showColumnFilters]);
const handleExport = useCallback(async () => {
if (isExporting) return;
if (!formId || !sheetName || (formType === 'PROJECT' && !year)) return;
setIsExporting(true);
try {
if (formType === 'PROJECT') {
await exportSheetProject(formId, sheetName, year);
} else {
await exportSheet(formId, sheetName, direction);
}
} finally {
setIsExporting(false);
}
}, [direction, formId, formType, isExporting, sheetName, year]);
const handleTogglePin = useCallback(() => {
if (!selectedColumnId) return;
isPinned ? onUnpinColumn?.(selectedColumnId) : onPinColumn?.(selectedColumnId);
}, [isPinned, onPinColumn, onUnpinColumn, selectedColumnId]);
const leafColumns = useMemo(() => collectLeafColumns({ columns }), [columns]);
const stageColumnIds = useMemo(() => new Set([...stageColumnsHiddenFromPicker, ...(columnsCurStage || [])]), [columnsCurStage]);
const showOnlyCurrentStageColumns = useMemo(() => {
if (!columnsCurStage?.length || !leafColumns.length) return false;
return leafColumns.every((column) =>
stageColumnIds.has(column.id) ? columnVisibility[column.id] !== false : columnVisibility[column.id] === false,
);
}, [columnVisibility, columnsCurStage, leafColumns, stageColumnIds]);
const handleToggleCurrentStageColumns = useCallback(() => {
if (!columnsCurStage?.length) {
console.warn('Нет столбцов для текущего этапа');
return;
}
const visibilityByColumnId = {};
for (const column of leafColumns) {
visibilityByColumnId[column.id] = showOnlyCurrentStageColumns || stageColumnIds.has(column.id);
}
onSetColumnsVisibility(visibilityByColumnId);
}, [columnsCurStage, leafColumns, onSetColumnsVisibility, showOnlyCurrentStageColumns, stageColumnIds]);
const handleSetAllColumnsVisibility = useCallback(
(columnIds, isVisible) => {
const visibilityByColumnId = Object.fromEntries(columnIds.map((columnId) => [columnId, isVisible]));
onSetColumnsVisibility(visibilityByColumnId);
},
[onSetColumnsVisibility],
);
return (
<>
<Panel data-pin-panel>
<Stack direction='row' sx={{ gap: '1.25rem' }}>
<GroupByObject title='Отображение'>
{/* <ColorPickerButton onColorApply={addColorForCells} /> */}
<Tooltip title='Закрепить область'>
<IconButton
variant='outlined'
sx={controlSx}
data-active={isPinned}
disabled={!selectedColumnId}
onMouseDown={(e) => e.stopPropagation()}
onClick={handleTogglePin}>
<Pin />
</IconButton>
</Tooltip>
</GroupByObject>
<Divider orientation='vertical' flexItem />
<GroupByObject title='Строки'>
{!isFormNoCanAddRow && canModifyRows && (
<>
<Tooltip title='Добавить строку'>
<IconButton onClick={onAddRow} variant='outlined' sx={controlSx}>
<Plus />
<AddLine />
</IconButton>
</Tooltip>
<Tooltip title='Удалить строку'>
<IconButton onClick={onDeleteRow} variant='outlined' sx={controlSx}>
<Minus />
<RemoveLine />
</IconButton>
</Tooltip>
{/* проверяем на 4 форму */}
{formType === FORM_TYPE_OPTIONS[2] && (
<>
<Tooltip title='Добавить программу'>
<IconButton onClick={onAddProgram} variant='outlined' sx={controlSx}>
<Plus />
<Program />
</IconButton>
</Tooltip>
<Tooltip title='Добавить проект'>
<IconButton onClick={onAddProject} variant='outlined' sx={controlSx}>
<Plus />
<Project />
</IconButton>
</Tooltip>
</>
)}
</>
)}
<Tooltip
title={
expenseItemsVisibilityAction === 'show'
? 'Показать следующий уровень статей расходов'
: 'Скрыть нижний уровень статей расходов'
}>
<IconButton
onClick={onToggleDepthVisibility}
variant='outlined'
data-active={expenseItemsVisibilityAction === 'show'}
sx={controlSx}>
{expenseItemsVisibilityAction === 'show' ? <VisibleExpenseItems /> : <HiddenExpenseItems />}
</IconButton>
</Tooltip>
</GroupByObject>
<Divider orientation='vertical' flexItem />
<GroupByObject title='Столбцы'>
{columns ? (
<CollapsibleTree
columnTree={columns}
onSelectNode={onToggleColumnVisibility}
onSelectAllNodes={handleSetAllColumnsVisibility}
selectedIds={selectedColumnIds}
/>
) : (
''
)}
<Tooltip title={showOnlyCurrentStageColumns ? 'Показать все столбцы' : 'Скрыть все столбцы кроме текущих'}>
<IconButton
variant='outlined'
sx={controlSx}
color='success'
data-active={showOnlyCurrentStageColumns}
onClick={handleToggleCurrentStageColumns}
disabled={!columnsCurStage || columnsCurStage.length === 0}>
{showOnlyCurrentStageColumns ? <AllStageColumns /> : <CurrentStageColumns />}
</IconButton>
</Tooltip>
</GroupByObject>
<Divider orientation='vertical' flexItem />
<GroupByObject title='Масштаб'>
<ZoomSlider onChange={onChangeSizeMult} curScale={sizeMult} />
</GroupByObject>
<Divider orientation='vertical' flexItem />
<GroupByObject title='Поиск'>
<SearchComponent
value={globalFilter}
onChange={onGlobalFilterChange}
onKeyDown={onSearchKeyDown}
height='2.5rem'
sx={searchSx}
/>
<span style={searchNavigationSx}>
<Tooltip title='Предыдущий результат (Shift+Enter)'>
<span>
<IconButton
onClick={onSearchPrevious}
disabled={!searchMatchCount}
sx={searchNavigationButtonSx}
aria-label='Предыдущий результат поиска'>
</IconButton>
</span>
</Tooltip>
<span style={searchMatchCounterSx}>
{searchMatchCount ? `${Math.max(searchMatchIndex + 1, 1)}/${searchMatchCount}` : '0/0'}
</span>
<Tooltip title='Следующий результат (Enter)'>
<span>
<IconButton
onClick={onSearchNext}
disabled={!searchMatchCount}
sx={searchNavigationButtonSx}
aria-label='Следующий результат поиска'>
</IconButton>
</span>
</Tooltip>
</span>
<Tooltip title='Поиск по колонкам'>
<IconButton
variant='outlined'
color='success'
data-active={showColumnFilters}
onClick={handleChangeShowColumnFilters}
sx={controlSx}>
<Search />
</IconButton>
</Tooltip>
</GroupByObject>
<Divider orientation='vertical' flexItem />
<GroupByObject title='Экспорт'>
<ExportDefaultButton
onClick={handleExport}
disabled={!formId || !sheetName || isExporting}
text={isExporting ? 'Экспорт...' : 'Экспорт'}
sx={exportButtonSx}
/>
</GroupByObject>
</Stack>
<ValidationErrorsAlert errors={validationErrors} onNavigateToColumn={onNavigateToColumn} />
</Panel>
</>
);
};
export default memo(SettingPanel);