SME-Web/src/components/home/Cards.tsx

324 lines
11 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import {
FaCheckCircle,
FaClock,
FaHourglassHalf,
FaTasks,
FaChevronDown,
FaPlus,
FaUserPlus,
FaFilter,
FaCalendarAlt
} from 'react-icons/fa';
// Define a type for card data
interface CardData {
title: string;
count: number;
icon: JSX.Element;
gradient: string;
shadowColor: string;
iconBg: string;
textColor: string;
percentage?: number;
trend?: 'up' | 'down' | 'stable';
}
// interface CardData {
// title: string;
// count: number;
// icon: React.ReactNode;
// gradient: string;
// shadowColor: string;
// iconBg: string;
// textColor: string;
// trend: 'up' | 'down';
// }
interface CardsProps {
type?: 'dashboard' | 'account' | 'transactions';
onOpenModal?: () => void;
dashboardData: any;
fromDate: string;
toDate: string;
onDateRangeChange: (range: { from: string; to: string }) => void;
cards: CardData[];
isLoading: boolean
}
const AccountData: CardData[] = [
{
title: 'Active Accounts',
count: 12,
icon: <FaTasks size={18} className="text-blue-600" />,
gradient: 'bg-gradient-to-br from-blue-50 to-blue-100',
shadowColor: 'shadow-blue-200/50',
iconBg: 'bg-blue-100 border-blue-200',
textColor: 'text-blue-700',
trend: 'up'
},
{
title: 'In-Progress',
count: 8,
icon: <FaClock size={18} className="text-purple-600" />,
gradient: 'bg-gradient-to-br from-purple-50 to-purple-100',
shadowColor: 'shadow-purple-200/50',
iconBg: 'bg-purple-100 border-purple-200',
textColor: 'text-purple-700',
trend: 'stable'
},
{
title: 'Completed',
count: 27,
icon: <FaCheckCircle size={18} className="text-green-600" />,
gradient: 'bg-gradient-to-br from-green-50 to-green-100',
shadowColor: 'shadow-green-200/50',
iconBg: 'bg-green-100 border-green-200',
textColor: 'text-green-700',
trend: 'up'
},
{
title: 'Inactive',
count: 5,
icon: <FaHourglassHalf size={18} className="text-gray-600" />,
gradient: 'bg-gradient-to-br from-gray-50 to-gray-100',
shadowColor: 'shadow-gray-200/50',
iconBg: 'bg-gray-100 border-gray-200',
textColor: 'text-gray-700',
trend: 'down'
},
];
const filters = ['Last 7 Days', 'Last 30 Days', 'Last 6 Months', 'This Year'];
const accounts = ['All Accounts', 'Account 1', 'Account 2', 'Account 3'];
const Cards: React.FC<CardsProps> = ({ type = 'dashboard', onOpenModal, onDateRangeChange, cards, isLoading }) => {
const [selectedFilter, setSelectedFilter] = useState<string>('Last 7 Days');
const [selectedAccount, setSelectedAccount] = useState<string>('All Accounts');
const [showCards, setShowCards] = useState<CardData[]>([]);
// const [isLoading, setIsLoading] = useState<boolean>(true);
// useEffect(() => {
// setIsLoading(true);
// setTimeout(() => {
// if (type === 'account') {
// setShowCards(AccountData);
// } else {
// setShowCards(cards);
// }
// setIsLoading(false);
// }, 500);
// }, [type]);
useEffect(() => {
if (type === 'dashboard') {
const now = new Date();
let fromDate = new Date();
switch (selectedFilter) {
case 'Last 7 Days':
fromDate.setDate(now.getDate() - 7);
break;
case 'Last 30 Days':
fromDate.setDate(now.getDate() - 30);
break;
case 'Last 6 Months':
fromDate.setMonth(now.getMonth() - 6);
break;
case 'This Year':
fromDate = new Date(now.getFullYear(), 0, 1);
break;
default:
fromDate.setDate(now.getDate() - 7);
}
const from = fromDate.toISOString().split('T')[0];
const to = now.toISOString().split('T')[0];
onDateRangeChange({ from, to });
}
}, [selectedFilter]);
const getButtonConfig = () => {
switch (type) {
case 'account':
return {
text: 'Add Account',
icon: <FaPlus size={14} />,
gradient: 'bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800'
};
case 'transactions':
return {
text: 'Send Invite',
icon: <FaUserPlus size={14} />,
gradient: 'bg-gradient-to-r from-emerald-600 to-emerald-700 hover:from-emerald-700 hover:to-emerald-800'
};
default:
return null;
}
};
const buttonConfig = getButtonConfig();
const renderTrendIndicator = (trend: string | undefined, percentage: number | undefined) => {
if (!trend || !percentage) return null;
const trendColors = {
up: 'text-emerald-500',
down: 'text-red-500',
stable: 'text-gray-500'
};
const trendSymbols = {
up: '↗',
down: '↘',
stable: '→'
};
return (
<div className={`flex items-center text-xs ${trendColors[trend as keyof typeof trendColors]} mt-1`}>
<span className="mr-1">{trendSymbols[trend as keyof typeof trendSymbols]}</span>
{/* <span>{percentage}%</span> */}
</div>
);
};
// if (isLoading) {
// return (
// <div className="w-full [h-50vh] px-6 py-8">
// <div className="animate-pulse">
// <div className="h-8 bg-gray-200 rounded w-32 mb-8"></div>
// <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
// {[...Array(7)].map((_, i) => (
// <div key={i} className="h-32 bg-gray-200 rounded-xl"></div>
// ))}
// </div>
// </div>
// </div>
// );
// }
return (
<div className="w-full px-3 rounded-md">
{/* Header */}
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between mb-2 gap-4">
<div className="flex items-center gap-3">
{/* <div className="w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg">
<FaFilter className="text-white text-lg" />
</div> */}
<div>
{/* <h2 className="text-lg font-semibold text-gray-800">Overview</h2> */}
{/* <p className="text-gray-600 text-sm">Monitor performance metrics</p> */}
</div>
</div>
</div>
{/* Cards */}
{isLoading ? <div className="w-full [h-50vh] px-6 py-8">
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-32 mb-8"></div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{[...Array(7)].map((_, i) => (
<div key={i} className="h-32 bg-gray-200 rounded-xl"></div>
))}
</div>
</div>
</div> :
<div className="flex flex-wrap gap-2 justify-end">
{cards.map((card, index) => (
<div
key={card.title}
className={`group relative overflow-hidden rounded-lg ${card.gradient} border border-white/40 px-3 py-1 transition-all duration-300 hover:scale-105 hover:shadow-lg ${card.shadowColor} cursor-pointer backdrop-blur-sm min-w-[200px] max-w-[220px]`}
style={{
animationDelay: `${index * 100}ms`
}}
>
{/* Background Pattern */}
<div className="absolute inset-0 opacity-5">
<div className="absolute -top-6 -right-6 w-12 h-12 rounded-full bg-current"></div>
<div className="absolute -bottom-6 -left-6 w-10 h-10 rounded-full bg-current"></div>
</div>
{/* Content */}
<div className="relative flex justify-between items-center z-10">
<div className="flex items-center gap-2">
<div className={`p-2 rounded-lg border ${card.iconBg} group-hover:scale-110 transition-transform duration-200`}>
{card.icon}
</div>
<h3 className="text-sm font-medium text-gray-600 group-hover:text-gray-700 transition-colors">
{card.title}
</h3>
</div>
<div className="flex items-center">
<p className={`text-xl font-bold ${card.textColor} group-hover:scale-105 transition-transform duration-200 leading-none mb-1`}>
{card.count}
</p>
</div>
</div>
{/* Hover Effect */}
<div className="absolute inset-0 bg-gradient-to-br from-white/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
</div>
))}
</div>
}
</div>
);
};
export default Cards;
{/* <div className="flex flex-wrap gap-3 w-full lg:w-auto">
{type === 'dashboard' && <>
Account Selector
<div className="relative">
<select
value={selectedAccount}
onChange={(e) => setSelectedAccount(e.target.value)}
className="appearance-none text-sm text-gray-700 border border-gray-300 rounded-xl px-4 py-3 pr-10 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent bg-white shadow-sm hover:shadow-md transition-all cursor-pointer min-w-[140px]"
>
{accounts.map((account) => (
<option key={account} value={account}>{account}</option>
))}
</select>
<FaChevronDown className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 text-xs pointer-events-none" />
</div>
Date Filter
<div className="relative">
<select
value={selectedFilter}
onChange={(e) => setSelectedFilter(e.target.value)}
className="appearance-none text-sm text-gray-700 border border-gray-300 rounded-xl px-4 py-3 pr-10 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent bg-white shadow-sm hover:shadow-md transition-all cursor-pointer min-w-[140px]"
>
{filters.map((filter) => (
<option key={filter} value={filter}>{filter}</option>
))}
</select>
<FaCalendarAlt className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 text-xs pointer-events-none" />
</div>
</>}
Action Button
{buttonConfig && (
<button
className={`flex items-center gap-1 px-3 py-3 text-sm font-semibold text-white rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:scale-105 active:scale-95 cursor-pointer ${buttonConfig.gradient}`}
onClick={onOpenModal}
>
{buttonConfig.icon}
{buttonConfig.text}
</button>
)}
</div> */}