Файловый менеджер - Редактировать - /home/gqdcvggs/store.imators.com/admin/index.php
Назад
<!DOCTYPE html> <html lang="en-GB"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin - Imators Store</title> <link href="https://cdn.imators.com/logo.png" rel="icon" type="image/png" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.1.2/dist/tailwind.min.css"> <style> @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap'); body { font-family: 'Poppins', sans-serif; background: #000; color: #fff; } .sidebar { width: 250px; background: #111; border-right: 1px solid #333; height: 100vh; position: fixed; left: 0; top: 0; padding: 32px 0; } .content { margin-left: 250px; padding: 32px; } .nav-item { display: block; padding: 16px 24px; color: #999; cursor: pointer; border-bottom: 1px solid #222; transition: all 0.2s; } .nav-item:hover, .nav-item.active { color: #fff; background: #222; } .btn { background: #fff; color: #000; border: none; padding: 12px 24px; border-radius: 6px; font-size: 14px; cursor: pointer; font-family: inherit; } .btn:hover { background: #f0f0f0; } .btn-danger { background: #dc3545; color: #fff; } .btn-danger:hover { background: #c82333; } .btn-secondary { background: none; color: #fff; border: 1px solid #333; } .btn-secondary:hover { border-color: #666; background: rgba(255, 255, 255, 0.05); } .table { width: 100%; border-collapse: collapse; background: #111; border: 1px solid #333; border-radius: 8px; overflow: hidden; } .table th, .table td { padding: 16px; text-align: left; border-bottom: 1px solid #333; } .table th { background: #222; color: #fff; font-size: 13px; text-transform: uppercase; } .table td { color: #ccc; font-size: 14px; } .table tr:hover { background: #1a1a1a; } .modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.9); z-index: 1000; display: none; align-items: center; justify-content: center; } .modal.show { display: flex; } .modal-content { background: #111; border: 1px solid #333; border-radius: 8px; padding: 32px; width: 90vw; max-width: 600px; max-height: 90vh; overflow-y: auto; position: relative; } .modal-close { position: absolute; top: 16px; right: 16px; background: none; border: none; color: #999; font-size: 24px; cursor: pointer; } .modal-close:hover { color: #fff; } .form-group { margin-bottom: 24px; } .label { display: block; margin-bottom: 8px; font-size: 14px; color: #fff; } .input, .select, .textarea { width: 100%; background: #000; border: 1px solid #333; border-radius: 6px; padding: 12px 16px; color: #fff; font-size: 14px; font-family: inherit; } .input:focus, .select:focus, .textarea:focus { outline: none; border-color: #666; } .textarea { min-height: 80px; resize: vertical; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .section { display: none; } .section.active { display: block; } .preview-img { width: 60px; height: 60px; object-fit: contain; border-radius: 8px; border: 1px solid #333; background: rgba(255, 255, 255, 0.05); padding: 4px; } .status-badge { padding: 4px 12px; border-radius: 12px; font-size: 12px; text-transform: uppercase; } .status-active { background: rgba(40, 167, 69, 0.2); color: #28a745; } .status-inactive { background: rgba(220, 53, 69, 0.2); color: #dc3545; } .status-coming_soon { background: rgba(255, 193, 7, 0.2); color: #ffc107; } </style> </head> <body> <div class="sidebar"> <div style="padding: 0 24px 24px; border-bottom: 1px solid #333; display: flex; align-items: center; gap: 12px;"> <img src="https://cdn.imators.com/logo.png" alt="Imators" style="width: 32px; height: 32px;"> <h2 style="font-size: 18px; font-weight: 400;">Admin Panel</h2> </div> <nav> <div class="nav-item active" onclick="showSection('apps')">Apps Management</div> <div class="nav-item" onclick="showSection('stats')">Statistics</div> </nav> </div> <div class="content"> <div id="apps-section" class="section active"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px solid #333;"> <h1 style="font-size: 32px; font-weight: 300;">Apps Management</h1> <button class="btn" onclick="openModal()">Add New App</button> </div> <div style="background: #111; border: 1px solid #333; border-radius: 8px; padding: 24px; margin-bottom: 32px;"> <div class="grid-2"> <div class="form-group"> <label class="label">Search Apps</label> <input type="text" class="input" placeholder="Search by name..." id="search" onkeyup="searchApps()"> </div> <div class="form-group"> <label class="label">Filter by Category</label> <select class="select" id="category-filter" onchange="filterApps()"> <option value="">All Categories</option> <option value="made_by_imators">Made by Imators</option> <option value="productivity">Productivity</option> <option value="development">Development</option> <option value="security">Security</option> <option value="utilities">Utilities</option> </select> </div> </div> </div> <table class="table"> <thead> <tr> <th>Icon</th> <th>Name</th> <th>Category</th> <th>Price</th> <th>Downloads</th> <th>Status</th> <th>Actions</th> </tr> </thead> <tbody id="apps-table"> <tr> <td colspan="7" style="text-align: center; padding: 40px; color: #666;"> Loading apps... </td> </tr> </tbody> </table> </div> <div id="stats-section" class="section"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px solid #333;"> <h1 style="font-size: 32px; font-weight: 300;">Statistics</h1> </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 24px;"> <div style="background: #111; border: 1px solid #333; border-radius: 8px; padding: 32px; text-align: center;"> <h3 style="font-size: 32px; margin-bottom: 8px;" id="total-apps">0</h3> <p style="color: #999;">Total Apps</p> </div> <div style="background: #111; border: 1px solid #333; border-radius: 8px; padding: 32px; text-align: center;"> <h3 style="font-size: 32px; margin-bottom: 8px;" id="active-apps">0</h3> <p style="color: #999;">Active Apps</p> </div> <div style="background: #111; border: 1px solid #333; border-radius: 8px; padding: 32px; text-align: center;"> <h3 style="font-size: 32px; margin-bottom: 8px;" id="total-downloads">0</h3> <p style="color: #999;">Total Downloads</p> </div> <div style="background: #111; border: 1px solid #333; border-radius: 8px; padding: 32px; text-align: center;"> <h3 style="font-size: 32px; margin-bottom: 8px;" id="featured-apps">0</h3> <p style="color: #999;">Featured Apps</p> </div> </div> </div> </div> <div class="modal" id="app-modal"> <div class="modal-content"> <button class="modal-close" onclick="closeModal()">×</button> <h2 style="font-size: 24px; font-weight: 300; margin-bottom: 24px;" id="modal-title">Add New App</h2> <form id="app-form" onsubmit="saveApp(event)"> <input type="hidden" id="app-id"> <div class="grid-2"> <div class="form-group"> <label class="label">App Name *</label> <input type="text" class="input" id="app-name" required onkeyup="generateSlug()"> </div> <div class="form-group"> <label class="label">Slug *</label> <input type="text" class="input" id="app-slug" required> </div> </div> <div class="form-group"> <label class="label">Description *</label> <textarea class="textarea" id="app-description" required></textarea> </div> <div class="form-group"> <label class="label">Long Description</label> <textarea class="textarea" id="app-long-description" style="min-height: 120px;"></textarea> </div> <div class="grid-2"> <div class="form-group"> <label class="label">Category *</label> <select class="select" id="app-category" required> <option value="">Select Category</option> <option value="made_by_imators">Made by Imators</option> <option value="productivity">Productivity</option> <option value="development">Development</option> <option value="security">Security</option> <option value="utilities">Utilities</option> </select> </div> <div class="form-group"> <label class="label">Price Type *</label> <select class="select" id="app-price-type" required> <option value="free">Free</option> <option value="one_time">One-time Purchase</option> <option value="subscription">Subscription</option> </select> </div> </div> <div class="grid-2"> <div class="form-group"> <label class="label">Price (£)</label> <input type="number" class="input" id="app-price" step="0.01" min="0"> </div> <div class="form-group"> <label class="label">Version</label> <input type="text" class="input" id="app-version" placeholder="1.0.0"> </div> </div> <div class="form-group"> <label class="label">App Icon</label> <div style="border: 2px dashed #333; border-radius: 8px; padding: 32px; text-align: center; cursor: pointer;" onclick="document.getElementById('icon-file').click()"> <input type="file" id="icon-file" accept="image/*" style="display: none;" onchange="handleIconUpload()"> <div id="icon-upload-area"> <p style="margin-bottom: 8px; color: #999;">Drop icon here or click to upload</p> <p style="font-size: 12px; color: #666;">PNG, JPG up to 2MB</p> </div> <div id="icon-preview" style="display: none;"> <img id="icon-preview-img" style="width: 80px; height: 80px; object-fit: cover; border-radius: 8px; margin-bottom: 8px;"> <p style="font-size: 12px; color: #999;" id="icon-filename"></p> </div> </div> <input type="hidden" id="app-icon-url"> </div> <div class="form-group"> <label class="label">Download File (for free apps)</label> <div style="border: 2px dashed #333; border-radius: 8px; padding: 32px; text-align: center; cursor: pointer;" onclick="document.getElementById('download-file').click()"> <input type="file" id="download-file" style="display: none;" onchange="handleDownloadUpload()"> <div id="download-upload-area"> <p style="margin-bottom: 8px; color: #999;">Drop file here or click to upload</p> <p style="font-size: 12px; color: #666;">ZIP, EXE, APK, etc.</p> </div> <div id="download-preview" style="display: none;"> <p style="font-size: 14px; color: #fff;" id="download-filename"></p> <p style="font-size: 12px; color: #999;" id="download-size"></p> </div> </div> <input type="hidden" id="app-download-url"> </div> <div class="form-group"> <label class="label">Purchase URL (for paid apps)</label> <input type="url" class="input" id="app-purchase-url" placeholder="https://example.com/purchase"> </div> <div class="form-group"> <label class="label">Requirements</label> <textarea class="textarea" id="app-requirements" placeholder="Windows 10+, macOS 10.15+, etc."></textarea> </div> <div class="grid-2"> <div class="form-group"> <label class="label">Size (MB)</label> <input type="number" class="input" id="app-size" step="0.1" min="0"> </div> <div class="form-group"> <label class="label">Status</label> <select class="select" id="app-status"> <option value="active">Active</option> <option value="inactive">Inactive</option> <option value="coming_soon">Coming Soon</option> </select> </div> </div> <div class="form-group"> <label style="display: flex; align-items: center; gap: 8px; cursor: pointer;"> <input type="checkbox" id="app-featured"> <span class="label" style="margin: 0;">Featured App</span> </label> </div> <div class="form-group"> <label style="display: flex; align-items: center; gap: 8px; cursor: pointer;"> <input type="checkbox" id="app-in-app-purchases"> <span class="label" style="margin: 0;">Has In-App Purchases</span> </label> </div> <div style="display: flex; gap: 16px; justify-content: flex-end; margin-top: 32px;"> <button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button> <button type="submit" class="btn">Save App</button> </div> </form> </div> </div> <script> let apps = []; let allApps = []; let currentEditId = null; // Navigation function showSection(section) { document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active')); document.querySelectorAll('.section').forEach(sec => sec.classList.remove('active')); event.target.classList.add('active'); document.getElementById(section + '-section').classList.add('active'); if (section === 'apps') { loadApps(); } else if (section === 'stats') { loadStats(); } } // Load apps async function loadApps() { try { const response = await fetch('admin_api.php?action=get_apps'); if (!response.ok) throw new Error('Failed to load apps'); const data = await response.json(); allApps = data; apps = data; renderApps(); } catch (error) { console.error('Error loading apps:', error); document.getElementById('apps-table').innerHTML = ` <tr><td colspan="7" style="text-align: center; padding: 40px; color: #dc3545;"> Error loading apps: ${error.message} </td></tr> `; } } // Load stats async function loadStats() { try { const response = await fetch('admin_api.php?action=get_stats'); if (!response.ok) throw new Error('Failed to load stats'); const stats = await response.json(); document.getElementById('total-apps').textContent = stats.totals.apps; document.getElementById('active-apps').textContent = stats.totals.active_apps; document.getElementById('total-downloads').textContent = (stats.totals.total_downloads || 0).toLocaleString(); document.getElementById('featured-apps').textContent = stats.totals.featured_apps; } catch (error) { console.error('Error loading stats:', error); } } // Render apps table function renderApps() { const tbody = document.getElementById('apps-table'); if (apps.length === 0) { tbody.innerHTML = ` <tr><td colspan="7" style="text-align: center; padding: 40px; color: #666;"> No apps found </td></tr> `; return; } tbody.innerHTML = apps.map(app => ` <tr> <td> <img src="${app.icon_url || '/uploads/icons/default.png'}" class="preview-img" onerror="this.src='/uploads/icons/default.png'"> </td> <td> <div style="font-weight: 400;">${app.name}</div> <div style="font-size: 12px; color: #666;">${app.slug}</div> </td> <td>${formatCategory(app.category)}</td> <td>${formatPrice(app.price, app.price_type)}</td> <td>${(app.downloads || 0).toLocaleString()}</td> <td> <span class="status-badge status-${app.status}"> ${app.status} </span> </td> <td> <div style="display: flex; gap: 8px;"> <button class="btn btn-secondary" style="padding: 6px 12px; font-size: 12px;" onclick="editApp(${app.id})">Edit</button> <button class="btn btn-danger" style="padding: 6px 12px; font-size: 12px;" onclick="deleteApp(${app.id})">Delete</button> </div> </td> </tr> `).join(''); } // Format functions function formatCategory(category) { const categories = { 'made_by_imators': 'Made by Imators', 'productivity': 'Productivity', 'development': 'Development', 'security': 'Security', 'utilities': 'Utilities' }; return categories[category] || category; } function formatPrice(price, type) { if (type === 'free') return 'Free'; if (type === 'subscription') return `£${price}/mo`; return `£${price}`; } // Search and filter function searchApps() { const searchTerm = document.getElementById('search').value.toLowerCase(); const categoryFilter = document.getElementById('category-filter').value; apps = allApps.filter(app => { const matchesSearch = app.name.toLowerCase().includes(searchTerm) || app.slug.toLowerCase().includes(searchTerm); const matchesCategory = !categoryFilter || app.category === categoryFilter; return matchesSearch && matchesCategory; }); renderApps(); } function filterApps() { searchApps(); } // Modal functions function openModal() { currentEditId = null; document.getElementById('modal-title').textContent = 'Add New App'; document.getElementById('app-form').reset(); document.getElementById('app-id').value = ''; document.getElementById('app-modal').classList.add('show'); } function editApp(id) { const app = allApps.find(a => a.id == id); if (!app) return; currentEditId = id; document.getElementById('modal-title').textContent = 'Edit App'; document.getElementById('app-id').value = app.id; document.getElementById('app-name').value = app.name; document.getElementById('app-slug').value = app.slug; document.getElementById('app-description').value = app.description; document.getElementById('app-long-description').value = app.long_description || ''; document.getElementById('app-category').value = app.category; document.getElementById('app-price-type').value = app.price_type; document.getElementById('app-price').value = app.price || ''; document.getElementById('app-version').value = app.version || ''; document.getElementById('app-purchase-url').value = app.purchase_url || ''; document.getElementById('app-requirements').value = app.requirements || ''; document.getElementById('app-size').value = app.size_mb || ''; document.getElementById('app-status').value = app.status; document.getElementById('app-featured').checked = app.featured == 1; document.getElementById('app-in-app-purchases').checked = app.has_in_app_purchases == 1; document.getElementById('app-modal').classList.add('show'); } function closeModal() { document.getElementById('app-modal').classList.remove('show'); resetFileUploads(); } // Generate slug function generateSlug() { const name = document.getElementById('app-name').value; const slug = name.toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .trim(); document.getElementById('app-slug').value = slug; } // Save app async function saveApp(event) { event.preventDefault(); const formData = new FormData(); formData.append('action', currentEditId ? 'update_app' : 'create_app'); if (currentEditId) { formData.append('id', currentEditId); } formData.append('name', document.getElementById('app-name').value); formData.append('slug', document.getElementById('app-slug').value); formData.append('description', document.getElementById('app-description').value); formData.append('long_description', document.getElementById('app-long-description').value); formData.append('category', document.getElementById('app-category').value); formData.append('price_type', document.getElementById('app-price-type').value); formData.append('price', document.getElementById('app-price').value || '0'); formData.append('version', document.getElementById('app-version').value || '1.0.0'); formData.append('purchase_url', document.getElementById('app-purchase-url').value); formData.append('requirements', document.getElementById('app-requirements').value); formData.append('size_mb', document.getElementById('app-size').value || '0'); formData.append('status', document.getElementById('app-status').value); formData.append('featured', document.getElementById('app-featured').checked ? '1' : '0'); formData.append('has_in_app_purchases', document.getElementById('app-in-app-purchases').checked ? '1' : '0'); // Add files if uploaded const iconFile = document.getElementById('icon-file').files[0]; if (iconFile) { formData.append('icon', iconFile); } const downloadFile = document.getElementById('download-file').files[0]; if (downloadFile) { formData.append('download_file', downloadFile); } try { const response = await fetch('admin_api.php', { method: 'POST', body: formData }); if (!response.ok) throw new Error('Save failed'); const result = await response.json(); closeModal(); loadApps(); alert('App saved successfully!'); } catch (error) { console.error('Error saving app:', error); alert('Error saving app: ' + error.message); } } // Delete app async function deleteApp(id) { if (!confirm('Are you sure you want to delete this app? This cannot be undone.')) { return; } try { const response = await fetch('admin_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete_app', id: parseInt(id) }) }); if (!response.ok) throw new Error('Delete failed'); loadApps(); alert('App deleted successfully!'); } catch (error) { console.error('Error deleting app:', error); alert('Error deleting app: ' + error.message); } } // Initialize loadApps(); </script> </body> </html>
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка