feat: melhorias de UX e redesign de comentários

- Corrige sincronização do avatar no perfil após upload
- Reduz tamanho dos ícones de câmera/lixeira no avatar
- Remove atributos title (tooltips nativos) de toda aplicação
- Adiciona regra no AGENTS.md sobre uso de tooltips
- Permite desmarcar resposta no checklist (toggle)
- Torna campo answer opcional na mutation setChecklistItemAnswer
- Adiciona edição inline dos campos de resumo no painel de detalhes
- Redesenha comentários com layout mais limpo e consistente
- Cria tratamento especial para comentários automáticos de sistema
- Aplica fundo ciano semi-transparente em comentários públicos
- Corrige import do Loader2 no notification-preferences-form

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-15 22:05:27 -03:00
parent 23ea426c68
commit 022e1f63ba
17 changed files with 636 additions and 180 deletions

View file

@ -16,7 +16,6 @@ import { Checkbox } from "@/components/ui/checkbox"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Progress } from "@/components/ui/progress"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
@ -391,38 +390,39 @@ export function TicketChecklistCard({
)}
{isQuestion && options.length > 0 && (
<RadioGroup
value={item.answer ?? ""}
onValueChange={async (value) => {
if (!actorId || !canToggle) return
try {
await setChecklistItemAnswer({
ticketId: ticket.id as Id<"tickets">,
actorId,
itemId: item.id,
answer: value,
})
} catch (error) {
toast.error(error instanceof Error ? error.message : "Falha ao responder pergunta.")
}
}}
disabled={!canToggle || !actorId}
className="flex flex-wrap items-center gap-4"
>
{options.map((option) => (
<label
key={option}
className={`flex cursor-pointer items-center gap-2 rounded-lg border px-3 py-1.5 text-sm transition-colors ${
item.answer === option
? "border-neutral-900 bg-neutral-900 text-white"
: "border-slate-200 bg-white text-slate-600 hover:border-slate-300 hover:bg-slate-50"
}`}
>
<RadioGroupItem value={option} className="sr-only" />
{option}
</label>
))}
</RadioGroup>
<div className="flex flex-wrap items-center gap-2">
{options.map((option) => {
const isSelected = item.answer === option
return (
<button
key={option}
type="button"
disabled={!canToggle || !actorId}
onClick={async () => {
if (!actorId || !canToggle) return
try {
await setChecklistItemAnswer({
ticketId: ticket.id as Id<"tickets">,
actorId,
itemId: item.id,
// Toggle: se já está selecionado, limpa; senão, seleciona
answer: isSelected ? undefined : option,
})
} catch (error) {
toast.error(error instanceof Error ? error.message : "Falha ao responder pergunta.")
}
}}
className={`rounded-lg border px-3 py-1.5 text-sm transition-colors ${
isSelected
? "border-neutral-900 bg-neutral-900 text-white"
: "border-slate-200 bg-white text-slate-600 hover:border-slate-300 hover:bg-slate-50"
} ${!canToggle || !actorId ? "cursor-not-allowed opacity-50" : "cursor-pointer"}`}
>
{option}
</button>
)
})}
</div>
)}
<div className="flex flex-wrap items-center gap-2">
@ -476,7 +476,6 @@ export function TicketChecklistCard({
variant="ghost"
size="icon"
className="h-9 w-9 text-slate-500 hover:bg-red-50 hover:text-red-700"
title="Remover"
onClick={() => setDeleteTarget(item)}
>
<Trash2 className="size-4" />