feat: migrate auth stack and admin portal

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
esdrasrenan 2025-10-05 17:25:57 -03:00
parent ff674d5bb5
commit 7946b8d017
46 changed files with 2564 additions and 178 deletions

View file

@ -57,7 +57,7 @@ function formatDuration(durationMs: number) {
}
export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const { userId } = useAuth()
const { convexUserId } = useAuth()
const changeAssignee = useMutation(api.tickets.changeAssignee)
const changeQueue = useMutation(api.tickets.changeQueue)
const updateSubject = useMutation(api.tickets.updateSubject)
@ -69,7 +69,12 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const queues = (useQuery(api.queues.summary, { tenantId: ticket.tenantId }) as TicketQueueSummary[] | undefined) ?? []
const { categories, isLoading: categoriesLoading } = useTicketCategories(ticket.tenantId)
const [status] = useState<TicketStatus>(ticket.status)
const workSummaryRemote = useQuery(api.tickets.workSummary, { ticketId: ticket.id as Id<"tickets"> }) as
const workSummaryRemote = useQuery(
api.tickets.workSummary,
convexUserId
? { ticketId: ticket.id as Id<"tickets">, viewerId: convexUserId as Id<"users"> }
: "skip"
) as
| {
ticketId: Id<"tickets">
totalWorkedMs: number
@ -103,14 +108,14 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const secondaryOptions = useMemo(() => activeCategory?.secondary ?? [], [activeCategory])
async function handleSave() {
if (!userId) return
if (!convexUserId) return
toast.loading("Salvando alterações...", { id: "save-header" })
try {
if (subject !== ticket.subject) {
await updateSubject({ ticketId: ticket.id as Id<"tickets">, subject: subject.trim(), actorId: userId as Id<"users"> })
await updateSubject({ ticketId: ticket.id as Id<"tickets">, subject: subject.trim(), actorId: convexUserId as Id<"users"> })
}
if ((summary ?? "") !== (ticket.summary ?? "")) {
await updateSummary({ ticketId: ticket.id as Id<"tickets">, summary: (summary ?? "").trim(), actorId: userId as Id<"users"> })
await updateSummary({ ticketId: ticket.id as Id<"tickets">, summary: (summary ?? "").trim(), actorId: convexUserId as Id<"users"> })
}
toast.success("Cabeçalho atualizado!", { id: "save-header" })
setEditing(false)
@ -170,7 +175,7 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
useEffect(() => {
if (!editing) return
if (!userId) return
if (!convexUserId) return
const categoryId = selectedCategoryId
const subcategoryId = selectedSubcategoryId
if (!categoryId || !subcategoryId) return
@ -200,7 +205,7 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
ticketId: ticket.id as Id<"tickets">,
categoryId: categoryId as Id<"ticketCategories">,
subcategoryId: subcategoryId as Id<"ticketSubcategories">,
actorId: userId as Id<"users">,
actorId: convexUserId as Id<"users">,
})
if (!cancelled) {
toast.success("Categoria atualizada!", { id: "ticket-category" })
@ -225,7 +230,7 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
return () => {
cancelled = true
}
}, [editing, selectedCategoryId, selectedSubcategoryId, ticket.category?.id, ticket.subcategory?.id, ticket.id, updateCategories, userId])
}, [editing, selectedCategoryId, selectedSubcategoryId, ticket.category?.id, ticket.subcategory?.id, ticket.id, updateCategories, convexUserId])
const workSummary = useMemo(() => {
if (workSummaryRemote !== undefined) return workSummaryRemote ?? null
@ -288,19 +293,19 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
size="sm"
className={isPlaying ? pauseButtonClass : startButtonClass}
onClick={async () => {
if (!userId) return
if (!convexUserId) return
toast.dismiss("work")
toast.loading(isPlaying ? "Pausando atendimento..." : "Iniciando atendimento...", { id: "work" })
try {
if (isPlaying) {
const result = await pauseWork({ ticketId: ticket.id as Id<"tickets">, actorId: userId as Id<"users"> })
const result = await pauseWork({ ticketId: ticket.id as Id<"tickets">, actorId: convexUserId as Id<"users"> })
if (result?.status === "already_paused") {
toast.info("O atendimento já estava pausado", { id: "work" })
} else {
toast.success("Atendimento pausado", { id: "work" })
}
} else {
const result = await startWork({ ticketId: ticket.id as Id<"tickets">, actorId: userId as Id<"users"> })
const result = await startWork({ ticketId: ticket.id as Id<"tickets">, actorId: convexUserId as Id<"users"> })
if (result?.status === "already_started") {
toast.info("O atendimento já estava em andamento", { id: "work" })
} else {
@ -418,12 +423,12 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
<Select
value={ticket.queue ?? ""}
onValueChange={async (value) => {
if (!userId) return
if (!convexUserId) return
const queue = queues.find((item) => item.name === value)
if (!queue) return
toast.loading("Atualizando fila...", { id: "queue" })
try {
await changeQueue({ ticketId: ticket.id as Id<"tickets">, queueId: queue.id as Id<"queues">, actorId: userId as Id<"users"> })
await changeQueue({ ticketId: ticket.id as Id<"tickets">, queueId: queue.id as Id<"queues">, actorId: convexUserId as Id<"users"> })
toast.success("Fila atualizada!", { id: "queue" })
} catch {
toast.error("Não foi possível atualizar a fila.", { id: "queue" })
@ -455,10 +460,10 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
<Select
value={ticket.assignee?.id ?? ""}
onValueChange={async (value) => {
if (!userId) return
if (!convexUserId) return
toast.loading("Atribuindo responsável...", { id: "assignee" })
try {
await changeAssignee({ ticketId: ticket.id as Id<"tickets">, assigneeId: value as Id<"users">, actorId: userId as Id<"users"> })
await changeAssignee({ ticketId: ticket.id as Id<"tickets">, assigneeId: value as Id<"users">, actorId: convexUserId as Id<"users"> })
toast.success("Responsável atualizado!", { id: "assignee" })
} catch {
toast.error("Não foi possível atribuir.", { id: "assignee" })