diff --git a/src/App.tsx b/src/App.tsx index 65a647d..42004c9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,6 +56,10 @@ import PolReports from "./pages/dashboard/politur/Reports"; import GuideDashboard from "./pages/dashboard/guides/GuideDashboard"; import ItineraryBuilder from "./pages/dashboard/guides/ItineraryBuilder"; import ContentLibrary from "./pages/dashboard/guides/ContentLibrary"; +// Commissions pages +import CommissionsDashboard from "./pages/dashboard/commissions/CommissionsDashboard"; +import CommissionRules from "./pages/dashboard/commissions/CommissionRules"; +import PaymentHistory from "./pages/dashboard/commissions/PaymentHistory"; // Tourist App import TouristApp from "./pages/TouristApp"; // Commerce pages (for retail stores) @@ -626,6 +630,31 @@ const AppRouter = () => ( } /> + {/* Commissions Routes */} + + + + + + } /> + + + + + + + } /> + + + + + + + } /> + {/* Catch-all route */} } /> diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx index 8d79d96..f642e17 100644 --- a/src/components/DashboardLayout.tsx +++ b/src/components/DashboardLayout.tsx @@ -197,6 +197,16 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => { { icon: BookOpen, label: 'Biblioteca', path: '/dashboard/guides/library' } ] }, + { + icon: DollarSign, + label: 'Comisiones', + path: '/dashboard/commissions', + subItems: [ + { icon: BarChart3, label: 'Dashboard', path: '/dashboard/commissions/dashboard' }, + { icon: Settings, label: 'Reglas', path: '/dashboard/commissions/rules' }, + { icon: FileText, label: 'Historial', path: '/dashboard/commissions/payments' } + ] + }, { icon: Store, label: t('commerce'), diff --git a/src/pages/dashboard/commissions/CommissionRules.tsx b/src/pages/dashboard/commissions/CommissionRules.tsx new file mode 100644 index 0000000..5b1c348 --- /dev/null +++ b/src/pages/dashboard/commissions/CommissionRules.tsx @@ -0,0 +1,267 @@ +import { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Plus, Edit, Trash2, Save } from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; + +interface CommissionRule { + id: number; + name: string; + category: string; + type: 'percentage' | 'fixed'; + value: number; + minAmount?: number; + maxAmount?: number; + active: boolean; +} + +const CommissionRules = () => { + const { toast } = useToast(); + const [rules, setRules] = useState([ + { + id: 1, + name: 'Hoteles Premium', + category: 'Alojamiento', + type: 'percentage', + value: 15, + minAmount: 100, + active: true, + }, + { + id: 2, + name: 'Tours Estándar', + category: 'Tours', + type: 'percentage', + value: 12, + active: true, + }, + { + id: 3, + name: 'Restaurantes', + category: 'Gastronomía', + type: 'percentage', + value: 10, + minAmount: 50, + active: true, + }, + { + id: 4, + name: 'Servicios Spa', + category: 'Bienestar', + type: 'percentage', + value: 15, + active: true, + }, + ]); + + const [editingRule, setEditingRule] = useState(null); + const [showNewRule, setShowNewRule] = useState(false); + + const handleSaveRule = () => { + toast({ + title: 'Regla guardada', + description: 'La regla de comisión ha sido guardada exitosamente', + }); + setEditingRule(null); + setShowNewRule(false); + }; + + const handleDeleteRule = (id: number) => { + setRules(rules.filter((rule) => rule.id !== id)); + toast({ + title: 'Regla eliminada', + description: 'La regla de comisión ha sido eliminada', + variant: 'destructive', + }); + }; + + return ( +
+ {/* Header */} +
+
+

Reglas de Comisión

+

+ Configura y gestiona las reglas de comisión por categoría +

+
+ +
+ + {/* New/Edit Rule Form */} + {(showNewRule || editingRule) && ( + + + + {editingRule ? 'Editar Regla' : 'Nueva Regla de Comisión'} + + + Define los parámetros para calcular comisiones + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ )} + + {/* Rules List */} +
+ {rules.map((rule) => ( + + +
+
+ {rule.name} + {rule.category} +
+ + {rule.active ? 'Activa' : 'Inactiva'} + +
+
+ +
+
+

Comisión

+

+ {rule.type === 'percentage' ? `${rule.value}%` : `$${rule.value}`} +

+
+ {rule.minAmount && ( +
+

Monto Mínimo

+

${rule.minAmount}

+
+ )} + {rule.maxAmount && ( +
+

Monto Máximo

+

${rule.maxAmount}

+
+ )} +
+ + +
+
+
+
+ ))} +
+
+ ); +}; + +export default CommissionRules; diff --git a/src/pages/dashboard/commissions/CommissionsDashboard.tsx b/src/pages/dashboard/commissions/CommissionsDashboard.tsx new file mode 100644 index 0000000..938cfcd --- /dev/null +++ b/src/pages/dashboard/commissions/CommissionsDashboard.tsx @@ -0,0 +1,223 @@ +import { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { DollarSign, TrendingUp, Users, Calendar, Download, Filter } from 'lucide-react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; + +const CommissionsDashboard = () => { + const [period, setPeriod] = useState('month'); + + const stats = [ + { + title: 'Comisiones Totales', + value: '$45,231', + change: '+12.5%', + icon: DollarSign, + color: 'text-success', + }, + { + title: 'Comisiones Pendientes', + value: '$8,450', + change: '+8.2%', + icon: TrendingUp, + color: 'text-warning', + }, + { + title: 'Partners Activos', + value: '127', + change: '+5', + icon: Users, + color: 'text-primary', + }, + { + title: 'Transacciones', + value: '1,429', + change: '+23.1%', + icon: Calendar, + color: 'text-secondary', + }, + ]; + + const recentTransactions = [ + { + id: 1, + partner: 'Hotel Paraíso', + type: 'Reserva', + amount: 450, + commission: 67.5, + date: '2024-01-15', + status: 'Pagado', + }, + { + id: 2, + partner: 'Tours Caribe', + type: 'Tour Guiado', + amount: 1200, + commission: 180, + date: '2024-01-14', + status: 'Pendiente', + }, + { + id: 3, + partner: 'Restaurante El Mar', + type: 'Reserva', + amount: 300, + commission: 30, + date: '2024-01-14', + status: 'Pagado', + }, + { + id: 4, + partner: 'Spa Wellness', + type: 'Servicio', + amount: 800, + commission: 120, + date: '2024-01-13', + status: 'Procesando', + }, + ]; + + const topPartners = [ + { name: 'Hotel Paraíso', commission: 5420, transactions: 48 }, + { name: 'Tours Caribe', commission: 4890, transactions: 35 }, + { name: 'Restaurante El Mar', commission: 3210, transactions: 67 }, + { name: 'Spa Wellness', commission: 2980, transactions: 28 }, + { name: 'Adventure Park', commission: 2450, transactions: 31 }, + ]; + + return ( +
+ {/* Header */} +
+
+

Sistema de Comisiones

+

+ Gestiona y monitorea las comisiones de tus partners +

+
+
+ + + +
+
+ + {/* Stats Cards */} +
+ {stats.map((stat, index) => ( + + + {stat.title} + + + +
{stat.value}
+

+ {stat.change} vs período anterior +

+
+
+ ))} +
+ +
+ {/* Recent Transactions */} + + + Transacciones Recientes + Últimas comisiones generadas + + +
+ {recentTransactions.map((transaction) => ( +
+
+

{transaction.partner}

+

{transaction.type}

+
+
+

+ ${transaction.amount.toFixed(2)} +

+

+ +${transaction.commission.toFixed(2)} +

+
+
+

{transaction.date}

+ + {transaction.status} + +
+
+ ))} +
+
+
+ + {/* Top Partners */} + + + Top Partners + Partners con más comisiones + + +
+ {topPartners.map((partner, index) => ( +
+
+
+ {index + 1} +
+
+

{partner.name}

+

+ {partner.transactions} transacciones +

+
+
+

${partner.commission}

+
+ ))} +
+
+
+
+
+ ); +}; + +export default CommissionsDashboard; diff --git a/src/pages/dashboard/commissions/PaymentHistory.tsx b/src/pages/dashboard/commissions/PaymentHistory.tsx new file mode 100644 index 0000000..c7813a1 --- /dev/null +++ b/src/pages/dashboard/commissions/PaymentHistory.tsx @@ -0,0 +1,268 @@ +import { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Search, Download, FileText, CheckCircle, Clock, XCircle } from 'lucide-react'; + +interface Payment { + id: string; + partner: string; + amount: number; + commission: number; + date: string; + paymentDate?: string; + status: 'Pagado' | 'Pendiente' | 'Procesando' | 'Cancelado'; + method: string; + invoiceNumber: string; +} + +const PaymentHistory = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); + + const payments: Payment[] = [ + { + id: 'PAY-001', + partner: 'Hotel Paraíso', + amount: 4500, + commission: 675, + date: '2024-01-15', + paymentDate: '2024-01-20', + status: 'Pagado', + method: 'Transferencia', + invoiceNumber: 'INV-2024-001', + }, + { + id: 'PAY-002', + partner: 'Tours Caribe', + amount: 12000, + commission: 1800, + date: '2024-01-14', + status: 'Pendiente', + method: 'Transferencia', + invoiceNumber: 'INV-2024-002', + }, + { + id: 'PAY-003', + partner: 'Restaurante El Mar', + amount: 3000, + commission: 300, + date: '2024-01-14', + paymentDate: '2024-01-18', + status: 'Pagado', + method: 'Cheque', + invoiceNumber: 'INV-2024-003', + }, + { + id: 'PAY-004', + partner: 'Spa Wellness', + amount: 8000, + commission: 1200, + date: '2024-01-13', + status: 'Procesando', + method: 'Transferencia', + invoiceNumber: 'INV-2024-004', + }, + { + id: 'PAY-005', + partner: 'Adventure Park', + amount: 6500, + commission: 975, + date: '2024-01-12', + paymentDate: '2024-01-17', + status: 'Pagado', + method: 'Transferencia', + invoiceNumber: 'INV-2024-005', + }, + ]; + + const getStatusIcon = (status: Payment['status']) => { + switch (status) { + case 'Pagado': + return ; + case 'Pendiente': + return ; + case 'Procesando': + return ; + case 'Cancelado': + return ; + } + }; + + const getStatusBadge = (status: Payment['status']) => { + const styles = { + Pagado: 'bg-success/10 text-success', + Pendiente: 'bg-warning/10 text-warning', + Procesando: 'bg-primary/10 text-primary', + Cancelado: 'bg-destructive/10 text-destructive', + }; + return styles[status]; + }; + + const filteredPayments = payments.filter((payment) => { + const matchesSearch = + payment.partner.toLowerCase().includes(searchTerm.toLowerCase()) || + payment.invoiceNumber.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesStatus = statusFilter === 'all' || payment.status === statusFilter; + return matchesSearch && matchesStatus; + }); + + const totalCommissions = filteredPayments.reduce((sum, p) => sum + p.commission, 0); + const paidCommissions = filteredPayments + .filter((p) => p.status === 'Pagado') + .reduce((sum, p) => sum + p.commission, 0); + const pendingCommissions = filteredPayments + .filter((p) => p.status === 'Pendiente') + .reduce((sum, p) => sum + p.commission, 0); + + return ( +
+ {/* Header */} +
+
+

Historial de Pagos

+

+ Consulta y gestiona el historial de comisiones pagadas +

+
+ +
+ + {/* Summary Cards */} +
+ + + + Total Comisiones + + + +

+ ${totalCommissions.toFixed(2)} +

+
+
+ + + + Comisiones Pagadas + + + +

${paidCommissions.toFixed(2)}

+
+
+ + + + Comisiones Pendientes + + + +

+ ${pendingCommissions.toFixed(2)} +

+
+
+
+ + {/* Filters */} + + +
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ +
+
+
+ + {/* Payments Table */} + + + + + + ID + Partner + Monto + Comisión + Fecha Transacción + Fecha Pago + Método + Estado + Acciones + + + + {filteredPayments.map((payment) => ( + + {payment.id} + {payment.partner} + ${payment.amount.toFixed(2)} + + ${payment.commission.toFixed(2)} + + {payment.date} + {payment.paymentDate || '-'} + {payment.method} + +
+ {getStatusIcon(payment.status)} + + {payment.status} + +
+
+ + + +
+ ))} +
+
+
+
+
+ ); +}; + +export default PaymentHistory;