feat(#5): Add title-body control element that can be added dynamically, refactored sectionIndex/create
This commit is contained in:
8
CHANGELOG.md
Normal file
8
CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-11-02 - Form Element Management API
|
||||
|
||||
- New input field with title and text needed to be added to application forms
|
||||
- Adding new form elements to existing application forms caused type conflicts: frontend needed to add `CreateFormElementDto` (without ID) to arrays of `FormElementDto` (with ID)
|
||||
- Implemented separate endpoint for adding form elements: `POST /application-forms/{applicationFormId}/sections/{sectionId}/form-elements`
|
||||
|
||||
@@ -224,6 +224,57 @@ paths:
|
||||
"503":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
|
||||
|
||||
/application-forms/{applicationFormId}/sections/{sectionId}/form-elements:
|
||||
post:
|
||||
summary: Add a new form element to a specific section
|
||||
operationId: addFormElementToSection
|
||||
tags:
|
||||
- application-form
|
||||
parameters:
|
||||
- name: applicationFormId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: The ID of the application form
|
||||
- name: sectionId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: The ID of the form element section
|
||||
- name: position
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The position to insert the form element
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateFormElementDto"
|
||||
responses:
|
||||
"201":
|
||||
description: Form element successfully added
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ApplicationFormDto"
|
||||
"400":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/NotFound"
|
||||
"500":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServerError"
|
||||
"503":
|
||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
|
||||
|
||||
####### Application Form Templates #######
|
||||
/application-form-templates:
|
||||
get:
|
||||
@@ -1080,6 +1131,7 @@ components:
|
||||
- RADIOBUTTON
|
||||
- TEXTFIELD
|
||||
- SWITCH
|
||||
- TITLE_BODY_TEXTFIELDS
|
||||
|
||||
####### UserDto #######
|
||||
UserDto:
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.betriebsratkanzlei.legalconsenthub.application_form
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.api.ApplicationFormApi
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.PagedApplicationFormDto
|
||||
import org.springframework.core.io.ByteArrayResource
|
||||
import org.springframework.core.io.Resource
|
||||
@@ -108,4 +109,24 @@ class ApplicationFormController(
|
||||
applicationFormService.submitApplicationForm(id),
|
||||
),
|
||||
)
|
||||
|
||||
@PreAuthorize(
|
||||
"hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')",
|
||||
)
|
||||
override fun addFormElementToSection(
|
||||
applicationFormId: UUID,
|
||||
sectionId: UUID,
|
||||
position: Int,
|
||||
createFormElementDto: CreateFormElementDto,
|
||||
): ResponseEntity<ApplicationFormDto> =
|
||||
ResponseEntity.status(201).body(
|
||||
applicationFormMapper.toApplicationFormDto(
|
||||
applicationFormService.addFormElementToSection(
|
||||
applicationFormId,
|
||||
sectionId,
|
||||
createFormElementDto,
|
||||
position,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ 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_api.model.ApplicationFormDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateFormElementDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateNotificationDto
|
||||
import com.betriebsratkanzlei.legalconsenthub_api.model.NotificationType
|
||||
import org.springframework.data.domain.Page
|
||||
@@ -20,6 +23,7 @@ import java.util.UUID
|
||||
class ApplicationFormService(
|
||||
private val applicationFormRepository: ApplicationFormRepository,
|
||||
private val applicationFormMapper: ApplicationFormMapper,
|
||||
private val formElementMapper: FormElementMapper,
|
||||
private val notificationService: NotificationService,
|
||||
) {
|
||||
fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
|
||||
@@ -45,7 +49,6 @@ class ApplicationFormService(
|
||||
}
|
||||
|
||||
fun updateApplicationForm(applicationFormDto: ApplicationFormDto): ApplicationForm {
|
||||
// TODO find statt mappen?
|
||||
val applicationForm = applicationFormMapper.toApplicationForm(applicationFormDto)
|
||||
val updatedApplicationForm: ApplicationForm
|
||||
|
||||
@@ -112,4 +115,35 @@ class ApplicationFormService(
|
||||
|
||||
notificationService.createNotificationForOrganization(createNotificationDto)
|
||||
}
|
||||
|
||||
fun addFormElementToSection(
|
||||
applicationFormId: UUID,
|
||||
sectionId: UUID,
|
||||
createFormElementDto: CreateFormElementDto,
|
||||
position: Int,
|
||||
): ApplicationForm {
|
||||
val applicationForm = getApplicationFormById(applicationFormId)
|
||||
|
||||
val section =
|
||||
applicationForm.formElementSections
|
||||
.find { it.id == sectionId }
|
||||
?: throw FormElementSectionNotFoundException(sectionId)
|
||||
|
||||
val newFormElement = formElementMapper.toFormElement(createFormElementDto, section)
|
||||
|
||||
if (position >= 0 && position < section.formElements.size) {
|
||||
section.formElements.add(position, newFormElement)
|
||||
} else {
|
||||
section.formElements.add(newFormElement)
|
||||
}
|
||||
|
||||
val updatedApplicationForm =
|
||||
try {
|
||||
applicationFormRepository.save(applicationForm)
|
||||
} catch (e: Exception) {
|
||||
throw ApplicationFormNotUpdatedException(e, applicationFormId)
|
||||
}
|
||||
|
||||
return updatedApplicationForm
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<template v-for="(formElement, index) in props.modelValue" :key="formElement.id">
|
||||
<div class="group py-3 lg:py-4">
|
||||
<p v-if="formElement.title" class="font-semibold">{{ formElement.title }}</p>
|
||||
<p v-if="formElement.description" class="text-dimmed pb-3">{{ formElement.description }}</p>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex py-3 lg:py-4">
|
||||
<div class="group flex-auto">
|
||||
<p v-if="formElement.title" class="font-semibold">{{ formElement.title }}</p>
|
||||
<p v-if="formElement.description" class="text-dimmed pb-3">{{ formElement.description }}</p>
|
||||
<component
|
||||
:is="getResolvedComponent(formElement)"
|
||||
:form-options="formElement.options"
|
||||
:disabled="props.disabled"
|
||||
@update:form-options="updateFormOptions($event, index)"
|
||||
/>
|
||||
<UIcon
|
||||
name="i-lucide-message-square-more"
|
||||
class="size-5 cursor-pointer hidden group-hover:block"
|
||||
@click="toggleComments(formElement.id)"
|
||||
<TheComment
|
||||
v-if="applicationFormId && activeFormElement === formElement.id"
|
||||
:form-element-id="formElement.id"
|
||||
:application-form-id="applicationFormId"
|
||||
:comments="comments?.[formElement.id]"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<UDropdownMenu :items="getDropdownItems(formElement.id, index)" :content="{ align: 'end' }">
|
||||
<UButton icon="i-lucide-ellipsis-vertical" color="neutral" variant="ghost" />
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<TheComment
|
||||
v-if="applicationFormId && activeFormElement === formElement.id"
|
||||
:form-element-id="formElement.id"
|
||||
:application-form-id="applicationFormId"
|
||||
:comments="comments?.[formElement.id]"
|
||||
/>
|
||||
<USeparator />
|
||||
</template>
|
||||
</template>
|
||||
@@ -31,6 +31,7 @@
|
||||
import type { FormElementDto, FormOptionDto } from '~/.api-client'
|
||||
import { resolveComponent } from 'vue'
|
||||
import TheComment from '~/components/TheComment.vue'
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: FormElementDto[]
|
||||
@@ -41,6 +42,7 @@ const props = defineProps<{
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', formElementDto: FormElementDto[]): void
|
||||
(e: 'click:comments', formElementId: string): void
|
||||
(e: 'add:input-form', position: number): void
|
||||
}>()
|
||||
|
||||
const commentStore = useCommentStore()
|
||||
@@ -66,11 +68,30 @@ function getResolvedComponent(formElement: FormElementDto) {
|
||||
return resolveComponent('TheSwitch')
|
||||
case 'TEXTFIELD':
|
||||
return resolveComponent('TheInput')
|
||||
case 'TITLE_BODY_TEXTFIELDS':
|
||||
return resolveComponent('TheTitleBodyInput')
|
||||
default:
|
||||
return resolveComponent('Unimplemented')
|
||||
}
|
||||
}
|
||||
|
||||
function getDropdownItems(formElementId: string, formElementPosition: number): DropdownMenuItem[] {
|
||||
return [
|
||||
[
|
||||
{
|
||||
label: 'Comments',
|
||||
icon: 'i-lucide-message-square-more',
|
||||
onClick: () => toggleComments(formElementId)
|
||||
},
|
||||
{
|
||||
label: 'Add input field below',
|
||||
icon: 'i-lucide-list-plus',
|
||||
onClick: () => emit('add:input-form', formElementPosition)
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
function updateFormOptions(formOptions: FormOptionDto[], formElementIndex: number) {
|
||||
const updatedModelValue = [...props.modelValue]
|
||||
updatedModelValue[formElementIndex] = { ...updatedModelValue[formElementIndex], options: formOptions }
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<UFormField label="Titel">
|
||||
<UInput v-model="title" class="w-full" :disabled="props.disabled" />
|
||||
</UFormField>
|
||||
<UFormField label="Text">
|
||||
<UTextarea v-model="body" class="w-full" autoresize :disabled="props.disabled" />
|
||||
</UFormField>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~/.api-client'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const SEPARATOR = '|||'
|
||||
|
||||
const title = computed({
|
||||
get: () => {
|
||||
const currentValue = props.formOptions?.[0]?.value ?? ''
|
||||
return splitValue(currentValue).title
|
||||
},
|
||||
set: (newTitle: string) => {
|
||||
const currentValue = props.formOptions?.[0]?.value ?? ''
|
||||
const { body: currentBody } = splitValue(currentValue)
|
||||
const combinedValue = joinValue(newTitle, currentBody)
|
||||
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = { ...updatedModelValue[0], value: combinedValue }
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
})
|
||||
|
||||
const body = computed({
|
||||
get: () => {
|
||||
const currentValue = props.formOptions?.[0]?.value ?? ''
|
||||
return splitValue(currentValue).body
|
||||
},
|
||||
set: (newBody: string) => {
|
||||
const currentValue = props.formOptions?.[0]?.value ?? ''
|
||||
const { title: currentTitle } = splitValue(currentValue)
|
||||
const combinedValue = joinValue(currentTitle, newBody)
|
||||
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = { ...updatedModelValue[0], value: combinedValue }
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
})
|
||||
|
||||
function splitValue(value: string): { title: string; body: string } {
|
||||
const parts = value.split(SEPARATOR)
|
||||
return {
|
||||
title: parts[0] || '',
|
||||
body: parts[1] || ''
|
||||
}
|
||||
}
|
||||
function joinValue(title: string, body: string): string {
|
||||
return `${title}${SEPARATOR}${body}`
|
||||
}
|
||||
</script>
|
||||
@@ -1,4 +1,9 @@
|
||||
import { type CreateApplicationFormDto, type ApplicationFormDto, type PagedApplicationFormDto } from '~/.api-client'
|
||||
import type {
|
||||
CreateApplicationFormDto,
|
||||
CreateFormElementDto,
|
||||
ApplicationFormDto,
|
||||
PagedApplicationFormDto
|
||||
} from '~/.api-client'
|
||||
import { useApplicationFormApi } from './useApplicationFormApi'
|
||||
|
||||
export function useApplicationForm() {
|
||||
@@ -71,12 +76,36 @@ export function useApplicationForm() {
|
||||
}
|
||||
}
|
||||
|
||||
async function addFormElementToSection(
|
||||
applicationFormId: string,
|
||||
sectionId: string,
|
||||
createFormElementDto: CreateFormElementDto,
|
||||
position: number
|
||||
): Promise<ApplicationFormDto> {
|
||||
if (!applicationFormId || !sectionId) {
|
||||
return Promise.reject(new Error('Application form ID or section ID missing'))
|
||||
}
|
||||
|
||||
try {
|
||||
return await applicationFormApi.addFormElementToSection(
|
||||
applicationFormId,
|
||||
sectionId,
|
||||
createFormElementDto,
|
||||
position
|
||||
)
|
||||
} catch (e: unknown) {
|
||||
console.error(`Failed adding form element to section ${sectionId}:`, e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createApplicationForm,
|
||||
getAllApplicationForms,
|
||||
getApplicationFormById,
|
||||
updateApplicationForm,
|
||||
deleteApplicationFormById,
|
||||
submitApplicationForm
|
||||
submitApplicationForm,
|
||||
addFormElementToSection
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
ApplicationFormApi,
|
||||
Configuration,
|
||||
type CreateApplicationFormDto,
|
||||
type CreateFormElementDto,
|
||||
type ApplicationFormDto,
|
||||
type PagedApplicationFormDto
|
||||
} from '~/.api-client'
|
||||
@@ -53,12 +54,27 @@ export function useApplicationFormApi() {
|
||||
return applicationFormApiClient.submitApplicationForm({ id })
|
||||
}
|
||||
|
||||
async function addFormElementToSection(
|
||||
applicationFormId: string,
|
||||
sectionId: string,
|
||||
createFormElementDto: CreateFormElementDto,
|
||||
position: number
|
||||
): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.addFormElementToSection({
|
||||
applicationFormId,
|
||||
sectionId,
|
||||
createFormElementDto,
|
||||
position
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
createApplicationForm,
|
||||
getAllApplicationForms,
|
||||
getApplicationFormById,
|
||||
updateApplicationForm,
|
||||
deleteApplicationFormById,
|
||||
submitApplicationForm
|
||||
submitApplicationForm,
|
||||
addFormElementToSection
|
||||
}
|
||||
}
|
||||
|
||||
48
legalconsenthub/composables/useFormElementManagement.ts
Normal file
48
legalconsenthub/composables/useFormElementManagement.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { ApplicationFormDto, CreateFormElementDto, FormElementSectionDto } from '~/.api-client'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
export function useFormElementManagement(
|
||||
currentFormElementSection: MaybeRefOrGetter<FormElementSectionDto | undefined>,
|
||||
applicationFormId?: string
|
||||
) {
|
||||
const { addFormElementToSection } = useApplicationForm()
|
||||
|
||||
async function addInputFormToApplicationForm(position: number): Promise<ApplicationFormDto | undefined> {
|
||||
const section = toValue(currentFormElementSection)
|
||||
if (!section) return
|
||||
|
||||
const { formElements } = section
|
||||
|
||||
const inputFormElement: CreateFormElementDto = {
|
||||
title: 'Formular ergänzen',
|
||||
description: 'Bitte fügen Sie hier Ihre Ergänzungen ein.',
|
||||
options: [
|
||||
{
|
||||
value: '|||',
|
||||
label: '',
|
||||
processingPurpose: 'NONE',
|
||||
employeeDataCategory: 'NONE'
|
||||
}
|
||||
],
|
||||
type: 'TITLE_BODY_TEXTFIELDS'
|
||||
}
|
||||
|
||||
if (applicationFormId) {
|
||||
try {
|
||||
return await addFormElementToSection(applicationFormId, section.id, inputFormElement, position + 1)
|
||||
} catch (error) {
|
||||
console.error('Failed to add form element:', error)
|
||||
throw error
|
||||
}
|
||||
} else {
|
||||
// @ts-expect-error Add CreateFormElementDto to formElements array. ID will be generated by the backend.
|
||||
formElements.splice(position + 1, 0, inputFormElement)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
addInputFormToApplicationForm
|
||||
}
|
||||
}
|
||||
|
||||
58
legalconsenthub/composables/useFormStepper.ts
Normal file
58
legalconsenthub/composables/useFormStepper.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { FormElementSectionDto } from '~/.api-client'
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
interface Stepper {
|
||||
hasPrev: boolean
|
||||
hasNext: boolean
|
||||
next: () => void
|
||||
prev: () => void
|
||||
}
|
||||
|
||||
export function useFormStepper(
|
||||
formElementSections: MaybeRefOrGetter<FormElementSectionDto[] | undefined>,
|
||||
options?: {
|
||||
onNavigate?: (direction: 'forward' | 'backward', newIndex: number) => void | Promise<void>
|
||||
}
|
||||
) {
|
||||
const stepper = useTemplateRef<Stepper>('stepper')
|
||||
const activeStepperItemIndex = ref<number>(0)
|
||||
|
||||
const sections = computed(() => toValue(formElementSections) ?? [])
|
||||
|
||||
const stepperItems = computed(() => {
|
||||
const items: StepperItem[] = []
|
||||
sections.value.forEach((section: FormElementSectionDto) => {
|
||||
items.push({
|
||||
title: section.shortTitle,
|
||||
description: section.description
|
||||
})
|
||||
})
|
||||
return items
|
||||
})
|
||||
|
||||
const currentFormElementSection = computed<FormElementSectionDto | undefined>(
|
||||
() => sections.value[activeStepperItemIndex.value]
|
||||
)
|
||||
|
||||
async function navigateStepper(direction: 'forward' | 'backward') {
|
||||
if (direction === 'forward') {
|
||||
stepper.value?.next()
|
||||
} else {
|
||||
stepper.value?.prev()
|
||||
}
|
||||
|
||||
if (options?.onNavigate) {
|
||||
await options.onNavigate(direction, activeStepperItemIndex.value)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stepper,
|
||||
activeStepperItemIndex,
|
||||
stepperItems,
|
||||
currentFormElementSection,
|
||||
navigateStepper
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,16 @@
|
||||
<template #body>
|
||||
<div class="flex flex-col w-full lg:max-w-4xl mx-auto">
|
||||
<UStepper ref="stepper" v-model="activeStepperItemIndex" :items="stepperItems" class="w-full" />
|
||||
<h1 class="text-xl text-pretty font-bold text-highlighted">
|
||||
<h1 v-if="currentFormElementSection?.title" class="text-xl text-pretty font-bold text-highlighted">
|
||||
{{ currentFormElementSection.title }}
|
||||
</h1>
|
||||
<UCard variant="subtle">
|
||||
<FormEngine
|
||||
v-if="applicationForm"
|
||||
v-if="applicationForm && currentFormElementSection?.formElements"
|
||||
v-model="currentFormElementSection.formElements"
|
||||
:application-form-id="applicationForm.id"
|
||||
:disabled="isReadOnly"
|
||||
@add:input-form="handleAddInputForm"
|
||||
/>
|
||||
<div class="flex gap-2 justify-between mt-4">
|
||||
<UButton
|
||||
@@ -75,8 +76,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ApplicationFormDto, FormElementSectionDto } from '~/.api-client'
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
import type { ApplicationFormDto } from '~/.api-client'
|
||||
|
||||
const { getApplicationFormById, updateApplicationForm, submitApplicationForm } = useApplicationForm()
|
||||
const route = useRoute()
|
||||
@@ -90,11 +90,6 @@ definePageMeta({
|
||||
key: (route) => `${route.params.id}`
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const sectionIndex = parseInt(route.params.sectionIndex[0])
|
||||
activeStepperItemIndex.value = !isNaN(sectionIndex) ? sectionIndex : 0
|
||||
})
|
||||
|
||||
const items = [
|
||||
[
|
||||
{
|
||||
@@ -105,13 +100,6 @@ const items = [
|
||||
]
|
||||
]
|
||||
|
||||
const stepper = useTemplateRef('stepper')
|
||||
const activeStepperItemIndex = ref<number>(0)
|
||||
|
||||
const currentFormElementSection = computed<FormElementSectionDto>(
|
||||
() => applicationForm.value?.formElementSections[activeStepperItemIndex.value]
|
||||
)
|
||||
|
||||
const { data, error } = await useAsyncData<ApplicationFormDto>(`application-form-${route.params.id}`, async () => {
|
||||
console.log('Fetching application form with ID:', route.params.id)
|
||||
return await getApplicationFormById(Array.isArray(route.params.id) ? route.params.id[0] : route.params.id)
|
||||
@@ -124,29 +112,35 @@ if (error.value) {
|
||||
const applicationForm = computed<ApplicationFormDto>(() => data?.value as ApplicationFormDto)
|
||||
|
||||
const isReadOnly = computed(() => {
|
||||
return applicationForm.value?.createdBy.id !== user.value?.id
|
||||
return applicationForm.value?.createdBy.keycloakId !== user.value?.keycloakId
|
||||
})
|
||||
|
||||
const stepperItems = computed(() => {
|
||||
const stepperItems: StepperItem[] = []
|
||||
applicationForm.value.formElementSections.forEach((section: FormElementSectionDto) => {
|
||||
stepperItems.push({
|
||||
title: section.shortTitle,
|
||||
description: section.description
|
||||
})
|
||||
})
|
||||
return stepperItems
|
||||
})
|
||||
|
||||
async function navigateStepper(direction: 'forward' | 'backward') {
|
||||
if (direction === 'forward') {
|
||||
stepper.value?.next()
|
||||
} else {
|
||||
stepper.value?.prev()
|
||||
const { stepper, activeStepperItemIndex, stepperItems, currentFormElementSection, navigateStepper } = useFormStepper(
|
||||
computed(() => applicationForm.value?.formElementSections),
|
||||
{
|
||||
onNavigate: async () => {
|
||||
await navigateTo(`/application-forms/${route.params.id}/${activeStepperItemIndex.value}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const { addInputFormToApplicationForm } = useFormElementManagement(
|
||||
currentFormElementSection,
|
||||
applicationForm.value?.id
|
||||
)
|
||||
|
||||
async function handleAddInputForm(position: number) {
|
||||
const updatedForm = await addInputFormToApplicationForm(position)
|
||||
if (updatedForm) {
|
||||
data.value = updatedForm
|
||||
}
|
||||
await navigateTo(`/application-forms/${route.params.id}/${activeStepperItemIndex.value}`)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const sectionIndex = parseInt(route.params.sectionIndex[0])
|
||||
activeStepperItemIndex.value = !isNaN(sectionIndex) ? sectionIndex : 0
|
||||
})
|
||||
|
||||
async function onSave() {
|
||||
if (data?.value) {
|
||||
await updateApplicationForm(data.value.id, data.value)
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<FormEngine
|
||||
v-if="currentFormElementSection?.formElements"
|
||||
v-model="currentFormElementSection.formElements"
|
||||
@add:input-form="addInputFormToApplicationForm"
|
||||
/>
|
||||
<div class="flex gap-2 justify-between mt-4">
|
||||
<UButton
|
||||
@@ -71,10 +72,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ComplianceStatus, type FormElementSectionDto, type PagedApplicationFormDto } from '~/.api-client'
|
||||
import { ComplianceStatus, type PagedApplicationFormDto } from '~/.api-client'
|
||||
import { useApplicationFormValidator } from '~/composables/useApplicationFormValidator'
|
||||
import type { FormElementId } from '~/types/formElement'
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const { getAllApplicationFormTemplates } = await useApplicationFormTemplate()
|
||||
const { createApplicationForm, submitApplicationForm } = useApplicationForm()
|
||||
@@ -84,17 +84,6 @@ const userStore = useUserStore()
|
||||
const { selectedOrganization } = storeToRefs(userStore)
|
||||
const toast = useToast()
|
||||
|
||||
const stepper = useTemplateRef('stepper')
|
||||
const activeStepperItemIndex = ref<number>(0)
|
||||
|
||||
const currentFormElementSection = computed(
|
||||
() => applicationFormTemplate.value?.formElementSections[activeStepperItemIndex.value]
|
||||
)
|
||||
|
||||
watch(activeStepperItemIndex, async (newActiveStepperItem: number) => {
|
||||
activeStepperItemIndex.value = newActiveStepperItem
|
||||
})
|
||||
|
||||
const { data, error } = await useAsyncData<PagedApplicationFormDto>(async () => {
|
||||
return await getAllApplicationFormTemplates()
|
||||
})
|
||||
@@ -103,34 +92,17 @@ if (error.value) {
|
||||
throw createError({ statusText: error.value.message })
|
||||
}
|
||||
|
||||
const stepperItems = computed(() => {
|
||||
const stepperItems: StepperItem[] = []
|
||||
if (!applicationFormTemplate.value) {
|
||||
return stepperItems
|
||||
}
|
||||
|
||||
applicationFormTemplate.value.formElementSections.forEach((section: FormElementSectionDto) => {
|
||||
stepperItems.push({
|
||||
title: section.shortTitle,
|
||||
description: section.description
|
||||
})
|
||||
})
|
||||
return stepperItems
|
||||
})
|
||||
|
||||
async function navigateStepper(direction: 'forward' | 'backward') {
|
||||
if (direction === 'forward') {
|
||||
stepper.value?.next()
|
||||
} else {
|
||||
stepper.value?.prev()
|
||||
}
|
||||
}
|
||||
|
||||
const applicationFormTemplate = computed(
|
||||
// TODO: Don't select always the first item, allow user to select a template
|
||||
() => data?.value?.content[0] ?? undefined
|
||||
)
|
||||
|
||||
const { stepper, activeStepperItemIndex, stepperItems, currentFormElementSection, navigateStepper } = useFormStepper(
|
||||
computed(() => applicationFormTemplate.value?.formElementSections)
|
||||
)
|
||||
|
||||
const { addInputFormToApplicationForm } = useFormElementManagement(currentFormElementSection)
|
||||
|
||||
const formElements = computed({
|
||||
get: () => currentFormElementSection?.value?.formElements ?? [],
|
||||
set: (val) => {
|
||||
|
||||
Reference in New Issue
Block a user