202 lines
6.2 KiB
Vue
202 lines
6.2 KiB
Vue
<template>
|
|
<UDashboardPanel id="home">
|
|
<template #header>
|
|
<UDashboardNavbar :title="$t('common.home')" :ui="{ right: 'gap-3' }">
|
|
<template #leading>
|
|
<UDashboardSidebarCollapse />
|
|
</template>
|
|
|
|
<template #right>
|
|
<UButton
|
|
icon="i-lucide-circle-plus"
|
|
:label="$t('applicationForms.createNew')"
|
|
to="/create"
|
|
:disabled="!canWriteApplicationForms"
|
|
size="xl"
|
|
variant="outline"
|
|
color="neutral"
|
|
:ui="{
|
|
leadingIcon: 'text-primary'
|
|
}"
|
|
/>
|
|
</template>
|
|
</UDashboardNavbar>
|
|
|
|
<UDashboardToolbar>
|
|
<UNavigationMenu :items="links" highlight class="-mx-1 flex-1" />
|
|
<USeparator orientation="vertical" class="h-8 mx-2" />
|
|
<FormValidationIndicator :status="validationStatus" />
|
|
</UDashboardToolbar>
|
|
</template>
|
|
|
|
<template #body>
|
|
<div class="flex flex-col gap-4 sm:gap-6 lg:gap-12 w-full lg:max-w-4xl mx-auto">
|
|
<FormStepperWithNavigation
|
|
:form-element-sections="applicationForm.formElementSections"
|
|
:initial-section-index="sectionIndex"
|
|
:application-form-id="applicationForm.id ?? undefined"
|
|
:disabled="isReadOnly"
|
|
@save="onSave"
|
|
@submit="onSubmit"
|
|
@navigate="handleNavigate"
|
|
@add-input-form="handleAddInputForm"
|
|
@update:form-element-sections="handleFormElementSectionsUpdate"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</UDashboardPanel>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
ComplianceStatus,
|
|
type ApplicationFormDto,
|
|
type FormElementDto,
|
|
type FormElementSectionDto
|
|
} from '~~/.api-client'
|
|
import type { FormElementId } from '~~/types/formElement'
|
|
import { useApplicationFormValidator } from '~/composables/useApplicationFormValidator'
|
|
import { useUserStore } from '~~/stores/useUserStore'
|
|
import { useCommentStore } from '~~/stores/useCommentStore'
|
|
|
|
const route = useRoute()
|
|
const toast = useToast()
|
|
const { t: $t } = useI18n()
|
|
const commentStore = useCommentStore()
|
|
|
|
definePageMeta({
|
|
// Prevent whole page from re-rendering when navigating between sections to keep state
|
|
key: (route) => `${route.params.id}`
|
|
})
|
|
|
|
const applicationFormId = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id
|
|
const {
|
|
applicationForm,
|
|
navigationLinks: links,
|
|
updateApplicationForm
|
|
} = await useApplicationFormNavigation(applicationFormId!)
|
|
|
|
if (applicationFormId) {
|
|
await commentStore.loadCounts(applicationFormId)
|
|
}
|
|
|
|
const { updateApplicationForm: updateForm, submitApplicationForm } = useApplicationForm()
|
|
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
|
const { evaluateFormElementVisibility } = useFormElementVisibility()
|
|
const { canWriteApplicationForms } = usePermissions()
|
|
const userStore = useUserStore()
|
|
const { user } = storeToRefs(userStore)
|
|
|
|
const sectionIndex = computed(() => {
|
|
const param = route.params.sectionIndex
|
|
const index = parseInt(Array.isArray(param) ? param[0]! : (param ?? '0'))
|
|
return !isNaN(index) ? index : 0
|
|
})
|
|
|
|
const isReadOnly = computed(() => {
|
|
return applicationForm.value?.createdBy?.keycloakId !== user.value?.keycloakId
|
|
})
|
|
|
|
// Unsaved changes tracking
|
|
const originalFormJson = ref<string>('')
|
|
|
|
onMounted(() => {
|
|
originalFormJson.value = JSON.stringify(applicationForm.value)
|
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
})
|
|
|
|
const hasUnsavedChanges = computed(() => {
|
|
if (!originalFormJson.value) return false
|
|
return JSON.stringify(applicationForm.value) !== originalFormJson.value
|
|
})
|
|
|
|
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
|
if (hasUnsavedChanges.value) {
|
|
e.preventDefault()
|
|
}
|
|
}
|
|
|
|
onBeforeRouteLeave((to, from, next) => {
|
|
// Allow navigation between sections of the same form
|
|
if (to.params.id === from.params.id) {
|
|
next()
|
|
return
|
|
}
|
|
|
|
if (hasUnsavedChanges.value) {
|
|
const answer = window.confirm($t('applicationForms.unsavedWarning'))
|
|
next(answer)
|
|
} else {
|
|
next()
|
|
}
|
|
})
|
|
|
|
const validationMap = ref<Map<FormElementId, ComplianceStatus> | undefined>()
|
|
const validationStatus = ref<ComplianceStatus>(ComplianceStatus.NonCritical)
|
|
|
|
const allFormElements = computed(() => {
|
|
return (
|
|
applicationForm.value?.formElementSections?.flatMap((section: FormElementSectionDto) =>
|
|
section.formElementSubSections.flatMap((subsection) => subsection.formElements)
|
|
) ?? []
|
|
)
|
|
})
|
|
|
|
const visibilityMap = computed(() => {
|
|
return evaluateFormElementVisibility(allFormElements.value)
|
|
})
|
|
|
|
watch(
|
|
() => allFormElements.value,
|
|
(updatedFormElements: FormElementDto[]) => {
|
|
validationMap.value = validateFormElements(updatedFormElements, visibilityMap.value)
|
|
validationStatus.value = getHighestComplianceStatus()
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
async function onSave() {
|
|
if (applicationForm.value?.id) {
|
|
const updated = await updateForm(applicationForm.value.id, applicationForm.value)
|
|
if (updated) {
|
|
updateApplicationForm(updated)
|
|
originalFormJson.value = JSON.stringify(updated)
|
|
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
|
}
|
|
}
|
|
}
|
|
|
|
async function onSubmit() {
|
|
if (applicationForm.value?.id) {
|
|
// Save the form first to persist any unsaved changes before submitting
|
|
const updated = await updateForm(applicationForm.value.id, applicationForm.value)
|
|
if (updated) {
|
|
updateApplicationForm(updated)
|
|
}
|
|
await submitApplicationForm(applicationForm.value.id)
|
|
await navigateTo('/')
|
|
toast.add({ title: $t('common.success'), description: $t('applicationForms.submitted'), color: 'success' })
|
|
}
|
|
}
|
|
|
|
async function handleNavigate({ index }: { direction: 'forward' | 'backward'; index: number }) {
|
|
await navigateTo(`/application-forms/${applicationFormId}/${index}`)
|
|
}
|
|
|
|
function handleAddInputForm(updatedForm: ApplicationFormDto | undefined) {
|
|
if (updatedForm) {
|
|
updateApplicationForm(updatedForm)
|
|
}
|
|
}
|
|
|
|
function handleFormElementSectionsUpdate(sections: FormElementSectionDto[]) {
|
|
if (applicationForm.value) {
|
|
applicationForm.value.formElementSections = sections
|
|
}
|
|
}
|
|
</script>
|