feat(#9): Nuxt 4 migration
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import type {
|
||||
CreateApplicationFormDto,
|
||||
CreateFormElementDto,
|
||||
ApplicationFormDto,
|
||||
PagedApplicationFormDto
|
||||
} from '~~/.api-client'
|
||||
import { useApplicationFormApi } from './useApplicationFormApi'
|
||||
|
||||
export function useApplicationForm() {
|
||||
const applicationFormApi = useApplicationFormApi()
|
||||
|
||||
async function createApplicationForm(
|
||||
createApplicationFormDto: CreateApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
try {
|
||||
return await applicationFormApi.createApplicationForm(createApplicationFormDto)
|
||||
} catch (e: unknown) {
|
||||
console.error('Failed creating application form:', e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllApplicationForms(organizationId: string): Promise<PagedApplicationFormDto> {
|
||||
try {
|
||||
return await applicationFormApi.getAllApplicationForms(organizationId)
|
||||
} catch (e: unknown) {
|
||||
console.error('Failed retrieving application forms:', e, JSON.stringify(e))
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getApplicationFormById(id: string): Promise<ApplicationFormDto> {
|
||||
try {
|
||||
return await applicationFormApi.getApplicationFormById(id)
|
||||
} catch (e: unknown) {
|
||||
console.error(`Failed retrieving application form with ID ${id}:`, e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateApplicationForm(
|
||||
id?: string,
|
||||
applicationFormDto?: ApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
if (!id || !applicationFormDto) {
|
||||
return Promise.reject(new Error('ID or application form DTO missing'))
|
||||
}
|
||||
|
||||
try {
|
||||
return await applicationFormApi.updateApplicationForm(id, applicationFormDto)
|
||||
} catch (e: unknown) {
|
||||
console.error(`Failed updating application form with ID ${id}:`, e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteApplicationFormById(id: string): Promise<void> {
|
||||
try {
|
||||
return await applicationFormApi.deleteApplicationFormById(id)
|
||||
} catch (e: unknown) {
|
||||
console.error(`Failed deleting application form with ID ${id}:`, e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function submitApplicationForm(id: string): Promise<ApplicationFormDto> {
|
||||
if (!id) {
|
||||
return Promise.reject(new Error('ID missing'))
|
||||
}
|
||||
|
||||
try {
|
||||
return await applicationFormApi.submitApplicationForm(id)
|
||||
} catch (e: unknown) {
|
||||
console.error(`Failed submitting application form with ID ${id}:`, e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
addFormElementToSection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
ApplicationFormApi,
|
||||
Configuration,
|
||||
type CreateApplicationFormDto,
|
||||
type CreateFormElementDto,
|
||||
type ApplicationFormDto,
|
||||
type PagedApplicationFormDto
|
||||
} from '~~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
|
||||
|
||||
export function useApplicationFormApi() {
|
||||
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||
|
||||
const basePath = withoutTrailingSlash(
|
||||
cleanDoubleSlashes(
|
||||
import.meta.client
|
||||
? appBaseUrl + clientProxyBasePath
|
||||
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
|
||||
)
|
||||
)
|
||||
|
||||
const applicationFormApiClient = new ApplicationFormApi(
|
||||
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
|
||||
)
|
||||
|
||||
async function createApplicationForm(
|
||||
createApplicationFormDto: CreateApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.createApplicationForm({ createApplicationFormDto })
|
||||
}
|
||||
|
||||
async function getAllApplicationForms(organizationId: string): Promise<PagedApplicationFormDto> {
|
||||
return applicationFormApiClient.getAllApplicationForms({ organizationId })
|
||||
}
|
||||
|
||||
async function getApplicationFormById(id: string): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.getApplicationFormById({ id })
|
||||
}
|
||||
|
||||
async function updateApplicationForm(
|
||||
id: string,
|
||||
applicationFormDto: ApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.updateApplicationForm({ id, applicationFormDto })
|
||||
}
|
||||
|
||||
async function deleteApplicationFormById(id: string): Promise<void> {
|
||||
return applicationFormApiClient.deleteApplicationForm({ id })
|
||||
}
|
||||
|
||||
async function submitApplicationForm(id: string): Promise<ApplicationFormDto> {
|
||||
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,
|
||||
addFormElementToSection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
type CreateApplicationFormDto,
|
||||
type ApplicationFormDto,
|
||||
type PagedApplicationFormDto,
|
||||
ResponseError
|
||||
} from '~~/.api-client'
|
||||
import { useApplicationFormTemplateApi } from './useApplicationFormTemplateApi'
|
||||
|
||||
const currentApplicationForm: Ref<ApplicationFormDto | undefined> = ref()
|
||||
|
||||
export async function useApplicationFormTemplate() {
|
||||
const applicationFormApi = await useApplicationFormTemplateApi()
|
||||
|
||||
async function createApplicationFormTemplate(
|
||||
createApplicationFormDto: CreateApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
try {
|
||||
currentApplicationForm.value = await applicationFormApi.createApplicationFormTemplate(createApplicationFormDto)
|
||||
return currentApplicationForm.value
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error('Failed creating application form:', e.response)
|
||||
} else {
|
||||
console.error('Failed creating application form:', e)
|
||||
}
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllApplicationFormTemplates(): Promise<PagedApplicationFormDto> {
|
||||
try {
|
||||
return await applicationFormApi.getAllApplicationFormTemplates()
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error('Failed retrieving application forms:', e.response)
|
||||
} else {
|
||||
console.error('Failed retrieving application forms:', e)
|
||||
}
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getApplicationFormTemplateById(id: string): Promise<ApplicationFormDto> {
|
||||
try {
|
||||
return await applicationFormApi.getApplicationFormTemplateById(id)
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error(`Failed retrieving application form with ID ${id}:`, e.response)
|
||||
} else {
|
||||
console.error(`Failed retrieving application form with ID ${id}:`, e)
|
||||
}
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateApplicationFormTemplate(
|
||||
id?: string,
|
||||
applicationFormDto?: ApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
if (!id || !applicationFormDto) {
|
||||
return Promise.reject(new Error('ID or application form DTO missing'))
|
||||
}
|
||||
|
||||
try {
|
||||
currentApplicationForm.value = await applicationFormApi.updateApplicationFormTemplate(id, applicationFormDto)
|
||||
return currentApplicationForm.value
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error(`Failed updating application form with ID ${id}:`, e.response)
|
||||
} else {
|
||||
console.error(`Failed updating application form with ID ${id}:`, e)
|
||||
}
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteApplicationFormTemplateById(id: string): Promise<void> {
|
||||
try {
|
||||
return await applicationFormApi.deleteApplicationFormTemplateById(id)
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error(`Failed deleting application form with ID ${id}:`, e.response)
|
||||
} else {
|
||||
console.error(`Failed deleting application form with ID ${id}:`, e)
|
||||
}
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createApplicationFormTemplate,
|
||||
getAllApplicationFormTemplates,
|
||||
getApplicationFormTemplateById,
|
||||
updateApplicationFormTemplate,
|
||||
deleteApplicationFormTemplateById
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ApplicationFormTemplateApi, Configuration } from '../../../.api-client'
|
||||
import type { CreateApplicationFormDto, ApplicationFormDto, PagedApplicationFormDto } from '~~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
|
||||
|
||||
export async function useApplicationFormTemplateApi() {
|
||||
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||
|
||||
const basePath = withoutTrailingSlash(
|
||||
cleanDoubleSlashes(
|
||||
import.meta.client
|
||||
? appBaseUrl + clientProxyBasePath
|
||||
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
|
||||
)
|
||||
)
|
||||
|
||||
const applicationFormApiClient = new ApplicationFormTemplateApi(
|
||||
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
|
||||
)
|
||||
|
||||
async function createApplicationFormTemplate(
|
||||
createApplicationFormDto: CreateApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.createApplicationFormTemplate({ createApplicationFormDto })
|
||||
}
|
||||
|
||||
async function getAllApplicationFormTemplates(): Promise<PagedApplicationFormDto> {
|
||||
return applicationFormApiClient.getAllApplicationFormTemplates()
|
||||
}
|
||||
|
||||
async function getApplicationFormTemplateById(id: string): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.getApplicationFormTemplateById({ id })
|
||||
}
|
||||
|
||||
async function updateApplicationFormTemplate(
|
||||
id: string,
|
||||
applicationFormDto: ApplicationFormDto
|
||||
): Promise<ApplicationFormDto> {
|
||||
return applicationFormApiClient.updateApplicationFormTemplate({ id, applicationFormDto })
|
||||
}
|
||||
|
||||
async function deleteApplicationFormTemplateById(id: string): Promise<void> {
|
||||
return applicationFormApiClient.deleteApplicationFormTemplate({ id })
|
||||
}
|
||||
|
||||
return {
|
||||
createApplicationFormTemplate,
|
||||
getAllApplicationFormTemplates,
|
||||
getApplicationFormTemplateById,
|
||||
updateApplicationFormTemplate,
|
||||
deleteApplicationFormTemplateById
|
||||
}
|
||||
}
|
||||
47
legalconsenthub/app/composables/comment/useCommentApi.ts
Normal file
47
legalconsenthub/app/composables/comment/useCommentApi.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { CommentApi, Configuration, type CommentDto, type CreateCommentDto, type PagedCommentDto } from '~~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
|
||||
|
||||
export function useCommentApi() {
|
||||
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||
|
||||
const basePath = withoutTrailingSlash(
|
||||
cleanDoubleSlashes(
|
||||
import.meta.client
|
||||
? appBaseUrl + clientProxyBasePath
|
||||
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
|
||||
)
|
||||
)
|
||||
|
||||
const commentApiClient = new CommentApi(
|
||||
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { CreateCommentDto, CommentDto } from '~~/.api-client'
|
||||
import { useCommentStore } from '~~/stores/useCommentStore'
|
||||
import { useUserStore } from '~~/stores/useUserStore'
|
||||
|
||||
export function useCommentTextarea(applicationFormId: string) {
|
||||
const commentStore = useCommentStore()
|
||||
const { createComment, updateComment } = commentStore
|
||||
const userStore = useUserStore()
|
||||
const { user } = storeToRefs(userStore)
|
||||
const isEditingComment = ref(false)
|
||||
const currentEditedComment = ref<CommentDto | null>(null)
|
||||
const commentTextAreaValue = ref('')
|
||||
const toast = useToast()
|
||||
|
||||
async function submitComment(formElementId: string) {
|
||||
const newCommentDto: CreateCommentDto = {
|
||||
message: commentTextAreaValue.value
|
||||
}
|
||||
try {
|
||||
await createComment(applicationFormId, formElementId, newCommentDto)
|
||||
commentTextAreaValue.value = ''
|
||||
toast.add({ title: 'Comment created successfully', color: 'success' })
|
||||
} catch (e) {
|
||||
toast.add({ title: 'Error creating comment', color: 'error' })
|
||||
console.error('Error creating comment:', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateEditComment() {
|
||||
if (!currentEditedComment.value) return
|
||||
const updatedComment: CommentDto = { ...currentEditedComment.value, message: commentTextAreaValue.value }
|
||||
try {
|
||||
await updateComment(currentEditedComment.value.id, updatedComment)
|
||||
commentTextAreaValue.value = ''
|
||||
currentEditedComment.value = null
|
||||
isEditingComment.value = false
|
||||
toast.add({ title: 'Comment updated successfully', color: 'success' })
|
||||
} catch (e) {
|
||||
toast.add({ title: 'Error updating comment', color: 'error' })
|
||||
console.error('Error updating comment:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function editComment(comment: CommentDto) {
|
||||
isEditingComment.value = true
|
||||
currentEditedComment.value = comment
|
||||
commentTextAreaValue.value = comment.message || ''
|
||||
}
|
||||
|
||||
function cancelEditComment() {
|
||||
isEditingComment.value = false
|
||||
currentEditedComment.value = null
|
||||
commentTextAreaValue.value = ''
|
||||
}
|
||||
|
||||
function isCommentByUser(comment: CommentDto) {
|
||||
return comment.createdBy.keycloakId === user.value?.keycloakId
|
||||
}
|
||||
|
||||
return {
|
||||
commentTextAreaValue,
|
||||
submitComment,
|
||||
updateEditComment,
|
||||
editComment,
|
||||
cancelEditComment,
|
||||
isEditingComment,
|
||||
isCommentByUser
|
||||
}
|
||||
}
|
||||
47
legalconsenthub/app/composables/complianceMap.ts
Normal file
47
legalconsenthub/app/composables/complianceMap.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ComplianceStatus, EmployeeDataCategory, FormElementType, ProcessingPurpose } from '~~/.api-client'
|
||||
|
||||
export const complianceMap = new Map<ProcessingPurpose, Map<EmployeeDataCategory, ComplianceStatus>>([
|
||||
[
|
||||
ProcessingPurpose.SystemOperation,
|
||||
new Map([
|
||||
[EmployeeDataCategory.None, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.NonCritical, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.ReviewRequired, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.Sensitive, ComplianceStatus.NonCritical]
|
||||
])
|
||||
],
|
||||
[
|
||||
ProcessingPurpose.BusinessProcess,
|
||||
new Map([
|
||||
[EmployeeDataCategory.None, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.NonCritical, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.ReviewRequired, ComplianceStatus.Warning],
|
||||
[EmployeeDataCategory.Sensitive, ComplianceStatus.Critical]
|
||||
])
|
||||
],
|
||||
[
|
||||
ProcessingPurpose.DataAnalysis,
|
||||
new Map([
|
||||
[EmployeeDataCategory.None, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.NonCritical, ComplianceStatus.Warning],
|
||||
[EmployeeDataCategory.ReviewRequired, ComplianceStatus.Warning],
|
||||
[EmployeeDataCategory.Sensitive, ComplianceStatus.Critical]
|
||||
])
|
||||
],
|
||||
[
|
||||
ProcessingPurpose.None,
|
||||
new Map([
|
||||
[EmployeeDataCategory.None, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.NonCritical, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.ReviewRequired, ComplianceStatus.NonCritical],
|
||||
[EmployeeDataCategory.Sensitive, ComplianceStatus.NonCritical]
|
||||
])
|
||||
]
|
||||
])
|
||||
|
||||
export const complianceCheckableElementTypes: FormElementType[] = [
|
||||
FormElementType.Switch,
|
||||
FormElementType.Select,
|
||||
FormElementType.Checkbox,
|
||||
FormElementType.Radiobutton
|
||||
]
|
||||
4
legalconsenthub/app/composables/index.ts
Normal file
4
legalconsenthub/app/composables/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { useApplicationFormTemplate } from './applicationFormTemplate/useApplicationFormTemplate'
|
||||
export { useApplicationForm } from './applicationForm/useApplicationForm'
|
||||
export { useNotification } from './notification/useNotification'
|
||||
export { useNotificationApi } from './notification/useNotificationApi'
|
||||
144
legalconsenthub/app/composables/notification/useNotification.ts
Normal file
144
legalconsenthub/app/composables/notification/useNotification.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import type { NotificationDto } from '~~/.api-client'
|
||||
import { useUserStore } from '~~/stores/useUserStore'
|
||||
|
||||
export const useNotification = () => {
|
||||
const {
|
||||
getNotifications,
|
||||
getUnreadNotifications,
|
||||
getUnreadNotificationCount,
|
||||
markAllNotificationsAsRead,
|
||||
markNotificationAsRead
|
||||
} = useNotificationApi()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const organizationId = computed(() => userStore.selectedOrganization?.id)
|
||||
const { user } = useUserSession()
|
||||
const userId = computed(() => user.value?.keycloakId)
|
||||
|
||||
const notifications = ref<NotificationDto[]>([])
|
||||
const unreadNotifications = ref<NotificationDto[]>([])
|
||||
const unreadCount = ref<number>(0)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const fetchNotifications = async (page: number = 0, size: number = 20) => {
|
||||
if (!organizationId.value) {
|
||||
console.warn('No organization selected')
|
||||
return
|
||||
}
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response = await getNotifications(organizationId.value, page, size)
|
||||
notifications.value = response.content || []
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch notifications:', error)
|
||||
throw error
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchUnreadNotifications = async () => {
|
||||
if (!organizationId.value) {
|
||||
console.warn('No organization selected')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const response = await getUnreadNotifications(organizationId.value)
|
||||
unreadNotifications.value = response || []
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch unread notifications:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const fetchUnreadCount = async () => {
|
||||
if (!userId.value || !organizationId.value) {
|
||||
console.warn('No user or organization selected')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const count = await getUnreadNotificationCount(userId.value, organizationId.value)
|
||||
unreadCount.value = count || 0
|
||||
return count
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch unread count:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const markAllAsRead = async () => {
|
||||
if (!organizationId.value) {
|
||||
console.warn('No organization selected')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await markAllNotificationsAsRead(organizationId.value)
|
||||
unreadCount.value = 0
|
||||
unreadNotifications.value = []
|
||||
notifications.value = notifications.value.map((n) => ({ ...n, isRead: true }))
|
||||
} catch (error) {
|
||||
console.error('Failed to mark all as read:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const markAsRead = async (notificationId: string) => {
|
||||
if (!organizationId.value) {
|
||||
console.warn('No organization selected')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await markNotificationAsRead(notificationId, organizationId.value)
|
||||
const index = notifications.value.findIndex((n) => n.id === notificationId)
|
||||
if (index !== -1) {
|
||||
notifications.value[index].isRead = true
|
||||
}
|
||||
// Remove from unread notifications
|
||||
unreadNotifications.value = unreadNotifications.value.filter((n) => n.id !== notificationId)
|
||||
|
||||
if (unreadCount.value > 0) {
|
||||
unreadCount.value--
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to mark notification as read:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const handleNotificationClick = async (notification: NotificationDto) => {
|
||||
if (!notification.isRead) {
|
||||
await markAsRead(notification.id)
|
||||
}
|
||||
if (notification.clickTarget) {
|
||||
await navigateTo(notification.clickTarget)
|
||||
}
|
||||
}
|
||||
|
||||
const startPeriodicRefresh = (intervalMs: number = 30000) => {
|
||||
const interval = setInterval(() => {
|
||||
void fetchUnreadCount()
|
||||
}, intervalMs)
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(interval)
|
||||
})
|
||||
|
||||
return interval
|
||||
}
|
||||
|
||||
return {
|
||||
notifications,
|
||||
unreadNotifications,
|
||||
unreadCount,
|
||||
isLoading,
|
||||
fetchNotifications,
|
||||
fetchUnreadNotifications,
|
||||
fetchUnreadCount,
|
||||
markAllAsRead,
|
||||
markAsRead,
|
||||
handleNotificationClick,
|
||||
startPeriodicRefresh
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
NotificationApi,
|
||||
Configuration,
|
||||
type NotificationDto,
|
||||
type PagedNotificationDto,
|
||||
type CreateNotificationDto
|
||||
} from '~~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
|
||||
|
||||
export function useNotificationApi() {
|
||||
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||
|
||||
const basePath = withoutTrailingSlash(
|
||||
cleanDoubleSlashes(
|
||||
import.meta.client
|
||||
? appBaseUrl + clientProxyBasePath
|
||||
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
|
||||
)
|
||||
)
|
||||
|
||||
const notificationApiClient = new NotificationApi(
|
||||
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
|
||||
)
|
||||
|
||||
async function createNotification(createNotificationDto: CreateNotificationDto): Promise<NotificationDto> {
|
||||
return notificationApiClient.createNotification({ createNotificationDto })
|
||||
}
|
||||
|
||||
async function getNotifications(organizationId: string, page?: number, size?: number): Promise<PagedNotificationDto> {
|
||||
return notificationApiClient.getNotifications({ organizationId, page, size })
|
||||
}
|
||||
|
||||
async function getUnreadNotifications(organizationId: string): Promise<NotificationDto[]> {
|
||||
return notificationApiClient.getUnreadNotifications({ organizationId })
|
||||
}
|
||||
|
||||
async function getUnreadNotificationCount(userId: string, organizationId: string): Promise<number> {
|
||||
return notificationApiClient.getUnreadNotificationCount({ userId, organizationId })
|
||||
}
|
||||
|
||||
async function markAllNotificationsAsRead(organizationId: string): Promise<void> {
|
||||
return notificationApiClient.markAllNotificationsAsRead({ organizationId })
|
||||
}
|
||||
|
||||
async function markNotificationAsRead(id: string, organizationId: string): Promise<NotificationDto> {
|
||||
return notificationApiClient.markNotificationAsRead({ id, organizationId })
|
||||
}
|
||||
|
||||
async function clearAllNotifications(organizationId: string): Promise<void> {
|
||||
return notificationApiClient.clearAllNotifications({ organizationId })
|
||||
}
|
||||
|
||||
return {
|
||||
createNotification,
|
||||
getNotifications,
|
||||
getUnreadNotifications,
|
||||
getUnreadNotificationCount,
|
||||
markAllNotificationsAsRead,
|
||||
markNotificationAsRead,
|
||||
clearAllNotifications
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { ComplianceStatus, type FormElementDto } from '~~/.api-client'
|
||||
import { complianceCheckableElementTypes, complianceMap } from './complianceMap'
|
||||
import type { FormElementId } from '~~/types/formElement'
|
||||
|
||||
const formElementComplianceMap = ref(new Map<FormElementId, ComplianceStatus>())
|
||||
|
||||
export function useApplicationFormValidator() {
|
||||
function getHighestComplianceStatus(): ComplianceStatus {
|
||||
const complianceStatusValues = Array.from(formElementComplianceMap.value.values())
|
||||
const highestComplianceNumber = Math.max(
|
||||
...complianceStatusValues.map((complianceStatus) => Object.values(ComplianceStatus).indexOf(complianceStatus))
|
||||
)
|
||||
return Object.values(ComplianceStatus)[highestComplianceNumber]
|
||||
}
|
||||
|
||||
function validateFormElements(formElements: FormElementDto[]): Map<FormElementId, ComplianceStatus> {
|
||||
formElementComplianceMap.value.clear()
|
||||
|
||||
formElements.forEach((formElement) => {
|
||||
if (!complianceCheckableElementTypes.includes(formElement.type)) return
|
||||
|
||||
// Reset any previously set compliance status when all options are false
|
||||
const hasAtLeastOneOptionSet = formElement.options.some((option) => option.value && option.value !== 'false')
|
||||
if (!hasAtLeastOneOptionSet) {
|
||||
// No value set, continue with next form element
|
||||
return
|
||||
}
|
||||
|
||||
formElement.options.forEach((option) => {
|
||||
if (!option.value) {
|
||||
console.log(`Value missing for ${formElement.type}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Value not set to true, continue with next option
|
||||
if (option.value === 'false') {
|
||||
return
|
||||
}
|
||||
|
||||
const currentHighestComplianceStatus =
|
||||
complianceMap?.get(option.processingPurpose)?.get(option.employeeDataCategory) ?? ComplianceStatus.NonCritical
|
||||
const currentHighestComplianceStatusPos =
|
||||
Object.values(ComplianceStatus).indexOf(currentHighestComplianceStatus)
|
||||
|
||||
if (formElementComplianceMap.value.has(formElement.id)) {
|
||||
const newComplianceStatus = formElementComplianceMap.value.get(formElement.id)!
|
||||
const newComplianceStatusPos = Object.values(ComplianceStatus).indexOf(newComplianceStatus)
|
||||
|
||||
if (newComplianceStatusPos > currentHighestComplianceStatusPos) {
|
||||
formElementComplianceMap.value.set(formElement.id, newComplianceStatus)
|
||||
}
|
||||
} else {
|
||||
formElementComplianceMap.value.set(formElement.id, currentHighestComplianceStatus)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return formElementComplianceMap.value
|
||||
}
|
||||
|
||||
return { getHighestComplianceStatus, validateFormElements }
|
||||
}
|
||||
47
legalconsenthub/app/composables/useFormElementManagement.ts
Normal file
47
legalconsenthub/app/composables/useFormElementManagement.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
57
legalconsenthub/app/composables/useFormStepper.ts
Normal file
57
legalconsenthub/app/composables/useFormStepper.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
152
legalconsenthub/app/composables/usePermissions.ts
Normal file
152
legalconsenthub/app/composables/usePermissions.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
export type Permission =
|
||||
| 'application-form:read'
|
||||
| 'application-form:write'
|
||||
| 'application-form:sign'
|
||||
| 'application-form-template:add'
|
||||
| 'application-form-template:edit'
|
||||
| 'application-form-template:delete'
|
||||
| 'comment:add'
|
||||
| 'comment:edit'
|
||||
| 'comment:delete'
|
||||
|
||||
export type Role =
|
||||
| 'CHIEF_EXECUTIVE_OFFICER'
|
||||
| 'BUSINESS_DEPARTMENT'
|
||||
| 'IT_DEPARTMENT'
|
||||
| 'HUMAN_RESOURCES'
|
||||
| 'HEAD_OF_WORKS_COUNCIL'
|
||||
| 'WORKS_COUNCIL'
|
||||
| 'EMPLOYEE'
|
||||
|
||||
const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
|
||||
CHIEF_EXECUTIVE_OFFICER: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form:sign',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
HEAD_OF_WORKS_COUNCIL: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form:sign',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
BUSINESS_DEPARTMENT: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
IT_DEPARTMENT: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
HUMAN_RESOURCES: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
WORKS_COUNCIL: [
|
||||
'application-form:read',
|
||||
'application-form:write',
|
||||
'application-form-template:add',
|
||||
'application-form-template:edit',
|
||||
'application-form-template:delete',
|
||||
'comment:add',
|
||||
'comment:edit',
|
||||
'comment:delete'
|
||||
],
|
||||
EMPLOYEE: ['application-form:read', 'comment:add', 'comment:edit']
|
||||
}
|
||||
|
||||
export const usePermissions = () => {
|
||||
const { user } = useUserSession()
|
||||
|
||||
const userRoles = computed<Role[]>(() => {
|
||||
return (user.value?.roles ?? []) as Role[]
|
||||
})
|
||||
|
||||
const userPermissions = computed<Permission[]>(() => {
|
||||
const permissions = new Set<Permission>()
|
||||
userRoles.value.forEach((role) => {
|
||||
const rolePermissions = ROLE_PERMISSIONS[role] ?? []
|
||||
rolePermissions.forEach((permission) => permissions.add(permission))
|
||||
})
|
||||
return Array.from(permissions)
|
||||
})
|
||||
|
||||
const hasPermission = (permission: Permission): boolean => {
|
||||
return userPermissions.value.includes(permission)
|
||||
}
|
||||
|
||||
const hasAnyPermission = (permissions: Permission[]): boolean => {
|
||||
return permissions.some((permission) => hasPermission(permission))
|
||||
}
|
||||
|
||||
const hasAllPermissions = (permissions: Permission[]): boolean => {
|
||||
return permissions.every((permission) => hasPermission(permission))
|
||||
}
|
||||
|
||||
const hasRole = (role: Role): boolean => {
|
||||
return userRoles.value.includes(role)
|
||||
}
|
||||
|
||||
const hasAnyRole = (roles: Role[]): boolean => {
|
||||
return roles.some((role) => hasRole(role))
|
||||
}
|
||||
|
||||
const canReadApplicationForms = computed(() => hasPermission('application-form:read'))
|
||||
const canWriteApplicationForms = computed(() => hasPermission('application-form:write'))
|
||||
const canSignApplicationForms = computed(() => hasPermission('application-form:sign'))
|
||||
const canAddTemplate = computed(() => hasPermission('application-form-template:add'))
|
||||
const canEditTemplate = computed(() => hasPermission('application-form-template:edit'))
|
||||
const canDeleteTemplate = computed(() => hasPermission('application-form-template:delete'))
|
||||
const canAddComment = computed(() => hasPermission('comment:add'))
|
||||
const canEditComment = computed(() => hasPermission('comment:edit'))
|
||||
const canDeleteComment = computed(() => hasPermission('comment:delete'))
|
||||
|
||||
return {
|
||||
userRoles,
|
||||
userPermissions,
|
||||
hasPermission,
|
||||
hasAnyPermission,
|
||||
hasAllPermissions,
|
||||
hasRole,
|
||||
hasAnyRole,
|
||||
canReadApplicationForms,
|
||||
canWriteApplicationForms,
|
||||
canSignApplicationForms,
|
||||
canAddTemplate,
|
||||
canEditTemplate,
|
||||
canDeleteTemplate,
|
||||
canAddComment,
|
||||
canEditComment,
|
||||
canDeleteComment
|
||||
}
|
||||
}
|
||||
|
||||
92
legalconsenthub/app/composables/useServerHealth.ts
Normal file
92
legalconsenthub/app/composables/useServerHealth.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
export const isServerAvailable = ref(true)
|
||||
export const isChecking = ref(false)
|
||||
export const lastCheckTime = ref<Date | null>(null)
|
||||
|
||||
export function useServerHealth() {
|
||||
const checkInterval = ref<NodeJS.Timeout | null>(null)
|
||||
const healthCheckUrl = '/api/actuator/health'
|
||||
|
||||
async function checkServerHealth(): Promise<boolean> {
|
||||
if (isChecking.value) return isServerAvailable.value
|
||||
|
||||
isChecking.value = true
|
||||
lastCheckTime.value = new Date()
|
||||
|
||||
try {
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000)
|
||||
|
||||
const response = await fetch(healthCheckUrl, {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
const wasAvailable = isServerAvailable.value
|
||||
isServerAvailable.value = response.ok
|
||||
|
||||
if (!wasAvailable && isServerAvailable.value) {
|
||||
console.info('Server is back online')
|
||||
}
|
||||
|
||||
if (wasAvailable && !isServerAvailable.value) {
|
||||
console.warn('Server is no longer available')
|
||||
}
|
||||
|
||||
return isServerAvailable.value
|
||||
} catch (error) {
|
||||
const wasAvailable = isServerAvailable.value
|
||||
isServerAvailable.value = false
|
||||
|
||||
if (wasAvailable) {
|
||||
console.warn('Server health check failed:', error)
|
||||
}
|
||||
|
||||
return false
|
||||
} finally {
|
||||
isChecking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function startPeriodicHealthCheck(intervalMs: number = 60000) {
|
||||
if (checkInterval.value) {
|
||||
clearInterval(checkInterval.value)
|
||||
}
|
||||
|
||||
checkServerHealth()
|
||||
|
||||
checkInterval.value = setInterval(() => {
|
||||
checkServerHealth()
|
||||
}, intervalMs)
|
||||
|
||||
onUnmounted(() => {
|
||||
if (checkInterval.value) {
|
||||
clearInterval(checkInterval.value)
|
||||
checkInterval.value = null
|
||||
}
|
||||
})
|
||||
|
||||
return checkInterval.value
|
||||
}
|
||||
|
||||
const stopHealthCheck = () => {
|
||||
if (checkInterval.value) {
|
||||
clearInterval(checkInterval.value)
|
||||
checkInterval.value = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isServerAvailable,
|
||||
isChecking,
|
||||
lastCheckTime,
|
||||
healthCheckUrl,
|
||||
checkServerHealth,
|
||||
startPeriodicHealthCheck,
|
||||
stopHealthCheck
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user