feat: adiciona SLA por empresa e modal de exclusao de automacoes
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 8s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m45s
Quality Checks / Lint, Test and Build (push) Successful in 3m58s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 1m17s

## SLA por Empresa
- Adiciona tabela companySlaSettings no schema
- Cria convex/companySlas.ts com queries e mutations
- Modifica resolveTicketSlaSnapshot para verificar SLA da empresa primeiro
- Fallback: empresa > categoria > padrao

## Modal de Exclusao de Automacoes
- Substitui confirm() nativo por Dialog gracioso
- Segue padrao do delete-ticket-dialog

🤖 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-17 18:44:05 -03:00
parent b3fcbcc682
commit 33f0cc2e13
4 changed files with 404 additions and 22 deletions

View file

@ -2,7 +2,7 @@
import { useMemo, useState } from "react"
import { useMutation, useQuery } from "convex/react"
import { History, MoreHorizontal, Pencil, Plus, Trash2 } from "lucide-react"
import { AlertTriangle, History, MoreHorizontal, Pencil, Plus, Trash2 } from "lucide-react"
import { toast } from "sonner"
import { api } from "@/convex/_generated/api"
@ -12,7 +12,7 @@ import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Dialog, DialogTrigger } from "@/components/ui/dialog"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
@ -81,6 +81,9 @@ export function AutomationsManager() {
const [editing, setEditing] = useState<AutomationRow | null>(null)
const [runsOpen, setRunsOpen] = useState(false)
const [runsAutomation, setRunsAutomation] = useState<AutomationRow | null>(null)
const [deleteOpen, setDeleteOpen] = useState(false)
const [deletingAutomation, setDeletingAutomation] = useState<AutomationRow | null>(null)
const [deleteLoading, setDeleteLoading] = useState(false)
const list = useQuery(
api.automations.list,
@ -134,19 +137,27 @@ export function AutomationsManager() {
}
}
const handleDelete = async (row: AutomationRow) => {
if (!convexUserId) return
const ok = confirm(`Excluir a automação "${row.name}"?`)
if (!ok) return
const handleDeleteClick = (row: AutomationRow) => {
setDeletingAutomation(row)
setDeleteOpen(true)
}
const handleDeleteConfirm = async () => {
if (!convexUserId || !deletingAutomation) return
setDeleteLoading(true)
try {
await removeAutomation({
tenantId,
viewerId: convexUserId as Id<"users">,
automationId: row.id,
automationId: deletingAutomation.id,
})
toast.success("Automação excluída")
setDeleteOpen(false)
setDeletingAutomation(null)
} catch (error) {
toast.error(error instanceof Error ? error.message : "Falha ao excluir automação")
} finally {
setDeleteLoading(false)
}
}
@ -340,7 +351,7 @@ export function AutomationsManager() {
<Pencil className="size-4" />
Editar
</DropdownMenuItem>
<DropdownMenuItem variant="destructive" onClick={() => handleDelete(row)} className="gap-2">
<DropdownMenuItem variant="destructive" onClick={() => handleDeleteClick(row)} className="gap-2">
<Trash2 className="size-4" />
Excluir
</DropdownMenuItem>
@ -355,6 +366,31 @@ export function AutomationsManager() {
</div>
)}
</CardContent>
{/* Modal de confirmação de exclusão */}
<Dialog open={deleteOpen} onOpenChange={(open) => {
setDeleteOpen(open)
if (!open) setDeletingAutomation(null)
}}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="size-5" /> Excluir automação
</DialogTitle>
<DialogDescription>
Esta ação é permanente e removerá a automação <span className="font-semibold text-neutral-700">&quot;{deletingAutomation?.name}&quot;</span> e todo o seu histórico de execuções.
</DialogDescription>
</DialogHeader>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" onClick={() => setDeleteOpen(false)} disabled={deleteLoading}>
Cancelar
</Button>
<Button variant="destructive" onClick={handleDeleteConfirm} disabled={deleteLoading} className="gap-2">
{deleteLoading ? "Excluindo..." : (<><Trash2 className="size-4" /> Excluir</>)}
</Button>
</div>
</DialogContent>
</Dialog>
</Card>
)
}