-
-
Tickets abertos por esta máquina
- {machineTicketsHref ? (
-
- Ver todos
-
- ) : null}
-
+
+
+
Tickets abertos por esta máquina
{totalOpenTickets === 0 ? (
Nenhum chamado em aberto registrado diretamente por esta máquina.
+ ) : hasAdditionalOpenTickets ? (
+
+ Mostrando últimos {Math.min(displayLimit, totalOpenTickets)} de {totalOpenTickets} chamados em aberto
+
) : (
-
- {hasAdditionalOpenTickets ? (
-
- Mostrando últimos {Math.min(displayLimit, totalOpenTickets)} de {totalOpenTickets} chamados
- em aberto
-
- ) : null}
-
-
+
+ Últimos chamados vinculados a esta máquina.
+
)}
-
-
-
- {totalOpenTickets}
-
+
+
+ {totalOpenTickets}
+ {machineTicketsHref ? (
+
+ Ver todos
+
+ ) : null}
+ {totalOpenTickets > 0 ? (
+
+ {displayedMachineTickets.map((ticket) => {
+ const priorityMeta = getTicketPriorityMeta(ticket.priority)
+ return (
+
+
+
+ #{ticket.reference} · {ticket.subject}
+
+
+ Atualizado {formatRelativeTime(new Date(ticket.updatedAt))}
+
+
+
+
+ {priorityMeta.label}
+
+
+
+
+ )
+ })}
+
+ ) : null}
{machine.authEmail ? (
diff --git a/src/components/admin/machines/machine-tickets-history.client.tsx b/src/components/admin/machines/machine-tickets-history.client.tsx
index 6af2e9f..0bcec6f 100644
--- a/src/components/admin/machines/machine-tickets-history.client.tsx
+++ b/src/components/admin/machines/machine-tickets-history.client.tsx
@@ -5,6 +5,7 @@ import Link from "next/link"
import { usePaginatedQuery, useQuery } from "convex/react"
import { format, formatDistanceToNowStrict } from "date-fns"
import { ptBR } from "date-fns/locale"
+import { IconUserOff } from "@tabler/icons-react"
import type { Id } from "@/convex/_generated/dataModel"
import { api } from "@/convex/_generated/api"
@@ -24,6 +25,7 @@ import { Spinner } from "@/components/ui/spinner"
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty"
import { TicketStatusBadge } from "@/components/tickets/status-badge"
import type { TicketPriority, TicketStatus } from "@/lib/schemas/ticket"
+import { EmptyIndicator } from "@/components/ui/empty-indicator"
type MachineTicketHistoryItem = {
id: string
@@ -355,7 +357,6 @@ export function MachineTicketsHistoryClient({ tenantId: _tenantId, machineId }:
{tickets.map((ticket) => {
const priorityMeta = getPriorityMeta(ticket.priority)
const requesterLabel = ticket.requester?.name ?? ticket.requester?.email ?? "Solicitante não informado"
- const assigneeLabel = ticket.assignee?.name ?? ticket.assignee?.email ?? "Sem responsável"
const updatedLabel = formatRelativeTime(ticket.updatedAt)
const updatedAbsolute = formatAbsoluteTime(ticket.updatedAt)
return (
@@ -394,11 +395,21 @@ export function MachineTicketsHistoryClient({ tenantId: _tenantId, machineId }:
-
-
{assigneeLabel}
- {ticket.assignee?.email ? (
-
{ticket.assignee.email}
- ) : null}
+
+ {ticket.assignee ? (
+ <>
+ {ticket.assignee.name ?? ticket.assignee.email ?? "—"}
+ {ticket.assignee.email ? (
+ {ticket.assignee.email}
+ ) : null}
+ >
+ ) : (
+
+ )}
diff --git a/src/components/admin/users/admin-users-workspace.tsx b/src/components/admin/users/admin-users-workspace.tsx
index 42f9b56..e4c1050 100644
--- a/src/components/admin/users/admin-users-workspace.tsx
+++ b/src/components/admin/users/admin-users-workspace.tsx
@@ -18,6 +18,8 @@ import {
IconTrash,
IconUserPlus,
IconUsers,
+ IconBuildingOff,
+ IconUserOff,
} from "@tabler/icons-react"
import { toast } from "sonner"
@@ -62,8 +64,8 @@ import {
} from "@/components/ui/table"
import { Textarea } from "@/components/ui/textarea"
import { Badge } from "@/components/ui/badge"
-import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { SearchableCombobox, type SearchableComboboxOption } from "@/components/ui/searchable-combobox"
+import { EmptyIndicator } from "@/components/ui/empty-indicator"
export type AdminAccount = {
id: string
@@ -209,6 +211,15 @@ function AccountsTable({
const effectiveTenantId = tenantId || DEFAULT_TENANT_ID
+ const headerCellClass =
+ "px-3 py-3 text-[11px] font-semibold uppercase tracking-wide text-neutral-600 first:pl-4 last:pr-4"
+ const cellClass =
+ "px-3 py-4 text-sm text-neutral-700 first:pl-4 last:pr-4 whitespace-pre-wrap leading-snug"
+ const rowClass = "border-b border-border/60 text-sm transition-colors hover:bg-muted/40 last:border-b-0"
+ const metaLabelClass =
+ "text-[11px] font-semibold uppercase tracking-wide text-neutral-500"
+ const metaValueClass = "text-sm text-neutral-600 leading-tight break-words"
+
const filteredAccounts = useMemo(() => {
const term = search.trim().toLowerCase()
return accounts.filter((account) => {
@@ -654,122 +665,192 @@ function AccountsTable({
-
-
-
-
+
+
+
+
+
+ toggleVisibleSelection(checked === true)}
+ aria-label="Selecionar todos os usuários visíveis"
+ />
+
+ Usuário
+ Cargo
+ Gestor
+ Empresa
+ Papel
+ Último acesso
+ Ações
+
+
+
+ {filteredAccounts.length === 0 ? (
-
- toggleVisibleSelection(checked === true)}
- aria-label="Selecionar todos os usuários visíveis"
- />
-
- Usuário
- Cargo
- Gestor
- Empresa
- Papel
- Último acesso
- Ações
+
+ Nenhum usuário encontrado.
+
-
-
- {filteredAccounts.length === 0 ? (
-
-
- Nenhum usuário encontrado.
-
-
- ) : (
- filteredAccounts.map((account) => {
- const initials = account.name
- .split(" ")
- .filter(Boolean)
- .slice(0, 2)
- .map((part) => part.charAt(0).toUpperCase())
- .join("")
- return (
-
-
-
- setRowSelection((prev) => ({ ...prev, [account.id]: checked === true }))
- }
- aria-label={`Selecionar ${account.name}`}
- />
-
-
-
-
- {initials || account.email.charAt(0).toUpperCase()}
-
-
-
{account.name}
-
{account.email}
+ ) : (
+ filteredAccounts.map((account) => {
+ const initials = account.name
+ .split(" ")
+ .filter(Boolean)
+ .slice(0, 2)
+ .map((part) => part.charAt(0).toUpperCase())
+ .join("")
+ return (
+
+
+
+ setRowSelection((prev) => ({ ...prev, [account.id]: checked === true }))
+ }
+ aria-label={`Selecionar ${account.name}`}
+ />
+
+
+
+
+
+ {account.name}
+
+
{account.email}
+
+
+
+ Cargo
+
+ {account.jobTitle ?? "Sem cargo"}
+
+
+
+ Empresa
+ {account.companyName ? (
+ {account.companyName}
+ ) : (
+
+
+
+ )}
+
+
+
Gestor
+ {account.managerName ? (
+
+ {account.managerName}
+ {account.managerEmail ? (
+
+ {account.managerEmail}
+
+ ) : null}
+
+ ) : (
+
+
+
+ )}
-
-
- {account.jobTitle ? (
- account.jobTitle
- ) : (
- Sem cargo
- )}
-
-
- {account.managerName ? (
-
- {account.managerName}
- {account.managerEmail ? (
- {account.managerEmail}
- ) : null}
-
- ) : (
- Sem gestor
- )}
-
-
- {account.companyName ?? Sem empresa}
-
-
- {ROLE_LABEL[account.role]}
-
-
- {formatDate(account.lastSeenAt)}
-
-
-
-
-
+
+
+
+ {account.jobTitle ? (
+
+ {account.jobTitle}
+
+ ) : (
+ Sem cargo
+ )}
+
+
+ {account.managerName ? (
+
+
+ {account.managerName}
+
+ {account.managerEmail ? (
+
+ {account.managerEmail}
+
+ ) : null}
-
-
- )
- })
- )}
-
-
-
+ ) : (
+
+
+
+ )}
+
+
+ {account.companyName ? (
+
+ {account.companyName}
+
+ ) : (
+
+
+
+ )}
+
+
+
+ {ROLE_LABEL[account.role]}
+
+
+
+ {formatDate(account.lastSeenAt)}
+
+
+
+
+
+
+
+
+ )
+ })
+ )}
+
+
diff --git a/src/components/tickets/tickets-board.tsx b/src/components/tickets/tickets-board.tsx
index 349954e..5ba36b7 100644
--- a/src/components/tickets/tickets-board.tsx
+++ b/src/components/tickets/tickets-board.tsx
@@ -5,6 +5,7 @@ import Link from "next/link"
import { formatDistanceStrict } from "date-fns"
import { ptBR } from "date-fns/locale"
import { LayoutGrid } from "lucide-react"
+import { IconBuildingOff, IconCategory, IconUserOff } from "@tabler/icons-react"
import type { Ticket } from "@/lib/schemas/ticket"
import { Badge } from "@/components/ui/badge"
@@ -12,6 +13,7 @@ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from "
import { cn } from "@/lib/utils"
import { PrioritySelect } from "@/components/tickets/priority-select"
import { getTicketStatusChipClass, getTicketStatusLabel } from "@/lib/ticket-status-style"
+import { EmptyIndicator } from "@/components/ui/empty-indicator"
type TicketsBoardProps = {
tickets: Ticket[]
@@ -185,7 +187,13 @@ export function TicketsBoard({ tickets, enteringIds }: TicketsBoardProps) {
Empresa
- {ticket.company?.name ?? "Sem empresa"}
+ {ticket.company?.name ?? (
+
+ )}
@@ -193,7 +201,13 @@ export function TicketsBoard({ tickets, enteringIds }: TicketsBoardProps) {
Responsável
- {ticket.assignee?.name ?? "Sem responsável"}
+ {ticket.assignee?.name ?? (
+
+ )}
@@ -222,7 +236,15 @@ export function TicketsBoard({ tickets, enteringIds }: TicketsBoardProps) {
Categoria:{" "}
- {ticket.category?.name ?? "Sem categoria"}
+ {ticket.category?.name ? (
+ {ticket.category.name}
+ ) : (
+
+ )}
diff --git a/src/components/tickets/tickets-table.tsx b/src/components/tickets/tickets-table.tsx
index cd31309..ffbb17d 100644
--- a/src/components/tickets/tickets-table.tsx
+++ b/src/components/tickets/tickets-table.tsx
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from "react"
import { useRouter } from "next/navigation"
import { format, formatDistanceToNowStrict } from "date-fns"
import { ptBR } from "date-fns/locale"
+import { IconBuildingOff, IconCategory, IconHierarchyOff, IconUserOff } from "@tabler/icons-react"
import type { Ticket } from "@/lib/schemas/ticket"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
@@ -23,6 +24,7 @@ import { PrioritySelect } from "@/components/tickets/priority-select"
import { cn } from "@/lib/utils"
import { deriveServerOffset, toServerTimestamp } from "@/components/tickets/ticket-timer.utils"
import { getTicketStatusLabel, getTicketStatusTextClass } from "@/lib/ticket-status-style"
+import { EmptyIndicator } from "@/components/ui/empty-indicator"
const cellClass =
"px-3 py-4 sm:px-4 xl:px-3 xl:py-3 align-middle text-sm text-neutral-700 whitespace-normal " +
@@ -60,7 +62,15 @@ function formatQueueLabel(queue?: string | null) {
function AssigneeCell({ ticket }: { ticket: Ticket }) {
if (!ticket.assignee) {
- return
Sem responsável
+ return (
+
+
+
+ )
}
const initials = ticket.assignee.name
@@ -188,13 +198,19 @@ export function TicketsTable({ tickets, enteringIds }: TicketsTableProps) {
}}
>
-
-
- #{ticket.reference}
-
-
- {ticket.queue ?? "Sem fila"}
-
+
+
#{ticket.reference}
+
+ {ticket.queue ? (
+ {ticket.queue}
+ ) : (
+
+ )}
+
@@ -220,7 +236,11 @@ export function TicketsTable({ tickets, enteringIds }: TicketsTableProps) {
) : null}
) : (
- Sem categoria
+
)}
@@ -230,12 +250,17 @@ export function TicketsTable({ tickets, enteringIds }: TicketsTableProps) {