Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { format, formatDistanceToNow } from 'date-fns';
import { toast } from 'sonner';
import { getWebhookRoutes } from '@/lib/webhook-routes';
import { isNewSession } from '@/lib/cloud-agent/session-type';
import { useAdminPermissions } from '@/app/admin/useAdminPermissions';

import { Button } from '@/components/ui/button';
import { CopyTextButton } from '@/components/admin/CopyEmailButton';
Expand Down Expand Up @@ -131,6 +132,7 @@ export function WebhookRequestsContent({
// Build URLs based on context
const routes = getWebhookRoutes(organizationId);
const isAdminView = !!adminPathBase;
const { canViewSessions } = useAdminPermissions(isAdminView);
const adminRoutes = adminPathBase
? {
list: adminPathBase,
Expand Down Expand Up @@ -520,7 +522,7 @@ export function WebhookRequestsContent({
(() => {
const sessionId = request.kiloSessionId;
if (isAdminView) {
return (
return canViewSessions ? (
<div className="flex items-center gap-2">
<Button
variant="link"
Expand All @@ -538,6 +540,10 @@ export function WebhookRequestsContent({
<CopyTextButton text={sessionId} />
</span>
</div>
) : (
<span className="text-muted-foreground text-xs">
Session viewer access required
</span>
);
}

Expand Down
19 changes: 11 additions & 8 deletions apps/web/src/app/admin/api/session-traces/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,41 @@
import { useTRPC } from '@/lib/trpc/utils';
import { useQuery } from '@tanstack/react-query';

export function useAdminSessionTrace(sessionId: string | null) {
export function useAdminSessionTrace(sessionId: string | null, enabled = true) {
const trpc = useTRPC();
return useQuery({
...trpc.admin.sessionTraces.get.queryOptions({ session_id: sessionId ?? '' }),
enabled: !!sessionId,
enabled: enabled && !!sessionId,
});
}

export function useAdminSessionMessages(sessionId: string | null) {
export function useAdminSessionMessages(sessionId: string | null, enabled = true) {
const trpc = useTRPC();
return useQuery({
...trpc.admin.sessionTraces.getMessages.queryOptions({ session_id: sessionId ?? '' }),
enabled: !!sessionId,
enabled: enabled && !!sessionId,
});
}

export function useAdminApiConversationHistory(sessionId: string | null) {
export function useAdminApiConversationHistory(sessionId: string | null, enabled = true) {
const trpc = useTRPC();
return useQuery({
...trpc.admin.sessionTraces.getApiConversationHistory.queryOptions({
session_id: sessionId ?? '',
}),
enabled: !!sessionId,
enabled: enabled && !!sessionId,
});
}

export function useAdminResolveCloudAgentSession(cloudAgentSessionId: string | null) {
export function useAdminResolveCloudAgentSession(
cloudAgentSessionId: string | null,
enabled = true
) {
const trpc = useTRPC();
return useQuery({
...trpc.admin.sessionTraces.resolveCloudAgentSession.queryOptions({
cloud_agent_session_id: cloudAgentSessionId ?? '',
}),
enabled: !!cloudAgentSessionId,
enabled: enabled && !!cloudAgentSessionId,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from 'lucide-react';
import Link from 'next/link';
import { formatDistanceToNow } from 'date-fns';
import { useAdminPermissions } from '@/app/admin/useAdminPermissions';

function formatRelativeTime(timestamp: string | null): string {
if (!timestamp) return 'Never';
Expand Down Expand Up @@ -59,6 +60,7 @@ function AppBuilderProjectDetailPage({ children, projectTitle }: AppBuilderProje

export function AppBuilderProjectDetail({ projectId }: { projectId: string }) {
const { data: project, isLoading, error } = useAdminAppBuilderProject(projectId);
const { canViewSessions } = useAdminPermissions();

if (isLoading) {
return (
Expand Down Expand Up @@ -255,7 +257,7 @@ export function AppBuilderProjectDetail({ projectId }: { projectId: string }) {
)}
</div>

{session.cli_session_id ? (
{session.cli_session_id && canViewSessions ? (
<div className="flex items-center gap-2">
<Link href={`/admin/session-traces?sessionId=${session.cli_session_id}`}>
<Button variant="outline" size="sm">
Expand All @@ -264,6 +266,10 @@ export function AppBuilderProjectDetail({ projectId }: { projectId: string }) {
</Button>
</Link>
</div>
) : session.cli_session_id ? (
<span className="text-muted-foreground text-sm">
Session viewer access required
</span>
) : (
<span className="text-muted-foreground text-sm">No linked CLI session</span>
)}
Expand Down
61 changes: 34 additions & 27 deletions apps/web/src/app/admin/components/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
import Link from 'next/link';
import { useTRPC } from '@/lib/trpc/utils';
import { cn } from '@/lib/utils';
import { useAdminPermissions } from '@/app/admin/useAdminPermissions';

type MenuItem = {
title: (session: Session | null) => string;
Expand Down Expand Up @@ -291,13 +292,15 @@ type AppSidebarViewProps = {
pathname: string;
session: Session | null;
pendingDisputesCount?: number;
canViewSessions?: boolean;
} & React.ComponentProps<typeof Sidebar>;

export function AppSidebarView({
children,
pathname,
session,
pendingDisputesCount = 0,
canViewSessions = false,
...props
}: AppSidebarViewProps) {
return (
Expand Down Expand Up @@ -330,35 +333,37 @@ export function AppSidebarView({
<SidebarGroupLabel>{section.label}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{section.items.map(item => {
const isActive = isAdminMenuItemActive(pathname, item.url);
{section.items
.filter(item => item.url !== '/admin/session-traces' || canViewSessions)
.map(item => {
const isActive = isAdminMenuItemActive(pathname, item.url);

return (
<SidebarMenuItem key={item.url}>
<SidebarMenuButton
asChild
isActive={isActive}
className={cn(
item.url === '/admin/disputes' && pendingDisputesCount > 0 && 'pr-10'
)}
>
<Link
href={item.url}
prefetch={false}
aria-current={isActive ? 'page' : undefined}
return (
<SidebarMenuItem key={item.url}>
<SidebarMenuButton
asChild
isActive={isActive}
className={cn(
item.url === '/admin/disputes' && pendingDisputesCount > 0 && 'pr-10'
)}
>
{item.icon(session)}
<span>{item.title(session)}</span>
</Link>
</SidebarMenuButton>
{item.url === '/admin/disputes' && pendingDisputesCount > 0 ? (
<SidebarMenuBadge className="bg-destructive/15 text-destructive ring-1 ring-destructive/30">
{pendingDisputesCount}
</SidebarMenuBadge>
) : null}
</SidebarMenuItem>
);
})}
<Link
href={item.url}
prefetch={false}
aria-current={isActive ? 'page' : undefined}
>
{item.icon(session)}
<span>{item.title(session)}</span>
</Link>
</SidebarMenuButton>
{item.url === '/admin/disputes' && pendingDisputesCount > 0 ? (
<SidebarMenuBadge className="bg-destructive/15 text-destructive ring-1 ring-destructive/30">
{pendingDisputesCount}
</SidebarMenuBadge>
) : null}
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
Expand All @@ -379,6 +384,7 @@ export function AppSidebar({
const session = useSession();
const pathname = usePathname();
const trpc = useTRPC();
const { canViewSessions } = useAdminPermissions();
const disputesSummaryQuery = useQuery({
...trpc.admin.disputes.summary.queryOptions(),
staleTime: DISPUTES_SUMMARY_STALE_TIME_MS,
Expand All @@ -391,6 +397,7 @@ export function AppSidebar({
pathname={pathname}
session={session.data}
pendingDisputesCount={pendingDisputesCount}
canViewSessions={canViewSessions}
>
{children}
</AppSidebarView>
Expand Down
Loading