mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
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>
90 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|