Sindbad~EG File Manager

Current Path : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/module-management/
Upload File :
Current File : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/module-management/index.php

<?php
require_once '../../config/config.php';
checkLogin();

// Only superusers can access this module
if (!isSuperuser()) {
    $_SESSION['error'] = "Access denied. Only superusers can manage modules.";
    header('Location: ' . BASE_URL . 'dashboard.php');
    exit;
}

$pageTitle = "Module Management - " . APP_NAME;
$db = Database::getInstance()->getConnection();

// Handle AJAX requests for toggling module access
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    header('Content-Type: application/json');
    
    try {
        if ($_POST['action'] === 'toggle_access') {
            $moduleId = (int)$_POST['module_id'];
            $accessLevel = $_POST['access_level'];
            $isEnabled = filter_var($_POST['is_enabled'], FILTER_VALIDATE_BOOLEAN);
            
            // Check if module exists
            $stmt = $db->prepare("SELECT module_name FROM module_management WHERE id = ?");
            $stmt->execute([$moduleId]);
            $module = $stmt->fetch();
            
            if (!$module) {
                throw new Exception("Module not found");
            }
            
            // Prevent disabling superuser access (but allow disabling for other levels)
            if ($accessLevel === 'superuser' && !$isEnabled) {
                throw new Exception("Cannot disable superuser access");
            }
            
            // Update or insert access level
            $stmt = $db->prepare("
                INSERT INTO module_access_levels (module_id, access_level, is_enabled, enabled_by)
                VALUES (?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE 
                    is_enabled = VALUES(is_enabled),
                    enabled_by = VALUES(enabled_by),
                    updated_at = CURRENT_TIMESTAMP
            ");
            $stmt->execute([$moduleId, $accessLevel, $isEnabled ? 1 : 0, $_SESSION['user_id']]);
            
            // Log the change
            $stmt = $db->prepare("
                INSERT INTO module_access_audit (module_id, access_level, action, performed_by, reason)
                VALUES (?, ?, ?, ?, ?)
            ");
            $action = $isEnabled ? 'enabled' : 'disabled';
            $reason = "Module access {$action} for {$accessLevel} level";
            $stmt->execute([$moduleId, $accessLevel, $action, $_SESSION['user_id'], $reason]);
            
            echo json_encode([
                'success' => true,
                'message' => "Module access updated successfully"
            ]);
            exit;
            
        } elseif ($_POST['action'] === 'toggle_module') {
            $moduleId = (int)$_POST['module_id'];
            $isActive = filter_var($_POST['is_active'], FILTER_VALIDATE_BOOLEAN);
            
            // Check if module is system critical
            $stmt = $db->prepare("SELECT is_system_critical, module_name FROM module_management WHERE id = ?");
            $stmt->execute([$moduleId]);
            $module = $stmt->fetch();
            
            if (!$module) {
                throw new Exception("Module not found");
            }
            
            if (!empty($module['is_system_critical']) && !$isActive) {
                throw new Exception("Cannot deactivate system critical module: " . $module['module_name']);
            }
            
            // Toggle module active status
            $stmt = $db->prepare("UPDATE module_management SET is_active = ? WHERE id = ?");
            $stmt->execute([$isActive, $moduleId]);
            
            echo json_encode([
                'success' => true,
                'message' => "Module " . ($isActive ? 'activated' : 'deactivated') . " successfully"
            ]);
            exit;
        }
        
    } catch (Exception $e) {
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'message' => $e->getMessage()
        ]);
        exit;
    }
}

// Get all modules with their access levels
$query = "
    SELECT 
        m.*,
        GROUP_CONCAT(
            CONCAT(mal.access_level, ':', IF(mal.is_enabled, '1', '0'))
            ORDER BY FIELD(mal.access_level, 'assembly', 'district', 'area', 'superuser')
            SEPARATOR '|'
        ) as access_config
    FROM module_management m
    LEFT JOIN module_access_levels mal ON m.id = mal.module_id
    GROUP BY m.id
    ORDER BY m.category, m.display_order
";

$allModules = $db->query($query)->fetchAll(PDO::FETCH_ASSOC);

// Get statistics
$stats = [
    'total_modules' => count($allModules),
    'active_modules' => count(array_filter($allModules, fn($m) => !empty($m['is_active']))),
    'critical_modules' => count(array_filter($allModules, fn($m) => !empty($m['is_system_critical']))),
    'categories' => count(array_unique(array_column($allModules, 'category')))
];

