diff --git a/src/component/BasicElements/ButtonGroupElement.tsx b/src/component/BasicElements/ButtonGroupElement.tsx index 90a2453..ffe3607 100644 --- a/src/component/BasicElements/ButtonGroupElement.tsx +++ b/src/component/BasicElements/ButtonGroupElement.tsx @@ -28,6 +28,8 @@ export const buttonGroupDefinition: ElementDefinition< layout: 'horizontal', buttonStyle: 'default', }, + // Берём целевые ячейки прямо из конфига, если они заданы в редакторе + mapToCells: cfg => cfg.targetCells || [], Editor: ({ config, onChange }) => { const handleAddOption = () => { onChange({ diff --git a/src/component/TemplateManager/ExcelUploadPanel.tsx b/src/component/TemplateManager/ExcelUploadPanel.tsx index b3238eb..d971f85 100644 --- a/src/component/TemplateManager/ExcelUploadPanel.tsx +++ b/src/component/TemplateManager/ExcelUploadPanel.tsx @@ -38,7 +38,7 @@ export const ExcelUploadPanel: React.FC = ({ className={`h-3 w-3 ${fileName ? 'text-emerald-600' : 'text-gray-400'}`} /> diff --git a/src/component/TemplateManager/HeaderBar.tsx b/src/component/TemplateManager/HeaderBar.tsx index f1488ab..600405b 100644 --- a/src/component/TemplateManager/HeaderBar.tsx +++ b/src/component/TemplateManager/HeaderBar.tsx @@ -13,9 +13,9 @@ export const HeaderBar: React.FC = ({ template, onBack }) => { const navigate = useNavigate() return ( -
+
-
+
@@ -30,22 +30,22 @@ export const HeaderBar: React.FC = ({ template, onBack }) => {
diff --git a/src/hook/useSheetAutoSave.ts b/src/hook/useSheetAutoSave.ts index e489f2e..72dd2f4 100644 --- a/src/hook/useSheetAutoSave.ts +++ b/src/hook/useSheetAutoSave.ts @@ -79,10 +79,38 @@ export const useSheetAutoSave = ( setIsLoading(true) try { const response = await getSheetsByTemplate(cleanTemplateId) + /* + * В API могут существовать листы как с «сырым» именем (Report/Calculations), + * так и уже нормализованные (L/R). Чтобы случайный порядок ответов не + * приводил к потере данных (один объект перезаписывал другой), собираем + * листы в map с объединением ячеек, а не простым overwrite. + */ const sheetsMap = response.sheets.reduce( (acc, sheet) => { - const normalizedName = sheet.name === 'Report' ? 'L' : sheet.name - acc[normalizedName] = { ...sheet, name: normalizedName } + // Приводим названия к единым «L»/«R» + const normalizedName = + sheet.name === 'Report' + ? 'L' + : sheet.name === 'Calculations' + ? 'R' + : sheet.name + + if (acc[normalizedName]) { + // Уже есть лист с таким именем — мерджим данные, приоритет у более + // свежего объекта (sheet), но оставляем существующие ячейки если их + // нет в новом + acc[normalizedName] = { + ...sheet, + name: normalizedName, + cells: { + ...acc[normalizedName].cells, + ...sheet.cells, + }, + } + } else { + acc[normalizedName] = { ...sheet, name: normalizedName } + } + return acc }, {} as Record diff --git a/src/page/ProtocolCreation/Page.tsx b/src/page/ProtocolCreation/Page.tsx index 6702bb8..22bec9a 100644 --- a/src/page/ProtocolCreation/Page.tsx +++ b/src/page/ProtocolCreation/Page.tsx @@ -15,6 +15,7 @@ import { } from '@/component/ui/select' import { Textarea } from '@/component/ui/textarea' import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' +import { cellAddressToCoordinates } from '@/lib/cell-utils' import { getElementDefinition } from '@/lib/element-registry' import { getLatestFileForTemplate } from '@/service/fileApiService' import { Template, TemplateElement } from '@/type/template' @@ -381,7 +382,33 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { if (engineRef.current) { const element = template.elements.find(el => el.id === elementId) if (element && element.targetCells) { - element.targetCells.forEach(tc => { + const cells = element.targetCells + + cells.forEach((tc, idx) => { + let cellValue: any = value + + // Специальная обработка для массивов эталонов + if (element.type === 'standards' && Array.isArray(value)) { + cellValue = getStandardById(value[idx] as string)?.name || '' + } + + // Специальная обработка для условий калибровки + if ( + element.type === 'calibration-conditions' && + typeof value === 'object' && + value !== null + ) { + const propsOrder = [ + 'temperature', + 'humidity', + 'pressure', + 'voltage', + 'frequency', + 'lastUpdated', + ] as const + cellValue = (value as any)[propsOrder[idx]] ?? '' + } + const { row, col } = cellAddressToCoordinates(tc.cell) const sheetName = tc.sheet === 'R' || tc.sheet === 'Calculations' ? 'R' : 'L' @@ -389,7 +416,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { sheetName, row, col, - value + cellValue ) }) engineRef.current.debouncedRecalc() @@ -444,18 +471,78 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { if (def?.mapToCells) cells = def.mapToCells(el as any) } - cells?.forEach(tc => { + // === Новая логика распределения значений по ячейкам === + if (!cells || cells.length === 0) return + + // 1) Массив (например, standards) -> каждой ячейке своё значение + if (el.type === 'standards' && Array.isArray(value)) { + cells.forEach((tc, idx) => { + const cellValue = getStandardById(value[idx] as string)?.name || '' + const { row, col } = cellAddressToCoordinates(tc.cell) + if (tc.sheet === 'Calculations') alert('Лист Calculations') + const sheetName = + tc.sheet === 'R' || tc.sheet === 'Calculations' ? 'R' : 'L' + engineRef.current.setCellValueWithoutRecalc( + sheetName, + row, + col, + cellValue + ) + }) + return + } + + // Обработка любых других массивов + if (Array.isArray(value)) { + cells.forEach((tc, idx) => { + const cellValue = value[idx] ?? '' + const { row, col } = cellAddressToCoordinates(tc.cell) + const sheetName = + tc.sheet === 'R' || tc.sheet === 'Calculations' ? 'R' : 'L' + engineRef.current.setCellValueWithoutRecalc( + sheetName, + row, + col, + cellValue + ) + }) + return + } + + // 2) Объект условия калибровки -> пишем свойства по порядку + if ( + el.type === 'calibration-conditions' && + typeof value === 'object' && + value !== null + ) { + const propsOrder = [ + 'temperature', + 'humidity', + 'pressure', + 'voltage', + 'frequency', + 'lastUpdated', + ] as const + cells.forEach((tc, idx) => { + const cellValue = (value as any)[propsOrder[idx]] ?? '' + const { row, col } = cellAddressToCoordinates(tc.cell) + const sheetName = + tc.sheet === 'R' || tc.sheet === 'Calculations' ? 'R' : 'L' + engineRef.current.setCellValueWithoutRecalc( + sheetName, + row, + col, + cellValue + ) + }) + return + } + + // 3) Примитив или прочие объекты -> одно значение во все ячейки + cells.forEach(tc => { const { row, col } = cellAddressToCoordinates(tc.cell) const sheetName = tc.sheet === 'R' || tc.sheet === 'Calculations' ? 'R' : 'L' - console.log( - '[DBG] write to', - tc.cell, - 'sheet', - sheetName, - 'value', - value - ) engineRef.current.setCellValueWithoutRecalc( sheetName, row, @@ -546,9 +633,10 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { const protocolNameElement = template.elements.find( el => el.name === 'protocolName' || el.label === 'Название протокола' ) - const canSave = protocolNameElement - ? !!formData[protocolNameElement.id]?.trim() - : false + // const canSave = protocolNameElement + // ? !!formData[protocolNameElement.id]?.trim() + // : false + const canSave = true return (
@@ -591,9 +679,7 @@ const ProtocolForm: FC = ({ template, onSave, onBack }) => { {/* Всегда монтируем DualSpreadsheet, скрываем/показываем через hidden */}
(engineRef.current = engine)}