diff --git a/.eslintrc.js b/.eslintrc.js index b744197..008f6e7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,7 +31,7 @@ module.exports = { pathGroups: [ { pattern: 'react', group: 'builtin' }, { pattern: '~shared/**', group: 'internal', position: 'before' }, - { pattern: '~entitiy/**', group: 'internal', position: 'before' }, + { pattern: '~entity/**', group: 'internal', position: 'before' }, { pattern: '~feature/**', group: 'internal', position: 'before' }, { pattern: '~widget/**', group: 'internal', position: 'before' }, { pattern: '~page/**', group: 'internal', position: 'before' }, diff --git a/database-schema.sql b/database-schema.sql deleted file mode 100644 index b8c77ab..0000000 --- a/database-schema.sql +++ /dev/null @@ -1,90 +0,0 @@ --- Схема БД для элемента "Условия поверки" - --- Таблица лабораторий -CREATE TABLE laboratories ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - code VARCHAR(50) UNIQUE NOT NULL, -- Уникальный код лаборатории - description TEXT, - address TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Таблица типов условий поверки (температура, влажность, давление и т.д.) -CREATE TABLE condition_types ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) NOT NULL, -- "Температура", "Влажность", "Давление" - unit VARCHAR(20), -- "°C", "%", "кПа" - data_type VARCHAR(20) DEFAULT 'number', -- number, string, boolean - description TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Связь лаборатории с типами условий (какие условия отслеживает каждая лаборатория) -CREATE TABLE laboratory_condition_types ( - id SERIAL PRIMARY KEY, - laboratory_id INTEGER REFERENCES laboratories(id) ON DELETE CASCADE, - condition_type_id INTEGER REFERENCES condition_types(id) ON DELETE CASCADE, - is_required BOOLEAN DEFAULT false, -- Обязательно ли это условие для данной лаборатории - default_value VARCHAR(100), -- Значение по умолчанию - min_value DECIMAL(10,3), -- Минимальное значение (для валидации) - max_value DECIMAL(10,3), -- Максимальное значение (для валидации) - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(laboratory_id, condition_type_id) -); - --- Таблица для сохранения условий по дням -CREATE TABLE daily_conditions ( - id SERIAL PRIMARY KEY, - laboratory_id INTEGER REFERENCES laboratories(id) ON DELETE CASCADE, - date DATE NOT NULL, - conditions JSONB NOT NULL, -- JSON объект с условиями: {"temperature": 23.5, "humidity": 45.2, "pressure": 101.3} - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - created_by VARCHAR(100), -- Кто создал запись - UNIQUE(laboratory_id, date) -- Одна запись условий на день для лаборатории -); - --- Индексы для оптимизации -CREATE INDEX idx_daily_conditions_lab_date ON daily_conditions(laboratory_id, date); -CREATE INDEX idx_daily_conditions_date ON daily_conditions(date); -CREATE INDEX idx_laboratory_condition_types_lab ON laboratory_condition_types(laboratory_id); - --- Вставка начальных данных - --- Типы условий -INSERT INTO condition_types (name, unit, data_type, description) VALUES -('Температура', '°C', 'number', 'Температура окружающей среды'), -('Влажность', '%', 'number', 'Относительная влажность воздуха'), -('Атмосферное давление', 'кПа', 'number', 'Атмосферное давление'), -('Давление среды', 'МПа', 'number', 'Давление рабочей среды'), -('Напряжение сети', 'В', 'number', 'Напряжение питающей сети'), -('Частота сети', 'Гц', 'number', 'Частота питающей сети'); - --- Примеры лабораторий -INSERT INTO laboratories (name, code, description) VALUES -('Лаборатория температуры', 'TEMP_LAB', 'Лаборатория поверки температурных приборов'), -('Лаборатория давления', 'PRESS_LAB', 'Лаборатория поверки приборов давления'), -('Электрическая лаборатория', 'ELEC_LAB', 'Лаборатория поверки электрических приборов'); - --- Настройка условий для лабораторий --- Лаборатория температуры -INSERT INTO laboratory_condition_types (laboratory_id, condition_type_id, is_required, default_value, min_value, max_value) VALUES -(1, 1, true, '20', 15, 30), -- Температура -(1, 2, true, '50', 30, 80), -- Влажность -(1, 3, false, '101.3', 80, 120); -- Атмосферное давление - --- Лаборатория давления -INSERT INTO laboratory_condition_types (laboratory_id, condition_type_id, is_required, default_value, min_value, max_value) VALUES -(2, 1, true, '23', 18, 28), -- Температура -(2, 2, false, '45', 20, 70), -- Влажность -(2, 3, true, '101.3', 90, 110), -- Атмосферное давление -(2, 4, true, '0.1', 0, 100); -- Давление среды - --- Электрическая лаборатория -INSERT INTO laboratory_condition_types (laboratory_id, condition_type_id, is_required, default_value, min_value, max_value) VALUES -(3, 1, true, '22', 20, 25), -- Температура -(3, 2, true, '60', 40, 70), -- Влажность -(3, 5, true, '220', 198, 242), -- Напряжение сети -(3, 6, true, '50', 49.5, 50.5); -- Частота сети \ No newline at end of file diff --git a/package.json b/package.json index a612682..20135b9 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "react-redux": "^8.1.1", "react-router-dom": "^6.14.1", "tailwindcss": "^3.3.2", + "ts-prune": "^0.10.3", "typescript": "^4.4.4", "vite": "^4.3.9" } diff --git a/src/app/App.tsx b/src/app/App.tsx index d56e3ce..969c6f0 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -2,8 +2,8 @@ import { FC, useEffect } from 'react' import { Navigate, Route, Routes } from 'react-router-dom' -import { CategoryProvider } from '@/entitiy/template/model/CategoryContext' -import { TemplateProvider } from '@/entitiy/template/model/TemplateContext' +import { CategoryProvider } from '@/entity/template/model/CategoryContext' +import { TemplateProvider } from '@/entity/template/model/TemplateContext' import { setGlobalToastContext, ToastProvider, diff --git a/src/app/index.tsx b/src/app/index.tsx index 84f5e47..921c41f 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -4,7 +4,7 @@ import { BrowserRouter } from 'react-router-dom' import { store } from './store' -import { initializeElementRegistry } from '@/entitiy/element/model/interface' +import { initializeElementRegistry } from '@/entity/element/model/interface' import App from './App' import './index.css' diff --git a/src/component/TemplateManager/CategoryFieldsSelector.tsx b/src/component/TemplateManager/CategoryFieldsSelector.tsx index 4aa9549..b4efb20 100644 --- a/src/component/TemplateManager/CategoryFieldsSelector.tsx +++ b/src/component/TemplateManager/CategoryFieldsSelector.tsx @@ -1,6 +1,6 @@ import { Input } from '@/component/ui/input' import { Label } from '@/component/ui/label' -import { useCategoryContext } from '@/entitiy/template/model/CategoryContext' +import { useCategoryContext } from '@/entity/template/model/CategoryContext' import { Attribute } from '@/type/template' import { AlertCircle } from 'lucide-react' import React from 'react' diff --git a/src/component/TemplateManager/ElementConstructor.tsx b/src/component/TemplateManager/ElementConstructor.tsx index 0c2af21..3eab68f 100644 --- a/src/component/TemplateManager/ElementConstructor.tsx +++ b/src/component/TemplateManager/ElementConstructor.tsx @@ -1,7 +1,7 @@ import { getAvailableElementTypes, getElementDefinition, -} from '@/entitiy/element/model/interface' +} from '@/entity/element/model/interface' import { useToast } from '@/lib/hooks/useToast' import { generateDefaultLayout } from '@/lib/utils' import { @@ -47,6 +47,14 @@ import { Separator } from '../ui/separator' import { ElementFormulaBar } from './ElementFormulaBar' import { VisualLayoutEditor } from './VisualLayoutEditor' +export function getDefaultLayoutSettings(): FormLayoutSettings { + return { + gridSize: 20, + showGrid: true, + snapToGrid: true, + } +} + interface ElementFormProps { formData: Partial setFormData: React.Dispatch>> diff --git a/src/component/TemplateManager/TemplateEditor.tsx b/src/component/TemplateManager/TemplateEditor.tsx index bf15580..d5e6426 100644 --- a/src/component/TemplateManager/TemplateEditor.tsx +++ b/src/component/TemplateManager/TemplateEditor.tsx @@ -1,4 +1,4 @@ -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { getFileData, getLatestFileForTemplate, diff --git a/src/component/TemplateManager/TemplateFilterSidebar.tsx b/src/component/TemplateManager/TemplateFilterSidebar.tsx index bf7c0c0..a4e14d9 100644 --- a/src/component/TemplateManager/TemplateFilterSidebar.tsx +++ b/src/component/TemplateManager/TemplateFilterSidebar.tsx @@ -9,7 +9,7 @@ import { SidebarHeader, SidebarSeparator, } from '@/component/ui/sidebar' -import { useCategoryContext } from '@/entitiy/template/model/CategoryContext' +import { useCategoryContext } from '@/entity/template/model/CategoryContext' import { Template } from '@/type/template' import { Filter, X } from 'lucide-react' import React, { useMemo } from 'react' diff --git a/src/component/TemplateManager/TemplateManager.tsx b/src/component/TemplateManager/TemplateManager.tsx index bc75cb0..b05d98f 100644 --- a/src/component/TemplateManager/TemplateManager.tsx +++ b/src/component/TemplateManager/TemplateManager.tsx @@ -1,4 +1,4 @@ -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { Template } from '@/type/template' import { Circle, CircleCheck, FileText, Plus, Trash2 } from 'lucide-react' import React, { useEffect, useState } from 'react' diff --git a/src/component/TemplateManager/VisualLayoutEditor.tsx b/src/component/TemplateManager/VisualLayoutEditor.tsx index c2ba106..a3bd1c0 100644 --- a/src/component/TemplateManager/VisualLayoutEditor.tsx +++ b/src/component/TemplateManager/VisualLayoutEditor.tsx @@ -1,7 +1,7 @@ import { Edit, Grid, Move, Trash2 } from 'lucide-react' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Rnd } from 'react-rnd' -import { getElementDefinition } from '../../entitiy/element/model/interface' +import { getElementDefinition } from '../../entity/element/model/interface' import { ElementLayout, FormLayoutSettings, diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx b/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx deleted file mode 100644 index 02207d7..0000000 --- a/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { Button } from '@/component/ui/button' -import { Calendar } from '@/component/ui/calendar' -import { Card, CardContent, CardHeader, CardTitle } from '@/component/ui/card' -import { Popover, PopoverContent, PopoverTrigger } from '@/component/ui/popover' -import { - AlertCircle, - Building2, - Calendar as CalendarIcon, - CheckCircle, - Plus, -} from 'lucide-react' -import { useState } from 'react' -import { - useDailyConditions, - useLaboratories, - useLaboratoryConditions, -} from './hooks' -import { VerificationConditionsConfig } from './types' - -interface VerificationConditionsPreviewProps { - config: VerificationConditionsConfig - onChange?: (config: VerificationConditionsConfig) => void -} - -export function VerificationConditionsPreview({ - config, - onChange, -}: VerificationConditionsPreviewProps) { - const [selectedDate, setSelectedDate] = useState( - config.selectedDate || new Date().toISOString().split('T')[0] - ) - const [calendarOpen, setCalendarOpen] = useState(false) - - const { data: laboratories = [] } = useLaboratories() - const { data: labData } = useLaboratoryConditions(config.selectedLaboratoryId) - const { data: dailyCondition, isLoading } = useDailyConditions( - config.selectedLaboratoryId, - selectedDate - ) - - const selectedLab = laboratories.find( - lab => lab.id === config.selectedLaboratoryId - ) - - const hasConditions = - dailyCondition && Object.keys(dailyCondition.conditions).length > 0 - - const handleDateChange = (date: string) => { - setSelectedDate(date) - if (onChange) { - onChange({ ...config, selectedDate: date }) - } - } - - const handleCalendarSelect = (date: Date | undefined) => { - if (date) { - const dateString = date.toISOString().split('T')[0] - setSelectedDate(dateString) - setCalendarOpen(false) - if (onChange) { - onChange({ ...config, selectedDate: dateString }) - } - } - } - - if (!config.selectedLaboratoryId) { - return ( - - -
- -

Лаборатория не выбрана

-
-
-
- ) - } - - if (!labData) { - return ( - - -
-
-

Загрузка...

-
- - - ) - } - - return ( - - - - - {selectedLab?.name || 'Лаборатория'} - - - - - {/* Выбор даты */} -
- - - - - - - - - -
- - {/* Статус и данные */} -
- {isLoading ? ( -
-
- Загрузка... -
- ) : hasConditions ? ( -
-
- - Данные внесены -
- -
- {labData.conditionTypes.map(conditionType => { - const conditionValue = - dailyCondition!.conditions[conditionType.id] - return ( -
- - {conditionType.name} - - - {conditionValue || '—'} - {conditionValue && conditionType.unit && ( - {conditionType.unit} - )} - -
- ) - })} -
-
- ) : ( -
-
- - Данные не внесены -
- - -
- )} -
- - - ) -} diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx b/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx deleted file mode 100644 index 4aa4063..0000000 --- a/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx +++ /dev/null @@ -1,446 +0,0 @@ -import { Button } from '@/component/ui/button' -import { Card, CardContent, CardHeader, CardTitle } from '@/component/ui/card' -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@/component/ui/dialog' -import { Input } from '@/component/ui/input' -import { Label } from '@/component/ui/label' -import { CellsBadge } from '@/entitiy/element/ui/cellsBadge' -import { - AlertCircle, - Building2, - Calendar as CalendarIcon, - CheckCircle, - Plus, - Settings, -} from 'lucide-react' -import { useEffect, useState } from 'react' -import { - useDailyConditions, - useLaboratories, - useLaboratoryConditions, -} from './hooks' -import { - VerificationConditionsConfig, - VerificationConditionsValue, -} from './types' - -// Утилиты для работы с localStorage -const getLocalStorageKey = (type: string, id: string) => `${type}_${id}` - -const saveToLocalStorage = (key: string, value: string) => { - try { - localStorage.setItem(key, value) - } catch (error) { - console.warn('Failed to save to localStorage:', error) - } -} - -const getFromLocalStorage = (key: string): string => { - try { - return localStorage.getItem(key) || '' - } catch (error) { - console.warn('Failed to get from localStorage:', error) - return '' - } -} - -interface VerificationConditionsRenderProps { - config: VerificationConditionsConfig & { id?: string } - value?: VerificationConditionsValue - onChange?: (value: VerificationConditionsValue) => void - onSave?: (value: VerificationConditionsValue) => Promise -} - -export function VerificationConditionsRender({ - config, - value, - onChange, - onSave, -}: VerificationConditionsRenderProps) { - // Инициализация даты с учетом localStorage - const getInitialDate = () => { - if (value?.date) return value.date - if (config.selectedDate) return config.selectedDate - - // Попытка загрузить из localStorage - if (config.id) { - const key = getLocalStorageKey('verification_conditions_date', config.id) - const savedDate = getFromLocalStorage(key) - if (savedDate) return savedDate - } - - return new Date().toISOString().split('T')[0] - } - - const [selectedDate, setSelectedDate] = useState(getInitialDate) - const [editDialogOpen, setEditDialogOpen] = useState(false) - const [editingConditions, setEditingConditions] = useState< - Record - >({}) - - const { data: laboratories = [] } = useLaboratories() - const { data: labData } = useLaboratoryConditions(config.selectedLaboratoryId) - const { - data: dailyCondition, - saveConditions, - isSaving, - } = useDailyConditions(config.selectedLaboratoryId, selectedDate) - - const selectedLab = laboratories.find( - lab => lab.id === config.selectedLaboratoryId - ) - - const hasConditions = - dailyCondition && Object.keys(dailyCondition.conditions).length > 0 - - // Инициализация данных при загрузке из БД, только если данные еще не заполнены - useEffect(() => { - if ( - dailyCondition && - hasConditions && - onChange && - (!value?.conditions || Object.keys(value.conditions).length === 0) - ) { - onChange({ - date: selectedDate, - conditions: dailyCondition.conditions, - }) - } - }, [dailyCondition, hasConditions, onChange, selectedDate, value?.conditions]) - - // Синхронизация selectedDate с внешним value.date - useEffect(() => { - if (value?.date && value.date !== selectedDate) { - setSelectedDate(value.date) - } - }, [value?.date, selectedDate]) - - // Инициализация значения если оно не установлено - useEffect(() => { - if (!value && onChange) { - onChange({ - date: selectedDate, - conditions: {}, - }) - } - }, [value, onChange, selectedDate]) - - const handleDateChange = (event: React.ChangeEvent) => { - const newDate = event.target.value - setSelectedDate(newDate) - - // Сохранение в localStorage - if (config.id) { - const key = getLocalStorageKey('verification_conditions_date', config.id) - saveToLocalStorage(key, newDate) - } - - // Обновляем значение даты в value - if (onChange) { - onChange({ - date: newDate, - conditions: value?.conditions || {}, - }) - } - } - - const handleOpenEditDialog = (isCreating = false) => { - if (isCreating) { - const emptyConditions: Record = {} - labData?.conditionTypes.forEach(conditionType => { - emptyConditions[conditionType.id] = '' - }) - setEditingConditions(emptyConditions) - } else { - setEditingConditions(dailyCondition?.conditions || {}) - } - setEditDialogOpen(true) - } - - const handleSaveConditions = async () => { - if (!config.selectedLaboratoryId || !selectedDate || !labData) return - - try { - await saveConditions(editingConditions) - - if (onChange) { - onChange({ - date: selectedDate, - conditions: editingConditions, - }) - } - - setEditDialogOpen(false) - } catch (error) { - console.error('Ошибка сохранения:', error) - } - } - - const handleConditionChange = (conditionId: string, value: string) => { - setEditingConditions(prev => ({ - ...prev, - [conditionId]: value, - })) - } - - if (!config.selectedLaboratoryId) { - return ( - - -
- -

Лаборатория не настроена

-
-
-
- ) - } - - if (!labData) { - return ( - - -
-
-

Загрузка...

-
- - - ) - } - - return ( -
- {/* Бейдж ячеек */} - - - - - - - - {selectedLab?.name || 'Лаборатория'} - - - - - - {/* Выбор даты */} -
- - -
- - {/* Статус и данные */} -
- {hasConditions ? ( -
-
-
- - Данные внесены -
- - - - - - - - - Условия поверки - - -
-
-
{selectedLab?.name}
-
- {new Date(selectedDate).toLocaleDateString('ru-RU')} -
-
- -
- {labData.conditionTypes.map(conditionType => ( -
- - - handleConditionChange( - conditionType.id, - e.target.value - ) - } - placeholder="Значение" - className="h-7 text-xs" - /> -
- ))} -
- -
- - -
-
-
-
-
- -
- {labData.conditionTypes.map(conditionType => { - const conditionValue = - dailyCondition!.conditions[conditionType.id] - return ( -
- - {conditionType.name} - - - {conditionValue || '—'} - {conditionValue && conditionType.unit && ( - {conditionType.unit} - )} - -
- ) - })} -
-
- ) : ( -
-
- - Данные не внесены -
- - - - - - - - - Условия поверки - - -
-
-
{selectedLab?.name}
-
- {new Date(selectedDate).toLocaleDateString('ru-RU')} -
-
- -
- {labData.conditionTypes.map(conditionType => ( -
- - - handleConditionChange( - conditionType.id, - e.target.value - ) - } - placeholder="Значение" - className="h-7 text-xs" - /> -
- ))} -
- -
- - -
-
-
-
-
- )} -
-
-
-
- ) -} diff --git a/src/entitiy/element/model/implementations/ButtonGroup.tsx b/src/entity/element/model/implementations/ButtonGroup.tsx similarity index 98% rename from src/entitiy/element/model/implementations/ButtonGroup.tsx rename to src/entity/element/model/implementations/ButtonGroup.tsx index df3bed8..b61f4b6 100644 --- a/src/entitiy/element/model/implementations/ButtonGroup.tsx +++ b/src/entity/element/model/implementations/ButtonGroup.tsx @@ -1,7 +1,7 @@ import { Button } from '@/component/ui/button' import { Input } from '@/component/ui/input' -import { ElementDefinition } from '@/entitiy/element/model/interface' -import { CellsBadge } from '@/entitiy/element/ui/cellsBadge' +import { ElementDefinition } from '@/entity/element/model/interface' +import { CellsBadge } from '@/entity/element/ui/cellsBadge' import { ElementOption } from '@/type/template' import { Plus, Square, Trash2 } from 'lucide-react' diff --git a/src/entitiy/element/model/implementations/SelectElement.tsx b/src/entity/element/model/implementations/SelectElement.tsx similarity index 98% rename from src/entitiy/element/model/implementations/SelectElement.tsx rename to src/entity/element/model/implementations/SelectElement.tsx index a964f17..f1eb754 100644 --- a/src/entitiy/element/model/implementations/SelectElement.tsx +++ b/src/entity/element/model/implementations/SelectElement.tsx @@ -8,8 +8,8 @@ import { SelectTrigger, SelectValue, } from '@/component/ui/select' -import { ElementDefinition } from '@/entitiy/element/model/interface' -import { CellsBadge } from '@/entitiy/element/ui/cellsBadge' +import { ElementDefinition } from '@/entity/element/model/interface' +import { CellsBadge } from '@/entity/element/ui/cellsBadge' import { useToast } from '@/lib/hooks/useToast' import { ElementOption } from '@/type/template' import { ChevronDown, Plus, Trash2 } from 'lucide-react' diff --git a/src/entitiy/element/model/implementations/StandardsElement/StandardsEditor.tsx b/src/entity/element/model/implementations/StandardsElement/StandardsEditor.tsx similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/StandardsEditor.tsx rename to src/entity/element/model/implementations/StandardsElement/StandardsEditor.tsx diff --git a/src/entitiy/element/model/implementations/StandardsElement/StandardsPreview.tsx b/src/entity/element/model/implementations/StandardsElement/StandardsPreview.tsx similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/StandardsPreview.tsx rename to src/entity/element/model/implementations/StandardsElement/StandardsPreview.tsx diff --git a/src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx b/src/entity/element/model/implementations/StandardsElement/StandardsSelector.tsx similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx rename to src/entity/element/model/implementations/StandardsElement/StandardsSelector.tsx diff --git a/src/entitiy/element/model/implementations/StandardsElement/api.ts b/src/entity/element/model/implementations/StandardsElement/api.ts similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/api.ts rename to src/entity/element/model/implementations/StandardsElement/api.ts diff --git a/src/entitiy/element/model/implementations/StandardsElement/definition.tsx b/src/entity/element/model/implementations/StandardsElement/definition.tsx similarity index 98% rename from src/entitiy/element/model/implementations/StandardsElement/definition.tsx rename to src/entity/element/model/implementations/StandardsElement/definition.tsx index 65ce418..196901a 100644 --- a/src/entitiy/element/model/implementations/StandardsElement/definition.tsx +++ b/src/entity/element/model/implementations/StandardsElement/definition.tsx @@ -1,8 +1,8 @@ import { Badge } from '@/component/ui/badge' import { Button } from '@/component/ui/button' import { Label } from '@/component/ui/label' -import { ElementDefinition } from '@/entitiy/element/model/interface' -import { CellsBadge } from '@/entitiy/element/ui/cellsBadge' +import { ElementDefinition } from '@/entity/element/model/interface' +import { CellsBadge } from '@/entity/element/ui/cellsBadge' import { CellTarget } from '@/type/template' import { FileText, Settings, Weight } from 'lucide-react' import { useEffect, useState } from 'react' diff --git a/src/entitiy/element/model/implementations/StandardsElement/hooks.ts b/src/entity/element/model/implementations/StandardsElement/hooks.ts similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/hooks.ts rename to src/entity/element/model/implementations/StandardsElement/hooks.ts diff --git a/src/entitiy/element/model/implementations/StandardsElement/types.ts b/src/entity/element/model/implementations/StandardsElement/types.ts similarity index 100% rename from src/entitiy/element/model/implementations/StandardsElement/types.ts rename to src/entity/element/model/implementations/StandardsElement/types.ts diff --git a/src/entitiy/element/model/implementations/TextElement.tsx b/src/entity/element/model/implementations/TextElement.tsx similarity index 99% rename from src/entitiy/element/model/implementations/TextElement.tsx rename to src/entity/element/model/implementations/TextElement.tsx index 111cfa8..8204fa5 100644 --- a/src/entitiy/element/model/implementations/TextElement.tsx +++ b/src/entity/element/model/implementations/TextElement.tsx @@ -1,7 +1,7 @@ import { Checkbox } from '@/component/ui/checkbox' import { Input } from '@/component/ui/input' -import { ElementDefinition } from '@/entitiy/element/model/interface' -import { CellsBadge } from '@/entitiy/element/ui/cellsBadge' +import { ElementDefinition } from '@/entity/element/model/interface' +import { CellsBadge } from '@/entity/element/ui/cellsBadge' import { Plus, Save, SaveOff, Type } from 'lucide-react' import { useEffect, useState } from 'react' diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsEditor.tsx b/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsEditor.tsx similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/VerificationConditionsEditor.tsx rename to src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsEditor.tsx diff --git a/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx b/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx new file mode 100644 index 0000000..296f90f --- /dev/null +++ b/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsPreview.tsx @@ -0,0 +1,121 @@ +import { Button } from '@/component/ui/button' +import { Card, CardContent } from '@/component/ui/card' +import { + AlertCircle, + Building2, + Calendar as CalendarIcon, + Droplets, + Gauge, + Settings, + Thermometer, +} from 'lucide-react' +import { VerificationConditionsConfig } from './types' + +interface VerificationConditionsPreviewProps { + config: VerificationConditionsConfig + onChange?: (config: VerificationConditionsConfig) => void +} + +const ConditionSkeleton = ({ + index, + icon: Icon, + name, + unit, +}: { + index: number + icon: any + name: string + unit: string +}) => ( +
+
+ +
+
+
{name}
+
+
+ — {unit} +
+
+) + +// Моковые условия поверки +const mockConditions = [ + { name: 'Температура', unit: '°C', icon: Thermometer }, + { name: 'Влажность', unit: '%', icon: Droplets }, + { name: 'Давление', unit: 'кПа', icon: Gauge }, + { name: 'Освещенность', unit: 'лк', icon: AlertCircle }, + { name: 'Вибрация', unit: 'м/с²', icon: AlertCircle }, + { name: 'Магнитное поле', unit: 'А/м', icon: AlertCircle }, + { name: 'Электромагнитное поле', unit: 'В/м', icon: AlertCircle }, +] + +export function VerificationConditionsPreview({ + config, + onChange, +}: VerificationConditionsPreviewProps) { + const conditionsToShow = mockConditions.slice(0, 7) + const selectedDate = + config.selectedDate || new Date().toISOString().split('T')[0] + + if (!config.selectedLaboratoryId) { + return ( + + +
+ +

