refactor: align routes with next 16 and local fonts

This commit is contained in:
Esdras Renan 2025-10-22 02:08:18 -03:00
parent 2e3b46a7b5
commit dad84d7d0e
16 changed files with 2448 additions and 198 deletions

View file

@ -38,9 +38,16 @@ async function loadUsers() {
},
})
const domainByEmail = new Map(domainUsers.map((user) => [user.email.toLowerCase(), user]))
const domainByEmail = new Map<string, (typeof domainUsers)[number]>(
domainUsers.map(
(user: (typeof domainUsers)[number]): [string, (typeof domainUsers)[number]] => [
user.email.toLowerCase(),
user,
]
)
)
return users.map((user) => {
return users.map((user: (typeof users)[number]) => {
const domain = domainByEmail.get(user.email.toLowerCase())
const normalizedRole = (normalizeRole(user.role) ?? "agent") as RoleOption
return {
@ -71,8 +78,8 @@ async function loadInvites(): Promise<NormalizedInvite[]> {
const now = new Date()
const cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
return invites
.map((invite) => normalizeInvite(invite, now))
.filter((invite) => {
.map((invite: (typeof invites)[number]) => normalizeInvite(invite, now))
.filter((invite: NormalizedInvite) => {
if (invite.status !== "revoked") return true
if (!invite.revokedAt) return true
return new Date(invite.revokedAt) > cutoff

View file

@ -29,14 +29,18 @@ export default async function AdminUsersPage() {
orderBy: { createdAt: "desc" },
})
const emails = users.map((user) => user.email)
const emails = users.map((user: (typeof users)[number]) => user.email)
const authUsers = await prisma.authUser.findMany({
where: { email: { in: emails } },
select: { id: true, email: true, updatedAt: true, createdAt: true },
})
const sessions = await prisma.authSession.findMany({
where: { userId: { in: authUsers.map((auth) => auth.id) } },
where: {
userId: {
in: authUsers.map((auth: (typeof authUsers)[number]) => auth.id),
},
},
orderBy: { updatedAt: "desc" },
select: { userId: true, updatedAt: true },
})
@ -57,7 +61,7 @@ export default async function AdminUsersPage() {
})
}
const accounts: AdminAccount[] = users.map((user) => {
const accounts: AdminAccount[] = users.map((user: (typeof users)[number]) => {
const auth = authByEmail.get(user.email.toLowerCase())
const lastSeenAt = auth ? sessionByUserId.get(auth.id) ?? auth.updatedAt : null
return {

View file

@ -1,2 +1,2 @@
export { runtime } from "../users/route"
export const runtime = "nodejs"
export { GET, DELETE } from "../users/route"

View file

@ -1,5 +1,6 @@
import { NextResponse } from "next/server"
import { ZodError } from "zod"
import { Prisma } from "@prisma/client"
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"
import { prisma } from "@/lib/prisma"
@ -142,7 +143,7 @@ export async function DELETE(
}
try {
const result = await prisma.$transaction(async (tx) => {
const result = await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
const users = await tx.user.updateMany({
where: { companyId: company.id, tenantId: company.tenantId },
data: { companyId: null },

View file

@ -1,5 +1,4 @@
import { NextResponse } from "next/server"
import { Prisma } from "@prisma/client"
import { ConvexHttpClient } from "convex/browser"
@ -124,14 +123,14 @@ export async function PATCH(request: Request, context: { params: Promise<{ id: s
},
})
const event = await prisma.authInviteEvent.create({
data: {
inviteId: invite.id,
type: "revoked",
payload: reason ? { reason } : Prisma.JsonNull,
actorId: session.user.id ?? null,
},
})
const event = await prisma.authInviteEvent.create({
data: {
inviteId: invite.id,
type: "revoked",
payload: reason ? { reason } : Prisma.JsonNull,
actorId: session.user.id ?? null,
},
})
const normalized = normalizeInvite({ ...updated, events: [...invite.events, event] }, now)
await syncInvite(normalized)

View file

@ -1,13 +1,37 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
@font-face {
font-family: "InterVariable";
src: url("/fonts/Inter-VariableFont_opsz,wght.ttf") format("truetype");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "InterVariable";
src: url("/fonts/Inter-Italic-VariableFont_opsz,wght.ttf") format("truetype");
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "JetBrainsMonoVariable";
src: url("/fonts/JetBrainsMono-VariableFont_wght.ttf") format("truetype");
font-weight: 100 800;
font-style: normal;
font-display: swap;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
@ -81,8 +105,8 @@
--sidebar-border: #cbd5e1;
--sidebar-ring: #00d6eb;
--destructive-foreground: oklch(1 0 0);
--font-geist-sans: "Geist Sans", sans-serif;
--font-geist-mono: "Geist Mono", monospace;
--font-geist-sans: "InterVariable", "Inter", sans-serif;
--font-geist-mono: "JetBrainsMonoVariable", "JetBrains Mono", monospace;
}
.dark {

View file

@ -1,22 +1,9 @@
import type { Metadata } from "next"
import { Inter, JetBrains_Mono } from "next/font/google"
import "./globals.css"
import { ConvexClientProvider } from "./ConvexClientProvider"
import { AuthProvider } from "@/lib/auth-client"
import { Toaster } from "@/components/ui/sonner"
const inter = Inter({
subsets: ["latin"],
variable: "--font-geist-sans",
display: "swap",
})
const jetBrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-geist-mono",
display: "swap",
})
export const metadata: Metadata = {
title: "Raven - Sistema de chamados",
description: "Plataforma Raven da Rever",
@ -41,9 +28,7 @@ export default async function RootLayout({
}>) {
return (
<html lang="pt-BR" className="h-full">
<body
className={`${inter.variable} ${jetBrainsMono.variable} min-h-screen bg-background text-foreground antialiased`}
>
<body className="min-h-screen bg-background text-foreground antialiased">
<ConvexClientProvider>
<AuthProvider>
{children}

View file

@ -296,12 +296,23 @@ export function AdminCompaniesManager({ initialCompanies, tenantId }: Props) {
const machines = useQuery(api.machines.listByTenant, {
tenantId: effectiveTenantId,
includeMetadata: false,
}) as Array<Record<string, unknown>> | undefined
}) as unknown[] | undefined
function extractCompanySlug(entry: unknown): string | undefined {
if (!entry || typeof entry !== "object") {
return undefined
}
if ("companySlug" in entry) {
const value = (entry as { companySlug?: unknown }).companySlug
return typeof value === "string" && value.length > 0 ? value : undefined
}
return undefined
}
const machineCountsBySlug = useMemo(() => {
const map: Record<string, number> = {}
;(machines ?? []).forEach((m) => {
const slug = (m as any)?.companySlug as string | undefined
;(machines ?? []).forEach((entry) => {
const slug = extractCompanySlug(entry)
if (!slug) return
map[slug] = (map[slug] ?? 0) + 1
})
@ -934,7 +945,15 @@ function TableView({ companies, machineCountsBySlug, onEdit, onDelete }: TableVi
<PaginationPrevious disabled={pageIndex === 0} onClick={() => setPageIndex((p) => Math.max(0, p - 1))} />
</PaginationItem>
<PaginationItem>
<PaginationLink isActive>{pageIndex + 1}</PaginationLink>
<PaginationLink
href="#"
isActive
onClick={(event) => {
event.preventDefault()
}}
>
{pageIndex + 1}
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext disabled={pageIndex >= pageCount - 1} onClick={() => setPageIndex((p) => Math.min(pageCount - 1, p + 1))} />

View file

@ -8,7 +8,6 @@ import { useAuth } from "@/lib/auth-client"
export function AuthGuard() {
// Podemos, se quisermos, ler isLoading para futuramente exibir um skeleton.
// No momento, não fazemos nada aqui.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { isLoading } = useAuth()
useAuth()
return null
}