feat(frontend): UI archivos en cards (issue 0128)

- CardFilesPanel: tab Archivos con grid thumbs + boton subir/borrar
- CardForm: drag&drop en descripcion, inserta ref markdown en cursor
- CardChatPanel: drag&drop + boton paperclip, sube y envia ref como mensaje
- MessageBody: renderer markdown minimo (img inline + link chip)
- api.ts: listCardFiles, uploadCardFile (multipart), deleteCardFile
- types.ts: CardFile
This commit is contained in:
2026-05-27 10:52:01 +02:00
parent 2401eb5abc
commit ac5f016e7e
7 changed files with 605 additions and 27 deletions
+10 -5
View File
@@ -1,8 +1,9 @@
import { Box, Divider, Group, Tabs, Text } from "@mantine/core";
import { Box, Divider, Group, Tabs } from "@mantine/core";
import { IconLink, IconMessage, IconPaperclip } from "@tabler/icons-react";
import { useState } from "react";
import type { Card, CardMessage, User } from "../types";
import { CardChatPanel } from "./CardChatPanel";
import { CardFilesPanel } from "./CardFilesPanel";
import { CardLinksPanel } from "./CardLinksPanel";
import { CardForm, CardFormValues } from "./CardForm";
@@ -27,12 +28,15 @@ export function CardEditPanel({
}: Props) {
const [messages, setMessages] = useState<CardMessage[]>([]);
const [liveCard, setLiveCard] = useState(card);
const [filesRefreshKey, setFilesRefreshKey] = useState(0);
const wrappedSubmit = async (v: CardFormValues) => {
setLiveCard((c) => ({ ...c, title: v.title, description: v.description, requester: v.requester, tags: v.tags, assignee_id: v.assignee_id }));
await onSubmit(v);
};
const bumpFiles = () => setFilesRefreshKey((k) => k + 1);
return (
<Group align="stretch" gap="md" wrap="nowrap" style={{ minHeight: 460 }}>
<Box style={{ flex: "1 1 0", minWidth: 320 }}>
@@ -48,6 +52,8 @@ export function CardEditPanel({
tags: liveCard.tags || [],
}}
submitLabel="Guardar"
cardId={liveCard.id}
onFileUploaded={bumpFiles}
onSubmit={wrappedSubmit}
onCancel={onCancel}
/>
@@ -58,7 +64,7 @@ export function CardEditPanel({
<Tabs.List>
<Tabs.Tab value="chat" leftSection={<IconMessage size={14} />}>Chat</Tabs.Tab>
<Tabs.Tab value="links" leftSection={<IconLink size={14} />}>Enlaces</Tabs.Tab>
<Tabs.Tab value="files" leftSection={<IconPaperclip size={14} />} disabled>Archivos</Tabs.Tab>
<Tabs.Tab value="files" leftSection={<IconPaperclip size={14} />}>Archivos</Tabs.Tab>
</Tabs.List>
<Box pt="xs" style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
<Tabs.Panel value="chat" style={{ flex: 1, minHeight: 0, display: "flex" }}>
@@ -68,6 +74,7 @@ export function CardEditPanel({
users={users}
currentUserId={currentUserId}
onMessagesChange={setMessages}
onFileUploaded={bumpFiles}
/>
</Box>
</Tabs.Panel>
@@ -75,9 +82,7 @@ export function CardEditPanel({
<CardLinksPanel card={liveCard} messages={messages} />
</Tabs.Panel>
<Tabs.Panel value="files">
<Text size="sm" c="dimmed" ta="center" p="md">
Proximamente: adjuntos de archivos.
</Text>
<CardFilesPanel cardId={liveCard.id} refreshKey={filesRefreshKey} />
</Tabs.Panel>
</Box>
</Tabs>