Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-07 13:32:35 +03:00
co-authored by Copilot
parent 4faafe6667
commit c7eb23dd3f
17 changed files with 668 additions and 120 deletions
@@ -1,5 +1,7 @@
"use client"
import { useMemo } from "react"
import { useSearchParams } from "next/navigation"
import { Loader2 } from "lucide-react"
import { useDashboardData } from "./use-dashboard-data"
import { FinancialTotalsCards } from "./financial-totals-cards"
@@ -12,9 +14,38 @@ import { ItemsTotalsCard } from "./items-totals-card"
import { CustomersTotalsCard } from "./customers-totals-card"
import { SalesPurchaseCards } from "./sales-purchase-cards"
import { VehicleStatsCards } from "./vehicle-stats-cards"
import { DashboardPeriods, type DashboardPeriod, type HomeDashboardQuery } from "@garage/api"
const DEFAULT_PERIOD: DashboardPeriod = "this_month"
function isDashboardPeriod(value: string | null): value is DashboardPeriod {
return Boolean(value && (DashboardPeriods as readonly string[]).includes(value))
}
export function DashboardContent() {
const { data, isLoading, isError, error } = useDashboardData()
const searchParams = useSearchParams()
const filters = useMemo<HomeDashboardQuery>(() => {
const periodValue = searchParams.get("period")
const period = isDashboardPeriod(periodValue) ? periodValue : DEFAULT_PERIOD
const nextFilters: HomeDashboardQuery = { period }
if (period === "custom") {
const startDate = searchParams.get("start_date")
const endDate = searchParams.get("end_date")
if (startDate) {
nextFilters.start_date = startDate
}
if (endDate) {
nextFilters.end_date = endDate
}
}
return nextFilters
}, [searchParams])
const { data, isLoading, isError, error } = useDashboardData(filters)
if (isLoading) {
return (
@@ -0,0 +1,114 @@
"use client"
import { useMemo } from "react"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { DashboardPeriods, type DashboardPeriod } from "@garage/api"
import { Input } from "@/shared/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select"
const DEFAULT_PERIOD: DashboardPeriod = "this_month"
const PERIOD_LABELS: Record<DashboardPeriod, string> = {
today: "Today",
yesterday: "Yesterday",
this_week: "This Week",
this_month: "This Month",
last_month: "Last Month",
last_3_months: "Last 3 Months",
last_6_months: "Last 6 Months",
last_year: "Last Year",
year_to_date: "Year to Date",
all_time: "All Time",
custom: "Custom",
}
function isDashboardPeriod(value: string | null): value is DashboardPeriod {
return Boolean(value && (DashboardPeriods as readonly string[]).includes(value))
}
export function DashboardFiltersToolbar() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const period = useMemo<DashboardPeriod>(() => {
const value = searchParams.get("period")
return isDashboardPeriod(value) ? value : DEFAULT_PERIOD
}, [searchParams])
const startDate = searchParams.get("start_date") ?? ""
const endDate = searchParams.get("end_date") ?? ""
const updateParams = (updates: Record<string, string | null>) => {
const params = new URLSearchParams(searchParams.toString())
Object.entries(updates).forEach(([key, value]) => {
if (!value) {
params.delete(key)
return
}
params.set(key, value)
})
const query = params.toString()
router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false })
}
return (
<div className="flex flex-wrap items-end gap-2">
<div className="w-44">
<Select
value={period}
onValueChange={(value) => {
const nextPeriod = value as DashboardPeriod
updateParams({
period: nextPeriod,
start_date: nextPeriod === "custom" ? startDate || null : null,
end_date: nextPeriod === "custom" ? endDate || null : null,
})
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Period" />
</SelectTrigger>
<SelectContent>
{DashboardPeriods.map((value) => (
<SelectItem key={value} value={value}>
{PERIOD_LABELS[value]}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{period === "custom" ? (
<>
<div className="w-36">
<Input
type="date"
value={startDate}
onChange={(event) => {
updateParams({ start_date: event.target.value || null })
}}
/>
</div>
<div className="w-36">
<Input
type="date"
value={endDate}
onChange={(event) => {
updateParams({ end_date: event.target.value || null })
}}
/>
</div>
</>
) : null}
</div>
)
}
@@ -2,15 +2,15 @@
import { useQuery } from "@tanstack/react-query"
import { useAuthApi } from "@/shared/useApi"
import type { HomeDashboardResponse } from "@garage/api"
import type { HomeDashboardQuery, HomeDashboardResponse } from "@garage/api"
export type DashboardData = HomeDashboardResponse & Record<string, any>
export function useDashboardData() {
export function useDashboardData(filters?: HomeDashboardQuery) {
const api = useAuthApi()
return useQuery<DashboardData>({
queryKey: ["home", "dashboard"],
queryFn: () => api.home.dashboard(),
queryKey: ["home", "dashboard", filters],
queryFn: () => api.home.dashboard(filters),
})
}