готово #78

Merged
PotapovaA merged 1 commits from color-smeta into test 2026-08-07 17:32:34 +03:00
6 changed files with 314 additions and 550 deletions

View File

@ -68,7 +68,9 @@ const INTEGER_COLUMN_IDS = new Set([
const getColorBrightness = (hexColor) => { const getColorBrightness = (hexColor) => {
if (!hexColor) return 255; if (!hexColor) return 255;
const color = hexColor.replace('#', ''); const color = hexColor.replace('#', '');
let r, g, b; let r;
let g;
let b;
if (color.length === 3) { if (color.length === 3) {
r = Number.parseInt(color[0] + color[0], 16); r = Number.parseInt(color[0] + color[0], 16);
g = Number.parseInt(color[1] + color[1], 16); g = Number.parseInt(color[1] + color[1], 16);
@ -86,7 +88,7 @@ const getColorBrightness = (hexColor) => {
const formatNumber = (value, asInteger = false) => { const formatNumber = (value, asInteger = false) => {
if (value === null || value === undefined || value === '') return String(value || ''); if (value === null || value === undefined || value === '') return String(value || '');
const num = Number(value); const num = Number(value);
if (isNaN(num)) return String(value); if (Number.isNaN(num)) return String(value);
return num.toLocaleString('ru-RU', { return num.toLocaleString('ru-RU', {
minimumFractionDigits: asInteger ? 0 : 1, minimumFractionDigits: asInteger ? 0 : 1,
maximumFractionDigits: asInteger ? 0 : 1, maximumFractionDigits: asInteger ? 0 : 1,
@ -146,7 +148,7 @@ const CellComponent = ({
// Мемоизация значения // Мемоизация значения
const { rawValue, displayValue, isNumeric } = useMemo(() => { const { rawValue, displayValue, isNumeric } = useMemo(() => {
const value = cell.getValue(); const value = cell.getValue();
const isNum = !isNaN(Number(value)) && value !== null && value !== undefined && value !== ''; const isNum = value !== null && value !== undefined && value !== '' && !Number.isNaN(Number(value));
let display = String(value || ''); let display = String(value || '');
if (isNum) { if (isNum) {
@ -191,7 +193,8 @@ const CellComponent = ({
}, [isLocked, onClick]); }, [isLocked, onClick]);
return ( return (
<div className='cell' style={cellStyles} onClick={handleClick}> // biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<div type="button" className='cell' style={cellStyles} onClick={handleClick}>
<span style={CONTAINER_STYLES}>{highlightedContent}</span> <span style={CONTAINER_STYLES}>{highlightedContent}</span>
{lockBadge} {lockBadge}
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
import { blueColumn, greenColumn, orangeColumn } from "./columnColors";
export const columnPinningDefault = { export const columnPinningDefault = {
left: ['mrt-row-expand', 'sort_order'], left: ['mrt-row-expand', 'sort_order'],
right: [], right: [],
}; };
export const columnVisibilityDefault = { 'mrt-row-select': false }; export const columnVisibilityDefault = { 'mrt-row-select': false };
export const sectionCodeColor = { 1: greenColumn, 3: orangeColumn, 4: blueColumn}

View File

@ -2,6 +2,8 @@ import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react'
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { useRealtime } from './contexts/RealtimeContext'; import { useRealtime } from './contexts/RealtimeContext';
import { sectionCodeColor } from './constants/columnConfig';
import { blueColumn, greenColumn } from './constants/columnColors';
const EditCellPortal = React.memo(({ const EditCellPortal = React.memo(({
cell, cell,
@ -76,6 +78,13 @@ const EditCellPortal = React.memo(({
); );
}); });
function getColorCell(colors, column, row) {
if (row.original.data.section_code){
const section_code = row.original.data.section_code[0];
return (sectionCodeColor[section_code] || blueColumn)[row.original?.row_type || row.row_type];
}
return colors[column.id]?.color_type?.[row.original?.row_type || row.row_type];
}
export const getTableColumns = ({ export const getTableColumns = ({
Cell, Cell,
@ -121,7 +130,7 @@ export const getTableColumns = ({
globalFilter, globalFilter,
columnFilter, columnFilter,
isEditable: row.original?.row_type === 'INPUT' || false, isEditable: row.original?.row_type === 'INPUT' || false,
backgroundColor: columnColors[column.id]?.color_type?.[row.original?.row_type || row.row_type], backgroundColor: getColorCell(columnColors, column, row),
isUpdating: table.options.meta?.updatingCells?.[`${row.id}_${column.id}`], isUpdating: table.options.meta?.updatingCells?.[`${row.id}_${column.id}`],
isInvalid, isInvalid,
value, value,
@ -154,7 +163,7 @@ export const getTableColumns = ({
// Оптимизированная функция processColumns // Оптимизированная функция processColumns
const processColumns = (columns) => { const processColumns = (columns) => {
columns.forEach((col) => { for (const col of columns) {
col.Cell = ({ cell, table, column, row }) => { col.Cell = ({ cell, table, column, row }) => {
const props = getCachedCellProps(row, column, table); const props = getCachedCellProps(row, column, table);
@ -163,7 +172,7 @@ export const getTableColumns = ({
column, column,
cell, cell,
...props, ...props,
}), [row.id, column.id, cell.getValue(), props._hash]); }), [row, column, cell, props]);
return <Cell {...cellProps} />; return <Cell {...cellProps} />;
}; };
@ -187,7 +196,7 @@ export const getTableColumns = ({
if (col.columns?.length) { if (col.columns?.length) {
processColumns(col.columns); processColumns(col.columns);
} }
}); }
}; };
processColumns(columns); processColumns(columns);
@ -201,8 +210,7 @@ export const getTableColumns = ({
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
onCellNumberClick(row.id); onCellNumberClick(row.id);
}, [row.id, onCellNumberClick]); }, [row.id, onCellNumberClick]);
return <button type="button" onClick={handleClick}>{`${cell.getValue()} `}</button>;
return <button onClick={handleClick}>{cell.getValue() + ' '}</button>;
}, },
}; };

View File

@ -1,6 +1,6 @@
import { Box, Button, Divider, FormControl, FormLabel, IconButton, MenuItem, Select, Stack, TextField } from '@mui/material'; import { Box, Button, Divider, FormControl, FormLabel, IconButton, MenuItem, Select, Stack, TextField } from '@mui/material';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants'; import { STAGE_ROLE_RUSSIAN_NAME, DIRECTION_TRANSLATE } from '../../constants/constants';
import { formatDateForApi } from '../../utils/formatDate'; import { formatDateForApi } from '../../utils/formatDate';
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils'; import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
import CollapsibleTree from '../common/CollapsibleTree'; import CollapsibleTree from '../common/CollapsibleTree';
@ -27,6 +27,7 @@ export const AddStagesModal = ({
const [endDate, setEndDate] = useState(null); const [endDate, setEndDate] = useState(null);
const [stageName, setStageName] = useState(''); const [stageName, setStageName] = useState('');
const [selectedSheet, setSelectedSheet] = useState(defaultSheet); const [selectedSheet, setSelectedSheet] = useState(defaultSheet);
const [selectedDirection, setSelectedDirection] = useState('');
const [selectedRole, setSelectedRole] = useState(); const [selectedRole, setSelectedRole] = useState();
const [columnsConfig, setColumnsConfig] = useState({ columns: [], colors: {} }); const [columnsConfig, setColumnsConfig] = useState({ columns: [], colors: {} });
const [selectedColumns, setSelectedColumns] = useState([]); const [selectedColumns, setSelectedColumns] = useState([]);
@ -34,7 +35,7 @@ export const AddStagesModal = ({
useEffect(() => { useEffect(() => {
const loadColumnsConfig = async () => { const loadColumnsConfig = async () => {
if (!selectedSheet) return; if (!selectedSheet) return;
const sheetObj = sheetOptions.find((s) => s.value == selectedSheet); const sheetObj = sheetOptions.find((s) => s.value === selectedSheet);
try { try {
const { config } = await import(`../RealtimeTable/constants/${formType}/${sheetObj.sheet}.js`); const { config } = await import(`../RealtimeTable/constants/${formType}/${sheetObj.sheet}.js`);
@ -54,13 +55,15 @@ export const AddStagesModal = ({
setEndDate(stage.closes_at ?? null); setEndDate(stage.closes_at ?? null);
setStageName(stage.phase_code ?? ''); setStageName(stage.phase_code ?? '');
setSelectedSheet(stage.sheet ?? null); setSelectedSheet(stage.sheet ?? null);
setSelectedDirection(stage.direction ?? '');
setSelectedRole(stage.role ?? null); setSelectedRole(stage.role ?? null);
setSelectedColumns(stage.column_keys.map((c) => 'data.' + c)); setSelectedColumns(stage.column_keys.map((c) => `data.${c}`));
} else { } else {
setStartDate(null); setStartDate(null);
setEndDate(null); setEndDate(null);
setStageName(''); setStageName('');
setSelectedSheet(defaultSheet); setSelectedSheet(defaultSheet);
setSelectedDirection('');
setSelectedRole(); setSelectedRole();
setSelectedColumns([]); setSelectedColumns([]);
} }
@ -69,12 +72,12 @@ export const AddStagesModal = ({
const handleSave = () => { const handleSave = () => {
const opensAt = formatDateForApi(startDate); const opensAt = formatDateForApi(startDate);
const closesAt = formatDateForApi(endDate); const closesAt = formatDateForApi(endDate);
const sheetObj = sheetOptions.find((s) => s.value == selectedSheet); const sheetObj = sheetOptions.find((s) => s.value === selectedSheet);
const stageData = { const stageData = {
phase_code: stageName, phase_code: stageName,
sheet: sheetObj.sheet, sheet: sheetObj.sheet,
direction: sheetObj.direction, direction: selectedDirection,
role: selectedRole, role: selectedRole,
opens_at: opensAt, opens_at: opensAt,
closes_at: closesAt, closes_at: closesAt,
@ -101,6 +104,7 @@ export const AddStagesModal = ({
setEndDate(null); setEndDate(null);
setStageName(''); setStageName('');
setSelectedSheet(defaultSheet); setSelectedSheet(defaultSheet);
setSelectedDirection('');
setSelectedRole(); setSelectedRole();
setSelectedColumns([]); setSelectedColumns([]);
onClose(); onClose();
@ -115,6 +119,7 @@ export const AddStagesModal = ({
const handleSheetChange = (event) => { const handleSheetChange = (event) => {
setSelectedSheet(event.target.value); setSelectedSheet(event.target.value);
setSelectedColumns([]); setSelectedColumns([]);
setSelectedDirection(''); // Сбрасываем direction при смене листа
}; };
const handleColumnsSelect = (column, isSelect) => { const handleColumnsSelect = (column, isSelect) => {
@ -129,7 +134,7 @@ export const AddStagesModal = ({
} }
}; };
const isFormValid = startDate && endDate && stageName.trim() && selectedSheet; const isFormValid = startDate && endDate && stageName.trim() && selectedSheet && selectedDirection;
if (!isOpen) return null; if (!isOpen) return null;
@ -138,6 +143,10 @@ export const AddStagesModal = ({
const columnTree = columnsConfig.columns || []; const columnTree = columnsConfig.columns || [];
// Проверяем, нужно ли показывать выбор направления (для AHR CAP)
const sheetObj = sheetOptions.find((s) => s.value === selectedSheet);
const showDirectionSelect = sheetObj?.direction;
return ( return (
<ModalBackdrop isOpen={isOpen} onClick={handleBackdropClick}> <ModalBackdrop isOpen={isOpen} onClick={handleBackdropClick}>
<ModalContainer> <ModalContainer>
@ -187,6 +196,31 @@ export const AddStagesModal = ({
</Select> </Select>
</FormControl> </FormControl>
</Stack> </Stack>
{/* Выбор направления (только для AHR CAP) */}
{showDirectionSelect && (
<Stack sx={{ marginBottom: '0.75rem', width: '100%' }}>
<FormControl fullWidth>
<FormLabel>Направление</FormLabel>
<Select
value={selectedDirection}
onChange={(e) => setSelectedDirection(e.target.value)}
displayEmpty
size='small'
>
<MenuItem value='' disabled>
Выберите направление
</MenuItem>
{Object.entries(DIRECTION_TRANSLATE).map(([key, label]) => (
<MenuItem key={key} value={key}>
{label}
</MenuItem>
))}
</Select>
</FormControl>
</Stack>
)}
{selectedSheet && columnTree.length > 0 && ( {selectedSheet && columnTree.length > 0 && (
<Stack sx={{ marginBottom: '0.75rem', width: '100%' }}> <Stack sx={{ marginBottom: '0.75rem', width: '100%' }}>
<FormControl fullWidth> <FormControl fullWidth>
@ -258,4 +292,4 @@ export const AddStagesModal = ({
</ModalContainer> </ModalContainer>
</ModalBackdrop> </ModalBackdrop>
); );
}; };

View File

@ -7,5 +7,5 @@ export const stageColumnsHiddenFromPicker = [
'data.header.internal_order', 'data.header.internal_order',
'data.header.vsp_id', 'data.header.vsp_id',
'data.header.section_code', 'data.header.section_code',
'data.header.justification', // 'data.header.justification',
]; ];