diff --git a/apps/web/src/components/admin/ActivityLogClient.tsx b/apps/web/src/components/admin/ActivityLogClient.tsx index f3cdd97..4550593 100644 --- a/apps/web/src/components/admin/ActivityLogClient.tsx +++ b/apps/web/src/components/admin/ActivityLogClient.tsx @@ -122,6 +122,28 @@ function DiffView({ changes }: { changes: Changes }) { ); } +function ExpandedDiff({ entryId }: { entryId: string }) { + const { data, isLoading } = trpc.auditLog.getById.useQuery( + { id: entryId }, + { staleTime: 300_000 }, + ); + + if (isLoading) { + return ( +
+
+
+ ); + } + + const changes = parseChanges((data as any)?.changes); + return ( +
+ +
+ ); +} + function SummaryCards({ summary }: { summary: { byEntityType: Record; total: number } }) { const sorted = useMemo(() => { return Object.entries(summary.byEntityType) @@ -355,7 +377,6 @@ export function ActivityLogClient() { )} {allEntries.map((entry) => { - const changes = parseChanges(entry.changes); const isExpanded = expandedId === entry.id; const entityLink = ENTITY_LINKS[entry.entityType]?.(entry.entityId); @@ -431,12 +452,8 @@ export function ActivityLogClient() { - {/* Expanded Diff */} - {isExpanded && ( -
- -
- )} + {/* Expanded Diff — fetched on demand */} + {isExpanded && }
); })} diff --git a/packages/api/src/router/audit-log.ts b/packages/api/src/router/audit-log.ts index a720d24..5de9f09 100644 --- a/packages/api/src/router/audit-log.ts +++ b/packages/api/src/router/audit-log.ts @@ -49,10 +49,27 @@ export const auditLogRouter = createTRPCRouter({ ]; } + // Default to last 30 days if no date filter to avoid full table scan + if (!startDate && !endDate && !entityId) { + const thirtyDaysAgo = new Date(); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); + where.createdAt = { ...(where.createdAt as Record ?? {}), gte: thirtyDaysAgo }; + } + const items = await ctx.db.auditLog.findMany({ where, - include: { + select: { + id: true, + entityType: true, + entityId: true, + entityName: true, + action: true, + userId: true, + source: true, + summary: true, + createdAt: true, user: { select: { id: true, name: true, email: true } }, + // Exclude 'changes' from list query — fetch on demand when expanding }, orderBy: { createdAt: "desc" }, take: limit + 1, @@ -68,6 +85,18 @@ export const auditLogRouter = createTRPCRouter({ return { items, nextCursor }; }), + /** + * Get a single audit entry with full changes JSONB (for expand/detail view). + */ + getById: controllerProcedure + .input(z.object({ id: z.string() })) + .query(async ({ ctx, input }) => { + return ctx.db.auditLog.findUniqueOrThrow({ + where: { id: input.id }, + include: { user: { select: { id: true, name: true, email: true } } }, + }); + }), + /** * Get all audit entries for a specific entity (e.g. a project or resource). */