Skip to content

Jo #2

@JJGGH829

Description

@JJGGH829
<title>Sistema de Rifas - Panel de Administración</title> <script src="https://cdn.tailwindcss.com"></script> <style> @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
    body {
        font-family: 'Inter', sans-serif;
    }
    
    .sidebar-transition {
        transition: transform 0.3s ease-in-out;
    }
    
    .fade-in {
        animation: fadeIn 0.5s ease-in;
    }
    
    @keyframes fadeIn {
        from { opacity: 0; transform: translateY(20px); }
        to { opacity: 1; transform: translateY(0); }
    }
    
    .winner-animation {
        animation: winnerPulse 2s infinite;
    }
    
    @keyframes winnerPulse {
        0%, 100% { transform: scale(1); }
        50% { transform: scale(1.05); }
    }
    
    .chart-bar {
        transition: height 0.5s ease-in-out;
    }
    
    .notification {
        animation: slideIn 0.3s ease-out;
    }
    
    @keyframes slideIn {
        from { transform: translateX(100%); }
        to { transform: translateX(0); }
    }
</style>

🎟️ RifaAdmin Pro

👤 Administrador
<div class="flex">
    <!-- Sidebar -->
    <div class="w-64 bg-white shadow-lg h-screen sticky top-0">
        <div class="p-6">
            <nav class="space-y-2">
                <button onclick="showSection('dashboard')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    📊 Dashboard
                </button>
                <button onclick="showSection('rifas')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    🎯 Gestionar Rifas
                </button>
                <button onclick="showSection('participants')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    👥 Participantes
                </button>
                <button onclick="showSection('winners')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    🏆 Ganadores
                </button>
                <button onclick="showSection('reports')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    📈 Reportes
                </button>
                <button onclick="showSection('settings')" class="nav-item w-full text-left px-4 py-3 rounded-lg hover:bg-gray-100 transition-colors flex items-center gap-3">
                    ⚙️ Configuración
                </button>
            </nav>
        </div>
    </div>

    <!-- Main Content -->
    <div class="flex-1 p-8">
        <!-- Dashboard Section -->
        <div id="dashboard" class="section">
            <div class="mb-8">
                <h2 class="text-3xl font-bold text-gray-900 mb-2">Dashboard</h2>
                <p class="text-gray-600">Resumen general del sistema de rifas</p>
            </div>

            <!-- Stats Cards -->
            <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <div class="flex items-center justify-between">
                        <div>
                            <p class="text-sm font-medium text-gray-600">Rifas Activas</p>
                            <p class="text-3xl font-bold text-gray-900" id="activeRifas">0</p>
                        </div>
                        <div class="bg-blue-100 p-3 rounded-full">
                            <span class="text-2xl">🎯</span>
                        </div>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <div class="flex items-center justify-between">
                        <div>
                            <p class="text-sm font-medium text-gray-600">Total Participantes</p>
                            <p class="text-3xl font-bold text-gray-900" id="totalParticipants">0</p>
                        </div>
                        <div class="bg-green-100 p-3 rounded-full">
                            <span class="text-2xl">👥</span>
                        </div>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <div class="flex items-center justify-between">
                        <div>
                            <p class="text-sm font-medium text-gray-600">Ganadores</p>
                            <p class="text-3xl font-bold text-gray-900" id="totalWinners">0</p>
                        </div>
                        <div class="bg-yellow-100 p-3 rounded-full">
                            <span class="text-2xl">🏆</span>
                        </div>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <div class="flex items-center justify-between">
                        <div>
                            <p class="text-sm font-medium text-gray-600">Ingresos Totales</p>
                            <p class="text-3xl font-bold text-gray-900" id="totalRevenue">$0</p>
                        </div>
                        <div class="bg-purple-100 p-3 rounded-full">
                            <span class="text-2xl">💰</span>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Recent Activity -->
            <div class="grid lg:grid-cols-2 gap-8">
                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Actividad Reciente</h3>
                    <div id="recentActivity" class="space-y-3">
                        <div class="text-gray-500 text-center py-8">No hay actividad reciente</div>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Rifas por Estado</h3>
                    <div id="rifaChart" class="space-y-3">
                        <!-- Chart will be generated here -->
                    </div>
                </div>
            </div>
        </div>

        <!-- Rifas Section -->
        <div id="rifas" class="section hidden">
            <div class="mb-8">
                <div class="flex justify-between items-center">
                    <div>
                        <h2 class="text-3xl font-bold text-gray-900 mb-2">Gestionar Rifas</h2>
                        <p class="text-gray-600">Crear y administrar rifas</p>
                    </div>
                    <button onclick="showCreateRifaModal()" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2">
                        ➕ Nueva Rifa
                    </button>
                </div>
            </div>

            <!-- Rifas List -->
            <div class="bg-white rounded-xl shadow-sm border border-gray-200">
                <div class="p-6">
                    <div class="overflow-x-auto">
                        <table class="w-full">
                            <thead>
                                <tr class="border-b border-gray-200">
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Nombre</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Premio</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Participantes</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Estado</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Fecha</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Acciones</th>
                                </tr>
                            </thead>
                            <tbody id="rifasTable">
                                <tr>
                                    <td colspan="6" class="text-center py-8 text-gray-500">No hay rifas creadas</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>

        <!-- Participants Section -->
        <div id="participants" class="section hidden">
            <div class="mb-8">
                <div class="flex justify-between items-center">
                    <div>
                        <h2 class="text-3xl font-bold text-gray-900 mb-2">Participantes</h2>
                        <p class="text-gray-600">Gestionar participantes de las rifas</p>
                    </div>
                    <button onclick="showAddParticipantModal()" class="bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2">
                        ➕ Agregar Participante
                    </button>
                </div>
            </div>

            <!-- Search and Filter -->
            <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200 mb-6">
                <div class="flex gap-4">
                    <input type="text" id="searchParticipants" placeholder="Buscar participantes..." 
                           class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                    <select id="filterRifa" class="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                        <option value="">Todas las rifas</option>
                    </select>
                </div>
            </div>

            <!-- Participants List -->
            <div class="bg-white rounded-xl shadow-sm border border-gray-200">
                <div class="p-6">
                    <div class="overflow-x-auto">
                        <table class="w-full">
                            <thead>
                                <tr class="border-b border-gray-200">
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Nombre</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Email</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Teléfono</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Número</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Rifa</th>
                                    <th class="text-left py-3 px-4 font-semibold text-gray-900">Acciones</th>
                                </tr>
                            </thead>
                            <tbody id="participantsTable">
                                <tr>
                                    <td colspan="6" class="text-center py-8 text-gray-500">No hay participantes registrados</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>

        <!-- Winners Section -->
        <div id="winners" class="section hidden">
            <div class="mb-8">
                <h2 class="text-3xl font-bold text-gray-900 mb-2">Ganadores</h2>
                <p class="text-gray-600">Historial de ganadores de todas las rifas</p>
            </div>

            <div class="bg-white rounded-xl shadow-sm border border-gray-200">
                <div class="p-6">
                    <div id="winnersContent" class="space-y-4">
                        <div class="text-center py-8 text-gray-500">No hay ganadores registrados</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Reports Section -->
        <div id="reports" class="section hidden">
            <div class="mb-8">
                <h2 class="text-3xl font-bold text-gray-900 mb-2">Reportes</h2>
                <p class="text-gray-600">Análisis y estadísticas detalladas</p>
            </div>

            <div class="grid lg:grid-cols-2 gap-8">
                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Participación por Mes</h3>
                    <div id="monthlyChart" class="h-64 flex items-end justify-around gap-2">
                        <!-- Monthly chart will be generated here -->
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Top Rifas</h3>
                    <div id="topRifas" class="space-y-3">
                        <div class="text-gray-500 text-center py-8">No hay datos suficientes</div>
                    </div>
                </div>
            </div>

            <!-- Export Options -->
            <div class="mt-8 bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                <h3 class="text-lg font-semibold text-gray-900 mb-4">Exportar Datos</h3>
                <div class="flex gap-4">
                    <button onclick="exportData('csv')" class="bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700 transition-colors">
                        📊 Exportar CSV
                    </button>
                    <button onclick="exportData('pdf')" class="bg-red-600 text-white px-6 py-3 rounded-lg hover:bg-red-700 transition-colors">
                        📄 Exportar PDF
                    </button>
                    <button onclick="generateReport()" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors">
                        📈 Generar Reporte
                    </button>
                </div>
            </div>
        </div>

        <!-- Settings Section -->
        <div id="settings" class="section hidden">
            <div class="mb-8">
                <h2 class="text-3xl font-bold text-gray-900 mb-2">Configuración</h2>
                <p class="text-gray-600">Configurar el sistema de rifas</p>
            </div>

            <div class="grid lg:grid-cols-2 gap-8">
                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Configuración General</h3>
                    <div class="space-y-4">
                        <div>
                            <label class="block text-sm font-medium text-gray-700 mb-2">Nombre de la Organización</label>
                            <input type="text" id="orgName" value="Mi Organización" 
                                   class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                        </div>
                        <div>
                            <label class="block text-sm font-medium text-gray-700 mb-2">Email de Contacto</label>
                            <input type="email" id="contactEmail" value="admin@miorg.com" 
                                   class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                        </div>
                        <div>
                            <label class="block text-sm font-medium text-gray-700 mb-2">Moneda</label>
                            <select id="currency" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                                <option value="$">Dólar ($)</option>
                                <option value="€">Euro (€)</option>
                                <option value="£">Libra (£)</option>
                                <option value="¥">Yen (¥)</option>
                            </select>
                        </div>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-sm p-6 border border-gray-200">
                    <h3 class="text-lg font-semibold text-gray-900 mb-4">Configuración de Rifas</h3>
                    <div class="space-y-4">
                        <div class="flex items-center justify-between">
                            <span class="text-sm font-medium text-gray-700">Permitir números duplicados</span>
                            <input type="checkbox" id="allowDuplicates" class="rounded">
                        </div>
                        <div class="flex items-center justify-between">
                            <span class="text-sm font-medium text-gray-700">Notificaciones automáticas</span>
                            <input type="checkbox" id="autoNotifications" checked class="rounded">
                        </div>
                        <div class="flex items-center justify-between">
                            <span class="text-sm font-medium text-gray-700">Backup automático</span>
                            <input type="checkbox" id="autoBackup" checked class="rounded">
                        </div>
                    </div>
                </div>
            </div>

            <div class="mt-8 text-center">
                <button onclick="saveSettings()" class="bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 transition-colors">
                    💾 Guardar Configuración
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Create Rifa Modal -->
<div id="createRifaModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden items-center justify-center z-50">
    <div class="bg-white rounded-2xl p-8 max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-2xl font-bold text-gray-900">Crear Nueva Rifa</h2>
            <button onclick="hideCreateRifaModal()" class="text-gray-500 hover:text-gray-700 text-2xl">×</button>
        </div>
        
        <form id="createRifaForm" class="space-y-6">
            <div class="grid md:grid-cols-2 gap-6">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Nombre de la Rifa</label>
                    <input type="text" id="newRifaName" required 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Premio</label>
                    <input type="text" id="newRifaPrize" required 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
            </div>
            
            <div class="grid md:grid-cols-3 gap-6">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Precio del Boleto</label>
                    <input type="number" id="ticketPrice" min="0" step="0.01" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Número Inicial</label>
                    <input type="number" id="newStartNumber" value="1" min="0" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Número Final</label>
                    <input type="number" id="newEndNumber" value="100" min="1" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
            </div>
            
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Descripción</label>
                <textarea id="rifaDescription" rows="3" 
                          class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
            </div>
            
            <div class="grid md:grid-cols-2 gap-6">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Fecha de Inicio</label>
                    <input type="datetime-local" id="startDate" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Fecha de Sorteo</label>
                    <input type="datetime-local" id="drawDate" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
            </div>
            
            <div class="flex justify-end gap-4">
                <button type="button" onclick="hideCreateRifaModal()" 
                        class="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
                    Cancelar
                </button>
                <button type="submit" 
                        class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors">
                    Crear Rifa
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Add Participant Modal -->
<div id="addParticipantModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden items-center justify-center z-50">
    <div class="bg-white rounded-2xl p-8 max-w-lg mx-4">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-2xl font-bold text-gray-900">Agregar Participante</h2>
            <button onclick="hideAddParticipantModal()" class="text-gray-500 hover:text-gray-700 text-2xl">×</button>
        </div>
        
        <form id="addParticipantForm" class="space-y-6">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Nombre Completo</label>
                <input type="text" id="participantName" required 
                       class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            </div>
            
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Email</label>
                <input type="email" id="participantEmail" 
                       class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            </div>
            
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Teléfono</label>
                <input type="tel" id="participantPhone" 
                       class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            </div>
            
            <div class="grid grid-cols-2 gap-4">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Rifa</label>
                    <select id="participantRifa" required 
                            class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                        <option value="">Seleccionar rifa</option>
                    </select>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">Número</label>
                    <input type="number" id="participantNumber" min="1" 
                           class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
                </div>
            </div>
            
            <div class="flex justify-end gap-4">
                <button type="button" onclick="hideAddParticipantModal()" 
                        class="px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
                    Cancelar
                </button>
                <button type="submit" 
                        class="bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700 transition-colors">
                    Agregar Participante
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Notification Container -->
<div id="notifications" class="fixed top-4 right-4 z-50 space-y-2"></div>

<script>
    // Global data storage
    let rifas = [];
    let participants = [];
    let winners = [];
    let currentSection = 'dashboard';

    // Initialize the system
    function init() {
        updateDateTime();
        setInterval(updateDateTime, 1000);
        showSection('dashboard');
        updateDashboard();
        
        // Set default dates
        const now = new Date();
        const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
        document.getElementById('startDate').value = now.toISOString().slice(0, 16);
        document.getElementById('drawDate').value = tomorrow.toISOString().slice(0, 16);
    }

    // Update current date and time
    function updateDateTime() {
        const now = new Date();
        document.getElementById('currentDateTime').textContent = now.toLocaleString('es-ES');
    }

    // Show notification
    function showNotification(message, type = 'success') {
        const notification = document.createElement('div');
        notification.className = `notification px-6 py-4 rounded-lg shadow-lg text-white ${
            type === 'success' ? 'bg-green-500' : 
            type === 'error' ? 'bg-red-500' : 
            type === 'warning' ? 'bg-yellow-500' : 'bg-blue-500'
        }`;
        notification.textContent = message;
        
        document.getElementById('notifications').appendChild(notification);
        
        setTimeout(() => {
            notification.remove();
        }, 5000);
    }

    // Show section
    function showSection(sectionName) {
        // Hide all sections
        document.querySelectorAll('.section').forEach(section => {
            section.classList.add('hidden');
        });
        
        // Show selected section
        document.getElementById(sectionName).classList.remove('hidden');
        document.getElementById(sectionName).classList.add('fade-in');
        
        // Update navigation
        document.querySelectorAll('.nav-item').forEach(item => {
            item.classList.remove('bg-blue-100', 'text-blue-700');
        });
        event.target.classList.add('bg-blue-100', 'text-blue-700');
        
        currentSection = sectionName;
        
        // Update section-specific data
        if (sectionName === 'rifas') updateRifasTable();
        if (sectionName === 'participants') updateParticipantsTable();
        if (sectionName === 'winners') updateWinnersDisplay();
        if (sectionName === 'reports') updateReports();
    }

    // Update dashboard
    function updateDashboard() {
        const activeRifas = rifas.filter(r => r.status === 'active').length;
        const totalParticipants = participants.length;
        const totalWinners = winners.length;
        const totalRevenue = rifas.reduce((sum, rifa) => {
            const rifaParticipants = participants.filter(p => p.rifaId === rifa.id);
            return sum + (rifaParticipants.length * (rifa.ticketPrice || 0));
        }, 0);

        document.getElementById('activeRifas').textContent = activeRifas;
        document.getElementById('totalParticipants').textContent = totalParticipants;
        document.getElementById('totalWinners').textContent = totalWinners;
        document.getElementById('totalRevenue').textContent = `$${totalRevenue.toFixed(2)}`;

        updateRecentActivity();
        updateRifaChart();
    }

    // Update recent activity
    function updateRecentActivity() {
        const activities = [];
        
        // Add recent rifas
        rifas.slice(-5).forEach(rifa => {
            activities.push({
                type: 'rifa',
                message: `Nueva rifa creada: ${rifa.name}`,
                time: rifa.createdAt
            });
        });
        
        // Add recent participants
        participants.slice(-5).forEach(participant => {
            const rifa = rifas.find(r => r.id === participant.rifaId);
            activities.push({
                type: 'participant',
                message: `${participant.name} se unió a ${rifa?.name || 'una rifa'}`,
                time: participant.createdAt
            });
        });
        
        // Sort by time
        activities.sort((a, b) => new Date(b.time) - new Date(a.time));
        
        const container = document.getElementById('recentActivity');
        if (activities.length === 0) {
            container.innerHTML = '<div class="text-gray-500 text-center py-8">No hay actividad reciente</div>';
            return;
        }
        
        container.innerHTML = activities.slice(0, 5).map(activity => `
            <div class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
                <div class="text-2xl">
                    ${activity.type === 'rifa' ? '🎯' : '👤'}
                </div>
                <div class="flex-1">
                    <div class="text-sm font-medium text-gray-900">${activity.message}</div>
                    <div class="text-xs text-gray-500">${new Date(activity.time).toLocaleString('es-ES')}</div>
                </div>
            </div>
        `).join('');
    }

    // Update rifa chart
    function updateRifaChart() {
        const statusCounts = {
            active: rifas.filter(r => r.status === 'active').length,
            completed: rifas.filter(r => r.status === 'completed').length,
            pending: rifas.filter(r => r.status === 'pending').length
        };
        
        const total = Object.values(statusCounts).reduce((a, b) => a + b, 0);
        
        const container = document.getElementById('rifaChart');
        if (total === 0) {
            container.innerHTML = '<div class="text-gray-500 text-center py-8">No hay rifas para mostrar</div>';
            return;
        }
        
        container.innerHTML = Object.entries(statusCounts).map(([status, count]) => {
            const percentage = (count / total) * 100;
            const color = status === 'active' ? 'bg-green-500' : 
                         status === 'completed' ? 'bg-blue-500' : 'bg-yellow-500';
            
            return `
                <div class="flex items-center justify-between mb-2">
                    <span class="text-sm font-medium text-gray-700 capitalize">${status}</span>
                    <span class="text-sm text-gray-500">${count}</span>
                </div>
                <div class="w-full bg-gray-200 rounded-full h-2 mb-3">
                    <div class="${color} h-2 rounded-full chart-bar" style="width: ${percentage}%"></div>
                </div>
            `;
        }).join('');
    }

    // Show create rifa modal
    function showCreateRifaModal() {
        document.getElementById('createRifaModal').classList.remove('hidden');
        document.getElementById('createRifaModal').classList.add('flex');
    }

    // Hide create rifa modal
    function hideCreateRifaModal() {
        document.getElementById('createRifaModal').classList.add('hidden');
        document.getElementById('createRifaModal').classList.remove('flex');
        document.getElementById('createRifaForm').reset();
    }

    // Create new rifa
    document.getElementById('createRifaForm').addEventListener('submit', function(e) {
        e.preventDefault();
        
        const newRifa = {
            id: Date.now(),
            name: document.getElementById('newRifaName').value,
            prize: document.getElementById('newRifaPrize').value,
            ticketPrice: parseFloat(document.getElementById('ticketPrice').value) || 0,
            startNumber: parseInt(document.getElementById('newStartNumber').value),
            endNumber: parseInt(document.getElementById('newEndNumber').value),
            description: document.getElementById('rifaDescription').value,
            startDate: document.getElementById('startDate').value,
            drawDate: document.getElementById('drawDate').value,
            status: 'active',
            createdAt: new Date().toISOString()
        };
        
        rifas.push(newRifa);
        hideCreateRifaModal();
        showNotification('Rifa creada exitosamente');
        updateDashboard();
        updateRifasTable();
        updateParticipantRifaOptions();
    });

    // Update rifas table
    function updateRifasTable() {
        const tbody = document.getElementById('rifasTable');
        
        if (rifas.length === 0) {
            tbody.innerHTML = '<tr><td colspan="6" class="text-center py-8 text-gray-500">No hay rifas creadas</td></tr>';
            return;
        }
        
        tbody.innerHTML = rifas.map(rifa => {
            const participantCount = participants.filter(p => p.rifaId === rifa.id).length;
            const statusColor = rifa.status === 'active' ? 'bg-green-100 text-green-800' : 
                               rifa.status === 'completed' ? 'bg-blue-100 text-blue-800' : 
                               'bg-yellow-100 text-yellow-800';
            
            return `
                <tr class="border-b border-gray-100">
                    <td class="py-4 px-4 font-medium text-gray-900">${rifa.name}</td>
                    <td class="py-4 px-4 text-gray-600">${rifa.prize}</td>
                    <td class="py-4 px-4 text-gray-600">${participantCount}</td>
                    <td class="py-4 px-4">
                        <span class="px-2 py-1 rounded-full text-xs font-medium ${statusColor}">
                            ${rifa.status}
                        </span>
                    </td>
                    <td class="py-4 px-4 text-gray-600">${new Date(rifa.createdAt).toLocaleDateString('es-ES')}</td>
                    <td class="py-4 px-4">
                        <div class="flex gap-2">
                            <button onclick="performDraw(${rifa.id})" class="bg-blue-600 text-white px-3 py-1 rounded text-sm hover:bg-blue-700 transition-colors">
                                Sortear
                            </button>
                            <button onclick="deleteRifa(${rifa.id})" class="bg-red-600 text-white px-3 py-1 rounded text-sm hover:bg-red-700 transition-colors">
                                Eliminar
                            </button>
                        </div>
                    </td>
                </tr>
            `;
        }).join('');
    }

    // Perform draw
    function performDraw(rifaId) {
        const rifa = rifas.find(r => r.id === rifaId);
        const rifaParticipants = participants.filter(p => p.rifaId === rifaId);
        
        if (rifaParticipants.length === 0) {
            showNotification('No hay participantes en esta rifa', 'error');
            return;
        }
        
        const winner = rifaParticipants[Math.floor(Math.random() * rifaParticipants.length)];
        
        const winnerRecord = {
            id: Date.now(),
            rifaId: rifaId,
            rifaName: rifa.name,
            prize: rifa.prize,
            participantId: winner.id,
            participantName: winner.name,
            participantEmail: winner.email,
            number: winner.number,
            date: new Date().toISOString()
        };
        
        winners.push(winnerRecord);
        rifa.status = 'completed';
        
        showNotification(`¡${winner.name} ha ganado ${rifa.prize}!`, 'success');
        updateDashboard();
        updateRifasTable();
        updateWinnersDisplay();
    }

    // Delete rifa
    function deleteRifa(rifaId) {
        if (confirm('¿Estás seguro de que quieres eliminar esta rifa?')) {
            rifas = rifas.filter(r => r.id !== rifaId);
            participants = participants.filter(p => p.rifaId !== rifaId);
            winners = winners.filter(w => w.rifaId !== rifaId);
            
            showNotification('Rifa eliminada exitosamente');
            updateDashboard();
            updateRifasTable();
            updateParticipantsTable();
            updateWinnersDisplay();
        }
    }

    // Show add participant modal
    function showAddParticipantModal() {
        updateParticipantRifaOptions();
        document.getElementById('addParticipantModal').classList.remove('hidden');
        document.getElementById('addParticipantModal').classList.add('flex');
    }

    // Hide add participant modal
    function hideAddParticipantModal() {
        document.getElementById('addParticipantModal').classList.add('hidden');
        document.getElementById('addParticipantModal').classList.remove('flex');
        document.getElementById('addParticipantForm').reset();
    }

    // Update participant rifa options
    function updateParticipantRifaOptions() {
        const select = document.getElementById('participantRifa');
        const activeRifas = rifas.filter(r => r.status === 'active');
        
        select.innerHTML = '<option value="">Seleccionar rifa</option>' + 
            activeRifas.map(rifa => `<option value="${rifa.id}">${rifa.name}</option>`).join('');
    }

    // Add participant
    document.getElementById('addParticipantForm').addEventListener('submit', function(e) {
        e.preventDefault();
        
        const rifaId = parseInt(document.getElementById('participantRifa').value);
        const number = parseInt(document.getElementById('participantNumber').value);
        
        // Check if number is already taken
        if (participants.some(p => p.rifaId === rifaId && p.number === number)) {
            showNotification('Este número ya está ocupado en esta rifa', 'error');
            return;
        }
        
        const newParticipant = {
            id: Date.now(),
            name: document.getElementById('participantName').value,
            email: document.getElementById('participantEmail').value,
            phone: document.getElementById('participantPhone').value,
            rifaId: rifaId,
            number: number,
            createdAt: new Date().toISOString()
        };
        
        participants.push(newParticipant);
        hideAddParticipantModal();
        showNotification('Participante agregado exitosamente');
        updateDashboard();
        updateParticipantsTable();
    });

    // Update participants table
    function updateParticipantsTable() {
        const tbody = document.getElementById('participantsTable');
        
        if (participants.length === 0) {
            tbody.innerHTML = '<tr><td colspan="6" class="text-center py-8 text-gray-500">No hay participantes registrados</td></tr>';
            return;
        }
        
        tbody.innerHTML = participants.map(participant => {
            const rifa = rifas.find(r => r.id === participant.rifaId);
            
            return `
                <tr class="border-b border-gray-100">
                    <td class="py-4 px-4 font-medium text-gray-900">${participant.name}</td>
                    <td class="py-4 px-4 text-gray-600">${participant.email || 'N/A'}</td>
                    <td class="py-4 px-4 text-gray-600">${participant.phone || 'N/A'}</td>
                    <td class="py-4 px-4">
                        <span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-sm font-medium">
                            #${participant.number}
                        </span>
                    </td>
                    <td class="py-4 px-4 text-gray-600">${rifa?.name || 'Rifa eliminada'}</td>
                    <td class="py-4 px-4">
                        <button onclick="deleteParticipant(${participant.id})" class="bg-red-600 text-white px-3 py-1 rounded text-sm hover:bg-red-700 transition-colors">
                            Eliminar
                        </button>
                    </td>
                </tr>
            `;
        }).join('');
    }

    // Delete participant
    function deleteParticipant(participantId) {
        if (confirm('¿Estás seguro de que quieres eliminar este participante?')) {
            participants = participants.filter(p => p.id !== participantId);
            showNotification('Participante eliminado exitosamente');
            updateDashboard();
            updateParticipantsTable();
        }
    }

    // Update winners display
    function updateWinnersDisplay() {
        const container = document.getElementById('winnersContent');
        
        if (winners.length === 0) {
            container.innerHTML = '<div class="text-center py-8 text-gray-500">No hay ganadores registrados</div>';
            return;
        }
        
        container.innerHTML = winners.map(winner => `
            <div class="bg-gradient-to-r from-yellow-50 to-orange-50 border border-yellow-200 rounded-lg p-6 winner-animation">
                <div class="flex items-center justify-between">
                    <div class="flex items-center gap-4">
                        <div class="text-4xl">🏆</div>
                        <div>
                            <h3 class="text-xl font-bold text-gray-900">${winner.participantName}</h3>
                            <p class="text-gray-600">Ganó: ${winner.prize}</p>
                            <p class="text-sm text-gray-500">Rifa: ${winner.rifaName}</p>
                        </div>
                    </div>
                    <div class="text-right">
                        <div class="bg-yellow-500 text-white px-4 py-2 rounded-full text-lg font-bold">
                            #${winner.number}
                        </div>
                        <div class="text-sm text-gray-500 mt-2">
                            ${new Date(winner.date).toLocaleDateString('es-ES')}
                        </div>
                    </div>
                </div>
            </div>
        `).join('');
    }

    // Update reports
    function updateReports() {
        updateMonthlyChart();
        updateTopRifas();
    }

    // Update monthly chart
    function updateMonthlyChart() {
        const monthlyData = {};
        const months = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];
        
        participants.forEach(participant => {
            const month = new Date(participant.createdAt).getMonth();
            monthlyData[month] = (monthlyData[month] || 0) + 1;
        });
        
        const maxValue = Math.max(...Object.values(monthlyData), 1);
        const container = document.getElementById('monthlyChart');
        
        container.innerHTML = months.map((month, index) => {
            const value = monthlyData[index] || 0;
            const height = (value / maxValue) * 200;
            
            return `
                <div class="flex flex-col items-center">
                    <div class="bg-blue-500 rounded-t chart-bar" style="height: ${height}px; width: 20px; min-height: 4px;"></div>
                    <div class="text-xs text-gray-600 mt-2">${month}</div>
                    <div class="text-xs font-semibold text-gray-800">${value}</div>
                </div>
            `;
        }).join('');
    }

    // Update top rifas
    function updateTopRifas() {
        const rifaStats = rifas.map(rifa => ({
            ...rifa,
            participantCount: participants.filter(p => p.rifaId === rifa.id).length,
            revenue: participants.filter(p => p.rifaId === rifa.id).length * (rifa.ticketPrice || 0)
        })).sort((a, b) => b.participantCount - a.participantCount);
        
        const container = document.getElementById('topRifas');
        
        if (rifaStats.length === 0) {
            container.innerHTML = '<div class="text-gray-500 text-center py-8">No hay datos suficientes</div>';
            return;
        }
        
        container.innerHTML = rifaStats.slice(0, 5).map((rifa, index) => `
            <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
                <div class="flex items-center gap-3">
                    <div class="bg-blue-100 text-blue-600 rounded-full w-8 h-8 flex items-center justify-center text-sm font-bold">
                        ${index + 1}
                    </div>
                    <div>
                        <div class="font-medium text-gray-900">${rifa.name}</div>
                        <div class="text-sm text-gray-500">${rifa.participantCount} participantes</div>
                    </div>
                </div>
                <div class="text-right">
                    <div class="font-semibold text-gray-900">$${rifa.revenue.toFixed(2)}</div>
                    <div class="text-sm text-gray-500">ingresos</div>
                </div>
            </div>
        `).join('');
    }

    // Export data
    function exportData(format) {
        if (format === 'csv') {
            exportCSV();
        } else if (format === 'pdf') {
            showNotification('Función de exportar PDF en desarrollo', 'info');
        }
    }

    // Export CSV
    function exportCSV() {
        const csvData = [
            ['Nombre', 'Email', 'Teléfono', 'Número', 'Rifa', 'Fecha'],
            ...participants.map(p => {
                const rifa = rifas.find(r => r.id === p.rifaId);
                return [p.name, p.email || '', p.phone || '', p.number, rifa?.name || '', new Date(p.createdAt).toLocaleDateString('es-ES')];
            })
        ];
        
        const csvContent = csvData.map(row => row.join(',')).join('\n');
        const blob = new Blob([csvContent], { type: 'text/csv' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'participantes.csv';
        a.click();
        window.URL.revokeObjectURL(url);
        
        showNotification('Datos exportados exitosamente');
    }

    // Generate report
    function generateReport() {
        const report = {
            fecha: new Date().toLocaleDateString('es-ES'),
            rifas: rifas.length,
            participantes: participants.length,
            ganadores: winners.length,
            ingresos: rifas.reduce((sum, rifa) => {
                const rifaParticipants = participants.filter(p => p.rifaId === rifa.id);
                return sum + (rifaParticipants.length * (rifa.ticketPrice || 0));
            }, 0)
        };
        
        const reportContent = `
            REPORTE DEL SISTEMA DE RIFAS
            ============================
            Fecha: ${report.fecha}
            
            RESUMEN GENERAL:
            - Total de rifas: ${report.rifas}
            - Total de participantes: ${report.participantes}
            - Total de ganadores: ${report.ganadores}
            - Ingresos totales: $${report.ingresos.toFixed(2)}
            
            RIFAS ACTIVAS:
            ${rifas.filter(r => r.status === 'active').map(r => `- ${r.name}: ${participants.filter(p => p.rifaId === r.id).length} participantes`).join('\n')}
            
            GANADORES RECIENTES:
            ${winners.slice(-5).map(w => `- ${w.participantName} ganó ${w.prize} (${new Date(w.date).toLocaleDateString('es-ES')})`).join('\n')}
        `;
        
        const blob = new Blob([reportContent], { type: 'text/plain' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `reporte_rifas_${new Date().toISOString().split('T')[0]}.txt`;
        a.click();
        window.URL.revokeObjectURL(url);
        
        showNotification('Reporte generado exitosamente');
    }

    // Save settings
    function saveSettings() {
        const settings = {
            orgName: document.getElementById('orgName').value,
            contactEmail: document.getElementById('contactEmail').value,
            currency: document.getElementById('currency').value,
            allowDuplicates: document.getElementById('allowDuplicates').checked,
            autoNotifications: document.getElementById('autoNotifications').checked,
            autoBackup: document.getElementById('autoBackup').checked
        };
        
        localStorage.setItem('rifaSettings', JSON.stringify(settings));
        showNotification('Configuración guardada exitosamente');
    }

    // Load settings
    function loadSettings() {
        const settings = JSON.parse(localStorage.getItem('rifaSettings') || '{}');
        
        if (settings.orgName) document.getElementById('orgName').value = settings.orgName;
        if (settings.contactEmail) document.getElementById('contactEmail').value = settings.contactEmail;
        if (settings.currency) document.getElementById('currency').value = settings.currency;
        if (settings.allowDuplicates !== undefined) document.getElementById('allowDuplicates').checked = settings.allowDuplicates;
        if (settings.autoNotifications !== undefined) document.getElementById('autoNotifications').checked = settings.autoNotifications;
        if (settings.autoBackup !== undefined) document.getElementById('autoBackup').checked = settings.autoBackup;
    }

    // Initialize the application
    document.addEventListener('DOMContentLoaded', function() {
        init();
        loadSettings();
    });
</script>
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9679d4a7c28ee716',t:'MTc1MzkzMDY3MS4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions