элементы и передвижение

This commit is contained in:
2025-07-23 17:50:44 +03:00
parent 907b3e315e
commit 7c7e0db426
9 changed files with 100 additions and 193 deletions

View File

@@ -11,6 +11,7 @@ import {
SelectValue,
} from '@/component/ui/select'
import { ElementDefinition } from '@/entitiy/element/model/interface'
import { cn } from '@/lib/utils'
import {
AlertCircle,
Check,
@@ -255,8 +256,7 @@ function CalibrationCondInfo({
return (
<>
{/* Компактное отображение */}
<div className="flex items-center gap-3 rounded-lg border bg-muted/50 px-3 py-2">
<div className="flex items-center justify-between gap-3 rounded-lg border bg-muted/50 px-3 py-2">
<div className="flex items-center gap-3 text-xs">
{fieldConfigs.map(({ key, icon: Icon, unit }) => (
<div key={key} className="flex items-center gap-1">
@@ -268,19 +268,24 @@ function CalibrationCondInfo({
</div>
))}
</div>
<Button
variant="ghost"
size="icon"
onClick={openModal}
className={`h-6 w-6 p-0 ${
isOutdated ? 'text-yellow-600 hover:text-yellow-700' : ''
}`}
>
<Edit3 className="h-3 w-3" />
</Button>
{isOutdated && (
<div className="h-2 w-2 animate-pulse rounded-full bg-yellow-500" />
)}
<div className="flex items-center gap-2">
{isOutdated && (
<div className="h-2 w-2 animate-pulse rounded-full bg-yellow-500" />
)}
<Button
variant="ghost"
size="icon"
onClick={openModal}
className={cn(
'h-6 w-6 p-0',
isOutdated && 'text-yellow-600 hover:text-yellow-700'
)}
>
<Edit3 className="h-3 w-3" />
</Button>
</div>
</div>
{/* Модальное окно */}
@@ -729,23 +734,20 @@ export const calibrationConditionsDefinition: ElementDefinition<
},
Preview: ({ config }) => (
<div className="space-y-3">
<div className="text-sm font-medium text-gray-700">Предпросмотр</div>
<div className="rounded-lg border border-gray-200 bg-white p-4">
<CalibrationCondInfo
conditions={
config.defaultConditions || {
temperature: '20',
humidity: '65',
pressure: '101.3',
voltage: '220',
frequency: '50',
lastUpdated: new Date().toISOString(),
}
<CalibrationCondInfo
conditions={
config.defaultConditions || {
temperature: '20',
humidity: '65',
pressure: '101.3',
voltage: '220',
frequency: '50',
lastUpdated: new Date().toISOString(),
}
isOutdated={false}
onUpdate={async () => true}
/>
</div>
}
isOutdated={false}
onUpdate={async () => true}
/>
</div>
),
Render: ({ config, value, onChange }) => {

View File

@@ -773,7 +773,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
const autoSaveOptions = useMemo(
() => ({
templateId,
autoSaveDelay: 2000,
autoSaveDelay: 4000,
enableAutoSave: enableAutoSave && userEdited,
}),
[templateId, enableAutoSave, userEdited]
@@ -824,7 +824,6 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
if (fileData && Object.keys(fileData.data).length > 0) {
finalBaseData = { [SHEET_NAMES.report]: fileData.data }
setBaseFileData(finalBaseData)
// Сохраняем baseline, чтобы в листы попадали только пользовательские изменения
setTemplateData(finalBaseData)
}

View File

@@ -3,11 +3,7 @@ import {
getElementDefinition,
} from '@/entitiy/element/model/interface'
import { useToast } from '@/lib/hooks/useToast'
import {
autoArrangeElements,
findFreePosition,
generateDefaultLayout,
} from '@/lib/utils'
import { generateDefaultLayout } from '@/lib/utils'
import {
CellTarget,
ElementType,
@@ -591,15 +587,8 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
}
// Генерируем layout для нового элемента
const existingLayouts = elements.map(e => e.layout).filter(Boolean)
const defaultLayout = generateDefaultLayout(elements.length)
const layout = findFreePosition(
defaultLayout,
existingLayouts as any[],
layoutSettings.gridSize
)
// Используем targetCells из формы или генерируем через mapToCells
let targetCells = formData.targetCells || []
if (!targetCells.length) {
@@ -609,14 +598,14 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
const newElement: TemplateElement = {
id: Date.now().toString(),
type: formData.type as ElementType,
name: formData.label?.toLowerCase().replace(/\s+/g, '_') || '',
name: formData.label || '',
label: formData.label,
targetCells,
placeholder: formData.placeholder,
required: formData.required,
options: formData.options,
order: elements.length,
layout,
layout: defaultLayout,
}
onElementAdd(newElement)
@@ -666,21 +655,6 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
resetForm()
}
const handleAutoArrange = () => {
const arrangedElements = autoArrangeElements(
elements,
layoutSettings.gridSize
)
arrangedElements.forEach((element, index) => {
onElementUpdate(element.id, {
...element,
layout: element.layout,
order: index,
})
})
}
const handleDeleteAndSelect = (elementId: string) => {
onElementDelete(elementId)
setEditingElement(null)
@@ -697,7 +671,6 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
<ElementFormulaBar
elementCount={elements.length}
onAddElement={() => setIsAddDialogOpen(true)}
onAutoArrange={handleAutoArrange}
disabled={disabled}
hasUnsavedChanges={hasUnsavedChanges}
isSaving={isSaving}

View File

@@ -30,7 +30,7 @@ export const HeaderBar: React.FC<HeaderBarProps> = ({ template, onBack }) => {
</div>
<div className="flex gap-2">
<Button
variant="secondary"
variant="outline"
size="sm"
onClick={() => navigate(`/templates/${template.id}/elements`)}
className="flex items-center gap-1 hover:bg-muted"
@@ -39,7 +39,7 @@ export const HeaderBar: React.FC<HeaderBarProps> = ({ template, onBack }) => {
<span className="text-sm">Редактор интерфейса</span>
</Button>
<Button
variant="secondary"
variant="outline"
size="sm"
onClick={() => navigate(`/templates/${template.id}/protocols`)}
className="flex items-center gap-1 hover:bg-muted"

View File

@@ -21,7 +21,7 @@ const ELEMENT_MIN_SIZE: Record<string, { width: number; height: number }> = {
radio: { width: 160, height: 80 },
checkbox: { width: 140, height: 40 },
standards: { width: 200, height: 50 },
calibration_conditions: { width: 250, height: 60 },
calibration_conditions: { width: 350, height: 50 },
button_group: { width: 180, height: 80 },
}