planpost-affiliate-ui/src/pages/Overview.tsx
S M Fahim Hossen 085417ab7e update
2025-10-08 16:04:20 +06:00

232 lines
7.7 KiB
TypeScript

import React, { useEffect, useState } from "react";
import {
Copy,
TrendingUp,
Users,
DollarSign,
Clock,
CheckCircle2,
} from "lucide-react";
import { useUserStore } from "@/stores/userStore";
import { useAffiliateStore } from "@/stores/affiliateStore";
type MetricsCardProps = {
title: string;
value: string | number;
subtitle?: string;
icon: React.ElementType;
trend?: string;
bgColor: string;
};
const MetricsCard = ({
title,
value,
subtitle,
icon: Icon,
trend,
bgColor,
}: MetricsCardProps) => (
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between mb-3 sm:mb-4">
<div className={`${bgColor} p-2 sm:p-3 rounded-lg`}>
<Icon className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div>
{trend && (
<span className="flex items-center text-xs sm:text-sm font-medium text-green-600 bg-green-50 px-2 py-1 rounded-full">
<TrendingUp className="w-3 h-3 mr-1" />
{trend}
</span>
)}
</div>
<h3 className="text-gray-600 text-xs sm:text-sm font-medium mb-1">
{title}
</h3>
<p className="text-xl sm:text-2xl font-bold text-gray-900">{value}</p>
{subtitle && (
<p className="text-xs sm:text-sm text-gray-500 mt-1">{subtitle}</p>
)}
</div>
);
const Overview = () => {
const [copied, setCopied] = useState(false);
const { user, fetchUser, isLoading: userLoading } = useUserStore();
const {
data: affiliateData,
fetchEarnings,
isLoading: earningsLoading,
} = useAffiliateStore();
useEffect(() => {
// Get user ID from localStorage
const userStr = localStorage.getItem("aff-user");
if (userStr) {
const userData = JSON.parse(userStr);
if (userData.id) {
fetchUser(userData.id);
fetchEarnings(userData.id);
}
}
}, [fetchUser, fetchEarnings]);
const referralLink = `https://dashboard.planpostai.com/sign-up?ref=${user?.referral_code}`;
const handleCopy = () => {
navigator.clipboard.writeText(referralLink);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
// Calculate metrics from affiliate data
const totalEarnings = affiliateData?.summary.total_earning || 0;
const totalWithdraw = affiliateData?.summary.total_withdraw || 0;
const pendingAmount = affiliateData?.summary.pending_amount || 0;
const totalReferrals = affiliateData?.total || 0;
// Calculate average commission
const averageCommission =
totalReferrals > 0 ? totalEarnings / totalReferrals : 0;
const isLoading = userLoading || earningsLoading;
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Loading...</div>
</div>
);
}
return (
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-0 ml-3">
{/* Header */}
<div className="mb-6 sm:mb-8">
<h3 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">
Dashboard Overview
</h3>
<p className="text-sm sm:text-base text-gray-600">
Track your affiliate performance and earnings
</p>
</div>
{/* Metrics Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 mb-6 sm:mb-8">
<MetricsCard
title="Total Earnings"
value={`$${totalEarnings.toFixed(2)}`}
icon={DollarSign}
bgColor="bg-gradient-to-br from-blue-500 to-blue-600"
/>
<MetricsCard
title="Total Referrals"
value={totalReferrals}
subtitle={`${affiliateData?.history.length || 0} transactions`}
icon={Users}
bgColor="bg-gradient-to-br from-purple-500 to-purple-600"
/>
<MetricsCard
title="Total Withdrawn"
value={`$${totalWithdraw.toFixed(2)}`}
icon={TrendingUp}
bgColor="bg-gradient-to-br from-green-500 to-green-600"
/>
<MetricsCard
title="Pending Commission"
value={`$${Math.max(pendingAmount, 0).toFixed(2)}`}
icon={Clock}
bgColor="bg-gradient-to-br from-orange-500 to-orange-600"
/>
</div>
{/* Referral Link Section */}
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-4 sm:p-6 shadow-sm border border-blue-100">
<div className="flex items-center gap-2 mb-3 sm:mb-4">
<div className="bg-blue-500 p-2 rounded-lg">
<Users className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
</div>
<h2 className="text-lg sm:text-xl font-semibold text-gray-900">
Your Referral Link
</h2>
</div>
<p className="text-gray-600 text-xs sm:text-sm mb-3 sm:mb-4">
Share this link with your network to start earning commissions
</p>
<div className="flex flex-col sm:flex-row gap-3">
<div className="flex-1 relative">
<input
type="text"
value={referralLink}
readOnly
className="w-full border-2 border-gray-200 rounded-lg px-3 sm:px-4 py-2.5 sm:py-3 bg-white focus:outline-none focus:border-blue-500 transition-colors font-mono text-xs sm:text-sm"
/>
</div>
<button
onClick={handleCopy}
className={`px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg font-medium transition-all flex items-center justify-center gap-2 min-w-[100px] sm:min-w-[120px] text-sm sm:text-base ${
copied
? "bg-green-500 text-white"
: "bg-blue-600 text-white hover:bg-blue-700 active:scale-95"
}`}
>
{copied ? (
<>
<CheckCircle2 className="w-4 h-4" />
Copied!
</>
) : (
<>
<Copy className="w-4 h-4" />
Copy
</>
)}
</button>
</div>
</div>
{/* Quick Stats */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 mt-6 sm:mt-8">
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6">
<h3 className="text-xs sm:text-sm font-medium text-gray-600 mb-2 sm:mb-3">
Average Commission
</h3>
<p className="text-2xl sm:text-3xl font-bold text-gray-900">
${averageCommission.toFixed(2)}
</p>
<p className="text-xs sm:text-sm text-gray-500 mt-2">Per referral</p>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6">
<h3 className="text-xs sm:text-sm font-medium text-gray-600 mb-2 sm:mb-3">
Available Balance
</h3>
<p className="text-2xl sm:text-3xl font-bold text-gray-900">
${(totalEarnings - totalWithdraw).toFixed(2)}
</p>
<p className="text-xs sm:text-sm text-green-600 mt-2">
Ready to withdraw
</p>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6 sm:col-span-2 lg:col-span-1">
<h3 className="text-xs sm:text-sm font-medium text-gray-600 mb-2 sm:mb-3">
Latest Transaction
</h3>
<p className="text-2xl sm:text-3xl font-bold text-gray-900">
${affiliateData?.history[0]?.amount.toFixed(2) || "0.00"}
</p>
<p className="text-xs sm:text-sm text-gray-500 mt-2">
{affiliateData?.history[0]
? new Date(
affiliateData.history[0].created_at
).toLocaleDateString()
: "No transactions yet"}
</p>
</div>
</div>
</div>
);
};
export default Overview;