Исправлены проблемы с сохранением и убраны console.logs

This commit is contained in:
2025-07-22 07:45:13 +03:00
parent 1b17638565
commit 3275cdc661
18 changed files with 439 additions and 587 deletions

View File

@@ -25,6 +25,8 @@
"@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-toast": "^1.2.14", "@radix-ui/react-toast": "^1.2.14",
"@reduxjs/toolkit": "^1.9.5", "@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-grid-layout": "^1.3.5",
"@types/react-window": "^1.8.8", "@types/react-window": "^1.8.8",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@@ -32,6 +34,7 @@
"eslint-import-resolver-typescript": "^4.4.4", "eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"immer": "^10.1.1", "immer": "^10.1.1",
"lodash.isequal": "^4.5.0",
"lucide-react": "^0.525.0", "lucide-react": "^0.525.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
@@ -40,7 +43,8 @@
"react-rnd": "^10.5.2", "react-rnd": "^10.5.2",
"react-window": "^1.8.11", "react-window": "^1.8.11",
"redux": "^4.2.1", "redux": "^4.2.1",
"tailwind-merge": "^3.3.1" "tailwind-merge": "^3.3.1",
"use-debounce": "^10.0.5"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.15", "@types/node": "^24.0.15",

View File

@@ -153,22 +153,22 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
initialTemplateValues.current[cellAddress] = String(value || '') initialTemplateValues.current[cellAddress] = String(value || '')
}) })
}) })
console.log( // console.log(
'✅ Загружено значений baseFileData для сравнения (приоритет):', // '✅ Загружено значений baseFileData для сравнения (приоритет):',
Object.keys(initialTemplateValues.current).length // Object.keys(initialTemplateValues.current).length
) //)
} else if (hasTemplateData) { } else if (hasTemplateData) {
Object.entries(templateData).forEach(([sheetName, sheetData]) => { Object.entries(templateData).forEach(([sheetName, sheetData]) => {
Object.entries(sheetData).forEach(([cellAddress, value]) => { Object.entries(sheetData).forEach(([cellAddress, value]) => {
initialTemplateValues.current[cellAddress] = String(value || '') initialTemplateValues.current[cellAddress] = String(value || '')
}) })
}) })
console.log( // console.log(
'✅ Загружено значений templateData для сравнения (fallback):', // '✅ Загружено значений templateData для сравнения (fallback):',
Object.keys(initialTemplateValues.current).length // Object.keys(initialTemplateValues.current).length
) // )
} else { } else {
console.log('⚠️ DualSpreadsheet: Нет базовых данных для сравнения') // console.log('⚠️ DualSpreadsheet: Нет базовых данных для сравнения')
} }
}, [baseFileData, templateData]) }, [baseFileData, templateData])
@@ -250,11 +250,11 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
baseFileValue && baseFileValue &&
rawValue !== baseFileValue rawValue !== baseFileValue
) { ) {
console.log(`🔧 Измененная ячейка ${cellName}:`, { // console.log(`🔧 Измененная ячейка ${cellName}:`, {
baseFileValue, // baseFileValue,
rawValue, // rawValue,
isModified: true, // isModified: true,
}) // })
} }
// Нормализуем значения для сравнения // Нормализуем значения для сравнения
@@ -274,11 +274,11 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
// Отладка только для измененных ячеек // Отладка только для измененных ячеек
if (sheetType === 'report' && isModified) { if (sheetType === 'report' && isModified) {
console.log(`🔍 Измененная ячейка ${cellName}:`, { // console.log(`🔍 Измененная ячейка ${cellName}:`, {
baseFileValue, // baseFileValue,
rawValue, // rawValue,
isModified, // isModified,
}) // })
} }
cachedData[cellKey] = { cachedData[cellKey] = {
@@ -326,10 +326,10 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
setHighlightedCells(highlights) setHighlightedCells(highlights)
console.log('🎨 Подсветка ячеек для формулы:', formula, { // console.log('🎨 Подсветка ячеек для формулы:', formula, {
references, // references,
highlights, // highlights,
}) // })
} catch (error) { } catch (error) {
console.error('Ошибка при обновлении подсветки ячеек:', error) console.error('Ошибка при обновлении подсветки ячеек:', error)
setHighlightedCells([]) setHighlightedCells([])
@@ -411,14 +411,14 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
) )
const baseFileValue = initialTemplateValues.current[cellName] ?? '' // Базовое значение из файла const baseFileValue = initialTemplateValues.current[cellName] ?? '' // Базовое значение из файла
console.log('💾 Сохранение ячейки:', { // console.log('💾 Сохранение ячейки:', {
cellName, //cellName,
oldValue, //oldValue,
newValue: editingValue, //newValue: editingValue,
baseFileValue, //baseFileValue,
isChanged: oldValue !== editingValue, //isChanged: oldValue !== editingValue,
isModifiedFromBaseFile: baseFileValue !== editingValue, //isModifiedFromBaseFile: baseFileValue !== editingValue,
}) //})
multiSheetEngine.setCellValueWithoutRecalc( multiSheetEngine.setCellValueWithoutRecalc(
sheetName, sheetName,
@@ -529,12 +529,12 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
const handleCellClick = useCallback( const handleCellClick = useCallback(
(row: number, col: number) => { (row: number, col: number) => {
console.log('🖱️ Обычный клик по ячейке:', { // console.log('🖱️ Обычный клик по ячейке:', {
row, // row,
col, //col,
isEditing, //isEditing,
activeSheet, //activeSheet,
}) //})
if (isEditing) { if (isEditing) {
stopEditing(true) stopEditing(true)
@@ -548,20 +548,20 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
// Новый обработчик для кликов в режиме редактирования - вставляет ссылку // Новый обработчик для кликов в режиме редактирования - вставляет ссылку
const handleCellReferenceClick = useCallback( const handleCellReferenceClick = useCallback(
(targetSheetType: SheetType, row: number, col: number) => { (targetSheetType: SheetType, row: number, col: number) => {
console.log('🔗 Клик для вставки ссылки:', { // console.log('🔗 Клик для вставки ссылки:', {
targetSheetType, //targetSheetType,
row, //row,
col, //col,
isEditing, //isEditing,
selectedCell, //selectedCell,
editingValue, //editingValue,
}) //})
// Проверяем, что мы в режиме редактирования // Проверяем, что мы в режиме редактирования
if (!isEditing || !selectedCell) { if (!isEditing || !selectedCell) {
console.log( // console.log(
'⚠️ Игнорируем клик ссылки: не в режиме редактирования или нет выбранной ячейки' // '⚠️ Игнорируем клик ссылки: не в режиме редактирования или нет выбранной ячейки'
) //)
return return
} }
@@ -571,19 +571,19 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
// Если это другой лист, делаем квалифицированную ссылку // Если это другой лист, делаем квалифицированную ссылку
if (targetSheetType !== activeSheet) { if (targetSheetType !== activeSheet) {
cellReference = createQualifiedCellReference(targetSheetType, row, col) cellReference = createQualifiedCellReference(targetSheetType, row, col)
console.log('📋 Создана межлистовая ссылка:', cellReference) // console.log('📋 Создана межлистовая ссылка:', cellReference)
} else { } else {
// Если это тот же лист, делаем локальную ссылку // Если это тот же лист, делаем локальную ссылку
cellReference = createCellAddress(row, col) cellReference = createCellAddress(row, col)
console.log('📋 Создана локальная ссылка:', cellReference) // console.log('📋 Создана локальная ссылка:', cellReference)
} }
// Добавляем ссылку к текущему значению редактирования // Добавляем ссылку к текущему значению редактирования
const newValue = editingValue + cellReference const newValue = editingValue + cellReference
console.log('✏️ Новое значение формулы:', { // console.log('✏️ Новое значение формулы:', {
oldValue: editingValue, //oldValue: editingValue,
newValue, //newValue,
}) //})
setEditingValue(newValue) setEditingValue(newValue)
@@ -739,15 +739,15 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
// Отладка templateId // Отладка templateId
useEffect(() => { useEffect(() => {
console.log('🆔 Текущий templateId в DualSpreadsheet:', { // console.log('🆔 Текущий templateId в DualSpreadsheet:', {
templateId, //templateId,
type: typeof templateId, //type: typeof templateId,
isValid: 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( // ? /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
templateId // templateId
) // )
: false, // : false,
}) //})
}, [templateId]) }, [templateId])
// Используем хук для перетаскивания разделителя // Используем хук для перетаскивания разделителя
@@ -774,7 +774,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
try { try {
const latestFile = await getLatestFileForTemplate(templateId) const latestFile = await getLatestFileForTemplate(templateId)
if (latestFile) { if (latestFile) {
console.log('📁 Найден последний файл для шаблона:', latestFile.name) // console.log('📁 Найден последний файл для шаблона:', latestFile.name)
const fileData = await getFileData(latestFile.id) const fileData = await getFileData(latestFile.id)
// TODO: Пока getFileData возвращает пустые данные, используем templateData // TODO: Пока getFileData возвращает пустые данные, используем templateData
@@ -784,14 +784,14 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
setBaseFileData(finalBaseData) setBaseFileData(finalBaseData)
// Сохраняем baseline, чтобы в L попадали только пользовательские изменения // Сохраняем baseline, чтобы в L попадали только пользовательские изменения
setTemplateData(finalBaseData) setTemplateData(finalBaseData)
console.log('📄 Загружены базовые данные файла:', finalBaseData) // console.log('📄 Загружены базовые данные файла:', finalBaseData)
} else { } else {
console.log( // console.log(
'⚠️ getFileData вернул пустые данные, используем templateData' // '⚠️ getFileData вернул пустые данные, используем templateData'
) //)
} }
} else { } else {
console.log('⚠️ Файл не найден для templateId:', templateId) // console.log('⚠️ Файл не найден для templateId:', templateId)
} }
} catch (error) { } catch (error) {
console.error('❌ Ошибка при загрузке данных файла:', error) console.error('❌ Ошибка при загрузке данных файла:', error)
@@ -806,16 +806,16 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
if (hasTemplateData && Object.keys(finalBaseData).length === 0) { if (hasTemplateData && Object.keys(finalBaseData).length === 0) {
finalBaseData = templateData finalBaseData = templateData
setBaseFileData(finalBaseData) setBaseFileData(finalBaseData)
console.log( // console.log(
'📄 Используем templateData как базовые данные:', // '📄 Используем templateData как базовые данные:',
finalBaseData // finalBaseData
) //)
setTemplateData(templateData) setTemplateData(templateData)
} }
// 3. ЗАГРУЖАЕМ ПОЛЬЗОВАТЕЛЬСКИЕ ИЗМЕНЕНИЯ ИЗ API // 3. ЗАГРУЖАЕМ ПОЛЬЗОВАТЕЛЬСКИЕ ИЗМЕНЕНИЯ ИЗ API
try { try {
console.log('🔍 Загружаем пользовательские изменения из API...') // console.log('🔍 Загружаем пользовательские изменения из API...')
const apiSheets = await loadSheets() const apiSheets = await loadSheets()
// Преобразуем листы API в формат данных // Преобразуем листы API в формат данных
@@ -840,16 +840,16 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
} }
}) })
console.log( // console.log(
'📂 Загружены пользовательские изменения:', // '📂 Загружены пользовательские изменения:',
Object.keys(userChanges) // Object.keys(userChanges)
) //)
// 4. ОБЪЕДИНЯЕМ БАЗОВЫЕ ДАННЫЕ С ПОЛЬЗОВАТЕЛЬСКИМИ ИЗМЕНЕНИЯМИ // 4. ОБЪЕДИНЯЕМ БАЗОВЫЕ ДАННЫЕ С ПОЛЬЗОВАТЕЛЬСКИМИ ИЗМЕНЕНИЯМИ
if (Object.keys(userChanges).length > 0) { if (Object.keys(userChanges).length > 0) {
console.log( // console.log(
'🔄 Объединяем базовые данные с пользовательскими изменениями' // '🔄 Объединяем базовые данные с пользовательскими изменениями'
) //)
// Создаем объединенные данные: базовые данные файла + пользовательские изменения // Создаем объединенные данные: базовые данные файла + пользовательские изменения
const mergedData: Record<string, Record<string, any>> = {} const mergedData: Record<string, Record<string, any>> = {}
@@ -869,24 +869,24 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
}) })
}) })
console.log( // console.log(
'📊 Загружаем объединенные данные (базовые + пользовательские)' // '📊 Загружаем объединенные данные (базовые + пользовательские)'
) //)
multiSheetEngine.loadData(mergedData) multiSheetEngine.loadData(mergedData)
} else if (Object.keys(finalBaseData).length > 0) { } else if (Object.keys(finalBaseData).length > 0) {
console.log( // console.log(
'📋 Используем только базовые данные (нет пользовательских изменений)' // '📋 Используем только базовые данные (нет пользовательских изменений)'
) //)
multiSheetEngine.loadData(finalBaseData) multiSheetEngine.loadData(finalBaseData)
} else { } else {
console.log('⚠️ Нет данных для загрузки') // console.log('⚠️ Нет данных для загрузки')
} }
} catch (error) { } catch (error) {
console.error('❌ Ошибка при загрузке пользовательских изменений:', error) console.error('❌ Ошибка при загрузке пользовательских изменений:', error)
// В случае ошибки используем только базовые данные // В случае ошибки используем только базовые данные
if (Object.keys(finalBaseData).length > 0) { if (Object.keys(finalBaseData).length > 0) {
console.log('📋 Ошибка API, используем только базовые данные') // console.log('📋 Ошибка API, используем только базовые данные')
multiSheetEngine.loadData(finalBaseData) multiSheetEngine.loadData(finalBaseData)
} }
} }

View File

@@ -44,13 +44,13 @@ export const Cell = memo(
// Отладка стилей для измененных ячеек // Отладка стилей для измененных ячеек
if (isModified) { if (isModified) {
console.log(`🎨 Стили для измененной ячейки ${rowIndex},${colIndex}:`, { // console.log(`🎨 Стили для измененной ячейки ${rowIndex},${colIndex}:`, //{
className, // className,
isModified, // isModified,
isSelected, // isSelected,
displayValue, // displayValue,
highlightColor, // highlightColor,
}) //})
} }
// Для объединенных ячеек нужно рассчитать правильные размеры // Для объединенных ячеек нужно рассчитать правильные размеры
@@ -109,14 +109,14 @@ export const Cell = memo(
const handleClick = useCallback( const handleClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
console.log('🖱️ Cell клик:', { // console.log('🖱️ Cell клик:', {
rowIndex, // rowIndex,
colIndex, // colIndex,
sheetType, // sheetType,
isInEditMode, // isInEditMode,
isSelected, // isSelected,
hasReferenceHandler: !!onCellReferenceClick, //hasReferenceHandler: !!onCellReferenceClick,
}) //})
// Если мы в режиме редактирования, вставка была обработана на mousedown. // Если мы в режиме редактирования, вставка была обработана на mousedown.
// Останавливаем всплытие, чтобы родительский Spreadsheet не сменил активный лист. // Останавливаем всплытие, чтобы родительский Spreadsheet не сменил активный лист.
@@ -127,17 +127,17 @@ export const Cell = memo(
} }
// Обычная логика клика (выделение ячейки) // Обычная логика клика (выделение ячейки)
console.log('📍 Обычный клик - выбираем ячейку:', { // console.log('📍 Обычный клик - выбираем ячейку:', {
rowIndex, // rowIndex,
colIndex, // colIndex,
sheetType, // sheetType,
}) //})
// Подсказка для пользователя // Подсказка для пользователя
if (!isInEditMode) { if (!isInEditMode) {
console.log( // console.log(
'💡 Чтобы вставить ссылку на ячейку: 1) Выберите ячейку 2) Нажмите F2 или Enter для редактирования 3) Кликните на нужную ячейку' // '💡 Чтобы вставить ссылку на ячейку: 1) Выберите ячейку 2) Нажмите F2 или Enter для редактирования 3) Кликните на нужную ячейку'
) //)
} }
setActiveSheet(sheetType) setActiveSheet(sheetType)
@@ -166,11 +166,11 @@ export const Cell = memo(
if (isInEditMode && onCellReferenceClick && !isSelected) { if (isInEditMode && onCellReferenceClick && !isSelected) {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
console.log('🖱️ mousedown вставка ссылки до blur:', { // console.log('🖱️ mousedown вставка ссылки до blur:', {
rowIndex, // rowIndex,
colIndex, // colIndex,
sheetType, // sheetType,
}) //})
onCellReferenceClick(sheetType, rowIndex, colIndex) onCellReferenceClick(sheetType, rowIndex, colIndex)
return return
} }

View File

@@ -55,16 +55,16 @@ export const CellRenderer = ({
// Отладка для измененных ячеек // Отладка для измененных ячеек
if (cellData?.isModified) { if (cellData?.isModified) {
console.log( // console.log(
`🔍 CellRenderer: Измененная ячейка ${rowIndex},${columnIndex}:`, // `🔍 CellRenderer: Измененная ячейка ${rowIndex},${columnIndex}:`,
{ // {
cellKey, // cellKey,
isModified: cellData.isModified, // isModified: cellData.isModified,
displayValue, // displayValue,
rawValue, // rawValue,
highlightColor, // highlightColor,
} // }
) // )
} }
// Получаем информацию об объединенных ячейках только для листа отчета // Получаем информацию об объединенных ячейках только для листа отчета

View File

@@ -56,13 +56,13 @@ export const Spreadsheet = ({
} }
// Отладочное логирование для проверки состояния // Отладочное логирование для проверки состояния
console.log(`📊 Spreadsheet ${sheetType} состояние:`, { // console.log(`📊 Spreadsheet ${sheetType} состояние:`, {
isEditing, // isEditing,
isInEditMode, // isInEditMode,
selectedCell, // selectedCell,
activeSheet, // activeSheet,
isActiveSheet: activeSheet === sheetType, // isActiveSheet: activeSheet === sheetType,
}) //})
const parentRef = useRef<HTMLDivElement>(null) const parentRef = useRef<HTMLDivElement>(null)
const [gridSize, setGridSize] = useState({ width: 0, height: 0 }) const [gridSize, setGridSize] = useState({ width: 0, height: 0 })

View File

@@ -395,7 +395,7 @@ const ElementForm: React.FC<ElementFormProps> = ({ formData, setFormData }) => {
value={formData.type} value={formData.type}
onValueChange={value => { onValueChange={value => {
const newType = value as ElementType const newType = value as ElementType
console.log('Element type changed to:', newType) // console.log('Element type changed to:', newType)
setFormData(prev => { setFormData(prev => {
const updates: Partial<TemplateElement> = { type: newType } const updates: Partial<TemplateElement> = { type: newType }
@@ -405,24 +405,24 @@ const ElementForm: React.FC<ElementFormProps> = ({ formData, setFormData }) => {
newType === 'standards' || newType === 'standards' ||
newType === 'calibration-conditions' newType === 'calibration-conditions'
) { ) {
console.log( // console.log(
`${newType} type selected, getting element definition` // `${newType} type selected, getting element definition`
) // )
const elementDefinition = getElementDefinition(newType) const elementDefinition = getElementDefinition(newType)
console.log('Element definition:', elementDefinition) // console.log('Element definition:', elementDefinition)
if (elementDefinition && elementDefinition.defaultConfig) { if (elementDefinition && elementDefinition.defaultConfig) {
const defaultConfig = const defaultConfig =
elementDefinition.defaultConfig as any elementDefinition.defaultConfig as any
console.log('Default config:', defaultConfig) // console.log('Default config:', defaultConfig)
updates.targetCells = updates.targetCells =
elementDefinition.mapToCells?.(defaultConfig) || [] elementDefinition.mapToCells?.(defaultConfig) || []
console.log('Generated targetCells:', updates.targetCells) // console.log('Generated targetCells:', updates.targetCells)
} }
} }
const newFormData = { ...prev, ...updates } const newFormData = { ...prev, ...updates }
console.log('New form data:', newFormData) // console.log('New form data:', newFormData)
return newFormData return newFormData
}) })
}} }}
@@ -520,7 +520,7 @@ const ElementForm: React.FC<ElementFormProps> = ({ formData, setFormData }) => {
value={formData.type} value={formData.type}
onValueChange={value => { onValueChange={value => {
const newType = value as ElementType const newType = value as ElementType
console.log('Element type changed to (fallback):', newType) // console.log('Element type changed to (fallback):', newType)
setFormData(prev => { setFormData(prev => {
const updates: Partial<TemplateElement> = { type: newType } const updates: Partial<TemplateElement> = { type: newType }
@@ -530,29 +530,29 @@ const ElementForm: React.FC<ElementFormProps> = ({ formData, setFormData }) => {
newType === 'standards' || newType === 'standards' ||
newType === 'calibration-conditions' newType === 'calibration-conditions'
) { ) {
console.log( // console.log(
`${newType} type selected (fallback), getting element definition` // `${newType} type selected (fallback), getting element definition`
) // )
const elementDefinition = getElementDefinition(newType) const elementDefinition = getElementDefinition(newType)
console.log( // console.log(
'Element definition (fallback):', // 'Element definition (fallback):',
elementDefinition // elementDefinition
) // )
if (elementDefinition && elementDefinition.defaultConfig) { if (elementDefinition && elementDefinition.defaultConfig) {
const defaultConfig = elementDefinition.defaultConfig as any const defaultConfig = elementDefinition.defaultConfig as any
console.log('Default config (fallback):', defaultConfig) // console.log('Default config (fallback):', defaultConfig)
updates.targetCells = updates.targetCells =
elementDefinition.mapToCells?.(defaultConfig) || [] elementDefinition.mapToCells?.(defaultConfig) || []
console.log( // console.log(
'Generated targetCells (fallback):', // 'Generated targetCells (fallback):',
updates.targetCells // updates.targetCells
) // )
} }
} }
const newFormData = { ...prev, ...updates } const newFormData = { ...prev, ...updates }
console.log('New form data (fallback):', newFormData) // console.log('New form data (fallback):', newFormData)
return newFormData return newFormData
}) })
}} }}
@@ -746,10 +746,10 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
} }
const handleAddElement = () => { const handleAddElement = () => {
console.log('handleAddElement called with formData:', formData) // console.log('handleAddElement called with formData:', formData)
if (!formData.label) { if (!formData.label) {
console.log('Validation failed: missing label') // console.log('Validation failed: missing label')
return return
} }
@@ -760,9 +760,9 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
formData.type !== 'button-group' && formData.type !== 'button-group' &&
!formData.targetCells?.length !formData.targetCells?.length
) { ) {
console.log( // console.log(
'Validation failed: missing targetCells for element that requires manual targetCells' // 'Validation failed: missing targetCells for element that requires manual targetCells'
) // )
return return
} }

View File

@@ -101,7 +101,7 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
}) })
setDataVersion(v => v + 1) setDataVersion(v => v + 1)
console.log('✅ Файл загружен с ID:', fileId) // console.log('✅ Файл загружен с ID:', fileId)
} catch (err) { } catch (err) {
console.error(err) console.error(err)
alert('Ошибка при загрузке Excel файла на сервер') alert('Ошибка при загрузке Excel файла на сервер')

View File

@@ -398,7 +398,7 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
onLayoutSettingsChange, onLayoutSettingsChange,
onElementEdit, onElementEdit,
}) => { }) => {
console.log('VisualLayoutEditor render with elements:', elements) // console.log('VisualLayoutEditor render with elements:', elements)
const [selectedElementId, setSelectedElementId] = useState<string | null>( const [selectedElementId, setSelectedElementId] = useState<string | null>(
null null
) )

View File

@@ -15,7 +15,7 @@ export interface UseFormulaAutoSaveOptions {
// Ключ для localStorage // Ключ для localStorage
const getStorageKey = (templateId: string) => { const getStorageKey = (templateId: string) => {
console.log('🔑 Получен templateId:', templateId) // console.log('🔑 Получен templateId:', templateId)
return `formula_data_${templateId}` return `formula_data_${templateId}`
} }
@@ -58,7 +58,7 @@ export const useFormulaAutoSave = (
const parsedData: SavedFormulaData = JSON.parse(savedDataStr) const parsedData: SavedFormulaData = JSON.parse(savedDataStr)
setSavedData(parsedData) setSavedData(parsedData)
versionCounter.current = parsedData.version + 1 versionCounter.current = parsedData.version + 1
console.log( // console.log(
'📂 Загружены сохраненные формулы (версия:', '📂 Загружены сохраненные формулы (версия:',
parsedData.version, parsedData.version,
')' ')'
@@ -73,7 +73,7 @@ export const useFormulaAutoSave = (
const performSave = useCallback( const performSave = useCallback(
async (data: Record<string, Record<string, any>>) => { async (data: Record<string, Record<string, any>>) => {
if (Object.keys(data).length === 0) { if (Object.keys(data).length === 0) {
console.log('📭 Нет данных для сохранения') // console.log('📭 Нет данных для сохранения')
return return
} }
@@ -110,7 +110,7 @@ export const useFormulaAutoSave = (
setSavedData(savedData) setSavedData(savedData)
setLastSavedRevision(revision) setLastSavedRevision(revision)
console.log( // console.log(
'💾 Сохранено формул:', '💾 Сохранено формул:',
Object.values(data).reduce( Object.values(data).reduce(
(sum, sheet) => sum + Object.keys(sheet).length, (sum, sheet) => sum + Object.keys(sheet).length,
@@ -136,7 +136,7 @@ export const useFormulaAutoSave = (
// Автосохранение с дебаунсом // Автосохранение с дебаунсом
const scheduleAutoSave = useCallback(() => { const scheduleAutoSave = useCallback(() => {
console.log('⏰ scheduleAutoSave вызван, проверяем условия:', { // console.log('⏰ scheduleAutoSave вызван, проверяем условия:', {
enableAutoSave, enableAutoSave,
isCalculating, isCalculating,
isSaving, isSaving,
@@ -144,21 +144,21 @@ export const useFormulaAutoSave = (
}) })
if (!enableAutoSave || isCalculating || isSaving || !templateId) { if (!enableAutoSave || isCalculating || isSaving || !templateId) {
console.log('❌ Автосохранение отменено по условиям') // console.log('❌ Автосохранение отменено по условиям')
return return
} }
// Очищаем предыдущий таймер // Очищаем предыдущий таймер
if (saveTimeoutRef.current) { if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
console.log('🔄 Очищен предыдущий таймер автосохранения') // console.log('🔄 Очищен предыдущий таймер автосохранения')
} }
console.log(`⏳ Запланировано автосохранение через ${autoSaveDelay}ms`) // console.log(`⏳ Запланировано автосохранение через ${autoSaveDelay}ms`)
saveTimeoutRef.current = setTimeout(() => { saveTimeoutRef.current = setTimeout(() => {
console.log('🎯 Выполняем отложенное автосохранение') // console.log('🎯 Выполняем отложенное автосохранение')
const currentData = getAllModifiedCells() const currentData = getAllModifiedCells()
console.log('📋 Данные для автосохранения:', currentData) // console.log('📋 Данные для автосохранения:', currentData)
performSave(currentData) performSave(currentData)
}, autoSaveDelay) }, autoSaveDelay)
}, [ }, [
@@ -173,7 +173,7 @@ export const useFormulaAutoSave = (
// Эффект для автосохранения при изменениях // Эффект для автосохранения при изменениях
useEffect(() => { useEffect(() => {
console.log('🔍 Проверка условий автосохранения:', { // console.log('🔍 Проверка условий автосохранения:', {
enableAutoSave, enableAutoSave,
revision, revision,
lastSavedRevision, lastSavedRevision,
@@ -193,10 +193,10 @@ export const useFormulaAutoSave = (
!isCalculating && !isCalculating &&
!isSaving !isSaving
) { ) {
console.log('✅ Планируем автосохранение...') // console.log('✅ Планируем автосохранение...')
scheduleAutoSave() scheduleAutoSave()
} else { } else {
console.log('⏸️ Автосохранение пропущено по условиям') // console.log('⏸️ Автосохранение пропущено по условиям')
} }
}, [ }, [
enableAutoSave, enableAutoSave,
@@ -209,20 +209,20 @@ export const useFormulaAutoSave = (
// Ручное сохранение // Ручное сохранение
const manualSave = useCallback(() => { const manualSave = useCallback(() => {
console.log('🖱️ Ручное сохранение вызвано') // console.log('🖱️ Ручное сохранение вызвано')
// Отменяем автосохранение если оно запланировано // Отменяем автосохранение если оно запланировано
if (saveTimeoutRef.current) { if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
saveTimeoutRef.current = null saveTimeoutRef.current = null
console.log('⏹️ Отменено запланированное автосохранение') // console.log('⏹️ Отменено запланированное автосохранение')
} }
const currentData = getAllModifiedCells() const currentData = getAllModifiedCells()
console.log('📋 Данные для сохранения:', currentData) // console.log('📋 Данные для сохранения:', currentData)
if (Object.keys(currentData).length === 0) { if (Object.keys(currentData).length === 0) {
console.log('📭 Нет измененных данных для сохранения') // console.log('📭 Нет измененных данных для сохранения')
return return
} }
@@ -243,7 +243,7 @@ export const useFormulaAutoSave = (
if (savedDataStr) { if (savedDataStr) {
const parsedData: SavedFormulaData = JSON.parse(savedDataStr) const parsedData: SavedFormulaData = JSON.parse(savedDataStr)
console.log('📂 Формулы загружены из localStorage:', { // console.log('📂 Формулы загружены из localStorage:', {
templateId, templateId,
version: parsedData.version, version: parsedData.version,
timestamp: parsedData.timestamp, timestamp: parsedData.timestamp,
@@ -273,7 +273,7 @@ export const useFormulaAutoSave = (
setLastSavedRevision(0) setLastSavedRevision(0)
versionCounter.current = 1 versionCounter.current = 1
console.log('🗑️ Сохраненные формулы очищены:', { templateId }) // console.log('🗑️ Сохраненные формулы очищены:', { templateId })
} catch (error) { } catch (error) {
console.error('❌ Ошибка при очистке сохраненных формул:', error) console.error('❌ Ошибка при очистке сохраненных формул:', error)
} }

View File

@@ -218,7 +218,7 @@ export function useMultiSheetEngine({
const setTemplateData = useCallback( const setTemplateData = useCallback(
(data: Record<string, Record<string, any>>) => { (data: Record<string, Record<string, any>>) => {
templateDataRef.current = JSON.parse(JSON.stringify(data)) // Глубокая копия 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) { if (normalizedCurrent !== normalizedTemplate) {
// Если текущего значения нет (ячейка очищена), отправляем '' // Если текущего значения нет (ячейка очищена), отправляем ''
modifiedSheet[cellAddress] = currentVal modifiedSheet[cellAddress] = currentVal
console.log( // console.log(
`🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`, // `🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`,
{ // {
template: normalizedTemplate, // template: normalizedTemplate,
current: normalizedCurrent, // current: normalizedCurrent,
value: currentVal, // value: currentVal,
} // }
) // )
} }
}) })

View File

@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useDebounce } from 'use-debounce'
import { import {
ApiSheet, ApiSheet,
createSheet, createSheet,
@@ -12,41 +13,13 @@ export interface UseSheetAutoSaveOptions {
enableAutoSave?: boolean 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 = ( export const useSheetAutoSave = (
getAllModifiedCells: () => Record<string, Record<string, any>>, getAllModifiedCells: () => Record<string, Record<string, any>>,
isCalculating: boolean, isCalculating: boolean,
revision: number, revision: number,
options: UseSheetAutoSaveOptions = {} options: UseSheetAutoSaveOptions = {}
) => { ) => {
console.log('getAllModifiedCells', getAllModifiedCells())
const { templateId, autoSaveDelay = 2000, enableAutoSave = true } = options const { templateId, autoSaveDelay = 2000, enableAutoSave = true } = options
// Состояние // Состояние
@@ -56,68 +29,40 @@ export const useSheetAutoSave = (
const [sheets, setSheets] = useState<Record<string, ApiSheet>>({}) const [sheets, setSheets] = useState<Record<string, ApiSheet>>({})
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
// Refs // Дебаунсинг ревизии для автосохранения
const saveTimeoutRef = useRef<number | null>(null) const [debouncedRevision] = useDebounce(revision, autoSaveDelay)
// Загрузка листов для шаблона при инициализации // Загрузка листов для шаблона при инициализации
const loadSheets = useCallback(async () => { const loadSheets = useCallback(async () => {
if (!templateId) return {} 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) setIsLoading(true)
try { try {
const response = await getSheetsByTemplate(cleanTemplateId) const response = await getSheetsByTemplate(templateId)
/*
* В 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
if (acc[normalizedName]) { const sheetsMap = response.sheets.reduce<Record<string, ApiSheet>>(
// Уже есть лист с таким именем — мерджим данные, приоритет у более (acc, sheet) => {
// свежего объекта (sheet), но оставляем существующие ячейки если их const existingSheet = acc[sheet.name]
// нет в новом
acc[normalizedName] = { if (existingSheet) {
...sheet, // Если лист уже есть, объединяем ячейки
name: normalizedName, acc[sheet.name] = {
...sheet, // берем актуальную версию листа
cells: { cells: {
...acc[normalizedName].cells, ...existingSheet.cells, // старые ячейки
...sheet.cells, ...sheet.cells, // новые заменяют старые с тем же ключом
}, },
} }
} else { } else {
acc[normalizedName] = { ...sheet, name: normalizedName } acc[sheet.name] = { ...sheet }
} }
return acc return acc
}, },
{} as Record<string, ApiSheet> {}
) )
setSheets(sheetsMap) setSheets(sheetsMap)
console.log('📂 Загружены листы для шаблона:', Object.keys(sheetsMap))
return sheetsMap return sheetsMap
} catch (error) { } catch (error) {
console.error('❌ Ошибка при загрузке листов:', error) console.error('❌ Ошибка при загрузке листов:', error)
@@ -138,71 +83,29 @@ export const useSheetAutoSave = (
// Основная функция сохранения через API // Основная функция сохранения через API
const performSave = useCallback( const performSave = useCallback(
async (data: Record<string, Record<string, any>>) => { async (data: Record<string, Record<string, any>>) => {
// Сохраняем все листы, но пропускаем пустые if (!templateId) return
const sheetNames = Object.keys(data)
if (sheetNames.length === 0) {
console.log('📭 Нет данных для сохранения')
return
}
if (!templateId) { // Получаем список всех листов, которые нужно обработать:
console.warn('⚠️ Не указан templateId для сохранения листов') // 1. Листы с данными (из data)
return // 2. Существующие листы, которые могли быть очищены
} const dataSheetNames = Object.keys(data)
const existingSheetNames = Object.keys(sheets)
const allSheetNames = new Set([...dataSheetNames, ...existingSheetNames])
// Извлекаем и валидируем UUID из templateId if (allSheetNames.size === 0) 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 из templateId:', {
original: templateId,
extracted: cleanTemplateId,
})
setLastSaveError('Неверный ID шаблона')
return
}
console.log('🧹 Очищенный templateId:', {
original: templateId,
clean: cleanTemplateId,
})
setIsSaving(true) setIsSaving(true)
setLastSaveError(null) setLastSaveError(null)
try { try {
const savePromises = sheetNames.map(async sheetName => { const savePromises = Array.from(allSheetNames).map(async sheetName => {
const sheetCells = data[sheetName] const sheetCells = data[sheetName] || {} // если данных нет, используем пустой объект
const existingSheet = sheets[sheetName] const existingSheet = sheets[sheetName]
// Проверяем что есть данные для сохранения
if (!sheetCells || Object.keys(sheetCells).length === 0) {
console.log(`⏭️ Пропускаем лист "${sheetName}" - нет данных`)
return
}
if (existingSheet) { 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, { await patchSheet(existingSheet.id, {
cells: mergedCells, cells: sheetCells,
}) })
// Обновляем локальное состояние // Обновляем локальное состояние
@@ -210,23 +113,17 @@ export const useSheetAutoSave = (
...prev, ...prev,
[sheetName]: { [sheetName]: {
...existingSheet, ...existingSheet,
cells: mergedCells, cells: sheetCells,
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}, },
})) }))
} else { } else {
// Создаем новый лист // Создаем новый лист только если есть данные
console.log(`✨ Создаем новый лист "${sheetName}"`) if (Object.keys(sheetCells).length > 0) {
console.log('📋 Данные для создания:', {
name: sheetName,
cells: sheetCells,
template_id: cleanTemplateId,
cellsCount: Object.keys(sheetCells).length,
})
const newSheet = await createSheet({ const newSheet = await createSheet({
name: sheetName, name: sheetName,
cells: sheetCells, cells: sheetCells,
template_id: cleanTemplateId, template_id: templateId,
}) })
// Получаем полную информацию о созданном листе // Получаем полную информацию о созданном листе
@@ -234,7 +131,7 @@ export const useSheetAutoSave = (
id: newSheet.id, id: newSheet.id,
name: sheetName, name: sheetName,
cells: sheetCells, cells: sheetCells,
template_id: cleanTemplateId, template_id: templateId,
created_at: new Date().toISOString(), created_at: new Date().toISOString(),
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
deleted_at: null, deleted_at: null,
@@ -246,16 +143,11 @@ export const useSheetAutoSave = (
[sheetName]: fullSheetData, [sheetName]: fullSheetData,
})) }))
} }
}
}) })
await Promise.all(savePromises) await Promise.all(savePromises)
setLastSavedRevision(revision) setLastSavedRevision(revision)
const totalCells = Object.values(data).reduce(
(sum, sheet) => sum + Object.keys(sheet).length,
0
)
console.log('💾 Сохранено ячеек:', totalCells, 'в листах:', sheetNames)
} catch (error) { } catch (error) {
const errorMessage = const errorMessage =
error instanceof Error ? error.message : 'Неизвестная ошибка' error instanceof Error ? error.message : 'Неизвестная ошибка'
@@ -268,98 +160,33 @@ export const useSheetAutoSave = (
[templateId, sheets, revision] [templateId, sheets, revision]
) )
// Автосохранение с дебаунсом // Автосохранение при изменении дебаунсированной ревизии
const scheduleAutoSave = useCallback(() => { useEffect(() => {
console.log('⏰ scheduleAutoSave вызван, проверяем условия:', { const shouldAutoSave =
enableAutoSave, enableAutoSave &&
isCalculating, debouncedRevision > lastSavedRevision &&
isSaving, !isCalculating &&
templateId, !isSaving &&
}) templateId
if (!enableAutoSave || isCalculating || isSaving || !templateId) { if (shouldAutoSave) {
console.log('Автосохранение отменено по условиям') console.log('✅ Выполняем автосохранение для ревизии:', debouncedRevision)
return 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, enableAutoSave,
debouncedRevision,
lastSavedRevision,
isCalculating, isCalculating,
isSaving, isSaving,
templateId, templateId,
autoSaveDelay,
getAllModifiedCells, getAllModifiedCells,
performSave, 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(() => { const manualSave = useCallback(() => {
console.log('🖱️ Ручное сохранение вызвано')
// Отменяем автосохранение если оно запланировано
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current)
saveTimeoutRef.current = null
console.log('⏹️ Отменено запланированное автосохранение')
}
const currentData = getAllModifiedCells() const currentData = getAllModifiedCells()
console.log('📋 Данные для сохранения:', currentData)
if (Object.keys(currentData).length === 0) {
console.log('📭 Нет измененных данных для сохранения')
return
}
performSave(currentData) performSave(currentData)
}, [getAllModifiedCells, performSave]) }, [getAllModifiedCells, performSave])
@@ -376,19 +203,9 @@ export const useSheetAutoSave = (
} }
}) })
console.log('📂 Загружены сохраненные листы:', Object.keys(result))
return result return result
}, [sheets]) }, [sheets])
// Очистка таймаута при размонтировании
useEffect(() => {
return () => {
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current)
}
}
}, [])
// Вычисляемые значения // Вычисляемые значения
const isDataChanged = revision > lastSavedRevision const isDataChanged = revision > lastSavedRevision
const hasUnsavedChanges = isDataChanged && !isSaving const hasUnsavedChanges = isDataChanged && !isSaving

View File

@@ -20,28 +20,28 @@ export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
const flatArgs = args.flat() const flatArgs = args.flat()
return flatArgs.reduce( return flatArgs.reduce(
(sum, val) => sum + (typeof val === 'number' ? val : 0), (sum, val) => sum + (typeof val === 'number' ? val : 0),
0, 0
) )
}, },
СРЗНАЧ: (...args: any[]) => { СРЗНАЧ: (...args: any[]) => {
const flatArgs = args.flat() const flatArgs = args.flat()
const validArgs = flatArgs.filter((val) => typeof val === 'number') const validArgs = flatArgs.filter(val => typeof val === 'number')
return validArgs.length > 0 return validArgs.length > 0
? validArgs.reduce((sum, val) => sum + val, 0) / validArgs.length ? validArgs.reduce((sum, val) => sum + val, 0) / validArgs.length
: 0 : 0
}, },
СЧЁТ: (...args: any[]) => { СЧЁТ: (...args: any[]) => {
const flatArgs = args.flat() const flatArgs = args.flat()
return flatArgs.filter((val) => typeof val === 'number').length return flatArgs.filter(val => typeof val === 'number').length
}, },
МАКС: (...args: any[]) => { МАКС: (...args: any[]) => {
const flatArgs = args.flat() 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 return validArgs.length > 0 ? Math.max(...validArgs) : 0
}, },
МИН: (...args: any[]) => { МИН: (...args: any[]) => {
const flatArgs = args.flat() 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 return validArgs.length > 0 ? Math.min(...validArgs) : 0
}, },
ЕСЛИ: (условие: boolean, истина: CellValue, ложь: CellValue) => ЕСЛИ: (условие: boolean, истина: CellValue, ложь: CellValue) =>
@@ -96,7 +96,7 @@ export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
// Отладочная функция // Отладочная функция
ТЕСТ: (значение: any = 'Функция работает!') => { ТЕСТ: (значение: any = 'Функция работает!') => {
console.log('ТЕСТ вызван с:', значение) // console.log('ТЕСТ вызван с:', значение)
return значение return значение
}, },
} }

View File

@@ -143,9 +143,9 @@ export class Cell {
// Отладка // Отладка
if (expr.includes('ТЕСТ')) { if (expr.includes('ТЕСТ')) {
console.log('Processing ТЕСТ function') // console.log('Processing ТЕСТ function')
console.log('Available functions:', Object.keys(functions)) // console.log('Available functions:', Object.keys(functions))
console.log('Original code:', code) // console.log('Original code:', code)
} }
for (const [funcName] of Object.entries(functions)) { for (const [funcName] of Object.entries(functions)) {
@@ -164,12 +164,12 @@ export class Cell {
}) })
if (before !== code && expr.includes('ТЕСТ')) { if (before !== code && expr.includes('ТЕСТ')) {
console.log(`Replaced ${funcName}:`, before, '->', code) // console.log(`Replaced ${funcName}:`, before, '->', code)
} }
} }
if (expr.includes('ТЕСТ')) { if (expr.includes('ТЕСТ')) {
console.log('Final code:', code) // console.log('Final code:', code)
} }
// Создаём контекст для выполнения // Создаём контекст для выполнения

View File

@@ -29,10 +29,10 @@ export const ElementsCreation: FC = () => {
}, [templateId, templates, navigate, isLoading]) }, [templateId, templates, navigate, isLoading])
const handleElementAdd = async (element: TemplateElement) => { const handleElementAdd = async (element: TemplateElement) => {
console.log('handleElementAdd called with element:', element) // console.log('handleElementAdd called with element:', element)
if (!selectedTemplate) { if (!selectedTemplate) {
console.log('No selected template') // console.log('No selected template')
return return
} }
@@ -44,8 +44,8 @@ export const ElementsCreation: FC = () => {
updatedAt: new Date(), updatedAt: new Date(),
} }
console.log('Updated template:', updatedTemplate) // console.log('Updated template:', updatedTemplate)
console.log('Elements count:', updatedTemplate.elements.length) // console.log('Elements count:', updatedTemplate.elements.length)
await updateTemplate(updatedTemplate) await updateTemplate(updatedTemplate)
setSelectedTemplate(updatedTemplate) setSelectedTemplate(updatedTemplate)

View File

@@ -441,15 +441,15 @@ const ProtocolForm: FC<ProtocolFormProps> = ({ template, onSave, onBack }) => {
} }
onSave(data) onSave(data)
console.log( // console.log(
'[DBG] template elements', // '[DBG] template elements',
template.elements.map(e => ({ // template.elements.map(e => ({
id: e.id, // id: e.id,
label: e.label, // label: e.label,
target: e.targetCells, // target: e.targetCells,
type: e.type, // type: e.type,
})) // }))
) // )
try { try {
if (!engineRef.current) { if (!engineRef.current) {
@@ -569,7 +569,7 @@ const ProtocolForm: FC<ProtocolFormProps> = ({ 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) const latestFile = await getLatestFileForTemplate(template.id)
@@ -584,7 +584,7 @@ const ProtocolForm: FC<ProtocolFormProps> = ({ template, onSave, onBack }) => {
template_id: template.id, template_id: template.id,
} }
console.log('[DBG] POST body', body) // console.log('[DBG] POST body', body)
const resp = await fetch('/api/protocols/', { const resp = await fetch('/api/protocols/', {
method: 'POST', method: 'POST',
@@ -595,7 +595,7 @@ const ProtocolForm: FC<ProtocolFormProps> = ({ template, onSave, onBack }) => {
if (!resp.ok) throw new Error(`HTTP ${resp.status}`) if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const result = await resp.json() const result = await resp.json()
console.log('✅ Протокол создан, ID:', result.protocol_id) // console.log('✅ Протокол создан, ID:', result.protocol_id)
} catch (err) { } catch (err) {
console.error('Ошибка при создании протокола', err) console.error('Ошибка при создании протокола', err)
alert('Ошибка при создании протокола') alert('Ошибка при создании протокола')
@@ -603,7 +603,7 @@ const ProtocolForm: FC<ProtocolFormProps> = ({ template, onSave, onBack }) => {
} }
const handleExport = () => { const handleExport = () => {
console.log('Экспорт в Excel:', { formData }) // console.log('Экспорт в Excel:', { formData })
alert('Функция экспорта будет реализована в следующих версиях') alert('Функция экспорта будет реализована в следующих версиях')
} }
@@ -767,7 +767,7 @@ export const ProtocolCreation: FC = () => {
}, [templateId, templates]) }, [templateId, templates])
const handleProtocolSave = (data: Record<string, any>) => { const handleProtocolSave = (data: Record<string, any>) => {
console.log('Сохранение протокола:', data) // console.log('Сохранение протокола:', data)
// Здесь будет логика сохранения протокола // Здесь будет логика сохранения протокола
alert(`Протокол "${data.protocolName}" сохранен!`) alert(`Протокол "${data.protocolName}" сохранен!`)
navigate('/templates') navigate('/templates')

View File

@@ -41,7 +41,7 @@ export async function createSheet(
sheet: CreateSheetInput sheet: CreateSheetInput
): Promise<CreateSheetOutput> { ): Promise<CreateSheetOutput> {
try { try {
console.log('📤 Отправляем данные для создания листа:', sheet) // console.log('📤 Отправляем данные для создания листа:', sheet)
const response = await fetch('/api/sheets/', { const response = await fetch('/api/sheets/', {
method: 'POST', method: 'POST',
@@ -65,7 +65,7 @@ export async function createSheet(
} }
const result: CreateSheetOutput = await response.json() const result: CreateSheetOutput = await response.json()
console.log('✅ Лист создан успешно:', result) // console.log('✅ Лист создан успешно:', result)
return result return result
} catch (error) { } catch (error) {
console.error('Ошибка при создании листа:', error) console.error('Ошибка при создании листа:', error)
@@ -100,7 +100,7 @@ export async function getSheetsByTemplate(
templateId: string templateId: string
): Promise<GetSheetsOutput> { ): Promise<GetSheetsOutput> {
try { try {
console.log('📋 Запрашиваем листы для templateId:', templateId) // console.log('📋 Запрашиваем листы для templateId:', templateId)
// Получаем все листы и фильтруем на клиенте // Получаем все листы и фильтруем на клиенте
const response = await fetch(`/api/sheets/`, { 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) { if (!response.ok) {
// Пытаемся получить подробности ошибки от сервера // Пытаемся получить подробности ошибки от сервера
@@ -134,12 +134,12 @@ export async function getSheetsByTemplate(
sheet => sheet.template_id === templateId sheet => sheet.template_id === templateId
) )
console.log('✅ Получены и отфильтрованы листы для шаблона:', { // console.log('✅ Получены и отфильтрованы листы для шаблона:', {
templateId, // templateId,
totalSheets: allSheets.sheets.length, // totalSheets: allSheets.sheets.length,
filteredSheetsCount: filteredSheets.length, // filteredSheetsCount: filteredSheets.length,
sheetNames: filteredSheets.map(s => s.name), // sheetNames: filteredSheets.map(s => s.name),
}) // })
return { sheets: filteredSheets } return { sheets: filteredSheets }
} catch (error) { } catch (error) {
@@ -147,7 +147,7 @@ export async function getSheetsByTemplate(
// Если ошибка 404 или подобная, возвращаем пустой список вместо выброса ошибки // Если ошибка 404 или подобная, возвращаем пустой список вместо выброса ошибки
if (error instanceof Error && error.message.includes('404')) { if (error instanceof Error && error.message.includes('404')) {
console.log('📭 Листы для шаблона не найдены, возвращаем пустой список') // console.log('📭 Листы для шаблона не найдены, возвращаем пустой список')
return { sheets: [] } return { sheets: [] }
} }
@@ -179,10 +179,11 @@ export async function getSheet(sheetId: string): Promise<GetSheetOutput> {
// API обновление листа // API обновление листа
export async function patchSheet( export async function patchSheet(
sheetId: string, sheetId: UUID,
sheet: PatchSheetInput sheet: PatchSheetInput
): Promise<PatchSheetOutput> { ): Promise<PatchSheetOutput> {
try { try {
console.log('POST BODY:' + JSON.stringify(sheet))
const response = await fetch(`/api/sheets/${sheetId}`, { const response = await fetch(`/api/sheets/${sheetId}`, {
method: 'PATCH', method: 'PATCH',
headers: { headers: {

View File

@@ -26,8 +26,7 @@ const TemplateCard: React.FC<TemplateCardProps> = ({
const isConfigured = true // TODO: remove const isConfigured = true // TODO: remove
return ( return (
console.log(template.excelFile), // console.log(template.excelFile),
(
<Card <Card
aria-label={`Шаблон ${template.name}`} aria-label={`Шаблон ${template.name}`}
onClick={selectionMode ? () => onToggleSelect(template.id) : undefined} onClick={selectionMode ? () => onToggleSelect(template.id) : undefined}
@@ -61,9 +60,7 @@ const TemplateCard: React.FC<TemplateCardProps> = ({
)} )}
<CardHeader className="pb-4"> <CardHeader className="pb-4">
<CardTitle className="text-base font-medium"> <CardTitle className="text-base font-medium">{template.name}</CardTitle>
{template.name}
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex flex-col justify-between pt-0"> <CardContent className="flex flex-col justify-between pt-0">
<div> <div>
@@ -134,7 +131,6 @@ const TemplateCard: React.FC<TemplateCardProps> = ({
</CardContent> </CardContent>
</Card> </Card>
) )
)
} }
export default TemplateCard export default TemplateCard

View File

@@ -815,6 +815,18 @@
dependencies: dependencies:
"@swc/counter" "^0.1.3" "@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": "@tybys/wasm-util@^0.10.0":
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369" 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" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== 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": "@types/node@^24.0.15":
version "24.0.15" version "24.0.15"
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.15.tgz#f34fbc973e7d64217106e0c59ed8761e6b51381e" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.15.tgz#f34fbc973e7d64217106e0c59ed8761e6b51381e"
@@ -2667,6 +2691,11 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" 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: lodash.merge@^4.6.2:
version "4.6.2" version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 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: dependencies:
tslib "^2.0.0" 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: use-memo-one@^1.1.1:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99" resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"