Files
Ske_Signage/app/templates/admin/index.html
2025-07-16 16:08:40 +03:00

1016 lines
47 KiB
HTML

{% extends "base.html" %}
{% block title %}Admin Panel - SKE Digital Signage{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
<div class="col">
<h1><i class="bi bi-gear-fill"></i> Admin Panel</h1>
<p class="text-muted">System administration and user management</p>
</div>
</div>
<!-- Quick Stats -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-white bg-primary">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5 class="card-title">Total Users</h5>
<h2>{{ users|length }}</h2>
</div>
<div class="align-self-center">
<i class="bi bi-people" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-success">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5 class="card-title">Active Users</h5>
<h2>{{ users|selectattr('is_active_user')|list|length }}</h2>
</div>
<div class="align-self-center">
<i class="bi bi-person-check" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-warning">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5 class="card-title">Admin Users</h5>
<h2>{{ users|selectattr('role', 'equalto', 'admin')|list|length }}</h2>
</div>
<div class="align-self-center">
<i class="bi bi-shield-check" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-info">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5 class="card-title">Regular Users</h5>
<h2>{{ users|selectattr('role', 'equalto', 'user')|list|length }}</h2>
</div>
<div class="align-self-center">
<i class="bi bi-person" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation Tabs -->
<ul class="nav nav-tabs mb-4" id="adminTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="users-tab" data-bs-toggle="tab" data-bs-target="#users" type="button" role="tab">
<i class="bi bi-people"></i> User Management
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="maintenance-tab" data-bs-toggle="tab" data-bs-target="#maintenance" type="button" role="tab">
<i class="bi bi-tools"></i> System Maintenance
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="scheduler-tab" data-bs-toggle="tab" data-bs-target="#scheduler" type="button" role="tab">
<i class="bi bi-clock"></i> Task Scheduler
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="tab-content" id="adminTabContent">
<!-- User Management Tab -->
<div class="tab-pane fade show active" id="users" role="tabpanel">
<div class="row">
<!-- User Management -->
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="bi bi-people"></i> User Management</h5>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal">
<i class="bi bi-plus"></i> Add User
</button>
</div>
<div class="card-body">
{% if users %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Username</th>
<th>Role</th>
<th>Status</th>
<th>Created</th>
<th>Last Login</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>
<strong>{{ user.username }}</strong>
{% if user.username == current_user.username %}
<span class="badge bg-secondary">You</span>
{% endif %}
</td>
<td>
{% if user.role == 'admin' %}
<span class="badge bg-danger">Admin</span>
{% else %}
<span class="badge bg-primary">User</span>
{% endif %}
</td>
<td>
{% if user.is_active_user %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Inactive</span>
{% endif %}
</td>
<td>{{ user.created_at.strftime('%Y-%m-%d') if user.created_at else 'N/A' }}</td>
<td>{{ user.last_login.strftime('%Y-%m-%d %H:%M') if user.last_login else 'Never' }}</td>
<td>
<div class="btn-group btn-group-sm">
{% if user.username != current_user.username %}
<button type="button" class="btn btn-outline-warning"
onclick="editUser('{{ user.id }}', '{{ user.username }}', '{{ user.role }}', {{ user.is_active_user|tojson }})">
<i class="bi bi-pencil"></i>
</button>
<button type="button" class="btn btn-outline-danger"
onclick="deleteUser('{{ user.id }}', '{{ user.username }}')">
<i class="bi bi-trash"></i>
</button>
{% else %}
<button type="button" class="btn btn-outline-secondary" disabled>
<i class="bi bi-lock"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4">
<i class="bi bi-people text-muted" style="font-size: 3rem;"></i>
<h5 class="text-muted mt-2">No Users Found</h5>
<p class="text-muted">Create your first user to get started.</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- System Info -->
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-info-circle"></i> System Assets</h5>
</div>
<div class="card-body">
<div class="mb-3">
<h6>Logo Asset</h6>
{% if logo_exists %}
<span class="badge bg-success">Available</span>
<div class="mt-2">
<img src="{{ url_for('static', filename='assets/logo.png') }}" alt="Logo" class="img-thumbnail" style="max-height: 100px;">
</div>
{% else %}
<span class="badge bg-warning">Missing</span>
<p class="text-muted small mt-1">Upload logo.png to static/assets/</p>
{% endif %}
</div>
<div class="mb-3">
<h6>Login Picture</h6>
{% if login_picture_exists %}
<span class="badge bg-success">Available</span>
<div class="mt-2">
<img src="{{ url_for('static', filename='assets/login_picture.png') }}" alt="Login Picture" class="img-thumbnail" style="max-height: 100px;">
</div>
{% else %}
<span class="badge bg-warning">Missing</span>
<p class="text-muted small mt-1">Upload login_picture.png to static/assets/</p>
{% endif %}
</div>
</div>
</div>
<div class="card mt-3">
<div class="card-header">
<h5><i class="bi bi-tools"></i> Quick Actions</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('dashboard.index') }}" class="btn btn-outline-primary">
<i class="bi bi-speedometer2"></i> Dashboard
</a>
<a href="{{ url_for('player.index') }}" class="btn btn-outline-info">
<i class="bi bi-display"></i> Manage Players
</a>
<a href="{{ url_for('content.upload') }}" class="btn btn-outline-success">
<i class="bi bi-upload"></i> Upload Content
</a>
<button type="button" class="btn btn-outline-warning" onclick="clearLogs()">
<i class="bi bi-trash"></i> Clear Logs
</button>
</div>
</div>
</div>
</div>
<!-- System Maintenance Tab -->
<div class="tab-pane fade" id="maintenance" role="tabpanel">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-trash"></i> File Management</h5>
</div>
<div class="card-body">
<div class="mb-4">
<h6><i class="bi bi-trash"></i> Clean Unused Files</h6>
<p class="text-muted small">Remove media files from uploads folder that are not referenced by any players. This helps free up storage space by removing orphaned files.</p>
<div class="d-flex gap-2">
<button type="button" class="btn btn-warning" onclick="cleanUnusedFiles()">
<i class="bi bi-play-circle"></i> Run Now
</button>
<button type="button" class="btn btn-outline-primary" onclick="scheduleCleanupTask()">
<i class="bi bi-clock"></i> Schedule
</button>
</div>
<div id="cleanupStatus" class="mt-2"></div>
</div>
<div class="mb-4">
<h6><i class="bi bi-journal-x"></i> Clear Server Logs</h6>
<p class="text-muted small">Remove all server log entries from the database to free up space.</p>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-warning" onclick="clearLogs()">
<i class="bi bi-play-circle"></i> Clear Now
</button>
<button type="button" class="btn btn-outline-primary" onclick="scheduleLogClearTask()">
<i class="bi bi-clock"></i> Schedule
</button>
</div>
</div>
<div class="mb-3">
<h6><i class="bi bi-database"></i> Database Maintenance</h6>
<p class="text-muted small">Optimize database performance and clean up orphaned records.</p>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-info" onclick="optimizeDatabase()">
<i class="bi bi-play-circle"></i> Optimize Now
</button>
<button type="button" class="btn btn-outline-primary" onclick="scheduleDatabaseTask()">
<i class="bi bi-clock"></i> Schedule
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-info-circle"></i> Maintenance Status</h5>
</div>
<div class="card-body">
<div id="maintenanceStatus">
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> Ready to perform maintenance tasks.
</div>
</div>
</div>
</div>
<div class="card mt-3">
<div class="card-header">
<h5><i class="bi bi-upload"></i> Asset Upload</h5>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('admin.upload_assets') }}" enctype="multipart/form-data">
<div class="mb-3">
<label for="logo" class="form-label">Logo (PNG format)</label>
<input type="file" class="form-control" id="logo" name="logo" accept=".png">
</div>
<div class="mb-3">
<label for="login_picture" class="form-label">Login Picture (PNG format)</label>
<input type="file" class="form-control" id="login_picture" name="login_picture" accept=".png">
</div>
<button type="submit" class="btn btn-primary">
<i class="bi bi-upload"></i> Upload Assets
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Task Scheduler Tab -->
<div class="tab-pane fade" id="scheduler" role="tabpanel">
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="bi bi-clock"></i> Scheduled Tasks</h5>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createTaskModal">
<i class="bi bi-plus"></i> Add Task
</button>
</div>
<div class="card-body">
<div id="scheduledTasks">
<!-- Tasks will be loaded here -->
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-gear"></i> Quick Setup</h5>
</div>
<div class="card-body">
<h6>Daily File Cleanup</h6>
<p class="text-muted small">Automatically clean unused files daily at 1:00 AM</p>
<form method="POST" action="{{ url_for('admin.create_scheduled_task') }}">
<input type="hidden" name="task_type" value="cleanup_files">
<input type="hidden" name="schedule" value="0 1 * * *">
<input type="hidden" name="enabled" value="true">
<button type="submit" class="btn btn-success btn-sm w-100">
<i class="bi bi-clock"></i> Enable Daily Cleanup
</button>
</form>
<hr>
<h6>Custom Schedule</h6>
<form method="POST" action="{{ url_for('admin.create_scheduled_task') }}">
<div class="mb-2">
<label class="form-label">Task Type</label>
<select class="form-select form-select-sm" name="task_type" required>
<option value="cleanup_files">Clean Unused Files</option>
<option value="clear_logs">Clear Server Logs</option>
<option value="optimize_db">Optimize Database</option>
</select>
</div>
<div class="mb-2">
<label class="form-label">Time</label>
<input type="time" class="form-control form-control-sm" name="time" value="01:00" required>
</div>
<div class="mb-3">
<label class="form-label">Frequency</label>
<select class="form-select form-select-sm" name="frequency" required>
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100">
<i class="bi bi-plus"></i> Create Task
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Create Scheduled Task Modal -->
<div class="modal fade" id="createTaskModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create Scheduled Task</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ url_for('admin.create_scheduled_task') }}">
<div class="modal-body">
<div class="mb-3">
<label for="taskName" class="form-label">Task Name *</label>
<input type="text" class="form-control" id="taskName" name="name" required>
</div>
<div class="mb-3">
<label for="taskType" class="form-label">Task Type *</label>
<select class="form-select" id="taskType" name="task_type" required>
<option value="cleanup_files">Clean Unused Files</option>
<option value="clear_logs">Clear Server Logs</option>
<option value="optimize_db">Optimize Database</option>
<option value="backup_db">Backup Database</option>
</select>
</div>
<div class="mb-3">
<label for="cronExpression" class="form-label">Cron Expression *</label>
<input type="text" class="form-control" id="cronExpression" name="schedule"
placeholder="0 1 * * * (daily at 1:00 AM)" required>
<div class="form-text">
Examples: <br>
<code>0 1 * * *</code> - Daily at 1:00 AM<br>
<code>0 0 * * 0</code> - Weekly on Sunday at midnight<br>
<code>0 2 1 * *</code> - Monthly on 1st at 2:00 AM
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="taskEnabled" name="enabled" checked>
<label class="form-check-label" for="taskEnabled">
Enable Task
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create Task</button>
</div>
</form>
</div>
</div>
</div>
<!-- Create User Modal -->
<div class="modal fade" id="createUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ url_for('admin.create_user') }}">
<div class="modal-body">
<div class="mb-3">
<label for="username" class="form-label">Username *</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password *</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select class="form-select" id="role" name="role">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create User</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit User Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ url_for('admin.edit_user') }}" onsubmit="setTimeout(function(){ window.location.reload(); }, 1000);">
<input type="hidden" id="editUserId" name="user_id">
<div class="modal-body">
<div class="mb-3">
<label for="editUsername" class="form-label">Username</label>
<input type="text" class="form-control" id="editUsername" name="username" readonly>
</div>
<div class="mb-3">
<label for="editRole" class="form-label">Role</label>
<select class="form-select" id="editRole" name="role">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="editIsActive" name="is_active">
<label class="form-check-label" for="editIsActive">
Active User
</label>
</div>
</div>
<div class="mb-3">
<label for="editPassword" class="form-label">New Password (leave blank to keep current)</label>
<input type="password" class="form-control" id="editPassword" name="password">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Update User</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete User Modal -->
<div class="modal fade" id="deleteUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete user <strong id="deleteUsername"></strong>?</p>
<p class="text-danger small">This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('admin.delete_user') }}" style="display:inline;" onsubmit="setTimeout(function(){ window.location.reload(); }, 1000);">
<input type="hidden" id="deleteUserId" name="user_id">
<button type="submit" class="btn btn-danger">Delete User</button>
</form>
</div>
</div>
</div>
</div>
<!-- Quick Schedule Task Modal -->
<div class="modal fade" id="quickScheduleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="quickScheduleTitle">Schedule Maintenance Task</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ url_for('admin.create_scheduled_task') }}" id="quickScheduleForm">
<input type="hidden" id="quickTaskType" name="task_type">
<input type="hidden" name="enabled" value="true">
<div class="modal-body">
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> <span id="quickScheduleDescription"></span>
</div>
<div class="mb-3">
<label for="quickTaskName" class="form-label">Task Name</label>
<input type="text" class="form-control" id="quickTaskName" name="name" required>
</div>
<div class="row">
<div class="col-md-6">
<label for="quickTime" class="form-label">Time</label>
<input type="time" class="form-control" id="quickTime" name="time" value="01:00" required>
</div>
<div class="col-md-6">
<label for="quickFrequency" class="form-label">Frequency</label>
<select class="form-select" id="quickFrequency" name="frequency" required>
<option value="daily">Daily</option>
<option value="weekly">Weekly (Sunday)</option>
<option value="monthly">Monthly (1st day)</option>
</select>
</div>
</div>
<div class="mt-3">
<small class="text-muted">
<strong>Preview:</strong> <span id="schedulePreview">Daily at 01:00</span>
</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="bi bi-clock"></i> Schedule Task
</button>
</div>
</form>
</div>
</div>
</div>
<script>
let currentUserId = null;
function editUser(userId, username, role, isActive) {
currentUserId = userId;
document.getElementById('editUserId').value = userId;
document.getElementById('editUsername').value = username;
document.getElementById('editRole').value = role;
document.getElementById('editIsActive').checked = isActive;
document.getElementById('editPassword').value = '';
const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
modal.show();
}
function deleteUser(userId, username) {
document.getElementById('deleteUserId').value = userId;
document.getElementById('deleteUsername').textContent = username;
const modal = new bootstrap.Modal(document.getElementById('deleteUserModal'));
modal.show();
}
function clearLogs() {
if (confirm('Are you sure you want to clear all server logs? This action cannot be undone.')) {
fetch('/admin/clear_logs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMaintenanceStatus('success', 'Server logs cleared successfully');
} else {
showMaintenanceStatus('danger', 'Error clearing logs: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
showMaintenanceStatus('danger', 'Error clearing logs');
});
}
}
function cleanUnusedFiles() {
if (confirm('Are you sure you want to clean unused files? This will remove files from uploads folder that are not referenced in the database.')) {
showCleanupStatus('info', 'Scanning for unused files...');
showMaintenanceStatus('info', 'Cleaning unused files...');
fetch('/admin/clean_unused_files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const message = `Successfully cleaned ${data.deleted_count} unused files`;
showCleanupStatus('success', message);
showMaintenanceStatus('success', message);
} else {
const errorMsg = 'Error cleaning files: ' + data.message;
showCleanupStatus('danger', errorMsg);
showMaintenanceStatus('danger', errorMsg);
}
})
.catch(error => {
console.error('Error:', error);
const errorMsg = 'Error cleaning files';
showCleanupStatus('danger', errorMsg);
showMaintenanceStatus('danger', errorMsg);
});
}
}
function showCleanupStatus(type, message) {
const statusDiv = document.getElementById('cleanupStatus');
if (statusDiv) {
const alertClass = type === 'success' ? 'alert-success' :
type === 'danger' ? 'alert-danger' :
type === 'warning' ? 'alert-warning' : 'alert-info';
const icon = type === 'success' ? 'check-circle' :
type === 'danger' ? 'exclamation-triangle' :
type === 'warning' ? 'exclamation-triangle' : 'info-circle';
statusDiv.innerHTML = `
<div class="alert ${alertClass} alert-sm">
<i class="bi bi-${icon}"></i> ${message}
</div>
`;
// Auto-hide success messages after 5 seconds
if (type === 'success') {
setTimeout(() => {
statusDiv.innerHTML = '';
}, 5000);
}
}
}
function optimizeDatabase() {
if (confirm('Are you sure you want to optimize the database? This may take a few moments.')) {
showMaintenanceStatus('info', 'Optimizing database...');
fetch('/admin/optimize_database', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMaintenanceStatus('success', 'Database optimized successfully');
} else {
showMaintenanceStatus('danger', 'Error optimizing database: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
showMaintenanceStatus('danger', 'Error optimizing database');
});
}
}
function showMaintenanceStatus(type, message) {
const statusDiv = document.getElementById('maintenanceStatus');
const alertClass = type === 'success' ? 'alert-success' :
type === 'danger' ? 'alert-danger' :
type === 'warning' ? 'alert-warning' : 'alert-info';
const icon = type === 'success' ? 'check-circle' :
type === 'danger' ? 'exclamation-triangle' :
type === 'warning' ? 'exclamation-triangle' : 'info-circle';
statusDiv.innerHTML = `
<div class="alert ${alertClass}">
<i class="bi bi-${icon}"></i> ${message}
</div>
`;
// Auto-hide success messages after 5 seconds
if (type === 'success') {
setTimeout(() => {
statusDiv.innerHTML = `
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> Ready to perform maintenance tasks.
</div>
`;
}, 5000);
}
}
function loadScheduledTasks() {
fetch('/admin/scheduled_tasks')
.then(response => response.json())
.then(data => {
const tasksDiv = document.getElementById('scheduledTasks');
if (data.tasks && data.tasks.length > 0) {
let tasksHtml = '<div class="list-group">';
data.tasks.forEach(task => {
const statusBadge = task.enabled ?
'<span class="badge bg-success">Enabled</span>' :
'<span class="badge bg-secondary">Disabled</span>';
tasksHtml += `
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1">${task.name}</h6>
<p class="mb-1 text-muted">${task.task_type} - ${task.schedule}</p>
<small class="text-muted">Next run: ${task.next_run || 'Not scheduled'}</small>
</div>
<div>
${statusBadge}
<button class="btn btn-sm btn-outline-warning ms-2" onclick="toggleTask(${task.id})">
<i class="bi bi-${task.enabled ? 'pause' : 'play'}"></i>
</button>
<button class="btn btn-sm btn-outline-danger ms-1" onclick="deleteTask(${task.id})">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
`;
});
tasksHtml += '</div>';
tasksDiv.innerHTML = tasksHtml;
} else {
tasksDiv.innerHTML = `
<div class="text-center py-4">
<i class="bi bi-clock text-muted" style="font-size: 3rem;"></i>
<h5 class="text-muted mt-2">No Scheduled Tasks</h5>
<p class="text-muted">Create your first scheduled task to automate maintenance.</p>
</div>
`;
}
})
.catch(error => {
console.error('Error loading tasks:', error);
document.getElementById('scheduledTasks').innerHTML = `
<div class="alert alert-danger">
<i class="bi bi-exclamation-triangle"></i> Error loading scheduled tasks.
</div>
`;
});
}
function toggleTask(taskId) {
fetch(`/admin/toggle_task/${taskId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadScheduledTasks(); // Reload tasks
} else {
alert('Error toggling task: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error toggling task');
});
}
function deleteTask(taskId) {
if (confirm('Are you sure you want to delete this scheduled task?')) {
fetch(`/admin/delete_task/${taskId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadScheduledTasks(); // Reload tasks
} else {
alert('Error deleting task: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error deleting task');
});
}
}
// Consolidated DOMContentLoaded event
document.addEventListener('DOMContentLoaded', function() {
console.log('Admin page loaded - initializing...');
// Check for successful user operations and handle refresh
checkForUserOperationSuccess();
// Load scheduled tasks immediately on page load
loadScheduledTasks();
// Load scheduled tasks when scheduler tab is shown
const schedulerTab = document.getElementById('scheduler-tab');
if (schedulerTab) {
schedulerTab.addEventListener('shown.bs.tab', function() {
console.log('Loading scheduled tasks...');
loadScheduledTasks();
});
}
// Add event listeners for the schedule preview
const timeInput = document.getElementById('quickTime');
const frequencySelect = document.getElementById('quickFrequency');
if (timeInput && frequencySelect) {
timeInput.addEventListener('change', updateSchedulePreview);
frequencySelect.addEventListener('change', updateSchedulePreview);
}
// Debug: Check if tab content is visible
const maintenanceContent = document.getElementById('maintenance');
const schedulerContent = document.getElementById('scheduler');
console.log('Maintenance tab content found:', !!maintenanceContent);
console.log('Scheduler tab content found:', !!schedulerContent);
});
const frequency = this.value;
const timeInput = document.getElementById('quickTime');
const previewText = document.getElementById('schedulePreview');
let defaultTime = '01:00';
let description = '';
switch (frequency) {
case 'daily':
description = 'Runs daily at the specified time.';
break;
case 'weekly':
description = 'Runs weekly on Sunday at the specified time.';
defaultTime = '01:00';
break;
case 'monthly':
description = 'Runs monthly on the 1st day at the specified time.';
defaultTime = '01:00';
break;
}
timeInput.value = defaultTime;
previewText.textContent = description;
});
// Open quick schedule modal
function openQuickScheduleModal(taskType) {
const title = taskType === 'cleanup_files' ? 'Quick Schedule: Daily File Cleanup' :
taskType === 'clear_logs' ? 'Quick Schedule: Weekly Log Clearance' :
'Quick Schedule: Monthly Database Optimization';
const description = taskType === 'cleanup_files' ? 'This task will clean unused files daily at 1:00 AM.' :
taskType === 'clear_logs' ? 'This task will clear server logs weekly on Sunday at 1:00 AM.' :
'This task will optimize the database monthly on the 1st day at 1:00 AM.';
document.getElementById('quickScheduleTitle').textContent = title;
document.getElementById('quickScheduleDescription').innerHTML = `<i class="bi bi-info-circle"></i> ${description}`;
document.getElementById('quickTaskType').value = taskType;
const modal = new bootstrap.Modal(document.getElementById('quickScheduleModal'));
modal.show();
}
function scheduleCleanupTask() {
showQuickScheduleModal('cleanup_files', 'Clean Unused Files', 'Automatically remove unused media files from uploads folder');
}
function scheduleLogClearTask() {
showQuickScheduleModal('clear_logs', 'Clear Server Logs', 'Automatically clear server log entries to free up database space');
}
function scheduleDatabaseTask() {
showQuickScheduleModal('optimize_db', 'Optimize Database', 'Automatically optimize database performance and clean up orphaned records');
}
function showQuickScheduleModal(taskType, taskName, description) {
document.getElementById('quickTaskType').value = taskType;
document.getElementById('quickTaskName').value = taskName;
document.getElementById('quickScheduleTitle').textContent = `Schedule ${taskName}`;
document.getElementById('quickScheduleDescription').textContent = description;
// Update preview
updateSchedulePreview();
const modal = new bootstrap.Modal(document.getElementById('quickScheduleModal'));
modal.show();
}
function updateSchedulePreview() {
const time = document.getElementById('quickTime').value;
const frequency = document.getElementById('quickFrequency').value;
const preview = document.getElementById('schedulePreview');
let frequencyText = frequency.charAt(0).toUpperCase() + frequency.slice(1);
if (frequency === 'weekly') {
frequencyText = 'Weekly (Sunday)';
} else if (frequency === 'monthly') {
frequencyText = 'Monthly (1st day)';
}
preview.textContent = `${frequencyText} at ${time}`;
}
// Check for flash messages indicating user operations and refresh if needed
function checkForUserOperationSuccess() {
// Check if there are any flash messages indicating successful user operations
const flashMessages = document.querySelectorAll('.alert');
let shouldRefresh = false;
flashMessages.forEach(function(alert) {
const text = alert.textContent.toLowerCase();
if (text.includes('created successfully') ||
text.includes('updated successfully') ||
text.includes('deleted successfully')) {
shouldRefresh = true;
}
});
// If we just completed a user operation, we might need to refresh to show updated data
if (shouldRefresh && window.performance && window.performance.navigation.type === 1) {
// This is a refresh after a form submission, ensure we show the latest data
setTimeout(function() {
if (document.querySelector('.nav-link.active').getAttribute('data-bs-target') === '#users') {
// We're on the users tab, reload to show updated user list
window.location.reload();
}
}, 2000); // Wait 2 seconds to let user read the success message
}
}
}
</script>
{% endblock %}