Update favicon and site title
This commit is contained in:
47
src/features/partners/PartnerDirectory.tsx
Normal file
47
src/features/partners/PartnerDirectory.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useState } from 'react';
|
||||
import { AppLayout } from '@/components/layout/AppLayout';
|
||||
import { PartnerFilters } from './components/PartnerFilters';
|
||||
import { PartnerCard } from './components/PartnerCard';
|
||||
import { mockPartners } from '@/data/mockPartnerData';
|
||||
|
||||
export default function PartnerDirectory() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const filteredPartners = mockPartners.filter(partner =>
|
||||
partner.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
partner.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
partner.primaryContact.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<AppLayout title="Partners">
|
||||
<div className="p-6 max-w-[1600px] mx-auto space-y-8">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Partner Management</h1>
|
||||
<p className="text-muted-foreground">Manage your relationships with venues, promoters, sponsors, and vendors.</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-card/30 p-1 rounded-xl glass-panel">
|
||||
<PartnerFilters
|
||||
onSearch={setSearchQuery}
|
||||
onFilter={() => console.log('Filter clicked')}
|
||||
onAdd={() => console.log('Add clicked')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{filteredPartners.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{filteredPartners.map(partner => (
|
||||
<PartnerCard key={partner.id} partner={partner} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-20 bg-card/20 rounded-xl border border-dashed border-border/50">
|
||||
<h3 className="text-lg font-medium text-foreground">No partners found</h3>
|
||||
<p className="text-muted-foreground mt-2">Try adjusting your search or filters</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
277
src/features/partners/PartnerProfile.tsx
Normal file
277
src/features/partners/PartnerProfile.tsx
Normal file
@@ -0,0 +1,277 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { AppLayout } from '@/components/layout/AppLayout';
|
||||
import { mockPartners, mockDealTerms, mockLedger, mockDocuments } from '@/data/mockPartnerData';
|
||||
import { StatusBadge, TypeBadge } from './components/PartnerBadges';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Calendar, Download, Edit, FileText, Mail, Phone, ExternalLink, Wallet } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function PartnerProfile() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
// In a real app, fetch data based on ID
|
||||
const partner = mockPartners.find(p => p.id === id) || mockPartners[0];
|
||||
const dealTerms = mockDealTerms.filter(dt => dt.partnerId === partner.id);
|
||||
const ledger = mockLedger.filter(l => l.partnerId === partner.id);
|
||||
const documents = mockDocuments.filter(d => d.partnerId === partner.id);
|
||||
|
||||
if (!partner) return <div>Partner not found</div>;
|
||||
|
||||
return (
|
||||
<AppLayout title={partner.name}>
|
||||
<div className="p-6 max-w-[1600px] mx-auto space-y-6">
|
||||
{/* Header Profile */}
|
||||
<div className="relative overflow-hidden rounded-2xl bg-card border border-border/50 shadow-lg group">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-accent/5 to-transparent opacity-50" />
|
||||
|
||||
<div className="relative p-8 flex flex-col md:flex-row gap-8 items-start">
|
||||
<div className="h-28 w-28 rounded-2xl bg-secondary flex items-center justify-center overflow-hidden border-2 border-border shadow-2xl">
|
||||
{partner.logo ? (
|
||||
<img src={partner.logo} alt={partner.name} className="h-full w-full object-cover" />
|
||||
) : (
|
||||
<span className="text-3xl font-bold text-muted-foreground">{partner.name.substring(0, 2)}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-4">
|
||||
<div className="flex justify-between items-start">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground flex items-center gap-3">
|
||||
{partner.name}
|
||||
<StatusBadge status={partner.status} />
|
||||
</h1>
|
||||
<div className="flex items-center gap-4 mt-2 text-muted-foreground">
|
||||
<TypeBadge type={partner.type} />
|
||||
<span className="flex items-center gap-1 text-sm"><Calendar className="h-4 w-4" /> Joined {new Date(partner.joinedAt).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" className="gap-2"><Edit className="h-4 w-4" /> Edit Profile</Button>
|
||||
<Button className="bg-accent text-white gap-2"><Wallet className="h-4 w-4" /> New Settlement</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-6 pt-4 border-t border-border/30">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-8 w-8 rounded-full bg-secondary/50 flex items-center justify-center text-primary">
|
||||
<span className="text-xs font-bold">{partner.primaryContact.name.substring(0, 2)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium">{partner.primaryContact.name}</p>
|
||||
<p className="text-xs text-muted-foreground">{partner.primaryContact.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-sm text-foreground/80 bg-secondary/30 px-4 py-2 rounded-lg border border-border/30">
|
||||
<a href={`mailto:${partner.primaryContact.email}`} className="flex items-center gap-2 hover:text-accent"><Mail className="h-4 w-4" /> {partner.primaryContact.email}</a>
|
||||
{partner.primaryContact.phone && (
|
||||
<span className="flex items-center gap-2 border-l border-border pl-4"><Phone className="h-4 w-4" /> {partner.primaryContact.phone}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="neu-card p-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wider">Total Revenue</p>
|
||||
<p className="text-2xl font-bold mt-1">₹{partner.metrics.totalRevenue.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="h-10 w-10 rounded-full bg-success/10 flex items-center justify-center text-success">
|
||||
<Wallet className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="neu-card p-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wider">Open Balance</p>
|
||||
<p className="text-2xl font-bold mt-1">₹{partner.metrics.openBalance.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="h-10 w-10 rounded-full bg-warning/10 flex items-center justify-center text-warning">
|
||||
<Wallet className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="neu-card p-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wider">Active Deals</p>
|
||||
<p className="text-2xl font-bold mt-1">{partner.metrics.activeDeals}</p>
|
||||
</div>
|
||||
<div className="h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center text-primary">
|
||||
<FileText className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="neu-card p-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wider">Events</p>
|
||||
<p className="text-2xl font-bold mt-1">{partner.metrics.eventsCount}</p>
|
||||
</div>
|
||||
<div className="h-10 w-10 rounded-full bg-accent/10 flex items-center justify-center text-accent">
|
||||
<Calendar className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs Content */}
|
||||
<div className="neu-card min-h-[500px]">
|
||||
<Tabs defaultValue="overview" className="w-full">
|
||||
<div className="border-b border-border/40 px-6 pt-4">
|
||||
<TabsList className="bg-transparent h-auto p-0 gap-6">
|
||||
<TabsTrigger value="overview" className="tab-trigger pb-4 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-accent data-[state=active]:bg-transparent">Overview</TabsTrigger>
|
||||
<TabsTrigger value="assignments" className="tab-trigger pb-4 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-accent data-[state=active]:bg-transparent">Assignments</TabsTrigger>
|
||||
<TabsTrigger value="terms" className="tab-trigger pb-4 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-accent data-[state=active]:bg-transparent">Deal Terms</TabsTrigger>
|
||||
<TabsTrigger value="finance" className="tab-trigger pb-4 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-accent data-[state=active]:bg-transparent">Financials</TabsTrigger>
|
||||
<TabsTrigger value="docs" className="tab-trigger pb-4 rounded-none data-[state=active]:border-b-2 data-[state=active]:border-accent data-[state=active]:bg-transparent">Documents</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<TabsContent value="overview" className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-semibold text-lg">Partner Details</h3>
|
||||
<div className="grid grid-cols-2 gap-y-4 text-sm">
|
||||
<span className="text-muted-foreground">Legal Name</span>
|
||||
<span>{partner.companyDetails?.legalName || partner.name}</span>
|
||||
<span className="text-muted-foreground">Tax ID</span>
|
||||
<span>{partner.companyDetails?.taxId || '-'}</span>
|
||||
<span className="text-muted-foreground">Website</span>
|
||||
<a href={partner.companyDetails?.website} target="_blank" className="text-accent hover:underline flex items-center gap-1">{partner.companyDetails?.website || '-'} <ExternalLink className="h-3 w-3" /></a>
|
||||
<span className="text-muted-foreground">Address</span>
|
||||
<span>{partner.companyDetails?.address || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-semibold text-lg">Tags & Notes</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{partner.tags.map(tag => (
|
||||
<Badge key={tag} variant="secondary" className="px-3 py-1">{tag}</Badge>
|
||||
))}
|
||||
</div>
|
||||
<div className="p-4 bg-secondary/30 rounded-lg text-sm text-balance">
|
||||
{partner.notes || "No notes added for this partner."}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="finance">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="font-semibold text-lg">Ledger & Settlements</h3>
|
||||
<Button variant="outline" size="sm" className="gap-2"><Download className="h-4 w-4" /> Export CSV</Button>
|
||||
</div>
|
||||
<div className="border border-border/50 rounded-lg overflow-hidden">
|
||||
<table className="w-full text-sm text-left">
|
||||
<thead className="bg-secondary/50 text-muted-foreground">
|
||||
<tr>
|
||||
<th className="p-3">Date</th>
|
||||
<th className="p-3">Description</th>
|
||||
<th className="p-3">Type</th>
|
||||
<th className="p-3 text-right">Amount</th>
|
||||
<th className="p-3 text-center">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border/30">
|
||||
{ledger.map(entry => (
|
||||
<tr key={entry.id} className="hover:bg-accent/5">
|
||||
<td className="p-3">{new Date(entry.createdAt).toLocaleDateString()}</td>
|
||||
<td className="p-3">
|
||||
<div className="font-medium">{entry.description}</div>
|
||||
{entry.referenceId && <div className="text-xs text-muted-foreground">Ref: {entry.referenceId}</div>}
|
||||
</td>
|
||||
<td className="p-3"><Badge variant="outline">{entry.type}</Badge></td>
|
||||
<td className={cn("p-3 text-right font-medium", entry.amount < 0 ? "text-error" : "text-success")}>
|
||||
{entry.amount < 0 ? '-' : '+'}₹{Math.abs(entry.amount).toLocaleString()}
|
||||
</td>
|
||||
<td className="p-3 text-center">
|
||||
<span className={cn("text-xs px-2 py-1 rounded-full border",
|
||||
entry.status === 'Cleared' ? 'bg-success/10 border-success/20 text-success' :
|
||||
entry.status === 'Pending' ? 'bg-warning/10 border-warning/20 text-warning' : 'bg-muted border-border'
|
||||
)}>{entry.status}</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{ledger.length === 0 && <div className="p-8 text-center text-muted-foreground">No transactions found</div>}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="docs">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="font-semibold text-lg">Contracts & Documents</h3>
|
||||
<Button variant="outline" size="sm" className="gap-2"><Plus className="h-4 w-4" /> Upload Document</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{documents.map(doc => (
|
||||
<div key={doc.id} className="p-4 border border-border/30 rounded-lg bg-card/50 flex items-start gap-3 hover:border-accent/40 transition-colors">
|
||||
<div className="h-10 w-10 bg-secondary rounded-lg flex items-center justify-center text-muted-foreground">
|
||||
<FileText className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<p className="font-medium truncate">{doc.name}</p>
|
||||
<p className="text-xs text-muted-foreground capitalize">{doc.type} • {doc.status}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">Uploaded {new Date(doc.uploadedAt).toLocaleDateString()}</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8"><Download className="h-4 w-4" /></Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="terms">
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h3 className="font-semibold text-lg">Active Deal Terms</h3>
|
||||
<Button size="sm">Add New Term</Button>
|
||||
</div>
|
||||
{dealTerms.map(term => (
|
||||
<div key={term.id} className="p-4 border border-border/50 rounded-xl bg-gradient-to-br from-card to-secondary/30">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div>
|
||||
<h4 className="font-bold flex items-center gap-2">
|
||||
{term.name}
|
||||
<Badge variant="secondary" className="text-xs font-normal">v{term.version}</Badge>
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground mt-1">Effective from {new Date(term.effectiveFrom).toLocaleDateString()}</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="bg-success/5 border-success/20 text-success">{term.status}</Badge>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 p-3 bg-secondary/50 rounded-lg text-sm grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<span className="text-muted-foreground block text-xs uppercase">Type</span>
|
||||
<span className="font-medium">{term.type}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground block text-xs uppercase">Parameters</span>
|
||||
<span className="font-medium">
|
||||
{term.type === 'RevenueShare' ? `${term.params.percentage}% Share` :
|
||||
term.type === 'CommissionPerTicket' ? `₹${term.params.amount} per ticket` : 'Custom'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="assignments">
|
||||
<div className="p-8 text-center border-2 border-dashed border-border/50 rounded-xl">
|
||||
<Calendar className="h-12 w-12 text-muted-foreground mx-auto mb-4 opacity-50" />
|
||||
<h3 className="text-lg font-medium">No event assignments yet</h3>
|
||||
<p className="text-muted-foreground mb-4">Assign this partner to an upcoming event</p>
|
||||
<Button>Assign to Event</Button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
|
||||
function Plus({ className }: { className?: string }) {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M5 12h14" /><path d="M12 5v14" /></svg>
|
||||
}
|
||||
35
src/features/partners/components/PartnerBadges.tsx
Normal file
35
src/features/partners/components/PartnerBadges.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { PartnerStatus, PartnerType } from '@/types/partner';
|
||||
|
||||
export const StatusBadge = ({ status }: { status: PartnerStatus }) => {
|
||||
const styles = {
|
||||
Active: 'bg-success/10 text-success border-success/20',
|
||||
Invited: 'bg-primary/10 text-primary border-primary/20',
|
||||
Suspended: 'bg-error/10 text-error border-error/20',
|
||||
Archived: 'bg-muted text-muted-foreground border-border',
|
||||
};
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={cn("font-medium", styles[status])}>
|
||||
{status}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const TypeBadge = ({ type }: { type: PartnerType }) => {
|
||||
const styles = {
|
||||
Venue: 'text-purple-400 border-purple-400/30 bg-purple-400/10',
|
||||
Promoter: 'text-amber-400 border-amber-400/30 bg-amber-400/10',
|
||||
Sponsor: 'text-emerald-400 border-emerald-400/30 bg-emerald-400/10',
|
||||
Vendor: 'text-blue-400 border-blue-400/30 bg-blue-400/10',
|
||||
Affiliate: 'text-pink-400 border-pink-400/30 bg-pink-400/10',
|
||||
Other: 'text-gray-400 border-gray-400/30 bg-gray-400/10',
|
||||
};
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={cn("font-medium", styles[type])}>
|
||||
{type}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
82
src/features/partners/components/PartnerCard.tsx
Normal file
82
src/features/partners/components/PartnerCard.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { MoreHorizontal, ExternalLink, Calendar, Wallet } from 'lucide-react';
|
||||
import { Partner } from '@/types/partner';
|
||||
import { StatusBadge, TypeBadge } from './PartnerBadges';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface PartnerCardProps {
|
||||
partner: Partner;
|
||||
}
|
||||
|
||||
export function PartnerCard({ partner }: PartnerCardProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group relative neu-card p-5 hover:border-accent/50 transition-all duration-300 cursor-pointer"
|
||||
onClick={() => navigate(`/partners/${partner.id}`)}
|
||||
>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-12 w-12 rounded-xl bg-secondary flex items-center justify-center overflow-hidden border border-border/50">
|
||||
{partner.logo ? (
|
||||
<img src={partner.logo} alt={partner.name} className="h-full w-full object-cover" />
|
||||
) : (
|
||||
<span className="text-lg font-bold text-muted-foreground">{partner.name.substring(0, 2)}</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground group-hover:text-accent transition-colors">{partner.name}</h3>
|
||||
<div className="flex gap-2 mt-1">
|
||||
<TypeBadge type={partner.type} />
|
||||
<StatusBadge status={partner.status} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => navigate(`/partners/${partner.id}`)}>View Details</DropdownMenuItem>
|
||||
<DropdownMenuItem>Assign to Event</DropdownMenuItem>
|
||||
<DropdownMenuItem className="text-error">Suspend Partner</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mt-6 py-4 border-t border-border/30">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-1 flex items-center gap-1">
|
||||
<Calendar className="h-3 w-3" /> Active Deals
|
||||
</p>
|
||||
<p className="font-semibold text-foreground">{partner.metrics.activeDeals}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-1 flex items-center gap-1">
|
||||
<Wallet className="h-3 w-3" /> Open Balance
|
||||
</p>
|
||||
<p className="font-semibold text-foreground">₹{partner.metrics.openBalance.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>{partner.primaryContact.name}</span>
|
||||
<span className="flex items-center gap-1 hover:text-accent">
|
||||
View Portal <ExternalLink className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
src/features/partners/components/PartnerFilters.tsx
Normal file
35
src/features/partners/components/PartnerFilters.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Search, Filter, Plus } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
||||
interface PartnerFiltersProps {
|
||||
onSearch: (query: string) => void;
|
||||
onFilter: () => void;
|
||||
onAdd: () => void;
|
||||
}
|
||||
|
||||
export function PartnerFilters({ onSearch, onFilter, onAdd }: PartnerFiltersProps) {
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-between items-center mb-6">
|
||||
<div className="relative w-full sm:w-96">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search partners by name, type, or contact..."
|
||||
className="pl-10 bg-secondary border-border/50 focus:border-accent"
|
||||
onChange={(e) => onSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 w-full sm:w-auto">
|
||||
<Button variant="outline" onClick={onFilter} className="gap-2">
|
||||
<Filter className="h-4 w-4" />
|
||||
Filters
|
||||
</Button>
|
||||
<Button onClick={onAdd} className="gap-2 bg-accent text-white hover:bg-accent/90 shadow-lg shadow-accent/20">
|
||||
<Plus className="h-4 w-4" />
|
||||
Add Partner
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user