planpost-affiliate-ui/src/pages/Referrals.tsx
2025-10-04 12:06:08 +06:00

420 lines
16 KiB
TypeScript

import React, { useState } from "react";
import {
Users,
DollarSign,
TrendingUp,
Link2,
Copy,
Search,
Filter,
Download,
Calendar,
CheckCircle,
Clock,
MoreVertical,
} from "lucide-react";
interface Referral {
name: string;
email: string;
date: string;
earnings: string;
status: "Active" | "Pending";
signupDate: string;
totalCommission: string;
country: string;
}
const referrals: Referral[] = [
{
name: "John Doe",
email: "john@example.com",
date: "2025-09-20",
earnings: "$50",
status: "Active",
signupDate: "2025-08-15",
totalCommission: "$450",
country: "United States",
},
{
name: "Jane Smith",
email: "jane@example.com",
date: "2025-09-18",
earnings: "$20",
status: "Pending",
signupDate: "2025-09-10",
totalCommission: "$20",
country: "Canada",
},
{
name: "Michael Chen",
email: "michael@example.com",
date: "2025-09-15",
earnings: "$120",
status: "Active",
signupDate: "2025-07-22",
totalCommission: "$890",
country: "Singapore",
},
{
name: "Sarah Johnson",
email: "sarah@example.com",
date: "2025-09-12",
earnings: "$85",
status: "Active",
signupDate: "2025-06-30",
totalCommission: "$1,240",
country: "United Kingdom",
},
{
name: "David Williams",
email: "david@example.com",
date: "2025-09-25",
earnings: "$15",
status: "Pending",
signupDate: "2025-09-20",
totalCommission: "$15",
country: "Australia",
},
];
const Referrals: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [copied, setCopied] = useState(false);
const referralLink = "https://affiliatepro.com/ref/ABC123";
const handleCopy = () => {
navigator.clipboard.writeText(referralLink);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const stats = [
{
label: "Total Referrals",
value: "5",
icon: Users,
color: "bg-blue-500",
change: "+12% from last month",
},
{
label: "Active Referrals",
value: "3",
icon: CheckCircle,
color: "bg-green-500",
change: "60% conversion rate",
},
{
label: "Total Earnings",
value: "$2,615",
icon: DollarSign,
color: "bg-purple-500",
change: "+$450 this month",
},
{
label: "Avg. Per Referral",
value: "$523",
icon: TrendingUp,
color: "bg-orange-500",
change: "+18% growth",
},
];
const filteredReferrals = referrals.filter(
(r) =>
r.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
r.email.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="min-h-screen bg-gray-50 p-4">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="mb-6 sm:mb-8">
<h3 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">
Referrals
</h3>
<p className="text-sm sm:text-base text-gray-600">
Track and manage your affiliate referrals
</p>
</div>
{/* Stats 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">
{stats.map((stat, idx) => (
<div
key={idx}
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-center justify-between mb-3 sm:mb-4">
<div
className={`${stat.color} w-10 h-10 sm:w-12 sm:h-12 rounded-lg flex items-center justify-center`}
>
<stat.icon className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div>
</div>
<div className="text-2xl sm:text-3xl font-bold text-gray-900 mb-1">
{stat.value}
</div>
<div className="text-xs sm:text-sm text-gray-600 mb-2">
{stat.label}
</div>
<div className="text-xs text-gray-500">{stat.change}</div>
</div>
))}
</div>
{/* Referral Link Section */}
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-xl p-4 sm:p-6 mb-6 sm:mb-8 shadow-lg">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<Link2 className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
<h3 className="text-base sm:text-lg font-semibold text-white">
Your Referral Link
</h3>
</div>
<p className="text-indigo-100 text-xs sm:text-sm mb-3 sm:mb-4">
Share this link to earn commissions
</p>
<div className="flex flex-col sm:flex-row gap-3">
<div className="flex-1 bg-white bg-opacity-20 backdrop-blur-sm rounded-lg px-3 sm:px-4 py-2.5 sm:py-3 text-white font-mono text-xs sm:text-sm break-all">
{referralLink}
</div>
<button
onClick={handleCopy}
className="bg-white text-indigo-600 px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg font-semibold hover:bg-gray-50 transition-all flex items-center justify-center gap-2 shadow-md text-sm sm:text-base"
>
{copied ? (
<>
<CheckCircle className="w-4 h-4 sm:w-5 sm:h-5" />
Copied!
</>
) : (
<>
<Copy className="w-4 h-4 sm:w-5 sm:h-5" />
Copy Link
</>
)}
</button>
</div>
</div>
</div>
</div>
{/* Table Section */}
<div className="bg-white rounded-xl shadow-sm border border-gray-100">
{/* Table Header */}
<div className="p-4 sm:p-6 border-b border-gray-100">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<h2 className="text-lg sm:text-xl font-semibold text-gray-900">
Referral List
</h2>
<div className="flex gap-2 sm:gap-3">
<div className="relative flex-1 sm:flex-initial">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 sm:w-5 sm:h-5 text-gray-400" />
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-9 sm:pl-10 pr-3 sm:pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none w-full sm:w-64 text-sm"
/>
</div>
<button className="px-3 sm:px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors flex items-center gap-2">
<Filter className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
<span className="hidden sm:inline text-sm">Filter</span>
</button>
<button className="px-3 sm:px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors flex items-center gap-2">
<Download className="w-4 h-4 sm:w-5 sm:h-5" />
<span className="hidden sm:inline text-sm">Export</span>
</button>
</div>
</div>
</div>
{/* Mobile Card View */}
<div className="block lg:hidden">
{filteredReferrals.map((r, idx) => (
<div
key={idx}
className="p-4 border-b border-gray-100 hover:bg-gray-50 transition-colors"
>
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 to-purple-500 flex items-center justify-center text-white font-semibold text-sm">
{r.name.charAt(0)}
</div>
<div>
<div className="font-semibold text-gray-900 text-sm">
{r.name}
</div>
<div className="text-xs text-gray-500">{r.email}</div>
</div>
</div>
<span
className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-semibold ${
r.status === "Active"
? "bg-green-100 text-green-700"
: "bg-yellow-100 text-yellow-700"
}`}
>
{r.status === "Active" ? (
<CheckCircle className="w-3 h-3" />
) : (
<Clock className="w-3 h-3" />
)}
{r.status}
</span>
</div>
<div className="grid grid-cols-2 gap-3 text-sm">
<div>
<div className="text-xs text-gray-500 mb-1">
Signup Date
</div>
<div className="text-gray-700">{r.signupDate}</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">Country</div>
<div className="text-gray-700">{r.country}</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">
Last Earning
</div>
<div className="font-semibold text-gray-900">
{r.earnings}
</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">
Total Commission
</div>
<div className="font-semibold text-gray-900">
{r.totalCommission}
</div>
</div>
</div>
</div>
))}
</div>
{/* Desktop Table View */}
<div className="hidden lg:block overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 border-b border-gray-100">
<tr>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Referral
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Signup Date
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Country
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Last Earnings
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Total Commission
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700">
Status
</th>
<th className="text-left p-4 text-sm font-semibold text-gray-700"></th>
</tr>
</thead>
<tbody>
{filteredReferrals.map((r, idx) => (
<tr
key={idx}
className="border-b border-gray-100 hover:bg-gray-50 transition-colors"
>
<td className="p-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 to-purple-500 flex items-center justify-center text-white font-semibold">
{r.name.charAt(0)}
</div>
<div>
<div className="font-semibold text-gray-900">
{r.name}
</div>
<div className="text-sm text-gray-500">{r.email}</div>
</div>
</div>
</td>
<td className="p-4">
<div className="flex items-center gap-2 text-gray-700">
<Calendar className="w-4 h-4 text-gray-400" />
<span className="text-sm">{r.signupDate}</span>
</div>
</td>
<td className="p-4">
<span className="text-sm text-gray-700">{r.country}</span>
</td>
<td className="p-4">
<div className="flex items-center gap-1">
<DollarSign className="w-4 h-4 text-green-600" />
<span className="font-semibold text-gray-900">
{r.earnings}
</span>
</div>
<div className="text-xs text-gray-500">{r.date}</div>
</td>
<td className="p-4">
<span className="font-semibold text-gray-900">
{r.totalCommission}
</span>
</td>
<td className="p-4">
<span
className={`inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs font-semibold ${
r.status === "Active"
? "bg-green-100 text-green-700"
: "bg-yellow-100 text-yellow-700"
}`}
>
{r.status === "Active" ? (
<CheckCircle className="w-3 h-3" />
) : (
<Clock className="w-3 h-3" />
)}
{r.status}
</span>
</td>
<td className="p-4">
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
<MoreVertical className="w-5 h-5 text-gray-400" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Table Footer */}
<div className="p-4 border-t border-gray-100 flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="text-xs sm:text-sm text-gray-600">
Showing{" "}
<span className="font-semibold">{filteredReferrals.length}</span>{" "}
of <span className="font-semibold">{referrals.length}</span>{" "}
referrals
</div>
<div className="flex gap-2">
<button className="px-3 sm:px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors text-xs sm:text-sm font-medium">
Previous
</button>
<button className="px-3 sm:px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors text-xs sm:text-sm font-medium">
Next
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Referrals;