From bbb5fd2b1ae76672aa3d3c3d3a7311b39a8a0032 Mon Sep 17 00:00:00 2001 From: Nurfog Date: Wed, 14 Oct 2026 13:16:30 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20l=C3=ADnea=20de=20tiempo=20mezclada=20e?= =?UTF-8?q?n=20el=20detalle=20(Fase=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/issues/[id]/page.tsx | 57 ++++--- src/components/issues/ActivityFeed.tsx | 201 +++++++++++++++++++++++++ src/lib/api/client.ts | 4 + src/lib/api/http.ts | 3 + src/lib/api/mock.ts | 51 +++++++ src/lib/types.ts | 23 ++- 6 files changed, 307 insertions(+), 32 deletions(-) create mode 100644 src/components/issues/ActivityFeed.tsx diff --git a/src/app/issues/[id]/page.tsx b/src/app/issues/[id]/page.tsx index 5c07829..171991b 100644 --- a/src/app/issues/[id]/page.tsx +++ b/src/app/issues/[id]/page.tsx @@ -4,13 +4,13 @@ import Link from "next/link"; import { useParams, useRouter } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; import { api } from "@/lib/api"; -import type { Comment, Issue, IssueStatus } from "@/lib/types"; +import type { Activity, Comment, Issue, IssueStatus } from "@/lib/types"; import { PRIORITY_META, TYPE_META } from "@/lib/meta"; import { formatLongDate, formatRelative } from "@/lib/format"; import { useReference, useReferenceLookups, useWorkflowStates } from "@/lib/store"; import { useAuth } from "@/lib/auth"; import { AppShell } from "@/components/layout/AppShell"; -import { Avatar } from "@/components/ui/Avatar"; +import { ActivityFeed } from "@/components/issues/ActivityFeed"; import { EmptyState } from "@/components/ui/EmptyState"; import { TypeIcon } from "@/components/ui/Icons"; @@ -36,22 +36,25 @@ function IssueDetail({ issueId }: { issueId: string }) { const { user } = useAuth(); const { meta, order } = useWorkflowStates(); const { users, projects, cycles, labels } = useReference(); - const { projectById, userById } = useReferenceLookups(); + const { projectById } = useReferenceLookups(); const [issue, setIssue] = useState(null); const [comments, setComments] = useState([]); + const [activity, setActivity] = useState([]); const [loading, setLoading] = useState(true); const [draft, setDraft] = useState(""); const load = useCallback(async () => { setLoading(true); - const [found, issueComments] = await Promise.all([ + const [found, issueComments, issueActivity] = await Promise.all([ api.getIssue(issueId), api.listComments(issueId), + api.listActivity(issueId), ]); setIssue(found); setComments(issueComments); + setActivity(issueActivity); setLoading(false); }, [issueId]); @@ -67,8 +70,13 @@ function IssueDetail({ issueId }: { issueId: string }) { setIssue((current) => (current ? { ...current, ...partial } : current)); try { - const updated = await api.updateIssue(issue.id, partial); + const updated = await api.updateIssue(issue.id, { + ...partial, + ...(user ? { actorId: user.id } : {}), + }); setIssue(updated); + // Refrescar la línea de tiempo con los eventos recién creados. + void api.listActivity(issue.id).then(setActivity); } catch { // Revertir si el backend rechaza el cambio. void load(); @@ -84,6 +92,7 @@ function IssueDetail({ issueId }: { issueId: string }) { setDraft(""); const created = await api.addComment(issueId, body, user.id); setComments((current) => [...current, created]); + void api.listActivity(issueId).then(setActivity); } if (loading) { @@ -150,35 +159,21 @@ function IssueDetail({ issueId }: { issueId: string }) { )} - {/* Comentarios */} + {/* Línea de tiempo: comentarios + eventos del log (Fase 2) */}
Actividad
- {comments.length === 0 && ( -

- Sin comentarios todavía. -

- )} - - {comments.map((comment) => { - const author = userById.get(comment.authorId); - return ( -
- -
-
- - {author?.name ?? "Desconocido"} - - - {formatRelative(comment.createdAt)} - -
-
{comment.body}
-
-
- ); - })} + [u.id, u]))} + states={Object.fromEntries( + order.map((key) => [key, { label: meta[key]?.label ?? key }]), + )} + projects={new Map(projects.map((p) => [p.id, p]))} + cycles={new Map(cycles.map((c) => [c.id, c]))} + labels={new Map(labels.map((l) => [l.id, l]))} + />