wacrm/src/app/(dashboard)/agents/page.tsx
Arnas Donauskas c736e7a43f feat(ai): add token-usage dashboard (AI Agents → Usage)
Surfaces the ai_usage_log data added in the previous commit:
- GET /api/ai/usage — admin-only aggregation (totals, per-mode,
  per-model, zero-filled daily series) over a 7/30/90-day window,
  mirroring the ai_usage_log SELECT policy
- AiUsageCard — stat tiles + daily token bar chart + by-model list,
  rendered under a new admin-only "Usage" tab on the AI Agents page

Counts only; no message content is stored or shown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 14:31:36 +03:00

90 lines
2.8 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { Bot, Sparkles, Settings2, BarChart3 } from 'lucide-react';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { AiPlayground } from '@/components/agents/ai-playground';
import { AiUsageCard } from '@/components/agents/ai-usage';
import { AiConfig } from '@/components/settings/ai-config';
import { useAuth } from '@/hooks/use-auth';
import { canEditSettings } from '@/lib/auth/roles';
type Tab = 'playground' | 'setup' | 'usage';
export default function AgentsPage() {
const { accountRole } = useAuth();
const canViewUsage = accountRole ? canEditSettings(accountRole) : false;
const [tab, setTab] = useState<Tab>('playground');
const [decided, setDecided] = useState(false);
// Land first-time users on Setup, returning users on the Playground.
useEffect(() => {
let cancelled = false;
(async () => {
try {
const res = await fetch('/api/ai/config');
const data = await res.json().catch(() => ({}));
if (!cancelled) setTab(data?.configured ? 'playground' : 'setup');
} catch {
if (!cancelled) setTab('setup');
} finally {
if (!cancelled) setDecided(true);
}
})();
return () => {
cancelled = true;
};
}, []);
return (
<div>
<div className="flex items-center gap-2">
<Bot className="h-6 w-6 text-primary" />
<h1 className="text-2xl font-bold tracking-tight text-foreground">
AI Agents
</h1>
</div>
<p className="mt-1 text-sm text-muted-foreground">
Your bring-your-own-key AI agent — set it up, then test it in the
playground before it replies to customers in the inbox.
</p>
{decided && (
<Tabs
value={tab}
onValueChange={(v) => setTab(v as Tab)}
className="mt-6"
>
<TabsList>
<TabsTrigger value="playground">
<Sparkles className="mr-1.5 h-4 w-4" /> Playground
</TabsTrigger>
<TabsTrigger value="setup">
<Settings2 className="mr-1.5 h-4 w-4" /> Setup
</TabsTrigger>
{canViewUsage && (
<TabsTrigger value="usage">
<BarChart3 className="mr-1.5 h-4 w-4" /> Usage
</TabsTrigger>
)}
</TabsList>
<TabsContent value="playground" className="mt-4">
<AiPlayground onGoToSetup={() => setTab('setup')} />
</TabsContent>
<TabsContent value="setup" className="mt-4">
<AiConfig />
</TabsContent>
{canViewUsage && (
<TabsContent value="usage" className="mt-4">
<AiUsageCard />
</TabsContent>
)}
</Tabs>
)}
</div>
);
}