auto-claude: subtask-1-2 - Create CategoryFilter component with category buttons/dropdown

Created CategoryFilter component with the following features:
- Client component with 'use client' directive and framer-motion animations
- Props: categories, selectedCategory, onCategoryChange, className
- Uses lucide-react Tag icon
- Responsive design: horizontal scrollable on mobile, grid layout on desktop
- Active category highlighted with bg-[#697565] text-white
- Includes 'All' option to clear filter
- Follows established patterns from SearchBar and ContactForm
- Consistent styling with bg-zinc-900/50 and border-zinc-800
- Smooth transitions and hover states

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 02:39:30 +01:00
co-authored by Claude Sonnet 4.5
parent 0cc34aa007
commit cfa2bc81e6
5 changed files with 377 additions and 1 deletions
+82
View File
@@ -0,0 +1,82 @@
'use client';
import { Tag } from 'lucide-react';
import { motion } from 'framer-motion';
interface CategoryFilterProps {
categories: string[];
selectedCategory: string | null;
onCategoryChange: (category: string | null) => void;
className?: string;
}
export function CategoryFilter({
categories,
selectedCategory,
onCategoryChange,
className = ''
}: CategoryFilterProps) {
const allCategories = ['All', ...categories];
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className={`${className}`}
>
<div className="flex items-center gap-3 mb-4">
<Tag className="h-5 w-5 text-zinc-400 flex-shrink-0" />
<h3 className="text-sm font-medium text-zinc-300">Filter by Category</h3>
</div>
{/* Mobile: Horizontal scrollable */}
<div className="md:hidden overflow-x-auto pb-2 -mx-4 px-4">
<div className="flex gap-2 min-w-max">
{allCategories.map((category) => {
const isActive = category === 'All'
? selectedCategory === null
: selectedCategory === category;
return (
<button
key={category}
onClick={() => onCategoryChange(category === 'All' ? null : category)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all whitespace-nowrap ${
isActive
? 'bg-[#697565] text-white'
: 'bg-zinc-900/50 text-zinc-400 border border-zinc-800 hover:bg-zinc-800/50 hover:text-white'
}`}
>
{category}
</button>
);
})}
</div>
</div>
{/* Desktop: Grid */}
<div className="hidden md:grid md:grid-cols-4 lg:grid-cols-6 gap-2">
{allCategories.map((category) => {
const isActive = category === 'All'
? selectedCategory === null
: selectedCategory === category;
return (
<button
key={category}
onClick={() => onCategoryChange(category === 'All' ? null : category)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
isActive
? 'bg-[#697565] text-white'
: 'bg-zinc-900/50 text-zinc-400 border border-zinc-800 hover:bg-zinc-800/50 hover:text-white'
}`}
>
{category}
</button>
);
})}
</div>
</motion.div>
);
}