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:
parent
2c21daee79
commit
bc5ba0c73a
2 changed files with 57 additions and 12 deletions
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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 ? (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue