diff --git a/api/legalconsenthub.yml b/api/legalconsenthub.yml index 05be21b..31a6ed8 100644 --- a/api/legalconsenthub.yml +++ b/api/legalconsenthub.yml @@ -224,10 +224,10 @@ paths: "503": $ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable" - /application-forms/{applicationFormId}/sections/{sectionId}/form-elements: + /application-forms/{applicationFormId}/subsections/{subsectionId}/form-elements: post: - summary: Add a new form element to a specific section - operationId: addFormElementToSection + summary: Add a new form element to a specific subsection + operationId: addFormElementToSubSection tags: - application-form parameters: @@ -238,13 +238,13 @@ paths: type: string format: uuid description: The ID of the application form - - name: sectionId + - name: subsectionId in: path required: true schema: type: string format: uuid - description: The ID of the form element section + description: The ID of the form element subsection - name: position in: query required: true @@ -1215,7 +1215,7 @@ components: required: - id - title - - formElements + - formElementSubSections - applicationFormId properties: id: @@ -1227,10 +1227,10 @@ components: type: string description: type: string - formElements: + formElementSubSections: type: array items: - $ref: "#/components/schemas/FormElementDto" + $ref: "#/components/schemas/FormElementSubSectionDto" applicationFormId: type: string format: uuid @@ -1239,7 +1239,7 @@ components: type: object required: - title - - elements + - subsections properties: title: type: string @@ -1247,16 +1247,16 @@ components: type: string description: type: string - elements: + subsections: type: array items: - $ref: "#/components/schemas/FormElementSnapshotDto" + $ref: "#/components/schemas/FormElementSubSectionSnapshotDto" CreateFormElementSectionDto: type: object required: - title - - formElements + - formElementSubSections properties: id: type: string @@ -1267,6 +1267,59 @@ components: type: string description: type: string + formElementSubSections: + type: array + items: + $ref: "#/components/schemas/CreateFormElementSubSectionDto" + + FormElementSubSectionDto: + type: object + required: + - id + - title + - formElements + - formElementSectionId + properties: + id: + type: string + format: uuid + title: + type: string + subtitle: + type: string + formElements: + type: array + items: + $ref: "#/components/schemas/FormElementDto" + formElementSectionId: + type: string + format: uuid + + FormElementSubSectionSnapshotDto: + type: object + required: + - title + - elements + properties: + title: + type: string + subtitle: + type: string + elements: + type: array + items: + $ref: "#/components/schemas/FormElementSnapshotDto" + + CreateFormElementSubSectionDto: + type: object + required: + - title + - formElements + properties: + title: + type: string + subtitle: + type: string formElements: type: array items: @@ -1278,7 +1331,7 @@ components: - id - options - type - - formElementSectionId + - formElementSubSectionId properties: id: type: string @@ -1293,7 +1346,7 @@ components: $ref: "#/components/schemas/FormOptionDto" type: $ref: "#/components/schemas/FormElementType" - formElementSectionId: + formElementSubSectionId: type: string format: uuid diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt index 998b11b..1c1be94 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt @@ -31,7 +31,7 @@ class ApplicationForm( var name: String = "", @OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL], orphanRemoval = true) var formElementSections: MutableList = mutableListOf(), - @OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL], orphanRemoval = true) + @OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL]) var versions: MutableList = mutableListOf(), @Column(nullable = false) var isTemplate: Boolean, diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormController.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormController.kt index 641ae1a..c0f97ba 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormController.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormController.kt @@ -113,17 +113,17 @@ class ApplicationFormController( @PreAuthorize( "hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')", ) - override fun addFormElementToSection( + override fun addFormElementToSubSection( applicationFormId: UUID, - sectionId: UUID, + subsectionId: UUID, position: Int, createFormElementDto: CreateFormElementDto, ): ResponseEntity = ResponseEntity.status(201).body( applicationFormMapper.toApplicationFormDto( - applicationFormService.addFormElementToSection( + applicationFormService.addFormElementToSubSection( applicationFormId, - sectionId, + subsectionId, createFormElementDto, position, ), diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt index 9610f66..d032eb4 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt @@ -34,23 +34,26 @@ class ApplicationFormMapper( status = applicationForm.status, ) - fun toApplicationForm(applicationForm: ApplicationFormDto): ApplicationForm = - ApplicationForm( - id = applicationForm.id, - name = applicationForm.name, - formElementSections = - applicationForm.formElementSections - .map { - formElementSectionMapper.toFormElementSection(it) - }.toMutableList(), - isTemplate = applicationForm.isTemplate, - organizationId = applicationForm.organizationId, - status = applicationForm.status, - createdBy = userMapper.toUser(applicationForm.createdBy), - lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy), - createdAt = applicationForm.createdAt, - modifiedAt = applicationForm.modifiedAt, - ) + fun toApplicationForm(applicationForm: ApplicationFormDto): ApplicationForm { + val form = + ApplicationForm( + id = applicationForm.id, + name = applicationForm.name, + isTemplate = applicationForm.isTemplate, + organizationId = applicationForm.organizationId, + status = applicationForm.status, + createdBy = userMapper.toUser(applicationForm.createdBy), + lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy), + createdAt = applicationForm.createdAt, + modifiedAt = applicationForm.modifiedAt, + ) + form.formElementSections = + applicationForm.formElementSections + .map { + formElementSectionMapper.toFormElementSection(it, form) + }.toMutableList() + return form + } fun toApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm { val currentUser = userService.getCurrentUser() diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormService.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormService.kt index 5860f9c..2bc945f 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormService.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormService.kt @@ -6,7 +6,6 @@ import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotCreatedExc import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotDeletedException import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotUpdatedException -import com.betriebsratkanzlei.legalconsenthub.error.FormElementSectionNotFoundException import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementMapper import com.betriebsratkanzlei.legalconsenthub.notification.NotificationService import com.betriebsratkanzlei.legalconsenthub.user.UserService @@ -57,6 +56,8 @@ class ApplicationFormService( fun updateApplicationForm(applicationFormDto: ApplicationFormDto): ApplicationForm { val existingApplicationForm = getApplicationFormById(applicationFormDto.id) + val existingSnapshot = versionService.createSnapshot(existingApplicationForm) + val applicationForm = applicationFormMapper.toApplicationForm(applicationFormDto) val updatedApplicationForm: ApplicationForm @@ -67,7 +68,9 @@ class ApplicationFormService( } val currentUser = userService.getCurrentUser() - if (versionService.hasChanges(existingApplicationForm, updatedApplicationForm)) { + val newSnapshot = versionService.createSnapshot(updatedApplicationForm) + + if (existingSnapshot != newSnapshot) { versionService.createVersion(updatedApplicationForm, currentUser) } @@ -132,25 +135,26 @@ class ApplicationFormService( notificationService.createNotificationForOrganization(createNotificationDto) } - fun addFormElementToSection( + fun addFormElementToSubSection( applicationFormId: UUID, - sectionId: UUID, + subsectionId: UUID, createFormElementDto: CreateFormElementDto, position: Int, ): ApplicationForm { val applicationForm = getApplicationFormById(applicationFormId) - val section = + val subsection = applicationForm.formElementSections - .find { it.id == sectionId } - ?: throw FormElementSectionNotFoundException(sectionId) + .flatMap { it.formElementSubSections } + .find { it.id == subsectionId } + ?: throw IllegalArgumentException("FormElementSubSection with id $subsectionId not found") - val newFormElement = formElementMapper.toFormElement(createFormElementDto, section) + val newFormElement = formElementMapper.toFormElement(createFormElementDto, subsection) - if (position >= 0 && position < section.formElements.size) { - section.formElements.add(position, newFormElement) + if (position >= 0 && position < subsection.formElements.size) { + subsection.formElements.add(position, newFormElement) } else { - section.formElements.add(newFormElement) + subsection.formElements.add(newFormElement) } val updatedApplicationForm = diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form_version/ApplicationFormVersionService.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form_version/ApplicationFormVersionService.kt index 1950ccd..2388461 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form_version/ApplicationFormVersionService.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form_version/ApplicationFormVersionService.kt @@ -6,11 +6,13 @@ import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundExcep import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormVersionNotFoundException import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection +import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSubSection import com.betriebsratkanzlei.legalconsenthub.form_element.FormOption import com.betriebsratkanzlei.legalconsenthub.user.User import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormSnapshotDto import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSectionSnapshotDto import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSnapshotDto +import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSubSectionSnapshotDto import com.betriebsratkanzlei.legalconsenthub_api.model.FormOptionDto import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.stereotype.Service @@ -47,15 +49,6 @@ class ApplicationFormVersionService( return versionRepository.save(version) } - fun hasChanges( - existingForm: ApplicationForm, - newForm: ApplicationForm, - ): Boolean { - val existingSnapshot = createSnapshot(existingForm) - val newSnapshot = createSnapshot(newForm) - return existingSnapshot != newSnapshot - } - fun getVersionsByApplicationFormId(applicationFormId: UUID): List = versionRepository.findByApplicationFormIdOrderByVersionNumberDesc(applicationFormId) @@ -99,7 +92,7 @@ class ApplicationFormVersionService( return restoredForm } - private fun createSnapshot(applicationForm: ApplicationForm): ApplicationFormSnapshotDto = + fun createSnapshot(applicationForm: ApplicationForm): ApplicationFormSnapshotDto = ApplicationFormSnapshotDto( name = applicationForm.name, status = applicationForm.status, @@ -110,19 +103,26 @@ class ApplicationFormVersionService( title = section.title, shortTitle = section.shortTitle, description = section.description, - elements = - section.formElements.map { element -> - FormElementSnapshotDto( - title = element.title, - description = element.description, - type = element.type, - options = - element.options.map { option -> - FormOptionDto( - value = option.value, - label = option.label, - processingPurpose = option.processingPurpose, - employeeDataCategory = option.employeeDataCategory, + subsections = + section.formElementSubSections.map { subsection -> + FormElementSubSectionSnapshotDto( + title = subsection.title, + subtitle = subsection.subtitle, + elements = + subsection.formElements.map { element -> + FormElementSnapshotDto( + title = element.title, + description = element.description, + type = element.type, + options = + element.options.map { option -> + FormOptionDto( + value = option.value, + label = option.label, + processingPurpose = option.processingPurpose, + employeeDataCategory = option.employeeDataCategory, + ) + }, ) }, ) @@ -143,25 +143,36 @@ class ApplicationFormVersionService( applicationForm = applicationForm, ) - sectionSnapshot.elements.forEach { elementSnapshot -> - val element = - FormElement( - title = elementSnapshot.title, - description = elementSnapshot.description, - type = elementSnapshot.type, + sectionSnapshot.subsections.forEach { subsectionSnapshot -> + val subsection = + FormElementSubSection( + title = subsectionSnapshot.title, + subtitle = subsectionSnapshot.subtitle, formElementSection = section, - options = - elementSnapshot.options - .map { optionDto -> - FormOption( - value = optionDto.value, - label = optionDto.label, - processingPurpose = optionDto.processingPurpose, - employeeDataCategory = optionDto.employeeDataCategory, - ) - }.toMutableList(), ) - section.formElements.add(element) + + subsectionSnapshot.elements.forEach { elementSnapshot -> + val element = + FormElement( + title = elementSnapshot.title, + description = elementSnapshot.description, + type = elementSnapshot.type, + formElementSubSection = subsection, + options = + elementSnapshot.options + .map { optionDto -> + FormOption( + value = optionDto.value, + label = optionDto.label, + processingPurpose = optionDto.processingPurpose, + employeeDataCategory = optionDto.employeeDataCategory, + ) + }.toMutableList(), + ) + subsection.formElements.add(element) + } + + section.formElementSubSections.add(subsection) } return section diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElement.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElement.kt index d7dc016..5091bfc 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElement.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElement.kt @@ -24,6 +24,6 @@ class FormElement( @Column(nullable = false) var type: FormElementType, @ManyToOne - @JoinColumn(name = "form_element_section_id", nullable = false) - var formElementSection: FormElementSection? = null, + @JoinColumn(name = "form_element_sub_section_id", nullable = false) + var formElementSubSection: FormElementSubSection? = null, ) diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementMapper.kt index 11f8ffd..e58fcf9 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementMapper.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementMapper.kt @@ -1,6 +1,5 @@ package com.betriebsratkanzlei.legalconsenthub.form_element -import com.betriebsratkanzlei.legalconsenthub.error.FormElementSectionNotFoundException import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementDto import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementDto import org.springframework.stereotype.Component @@ -8,39 +7,35 @@ import org.springframework.stereotype.Component @Component class FormElementMapper( private val formOptionMapper: FormOptionMapper, - private val formElementSectionRepository: FormElementSectionRepository, ) { fun toFormElementDto(formElement: FormElement): FormElementDto = FormElementDto( - id = formElement.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"), + id = formElement.id ?: throw IllegalStateException("FormElement ID must not be null!"), title = formElement.title, description = formElement.description, options = formElement.options.map { formOptionMapper.toFormOptionDto(it) }, type = formElement.type, - formElementSectionId = - formElement.formElementSection?.id - ?: throw IllegalStateException("FormElementSection ID must not be null!"), + formElementSubSectionId = + formElement.formElementSubSection?.id + ?: throw IllegalStateException("FormElementSubSection ID must not be null!"), ) - fun toFormElement(formElement: FormElementDto): FormElement { - val formElementSection = - formElementSectionRepository - .findById(formElement.formElementSectionId) - .orElseThrow { FormElementSectionNotFoundException(formElement.formElementSectionId) } - - return FormElement( + fun toFormElement( + formElement: FormElementDto, + formElementSubSection: FormElementSubSection, + ): FormElement = + FormElement( id = formElement.id, title = formElement.title, description = formElement.description, options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(), type = formElement.type, - formElementSection = formElementSection, + formElementSubSection = formElementSubSection, ) - } fun toFormElement( formElement: CreateFormElementDto, - formElementSection: FormElementSection, + formElementSubSection: FormElementSubSection, ): FormElement = FormElement( id = null, @@ -48,6 +43,6 @@ class FormElementMapper( description = formElement.description, options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(), type = formElement.type, - formElementSection = formElementSection, + formElementSubSection = formElementSubSection, ) } diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSection.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSection.kt index 4933237..97010fc 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSection.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSection.kt @@ -22,8 +22,8 @@ class FormElementSection( var shortTitle: String? = null, var description: String? = null, @OneToMany(mappedBy = "formElementSection", cascade = [CascadeType.ALL], orphanRemoval = true) - @OrderColumn(name = "form_element_order") - var formElements: MutableList = mutableListOf(), + @OrderColumn(name = "form_element_sub_section_order") + var formElementSubSections: MutableList = mutableListOf(), @ManyToOne @JoinColumn(name = "application_form_id", nullable = false) var applicationForm: ApplicationForm? = null, diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSectionMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSectionMapper.kt index cf3002f..98895a0 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSectionMapper.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSectionMapper.kt @@ -1,16 +1,13 @@ package com.betriebsratkanzlei.legalconsenthub.form_element import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm -import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository -import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementSectionDto import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSectionDto import org.springframework.stereotype.Component @Component class FormElementSectionMapper( - private val formElementMapper: FormElementMapper, - private val applicationFormRepository: ApplicationFormRepository, + private val formElementSubSectionMapper: FormElementSubSectionMapper, ) { fun toFormElementSectionDto(formElementSection: FormElementSection): FormElementSectionDto = FormElementSectionDto( @@ -18,26 +15,32 @@ class FormElementSectionMapper( title = formElementSection.title, description = formElementSection.description, shortTitle = formElementSection.shortTitle, - formElements = formElementSection.formElements.map { formElementMapper.toFormElementDto(it) }, + formElementSubSections = + formElementSection.formElementSubSections.map { + formElementSubSectionMapper.toFormElementSubSectionDto(it) + }, applicationFormId = formElementSection.applicationForm?.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"), ) - fun toFormElementSection(formElementSection: FormElementSectionDto): FormElementSection { - val applicationForm = - applicationFormRepository - .findById(formElementSection.applicationFormId) - .orElseThrow { ApplicationFormNotFoundException(formElementSection.applicationFormId) } - - return FormElementSection( - id = formElementSection.id, - title = formElementSection.title, - description = formElementSection.description, - shortTitle = formElementSection.shortTitle, - formElements = formElementSection.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(), - applicationForm = applicationForm, - ) + fun toFormElementSection( + formElementSection: FormElementSectionDto, + applicationForm: ApplicationForm, + ): FormElementSection { + val section = + FormElementSection( + id = formElementSection.id, + title = formElementSection.title, + description = formElementSection.description, + shortTitle = formElementSection.shortTitle, + applicationForm = applicationForm, + ) + section.formElementSubSections = + formElementSection.formElementSubSections + .map { formElementSubSectionMapper.toFormElementSubSection(it, section) } + .toMutableList() + return section } fun toFormElementSection( @@ -51,9 +54,9 @@ class FormElementSectionMapper( shortTitle = createFormElementSection.shortTitle, applicationForm = applicationForm, ) - formElementSection.formElements = - createFormElementSection.formElements - .map { formElementMapper.toFormElement(it, formElementSection) } + formElementSection.formElementSubSections = + createFormElementSection.formElementSubSections + .map { formElementSubSectionMapper.toFormElementSubSection(it, formElementSection) } .toMutableList() return formElementSection } diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSection.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSection.kt new file mode 100644 index 0000000..87f917d --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSection.kt @@ -0,0 +1,28 @@ +package com.betriebsratkanzlei.legalconsenthub.form_element + +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.OrderColumn +import java.util.UUID + +@Entity +class FormElementSubSection( + @Id + @GeneratedValue + var id: UUID? = null, + @Column(nullable = false) + var title: String, + var subtitle: String? = null, + @OneToMany(mappedBy = "formElementSubSection", cascade = [CascadeType.ALL], orphanRemoval = true) + @OrderColumn(name = "form_element_order") + var formElements: MutableList = mutableListOf(), + @ManyToOne + @JoinColumn(name = "form_element_section_id", nullable = false) + var formElementSection: FormElementSection? = null, +) diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSectionMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSectionMapper.kt new file mode 100644 index 0000000..5673c9d --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/form_element/FormElementSubSectionMapper.kt @@ -0,0 +1,56 @@ +package com.betriebsratkanzlei.legalconsenthub.form_element + +import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementSubSectionDto +import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSubSectionDto +import org.springframework.stereotype.Component + +@Component +class FormElementSubSectionMapper( + private val formElementMapper: FormElementMapper, +) { + fun toFormElementSubSectionDto(formElementSubSection: FormElementSubSection): FormElementSubSectionDto = + FormElementSubSectionDto( + id = formElementSubSection.id ?: throw IllegalStateException("FormElementSubSection ID must not be null!"), + title = formElementSubSection.title, + subtitle = formElementSubSection.subtitle, + formElements = formElementSubSection.formElements.map { formElementMapper.toFormElementDto(it) }, + formElementSectionId = + formElementSubSection.formElementSection?.id + ?: throw IllegalStateException("FormElementSection ID must not be null!"), + ) + + fun toFormElementSubSection( + formElementSubSection: FormElementSubSectionDto, + formElementSection: FormElementSection, + ): FormElementSubSection { + val subsection = + FormElementSubSection( + id = formElementSubSection.id, + title = formElementSubSection.title, + subtitle = formElementSubSection.subtitle, + formElementSection = formElementSection, + ) + subsection.formElements = + formElementSubSection.formElements + .map { formElementMapper.toFormElement(it, subsection) } + .toMutableList() + return subsection + } + + fun toFormElementSubSection( + createFormElementSubSection: CreateFormElementSubSectionDto, + formElementSection: FormElementSection, + ): FormElementSubSection { + val formElementSubSection = + FormElementSubSection( + title = createFormElementSubSection.title, + subtitle = createFormElementSubSection.subtitle, + formElementSection = formElementSection, + ) + formElementSubSection.formElements = + createFormElementSubSection.formElements + .map { formElementMapper.toFormElement(it, formElementSubSection) } + .toMutableList() + return formElementSubSection + } +} diff --git a/legalconsenthub/app/components/FormEngine.vue b/legalconsenthub/app/components/FormEngine.vue index 9cca2df..6ac5c7e 100644 --- a/legalconsenthub/app/components/FormEngine.vue +++ b/legalconsenthub/app/components/FormEngine.vue @@ -23,7 +23,7 @@ - + diff --git a/legalconsenthub/app/components/FormStepperWithNavigation.vue b/legalconsenthub/app/components/FormStepperWithNavigation.vue index 745d971..35462d1 100644 --- a/legalconsenthub/app/components/FormStepperWithNavigation.vue +++ b/legalconsenthub/app/components/FormStepperWithNavigation.vue @@ -8,39 +8,49 @@ {{ currentFormElementSection.title }} - - - -
- - Prev - - - - Next - - -
- - Save - - - Submit - + + +
+ + Prev + + + + Next + + +
+ + Save + + + Submit +
- +
@@ -65,16 +75,28 @@ const { stepper, activeStepperItemIndex, stepperItems, currentFormElementSection computed(() => props.formElementSections) ) -const { addInputFormToApplicationForm } = useFormElementManagement(currentFormElementSection, props.applicationFormId) - onMounted(() => { if (props.initialSectionIndex !== undefined) { activeStepperItemIndex.value = props.initialSectionIndex } }) -async function handleAddInputForm(position: number) { - const updatedForm = await addInputFormToApplicationForm(position) +async function handleAddInputForm(position: number, subsectionId: string) { + const subsection = props.formElementSections + .flatMap((section) => section.formElementSubSections) + .find((sub) => sub.id === subsectionId) + + if (!subsection) { + return + } + + const { addFormElementToSubSection } = useFormElementManagement() + const updatedForm = await addFormElementToSubSection( + props.applicationFormId, + subsectionId, + subsection.formElements, + position + ) emit('add-input-form', updatedForm) } diff --git a/legalconsenthub/app/composables/applicationForm/useApplicationForm.ts b/legalconsenthub/app/composables/applicationForm/useApplicationForm.ts index dd752b8..4bc3995 100644 --- a/legalconsenthub/app/composables/applicationForm/useApplicationForm.ts +++ b/legalconsenthub/app/composables/applicationForm/useApplicationForm.ts @@ -76,25 +76,25 @@ export function useApplicationForm() { } } - async function addFormElementToSection( + async function addFormElementToSubSection( applicationFormId: string, - sectionId: string, + subsectionId: string, createFormElementDto: CreateFormElementDto, position: number ): Promise { - if (!applicationFormId || !sectionId) { - return Promise.reject(new Error('Application form ID or section ID missing')) + if (!applicationFormId || !subsectionId) { + return Promise.reject(new Error('Application form ID or subsection ID missing')) } try { - return await applicationFormApi.addFormElementToSection( + return await applicationFormApi.addFormElementToSubSection( applicationFormId, - sectionId, + subsectionId, createFormElementDto, position ) } catch (e: unknown) { - console.error(`Failed adding form element to section ${sectionId}:`, e) + console.error(`Failed adding form element to subsection ${subsectionId}:`, e) return Promise.reject(e) } } @@ -106,6 +106,6 @@ export function useApplicationForm() { updateApplicationForm, deleteApplicationFormById, submitApplicationForm, - addFormElementToSection + addFormElementToSubSection } } diff --git a/legalconsenthub/app/composables/applicationForm/useApplicationFormApi.ts b/legalconsenthub/app/composables/applicationForm/useApplicationFormApi.ts index 46cb94b..2ff12b4 100644 --- a/legalconsenthub/app/composables/applicationForm/useApplicationFormApi.ts +++ b/legalconsenthub/app/composables/applicationForm/useApplicationFormApi.ts @@ -54,15 +54,15 @@ export function useApplicationFormApi() { return applicationFormApiClient.submitApplicationForm({ id }) } - async function addFormElementToSection( + async function addFormElementToSubSection( applicationFormId: string, - sectionId: string, + subsectionId: string, createFormElementDto: CreateFormElementDto, position: number ): Promise { - return applicationFormApiClient.addFormElementToSection({ + return applicationFormApiClient.addFormElementToSubSection({ applicationFormId, - sectionId, + subsectionId, createFormElementDto, position }) @@ -75,6 +75,6 @@ export function useApplicationFormApi() { updateApplicationForm, deleteApplicationFormById, submitApplicationForm, - addFormElementToSection + addFormElementToSubSection } } diff --git a/legalconsenthub/app/composables/useFormElementManagement.ts b/legalconsenthub/app/composables/useFormElementManagement.ts index bff9739..7a943a3 100644 --- a/legalconsenthub/app/composables/useFormElementManagement.ts +++ b/legalconsenthub/app/composables/useFormElementManagement.ts @@ -1,18 +1,14 @@ -import type { ApplicationFormDto, CreateFormElementDto, FormElementSectionDto } from '~~/.api-client' -import type { MaybeRefOrGetter } from 'vue' +import type { ApplicationFormDto, CreateFormElementDto, FormElementDto } from '~~/.api-client' -export function useFormElementManagement( - currentFormElementSection: MaybeRefOrGetter, - applicationFormId?: string -) { - const { addFormElementToSection } = useApplicationForm() - - async function addInputFormToApplicationForm(position: number): Promise { - const section = toValue(currentFormElementSection) - if (!section) return - - const { formElements } = section +export function useFormElementManagement() { + const applicationForm = useApplicationForm() + async function addFormElementToSubSection( + applicationFormId: string | undefined, + subsectionId: string, + formElements: FormElementDto[], + position: number + ): Promise { const inputFormElement: CreateFormElementDto = { title: 'Formular ergänzen', description: 'Bitte fügen Sie hier Ihre Ergänzungen ein.', @@ -29,7 +25,7 @@ export function useFormElementManagement( if (applicationFormId) { try { - return await addFormElementToSection(applicationFormId, section.id, inputFormElement, position + 1) + return await applicationForm.addFormElementToSubSection(applicationFormId, subsectionId, inputFormElement, position + 1) } catch (error) { console.error('Failed to add form element:', error) throw error @@ -42,6 +38,6 @@ export function useFormElementManagement( } return { - addInputFormToApplicationForm + addFormElementToSubSection } } diff --git a/legalconsenthub/app/pages/application-forms/[id]/[sectionIndex].vue b/legalconsenthub/app/pages/application-forms/[id]/[sectionIndex].vue index 742d0dc..a7b6d35 100644 --- a/legalconsenthub/app/pages/application-forms/[id]/[sectionIndex].vue +++ b/legalconsenthub/app/pages/application-forms/[id]/[sectionIndex].vue @@ -83,7 +83,9 @@ const validationStatus = ref(ComplianceStatus.NonCritical) const allFormElements = computed(() => { return ( - applicationForm.value?.formElementSections?.flatMap((section: FormElementSectionDto) => section.formElements) ?? [] + applicationForm.value?.formElementSections?.flatMap((section: FormElementSectionDto) => + section.formElementSubSections.flatMap((subsection) => subsection.formElements) + ) ?? [] ) }) diff --git a/legalconsenthub/app/pages/create.vue b/legalconsenthub/app/pages/create.vue index 3e3144a..81884e4 100644 --- a/legalconsenthub/app/pages/create.vue +++ b/legalconsenthub/app/pages/create.vue @@ -78,8 +78,8 @@ const validationStatus = ref(ComplianceStatus.NonCritical) const allFormElements = computed(() => { return ( - applicationFormTemplate.value?.formElementSections?.flatMap( - (section: FormElementSectionDto) => section.formElements + applicationFormTemplate.value?.formElementSections?.flatMap((section: FormElementSectionDto) => + section.formElementSubSections.flatMap((subsection) => subsection.formElements) ) ?? [] ) }) diff --git a/legalconsenthub/app/utils/formDiff.ts b/legalconsenthub/app/utils/formDiff.ts index 2bb51ef..8a7a480 100644 --- a/legalconsenthub/app/utils/formDiff.ts +++ b/legalconsenthub/app/utils/formDiff.ts @@ -72,8 +72,10 @@ export function compareApplicationForms( 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) { + for (const subsection of section.formElementSubSections) { + for (const element of subsection.formElements) { elements.push({ element, sectionTitle: section.title }) + } } } return elements @@ -84,8 +86,10 @@ function flattenSnapshotElements( ): Array<{ element: FormElementSnapshotDto; sectionTitle: string }> { const elements: Array<{ element: FormElementSnapshotDto; sectionTitle: string }> = [] for (const section of snapshot.sections) { - for (const element of section.elements) { + for (const subsection of section.subsections) { + for (const element of subsection.elements) { elements.push({ element, sectionTitle: section.title }) + } } } return elements