Files
Inventory_frontend/app/composables/useComments.ts
Matthieu 10ad7b7f41 feat(comments) : add file attachments UI for comments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:10:19 +01:00

208 lines
5.9 KiB
TypeScript

import { ref } from 'vue'
import { useApi } from './useApi'
import { useToast } from './useToast'
import { extractCollection } from '~/shared/utils/apiHelpers'
export interface CommentDocument {
id: string
name: string
filename: string
mimeType: string
size: number
type: string
fileUrl: string
downloadUrl: string
createdAt: string
}
export interface Comment {
id: string
content: string
entityType: string
entityId: string
entityName?: string | null
authorId: string
authorName: string
status: 'open' | 'resolved'
resolvedById?: string | null
resolvedByName?: string | null
resolvedAt?: string | null
createdAt: string
updatedAt: string
documents?: CommentDocument[]
}
interface CommentResult {
success: boolean
data?: Comment | Comment[]
error?: string
}
interface CommentListResult {
success: boolean
data?: Comment[]
total?: number
error?: string
}
export function useComments() {
const { get, post, patch, postFormData, delete: del } = useApi()
const { showSuccess, showError } = useToast()
const loading = ref(false)
const fetchComments = async (
entityType: string,
entityId: string,
status: string = 'open',
): Promise<CommentListResult> => {
loading.value = true
try {
const result = await get<Comment[]>(`/comments/by-entity/${entityType}/${entityId}?status=${status}`)
if (result.success) {
const items = (result.data ?? []) as Comment[]
return { success: true, data: items }
}
return { success: false, error: result.error }
} catch (error) {
const err = error as Error
return { success: false, error: err.message }
} finally {
loading.value = false
}
}
const fetchAllComments = async (options: {
status?: string
entityType?: string
entityName?: string
page?: number
itemsPerPage?: number
orderBy?: string
orderDir?: string
} = {}): Promise<CommentListResult> => {
loading.value = true
try {
const params = new URLSearchParams()
if (options.status) params.set('status', options.status)
if (options.entityType) params.set('entityType', options.entityType)
if (options.entityName) params.set('entityName', options.entityName)
params.set('sort', options.orderBy || 'createdAt')
params.set('direction', options.orderDir || 'desc')
params.set('itemsPerPage', String(options.itemsPerPage || 30))
params.set('page', String(options.page || 1))
const result = await get<{ items: Comment[]; total: number }>(`/comments/search/list?${params.toString()}`)
if (result.success && result.data) {
const data = result.data as { items: Comment[]; total: number }
return { success: true, data: data.items, total: data.total }
}
return { success: false, error: result.error }
} catch (error) {
const err = error as Error
return { success: false, error: err.message }
} finally {
loading.value = false
}
}
const createComment = async (
entityType: string,
entityId: string,
content: string,
entityName?: string,
files?: File[],
): Promise<CommentResult> => {
loading.value = true
try {
let result
if (files && files.length > 0) {
const formData = new FormData()
formData.append('content', content)
formData.append('entityType', entityType)
formData.append('entityId', entityId)
if (entityName) formData.append('entityName', entityName)
for (const file of files) {
formData.append('files[]', file)
}
result = await postFormData('/comments', formData)
} else {
const payload: Record<string, string> = { entityType, entityId, content }
if (entityName) payload.entityName = entityName
result = await post('/comments', payload)
}
if (result.success) {
showSuccess('Commentaire ajouté')
return { success: true, data: result.data as Comment }
}
if (result.error) showError(result.error)
return { success: false, error: result.error }
} catch (error) {
const err = error as Error
showError('Impossible d\'ajouter le commentaire')
return { success: false, error: err.message }
} finally {
loading.value = false
}
}
const resolveComment = async (commentId: string): Promise<CommentResult> => {
loading.value = true
try {
const result = await patch(`/comments/${commentId}/resolve`)
if (result.success) {
showSuccess('Commentaire résolu')
return { success: true, data: result.data as Comment }
}
if (result.error) showError(result.error)
return { success: false, error: result.error }
} catch (error) {
const err = error as Error
showError('Impossible de résoudre le commentaire')
return { success: false, error: err.message }
} finally {
loading.value = false
}
}
const deleteComment = async (commentId: string): Promise<CommentResult> => {
loading.value = true
try {
const result = await del(`/comments/${commentId}`)
if (result.success) {
showSuccess('Commentaire supprimé')
return { success: true }
}
if (result.error) showError(result.error)
return { success: false, error: result.error }
} catch (error) {
const err = error as Error
showError('Impossible de supprimer le commentaire')
return { success: false, error: err.message }
} finally {
loading.value = false
}
}
const fetchUnresolvedCount = async (): Promise<number> => {
try {
const result = await get<{ count: number }>('/comments/stats/unresolved-count')
if (result.success && result.data) {
return result.data.count
}
return 0
} catch {
return 0
}
}
return {
loading,
fetchComments,
fetchAllComments,
createComment,
resolveComment,
deleteComment,
fetchUnresolvedCount,
}
}