feat: bootstrap taller-turnos project
Estructura inicial con Next.js 14, PostgreSQL/Prisma 7, autenticación por magic link (clientes) y JWT (admins), lógica de scheduling con 4 slots paralelos y API routes completas para cliente y panel admin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const autos = await prisma.auto.findMany({
|
||||
where: { clienteId: sesion.sub },
|
||||
orderBy: { createdAt: "desc" },
|
||||
});
|
||||
|
||||
return NextResponse.json(autos);
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { patente, marca, modelo, anio } = await req.json();
|
||||
if (!patente || !marca || !modelo || !anio)
|
||||
return NextResponse.json({ error: "Todos los campos son requeridos" }, { status: 400 });
|
||||
|
||||
const existe = await prisma.auto.findUnique({ where: { patente: patente.toUpperCase() } });
|
||||
if (existe) return NextResponse.json({ error: "Patente ya registrada" }, { status: 409 });
|
||||
|
||||
const auto = await prisma.auto.create({
|
||||
data: {
|
||||
clienteId: sesion.sub,
|
||||
patente: patente.toUpperCase().trim(),
|
||||
marca,
|
||||
modelo,
|
||||
anio: Number(anio),
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(auto, { status: 201 });
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
const autoId = searchParams.get("autoId");
|
||||
if (!autoId) return NextResponse.json({ error: "autoId requerido" }, { status: 400 });
|
||||
|
||||
// Verificar que el auto pertenece al cliente
|
||||
const auto = await prisma.auto.findFirst({ where: { id: autoId, clienteId: sesion.sub } });
|
||||
if (!auto) return NextResponse.json({ error: "Auto no encontrado" }, { status: 404 });
|
||||
|
||||
const historial = await prisma.historialServicio.findMany({
|
||||
where: { autoId },
|
||||
include: { admin: { select: { nombre: true } }, tipoServicio: { select: { nombre: true } } },
|
||||
orderBy: { fechaRealizado: "desc" },
|
||||
});
|
||||
|
||||
return NextResponse.json(historial);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { email, nombre, apellido, telefono } = await req.json();
|
||||
|
||||
if (!email || !nombre || !apellido || !telefono)
|
||||
return NextResponse.json({ error: "Todos los campos son requeridos" }, { status: 400 });
|
||||
|
||||
const existe = await prisma.cliente.findUnique({ where: { email: email.toLowerCase() } });
|
||||
if (existe) return NextResponse.json({ error: "El email ya está registrado" }, { status: 409 });
|
||||
|
||||
const cliente = await prisma.cliente.create({
|
||||
data: { email: email.toLowerCase().trim(), nombre, apellido, telefono },
|
||||
});
|
||||
|
||||
return NextResponse.json({ ok: true, id: cliente.id }, { status: 201 });
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
import { obtenerSlotsDisponibles } from "@/lib/scheduling";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
const tipoServicioId = searchParams.get("tipoServicioId");
|
||||
if (!tipoServicioId)
|
||||
return NextResponse.json({ error: "tipoServicioId requerido" }, { status: 400 });
|
||||
|
||||
const servicio = await prisma.tipoServicio.findUnique({ where: { id: tipoServicioId } });
|
||||
if (!servicio || !servicio.activo)
|
||||
return NextResponse.json({ error: "Servicio no encontrado" }, { status: 404 });
|
||||
|
||||
const slots = await obtenerSlotsDisponibles(servicio.duracionHoras);
|
||||
return NextResponse.json(slots);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
import { slotEstaDisponible } from "@/lib/scheduling";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const turnos = await prisma.turno.findMany({
|
||||
where: { clienteId: sesion.sub },
|
||||
include: { auto: true, tipoServicio: true },
|
||||
orderBy: { fechaInicio: "desc" },
|
||||
});
|
||||
|
||||
return NextResponse.json(turnos);
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { autoId, tipoServicioId, fechaInicio } = await req.json();
|
||||
if (!autoId || !tipoServicioId || !fechaInicio)
|
||||
return NextResponse.json({ error: "Datos incompletos" }, { status: 400 });
|
||||
|
||||
// Verificar que el auto pertenece al cliente
|
||||
const auto = await prisma.auto.findFirst({
|
||||
where: { id: autoId, clienteId: sesion.sub },
|
||||
});
|
||||
if (!auto) return NextResponse.json({ error: "Auto no encontrado" }, { status: 404 });
|
||||
|
||||
const servicio = await prisma.tipoServicio.findUnique({ where: { id: tipoServicioId } });
|
||||
if (!servicio || !servicio.activo)
|
||||
return NextResponse.json({ error: "Servicio no disponible" }, { status: 404 });
|
||||
|
||||
const inicio = new Date(fechaInicio);
|
||||
const fin = new Date(inicio.getTime() + servicio.duracionHoras * 60 * 60 * 1000);
|
||||
|
||||
const disponible = await slotEstaDisponible(inicio, fin);
|
||||
if (!disponible)
|
||||
return NextResponse.json({ error: "El horario ya no está disponible" }, { status: 409 });
|
||||
|
||||
const turno = await prisma.turno.create({
|
||||
data: { clienteId: sesion.sub, autoId, tipoServicioId, fechaInicio: inicio, fechaFin: fin },
|
||||
include: { auto: true, tipoServicio: true },
|
||||
});
|
||||
|
||||
return NextResponse.json(turno, { status: 201 });
|
||||
}
|
||||
Reference in New Issue
Block a user