migration wip
This commit is contained in:
194
components/cards/CategoryCard.tsx
Normal file
194
components/cards/CategoryCard.tsx
Normal file
@@ -0,0 +1,194 @@
|
||||
'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 };
|
||||
Reference in New Issue
Block a user