- Added Terms of Service and Privacy Policy pages with contact info - Implemented Google Analytics with Consent Mode v2 for GDPR compliance - Created sitemap.xml and robots.txt for search engine optimization - Added dynamic meta tags, Open Graph, and structured data (JSON-LD) - Implemented GDPR consent banner with TCF 2.2 compatibility - Enhanced sidebar with category-colored hover states and proper active/inactive styling - Fixed all ESLint warnings for clean deployment - Added comprehensive SEO utilities and privacy-first analytics tracking Ready for production deployment with full legal compliance and SEO optimization.
109 lines
5.2 KiB
JavaScript
109 lines
5.2 KiB
JavaScript
import React from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { ArrowRight } from 'lucide-react';
|
|
import { getCategoryConfig } from '../config/tools';
|
|
|
|
const ToolCard = ({ icon: Icon, title, description, path, tags, category }) => {
|
|
const categoryConfig = getCategoryConfig(category);
|
|
|
|
// Define explicit hover classes for Tailwind CSS purging
|
|
const getHoverClasses = (category) => {
|
|
switch (category) {
|
|
case 'editor':
|
|
return {
|
|
border: 'hover:border-blue-300 dark:hover:border-blue-500',
|
|
shadow: 'hover:shadow-blue-500/20',
|
|
titleColor: 'group-hover:text-blue-600 dark:group-hover:text-blue-400',
|
|
arrowColor: 'group-hover:text-blue-600',
|
|
badgeColor: 'group-hover:bg-blue-100 dark:group-hover:bg-blue-900/30 group-hover:text-blue-700 dark:group-hover:text-blue-300'
|
|
};
|
|
case 'encoder':
|
|
return {
|
|
border: 'hover:border-purple-300 dark:hover:border-purple-500',
|
|
shadow: 'hover:shadow-purple-500/20',
|
|
titleColor: 'group-hover:text-purple-600 dark:group-hover:text-purple-400',
|
|
arrowColor: 'group-hover:text-purple-600',
|
|
badgeColor: 'group-hover:bg-purple-100 dark:group-hover:bg-purple-900/30 group-hover:text-purple-700 dark:group-hover:text-purple-300'
|
|
};
|
|
case 'formatter':
|
|
return {
|
|
border: 'hover:border-green-300 dark:hover:border-green-500',
|
|
shadow: 'hover:shadow-green-500/20',
|
|
titleColor: 'group-hover:text-green-600 dark:group-hover:text-green-400',
|
|
arrowColor: 'group-hover:text-green-600',
|
|
badgeColor: 'group-hover:bg-green-100 dark:group-hover:bg-green-900/30 group-hover:text-green-700 dark:group-hover:text-green-300'
|
|
};
|
|
case 'analyzer':
|
|
return {
|
|
border: 'hover:border-orange-300 dark:hover:border-orange-500',
|
|
shadow: 'hover:shadow-orange-500/20',
|
|
titleColor: 'group-hover:text-orange-600 dark:group-hover:text-orange-400',
|
|
arrowColor: 'group-hover:text-orange-600',
|
|
badgeColor: 'group-hover:bg-orange-100 dark:group-hover:bg-orange-900/30 group-hover:text-orange-700 dark:group-hover:text-orange-300'
|
|
};
|
|
default:
|
|
return {
|
|
border: 'hover:border-slate-300 dark:hover:border-slate-500',
|
|
shadow: 'hover:shadow-slate-500/20',
|
|
titleColor: 'group-hover:text-slate-600 dark:group-hover:text-slate-400',
|
|
arrowColor: 'group-hover:text-slate-600',
|
|
badgeColor: 'group-hover:bg-slate-100 dark:group-hover:bg-slate-700 group-hover:text-slate-700 dark:group-hover:text-slate-300'
|
|
};
|
|
}
|
|
};
|
|
|
|
const hoverClasses = getHoverClasses(category);
|
|
|
|
return (
|
|
<Link to={path} className="block group">
|
|
<div className={`relative overflow-hidden rounded-2xl bg-white/70 dark:bg-slate-800/70 backdrop-blur-sm border border-slate-200 dark:border-slate-700 ${hoverClasses.border} transition-all duration-300 hover:shadow-2xl ${hoverClasses.shadow} hover:-translate-y-1`}>
|
|
{/* Gradient overlay on hover */}
|
|
<div className={`absolute inset-0 bg-gradient-to-br ${categoryConfig.color} opacity-0 group-hover:opacity-5 transition-opacity duration-300`}></div>
|
|
|
|
<div className="relative p-6">
|
|
{/* Header */}
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div className={`flex-shrink-0 p-3 rounded-xl bg-gradient-to-br ${categoryConfig.color} shadow-lg group-hover:scale-110 transition-transform duration-300`}>
|
|
<Icon className="h-6 w-6 text-white" />
|
|
</div>
|
|
<div className="flex-shrink-0 ml-4">
|
|
<ArrowRight className={`h-5 w-5 text-slate-400 ${hoverClasses.arrowColor} group-hover:translate-x-1 transition-all duration-300`} />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="space-y-3">
|
|
<div className="flex items-center gap-2">
|
|
<h3 className={`text-xl font-bold text-slate-800 dark:text-white ${hoverClasses.titleColor} transition-colors`}>
|
|
{title}
|
|
</h3>
|
|
<span className={`px-2 py-1 text-xs font-medium bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 rounded-full ${hoverClasses.badgeColor} transition-colors`}>
|
|
{categoryConfig.name}
|
|
</span>
|
|
</div>
|
|
|
|
<p className="text-slate-600 dark:text-slate-300 leading-relaxed group-hover:text-slate-700 dark:group-hover:text-slate-200 transition-colors">
|
|
{description}
|
|
</p>
|
|
|
|
{tags && tags.length > 0 && (
|
|
<div className="flex flex-wrap gap-2 pt-2">
|
|
{tags.map((tag, index) => (
|
|
<span
|
|
key={index}
|
|
className="px-3 py-1 text-xs font-medium bg-slate-50 dark:bg-slate-700/50 text-slate-500 dark:text-slate-400 rounded-full border border-slate-200 dark:border-slate-600 group-hover:border-slate-300 dark:group-hover:border-slate-500 transition-colors"
|
|
>
|
|
{tag}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
);
|
|
};
|
|
|
|
export default ToolCard;
|