import type { ApplicationFormDto, ApplicationFormSnapshotDto, FormElementDto, FormElementSnapshotDto, FormOptionDto } from '~~/.api-client' import type { FormDiff, ElementModification, OptionChange, OptionModification } from '~~/types/formDiff' export function compareApplicationForms( current: ApplicationFormDto, versionSnapshot: ApplicationFormSnapshotDto ): FormDiff { const diff: FormDiff = { elementsAdded: [], elementsRemoved: [], elementsModified: [] } const currentElements = flattenFormElements(current) const versionElements = flattenSnapshotElements(versionSnapshot) const currentElementsMap = new Map( currentElements.map((el, idx) => [ createElementKey(el.element, idx), { element: el.element, index: idx, sectionTitle: el.sectionTitle } ]) ) const versionElementsMap = new Map( versionElements.map((el, idx) => [ createSnapshotElementKey(el.element, idx), { element: el.element, index: idx, sectionTitle: el.sectionTitle } ]) ) for (const [key, currentData] of currentElementsMap) { if (!versionElementsMap.has(key)) { diff.elementsAdded.push({ sectionTitle: currentData.sectionTitle, title: currentData.element.title, type: currentData.element.type, position: currentData.index }) } else { const versionData = versionElementsMap.get(key)! const modifications = compareElements( currentData.element, versionData.element, currentData.sectionTitle, currentData.index ) if (modifications) { diff.elementsModified.push(modifications) } } } for (const [key, versionData] of versionElementsMap) { if (!currentElementsMap.has(key)) { diff.elementsRemoved.push({ sectionTitle: versionData.sectionTitle, title: versionData.element.title, type: versionData.element.type, position: versionData.index }) } } return diff } function flattenFormElements(form: ApplicationFormDto): Array<{ element: FormElementDto; sectionTitle: string }> { const elements: Array<{ element: FormElementDto; sectionTitle: string }> = [] for (const section of form.formElementSections) { for (const element of section.formElements) { elements.push({ element, sectionTitle: section.title }) } } return elements } function flattenSnapshotElements( snapshot: ApplicationFormSnapshotDto ): Array<{ element: FormElementSnapshotDto; sectionTitle: string }> { const elements: Array<{ element: FormElementSnapshotDto; sectionTitle: string }> = [] for (const section of snapshot.sections) { for (const element of section.elements) { elements.push({ element, sectionTitle: section.title }) } } return elements } function createElementKey(element: FormElementDto, index: number): string { return `${index}-${element.type}-${element.title || 'untitled'}` } function createSnapshotElementKey(element: FormElementSnapshotDto, index: number): string { return `${index}-${element.type}-${element.title || 'untitled'}` } function compareElements( current: FormElementDto, version: FormElementSnapshotDto, sectionTitle: string, position: number ): ElementModification | null { const optionsDiff = compareOptions(current.options, version.options) if (optionsDiff.added.length > 0 || optionsDiff.removed.length > 0 || optionsDiff.modified.length > 0) { return { sectionTitle, position, optionsAdded: optionsDiff.added, optionsRemoved: optionsDiff.removed, optionsModified: optionsDiff.modified } } return null } function compareOptions( currentOptions: FormOptionDto[], versionOptions: FormOptionDto[] ): { added: OptionChange[]; removed: OptionChange[]; modified: OptionModification[] } { const result = { added: [] as OptionChange[], removed: [] as OptionChange[], modified: [] as OptionModification[] } const currentMap = new Map(currentOptions.map((opt) => [opt.value, opt])) const versionMap = new Map(versionOptions.map((opt) => [opt.value, opt])) for (const [value, currentOpt] of currentMap) { if (!versionMap.has(value)) { result.added.push({ value: currentOpt.value, label: currentOpt.label }) } else { const versionOpt = versionMap.get(value)! if (currentOpt.label !== versionOpt.label) { result.modified.push({ value, labelChanged: { from: versionOpt.label, to: currentOpt.label } }) } } } for (const [value, versionOpt] of versionMap) { if (!currentMap.has(value)) { result.removed.push({ value: versionOpt.value, label: versionOpt.label }) } } return result }