diff --git a/src/app/App.tsx b/src/app/App.tsx
index 8a38b9a..61bc06f 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -2,6 +2,7 @@ import { FC, useEffect } from 'react'
import { Navigate, Route, Routes } from 'react-router-dom'
+import Layout from '@/app/Layout'
import { CategoryProvider } from '@/entity/template/model/CategoryContext'
import { TemplateProvider } from '@/entity/template/model/TemplateContext'
import {
@@ -17,7 +18,6 @@ import {
TemplatesOverviewPage,
} from '@/page'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-import { Layout } from './Layout'
const queryClient = new QueryClient()
diff --git a/src/app/Layout/Layout.tsx b/src/app/Layout.tsx
similarity index 86%
rename from src/app/Layout/Layout.tsx
rename to src/app/Layout.tsx
index 6328804..ddbb80b 100644
--- a/src/app/Layout/Layout.tsx
+++ b/src/app/Layout.tsx
@@ -4,7 +4,6 @@ import { Outlet } from 'react-router-dom'
const Layout: FC = () => {
return (
- {/* Основной контент */}
diff --git a/src/app/Layout/index.ts b/src/app/Layout/index.ts
deleted file mode 100644
index 4ea2400..0000000
--- a/src/app/Layout/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import Layout from "./Layout";
-
-export { Layout };
diff --git a/src/app/hooks.ts b/src/app/hooks.ts
deleted file mode 100644
index e1fda31..0000000
--- a/src/app/hooks.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { useDispatch, useSelector } from "react-redux";
-import type { TypedUseSelectorHook } from "react-redux";
-import type { RootState, AppDispatch } from "./store";
-
-export const useAppDispatch: () => AppDispatch = useDispatch;
-export const useAppSelector: TypedUseSelectorHook = useSelector;
diff --git a/src/app/index.tsx b/src/app/index.tsx
index 921c41f..497afe7 100644
--- a/src/app/index.tsx
+++ b/src/app/index.tsx
@@ -9,7 +9,6 @@ import { initializeElementRegistry } from '@/entity/element/model/interface'
import App from './App'
import './index.css'
-// Инициализируем реестр элементов
initializeElementRegistry()
createRoot(document.getElementById('root') as HTMLElement).render(
diff --git a/src/component/DualSpreadsheet/types.ts b/src/component/DualSpreadsheet/types.ts
index 15294d7..5896e03 100644
--- a/src/component/DualSpreadsheet/types.ts
+++ b/src/component/DualSpreadsheet/types.ts
@@ -32,12 +32,6 @@ export interface CellSelection {
endCol: number
}
-// Интерфейс для выделения множества ячеек
-export interface MultiSelection {
- sheet: SheetType
- selections: CellSelection[]
-}
-
// Кэшированные данные ячеек для оптимизации
export interface CachedCellData {
displayValue: string
diff --git a/src/entity/element/model/implementations/VerificationConditionsElement/api.ts b/src/entity/element/model/implementations/VerificationConditionsElement/api.ts
index 04cc328..f199e23 100644
--- a/src/entity/element/model/implementations/VerificationConditionsElement/api.ts
+++ b/src/entity/element/model/implementations/VerificationConditionsElement/api.ts
@@ -13,14 +13,11 @@ import {
UpdateDailyConditionOutput,
} from './types'
-// Базовый URL для API (может быть настроен через переменные окружения)
-const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'
-
// API для лабораторий
export const laboratoriesApi = {
// Получить все лаборатории
getAll: async (): Promise => {
- const response = await fetch(`${BASE_URL}/laboratories/`)
+ const response = await fetch(`api/laboratories/`)
if (!response.ok) {
throw new Error('Ошибка загрузки лабораторий')
}
@@ -30,7 +27,7 @@ export const laboratoriesApi = {
// Получить лабораторию по ID
getById: async (id: string): Promise => {
- const response = await fetch(`${BASE_URL}/laboratories/${id}`)
+ const response = await fetch(`api/laboratories/${id}`)
if (!response.ok) {
if (response.status === 404) {
return null
@@ -60,8 +57,8 @@ export const dailyConditionsApi = {
}
const url = searchParams.toString()
- ? `${BASE_URL}/daily-conditions/?${searchParams}`
- : `${BASE_URL}/daily-conditions/`
+ ? `api/daily-conditions/?${searchParams}`
+ : `api/daily-conditions/`
const response = await fetch(url)
if (!response.ok) {
@@ -73,7 +70,7 @@ export const dailyConditionsApi = {
// Получить конкретное условие по ID
getById: async (id: string): Promise => {
- const response = await fetch(`${BASE_URL}/daily-conditions/${id}`)
+ const response = await fetch(`api/daily-conditions/${id}`)
if (!response.ok) {
if (response.status === 404) {
return null
@@ -86,7 +83,7 @@ export const dailyConditionsApi = {
// Создать новые условия
create: async (input: CreateDailyConditionInput): Promise => {
- const response = await fetch(`${BASE_URL}/daily-conditions/`, {
+ const response = await fetch(`api/daily-conditions/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -108,7 +105,7 @@ export const dailyConditionsApi = {
id: string,
input: UpdateDailyConditionInput
): Promise => {
- const response = await fetch(`${BASE_URL}/daily-conditions/${id}`, {
+ const response = await fetch(`api/daily-conditions/${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
@@ -124,7 +121,7 @@ export const dailyConditionsApi = {
// Удалить условия
delete: async (id: string): Promise => {
- const response = await fetch(`${BASE_URL}/daily-conditions/${id}`, {
+ const response = await fetch(`api/daily-conditions/${id}`, {
method: 'DELETE',
})
if (!response.ok) {
@@ -203,56 +200,3 @@ export const elementApi = {
return savedCondition
},
}
-
-// Вспомогательные функции
-export const verificationConditionsUtils = {
- // Валидация значения условия
- validateConditionValue: (
- value: any,
- dataType: string = 'string',
- minValue?: number,
- maxValue?: number
- ): { isValid: boolean; error?: string } => {
- if (dataType === 'number') {
- const numValue = parseFloat(value)
- if (isNaN(numValue)) {
- return { isValid: false, error: 'Должно быть числом' }
- }
- if (minValue !== undefined && numValue < minValue) {
- return { isValid: false, error: `Минимальное значение: ${minValue}` }
- }
- if (maxValue !== undefined && numValue > maxValue) {
- return { isValid: false, error: `Максимальное значение: ${maxValue}` }
- }
- }
- return { isValid: true }
- },
-
- // Форматирование значения с единицей измерения
- formatValueWithUnit: (value: any, unit?: string): string => {
- if (value === undefined || value === null || value === '') {
- return '-'
- }
- return unit ? `${value} ${unit}` : value.toString()
- },
-
- // Получение ключа условия для хранения в conditions JSON (обычно используется ID типа условия)
- getConditionKey: (conditionTypeId: string): string => {
- return conditionTypeId
- },
-
- // Генерация ключа на основе названия условия (для обратной совместимости)
- generateConditionKey: (conditionTypeName: string): string => {
- return conditionTypeName
- .toLowerCase()
- .replace(/\s+/g, '_')
- .replace(/[^\w]/g, '')
- },
-
- // Проверка, является ли строка валидным UUID
- isValidUUID: (str: string): boolean => {
- const uuidRegex =
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
- return uuidRegex.test(str)
- },
-}
diff --git a/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx b/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx
index b3c8781..fcdcddc 100644
--- a/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx
+++ b/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx
@@ -9,7 +9,6 @@ import { VerificationConditionsEditor } from './VerificationConditionsEditor'
import { VerificationConditionsPreview } from './VerificationConditionsPreview'
import { VerificationConditionsRender } from './VerificationConditionsRender'
-// Форматирование даты как в функции ДАТАПРОТОКОЛА
const formatVerificationDate = (dateStr: string): string => {
try {
const months = [
diff --git a/src/feature/excelEngine/excelEngine.ts b/src/feature/excelEngine/excelEngine.ts
index 3185ee2..3ed9e7f 100644
--- a/src/feature/excelEngine/excelEngine.ts
+++ b/src/feature/excelEngine/excelEngine.ts
@@ -4,13 +4,12 @@ import {
VOLATILE_FUNCTIONS,
} from './functions'
-// Регулярные выражения для ссылок
// Ограничиваем поиск ячеек диапазоном A1-Z90
-const QUALIFIED_REF_RE = /\b[A-Za-z0-9_]+\b/g // SheetName!A1 до SheetName!Z90
-const LOCAL_REF_RE = /\b[A-Z]([1-9]|[1-8][0-9]|90)\b/g // A1 до Z90
-const RANGE_RE = /\b[A-Z]([1-9]|[1-8][0-9]|90):[A-Z]([1-9]|[1-8][0-9]|90)\b/g // A1:B2, D5:D7, etc. в диапазоне A1-Z90
+const QUALIFIED_REF_RE = /\b[A-Za-z0-9_]+\b/g
+const LOCAL_REF_RE = /\b[A-Z]([1-9]|[1-8][0-9]|90)\b/g
+const RANGE_RE = /\b[A-Z]([1-9]|[1-8][0-9]|90):[A-Z]([1-9]|[1-8][0-9]|90)\b/g
const QUALIFIED_RANGE_RE =
- /\b[A-Za-z0-9_]+:[A-Z]([1-9]|[1-8][0-9]|90)\b/g // SheetName!A1:B2 в диапазоне A1-Z90
+ /\b[A-Za-z0-9_]+:[A-Z]([1-9]|[1-8][0-9]|90)\b/g
// Типы для значений ячеек
export type CellValue = string | number | boolean | null | undefined
@@ -598,10 +597,6 @@ export class Workbook {
}
}
- removeFunction(name: string): void {
- delete this._functions[name.toUpperCase()]
- }
-
getFunctions(): Record {
return this._functions
}
@@ -641,14 +636,6 @@ export class Workbook {
// Утилиты для работы с координатами ячеек
export class CellUtils {
- static columnToIndex(column: string): number {
- let result = 0
- for (let i = 0; i < column.length; i++) {
- result = result * 26 + (column.charCodeAt(i) - 65 + 1)
- }
- return result - 1
- }
-
static indexToColumn(index: number): string {
let result = ''
while (index >= 0) {
@@ -658,17 +645,6 @@ export class CellUtils {
return result
}
- static parseCellName(cellName: string): { column: string; row: number } {
- const match = cellName.match(/^([A-Z]+)(\d+)$/)
- if (!match) {
- throw new Error(`Invalid cell name: ${cellName}`)
- }
- return {
- column: match[1],
- row: parseInt(match[2], 10),
- }
- }
-
static createCellName(column: string | number, row: number): string {
const col =
typeof column === 'number' ? CellUtils.indexToColumn(column) : column
diff --git a/src/hook/useFileQueries.ts b/src/hook/useFileQueries.ts
index e53c389..45a1020 100644
--- a/src/hook/useFileQueries.ts
+++ b/src/hook/useFileQueries.ts
@@ -1,10 +1,5 @@
-import {
- getFileData,
- getFilesByTemplateId,
- getLatestFileForTemplate,
- parseExcelFile,
-} from '@/service/fileApiService'
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+import { getFileData, getLatestFileForTemplate } from '@/service/fileApiService'
+import { useQuery } from '@tanstack/react-query'
// Ключи для React Query
export const fileKeys = {
@@ -16,17 +11,6 @@ export const fileKeys = {
data: (fileId: string) => [...fileKeys.all, 'data', fileId] as const,
}
-// Хук для получения файлов по template_id с кешированием
-export function useFilesByTemplate(templateId: string | undefined) {
- return useQuery({
- queryKey: fileKeys.byTemplate(templateId || ''),
- queryFn: () => getFilesByTemplateId(templateId!),
- enabled: !!templateId,
- staleTime: 5 * 60 * 1000, // 5 минут
- gcTime: 10 * 60 * 1000, // 10 минут кеш
- })
-}
-
// Хук для получения последнего файла для шаблона
export function useLatestFileForTemplate(templateId: string | undefined) {
return useQuery({
@@ -48,38 +32,3 @@ export function useFileData(fileId: string | undefined) {
gcTime: 30 * 60 * 1000, // 30 минут кеш
})
}
-
-// Хук для загрузки Excel файла
-export function useParseExcelFile() {
- const queryClient = useQueryClient()
-
- return useMutation({
- mutationFn: ({ file, templateId }: { file: File; templateId: string }) =>
- parseExcelFile(file, templateId),
- onSuccess: (data, variables) => {
- // Инвалидируем связанные кеши после успешной загрузки
- queryClient.invalidateQueries({
- queryKey: fileKeys.byTemplate(variables.templateId),
- })
- queryClient.invalidateQueries({
- queryKey: fileKeys.latest(variables.templateId),
- })
-
- // Сохраняем данные файла в кеш
- queryClient.setQueryData(fileKeys.data(data.fileId), data)
- },
- })
-}
-
-// Хук для предзагрузки данных файла
-export function usePrefetchFileData() {
- const queryClient = useQueryClient()
-
- return (fileId: string) => {
- queryClient.prefetchQuery({
- queryKey: fileKeys.data(fileId),
- queryFn: () => getFileData(fileId),
- staleTime: 10 * 60 * 1000,
- })
- }
-}
diff --git a/src/hook/useMultiSheetEngine.ts b/src/hook/useMultiSheetEngine.ts
index 88bf628..699ecba 100644
--- a/src/hook/useMultiSheetEngine.ts
+++ b/src/hook/useMultiSheetEngine.ts
@@ -69,14 +69,7 @@ export function useMultiSheetEngine({
if (!workbookRef.current) return
// Предотвращаем случайную очистку данных при смене вкладки
- if (document.hidden) {
- if (process.env.NODE_ENV !== 'production') {
- console.log(
- '⚠️ useMultiSheetEngine: предотвращаем загрузку данных когда страница скрыта'
- )
- }
- return
- }
+ if (document.hidden) return
// Создаем ключ для проверки изменений данных
const dataKey = JSON.stringify(initialData)
@@ -85,21 +78,10 @@ export function useMultiSheetEngine({
if (
loadedDataRef.current === dataKey &&
Object.keys(initialData).length > 0
- ) {
- if (process.env.NODE_ENV !== 'production') {
- console.log('⏭️ useMultiSheetEngine: данные уже загружены, пропускаем')
- }
+ )
return
- }
if (Object.keys(initialData).length > 0) {
- if (process.env.NODE_ENV !== 'production') {
- console.log(
- '🔄 useMultiSheetEngine: загрузка initialData:',
- Object.keys(initialData)
- )
- }
-
// Очищаем только те листы, для которых есть новые данные
for (const sheetName of Object.keys(initialData)) {
const sheet = workbookRef.current.getSheet(sheetName)
@@ -117,42 +99,6 @@ export function useMultiSheetEngine({
}
}, [initialData, safeRecalc])
- // Синхронизация всех листов из движка
- // const syncAllSheetsFromEngine = useCallback(() => {
- // if (!workbookRef.current || isRecalculatingRef.current) return
-
- // const newSheetCells: Record = {}
-
- // sheets.forEach((sheetConfig) => {
- // const sheet = workbookRef.current!.getSheet(sheetConfig.name)
- // if (!sheet) return
-
- // const cells = Array(sheetConfig.rows)
- // .fill(null)
- // .map(() =>
- // Array(sheetConfig.cols)
- // .fill(null)
- // .map(() => ({ value: '', isSelected: false })),
- // )
-
- // // Получаем все ячейки из движка
- // for (let row = 0; row < sheetConfig.rows; row++) {
- // for (let col = 0; col < sheetConfig.cols; col++) {
- // const cellName = columnToLabel(col) + (row + 1)
- // const cell = sheet.getCell(cellName)
-
- // if (cell.raw !== null && cell.raw !== undefined) {
- // cells[row][col] = String(cell.raw)
- // }
- // }
- // }
-
- // newSheetCells[sheetConfig.name] = cells
- // })
-
- // // setSheetCells(newSheetCells) // Удалено, так как sheetCells убрано
- // }, [sheets])
-
// Отложенный пересчет с дебаунсом
const debouncedRecalc = useCallback(() => {
if (!workbookRef.current || isRecalculatingRef.current) return
@@ -276,15 +222,6 @@ export function useMultiSheetEngine({
const setTemplateData = useCallback(
(data: Record>) => {
templateDataRef.current = JSON.parse(JSON.stringify(data)) // Глубокая копия
- if (process.env.NODE_ENV !== 'production') {
- console.log('📋 Установлены базовые данные шаблона:', {
- sheets: Object.keys(templateDataRef.current),
- cellCount: Object.values(templateDataRef.current).reduce(
- (total, sheet) => total + Object.keys(sheet).length,
- 0
- ),
- })
- }
},
[]
)
@@ -333,16 +270,6 @@ export function useMultiSheetEngine({
if (normalizedCurrent !== normalizedTemplate) {
// Если текущего значения нет (ячейка очищена), отправляем ''
modifiedSheet[cellAddress] = currentVal
- if (process.env.NODE_ENV !== 'production') {
- console.log(
- `🔧 Пользовательское изменение ${sheetName}:${cellAddress}:`,
- {
- template: normalizedTemplate,
- current: normalizedCurrent,
- value: currentVal,
- }
- )
- }
}
})
@@ -351,22 +278,6 @@ export function useMultiSheetEngine({
}
})
- if (process.env.NODE_ENV !== 'production') {
- const totalModified = Object.values(modifiedData).reduce(
- (total, sheet) => total + Object.keys(sheet).length,
- 0
- )
- console.log(`📊 Найдено ${totalModified} измененных ячеек:`, {
- sheets: Object.keys(modifiedData),
- details: Object.fromEntries(
- Object.entries(modifiedData).map(([sheetName, cells]) => [
- sheetName,
- Object.keys(cells).length,
- ])
- ),
- })
- }
-
return modifiedData
}, [])
@@ -405,7 +316,6 @@ export function useMultiSheetEngine({
}, [])
return {
- // sheetCells убран
setCellValue,
setCellValueWithoutRecalc,
debouncedRecalc,
diff --git a/src/hook/useSheetQueries.ts b/src/hook/useSheetQueries.ts
index 24b44cb..599a23f 100644
--- a/src/hook/useSheetQueries.ts
+++ b/src/hook/useSheetQueries.ts
@@ -1,7 +1,5 @@
import {
createSheet,
- getSheet,
- getSheets,
getSheetsByTemplate,
patchSheet,
type ApiSheet,
@@ -18,16 +16,6 @@ export const sheetKeys = {
detail: (sheetId: string) => [...sheetKeys.all, 'detail', sheetId] as const,
}
-// Хук для получения всех листов
-export function useSheets() {
- return useQuery({
- queryKey: sheetKeys.all,
- queryFn: getSheets,
- staleTime: 2 * 60 * 1000, // 2 минуты
- gcTime: 10 * 60 * 1000, // 10 минут кеш
- })
-}
-
// Хук для получения листов по template_id с кешированием
export function useSheetsByTemplate(templateId: string | undefined) {
return useQuery({
@@ -40,17 +28,6 @@ export function useSheetsByTemplate(templateId: string | undefined) {
})
}
-// Хук для получения конкретного листа
-export function useSheet(sheetId: string | undefined) {
- return useQuery({
- queryKey: sheetKeys.detail(sheetId || ''),
- queryFn: () => getSheet(sheetId!),
- enabled: !!sheetId,
- staleTime: 5 * 60 * 1000, // 5 минут
- gcTime: 10 * 60 * 1000, // 10 минут кеш
- })
-}
-
// Хук для создания листа
export function useCreateSheet() {
const queryClient = useQueryClient()
@@ -149,48 +126,3 @@ export function usePatchSheet() {
},
})
}
-
-// Хук для массового обновления листов (для автосохранения)
-export function useBatchUpdateSheets() {
- const queryClient = useQueryClient()
-
- return useMutation({
- mutationFn: async (
- updates: Array<{
- sheetId: string
- data: PatchSheetInput
- templateId: string
- }>
- ) => {
- // Выполняем все обновления параллельно
- const results = await Promise.allSettled(
- updates.map(({ sheetId, data }) => patchSheet(sheetId, data))
- )
-
- return results
- },
- onSuccess: (results, variables) => {
- // Обновляем кеши для всех затронутых шаблонов
- const templateIds = new Set(variables.map(v => v.templateId))
-
- templateIds.forEach(templateId => {
- queryClient.invalidateQueries({
- queryKey: sheetKeys.byTemplate(templateId),
- })
- })
- },
- })
-}
-
-// Хук для предзагрузки листов шаблона
-export function usePrefetchSheetsByTemplate() {
- const queryClient = useQueryClient()
-
- return (templateId: string) => {
- queryClient.prefetchQuery({
- queryKey: sheetKeys.byTemplate(templateId),
- queryFn: () => getSheetsByTemplate(templateId),
- staleTime: 1 * 60 * 1000,
- })
- }
-}
diff --git a/src/hook/useSpreadsheetEngine.ts b/src/hook/useSpreadsheetEngine.ts
deleted file mode 100644
index 6e07fa7..0000000
--- a/src/hook/useSpreadsheetEngine.ts
+++ /dev/null
@@ -1,335 +0,0 @@
-import { useCallback, useEffect, useRef, useState } from 'react'
-import {
- CellUtils,
- CellValue,
- Workbook,
-} from '../feature/excelEngine/excelEngine'
-
-interface CellData {
- value: string
- isSelected: boolean
-}
-
-interface UseSpreadsheetEngineProps {
- rows?: number
- cols?: number
- sheetName?: string
- initialData?: string[][]
-}
-
-export function useSpreadsheetEngine({
- rows = 50,
- cols = 20,
- sheetName = 'Sheet1',
- initialData,
-}: UseSpreadsheetEngineProps = {}) {
- const workbookRef = useRef(null)
- const [cells, setCells] = useState(() => {
- return Array(rows)
- .fill(null)
- .map(() =>
- Array(cols)
- .fill(null)
- .map(() => ({ value: '', isSelected: false }))
- )
- })
- const [isCalculating, setIsCalculating] = useState(false)
-
- // Инициализация workbook
- useEffect(() => {
- if (!workbookRef.current) {
- workbookRef.current = new Workbook()
- workbookRef.current.addSheet(sheetName)
- // addCustomFunctions(workbookRef.current)
- }
-
- // Загружаем начальные данные если они есть
- if (initialData) {
- loadDataFromArray(initialData)
- }
- }, [sheetName, initialData])
-
- // Конвертация данных из движка в React состояние
- const syncFromEngine = useCallback(() => {
- if (!workbookRef.current) return
-
- const sheet = workbookRef.current.getSheet(sheetName)
- if (!sheet) return
-
- const newCells = Array(rows)
- .fill(null)
- .map(() =>
- Array(cols)
- .fill(null)
- .map(() => ({ value: '', isSelected: false }))
- )
-
- // Получаем все ячейки из движка
- for (let row = 0; row < rows; row++) {
- for (let col = 0; col < cols; col++) {
- const cellName = CellUtils.createCellName(col, row + 1)
- const cell = sheet.getCell(cellName)
-
- // Показываем сырое значение (raw) в UI, а не вычисленное
- if (cell.raw !== null && cell.raw !== undefined) {
- newCells[row][col].value = String(cell.raw)
- }
- }
- }
-
- setCells(newCells)
- }, [rows, cols, sheetName])
-
- // Установка значения ячейки
- const setCellValue = useCallback(
- (row: number, col: number, value: string) => {
- if (!workbookRef.current) return
-
- const cellName = CellUtils.createCellName(col, row + 1)
- const qualifiedName = `${sheetName}!${cellName}`
-
- setIsCalculating(true)
-
- try {
- // Конвертируем пустую строку в null
- const cellValue: CellValue = value === '' ? null : value
-
- // Если значение начинается с =, то это формула
- if (typeof cellValue === 'string' && cellValue.startsWith('=')) {
- workbookRef.current.setCell(qualifiedName, cellValue)
- } else {
- // Пытаемся конвертировать в число, если возможно
- const numValue = Number(cellValue)
- if (!isNaN(numValue) && cellValue !== null && cellValue !== '') {
- workbookRef.current.setCell(qualifiedName, numValue)
- } else {
- workbookRef.current.setCell(qualifiedName, cellValue)
- }
- }
-
- // Пересчитываем все формулы
- workbookRef.current.recalc()
-
- // Синхронизируем с React состоянием
- syncFromEngine()
- } catch (error) {
- console.error('Error setting cell value:', error)
- } finally {
- setIsCalculating(false)
- }
- },
- [sheetName, syncFromEngine]
- )
-
- // Получение вычисленного значения ячейки
- const getCellValue = useCallback(
- (row: number, col: number): CellValue => {
- if (!workbookRef.current) return null
-
- const cellName = CellUtils.createCellName(col, row + 1)
- const qualifiedName = `${sheetName}!${cellName}`
-
- try {
- return workbookRef.current.getValue(qualifiedName)
- } catch (error) {
- console.error('Error getting cell value:', error)
- return '#ERROR!'
- }
- },
- [sheetName]
- )
-
- // Получение сырого значения ячейки (для отображения в строке формул)
- const getCellRawValue = useCallback(
- (row: number, col: number): string => {
- if (!workbookRef.current) return ''
-
- const cellName = CellUtils.createCellName(col, row + 1)
- const sheet = workbookRef.current.getSheet(sheetName)
-
- if (!sheet) return ''
-
- const cell = sheet.getCell(cellName)
- return cell.raw !== null && cell.raw !== undefined ? String(cell.raw) : ''
- },
- [sheetName]
- )
-
- // Загрузка данных из массива
- const loadDataFromArray = useCallback(
- (data: string[][]) => {
- if (!workbookRef.current) return
-
- const sheet = workbookRef.current.getSheet(sheetName)
- if (!sheet) return
-
- setIsCalculating(true)
-
- try {
- // Очищаем лист
- sheet.cells.clear()
-
- // Загружаем данные
- for (let row = 0; row < data.length && row < rows; row++) {
- for (let col = 0; col < data[row].length && col < cols; col++) {
- const value = data[row][col]
- if (value !== null && value !== undefined && value !== '') {
- const cellName = CellUtils.createCellName(col, row + 1)
- sheet.setCell(cellName, value)
- }
- }
- }
-
- // Пересчитываем все формулы
- workbookRef.current.recalc()
-
- // Синхронизируем с React состоянием
- syncFromEngine()
- } catch (error) {
- console.error('Error loading data:', error)
- } finally {
- setIsCalculating(false)
- }
- },
- [rows, cols, sheetName, syncFromEngine]
- )
-
- // Экспорт данных в массив (для сохранения)
- const exportToArray = useCallback((): string[][] => {
- if (!workbookRef.current) return []
-
- const sheet = workbookRef.current.getSheet(sheetName)
- if (!sheet) return []
-
- const result: string[][] = Array(rows)
- .fill(null)
- .map(() => Array(cols).fill(''))
-
- for (let row = 0; row < rows; row++) {
- for (let col = 0; col < cols; col++) {
- const cellName = CellUtils.createCellName(col, row + 1)
- const cell = sheet.getCell(cellName)
-
- if (cell.raw !== null && cell.raw !== undefined) {
- result[row][col] = String(cell.raw)
- }
- }
- }
-
- return result
- }, [rows, cols, sheetName])
-
- // Экспорт в JSON для бэкенда
- const exportToJSON = useCallback((): string => {
- if (!workbookRef.current) return '{}'
- return JSON.stringify(workbookRef.current.toJSON())
- }, [])
-
- // Импорт из JSON с бэкенда
- const importFromJSON = useCallback(
- (jsonData: string) => {
- if (!workbookRef.current) return
-
- setIsCalculating(true)
-
- try {
- const data = JSON.parse(jsonData)
- workbookRef.current.fromJSON(data)
- // addCustomFunctions(workbookRef.current)
- workbookRef.current.recalc()
- syncFromEngine()
- } catch (error) {
- console.error('Error importing from JSON:', error)
- } finally {
- setIsCalculating(false)
- }
- },
- [syncFromEngine]
- )
-
- // Добавление пользовательской функции
- const addFunction = useCallback(
- (name: string, func: (...args: any[]) => any) => {
- if (!workbookRef.current) return
- workbookRef.current.addFunction(name, func)
- },
- []
- )
-
- // Пересчёт всех формул
- const recalculate = useCallback(() => {
- if (!workbookRef.current) return
-
- setIsCalculating(true)
-
- try {
- workbookRef.current.recalc()
- syncFromEngine()
- } catch (error) {
- console.error('Error recalculating:', error)
- } finally {
- setIsCalculating(false)
- }
- }, [syncFromEngine])
-
- // Получение информации о зависимостях ячейки
- const getCellDependencies = useCallback(
- (row: number, col: number) => {
- if (!workbookRef.current) return { precedents: [], dependents: [] }
-
- const cellName = CellUtils.createCellName(col, row + 1)
- const sheet = workbookRef.current.getSheet(sheetName)
-
- if (!sheet) return { precedents: [], dependents: [] }
-
- const cell = sheet.getCell(cellName)
-
- return {
- precedents: Array.from(cell.precedents),
- dependents: Array.from(cell.dependents),
- }
- },
- [sheetName]
- )
-
- // Получение статистики workbook
- const getWorkbookStats = useCallback(() => {
- if (!workbookRef.current) return { sheets: 0, cells: 0, formulas: 0 }
-
- let totalCells = 0
- let totalFormulas = 0
-
- for (const sheet of workbookRef.current.sheets.values()) {
- totalCells += sheet.cells.size
-
- for (const cell of sheet.cells.values()) {
- if (typeof cell.raw === 'string' && cell.raw.startsWith('=')) {
- totalFormulas++
- }
- }
- }
-
- return {
- sheets: workbookRef.current.sheets.size,
- cells: totalCells,
- formulas: totalFormulas,
- }
- }, [])
-
- return {
- cells,
- setCellValue,
- getCellValue,
- getCellRawValue,
- loadDataFromArray,
- exportToArray,
- exportToJSON,
- importFromJSON,
- addFunction,
- recalculate,
- getCellDependencies,
- getWorkbookStats,
- isCalculating,
- workbook: workbookRef.current,
- }
-}