diff --git a/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql b/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql new file mode 100644 index 0000000..aab9f1b --- /dev/null +++ b/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - You are about to drop the column `product_id` on the `cart_items` table. All the data in the column will be lost. + - A unique constraint covering the columns `[cart_id,attribute_value_id]` on the table `cart_items` will be added. If there are existing duplicate values, this will fail. + - Added the required column `attribute_value_id` to the `cart_items` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "cart_items" DROP CONSTRAINT "cart_items_product_id_fkey"; + +-- DropIndex +DROP INDEX "cart_items_cart_id_product_id_key"; + +-- AlterTable +ALTER TABLE "cart_items" DROP COLUMN "product_id", +ADD COLUMN "attribute_value_id" INTEGER NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "cart_items_cart_id_attribute_value_id_key" ON "cart_items"("cart_id", "attribute_value_id"); + +-- AddForeignKey +ALTER TABLE "cart_items" ADD CONSTRAINT "cart_items_attribute_value_id_fkey" FOREIGN KEY ("attribute_value_id") REFERENCES "variants_attributes_values"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d251f4a..b8df94a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -63,7 +63,7 @@ model Product { updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) - cartItems CartItem[] + orderItems OrderItem[] variantAttributeValues VariantAttributeValue[] @@ -92,6 +92,8 @@ model VariantAttributeValue { variantAttribute VariantAttribute @relation(fields: [attributeId], references: [id]) product Product @relation(fields: [productId], references: [id]) + + CartItem CartItem[] @@unique([attributeId, productId, value], name: "unique_attribute_product_value") @@map("variants_attributes_values") @@ -113,15 +115,15 @@ model Cart { model CartItem { id Int @id @default(autoincrement()) cartId Int @map("cart_id") - productId Int @map("product_id") + attributeValueId Int @map("attribute_value_id") quantity Int createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade) - product Product @relation(fields: [productId], references: [id], onDelete: Cascade) + variantAttributeValue VariantAttributeValue @relation(fields: [attributeValueId], references: [id], onDelete: Cascade) - @@unique([cartId, productId], name: "unique_cart_item") + @@unique([cartId, attributeValueId], name: "unique_cart_item") @@map("cart_items") } diff --git a/src/models/cart.model.ts b/src/models/cart.model.ts index ad4206a..8550190 100644 --- a/src/models/cart.model.ts +++ b/src/models/cart.model.ts @@ -33,6 +33,7 @@ export type CartProductInfo = Pick< export type CartItemWithProduct = { product: CartProductInfo; quantity: number; + attributeId: number; }; // Tipo para el carrito con items y productos incluidos diff --git a/src/routes/category/components/product-card/index.tsx b/src/routes/category/components/product-card/index.tsx index 05d36bd..23c402b 100644 --- a/src/routes/category/components/product-card/index.tsx +++ b/src/routes/category/components/product-card/index.tsx @@ -1,16 +1,17 @@ import { Link } from "react-router"; + import type { Product } from "@/models/product.model"; interface ProductCardProps { product: Product; } -const stickerCategoryId = 3; +const stickerCategoryId = 3; // ID de la categoría "Stickers" export function ProductCard({ product }: ProductCardProps) { - - const isSticker = stickerCategoryId; - + + const isSticker = product.categoryId === stickerCategoryId; + return ( <> = min } + if (max) { return (productPrice||maxProductPrice) <= max - } return true }); diff --git a/src/services/cart.service.ts b/src/services/cart.service.ts index 74cbdfc..af05fa3 100644 --- a/src/services/cart.service.ts +++ b/src/services/cart.service.ts @@ -18,52 +18,46 @@ async function getCart( : undefined; if (!whereCondition) return null; - try { - const data = await prisma.cart.findFirst({ - where: whereCondition, - include: { - items: { - include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + const data = await prisma.cart.findFirst({ + where: whereCondition, + include: { + items: { + include: { + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, }, }, }, - orderBy: { - id: "asc", - }, }, - }, - }); - - if (!data) return null; - - return { - ...data, - items: data.items.map((item) => ({ - ...item, - product: { - ...item.product, - price: item.product.price.toNumber(), + orderBy: { + id: "asc", }, - })), - }; - }catch(e) { - console.log(e) - return { - error: true, - status: 500, - message: "Error al obtener el carrito. Verifica el modelo Product.", - }; - } + }, + }, + }); + if (!data) return null; + + return { + ...data, + items: data.items.map((item) => ({ + ...item, + product: { + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), + }, + variantAttributeValue: item.variantAttributeValue, + })), + }; } export async function getRemoteCart( @@ -92,14 +86,17 @@ export async function getOrCreateCart( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, @@ -114,9 +111,10 @@ export async function getOrCreateCart( items: newCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } @@ -140,7 +138,7 @@ export async function createRemoteItems( await prisma.cartItem.createMany({ data: items.map((item) => ({ cartId: cart.id, - productId: item.product.id, + attributeValueId: item.attributeId, // modificar quantity: item.quantity, })), }); @@ -156,12 +154,14 @@ export async function createRemoteItems( export async function alterQuantityCartItem( userId: User["id"] | undefined, sessionCartId: string | undefined, - productId: number, + attributeId: number, quantity: number = 1 ): Promise { const cart = await getOrCreateCart(userId, sessionCartId); - const existingItem = cart.items.find((item) => item.product.id === productId); + const existingItem = cart.items.find( + (item) => item.attributeValueId === attributeId + ); if (existingItem) { const newQuantity = existingItem.quantity + quantity; @@ -180,7 +180,7 @@ export async function alterQuantityCartItem( await prisma.cartItem.create({ data: { cartId: cart.id, - productId, + attributeValueId: attributeId, quantity, }, }); @@ -246,14 +246,17 @@ export async function linkCartToUser( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, @@ -268,9 +271,10 @@ export async function linkCartToUser( items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } @@ -295,41 +299,48 @@ export async function mergeGuestCartWithUserCart( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, }, }, }); + return { ...updatedCart, items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } // Obtener productos duplicados para eliminarlos del carrito del usuario - const guestProductIds = guestCart.items.map((item) => item.productId); + const guestAttributeValueIds = guestCart.items.map( + (item) => item.attributeValueId + ); // Eliminar productos del carrito usuario que también existan en el carrito invitado await prisma.cartItem.deleteMany({ where: { cartId: userCart.id, - productId: { - in: guestProductIds, + attributeValueId: { + in: guestAttributeValueIds, }, }, }); @@ -338,7 +349,7 @@ export async function mergeGuestCartWithUserCart( await prisma.cartItem.createMany({ data: guestCart.items.map((item) => ({ cartId: userCart.id, - productId: item.productId, + attributeValueId: item.attributeValueId, quantity: item.quantity, })), }); @@ -351,3 +362,4 @@ export async function mergeGuestCartWithUserCart( // Devolver el carrito actualizado del usuario return await getCart(userId); } +