src/app/api/panier/route.ts

route·app·6.4 KB · 190 lignes· Voir l'itinéraire
Annotation non disponible

Lance npm run annotate (nécessite ANTHROPIC_API_KEY dans .env.local) pour générer une annotation française par Claude Haiku 4.5.

2 exports

GETPOST

Code source· typescript

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma/client";
import { getSession } from "@/lib/auth/session";
import { getTenant } from "@/lib/auth/get-tenant";
import { cookies } from "next/headers";

function getSessionId(cookieStore: Awaited<ReturnType<typeof cookies>>) {
  let sessionId = cookieStore.get("panier_session")?.value;
  return sessionId ?? null;
}

export async function GET(req: NextRequest) {
  try {
    const tenant = await getTenant();
    if (!tenant) return NextResponse.json({ error: "Tenant introuvable" }, { status: 404 });

    const session = await getSession();
    const cookieStore = await cookies();
    const sessionId = getSessionId(cookieStore);

    const where = session?.role === "CLIENT"
      ? { tenantId: tenant.id, clientId: session.userId, statut: "ACTIF" as const }
      : sessionId
        ? { tenantId: tenant.id, sessionId, statut: "ACTIF" as const }
        : null;

    if (!where) return NextResponse.json({ panier: null, lignes: [] });

    const panier = await prisma.panier.findFirst({
      where,
      include: {
        lignes: true,
      },
    });

    if (!panier) return NextResponse.json({ panier: null, lignes: [] });

    // Enrichir avec les infos produit
    const produitIds = panier.lignes.map((l) => l.produitId);
    const produits = await prisma.produit.findMany({
      where: { id: { in: produitIds } },
      include: { medias: { orderBy: { ordre: "asc" }, take: 1 } },
    });

    // Récupérer les variantes référencées
    const varianteIds = panier.lignes
      .map((l) => l.varianteId)
      .filter((v): v is string => v !== null);
    const variantes = varianteIds.length
      ? await prisma.varianteProduit.findMany({ where: { id: { in: varianteIds } } })
      : [];

    const lignesEnrichies = panier.lignes.map((ligne) => ({
      ...ligne,
      produit: produits.find((p) => p.id === ligne.produitId) ?? null,
      variante: variantes.find((v) => v.id === ligne.varianteId) ?? null,
    }));

    return NextResponse.json({ panier, lignes: lignesEnrichies });
  } catch (error) {
    console.error("panier GET error:", error);
    return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
  }
}

export async function POST(req: NextRequest) {
  try {
    const tenant = await getTenant();
    if (!tenant) return NextResponse.json({ error: "Tenant introuvable" }, { status: 404 });

    const { produitId, varianteId = null, quantite = 1 } = await req.json();
    if (!produitId) return NextResponse.json({ error: "produitId requis" }, { status: 400 });

    // Vérifier que le produit existe et appartient au tenant
    const produit = await prisma.produit.findFirst({
      where: { id: produitId, tenantId: tenant.id, disponible: true },
    });
    if (!produit) return NextResponse.json({ error: "Produit introuvable" }, { status: 404 });

    // Vérifier variante si fournie
    let prixFinal = produit.prix;
    if (varianteId) {
      const variante = await prisma.varianteProduit.findFirst({
        where: { id: varianteId, produitId },
      });
      if (!variante) return NextResponse.json({ error: "Variante introuvable" }, { status: 404 });
      if (variante.prix !== null && variante.prix !== undefined) prixFinal = variante.prix;
    }

    const session = await getSession();
    const cookieStore = await cookies();
    let sessionId = getSessionId(cookieStore);

    // Créer un sessionId anonyme si besoin
    const response = NextResponse.json({ success: true });
    if (!session && !sessionId) {
      sessionId = crypto.randomUUID();
      response.cookies.set("panier_session", sessionId, {
        httpOnly: true,
        sameSite: "lax",
        maxAge: 60 * 60 * 24 * 30, // 30 jours
        path: "/",
      });
    }

    const panierWhere = session?.role === "CLIENT"
      ? { tenantId: tenant.id, clientId: session.userId, statut: "ACTIF" as const }
      : { tenantId: tenant.id, sessionId: sessionId!, statut: "ACTIF" as const };

    // Trouver ou créer le panier
    // Chercher un panier existant (actif ou non) pour éviter les conflits de contrainte unique
    let panier = await prisma.panier.findFirst({
      where: session?.role === "CLIENT"
        ? { tenantId: tenant.id, clientId: session.userId }
        : { tenantId: tenant.id, sessionId: sessionId! },
    });
    if (!panier) {
      panier = await prisma.panier.create({
        data: {
          tenantId: tenant.id,
          clientId: session?.role === "CLIENT" ? session.userId : null,
          sessionId: session?.role === "CLIENT" ? null : sessionId,
          statut: "ACTIF",
        },
      });
    } else if (panier.statut !== "ACTIF") {
      // Réactiver le panier si converti/abandonné
      panier = await prisma.panier.update({
        where: { id: panier.id },
        data: { statut: "ACTIF" },
      });
    }

    // Ajouter ou mettre à jour la ligne
    // Prisma findUnique ne supporte pas null dans les composite keys → findFirst si varianteId null
    const ligneExistante = varianteId
      ? await prisma.panierLigne.findUnique({
          where: {
            panierId_produitId_varianteId: {
              panierId: panier.id,
              produitId,
              varianteId,
            },
          },
        })
      : await prisma.panierLigne.findFirst({
          where: {
            panierId: panier.id,
            produitId,
            varianteId: null,
          },
        });

    if (ligneExistante) {
      await prisma.panierLigne.update({
        where: { id: ligneExistante.id },
        data: { quantite: ligneExistante.quantite + quantite },
      });
    } else {
      await prisma.panierLigne.create({
        data: {
          panierId: panier.id,
          produitId,
          varianteId: varianteId ?? null,
          quantite,
          prixSnapshot: prixFinal,
          devise: produit.devise,
        },
      });
    }

    // Compter les lignes pour le badge panier
    const nbLignes = await prisma.panierLigne.count({ where: { panierId: panier.id } });

    const finalResponse = NextResponse.json({ success: true, nbLignes });
    if (!session && sessionId && !getSessionId(cookieStore)) {
      finalResponse.cookies.set("panier_session", sessionId, {
        httpOnly: true,
        sameSite: "lax",
        maxAge: 60 * 60 * 24 * 30,
        path: "/",
      });
    }
    return finalResponse;
  } catch (error) {
    console.error("panier POST error:", error);
    return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
  }
}