major: Rename legalconsenthub to gremiumhub
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<UCheckboxGroup v-model="modelValue" :items="items" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormElementDto, FormOptionDto } from '~~/.api-client'
|
||||
import { useFormElementVisibility } from '~/composables/useFormElementVisibility'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
allFormElements?: FormElementDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const { isFormOptionVisible } = useFormElementVisibility()
|
||||
|
||||
const visibleOptions = computed(() => {
|
||||
if (!props.allFormElements) return props.formOptions
|
||||
return props.formOptions.filter((opt) => isFormOptionVisible(opt.visibilityConditions, props.allFormElements!))
|
||||
})
|
||||
|
||||
// Auto-clear hidden options that are still selected
|
||||
watchEffect(() => {
|
||||
if (!props.allFormElements) return
|
||||
const hiddenSelected = props.formOptions.filter(
|
||||
(opt) => opt.value === 'true' && !isFormOptionVisible(opt.visibilityConditions, props.allFormElements!)
|
||||
)
|
||||
if (hiddenSelected.length === 0) return
|
||||
emit(
|
||||
'update:formOptions',
|
||||
props.formOptions.map((opt) => (hiddenSelected.includes(opt) ? { ...opt, value: 'false' } : opt))
|
||||
)
|
||||
})
|
||||
|
||||
const items = computed(() => visibleOptions.value.map((option) => ({ label: option.label, value: option.label })))
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions.filter((option) => option.value === 'true').map((option) => option.label),
|
||||
set: (selectedLabels: string[]) => {
|
||||
const updatedModelValue = props.formOptions.map((option) => ({
|
||||
...option,
|
||||
value: selectedLabels.includes(option.label) ? 'true' : 'false'
|
||||
}))
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,62 +0,0 @@
|
||||
<template>
|
||||
<UFormField :label="label">
|
||||
<UInputDate ref="inputDateRef" v-model="dateValue" :disabled="disabled">
|
||||
<template #trailing>
|
||||
<UPopover :reference="inputDateRef?.inputsRef[3]?.$el">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="link"
|
||||
size="sm"
|
||||
icon="i-lucide-calendar"
|
||||
:aria-label="$t('applicationForms.formElements.selectDate')"
|
||||
class="px-0"
|
||||
/>
|
||||
<template #content>
|
||||
<UCalendar v-model="dateValue" class="p-2" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
</UInputDate>
|
||||
</UFormField>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
import type { CalendarDate } from '@internationalized/date'
|
||||
import { parseDate } from '@internationalized/date'
|
||||
|
||||
const props = defineProps<{
|
||||
label?: string
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const inputDateRef = useTemplateRef('inputDateRef')
|
||||
|
||||
const dateValue = computed({
|
||||
get: () => {
|
||||
const value = props.formOptions[0]?.value ?? ''
|
||||
if (!value) return null
|
||||
try {
|
||||
return parseDate(value)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
set: (val: CalendarDate | null) => {
|
||||
const firstOption = props.formOptions[0]
|
||||
if (firstOption) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = {
|
||||
...firstOption,
|
||||
value: val ? val.toString() : ''
|
||||
}
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,119 +0,0 @@
|
||||
<template>
|
||||
<div class="bg-white dark:bg-white rounded-md border border-gray-200 dark:border-gray-200 overflow-hidden">
|
||||
<UEditor
|
||||
v-slot="{ editor }"
|
||||
v-model="editorContent"
|
||||
content-type="json"
|
||||
:editable="!props.disabled"
|
||||
:placeholder="t('applicationForms.formElements.richTextPlaceholder')"
|
||||
:ui="{
|
||||
content: 'bg-white dark:bg-white',
|
||||
base: 'min-h-[200px] p-3 bg-white dark:bg-white'
|
||||
}"
|
||||
class="w-full"
|
||||
>
|
||||
<UEditorToolbar
|
||||
:editor="editor"
|
||||
:items="toolbarItems"
|
||||
class="border-b border-muted sticky top-0 inset-x-0 px-3 py-2 z-50 bg-default overflow-x-auto"
|
||||
/>
|
||||
<UEditorSuggestionMenu :editor="editor" :items="suggestionItems" />
|
||||
<UEditorMentionMenu :editor="editor" :items="mentionItems" />
|
||||
<UEditorDragHandle :editor="editor" />
|
||||
</UEditor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { EmployeeDataCategory, ProcessingPurpose, type FormOptionDto } from '~~/.api-client'
|
||||
import type { JSONContent } from '@tiptap/vue-3'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const editorContent = computed({
|
||||
get: () => {
|
||||
const rawValue = props.formOptions[0]?.value ?? ''
|
||||
if (rawValue.trim().startsWith('{')) {
|
||||
try {
|
||||
return JSON.parse(rawValue) as JSONContent
|
||||
} catch {
|
||||
return rawValue // Fallback to HTML if JSON parse fails
|
||||
}
|
||||
}
|
||||
return rawValue // Treat as HTML
|
||||
},
|
||||
set: (newValue: string | JSONContent) => {
|
||||
const updatedOptions: FormOptionDto[] = [...props.formOptions]
|
||||
const stringifiedValue = typeof newValue === 'string' ? newValue : JSON.stringify(newValue)
|
||||
|
||||
if (updatedOptions.length === 0) {
|
||||
const createdOption: FormOptionDto = {
|
||||
value: stringifiedValue,
|
||||
label: '',
|
||||
processingPurpose: ProcessingPurpose.None,
|
||||
employeeDataCategory: EmployeeDataCategory.None
|
||||
}
|
||||
updatedOptions.push(createdOption)
|
||||
} else {
|
||||
const firstOption = updatedOptions[0]!
|
||||
updatedOptions[0] = { ...firstOption, value: stringifiedValue }
|
||||
}
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
})
|
||||
|
||||
const toolbarItems = [
|
||||
[
|
||||
{ kind: 'undo', icon: 'i-lucide-undo' },
|
||||
{ kind: 'redo', icon: 'i-lucide-redo' }
|
||||
],
|
||||
[
|
||||
{ kind: 'heading', level: 1, icon: 'i-lucide-heading-1', label: 'H1' },
|
||||
{ kind: 'heading', level: 2, icon: 'i-lucide-heading-2', label: 'H2' },
|
||||
{ kind: 'heading', level: 3, icon: 'i-lucide-heading-3', label: 'H3' }
|
||||
],
|
||||
[
|
||||
{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' },
|
||||
{ kind: 'mark', mark: 'italic', icon: 'i-lucide-italic' },
|
||||
{ kind: 'mark', mark: 'underline', icon: 'i-lucide-underline' },
|
||||
{ kind: 'mark', mark: 'strike', icon: 'i-lucide-strikethrough' }
|
||||
],
|
||||
[
|
||||
{ kind: 'bulletList', icon: 'i-lucide-list' },
|
||||
{ kind: 'orderedList', icon: 'i-lucide-list-ordered' }
|
||||
],
|
||||
[
|
||||
{ kind: 'blockquote', icon: 'i-lucide-quote' },
|
||||
{ kind: 'codeBlock', icon: 'i-lucide-code' }
|
||||
],
|
||||
[{ kind: 'link', icon: 'i-lucide-link' }]
|
||||
]
|
||||
|
||||
const suggestionItems = [
|
||||
[
|
||||
{ kind: 'heading', level: 1, label: 'Überschrift 1', icon: 'i-lucide-heading-1' },
|
||||
{ kind: 'heading', level: 2, label: 'Überschrift 2', icon: 'i-lucide-heading-2' },
|
||||
{ kind: 'heading', level: 3, label: 'Überschrift 3', icon: 'i-lucide-heading-3' }
|
||||
],
|
||||
[
|
||||
{ kind: 'bulletList', label: 'Aufzählung', icon: 'i-lucide-list' },
|
||||
{ kind: 'orderedList', label: 'Nummerierung', icon: 'i-lucide-list-ordered' }
|
||||
],
|
||||
[
|
||||
{ kind: 'blockquote', label: 'Zitat', icon: 'i-lucide-quote' },
|
||||
{ kind: 'codeBlock', label: 'Code Block', icon: 'i-lucide-code' },
|
||||
{ kind: 'horizontalRule', label: 'Trennlinie', icon: 'i-lucide-separator-horizontal' }
|
||||
]
|
||||
]
|
||||
|
||||
const mentionItems: Array<{ label: string; avatar?: { src: string } }> = []
|
||||
</script>
|
||||
@@ -1,234 +0,0 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- Upload Area -->
|
||||
<div>
|
||||
<UFileUpload
|
||||
v-model="selectedFiles"
|
||||
:accept="allowedFileTypes"
|
||||
:multiple="true"
|
||||
:disabled="isUploading || disabled"
|
||||
:label="t('applicationForms.formElements.fileUpload.label')"
|
||||
:description="t('applicationForms.formElements.fileUpload.allowedTypes')"
|
||||
variant="area"
|
||||
layout="list"
|
||||
position="inside"
|
||||
@change="handleFileSelect"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Upload Progress -->
|
||||
<div v-if="isUploading" class="space-y-2">
|
||||
<UProgress :value="uploadProgress" />
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ t('applicationForms.formElements.fileUpload.uploading') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Error Message -->
|
||||
<UAlert
|
||||
v-if="errorMessage"
|
||||
color="error"
|
||||
variant="soft"
|
||||
:title="t('applicationForms.formElements.fileUpload.uploadError')"
|
||||
:description="errorMessage"
|
||||
:close-button="{ icon: 'i-ph-x', color: 'red', variant: 'link' }"
|
||||
@close="errorMessage = ''"
|
||||
/>
|
||||
|
||||
<!-- Uploaded Files List -->
|
||||
<div v-if="uploadedFiles.length > 0" class="space-y-2">
|
||||
<p class="text-sm font-medium">
|
||||
{{ t('applicationForms.formElements.fileUpload.uploadedFiles') }}
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="file in uploadedFiles"
|
||||
:key="file.fileId"
|
||||
class="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center gap-3 flex-1 min-w-0">
|
||||
<UIcon :name="getFileIcon(file.mimeType)" class="text-xl flex-shrink-0" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium truncate">{{ file.filename }}</p>
|
||||
<p class="text-xs text-gray-500">{{ formatFileSize(file.size) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0">
|
||||
<UButton
|
||||
v-if="isViewableInBrowser(file.mimeType)"
|
||||
icon="i-ph-eye"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:title="t('applicationForms.formElements.fileUpload.view')"
|
||||
@click="viewFile(file.fileId)"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-ph-download"
|
||||
color="info"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:disabled="isDownloading"
|
||||
@click="downloadFile(file.fileId, file.filename)"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-ph-trash"
|
||||
color="error"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:disabled="disabled || isDeleting"
|
||||
@click="deleteFile(file.fileId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { EmployeeDataCategory, type FormOptionDto, ProcessingPurpose } from '~~/.api-client'
|
||||
import { useFile, type UploadedFileMetadata } from '~/composables/file/useFile'
|
||||
import { useUserStore } from '~~/stores/useUserStore'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
applicationFormId?: string
|
||||
formElementReference?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const {
|
||||
uploadFile: uploadFileApi,
|
||||
downloadFile: downloadFileApi,
|
||||
viewFile: viewFileApi,
|
||||
deleteFile: deleteFileApi,
|
||||
parseUploadedFiles,
|
||||
createFileMetadata,
|
||||
getFileIcon,
|
||||
formatFileSize,
|
||||
isViewableInBrowser
|
||||
} = useFile()
|
||||
|
||||
const isUploading = ref(false)
|
||||
const isDownloading = ref(false)
|
||||
const isDeleting = ref(false)
|
||||
const uploadProgress = ref(0)
|
||||
const errorMessage = ref('')
|
||||
const selectedFiles = ref<File[] | null>(null)
|
||||
|
||||
const allowedFileTypes = '.pdf,.docx,.doc,.odt,.jpg,.jpeg,.png,.zip'
|
||||
|
||||
// Parse uploaded files from formOptions
|
||||
const uploadedFiles = computed<UploadedFileMetadata[]>(() => {
|
||||
const values = props.formOptions.map((option) => option.value)
|
||||
return parseUploadedFiles(values)
|
||||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
const organizationId = computed(() => userStore.selectedOrganization?.id)
|
||||
|
||||
const handleFileSelect = async () => {
|
||||
if (!selectedFiles.value || selectedFiles.value.length === 0) return
|
||||
|
||||
const files = Array.isArray(selectedFiles.value) ? selectedFiles.value : [selectedFiles.value]
|
||||
errorMessage.value = ''
|
||||
|
||||
for (const file of files) {
|
||||
const maxFileSize = 10 * 1024 * 1024 // 10 MB
|
||||
if (file.size > maxFileSize) {
|
||||
errorMessage.value = t('applicationForms.formElements.fileUpload.fileTooLarge', {
|
||||
filename: file.name,
|
||||
maxSize: formatFileSize(maxFileSize)
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
await uploadFile(file)
|
||||
}
|
||||
|
||||
selectedFiles.value = null
|
||||
}
|
||||
|
||||
const uploadFile = async (file: File) => {
|
||||
if (!props.formElementReference) {
|
||||
errorMessage.value = 'Missing required context: formElementReference'
|
||||
return
|
||||
}
|
||||
|
||||
isUploading.value = true
|
||||
uploadProgress.value = 0
|
||||
|
||||
try {
|
||||
const response = await uploadFileApi({
|
||||
file,
|
||||
applicationFormId: props.applicationFormId,
|
||||
formElementReference: props.formElementReference,
|
||||
organizationId: organizationId.value
|
||||
})
|
||||
|
||||
const metadata = createFileMetadata(response)
|
||||
|
||||
const newOption: FormOptionDto = {
|
||||
value: JSON.stringify(metadata),
|
||||
label: props.formElementReference,
|
||||
processingPurpose: props.formOptions[0]?.processingPurpose ?? ProcessingPurpose.None,
|
||||
employeeDataCategory: props.formOptions[0]?.employeeDataCategory ?? EmployeeDataCategory.None
|
||||
}
|
||||
|
||||
const updatedOptions = [...props.formOptions, newOption]
|
||||
emit('update:formOptions', updatedOptions)
|
||||
|
||||
uploadProgress.value = 100
|
||||
} catch (error: unknown) {
|
||||
errorMessage.value = error instanceof Error ? error.message : String(error)
|
||||
} finally {
|
||||
isUploading.value = false
|
||||
uploadProgress.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
const downloadFile = async (fileId: string, filename: string) => {
|
||||
isDownloading.value = true
|
||||
try {
|
||||
await downloadFileApi(fileId, filename)
|
||||
} catch (error: unknown) {
|
||||
errorMessage.value = error instanceof Error ? error.message : String(error)
|
||||
} finally {
|
||||
isDownloading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const viewFile = (fileId: string) => {
|
||||
viewFileApi(fileId)
|
||||
}
|
||||
|
||||
const deleteFile = async (fileId: string) => {
|
||||
if (!confirm(t('common.confirmDelete'))) return
|
||||
|
||||
isDeleting.value = true
|
||||
try {
|
||||
await deleteFileApi(fileId)
|
||||
|
||||
// Remove from formOptions
|
||||
const updatedOptions = props.formOptions.filter((option) => {
|
||||
try {
|
||||
const metadata = JSON.parse(option.value) as UploadedFileMetadata
|
||||
return metadata.fileId !== fileId
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
})
|
||||
emit('update:formOptions', updatedOptions)
|
||||
} catch (error: unknown) {
|
||||
errorMessage.value = error instanceof Error ? error.message : String(error)
|
||||
} finally {
|
||||
isDeleting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<UFormField :label="label">
|
||||
<UInput v-model="modelValue" class="w-full" />
|
||||
</UFormField>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
|
||||
const props = defineProps<{
|
||||
label?: string
|
||||
formOptions: FormOptionDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions[0]?.value ?? '',
|
||||
set: (val) => {
|
||||
const firstOption = props.formOptions[0]
|
||||
if (val && firstOption) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = { ...firstOption, value: val.toString() }
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<URadioGroup v-model="modelValue" :items="items" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormElementDto, FormOptionDto } from '~~/.api-client'
|
||||
import { useFormElementVisibility } from '~/composables/useFormElementVisibility'
|
||||
|
||||
const props = defineProps<{
|
||||
label?: string
|
||||
formOptions: FormOptionDto[]
|
||||
allFormElements?: FormElementDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const { isFormOptionVisible } = useFormElementVisibility()
|
||||
|
||||
const visibleOptions = computed(() => {
|
||||
if (!props.allFormElements) return props.formOptions
|
||||
return props.formOptions.filter((opt) => isFormOptionVisible(opt.visibilityConditions, props.allFormElements!))
|
||||
})
|
||||
|
||||
// Auto-clear selected option if it becomes hidden
|
||||
watchEffect(() => {
|
||||
if (!props.allFormElements) return
|
||||
const selectedOption = props.formOptions.find((opt) => opt.value === 'true')
|
||||
if (!selectedOption) return
|
||||
if (!isFormOptionVisible(selectedOption.visibilityConditions, props.allFormElements!)) {
|
||||
emit(
|
||||
'update:formOptions',
|
||||
props.formOptions.map((opt) => ({ ...opt, value: 'false' }))
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Our "label" is the "value" of the radio button
|
||||
const items = computed(() => visibleOptions.value.map((option) => ({ label: option.label, value: option.label })))
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions.find((option) => option.value === 'true')?.label,
|
||||
set: (val) => {
|
||||
if (val) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue.forEach((option) => {
|
||||
option.value = option.label === val ? 'true' : 'false'
|
||||
})
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<USelect v-model="modelValue" :placeholder="$t('applicationForms.formElements.selectPlaceholder')" :items="items" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
|
||||
const props = defineProps<{
|
||||
label?: string
|
||||
formOptions: FormOptionDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
// Our "label" is the "value" of the select
|
||||
const items = computed(() => props.formOptions.map((option) => ({ label: option.label, value: option.label })))
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions.find((option) => option.value === 'true')?.label,
|
||||
set: (val) => {
|
||||
if (val) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue.forEach((option) => {
|
||||
option.value = option.label === val ? 'true' : 'false'
|
||||
})
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<template>
|
||||
<USwitch v-model="modelValue" :label="label" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions[0]?.value === 'true',
|
||||
set: (val) => {
|
||||
const firstOption = props.formOptions[0]
|
||||
if (firstOption) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = { ...firstOption, value: val.toString() }
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const label = computed(() => props.formOptions[0]?.label ?? '')
|
||||
</script>
|
||||
@@ -1,412 +0,0 @@
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-end">
|
||||
<UTooltip :text="$t('applicationForms.formElements.table.enlargeTable')">
|
||||
<UButton
|
||||
icon="i-lucide-maximize-2"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
:aria-label="$t('applicationForms.formElements.table.enlargeTable')"
|
||||
@click="fullscreenOpen = true"
|
||||
/>
|
||||
</UTooltip>
|
||||
</div>
|
||||
|
||||
<!-- Regular table -->
|
||||
<TheTableContent
|
||||
:table-data="tableData"
|
||||
:table-columns="tableColumns"
|
||||
:data-columns="dataColumns"
|
||||
:form-options="formOptions"
|
||||
:disabled="disabled"
|
||||
:can-modify-rows="canModifyRows"
|
||||
:get-column-options="getColumnOptions"
|
||||
:read-only-column-indices="readOnlyColumnIndices"
|
||||
:is-cell-visible="isCellVisible"
|
||||
@update:cell="updateCell"
|
||||
@update:cell-value="updateCellValue"
|
||||
@update:checkbox-cell="updateCheckboxCell"
|
||||
@add-row="addRow"
|
||||
@remove-row="removeRow"
|
||||
/>
|
||||
|
||||
<!-- Fullscreen Modal -->
|
||||
<UModal
|
||||
v-model:open="fullscreenOpen"
|
||||
:title="$t('applicationForms.formElements.table.enlargeTable')"
|
||||
class="min-w-3/4"
|
||||
>
|
||||
<template #content>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between p-4 border-b border-default">
|
||||
<h2 class="text-lg font-semibold">{{ $t('applicationForms.formElements.table.editTable') }}</h2>
|
||||
<UButton
|
||||
icon="i-lucide-x"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:aria-label="$t('common.close')"
|
||||
@click="fullscreenOpen = false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Modal body with full-width table -->
|
||||
<div class="flex-1 overflow-auto p-4">
|
||||
<TheTableContent
|
||||
:table-data="tableData"
|
||||
:table-columns="tableColumns"
|
||||
:data-columns="dataColumns"
|
||||
:form-options="formOptions"
|
||||
:disabled="disabled"
|
||||
:can-modify-rows="canModifyRows"
|
||||
:get-column-options="getColumnOptions"
|
||||
:read-only-column-indices="readOnlyColumnIndices"
|
||||
:is-cell-visible="isCellVisible"
|
||||
add-row-button-class="mt-4"
|
||||
@update:cell="updateCell"
|
||||
@update:cell-value="updateCellValue"
|
||||
@update:checkbox-cell="updateCheckboxCell"
|
||||
@add-row="addRow"
|
||||
@remove-row="removeRow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormElementDto, FormOptionDto, TableRowPresetDto } from '~~/.api-client'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useTableCrossReferences } from '~/composables/useTableCrossReferences'
|
||||
import { useFormElementVisibility } from '~/composables/useFormElementVisibility'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
allFormElements?: FormElementDto[]
|
||||
tableRowPreset?: TableRowPresetDto
|
||||
}>()
|
||||
|
||||
const { isFormOptionVisible } = useFormElementVisibility()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const { getReferencedColumnValues, getConstrainedColumnValues, applyRowPresets } = useTableCrossReferences()
|
||||
|
||||
const fullscreenOpen = ref(false)
|
||||
|
||||
const canModifyRows = computed(() => {
|
||||
if (!props.tableRowPreset) return true
|
||||
return props.tableRowPreset.canAddRows !== false
|
||||
})
|
||||
|
||||
// Watch for changes in source table and apply row presets reactively
|
||||
const sourceTableOptions = computed(() => {
|
||||
if (!props.tableRowPreset?.sourceTableReference || !props.allFormElements) return null
|
||||
const sourceTable = props.allFormElements.find(
|
||||
(el) => el.reference === props.tableRowPreset?.sourceTableReference && el.type === 'TABLE'
|
||||
)
|
||||
return sourceTable?.options
|
||||
})
|
||||
|
||||
watch(
|
||||
sourceTableOptions,
|
||||
() => {
|
||||
if (!sourceTableOptions.value || !props.tableRowPreset || !props.allFormElements) return
|
||||
|
||||
const updatedOptions = applyRowPresets(props.tableRowPreset, props.formOptions, props.allFormElements)
|
||||
|
||||
const hasChanges = updatedOptions.some((opt, idx) => opt.value !== props.formOptions[idx]?.value)
|
||||
if (hasChanges) {
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
type CellValue = string | string[] | boolean
|
||||
type TableRowData = Record<string, CellValue>
|
||||
|
||||
interface DataColumn {
|
||||
key: string
|
||||
colIndex: number
|
||||
}
|
||||
|
||||
// Filter columns based on visibility conditions
|
||||
interface VisibleColumn {
|
||||
option: FormOptionDto
|
||||
originalIndex: number
|
||||
}
|
||||
|
||||
const visibleColumns = computed<VisibleColumn[]>(() => {
|
||||
return props.formOptions
|
||||
.map((option, index) => ({ option, originalIndex: index }))
|
||||
.filter(({ option }) => {
|
||||
if (!option.visibilityConditions || !props.allFormElements) {
|
||||
return true
|
||||
}
|
||||
return isFormOptionVisible(option.visibilityConditions, props.allFormElements)
|
||||
})
|
||||
})
|
||||
|
||||
const readOnlyColumnIndices = computed<Set<number>>(() => {
|
||||
if (!props.allFormElements) return new Set()
|
||||
|
||||
return new Set(
|
||||
props.formOptions
|
||||
.map((option, index) => ({ option, index }))
|
||||
.filter(({ option }) => {
|
||||
const conditions = option.columnConfig?.readOnlyConditions
|
||||
return conditions && isFormOptionVisible(conditions, props.allFormElements!)
|
||||
})
|
||||
.map(({ index }) => index)
|
||||
)
|
||||
})
|
||||
|
||||
// When columns become read-only, reset their values to the configured default
|
||||
watch(
|
||||
readOnlyColumnIndices,
|
||||
(currentSet, previousSet) => {
|
||||
const newlyReadOnlyIndices = [...currentSet].filter((i) => !previousSet?.has(i))
|
||||
if (newlyReadOnlyIndices.length === 0) return
|
||||
|
||||
const updatedOptions = props.formOptions.map((option, colIndex) => {
|
||||
if (!newlyReadOnlyIndices.includes(colIndex)) return option
|
||||
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
const defaultValue = isColumnCheckbox(colIndex) ? false : (option.columnConfig?.readOnlyDefaultValue ?? '')
|
||||
const newValue = JSON.stringify(columnValues.map(() => defaultValue))
|
||||
|
||||
return newValue !== option.value ? { ...option, value: newValue } : option
|
||||
})
|
||||
|
||||
if (updatedOptions.some((opt, i) => opt !== props.formOptions[i])) {
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const dataColumns = computed<DataColumn[]>(() =>
|
||||
visibleColumns.value.map(({ originalIndex }) => ({
|
||||
key: `col_${originalIndex}`,
|
||||
colIndex: originalIndex
|
||||
}))
|
||||
)
|
||||
|
||||
const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
|
||||
const columns: TableColumn<TableRowData>[] = visibleColumns.value.map(({ option, originalIndex }) => ({
|
||||
accessorKey: `col_${originalIndex}`,
|
||||
header: option.label || ''
|
||||
}))
|
||||
|
||||
// Only show actions column if not disabled AND rows can be modified
|
||||
if (!props.disabled && canModifyRows.value) {
|
||||
columns.push({
|
||||
id: 'actions',
|
||||
header: ''
|
||||
})
|
||||
}
|
||||
|
||||
return columns
|
||||
})
|
||||
|
||||
const tableData = computed<TableRowData[]>(() => {
|
||||
if (props.formOptions.length === 0) return []
|
||||
|
||||
const columnData: CellValue[][] = props.formOptions.map((option, colIndex) => {
|
||||
const parsed = parseColumnValues(option.value)
|
||||
|
||||
// Normalize cell values based on column type
|
||||
if (isColumnMultipleAllowed(colIndex)) {
|
||||
return parsed.map((val) => (Array.isArray(val) ? val : []))
|
||||
}
|
||||
if (isColumnCheckbox(colIndex)) {
|
||||
return parsed.map((val) => val === true)
|
||||
}
|
||||
return parsed
|
||||
})
|
||||
|
||||
const rowCount = Math.max(...columnData.map((col) => col.length), 0)
|
||||
|
||||
const rows: TableRowData[] = []
|
||||
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
||||
const row: TableRowData = {}
|
||||
props.formOptions.forEach((_, colIndex) => {
|
||||
const cellValue = columnData[colIndex]?.[rowIndex]
|
||||
if (isColumnMultipleAllowed(colIndex)) {
|
||||
row[`col_${colIndex}`] = Array.isArray(cellValue) ? cellValue : []
|
||||
} else if (isColumnCheckbox(colIndex)) {
|
||||
row[`col_${colIndex}`] = cellValue === true
|
||||
} else {
|
||||
row[`col_${colIndex}`] = typeof cellValue === 'string' ? cellValue : ''
|
||||
}
|
||||
})
|
||||
rows.push(row)
|
||||
}
|
||||
|
||||
return rows
|
||||
})
|
||||
|
||||
function isColumnMultipleAllowed(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return option?.columnConfig?.isMultipleAllowed === true
|
||||
}
|
||||
|
||||
function isColumnCheckbox(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return option?.columnConfig?.isCheckbox === true
|
||||
}
|
||||
|
||||
function getColumnOptions(colIndex: number, currentRowData?: TableRowData): string[] {
|
||||
const option = props.formOptions[colIndex]
|
||||
if (!option?.columnConfig || !props.allFormElements) {
|
||||
return []
|
||||
}
|
||||
|
||||
const { columnConfig } = option
|
||||
const { rowConstraint } = columnConfig
|
||||
|
||||
// If row constraint is configured, filter values based on current row's key value
|
||||
if (rowConstraint?.constraintTableReference && currentRowData) {
|
||||
const currentRowAsRecord: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(currentRowData)) {
|
||||
currentRowAsRecord[key] =
|
||||
typeof value === 'string' ? value : Array.isArray(value) ? value.join(',') : String(value)
|
||||
}
|
||||
|
||||
return getConstrainedColumnValues(
|
||||
columnConfig,
|
||||
currentRowAsRecord,
|
||||
rowConstraint.constraintTableReference,
|
||||
rowConstraint.constraintKeyColumnIndex ?? 0,
|
||||
rowConstraint.constraintValueColumnIndex ?? 1,
|
||||
props.allFormElements,
|
||||
rowConstraint.currentRowKeyColumnIndex
|
||||
)
|
||||
}
|
||||
|
||||
return getReferencedColumnValues(columnConfig, props.allFormElements)
|
||||
}
|
||||
|
||||
function updateCell(rowIndex: number, columnKey: string, value: string) {
|
||||
const colIndex = parseInt(columnKey.replace('col_', ''), 10)
|
||||
|
||||
const updatedOptions = props.formOptions.map((option, index) => {
|
||||
if (index !== colIndex) return option
|
||||
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
while (columnValues.length <= rowIndex) {
|
||||
columnValues.push('')
|
||||
}
|
||||
columnValues[rowIndex] = value
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function updateCellValue(rowIndex: number, _columnKey: string, colIndex: number, value: string | string[]) {
|
||||
const updatedOptions = props.formOptions.map((option, index) => {
|
||||
if (index !== colIndex) return option
|
||||
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
const isMultiple = isColumnMultipleAllowed(colIndex)
|
||||
while (columnValues.length <= rowIndex) {
|
||||
columnValues.push(isMultiple ? [] : '')
|
||||
}
|
||||
columnValues[rowIndex] = value
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function updateCheckboxCell(rowIndex: number, colIndex: number, value: boolean) {
|
||||
const updatedOptions = props.formOptions.map((option, index) => {
|
||||
if (index !== colIndex) return option
|
||||
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
while (columnValues.length <= rowIndex) {
|
||||
columnValues.push(false)
|
||||
}
|
||||
columnValues[rowIndex] = value
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
const updatedOptions = props.formOptions.map((option, colIndex) => {
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
|
||||
// Determine initial value based on column type
|
||||
let initialValue: CellValue = ''
|
||||
if (readOnlyColumnIndices.value.has(colIndex)) {
|
||||
initialValue = isColumnCheckbox(colIndex) ? false : (option.columnConfig?.readOnlyDefaultValue ?? '')
|
||||
} else if (isColumnMultipleAllowed(colIndex)) {
|
||||
initialValue = []
|
||||
} else if (isColumnCheckbox(colIndex)) {
|
||||
initialValue = false
|
||||
}
|
||||
columnValues.push(initialValue)
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function removeRow(rowIndex: number) {
|
||||
const updatedOptions = props.formOptions.map((option) => {
|
||||
const columnValues = parseColumnValues(option.value)
|
||||
columnValues.splice(rowIndex, 1)
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function isCellVisible(colIndex: number, rowData: TableRowData): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
const rowVisibility = option?.columnConfig?.rowVisibilityCondition
|
||||
if (!rowVisibility) return true
|
||||
|
||||
const { sourceColumnIndex, expectedValues, operator } = rowVisibility
|
||||
const sourceKey = `col_${sourceColumnIndex}`
|
||||
const cellValue = rowData[sourceKey]
|
||||
|
||||
let sourceValues: string[] = []
|
||||
if (Array.isArray(cellValue)) {
|
||||
sourceValues = cellValue
|
||||
} else if (typeof cellValue === 'string' && cellValue) {
|
||||
sourceValues = cellValue.split(',').map((v) => v.trim())
|
||||
}
|
||||
|
||||
if (operator === 'CONTAINS' || operator === 'EQUALS') {
|
||||
return (expectedValues ?? []).some((expected) =>
|
||||
sourceValues.some((v) => v.toLowerCase() === expected.toLowerCase())
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function parseColumnValues(value: string | undefined): CellValue[] {
|
||||
try {
|
||||
const parsed = JSON.parse(value || '[]')
|
||||
return Array.isArray(parsed) ? parsed : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,172 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<UTable :data="tableData" :columns="tableColumns" class="w-full" :ui="{ td: 'p-2' }">
|
||||
<template v-for="col in dataColumns" :key="col.key" #[`${col.key}-cell`]="slotProps">
|
||||
<span
|
||||
v-if="
|
||||
props.isCellVisible &&
|
||||
!props.isCellVisible(col.colIndex, (slotProps.row as TableRow<TableRowData>).original)
|
||||
"
|
||||
>
|
||||
-
|
||||
</span>
|
||||
<template v-else>
|
||||
<!-- Column with cross-reference -->
|
||||
<USelectMenu
|
||||
v-if="hasColumnReference(col.colIndex)"
|
||||
:model-value="getCellValueForSelect(slotProps.row as TableRow<TableRowData>, col.key, col.colIndex)"
|
||||
:items="getColumnOptions(col.colIndex, (slotProps.row as TableRow<TableRowData>).original)"
|
||||
:disabled="disabled"
|
||||
:placeholder="$t('applicationForms.formElements.table.selectValue')"
|
||||
:multiple="isColumnMultipleAllowed(col.colIndex)"
|
||||
class="w-full min-w-32"
|
||||
@update:model-value="
|
||||
(val: string | string[]) =>
|
||||
$emit('update:cellValue', (slotProps.row as TableRow<TableRowData>).index, col.key, col.colIndex, val)
|
||||
"
|
||||
/>
|
||||
<!-- Read-only column -->
|
||||
<span v-else-if="isColumnReadOnly(col.colIndex)" class="text-muted px-2 py-1">
|
||||
{{ formatCellDisplay(slotProps.row as any, col.key, col.colIndex) }}
|
||||
</span>
|
||||
<!-- Checkbox column -->
|
||||
<div v-else-if="isColumnCheckbox(col.colIndex)" class="flex justify-center">
|
||||
<UCheckbox
|
||||
:model-value="getCellValueForCheckbox(slotProps.row as TableRow<TableRowData>, col.key)"
|
||||
:disabled="disabled"
|
||||
@update:model-value="
|
||||
(val: boolean | 'indeterminate') =>
|
||||
$emit(
|
||||
'update:checkboxCell',
|
||||
(slotProps.row as TableRow<TableRowData>).index,
|
||||
col.colIndex,
|
||||
val === true
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<!-- Regular text input with auto-resizing textarea -->
|
||||
<UTextarea
|
||||
v-else
|
||||
:model-value="getCellValue(slotProps.row as TableRow<TableRowData>, col.key)"
|
||||
:disabled="disabled"
|
||||
:rows="1"
|
||||
autoresize
|
||||
:maxrows="0"
|
||||
class="w-full min-w-32"
|
||||
@update:model-value="
|
||||
(val: string | number) =>
|
||||
$emit('update:cell', (slotProps.row as TableRow<TableRowData>).index, col.key, String(val))
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="canModifyRows" #actions-cell="{ row }">
|
||||
<UButton
|
||||
v-if="!disabled"
|
||||
icon="i-lucide-trash-2"
|
||||
color="error"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
:aria-label="$t('applicationForms.formElements.table.removeRow')"
|
||||
@click="$emit('removeRow', row.index)"
|
||||
/>
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<div v-if="tableData.length === 0" class="text-center text-dimmed py-4">
|
||||
{{ $t('applicationForms.formElements.table.noData') }}
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
v-if="!disabled && canModifyRows"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
leading-icon="i-lucide-plus"
|
||||
:class="addRowButtonClass"
|
||||
@click="$emit('addRow')"
|
||||
>
|
||||
{{ $t('applicationForms.formElements.table.addRow') }}
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||
|
||||
type CellValue = string | string[] | boolean
|
||||
type TableRowData = Record<string, CellValue>
|
||||
|
||||
interface DataColumn {
|
||||
key: string
|
||||
colIndex: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
tableData: TableRowData[]
|
||||
tableColumns: TableColumn<TableRowData>[]
|
||||
dataColumns: DataColumn[]
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
canModifyRows: boolean
|
||||
addRowButtonClass?: string
|
||||
getColumnOptions: (colIndex: number, currentRowData?: TableRowData) => string[]
|
||||
readOnlyColumnIndices?: Set<number>
|
||||
isCellVisible?: (colIndex: number, rowData: TableRowData) => boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'update:cell', rowIndex: number, columnKey: string, value: string): void
|
||||
(e: 'update:cellValue', rowIndex: number, columnKey: string, colIndex: number, value: string | string[]): void
|
||||
(e: 'update:checkboxCell', rowIndex: number, colIndex: number, value: boolean): void
|
||||
(e: 'addRow'): void
|
||||
(e: 'removeRow', rowIndex: number): void
|
||||
}>()
|
||||
|
||||
function hasColumnReference(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return !!option?.columnConfig?.sourceTableReference && !isColumnReadOnly(colIndex)
|
||||
}
|
||||
|
||||
function isColumnReadOnly(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return option?.columnConfig?.isReadOnly === true || (props.readOnlyColumnIndices?.has(colIndex) ?? false)
|
||||
}
|
||||
|
||||
function isColumnMultipleAllowed(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return option?.columnConfig?.isMultipleAllowed === true
|
||||
}
|
||||
|
||||
function isColumnCheckbox(colIndex: number): boolean {
|
||||
const option = props.formOptions[colIndex]
|
||||
return option?.columnConfig?.isCheckbox === true
|
||||
}
|
||||
|
||||
function getCellValue(row: TableRow<TableRowData>, columnKey: string): string {
|
||||
const value = row.original[columnKey]
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function getCellValueForSelect(row: TableRow<TableRowData>, columnKey: string, colIndex: number): string | string[] {
|
||||
const value = row.original[columnKey]
|
||||
if (isColumnMultipleAllowed(colIndex)) {
|
||||
return Array.isArray(value) ? value : []
|
||||
}
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function getCellValueForCheckbox(row: TableRow<TableRowData>, columnKey: string): boolean {
|
||||
const value = row.original[columnKey]
|
||||
return value === true
|
||||
}
|
||||
|
||||
function formatCellDisplay(row: TableRow<TableRowData>, columnKey: string, colIndex: number): string {
|
||||
const value = row.original[columnKey]
|
||||
if (isColumnMultipleAllowed(colIndex) && Array.isArray(value)) {
|
||||
return value.length > 0 ? value.join(', ') : '-'
|
||||
}
|
||||
return (typeof value === 'string' ? value : '') || '-'
|
||||
}
|
||||
</script>
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<UFormField :label="label">
|
||||
<UTextarea v-model="modelValue" class="w-full" autoresize />
|
||||
</UFormField>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
|
||||
const props = defineProps<{
|
||||
label?: string
|
||||
formOptions: FormOptionDto[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
const modelValue = computed({
|
||||
get: () => props.formOptions[0]?.value ?? '',
|
||||
set: (val) => {
|
||||
const firstOption = props.formOptions[0]
|
||||
if (val && firstOption) {
|
||||
const updatedModelValue = [...props.formOptions]
|
||||
updatedModelValue[0] = { ...firstOption, value: val.toString() }
|
||||
emit('update:formOptions', updatedModelValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div>{{ $t('applicationForms.formElements.unimplemented') }}</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user