"use client" import { useMemo, useState } from "react" import { useMutation, useQuery } from "convex/react" import { toast } from "sonner" import { IconInbox, IconHierarchy2, IconLink, IconPlus } from "@tabler/icons-react" import { api } from "@/convex/_generated/api" import type { Id } from "@/convex/_generated/dataModel" import { useAuth } from "@/lib/auth-client" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" import { Skeleton } from "@/components/ui/skeleton" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { useDefaultQueues } from "@/hooks/use-default-queues" type Queue = { id: string name: string slug: string team: { id: string; name: string } | null } type TeamOption = { id: string name: string } export function QueuesManager() { const { session, convexUserId } = useAuth() const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID useDefaultQueues(tenantId) const NO_TEAM_VALUE = "__none__" const queues = useQuery( api.queues.list, convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip" ) as Queue[] | undefined const teams = useQuery( api.teams.list, convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip" ) as TeamOption[] | undefined const createQueue = useMutation(api.queues.create) const updateQueue = useMutation(api.queues.update) const removeQueue = useMutation(api.queues.remove) const [name, setName] = useState("") const [teamId, setTeamId] = useState() const [editingQueue, setEditingQueue] = useState(null) const [saving, setSaving] = useState(false) const totalQueues = queues?.length ?? 0 const withoutTeam = useMemo(() => { if (!queues) return 0 return queues.filter((queue) => !queue.team).length }, [queues]) const handleCreate = async (event: React.FormEvent) => { event.preventDefault() if (!name.trim()) { toast.error("Informe o nome da fila") return } if (!convexUserId) { toast.error("Sessão não sincronizada com o Convex") return } setSaving(true) toast.loading("Criando fila...", { id: "queue" }) try { await createQueue({ tenantId, actorId: convexUserId as Id<"users">, name: name.trim(), teamId: teamId as Id<"teams"> | undefined, }) setName("") setTeamId(undefined) toast.success("Fila criada", { id: "queue" }) } catch (error) { console.error(error) toast.error("Não foi possível criar a fila", { id: "queue" }) } finally { setSaving(false) } } const openEdit = (queue: Queue) => { setEditingQueue(queue) setName(queue.name) setTeamId(queue.team?.id) } const handleUpdate = async () => { if (!editingQueue) return if (!name.trim()) { toast.error("Informe o nome da fila") return } if (!convexUserId) { toast.error("Sessão não sincronizada com o Convex") return } setSaving(true) toast.loading("Salvando alterações...", { id: "queue-edit" }) try { await updateQueue({ queueId: editingQueue.id as Id<"queues">, tenantId, actorId: convexUserId as Id<"users">, name: name.trim(), teamId: (teamId ?? undefined) as Id<"teams"> | undefined, }) toast.success("Fila atualizada", { id: "queue-edit" }) setEditingQueue(null) setName("") setTeamId(undefined) } catch (error) { console.error(error) toast.error("Não foi possível atualizar a fila", { id: "queue-edit" }) } finally { setSaving(false) } } const handleRemove = async (queue: Queue) => { const confirmed = window.confirm(`Remover a fila ${queue.name}?`) if (!confirmed) return if (!convexUserId) { toast.error("Sessão não sincronizada com o Convex") return } toast.loading("Removendo fila...", { id: `queue-remove-${queue.id}` }) try { await removeQueue({ tenantId, queueId: queue.id as Id<"queues">, actorId: convexUserId as Id<"users"> }) toast.success("Fila removida", { id: `queue-remove-${queue.id}` }) } catch (error) { console.error(error) toast.error("Não foi possível remover a fila", { id: `queue-remove-${queue.id}` }) } } return (
Filas criadas Rotas que recebem tickets dos canais conectados. {queues ? totalQueues : } Com time definido Filas com time responsável atribuído. {queues ? totalQueues - withoutTeam : } Sem vinculação Filas aguardando responsáveis. {queues ? withoutTeam : }
Nova fila Defina as filas de atendimento, conectando-as aos times responsáveis.
setName(event.target.value)} required />
{queues === undefined ? ( Array.from({ length: 4 }).map((_, index) => ) ) : queues.length === 0 ? ( Nenhuma fila cadastrada Crie filas para segmentar os atendimentos por canal ou especialidade. ) : ( queues.map((queue) => (
{queue.name} Slug: {queue.slug}
Time: {queue.team ? ( {queue.team.name} ) : ( Sem time vinculado )}
)) )}
(!value ? setEditingQueue(null) : null)}> Editar fila
setName(event.target.value)} autoFocus />
) }