feat(frontend,backend): Create and load comments
This commit is contained in:
@@ -8,17 +8,20 @@ import jakarta.persistence.AttributeOverrides
|
|||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Embedded
|
import jakarta.persistence.Embedded
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
|
import jakarta.persistence.EntityListeners
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn
|
import jakarta.persistence.JoinColumn
|
||||||
import jakarta.persistence.ManyToOne
|
import jakarta.persistence.ManyToOne
|
||||||
import org.springframework.data.annotation.CreatedDate
|
import org.springframework.data.annotation.CreatedDate
|
||||||
import org.springframework.data.annotation.LastModifiedDate
|
import org.springframework.data.annotation.LastModifiedDate
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@EntityListeners(AuditingEntityListener::class)
|
||||||
class Comment(
|
class Comment(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.comment
|
package com.betriebsratkanzlei.legalconsenthub.comment
|
||||||
|
|
||||||
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
|
||||||
import org.springframework.data.domain.Page
|
import org.springframework.data.domain.Page
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
@@ -9,5 +9,5 @@ import java.util.UUID
|
|||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface CommentRepository : JpaRepository<Comment, UUID> {
|
interface CommentRepository : JpaRepository<Comment, UUID> {
|
||||||
fun findAllByFormElement(formElement: FormElement, pageable: Pageable): Page<Comment>
|
fun findAllByApplicationForm(applicationForm: ApplicationForm, pageable: Pageable): Page<Comment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.comment
|
package com.betriebsratkanzlei.legalconsenthub.comment
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
|
||||||
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotCreatedException
|
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotCreatedException
|
||||||
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotDeletedException
|
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotDeletedException
|
||||||
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotFoundException
|
import com.betriebsratkanzlei.legalconsenthub.error.CommentNotFoundException
|
||||||
@@ -16,7 +17,7 @@ import java.util.UUID
|
|||||||
@Service
|
@Service
|
||||||
class CommentService(
|
class CommentService(
|
||||||
private val commentRepository: CommentRepository,
|
private val commentRepository: CommentRepository,
|
||||||
private val formElementRepository: FormElementRepository,
|
private val applicationFormRepository: ApplicationFormRepository,
|
||||||
private val commentMapper: CommentMapper
|
private val commentMapper: CommentMapper
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -36,11 +37,11 @@ class CommentService(
|
|||||||
return commentRepository.findById(id).orElseThrow { CommentNotFoundException(id) }
|
return commentRepository.findById(id).orElseThrow { CommentNotFoundException(id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getComments(formElementId: UUID): Page<Comment> {
|
fun getComments(applicationFormId: UUID): Page<Comment> {
|
||||||
val formElement =
|
val applicationForm =
|
||||||
formElementRepository.findById(formElementId).orElseThrow { FormElementNotFoundException(formElementId) }
|
applicationFormRepository.findById(applicationFormId).orElse(null)
|
||||||
val pageable = PageRequest.of(0, 10)
|
val pageable = PageRequest.of(0, 10)
|
||||||
return commentRepository.findAllByFormElement(formElement, pageable)
|
return commentRepository.findAllByApplicationForm(applicationForm, pageable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateComment(commentDto: CommentDto): Comment {
|
fun updateComment(commentDto: CommentDto): Comment {
|
||||||
|
|||||||
@@ -1,30 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="(formElement, index) in props.modelValue" :key="formElement.id">
|
<template v-for="(formElement, index) in props.modelValue" :key="formElement.id">
|
||||||
<UFormField>
|
<div class="group py-3 lg:py-4">
|
||||||
<component
|
<div class="flex justify-between">
|
||||||
:is="getResolvedComponent(formElement)"
|
<component
|
||||||
:form-options="formElement.options"
|
:is="getResolvedComponent(formElement)"
|
||||||
:disabled="props.disabled"
|
:form-options="formElement.options"
|
||||||
@update:form-options="updateFormOptions($event, index)"
|
:disabled="props.disabled"
|
||||||
/>
|
@update:form-options="updateFormOptions($event, index)"
|
||||||
</UFormField>
|
/>
|
||||||
|
<UIcon
|
||||||
|
name="i-lucide-message-square-more"
|
||||||
|
class="size-5 cursor-pointer hidden group-hover:block"
|
||||||
|
@click="toggleComments(formElement.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template v-if="applicationFormId && activeFormElement === formElement.id">
|
||||||
|
<template v-if="comments?.[formElement.id] && comments[formElement.id].length > 0">
|
||||||
|
<UChatMessages :auto-scroll="false" :should-scroll-to-bottom="false">
|
||||||
|
<UChatMessage
|
||||||
|
v-for="comment in comments[formElement.id]"
|
||||||
|
:id="comment.id"
|
||||||
|
:key="comment.id"
|
||||||
|
:avatar="{ icon: 'i-lucide-bot' }"
|
||||||
|
:content="comment.message"
|
||||||
|
role="user"
|
||||||
|
:side="comment.createdBy.id === userDto.id ? 'right' : 'left'"
|
||||||
|
variant="subtle"
|
||||||
|
>
|
||||||
|
<template #leading="{ avatar }">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<UAvatar icon="i-lucide-bot" />
|
||||||
|
<p class="text-sm">{{ comment.createdBy.name }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UChatMessage>
|
||||||
|
</UChatMessages>
|
||||||
|
</template>
|
||||||
|
<UTextarea v-model="commentTextAreaValue" class="w-full" />
|
||||||
|
<UButton class="my-3 lg:my-4" @click="submitComment(applicationFormId, formElement.id, commentTextAreaValue)">
|
||||||
|
Submit
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
<USeparator />
|
<USeparator />
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormElementDto, FormOptionDto } from '~/.api-client'
|
import type { CreateCommentDto, FormElementDto, FormOptionDto } from '~/.api-client'
|
||||||
import { resolveComponent } from 'vue'
|
import { resolveComponent } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: FormElementDto[]
|
modelValue: FormElementDto[]
|
||||||
|
applicationFormId?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:modelValue', value: FormElementDto[]): void
|
(e: 'update:modelValue', formElementDto: FormElementDto[]): void
|
||||||
|
(e: 'click:comments', formElementId: string): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const commentStore = useCommentStore()
|
||||||
|
const { load: loadComments, createComment } = commentStore
|
||||||
|
const { comments } = storeToRefs(commentStore)
|
||||||
|
const { userDto } = useAuth()
|
||||||
|
|
||||||
|
if (props.applicationFormId) {
|
||||||
|
console.log('Loading comments for application form:', props.applicationFormId)
|
||||||
|
await loadComments(props.applicationFormId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeFormElement = ref('')
|
||||||
|
const commentTextAreaValue = ref('')
|
||||||
|
|
||||||
// TODO: Lazy loading?
|
// TODO: Lazy loading?
|
||||||
function getResolvedComponent(formElement: FormElementDto) {
|
function getResolvedComponent(formElement: FormElementDto) {
|
||||||
switch (formElement.type) {
|
switch (formElement.type) {
|
||||||
@@ -48,4 +97,27 @@ function updateFormOptions(formOptions: FormOptionDto[], formElementIndex: numbe
|
|||||||
updatedModelValue[formElementIndex] = { ...updatedModelValue[formElementIndex], options: formOptions }
|
updatedModelValue[formElementIndex] = { ...updatedModelValue[formElementIndex], options: formOptions }
|
||||||
emit('update:modelValue', updatedModelValue)
|
emit('update:modelValue', updatedModelValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleComments(formElementId: string) {
|
||||||
|
if (activeFormElement.value === formElementId) {
|
||||||
|
activeFormElement.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activeFormElement.value = formElementId
|
||||||
|
emit('click:comments', formElementId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitComment(applicationFormId: string, formElementId: string, newComment: string) {
|
||||||
|
const newCommentDto: CreateCommentDto = {
|
||||||
|
message: newComment,
|
||||||
|
createdBy: userDto.value
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await createComment(applicationFormId, formElementId, newCommentDto)
|
||||||
|
commentTextAreaValue.value = ''
|
||||||
|
useToast().add({ title: 'Comment created successfully', color: 'success' })
|
||||||
|
} catch {
|
||||||
|
useToast().add({ title: 'Error creating comment', color: 'error' })
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import {
|
|||||||
} from '~/.api-client'
|
} from '~/.api-client'
|
||||||
import { useApplicationFormApi } from './useApplicationFormApi'
|
import { useApplicationFormApi } from './useApplicationFormApi'
|
||||||
|
|
||||||
const currentApplicationForm: Ref<ApplicationFormDto | undefined> = ref()
|
|
||||||
|
|
||||||
export function useApplicationForm() {
|
export function useApplicationForm() {
|
||||||
const applicationFormApi = useApplicationFormApi()
|
const applicationFormApi = useApplicationFormApi()
|
||||||
|
|
||||||
@@ -15,8 +13,7 @@ export function useApplicationForm() {
|
|||||||
createApplicationFormDto: CreateApplicationFormDto
|
createApplicationFormDto: CreateApplicationFormDto
|
||||||
): Promise<ApplicationFormDto> {
|
): Promise<ApplicationFormDto> {
|
||||||
try {
|
try {
|
||||||
currentApplicationForm.value = await applicationFormApi.createApplicationForm(createApplicationFormDto)
|
return await applicationFormApi.createApplicationForm(createApplicationFormDto)
|
||||||
return currentApplicationForm.value
|
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error('Failed creating application form:', e.response)
|
console.error('Failed creating application form:', e.response)
|
||||||
@@ -62,8 +59,7 @@ export function useApplicationForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentApplicationForm.value = await applicationFormApi.updateApplicationForm(id, applicationFormDto)
|
return await applicationFormApi.updateApplicationForm(id, applicationFormDto)
|
||||||
return currentApplicationForm.value
|
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed updating application form with ID ${id}:`, e.response)
|
console.error(`Failed updating application form with ID ${id}:`, e.response)
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { ApplicationFormApi, Configuration } from '../../.api-client'
|
import {
|
||||||
import type { CreateApplicationFormDto, ApplicationFormDto, PagedApplicationFormDto } from '~/.api-client'
|
ApplicationFormApi,
|
||||||
|
Configuration,
|
||||||
|
type CreateApplicationFormDto,
|
||||||
|
type ApplicationFormDto,
|
||||||
|
type PagedApplicationFormDto
|
||||||
|
} from '~/.api-client'
|
||||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||||
|
|
||||||
export function useApplicationFormApi() {
|
export function useApplicationFormApi() {
|
||||||
|
|||||||
73
legalconsenthub/composables/comment/useComment.ts
Normal file
73
legalconsenthub/composables/comment/useComment.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { type CreateCommentDto, type CommentDto, type PagedCommentDto, ResponseError } from '~/.api-client'
|
||||||
|
import { useCommentApi } from './useCommentApi'
|
||||||
|
|
||||||
|
export function useComment() {
|
||||||
|
const commentApi = useCommentApi()
|
||||||
|
|
||||||
|
async function createComment(
|
||||||
|
applicationFormId: string,
|
||||||
|
formElementId: string,
|
||||||
|
createCommentDto: CreateCommentDto
|
||||||
|
): Promise<CommentDto> {
|
||||||
|
try {
|
||||||
|
return await commentApi.createComment(applicationFormId, formElementId, createCommentDto)
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error('Failed creating comment:', e.response)
|
||||||
|
} else {
|
||||||
|
console.error('Failed creating comment:', e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCommentsByApplicationFormId(applicationFormId: string): Promise<PagedCommentDto> {
|
||||||
|
try {
|
||||||
|
return await commentApi.getCommentsByApplicationFormId(applicationFormId)
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error('Failed retrieving comments:', e.response)
|
||||||
|
} else {
|
||||||
|
console.error('Failed retrieving comments:', e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateComment(id?: string, commentDto?: CommentDto): Promise<CommentDto> {
|
||||||
|
if (!id || !commentDto) {
|
||||||
|
return Promise.reject(new Error('ID or comment DTO missing'))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await commentApi.updateComment(id, commentDto)
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error(`Failed updating comment with ID ${id}:`, e.response)
|
||||||
|
} else {
|
||||||
|
console.error(`Failed updating comment with ID ${id}:`, e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCommentById(id: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
return await commentApi.deleteCommentById(id)
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error(`Failed deleting comment with ID ${id}:`, e.response)
|
||||||
|
} else {
|
||||||
|
console.error(`Failed deleting comment with ID ${id}:`, e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
createComment,
|
||||||
|
getCommentsByApplicationFormId,
|
||||||
|
updateComment,
|
||||||
|
deleteCommentById
|
||||||
|
}
|
||||||
|
}
|
||||||
43
legalconsenthub/composables/comment/useCommentApi.ts
Normal file
43
legalconsenthub/composables/comment/useCommentApi.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { CommentApi, Configuration, type CommentDto, type CreateCommentDto, type PagedCommentDto } from '~/.api-client'
|
||||||
|
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||||
|
|
||||||
|
export function useCommentApi() {
|
||||||
|
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||||
|
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||||
|
const { jwt } = useAuth()
|
||||||
|
|
||||||
|
const basePath = withoutTrailingSlash(
|
||||||
|
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
|
||||||
|
)
|
||||||
|
|
||||||
|
const commentApiClient = new CommentApi(
|
||||||
|
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
|
||||||
|
)
|
||||||
|
|
||||||
|
async function createComment(
|
||||||
|
applicationFormId: string,
|
||||||
|
formElementId: string,
|
||||||
|
createCommentDto: CreateCommentDto
|
||||||
|
): Promise<CommentDto> {
|
||||||
|
return commentApiClient.createComment({ applicationFormId, formElementId, createCommentDto })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCommentsByApplicationFormId(applicationFormId: string): Promise<PagedCommentDto> {
|
||||||
|
return commentApiClient.getCommentsByApplicationFormId({ applicationFormId })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateComment(id: string, commentDto: CommentDto): Promise<CommentDto> {
|
||||||
|
return commentApiClient.updateComment({ id, commentDto })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCommentById(id: string): Promise<void> {
|
||||||
|
return commentApiClient.deleteComment({ id })
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
createComment,
|
||||||
|
getCommentsByApplicationFormId,
|
||||||
|
updateComment,
|
||||||
|
deleteCommentById
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { createAuthClient } from 'better-auth/client'
|
|||||||
import type { InferSessionFromClient, InferUserFromClient, ClientOptions } from 'better-auth/client'
|
import type { InferSessionFromClient, InferUserFromClient, ClientOptions } from 'better-auth/client'
|
||||||
import { organizationClient, jwtClient } from 'better-auth/client/plugins'
|
import { organizationClient, jwtClient } from 'better-auth/client/plugins'
|
||||||
import type { RouteLocationRaw } from 'vue-router'
|
import type { RouteLocationRaw } from 'vue-router'
|
||||||
|
import type { UserDto } from '~/.api-client'
|
||||||
|
|
||||||
interface RuntimeAuthConfig {
|
interface RuntimeAuthConfig {
|
||||||
redirectUserTo: RouteLocationRaw | string
|
redirectUserTo: RouteLocationRaw | string
|
||||||
@@ -103,9 +104,15 @@ export function useAuth() {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userDto = computed<UserDto>(() => ({
|
||||||
|
id: user.value?.id ?? '',
|
||||||
|
name: user.value?.name ?? 'Unknown'
|
||||||
|
}))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
session,
|
session,
|
||||||
user,
|
user,
|
||||||
|
userDto,
|
||||||
loggedIn: computed(() => !!session.value),
|
loggedIn: computed(() => !!session.value),
|
||||||
signIn: client.signIn,
|
signIn: client.signIn,
|
||||||
signUp: client.signUp,
|
signUp: client.signUp,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"migrate:betterauth": "pnpm dlx @better-auth/cli migrate --config server/utils/auth.ts"
|
"migrate:betterauth": "pnpm dlx @better-auth/cli migrate --config server/utils/auth.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/ui-pro": "3.0.1",
|
"@nuxt/ui-pro": "3.1.1",
|
||||||
"@pinia/nuxt": "0.10.1",
|
"@pinia/nuxt": "0.10.1",
|
||||||
"better-auth": "1.1.16",
|
"better-auth": "1.1.16",
|
||||||
"better-sqlite3": "11.8.1",
|
"better-sqlite3": "11.8.1",
|
||||||
|
|||||||
@@ -19,13 +19,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col gap-4 sm:gap-6 lg:gap-12 w-full lg:max-w-4xl mx-auto">
|
<div class="flex flex-col w-full lg:max-w-4xl mx-auto">
|
||||||
<UPageCard variant="subtle">
|
<UCard variant="subtle">
|
||||||
<UForm class="space-y-4" :state="{}" @submit="onSubmit">
|
<FormEngine
|
||||||
<FormEngine v-if="applicationForm" v-model="applicationForm.formElements" :disabled="isReadOnly" />
|
v-if="applicationForm"
|
||||||
<UButton type="submit" :disabled="isReadOnly">Submit</UButton>
|
v-model="applicationForm.formElements"
|
||||||
</UForm>
|
:application-form-id="applicationForm.id"
|
||||||
</UPageCard>
|
:disabled="isReadOnly"
|
||||||
|
@click:comments="openComments"
|
||||||
|
/>
|
||||||
|
<UButton :disabled="isReadOnly" class="my-3 lg:my-4" @click="onSubmit">Submit</UButton>
|
||||||
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardPanel>
|
</UDashboardPanel>
|
||||||
@@ -71,4 +75,8 @@ async function onSubmit() {
|
|||||||
await navigateTo('/')
|
await navigateTo('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openComments(formElementId: string) {
|
||||||
|
console.log('open comments for', formElementId)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -36,14 +36,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComplianceStatus, type PagedApplicationFormDto, type UserDto } from '~/.api-client'
|
import { ComplianceStatus, type PagedApplicationFormDto } from '~/.api-client'
|
||||||
import { useApplicationFormValidator } from '~/composables/useApplicationFormValidator'
|
import { useApplicationFormValidator } from '~/composables/useApplicationFormValidator'
|
||||||
import type { FormElementId } from '~/types/FormElement'
|
import type { FormElementId } from '~/types/FormElement'
|
||||||
|
|
||||||
const { getAllApplicationFormTemplates } = useApplicationFormTemplate()
|
const { getAllApplicationFormTemplates } = useApplicationFormTemplate()
|
||||||
const { createApplicationForm } = useApplicationForm()
|
const { createApplicationForm } = useApplicationForm()
|
||||||
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
||||||
const { user, selectedOrganization } = useAuth()
|
const { userDto, selectedOrganization } = useAuth()
|
||||||
|
|
||||||
const { data } = await useAsyncData<PagedApplicationFormDto>(async () => {
|
const { data } = await useAsyncData<PagedApplicationFormDto>(async () => {
|
||||||
return await getAllApplicationFormTemplates()
|
return await getAllApplicationFormTemplates()
|
||||||
@@ -93,12 +93,8 @@ const ampelStatusEmoji = computed(() => {
|
|||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
if (applicationFormTemplate.value) {
|
if (applicationFormTemplate.value) {
|
||||||
const userDto: UserDto = {
|
applicationFormTemplate.value.createdBy = userDto.value
|
||||||
id: user.value?.id ?? '',
|
applicationFormTemplate.value.lastModifiedBy = userDto.value
|
||||||
name: user.value?.name ?? 'Unknown'
|
|
||||||
}
|
|
||||||
applicationFormTemplate.value.createdBy = userDto
|
|
||||||
applicationFormTemplate.value.lastModifiedBy = userDto
|
|
||||||
applicationFormTemplate.value.organizationId = selectedOrganization.value?.id ?? ''
|
applicationFormTemplate.value.organizationId = selectedOrganization.value?.id ?? ''
|
||||||
|
|
||||||
await createApplicationForm(applicationFormTemplate.value)
|
await createApplicationForm(applicationFormTemplate.value)
|
||||||
|
|||||||
1214
legalconsenthub/pnpm-lock.yaml
generated
1214
legalconsenthub/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
98
legalconsenthub/stores/useCommentStore.ts
Normal file
98
legalconsenthub/stores/useCommentStore.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { type CreateCommentDto, type CommentDto, ResponseError } from '~/.api-client'
|
||||||
|
import { useCommentApi } from '~/composables/comment/useCommentApi'
|
||||||
|
|
||||||
|
export const useCommentStore = defineStore('comment', () => {
|
||||||
|
type FormElementId = string
|
||||||
|
const commentApi = useCommentApi()
|
||||||
|
const comments = ref<Record<FormElementId, CommentDto[]>>({})
|
||||||
|
const loadedForms = ref(new Set<string>())
|
||||||
|
|
||||||
|
async function load(applicationFormId: string) {
|
||||||
|
if (loadedForms.value.has(applicationFormId)) return
|
||||||
|
const { data, error } = await useAsyncData(`comments:${applicationFormId}`, () =>
|
||||||
|
commentApi.getCommentsByApplicationFormId(applicationFormId)
|
||||||
|
)
|
||||||
|
if (error.value) {
|
||||||
|
console.error('Failed loading comments:', error.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
comments.value =
|
||||||
|
data.value?.content.reduce((acc: Record<FormElementId, CommentDto[]>, comment: CommentDto) => {
|
||||||
|
const formElementId = comment.formElementId
|
||||||
|
if (!acc[formElementId]) {
|
||||||
|
acc[formElementId] = []
|
||||||
|
}
|
||||||
|
acc[formElementId].push(comment)
|
||||||
|
return acc
|
||||||
|
}, {}) || {}
|
||||||
|
loadedForms.value.add(applicationFormId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createComment(
|
||||||
|
applicationFormId: string,
|
||||||
|
formElementId: string,
|
||||||
|
createCommentDto: CreateCommentDto
|
||||||
|
): Promise<CommentDto> {
|
||||||
|
try {
|
||||||
|
const newComment = await commentApi.createComment(applicationFormId, formElementId, createCommentDto)
|
||||||
|
if (!comments.value[formElementId]) {
|
||||||
|
comments.value[formElementId] = []
|
||||||
|
}
|
||||||
|
comments.value[formElementId].push(newComment)
|
||||||
|
return newComment
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error('Failed creating comment:', e.response)
|
||||||
|
} else {
|
||||||
|
console.error('Failed creating comment:', e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateComment(id?: string, commentDto?: CommentDto): Promise<CommentDto> {
|
||||||
|
if (!id || !commentDto) {
|
||||||
|
return Promise.reject(new Error('ID or comment DTO missing'))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedComment = await commentApi.updateComment(id, commentDto)
|
||||||
|
const formElementId = updatedComment.formElementId
|
||||||
|
const index = comments.value?.[formElementId]?.findIndex((comment) => comment.id === id) ?? -1
|
||||||
|
if (index !== -1 && comments.value?.[formElementId][index]) {
|
||||||
|
comments.value[formElementId][index] = updatedComment
|
||||||
|
}
|
||||||
|
return updatedComment
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error(`Failed updating comment with ID ${id}:`, e.response)
|
||||||
|
} else {
|
||||||
|
console.error(`Failed updating comment with ID ${id}:`, e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCommentById(id: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await commentApi.deleteCommentById(id)
|
||||||
|
for (const formElementId in comments.value) {
|
||||||
|
const index = comments.value[formElementId].findIndex((comment) => comment.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
comments.value[formElementId].splice(index, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ResponseError) {
|
||||||
|
console.error(`Failed deleting comment with ID ${id}:`, e.response)
|
||||||
|
} else {
|
||||||
|
console.error(`Failed deleting comment with ID ${id}:`, e)
|
||||||
|
}
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { load, createComment, updateComment, deleteCommentById, comments }
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user