refactor: align routes with next 16 and local fonts
This commit is contained in:
parent
2e3b46a7b5
commit
dad84d7d0e
16 changed files with 2448 additions and 198 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export { runtime } from "../users/route"
|
||||
export const runtime = "nodejs"
|
||||
export { GET, DELETE } from "../users/route"
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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))} />
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue