feat(#21): Replaced chat comment component with proper cursor-based commenting

This commit is contained in:
2025-12-25 18:03:31 +01:00
parent 7f7852a66a
commit e472a5715d
27 changed files with 968 additions and 244 deletions

View File

@@ -32,35 +32,58 @@
:form-element-id="formElementItem.formElement.id"
:application-form-id="applicationFormId"
:comments="comments?.[formElementItem.formElement.id]"
:total-count="
commentCounts?.[formElementItem.formElement.id] ?? comments?.[formElementItem.formElement.id]?.length ?? 0
"
@close="activeFormElement = ''"
/>
</div>
<div
:class="[
'transition-opacity duration-200',
openDropdownId === getElementKey(formElementItem.formElement, formElementItem.indexInSubsection)
? 'opacity-100'
: 'opacity-0 group-hover:opacity-100'
]"
>
<UDropdownMenu
:items="
getDropdownItems(
formElementItem.formElement,
getElementKey(formElementItem.formElement, formElementItem.indexInSubsection),
formElementItem.indexInSubsection
)
"
:content="{ align: 'end' }"
@update:open="
(isOpen: boolean) =>
handleDropdownToggle(
getElementKey(formElementItem.formElement, formElementItem.indexInSubsection),
isOpen
)
"
<div class="flex items-start gap-1">
<div class="min-w-9">
<UButton
v-if="
applicationFormId &&
formElementItem.formElement.id &&
(commentCounts?.[formElementItem.formElement.id] ?? 0) > 0
"
color="neutral"
variant="soft"
size="xs"
icon="i-lucide-message-square"
class="w-full justify-center"
@click="toggleComments(formElementItem.formElement.id)"
>
{{ commentCounts?.[formElementItem.formElement.id] ?? 0 }}
</UButton>
</div>
<div
:class="[
'transition-opacity duration-200',
openDropdownId === getElementKey(formElementItem.formElement, formElementItem.indexInSubsection)
? 'opacity-100'
: 'opacity-100 lg:opacity-0 lg:group-hover:opacity-100 lg:focus-within:opacity-100'
]"
>
<UButton icon="i-lucide-ellipsis-vertical" color="neutral" variant="ghost" />
</UDropdownMenu>
<UDropdownMenu
:items="
getDropdownItems(
formElementItem.formElement,
getElementKey(formElementItem.formElement, formElementItem.indexInSubsection),
formElementItem.indexInSubsection
)
"
:content="{ align: 'end' }"
@update:open="
(isOpen: boolean) =>
handleDropdownToggle(
getElementKey(formElementItem.formElement, formElementItem.indexInSubsection),
isOpen
)
"
>
<UButton icon="i-lucide-ellipsis-vertical" color="neutral" variant="ghost" />
</UDropdownMenu>
</div>
</div>
</div>
<USeparator v-if="visibleIndex < visibleFormElements.length - 1" />
@@ -89,14 +112,18 @@ const emit = defineEmits<{
}>()
const commentStore = useCommentStore()
const { load: loadComments } = commentStore
const { comments } = storeToRefs(commentStore)
const logger = useLogger().withTag('FormEngine')
const { loadInitial: loadCommentsInitial } = commentStore
const { commentsByApplicationFormId, countsByApplicationFormId } = storeToRefs(commentStore)
if (props.applicationFormId) {
logger.debug('Loading comments for application form:', props.applicationFormId)
await loadComments(props.applicationFormId)
}
const comments = computed(() => {
if (!props.applicationFormId) return {}
return commentsByApplicationFormId.value[props.applicationFormId] ?? {}
})
const commentCounts = computed(() => {
if (!props.applicationFormId) return {}
return countsByApplicationFormId.value[props.applicationFormId] ?? {}
})
const route = useRoute()
const activeFormElement = ref('')
@@ -189,12 +216,15 @@ function updateFormOptions(formOptions: FormOptionDto[], target: VisibleFormElem
emit('update:modelValue', updatedModelValue)
}
function toggleComments(formElementId: string) {
async function toggleComments(formElementId: string) {
if (activeFormElement.value === formElementId) {
activeFormElement.value = ''
return
}
activeFormElement.value = formElementId
if (props.applicationFormId) {
await loadCommentsInitial(props.applicationFormId, formElementId)
}
emit('click:comments', formElementId)
}