Fix admin cleanup hook order and lint setup
This commit is contained in:
parent
37c32149a6
commit
c7aaa60d9a
4 changed files with 1886 additions and 31 deletions
|
|
@ -61,9 +61,9 @@
|
||||||
"pdfkit": "^0.17.2",
|
"pdfkit": "^0.17.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
|
"react-aria-components": "^1.4.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-hook-form": "^7.64.0",
|
"react-hook-form": "^7.64.0",
|
||||||
"react-aria-components": "^1.4.0",
|
|
||||||
"recharts": "^2.15.4",
|
"recharts": "^2.15.4",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
|
|
@ -98,6 +98,7 @@
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"tw-animate-css": "^1.3.8",
|
"tw-animate-css": "^1.3.8",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
|
"typescript-eslint": "^8.46.2",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^4.0.1"
|
"vitest": "^4.0.1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1841
pnpm-lock.yaml
generated
1841
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -10,6 +10,28 @@ export const runtime = "nodejs"
|
||||||
|
|
||||||
const MAX_RESULTS = 10
|
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) {
|
function normalizeRole(role?: string | null) {
|
||||||
return (role ?? "").toLowerCase()
|
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)
|
// Pesquisar pelos tickets visíveis ao viewer (assunto, resumo ou #referência)
|
||||||
let tickets: Array<{
|
let tickets: TicketRecord[] = []
|
||||||
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
|
|
||||||
}> = []
|
|
||||||
try {
|
try {
|
||||||
const res = (await client.query(api.tickets.list, {
|
const res = await client.query(api.tickets.list, {
|
||||||
tenantId,
|
tenantId,
|
||||||
viewerId: viewerId as unknown as Id<"users">,
|
viewerId: viewerId as unknown as Id<"users">,
|
||||||
search: query,
|
search: query,
|
||||||
limit: 40,
|
limit: 40,
|
||||||
})) as any[]
|
})
|
||||||
tickets = Array.isArray(res) ? res : []
|
tickets = Array.isArray(res) ? res.filter(isTicketRecord) : []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[mentions] tickets.list failed", error)
|
console.error("[mentions] tickets.list failed", error)
|
||||||
return NextResponse.json({ items: [] }, { status: 500 })
|
return NextResponse.json({ items: [] }, { status: 500 })
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { useCallback, useEffect, useMemo, useState, useTransition } from "react"
|
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"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
|
|
@ -273,21 +273,6 @@ export function AdminUsersManager({
|
||||||
return Array.from(unique)
|
return Array.from(unique)
|
||||||
}, [normalizedRoles, viewerIsAdmin])
|
}, [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
|
// Split users: team (admin/agent) and people (manager/collaborator); exclude machines
|
||||||
const teamUsers = useMemo(
|
const teamUsers = useMemo(
|
||||||
() => users.filter((user) => user.role !== "machine" && ["admin", "agent"].includes(coerceRole(user.role))),
|
() => 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 [cleanupKeepEmails, setCleanupKeepEmails] = useState(DEFAULT_KEEP_EMAILS.join(", "))
|
||||||
const [cleanupPending, setCleanupPending] = useState(false)
|
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)
|
// Máquinas (para listar vínculos por usuário)
|
||||||
type MachinesListItem = {
|
type MachinesListItem = {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue