diff --git a/web/src/components/RealtimeTable/Cell/EditCell/EditCell.jsx b/web/src/components/RealtimeTable/Cell/EditCell/EditCell.jsx index 5a0687c..0f87650 100644 --- a/web/src/components/RealtimeTable/Cell/EditCell/EditCell.jsx +++ b/web/src/components/RealtimeTable/Cell/EditCell/EditCell.jsx @@ -2,15 +2,18 @@ import { useEffect, useRef, useLayoutEffect, useState } from 'react'; import { isFormula, extractFormula, calculateFormula, formatNumber } from '../../utils/formulaUtils'; import { saveToStorage, loadFromStorage } from '../../utils/localStorageUtils'; +import { useRealtime } from '../../contexts/RealtimeContext'; + +const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId, table }) => { + const { endEditing: contextEndEditing } = useRealtime(); -const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId }) => { const [curValue, setCurValue] = useState(value); const [displayValue, setDisplayValue] = useState(value); const [isFormulaMode, setIsFormulaMode] = useState(false); - + // Ключ для хранения формул в localStorage const storageKey = `formula_${tableId}_${cellId}`; - + const trFrag = refCell.current.offsetParent.offsetParent; const tdFrag = refCell.current.offsetParent; const left = tdFrag.offsetLeft; @@ -69,7 +72,6 @@ const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId }) => { if (isFormula(curValue)) { // Сохраняем формулу в localStorage saveToStorage(storageKey, curValue); - // Вычисляем и сохраняем результат try { const formula = extractFormula(curValue); @@ -108,6 +110,32 @@ const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId }) => { return displayValue; }; + const finishEditing = () => { + if (!disabled) { + handleSave(); + } + const editingCell = table.getState().editingCell || null; + if (editingCell) { + contextEndEditing?.(editingCell.row, editingCell.column); + } + }; + + useEffect(() => { + const handleClickOutside = (event) => { + // Проверяем, был ли клик внутри элемента редактирования + const editElement = ref.current; + if (editElement && !editElement.contains(event.target)) { + finishEditing(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref, finishEditing]); + return ( @@ -150,16 +178,10 @@ const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId }) => { }} onChange={handleChangeValue} value={curValue} - onBlur={handleSave} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - handleSave(); - // Закрываем редактирование - if (onChange) { - // Симулируем завершение редактирования - document.activeElement?.blur(); - } + finishEditing(); } }} placeholder={isFormulaMode ? 'Введите формулу (начинается с =)' : ''} diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx index b75259b..c40ca7e 100644 --- a/web/src/components/RealtimeTable/RealtimeTable.jsx +++ b/web/src/components/RealtimeTable/RealtimeTable.jsx @@ -63,7 +63,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { addRow: contextAddRow, deleteRow: contextDeleteRow, startEditing: contextStartEditing, - endEditing: contextEndEditing, } = useRealtime(); const { @@ -180,8 +179,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { if (cell) { if (editingCell) return; contextStartEditing?.(cell.row, cell.column); - } else { - contextEndEditing?.(editingCell.row, editingCell.column); } }, columnVirtualizerOptions: ({ table }) => ({ diff --git a/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx b/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx index 3347501..292ee04 100644 --- a/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx +++ b/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx @@ -126,8 +126,7 @@ const SettingPanel = ({ - - {!isProject ?? + {!isProject && <> diff --git a/web/src/components/RealtimeTable/tableColumns.jsx b/web/src/components/RealtimeTable/tableColumns.jsx index 2c6dd4e..1353217 100644 --- a/web/src/components/RealtimeTable/tableColumns.jsx +++ b/web/src/components/RealtimeTable/tableColumns.jsx @@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'; import { columns as mockColumns } from '../../mocks/mockTable'; import { config } from './constants/FORM_2/AHR'; import { useParams } from 'react-router'; +import { useRealtime } from './contexts/RealtimeContext'; function EditCellPortal({ cell, @@ -20,6 +21,8 @@ function EditCellPortal({ const [isSaving, setIsSaving] = useState(false); const isDisabled = false; + const { endEditing: contextEndEditing } = useRealtime(); + useEffect(() => { if (ref.current) { const tbodyRef = ref.current.offsetParent?.offsetParent?.offsetParent; @@ -68,6 +71,7 @@ function EditCellPortal({ disabled={!isEditable} onChange={handleChange} tableId={tableKey} + table={table} cellId={cell.row.id + '_' + cell.column.id} />, refTbody, diff --git a/web/src/constants/projectConfig.jsx b/web/src/constants/projectConfig.jsx index d94a38f..6fd96a4 100644 --- a/web/src/constants/projectConfig.jsx +++ b/web/src/constants/projectConfig.jsx @@ -12,7 +12,7 @@ export const DEFAULT_PROJECT_CONFIG = { defaultValue: null, min: 0, step: 1, - required_field: true, + required_field: false, }, 'Тип проекта развития': { type: 'multiselect', diff --git a/web/src/pages/AdminPanel/AuditLogsPage/constants/configEvent.jsx b/web/src/pages/AdminPanel/AuditLogsPage/constants/configEvent.jsx index bebd544..a376655 100644 --- a/web/src/pages/AdminPanel/AuditLogsPage/constants/configEvent.jsx +++ b/web/src/pages/AdminPanel/AuditLogsPage/constants/configEvent.jsx @@ -12,8 +12,8 @@ export const EVENT_CONFIGS = { format: 'Утвержденный формат', opened_at: 'Дата открытия ВСП', notes: 'Примечания', - location_form: 'Форма расположения ВСП', - numbers: 'Штатная численность ВСП', + placement_type: 'Форма расположения ВСП', + staff_count: 'Штатная численность ВСП', total_area: 'Площадь помещений ВСП', rent_contract_num: 'Номер договора аренды', rent_end_date: 'Срок окончания договора аренды', @@ -21,8 +21,6 @@ export const EVENT_CONFIGS = { branch_name: 'Региональный филиал ', closed_at: 'Дата закрытия ВСП', user_email: 'Почта пользователя', - staff_count: 'Кол-во работников', - placement_type: 'Тип размещение', is_active: 'Активный', }, VSP_UPDATE: { @@ -32,8 +30,8 @@ export const EVENT_CONFIGS = { format: 'Утвержденный формат', opened_at: 'Дата открытия ВСП', notes: 'Примечания', - location_form: 'Форма расположения ВСП', - numbers: 'Штатная численность ВСП', + placement_type: 'Форма расположения ВСП', + staff_count: 'Штатная численность ВСП', total_area: 'Площадь помещений ВСП', rent_contract_num: 'Номер договора аренды', rent_end_date: 'Срок окончания договора аренды', @@ -41,8 +39,6 @@ export const EVENT_CONFIGS = { 'changes.branch_name': 'Региональный филиал ', closed_at: 'Дата закрытия ВСП', user_email: 'Почта пользователя', - staff_count: 'Кол-во работников', - placement_type: 'Тип размещение', is_active: 'Активный', }, USER_ACCESS_GRANTED: { diff --git a/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx b/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx index 96d67ee..1b56ad9 100644 --- a/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx +++ b/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx @@ -43,8 +43,8 @@ const ModalEditAddVsp = ({ open_date: null, close_date: null, notes: '', - location_form: '', - numbers: '', + placement_type: '', + staff_count: '', area: null, rent_contract_num: '', rent_end_date: null, @@ -94,8 +94,8 @@ const ModalEditAddVsp = ({ ? new Date(initialData.close_date) : null, notes: initialData.notes || '', - location_form: initialData.location_form || '', - numbers: initialData.staff_count?.toString() || '', + placement_type: initialData.placement_type || '', + staff_count: initialData.staff_count?.toString() || '', area: initialData.area || null, rent_contract_num: initialData.rent_contract_num || '', rent_end_date: initialData.rent_end_date @@ -114,8 +114,8 @@ const ModalEditAddVsp = ({ open_date: null, close_date: null, notes: '', - location_form: '', - numbers: '', + placement_type: '', + staff_count: '', area: null, rent_contract_num: '', rent_end_date: null, @@ -215,7 +215,7 @@ const ModalEditAddVsp = ({ const submitData = { ...formData, - numbers: formData.staff_count ? parseInt(formData.staff_count, 10) : null, + staff_count: formData.staff_count ? parseInt(formData.staff_count, 10) : null, area: formData.area ? parseFloat(formData.area) : null, open_date: formData.open_date ? formData.open_date.toLocaleDateString('en-CA') @@ -255,8 +255,8 @@ const ModalEditAddVsp = ({ open_date: null, close_date: null, notes: '', - location_form: '', - numbers: '', + placement_type: '', + staff_count: '', area: '', rent_contract_num: '', rent_end_date: null, @@ -461,11 +461,11 @@ const ModalEditAddVsp = ({ Форма расположения ВСП { const [projectData, setProjectData] = useState(() => { - // Если есть initialData, используем её, иначе создаем из конфига if (initialData) { return { ...initialData }; } - const initial = {}; + // Иначе создаем из конфига + const data = {}; Object.keys(config).forEach((key) => { - initial[key] = config[key].defaultValue; + data[key] = config[key].defaultValue; }); - return initial; + return data; }); const [errors, setErrors] = useState({}); - // Обновляем данные при изменении initialData useEffect(() => { if (initialData) { setProjectData({ ...initialData }); } else { - const initial = {}; + const data = {}; Object.keys(config).forEach((key) => { - initial[key] = config[key].defaultValue; + data[key] = config[key].defaultValue; }); - setProjectData(initial); + setProjectData(data); } setErrors({}); - }, [initialData, config]); + }, [initialData, config, open]); const handleChange = (field, value) => { setProjectData((prev) => ({ ...prev, [field]: value, })); - // Очищаем ошибку для поля при изменении if (errors[field]) { setErrors((prev) => ({ ...prev, @@ -59,8 +56,7 @@ export const ModalCreateProject = ({ const validateField = (fieldName, config) => { if (config.required_field) { const value = projectData[fieldName]; - - // Проверка для разных типов полей + if (config.type === 'multiselect') { if (!value || (Array.isArray(value) && value.length === 0) || value === '') { return `Поле "${fieldName}" обязательно для заполнения`; @@ -73,11 +69,9 @@ export const ModalCreateProject = ({ if (value === null || value === undefined || value === '') { return `Поле "${fieldName}" обязательно для заполнения`; } - // Проверка на минимальное значение if (config.min !== undefined && Number(value) < config.min) { return `Значение должно быть не меньше ${config.min}`; } - // Проверка на максимальное значение if (config.max !== undefined && Number(value) > config.max) { return `Значение должно быть не больше ${config.max}`; } @@ -105,7 +99,7 @@ export const ModalCreateProject = ({ const handleClickCreate = () => { if (validateAllFields()) { onCreate(projectData); - // Не закрываем модалку здесь, пусть это делает родительский компонент + onClose(); } }; @@ -225,7 +219,6 @@ export const ModalCreateProject = ({ value={projectData[fieldName] || ''} onChange={(e) => { const inputValue = e.target.value; - //только числа const isValidInput = /^\d*\.?\d*$/.test(inputValue) || inputValue === ''; if (isValidInput) { @@ -260,7 +253,7 @@ export const ModalCreateProject = ({ return ( - {isEdit ? 'Сохранить' : 'Создать'} + {buttonText} } diff --git a/web/src/pages/ProjectsPage/ProjectCard.jsx b/web/src/pages/ProjectsPage/ProjectCard.jsx index 15ed15c..36ace2a 100644 --- a/web/src/pages/ProjectsPage/ProjectCard.jsx +++ b/web/src/pages/ProjectsPage/ProjectCard.jsx @@ -151,7 +151,7 @@ export const ProjectCardComponent = ({ id, name, org_unit_name, - year, + years, status, level, project_type, @@ -180,7 +180,7 @@ export const ProjectCardComponent = ({ const prepareProjectDataForEdit = () => { return { 'ССП/РФ': project.org_unit_id || null, - 'Год': year || null, + 'Год': years || null, 'Тип проекта развития': project_type || null, 'Название проекта': currentName, 'Формат ВСП': vsp_format || null, @@ -316,7 +316,7 @@ export const ProjectCardComponent = ({ > }> - {year || 'Год не указан'} + {years.join(', ') || 'Год не указан'} }> {org_unit_name || 'Организация не указана'}