переименования папок

This commit is contained in:
2025-07-21 15:02:10 +03:00
parent d0c79bd334
commit 84f0c9c6aa
72 changed files with 245 additions and 249 deletions

View File

@@ -0,0 +1,387 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import {
ApiSheet,
createSheet,
getSheetsByTemplate,
patchSheet,
} from '../service/sheetApiService'
export interface UseSheetAutoSaveOptions {
templateId?: string
autoSaveDelay?: number // задержка автосохранения в мс
enableAutoSave?: boolean
}
/**
* Хук для автосохранения листов через API
*
* Функции:
* - Автоматическое сохранение с дебаунсом при изменениях
* - Ручное сохранение по требованию
* - Создание и обновление листов через API
* - Загрузка существующих листов
*/
// Утилита для извлечения UUID из templateId
const extractUUID = (id: string): string => {
// Если это уже чистый UUID, возвращаем как есть
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
if (uuidRegex.test(id)) {
return id
}
// Если есть префикс (например "template-"), извлекаем UUID
const match = id.match(
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i
)
if (match) {
return match[0]
}
return id // Возвращаем оригинал если не найден UUID
}
export const useSheetAutoSave = (
getAllModifiedCells: () => Record<string, Record<string, any>>,
isCalculating: boolean,
revision: number,
options: UseSheetAutoSaveOptions = {}
) => {
const { templateId, autoSaveDelay = 2000, enableAutoSave = true } = options
// Состояние
const [lastSavedRevision, setLastSavedRevision] = useState(0)
const [isSaving, setIsSaving] = useState(false)
const [lastSaveError, setLastSaveError] = useState<string | null>(null)
const [sheets, setSheets] = useState<Record<string, ApiSheet>>({})
const [isLoading, setIsLoading] = useState(false)
// Refs
const saveTimeoutRef = useRef<number | null>(null)
// Загрузка листов для шаблона при инициализации
const loadSheets = useCallback(async () => {
if (!templateId) return {}
const cleanTemplateId = extractUUID(templateId)
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
if (!uuidRegex.test(cleanTemplateId)) {
console.error('❌ Не удалось извлечь валидный UUID для загрузки:', {
original: templateId,
extracted: cleanTemplateId,
})
setLastSaveError('Неверный ID шаблона')
return {}
}
setIsLoading(true)
try {
const response = await getSheetsByTemplate(cleanTemplateId)
const sheetsMap = response.sheets.reduce(
(acc, sheet) => {
const normalizedName = sheet.name === 'Report' ? 'L' : sheet.name
acc[normalizedName] = { ...sheet, name: normalizedName }
return acc
},
{} as Record<string, ApiSheet>
)
setSheets(sheetsMap)
console.log('📂 Загружены листы для шаблона:', Object.keys(sheetsMap))
return sheetsMap
} catch (error) {
console.error('❌ Ошибка при загрузке листов:', error)
setLastSaveError('Ошибка загрузки листов')
return {}
} finally {
setIsLoading(false)
}
}, [templateId])
// Загрузка листов при изменении templateId
useEffect(() => {
if (templateId) {
loadSheets()
}
}, [templateId, loadSheets])
// Основная функция сохранения через API
const performSave = useCallback(
async (data: Record<string, Record<string, any>>) => {
// Сохраняем все листы, но пропускаем пустые
const sheetNames = Object.keys(data)
if (sheetNames.length === 0) {
console.log('📭 Нет данных для сохранения')
return
}
if (!templateId) {
console.warn('⚠️ Не указан templateId для сохранения листов')
return
}
// Извлекаем и валидируем UUID из templateId
const cleanTemplateId = extractUUID(templateId)
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
if (!uuidRegex.test(cleanTemplateId)) {
console.error('❌ Не удалось извлечь валидный UUID из templateId:', {
original: templateId,
extracted: cleanTemplateId,
})
setLastSaveError('Неверный ID шаблона')
return
}
console.log('🧹 Очищенный templateId:', {
original: templateId,
clean: cleanTemplateId,
})
setIsSaving(true)
setLastSaveError(null)
try {
const savePromises = sheetNames.map(async sheetName => {
const sheetCells = data[sheetName]
const existingSheet = sheets[sheetName]
// Проверяем что есть данные для сохранения
if (!sheetCells || Object.keys(sheetCells).length === 0) {
console.log(`⏭️ Пропускаем лист "${sheetName}" - нет данных`)
return
}
if (existingSheet) {
// Объединяем старые и новые данные, чтобы не потерять предыдущие изменения
const mergedCells = {
...existingSheet.cells,
...sheetCells,
}
// Проверяем, изменилось ли что-нибудь
const isChanged = Object.keys(sheetCells).some(
key => existingSheet.cells[key] !== sheetCells[key]
)
if (!isChanged) {
console.log(`⏭️ Лист "${sheetName}" без новых изменений`)
return
}
console.log(`🔄 Обновляем лист "${sheetName}" (patch)`)
await patchSheet(existingSheet.id, {
cells: mergedCells,
})
// Обновляем локальное состояние
setSheets(prev => ({
...prev,
[sheetName]: {
...existingSheet,
cells: mergedCells,
updated_at: new Date().toISOString(),
},
}))
} else {
// Создаем новый лист
console.log(`✨ Создаем новый лист "${sheetName}"`)
console.log('📋 Данные для создания:', {
name: sheetName,
cells: sheetCells,
template_id: cleanTemplateId,
cellsCount: Object.keys(sheetCells).length,
})
const newSheet = await createSheet({
name: sheetName,
cells: sheetCells,
template_id: cleanTemplateId,
})
// Получаем полную информацию о созданном листе
const fullSheetData: ApiSheet = {
id: newSheet.id,
name: sheetName,
cells: sheetCells,
template_id: cleanTemplateId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
deleted_at: null,
}
// Обновляем локальное состояние
setSheets(prev => ({
...prev,
[sheetName]: fullSheetData,
}))
}
})
await Promise.all(savePromises)
setLastSavedRevision(revision)
const totalCells = Object.values(data).reduce(
(sum, sheet) => sum + Object.keys(sheet).length,
0
)
console.log('💾 Сохранено ячеек:', totalCells, 'в листах:', sheetNames)
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Неизвестная ошибка'
setLastSaveError(errorMessage)
console.error('❌ Ошибка при сохранении листов:', errorMessage)
} finally {
setIsSaving(false)
}
},
[templateId, sheets, revision]
)
// Автосохранение с дебаунсом
const scheduleAutoSave = useCallback(() => {
console.log('⏰ scheduleAutoSave вызван, проверяем условия:', {
enableAutoSave,
isCalculating,
isSaving,
templateId,
})
if (!enableAutoSave || isCalculating || isSaving || !templateId) {
console.log('❌ Автосохранение отменено по условиям')
return
}
// Очищаем предыдущий таймер
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,
isCalculating,
isSaving,
templateId,
autoSaveDelay,
getAllModifiedCells,
performSave,
])
// Эффект для автосохранения при изменениях
useEffect(() => {
console.log('🔍 Проверка условий автосохранения:', {
enableAutoSave,
revision,
lastSavedRevision,
isCalculating,
isSaving,
templateId,
shouldSave:
enableAutoSave &&
revision > lastSavedRevision &&
!isCalculating &&
!isSaving,
})
if (
enableAutoSave &&
revision > lastSavedRevision &&
!isCalculating &&
!isSaving
) {
console.log('✅ Планируем автосохранение...')
scheduleAutoSave()
} else {
console.log('⏸️ Автосохранение пропущено по условиям')
}
}, [
enableAutoSave,
revision,
lastSavedRevision,
isCalculating,
isSaving,
scheduleAutoSave,
])
// Ручное сохранение
const manualSave = useCallback(() => {
console.log('🖱️ Ручное сохранение вызвано')
// Отменяем автосохранение если оно запланировано
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current)
saveTimeoutRef.current = null
console.log('⏹️ Отменено запланированное автосохранение')
}
const currentData = getAllModifiedCells()
console.log('📋 Данные для сохранения:', currentData)
if (Object.keys(currentData).length === 0) {
console.log('📭 Нет измененных данных для сохранения')
return
}
performSave(currentData)
}, [getAllModifiedCells, performSave])
// Функция загрузки сохраненных листов
const loadSavedSheets = useCallback((): Record<
string,
Record<string, any>
> => {
const result: Record<string, Record<string, any>> = {}
Object.entries(sheets).forEach(([sheetName, sheet]) => {
if (sheet.cells && Object.keys(sheet.cells).length > 0) {
result[sheetName] = sheet.cells
}
})
console.log('📂 Загружены сохраненные листы:', Object.keys(result))
return result
}, [sheets])
// Очистка таймаута при размонтировании
useEffect(() => {
return () => {
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current)
}
}
}, [])
// Вычисляемые значения
const isDataChanged = revision > lastSavedRevision
const hasUnsavedChanges = isDataChanged && !isSaving
return {
// Состояние
isSaving,
isLoading,
isDataChanged,
hasUnsavedChanges,
lastSaveError,
lastSavedRevision,
sheets,
// Методы
manualSave,
loadSavedSheets,
loadSheets,
// Утилиты
getCurrentData: getAllModifiedCells,
clearError: () => setLastSaveError(null),
}
}