chore: prep platform improvements

This commit is contained in:
Esdras Renan 2025-11-09 21:09:38 -03:00
parent a62f3d5283
commit c5ddd54a3e
24 changed files with 777 additions and 649 deletions

View file

@ -11,7 +11,6 @@ import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle }
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { Input } from "@/components/ui/input"
import { usePersistentCompanyFilter } from "@/lib/use-company-filter"
import { Progress } from "@/components/ui/progress"
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
@ -43,7 +42,6 @@ const topClientsChartConfig = {
export function HoursReport() {
const [timeRange, setTimeRange] = useState("90d")
const [query, setQuery] = useState("")
const [companyId, setCompanyId] = usePersistentCompanyFilter("all")
const { session, convexUserId, isStaff } = useAuth()
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
@ -74,12 +72,11 @@ export function HoursReport() {
}, [companies])
const filtered = useMemo(() => {
const items = data?.items ?? []
const q = query.trim().toLowerCase()
let list = items
if (companyId !== "all") list = list.filter((it) => String(it.companyId) === companyId)
if (q) list = list.filter((it) => it.name.toLowerCase().includes(q))
return list
}, [data?.items, query, companyId])
if (companyId !== "all") {
return items.filter((it) => String(it.companyId) === companyId)
}
return items
}, [data?.items, companyId])
const totals = useMemo(() => {
return filtered.reduce(
@ -185,33 +182,37 @@ export function HoursReport() {
<CardTitle>Horas</CardTitle>
<CardDescription>Visualize o esforço interno e externo por empresa e acompanhe o consumo contratado.</CardDescription>
<CardAction>
<div className="flex flex-col items-stretch gap-2 sm:flex-row sm:items-center sm:justify-end">
<Input
placeholder="Pesquisar empresa..."
value={query}
onChange={(e) => setQuery(e.target.value)}
className="h-9 w-full min-w-56 sm:w-72"
/>
<SearchableCombobox
value={companyId}
onValueChange={(next) => setCompanyId(next ?? "all")}
options={companyOptions}
placeholder="Todas as empresas"
className="w-full min-w-56 sm:w-64"
/>
<ToggleGroup type="single" value={timeRange} onValueChange={setTimeRange} variant="outline" className="hidden md:flex">
<ToggleGroupItem value="90d">90 dias</ToggleGroupItem>
<ToggleGroupItem value="30d">30 dias</ToggleGroupItem>
<ToggleGroupItem value="7d">7 dias</ToggleGroupItem>
</ToggleGroup>
<Button asChild size="sm" variant="outline">
<a
href={`/api/reports/hours-by-client.xlsx?range=${timeRange}${query ? `&q=${encodeURIComponent(query)}` : ""}${companyId !== "all" ? `&companyId=${companyId}` : ""}`}
download
>
Exportar XLSX
</a>
</Button>
<div className="space-y-4">
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
<SearchableCombobox
value={companyId}
onValueChange={(next) => setCompanyId(next ?? "all")}
options={companyOptions}
placeholder="Todas as empresas"
className="w-full min-w-56 lg:w-72"
/>
<div className="flex flex-wrap items-center gap-2">
{["90d", "30d", "7d"].map((range) => (
<Button
key={range}
type="button"
size="sm"
variant={timeRange === range ? "default" : "outline"}
onClick={() => setTimeRange(range)}
>
{range === "90d" ? "90 dias" : range === "30d" ? "30 dias" : "7 dias"}
</Button>
))}
<Button asChild size="sm" variant="outline" className="gap-2">
<a
href={`/api/reports/hours-by-client.xlsx?range=${timeRange}${companyId !== "all" ? `&companyId=${companyId}` : ""}`}
download
>
Exportar XLSX
</a>
</Button>
</div>
</div>
</div>
</CardAction>
</CardHeader>