Rename menus: 'Acessos', 'Filas', 'Produtividade'; add agent productivity section with bar chart; adjust CSV label; update channels page title

This commit is contained in:
codex-bot 2025-10-21 13:17:41 -03:00
parent 347609a186
commit 67df0d4308
8 changed files with 150 additions and 13 deletions

View file

@ -15,6 +15,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { useState } from "react"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
function formatMinutes(value: number | null) {
if (value === null) return "—"
@ -36,6 +38,12 @@ export function SlaReport() {
? ({ tenantId, viewerId: convexUserId as Id<"users">, range: timeRange, companyId: companyId === "all" ? undefined : (companyId as Id<"companies">) })
: "skip"
)
const agents = useQuery(
api.reports.agentProductivity,
convexUserId
? ({ tenantId, viewerId: convexUserId as Id<"users">, range: timeRange, companyId: companyId === "all" ? undefined : (companyId as Id<"companies">) })
: "skip"
) as { rangeDays: number; items: Array<{ agentId: string; name: string | null; email: string | null; open: number; resolved: number; avgFirstResponseMinutes: number | null; avgResolutionMinutes: number | null; workedHours: number }> } | undefined
const companies = useQuery(api.companies.list, convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip") as Array<{ id: Id<"companies">; name: string }> | undefined
const queueTotal = useMemo(
@ -164,6 +172,51 @@ export function SlaReport() {
)}
</CardContent>
</Card>
<Card className="border-slate-200">
<CardHeader>
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<div>
<CardTitle className="text-lg font-semibold text-neutral-900">Produtividade por agente</CardTitle>
<CardDescription className="text-neutral-600">
Chamados resolvidos no período por agente (top 10) e horas trabalhadas.
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
{!agents || agents.items.length === 0 ? (
<p className="rounded-lg border border-dashed border-slate-200 p-6 text-sm text-neutral-500">
Nenhum dado para o período selecionado.
</p>
) : (
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-3">
<p className="text-xs text-neutral-500">Resolvidos por agente</p>
<ChartContainer config={{}} className="aspect-auto h-[260px] w-full">
<BarChart data={agents.items.slice(0, 10).map((a) => ({ name: a.name || a.email || 'Agente', resolved: a.resolved }))} margin={{ left: 12, right: 12 }}>
<CartesianGrid vertical={false} />
<XAxis dataKey="name" tickLine={false} axisLine={false} tickMargin={8} interval={0} angle={-30} height={60} />
<Bar dataKey="resolved" fill="var(--chart-1)" radius={[4, 4, 0, 0]} />
<ChartTooltip content={<ChartTooltipContent className="w-[180px]" nameKey="resolved" />} />
</BarChart>
</ChartContainer>
</div>
<div className="space-y-3">
<p className="text-xs text-neutral-500">Horas trabalhadas (estimado)</p>
<ul className="divide-y divide-slate-200 overflow-hidden rounded-md border border-slate-200">
{agents.items.slice(0, 10).map((a) => (
<li key={a.agentId} className="flex items-center justify-between px-3 py-2 text-sm">
<span className="truncate">{a.name || a.email || 'Agente'}</span>
<span className="text-neutral-700">{a.workedHours.toFixed(1)} h</span>
</li>
))}
</ul>
</div>
</div>
)}
</CardContent>
</Card>
</div>
)
}