reports(SLA): aplica filtro de período (7d/30d/90d) no Convex e inclui período no filename do CSV; admin(alerts): filtros no servidor; alerts: batch de últimos alertas por slugs; filtros persistentes de empresa (localStorage) em relatórios; prisma: Company.contractedHoursPerMonth; smtp: suporte a múltiplos destinatários e timeout opcional
This commit is contained in:
parent
a23b429e4d
commit
384d4411b6
13 changed files with 133 additions and 38 deletions
|
|
@ -24,16 +24,21 @@ export function AdminAlertsManager() {
|
|||
|
||||
const alertsRaw = useQuery(
|
||||
api.alerts.list,
|
||||
convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
convexUserId
|
||||
? ({
|
||||
tenantId,
|
||||
viewerId: convexUserId as Id<"users">,
|
||||
start,
|
||||
end,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
})
|
||||
: "skip"
|
||||
) as Doc<"alerts">[] | undefined
|
||||
|
||||
const alerts = useMemo(() => {
|
||||
let list = alertsRaw ?? []
|
||||
if (companyId !== "all") list = list.filter((a) => String(a.companyId) === companyId)
|
||||
if (typeof start === "number") list = list.filter((a) => a.createdAt >= start)
|
||||
if (typeof end === "number") list = list.filter((a) => a.createdAt < end)
|
||||
const list = alertsRaw ?? []
|
||||
return list.sort((a, b) => b.createdAt - a.createdAt)
|
||||
}, [alertsRaw, companyId, start, end])
|
||||
}, [alertsRaw])
|
||||
|
||||
const companies = useQuery(
|
||||
api.companies.list,
|
||||
|
|
@ -124,4 +129,3 @@ export function AdminAlertsManager() {
|
|||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
|
||||
import {
|
||||
ToggleGroup,
|
||||
ToggleGroupItem,
|
||||
|
|
@ -44,8 +45,8 @@ export function ChartAreaInteractive() {
|
|||
const [mounted, setMounted] = React.useState(false)
|
||||
const isMobile = useIsMobile()
|
||||
const [timeRange, setTimeRange] = React.useState("7d")
|
||||
// Use a non-empty sentinel value for "all" to satisfy Select.Item requirements
|
||||
const [companyId, setCompanyId] = React.useState<string>("all")
|
||||
// Persistir seleção de empresa globalmente
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const [companyQuery, setCompanyQuery] = React.useState("")
|
||||
const { session, convexUserId } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|||
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"
|
||||
|
||||
const PRIORITY_LABELS: Record<string, string> = {
|
||||
LOW: "Baixa",
|
||||
|
|
@ -30,7 +31,7 @@ const STATUS_LABELS: Record<string, string> = {
|
|||
|
||||
export function BacklogReport() {
|
||||
const [timeRange, setTimeRange] = useState("90d")
|
||||
const [companyId, setCompanyId] = useState<string>("all")
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const { session, convexUserId } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
const data = useQuery(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { useState } from "react"
|
|||
import { Button } from "@/components/ui/button"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
|
||||
|
||||
function formatScore(value: number | null) {
|
||||
if (value === null) return "—"
|
||||
|
|
@ -20,7 +21,7 @@ function formatScore(value: number | null) {
|
|||
}
|
||||
|
||||
export function CsatReport() {
|
||||
const [companyId, setCompanyId] = useState<string>("all")
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const [timeRange, setTimeRange] = useState<string>("90d")
|
||||
const { session, convexUserId } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { Button } from "@/components/ui/button"
|
|||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
|
||||
|
||||
function formatHours(ms: number) {
|
||||
const hours = ms / 3600000
|
||||
|
|
@ -32,7 +33,7 @@ type HoursItem = {
|
|||
export function HoursReport() {
|
||||
const [timeRange, setTimeRange] = useState("90d")
|
||||
const [query, setQuery] = useState("")
|
||||
const [companyId, setCompanyId] = useState<string>("all")
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const { session, convexUserId } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button"
|
|||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { useState } from "react"
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
|
||||
|
||||
function formatMinutes(value: number | null) {
|
||||
if (value === null) return "—"
|
||||
|
|
@ -25,7 +26,7 @@ function formatMinutes(value: number | null) {
|
|||
}
|
||||
|
||||
export function SlaReport() {
|
||||
const [companyId, setCompanyId] = useState<string>("all")
|
||||
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
|
||||
const [timeRange, setTimeRange] = useState<string>("90d")
|
||||
const { session, convexUserId } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue