refactor: enhance user tables and machine ticket views
This commit is contained in:
parent
bd2f22d046
commit
28796bf105
7 changed files with 416 additions and 201 deletions
|
|
@ -1452,7 +1452,11 @@ export function AdminMachinesOverview({ tenantId, initialCompanyFilterSlug = "al
|
|||
<div className="max-h-64 overflow-auto rounded-md border border-slate-200">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setCompanyFilterSlug("all"); setCompanySearch(""); setIsCompanyPopoverOpen(false) }}
|
||||
onClick={() => {
|
||||
setCompanyFilterSlug("all")
|
||||
setCompanySearch("")
|
||||
setIsCompanyPopoverOpen(false)
|
||||
}}
|
||||
className="block w-full px-3 py-2 text-left text-sm hover:bg-slate-100"
|
||||
>
|
||||
Todas empresas
|
||||
|
|
@ -1463,7 +1467,11 @@ export function AdminMachinesOverview({ tenantId, initialCompanyFilterSlug = "al
|
|||
<button
|
||||
key={c.slug}
|
||||
type="button"
|
||||
onClick={() => { setCompanyFilterSlug(c.slug); setCompanySearch(""); setIsCompanyPopoverOpen(false) }}
|
||||
onClick={() => {
|
||||
setCompanyFilterSlug(c.slug)
|
||||
setCompanySearch("")
|
||||
setIsCompanyPopoverOpen(false)
|
||||
}}
|
||||
className="block w-full px-3 py-2 text-left text-sm hover:bg-slate-100"
|
||||
>
|
||||
{c.name}
|
||||
|
|
@ -1477,7 +1485,19 @@ export function AdminMachinesOverview({ tenantId, initialCompanyFilterSlug = "al
|
|||
<Checkbox checked={onlyAlerts} onCheckedChange={(v) => setOnlyAlerts(Boolean(v))} />
|
||||
<span>Somente com alertas</span>
|
||||
</label>
|
||||
<Button variant="outline" onClick={() => { setQ(""); setStatusFilter("all"); setCompanyFilterSlug("all"); setCompanySearch(""); setOnlyAlerts(false) }}>Limpar</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setQ("")
|
||||
setStatusFilter("all")
|
||||
setCompanyFilterSlug("all")
|
||||
setCompanySearch("")
|
||||
setOnlyAlerts(false)
|
||||
setIsCompanyPopoverOpen(false)
|
||||
}}
|
||||
>
|
||||
Limpar
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" className="gap-2" onClick={handleOpenExportDialog}>
|
||||
<Download className="size-4" />
|
||||
Exportar XLSX
|
||||
|
|
@ -2610,70 +2630,66 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
))}
|
||||
</div>
|
||||
<div className="rounded-2xl border border-[color:var(--accent)] bg-[color:var(--accent)]/80 px-4 py-4">
|
||||
<div className="grid gap-3 sm:grid-cols-[1fr_auto] sm:items-center">
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<h4 className="text-sm font-semibold text-accent-foreground">Tickets abertos por esta máquina</h4>
|
||||
{machineTicketsHref ? (
|
||||
<Link
|
||||
href={machineTicketsHref}
|
||||
className="text-xs font-semibold text-accent-foreground underline-offset-4 transition hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-foreground)] focus-visible:ring-offset-2"
|
||||
>
|
||||
Ver todos
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm font-semibold text-accent-foreground">Tickets abertos por esta máquina</h4>
|
||||
{totalOpenTickets === 0 ? (
|
||||
<p className="text-xs text-[color:var(--accent-foreground)]/80">
|
||||
Nenhum chamado em aberto registrado diretamente por esta máquina.
|
||||
</p>
|
||||
) : hasAdditionalOpenTickets ? (
|
||||
<p className="text-[10px] font-semibold uppercase tracking-[0.14em] text-[color:var(--accent-foreground)]/70">
|
||||
Mostrando últimos {Math.min(displayLimit, totalOpenTickets)} de {totalOpenTickets} chamados em aberto
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{hasAdditionalOpenTickets ? (
|
||||
<p className="text-[10px] font-semibold uppercase tracking-[0.14em] text-[color:var(--accent-foreground)]/70">
|
||||
Mostrando últimos {Math.min(displayLimit, totalOpenTickets)} de {totalOpenTickets} chamados
|
||||
em aberto
|
||||
</p>
|
||||
) : null}
|
||||
<ul className="space-y-2">
|
||||
{displayedMachineTickets.map((ticket) => {
|
||||
const priorityMeta = getTicketPriorityMeta(ticket.priority)
|
||||
return (
|
||||
<li key={ticket.id}>
|
||||
<Link
|
||||
href={`/tickets/${ticket.id}`}
|
||||
className="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-[color:var(--accent)] bg-white px-3 py-2 text-sm shadow-sm transition hover:-translate-y-0.5 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)] focus-visible:ring-offset-2"
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium text-neutral-900">
|
||||
#{ticket.reference} · {ticket.subject}
|
||||
</p>
|
||||
<p className="text-xs text-neutral-500">
|
||||
Atualizado {formatRelativeTime(new Date(ticket.updatedAt))}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className={cn("rounded-full px-3 py-1 text-xs font-semibold", priorityMeta.badgeClass)}>
|
||||
{priorityMeta.label}
|
||||
</Badge>
|
||||
<TicketStatusBadge status={ticket.status} className="h-7 px-3 text-xs font-semibold" />
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<p className="text-xs text-[color:var(--accent-foreground)]/80">
|
||||
Últimos chamados vinculados a esta máquina.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="self-center justify-self-end">
|
||||
<div className="flex h-12 min-w-[72px] items-center justify-center rounded-2xl border border-[color:var(--accent)] bg-white px-5 shadow-sm sm:min-w-[88px]">
|
||||
<span className="text-2xl font-semibold leading-none text-accent-foreground tabular-nums sm:text-3xl">
|
||||
{totalOpenTickets}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-10 min-w-[56px] items-center justify-center rounded-xl border border-[color:var(--accent)] bg-white px-3 text-[color:var(--accent-foreground)] shadow-sm">
|
||||
<span className="text-lg font-semibold leading-none tabular-nums">{totalOpenTickets}</span>
|
||||
</div>
|
||||
{machineTicketsHref ? (
|
||||
<Link
|
||||
href={machineTicketsHref}
|
||||
className="text-xs font-semibold text-accent-foreground underline-offset-4 transition hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-foreground)] focus-visible:ring-offset-2"
|
||||
>
|
||||
Ver todos
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{totalOpenTickets > 0 ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{displayedMachineTickets.map((ticket) => {
|
||||
const priorityMeta = getTicketPriorityMeta(ticket.priority)
|
||||
return (
|
||||
<Link
|
||||
key={ticket.id}
|
||||
href={`/tickets/${ticket.id}`}
|
||||
className="group flex h-full flex-col justify-between gap-3 rounded-xl border border-[color:var(--accent)] bg-white p-3 text-sm shadow-sm transition hover:-translate-y-0.5 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)] focus-visible:ring-offset-2"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<p className="line-clamp-2 font-medium text-neutral-900">
|
||||
#{ticket.reference} · {ticket.subject}
|
||||
</p>
|
||||
<p className="text-xs text-neutral-500">
|
||||
Atualizado {formatRelativeTime(new Date(ticket.updatedAt))}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<Badge className={cn("rounded-full px-3 py-1 text-xs font-semibold", priorityMeta.badgeClass)}>
|
||||
{priorityMeta.label}
|
||||
</Badge>
|
||||
<TicketStatusBadge status={ticket.status} className="h-7 px-3 text-xs font-semibold" />
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{machine.authEmail ? (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue