mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-11 21:33:00 +00:00
feat(plugins): add sorting to installed plugins section
Add A-Z, Z-A, and Enabled First sort options for installed plugins with localStorage persistence. Both installed and store sections now default to A-Z sorting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -897,6 +897,9 @@ window.currentPluginConfig = null;
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Installed plugins sort state
|
||||||
|
let installedSort = localStorage.getItem('installedSort') || 'a-z';
|
||||||
|
|
||||||
// Shared on-demand status store (mirrors Alpine store when available)
|
// Shared on-demand status store (mirrors Alpine store when available)
|
||||||
window.__onDemandStore = window.__onDemandStore || {
|
window.__onDemandStore = window.__onDemandStore || {
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -1010,7 +1013,7 @@ window.initPluginsPage = function() {
|
|||||||
// If we fetched data before the DOM existed, render it now
|
// If we fetched data before the DOM existed, render it now
|
||||||
if (window.__pendingInstalledPlugins) {
|
if (window.__pendingInstalledPlugins) {
|
||||||
console.log('[RENDER] Applying pending installed plugins data');
|
console.log('[RENDER] Applying pending installed plugins data');
|
||||||
renderInstalledPlugins(window.__pendingInstalledPlugins);
|
sortAndRenderInstalledPlugins(window.__pendingInstalledPlugins);
|
||||||
window.__pendingInstalledPlugins = null;
|
window.__pendingInstalledPlugins = null;
|
||||||
}
|
}
|
||||||
if (window.__pendingStorePlugins) {
|
if (window.__pendingStorePlugins) {
|
||||||
@@ -1266,7 +1269,7 @@ function loadInstalledPlugins(forceRefresh = false) {
|
|||||||
}));
|
}));
|
||||||
pluginLog('[CACHE] Dispatched pluginsUpdated event from cache');
|
pluginLog('[CACHE] Dispatched pluginsUpdated event from cache');
|
||||||
// Still render to ensure UI is updated
|
// Still render to ensure UI is updated
|
||||||
renderInstalledPlugins(pluginLoadCache.data);
|
sortAndRenderInstalledPlugins(pluginLoadCache.data);
|
||||||
return Promise.resolve(pluginLoadCache.data);
|
return Promise.resolve(pluginLoadCache.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1325,7 +1328,7 @@ function loadInstalledPlugins(forceRefresh = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInstalledPlugins(installedPlugins);
|
sortAndRenderInstalledPlugins(installedPlugins);
|
||||||
|
|
||||||
// Update count
|
// Update count
|
||||||
const countEl = document.getElementById('installed-count');
|
const countEl = document.getElementById('installed-count');
|
||||||
@@ -1366,6 +1369,24 @@ function refreshInstalledPlugins() {
|
|||||||
window.pluginManager.loadInstalledPlugins = loadInstalledPlugins;
|
window.pluginManager.loadInstalledPlugins = loadInstalledPlugins;
|
||||||
// Note: searchPluginStore will be exposed after its definition (see below)
|
// Note: searchPluginStore will be exposed after its definition (see below)
|
||||||
|
|
||||||
|
function sortAndRenderInstalledPlugins(plugins) {
|
||||||
|
const sorted = [...plugins].sort((a, b) => {
|
||||||
|
const nameA = (a.name || a.id || '').toLowerCase();
|
||||||
|
const nameB = (b.name || b.id || '').toLowerCase();
|
||||||
|
switch (installedSort) {
|
||||||
|
case 'z-a':
|
||||||
|
return nameB.localeCompare(nameA);
|
||||||
|
case 'enabled':
|
||||||
|
if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;
|
||||||
|
return nameA.localeCompare(nameB);
|
||||||
|
case 'a-z':
|
||||||
|
default:
|
||||||
|
return nameA.localeCompare(nameB);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderInstalledPlugins(sorted);
|
||||||
|
}
|
||||||
|
|
||||||
function renderInstalledPlugins(plugins) {
|
function renderInstalledPlugins(plugins) {
|
||||||
const container = document.getElementById('installed-plugins-grid');
|
const container = document.getElementById('installed-plugins-grid');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -5085,7 +5106,7 @@ function handleUninstallSuccess(pluginId) {
|
|||||||
if (typeof installedPlugins !== 'undefined') {
|
if (typeof installedPlugins !== 'undefined') {
|
||||||
installedPlugins = updatedPlugins;
|
installedPlugins = updatedPlugins;
|
||||||
}
|
}
|
||||||
renderInstalledPlugins(updatedPlugins);
|
sortAndRenderInstalledPlugins(updatedPlugins);
|
||||||
showNotification(`Plugin uninstalled successfully`, 'success');
|
showNotification(`Plugin uninstalled successfully`, 'success');
|
||||||
|
|
||||||
// Also refresh from server to ensure consistency
|
// Also refresh from server to ensure consistency
|
||||||
@@ -5127,20 +5148,16 @@ function restartDisplay() {
|
|||||||
// --- Store Filter/Sort Functions ---
|
// --- Store Filter/Sort Functions ---
|
||||||
|
|
||||||
function setupStoreFilterListeners() {
|
function setupStoreFilterListeners() {
|
||||||
console.log('[FILTER SETUP] setupStoreFilterListeners() called');
|
|
||||||
// Sort dropdown
|
// Sort dropdown
|
||||||
const sortSelect = document.getElementById('store-sort');
|
const sortSelect = document.getElementById('store-sort');
|
||||||
console.log('[FILTER SETUP] store-sort element:', !!sortSelect, 'listenerSetup:', sortSelect?._listenerSetup);
|
|
||||||
if (sortSelect && !sortSelect._listenerSetup) {
|
if (sortSelect && !sortSelect._listenerSetup) {
|
||||||
sortSelect._listenerSetup = true;
|
sortSelect._listenerSetup = true;
|
||||||
sortSelect.value = storeFilterState.sort;
|
sortSelect.value = storeFilterState.sort;
|
||||||
sortSelect.addEventListener('change', () => {
|
sortSelect.addEventListener('change', () => {
|
||||||
console.log('[FILTER] Sort changed to:', sortSelect.value, 'cache:', !!pluginStoreCache);
|
|
||||||
storeFilterState.sort = sortSelect.value;
|
storeFilterState.sort = sortSelect.value;
|
||||||
storeFilterState.persist();
|
storeFilterState.persist();
|
||||||
applyStoreFiltersAndSort();
|
applyStoreFiltersAndSort();
|
||||||
});
|
});
|
||||||
console.log('[FILTER SETUP] Sort listener attached');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verified filter toggle
|
// Verified filter toggle
|
||||||
@@ -5197,11 +5214,9 @@ function setupStoreFilterListeners() {
|
|||||||
// Tag pills (event delegation on container)
|
// Tag pills (event delegation on container)
|
||||||
// Category pills (event delegation on container)
|
// Category pills (event delegation on container)
|
||||||
const catsPills = document.getElementById('filter-categories-pills');
|
const catsPills = document.getElementById('filter-categories-pills');
|
||||||
console.log('[FILTER SETUP] filter-categories-pills element:', catsPills, 'listenerSetup:', catsPills?._listenerSetup);
|
|
||||||
if (catsPills && !catsPills._listenerSetup) {
|
if (catsPills && !catsPills._listenerSetup) {
|
||||||
catsPills._listenerSetup = true;
|
catsPills._listenerSetup = true;
|
||||||
catsPills.addEventListener('click', (e) => {
|
catsPills.addEventListener('click', (e) => {
|
||||||
console.log('[FILTER] Category pill click, target:', e.target, 'closest:', e.target.closest('.category-filter-pill'));
|
|
||||||
const pill = e.target.closest('.category-filter-pill');
|
const pill = e.target.closest('.category-filter-pill');
|
||||||
if (!pill) return;
|
if (!pill) return;
|
||||||
const cat = pill.dataset.category;
|
const cat = pill.dataset.category;
|
||||||
@@ -5213,10 +5228,8 @@ function setupStoreFilterListeners() {
|
|||||||
storeFilterState.filterCategories.push(cat);
|
storeFilterState.filterCategories.push(cat);
|
||||||
pill.dataset.active = 'true';
|
pill.dataset.active = 'true';
|
||||||
}
|
}
|
||||||
console.log('[FILTER] Categories now:', storeFilterState.filterCategories, 'cache size:', pluginStoreCache?.length);
|
|
||||||
applyStoreFiltersAndSort();
|
applyStoreFiltersAndSort();
|
||||||
});
|
});
|
||||||
console.log('[FILTER SETUP] Category pill listener attached');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear filters button
|
// Clear filters button
|
||||||
@@ -5248,11 +5261,25 @@ function setupStoreFilterListeners() {
|
|||||||
applyStoreFiltersAndSort();
|
applyStoreFiltersAndSort();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Installed plugins sort dropdown
|
||||||
|
const installedSortSelect = document.getElementById('installed-sort');
|
||||||
|
if (installedSortSelect && !installedSortSelect._listenerSetup) {
|
||||||
|
installedSortSelect._listenerSetup = true;
|
||||||
|
installedSortSelect.value = installedSort;
|
||||||
|
installedSortSelect.addEventListener('change', () => {
|
||||||
|
installedSort = installedSortSelect.value;
|
||||||
|
localStorage.setItem('installedSort', installedSort);
|
||||||
|
const plugins = window.installedPlugins || [];
|
||||||
|
if (plugins.length > 0) {
|
||||||
|
sortAndRenderInstalledPlugins(plugins);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyStoreFiltersAndSort() {
|
function applyStoreFiltersAndSort() {
|
||||||
console.log('[FILTER] applyStoreFiltersAndSort called, cache:', pluginStoreCache?.length, 'state:', JSON.stringify(storeFilterState));
|
if (!pluginStoreCache) return;
|
||||||
if (!pluginStoreCache) { console.log('[FILTER] No cache, returning'); return; }
|
|
||||||
|
|
||||||
let plugins = [...pluginStoreCache];
|
let plugins = [...pluginStoreCache];
|
||||||
const installedIds = new Set(
|
const installedIds = new Set(
|
||||||
|
|||||||
@@ -1375,7 +1375,7 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
|
||||||
<!-- Custom v3 styles -->
|
<!-- Custom v3 styles -->
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='v3/app.css') }}?v=20260216a">
|
<link rel="stylesheet" href="{{ url_for('static', filename='v3/app.css') }}?v=20260216b">
|
||||||
</head>
|
</head>
|
||||||
<body x-data="app()" class="bg-gray-50 min-h-screen">
|
<body x-data="app()" class="bg-gray-50 min-h-screen">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
@@ -5013,7 +5013,7 @@
|
|||||||
<script src="{{ url_for('static', filename='v3/js/widgets/plugin-loader.js') }}" defer></script>
|
<script src="{{ url_for('static', filename='v3/js/widgets/plugin-loader.js') }}" defer></script>
|
||||||
|
|
||||||
<!-- Legacy plugins_manager.js (for backward compatibility during migration) -->
|
<!-- Legacy plugins_manager.js (for backward compatibility during migration) -->
|
||||||
<script src="{{ url_for('static', filename='v3/plugins_manager.js') }}?v=20260216a" defer></script>
|
<script src="{{ url_for('static', filename='v3/plugins_manager.js') }}?v=20260216b" defer></script>
|
||||||
|
|
||||||
<!-- Custom feeds table helper functions -->
|
<!-- Custom feeds table helper functions -->
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -28,6 +28,16 @@
|
|||||||
<h3 class="text-lg font-bold text-gray-900">Installed Plugins</h3>
|
<h3 class="text-lg font-bold text-gray-900">Installed Plugins</h3>
|
||||||
<span id="installed-count" class="text-sm text-gray-500 font-medium">0 installed</span>
|
<span id="installed-count" class="text-sm text-gray-500 font-medium">0 installed</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label for="installed-sort" class="text-sm font-medium text-gray-700 whitespace-nowrap">
|
||||||
|
<i class="fas fa-sort mr-1"></i>Sort:
|
||||||
|
</label>
|
||||||
|
<select id="installed-sort" class="text-sm px-3 py-1.5 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500">
|
||||||
|
<option value="a-z">A → Z</option>
|
||||||
|
<option value="z-a">Z → A</option>
|
||||||
|
<option value="enabled">Enabled First</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="installed-plugins-content" class="block">
|
<div id="installed-plugins-content" class="block">
|
||||||
<div id="installed-plugins-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-6">
|
<div id="installed-plugins-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user