diff --git a/src/components/chart-area-interactive.tsx b/src/components/chart-area-interactive.tsx index 1e9eb49..2a907a5 100644 --- a/src/components/chart-area-interactive.tsx +++ b/src/components/chart-area-interactive.tsx @@ -18,12 +18,13 @@ import { CardTitle, } from "@/components/ui/card" import { Button } from "@/components/ui/button" -import { - ChartConfig, - ChartContainer, - ChartTooltip, - ChartTooltipContent, -} from "@/components/ui/chart" +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart" +import { formatDateDM, formatDateDMY } from "@/lib/utils" import { Skeleton } from "@/components/ui/skeleton" import { Select, @@ -233,24 +234,13 @@ export function ChartAreaInteractive() { axisLine={false} tickMargin={8} minTickGap={32} - tickFormatter={(value) => { - const date = new Date(value) - return date.toLocaleDateString("pt-BR", { - month: "short", - day: "2-digit", - }) - }} + tickFormatter={(value) => formatDateDM(new Date(value))} /> - new Date(value).toLocaleDateString("pt-BR", { - day: "2-digit", - month: "long", - }) - } + labelFormatter={(value) => formatDateDMY(new Date(value as string))} indicator="dot" /> } diff --git a/src/components/charts/chart-opened-resolved.tsx b/src/components/charts/chart-opened-resolved.tsx index 278e56a..417c761 100644 --- a/src/components/charts/chart-opened-resolved.tsx +++ b/src/components/charts/chart-opened-resolved.tsx @@ -13,6 +13,7 @@ import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" +import { formatDateDM, formatDateDMY } from "@/lib/utils" import { Skeleton } from "@/components/ui/skeleton" type SeriesPoint = { date: string; opened: number; resolved: number } @@ -99,10 +100,16 @@ export function ChartOpenedResolved() { axisLine={false} tickMargin={8} minTickGap={32} + tickFormatter={(v) => formatDateDM(new Date(v))} /> } + content={ + formatDateDMY(new Date(value as string))} + /> + } /> diff --git a/src/components/reports/backlog-report.tsx b/src/components/reports/backlog-report.tsx index 9da9ff1..307bdbc 100644 --- a/src/components/reports/backlog-report.tsx +++ b/src/components/reports/backlog-report.tsx @@ -206,11 +206,23 @@ export function BacklogReport() {

) : ( - ({ name: q.name, total: q.total }))}> + ({ name: q.name, total: q.total }))} + margin={{ left: 12, right: 12, bottom: 28 }} + barCategoryGap={16} + > - + - } /> + } /> )} diff --git a/src/components/reports/hours-report.tsx b/src/components/reports/hours-report.tsx index 73883f9..bfb7ade 100644 --- a/src/components/reports/hours-report.tsx +++ b/src/components/reports/hours-report.tsx @@ -17,6 +17,7 @@ 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" +import { formatHoursCompact } from "@/lib/utils" type HoursItem = { companyId: string @@ -111,12 +112,25 @@ export function HoursReport() { .sort((a, b) => b.total - a.total) .slice(0, 10) .map((r) => ({ name: r.name, internas: r.internal, externas: r.external }))} + margin={{ left: 12, right: 12, bottom: 28 }} > - + - } /> + ( + <> + {name} + {formatHoursCompact(Number(value))} + + )} + /> + } + /> )} @@ -165,13 +179,13 @@ export function HoursReport() {
{[ - { key: "internal", label: "Horas internas", value: numberFormatter.format(totals.internal) }, - { key: "external", label: "Horas externas", value: numberFormatter.format(totals.external) }, - { key: "total", label: "Total acumulado", value: numberFormatter.format(totals.total) }, + { key: "internal", label: "Horas internas", value: formatHoursCompact(totals.internal) }, + { key: "external", label: "Horas externas", value: formatHoursCompact(totals.external) }, + { key: "total", label: "Total acumulado", value: formatHoursCompact(totals.total) }, ].map((item) => (

{item.label}

-

{item.value} h

+

{item.value}

))}
@@ -199,17 +213,17 @@ export function HoursReport() {
Horas internas - {numberFormatter.format(row.internal)} h -
-
- Horas externas - {numberFormatter.format(row.external)} h -
-
- Total - {numberFormatter.format(row.total)} h -
+ {formatHoursCompact(row.internal)}
+
+ Horas externas + {formatHoursCompact(row.external)} +
+
+ Total + {formatHoursCompact(row.total)} +
+
Contratadas/mês diff --git a/src/components/reports/sla-report.tsx b/src/components/reports/sla-report.tsx index f6d333c..2772202 100644 --- a/src/components/reports/sla-report.tsx +++ b/src/components/reports/sla-report.tsx @@ -17,6 +17,7 @@ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" import { usePersistentCompanyFilter } from "@/lib/use-company-filter" import { Area, AreaChart, Bar, BarChart, CartesianGrid, XAxis } from "recharts" import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart" +import { formatDateDM, formatDateDMY, formatHoursCompact } from "@/lib/utils" function formatMinutes(value: number | null) { if (value === null) return "—" @@ -214,8 +215,22 @@ export function SlaReport() { - - } /> + formatDateDM(new Date(v))} + /> + formatDateDMY(new Date(value as string))} + /> + } + /> @@ -240,8 +255,22 @@ export function SlaReport() { ({ date: p.date, ...p.values }))}> - - } /> + formatDateDM(new Date(v))} + /> + formatDateDMY(new Date(value as string))} + /> + } + /> {channelsSeries.channels.map((ch, idx) => ( ))} @@ -272,11 +301,14 @@ export function SlaReport() {

Resolvidos por agente

- ({ name: a.name || a.email || 'Agente', resolved: a.resolved }))} margin={{ left: 12, right: 12 }}> + ({ name: a.name || a.email || 'Agente', resolved: a.resolved }))} + margin={{ left: 12, right: 12, bottom: 28 }} + > - + - } /> + } />
@@ -286,7 +318,7 @@ export function SlaReport() { {agents.items.slice(0, 10).map((a) => (
  • {a.name || a.email || 'Agente'} - {a.workedHours.toFixed(1)} h + {formatHoursCompact(a.workedHours)}
  • ))} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 2f00a25..25f470f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,34 @@ import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} + +// Format hours so that values under 1h are shown in minutes. +// Examples: 0.06 -> "4 min"; 0.5 -> "30 min"; 1.25 -> "1,25 h" (pt-BR) +export function formatHoursCompact(value: number, locale: string = "pt-BR"): string { + const hours = Number(value) || 0 + if (hours < 1 && hours > 0) { + const mins = Math.round(hours * 60) + return `${mins} min` + } + const nf = new Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + return `${nf.format(hours)} h` +} + +// Format date to dd/MM or dd/MM/yyyy. Accepts Date or ISO-like string (YYYY-MM-DD). +export function formatDateDM(value: Date | string | number): string { + const d = typeof value === "string" || typeof value === "number" ? new Date(value) : value + const dd = String(d.getDate()).padStart(2, "0") + const mm = String(d.getMonth() + 1).padStart(2, "0") + return `${dd}/${mm}` +} + +export function formatDateDMY(value: Date | string | number): string { + const d = typeof value === "string" || typeof value === "number" ? new Date(value) : value + const dd = String(d.getDate()).padStart(2, "0") + const mm = String(d.getMonth() + 1).padStart(2, "0") + const yyyy = d.getFullYear() + return `${dd}/${mm}/${yyyy}` +}