Лаборатория не выбрана

+
+
+
+ ) + } + + return ( +
+
+ {/* Информация о лаборатории и дате */} +
+ + Лаборатория +
+ + {new Date(selectedDate).toLocaleDateString('ru-RU')} +
+
+ + {/* Общий статус с кнопкой настроить */} +
+
+ + Данные не внесены +
+ + +
+ + {/* Моковые условия */} +
+ {conditionsToShow.map((condition, index) => ( + + ))} +
+
+
+ ) +} diff --git a/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx b/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx new file mode 100644 index 0000000..1ef4f85 --- /dev/null +++ b/src/entity/element/model/implementations/VerificationConditionsElement/VerificationConditionsRender.tsx @@ -0,0 +1,402 @@ +import { Button } from '@/component/ui/button' +import { Card, CardContent } from '@/component/ui/card' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/component/ui/dialog' +import { Input } from '@/component/ui/input' +import { Label } from '@/component/ui/label' +import { CellsBadge } from '@/entity/element/ui/cellsBadge' +import { + AlertCircle, + Building2, + Calendar as CalendarIcon, + CheckCircle, + Droplets, + Gauge, + Settings, + Thermometer, +} from 'lucide-react' +import { useEffect, useState } from 'react' +import { + useDailyConditions, + useLaboratories, + useLaboratoryConditions, +} from './hooks' +import { + VerificationConditionsConfig, + VerificationConditionsValue, +} from './types' + +// Утилиты для работы с localStorage +const getLocalStorageKey = (type: string, id: string) => `${type}_${id}` + +const saveToLocalStorage = (key: string, value: string) => { + try { + localStorage.setItem(key, value) + } catch (error) { + console.warn('Failed to save to localStorage:', error) + } +} + +const getFromLocalStorage = (key: string): string => { + try { + return localStorage.getItem(key) || '' + } catch (error) { + console.warn('Failed to get from localStorage:', error) + return '' + } +} + +// Иконки для разных типов условий +const getConditionIcon = (name: string) => { + const lowerName = name.toLowerCase() + if (lowerName.includes('температур')) return Thermometer + if (lowerName.includes('влажн')) return Droplets + if (lowerName.includes('давлен')) return Gauge + return AlertCircle +} + +const ConditionItem = ({ + name, + value, + unit, +}: { + name: string + value?: string + unit?: string +}) => { + const Icon = getConditionIcon(name) + + return ( +
+
+ +
+
+
{name}
+
+
+ {value || '—'} {unit || ''} +
+
+ ) +} + +interface VerificationConditionsRenderProps { + config: VerificationConditionsConfig & { id?: string } + value?: VerificationConditionsValue + onChange?: (value: VerificationConditionsValue) => void + onSave?: (value: VerificationConditionsValue) => Promise +} + +export function VerificationConditionsRender({ + config, + value, + onChange, + onSave, +}: VerificationConditionsRenderProps) { + // Инициализация даты с учетом localStorage + const getInitialDate = () => { + if (value?.date) return value.date + if (config.selectedDate) return config.selectedDate + + // Попытка загрузить из localStorage + if (config.id) { + const key = getLocalStorageKey('verification_conditions_date', config.id) + const savedDate = getFromLocalStorage(key) + if (savedDate) return savedDate + } + + return new Date().toISOString().split('T')[0] + } + + const [selectedDate, setSelectedDate] = useState(getInitialDate) + const [editDialogOpen, setEditDialogOpen] = useState(false) + const [editingConditions, setEditingConditions] = useState< + Record + >({}) + + const { data: laboratories = [] } = useLaboratories() + const { data: labData } = useLaboratoryConditions(config.selectedLaboratoryId) + const { + data: dailyCondition, + saveConditions, + isSaving, + } = useDailyConditions(config.selectedLaboratoryId, selectedDate) + + const selectedLab = laboratories.find( + lab => lab.id === config.selectedLaboratoryId + ) + + const hasConditions = + dailyCondition && Object.keys(dailyCondition.conditions).length > 0 + + // Инициализация данных при загрузке из БД, только если данные еще не заполнены + useEffect(() => { + if ( + dailyCondition && + hasConditions && + onChange && + (!value?.conditions || Object.keys(value.conditions).length === 0) + ) { + onChange({ + date: selectedDate, + conditions: dailyCondition.conditions, + }) + } + }, [dailyCondition, hasConditions, onChange, selectedDate, value?.conditions]) + + // Синхронизация selectedDate с внешним value.date + useEffect(() => { + if (value?.date && value.date !== selectedDate) { + setSelectedDate(value.date) + } + }, [value?.date, selectedDate]) + + // Инициализация значения если оно не установлено + useEffect(() => { + if (!value && onChange) { + onChange({ + date: selectedDate, + conditions: {}, + }) + } + }, [value, onChange, selectedDate]) + + const handleDateChange = (event: React.ChangeEvent) => { + const newDate = event.target.value + setSelectedDate(newDate) + + // Сохранение в localStorage + if (config.id) { + const key = getLocalStorageKey('verification_conditions_date', config.id) + saveToLocalStorage(key, newDate) + } + + // Обновляем значение даты в value + if (onChange) { + onChange({ + date: newDate, + conditions: value?.conditions || {}, + }) + } + } + + const handleOpenEditDialog = (isCreating = false) => { + if (isCreating) { + const emptyConditions: Record = {} + labData?.conditionTypes.forEach(conditionType => { + emptyConditions[conditionType.id] = '' + }) + setEditingConditions(emptyConditions) + } else { + setEditingConditions(dailyCondition?.conditions || {}) + } + setEditDialogOpen(true) + } + + const handleSaveConditions = async () => { + if (!config.selectedLaboratoryId || !selectedDate || !labData) return + + try { + await saveConditions(editingConditions) + + if (onChange) { + onChange({ + date: selectedDate, + conditions: editingConditions, + }) + } + + setEditDialogOpen(false) + } catch (error) { + console.error('Ошибка сохранения:', error) + } + } + + const handleConditionChange = (conditionId: string, value: string) => { + setEditingConditions(prev => ({ + ...prev, + [conditionId]: value, + })) + } + + if (!config.selectedLaboratoryId) { + return ( +
+ + + + +
+ +

Лаборатория не выбрана

+
+
+
+
+ ) + } + + if (!labData) { + return ( +
+ + + + +
+
+

Загрузка...

+
+ + +
+ ) + } + + return ( +
+ {/* Бейдж ячеек */} + + +
+
+ {/* Информация о лаборатории и дате */} +
+ + + {selectedLab?.name || 'Лаборатория'} + +
+ + +
+
+ + {/* Общий статус с кнопкой настроить */} +
+
+ {hasConditions ? ( + <> + + + Данные внесены + + + ) : ( + <> + + + Данные не внесены + + + )} +
+ + + + + + + + + Условия поверки + + +
+
+
{selectedLab?.name}
+
+ {new Date(selectedDate).toLocaleDateString('ru-RU')} +
+
+ +
+ {labData.conditionTypes.map(conditionType => ( +
+ + + handleConditionChange( + conditionType.id, + e.target.value + ) + } + placeholder="Значение" + className="h-7 text-xs" + /> +
+ ))} +
+ +
+ + +
+
+
+
+
+ + {/* Условия */} +
+ {labData.conditionTypes.map(conditionType => { + const conditionValue = + dailyCondition?.conditions[conditionType.id] + + return ( + + ) + })} +
+
+
+
+ ) +} diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/api.ts b/src/entity/element/model/implementations/VerificationConditionsElement/api.ts similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/api.ts rename to src/entity/element/model/implementations/VerificationConditionsElement/api.ts diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/definition.tsx b/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx similarity index 98% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/definition.tsx rename to src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx index a3d513f..b3c8781 100644 --- a/src/entitiy/element/model/implementations/VerificationConditionsElement/definition.tsx +++ b/src/entity/element/model/implementations/VerificationConditionsElement/definition.tsx @@ -1,4 +1,4 @@ -import { ElementDefinition } from '@/entitiy/element/model/interface' +import { ElementDefinition } from '@/entity/element/model/interface' import { CellTarget } from '@/type/template' import { Building2 } from 'lucide-react' import { diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/hooks.ts b/src/entity/element/model/implementations/VerificationConditionsElement/hooks.ts similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/hooks.ts rename to src/entity/element/model/implementations/VerificationConditionsElement/hooks.ts diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/index.ts b/src/entity/element/model/implementations/VerificationConditionsElement/index.ts similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/index.ts rename to src/entity/element/model/implementations/VerificationConditionsElement/index.ts diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/mockData.ts b/src/entity/element/model/implementations/VerificationConditionsElement/mockData.ts similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/mockData.ts rename to src/entity/element/model/implementations/VerificationConditionsElement/mockData.ts diff --git a/src/entitiy/element/model/implementations/VerificationConditionsElement/types.ts b/src/entity/element/model/implementations/VerificationConditionsElement/types.ts similarity index 100% rename from src/entitiy/element/model/implementations/VerificationConditionsElement/types.ts rename to src/entity/element/model/implementations/VerificationConditionsElement/types.ts diff --git a/src/entitiy/element/model/interface.ts b/src/entity/element/model/interface.ts similarity index 83% rename from src/entitiy/element/model/interface.ts rename to src/entity/element/model/interface.ts index 3d7d931..e5a9638 100644 --- a/src/entitiy/element/model/interface.ts +++ b/src/entity/element/model/interface.ts @@ -1,8 +1,8 @@ -import { buttonGroupDefinition } from '@/entitiy/element/model/implementations/ButtonGroup' -import { selectDefinition } from '@/entitiy/element/model/implementations/SelectElement' -import { standardsDefinition } from '@/entitiy/element/model/implementations/StandardsElement/definition' -import { textDefinition } from '@/entitiy/element/model/implementations/TextElement' -import { verificationConditionsElementDefinition as verificationConditionsDefinition } from '@/entitiy/element/model/implementations/VerificationConditionsElement/definition' +import { buttonGroupDefinition } from '@/entity/element/model/implementations/ButtonGroup' +import { selectDefinition } from '@/entity/element/model/implementations/SelectElement' +import { standardsDefinition } from '@/entity/element/model/implementations/StandardsElement/definition' +import { textDefinition } from '@/entity/element/model/implementations/TextElement' +import { verificationConditionsElementDefinition as verificationConditionsDefinition } from '@/entity/element/model/implementations/VerificationConditionsElement/definition' import { CellTarget } from '@/type/template' import { ReactNode } from 'react' diff --git a/src/entitiy/element/ui/cellsBadge.tsx b/src/entity/element/ui/cellsBadge.tsx similarity index 100% rename from src/entitiy/element/ui/cellsBadge.tsx rename to src/entity/element/ui/cellsBadge.tsx diff --git a/src/entitiy/element/ui/extraSettingsBadge.tsx b/src/entity/element/ui/extraSettingsBadge.tsx similarity index 100% rename from src/entitiy/element/ui/extraSettingsBadge.tsx rename to src/entity/element/ui/extraSettingsBadge.tsx diff --git a/src/entitiy/template/api/templateApiService.ts b/src/entity/template/api/templateApiService.ts similarity index 100% rename from src/entitiy/template/api/templateApiService.ts rename to src/entity/template/api/templateApiService.ts diff --git a/src/entitiy/template/model/CategoryContext.tsx b/src/entity/template/model/CategoryContext.tsx similarity index 100% rename from src/entitiy/template/model/CategoryContext.tsx rename to src/entity/template/model/CategoryContext.tsx diff --git a/src/entitiy/template/model/TemplateContext.tsx b/src/entity/template/model/TemplateContext.tsx similarity index 99% rename from src/entitiy/template/model/TemplateContext.tsx rename to src/entity/template/model/TemplateContext.tsx index cc4070c..fef19e3 100644 --- a/src/entitiy/template/model/TemplateContext.tsx +++ b/src/entity/template/model/TemplateContext.tsx @@ -10,7 +10,7 @@ import { patchTemplateApi, templateToCreateInput, templateToPatchInput, -} from '@/entitiy/template/api/templateApiService' +} from '@/entity/template/api/templateApiService' import { Template } from '@/type/template' import { QueryClient, diff --git a/src/lib/spreadsheet-engine/spreadsheet-engine.ts b/src/feature/excelEngine/excelEngine.ts similarity index 78% rename from src/lib/spreadsheet-engine/spreadsheet-engine.ts rename to src/feature/excelEngine/excelEngine.ts index 7e9305d..f00b6ed 100644 --- a/src/lib/spreadsheet-engine/spreadsheet-engine.ts +++ b/src/feature/excelEngine/excelEngine.ts @@ -14,10 +14,28 @@ const QUALIFIED_RANGE_RE = /\b[A-Za-z0-9_]+![A-Z]+[0-9]+:[A-Z]+[0-9]+\b/g // She export type CellValue = string | number | boolean | null | undefined // Утилитная функция для устранения машинной ошибки в числах с плавающей точкой +// Применяется только при финальном выводе значений function fixFloatingPointError(value: CellValue): CellValue { if (typeof value === 'number' && isFinite(value)) { - const factor = Math.pow(10, 14) - return Math.round(value * factor) / factor + // Проверяем, является ли число "почти целым" или "почти простой дробью" + const tolerance = 1e-10 + + // Проверка на целое число + if (Math.abs(value - Math.round(value)) < tolerance) { + return Math.round(value) + } + + // Проверка на простые дроби (до 6 знаков после запятой) + for (let precision = 1; precision <= 6; precision++) { + const factor = Math.pow(10, precision) + const rounded = Math.round(value * factor) / factor + if (Math.abs(value - rounded) < tolerance) { + return rounded + } + } + + // Для остальных случаев ограничиваем до разумного количества знаков + return Math.round(value * 1e10) / 1e10 } return value } @@ -66,6 +84,7 @@ export class Cell { return this.value } + // Убираем знак равенства const expr = this.raw.substring(1) // Проверяем, содержит ли формула волатильные функции @@ -166,13 +185,6 @@ export class Cell { // Заменяем функции Excel const functions = this.sheet.workbook.getFunctions() - // Отладка - if (expr.includes('ТЕСТ')) { - // console.log('Processing ТЕСТ function') - // console.log('Available functions:', Object.keys(functions)) - // console.log('Original code:', code) - } - for (const [funcName] of Object.entries(functions)) { // Экранируем специальные символы в названии функции для регулярного выражения const escapedFuncName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') @@ -193,10 +205,6 @@ export class Cell { } } - if (expr.includes('ТЕСТ')) { - // console.log('Final code:', code) - } - // Создаём контекст для выполнения const context = { __functions__: functions, @@ -249,18 +257,13 @@ export class Cell { } private evaluateExpression(expr: string, context: any): CellValue { - // Простая реализация eval для математических выражений и функций - // В реальном проекте лучше использовать специализированную библиотеку - // для парсинга и выполнения формул - try { // Создаём функцию с контекстом const func = new Function(...Object.keys(context), `return ${expr}`) const result = func(...Object.values(context)) - // Исправляем машинную ошибку в 16-м символе после запятой - return fixFloatingPointError(result) + return result } catch (error) { throw new Error(`Invalid expression: ${expr}`) } @@ -429,12 +432,133 @@ export class Workbook { } } + private topologicalSort(cells: Set): string[] { + const visited = new Set() + const visiting = new Set() + const result: string[] = [] + const hasCycle = new Set() + + const visit = (cellName: string): boolean => { + if (visiting.has(cellName)) { + // Обнаружена циклическая зависимость + hasCycle.add(cellName) + return false + } + + if (visited.has(cellName)) { + return true + } + + visiting.add(cellName) + + try { + const cell = this.getCell(cellName) + + // Посещаем всех предшественников (ячейки, от которых зависит текущая) + for (const precedent of cell.precedents) { + if (cells.has(precedent)) { + if (!visit(precedent)) { + hasCycle.add(cellName) + } + } + } + } catch (error) { + // Ячейка не существует или другая ошибка - игнорируем + } + + visiting.delete(cellName) + visited.add(cellName) + result.push(cellName) + + return true + } + + // Посещаем все ячейки + for (const cellName of cells) { + if (!visited.has(cellName)) { + visit(cellName) + } + } + + // Если обнаружены циклы, выводим предупреждение + if (hasCycle.size > 0) { + console.warn( + 'Обнаружены циклические зависимости в ячейках:', + Array.from(hasCycle) + ) + } + + return result + } + + private detectCycle(startCell: string): boolean { + const visited = new Set() + const visiting = new Set() + + const visit = (cellName: string): boolean => { + if (visiting.has(cellName)) { + return true // Цикл найден + } + + if (visited.has(cellName)) { + return false + } + + visiting.add(cellName) + + try { + const cell = this.getCell(cellName) + for (const precedent of cell.precedents) { + if (visit(precedent)) { + return true + } + } + } catch (error) { + // Ячейка не существует - игнорируем + } + + visiting.delete(cellName) + visited.add(cellName) + return false + } + + return visit(startCell) + } + recalc(): void { - // Пересчёт всех "грязных" ячеек - while (this._dirty.size > 0) { - const current = this._dirty.values().next().value! - this._dirty.delete(current) - this.getCell(current).evaluate() + if (this._dirty.size === 0) { + return + } + + // Выполняем топологическую сортировку для определения правильного порядка пересчета + const sortedCells = this.topologicalSort(this._dirty) + + // Очищаем множество "грязных" ячеек + this._dirty.clear() + + // Пересчитываем ячейки в правильном порядке + for (const cellName of sortedCells) { + try { + const cell = this.getCell(cellName) + + // Проверяем на циклические зависимости перед вычислением + if (this.detectCycle(cellName)) { + console.error( + `Циклическая зависимость обнаружена для ячейки ${cellName}` + ) + cell.value = '#CYCLE!' + } else { + cell.evaluate() + } + } catch (error) { + console.error(`Ошибка при пересчете ячейки ${cellName}:`, error) + try { + const cell = this.getCell(cellName) + cell.value = '#ERROR!' + } catch (getError) { + // Ячейка не существует - игнорируем + } + } } } @@ -444,7 +568,6 @@ export class Workbook { } const value = this.getCell(qualifiedName).value - // Исправляем машинную ошибку в 16-м символе после запятой для числовых значений return fixFloatingPointError(value) } diff --git a/src/lib/spreadsheet-engine/functions.ts b/src/feature/excelEngine/functions.ts similarity index 78% rename from src/lib/spreadsheet-engine/functions.ts rename to src/feature/excelEngine/functions.ts index eb46b37..8412f6e 100644 --- a/src/lib/spreadsheet-engine/functions.ts +++ b/src/feature/excelEngine/functions.ts @@ -5,13 +5,7 @@ export interface ExcelFunction { } // Волатильные функции - пересчитываются при любом изменении -export const VOLATILE_FUNCTIONS = new Set([ - 'СЛУЧ', - 'СЛУЧМЕЖДУ', - 'СЛУЧДР', - 'СЕЙЧАС', - 'СЕГОДНЯ', -]) +export const VOLATILE_FUNCTIONS = new Set(['СЛУЧ', 'СЛУЧМЕЖДУ', 'СЛУЧДР']) export const DEFAULT_FUNCTIONS: Record = { СЛУЧ: () => Math.random(), @@ -124,43 +118,4 @@ export const DEFAULT_FUNCTIONS: Record = { if (кратное === 0) return 0 return Math.round(число / кратное) * кратное }, - - // Функции даты и времени - СЕЙЧАС: () => Date.now(), - СЕГОДНЯ: () => { - const date = new Date() - date.setHours(0, 0, 0, 0) - return date.getTime() - }, - ДАТАПРОТОКОЛА: (дата: number | Date) => { - try { - const months = [ - '', - 'января', 'февраля', 'марта', - 'апреля', 'мая', 'июня', - 'июля', 'августа', 'сентября', - 'октября', 'ноября', 'декабря', - ] - let dateObj: Date - if (typeof дата === 'number') { - dateObj = new Date(дата) - } else if (дата instanceof Date) { - dateObj = дата - } else { - return 'Неверный формат даты' - } - const day = dateObj.getDate().toString().padStart(2, '0') - const month = months[dateObj.getMonth() + 1] - const year = dateObj.getFullYear() - return `${day} ${month} ${year}` - } catch (e) { - return 'Неверный формат даты' - } - }, - - // Отладочная функция - ТЕСТ: (значение: any = 'Функция работает!') => { - // console.log('ТЕСТ вызван с:', значение) - return значение - }, } diff --git a/src/hook/useMultiSheetEngine.ts b/src/hook/useMultiSheetEngine.ts index dd0857a..88bf628 100644 --- a/src/hook/useMultiSheetEngine.ts +++ b/src/hook/useMultiSheetEngine.ts @@ -1,8 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react' -import { - CellValue, - Workbook, -} from '../lib/spreadsheet-engine/spreadsheet-engine' +import { CellValue, Workbook } from '../feature/excelEngine/excelEngine' interface SheetConfig { name: string rows: number diff --git a/src/hook/useSpreadsheetEngine.ts b/src/hook/useSpreadsheetEngine.ts index 8e032e2..6e07fa7 100644 --- a/src/hook/useSpreadsheetEngine.ts +++ b/src/hook/useSpreadsheetEngine.ts @@ -3,7 +3,7 @@ import { CellUtils, CellValue, Workbook, -} from '../lib/spreadsheet-engine/spreadsheet-engine' +} from '../feature/excelEngine/excelEngine' interface CellData { value: string diff --git a/src/lib/cell-utils.ts b/src/lib/cell-utils.ts index ba28d2d..9dd92fb 100644 --- a/src/lib/cell-utils.ts +++ b/src/lib/cell-utils.ts @@ -1,8 +1,3 @@ -/** - * Унифицированные утилиты для работы с адресами ячеек Excel - * Заменяет дублированный код в разных частях приложения - */ - export interface CellAddress { column: string row: number @@ -25,23 +20,6 @@ export function columnToIndex(column: string): number { return result - 1 // zero-based } -/** - * Конвертирует числовой индекс в буквенный столбец - * 0 -> A, 1 -> B, ..., 25 -> Z, 26 -> AA, 27 -> AB, etc. - */ -export function indexToColumn(index: number): string { - let result = '' - let num = index + 1 // Convert to 1-based - - while (num > 0) { - num-- // Adjust for 0-based alphabet - result = String.fromCharCode(65 + (num % 26)) + result - num = Math.floor(num / 26) - } - - return result -} - /** * Парсит адрес ячейки в компоненты (столбец и строка) * "A1" -> { column: "A", row: 1 } @@ -71,82 +49,3 @@ export function cellAddressToCoordinates(address: string): CellCoordinates { col: columnToIndex(parsed.column), } } - -/** - * Конвертирует координаты в адрес ячейки - * { row: 0, col: 0 } -> "A1" - * { row: 2, col: 1 } -> "B3" - */ -export function coordinatesToCellAddress(coords: CellCoordinates): string { - const column = indexToColumn(coords.col) - const row = coords.row + 1 // Convert to 1-based - return `${column}${row}` -} - -/** - * Создает адрес ячейки из столбца и строки - * createCellAddress("A", 1) -> "A1" - * createCellAddress(0, 1) -> "A1" (если передан индекс столбца) - */ -export function createCellAddress( - column: string | number, - row: number -): string { - const columnStr = typeof column === 'number' ? indexToColumn(column) : column - return `${columnStr}${row}` -} - -/** - * Валидирует адрес ячейки - * isValidCellAddress("A1") -> true - * isValidCellAddress("ZZZ999999") -> true - * isValidCellAddress("A") -> false - */ -export function isValidCellAddress(cell: string): boolean { - return /^[A-Z]+\d+$/i.test(cell) -} - -/** - * Получает метку столбца из индекса - * getColumnLabel(0) -> "A" - * getColumnLabel(25) -> "Z" - * getColumnLabel(26) -> "AA" - */ -export function getColumnLabel(index: number): string { - return indexToColumn(index) -} - -/** - * Вспомогательная функция для совместимости с существующим кодом - * Аналог cellAddressToCoordinates, но с другим названием - */ -export const cellAddressToCoords = cellAddressToCoordinates - -/** - * Парсит диапазон ячеек в массив адресов - * "A1:C3" -> ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"] - */ -export function parseRange(range: string): string[] { - const match = range.match(/^([A-Z]+\d+):([A-Z]+\d+)$/i) - if (!match) return [] - - const startCell = parseCellAddress(match[1]) - const endCell = parseCellAddress(match[2]) - - if (!startCell || !endCell) return [] - - const startCol = columnToIndex(startCell.column) - const endCol = columnToIndex(endCell.column) - const startRow = startCell.row - const endRow = endCell.row - - const result: string[] = [] - - for (let row = startRow; row <= endRow; row++) { - for (let col = startCol; col <= endCol; col++) { - result.push(createCellAddress(col, row)) - } - } - - return result -} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e77c50c..7508461 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -6,7 +6,6 @@ import { FormLayoutSettings, TemplateElement, } from '../type/template' -import { parseCellAddress } from './cell-utils' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) @@ -73,13 +72,3 @@ export function generateDefaultLayout(order: number): ElementLayout { zIndex: 1, } } - -// Валидация адреса ячейки - теперь использует централизованную утилиту -export function validateCellAddress(cell: string): boolean { - return parseCellAddress(cell) !== null -} - -// Генерация уникального ID для элемента -export function generateElementId(): string { - return `element_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` -} diff --git a/src/page/ElementsCreation/Page.tsx b/src/page/ElementsCreation/Page.tsx index aef78d8..8f5db79 100644 --- a/src/page/ElementsCreation/Page.tsx +++ b/src/page/ElementsCreation/Page.tsx @@ -1,6 +1,6 @@ import { ElementConstructor } from '@/component/TemplateManager/ElementConstructor' import { Button } from '@/component/ui/button' -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { useDelayedSave } from '@/hook/useDelayedSave' import { FormLayoutSettings, Template, TemplateElement } from '@/type/template' import { ArrowLeft, FileText, Settings, Wrench } from 'lucide-react' diff --git a/src/page/ProtocolCreation/Page.tsx b/src/page/ProtocolCreation/Page.tsx index 55e85c1..bb85545 100644 --- a/src/page/ProtocolCreation/Page.tsx +++ b/src/page/ProtocolCreation/Page.tsx @@ -1,7 +1,7 @@ import DualSpreadsheet from '@/component/DualSpreadsheet/DualSpreadsheet' import { Button } from '@/component/ui/button' -import { getElementDefinition } from '@/entitiy/element/model/interface' -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { getElementDefinition } from '@/entity/element/model/interface' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { cellAddressToCoordinates } from '@/lib/cell-utils' import { useToast } from '@/lib/hooks/useToast' import { getLatestFileForTemplate } from '@/service/fileApiService' diff --git a/src/page/StandardsManagementPage.tsx b/src/page/StandardsManagementPage.tsx index 36ec06c..a1adc30 100644 --- a/src/page/StandardsManagementPage.tsx +++ b/src/page/StandardsManagementPage.tsx @@ -13,12 +13,12 @@ import { useDeleteStandard, useStandards, useUpdateStandard, -} from '@/entitiy/element/model/implementations/StandardsElement/hooks' +} from '@/entity/element/model/implementations/StandardsElement/hooks' import { CreateStandardInput, PatchStandardInput, Standard, -} from '@/entitiy/element/model/implementations/StandardsElement/types' +} from '@/entity/element/model/implementations/StandardsElement/types' import { Calendar, Edit, diff --git a/src/page/TemplateEditPage.tsx b/src/page/TemplateEditPage.tsx index 2b8bf94..7c69ba6 100644 --- a/src/page/TemplateEditPage.tsx +++ b/src/page/TemplateEditPage.tsx @@ -1,5 +1,5 @@ import { TemplateEditor } from '@/component/TemplateManager/TemplateEditor' -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { useNavigate, useParams } from 'react-router-dom' export const TemplateEditPage = () => { diff --git a/src/page/TemplatesOverviewPage.tsx b/src/page/TemplatesOverviewPage.tsx index 7a7a76e..b63194c 100644 --- a/src/page/TemplatesOverviewPage.tsx +++ b/src/page/TemplatesOverviewPage.tsx @@ -24,9 +24,9 @@ import { import { apiTemplateWithAttributesToTemplate, getTemplateWithAttributesApi, -} from '@/entitiy/template/api/templateApiService' -import { useCategoryContext } from '@/entitiy/template/model/CategoryContext' -import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +} from '@/entity/template/api/templateApiService' +import { useCategoryContext } from '@/entity/template/model/CategoryContext' +import { useTemplateContext } from '@/entity/template/model/TemplateContext' import { Template, TemplateAttributeDetail } from '@/type/template' import TemplateCard from '@/widget/template/ui/TemplateCard' import clsx from 'clsx' diff --git a/src/type/formula-api.ts b/src/type/formula-api.ts deleted file mode 100644 index 4d0c764..0000000 --- a/src/type/formula-api.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Типы для API формул электронных таблиц - */ - -export interface FormulaApiConfig { - baseUrl: string - apiKey?: string - timeout?: number -} - -export interface FormulaApiError { - code: string - message: string - timestamp: string -} - -export interface FormulaApiEvent { - type: - | 'SAVE_START' - | 'SAVE_SUCCESS' - | 'SAVE_ERROR' - | 'LOAD_START' - | 'LOAD_SUCCESS' - | 'LOAD_ERROR' - templateId: string - version?: number - error?: FormulaApiError -} - -export interface SaveFormulasRequest { - templateId: string - formulaData: Record> - timestamp: string -} - -export interface SaveFormulasResponse { - success: boolean - templateId: string - version: number - timestamp: string - message: string -} - -export interface LoadFormulasRequest { - templateId: string - version?: number -} - -export interface LoadFormulasResponse { - success: boolean - notFound?: boolean - data?: TemplateFormulaData - message: string -} - -export interface FormulaHistoryRequest { - templateId: string - limit?: number -} - -export interface FormulaHistoryResponse { - success: boolean - history: Array<{ - version: number - timestamp: string - description?: string - }> -} - -export interface SyncStatus { - templateId: string - lastSync: string - isOnline: boolean - pendingChanges: number -} - -export interface TemplateFormulaData { - templateId: string - version: number - sheets: Record - timestamp: string -} - -export interface SheetFormulaData { - sheetName: string - cells: Record - metadata: { - rowCount: number - columnCount: number - lastCalculated: string - } -} diff --git a/src/type/imports.d.ts b/src/type/imports.d.ts deleted file mode 100644 index 1276a89..0000000 --- a/src/type/imports.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Типы для контроля импортов между слоями архитектуры -declare module 'app/*' { - const content: any - export default content -} - -declare module 'page/*' { - const content: any - export default content -} - -declare module 'feature/*' { - const content: any - export default content -} - -declare module 'entitiy/*' { - const content: any - export default content -} - -declare module 'shared/*' { - const content: any - export default content -} diff --git a/src/type/template.ts b/src/type/template.ts index 622e6bf..f6f174e 100644 --- a/src/type/template.ts +++ b/src/type/template.ts @@ -1,7 +1,7 @@ // Типы элементов интерфейса // Импортируем ElementType из interface.ts -import type { ElementType } from '@/entitiy/element/model/interface' +import type { ElementType } from '@/entity/element/model/interface' export interface ElementOption { value: string @@ -80,19 +80,6 @@ export interface TemplateAttributeDetail { updated_at: string } -// Для совместимости со старым кодом - алиас -export type CategoryField = Attribute -export type CategoryFieldType = 'text' | 'select' | 'number' | 'textarea' -export interface CategoryFieldOption { - id: string - value: string - label: string -} -export interface CategoryFieldValue { - fieldId: string - value: string | number -} - // Типы шаблонов export interface Template { id: string @@ -106,10 +93,3 @@ export interface Template { createdAt: Date updatedAt: Date } - -// Типы для работы с данными -export interface TemplateData { - templateId: string - values: Record // elementId -> value - calculatedCells?: Record // cellName -> value -} diff --git a/src/type/vite-env.d.ts b/src/type/vite-env.d.ts index f5e1a25..11f02fe 100644 --- a/src/type/vite-env.d.ts +++ b/src/type/vite-env.d.ts @@ -1 +1 @@ -/// \ No newline at end of file +/// diff --git a/verification-conditions-api.md b/verification-conditions-api.md deleted file mode 100644 index 232de77..0000000 --- a/verification-conditions-api.md +++ /dev/null @@ -1,332 +0,0 @@ -# API для элемента "Условия поверки" - -## Базовый URL - -``` -/api/v1/verification-conditions -``` - -## Эндпоинты - -### 1. Лаборатории - -#### GET /laboratories - -Получить список всех лабораторий - -```json -Response 200: -[ - { - "id": 1, - "name": "Лаборатория температуры", - "code": "TEMP_LAB", - "description": "Лаборатория поверки температурных приборов", - "address": "ул. Примерная, 1", - "conditionTypes": [ - { - "id": 1, - "name": "Температура", - "unit": "°C", - "isRequired": true, - "defaultValue": "20", - "minValue": 15, - "maxValue": 30 - } - ] - } -] -``` - -#### GET /laboratories/:id - -Получить лабораторию по ID с настройками условий - -#### POST /laboratories - -Создать новую лабораторию - -```json -Request: -{ - "name": "Новая лаборатория", - "code": "NEW_LAB", - "description": "Описание", - "address": "Адрес" -} -``` - -#### PUT /laboratories/:id - -Обновить лабораторию - -#### DELETE /laboratories/:id - -Удалить лабораторию - -### 2. Типы условий - -#### GET /condition-types - -Получить все доступные типы условий - -```json -Response 200: -[ - { - "id": 1, - "name": "Температура", - "unit": "°C", - "dataType": "number", - "description": "Температура окружающей среды" - } -] -``` - -#### POST /condition-types - -Создать новый тип условия - -```json -Request: -{ - "name": "Новое условие", - "unit": "единица", - "dataType": "number", - "description": "Описание" -} -``` - -### 3. Настройка условий для лабораторий - -#### GET /laboratories/:labId/condition-types - -Получить настроенные типы условий для лаборатории - -#### POST /laboratories/:labId/condition-types - -Добавить тип условия к лаборатории - -```json -Request: -{ - "conditionTypeId": 1, - "isRequired": true, - "defaultValue": "20", - "minValue": 15, - "maxValue": 30 -} -``` - -#### PUT /laboratories/:labId/condition-types/:typeId - -Обновить настройки типа условия для лаборатории - -#### DELETE /laboratories/:labId/condition-types/:typeId - -Удалить тип условия из лаборатории - -### 4. Условия по дням (основная функциональность) - -#### GET /daily-conditions - -Получить условия по дням с фильтрацией - -``` -Query параметры: -- laboratoryId: ID лаборатории -- dateFrom: дата начала (YYYY-MM-DD) -- dateTo: дата окончания (YYYY-MM-DD) -- date: конкретная дата (YYYY-MM-DD) - -Примеры: -GET /daily-conditions?laboratoryId=1&dateFrom=2024-01-01&dateTo=2024-01-31 -GET /daily-conditions?laboratoryId=1&date=2024-01-15 -``` - -```json -Response 200: -[ - { - "id": 1, - "laboratoryId": 1, - "date": "2024-01-15", - "conditions": { - "temperature": 23.5, - "humidity": 45.2, - "atmosphericPressure": 101.3 - }, - "createdAt": "2024-01-15T10:30:00Z", - "updatedAt": "2024-01-15T10:30:00Z", - "createdBy": "user@example.com" - } -] -``` - -#### GET /daily-conditions/:id - -Получить условия для конкретного дня по ID - -#### POST /daily-conditions - -Создать/обновить условия для дня - -```json -Request: -{ - "laboratoryId": 1, - "date": "2024-01-15", - "conditions": { - "temperature": 23.5, - "humidity": 45.2, - "atmosphericPressure": 101.3 - } -} -``` - -#### PUT /daily-conditions/:id - -Обновить условия для дня - -#### DELETE /daily-conditions/:id - -Удалить запись условий - -### 5. Специальные эндпоинты для элемента - -#### GET /daily-conditions/for-element - -Получить условия для использования в элементе формы - -``` -Query параметры: -- laboratoryId: ID лаборатории (обязательный) -- date: дата (обязательный, YYYY-MM-DD) -``` - -```json -Response 200: -{ - "laboratoryId": 1, - "laboratoryName": "Лаборатория температуры", - "date": "2024-01-15", - "conditions": [ - { - "typeId": 1, - "typeName": "Температура", - "unit": "°C", - "value": 23.5, - "isRequired": true, - "minValue": 15, - "maxValue": 30 - }, - { - "typeId": 2, - "typeName": "Влажность", - "unit": "%", - "value": 45.2, - "isRequired": true, - "minValue": 30, - "maxValue": 80 - } - ], - "hasUnsavedChanges": false -} -``` - -#### POST /daily-conditions/save-for-element - -Сохранить условия из элемента формы - -```json -Request: -{ - "laboratoryId": 1, - "date": "2024-01-15", - "conditions": { - "temperature": 23.5, - "humidity": 45.2, - "atmosphericPressure": 101.3 - } -} - -Response 200: -{ - "id": 1, - "success": true, - "message": "Условия сохранены" -} -``` - -## Типы данных TypeScript - -```typescript -interface Laboratory { - id: number - name: string - code: string - description?: string - address?: string - conditionTypes?: LaboratoryConditionType[] - createdAt: string - updatedAt: string -} - -interface ConditionType { - id: number - name: string - unit?: string - dataType: 'number' | 'string' | 'boolean' - description?: string - createdAt: string -} - -interface LaboratoryConditionType { - id: number - laboratoryId: number - conditionTypeId: number - conditionType?: ConditionType - isRequired: boolean - defaultValue?: string - minValue?: number - maxValue?: number - createdAt: string -} - -interface DailyConditions { - id: number - laboratoryId: number - laboratory?: Laboratory - date: string // YYYY-MM-DD - conditions: Record // JSON объект - createdAt: string - updatedAt: string - createdBy?: string -} - -interface ConditionValue { - typeId: number - typeName: string - unit?: string - value: any - isRequired: boolean - minValue?: number - maxValue?: number -} - -interface ElementConditionsData { - laboratoryId: number - laboratoryName: string - date: string - conditions: ConditionValue[] - hasUnsavedChanges: boolean -} -``` - -## Коды ошибок - -- `400` - Неверные параметры запроса -- `404` - Лаборатория/условия не найдены -- `409` - Конфликт (условия для этой даты уже существуют) -- `422` - Ошибка валидации (значения не в допустимом диапазоне) -- `500` - Внутренняя ошибка сервера diff --git a/yarn.lock b/yarn.lock index e120b10..7d3ea34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,20 @@ resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== +"@babel/code-frame@^7.0.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + "@babel/runtime@^7.0.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.15.4", "@babel/runtime@^7.9.2": version "7.27.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" @@ -865,6 +879,16 @@ dependencies: "@tanstack/query-core" "5.83.0" +"@ts-morph/common@~0.12.3": + version "0.12.3" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.12.3.tgz#a96e250217cd30e480ab22ec6a0ebbe65fd784ff" + integrity sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w== + dependencies: + fast-glob "^3.2.7" + minimatch "^3.0.4" + mkdirp "^1.0.4" + path-browserify "^1.0.1" + "@tybys/wasm-util@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369" @@ -916,6 +940,11 @@ dependencies: undici-types "~7.8.0" +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + "@types/prop-types@*": version "15.7.15" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" @@ -1545,6 +1574,11 @@ clsx@^2.1.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== +code-block-writer@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" + integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1567,6 +1601,11 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + common-tags@^1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -1577,6 +1616,17 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +cosmiconfig@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cross-spawn@^7.0.2, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1761,6 +1811,13 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: version "1.24.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" @@ -2092,7 +2149,7 @@ fast-equals@^4.0.3: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-4.0.3.tgz#72884cc805ec3c6679b99875f6b7654f39f0e8c7" integrity sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg== -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -2473,6 +2530,11 @@ is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: call-bound "^1.0.3" get-intrinsic "^1.2.6" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + is-async-function@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" @@ -2694,7 +2756,7 @@ jiti@^1.21.6: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== -"js-tokens@^3.0.0 || ^4.0.0": +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -2711,6 +2773,11 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2728,6 +2795,11 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" +json5@^2.1.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -2835,7 +2907,7 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2859,6 +2931,11 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + motion-dom@^11.18.1: version "11.18.1" resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.18.1.tgz#e7fed7b7dc6ae1223ef1cce29ee54bec826dc3f2" @@ -3043,6 +3120,21 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -3893,6 +3985,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +"true-myth@^4.1.0": + version "4.1.1" + resolved "https://registry.yarnpkg.com/true-myth/-/true-myth-4.1.1.tgz#ff4ac9d5130276e34aa338757e2416ec19248ba2" + integrity sha512-rqy30BSpxPznbbTcAcci90oZ1YR4DqvKcNXNerG5gQBU2v4jk0cygheiul5J6ExIMrgDVuanv/MkGfqZbKrNNg== + ts-api-utils@^1.0.1: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" @@ -3903,6 +4000,26 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== +ts-morph@^13.0.1: + version "13.0.3" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-13.0.3.tgz#c0c51d1273ae2edb46d76f65161eb9d763444c1d" + integrity sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw== + dependencies: + "@ts-morph/common" "~0.12.3" + code-block-writer "^11.0.0" + +ts-prune@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/ts-prune/-/ts-prune-0.10.3.tgz#b6c71a525543b38dcf947a7d3adfb7f9e8b91f38" + integrity sha512-iS47YTbdIcvN8Nh/1BFyziyUqmjXz7GVzWu02RaZXqb+e/3Qe1B7IQ4860krOeCGUeJmterAlaM2FRH0Ue0hjw== + dependencies: + commander "^6.2.1" + cosmiconfig "^7.0.1" + json5 "^2.1.3" + lodash "^4.17.21" + "true-myth" "^4.1.0" + ts-morph "^13.0.1" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -4213,6 +4330,11 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yaml@^2.3.4: version "2.8.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6"