238 lines
6.5 KiB
TypeScript
238 lines
6.5 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
FaCheckCircle,
|
|
FaClock,
|
|
FaHourglassHalf,
|
|
FaTasks,
|
|
FaChevronDown,
|
|
FaPlus,
|
|
FaUserPlus,
|
|
FaFilter,
|
|
FaCalendarAlt
|
|
} from 'react-icons/fa';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
// 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 navigate = useNavigate();
|
|
const [selectedFilter, setSelectedFilter] = useState<string>('Last 7 Days');
|
|
|
|
// 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>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="w-full">
|
|
{/* Cards */}
|
|
{isLoading ? (
|
|
<div className="w-full h-[10vh] px-6 py-3">
|
|
<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">
|
|
{[...Array(4)].map((_, i) => (
|
|
<div key={i} className="h-10 bg-gray-200 border border-gray-100"></div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 shadow">
|
|
{cards.map((card, index) => (
|
|
<div
|
|
key={card.title}
|
|
onClick={() => navigate(card.navigateTo)}
|
|
className={`flex items-center justify-between bg-white px-4 py-3.5 transition-all duration-300 cursor-pointer ${index > 0 ? "border-l-1 border-gray-200" : ""}`}
|
|
style={{
|
|
animationDelay: `${index * 100}ms`,
|
|
}}
|
|
>
|
|
{/* Left side: Icon + Title */}
|
|
<div className="flex items-center gap-3">
|
|
<div className={`p-1.5 rounded-lg ${card.iconBg}`}>
|
|
{card.icon}
|
|
</div>
|
|
<h3 className="text-xs font-medium text-gray-600 text-nowrap">{card.title}</h3>
|
|
</div>
|
|
|
|
{/* Count */}
|
|
<p className={`text-sm font-bold`}>
|
|
{card.count}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
);
|
|
};
|
|
|
|
export default Cards; |