Skip to content

Commit 640cd78

Browse files
committed
feat: agregamos la logica de manejo para la interface de producto y poder mostrar las variantes asi como su comportamiento UI y diseño
1 parent cba1cee commit 640cd78

File tree

3 files changed

+123
-9
lines changed

3 files changed

+123
-9
lines changed

prisma/initial_data.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,22 @@ export const categoryVariants = [
4848
// Variantes para Stickers (categoryId: 3) - con modificador de precio
4949
{
5050
categoryId: 3,
51-
value: "3x3",
52-
label: "3×3 cm",
51+
value: "3x3 cm",
52+
label: "3×3",
5353
priceModifier: 0,
5454
sortOrder: 1,
5555
},
5656
{
5757
categoryId: 3,
58-
value: "5x5",
59-
label: "5×5 cm",
58+
value: "5x5 cm",
59+
label: "5×5",
6060
priceModifier: 1.0,
6161
sortOrder: 2,
6262
},
6363
{
6464
categoryId: 3,
65-
value: "10x10",
66-
label: "10×10 cm",
65+
value: "10x10 cm",
66+
label: "10×10",
6767
priceModifier: 3.0,
6868
sortOrder: 3,
6969
},

src/routes/product/index.tsx

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,79 @@
11
import { Form, useNavigation } from "react-router";
22

33
import { Button, Container, Separator } from "@/components/ui";
4+
import { cn } from "@/lib/utils";
45
import { type Product } from "@/models/product.model";
56
import { getProductById } from "@/services/product.service";
67

78
import NotFound from "../not-found";
89

910
import type { Route } from "./+types";
11+
import { useEffect, useState } from "react";
12+
import { getCategoryWithVariants } from "@/services/category.service";
13+
14+
interface CategoryVariant {
15+
id: number;
16+
value: string;
17+
label: string;
18+
}
1019

1120
export async function loader({ params }: Route.LoaderArgs) {
1221
try {
1322
const product = await getProductById(parseInt(params.id));
14-
return { product };
23+
const categoryWithVariants = product.categoryId
24+
? await getCategoryWithVariants(product.categoryId)
25+
: null;
26+
return { product, categoryWithVariants };
1527
} catch {
1628
return {};
1729
}
1830
}
1931

2032
export default function Product({ loaderData }: Route.ComponentProps) {
21-
const { product } = loaderData;
33+
const { product, categoryWithVariants } = loaderData;
2234
const navigation = useNavigation();
2335
const cartLoading = navigation.state === "submitting";
2436

37+
// Estado simple para variantes
38+
const [variants, setVariants] = useState<CategoryVariant[]>([]);
39+
const [selectedVariant, setSelectedVariant] =
40+
useState<CategoryVariant | null>(null);
41+
42+
// Cargar variantes si la categoría las tiene
43+
useEffect(() => {
44+
if (
45+
!categoryWithVariants?.hasVariants ||
46+
!categoryWithVariants.categoryVariants.length
47+
) {
48+
setVariants([]);
49+
setSelectedVariant(null);
50+
return;
51+
}
52+
53+
const mappedVariants: CategoryVariant[] =
54+
categoryWithVariants.categoryVariants.map((variant) => ({
55+
id: variant.id,
56+
value: variant.value,
57+
label: variant.label,
58+
}));
59+
60+
setVariants(mappedVariants);
61+
setSelectedVariant(mappedVariants[0] || null);
62+
}, [categoryWithVariants?.id, categoryWithVariants?.hasVariants]);
63+
2564
if (!product) {
2665
return <NotFound />;
2766
}
2867

68+
const hasVariants = categoryWithVariants?.hasVariants && variants.length > 0;
69+
70+
// Helper para obtener el label de la variante
71+
const getVariantLabel = () => {
72+
if (product.categoryId === 1) return "Talla";
73+
if (product.categoryId === 3) return "Tamaño";
74+
return "Opciones";
75+
};
76+
2977
return (
3078
<>
3179
<section className="py-12">
@@ -45,12 +93,55 @@ export default function Product({ loaderData }: Route.ComponentProps) {
4593
<p className="text-sm leading-5 text-muted-foreground mb-10">
4694
{product.description}
4795
</p>
96+
97+
{/* Toggle Button Group para Variantes - Implementación directa */}
98+
{hasVariants && (
99+
<div className="mb-6">
100+
<div className="space-y-3">
101+
<label className="text-sm font-medium text-foreground">
102+
{getVariantLabel()}
103+
</label>
104+
<div className="flex flex-wrap gap-2">
105+
{variants.map((variant) => (
106+
<Button
107+
key={variant.id}
108+
type="button"
109+
variant={
110+
selectedVariant?.id === variant.id
111+
? "default"
112+
: "outline"
113+
}
114+
size="default"
115+
onClick={() => setSelectedVariant(variant)}
116+
className={cn(
117+
"h-10 px-4 transition-all duration-200",
118+
selectedVariant?.id === variant.id
119+
? "bg-primary text-primary-foreground border-primary"
120+
: "bg-background text-foreground border-border hover:bg-muted"
121+
)}
122+
>
123+
{variant.label}
124+
</Button>
125+
))}
126+
</div>
127+
</div>
128+
</div>
129+
)}
130+
48131
<Form method="post" action="/cart/add-item">
49132
<input
50133
type="hidden"
51134
name="redirectTo"
52135
value={`/products/${product.id}`}
53136
/>
137+
<input type="hidden" name="productId" value={product.id} />
138+
{selectedVariant && (
139+
<input
140+
type="hidden"
141+
name="categoryVariantId"
142+
value={selectedVariant.id}
143+
/>
144+
)}
54145
<Button
55146
size="xl"
56147
className="w-full md:w-80"

src/services/category.service.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { prisma } from "@/db/prisma";
22

3-
import { type Category, type CategorySlug } from "@/../generated/prisma/client";
3+
import {
4+
type Category,
5+
type CategorySlug,
6+
type Prisma,
7+
} from "@/../generated/prisma/client";
8+
9+
type CategoryWithVariants = Prisma.CategoryGetPayload<{
10+
include: { categoryVariants: true };
11+
}>;
412

513
export async function getAllCategories(): Promise<Category[]> {
614
const categories = await prisma.category.findMany();
@@ -18,3 +26,18 @@ export async function getCategoryBySlug(slug: CategorySlug): Promise<Category> {
1826

1927
return category;
2028
}
29+
30+
export async function getCategoryWithVariants(
31+
categoryId: number
32+
): Promise<CategoryWithVariants | null> {
33+
const category = await prisma.category.findUnique({
34+
where: { id: categoryId },
35+
include: {
36+
categoryVariants: {
37+
orderBy: { sortOrder: "asc" },
38+
},
39+
},
40+
});
41+
42+
return category;
43+
}

0 commit comments

Comments
 (0)