Reseller-dashboard/src/pages/Customers/index.tsx
2025-08-03 18:49:28 +05:30

354 lines
15 KiB
TypeScript

import React, { useState } from 'react';
import {
Users,
Search,
Filter,
Plus,
Eye,
Edit,
Mail,
MoreHorizontal,
Download,
Send,
UserCheck,
UserX,
Building,
Phone,
Mail as MailIcon,
Calendar,
DollarSign,
Server
} from 'lucide-react';
import { mockCustomers } from '../../data/mockData';
import DualCurrencyDisplay from '../../components/DualCurrencyDisplay';
import { formatDate, formatNumber } from '../../utils/format';
import { cn } from '../../utils/cn';
import Modal from '../../components/Modal';
import DetailView from '../../components/DetailView';
import AddCustomerForm from '../../components/forms/AddCustomerForm';
import EditCustomerForm from '../../components/forms/EditCustomerForm';
import MailComposeForm from '../../components/forms/MailComposeForm';
import MoreOptionsDropdown from '../../components/MoreOptionsDropdown';
const Customers: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('');
const [selectedStatus, setSelectedStatus] = useState('all');
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isMailModalOpen, setIsMailModalOpen] = useState(false);
const [showMoreOptions, setShowMoreOptions] = useState<string | null>(null);
const [selectedCustomer, setSelectedCustomer] = useState<any>(null);
const filteredCustomers = mockCustomers.filter(customer => {
const matchesSearch = customer.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
customer.email.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus = selectedStatus === 'all' || customer.status === selectedStatus;
return matchesSearch && matchesStatus;
});
const stats = {
total: mockCustomers.length,
active: mockCustomers.filter(c => c.status === 'active').length,
pending: mockCustomers.filter(c => c.status === 'pending').length,
totalRevenue: mockCustomers.reduce((sum, c) => sum + c.totalSpent, 0)
};
const handleAddCustomer = (data: any) => {
console.log('Adding customer:', data);
setIsAddModalOpen(false);
};
const handleViewCustomer = (customer: any) => {
setSelectedCustomer(customer);
setIsDetailModalOpen(true);
};
const handleEditCustomer = (customer: any) => {
setSelectedCustomer(customer);
setIsEditModalOpen(true);
};
const handleMailCustomer = (customer: any) => {
setSelectedCustomer(customer);
setIsMailModalOpen(true);
};
const handleMoreOptions = (customerId: string) => {
setShowMoreOptions(showMoreOptions === customerId ? null : customerId);
};
const getStatusBadge = (status: string) => {
switch (status) {
case 'active':
return <span className="badge badge-success">Active</span>;
case 'pending':
return <span className="badge badge-warning">Pending</span>;
case 'inactive':
return <span className="badge badge-danger">Inactive</span>;
default:
return <span className="badge badge-secondary">{status}</span>;
}
};
const getTierBadge = (tier: string) => {
switch (tier) {
case 'platinum':
return <span className="badge badge-primary">Platinum</span>;
case 'gold':
return <span className="badge badge-warning">Gold</span>;
case 'silver':
return <span className="badge badge-secondary">Silver</span>;
default:
return <span className="badge badge-secondary">{tier}</span>;
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-emerald-50 dark:from-slate-900 dark:via-slate-800 dark:to-emerald-900/20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between space-y-4 sm:space-y-0 mb-8">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-slate-900 dark:text-white">
Customers
</h1>
<p className="text-slate-600 dark:text-slate-400 mt-1">
Manage your customer relationships and accounts
</p>
</div>
<button
onClick={() => setIsAddModalOpen(true)}
className="btn btn-primary btn-lg"
>
<Plus className="w-5 h-5 mr-2" />
Add New Customer
</button>
</div>
{/* Stats Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 mb-8">
<div className="card-elevated p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-slate-600 dark:text-slate-400">Total Customers</p>
<p className="text-2xl font-bold text-slate-900 dark:text-white">{stats.total}</p>
</div>
<div className="w-12 h-12 bg-gradient-to-br from-slate-500 to-slate-600 rounded-xl flex items-center justify-center">
<Users className="w-6 h-6 text-white" />
</div>
</div>
</div>
<div className="card-elevated p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-slate-600 dark:text-slate-400">Active Customers</p>
<p className="text-2xl font-bold text-slate-900 dark:text-white">{stats.active}</p>
</div>
<div className="w-12 h-12 bg-gradient-to-br from-green-500 to-green-600 rounded-xl flex items-center justify-center">
<Users className="w-6 h-6 text-white" />
</div>
</div>
</div>
<div className="card-elevated p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-slate-600 dark:text-slate-400">Pending</p>
<p className="text-2xl font-bold text-slate-900 dark:text-white">{stats.pending}</p>
</div>
<div className="w-12 h-12 bg-gradient-to-br from-amber-500 to-amber-600 rounded-xl flex items-center justify-center">
<Users className="w-6 h-6 text-white" />
</div>
</div>
</div>
<div className="card-elevated p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-slate-600 dark:text-slate-400">Total Revenue</p>
<DualCurrencyDisplay
amount={stats.totalRevenue}
currency="INR"
className="text-2xl font-bold"
/>
</div>
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center">
<DollarSign className="w-6 h-6 text-white" />
</div>
</div>
</div>
</div>
{/* Filters and Search */}
<div className="card-elevated p-6 mb-8">
<div className="flex flex-col sm:flex-row gap-4">
<div className="flex-1">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400" />
<input
type="text"
placeholder="Search customers..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="input pl-10 w-full"
/>
</div>
</div>
<div className="flex gap-2">
<select
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value)}
className="input"
>
<option value="all">All Status</option>
<option value="active">Active</option>
<option value="pending">Pending</option>
<option value="inactive">Inactive</option>
</select>
<button className="btn btn-secondary btn-md">
<Filter className="w-4 h-4 mr-2" />
Filters
</button>
</div>
</div>
</div>
{/* Customers Table */}
<div className="card-elevated overflow-hidden">
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>Customer</th>
<th>Contact</th>
<th>Status</th>
<th>Tier</th>
<th>Total Spent</th>
<th>Instances</th>
<th>Last Active</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{filteredCustomers.map((customer) => (
<tr key={customer.id}>
<td>
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-br from-emerald-500 to-emerald-600 rounded-lg flex items-center justify-center">
<Building className="w-5 h-5 text-white" />
</div>
<div>
<p className="font-semibold text-slate-900 dark:text-white">{customer.name}</p>
<p className="text-sm text-slate-500 dark:text-slate-400">{customer.email}</p>
</div>
</div>
</td>
<td>
<div className="flex items-center space-x-2">
<Phone className="w-4 h-4 text-slate-400" />
<p className="text-sm text-slate-600 dark:text-slate-400">{customer.phone}</p>
</div>
</td>
<td>
{getStatusBadge(customer.status)}
</td>
<td>
{getTierBadge(customer.tier)}
</td>
<td>
<DualCurrencyDisplay
amount={customer.totalSpent}
currency="INR"
className="font-semibold"
/>
</td>
<td>
<div className="flex items-center space-x-2">
<Server className="w-4 h-4 text-slate-400" />
<span className="text-sm font-medium">{customer.instances}</span>
</div>
</td>
<td>
<div className="flex items-center space-x-1">
<Calendar className="w-4 h-4 text-slate-400" />
<span className="text-sm">{formatDate(customer.lastActive)}</span>
</div>
</td>
<td>
<div className="flex items-center space-x-2">
<button
onClick={() => handleViewCustomer(customer)}
className="p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
title="View Details"
>
<Eye className="w-4 h-4 text-slate-600 dark:text-slate-400" />
</button>
<button
onClick={() => handleEditCustomer(customer)}
className="p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
title="Edit Customer"
>
<Edit className="w-4 h-4 text-slate-600 dark:text-slate-400" />
</button>
<button
onClick={() => handleMailCustomer(customer)}
className="p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
title="Send Email"
>
<Mail className="w-4 h-4 text-slate-600 dark:text-slate-400" />
</button>
<div className="relative">
<button
onClick={() => handleMoreOptions(customer.id)}
className="p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
title="More Options"
>
<MoreHorizontal className="w-4 h-4 text-slate-600 dark:text-slate-400" />
</button>
{showMoreOptions === customer.id && (
<MoreOptionsDropdown
itemType="customer"
onViewPerformance={() => handleViewCustomer(customer)}
onDownloadReport={() => console.log('Download report')}
onSendNotification={() => console.log('Send notification')}
onChangeTier={() => console.log('Change tier')}
onDeactivate={() => console.log('Deactivate customer')}
onDelete={() => console.log('Delete customer')}
onSendMail={() => handleMailCustomer(customer)}
onClose={() => setShowMoreOptions(null)}
/>
)}
</div>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* Modals */}
<Modal isOpen={isAddModalOpen} onClose={() => setIsAddModalOpen(false)} title="Add New Customer">
<AddCustomerForm onSubmit={handleAddCustomer} onCancel={() => setIsAddModalOpen(false)} />
</Modal>
<Modal isOpen={isDetailModalOpen} onClose={() => setIsDetailModalOpen(false)} title="Customer Details">
{selectedCustomer && <DetailView data={selectedCustomer} type="customer" />}
</Modal>
<Modal isOpen={isEditModalOpen} onClose={() => setIsEditModalOpen(false)} title="Edit Customer">
{selectedCustomer && <EditCustomerForm customer={selectedCustomer} onSubmit={handleEditCustomer} onCancel={() => setIsEditModalOpen(false)} />}
</Modal>
<Modal isOpen={isMailModalOpen} onClose={() => setIsMailModalOpen(false)} title="Send Email">
{selectedCustomer && <MailComposeForm recipient={selectedCustomer.name} onSubmit={() => setIsMailModalOpen(false)} onCancel={() => setIsMailModalOpen(false)} />}
</Modal>
</div>
</div>
);
};
export default Customers;