feat(checklist): adiciona tipo pergunta e descricao nos itens
- Adiciona campo `type` (checkbox/question) nos itens do checklist - Adiciona campo `description` para descricao do item - Adiciona campo `options` para opcoes de resposta em perguntas - Adiciona campo `answer` para resposta selecionada - Atualiza UI para mostrar descricao e opcoes de pergunta - Cria componente radio-group para selecao de respostas - Adiciona mutation setChecklistItemAnswer para salvar respostas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
98b23af4b2
commit
0f3ba07a5e
10 changed files with 446 additions and 76 deletions
|
|
@ -15,6 +15,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
|||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
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"
|
||||
|
||||
|
|
@ -60,6 +61,7 @@ export function TicketChecklistCard({
|
|||
const updateChecklistItemText = useMutation(api.tickets.updateChecklistItemText)
|
||||
const setChecklistItemDone = useMutation(api.tickets.setChecklistItemDone)
|
||||
const setChecklistItemRequired = useMutation(api.tickets.setChecklistItemRequired)
|
||||
const setChecklistItemAnswer = useMutation(api.tickets.setChecklistItemAnswer)
|
||||
const removeChecklistItem = useMutation(api.tickets.removeChecklistItem)
|
||||
const completeAllChecklistItems = useMutation(api.tickets.completeAllChecklistItems)
|
||||
const uncompleteAllChecklistItems = useMutation(api.tickets.uncompleteAllChecklistItems)
|
||||
|
|
@ -300,28 +302,36 @@ export function TicketChecklistCard({
|
|||
const required = item.required ?? true
|
||||
const canToggle = canToggleDone && !isResolved
|
||||
const templateLabel = item.templateId ? templateNameById.get(String(item.templateId)) ?? null : null
|
||||
const isQuestion = item.type === "question"
|
||||
const options = item.options ?? []
|
||||
|
||||
return (
|
||||
<div key={item.id} className="flex items-start justify-between gap-3 rounded-xl border border-slate-200 bg-white px-3 py-2">
|
||||
<label className="flex min-w-0 flex-1 items-start gap-3">
|
||||
<Checkbox
|
||||
checked={item.done}
|
||||
disabled={!canToggle || !actorId}
|
||||
onCheckedChange={async (checked) => {
|
||||
if (!actorId || !canToggle) return
|
||||
try {
|
||||
await setChecklistItemDone({
|
||||
ticketId: ticket.id as Id<"tickets">,
|
||||
actorId,
|
||||
itemId: item.id,
|
||||
done: Boolean(checked),
|
||||
})
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : "Falha ao atualizar checklist.")
|
||||
}
|
||||
}}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div className="flex min-w-0 flex-1 items-start gap-3">
|
||||
{isQuestion ? (
|
||||
<div className="mt-1 flex size-5 shrink-0 items-center justify-center rounded-full border border-slate-300 bg-slate-50 text-xs font-medium text-slate-600">
|
||||
?
|
||||
</div>
|
||||
) : (
|
||||
<Checkbox
|
||||
checked={item.done}
|
||||
disabled={!canToggle || !actorId}
|
||||
onCheckedChange={async (checked) => {
|
||||
if (!actorId || !canToggle) return
|
||||
try {
|
||||
await setChecklistItemDone({
|
||||
ticketId: ticket.id as Id<"tickets">,
|
||||
actorId,
|
||||
itemId: item.id,
|
||||
done: Boolean(checked),
|
||||
})
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : "Falha ao atualizar checklist.")
|
||||
}
|
||||
}}
|
||||
className="mt-1"
|
||||
/>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{editingId === item.id && canEdit && !isResolved ? (
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -357,18 +367,57 @@ export function TicketChecklistCard({
|
|||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<p
|
||||
className={`truncate text-sm ${item.done ? "text-neutral-500 line-through" : "text-neutral-900"}`}
|
||||
title={item.text}
|
||||
onDoubleClick={() => {
|
||||
if (!canEdit || isResolved) return
|
||||
setEditingId(item.id)
|
||||
setEditingText(item.text)
|
||||
}}
|
||||
>
|
||||
{item.text}
|
||||
</p>
|
||||
<>
|
||||
<p
|
||||
className={`text-sm ${item.done ? "text-neutral-500 line-through" : "text-neutral-900"}`}
|
||||
title={item.text}
|
||||
onDoubleClick={() => {
|
||||
if (!canEdit || isResolved) return
|
||||
setEditingId(item.id)
|
||||
setEditingText(item.text)
|
||||
}}
|
||||
>
|
||||
{item.text}
|
||||
</p>
|
||||
{item.description && (
|
||||
<p className="mt-0.5 text-xs text-slate-500">
|
||||
{item.description}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{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="mt-2 flex flex-wrap gap-3"
|
||||
>
|
||||
{options.map((option) => (
|
||||
<label
|
||||
key={option}
|
||||
className="flex cursor-pointer items-center gap-1.5 text-sm text-slate-700"
|
||||
>
|
||||
<RadioGroupItem value={option} />
|
||||
{option}
|
||||
</label>
|
||||
))}
|
||||
</RadioGroup>
|
||||
)}
|
||||
|
||||
<div className="mt-1 flex flex-wrap items-center gap-2">
|
||||
{required ? (
|
||||
<Badge variant="secondary" className="rounded-full text-[11px]">
|
||||
|
|
@ -379,6 +428,11 @@ export function TicketChecklistCard({
|
|||
Opcional
|
||||
</Badge>
|
||||
)}
|
||||
{isQuestion && (
|
||||
<Badge variant="outline" className="rounded-full border-cyan-200 bg-cyan-50 text-[11px] text-cyan-700">
|
||||
Pergunta
|
||||
</Badge>
|
||||
)}
|
||||
{templateLabel ? (
|
||||
<Badge variant="outline" className="rounded-full text-[11px]">
|
||||
Template: {templateLabel}
|
||||
|
|
@ -386,7 +440,7 @@ export function TicketChecklistCard({
|
|||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{canEdit && !isResolved ? (
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue