HEX
Server: LiteSpeed
System: Linux php-prod-1.spaceapp.ru 5.15.0-157-generic #167-Ubuntu SMP Wed Sep 17 21:35:53 UTC 2025 x86_64
User: xnsbb3110 (1041)
PHP: 8.1.33
Disabled: NONE
Upload Files
File: //proc/676643/root/usr/local/CyberCP/serverStatus/templates/serverStatus/packageManager.html
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Package Manager - CyberPanel" %}{% endblock %}
{% block content %}

{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->

<style>
    /* CSS Variables for Dark Mode Support */
    :root {
        /* Text colors */
        --text-primary: #1e293b;
        --text-secondary: #475569;
        --text-muted: #64748b;
        --text-on-gradient: rgba(255, 255, 255, 0.9);
        
        /* Background colors */
        --bg-primary: #ffffff;
        --bg-secondary: #f8f9ff;
        --bg-light: #f3f4f6;
        --bg-hover: #f8f9ff;
        --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        
        /* Border colors */
        --border-color: #e8e9ff;
        --border-light: #f1f5f9;
        
        /* Accent colors */
        --accent-color: #667eea;
        --accent-hover: #764ba2;
        --accent-bg: #f3e8ff;
        --accent-focus: rgba(102, 126, 234, 0.1);
        --accent-shadow: rgba(102, 126, 234, 0.3);
        
        /* Status colors */
        --success-bg: #d1fae5;
        --success-color: #065f46;
        --success-border: #a7f3d0;
        --success-accent: #10b981;
        --success-hover: #059669;
        
        --danger-bg: #fee2e2;
        --danger-color: #991b1b;
        --danger-border: #fecaca;
        --danger-accent: #dc2626;
        --danger-hover: #b91c1c;
        
        --warning-bg: #fef3c7;
        --warning-color: #92400e;
        --warning-border: #fde68a;
        
        --info-bg: #dbeafe;
        --info-color: #1e40af;
        --info-border: #bfdbfe;
        
        /* Shadow colors */
        --shadow-light: rgba(0,0,0,0.05);
        --shadow-medium: rgba(0,0,0,0.08);
        --shadow-color: rgba(0,0,0,0.1);
        
        /* Console colors */
        --console-bg: #1e293b;
        --console-text: #10b981;
        
        /* Lock colors */
        --lock-color: #dc2626;
        --unlock-color: #10b981;
        
        /* Table specific */
        --table-header-bg: #f8f9ff;
        --version-badge-bg: #f3f4f6;
        --package-type-bg: #f3e8ff;
        --lock-hover-bg: #f3f4f6;
        --empty-icon-bg: #f3f4f6;
    }
    
    [data-theme="dark"] {
        /* Dark mode overrides */
        --text-primary: #e4e4e7;
        --text-secondary: #9ca3af;
        --text-muted: #6b7280;
        --text-on-gradient: rgba(255, 255, 255, 0.9);
        
        --bg-primary: #1a1a3e;
        --bg-secondary: #1e1e42;
        --bg-light: #252550;
        --bg-hover: #252550;
        --bg-gradient: linear-gradient(135deg, #7c7ff3 0%, #9b5de5 100%);
        
        --border-color: #2a2a5e;
        --border-light: #374151;
        
        --accent-color: #7c7ff3;
        --accent-hover: #9b5de5;
        --accent-bg: #3730a3;
        --accent-focus: rgba(124, 127, 243, 0.1);
        --accent-shadow: rgba(124, 127, 243, 0.3);
        
        --success-bg: #064e3b;
        --success-color: #34d399;
        --success-border: #065f46;
        --success-accent: #10b981;
        --success-hover: #15803d;
        
        --danger-bg: #7f1d1d;
        --danger-color: #fca5a5;
        --danger-border: #991b1b;
        --danger-accent: #ef4444;
        --danger-hover: #dc2626;
        
        --warning-bg: #78350f;
        --warning-color: #fbbf24;
        --warning-border: #92400e;
        
        --info-bg: #1e3a8a;
        --info-color: #93c5fd;
        --info-border: #1e40af;
        
        --shadow-light: rgba(0,0,0,0.3);
        --shadow-medium: rgba(0,0,0,0.4);
        --shadow-color: rgba(0,0,0,0.5);
        
        --console-bg: #0f0f23;
        --console-text: #4ade80;
        
        --lock-color: #ef4444;
        --unlock-color: #10b981;
        
        --table-header-bg: #252550;
        --version-badge-bg: #16162e;
        --package-type-bg: #3730a3;
        --lock-hover-bg: #252550;
        --empty-icon-bg: #252550;
    }
    .modern-container {
        max-width: 1400px;
        margin: 0 auto;
        padding: 2rem;
    }
    
    .page-header {
        text-align: center;
        margin-bottom: 3rem;
        padding: 3rem 0;
        background: var(--bg-gradient);
        border-radius: 20px;
        animation: fadeInDown 0.5s ease-out;
        position: relative;
        overflow: hidden;
        color: var(--bg-primary);
    }
    
    .page-header::before {
        content: '';
        position: absolute;
        top: -50%;
        right: -50%;
        width: 200%;
        height: 200%;
        background: radial-gradient(circle at 70% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
        animation: rotate 30s linear infinite;
    }
    
    .header-content {
        position: relative;
        z-index: 1;
    }
    
    .page-title {
        font-size: 2.5rem;
        font-weight: 700;
        margin-bottom: 1rem;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 1rem;
    }
    
    .package-icon {
        width: 60px;
        height: 60px;
        background: rgba(255, 255, 255, 0.2);
        backdrop-filter: blur(10px);
        border-radius: 12px;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    }
    
    .page-subtitle {
        font-size: 1.125rem;
        color: var(--text-on-gradient);
        max-width: 600px;
        margin: 0 auto;
    }
    
    .stats-bar {
        display: flex;
        gap: 1rem;
        justify-content: center;
        margin-top: 2rem;
        flex-wrap: wrap;
    }
    
    .stat-badge {
        background: rgba(255, 255, 255, 0.2);
        backdrop-filter: blur(10px);
        padding: 0.5rem 1.5rem;
        border-radius: 20px;
        font-size: 0.875rem;
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    .stat-value {
        font-weight: 700;
    }
    
    .tabs-container {
        background: var(--bg-primary);
        border-radius: 16px;
        box-shadow: 0 1px 3px var(--shadow-light), 0 10px 40px var(--shadow-medium);
        border: 1px solid var(--border-color);
        overflow: hidden;
        animation: fadeInUp 0.5s ease-out;
    }
    
    .modern-tabs {
        display: flex;
        background: var(--bg-secondary);
        border-bottom: 1px solid var(--border-color);
        overflow-x: auto;
    }
    
    .tab-item {
        padding: 1.25rem 2rem;
        cursor: pointer;
        border: none;
        background: none;
        font-weight: 500;
        color: var(--text-muted);
        position: relative;
        white-space: nowrap;
        transition: all 0.3s ease;
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    .tab-item:hover {
        color: var(--accent-color);
        background: rgba(102, 126, 234, 0.05);
    }
    
    .tab-item.active {
        color: var(--accent-color);
        background: var(--bg-primary);
    }
    
    .tab-item.active::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 3px;
        background: var(--bg-gradient);
    }
    
    .tab-icon {
        font-size: 1.125rem;
    }
    
    .tab-content {
        padding: 2rem;
    }
    
    .controls-bar {
        display: flex;
        gap: 1rem;
        margin-bottom: 2rem;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
    }
    
    .search-box {
        flex: 1;
        min-width: 300px;
        position: relative;
    }
    
    .search-input {
        width: 100%;
        padding: 0.75rem 1rem 0.75rem 2.5rem;
        border: 1px solid var(--border-color);
        border-radius: 8px;
        font-size: 0.875rem;
        transition: all 0.3s ease;
        background: var(--bg-primary);
        color: var(--text-primary);
    }
    
    .search-input:focus {
        outline: none;
        border-color: var(--accent-color);
        box-shadow: 0 0 0 3px var(--accent-focus);
    }
    
    .search-icon {
        position: absolute;
        left: 0.75rem;
        top: 50%;
        transform: translateY(-50%);
        color: var(--text-muted);
    }
    
    .control-group {
        display: flex;
        gap: 1rem;
        align-items: center;
    }
    
    .select-control {
        padding: 0.75rem 2.5rem 0.75rem 1rem;
        border: 1px solid var(--border-color);
        border-radius: 8px;
        font-size: 0.875rem;
        background: var(--bg-primary);
        color: var(--text-primary);
        appearance: none;
        background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
        background-position: right 0.5rem center;
        background-repeat: no-repeat;
        background-size: 1.5em 1.5em;
        cursor: pointer;
    }
    
    .btn {
        padding: 0.75rem 1.5rem;
        border-radius: 8px;
        font-weight: 500;
        font-size: 0.875rem;
        cursor: pointer;
        transition: all 0.3s ease;
        border: none;
        display: inline-flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    .btn-primary {
        background: var(--accent-color);
        color: white;
    }
    
    .btn-primary:hover {
        background: var(--accent-hover);
        transform: translateY(-2px);
        box-shadow: 0 4px 12px var(--accent-shadow);
    }
    
    .btn-success {
        background: var(--success-accent);
        color: white;
    }
    
    .btn-success:hover {
        background: var(--success-hover);
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
    }
    
    .btn-info {
        background: var(--info-color);
        color: white;
    }
    
    .btn-info:hover {
        background: #2563eb;
        transform: translateY(-2px);
        box-shadow: 0 4px 12px var(--accent-shadow);
    }
    
    .btn-secondary {
        background: var(--bg-primary);
        color: var(--text-muted);
        border: 1px solid var(--border-color);
    }
    
    .btn-secondary:hover {
        background: var(--bg-secondary);
        border-color: var(--border-color);
    }
    
    .package-table {
        width: 100%;
        border-collapse: collapse;
        background: var(--bg-primary);
    }
    
    .package-table thead {
        background: var(--table-header-bg);
        position: sticky;
        top: 0;
        z-index: 10;
    }
    
    .package-table th {
        padding: 1rem;
        text-align: left;
        font-weight: 600;
        color: var(--text-secondary);
        font-size: 0.875rem;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        border-bottom: 2px solid var(--border-color);
    }
    
    .package-table td {
        padding: 1rem;
        font-size: 0.875rem;
        color: var(--text-primary);
        border-bottom: 1px solid var(--border-light);
    }
    
    .package-table tbody tr {
        transition: all 0.2s ease;
    }
    
    .package-table tbody tr:hover {
        background: var(--bg-secondary);
    }
    
    .package-name {
        font-weight: 600;
        color: var(--text-primary);
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    .package-type {
        font-size: 0.625rem;
        padding: 0.125rem 0.5rem;
        border-radius: 4px;
        background: var(--package-type-bg);
        color: var(--accent-color);
        font-weight: 500;
    }
    
    .version-badge {
        font-family: 'Monaco', 'Consolas', monospace;
        font-size: 0.875rem;
        background: var(--version-badge-bg);
        padding: 0.25rem 0.75rem;
        border-radius: 6px;
        display: inline-block;
        color: var(--text-primary);
    }
    
    .upgrade-badge {
        background: var(--success-bg);
        color: var(--success-color);
        padding: 0.25rem 0.75rem;
        border-radius: 6px;
        font-size: 0.75rem;
        font-weight: 500;
        display: inline-flex;
        align-items: center;
        gap: 0.25rem;
    }
    
    .lock-toggle {
        cursor: pointer;
        font-size: 1.125rem;
        transition: all 0.3s ease;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        border-radius: 6px;
    }
    
    .lock-toggle:hover {
        background: var(--lock-hover-bg);
    }
    
    .lock-toggle.locked {
        color: var(--lock-color);
    }
    
    .lock-toggle.unlocked {
        color: var(--unlock-color);
    }
    
    .action-buttons {
        display: flex;
        gap: 0.5rem;
    }
    
    .btn-sm {
        padding: 0.5rem 1rem;
        font-size: 0.75rem;
    }
    
    .loading-spinner {
        width: 20px;
        height: 20px;
        border: 2px solid var(--border-color);
        border-top: 2px solid var(--accent-color);
        border-radius: 50%;
        animation: spin 1s linear infinite;
        display: inline-block;
    }
    
    .pagination-bar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-top: 2rem;
        padding-top: 2rem;
        border-top: 1px solid var(--border-color);
    }
    
    .pagination-info {
        font-size: 0.875rem;
        color: var(--text-muted);
    }
    
    .pagination-controls {
        display: flex;
        gap: 0.5rem;
        align-items: center;
    }
    
    .page-select {
        padding: 0.5rem 1rem;
        border: 1px solid var(--border-color);
        border-radius: 6px;
        font-size: 0.875rem;
        background: var(--bg-primary);
        color: var(--text-primary);
    }
    
    /* Modal Styles */
    .modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0, 0, 0, 0.5);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 1000;
        backdrop-filter: blur(4px);
    }
    
    .modal-content {
        background: var(--bg-primary);
        border-radius: 16px;
        max-width: 800px;
        width: 90%;
        max-height: 90vh;
        overflow: hidden;
        box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
        animation: modalSlideIn 0.3s ease-out;
    }
    
    .modal-header {
        background: var(--bg-gradient);
        color: var(--bg-primary);
        padding: 1.5rem 2rem;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
    
    .modal-title {
        font-size: 1.25rem;
        font-weight: 600;
        display: flex;
        align-items: center;
        gap: 0.75rem;
    }
    
    .modal-close {
        background: none;
        border: none;
        color: white;
        font-size: 1.5rem;
        cursor: pointer;
        padding: 0;
        width: 32px;
        height: 32px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 6px;
        transition: all 0.2s ease;
    }
    
    .modal-close:hover {
        background: rgba(255, 255, 255, 0.2);
    }
    
    .modal-body {
        padding: 2rem;
        max-height: calc(90vh - 140px);
        overflow-y: auto;
    }
    
    .console-output {
        background: var(--console-bg);
        color: var(--console-text);
        font-family: 'Monaco', 'Consolas', monospace;
        font-size: 0.875rem;
        padding: 1.5rem;
        border-radius: 8px;
        height: 400px;
        overflow-y: auto;
        white-space: pre-wrap;
        word-wrap: break-word;
    }
    
    .detail-output {
        background: var(--bg-secondary);
        border: 1px solid var(--border-color);
        border-radius: 8px;
        padding: 1.5rem;
        font-family: 'Monaco', 'Consolas', monospace;
        font-size: 0.875rem;
        max-height: 400px;
        overflow-y: auto;
        white-space: pre-wrap;
    }
    
    .empty-state {
        text-align: center;
        padding: 4rem 2rem;
        color: var(--text-muted);
    }
    
    .empty-icon {
        width: 80px;
        height: 80px;
        background: var(--empty-icon-bg);
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        margin: 0 auto 1rem;
        font-size: 2rem;
        color: var(--text-muted);
    }
    
    .alert {
        padding: 1rem 1.5rem;
        border-radius: 8px;
        display: flex;
        align-items: center;
        gap: 0.75rem;
        margin-bottom: 1rem;
        animation: slideIn 0.3s ease-out;
    }
    
    .alert-info {
        background: var(--info-bg);
        border: 1px solid var(--info-border);
        color: var(--info-color);
    }
    
    @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
    
    @keyframes rotate {
        from { transform: rotate(0deg); }
        to { transform: rotate(360deg); }
    }
    
    @keyframes fadeInDown {
        from {
            opacity: 0;
            transform: translateY(-20px);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }
    
    @keyframes fadeInUp {
        from {
            opacity: 0;
            transform: translateY(20px);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }
    
    @keyframes slideIn {
        from {
            opacity: 0;
            transform: translateX(-10px);
        }
        to {
            opacity: 1;
            transform: translateX(0);
        }
    }
    
    @keyframes modalSlideIn {
        from {
            opacity: 0;
            transform: scale(0.9);
        }
        to {
            opacity: 1;
            transform: scale(1);
        }
    }
    
    @media (max-width: 768px) {
        .controls-bar {
            flex-direction: column;
            align-items: stretch;
        }
        
        .search-box {
            min-width: auto;
        }
        
        .control-group {
            justify-content: space-between;
        }
        
        .package-table {
            font-size: 0.75rem;
        }
        
        .action-buttons {
            flex-direction: column;
        }
    }
</style>

<div class="modern-container" ng-controller="listOSPackages">
    <div class="page-header">
        <div class="header-content">
            <h1 class="page-title">
                <div class="package-icon">
                    <i class="fas fa-cube" style="font-size: 1.75rem;"></i>
                </div>
                {% trans "System Package Manager" %}
            </h1>
            <p class="page-subtitle">
                {% trans "Manage system packages, updates, and dependencies using your system's package manager (apt/yum)" %}
            </p>
            <div class="stats-bar">
                <div class="stat-badge">
                    <i class="fas fa-boxes"></i>
                    <span>{% trans "Total Packages:" %} <span class="stat-value">{$ totalPackages || 0 $}</span></span>
                </div>
                <div class="stat-badge">
                    <i class="fas fa-eye"></i>
                    <span>{% trans "Showing:" %} <span class="stat-value">{$ fetchedPackages || 0 $}</span></span>
                </div>
                <div class="stat-badge" ng-hide="cyberpanelLoading">
                    <span class="loading-spinner"></span>
                </div>
            </div>
        </div>
    </div>

    <div class="tabs-container">
        <!-- Tab Navigation -->
        <div class="modern-tabs">
            <button class="tab-item active" ng-click="fetchPackages('upgrade')" data-tab="updates">
                <i class="fas fa-sync-alt tab-icon"></i>
                {% trans "Available Updates" %}
            </button>
            <button class="tab-item" ng-click="fetchPackages()" data-tab="all">
                <i class="fas fa-list tab-icon"></i>
                {% trans "All Packages" %}
            </button>
            <button class="tab-item" ng-click="fetchPackages('CyberPanel')" data-tab="cyberpanel">
                <i class="fas fa-shield-alt tab-icon"></i>
                {% trans "CyberPanel Packages" %}
            </button>
        </div>

        <!-- Tab Content -->
        <div class="tab-content">
            <!-- Controls Bar -->
            <div class="controls-bar">
                <div class="search-box">
                    <i class="fas fa-search search-icon"></i>
                    <input type="text" class="search-input" placeholder="{% trans 'Search packages...' %}" 
                           ng-model="packSearch">
                </div>
                
                <div class="control-group">
                    <select ng-model="recordsToShow" class="select-control" 
                            ng-change="fetchPackages(currentTab)">
                        <option value="10">10 {% trans "per page" %}</option>
                        <option value="50">50 {% trans "per page" %}</option>
                        <option value="100">100 {% trans "per page" %}</option>
                        <option value="500">500 {% trans "per page" %}</option>
                    </select>
                    
                    <button class="btn btn-success" ng-click="updatePackage('all')" 
                            ng-show="currentTab === 'upgrade'">
                        <i class="fas fa-download"></i>
                        {% trans "Update All" %}
                    </button>
                </div>
            </div>

            <!-- Alert for important info -->
            <div class="alert alert-info" ng-show="currentTab === 'upgrade' && totalPackages > 0">
                <i class="fas fa-info-circle"></i>
                <span>{% trans "Important: Always backup your system before performing major updates. Some updates may require system restart." %}</span>
            </div>

            <!-- Package Table -->
            <div class="table-container" ng-show="allPackages.length > 0">
                <table class="package-table">
                    <thead>
                        <tr>
                            <th>{% trans "Package" %}</th>
                            <th>{% trans "Current Version" %}</th>
                            <th ng-show="currentTab === 'upgrade'">{% trans "New Version" %}</th>
                            <th>{% trans "Status" %}</th>
                            <th>{% trans "Actions" %}</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="package in allPackages | filter:packSearch">
                            <td>
                                <div class="package-name">
                                    <span>{$ package.package $}</span>
                                    <span class="package-type" ng-if="package.type">
                                        {$ package.type $}
                                    </span>
                                </div>
                            </td>
                            <td>
                                <span class="version-badge">{$ package.version $}</span>
                            </td>
                            <td ng-show="currentTab === 'upgrade'">
                                <span class="upgrade-badge">
                                    <i class="fas fa-arrow-up"></i>
                                    {$ package.upgrade || '-' $}
                                </span>
                            </td>
                            <td>
                                <a class="lock-toggle" 
                                   ng-class="{'locked': package.lock == 1, 'unlocked': package.lock == 0}"
                                   ng-click="lockStatus(package.package, package.lock == 1 ? 1 : 0)"
                                   title="{$ package.lock == 1 ? 'Package is locked' : 'Package is unlocked' $}">
                                    <i class="fas" 
                                       ng-class="{'fa-lock': package.lock == 1, 'fa-lock-open': package.lock == 0}"></i>
                                </a>
                            </td>
                            <td>
                                <div class="action-buttons">
                                    <button class="btn btn-info btn-sm" 
                                            ng-click="showPackageDetails(package.package)">
                                        <i class="fas fa-info-circle"></i>
                                        {% trans "Details" %}
                                    </button>
                                    <button class="btn btn-primary btn-sm" 
                                            ng-click="showUpdateModal(package.package)"
                                            ng-show="currentTab === 'upgrade' || currentTab === 'cyberpanel'">
                                        <i class="fas fa-download"></i>
                                        {% trans "Update" %}
                                    </button>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <!-- Empty State -->
            <div class="empty-state" ng-show="allPackages.length === 0 && cyberpanelLoading">
                <div class="empty-icon">
                    <i class="fas fa-cube"></i>
                </div>
                <h3>{% trans "No packages found" %}</h3>
                <p>{% trans "Try adjusting your search criteria or check your connection" %}</p>
            </div>

            <!-- Pagination -->
            <div class="pagination-bar" ng-show="allPackages.length > 0">
                <div class="pagination-info">
                    {% trans "Showing" %} {$ ((currentPage - 1) * recordsToShow) + 1 $} - 
                    {$ Math.min(currentPage * recordsToShow, totalPackages) $} 
                    {% trans "of" %} {$ totalPackages $} {% trans "packages" %}
                </div>
                <div class="pagination-controls">
                    <span>{% trans "Page:" %}</span>
                    <select ng-model="currentPage" class="page-select" 
                            ng-change="fetchPackages(currentTab)">
                        <option ng-repeat="page in pagination" value="{$ $index + 1 $}">
                            {$ $index + 1 $}
                        </option>
                    </select>
                    <span>{% trans "of" %} {$ pagination.length $}</span>
                </div>
            </div>
        </div>
    </div>

    <!-- Package Details Modal -->
    <div class="modal-overlay" ng-show="showDetails" ng-click="closeModal($event)">
        <div class="modal-content" ng-click="$event.stopPropagation()">
            <div class="modal-header">
                <h3 class="modal-title">
                    <i class="fas fa-info-circle"></i>
                    {% trans "Package Details:" %} {$ selectedPackage $}
                </h3>
                <button class="modal-close" ng-click="closeDetails()">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="modal-body">
                <div class="detail-output" ng-bind="packageDetails"></div>
            </div>
        </div>
    </div>

    <!-- Update Progress Modal -->
    <div class="modal-overlay" ng-show="showUpdate" ng-click="closeModal($event)">
        <div class="modal-content" ng-click="$event.stopPropagation()">
            <div class="modal-header">
                <h3 class="modal-title">
                    <i class="fas fa-download"></i>
                    {% trans "Updating Package:" %} {$ updatingPackage $}
                    <span ng-hide="cyberpanelLoading" class="loading-spinner" style="margin-left: 1rem;"></span>
                </h3>
                <button class="modal-close" ng-click="closeUpdate()" ng-disabled="!updateComplete">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="modal-body">
                <div class="console-output" ng-bind="requestData"></div>
            </div>
        </div>
    </div>
</div>

<script>
// Tab switching functionality
document.addEventListener('DOMContentLoaded', function() {
    const tabButtons = document.querySelectorAll('.tab-item');
    
    tabButtons.forEach(button => {
        button.addEventListener('click', function() {
            // Remove active class from all tabs
            tabButtons.forEach(btn => btn.classList.remove('active'));
            // Add active class to clicked tab
            this.classList.add('active');
        });
    });
});

// Add to the Angular controller
app.controller('listOSPackages', function($scope, $http, $timeout) {
    // ... existing controller code ...
    
    // Initialize current tab
    $scope.currentTab = 'upgrade';
    
    // Show/hide modals
    $scope.showDetails = false;
    $scope.showUpdate = false;
    $scope.selectedPackage = '';
    $scope.updatingPackage = '';
    $scope.updateComplete = false;
    
    $scope.showPackageDetails = function(packageName) {
        $scope.selectedPackage = packageName;
        $scope.showDetails = true;
        $scope.fetchPackageDetails(packageName);
    };
    
    $scope.closeDetails = function() {
        $scope.showDetails = false;
        $scope.selectedPackage = '';
        $scope.packageDetails = '';
    };
    
    $scope.showUpdateModal = function(packageName) {
        $scope.updatingPackage = packageName;
        $scope.showUpdate = true;
        $scope.updateComplete = false;
        $scope.updatePackage(packageName);
    };
    
    $scope.closeUpdate = function() {
        $scope.showUpdate = false;
        $scope.updatingPackage = '';
        $scope.requestData = '';
    };
    
    $scope.closeModal = function(event) {
        if (event.target.classList.contains('modal-overlay')) {
            if ($scope.showDetails) $scope.closeDetails();
            if ($scope.showUpdate && $scope.updateComplete) $scope.closeUpdate();
        }
    };
    
    // Override fetchPackages to set current tab
    var originalFetchPackages = $scope.fetchPackages;
    $scope.fetchPackages = function(type) {
        if (type === 'upgrade') $scope.currentTab = 'upgrade';
        else if (type === 'CyberPanel') $scope.currentTab = 'cyberpanel';
        else $scope.currentTab = 'all';
        
        if (originalFetchPackages) {
            originalFetchPackages(type);
        }
    };
});
</script>

{% endblock %}

{% block footer_scripts %}
<script src="{% static 'serverStatus/serverStatus.js' %}"></script>
{% endblock %}