Fix admin cleanup hook order and lint setup

This commit is contained in:
Esdras Renan 2025-10-24 09:21:31 -03:00
parent 37c32149a6
commit c7aaa60d9a
4 changed files with 1886 additions and 31 deletions

View file

@ -10,6 +10,28 @@ export const runtime = "nodejs"
const MAX_RESULTS = 10
type TicketRecord = {
id: string | number
reference: string | number
subject?: string | null
status?: string | null
priority?: string | null
requester?: { name?: string | null } | null
assignee?: { name?: string | null } | null
company?: { name?: string | null } | null
updatedAt?: number | null
}
function isTicketRecord(value: unknown): value is TicketRecord {
if (!value || typeof value !== "object") {
return false
}
const record = value as Record<string, unknown>
const hasValidId = typeof record.id === "string" || typeof record.id === "number"
const hasValidReference = typeof record.reference === "string" || typeof record.reference === "number"
return hasValidId && hasValidReference
}
function normalizeRole(role?: string | null) {
return (role ?? "").toLowerCase()
}
@ -59,25 +81,15 @@ export async function GET(request: Request) {
}
// Pesquisar pelos tickets visíveis ao viewer (assunto, resumo ou #referência)
let tickets: Array<{
id: string
reference: number
subject: string
status: string
priority: string
requester?: { name?: string | null } | null
assignee?: { name?: string | null } | null
company?: { name?: string | null } | null
updatedAt?: number | null
}> = []
let tickets: TicketRecord[] = []
try {
const res = (await client.query(api.tickets.list, {
const res = await client.query(api.tickets.list, {
tenantId,
viewerId: viewerId as unknown as Id<"users">,
search: query,
limit: 40,
})) as any[]
tickets = Array.isArray(res) ? res : []
})
tickets = Array.isArray(res) ? res.filter(isTicketRecord) : []
} catch (error) {
console.error("[mentions] tickets.list failed", error)
return NextResponse.json({ items: [] }, { status: 500 })

View file

@ -2,7 +2,7 @@
import Link from "next/link"
import { useCallback, useEffect, useMemo, useState, useTransition } from "react"
import { IconSearch, IconUserPlus, IconTrash } from "@tabler/icons-react"
import { IconSearch, IconUserPlus, IconTrash, IconAlertTriangle } from "@tabler/icons-react"
import { toast } from "sonner"
@ -273,21 +273,6 @@ export function AdminUsersManager({
return Array.from(unique)
}, [normalizedRoles, viewerIsAdmin])
const buildKeepEmailSet = useCallback(() => {
const keep = new Set<string>()
DEFAULT_KEEP_EMAILS.forEach((email) => keep.add(email.toLowerCase()))
if (viewerEmail) {
keep.add(viewerEmail)
}
cleanupKeepEmails
.split(",")
.map((entry) => entry.trim().toLowerCase())
.filter(Boolean)
.forEach((email) => keep.add(email))
return keep
}, [cleanupKeepEmails, viewerEmail])
const cleanupPreview = useMemo(() => Array.from(buildKeepEmailSet()).join(", "), [buildKeepEmailSet])
// Split users: team (admin/agent) and people (manager/collaborator); exclude machines
const teamUsers = useMemo(
() => users.filter((user) => user.role !== "machine" && ["admin", "agent"].includes(coerceRole(user.role))),
@ -331,6 +316,22 @@ export function AdminUsersManager({
const [cleanupKeepEmails, setCleanupKeepEmails] = useState(DEFAULT_KEEP_EMAILS.join(", "))
const [cleanupPending, setCleanupPending] = useState(false)
const buildKeepEmailSet = useCallback(() => {
const keep = new Set<string>()
DEFAULT_KEEP_EMAILS.forEach((email) => keep.add(email.toLowerCase()))
if (viewerEmail) {
keep.add(viewerEmail)
}
cleanupKeepEmails
.split(",")
.map((entry) => entry.trim().toLowerCase())
.filter(Boolean)
.forEach((email) => keep.add(email))
return keep
}, [cleanupKeepEmails, viewerEmail])
const cleanupPreview = useMemo(() => Array.from(buildKeepEmailSet()).join(", "), [buildKeepEmailSet])
// Máquinas (para listar vínculos por usuário)
type MachinesListItem = {
id: string