fix(ui): ajustes em automações e checklist

This commit is contained in:
esdrasrenan 2025-12-13 22:03:35 -03:00
parent aa3c1855b2
commit 7c82ef18b3
5 changed files with 218 additions and 85 deletions

View file

@ -15,7 +15,7 @@ import { Checkbox } from "@/components/ui/checkbox"
import { DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectEmptyState, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch" import { Switch } from "@/components/ui/switch"
import { Textarea } from "@/components/ui/textarea" import { Textarea } from "@/components/ui/textarea"
@ -689,11 +689,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(companies ?? []).map((company) => ( {(companies ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhuma empresa disponível"
createLabel="Gerenciar empresas"
createHref="/admin/companies"
/>
) : (
(companies ?? []).map((company) => (
<SelectItem key={company.id} value={String(company.id)}> <SelectItem key={company.id} value={String(company.id)}>
{company.name} {company.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : c.field === "queueId" ? ( ) : c.field === "queueId" ? (
@ -707,11 +715,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(queues ?? []).map((queue) => ( {(queues ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhuma fila disponível"
createLabel="Gerenciar filas"
createHref="/settings/queues"
/>
) : (
(queues ?? []).map((queue) => (
<SelectItem key={queue.id} value={String(queue.id)}> <SelectItem key={queue.id} value={String(queue.id)}>
{queue.name} {queue.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : c.field === "categoryId" ? ( ) : c.field === "categoryId" ? (
@ -725,11 +741,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(categories ?? []).map((cat) => ( {(categories ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhuma categoria disponível"
createLabel="Gerenciar categorias"
createHref="/settings/categories"
/>
) : (
(categories ?? []).map((cat) => (
<SelectItem key={cat.id} value={cat.id}> <SelectItem key={cat.id} value={cat.id}>
{cat.name} {cat.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : c.field === "subcategoryId" ? ( ) : c.field === "subcategoryId" ? (
@ -743,11 +767,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{subcategoryOptions.map((sub) => ( {subcategoryOptions.length === 0 ? (
<SelectEmptyState
message="Nenhuma subcategoria disponível"
createLabel="Gerenciar categorias"
createHref="/settings/categories"
/>
) : (
subcategoryOptions.map((sub) => (
<SelectItem key={sub.id} value={sub.id}> <SelectItem key={sub.id} value={sub.id}>
{sub.name} {sub.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : ( ) : (
@ -764,12 +796,22 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(templates ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhum formulário disponível"
createLabel="Gerenciar formulários"
createHref="/settings/forms"
/>
) : (
<>
<SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem> <SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem>
{(templates ?? []).map((tpl) => ( {(templates ?? []).map((tpl) => (
<SelectItem key={tpl.key} value={tpl.key}> <SelectItem key={tpl.key} value={tpl.key}>
{tpl.label} {tpl.label}
</SelectItem> </SelectItem>
))} ))}
</>
)}
</SelectContent> </SelectContent>
</Select> </Select>
)} )}
@ -890,11 +932,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(queues ?? []).map((queue) => ( {(queues ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhuma fila disponível"
createLabel="Gerenciar filas"
createHref="/settings/queues"
/>
) : (
(queues ?? []).map((queue) => (
<SelectItem key={queue.id} value={String(queue.id)}> <SelectItem key={queue.id} value={String(queue.id)}>
{queue.name} {queue.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : a.type === "ASSIGN_TO" ? ( ) : a.type === "ASSIGN_TO" ? (
@ -908,11 +958,19 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(agents ?? []).map((u) => ( {(agents ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhum agente disponível"
createLabel="Gerenciar usuários"
createHref="/admin/users"
/>
) : (
(agents ?? []).map((u) => (
<SelectItem key={u._id} value={String(u._id)}> <SelectItem key={u._id} value={String(u._id)}>
{u.name} {u.name}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : a.type === "SET_FORM_TEMPLATE" ? ( ) : a.type === "SET_FORM_TEMPLATE" ? (
@ -929,12 +987,22 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(templates ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhum formulário disponível"
createLabel="Gerenciar formulários"
createHref="/settings/forms"
/>
) : (
<>
<SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem> <SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem>
{(templates ?? []).map((tpl) => ( {(templates ?? []).map((tpl) => (
<SelectItem key={tpl.key} value={tpl.key}> <SelectItem key={tpl.key} value={tpl.key}>
{tpl.label} {tpl.label}
</SelectItem> </SelectItem>
))} ))}
</>
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : a.type === "SET_CHAT_ENABLED" ? ( ) : a.type === "SET_CHAT_ENABLED" ? (
@ -959,12 +1027,20 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(checklistTemplates ?? []).map((tpl) => ( {(checklistTemplates ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhum template de checklist"
createLabel="Criar template"
createHref="/settings/checklists"
/>
) : (
(checklistTemplates ?? []).map((tpl) => (
<SelectItem key={tpl.id} value={String(tpl.id)}> <SelectItem key={tpl.id} value={String(tpl.id)}>
{tpl.name} {tpl.name}
{tpl.company ? `${tpl.company.name}` : ""} {tpl.company ? `${tpl.company.name}` : ""}
</SelectItem> </SelectItem>
))} ))
)}
</SelectContent> </SelectContent>
</Select> </Select>
) : a.type === "SEND_EMAIL" ? ( ) : a.type === "SEND_EMAIL" ? (
@ -1040,12 +1116,22 @@ export function AutomationEditorDialog({
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Selecione" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
{(agents ?? []).length === 0 ? (
<SelectEmptyState
message="Nenhum agente disponível"
createLabel="Gerenciar usuários"
createHref="/admin/users"
/>
) : (
<>
<SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem> <SelectItem value={CLEAR_SELECT_VALUE}>Nenhum</SelectItem>
{(agents ?? []).map((u) => ( {(agents ?? []).map((u) => (
<SelectItem key={u._id} value={String(u._id)}> <SelectItem key={u._id} value={String(u._id)}>
{u.name} {u.name}
</SelectItem> </SelectItem>
))} ))}
</>
)}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View file

@ -240,13 +240,13 @@ export function AutomationsManager() {
<div className="rounded-3xl border border-slate-200 bg-white/90 shadow-sm overflow-hidden"> <div className="rounded-3xl border border-slate-200 bg-white/90 shadow-sm overflow-hidden">
<Table className="w-full table-fixed"> <Table className="w-full table-fixed">
<colgroup> <colgroup>
<col style={{ width: "24%" }} /> <col style={{ width: "22%" }} />
<col style={{ width: "16%" }} /> <col style={{ width: "15%" }} />
<col style={{ width: "10%" }} /> <col style={{ width: "10%" }} />
<col style={{ width: "7%" }} /> <col style={{ width: "7%" }} />
<col style={{ width: "8%" }} /> <col style={{ width: "11%" }} />
<col style={{ width: "15%" }} /> <col style={{ width: "14%" }} />
<col style={{ width: "13%" }} /> <col style={{ width: "14%" }} />
<col style={{ width: "7%" }} /> <col style={{ width: "7%" }} />
</colgroup> </colgroup>
<TableHeader className="bg-slate-100/80"> <TableHeader className="bg-slate-100/80">

View file

@ -964,8 +964,8 @@ export function NewTicketDialog({
renderValue={(option) => { renderValue={(option) => {
if (!option) return <span className="text-muted-foreground">Selecionar solicitante</span> if (!option) return <span className="text-muted-foreground">Selecionar solicitante</span>
return ( return (
<div className="flex w-full items-center justify-between gap-2"> <div className="flex w-full flex-wrap items-start gap-2">
<div className="flex min-w-0 flex-col"> <div className="flex min-w-0 flex-1 flex-col">
<span className="truncate font-medium text-foreground">{option.label}</span> <span className="truncate font-medium text-foreground">{option.label}</span>
{option.description ? ( {option.description ? (
<span className="truncate text-xs text-muted-foreground">{option.description}</span> <span className="truncate text-xs text-muted-foreground">{option.description}</span>
@ -974,7 +974,7 @@ export function NewTicketDialog({
{selectedCompanyOption && selectedCompanyOption.id !== NO_COMPANY_VALUE ? ( {selectedCompanyOption && selectedCompanyOption.id !== NO_COMPANY_VALUE ? (
<Badge <Badge
variant="outline" variant="outline"
className="hidden shrink-0 rounded-full px-2.5 py-0.5 text-[10px] uppercase tracking-wide text-muted-foreground sm:inline-flex" className="hidden min-w-0 max-w-full truncate rounded-full px-2.5 py-0.5 text-[10px] uppercase tracking-wide text-muted-foreground sm:ml-auto sm:inline-flex sm:max-w-[45%]"
> >
{selectedCompanyOption.name} {selectedCompanyOption.name}
</Badge> </Badge>

View file

@ -200,6 +200,7 @@ export function TicketChecklistCard({
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
size="sm"
className="gap-2" className="gap-2"
onClick={handleCompleteAll} onClick={handleCompleteAll}
disabled={!canComplete || completingAll} disabled={!canComplete || completingAll}
@ -212,7 +213,7 @@ export function TicketChecklistCard({
{canEdit && !isResolved && (templates ?? []).length > 0 ? ( {canEdit && !isResolved && (templates ?? []).length > 0 ? (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Select value={selectedTemplateId} onValueChange={setSelectedTemplateId}> <Select value={selectedTemplateId} onValueChange={setSelectedTemplateId}>
<SelectTrigger className="h-9 w-[220px]"> <SelectTrigger size="sm" className="w-[220px]">
<SelectValue placeholder="Aplicar template..." /> <SelectValue placeholder="Aplicar template..." />
</SelectTrigger> </SelectTrigger>
<SelectContent className="rounded-xl"> <SelectContent className="rounded-xl">
@ -228,7 +229,7 @@ export function TicketChecklistCard({
type="button" type="button"
onClick={handleApplyTemplate} onClick={handleApplyTemplate}
disabled={!selectedTemplateId || applyingTemplate} disabled={!selectedTemplateId || applyingTemplate}
className="h-9" size="sm"
> >
Aplicar Aplicar
</Button> </Button>
@ -342,7 +343,7 @@ export function TicketChecklistCard({
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
className="h-9 px-2 text-xs text-neutral-700 hover:bg-slate-50" className="h-9 px-2 text-xs text-neutral-700 hover:bg-slate-100 hover:text-neutral-900 active:bg-slate-200/70 focus-visible:bg-slate-100"
onClick={async () => { onClick={async () => {
if (!actorId) return if (!actorId) return
try { try {
@ -397,7 +398,7 @@ export function TicketChecklistCard({
value={newText} value={newText}
onChange={(e) => setNewText(e.target.value)} onChange={(e) => setNewText(e.target.value)}
placeholder="Adicionar item do checklist..." placeholder="Adicionar item do checklist..."
className="h-9 flex-1 bg-white" className="h-8 flex-1 bg-white"
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault() e.preventDefault()
@ -405,11 +406,11 @@ export function TicketChecklistCard({
} }
}} }}
/> />
<label className="flex items-center gap-2 text-sm text-neutral-700"> <label className="flex h-8 items-center gap-2 text-sm text-neutral-700">
<Checkbox checked={newRequired} onCheckedChange={(checked) => setNewRequired(Boolean(checked))} /> <Checkbox checked={newRequired} onCheckedChange={(checked) => setNewRequired(Boolean(checked))} />
Obrigatório Obrigatório
</label> </label>
<Button type="button" onClick={handleAdd} disabled={adding} className="h-9 gap-2"> <Button type="button" size="sm" onClick={handleAdd} disabled={adding} className="gap-2">
<Plus className="size-4" /> <Plus className="size-4" />
Adicionar Adicionar
</Button> </Button>
@ -419,4 +420,3 @@ export function TicketChecklistCard({
</Card> </Card>
) )
} }

View file

@ -2,7 +2,8 @@
import * as React from "react" import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select" import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" import { CheckIcon, ChevronDownIcon, ChevronUpIcon, PlusCircle } from "lucide-react"
import Link from "next/link"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
@ -171,9 +172,55 @@ function SelectScrollDownButton({
) )
} }
function SelectEmptyState({
message = "Nenhuma opção disponível",
createLabel,
createHref,
onCreateClick,
className,
}: {
message?: string
createLabel?: string
createHref?: string
onCreateClick?: () => void
className?: string
}) {
return (
<div
data-slot="select-empty-state"
className={cn(
"flex flex-col items-center gap-2 px-3 py-4 text-center",
className
)}
>
<p className="text-sm text-neutral-500">{message}</p>
{createLabel && createHref && (
<Link
href={createHref}
className="inline-flex items-center gap-1.5 text-sm font-medium text-[#00b8cc] hover:text-[#009bb1] transition-colors"
>
<PlusCircle className="size-4" />
{createLabel}
</Link>
)}
{createLabel && onCreateClick && !createHref && (
<button
type="button"
onClick={onCreateClick}
className="inline-flex items-center gap-1.5 text-sm font-medium text-[#00b8cc] hover:text-[#009bb1] transition-colors"
>
<PlusCircle className="size-4" />
{createLabel}
</button>
)}
</div>
)
}
export { export {
Select, Select,
SelectContent, SelectContent,
SelectEmptyState,
SelectGroup, SelectGroup,
SelectItem, SelectItem,
SelectLabel, SelectLabel,