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).
*/