feat(checklist): adiciona opção de desmarcar todos os itens

O botão "Concluir todos" agora alterna para "Desmarcar todos"
quando todos os itens do checklist estão marcados como concluídos.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-15 11:46:13 -03:00
parent 2c21daee79
commit bc5ba0c73a
2 changed files with 57 additions and 12 deletions

View file

@ -2722,6 +2722,34 @@ export const completeAllChecklistItems = mutation({
},
});
export const uncompleteAllChecklistItems = mutation({
args: {
ticketId: v.id("tickets"),
actorId: v.id("users"),
},
handler: async (ctx, { ticketId, actorId }) => {
const ticket = await ctx.db.get(ticketId);
if (!ticket) {
throw new ConvexError("Ticket não encontrado");
}
const ticketDoc = ticket as Doc<"tickets">;
const viewer = await requireTicketStaff(ctx, actorId, ticketDoc);
ensureChecklistEditor(viewer);
const checklist = normalizeTicketChecklist(ticketDoc.checklist);
if (checklist.length === 0) return { ok: true };
const now = Date.now();
const nextChecklist = checklist.map((item) => {
if (item.done === false) return item;
return { ...item, done: false, doneAt: undefined, doneBy: undefined };
});
await ctx.db.patch(ticketId, { checklist: nextChecklist, updatedAt: now });
return { ok: true };
},
});
export const applyChecklistTemplate = mutation({
args: {
ticketId: v.id("tickets"),

View file

@ -2,7 +2,7 @@
import { useMemo, useState } from "react"
import { useMutation, useQuery } from "convex/react"
import { CheckCheck, ListChecks, Plus, Trash2 } from "lucide-react"
import { CheckCheck, ListChecks, Plus, RotateCcw, Trash2 } from "lucide-react"
import { toast } from "sonner"
import { api } from "@/convex/_generated/api"
@ -62,6 +62,7 @@ export function TicketChecklistCard({
const setChecklistItemRequired = useMutation(api.tickets.setChecklistItemRequired)
const removeChecklistItem = useMutation(api.tickets.removeChecklistItem)
const completeAllChecklistItems = useMutation(api.tickets.completeAllChecklistItems)
const uncompleteAllChecklistItems = useMutation(api.tickets.uncompleteAllChecklistItems)
const applyChecklistTemplate = useMutation(api.tickets.applyChecklistTemplate)
const templates = useQuery(
@ -167,20 +168,27 @@ export function TicketChecklistCard({
}
}
const handleCompleteAll = async () => {
const allItemsDone = checklist.length > 0 && allDone === totalItems
const handleToggleAll = async () => {
if (!actorId || !canEdit || isResolved) return
setCompletingAll(true)
try {
if (allItemsDone) {
await uncompleteAllChecklistItems({ ticketId: ticket.id as Id<"tickets">, actorId })
toast.success("Itens desmarcados.")
} else {
await completeAllChecklistItems({ ticketId: ticket.id as Id<"tickets">, actorId })
toast.success("Checklist concluído.")
}
} catch (error) {
toast.error(error instanceof Error ? error.message : "Falha ao concluir checklist.")
toast.error(error instanceof Error ? error.message : "Falha ao atualizar checklist.")
} finally {
setCompletingAll(false)
}
}
const canComplete = checklist.length > 0 && requiredPending > 0 && canEdit && !isResolved
const canToggleAll = checklist.length > 0 && canEdit && !isResolved
const visibleChecklist = useMemo(() => {
return onlyPending ? checklist.filter((item) => !item.done) : checklist
}, [checklist, onlyPending])
@ -220,18 +228,27 @@ export function TicketChecklistCard({
{onlyPending ? "Ver todos" : "Somente pendentes"}
</Button>
) : null}
{canEdit && !isResolved ? (
{canEdit && !isResolved && canToggleAll ? (
<Button
type="button"
variant="outline"
size="sm"
className="gap-2"
onClick={handleCompleteAll}
disabled={!canComplete || completingAll}
title={requiredPending > 0 ? "Marcar todos os itens como concluídos" : "Checklist já está concluído"}
onClick={handleToggleAll}
disabled={completingAll}
title={allItemsDone ? "Desmarcar todos os itens" : "Marcar todos os itens como concluídos"}
>
{allItemsDone ? (
<>
<RotateCcw className="size-4" />
Desmarcar todos
</>
) : (
<>
<CheckCheck className="size-4" />
Concluir todos
</>
)}
</Button>
) : null}
{canEdit && !isResolved && (templates ?? []).length > 0 ? (