Исправлены проблемы с сохранением и убраны console.logs
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем информацию об объединенных ячейках только для листа отчета
|
// Получаем информацию об объединенных ячейках только для листа отчета
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 файла на сервер')
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 значение
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаём контекст для выполнения
|
// Создаём контекст для выполнения
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
34
yarn.lock
34
yarn.lock
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user