added stubbed activity log

This commit is contained in:
Khalim Conn-Kowlessar 2026-05-07 20:50:27 +00:00
parent 9118f9202b
commit 49adf11a4e
3 changed files with 85 additions and 87 deletions

View file

@ -0,0 +1,82 @@
"use client";
import { useQuery } from "@tanstack/react-query";
type AuditEvent = {
id: string;
hubspotDealId: string;
measureName: string;
action: string;
actedByEmail: string;
actedByName: string | null;
actedAt: string;
};
function formatDate(iso: string) {
return new Date(iso).toLocaleString("en-GB", {
day: "numeric",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
export function ActivityLog({
dealId,
portfolioId,
}: {
dealId: string;
portfolioId: string;
}) {
const { data, isLoading } = useQuery<{ events: AuditEvent[] }>({
queryKey: ["approvalEvents", portfolioId, dealId],
queryFn: async () => {
const res = await fetch(
`/api/portfolio/${portfolioId}/approvals?dealIds=${dealId}&include=events`,
);
if (!res.ok) throw new Error("Failed to fetch events");
return res.json();
},
staleTime: 30_000,
});
if (isLoading) {
return (
<p className="text-xs text-gray-400 py-2 pl-4">Loading activity</p>
);
}
const events = data?.events ?? [];
if (events.length === 0) {
return (
<p className="text-xs text-gray-400 py-2 pl-4">No activity yet.</p>
);
}
return (
<div className="pl-4 pr-2 pb-3 space-y-1.5">
{events.map((e) => (
<div key={e.id} className="flex items-center gap-2 text-xs">
<span
className={`px-1.5 py-0.5 rounded text-xs font-medium ${
e.action === "approved"
? "bg-emerald-50 text-emerald-700"
: "bg-red-50 text-red-600"
}`}
>
{e.action === "approved" ? "Approved" : "Unapproved"}
</span>
<span className="font-medium text-gray-700">{e.measureName}</span>
<span className="text-gray-400">·</span>
<span className="text-gray-500">
{e.actedByName ?? e.actedByEmail}
</span>
<span className="text-gray-400">·</span>
<span className="text-gray-400">{formatDate(e.actedAt)}</span>
</div>
))}
</div>
);
}

View file

@ -2,7 +2,6 @@
import React, { useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import {
Table,
TableBody,
@ -17,16 +16,7 @@ import { Search, ChevronDown, ChevronRight } from "lucide-react";
import { STAGE_COLORS } from "./types";
import type { ClassifiedDeal, ApprovalsByDeal } from "./types";
import { parseMeasures } from "@/app/lib/parseMeasures";
type AuditEvent = {
id: string;
hubspotDealId: string;
measureName: string;
action: string; // 'approved' | 'unapproved'
actedByEmail: string;
actedByName: string | null;
actedAt: string; // ISO string
};
import { ActivityLog } from "./ActivityLog";
type Props = {
data: ClassifiedDeal[];
@ -66,75 +56,6 @@ function ApprovalStatus({
);
}
function formatDate(iso: string) {
return new Date(iso).toLocaleString("en-GB", {
day: "numeric",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
function ActivityLog({
dealId,
portfolioId,
}: {
dealId: string;
portfolioId: string;
}) {
const { data, isLoading } = useQuery<{ events: AuditEvent[] }>({
queryKey: ["approvalEvents", portfolioId, dealId],
queryFn: async () => {
const res = await fetch(
`/api/portfolio/${portfolioId}/approvals?dealIds=${dealId}&include=events`,
);
if (!res.ok) throw new Error("Failed to fetch events");
return res.json();
},
staleTime: 30_000,
});
if (isLoading) {
return (
<p className="text-xs text-gray-400 py-2 pl-4">Loading activity</p>
);
}
const events = data?.events ?? [];
if (events.length === 0) {
return (
<p className="text-xs text-gray-400 py-2 pl-4">No activity yet.</p>
);
}
return (
<div className="pl-4 pr-2 pb-3 space-y-1.5">
{events.map((e) => (
<div key={e.id} className="flex items-center gap-2 text-xs">
<span
className={`px-1.5 py-0.5 rounded text-xs font-medium ${
e.action === "approved"
? "bg-emerald-50 text-emerald-700"
: "bg-red-50 text-red-600"
}`}
>
{e.action === "approved" ? "Approved" : "Unapproved"}
</span>
<span className="font-medium text-gray-700">{e.measureName}</span>
<span className="text-gray-400">·</span>
<span className="text-gray-500">
{e.actedByName ?? e.actedByEmail}
</span>
<span className="text-gray-400">·</span>
<span className="text-gray-400">{formatDate(e.actedAt)}</span>
</div>
))}
</div>
);
}
export default function MeasuresTable({
data,
approvalsByDeal,

View file

@ -1,5 +1,6 @@
"use client";
import { ActivityLog } from "./ActivityLog";
import { useEffect, useMemo, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { X, CheckCircle2, Circle, AlertTriangle, ChevronRight, ChevronDown, Trash2, RotateCcw, Loader2 } from "lucide-react";
@ -516,13 +517,7 @@ export function SurveyRequestSection({
);
}
// -----------------------------------------------------------------------
// Approval log placeholder (expand into a real implementation as needed)
// -----------------------------------------------------------------------
export function ApprovalLogSection({ dealId, portfolioId }: { dealId: string; portfolioId: string }) {
void dealId; void portfolioId;
return <p className="text-xs text-gray-400">No approvals recorded.</p>;
}
export const ApprovalLogSection = ActivityLog;
function formatDateTime(d: string | Date | null | undefined): string {
if (!d) return "";