Refine tickets filter layout

This commit is contained in:
Esdras Renan 2025-11-13 19:50:06 -03:00
parent 12acbc5b1c
commit a08545fd40

View file

@ -1,7 +1,7 @@
"use client"
import { useEffect, useMemo, useState } from "react"
import { IconCalendar, IconFilter, IconRefresh } from "@tabler/icons-react"
import { IconCalendar, IconFilter, IconRefresh, IconSearch } from "@tabler/icons-react"
import type { DateRange } from "react-day-picker"
import {
@ -31,6 +31,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
type QueueOption = string
@ -146,10 +147,10 @@ function DateRangeButton({ from, to, onChange, className }: DateRangeButtonProps
<PopoverTrigger asChild>
<Button
variant="outline"
className={`w-full justify-start rounded-xl border-slate-300 bg-white/90 ${className ?? ""}`}
className={`flex h-10 w-full items-center justify-start gap-2 rounded-2xl border-slate-300 bg-white/95 text-sm font-semibold text-neutral-700 ${className ?? ""}`}
>
<IconCalendar className="mr-2 size-4" />
<span className="line-clamp-1">{label}</span>
<IconCalendar className="size-4 text-neutral-500" />
<span className="truncate">{label}</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="end">
@ -222,32 +223,47 @@ export function TicketsFilters({
return chips
}, [filters, assignees, categories, canUseAdvancedFilters])
const advancedFiltersCount = Number(Boolean(filters.status)) + Number(Boolean(filters.priority)) + Number(Boolean(filters.channel))
return (
<div className="flex flex-col gap-4">
<section className="rounded-2xl border border-slate-200 bg-white/80 p-4 shadow-sm">
<div className="grid gap-4">
<div className="grid grid-cols-[repeat(auto-fit,minmax(220px,1fr))] items-center gap-3">
<div className="col-span-2">
<section className="rounded-3xl border border-slate-200 bg-white/90 p-4 shadow-sm">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:gap-4">
<div className="flex flex-1 flex-col gap-2">
<div className="relative">
<IconSearch className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-neutral-400" />
<Input
placeholder="Buscar por assunto ou #ID"
value={filters.search}
onChange={(event) => setPartial({ search: event.target.value })}
className="w-full rounded-xl border-slate-300 bg-white/90"
className="w-full rounded-2xl border-slate-300 bg-white/95 pl-9"
/>
</div>
<div className="col-span-full flex items-center justify-end gap-2">
</div>
<div className="flex flex-wrap items-center gap-2">
<DateRangeButton
from={filters.dateFrom}
to={filters.dateTo}
onChange={({ from, to }) => setPartial({ dateFrom: from, dateTo: to })}
className="w-full min-w-[200px] rounded-2xl border-slate-300 bg-white/95 text-left text-sm font-semibold text-neutral-700 lg:w-auto"
/>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
size="icon"
className="size-9 rounded-full border-slate-300 bg-white text-neutral-800 shadow-sm hover:bg-slate-50"
aria-label="Filtros avançados"
className="h-10 gap-2 rounded-full border-dashed border-slate-300 bg-white/80 px-4 text-sm font-medium text-neutral-700 shadow-none hover:bg-slate-50"
>
<IconFilter className="size-4" />
<span>Filtros rápidos</span>
{advancedFiltersCount > 0 ? (
<Badge className="rounded-full bg-neutral-900 px-2 py-0 text-[0.65rem] font-semibold text-white">
{advancedFiltersCount}
</Badge>
) : null}
</Button>
</PopoverTrigger>
<PopoverContent className="w-64 space-y-4" align="end">
<PopoverContent className="w-72 space-y-4" align="end">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase text-neutral-500">Status</p>
<Select
@ -311,22 +327,22 @@ export function TicketsFilters({
</Popover>
<Button
variant="ghost"
size="icon"
className="size-9 rounded-full text-neutral-700 hover:bg-slate-100"
className="h-10 gap-2 rounded-full text-sm font-medium text-neutral-700 hover:bg-slate-100"
onClick={() => setPartial(defaultTicketFilters)}
aria-label="Resetar filtros"
>
<IconRefresh className="size-4" />
Limpar
</Button>
</div>
</div>
<div className="grid grid-cols-[repeat(auto-fit,minmax(220px,1fr))] gap-3">
<div className="flex flex-wrap gap-3">
{canUseAdvancedFilters && (
<div className="min-w-[220px] flex-1">
<Select
value={filters.queue ?? ALL_VALUE}
onValueChange={(value) => setPartial({ queue: value === ALL_VALUE ? null : value })}
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectTrigger className="w-full rounded-2xl border-slate-300 bg-slate-50/80 focus:ring-neutral-300">
<SelectValue placeholder="Fila" />
</SelectTrigger>
<SelectContent>
@ -338,13 +354,15 @@ export function TicketsFilters({
))}
</SelectContent>
</Select>
</div>
)}
{canUseAdvancedFilters && (
<div className="min-w-[220px] flex-1">
<Select
value={filters.company ?? ALL_VALUE}
onValueChange={(value) => setPartial({ company: value === ALL_VALUE ? null : value })}
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectTrigger className="w-full rounded-2xl border-slate-300 bg-slate-50/80 focus:ring-neutral-300">
<SelectValue placeholder="Empresa" />
</SelectTrigger>
<SelectContent>
@ -356,12 +374,14 @@ export function TicketsFilters({
))}
</SelectContent>
</Select>
</div>
)}
<div className="min-w-[220px] flex-1">
<Select
value={filters.categoryId ?? ALL_VALUE}
onValueChange={(value) => setPartial({ categoryId: value === ALL_VALUE ? null : value })}
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectTrigger className="w-full rounded-2xl border-slate-300 bg-slate-50/80 focus:ring-neutral-300">
<SelectValue placeholder="Categoria" />
</SelectTrigger>
<SelectContent>
@ -373,12 +393,14 @@ export function TicketsFilters({
))}
</SelectContent>
</Select>
</div>
{canUseAdvancedFilters && (
<div className="min-w-[220px] flex-1">
<Select
value={filters.assigneeId ?? ALL_VALUE}
onValueChange={(value) => setPartial({ assigneeId: value === ALL_VALUE ? null : value })}
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectTrigger className="w-full rounded-2xl border-slate-300 bg-slate-50/80 focus:ring-neutral-300">
<SelectValue placeholder="Responsável" />
</SelectTrigger>
<SelectContent>
@ -390,39 +412,48 @@ export function TicketsFilters({
))}
</SelectContent>
</Select>
</div>
)}
</div>
<div className="grid grid-cols-[repeat(auto-fit,minmax(220px,1fr))] gap-3">
<Select
<div className="flex flex-wrap gap-3">
<ToggleGroup
type="single"
value={filters.view}
onValueChange={(value) => setPartial({ view: value as TicketFiltersState["view"] })}
onValueChange={(value) => value && setPartial({ view: value as TicketFiltersState["view"] })}
className="flex h-10 min-w-[220px] flex-1 items-center rounded-full border border-slate-200 bg-slate-50/70 p-1"
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="active">Em andamento</SelectItem>
<SelectItem value="completed">Concluídos</SelectItem>
</SelectContent>
</Select>
<Select
<ToggleGroupItem
value="active"
className="inline-flex h-full flex-1 items-center justify-center rounded-full px-4 text-sm font-semibold text-neutral-600 transition data-[state=on]:bg-neutral-900 data-[state=on]:text-white"
>
Em andamento
</ToggleGroupItem>
<ToggleGroupItem
value="completed"
className="inline-flex h-full flex-1 items-center justify-center rounded-full px-4 text-sm font-semibold text-neutral-600 transition data-[state=on]:bg-neutral-900 data-[state=on]:text-white"
>
Concluídos
</ToggleGroupItem>
</ToggleGroup>
<ToggleGroup
type="single"
value={filters.sort}
onValueChange={(value) => setPartial({ sort: value as TicketFiltersState["sort"] })}
onValueChange={(value) => value && setPartial({ sort: value as TicketFiltersState["sort"] })}
className="flex h-10 min-w-[220px] flex-1 items-center rounded-full border border-slate-200 bg-slate-50/70 p-1"
>
<SelectTrigger className="w-full rounded-xl border-slate-300 bg-slate-50/70 focus:ring-neutral-300">
<SelectValue placeholder="Ordenar" />
</SelectTrigger>
<SelectContent>
<SelectItem value="recent">Mais recentes</SelectItem>
<SelectItem value="oldest">Mais antigos</SelectItem>
</SelectContent>
</Select>
<DateRangeButton
from={filters.dateFrom}
to={filters.dateTo}
onChange={({ from, to }) => setPartial({ dateFrom: from, dateTo: to })}
className="w-full"
/>
<ToggleGroupItem
value="recent"
className="inline-flex h-full flex-1 items-center justify-center rounded-full px-4 text-sm font-semibold text-neutral-600 transition data-[state=on]:bg-neutral-900 data-[state=on]:text-white"
>
Mais recentes
</ToggleGroupItem>
<ToggleGroupItem
value="oldest"
className="inline-flex h-full flex-1 items-center justify-center rounded-full px-4 text-sm font-semibold text-neutral-600 transition data-[state=on]:bg-neutral-900 data-[state=on]:text-white"
>
Mais antigos
</ToggleGroupItem>
</ToggleGroup>
</div>
</div>
</section>