feat(fullstack): Add form element section and stepper

This commit is contained in:
2025-06-01 18:16:38 +02:00
parent 7a9809909b
commit cb9abeed7f
15 changed files with 497 additions and 196 deletions

View File

@@ -665,7 +665,7 @@ components:
required:
- id
- name
- formElements
- formElementSections
- isTemplate
- organizationId
- createdBy
@@ -678,10 +678,10 @@ components:
format: uuid
name:
type: string
formElements:
formElementSections:
type: array
items:
$ref: "#/components/schemas/FormElementDto"
$ref: "#/components/schemas/FormElementSectionDto"
isTemplate:
type: boolean
organizationId:
@@ -700,16 +700,16 @@ components:
CreateApplicationFormDto:
required:
- name
- formElements
- formElementSections
- isTemplate
type: object
properties:
name:
type: string
formElements:
formElementSections:
type: array
items:
$ref: "#/components/schemas/CreateFormElementDto"
$ref: "#/components/schemas/CreateFormElementSectionDto"
isTemplate:
type: boolean
default: false
@@ -728,21 +728,64 @@ components:
items:
$ref: "#/components/schemas/ApplicationFormDto"
####### Form #######
FormElementDto:
FormElementSectionDto:
type: object
required:
- id
- title
- formElements
- applicationFormId
- options
- type
properties:
id:
type: string
format: uuid
title:
type: string
shortTitle:
type: string
description:
type: string
formElements:
type: array
items:
$ref: "#/components/schemas/FormElementDto"
applicationFormId:
type: string
format: uuid
CreateFormElementSectionDto:
type: object
required:
- title
- formElements
properties:
id:
type: string
format: uuid
title:
type: string
shortTitle:
type: string
description:
type: string
formElements:
type: array
items:
$ref: "#/components/schemas/CreateFormElementDto"
FormElementDto:
type: object
required:
- id
- options
- type
- formElementSectionId
properties:
id:
type: string
format: uuid
title:
type: string
description:
@@ -753,6 +796,9 @@ components:
$ref: "#/components/schemas/FormOptionDto"
type:
$ref: "#/components/schemas/FormElementType"
formElementSectionId:
type: string
format: uuid
CreateFormElementDto:
type: object

View File

