diff --git a/CHANGELOG.md b/CHANGELOG.md index 722ae4c..a38812f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog - project.linkedcorp.net +## [2.12.1] - 2026-10-14 +### Added +- Documentos (proyectos y actividades): las imágenes (.png, .jpg, .webp) también se pueden ver + en el popup con el botón "Ver", igual que ya funcionaba con .txt/.md — sin tocar ese flujo. + ## [2.12.0] - 2026-10-14 ### Added - Comentarios (de proyectos y actividades): ahora se pueden editar y borrar, solo quien lo diff --git a/VERSION b/VERSION index d8b6989..3cf561c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.0 +2.12.1 diff --git a/backend/package.json b/backend/package.json index a7b2309..ea2e756 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "project-linkedcorp-backend", - "version": "2.12.0", + "version": "2.12.1", "private": true, "type": "module", "main": "src/server.js", diff --git a/frontend/package.json b/frontend/package.json index a2317b6..9060e40 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "project-linkedcorp-frontend", "private": true, - "version": "2.12.0", + "version": "2.12.1", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/AttachmentsPanel.jsx b/frontend/src/components/AttachmentsPanel.jsx index 49e9b06..7a34a1a 100644 --- a/frontend/src/components/AttachmentsPanel.jsx +++ b/frontend/src/components/AttachmentsPanel.jsx @@ -2,7 +2,7 @@ import { useRef, useState } from 'react'; import { Upload, Download, Trash2, Paperclip, Eye } from 'lucide-react'; import { useUi } from '../context/ui-context.jsx'; import { useAsync, useAction } from '../hooks/useAsync.js'; -import { listAttachments, uploadAttachment, deleteAttachment, downloadAttachment, readAttachmentText } from '../services/attachments.js'; +import { listAttachments, uploadAttachment, deleteAttachment, downloadAttachment, readAttachmentText, fetchAttachmentBlob } from '../services/attachments.js'; import { renderMarkdown } from '../utils/renderMarkdown.js'; import { Button } from './Button.jsx'; import { DetailPopup } from './DetailPopup.jsx'; @@ -11,7 +11,9 @@ import { DetailPopup } from './DetailPopup.jsx'; import hljsLight from 'highlight.js/styles/vs.css?inline'; import hljsDark from 'highlight.js/styles/vs2015.css?inline'; -const VIEWABLE_MIME = new Set(['text/plain', 'text/markdown']); +const TEXT_VIEWABLE_MIME = new Set(['text/plain', 'text/markdown']); +const IMAGE_VIEWABLE_MIME = new Set(['image/png', 'image/jpeg', 'image/webp']); +const VIEWABLE_MIME = new Set([...TEXT_VIEWABLE_MIME, ...IMAGE_VIEWABLE_MIME]); export function AttachmentsPanel({ entityType, entityId, onChanged }) { const { t, openDialog, theme } = useUi(); @@ -20,14 +22,29 @@ export function AttachmentsPanel({ entityType, entityId, onChanged }) { const inputRef = useRef(null); const [viewing, setViewing] = useState(null); // adjunto abierto en el popup de lectura const [viewContent, setViewContent] = useState(''); + const [viewImageUrl, setViewImageUrl] = useState(null); const [viewLoading, setViewLoading] = useState(false); + function closeView() { + setViewing(null); + // Libera la memoria del blob de la imagen -- no toca nada del flujo de texto/markdown. + if (viewImageUrl) URL.revokeObjectURL(viewImageUrl); + setViewImageUrl(null); + } + async function openView(file) { setViewing(file); setViewContent(''); + if (viewImageUrl) URL.revokeObjectURL(viewImageUrl); + setViewImageUrl(null); setViewLoading(true); try { - setViewContent(await readAttachmentText(file.id)); + if (IMAGE_VIEWABLE_MIME.has(file.mime_type)) { + const blob = await fetchAttachmentBlob(file.id); + setViewImageUrl(URL.createObjectURL(blob)); + } else { + setViewContent(await readAttachmentText(file.id)); + } } catch { setViewContent(t('document_view_error')); } finally { @@ -84,9 +101,13 @@ export function AttachmentsPanel({ entityType, entityId, onChanged }) { ))} - setViewing(null)} width={720} height={600}> + {viewLoading ? (
{t('common_loading')}
+ ) : viewImageUrl ? ( +
+ {viewing?.original_name} +
) : viewing?.mime_type === 'text/markdown' ? ( <> diff --git a/frontend/src/services/attachments.js b/frontend/src/services/attachments.js index 242f177..a2398e0 100644 --- a/frontend/src/services/attachments.js +++ b/frontend/src/services/attachments.js @@ -12,7 +12,7 @@ export async function uploadAttachment(entityType, entityId, file) { export const deleteAttachment = (id) => api.delete(`/attachments/${id}`); -async function fetchAttachmentBlob(id) { +export async function fetchAttachmentBlob(id) { const token = localStorage.getItem('plc_token'); const res = await fetch(api.downloadUrl(`/attachments/${id}/download`), { headers: token ? { Authorization: `Bearer ${token}` } : {},