From e7682e825a0fac4376ad2af2bd33234e826ad401 Mon Sep 17 00:00:00 2001 From: TravisDev5x Date: Thu, 11 Jun 2026 20:12:43 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20User.id=20como=20UUID=20y=20unificar=20?= =?UTF-8?q?campos=20de=20Role=20a=20ingl=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User.id pasa de Int autoincrement a String UUID (@db.Uuid) para no exponer IDs secuenciales. - Role.nombre/descripcion/creadoEn -> name/description/createdAt. - User.roleId ahora es requerido con default 2 (Cliente) y relación role con onDelete: Restrict; se agrega @@index([roleId]). - Actualiza schema, prisma.config.ts, seeds, validaciones, rutas API de roles/users/auth y las páginas del dashboard (usuarios/roles) acorde a los nuevos nombres y tipos. Co-Authored-By: Claude Sonnet 4.6 --- app/api/auth/register/route.ts | 2 +- app/api/roles/[id]/route.ts | 4 +- app/api/roles/route.ts | 8 ++-- app/api/users/[id]/route.ts | 8 ++-- app/dashboard/page.tsx | 6 +-- app/dashboard/roles/page.tsx | 42 +++++++++---------- app/dashboard/usuarios/page.tsx | 8 ++-- lib/auth.config.ts | 8 ++-- lib/auth.ts | 2 +- lib/validations.ts | 9 ++-- prisma.config.ts | 4 +- .../migration.sql | 30 +++++++++++++ prisma/schema.prisma | 14 ++++--- prisma/seed-roles.ts | 8 ++-- prisma/seed.ts | 2 + 15 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 prisma/migrations/20260612020953_uuid_pk_and_role_i18n/migration.sql diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts index c2d0687..4f183c1 100644 --- a/app/api/auth/register/route.ts +++ b/app/api/auth/register/route.ts @@ -33,7 +33,7 @@ export async function POST(request: Request) { ) } - const clienteRole = await prisma.role.findUnique({ where: { nombre: "Cliente" } }) + const clienteRole = await prisma.role.findUnique({ where: { name: "Cliente" } }) const hashed = await bcrypt.hash(password, 12) diff --git a/app/api/roles/[id]/route.ts b/app/api/roles/[id]/route.ts index 204a775..919be41 100644 --- a/app/api/roles/[id]/route.ts +++ b/app/api/roles/[id]/route.ts @@ -28,8 +28,8 @@ export async function PUT(request: Request, { params }: RouteContext) { const rolActualizado = await prisma.role.update({ where: { id: parsedId.data }, data: { - nombre: parsed.data.nombre, - descripcion: parsed.data.descripcion, + name: parsed.data.name, + description: parsed.data.description, }, }); diff --git a/app/api/roles/route.ts b/app/api/roles/route.ts index f088181..15d980e 100644 --- a/app/api/roles/route.ts +++ b/app/api/roles/route.ts @@ -20,8 +20,8 @@ export async function GET(request: Request) { const where: Prisma.RoleWhereInput = {}; if (search !== '') { where.OR = [ - { nombre: { contains: search } }, - { descripcion: { contains: search } }, + { name: { contains: search } }, + { description: { contains: search } }, ]; } @@ -59,8 +59,8 @@ export async function POST(request: Request) { const nuevoRol = await prisma.role.create({ data: { - nombre: parsed.data.nombre, - descripcion: parsed.data.descripcion, + name: parsed.data.name, + description: parsed.data.description, }, }); diff --git a/app/api/users/[id]/route.ts b/app/api/users/[id]/route.ts index 73d0ca2..0148691 100644 --- a/app/api/users/[id]/route.ts +++ b/app/api/users/[id]/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from 'next/server'; import prisma from '../../../../lib/prisma'; export const runtime = 'nodejs' -import { updateUserSchema, idParamSchema } from '../../../../lib/validations'; +import { updateUserSchema, userIdParamSchema } from '../../../../lib/validations'; import { requireAdmin } from '../../../../lib/api-auth'; type RouteContext = { params: Promise<{ id: string }> }; @@ -14,7 +14,7 @@ export async function PUT(request: Request, { params }: RouteContext) { try { const { id } = await params; - const parsedId = idParamSchema.safeParse(id); + const parsedId = userIdParamSchema.safeParse(id); if (!parsedId.success) { return NextResponse.json({ error: "ID inválido" }, { status: 400 }); } @@ -45,7 +45,7 @@ export async function DELETE(request: Request, { params }: RouteContext) { try { const { id } = await params; - const parsedId = idParamSchema.safeParse(id); + const parsedId = userIdParamSchema.safeParse(id); if (!parsedId.success) { return NextResponse.json({ error: "ID inválido" }, { status: 400 }); } @@ -68,7 +68,7 @@ export async function PATCH(request: Request, { params }: RouteContext) { try { const { id } = await params; - const parsedId = idParamSchema.safeParse(id); + const parsedId = userIdParamSchema.safeParse(id); if (!parsedId.success) { return NextResponse.json({ error: "ID inválido" }, { status: 400 }); } diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index b2f668b..c73d59c 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -78,14 +78,14 @@ export default async function DashboardPage() { const [totalUsuarios, totalRoles, usuariosActivos, usuariosPendientes, nuevosClientes, recientes] = await Promise.all([ - prisma.user.count({ where: { role: { nombre: "Cliente" }, deletedAt: null } }), + prisma.user.count({ where: { role: { name: "Cliente" }, deletedAt: null } }), prisma.role.count(), prisma.user.count({ where: { emailVerified: true, deletedAt: null } }), prisma.user.count({ where: { emailVerified: false, deletedAt: null } }), prisma.user.count({ where: { createdAt: { gte: sieteDiasAtras }, - role: { nombre: "Cliente" }, + role: { name: "Cliente" }, deletedAt: null, }, }), @@ -196,7 +196,7 @@ export default async function DashboardPage() { {usuario.email} - {usuario.role?.nombre ?? "Sin rol"} + {usuario.role?.name ?? "Sin rol"} {dateFormatter.format(usuario.createdAt)} diff --git a/app/dashboard/roles/page.tsx b/app/dashboard/roles/page.tsx index abbae80..8ca2149 100644 --- a/app/dashboard/roles/page.tsx +++ b/app/dashboard/roles/page.tsx @@ -33,10 +33,10 @@ import { } from "@/components/ui/pagination"; import { toast } from "sonner"; -type Role = { id: number; nombre: string; descripcion: string | null; creadoEn: string }; -type RoleForm = { nombre: string; descripcion: string }; -type EditingRole = { id: number; nombre: string; descripcion: string } | null; -type ConfirmDelete = { id: number; nombre: string } | null; +type Role = { id: number; name: string; description: string | null; createdAt: string }; +type RoleForm = { name: string; description: string }; +type EditingRole = { id: number; name: string; description: string } | null; +type ConfirmDelete = { id: number; name: string } | null; const LIMIT = 5; @@ -50,7 +50,7 @@ const getPageNumbers = (current: number, total: number): (number | "ellipsis")[] return pages; }; -const emptyForm: RoleForm = { nombre: "", descripcion: "" }; +const emptyForm: RoleForm = { name: "", description: "" }; export default function RolesPage() { const [roles, setRoles] = useState([]); @@ -115,7 +115,7 @@ export default function RolesPage() { const res = await fetch(`/api/roles/${editingRole.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ nombre: editingRole.nombre, descripcion: editingRole.descripcion }), + body: JSON.stringify({ name: editingRole.name, description: editingRole.description }), }); if (!res.ok) { const data = await res.json(); @@ -181,10 +181,10 @@ export default function RolesPage() { roles.map((role) => ( {role.id} - {role.nombre} - {role.descripcion || "—"} + {role.name} + {role.description || "—"} - {new Date(role.creadoEn).toLocaleDateString("es-MX")} + {new Date(role.createdAt).toLocaleDateString("es-MX")}
@@ -195,8 +195,8 @@ export default function RolesPage() { onClick={() => setEditingRole({ id: role.id, - nombre: role.nombre, - descripcion: role.descripcion ?? "", + name: role.name, + description: role.description ?? "", }) } > @@ -207,7 +207,7 @@ export default function RolesPage() { size="icon-sm" aria-label="Eliminar" className="hover:text-destructive" - onClick={() => setConfirmDelete({ id: role.id, nombre: role.nombre })} + onClick={() => setConfirmDelete({ id: role.id, name: role.name })} > @@ -274,8 +274,8 @@ export default function RolesPage() { setCreateForm((f) => ({ ...f, nombre: e.target.value }))} + value={createForm.name} + onChange={(e) => setCreateForm((f) => ({ ...f, name: e.target.value }))} required />
@@ -283,8 +283,8 @@ export default function RolesPage() {