// Get recent access changes
$recentChanges = $db->query("
    SELECT 
        maa.*,
        m.module_name,
        u.full_name as performed_by_name
    FROM module_access_audit maa
    JOIN module_management m ON maa.module_id = m.id
    JOIN users u ON maa.performed_by = u.id
    ORDER BY maa.performed_at DESC
    LIMIT 10
")->fetchAll();

include '../../includes/header.php';
?>

<?php include '../../includes/sidebar.php'; ?>

<!-- Main Content -->
<main class="flex-1 md:ml-64 mt-16">
    <div class="container mx-auto px-4 py-8">
        
        <!-- Header -->
        <div class="mb-8">
            <div class="flex items-center justify-between mb-4">
                <div>
                    <h1 class="text-3xl font-bold text-gray-800">
                        <i class="fas fa-sliders-h text-blue-500 mr-3"></i>Module Management
                    </h1>
                    <p class="text-gray-600 mt-2">Control module access for different admin levels</p>
                </div>
                <div>
                    <?php echo getUserAccessBadge(); ?>
                </div>
            </div>
        </div>

        <!-- Success/Error Messages -->
        <?php if (isset($_SESSION['success'])): ?>
            <div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg shadow-md animate-fade-in">
                <div class="flex items-center">
                    <i class="fas fa-check-circle mr-3 text-xl"></i>
                    <p><?php echo htmlspecialchars($_SESSION['success']); ?></p>
                </div>
            </div>
            <?php unset($_SESSION['success']); ?>
        <?php endif; ?>

        <?php if (isset($_SESSION['error'])): ?>
            <div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg shadow-md animate-fade-in">
                <div class="flex items-center">
                    <i class="fas fa-exclamation-circle mr-3 text-xl"></i>
                    <p><?php echo htmlspecialchars($_SESSION['error']); ?></p>
                </div>
            </div>
            <?php unset($_SESSION['error']); ?>
        <?php endif; ?>

        <!-- Statistics Cards -->
        <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
            <!-- Total Modules -->
            <div class="bg-white rounded-xl shadow-lg p-6 border-l-4 border-blue-500">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-gray-500 text-sm font-medium mb-1">Total Modules</p>
                        <h3 class="text-3xl font-bold text-gray-800"><?php echo $stats['total_modules']; ?></h3>
                    </div>
                    <div class="rounded-full p-4" style="background: linear-gradient(135deg, #1E40AF 0%, #9333EA 100%);">
                        <i class="fas fa-cube text-3xl text-white"></i>
                    </div>
                </div>
            </div>

            <!-- Active Modules -->
            <div class="bg-white rounded-xl shadow-lg p-6 border-l-4 border-green-500">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-gray-500 text-sm font-medium mb-1">Active Modules</p>
                        <h3 class="text-3xl font-bold text-gray-800"><?php echo $stats['active_modules']; ?></h3>
                    </div>
                    <div class="bg-green-100 rounded-full p-4">
                        <i class="fas fa-check-circle text-3xl text-green-500"></i>
                    </div>
                </div>
            </div>

            <!-- Critical Modules -->
            <div class="bg-white rounded-xl shadow-lg p-6 border-l-4 border-yellow-500">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-gray-500 text-sm font-medium mb-1">Critical Modules</p>
                        <h3 class="text-3xl font-bold text-gray-800"><?php echo $stats['critical_modules']; ?></h3>
                    </div>
                    <div class="bg-yellow-100 rounded-full p-4">
                        <i class="fas fa-exclamation-triangle text-3xl text-yellow-500"></i>
                    </div>
                </div>
            </div>

            <!-- Categories -->
            <div class="bg-white rounded-xl shadow-lg p-6 border-l-4 border-purple-500">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-gray-500 text-sm font-medium mb-1">Categories</p>
                        <h3 class="text-3xl font-bold text-gray-800"><?php echo $stats['categories']; ?></h3>
                    </div>
                    <div class="bg-purple-100 rounded-full p-4">
                        <i class="fas fa-tags text-3xl text-purple-500"></i>
                    </div>
                </div>
            </div>
        </div>

        <!-- Access Level Legend -->
        <div class="bg-white rounded-xl shadow-lg p-6 mb-8">
            <h3 class="text-lg font-bold text-gray-800 mb-4">
                <i class="fas fa-info-circle text-blue-500 mr-2"></i>Access Level Guide
            </h3>
            <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
                <div class="flex items-center space-x-3 p-3 bg-yellow-50 rounded-lg">
                    <i class="fas fa-church text-2xl text-yellow-600"></i>
                    <div>
                        <p class="font-semibold text-yellow-800">Assembly Admin</p>
                        <p class="text-xs text-yellow-600">Single assembly access</p>
                    </div>
                </div>
                <div class="flex items-center space-x-3 p-3 bg-green-50 rounded-lg">
                    <i class="fas fa-map-marked-alt text-2xl text-green-600"></i>
                    <div>
                        <p class="font-semibold text-green-800">District Admin</p>
                        <p class="text-xs text-green-600">All assemblies in district</p>
                    </div>
                </div>
                <div class="flex items-center space-x-3 p-3 bg-blue-50 rounded-lg">
                    <i class="fas fa-map text-2xl text-blue-600"></i>
                    <div>
                        <p class="font-semibold text-blue-800">Area Admin</p>
                        <p class="text-xs text-blue-600">All districts in area</p>
                    </div>
                </div>
                <div class="flex items-center space-x-3 p-3 bg-purple-50 rounded-lg">
                    <i class="fas fa-crown text-2xl text-purple-600"></i>
                    <div>
                        <p class="font-semibold text-purple-800">Superuser</p>
                        <p class="text-xs text-purple-600">Full system access</p>
                    </div>
                </div>
            </div>
        </div>

        <!-- Modules Management Table -->
        <?php
        $categories = [];
        // Parse access configuration and group by category
        foreach ($allModules as $key => $mod) {
            // Set defaults for columns that might not exist
            $mod['is_system_critical'] = $mod['is_system_critical'] ?? false;
            $mod['category'] = $mod['category'] ?? 'General';
            $mod['module_description'] = $mod['module_description'] ?? '';
            
            // Parse access levels from access_config
            $mod['access_levels'] = [];
            if (!empty($mod['access_config'])) {
                $configs = explode('|', $mod['access_config']);
                foreach ($configs as $config) {
                    if (strpos($config, ':') !== false) {
                        list($level, $enabled) = explode(':', $config);
                        $mod['access_levels'][$level] = ($enabled === '1');
                    }
                }
            }
            
            // Set defaults for missing levels
            foreach (['assembly', 'district', 'area', 'superuser'] as $level) {
                if (!isset($mod['access_levels'][$level])) {
                    $mod['access_levels'][$level] = true;
                }
            }
            
            $categories[$mod['category']][] = $mod;
        }
        ?>

        <?php foreach ($categories as $category => $categoryModules): ?>
            <div class="bg-white rounded-xl shadow-lg mb-6">
                <div class="p-6 border-b border-gray-200">
                    <h3 class="text-xl font-bold text-gray-800">
                        <i class="fas fa-folder-open text-blue-500 mr-2"></i>
                        <?php echo htmlspecialchars($category); ?>
                        <span class="text-sm font-normal text-gray-500 ml-2">(<?php echo count($categoryModules); ?> modules)</span>
                    </h3>
                </div>

                <div class="overflow-x-auto">
                    <table class="min-w-full divide-y divide-gray-200">
                        <thead class="bg-gray-50">
                            <tr>
                                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Module</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Assembly</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">District</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Area</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Superuser</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
                                <th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
                            </tr>
                        </thead>
                        <tbody class="bg-white divide-y divide-gray-200">
                            <?php foreach ($categoryModules as $module): 
                                // Check if module is disabled (inactive)
                                $isDisabled = empty($module['is_active']);
                                $rowClass = $isDisabled ? 'bg-gray-100 opacity-60' : 'hover:bg-gray-50';
                            ?>
                                <tr class="<?php echo $rowClass; ?> transition">
                                    <td class="px-6 py-4">
                                        <div class="flex items-center">
                                            <div class="flex-shrink-0 w-10 h-10 rounded-full <?php echo $isDisabled ? 'bg-gray-400' : 'bg-gradient-to-br from-blue-500 to-purple-500'; ?> flex items-center justify-center">
                                                <i class="fas fa-<?php echo htmlspecialchars($module['module_icon']); ?> text-white"></i>
                                            </div>
                                            <div class="ml-4">
                                                <div class="text-sm font-semibold <?php echo $isDisabled ? 'text-gray-500' : 'text-gray-900'; ?>">
                                                    <?php echo htmlspecialchars($module['module_name']); ?>
                                                    <?php if ($isDisabled): ?>
                                                        <span class="ml-2 px-2 py-1 text-xs font-semibold rounded-full bg-gray-300 text-gray-700">
                                                            <i class="fas fa-ban"></i> Disabled
                                                        </span>
                                                    <?php endif; ?>
                                                    <?php if (!empty($module['is_system_critical'])): ?>
                                                        <span class="ml-2 px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
                                                            <i class="fas fa-lock"></i> Critical
                                                        </span>
                                                    <?php endif; ?>
                                                </div>
                                                <div class="text-xs text-gray-500"><?php echo htmlspecialchars($module['module_description'] ?? 'No description'); ?></div>
                                            </div>
                                        </div>
                                    </td>

                                    <!-- Access Level Toggles -->
                                    <?php foreach (['assembly', 'district', 'area', 'superuser'] as $level): 
                                        // Only lock the superuser column (superusers can now disable critical modules for other levels)
                                        $isDisabledToggle = ($level === 'superuser');
                                    ?>
                                        <td class="px-6 py-4 text-center">
                                            <label class="relative inline-flex items-center <?php echo $isDisabledToggle ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'; ?>">
                                                <input 
                                                    type="checkbox" 
                                                    class="sr-only peer access-toggle"
                                                    data-module-id="<?php echo $module['id']; ?>"
                                                    data-access-level="<?php echo $level; ?>"
                                                    <?php echo !empty($module['access_levels'][$level]) ? 'checked' : ''; ?>
                                                    <?php echo $isDisabledToggle ? 'disabled' : ''; ?>
                                                >
                                                <div class="toggle-switch" style="width: 44px; height: 24px; border-radius: 9999px; position: relative; transition: background-color 0.2s; background-color: <?php echo !empty($module['access_levels'][$level]) ? '#2563EB' : '#E5E7EB'; ?>; <?php echo $isDisabledToggle ? 'pointer-events: none;' : ''; ?>">
                                                    <div class="toggle-slider" style="position: absolute; top: 2px; left: 2px; background-color: white; border: 1px solid #D1D5DB; border-radius: 9999px; height: 20px; width: 20px; transition: transform 0.2s; transform: <?php echo !empty($module['access_levels'][$level]) ? 'translateX(20px)' : 'translateX(0)'; ?>;"></div>
                                                </div>
                                            </label>
                                        </td>
                                    <?php endforeach; ?>

                                    <!-- Module Status -->
                                    <td class="px-6 py-4 text-center">
                                        <?php if (!empty($module['is_active'])): ?>
                                            <span class="px-3 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
                                                <i class="fas fa-check-circle"></i> Active
                                            </span>
                                        <?php else: ?>
                                            <span class="px-3 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-800">
                                                <i class="fas fa-times-circle"></i> Inactive
                                            </span>
                                        <?php endif; ?>
                                    </td>

                                    <!-- Actions -->
                                    <td class="px-6 py-4 text-center">
                                        <button 
                                            class="toggle-module-btn text-<?php echo $module['is_active'] ? 'red' : 'green'; ?>-600 hover:text-<?php echo $module['is_active'] ? 'red' : 'green'; ?>-900"
                                            data-module-id="<?php echo $module['id']; ?>"
                                            data-is-active="<?php echo $module['is_active'] ? '0' : '1'; ?>"
                                            data-module-name="<?php echo htmlspecialchars($module['module_name']); ?>"
                                            <?php echo !empty($module['is_system_critical']) ? 'disabled title="Cannot disable critical modules"' : ''; ?>
                                        >
                                            <i class="fas fa-power-off text-lg"></i>
                                        </button>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            </div>
        <?php endforeach; ?>

        <!-- Recent Changes Log -->
        <?php if (!empty($recentChanges)): ?>
            <div class="bg-white rounded-xl shadow-lg p-6">
                <h3 class="text-xl font-bold text-gray-800 mb-4">
                    <i class="fas fa-history text-blue-500 mr-2"></i>Recent Access Changes
                </h3>
                <div class="space-y-3">
                    <?php foreach ($recentChanges as $change): ?>
                        <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
                            <div class="flex items-center space-x-3">
                                <i class="fas fa-<?php echo $change['action'] === 'enabled' ? 'check' : 'times'; ?>-circle text-<?php echo $change['action'] === 'enabled' ? 'green' : 'red'; ?>-500"></i>
                                <div>
                                    <p class="text-sm font-semibold text-gray-800">
                                        <?php echo htmlspecialchars($change['module_name']); ?> - 
                                        <span class="text-<?php echo $change['action'] === 'enabled' ? 'green' : 'red'; ?>-600">
                                            <?php echo ucfirst($change['access_level']); ?> <?php echo ucfirst($change['action']); ?>
                                        </span>
                                    </p>
                                    <p class="text-xs text-gray-500">
                                        By <?php echo htmlspecialchars($change['performed_by_name']); ?> • 
                                        <?php echo timeAgo($change['performed_at']); ?>
                                    </p>
                                </div>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            </div>
        <?php endif; ?>

    </div>
</main>

<!-- JavaScript -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Handle access level toggle
    document.querySelectorAll('.access-toggle').forEach(toggle => {
        toggle.addEventListener('change', function() {
            // Prevent action if checkbox is disabled
            if (this.disabled) {
                console.log('Toggle is disabled, ignoring');
                return;
            }
            
            const moduleId = this.dataset.moduleId;
            const accessLevel = this.dataset.accessLevel;
            const isEnabled = this.checked;
            
            // Get the toggle elements for visual update
            const toggleSwitch = this.nextElementSibling;
            const toggleSlider = toggleSwitch.querySelector('.toggle-slider');
            
            // Update visual state immediately (using inline styles)
            if (isEnabled) {
                toggleSwitch.style.backgroundColor = '#2563EB';
                toggleSlider.style.transform = 'translateX(20px)';
            } else {
                toggleSwitch.style.backgroundColor = '#E5E7EB';
                toggleSlider.style.transform = 'translateX(0)';
            }
            
            // Send AJAX request
            fetch(window.location.href, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    action: 'toggle_access',
                    module_id: moduleId,
                    access_level: accessLevel,
                    is_enabled: isEnabled ? '1' : '0'
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    showNotification('success', data.message);
                } else {
                    // Revert toggle on error - both checkbox and visual
                    this.checked = !isEnabled;
                    if (!isEnabled) {
                        toggleSwitch.style.backgroundColor = '#2563EB';
                        toggleSlider.style.transform = 'translateX(20px)';
                    } else {
                        toggleSwitch.style.backgroundColor = '#E5E7EB';
                        toggleSlider.style.transform = 'translateX(0)';
                    }
                    showNotification('error', data.message || 'Failed to update');
                }
            })
            .catch(error => {
                console.error('Toggle error:', error);
                // Revert toggle on error - both checkbox and visual
                this.checked = !isEnabled;
                if (!isEnabled) {
                    toggleSwitch.style.backgroundColor = '#2563EB';
                    toggleSlider.style.transform = 'translateX(20px)';
                } else {
                    toggleSwitch.style.backgroundColor = '#E5E7EB';
                    toggleSlider.style.transform = 'translateX(0)';
                }
                showNotification('error', 'An error occurred. Please try again.');
            });
        });
    });

    // Handle module activate/deactivate
    document.querySelectorAll('.toggle-module-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            const moduleId = this.dataset.moduleId;
            const isActive = this.dataset.isActive === '1';
            const moduleName = this.dataset.moduleName;
            
            if (confirm(`Are you sure you want to ${isActive ? 'activate' : 'deactivate'} ${moduleName}?`)) {
                fetch('', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: new URLSearchParams({
                        action: 'toggle_module',
                        module_id: moduleId,
                        is_active: isActive ? '1' : '0'
                    })
                })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        showNotification('success', data.message);
                        setTimeout(() => location.reload(), 1500);
                    } else {
                        showNotification('error', data.message);
                    }
                })
                .catch(error => {
                    showNotification('error', 'An error occurred. Please try again.');
                });
            }
        });
    });

    function showNotification(type, message) {
        const color = type === 'success' ? 'green' : 'red';
        const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
        
        const notification = document.createElement('div');
        notification.className = `fixed top-20 right-4 bg-${color}-100 border-l-4 border-${color}-500 text-${color}-700 p-4 rounded-lg shadow-lg animate-fade-in z-50`;
        notification.innerHTML = `
            <div class="flex items-center">
                <i class="fas fa-${icon} mr-3 text-xl"></i>
                <p>${message}</p>
            </div>
        `;
        
        document.body.appendChild(notification);
        
        setTimeout(() => {
            notification.remove();
        }, 3000);
    }
});
</script>

<?php include '../../includes/footer.php'; ?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists