элементы и передвижение
This commit is contained in:
@@ -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 }) => {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 },
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user