diff --git a/package.json b/package.json index 2e869e2..9123398 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toast": "^1.2.14", "@reduxjs/toolkit": "^1.9.5", + "@tanstack/react-query": "^5.83.0", + "@types/lodash.isequal": "^4.5.8", "@types/react-grid-layout": "^1.3.5", "@types/react-window": "^1.8.8", "class-variance-authority": "^0.7.1", @@ -32,6 +34,7 @@ "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "immer": "^10.1.1", + "lodash.isequal": "^4.5.0", "lucide-react": "^0.525.0", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", @@ -40,7 +43,8 @@ "react-rnd": "^10.5.2", "react-window": "^1.8.11", "redux": "^4.2.1", - "tailwind-merge": "^3.3.1" + "tailwind-merge": "^3.3.1", + "use-debounce": "^10.0.5" }, "devDependencies": { "@types/node": "^24.0.15", diff --git a/src/component/DualSpreadsheet/DualSpreadsheet.tsx b/src/component/DualSpreadsheet/DualSpreadsheet.tsx index 0e9e192..f10456f 100644 --- a/src/component/DualSpreadsheet/DualSpreadsheet.tsx +++ b/src/component/DualSpreadsheet/DualSpreadsheet.tsx @@ -153,22 +153,22 @@ const DualSpreadsheet: FC = ({ initialTemplateValues.current[cellAddress] = String(value || '') }) }) - console.log( - '✅ Загружено значений baseFileData для сравнения (приоритет):', - Object.keys(initialTemplateValues.current).length - ) + // console.log( + // '✅ Загружено значений baseFileData для сравнения (приоритет):', + // Object.keys(initialTemplateValues.current).length + //) } else if (hasTemplateData) { Object.entries(templateData).forEach(([sheetName, sheetData]) => { Object.entries(sheetData).forEach(([cellAddress, value]) => { initialTemplateValues.current[cellAddress] = String(value || '') }) }) - console.log( - '✅ Загружено значений templateData для сравнения (fallback):', - Object.keys(initialTemplateValues.current).length - ) + // console.log( + // '✅ Загружено значений templateData для сравнения (fallback):', + // Object.keys(initialTemplateValues.current).length + // ) } else { - console.log('⚠️ DualSpreadsheet: Нет базовых данных для сравнения') + // console.log('⚠️ DualSpreadsheet: Нет базовых данных для сравнения') } }, [baseFileData, templateData]) @@ -250,11 +250,11 @@ const DualSpreadsheet: FC = ({ baseFileValue && rawValue !== baseFileValue ) { - console.log(`🔧 Измененная ячейка ${cellName}:`, { - baseFileValue, - rawValue, - isModified: true, - }) + // console.log(`🔧 Измененная ячейка ${cellName}:`, { + // baseFileValue, + // rawValue, + // isModified: true, + // }) } // Нормализуем значения для сравнения @@ -274,11 +274,11 @@ const DualSpreadsheet: FC = ({ // Отладка только для измененных ячеек if (sheetType === 'report' && isModified) { - console.log(`🔍 Измененная ячейка ${cellName}:`, { - baseFileValue, - rawValue, - isModified, - }) + // console.log(`🔍 Измененная ячейка ${cellName}:`, { + // baseFileValue, + // rawValue, + // isModified, + // }) } cachedData[cellKey] = { @@ -326,10 +326,10 @@ const DualSpreadsheet: FC = ({ setHighlightedCells(highlights) - console.log('🎨 Подсветка ячеек для формулы:', formula, { - references, - highlights, - }) + // console.log('🎨 Подсветка ячеек для формулы:', formula, { + // references, + // highlights, + // }) } catch (error) { console.error('Ошибка при обновлении подсветки ячеек:', error) setHighlightedCells([]) @@ -411,14 +411,14 @@ const DualSpreadsheet: FC = ({ ) const baseFileValue = initialTemplateValues.current[cellName] ?? '' // Базовое значение из файла - console.log('💾 Сохранение ячейки:', { - cellName, - oldValue, - newValue: editingValue, - baseFileValue, - isChanged: oldValue !== editingValue, - isModifiedFromBaseFile: baseFileValue !== editingValue, - }) + // console.log('💾 Сохранение ячейки:', { + //cellName, + //oldValue, + //newValue: editingValue, + //baseFileValue, + //isChanged: oldValue !== editingValue, + //isModifiedFromBaseFile: baseFileValue !== editingValue, + //}) multiSheetEngine.setCellValueWithoutRecalc( sheetName, @@ -529,12 +529,12 @@ const DualSpreadsheet: FC = ({ const handleCellClick = useCallback( (row: number, col: number) => { - console.log('🖱️ Обычный клик по ячейке:', { - row, - col, - isEditing, - activeSheet, - }) + // console.log('🖱️ Обычный клик по ячейке:', { + // row, + //col, + //isEditing, + //activeSheet, + //}) if (isEditing) { stopEditing(true) @@ -548,20 +548,20 @@ const DualSpreadsheet: FC = ({ // Новый обработчик для кликов в режиме редактирования - вставляет ссылку const handleCellReferenceClick = useCallback( (targetSheetType: SheetType, row: number, col: number) => { - console.log('🔗 Клик для вставки ссылки:', { - targetSheetType, - row, - col, - isEditing, - selectedCell, - editingValue, - }) + // console.log('🔗 Клик для вставки ссылки:', { + //targetSheetType, + //row, + //col, + //isEditing, + //selectedCell, + //editingValue, + //}) // Проверяем, что мы в режиме редактирования if (!isEditing || !selectedCell) { - console.log( - '⚠️ Игнорируем клик ссылки: не в режиме редактирования или нет выбранной ячейки' - ) + // console.log( + // '⚠️ Игнорируем клик ссылки: не в режиме редактирования или нет выбранной ячейки' + //) return } @@ -571,19 +571,19 @@ const DualSpreadsheet: FC = ({ // Если это другой лист, делаем квалифицированную ссылку if (targetSheetType !== activeSheet) { cellReference = createQualifiedCellReference(targetSheetType, row, col) - console.log('📋 Создана межлистовая ссылка:', cellReference) + // console.log('📋 Создана межлистовая ссылка:', cellReference) } else { // Если это тот же лист, делаем локальную ссылку cellReference = createCellAddress(row, col) - console.log('📋 Создана локальная ссылка:', cellReference) + // console.log('📋 Создана локальная ссылка:', cellReference) } // Добавляем ссылку к текущему значению редактирования const newValue = editingValue + cellReference - console.log('✏️ Новое значение формулы:', { - oldValue: editingValue, - newValue, - }) + // console.log('✏️ Новое значение формулы:', { + //oldValue: editingValue, + //newValue, + //}) setEditingValue(newValue) @@ -739,15 +739,15 @@ const DualSpreadsheet: FC = ({ // Отладка templateId useEffect(() => { - console.log('🆔 Текущий templateId в DualSpreadsheet:', { - templateId, - type: typeof templateId, - isValid: templateId - ? /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( - templateId - ) - : false, - }) + // console.log('🆔 Текущий templateId в DualSpreadsheet:', { + //templateId, + //type: typeof templateId, + //isValid: templateId + // ? /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( + // templateId + // ) + // : false, + //}) }, [templateId]) // Используем хук для перетаскивания разделителя @@ -774,7 +774,7 @@ const DualSpreadsheet: FC = ({ try { const latestFile = await getLatestFileForTemplate(templateId) if (latestFile) { - console.log('📁 Найден последний файл для шаблона:', latestFile.name) + // console.log('📁 Найден последний файл для шаблона:', latestFile.name) const fileData = await getFileData(latestFile.id) // TODO: Пока getFileData возвращает пустые данные, используем templateData @@ -784,14 +784,14 @@ const DualSpreadsheet: FC = ({ setBaseFileData(finalBaseData) // Сохраняем baseline, чтобы в L попадали только пользовательские изменения setTemplateData(finalBaseData) - console.log('📄 Загружены базовые данные файла:', finalBaseData) + // console.log('📄 Загружены базовые данные файла:', finalBaseData) } else { - console.log( - '⚠️ getFileData вернул пустые данные, используем templateData' - ) + // console.log( + // '⚠️ getFileData вернул пустые данные, используем templateData' + //) } } else { - console.log('⚠️ Файл не найден для templateId:', templateId) + // console.log('⚠️ Файл не найден для templateId:', templateId) } } catch (error) { console.error('❌ Ошибка при загрузке данных файла:', error) @@ -806,16 +806,16 @@ const DualSpreadsheet: FC = ({ if (hasTemplateData && Object.keys(finalBaseData).length === 0) { finalBaseData = templateData setBaseFileData(finalBaseData) - console.log( - '📄 Используем templateData как базовые данные:', - finalBaseData - ) + // console.log( + // '📄 Используем templateData как базовые данные:', + // finalBaseData + //) setTemplateData(templateData) } // 3. ЗАГРУЖАЕМ ПОЛЬЗОВАТЕЛЬСКИЕ ИЗМЕНЕНИЯ ИЗ API try { - console.log('🔍 Загружаем пользовательские изменения из API...') + // console.log('🔍 Загружаем пользовательские изменения из API...') const apiSheets = await loadSheets() // Преобразуем листы API в формат данных @@ -840,16 +840,16 @@ const DualSpreadsheet: FC = ({ } }) - console.log( - '📂 Загружены пользовательские изменения:', - Object.keys(userChanges) - ) + // console.log( + // '📂 Загружены пользовательские изменения:', + // Object.keys(userChanges) + //) // 4. ОБЪЕДИНЯЕМ БАЗОВЫЕ ДАННЫЕ С ПОЛЬЗОВАТЕЛЬСКИМИ ИЗМЕНЕНИЯМИ if (Object.keys(userChanges).length > 0) { - console.log( - '🔄 Объединяем базовые данные с пользовательскими изменениями' - ) + // console.log( + // '🔄 Объединяем базовые данные с пользовательскими изменениями' + //) // Создаем объединенные данные: базовые данные файла + пользовательские изменения const mergedData: Record> = {} @@ -869,24 +869,24 @@ const DualSpreadsheet: FC = ({ }) }) - console.log( - '📊 Загружаем объединенные данные (базовые + пользовательские)' - ) + // console.log( + // '📊 Загружаем объединенные данные (базовые + пользовательские)' + //) multiSheetEngine.loadData(mergedData) } else if (Object.keys(finalBaseData).length > 0) { - console.log( - '📋 Используем только базовые данные (нет пользовательских изменений)' - ) + // console.log( + // '📋 Используем только базовые данные (нет пользовательских изменений)' + //) multiSheetEngine.loadData(finalBaseData) } else { - console.log('⚠️ Нет данных для загрузки') + // console.log('⚠️ Нет данных для загрузки') } } catch (error) { console.error('❌ Ошибка при загрузке пользовательских изменений:', error) // В случае ошибки используем только базовые данные if (Object.keys(finalBaseData).length > 0) { - console.log('📋 Ошибка API, используем только базовые данные') + // console.log('📋 Ошибка API, используем только базовые данные') multiSheetEngine.loadData(finalBaseData) } } diff --git a/src/component/DualSpreadsheet/components/Cell.tsx b/src/component/DualSpreadsheet/components/Cell.tsx index b69c3a3..e464e4d 100644 --- a/src/component/DualSpreadsheet/components/Cell.tsx +++ b/src/component/DualSpreadsheet/components/Cell.tsx @@ -44,13 +44,13 @@ export const Cell = memo( // Отладка стилей для измененных ячеек if (isModified) { - console.log(`🎨 Стили для измененной ячейки ${rowIndex},${colIndex}:`, { - className, - isModified, - isSelected, - displayValue, - highlightColor, - }) + // console.log(`🎨 Стили для измененной ячейки ${rowIndex},${colIndex}:`, //{ + // className, + // isModified, + // isSelected, + // displayValue, + // highlightColor, + //}) } // Для объединенных ячеек нужно рассчитать правильные размеры @@ -109,14 +109,14 @@ export const Cell = memo( const handleClick = useCallback( (e: React.MouseEvent) => { - console.log('🖱️ Cell клик:', { - rowIndex, - colIndex, - sheetType, - isInEditMode, - isSelected, - hasReferenceHandler: !!onCellReferenceClick, - }) + // console.log('🖱️ Cell клик:', { + // rowIndex, + // colIndex, + // sheetType, + // isInEditMode, + // isSelected, + //hasReferenceHandler: !!onCellReferenceClick, + //}) // Если мы в режиме редактирования, вставка была обработана на mousedown. // Останавливаем всплытие, чтобы родительский Spreadsheet не сменил активный лист. @@ -127,17 +127,17 @@ export const Cell = memo( } // Обычная логика клика (выделение ячейки) - console.log('📍 Обычный клик - выбираем ячейку:', { - rowIndex, - colIndex, - sheetType, - }) + // console.log('📍 Обычный клик - выбираем ячейку:', { + // rowIndex, + // colIndex, + // sheetType, + //}) // Подсказка для пользователя if (!isInEditMode) { - console.log( - '💡 Чтобы вставить ссылку на ячейку: 1) Выберите ячейку 2) Нажмите F2 или Enter для редактирования 3) Кликните на нужную ячейку' - ) + // console.log( + // '💡 Чтобы вставить ссылку на ячейку: 1) Выберите ячейку 2) Нажмите F2 или Enter для редактирования 3) Кликните на нужную ячейку' + //) } setActiveSheet(sheetType) @@ -166,11 +166,11 @@ export const Cell = memo( if (isInEditMode && onCellReferenceClick && !isSelected) { e.preventDefault() e.stopPropagation() - console.log('🖱️ mousedown – вставка ссылки до blur:', { - rowIndex, - colIndex, - sheetType, - }) + // console.log('🖱️ mousedown – вставка ссылки до blur:', { + // rowIndex, + // colIndex, + // sheetType, + //}) onCellReferenceClick(sheetType, rowIndex, colIndex) return } diff --git a/src/component/DualSpreadsheet/components/CellRenderer.tsx b/src/component/DualSpreadsheet/components/CellRenderer.tsx index 28dc32d..9612bc9 100644 --- a/src/component/DualSpreadsheet/components/CellRenderer.tsx +++ b/src/component/DualSpreadsheet/components/CellRenderer.tsx @@ -55,16 +55,16 @@ export const CellRenderer = ({ // Отладка для измененных ячеек if (cellData?.isModified) { - console.log( - `🔍 CellRenderer: Измененная ячейка ${rowIndex},${columnIndex}:`, - { - cellKey, - isModified: cellData.isModified, - displayValue, - rawValue, - highlightColor, - } - ) + // console.log( + // `🔍 CellRenderer: Измененная ячейка ${rowIndex},${columnIndex}:`, + // { + // cellKey, + // isModified: cellData.isModified, + // displayValue, + // rawValue, + // highlightColor, + // } + // ) } // Получаем информацию об объединенных ячейках только для листа отчета diff --git a/src/component/DualSpreadsheet/components/Spreadsheet.tsx b/src/component/DualSpreadsheet/components/Spreadsheet.tsx index 1e2ed60..2cc76c5 100644 --- a/src/component/DualSpreadsheet/components/Spreadsheet.tsx +++ b/src/component/DualSpreadsheet/components/Spreadsheet.tsx @@ -56,13 +56,13 @@ export const Spreadsheet = ({ } // Отладочное логирование для проверки состояния - console.log(`📊 Spreadsheet ${sheetType} состояние:`, { - isEditing, - isInEditMode, - selectedCell, - activeSheet, - isActiveSheet: activeSheet === sheetType, - }) + // console.log(`📊 Spreadsheet ${sheetType} состояние:`, { + // isEditing, + // isInEditMode, + // selectedCell, + // activeSheet, + // isActiveSheet: activeSheet === sheetType, + //}) const parentRef = useRef(null) const [gridSize, setGridSize] = useState({ width: 0, height: 0 }) diff --git a/src/component/TemplateManager/ElementConstructor.tsx b/src/component/TemplateManager/ElementConstructor.tsx index 1c33aad..f76f91b 100644 --- a/src/component/TemplateManager/ElementConstructor.tsx +++ b/src/component/TemplateManager/ElementConstructor.tsx @@ -395,7 +395,7 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { value={formData.type} onValueChange={value => { const newType = value as ElementType - console.log('Element type changed to:', newType) + // console.log('Element type changed to:', newType) setFormData(prev => { const updates: Partial = { type: newType } @@ -405,24 +405,24 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { newType === 'standards' || newType === 'calibration-conditions' ) { - console.log( - `${newType} type selected, getting element definition` - ) + // console.log( + // `${newType} type selected, getting element definition` + // ) const elementDefinition = getElementDefinition(newType) - console.log('Element definition:', elementDefinition) + // console.log('Element definition:', elementDefinition) if (elementDefinition && elementDefinition.defaultConfig) { const defaultConfig = elementDefinition.defaultConfig as any - console.log('Default config:', defaultConfig) + // console.log('Default config:', defaultConfig) updates.targetCells = elementDefinition.mapToCells?.(defaultConfig) || [] - console.log('Generated targetCells:', updates.targetCells) + // console.log('Generated targetCells:', updates.targetCells) } } const newFormData = { ...prev, ...updates } - console.log('New form data:', newFormData) + // console.log('New form data:', newFormData) return newFormData }) }} @@ -520,7 +520,7 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { value={formData.type} onValueChange={value => { const newType = value as ElementType - console.log('Element type changed to (fallback):', newType) + // console.log('Element type changed to (fallback):', newType) setFormData(prev => { const updates: Partial = { type: newType } @@ -530,29 +530,29 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { newType === 'standards' || newType === 'calibration-conditions' ) { - console.log( - `${newType} type selected (fallback), getting element definition` - ) + // console.log( + // `${newType} type selected (fallback), getting element definition` + // ) const elementDefinition = getElementDefinition(newType) - console.log( - 'Element definition (fallback):', - elementDefinition - ) + // console.log( + // 'Element definition (fallback):', + // elementDefinition + // ) if (elementDefinition && elementDefinition.defaultConfig) { const defaultConfig = elementDefinition.defaultConfig as any - console.log('Default config (fallback):', defaultConfig) + // console.log('Default config (fallback):', defaultConfig) updates.targetCells = elementDefinition.mapToCells?.(defaultConfig) || [] - console.log( - 'Generated targetCells (fallback):', - updates.targetCells - ) + // console.log( + // 'Generated targetCells (fallback):', + // updates.targetCells + // ) } } const newFormData = { ...prev, ...updates } - console.log('New form data (fallback):', newFormData) + // console.log('New form data (fallback):', newFormData) return newFormData }) }} @@ -746,10 +746,10 @@ export const ElementConstructor: React.FC = ({ } const handleAddElement = () => { - console.log('handleAddElement called with formData:', formData) + // console.log('handleAddElement called with formData:', formData) if (!formData.label) { - console.log('Validation failed: missing label') + // console.log('Validation failed: missing label') return } @@ -760,9 +760,9 @@ export const ElementConstructor: React.FC = ({ formData.type !== 'button-group' && !formData.targetCells?.length ) { - console.log( - 'Validation failed: missing targetCells for element that requires manual targetCells' - ) + // console.log( + // 'Validation failed: missing targetCells for element that requires manual targetCells' + // ) return } diff --git a/src/component/TemplateManager/TemplateEditor.tsx b/src/component/TemplateManager/TemplateEditor.tsx index 4ddffba..a1137fd 100644 --- a/src/component/TemplateManager/TemplateEditor.tsx +++ b/src/component/TemplateManager/TemplateEditor.tsx @@ -101,7 +101,7 @@ export const TemplateEditor: React.FC = ({ }) setDataVersion(v => v + 1) - console.log('✅ Файл загружен с ID:', fileId) + // console.log('✅ Файл загружен с ID:', fileId) } catch (err) { console.error(err) alert('Ошибка при загрузке Excel файла на сервер') diff --git a/src/component/TemplateManager/VisualLayoutEditor.tsx b/src/component/TemplateManager/VisualLayoutEditor.tsx index c719d8a..bb82103 100644 --- a/src/component/TemplateManager/VisualLayoutEditor.tsx +++ b/src/component/TemplateManager/VisualLayoutEditor.tsx @@ -398,7 +398,7 @@ export const VisualLayoutEditor: React.FC = ({ onLayoutSettingsChange, onElementEdit, }) => { - console.log('VisualLayoutEditor render with elements:', elements) + // console.log('VisualLayoutEditor render with elements:', elements) const [selectedElementId, setSelectedElementId] = useState( null ) diff --git a/src/hook/useFormulaAutoSave.ts b/src/hook/useFormulaAutoSave.ts index 1a311cb..e920bb3 100644 --- a/src/hook/useFormulaAutoSave.ts +++ b/src/hook/useFormulaAutoSave.ts @@ -15,7 +15,7 @@ export interface UseFormulaAutoSaveOptions { // Ключ для localStorage const getStorageKey = (templateId: string) => { - console.log('🔑 Получен templateId:', templateId) + // console.log('🔑 Получен templateId:', templateId) return `formula_data_${templateId}` } @@ -58,7 +58,7 @@ export const useFormulaAutoSave = ( const parsedData: SavedFormulaData = JSON.parse(savedDataStr) setSavedData(parsedData) versionCounter.current = parsedData.version + 1 - console.log( + // console.log( '📂 Загружены сохраненные формулы (версия:', parsedData.version, ')' @@ -73,7 +73,7 @@ export const useFormulaAutoSave = ( const performSave = useCallback( async (data: Record>) => { if (Object.keys(data).length === 0) { - console.log('📭 Нет данных для сохранения') + // console.log('📭 Нет данных для сохранения') return } @@ -110,7 +110,7 @@ export const useFormulaAutoSave = ( setSavedData(savedData) setLastSavedRevision(revision) - console.log( + // console.log( '💾 Сохранено формул:', Object.values(data).reduce( (sum, sheet) => sum + Object.keys(sheet).length, @@ -136,7 +136,7 @@ export const useFormulaAutoSave = ( // Автосохранение с дебаунсом const scheduleAutoSave = useCallback(() => { - console.log('⏰ scheduleAutoSave вызван, проверяем условия:', { + // console.log('⏰ scheduleAutoSave вызван, проверяем условия:', { enableAutoSave, isCalculating, isSaving, @@ -144,21 +144,21 @@ export const useFormulaAutoSave = ( }) if (!enableAutoSave || isCalculating || isSaving || !templateId) { - console.log('❌ Автосохранение отменено по условиям') + // console.log('❌ Автосохранение отменено по условиям') return } // Очищаем предыдущий таймер if (saveTimeoutRef.current) { clearTimeout(saveTimeoutRef.current) - console.log('🔄 Очищен предыдущий таймер автосохранения') + // console.log('🔄 Очищен предыдущий таймер автосохранения') } - console.log(`⏳ Запланировано автосохранение через ${autoSaveDelay}ms`) + // console.log(`⏳ Запланировано автосохранение через ${autoSaveDelay}ms`) saveTimeoutRef.current = setTimeout(() => { - console.log('🎯 Выполняем отложенное автосохранение') + // console.log('🎯 Выполняем отложенное автосохранение') const currentData = getAllModifiedCells() - console.log('📋 Данные для автосохранения:', currentData) + // console.log('📋 Данные для автосохранения:', currentData) performSave(currentData) }, autoSaveDelay) }, [ @@ -173,7 +173,7 @@ export const useFormulaAutoSave = ( // Эффект для автосохранения при изменениях useEffect(() => { - console.log('🔍 Проверка условий автосохранения:', { + // console.log('🔍 Проверка условий автосохранения:', { enableAutoSave, revision, lastSavedRevision, @@ -193,10 +193,10 @@ export const useFormulaAutoSave = ( !isCalculating && !isSaving ) { - console.log('✅ Планируем автосохранение...') + // console.log('✅ Планируем автосохранение...') scheduleAutoSave() } else { - console.log('⏸️ Автосохранение пропущено по условиям') + // console.log('⏸️ Автосохранение пропущено по условиям') } }, [ enableAutoSave, @@ -209,20 +209,20 @@ export const useFormulaAutoSave = ( // Ручное сохранение const manualSave = useCallback(() => { - console.log('🖱️ Ручное сохранение вызвано') + // console.log('🖱️ Ручное сохранение вызвано') // Отменяем автосохранение если оно запланировано if (saveTimeoutRef.current) { clearTimeout(saveTimeoutRef.current) saveTimeoutRef.current = null - console.log('⏹️ Отменено запланированное автосохранение') + // console.log('⏹️ Отменено запланированное автосохранение') } const currentData = getAllModifiedCells() - console.log('📋 Данные для сохранения:', currentData) + // console.log('📋 Данные для сохранения:', currentData) if (Object.keys(currentData).length === 0) { - console.log('📭 Нет измененных данных для сохранения') + // console.log('📭 Нет измененных данных для сохранения') return } @@ -243,7 +243,7 @@ export const useFormulaAutoSave = ( if (savedDataStr) { const parsedData: SavedFormulaData = JSON.parse(savedDataStr) - console.log('📂 Формулы загружены из localStorage:', { + // console.log('📂 Формулы загружены из localStorage:', { templateId, version: parsedData.version, timestamp: parsedData.timestamp, @@ -273,7 +273,7 @@ export const useFormulaAutoSave = ( setLastSavedRevision(0) versionCounter.current = 1 - console.log('🗑️ Сохраненные формулы очищены:', { templateId }) + // console.log('🗑️ Сохраненные формулы очищены:', { templateId }) } catch (error) { console.error('❌ Ошибка при очистке сохраненных формул:', error) } diff --git a/src/hook/useMultiSheetEngine.ts b/src/hook/useMultiSheetEngine.ts index 03037c7..b1ba809 100644 --- a/src/hook/useMultiSheetEngine.ts +++ b/src/hook/useMultiSheetEngine.ts @@ -218,7 +218,7 @@ export function useMultiSheetEngine({ const setTemplateData = useCallback( (data: Record>) => { templateDataRef.current = JSON.parse(JSON.stringify(data)) // Глубокая копия - console.log('📋 Установлены данные шаблона:', templateDataRef.current) + // console.log('📋 Установлены данные шаблона:', templateDataRef.current) }, [] ) @@ -267,14 +267,14 @@ export function useMultiSheetEngine({ if (normalizedCurrent !== normalizedTemplate) { // Если текущего значения нет (ячейка очищена), отправляем '' modifiedSheet[cellAddress] = currentVal - console.log( - `🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`, - { - template: normalizedTemplate, - current: normalizedCurrent, - value: currentVal, - } - ) + // console.log( + // `🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`, + // { + // template: normalizedTemplate, + // current: normalizedCurrent, + // value: currentVal, + // } + // ) } }) diff --git a/src/hook/useSheetAutoSave.ts b/src/hook/useSheetAutoSave.ts index 72dd2f4..92814e3 100644 --- a/src/hook/useSheetAutoSave.ts +++ b/src/hook/useSheetAutoSave.ts @@ -1,4 +1,5 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' +import { useDebounce } from 'use-debounce' import { ApiSheet, createSheet, @@ -12,41 +13,13 @@ export interface UseSheetAutoSaveOptions { enableAutoSave?: boolean } -/** - * Хук для автосохранения листов через API - * - * Функции: - * - Автоматическое сохранение с дебаунсом при изменениях - * - Ручное сохранение по требованию - * - Создание и обновление листов через API - * - Загрузка существующих листов - */ -// Утилита для извлечения UUID из templateId -const extractUUID = (id: string): string => { - // Если это уже чистый UUID, возвращаем как есть - const uuidRegex = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i - if (uuidRegex.test(id)) { - return id - } - - // Если есть префикс (например "template-"), извлекаем UUID - const match = id.match( - /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i - ) - if (match) { - return match[0] - } - - return id // Возвращаем оригинал если не найден UUID -} - export const useSheetAutoSave = ( getAllModifiedCells: () => Record>, isCalculating: boolean, revision: number, options: UseSheetAutoSaveOptions = {} ) => { + console.log('getAllModifiedCells', getAllModifiedCells()) const { templateId, autoSaveDelay = 2000, enableAutoSave = true } = options // Состояние @@ -56,68 +29,40 @@ export const useSheetAutoSave = ( const [sheets, setSheets] = useState>({}) const [isLoading, setIsLoading] = useState(false) - // Refs - const saveTimeoutRef = useRef(null) + // Дебаунсинг ревизии для автосохранения + const [debouncedRevision] = useDebounce(revision, autoSaveDelay) // Загрузка листов для шаблона при инициализации const loadSheets = useCallback(async () => { if (!templateId) return {} - const cleanTemplateId = extractUUID(templateId) - const uuidRegex = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i - - if (!uuidRegex.test(cleanTemplateId)) { - console.error('❌ Не удалось извлечь валидный UUID для загрузки:', { - original: templateId, - extracted: cleanTemplateId, - }) - setLastSaveError('Неверный ID шаблона') - return {} - } - setIsLoading(true) try { - const response = await getSheetsByTemplate(cleanTemplateId) - /* - * В API могут существовать листы как с «сырым» именем (Report/Calculations), - * так и уже нормализованные (L/R). Чтобы случайный порядок ответов не - * приводил к потере данных (один объект перезаписывал другой), собираем - * листы в map с объединением ячеек, а не простым overwrite. - */ - const sheetsMap = response.sheets.reduce( - (acc, sheet) => { - // Приводим названия к единым «L»/«R» - const normalizedName = - sheet.name === 'Report' - ? 'L' - : sheet.name === 'Calculations' - ? 'R' - : sheet.name + const response = await getSheetsByTemplate(templateId) - if (acc[normalizedName]) { - // Уже есть лист с таким именем — мерджим данные, приоритет у более - // свежего объекта (sheet), но оставляем существующие ячейки если их - // нет в новом - acc[normalizedName] = { - ...sheet, - name: normalizedName, + const sheetsMap = response.sheets.reduce>( + (acc, sheet) => { + const existingSheet = acc[sheet.name] + + if (existingSheet) { + // Если лист уже есть, объединяем ячейки + acc[sheet.name] = { + ...sheet, // берем актуальную версию листа cells: { - ...acc[normalizedName].cells, - ...sheet.cells, + ...existingSheet.cells, // старые ячейки + ...sheet.cells, // новые заменяют старые с тем же ключом }, } } else { - acc[normalizedName] = { ...sheet, name: normalizedName } + acc[sheet.name] = { ...sheet } } return acc }, - {} as Record + {} ) setSheets(sheetsMap) - console.log('📂 Загружены листы для шаблона:', Object.keys(sheetsMap)) return sheetsMap } catch (error) { console.error('❌ Ошибка при загрузке листов:', error) @@ -138,71 +83,29 @@ export const useSheetAutoSave = ( // Основная функция сохранения через API const performSave = useCallback( async (data: Record>) => { - // Сохраняем все листы, но пропускаем пустые - const sheetNames = Object.keys(data) - if (sheetNames.length === 0) { - console.log('📭 Нет данных для сохранения') - return - } + if (!templateId) return - if (!templateId) { - console.warn('⚠️ Не указан templateId для сохранения листов') - return - } + // Получаем список всех листов, которые нужно обработать: + // 1. Листы с данными (из data) + // 2. Существующие листы, которые могли быть очищены + const dataSheetNames = Object.keys(data) + const existingSheetNames = Object.keys(sheets) + const allSheetNames = new Set([...dataSheetNames, ...existingSheetNames]) - // Извлекаем и валидируем UUID из templateId - const cleanTemplateId = extractUUID(templateId) - const uuidRegex = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i - - if (!uuidRegex.test(cleanTemplateId)) { - console.error('❌ Не удалось извлечь валидный UUID из templateId:', { - original: templateId, - extracted: cleanTemplateId, - }) - setLastSaveError('Неверный ID шаблона') - return - } - - console.log('🧹 Очищенный templateId:', { - original: templateId, - clean: cleanTemplateId, - }) + if (allSheetNames.size === 0) return setIsSaving(true) setLastSaveError(null) try { - const savePromises = sheetNames.map(async sheetName => { - const sheetCells = data[sheetName] + const savePromises = Array.from(allSheetNames).map(async sheetName => { + const sheetCells = data[sheetName] || {} // если данных нет, используем пустой объект const existingSheet = sheets[sheetName] - // Проверяем что есть данные для сохранения - if (!sheetCells || Object.keys(sheetCells).length === 0) { - console.log(`⏭️ Пропускаем лист "${sheetName}" - нет данных`) - return - } - if (existingSheet) { - // Объединяем старые и новые данные, чтобы не потерять предыдущие изменения - const mergedCells = { - ...existingSheet.cells, - ...sheetCells, - } - - // Проверяем, изменилось ли что-нибудь - const isChanged = Object.keys(sheetCells).some( - key => existingSheet.cells[key] !== sheetCells[key] - ) - - if (!isChanged) { - console.log(`⏭️ Лист "${sheetName}" без новых изменений`) - return - } - - console.log(`🔄 Обновляем лист "${sheetName}" (patch)`) + // Всегда сохраняем, даже если ячейки пустые (очистка листа) await patchSheet(existingSheet.id, { - cells: mergedCells, + cells: sheetCells, }) // Обновляем локальное состояние @@ -210,52 +113,41 @@ export const useSheetAutoSave = ( ...prev, [sheetName]: { ...existingSheet, - cells: mergedCells, + cells: sheetCells, updated_at: new Date().toISOString(), }, })) } else { - // Создаем новый лист - console.log(`✨ Создаем новый лист "${sheetName}"`) - console.log('📋 Данные для создания:', { - name: sheetName, - cells: sheetCells, - template_id: cleanTemplateId, - cellsCount: Object.keys(sheetCells).length, - }) - const newSheet = await createSheet({ - name: sheetName, - cells: sheetCells, - template_id: cleanTemplateId, - }) + // Создаем новый лист только если есть данные + if (Object.keys(sheetCells).length > 0) { + const newSheet = await createSheet({ + name: sheetName, + cells: sheetCells, + template_id: templateId, + }) - // Получаем полную информацию о созданном листе - const fullSheetData: ApiSheet = { - id: newSheet.id, - name: sheetName, - cells: sheetCells, - template_id: cleanTemplateId, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - deleted_at: null, + // Получаем полную информацию о созданном листе + const fullSheetData: ApiSheet = { + id: newSheet.id, + name: sheetName, + cells: sheetCells, + template_id: templateId, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + deleted_at: null, + } + + // Обновляем локальное состояние + setSheets(prev => ({ + ...prev, + [sheetName]: fullSheetData, + })) } - - // Обновляем локальное состояние - setSheets(prev => ({ - ...prev, - [sheetName]: fullSheetData, - })) } }) await Promise.all(savePromises) setLastSavedRevision(revision) - - const totalCells = Object.values(data).reduce( - (sum, sheet) => sum + Object.keys(sheet).length, - 0 - ) - console.log('💾 Сохранено ячеек:', totalCells, 'в листах:', sheetNames) } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Неизвестная ошибка' @@ -268,98 +160,33 @@ export const useSheetAutoSave = ( [templateId, sheets, revision] ) - // Автосохранение с дебаунсом - const scheduleAutoSave = useCallback(() => { - console.log('⏰ scheduleAutoSave вызван, проверяем условия:', { - enableAutoSave, - isCalculating, - isSaving, - templateId, - }) + // Автосохранение при изменении дебаунсированной ревизии + useEffect(() => { + const shouldAutoSave = + enableAutoSave && + debouncedRevision > lastSavedRevision && + !isCalculating && + !isSaving && + templateId - if (!enableAutoSave || isCalculating || isSaving || !templateId) { - console.log('❌ Автосохранение отменено по условиям') - return + if (shouldAutoSave) { + console.log('✅ Выполняем автосохранение для ревизии:', debouncedRevision) + performSave(getAllModifiedCells()) } - - // Очищаем предыдущий таймер - if (saveTimeoutRef.current) { - clearTimeout(saveTimeoutRef.current) - console.log('🔄 Очищен предыдущий таймер автосохранения') - } - - console.log(`⏳ Запланировано автосохранение через ${autoSaveDelay}ms`) - saveTimeoutRef.current = setTimeout(() => { - console.log('🎯 Выполняем отложенное автосохранение') - const currentData = getAllModifiedCells() - console.log('📋 Данные для автосохранения:', currentData) - performSave(currentData) - }, autoSaveDelay) }, [ enableAutoSave, + debouncedRevision, + lastSavedRevision, isCalculating, isSaving, templateId, - autoSaveDelay, getAllModifiedCells, performSave, ]) - // Эффект для автосохранения при изменениях - useEffect(() => { - console.log('🔍 Проверка условий автосохранения:', { - enableAutoSave, - revision, - lastSavedRevision, - isCalculating, - isSaving, - templateId, - shouldSave: - enableAutoSave && - revision > lastSavedRevision && - !isCalculating && - !isSaving, - }) - - if ( - enableAutoSave && - revision > lastSavedRevision && - !isCalculating && - !isSaving - ) { - console.log('✅ Планируем автосохранение...') - scheduleAutoSave() - } else { - console.log('⏸️ Автосохранение пропущено по условиям') - } - }, [ - enableAutoSave, - revision, - lastSavedRevision, - isCalculating, - isSaving, - scheduleAutoSave, - ]) - // Ручное сохранение const manualSave = useCallback(() => { - console.log('🖱️ Ручное сохранение вызвано') - - // Отменяем автосохранение если оно запланировано - if (saveTimeoutRef.current) { - clearTimeout(saveTimeoutRef.current) - saveTimeoutRef.current = null - console.log('⏹️ Отменено запланированное автосохранение') - } - const currentData = getAllModifiedCells() - console.log('📋 Данные для сохранения:', currentData) - - if (Object.keys(currentData).length === 0) { - console.log('📭 Нет измененных данных для сохранения') - return - } - performSave(currentData) }, [getAllModifiedCells, performSave]) @@ -376,19 +203,9 @@ export const useSheetAutoSave = ( } }) - console.log('📂 Загружены сохраненные листы:', Object.keys(result)) return result }, [sheets]) - // Очистка таймаута при размонтировании - useEffect(() => { - return () => { - if (saveTimeoutRef.current) { - clearTimeout(saveTimeoutRef.current) - } - } - }, []) - // Вычисляемые значения const isDataChanged = revision > lastSavedRevision const hasUnsavedChanges = isDataChanged && !isSaving diff --git a/src/lib/spreadsheet-engine/functions.ts b/src/lib/spreadsheet-engine/functions.ts index 8bbe2ce..bf10306 100644 --- a/src/lib/spreadsheet-engine/functions.ts +++ b/src/lib/spreadsheet-engine/functions.ts @@ -20,28 +20,28 @@ export const DEFAULT_FUNCTIONS: Record = { const flatArgs = args.flat() return flatArgs.reduce( (sum, val) => sum + (typeof val === 'number' ? val : 0), - 0, + 0 ) }, СРЗНАЧ: (...args: any[]) => { const flatArgs = args.flat() - const validArgs = flatArgs.filter((val) => typeof val === 'number') + const validArgs = flatArgs.filter(val => typeof val === 'number') return validArgs.length > 0 ? validArgs.reduce((sum, val) => sum + val, 0) / validArgs.length : 0 }, СЧЁТ: (...args: any[]) => { const flatArgs = args.flat() - return flatArgs.filter((val) => typeof val === 'number').length + return flatArgs.filter(val => typeof val === 'number').length }, МАКС: (...args: any[]) => { const flatArgs = args.flat() - const validArgs = flatArgs.filter((val) => typeof val === 'number') + const validArgs = flatArgs.filter(val => typeof val === 'number') return validArgs.length > 0 ? Math.max(...validArgs) : 0 }, МИН: (...args: any[]) => { const flatArgs = args.flat() - const validArgs = flatArgs.filter((val) => typeof val === 'number') + const validArgs = flatArgs.filter(val => typeof val === 'number') return validArgs.length > 0 ? Math.min(...validArgs) : 0 }, ЕСЛИ: (условие: boolean, истина: CellValue, ложь: CellValue) => @@ -96,7 +96,7 @@ export const DEFAULT_FUNCTIONS: Record = { // Отладочная функция ТЕСТ: (значение: any = 'Функция работает!') => { - console.log('ТЕСТ вызван с:', значение) + // console.log('ТЕСТ вызван с:', значение) return значение }, } diff --git a/src/lib/spreadsheet-engine/spreadsheet-engine.ts b/src/lib/spreadsheet-engine/spreadsheet-engine.ts index 9baa4f0..611c78a 100644 --- a/src/lib/spreadsheet-engine/spreadsheet-engine.ts +++ b/src/lib/spreadsheet-engine/spreadsheet-engine.ts @@ -143,9 +143,9 @@ export class Cell { // Отладка if (expr.includes('ТЕСТ')) { - console.log('Processing ТЕСТ function') - console.log('Available functions:', Object.keys(functions)) - console.log('Original code:', code) + // console.log('Processing ТЕСТ function') + // console.log('Available functions:', Object.keys(functions)) + // console.log('Original code:', code) } for (const [funcName] of Object.entries(functions)) { @@ -164,12 +164,12 @@ export class Cell { }) if (before !== code && expr.includes('ТЕСТ')) { - console.log(`Replaced ${funcName}:`, before, '->', code) + // console.log(`Replaced ${funcName}:`, before, '->', code) } } if (expr.includes('ТЕСТ')) { - console.log('Final code:', code) + // console.log('Final code:', code) } // Создаём контекст для выполнения diff --git a/src/page/ElementsCreation/Page.tsx b/src/page/ElementsCreation/Page.tsx index 442d885..a9f438c 100644 --- a/src/page/ElementsCreation/Page.tsx +++ b/src/page/ElementsCreation/Page.tsx @@ -29,10 +29,10 @@ export const ElementsCreation: FC = () => { }, [templateId, templates, navigate, isLoading]) const handleElementAdd = async (element: TemplateElement) => { - console.log('handleElementAdd called with element:', element) + // console.log('handleElementAdd called with element:', element) if (!selectedTemplate) { - console.log('No selected template') + // console.log('No selected template') return } @@ -44,8 +44,8 @@ export const ElementsCreation: FC = () => { updatedAt: new Date(), } - console.log('Updated template:', updatedTemplate) - console.log('Elements count:', updatedTemplate.elements.length) + // console.log('Updated template:', updatedTemplate) + // console.log('Elements count:', updatedTemplate.elements.length) await updateTemplate(updatedTemplate) setSelectedTemplate(updatedTemplate) diff --git a/src/page/ProtocolCreation/Page.tsx b/src/page/ProtocolCreation/Page.tsx index 22bec9a..6cbfae7 100644 --- a/src/page/ProtocolCreation/Page.tsx +++ b/src/page/ProtocolCreation/Page.tsx @@ -441,15 +441,15 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { } onSave(data) - console.log( - '[DBG] template elements', - template.elements.map(e => ({ - id: e.id, - label: e.label, - target: e.targetCells, - type: e.type, - })) - ) + // console.log( + // '[DBG] template elements', + // template.elements.map(e => ({ + // id: e.id, + // label: e.label, + // target: e.targetCells, + // type: e.type, + // })) + // ) try { if (!engineRef.current) { @@ -569,7 +569,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { ) }) - console.log('[DBG] Final cells_to_update', cellsToUpdate) + // console.log('[DBG] Final cells_to_update', cellsToUpdate) // Получаем последний файл для шаблона const latestFile = await getLatestFileForTemplate(template.id) @@ -584,7 +584,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { template_id: template.id, } - console.log('[DBG] POST body', body) + // console.log('[DBG] POST body', body) const resp = await fetch('/api/protocols/', { method: 'POST', @@ -595,7 +595,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { if (!resp.ok) throw new Error(`HTTP ${resp.status}`) const result = await resp.json() - console.log('✅ Протокол создан, ID:', result.protocol_id) + // console.log('✅ Протокол создан, ID:', result.protocol_id) } catch (err) { console.error('Ошибка при создании протокола', err) alert('Ошибка при создании протокола') @@ -603,7 +603,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { } const handleExport = () => { - console.log('Экспорт в Excel:', { formData }) + // console.log('Экспорт в Excel:', { formData }) alert('Функция экспорта будет реализована в следующих версиях') } @@ -767,7 +767,7 @@ export const ProtocolCreation: FC = () => { }, [templateId, templates]) const handleProtocolSave = (data: Record) => { - console.log('Сохранение протокола:', data) + // console.log('Сохранение протокола:', data) // Здесь будет логика сохранения протокола alert(`Протокол "${data.protocolName}" сохранен!`) navigate('/templates') diff --git a/src/service/sheetApiService.ts b/src/service/sheetApiService.ts index 9b89b9b..15fc6e2 100644 --- a/src/service/sheetApiService.ts +++ b/src/service/sheetApiService.ts @@ -41,7 +41,7 @@ export async function createSheet( sheet: CreateSheetInput ): Promise { try { - console.log('📤 Отправляем данные для создания листа:', sheet) + // console.log('📤 Отправляем данные для создания листа:', sheet) const response = await fetch('/api/sheets/', { method: 'POST', @@ -65,7 +65,7 @@ export async function createSheet( } const result: CreateSheetOutput = await response.json() - console.log('✅ Лист создан успешно:', result) + // console.log('✅ Лист создан успешно:', result) return result } catch (error) { console.error('Ошибка при создании листа:', error) @@ -100,7 +100,7 @@ export async function getSheetsByTemplate( templateId: string ): Promise { try { - console.log('📋 Запрашиваем листы для templateId:', templateId) + // console.log('📋 Запрашиваем листы для templateId:', templateId) // Получаем все листы и фильтруем на клиенте const response = await fetch(`/api/sheets/`, { @@ -110,7 +110,7 @@ export async function getSheetsByTemplate( }, }) - console.log('📋 Ответ сервера на запрос листов:', response.status) + // console.log('📋 Ответ сервера на запрос листов:', response.status) if (!response.ok) { // Пытаемся получить подробности ошибки от сервера @@ -134,12 +134,12 @@ export async function getSheetsByTemplate( sheet => sheet.template_id === templateId ) - console.log('✅ Получены и отфильтрованы листы для шаблона:', { - templateId, - totalSheets: allSheets.sheets.length, - filteredSheetsCount: filteredSheets.length, - sheetNames: filteredSheets.map(s => s.name), - }) + // console.log('✅ Получены и отфильтрованы листы для шаблона:', { + // templateId, + // totalSheets: allSheets.sheets.length, + // filteredSheetsCount: filteredSheets.length, + // sheetNames: filteredSheets.map(s => s.name), + // }) return { sheets: filteredSheets } } catch (error) { @@ -147,7 +147,7 @@ export async function getSheetsByTemplate( // Если ошибка 404 или подобная, возвращаем пустой список вместо выброса ошибки if (error instanceof Error && error.message.includes('404')) { - console.log('📭 Листы для шаблона не найдены, возвращаем пустой список') + // console.log('📭 Листы для шаблона не найдены, возвращаем пустой список') return { sheets: [] } } @@ -179,10 +179,11 @@ export async function getSheet(sheetId: string): Promise { // API обновление листа export async function patchSheet( - sheetId: string, + sheetId: UUID, sheet: PatchSheetInput ): Promise { try { + console.log('POST BODY:' + JSON.stringify(sheet)) const response = await fetch(`/api/sheets/${sheetId}`, { method: 'PATCH', headers: { diff --git a/src/widget/template/ui/TemplateCard.tsx b/src/widget/template/ui/TemplateCard.tsx index 81b81fe..e14d443 100644 --- a/src/widget/template/ui/TemplateCard.tsx +++ b/src/widget/template/ui/TemplateCard.tsx @@ -26,114 +26,110 @@ const TemplateCard: React.FC = ({ const isConfigured = true // TODO: remove return ( - console.log(template.excelFile), - ( - onToggleSelect(template.id) : undefined} - className={clsx( - 'group relative shadow-sm transition-all duration-200', - selectionMode && 'hover:cursor-pointer', - selectionMode && - isSelected && - 'border border-primary ring-2 ring-primary ring-offset-1', - 'hover:border-primary hover:shadow-lg' - )} - > - {selectionMode && ( - + )} + + + {template.name} + + +
+ {template.description && ( +

+ {template.description} +

+ )} +
+ + Создан:{' '} + {new Date(template.createdAt).toLocaleDateString('ru-RU', { + day: '2-digit', + month: '2-digit', + year: '2-digit', + })} + + + Обновлён:{' '} + {new Date(template.updatedAt).toLocaleDateString('ru-RU', { + day: '2-digit', + month: '2-digit', + year: '2-digit', + })} + +
+
+
+ - )} - - - - {template.name} - - - -
- {template.description && ( -

- {template.description} -

- )} -
- - Создан:{' '} - {new Date(template.createdAt).toLocaleDateString('ru-RU', { - day: '2-digit', - month: '2-digit', - year: '2-digit', - })} - - - Обновлён:{' '} - {new Date(template.updatedAt).toLocaleDateString('ru-RU', { - day: '2-digit', - month: '2-digit', - year: '2-digit', - })} - -
-
-
- - - -
-
- - ) + + {isConfigured ? 'Создать протокол' : 'Требует настройки'} + + + +
+
+
) } diff --git a/yarn.lock b/yarn.lock index 30a269f..173a920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -815,6 +815,18 @@ dependencies: "@swc/counter" "^0.1.3" +"@tanstack/query-core@5.83.0": + version "5.83.0" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.83.0.tgz#ac3bf337007bb7ea97b1fd2e7c3ceb4240f36dbc" + integrity sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA== + +"@tanstack/react-query@^5.83.0": + version "5.83.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.83.0.tgz#e72cacdb03d2e6a7e4f82f5b2fc228118941d499" + integrity sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ== + dependencies: + "@tanstack/query-core" "5.83.0" + "@tybys/wasm-util@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369" @@ -840,6 +852,18 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash.isequal@^4.5.8": + version "4.5.8" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz#b30bb6ff6a5f6c19b3daf389d649ac7f7a250499" + integrity sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" + integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== + "@types/node@^24.0.15": version "24.0.15" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.15.tgz#f34fbc973e7d64217106e0c59ed8761e6b51381e" @@ -2667,6 +2691,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -3922,6 +3951,11 @@ use-callback-ref@^1.3.3: dependencies: tslib "^2.0.0" +use-debounce@^10.0.5: + version "10.0.5" + resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.5.tgz#dfad6efbab981a9cff6c7cd8678509d1bc21e34f" + integrity sha512-Q76E3lnIV+4YT9AHcrHEHYmAd9LKwUAbPXDm7FlqVGDHiSOhX3RDjT8dm0AxbJup6WgOb1YEcKyCr11kBJR5KQ== + use-memo-one@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"