ручной рефакторинг
This commit is contained in:
@@ -31,7 +31,7 @@ module.exports = {
|
|||||||
pathGroups: [
|
pathGroups: [
|
||||||
{ pattern: 'react', group: 'builtin' },
|
{ pattern: 'react', group: 'builtin' },
|
||||||
{ pattern: '~shared/**', group: 'internal', position: 'before' },
|
{ 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: '~feature/**', group: 'internal', position: 'before' },
|
||||||
{ pattern: '~widget/**', group: 'internal', position: 'before' },
|
{ pattern: '~widget/**', group: 'internal', position: 'before' },
|
||||||
{ pattern: '~page/**', group: 'internal', position: 'before' },
|
{ pattern: '~page/**', group: 'internal', position: 'before' },
|
||||||
|
|||||||
@@ -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); -- Частота сети
|
|
||||||
@@ -76,6 +76,7 @@
|
|||||||
"react-redux": "^8.1.1",
|
"react-redux": "^8.1.1",
|
||||||
"react-router-dom": "^6.14.1",
|
"react-router-dom": "^6.14.1",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
|
"ts-prune": "^0.10.3",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
"vite": "^4.3.9"
|
"vite": "^4.3.9"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { FC, useEffect } from 'react'
|
|||||||
|
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom'
|
import { Navigate, Route, Routes } from 'react-router-dom'
|
||||||
|
|
||||||
import { CategoryProvider } from '@/entitiy/template/model/CategoryContext'
|
import { CategoryProvider } from '@/entity/template/model/CategoryContext'
|
||||||
import { TemplateProvider } from '@/entitiy/template/model/TemplateContext'
|
import { TemplateProvider } from '@/entity/template/model/TemplateContext'
|
||||||
import {
|
import {
|
||||||
setGlobalToastContext,
|
setGlobalToastContext,
|
||||||
ToastProvider,
|
ToastProvider,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { BrowserRouter } from 'react-router-dom'
|
|||||||
|
|
||||||
import { store } from './store'
|
import { store } from './store'
|
||||||
|
|
||||||
import { initializeElementRegistry } from '@/entitiy/element/model/interface'
|
import { initializeElementRegistry } from '@/entity/element/model/interface'
|
||||||
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Input } from '@/component/ui/input'
|
import { Input } from '@/component/ui/input'
|
||||||
import { Label } from '@/component/ui/label'
|
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 { Attribute } from '@/type/template'
|
||||||
import { AlertCircle } from 'lucide-react'
|
import { AlertCircle } from 'lucide-react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
getAvailableElementTypes,
|
getAvailableElementTypes,
|
||||||
getElementDefinition,
|
getElementDefinition,
|
||||||
} from '@/entitiy/element/model/interface'
|
} from '@/entity/element/model/interface'
|
||||||
import { useToast } from '@/lib/hooks/useToast'
|
import { useToast } from '@/lib/hooks/useToast'
|
||||||
import { generateDefaultLayout } from '@/lib/utils'
|
import { generateDefaultLayout } from '@/lib/utils'
|
||||||
import {
|
import {
|
||||||
@@ -47,6 +47,14 @@ import { Separator } from '../ui/separator'
|
|||||||
import { ElementFormulaBar } from './ElementFormulaBar'
|
import { ElementFormulaBar } from './ElementFormulaBar'
|
||||||
import { VisualLayoutEditor } from './VisualLayoutEditor'
|
import { VisualLayoutEditor } from './VisualLayoutEditor'
|
||||||
|
|
||||||
|
export function getDefaultLayoutSettings(): FormLayoutSettings {
|
||||||
|
return {
|
||||||
|
gridSize: 20,
|
||||||
|
showGrid: true,
|
||||||
|
snapToGrid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ElementFormProps {
|
interface ElementFormProps {
|
||||||
formData: Partial<TemplateElement>
|
formData: Partial<TemplateElement>
|
||||||
setFormData: React.Dispatch<React.SetStateAction<Partial<TemplateElement>>>
|
setFormData: React.Dispatch<React.SetStateAction<Partial<TemplateElement>>>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
import { useTemplateContext } from '@/entity/template/model/TemplateContext'
|
||||||
import {
|
import {
|
||||||
getFileData,
|
getFileData,
|
||||||
getLatestFileForTemplate,
|
getLatestFileForTemplate,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
SidebarHeader,
|
SidebarHeader,
|
||||||
SidebarSeparator,
|
SidebarSeparator,
|
||||||
} from '@/component/ui/sidebar'
|
} from '@/component/ui/sidebar'
|
||||||
import { useCategoryContext } from '@/entitiy/template/model/CategoryContext'
|
import { useCategoryContext } from '@/entity/template/model/CategoryContext'
|
||||||
import { Template } from '@/type/template'
|
import { Template } from '@/type/template'
|
||||||
import { Filter, X } from 'lucide-react'
|
import { Filter, X } from 'lucide-react'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
import { useTemplateContext } from '@/entity/template/model/TemplateContext'
|
||||||
import { Template } from '@/type/template'
|
import { Template } from '@/type/template'
|
||||||
import { Circle, CircleCheck, FileText, Plus, Trash2 } from 'lucide-react'
|
import { Circle, CircleCheck, FileText, Plus, Trash2 } from 'lucide-react'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Edit, Grid, Move, Trash2 } from 'lucide-react'
|
import { Edit, Grid, Move, Trash2 } from 'lucide-react'
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { Rnd } from 'react-rnd'
|
import { Rnd } from 'react-rnd'
|
||||||
import { getElementDefinition } from '../../entitiy/element/model/interface'
|
import { getElementDefinition } from '../../entity/element/model/interface'
|
||||||
import {
|
import {
|
||||||
ElementLayout,
|
ElementLayout,
|
||||||
FormLayoutSettings,
|
FormLayoutSettings,
|
||||||
|
|||||||
@@ -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 (
|
|
||||||
<Card className="border-dashed">
|
|
||||||
<CardContent className="p-3">
|
|
||||||
<div className="text-center text-muted-foreground">
|
|
||||||
<Building2 className="mx-auto mb-1 h-4 w-4 opacity-50" />
|
|
||||||
<p className="text-xs">Лаборатория не выбрана</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!labData) {
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-3">
|
|
||||||
<div className="text-center text-muted-foreground">
|
|
||||||
<div className="mx-auto mb-1 h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
||||||
<p className="text-xs">Загрузка...</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="px-3 pb-1 pt-2">
|
|
||||||
<CardTitle className="flex items-center gap-1.5 text-sm">
|
|
||||||
<Building2 className="h-3.5 w-3.5" />
|
|
||||||
<span className="truncate">{selectedLab?.name || 'Лаборатория'}</span>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
|
|
||||||
<CardContent className="space-y-2 px-3 pb-3">
|
|
||||||
{/* Выбор даты */}
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<CalendarIcon className="h-3.5 w-3.5 text-muted-foreground" />
|
|
||||||
<Popover open={calendarOpen} onOpenChange={setCalendarOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="h-6 justify-start border-none bg-muted/50 px-2 text-xs font-normal hover:bg-muted/70"
|
|
||||||
disabled={!onChange}
|
|
||||||
>
|
|
||||||
{new Date(selectedDate).toLocaleDateString('ru-RU')}
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={new Date(selectedDate)}
|
|
||||||
onSelect={handleCalendarSelect}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Статус и данные */}
|
|
||||||
<div className="border-t pt-2">
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
||||||
<div className="h-3.5 w-3.5 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
||||||
<span>Загрузка...</span>
|
|
||||||
</div>
|
|
||||||
) : hasConditions ? (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-1.5 text-xs text-green-600">
|
|
||||||
<CheckCircle className="h-3.5 w-3.5" />
|
|
||||||
<span className="font-medium">Данные внесены</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1">
|
|
||||||
{labData.conditionTypes.map(conditionType => {
|
|
||||||
const conditionValue =
|
|
||||||
dailyCondition!.conditions[conditionType.id]
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={conditionType.id}
|
|
||||||
className="flex items-center justify-between rounded bg-muted/30 px-2 py-1 text-xs"
|
|
||||||
>
|
|
||||||
<span className="truncate font-medium">
|
|
||||||
{conditionType.name}
|
|
||||||
</span>
|
|
||||||
<span className="ml-2 font-mono text-muted-foreground">
|
|
||||||
{conditionValue || '—'}
|
|
||||||
{conditionValue && conditionType.unit && (
|
|
||||||
<span className="ml-1">{conditionType.unit}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-1.5 text-xs text-orange-600">
|
|
||||||
<AlertCircle className="h-3.5 w-3.5" />
|
|
||||||
<span className="font-medium">Данные не внесены</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
disabled
|
|
||||||
size="sm"
|
|
||||||
className="h-6 w-full text-xs"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<Plus className="mr-1.5 h-3 w-3" />
|
|
||||||
Внести данные
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
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<string, string>
|
|
||||||
>({})
|
|
||||||
|
|
||||||
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<HTMLInputElement>) => {
|
|
||||||
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<string, string> = {}
|
|
||||||
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 (
|
|
||||||
<Card className="border-dashed">
|
|
||||||
<CardContent className="p-3">
|
|
||||||
<div className="text-center text-muted-foreground">
|
|
||||||
<AlertCircle className="mx-auto mb-1 h-4 w-4 opacity-50" />
|
|
||||||
<p className="text-xs">Лаборатория не настроена</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!labData) {
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-3">
|
|
||||||
<div className="text-center text-muted-foreground">
|
|
||||||
<div className="mx-auto mb-1 h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
||||||
<p className="text-xs">Загрузка...</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
{/* Бейдж ячеек */}
|
|
||||||
<CellsBadge targetCells={config.targetCells || []} />
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="px-3 pb-1 pt-2">
|
|
||||||
<CardTitle className="flex items-center gap-1.5 text-sm">
|
|
||||||
<Building2 className="h-3.5 w-3.5" />
|
|
||||||
<span className="truncate">
|
|
||||||
{selectedLab?.name || 'Лаборатория'}
|
|
||||||
</span>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
|
|
||||||
<CardContent className="space-y-2 px-3 pb-3">
|
|
||||||
{/* Выбор даты */}
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<CalendarIcon className="h-3.5 w-3.5 text-muted-foreground" />
|
|
||||||
<Input
|
|
||||||
type="date"
|
|
||||||
value={selectedDate}
|
|
||||||
onChange={handleDateChange}
|
|
||||||
lang="ru"
|
|
||||||
className="h-6 border-none bg-muted/50 px-2 text-xs font-normal hover:bg-muted/70 focus:bg-background"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Статус и данные */}
|
|
||||||
<div className="border-t pt-2">
|
|
||||||
{hasConditions ? (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-1.5 text-xs text-green-600">
|
|
||||||
<CheckCircle className="h-3.5 w-3.5" />
|
|
||||||
<span className="font-medium">Данные внесены</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={editDialogOpen}
|
|
||||||
onOpenChange={setEditDialogOpen}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
className="h-6 px-2 text-xs"
|
|
||||||
onClick={() => handleOpenEditDialog(false)}
|
|
||||||
>
|
|
||||||
<Settings className="mr-1 h-3 w-3" />
|
|
||||||
Настроить
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-sm">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-base">
|
|
||||||
Условия поверки
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
|
||||||
<div>{selectedLab?.name}</div>
|
|
||||||
<div>
|
|
||||||
{new Date(selectedDate).toLocaleDateString('ru-RU')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{labData.conditionTypes.map(conditionType => (
|
|
||||||
<div key={conditionType.id} className="space-y-1">
|
|
||||||
<Label className="text-xs font-medium">
|
|
||||||
{conditionType.name}
|
|
||||||
{conditionType.unit && (
|
|
||||||
<span className="ml-1 font-normal text-muted-foreground">
|
|
||||||
({conditionType.unit})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
value={
|
|
||||||
editingConditions[conditionType.id] || ''
|
|
||||||
}
|
|
||||||
onChange={e =>
|
|
||||||
handleConditionChange(
|
|
||||||
conditionType.id,
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
placeholder="Значение"
|
|
||||||
className="h-7 text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
onClick={handleSaveConditions}
|
|
||||||
disabled={isSaving}
|
|
||||||
size="sm"
|
|
||||||
className="h-7 flex-1 text-xs"
|
|
||||||
>
|
|
||||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setEditDialogOpen(false)}
|
|
||||||
size="sm"
|
|
||||||
className="h-7 flex-1 text-xs"
|
|
||||||
>
|
|
||||||
Отмена
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1">
|
|
||||||
{labData.conditionTypes.map(conditionType => {
|
|
||||||
const conditionValue =
|
|
||||||
dailyCondition!.conditions[conditionType.id]
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={conditionType.id}
|
|
||||||
className="flex items-center justify-between rounded bg-muted/30 px-2 py-1 text-xs"
|
|
||||||
>
|
|
||||||
<span className="truncate font-medium">
|
|
||||||
{conditionType.name}
|
|
||||||
</span>
|
|
||||||
<span className="ml-2 font-mono text-muted-foreground">
|
|
||||||
{conditionValue || '—'}
|
|
||||||
{conditionValue && conditionType.unit && (
|
|
||||||
<span className="ml-1">{conditionType.unit}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-1.5 text-xs text-orange-600">
|
|
||||||
<AlertCircle className="h-3.5 w-3.5" />
|
|
||||||
<span className="font-medium">Данные не внесены</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button
|
|
||||||
onClick={() => handleOpenEditDialog(true)}
|
|
||||||
disabled={isSaving}
|
|
||||||
size="sm"
|
|
||||||
className="h-6 w-full text-xs"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<Plus className="mr-1.5 h-3 w-3" />
|
|
||||||
Внести данные
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-sm">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-base">
|
|
||||||
Условия поверки
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
|
||||||
<div>{selectedLab?.name}</div>
|
|
||||||
<div>
|
|
||||||
{new Date(selectedDate).toLocaleDateString('ru-RU')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{labData.conditionTypes.map(conditionType => (
|
|
||||||
<div key={conditionType.id} className="space-y-1">
|
|
||||||
<Label className="text-xs font-medium">
|
|
||||||
{conditionType.name}
|
|
||||||
{conditionType.unit && (
|
|
||||||
<span className="ml-1 font-normal text-muted-foreground">
|
|
||||||
({conditionType.unit})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
value={editingConditions[conditionType.id] || ''}
|
|
||||||
onChange={e =>
|
|
||||||
handleConditionChange(
|
|
||||||
conditionType.id,
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
placeholder="Значение"
|
|
||||||
className="h-7 text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
onClick={handleSaveConditions}
|
|
||||||
disabled={isSaving}
|
|
||||||
size="sm"
|
|
||||||
className="h-7 flex-1 text-xs"
|
|
||||||
>
|
|
||||||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setEditDialogOpen(false)}
|
|
||||||
size="sm"
|
|
||||||
className="h-7 flex-1 text-xs"
|
|
||||||
>
|
|
||||||
Отмена
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Button } from '@/component/ui/button'
|
import { Button } from '@/component/ui/button'
|
||||||
import { Input } from '@/component/ui/input'
|
import { Input } from '@/component/ui/input'
|
||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { CellsBadge } from '@/entitiy/element/ui/cellsBadge'
|
import { CellsBadge } from '@/entity/element/ui/cellsBadge'
|
||||||
import { ElementOption } from '@/type/template'
|
import { ElementOption } from '@/type/template'
|
||||||
import { Plus, Square, Trash2 } from 'lucide-react'
|
import { Plus, Square, Trash2 } from 'lucide-react'
|
||||||
|
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/component/ui/select'
|
} from '@/component/ui/select'
|
||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { CellsBadge } from '@/entitiy/element/ui/cellsBadge'
|
import { CellsBadge } from '@/entity/element/ui/cellsBadge'
|
||||||
import { useToast } from '@/lib/hooks/useToast'
|
import { useToast } from '@/lib/hooks/useToast'
|
||||||
import { ElementOption } from '@/type/template'
|
import { ElementOption } from '@/type/template'
|
||||||
import { ChevronDown, Plus, Trash2 } from 'lucide-react'
|
import { ChevronDown, Plus, Trash2 } from 'lucide-react'
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Badge } from '@/component/ui/badge'
|
import { Badge } from '@/component/ui/badge'
|
||||||
import { Button } from '@/component/ui/button'
|
import { Button } from '@/component/ui/button'
|
||||||
import { Label } from '@/component/ui/label'
|
import { Label } from '@/component/ui/label'
|
||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { CellsBadge } from '@/entitiy/element/ui/cellsBadge'
|
import { CellsBadge } from '@/entity/element/ui/cellsBadge'
|
||||||
import { CellTarget } from '@/type/template'
|
import { CellTarget } from '@/type/template'
|
||||||
import { FileText, Settings, Weight } from 'lucide-react'
|
import { FileText, Settings, Weight } from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Checkbox } from '@/component/ui/checkbox'
|
import { Checkbox } from '@/component/ui/checkbox'
|
||||||
import { Input } from '@/component/ui/input'
|
import { Input } from '@/component/ui/input'
|
||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { CellsBadge } from '@/entitiy/element/ui/cellsBadge'
|
import { CellsBadge } from '@/entity/element/ui/cellsBadge'
|
||||||
import { Plus, Save, SaveOff, Type } from 'lucide-react'
|
import { Plus, Save, SaveOff, Type } from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}) => (
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-muted/30 p-1.5 text-sm">
|
||||||
|
<div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
||||||
|
<Icon className="h-2.5 w-2.5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="font-medium text-foreground">{name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-5 shrink-0 rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
|
||||||
|
— {unit}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Моковые условия поверки
|
||||||
|
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 (
|
||||||
|
<Card className="border-dashed">
|
||||||
|
<CardContent className="p-3">
|
||||||
|
<div className="text-center text-muted-foreground">
|
||||||
|
<Building2 className="mx-auto mb-1 h-4 w-4 opacity-50" />
|
||||||
|
<p className="text-xs">Лаборатория не выбрана</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
{/* Информация о лаборатории и дате */}
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-muted/20 p-2 text-sm">
|
||||||
|
<Building2 className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<span className="font-medium text-muted-foreground">Лаборатория</span>
|
||||||
|
<div className="ml-auto flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
|
<CalendarIcon className="h-3 w-3" />
|
||||||
|
<span>{new Date(selectedDate).toLocaleDateString('ru-RU')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Общий статус с кнопкой настроить */}
|
||||||
|
<div className="flex items-center justify-between px-2 text-xs">
|
||||||
|
<div className="flex items-center gap-1.5 text-orange-600">
|
||||||
|
<AlertCircle className="h-3.5 w-3.5" />
|
||||||
|
<span className="font-medium">Данные не внесены</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled
|
||||||
|
className="h-6 px-2 text-xs"
|
||||||
|
>
|
||||||
|
<Settings className="mr-1 h-3 w-3" />
|
||||||
|
Настроить
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Моковые условия */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{conditionsToShow.map((condition, index) => (
|
||||||
|
<ConditionSkeleton
|
||||||
|
key={index}
|
||||||
|
index={index}
|
||||||
|
icon={condition.icon}
|
||||||
|
name={condition.name}
|
||||||
|
unit={condition.unit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-muted/30 p-1.5 text-sm">
|
||||||
|
<div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
||||||
|
<Icon className="h-2.5 w-2.5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="font-medium text-foreground">{name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-5 shrink-0 rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
|
||||||
|
{value || '—'} {unit || ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VerificationConditionsRenderProps {
|
||||||
|
config: VerificationConditionsConfig & { id?: string }
|
||||||
|
value?: VerificationConditionsValue
|
||||||
|
onChange?: (value: VerificationConditionsValue) => void
|
||||||
|
onSave?: (value: VerificationConditionsValue) => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<string, string>
|
||||||
|
>({})
|
||||||
|
|
||||||
|
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<HTMLInputElement>) => {
|
||||||
|
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<string, string> = {}
|
||||||
|
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 (
|
||||||
|
<div className="relative">
|
||||||
|
<CellsBadge targetCells={config.targetCells || []} />
|
||||||
|
|
||||||
|
<Card className="border-dashed">
|
||||||
|
<CardContent className="p-3">
|
||||||
|
<div className="text-center text-muted-foreground">
|
||||||
|
<Building2 className="mx-auto mb-1 h-4 w-4 opacity-50" />
|
||||||
|
<p className="text-xs">Лаборатория не выбрана</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!labData) {
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<CellsBadge targetCells={config.targetCells || []} />
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-3">
|
||||||
|
<div className="text-center text-muted-foreground">
|
||||||
|
<div className="mx-auto mb-1 h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||||
|
<p className="text-xs">Загрузка...</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
{/* Бейдж ячеек */}
|
||||||
|
<CellsBadge targetCells={config.targetCells || []} />
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
{/* Информация о лаборатории и дате */}
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-muted/20 p-2 text-sm">
|
||||||
|
<Building2 className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<span className="font-medium text-muted-foreground">
|
||||||
|
{selectedLab?.name || 'Лаборатория'}
|
||||||
|
</span>
|
||||||
|
<div className="ml-auto flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
|
<CalendarIcon className="h-3 w-3" />
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={selectedDate}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
lang="ru"
|
||||||
|
className="h-5 w-auto border-none bg-transparent p-0 text-xs font-normal focus:bg-background"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Общий статус с кнопкой настроить */}
|
||||||
|
<div className="flex items-center justify-between px-2 text-xs">
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
{hasConditions ? (
|
||||||
|
<>
|
||||||
|
<CheckCircle className="h-3.5 w-3.5 text-green-600" />
|
||||||
|
<span className="font-medium text-green-600">
|
||||||
|
Данные внесены
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<AlertCircle className="h-3.5 w-3.5 text-orange-600" />
|
||||||
|
<span className="font-medium text-orange-600">
|
||||||
|
Данные не внесены
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-6 px-2 text-xs"
|
||||||
|
onClick={() => handleOpenEditDialog(!hasConditions)}
|
||||||
|
>
|
||||||
|
<Settings className="mr-1 h-3 w-3" />
|
||||||
|
Настроить
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="max-w-sm">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-base">
|
||||||
|
Условия поверки
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
|
<div>{selectedLab?.name}</div>
|
||||||
|
<div>
|
||||||
|
{new Date(selectedDate).toLocaleDateString('ru-RU')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{labData.conditionTypes.map(conditionType => (
|
||||||
|
<div key={conditionType.id} className="space-y-1">
|
||||||
|
<Label className="text-xs font-medium">
|
||||||
|
{conditionType.name}
|
||||||
|
{conditionType.unit && (
|
||||||
|
<span className="ml-1 font-normal text-muted-foreground">
|
||||||
|
({conditionType.unit})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
value={editingConditions[conditionType.id] || ''}
|
||||||
|
onChange={e =>
|
||||||
|
handleConditionChange(
|
||||||
|
conditionType.id,
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="Значение"
|
||||||
|
className="h-7 text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={handleSaveConditions}
|
||||||
|
disabled={isSaving}
|
||||||
|
size="sm"
|
||||||
|
className="h-7 flex-1 text-xs"
|
||||||
|
>
|
||||||
|
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setEditDialogOpen(false)}
|
||||||
|
size="sm"
|
||||||
|
className="h-7 flex-1 text-xs"
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Условия */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{labData.conditionTypes.map(conditionType => {
|
||||||
|
const conditionValue =
|
||||||
|
dailyCondition?.conditions[conditionType.id]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionItem
|
||||||
|
key={conditionType.id}
|
||||||
|
name={conditionType.name}
|
||||||
|
value={conditionValue}
|
||||||
|
unit={conditionType.unit}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { CellTarget } from '@/type/template'
|
import { CellTarget } from '@/type/template'
|
||||||
import { Building2 } from 'lucide-react'
|
import { Building2 } from 'lucide-react'
|
||||||
import {
|
import {
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { buttonGroupDefinition } from '@/entitiy/element/model/implementations/ButtonGroup'
|
import { buttonGroupDefinition } from '@/entity/element/model/implementations/ButtonGroup'
|
||||||
import { selectDefinition } from '@/entitiy/element/model/implementations/SelectElement'
|
import { selectDefinition } from '@/entity/element/model/implementations/SelectElement'
|
||||||
import { standardsDefinition } from '@/entitiy/element/model/implementations/StandardsElement/definition'
|
import { standardsDefinition } from '@/entity/element/model/implementations/StandardsElement/definition'
|
||||||
import { textDefinition } from '@/entitiy/element/model/implementations/TextElement'
|
import { textDefinition } from '@/entity/element/model/implementations/TextElement'
|
||||||
import { verificationConditionsElementDefinition as verificationConditionsDefinition } from '@/entitiy/element/model/implementations/VerificationConditionsElement/definition'
|
import { verificationConditionsElementDefinition as verificationConditionsDefinition } from '@/entity/element/model/implementations/VerificationConditionsElement/definition'
|
||||||
import { CellTarget } from '@/type/template'
|
import { CellTarget } from '@/type/template'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
patchTemplateApi,
|
patchTemplateApi,
|
||||||
templateToCreateInput,
|
templateToCreateInput,
|
||||||
templateToPatchInput,
|
templateToPatchInput,
|
||||||
} from '@/entitiy/template/api/templateApiService'
|
} from '@/entity/template/api/templateApiService'
|
||||||
import { Template } from '@/type/template'
|
import { Template } from '@/type/template'
|
||||||
import {
|
import {
|
||||||
QueryClient,
|
QueryClient,
|
||||||
@@ -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
|
export type CellValue = string | number | boolean | null | undefined
|
||||||
|
|
||||||
// Утилитная функция для устранения машинной ошибки в числах с плавающей точкой
|
// Утилитная функция для устранения машинной ошибки в числах с плавающей точкой
|
||||||
|
// Применяется только при финальном выводе значений
|
||||||
function fixFloatingPointError(value: CellValue): CellValue {
|
function fixFloatingPointError(value: CellValue): CellValue {
|
||||||
if (typeof value === 'number' && isFinite(value)) {
|
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
|
return value
|
||||||
}
|
}
|
||||||
@@ -66,6 +84,7 @@ export class Cell {
|
|||||||
return this.value
|
return this.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Убираем знак равенства
|
||||||
const expr = this.raw.substring(1)
|
const expr = this.raw.substring(1)
|
||||||
|
|
||||||
// Проверяем, содержит ли формула волатильные функции
|
// Проверяем, содержит ли формула волатильные функции
|
||||||
@@ -166,13 +185,6 @@ export class Cell {
|
|||||||
// Заменяем функции Excel
|
// Заменяем функции Excel
|
||||||
const functions = this.sheet.workbook.getFunctions()
|
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)) {
|
for (const [funcName] of Object.entries(functions)) {
|
||||||
// Экранируем специальные символы в названии функции для регулярного выражения
|
// Экранируем специальные символы в названии функции для регулярного выражения
|
||||||
const escapedFuncName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
const escapedFuncName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||||
@@ -193,10 +205,6 @@ export class Cell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expr.includes('ТЕСТ')) {
|
|
||||||
// console.log('Final code:', code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создаём контекст для выполнения
|
// Создаём контекст для выполнения
|
||||||
const context = {
|
const context = {
|
||||||
__functions__: functions,
|
__functions__: functions,
|
||||||
@@ -249,18 +257,13 @@ export class Cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private evaluateExpression(expr: string, context: any): CellValue {
|
private evaluateExpression(expr: string, context: any): CellValue {
|
||||||
// Простая реализация eval для математических выражений и функций
|
|
||||||
// В реальном проекте лучше использовать специализированную библиотеку
|
|
||||||
// для парсинга и выполнения формул
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Создаём функцию с контекстом
|
// Создаём функцию с контекстом
|
||||||
const func = new Function(...Object.keys(context), `return ${expr}`)
|
const func = new Function(...Object.keys(context), `return ${expr}`)
|
||||||
|
|
||||||
const result = func(...Object.values(context))
|
const result = func(...Object.values(context))
|
||||||
|
|
||||||
// Исправляем машинную ошибку в 16-м символе после запятой
|
return result
|
||||||
return fixFloatingPointError(result)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Invalid expression: ${expr}`)
|
throw new Error(`Invalid expression: ${expr}`)
|
||||||
}
|
}
|
||||||
@@ -429,12 +432,133 @@ export class Workbook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private topologicalSort(cells: Set<string>): string[] {
|
||||||
|
const visited = new Set<string>()
|
||||||
|
const visiting = new Set<string>()
|
||||||
|
const result: string[] = []
|
||||||
|
const hasCycle = new Set<string>()
|
||||||
|
|
||||||
|
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<string>()
|
||||||
|
const visiting = new Set<string>()
|
||||||
|
|
||||||
|
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 {
|
recalc(): void {
|
||||||
// Пересчёт всех "грязных" ячеек
|
if (this._dirty.size === 0) {
|
||||||
while (this._dirty.size > 0) {
|
return
|
||||||
const current = this._dirty.values().next().value!
|
}
|
||||||
this._dirty.delete(current)
|
|
||||||
this.getCell(current).evaluate()
|
// Выполняем топологическую сортировку для определения правильного порядка пересчета
|
||||||
|
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
|
const value = this.getCell(qualifiedName).value
|
||||||
|
|
||||||
// Исправляем машинную ошибку в 16-м символе после запятой для числовых значений
|
|
||||||
return fixFloatingPointError(value)
|
return fixFloatingPointError(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,13 +5,7 @@ export interface ExcelFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Волатильные функции - пересчитываются при любом изменении
|
// Волатильные функции - пересчитываются при любом изменении
|
||||||
export const VOLATILE_FUNCTIONS = new Set([
|
export const VOLATILE_FUNCTIONS = new Set(['СЛУЧ', 'СЛУЧМЕЖДУ', 'СЛУЧДР'])
|
||||||
'СЛУЧ',
|
|
||||||
'СЛУЧМЕЖДУ',
|
|
||||||
'СЛУЧДР',
|
|
||||||
'СЕЙЧАС',
|
|
||||||
'СЕГОДНЯ',
|
|
||||||
])
|
|
||||||
|
|
||||||
export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
|
export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
|
||||||
СЛУЧ: () => Math.random(),
|
СЛУЧ: () => Math.random(),
|
||||||
@@ -124,43 +118,4 @@ export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
|
|||||||
if (кратное === 0) return 0
|
if (кратное === 0) return 0
|
||||||
return Math.round(число / кратное) * кратное
|
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 значение
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import { CellValue, Workbook } from '../feature/excelEngine/excelEngine'
|
||||||
CellValue,
|
|
||||||
Workbook,
|
|
||||||
} from '../lib/spreadsheet-engine/spreadsheet-engine'
|
|
||||||
interface SheetConfig {
|
interface SheetConfig {
|
||||||
name: string
|
name: string
|
||||||
rows: number
|
rows: number
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
CellUtils,
|
CellUtils,
|
||||||
CellValue,
|
CellValue,
|
||||||
Workbook,
|
Workbook,
|
||||||
} from '../lib/spreadsheet-engine/spreadsheet-engine'
|
} from '../feature/excelEngine/excelEngine'
|
||||||
|
|
||||||
interface CellData {
|
interface CellData {
|
||||||
value: string
|
value: string
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
/**
|
|
||||||
* Унифицированные утилиты для работы с адресами ячеек Excel
|
|
||||||
* Заменяет дублированный код в разных частях приложения
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface CellAddress {
|
export interface CellAddress {
|
||||||
column: string
|
column: string
|
||||||
row: number
|
row: number
|
||||||
@@ -25,23 +20,6 @@ export function columnToIndex(column: string): number {
|
|||||||
return result - 1 // zero-based
|
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 }
|
* "A1" -> { column: "A", row: 1 }
|
||||||
@@ -71,82 +49,3 @@ export function cellAddressToCoordinates(address: string): CellCoordinates {
|
|||||||
col: columnToIndex(parsed.column),
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
FormLayoutSettings,
|
FormLayoutSettings,
|
||||||
TemplateElement,
|
TemplateElement,
|
||||||
} from '../type/template'
|
} from '../type/template'
|
||||||
import { parseCellAddress } from './cell-utils'
|
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
@@ -73,13 +72,3 @@ export function generateDefaultLayout(order: number): ElementLayout {
|
|||||||
zIndex: 1,
|
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)}`
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ElementConstructor } from '@/component/TemplateManager/ElementConstructor'
|
import { ElementConstructor } from '@/component/TemplateManager/ElementConstructor'
|
||||||
import { Button } from '@/component/ui/button'
|
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 { useDelayedSave } from '@/hook/useDelayedSave'
|
||||||
import { FormLayoutSettings, Template, TemplateElement } from '@/type/template'
|
import { FormLayoutSettings, Template, TemplateElement } from '@/type/template'
|
||||||
import { ArrowLeft, FileText, Settings, Wrench } from 'lucide-react'
|
import { ArrowLeft, FileText, Settings, Wrench } from 'lucide-react'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import DualSpreadsheet from '@/component/DualSpreadsheet/DualSpreadsheet'
|
import DualSpreadsheet from '@/component/DualSpreadsheet/DualSpreadsheet'
|
||||||
import { Button } from '@/component/ui/button'
|
import { Button } from '@/component/ui/button'
|
||||||
import { getElementDefinition } from '@/entitiy/element/model/interface'
|
import { getElementDefinition } from '@/entity/element/model/interface'
|
||||||
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
import { useTemplateContext } from '@/entity/template/model/TemplateContext'
|
||||||
import { cellAddressToCoordinates } from '@/lib/cell-utils'
|
import { cellAddressToCoordinates } from '@/lib/cell-utils'
|
||||||
import { useToast } from '@/lib/hooks/useToast'
|
import { useToast } from '@/lib/hooks/useToast'
|
||||||
import { getLatestFileForTemplate } from '@/service/fileApiService'
|
import { getLatestFileForTemplate } from '@/service/fileApiService'
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ import {
|
|||||||
useDeleteStandard,
|
useDeleteStandard,
|
||||||
useStandards,
|
useStandards,
|
||||||
useUpdateStandard,
|
useUpdateStandard,
|
||||||
} from '@/entitiy/element/model/implementations/StandardsElement/hooks'
|
} from '@/entity/element/model/implementations/StandardsElement/hooks'
|
||||||
import {
|
import {
|
||||||
CreateStandardInput,
|
CreateStandardInput,
|
||||||
PatchStandardInput,
|
PatchStandardInput,
|
||||||
Standard,
|
Standard,
|
||||||
} from '@/entitiy/element/model/implementations/StandardsElement/types'
|
} from '@/entity/element/model/implementations/StandardsElement/types'
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
Edit,
|
Edit,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TemplateEditor } from '@/component/TemplateManager/TemplateEditor'
|
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'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
|
|
||||||
export const TemplateEditPage = () => {
|
export const TemplateEditPage = () => {
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
apiTemplateWithAttributesToTemplate,
|
apiTemplateWithAttributesToTemplate,
|
||||||
getTemplateWithAttributesApi,
|
getTemplateWithAttributesApi,
|
||||||
} from '@/entitiy/template/api/templateApiService'
|
} from '@/entity/template/api/templateApiService'
|
||||||
import { useCategoryContext } from '@/entitiy/template/model/CategoryContext'
|
import { useCategoryContext } from '@/entity/template/model/CategoryContext'
|
||||||
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
import { useTemplateContext } from '@/entity/template/model/TemplateContext'
|
||||||
import { Template, TemplateAttributeDetail } from '@/type/template'
|
import { Template, TemplateAttributeDetail } from '@/type/template'
|
||||||
import TemplateCard from '@/widget/template/ui/TemplateCard'
|
import TemplateCard from '@/widget/template/ui/TemplateCard'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|||||||
@@ -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<string, Record<string, any>>
|
|
||||||
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<string, SheetFormulaData>
|
|
||||||
timestamp: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SheetFormulaData {
|
|
||||||
sheetName: string
|
|
||||||
cells: Record<string, any>
|
|
||||||
metadata: {
|
|
||||||
rowCount: number
|
|
||||||
columnCount: number
|
|
||||||
lastCalculated: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
src/type/imports.d.ts
vendored
25
src/type/imports.d.ts
vendored
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Типы элементов интерфейса
|
// Типы элементов интерфейса
|
||||||
|
|
||||||
// Импортируем ElementType из interface.ts
|
// Импортируем ElementType из interface.ts
|
||||||
import type { ElementType } from '@/entitiy/element/model/interface'
|
import type { ElementType } from '@/entity/element/model/interface'
|
||||||
|
|
||||||
export interface ElementOption {
|
export interface ElementOption {
|
||||||
value: string
|
value: string
|
||||||
@@ -80,19 +80,6 @@ export interface TemplateAttributeDetail {
|
|||||||
updated_at: string
|
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 {
|
export interface Template {
|
||||||
id: string
|
id: string
|
||||||
@@ -106,10 +93,3 @@ export interface Template {
|
|||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// Типы для работы с данными
|
|
||||||
export interface TemplateData {
|
|
||||||
templateId: string
|
|
||||||
values: Record<string, any> // elementId -> value
|
|
||||||
calculatedCells?: Record<string, any> // cellName -> value
|
|
||||||
}
|
|
||||||
|
|||||||
2
src/type/vite-env.d.ts
vendored
2
src/type/vite-env.d.ts
vendored
@@ -1 +1 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|||||||
@@ -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<string, any> // 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` - Внутренняя ошибка сервера
|
|
||||||
128
yarn.lock
128
yarn.lock
@@ -7,6 +7,20 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
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":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.15.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.27.6"
|
version "7.27.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
|
||||||
@@ -865,6 +879,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-core" "5.83.0"
|
"@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":
|
"@tybys/wasm-util@^0.10.0":
|
||||||
version "0.10.0"
|
version "0.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369"
|
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369"
|
||||||
@@ -916,6 +940,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~7.8.0"
|
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@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.15"
|
version "15.7.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7"
|
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"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
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:
|
color-convert@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
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"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||||
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
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:
|
common-tags@^1.8.2:
|
||||||
version "1.8.2"
|
version "1.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
|
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"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
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:
|
cross-spawn@^7.0.2, cross-spawn@^7.0.6:
|
||||||
version "7.0.6"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
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"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
|
||||||
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
|
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:
|
es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0:
|
||||||
version "1.24.0"
|
version "1.24.0"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328"
|
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"
|
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-4.0.3.tgz#72884cc805ec3c6679b99875f6b7654f39f0e8c7"
|
||||||
integrity sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==
|
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"
|
version "3.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
||||||
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
|
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"
|
call-bound "^1.0.3"
|
||||||
get-intrinsic "^1.2.6"
|
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:
|
is-async-function@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523"
|
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"
|
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9"
|
||||||
integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
|
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"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
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"
|
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||||
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
|
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:
|
json-schema-traverse@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
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:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
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:
|
keyv@^4.5.3:
|
||||||
version "4.5.4"
|
version "4.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||||
@@ -2835,7 +2907,7 @@ minimatch@9.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^2.0.1"
|
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"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
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"
|
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
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:
|
motion-dom@^11.18.1:
|
||||||
version "11.18.1"
|
version "11.18.1"
|
||||||
resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.18.1.tgz#e7fed7b7dc6ae1223ef1cce29ee54bec826dc3f2"
|
resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.18.1.tgz#e7fed7b7dc6ae1223ef1cce29ee54bec826dc3f2"
|
||||||
@@ -3043,6 +3120,21 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
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:
|
path-exists@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
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:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
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:
|
ts-api-utils@^1.0.1:
|
||||||
version "1.4.3"
|
version "1.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
|
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"
|
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
||||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
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:
|
tsconfig-paths@^3.15.0:
|
||||||
version "3.15.0"
|
version "3.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
|
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"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
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:
|
yaml@^2.3.4:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6"
|
||||||
|
|||||||
Reference in New Issue
Block a user