Files
klz-cables.com/components/cards/CategoryCard.tsx
2025-12-29 18:18:48 +01:00

194 lines
6.1 KiB
TypeScript

'use client';
import React from 'react';
import { BaseCard, BaseCardProps, CardSize, CardLayout } from './BaseCard';
import { Badge, BadgeGroup } from '@/components/ui';
import { ProductCategory } from '@/lib/data';
import { cn } from '@/lib/utils';
// CategoryCard specific props
export interface CategoryCardProps extends Omit<BaseCardProps, 'title' | 'description' | 'image' | 'footer'> {
/** Category data from WordPress */
category: ProductCategory;
/** Display product count */
showCount?: boolean;
/** Display description */
showDescription?: boolean;
/** Display as icon instead of image */
useIcon?: boolean;
/** Icon component (if useIcon is true) */
icon?: React.ReactNode;
/** Category type */
categoryType?: 'product' | 'blog';
/** Locale for formatting */
locale?: string;
}
// Helper to get category image
const getCategoryImage = (category: ProductCategory): string | undefined => {
// In a real implementation, this would use getMediaById
// For now, return a placeholder based on category ID
if (category.id % 3 === 0) {
return '/media/6517-medium-voltage-category.webp';
} else if (category.id % 3 === 1) {
return '/media/6521-low-voltage-category.webp';
} else {
return '/media/10863-klz-directory-2-scaled.webp';
}
};
// Helper to get icon for category
const getCategoryIcon = (category: ProductCategory): React.ReactNode => {
const iconMap = {
1: '🔌', // Low Voltage
2: '⚡', // Medium Voltage
3: '🏭', // Industrial
4: '🏗️', // Construction
5: '🏠', // Residential
};
return iconMap[category.id as keyof typeof iconMap] || '📁';
};
// Helper to get category color variant
const getCategoryVariant = (category: ProductCategory): 'primary' | 'secondary' | 'success' | 'info' => {
const variants = ['primary', 'secondary', 'success', 'info'] as const;
return variants[category.id % variants.length];
};
export const CategoryCard: React.FC<CategoryCardProps> = ({
category,
size = 'md',
layout = 'vertical',
showCount = true,
showDescription = true,
useIcon = false,
icon,
categoryType = 'product',
locale = 'de',
className = '',
...props
}) => {
// Get category data
const title = category.name;
const description = showDescription && category.description ?
category.description.replace(/<[^>]*>/g, '').substring(0, 100) + (category.description.length > 100 ? '...' : '') :
'';
const count = showCount ? category.count : 0;
const image = useIcon ? undefined : getCategoryImage(category);
const categoryIcon = icon || getCategoryIcon(category);
// Build badge with count
const badge = showCount && count > 0 ? (
<Badge variant="neutral" size={size === 'sm' ? 'sm' : 'md'}>
{count} {locale === 'de' ? 'Produkte' : 'Products'}
</Badge>
) : null;
// Build header with icon
const header = useIcon ? (
<span className="text-3xl" role="img" aria-label="Category icon">
{categoryIcon}
</span>
) : null;
// Build footer with link text
const footer = (
<span className={cn(
'font-medium transition-colors',
getCategoryVariant(category) === 'primary' && 'text-primary hover:text-primary-dark',
getCategoryVariant(category) === 'secondary' && 'text-secondary hover:text-secondary-dark',
getCategoryVariant(category) === 'success' && 'text-success hover:text-success-dark',
getCategoryVariant(category) === 'info' && 'text-info hover:text-info-dark'
)}>
{locale === 'de' ? 'Anzeigen' : 'View'}
</span>
);
// Build description with count
const descriptionContent = (
<div>
{description && <div className="text-gray-600 mb-2">{description}</div>}
{showCount && count > 0 && (
<div className="text-sm font-semibold text-gray-700">
{count} {locale === 'de' ? 'Produkte' : 'Products'}
</div>
)}
</div>
);
// Build icon content for vertical layout
const iconContent = useIcon ? (
<div className={cn(
'flex items-center justify-center rounded-lg',
getCategoryVariant(category) === 'primary' && 'bg-primary/10 text-primary',
getCategoryVariant(category) === 'secondary' && 'bg-secondary/10 text-secondary',
getCategoryVariant(category) === 'success' && 'bg-success/10 text-success',
getCategoryVariant(category) === 'info' && 'bg-info/10 text-info',
size === 'sm' && 'w-12 h-12 text-xl',
size === 'md' && 'w-16 h-16 text-2xl',
size === 'lg' && 'w-20 h-20 text-3xl'
)}>
{categoryIcon}
</div>
) : null;
// Override title to include icon for horizontal layout
const titleContent = useIcon && layout === 'horizontal' ? (
<div className="flex items-center gap-2">
{iconContent}
<span>{title}</span>
</div>
) : title;
return (
<BaseCard
title={titleContent}
description={descriptionContent}
image={useIcon ? undefined : image}
imageAlt={title}
size={size}
layout={layout}
href={category.path}
badge={badge}
header={useIcon && layout === 'vertical' ? null : header}
footer={footer}
hoverable={true}
variant="elevated"
className={className}
{...props}
>
{/* For vertical layout with icon, add icon above title */}
{useIcon && layout === 'vertical' && (
<div className="mb-2">
{iconContent}
</div>
)}
</BaseCard>
);
};
// CategoryCard variations
export const CategoryCardVertical: React.FC<CategoryCardProps> = (props) => (
<CategoryCard {...props} layout="vertical" />
);
export const CategoryCardHorizontal: React.FC<CategoryCardProps> = (props) => (
<CategoryCard {...props} layout="horizontal" />
);
export const CategoryCardSmall: React.FC<CategoryCardProps> = (props) => (
<CategoryCard {...props} size="sm" />
);
export const CategoryCardLarge: React.FC<CategoryCardProps> = (props) => (
<CategoryCard {...props} size="lg" />
);
// Icon-only category card
export const CategoryCardIcon: React.FC<CategoryCardProps> = (props) => (
<CategoryCard {...props} useIcon={true} showDescription={false} />
);
// Export types
export type { CardSize, CardLayout };