feat(F-070): completed feature

This commit is contained in:
chattie
2026-08-19 18:21:01 +02:00
parent 60a567d22f
commit 4b12cacd65
11 changed files with 302 additions and 59 deletions

View File

@@ -12,6 +12,7 @@ import {
calcGrossPrice,
} from '@/lib/api';
import ProductAddToCart from '@/components/cart/ProductAddToCart';
import ProductAttributes from '@/components/product/ProductAttributes';
interface Props {
params: Promise<{ slug: string }>;
@@ -134,6 +135,8 @@ export default async function ProductPage({ params }: Props) {
{product.name}
</h1>
<ProductAttributes attributes={product.attributes} />
{/* Price */}
{price ? (
<div className="mt-6 bg-gray-50 rounded-xl p-6">

View File

@@ -0,0 +1,25 @@
import type { ProductAttribute } from '@/types/api';
import { ATTRIBUTE_LABELS } from '@/lib/product-attributes';
interface Props {
attributes: ProductAttribute[] | undefined;
className?: string;
}
export default function ProductAttributes({ attributes, className }: Props) {
if (!attributes || attributes.length === 0) return null;
return (
<div className={`flex flex-wrap gap-2 ${className ?? ''}`}>
{attributes.map((attr) => (
<span
key={attr}
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-100 text-emerald-800"
title={ATTRIBUTE_LABELS[attr]}
>
{ATTRIBUTE_LABELS[attr]}
</span>
))}
</div>
);
}

View File

@@ -0,0 +1,25 @@
import type { ProductAttribute } from '@/types/api';
/** Human-readable labels for product attribute tags. */
export const ATTRIBUTE_LABELS: Record<ProductAttribute, string> = {
bio: 'Eco',
'comercio-justo': 'Comercio justo',
congelado: 'Congelado',
'cruelty-free': 'Cruelty-free',
'de-temporada': 'De temporada',
demeter: 'Demeter',
'fruta-verdura': 'Fruta y verdura',
keto: 'Keto',
kosher: 'Kosher',
'low-carb': 'Low-carb',
'raw-food': 'Raw food',
'sin-azucar': 'Sin azúcar',
'sin-gluten': 'Sin gluten',
'sin-lactosa': 'Sin lactosa',
vegano: 'Vegano',
'zero-waste': 'Zero waste',
};
export function getAttributeLabel(attr: ProductAttribute): string {
return ATTRIBUTE_LABELS[attr] ?? attr;
}

View File

@@ -1,3 +1,25 @@
// Product attribute tags (matching backend ProductAttribute domain enum)
export const PRODUCT_ATTRIBUTES = [
'bio',
'comercio-justo',
'congelado',
'cruelty-free',
'de-temporada',
'demeter',
'fruta-verdura',
'keto',
'kosher',
'low-carb',
'raw-food',
'sin-azucar',
'sin-gluten',
'sin-lactosa',
'vegano',
'zero-waste',
] as const;
export type ProductAttribute = (typeof PRODUCT_ATTRIBUTES)[number];
export interface Product {
id: string;
name: string;
@@ -5,6 +27,7 @@ export interface Product {
url: string;
description?: string;
state: string;
attributes?: ProductAttribute[];
seoTitle?: string;
seoDescription?: string;
images: Array<{ id: string; url: string; altText?: string }>;