Fix report filters and zod resolver
This commit is contained in:
parent
5b22065609
commit
06fdb54480
12 changed files with 79 additions and 26 deletions
|
|
@ -1422,6 +1422,8 @@ export async function ticketsByMachineAndCategoryHandler(
|
|||
companyId,
|
||||
machineId,
|
||||
userId,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
}: {
|
||||
tenantId: string
|
||||
viewerId: Id<"users">
|
||||
|
|
@ -1429,10 +1431,12 @@ export async function ticketsByMachineAndCategoryHandler(
|
|||
companyId?: Id<"companies">
|
||||
machineId?: Id<"machines">
|
||||
userId?: Id<"users">
|
||||
dateFrom?: string
|
||||
dateTo?: string
|
||||
}
|
||||
) {
|
||||
const viewer = await requireStaff(ctx, viewerId, tenantId)
|
||||
const { startMs, endMs, days } = resolveRangeWindow(range, undefined, undefined, 90)
|
||||
const { startMs, endMs, days } = resolveRangeWindow(range, dateFrom, dateTo, 90)
|
||||
|
||||
const tickets =
|
||||
days === 0
|
||||
|
|
@ -1584,6 +1588,8 @@ export async function hoursByMachineHandler(
|
|||
companyId,
|
||||
machineId,
|
||||
userId,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
}: {
|
||||
tenantId: string
|
||||
viewerId: Id<"users">
|
||||
|
|
@ -1591,12 +1597,14 @@ export async function hoursByMachineHandler(
|
|||
companyId?: Id<"companies">
|
||||
machineId?: Id<"machines">
|
||||
userId?: Id<"users">
|
||||
dateFrom?: string
|
||||
dateTo?: string
|
||||
}
|
||||
) {
|
||||
const viewer = await requireStaff(ctx, viewerId, tenantId)
|
||||
const tickets = await fetchScopedTickets(ctx, tenantId, viewer)
|
||||
|
||||
const { startMs, endMs, days } = resolveRangeWindow(range, undefined, undefined, 90)
|
||||
const { startMs, endMs, days } = resolveRangeWindow(range, dateFrom, dateTo, 90)
|
||||
|
||||
const machinesById = new Map<string, Doc<"machines"> | null>()
|
||||
const companiesById = new Map<string, Doc<"companies"> | null>()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from "react"
|
||||
import dynamic from "next/dynamic"
|
||||
import { Controller, FormProvider, useFieldArray, useForm, type UseFormReturn } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { zodResolver } from "@/lib/zod-resolver"
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconBuildingSkyscraper,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from
|
|||
import { format } from "date-fns"
|
||||
import { ptBR } from "date-fns/locale"
|
||||
import { Controller, FormProvider, useFieldArray, useForm } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { zodResolver } from "@/lib/zod-resolver"
|
||||
import {
|
||||
IconBuildingSkyscraper,
|
||||
IconChevronRight,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ import { useSidebar } from "@/components/ui/sidebar"
|
|||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { zodResolver } from "@/lib/zod-resolver"
|
||||
import {
|
||||
Check,
|
||||
Copy,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@ export function BacklogReport() {
|
|||
const { session, convexUserId, isStaff, isAdmin } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
|
||||
const data = useQuery(
|
||||
api.reports.backlogOverview,
|
||||
enabled
|
||||
|
|
@ -60,8 +68,7 @@ export function BacklogReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
}
|
||||
: "skip"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ export function CategoryReport() {
|
|||
const [dateTo, setDateTo] = useState<string | null>(null)
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
|
||||
const companyFilter = companyId !== "all" ? (companyId as Id<"companies">) : undefined
|
||||
|
||||
const data = useQuery(
|
||||
|
|
@ -70,10 +77,9 @@ export function CategoryReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyFilter,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
}
|
||||
: "skip",
|
||||
: "skip"
|
||||
) as CategoryInsightsResponse | undefined
|
||||
|
||||
const companies = useQuery(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
|||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { useState } from "react"
|
||||
import { useMemo, useState } from "react"
|
||||
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
|
||||
import { SearchableCombobox, type SearchableComboboxOption } from "@/components/ui/searchable-combobox"
|
||||
|
|
@ -31,6 +31,12 @@ export function CsatReport() {
|
|||
const { session, convexUserId, isStaff, isAdmin } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
const data = useQuery(
|
||||
api.reports.csatOverview,
|
||||
enabled
|
||||
|
|
@ -39,8 +45,7 @@ export function CsatReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
})
|
||||
: "skip"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,12 @@ export function HoursReport() {
|
|||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
const data = useQuery(
|
||||
api.reports.hoursByClient,
|
||||
enabled && groupBy === "company"
|
||||
|
|
@ -86,8 +92,7 @@ export function HoursReport() {
|
|||
tenantId,
|
||||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange === "365d" || timeRange === "all" ? "90d" : timeRange,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
}
|
||||
: "skip"
|
||||
) as { rangeDays: number; items: HoursItem[] } | undefined
|
||||
|
|
@ -100,8 +105,7 @@ export function HoursReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
}
|
||||
: "skip"
|
||||
) as HoursByMachineResponse | undefined
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ export function MachineCategoryReport() {
|
|||
|
||||
const canView = Boolean(isStaff)
|
||||
const enabled = Boolean(canView && convexUserId)
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
|
||||
const data = useQuery(
|
||||
api.reports.ticketsByMachineAndCategory,
|
||||
|
|
@ -66,8 +72,7 @@ export function MachineCategoryReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
} as const)
|
||||
: "skip"
|
||||
) as MachineCategoryReportData | undefined
|
||||
|
|
@ -163,8 +168,7 @@ export function MachineCategoryReport() {
|
|||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
machineId: selectedMachineId !== "all" ? (selectedMachineId as Id<"machines">) : undefined,
|
||||
userId: selectedUserId !== "all" ? (selectedUserId as Id<"users">) : undefined,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
} as const)
|
||||
: "skip"
|
||||
) as MachineHoursResponse | undefined
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ export function SlaReport() {
|
|||
const { session, convexUserId, isStaff, isAdmin } = useAuth()
|
||||
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
|
||||
const enabled = Boolean(isStaff && convexUserId)
|
||||
const dateRangeFilters = useMemo(() => {
|
||||
const filters: { dateFrom?: string; dateTo?: string } = {}
|
||||
if (dateFrom) filters.dateFrom = dateFrom
|
||||
if (dateTo) filters.dateTo = dateTo
|
||||
return filters
|
||||
}, [dateFrom, dateTo])
|
||||
const data = useQuery(
|
||||
api.reports.slaOverview,
|
||||
enabled
|
||||
|
|
@ -76,8 +82,7 @@ export function SlaReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
})
|
||||
: "skip"
|
||||
)
|
||||
|
|
@ -96,8 +101,7 @@ export function SlaReport() {
|
|||
viewerId: convexUserId as Id<"users">,
|
||||
range: timeRange,
|
||||
companyId: companyId === "all" ? undefined : (companyId as Id<"companies">),
|
||||
dateFrom,
|
||||
dateTo,
|
||||
...dateRangeFilters,
|
||||
})
|
||||
: "skip"
|
||||
) as { rangeDays: number; series: Array<{ date: string; opened: number; resolved: number }> } | undefined
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { Input } from "@/components/ui/input"
|
|||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { FieldSet, FieldGroup, Field, FieldLabel, FieldError } from "@/components/ui/field"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { zodResolver } from "@/lib/zod-resolver"
|
||||
import { toast } from "sonner"
|
||||
import { Spinner } from "@/components/ui/spinner"
|
||||
import { Dropzone } from "@/components/ui/dropzone"
|
||||
|
|
|
|||
15
src/lib/zod-resolver.ts
Normal file
15
src/lib/zod-resolver.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { zodResolver as baseZodResolver } from "@hookform/resolvers/zod"
|
||||
import { ZodError } from "zod"
|
||||
|
||||
// Zod v4 renamed `error.errors` to `error.issues`, but @hookform/resolvers
|
||||
// still expects the legacy `errors` getter. Patch the prototype once so every
|
||||
// resolver consumer keeps working without duplicating validation logic.
|
||||
if (typeof ZodError !== "undefined" && !("errors" in ZodError.prototype)) {
|
||||
Object.defineProperty(ZodError.prototype, "errors", {
|
||||
get() {
|
||||
return this.issues
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const zodResolver = baseZodResolver
|
||||
Loading…
Add table
Add a link
Reference in a new issue