@@ -1,6 +1,6 @@
package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection
import com.betriebsratkanzlei.legalconsenthub.user.User
import jakarta.persistence.AttributeOverride
import jakarta.persistence.AttributeOverrides
@@ -29,7 +29,7 @@ class ApplicationForm(
var name: String = "",
@OneToMany(mappedBy = "applicationForm", cascade = [CascadeType.ALL], orphanRemoval = true)
var formElements: MutableList<FormElement> = mutableListOf(),
var formElementSections: MutableList<FormElementSection> = mutableListOf(),
@Column(nullable = false)
var isTemplate: Boolean,

View File

@@ -1,6 +1,6 @@
package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementMapper
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSectionMapper
import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtTokenPrincipal
import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub.user.UserMapper
@@ -11,12 +11,12 @@ import org.springframework.stereotype.Component
import java.time.LocalDateTime
@Component
class ApplicationFormMapper(private val formElementMapper: FormElementMapper, private val userMapper: UserMapper) {
class ApplicationFormMapper(private val formElementSectionMapper: FormElementSectionMapper, private val userMapper: UserMapper) {
fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto {
return ApplicationFormDto(
id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
name = applicationForm.name,
formElements = applicationForm.formElements.map { formElementMapper.toFormElementDto(it) },
formElementSections = applicationForm.formElementSections.map { formElementSectionMapper.toFormElementSectionDto(it) },
isTemplate = applicationForm.isTemplate,
organizationId = applicationForm.organizationId,
createdBy = userMapper.toUserDto(applicationForm.createdBy),
@@ -30,7 +30,7 @@ class ApplicationFormMapper(private val formElementMapper: FormElementMapper, pr
return ApplicationForm(
id = applicationForm.id,
name = applicationForm.name,
formElements = applicationForm.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(),
formElementSections = applicationForm.formElementSections.map { formElementSectionMapper.toFormElementSection(it) }.toMutableList(),
isTemplate = applicationForm.isTemplate,
organizationId = applicationForm.organizationId,
createdBy = userMapper.toUser(applicationForm.createdBy),
@@ -53,8 +53,8 @@ class ApplicationFormMapper(private val formElementMapper: FormElementMapper, pr
createdBy = createdBy,
lastModifiedBy = lastModifiedBy,
)
applicationForm.formElements = createApplicationFormDto.formElements
.map { formElementMapper.toFormElement(it, applicationForm) }
applicationForm.formElementSections = createApplicationFormDto.formElementSections
.map { formElementSectionMapper.toFormElementSection(it, applicationForm) }
.toMutableList()
return applicationForm
}

View File

@@ -0,0 +1,5 @@
package com.betriebsratkanzlei.legalconsenthub.error
import java.util.UUID
class FormElementSectionNotFoundException(id: UUID): RuntimeException("Couldn't find form element section with ID: $id")

View File

@@ -1,18 +1,16 @@
package com.betriebsratkanzlei.legalconsenthub.form_element;
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import com.betriebsratkanzlei.legalconsenthub.comment.Comment
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementType
import jakarta.persistence.CascadeType
import jakarta.persistence.CollectionTable
import jakarta.persistence.Column
import jakarta.persistence.ElementCollection
import jakarta.persistence.Embeddable
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 java.util.UUID;
@@ -22,10 +20,6 @@ class FormElement(
@GeneratedValue
var id: UUID? = null,
@ManyToOne
@JoinColumn(name = "application_form_id", nullable = false)
var applicationForm: ApplicationForm? = null,
var title: String? = null,
var description: String? = null,
@@ -35,5 +29,9 @@ class FormElement(
var options: MutableList<FormOption> = mutableListOf(),
@Column(nullable = false)
var type: FormElementType
var type: FormElementType,
@ManyToOne
@JoinColumn(name = "form_element_section_id", nullable = false)
var formElementSection: FormElementSection? = null
)

View File

@@ -1,8 +1,6 @@
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.error.FormElementSectionNotFoundException
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementDto
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementDto
import org.springframework.stereotype.Component
@@ -10,7 +8,7 @@ import org.springframework.stereotype.Component
@Component
class FormElementMapper(
private val formOptionMapper: FormOptionMapper,
private val applicationFormRepository: ApplicationFormRepository
private val formElementSectionRepository: FormElementSectionRepository
) {
fun toFormElementDto(formElement: FormElement): FormElementDto {
return FormElementDto(
@@ -19,14 +17,14 @@ class FormElementMapper(
description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOptionDto(it) },
type = formElement.type,
applicationFormId = formElement.applicationForm?.id
?: throw IllegalStateException("ApplicationForm ID must not be null!")
formElementSectionId = formElement.formElementSection?.id
?: throw IllegalStateException("FormElementSection ID must not be null!")
)
}
fun toFormElement(formElement: FormElementDto): FormElement {
val applicationForm = applicationFormRepository.findById(formElement.applicationFormId)
.orElseThrow { ApplicationFormNotFoundException(formElement.applicationFormId) }
val formElementSection = formElementSectionRepository.findById(formElement.formElementSectionId)
.orElseThrow { FormElementSectionNotFoundException(formElement.formElementSectionId) }
return FormElement(
id = formElement.id,
@@ -34,18 +32,18 @@ class FormElementMapper(
description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
type = formElement.type,
applicationForm = applicationForm
formElementSection = formElementSection
)
}
fun toFormElement(formElement: CreateFormElementDto, applicationForm: ApplicationForm): FormElement {
fun toFormElement(formElement: CreateFormElementDto, formElementSection: FormElementSection): FormElement {
return FormElement(
id = null,
title = formElement.title,
description = formElement.description,
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
type = formElement.type,
applicationForm = applicationForm
formElementSection = formElementSection
)
}
}

View File

@@ -0,0 +1,36 @@
package com.betriebsratkanzlei.legalconsenthub.form_element;
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import jakarta.persistence.CascadeType
import jakarta.persistence.CollectionTable
import jakarta.persistence.Column
import jakarta.persistence.ElementCollection
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 java.util.UUID;
@Entity
class FormElementSection(
@Id
@GeneratedValue
var id: UUID? = null,
@Column(nullable = false)
var title: String,
var shortTitle: String? = null,
var description: String? = null,
@OneToMany(mappedBy = "formElementSection", cascade = [CascadeType.ALL], orphanRemoval = true)
var formElements: MutableList<FormElement> = mutableListOf(),
@ManyToOne
@JoinColumn(name = "application_form_id", nullable = false)
var applicationForm: ApplicationForm? = null,
)

View File

@@ -0,0 +1,53 @@
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
) {
fun toFormElementSectionDto(formElementSection: FormElementSection): FormElementSectionDto {
return FormElementSectionDto(
id = formElementSection.id ?: throw IllegalStateException("FormElementSection ID must not be null!"),
title = formElementSection.title,
description = formElementSection.description,
shortTitle = formElementSection.shortTitle,
formElements = formElementSection.formElements.map { formElementMapper.toFormElementDto(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(createFormElementSection: CreateFormElementSectionDto, applicationForm: ApplicationForm): FormElementSection {
val formElementSection = FormElementSection(
title = createFormElementSection.title,
description = createFormElementSection.description,
shortTitle = createFormElementSection.shortTitle,
applicationForm = applicationForm
)
formElementSection.formElements = createFormElementSection.formElements
.map { formElementMapper.toFormElement(it, formElementSection) }
.toMutableList()
return formElementSection
}
}

View File

@@ -0,0 +1,8 @@
package com.betriebsratkanzlei.legalconsenthub.form_element
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import java.util.UUID
@Repository
interface FormElementSectionRepository : JpaRepository<FormElementSection, UUID>