- New usePeriod hook (day/week/month/all/custom presets) with from/to ISO string outputs - New PeriodSelector component (button group + custom date inputs) - UsersTab, UsageTab, FocusGroupsTab all wired up with period state - Backend /admin/users and /admin/focus-groups now accept from/to query params - MTD Cost column header now reflects selected period label (e.g. "Cost (MTD)") - Logout clears local state only (no account sign-out) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.8 KiB
TypeScript
67 lines
2.8 KiB
TypeScript
import { useAdminFocusGroups } from '@/hooks/useAdminFocusGroups';
|
|
import { usePeriod } from '@/hooks/usePeriod';
|
|
import PeriodSelector from '@/components/admin/PeriodSelector';
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
export default function FocusGroupsTab() {
|
|
const { period, setPeriod, customFrom, setCustomFrom, customTo, setCustomTo, from, to } = usePeriod('all');
|
|
const { data, isLoading } = useAdminFocusGroups({ from, to });
|
|
const fgs = data?.focus_groups ?? [];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<PeriodSelector
|
|
period={period} setPeriod={setPeriod}
|
|
customFrom={customFrom} setCustomFrom={setCustomFrom}
|
|
customTo={customTo} setCustomTo={setCustomTo}
|
|
/>
|
|
|
|
{isLoading ? (
|
|
<div className="flex justify-center py-12"><Loader2 className="h-8 w-8 animate-spin text-primary" /></div>
|
|
) : (
|
|
<div className="rounded-md border">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
<TableHead>Date</TableHead>
|
|
<TableHead>Model</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead className="text-right">Cost</TableHead>
|
|
<TableHead className="text-right">Calls</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{fgs.length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={6} className="text-center text-slate-500 py-8">No focus groups</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{fgs.map((fg: any) => (
|
|
<TableRow key={fg._id}>
|
|
<TableCell className="font-medium">{fg.name}</TableCell>
|
|
<TableCell className="text-xs text-slate-500">
|
|
{fg.date ? new Date(fg.date).toLocaleDateString() : '—'}
|
|
</TableCell>
|
|
<TableCell className="font-mono text-xs">{fg.llm_model ?? '—'}</TableCell>
|
|
<TableCell>
|
|
<Badge
|
|
variant={fg.status === 'completed' ? 'secondary' : fg.status === 'paused_quota' ? 'destructive' : 'outline'}
|
|
className="text-xs"
|
|
>
|
|
{fg.status ?? 'active'}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell className="text-right font-mono text-xs">${(fg.cost_total ?? 0).toFixed(4)}</TableCell>
|
|
<TableCell className="text-right text-xs">{fg.call_count ?? 0}</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|