major(fullstack): Add dynamic section spawning, removal of app. form create DTOs,
This commit is contained in:
197
legalconsenthub/app/composables/useSectionSpawning.ts
Normal file
197
legalconsenthub/app/composables/useSectionSpawning.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import type { FormElementDto, FormElementSectionDto, SectionSpawnTriggerDto } from '~~/.api-client'
|
||||
import { VisibilityConditionOperator, VisibilityConditionType } from '~~/.api-client'
|
||||
|
||||
export function useSectionSpawning() {
|
||||
function processSpawnTriggers(
|
||||
sections: FormElementSectionDto[],
|
||||
updatedFormElements: FormElementDto[]
|
||||
): FormElementSectionDto[] {
|
||||
let resultSections = sections
|
||||
|
||||
for (const formElement of updatedFormElements) {
|
||||
if (!formElement.sectionSpawnTrigger || !formElement.reference) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract trigger configuration and current element value
|
||||
const trigger = formElement.sectionSpawnTrigger
|
||||
const triggerValue = getFormElementValue(formElement)
|
||||
const shouldSpawn = shouldSpawnSection(trigger, triggerValue)
|
||||
// Use resultSections to check for existing spawned sections (in case multiple spawns happen)
|
||||
const existingSpawnedSections = getSpawnedSectionsForElement(resultSections, formElement.reference)
|
||||
|
||||
// Handle three spawn states:
|
||||
// 1. Condition met but no section spawned yet → create new section
|
||||
if (shouldSpawn && existingSpawnedSections.length === 0) {
|
||||
resultSections = spawnNewSection(resultSections, formElement, trigger, triggerValue)
|
||||
}
|
||||
// 2. Condition no longer met but section exists → remove spawned section
|
||||
else if (!shouldSpawn && existingSpawnedSections.length > 0) {
|
||||
resultSections = removeSpawnedSections(resultSections, formElement.reference)
|
||||
}
|
||||
// 3. Condition still met and section exists → update section titles if value changed
|
||||
else if (shouldSpawn && existingSpawnedSections.length > 0 && triggerValue) {
|
||||
resultSections = updateSpawnedSectionTitles(resultSections, formElement.reference, trigger, triggerValue)
|
||||
}
|
||||
}
|
||||
|
||||
return resultSections
|
||||
}
|
||||
|
||||
function spawnNewSection(
|
||||
sections: FormElementSectionDto[],
|
||||
element: FormElementDto,
|
||||
trigger: SectionSpawnTriggerDto,
|
||||
triggerValue: string
|
||||
): FormElementSectionDto[] {
|
||||
const templateSection = findTemplateSection(sections, trigger.templateReference)
|
||||
if (!templateSection) {
|
||||
return sections
|
||||
}
|
||||
|
||||
const newSection = spawnSectionFromTemplate(templateSection, element.reference!, triggerValue)
|
||||
return sections.concat(newSection as FormElementSectionDto)
|
||||
}
|
||||
|
||||
function updateSpawnedSectionTitles(
|
||||
sections: FormElementSectionDto[],
|
||||
elementReference: string,
|
||||
trigger: SectionSpawnTriggerDto,
|
||||
triggerValue: string
|
||||
): FormElementSectionDto[] {
|
||||
const template = findTemplateSection(sections, trigger.templateReference)
|
||||
if (!template) {
|
||||
return sections
|
||||
}
|
||||
|
||||
const hasTitleTemplate = template.titleTemplate
|
||||
const hasShortTitleTemplate = template.shortTitle?.includes('{{triggerValue}}')
|
||||
const hasDescriptionTemplate = template.description?.includes('{{triggerValue}}')
|
||||
|
||||
if (!hasTitleTemplate && !hasShortTitleTemplate && !hasDescriptionTemplate) {
|
||||
return sections
|
||||
}
|
||||
|
||||
return sections.map((section) => {
|
||||
if (section.spawnedFromElementReference === elementReference && !section.isTemplate) {
|
||||
const sectionUpdate: Partial<FormElementSectionDto> = {}
|
||||
|
||||
if (hasTitleTemplate) {
|
||||
sectionUpdate.title = interpolateTitle(template.titleTemplate!, triggerValue)
|
||||
}
|
||||
|
||||
if (hasShortTitleTemplate && template.shortTitle) {
|
||||
sectionUpdate.shortTitle = interpolateTitle(template.shortTitle, triggerValue)
|
||||
}
|
||||
|
||||
if (hasDescriptionTemplate && template.description) {
|
||||
sectionUpdate.description = interpolateTitle(template.description, triggerValue)
|
||||
}
|
||||
|
||||
return { ...section, ...sectionUpdate }
|
||||
}
|
||||
return section
|
||||
})
|
||||
}
|
||||
|
||||
function removeSpawnedSections(sections: FormElementSectionDto[], elementReference: string): FormElementSectionDto[] {
|
||||
return sections.filter((section) => section.spawnedFromElementReference !== elementReference || section.isTemplate)
|
||||
}
|
||||
|
||||
function spawnSectionFromTemplate(
|
||||
templateSection: FormElementSectionDto,
|
||||
triggerElementReference: string,
|
||||
triggerValue: string
|
||||
): FormElementSectionDto {
|
||||
const clonedSection = JSON.parse(JSON.stringify(templateSection)) as FormElementSectionDto
|
||||
|
||||
const title = templateSection.titleTemplate
|
||||
? interpolateTitle(templateSection.titleTemplate, triggerValue)
|
||||
: templateSection.title
|
||||
|
||||
const shortTitle = templateSection.shortTitle?.includes('{{triggerValue}}')
|
||||
? interpolateTitle(templateSection.shortTitle, triggerValue)
|
||||
: templateSection.shortTitle
|
||||
|
||||
const description = templateSection.description?.includes('{{triggerValue}}')
|
||||
? interpolateTitle(templateSection.description, triggerValue)
|
||||
: templateSection.description
|
||||
|
||||
return {
|
||||
...clonedSection,
|
||||
id: undefined,
|
||||
applicationFormId: undefined,
|
||||
title,
|
||||
shortTitle,
|
||||
description,
|
||||
isTemplate: false,
|
||||
spawnedFromElementReference: triggerElementReference,
|
||||
formElementSubSections: clonedSection.formElementSubSections.map((subsection) => ({
|
||||
...subsection,
|
||||
id: undefined,
|
||||
formElementSectionId: undefined,
|
||||
formElements: subsection.formElements.map((element) => ({
|
||||
...element,
|
||||
id: undefined,
|
||||
formElementSubSectionId: undefined
|
||||
}))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function shouldSpawnSection(trigger: SectionSpawnTriggerDto, triggerElementValue: string): boolean {
|
||||
const operator = trigger.sectionSpawnOperator || VisibilityConditionOperator.Equals
|
||||
const isConditionMet = evaluateCondition(triggerElementValue, trigger.sectionSpawnExpectedValue || '', operator)
|
||||
|
||||
return trigger.sectionSpawnConditionType === VisibilityConditionType.Show ? isConditionMet : !isConditionMet
|
||||
}
|
||||
|
||||
function getSpawnedSectionsForElement(
|
||||
sections: FormElementSectionDto[],
|
||||
elementReference: string
|
||||
): FormElementSectionDto[] {
|
||||
return sections.filter((section) => !section.isTemplate && section.spawnedFromElementReference === elementReference)
|
||||
}
|
||||
|
||||
function findTemplateSection(
|
||||
sections: FormElementSectionDto[],
|
||||
templateReference: string
|
||||
): FormElementSectionDto | undefined {
|
||||
return sections.find((section) => section.isTemplate && section.templateReference === templateReference)
|
||||
}
|
||||
|
||||
function evaluateCondition(
|
||||
actualValue: string,
|
||||
expectedValue: string,
|
||||
operator: VisibilityConditionOperator
|
||||
): boolean {
|
||||
switch (operator) {
|
||||
case VisibilityConditionOperator.Equals:
|
||||
return actualValue.toLowerCase() === expectedValue.toLowerCase()
|
||||
case VisibilityConditionOperator.NotEquals:
|
||||
return actualValue.toLowerCase() !== expectedValue.toLowerCase()
|
||||
case VisibilityConditionOperator.IsEmpty:
|
||||
return actualValue === ''
|
||||
case VisibilityConditionOperator.IsNotEmpty:
|
||||
return actualValue !== ''
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function interpolateTitle(titleTemplate: string, triggerValue: string): string {
|
||||
return titleTemplate.replace(/\{\{triggerValue\}\}/g, triggerValue)
|
||||
}
|
||||
|
||||
function getFormElementValue(element: FormElementDto): string {
|
||||
if (element.type === 'TEXTAREA' || element.type === 'TEXTFIELD') {
|
||||
return element.options[0]?.value || ''
|
||||
}
|
||||
const selectedOption = element.options.find((option) => option.value === 'true')
|
||||
return selectedOption?.label || ''
|
||||
}
|
||||
|
||||
return {
|
||||
processSpawnTriggers
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user