feat(ui): priority dropdown badge, delete ticket modal, Empty state component; Convex mutations (updatePriority/remove); layout polish
- PrioritySelect with translucent badge + Select options - DeleteTicketDialog with confirmation and icons - Empty UI primitives and basic usage (kept simple to ensure build) - Convex: tickets.updatePriority & tickets.remove - Header: integrate priority select, delete action, avatar-rich assignee select
This commit is contained in:
parent
ea60c3b841
commit
97ca2b3b54
5 changed files with 222 additions and 3 deletions
67
web/src/components/tickets/priority-select.tsx
Normal file
67
web/src/components/tickets/priority-select.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useMutation } from "convex/react"
|
||||
// @ts-ignore
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
import type { TicketStatus } from "@/lib/schemas/ticket"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { toast } from "sonner"
|
||||
|
||||
const labels: Record<string, string> = {
|
||||
LOW: "Baixa",
|
||||
MEDIUM: "Média",
|
||||
HIGH: "Alta",
|
||||
URGENT: "Urgente",
|
||||
}
|
||||
|
||||
function badgeClass(p: string) {
|
||||
switch (p) {
|
||||
case "URGENT":
|
||||
return "bg-red-100 text-red-700"
|
||||
case "HIGH":
|
||||
return "bg-amber-100 text-amber-700"
|
||||
case "MEDIUM":
|
||||
return "bg-blue-100 text-blue-700"
|
||||
default:
|
||||
return "bg-slate-100 text-slate-700"
|
||||
}
|
||||
}
|
||||
|
||||
export function PrioritySelect({ ticketId, value }: { ticketId: Id<"tickets">; value: "LOW" | "MEDIUM" | "HIGH" | "URGENT" }) {
|
||||
const updatePriority = useMutation(api.tickets.updatePriority)
|
||||
const [priority, setPriority] = useState(value)
|
||||
return (
|
||||
<Select
|
||||
value={priority}
|
||||
onValueChange={async (val) => {
|
||||
const prev = priority
|
||||
setPriority(val as typeof priority)
|
||||
toast.loading("Atualizando prioridade...", { id: "prio" })
|
||||
try {
|
||||
await updatePriority({ ticketId, priority: val as any, actorId: undefined as unknown as Id<"users"> })
|
||||
toast.success("Prioridade atualizada!", { id: "prio" })
|
||||
} catch {
|
||||
setPriority(prev)
|
||||
toast.error("Não foi possível atualizar a prioridade.", { id: "prio" })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-7 w-[140px] border-transparent bg-muted/50 px-2">
|
||||
<SelectValue>
|
||||
<Badge className={`rounded-full px-2 py-0.5 ${badgeClass(priority)}`}>{labels[priority]}</Badge>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{(["LOW","MEDIUM","HIGH","URGENT"] as const).map((p) => (
|
||||
<SelectItem key={p} value={p}>
|
||||
<span className="inline-flex items-center gap-2"><span className={`h-2 w-2 rounded-full ${p==="URGENT"?"bg-red-500":p==="HIGH"?"bg-amber-500":p==="MEDIUM"?"bg-blue-500":"bg-slate-400"}`}></span>{labels[p]}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue