diff --git a/src/components/reports/backlog-report.tsx b/src/components/reports/backlog-report.tsx index daa8f19..dfb61c3 100644 --- a/src/components/reports/backlog-report.tsx +++ b/src/components/reports/backlog-report.tsx @@ -14,6 +14,8 @@ import { Skeleton } from "@/components/ui/skeleton" import { Badge } from "@/components/ui/badge" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { usePersistentCompanyFilter } from "@/lib/use-company-filter" +import { Pie, PieChart, Bar, BarChart, CartesianGrid, XAxis } from "recharts" +import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" const PRIORITY_LABELS: Record = { LOW: "Baixa", @@ -161,18 +163,31 @@ export function BacklogReport() { -
- {(Object.entries(data.priorityCounts) as Array<[string, number]>).map(([priority, total]) => ( -
- - {PRIORITY_LABELS[priority] ?? priority} - - - {total} ticket{total === 1 ? "" : "s"} - -
- ))} -
+ {Object.keys(data.priorityCounts).length === 0 ? ( +

Sem dados para o período.

+ ) : ( +
+ + + } /> + ).map(([priority, total]) => ({ name: PRIORITY_LABELS[priority] ?? priority, total, fill: `var(--chart-${{LOW:1,MEDIUM:2,HIGH:3,URGENT:4}[priority as keyof typeof PRIORITY_LABELS]||5})` }))} + dataKey="total" + nameKey="name" + label + /> + + +
    + {(Object.entries(data.priorityCounts) as Array<[string, number]>).map(([priority, total]) => ( +
  • + {PRIORITY_LABELS[priority] ?? priority} + {total} ticket{total === 1 ? "" : "s"} +
  • + ))} +
+
+ )}
@@ -189,18 +204,14 @@ export function BacklogReport() { Nenhuma fila com tickets abertos no momento.

) : ( - + + ({ name: q.name, total: q.total }))}> + + + + } /> + + )} diff --git a/src/components/reports/csat-report.tsx b/src/components/reports/csat-report.tsx index b1fe88f..a4ce42b 100644 --- a/src/components/reports/csat-report.tsx +++ b/src/components/reports/csat-report.tsx @@ -10,6 +10,8 @@ import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } import { Skeleton } from "@/components/ui/skeleton" import { Badge } from "@/components/ui/badge" import { useState } from "react" +import { Bar, BarChart, CartesianGrid, XAxis } from "recharts" +import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" @@ -131,21 +133,18 @@ export function CsatReport() { - + {data.totalSurveys === 0 ? ( +

Sem respostas no período.

+ ) : ( + + ({ score: `Nota ${d.score}`, total: d.total }))}> + + + + } /> + + + )}
diff --git a/src/components/reports/hours-report.tsx b/src/components/reports/hours-report.tsx index 50ed6f8..9540d54 100644 --- a/src/components/reports/hours-report.tsx +++ b/src/components/reports/hours-report.tsx @@ -15,6 +15,8 @@ import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { usePersistentCompanyFilter } from "@/lib/use-company-filter" import { Progress } from "@/components/ui/progress" +import { Bar, BarChart, CartesianGrid, XAxis } from "recharts" +import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" type HoursItem = { companyId: string @@ -92,6 +94,34 @@ export function HoursReport() { return (
+ + + Top clientes por horas + Comparativo empilhado de horas internas x externas (top 10). + + + {!filteredWithComputed || filteredWithComputed.length === 0 ? ( +

Sem dados para o período.

+ ) : ( + + b.total - a.total) + .slice(0, 10) + .map((r) => ({ name: r.name, internas: r.internal, externas: r.external }))} + > + + + + + } /> + + + )} +
+
+ Horas diff --git a/src/components/reports/sla-report.tsx b/src/components/reports/sla-report.tsx index dc3e479..0bad3c9 100644 --- a/src/components/reports/sla-report.tsx +++ b/src/components/reports/sla-report.tsx @@ -15,7 +15,7 @@ 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 { Area, AreaChart, Bar, BarChart, CartesianGrid, XAxis } from "recharts" import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" function formatMinutes(value: number | null) { @@ -44,6 +44,20 @@ export function SlaReport() { ? ({ 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 openedResolved = useQuery( + api.reports.openedResolvedByDay, + convexUserId + ? ({ tenantId, viewerId: convexUserId as Id<"users">, range: timeRange, companyId: companyId === "all" ? undefined : (companyId as Id<"companies">) }) + : "skip" + ) as { rangeDays: number; series: Array<{ date: string; opened: number; resolved: number }> } | undefined + + const channelsSeries = useQuery( + api.reports.ticketsByChannel, + convexUserId + ? ({ tenantId, viewerId: convexUserId as Id<"users">, range: timeRange, companyId: companyId === "all" ? undefined : (companyId as Id<"companies">) }) + : "skip" + ) as { rangeDays: number; channels: string[]; points: Array<{ date: string; values: Record }> } | 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( @@ -173,6 +187,69 @@ export function SlaReport() { + + +
+
+ Abertos x Resolvidos + Comparativo diário no período selecionado. +
+
+
+ + {!openedResolved || openedResolved.series.length === 0 ? ( +

Sem dados para o período.

+ ) : ( + + + + + + + + + + + + + + + } /> + + + + + )} +
+
+ + + +
+
+ Volume por canal + Distribuição diária por canal (empilhado). +
+
+
+ + {!channelsSeries || channelsSeries.points.length === 0 ? ( +

Sem dados para o período.

+ ) : ( + + ({ date: p.date, ...p.values }))}> + + + } /> + {channelsSeries.channels.map((ch, idx) => ( + + ))} + + + )} +
+
+