- Replace Prisma client runtime with Drizzle ORM (zero Prisma engine/query engine in production) - Add Prisma-compatible facade (@/lib/prisma-facade.ts) backed by Drizzle for backwards compatibility - Runtime queries route through Drizzle ORM; @prisma/client is now devDependency (types only) - Remove @prisma/adapter-mariadb dependency; delete prisma-pool.ts and types/prisma.ts - New Drizzle schema layer: src/db/schema.ts (176 tables) and src/lib/db.ts (connection) - Update README documenting the dual-layer ORM architecture - Restore src/generated/ gitignore (build artifact for local type generation) - 0 TypeScript errors, 583 tests passing The facade intentionally uses `any` types to match the Prisma Client API surface, allowing existing code to run unmodified while routing queries through Drizzle at runtime.
143 lines
4.2 KiB
TypeScript
143 lines
4.2 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Prisma } from "@/lib/prisma";
|
|
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { getWatchedUserIds } from "@/lib/services/watch";
|
|
import { UserAccountInfo } from "../../_components/user-account-info";
|
|
import { UserHero } from "../../_components/user-hero";
|
|
import { loadUserById } from "../../_lib/load-user";
|
|
import { UserActivityLog } from "../../[id]/user-activity-log";
|
|
import { UserBadgesSection } from "../../[id]/user-badges-section";
|
|
import { UserBanSection } from "../../[id]/user-ban-section";
|
|
import { UserEditForm } from "../../[id]/user-edit-form";
|
|
import { UserQuickActions } from "../../[id]/user-quick-actions";
|
|
import { UserRooms } from "../../[id]/user-rooms";
|
|
|
|
export default async function UserEditPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ locale: string; id: string }>;
|
|
}) {
|
|
const { id } = await params;
|
|
const { session, permissions } = await getAdminContext();
|
|
if (!canAccess(permissions, PERMS.USERS_EDIT, session.user.rank)) {
|
|
redirect("/admin");
|
|
}
|
|
|
|
const data = await loadUserById(id);
|
|
const watched = await getWatchedUserIds(session.user.id);
|
|
|
|
// Fetch rank list from permissions table for the rank dropdown
|
|
const rawRanks: { id: number; rank_name: string }[] = await prisma.$queryRaw(
|
|
Prisma.sql`SELECT id, rank_name FROM permissions ORDER BY id ASC`,
|
|
);
|
|
const ranks = rawRanks.map((r) => ({
|
|
id: Number(r.id),
|
|
name: String(r.rank_name || `Rank ${r.id}`),
|
|
}));
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<UserHero
|
|
user={data.user}
|
|
diamonds={data.diamonds}
|
|
duckets={data.duckets}
|
|
roomCount={data.roomCount}
|
|
chatCount={data.chatCount}
|
|
tradeCount={data.tradeCount}
|
|
isOnline={data.isOnline}
|
|
activeBan={!!data.activeBan}
|
|
mode="edit"
|
|
canEdit={true}
|
|
watching={watched.has(data.user.id)}
|
|
/>
|
|
|
|
<div className="grid gap-6 lg:grid-cols-[1fr_320px] xl:grid-cols-[1fr_360px]">
|
|
<Tabs defaultValue="edit" className="space-y-4 min-w-0">
|
|
<TabsList className="flex-wrap">
|
|
<TabsTrigger value="edit">Edit Profile</TabsTrigger>
|
|
<TabsTrigger value="bans">
|
|
Bans
|
|
{data.activeBan && (
|
|
<span className="ml-1.5 flex h-2 w-2 rounded-full bg-destructive" />
|
|
)}
|
|
</TabsTrigger>
|
|
<TabsTrigger value="badges">
|
|
Badges ({data.user.badges.length})
|
|
</TabsTrigger>
|
|
<TabsTrigger value="rooms">Rooms ({data.roomCount})</TabsTrigger>
|
|
<TabsTrigger value="activity">Activity</TabsTrigger>
|
|
<TabsTrigger value="info">Account Info</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="edit">
|
|
<UserEditForm
|
|
user={{
|
|
id: data.user.id,
|
|
username: data.user.username,
|
|
mail: data.user.mail,
|
|
rank: data.user.rank,
|
|
motto: data.user.motto,
|
|
credits: data.user.credits,
|
|
pixels: data.user.pixels,
|
|
diamonds: data.diamonds,
|
|
duckets: data.duckets,
|
|
}}
|
|
ranks={ranks}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="bans">
|
|
<UserBanSection
|
|
userId={data.user.id}
|
|
username={data.user.username}
|
|
bans={data.user.bansReceived.map((b) => ({
|
|
id: b.id,
|
|
reason: b.banReason,
|
|
type: b.type,
|
|
staffName: b.staff.username,
|
|
timestamp: b.timestamp,
|
|
banExpire: b.banExpire,
|
|
}))}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="badges">
|
|
<UserBadgesSection
|
|
userId={data.user.id}
|
|
badges={data.user.badges.map((b) => ({
|
|
id: b.id,
|
|
badgeCode: b.badgeCode,
|
|
}))}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="rooms">
|
|
<UserRooms rooms={data.userRooms} />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="activity">
|
|
<UserActivityLog
|
|
chatLogs={data.chatLogs}
|
|
commandLogs={data.commandLogs}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="info">
|
|
<UserAccountInfo user={data.user} />
|
|
</TabsContent>
|
|
</Tabs>
|
|
|
|
<aside className="lg:sticky lg:top-4 lg:self-start">
|
|
<UserQuickActions
|
|
userId={data.user.id}
|
|
username={data.user.username}
|
|
isOnline={data.isOnline}
|
|
/>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|