feat: sync convex companies and dashboard metrics
This commit is contained in:
parent
4f52114b48
commit
7a3eca9361
10 changed files with 356 additions and 19 deletions
143
src/components/charts/chart-open-priority.tsx
Normal file
143
src/components/charts/chart-open-priority.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Bar, BarChart, CartesianGrid, Cell, XAxis } from "recharts"
|
||||
import { useQuery } from "convex/react"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
import { useAuth } from "@/lib/auth-client"
|
||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
|
||||
type PriorityKey = "LOW" | "MEDIUM" | "HIGH" | "URGENT"
|
||||
|
||||
const PRIORITY_CONFIG: Record<PriorityKey, { label: string; color: string }> = {
|
||||
LOW: { label: "Baixa", color: "var(--chart-4)" },
|
||||
MEDIUM: { label: "Média", color: "var(--chart-3)" },
|
||||
HIGH: { label: "Alta", color: "var(--chart-2)" },
|
||||
URGENT: { label: "Crítica", color: "var(--chart-1)" },
|
||||
}
|
||||
|
||||
const PRIORITY_ORDER: PriorityKey[] = ["URGENT", "HIGH", "MEDIUM", "LOW"]
|
||||
|
||||
export function ChartOpenByPriority() {
|
||||
const [timeRange, setTimeRange] = React.useState("30d")
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const { session, convexUserId, isStaff } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
|
||||
const report = useQuery(
|
||||
api.reports.backlogOverview,
|
||||
enabled
|
||||
? ({
|
||||
tenantId,
|
||||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
})
|
||||
: "skip"
|
||||
) as { priorityCounts: Record<string, number> } | undefined
|
||||
|
||||
const companies = useQuery(
|
||||
api.companies.list,
|
||||
enabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
) as Array<{ id: Id<"companies">; name: string }> | undefined
|
||||
|
||||
if (!report) {
|
||||
return <Skeleton className="h-[300px] w-full" />
|
||||
}
|
||||
|
||||
const chartData = PRIORITY_ORDER.map((key) => ({
|
||||
priority: PRIORITY_CONFIG[key].label,
|
||||
value: report.priorityCounts?.[key] ?? 0,
|
||||
fill: PRIORITY_CONFIG[key].color,
|
||||
}))
|
||||
|
||||
const totalTickets = chartData.reduce((sum, item) => sum + item.value, 0)
|
||||
|
||||
const chartConfig: ChartConfig = {
|
||||
value: {
|
||||
label: "Tickets em atendimento",
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="@container/card">
|
||||
<CardHeader>
|
||||
<CardTitle>Tickets em andamento por prioridade</CardTitle>
|
||||
<CardDescription>Distribuição de tickets iniciados e ainda abertos</CardDescription>
|
||||
<CardAction>
|
||||
<div className="flex flex-wrap items-center justify-end gap-2 md:gap-3">
|
||||
<Select value={companyId} onValueChange={setCompanyId}>
|
||||
<SelectTrigger className="w-48">
|
||||
<SelectValue placeholder="Todas as empresas" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
<SelectItem value="all">Todas as empresas</SelectItem>
|
||||
{(companies ?? []).map((c) => (
|
||||
<SelectItem key={c.id} value={c.id}>
|
||||
{c.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={timeRange}
|
||||
onValueChange={(value) => value && setTimeRange(value)}
|
||||
variant="outline"
|
||||
className="hidden *:data-[slot=toggle-group-item]:!px-4 @[640px]/card:flex"
|
||||
>
|
||||
<ToggleGroupItem value="90d">90 dias</ToggleGroupItem>
|
||||
<ToggleGroupItem value="30d">30 dias</ToggleGroupItem>
|
||||
<ToggleGroupItem value="7d">7 dias</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent className="px-2 pb-6 pt-4 sm:px-6">
|
||||
{totalTickets === 0 ? (
|
||||
<div className="flex h-[260px] items-center justify-center rounded-xl border border-dashed border-border/60 text-sm text-muted-foreground">
|
||||
Sem tickets em andamento no período selecionado.
|
||||
</div>
|
||||
) : (
|
||||
<ChartContainer config={chartConfig} className="aspect-auto h-[260px] w-full">
|
||||
<BarChart data={chartData} margin={{ top: 12, left: 12, right: 12 }}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis dataKey="priority" tickLine={false} axisLine={false} tickMargin={8} />
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent indicator="dot" nameKey="value" labelKey="priority" />}
|
||||
/>
|
||||
<Bar dataKey="value" radius={8}>
|
||||
{chartData.map((entry) => (
|
||||
<Cell key={`cell-${entry.priority}`} fill={entry.fill} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue