diff --git a/Frontend/src/components/analytics/DataFreshnessIndicator.tsx b/Frontend/src/components/analytics/DataFreshnessIndicator.tsx new file mode 100644 index 00000000..ac0d92dc --- /dev/null +++ b/Frontend/src/components/analytics/DataFreshnessIndicator.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +export interface DataFreshnessIndicatorProps { + lastUpdated?: string; + isRefreshing?: boolean; + onRefresh?: () => void; +} + +export const DataFreshnessIndicator: React.FC = ({ + lastUpdated = 'Just now', + isRefreshing = false, + onRefresh, +}) => { + return ( +
+ + + Synced {lastUpdated} + + +
+ ); +}; + +export default DataFreshnessIndicator; diff --git a/Frontend/src/components/analytics/SparklineKpiCard.tsx b/Frontend/src/components/analytics/SparklineKpiCard.tsx new file mode 100644 index 00000000..c44864ee --- /dev/null +++ b/Frontend/src/components/analytics/SparklineKpiCard.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +export interface SparklineKpiCardProps { + title: string; + value: string | number; + trend: number[]; // Array of numeric points + changePercent?: number; +} + +export const SparklineKpiCard: React.FC = ({ + title, + value, + trend = [10, 20, 15, 30, 25, 40], + changePercent = 12.5, +}) => { + const min = Math.min(...trend); + const max = Math.max(...trend, 1); + const points = trend.map((val, idx) => { + const x = (idx / (trend.length - 1)) * 100; + const y = 30 - ((val - min) / (max - min)) * 25; + return `${x},${y}`; + }).join(' '); + + return ( +
+

{title}

+
+ {value} + = 0 ? 'text-emerald-500' : 'text-rose-500'}`}> + {changePercent >= 0 ? '+' : ''}{changePercent}% + +
+ + + +
+ ); +}; + +export default SparklineKpiCard;