mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-07-07 18:11:57 +02:00
Add NEW/Stale badges to Plugin Store and fix intermittent display issues
- Added NEW badge for plugins updated within last 3 months (90 days) - Added Stale badge for plugins not updated in last 2 years (730 days) - Removed Author, Status, and Active columns from Plugin Store view - Fixed intermittent old table display with cache-busting v7 - Added column count validation to ensure correct 8-column structure - Added tooltips for NEW and Stale badges with descriptive messages - Cleared plugin store cache to prevent stale data display - Updated cache-busting version to v7 to force browser refresh
This commit is contained in:
@@ -206,6 +206,32 @@
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
/* NEW and Stale badges */
|
||||
.plugin-status-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
cursor: help;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.plugin-status-badge.new {
|
||||
background: #ffc107;
|
||||
color: #000;
|
||||
box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.3);
|
||||
}
|
||||
|
||||
.plugin-status-badge.stale {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
|
||||
.paid-badge {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
@@ -1249,7 +1275,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Cache-busting version: 2026-01-25-v5 - Fixed is_paid boolean type enforcement and consistent rendering
|
||||
// Cache-busting version: 2026-01-25-v7 - Added NEW/Stale badges, removed Author/Status/Active columns, fixed intermittent display
|
||||
// Force browser to reload this script by changing version number
|
||||
let storePlugins = [];
|
||||
let currentFilter = 'all';
|
||||
@@ -1347,8 +1373,9 @@ function escapeHtml(text) {
|
||||
}
|
||||
|
||||
function displayStorePlugins() {
|
||||
// Version: 2026-01-25-v5 - Store view: Removed Status column, always show Free/Paid badges, fixed boolean handling
|
||||
// CRITICAL: This function MUST create exactly 7 columns (no Status, no Deactivate/Uninstall)
|
||||
// Version: 2026-01-25-v7 - Store view: 8 columns (Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
|
||||
// CRITICAL: NO Author, NO Status, NO Active columns - these are for Grid/Table views only
|
||||
// Store view shows: Icon | Plugin Name (with NEW/Stale badges) | Version | Pricing | Modify Date | Action (Installed/Install) | Help | About
|
||||
const tbody = document.getElementById('storeTableBody');
|
||||
if (!tbody) {
|
||||
console.error('storeTableBody not found!');
|
||||
@@ -1361,6 +1388,9 @@ function displayStorePlugins() {
|
||||
return;
|
||||
}
|
||||
|
||||
// CRITICAL: Clear any old cached table structure that might have Author/Status/Active columns
|
||||
// Force the correct 8-column structure
|
||||
|
||||
let filteredPlugins = storePlugins;
|
||||
|
||||
if (currentFilter !== 'all') {
|
||||
@@ -1434,11 +1464,22 @@ function displayStorePlugins() {
|
||||
? '<span class="plugin-pricing-badge paid">Paid</span>'
|
||||
: '<span class="plugin-pricing-badge free">Free</span>';
|
||||
|
||||
// Version: 2026-01-25-v5 - Added plugin icons to Store view (8 columns: Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
|
||||
// NEW and Stale badges
|
||||
let statusBadges = '';
|
||||
if (plugin.is_new === true) {
|
||||
statusBadges += '<span class="plugin-status-badge new" title="This plugin was released/updated within the last 3 months">NEW</span>';
|
||||
}
|
||||
if (plugin.is_stale === true) {
|
||||
statusBadges += '<span class="plugin-status-badge stale" title="This plugin is marked \'Stale\' (Last release over two years ago). This means it may work fine, but it has not had any recent development. Use your own discretion when using this plugin!">STALE</span>';
|
||||
}
|
||||
|
||||
// Version: 2026-01-25-v7 - Store view: 8 columns only (Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
|
||||
// NO Author, NO Status, NO Active columns - these are removed from Store view
|
||||
// Plugin Name includes NEW/Stale badges
|
||||
row.innerHTML = `
|
||||
<td style="text-align: center;">${iconHtml}</td>
|
||||
<td>
|
||||
<strong>${escapeHtml(plugin.name)}</strong>
|
||||
<strong>${escapeHtml(plugin.name)}</strong>${statusBadges}
|
||||
</td>
|
||||
<td><span class="plugin-version-number">${escapeHtml(plugin.version)}</span></td>
|
||||
<td>${pricingBadge}</td>
|
||||
@@ -1448,6 +1489,14 @@ function displayStorePlugins() {
|
||||
<td>${aboutHtml}</td>
|
||||
`;
|
||||
|
||||
// Ensure row has exactly 8 cells (no more, no less)
|
||||
if (row.cells.length !== 8) {
|
||||
console.warn(`Plugin ${plugin.name} row has ${row.cells.length} cells, expected 8. Rebuilding...`);
|
||||
const newRow = document.createElement('tr');
|
||||
newRow.innerHTML = row.innerHTML;
|
||||
row.parentNode.replaceChild(newRow, row);
|
||||
}
|
||||
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -697,6 +697,57 @@ def _enrich_store_plugins(plugins):
|
||||
# Ensure it's a proper boolean (not string or other type)
|
||||
plugin['is_paid'] = bool(plugin['is_paid']) if plugin['is_paid'] not in [True, False] else plugin['is_paid']
|
||||
|
||||
# Calculate NEW and Stale badges based on modify_date
|
||||
modify_date_str = plugin.get('modify_date', 'N/A')
|
||||
plugin['is_new'] = False
|
||||
plugin['is_stale'] = False
|
||||
|
||||
if modify_date_str and modify_date_str != 'N/A':
|
||||
try:
|
||||
# Parse the modify_date (could be various formats)
|
||||
modify_date = None
|
||||
if isinstance(modify_date_str, str):
|
||||
# Handle ISO format with timezone (from GitHub API)
|
||||
if 'T' in modify_date_str:
|
||||
# ISO format: 2026-01-25T04:24:52Z or 2026-01-25T04:24:52+00:00
|
||||
try:
|
||||
# Remove timezone info for simpler parsing
|
||||
date_part = modify_date_str.split('T')[0]
|
||||
time_part = modify_date_str.split('T')[1].split('+')[0].split('Z')[0]
|
||||
modify_date = datetime.strptime(f"{date_part} {time_part}", '%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
# Fallback: try standard format
|
||||
modify_date = datetime.strptime(modify_date_str[:19], '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
# Standard format: YYYY-MM-DD HH:MM:SS
|
||||
modify_date = datetime.strptime(modify_date_str, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
modify_date = modify_date_str
|
||||
|
||||
if modify_date:
|
||||
now = datetime.now()
|
||||
# Handle timezone-aware datetime
|
||||
if modify_date.tzinfo:
|
||||
from datetime import timezone
|
||||
modify_date = modify_date.replace(tzinfo=None)
|
||||
|
||||
# Calculate time difference
|
||||
time_diff = now - modify_date
|
||||
|
||||
# NEW: updated within last 3 months (90 days)
|
||||
if time_diff.days <= 90:
|
||||
plugin['is_new'] = True
|
||||
|
||||
# Stale: not updated in last 2 years (730 days)
|
||||
if time_diff.days > 730:
|
||||
plugin['is_stale'] = True
|
||||
|
||||
except Exception as e:
|
||||
logging.writeToFile(f"Error calculating NEW/Stale status for {plugin_dir}: {str(e)}")
|
||||
# Default to not new and not stale if parsing fails
|
||||
plugin['is_new'] = False
|
||||
plugin['is_stale'] = False
|
||||
|
||||
enriched.append(plugin)
|
||||
|
||||
return enriched
|
||||
@@ -768,6 +819,46 @@ def _fetch_plugins_from_github():
|
||||
logging.writeToFile(f"Could not fetch commit date for {plugin_name}: {str(e)}")
|
||||
modify_date = 'N/A'
|
||||
|
||||
# Calculate NEW and Stale badges based on modify_date
|
||||
is_new = False
|
||||
is_stale = False
|
||||
|
||||
if modify_date and modify_date != 'N/A':
|
||||
try:
|
||||
# Parse the modify_date
|
||||
modify_date_obj = None
|
||||
if isinstance(modify_date, str):
|
||||
if 'T' in modify_date:
|
||||
# ISO format: 2026-01-25T04:24:52Z or 2026-01-25T04:24:52+00:00
|
||||
try:
|
||||
date_part = modify_date.split('T')[0]
|
||||
time_part = modify_date.split('T')[1].split('+')[0].split('Z')[0]
|
||||
modify_date_obj = datetime.strptime(f"{date_part} {time_part}", '%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
modify_date_obj = datetime.strptime(modify_date[:19], '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
# Standard format: YYYY-MM-DD HH:MM:SS
|
||||
modify_date_obj = datetime.strptime(modify_date, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
modify_date_obj = modify_date
|
||||
|
||||
if modify_date_obj:
|
||||
now = datetime.now()
|
||||
if modify_date_obj.tzinfo:
|
||||
modify_date_obj = modify_date_obj.replace(tzinfo=None)
|
||||
|
||||
time_diff = now - modify_date_obj
|
||||
|
||||
# NEW: updated within last 3 months (90 days)
|
||||
if time_diff.days <= 90:
|
||||
is_new = True
|
||||
|
||||
# Stale: not updated in last 2 years (730 days)
|
||||
if time_diff.days > 730:
|
||||
is_stale = True
|
||||
except Exception as e:
|
||||
logging.writeToFile(f"Error calculating NEW/Stale for {plugin_name}: {str(e)}")
|
||||
|
||||
# Extract paid plugin information
|
||||
paid_elem = root.find('paid')
|
||||
patreon_tier_elem = root.find('patreon_tier')
|
||||
@@ -796,7 +887,9 @@ def _fetch_plugins_from_github():
|
||||
'modify_date': modify_date,
|
||||
'is_paid': is_paid,
|
||||
'patreon_tier': patreon_tier,
|
||||
'patreon_url': patreon_url
|
||||
'patreon_url': patreon_url,
|
||||
'is_new': is_new,
|
||||
'is_stale': is_stale
|
||||
}
|
||||
|
||||
plugins.append(plugin_data)
|
||||
|
||||
Reference in New Issue
Block a user