1
1
import { Form , useNavigation } from "react-router" ;
2
2
3
3
import { Button , Container , Separator } from "@/components/ui" ;
4
+ import { cn } from "@/lib/utils" ;
4
5
import { type Product } from "@/models/product.model" ;
5
6
import { getProductById } from "@/services/product.service" ;
6
7
7
8
import NotFound from "../not-found" ;
8
9
9
10
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
+ }
10
19
11
20
export async function loader ( { params } : Route . LoaderArgs ) {
12
21
try {
13
22
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 } ;
15
27
} catch {
16
28
return { } ;
17
29
}
18
30
}
19
31
20
32
export default function Product ( { loaderData } : Route . ComponentProps ) {
21
- const { product } = loaderData ;
33
+ const { product, categoryWithVariants } = loaderData ;
22
34
const navigation = useNavigation ( ) ;
23
35
const cartLoading = navigation . state === "submitting" ;
24
36
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
+
25
64
if ( ! product ) {
26
65
return < NotFound /> ;
27
66
}
28
67
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
+
29
77
return (
30
78
< >
31
79
< section className = "py-12" >
@@ -45,12 +93,55 @@ export default function Product({ loaderData }: Route.ComponentProps) {
45
93
< p className = "text-sm leading-5 text-muted-foreground mb-10" >
46
94
{ product . description }
47
95
</ 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
+
48
131
< Form method = "post" action = "/cart/add-item" >
49
132
< input
50
133
type = "hidden"
51
134
name = "redirectTo"
52
135
value = { `/products/${ product . id } ` }
53
136
/>
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
+ ) }
54
145
< Button
55
146
size = "xl"
56
147
className = "w-full md:w-80"
0 commit comments