diff --git a/frontend/components/task/TaskDocumentList.vue b/frontend/components/task/TaskDocumentList.vue index 270f8c2..a9be141 100644 --- a/frontend/components/task/TaskDocumentList.vue +++ b/frontend/components/task/TaskDocumentList.vue @@ -68,6 +68,7 @@ function isImage(mimeType: string): boolean { } function getIconForMime(mimeType: string): string { + if (mimeType === 'text/markdown') return 'mdi:language-markdown' if (mimeType === 'application/pdf') return 'heroicons:document-text' if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'heroicons:table-cells' if (mimeType.includes('word') || mimeType.includes('document')) return 'heroicons:document' diff --git a/frontend/components/task/TaskDocumentPreview.vue b/frontend/components/task/TaskDocumentPreview.vue index 8e7f2d3..deb7781 100644 --- a/frontend/components/task/TaskDocumentPreview.vue +++ b/frontend/components/task/TaskDocumentPreview.vue @@ -58,6 +58,46 @@ class="h-[85vh] w-[80vw] rounded-lg bg-white" /> + +
+
+

{{ document.originalName }}

+
+ + + {{ $t('taskDocuments.download') }} + +
+
+
+
+ +
+
{{ textContent }}
+
+
+
@@ -73,7 +113,7 @@
-

{{ document.originalName }}

+

{{ document.originalName }}

@@ -98,19 +138,56 @@ defineEmits<{ }>() const overlayRef = ref(null) +const textContent = ref('') +const loadingText = ref(false) +const copied = ref(false) -const { getDownloadUrl } = useTaskDocumentService() +const { getDownloadUrl, getContent } = useTaskDocumentService() +const { t } = useI18n() + +const TEXT_MIME_TYPES = ['text/markdown', 'text/plain', 'text/csv', 'application/json', 'application/xml', 'text/xml'] + +function isTextDocument(doc: TaskDocument | null): boolean { + if (!doc) return false + if (TEXT_MIME_TYPES.includes(doc.mimeType)) return true + return /\.(md|markdown|txt|csv|json|xml)$/i.test(doc.originalName) +} const downloadUrl = computed(() => props.document ? getDownloadUrl(props.document.id) : '') const isImage = computed(() => props.document?.mimeType.startsWith('image/') ?? false) const isPdf = computed(() => props.document?.mimeType === 'application/pdf') +const isText = computed(() => isTextDocument(props.document)) -// Focus overlay for keyboard events -watch(() => props.document, (doc) => { - if (doc) { - nextTick(() => overlayRef.value?.focus()) +async function copyContent() { + try { + await navigator.clipboard.writeText(textContent.value) + copied.value = true + useToast().success(t('taskDocuments.copied')) + setTimeout(() => { copied.value = false }, 2000) + } catch { + // Clipboard unavailable } -}) +} + +// Focus overlay for keyboard events, and load text content for text/markdown documents +watch(() => props.document, async (doc) => { + textContent.value = '' + copied.value = false + if (!doc) return + + nextTick(() => overlayRef.value?.focus()) + + if (isTextDocument(doc)) { + loadingText.value = true + try { + textContent.value = await getContent(doc.id) + } catch { + textContent.value = '' + } finally { + loadingText.value = false + } + } +}, { immediate: true })