Skip to content

Commit 775e4ae

Browse files
author
Admin9705
committed
update
1 parent 096d314 commit 775e4ae

File tree

5 files changed

+794
-44
lines changed

5 files changed

+794
-44
lines changed

frontend/static/js/apps.js

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,5 +564,294 @@ const appsModule = {
564564
}
565565
};
566566

567+
// Apps Dashboard functionality
568+
const appsDashboard = {
569+
statusCheckInterval: null,
570+
activityRefreshInterval: null,
571+
572+
// Initialize the dashboard
573+
init: function() {
574+
console.log('[Apps Dashboard] Initializing...');
575+
this.checkAllAppStatus();
576+
this.loadRecentActivity();
577+
this.loadConfigurationSummary();
578+
this.startAutoRefresh();
579+
},
580+
581+
// Check status of all apps
582+
checkAllAppStatus: function() {
583+
const apps = ['sonarr', 'radarr', 'lidarr', 'readarr', 'whisparr', 'eros', 'prowlarr'];
584+
585+
apps.forEach(app => {
586+
this.checkAppStatus(app);
587+
});
588+
},
589+
590+
// Check status of a specific app
591+
checkAppStatus: function(app) {
592+
const statusElement = document.getElementById(`${app}Status`);
593+
const instancesElement = document.getElementById(`${app}Instances`);
594+
595+
if (!statusElement) return;
596+
597+
// Set checking status
598+
statusElement.textContent = 'Checking...';
599+
statusElement.className = 'status-badge checking';
600+
601+
// Fetch app settings to check configuration and instances
602+
HuntarrUtils.fetchWithTimeout(`./api/settings/${app}`)
603+
.then(response => {
604+
if (!response.ok) {
605+
throw new Error(`HTTP ${response.status}`);
606+
}
607+
return response.json();
608+
})
609+
.then(settings => {
610+
let instanceCount = 0;
611+
let onlineCount = 0;
612+
613+
if (settings.instances && Array.isArray(settings.instances)) {
614+
instanceCount = settings.instances.length;
615+
616+
// Check each instance status
617+
const statusPromises = settings.instances.map(instance => {
618+
return this.checkInstanceConnection(app, instance)
619+
.then(isOnline => {
620+
if (isOnline) onlineCount++;
621+
return isOnline;
622+
})
623+
.catch(() => false);
624+
});
625+
626+
Promise.all(statusPromises).then(() => {
627+
this.updateAppStatus(app, instanceCount, onlineCount);
628+
});
629+
} else {
630+
this.updateAppStatus(app, 0, 0);
631+
}
632+
})
633+
.catch(error => {
634+
console.error(`Error checking ${app} status:`, error);
635+
statusElement.textContent = 'Error';
636+
statusElement.className = 'status-badge offline';
637+
if (instancesElement) {
638+
instancesElement.textContent = 'Not configured';
639+
}
640+
});
641+
},
642+
643+
// Check if a specific instance is online
644+
checkInstanceConnection: function(app, instance) {
645+
if (!instance.url || !instance.api_key) {
646+
return Promise.resolve(false);
647+
}
648+
649+
const testUrl = `${instance.url}/api/v3/system/status`;
650+
651+
return fetch(testUrl, {
652+
method: 'GET',
653+
headers: {
654+
'X-Api-Key': instance.api_key,
655+
'Content-Type': 'application/json'
656+
},
657+
timeout: 5000
658+
})
659+
.then(response => response.ok)
660+
.catch(() => false);
661+
},
662+
663+
// Update app status display
664+
updateAppStatus: function(app, totalInstances, onlineInstances) {
665+
const statusElement = document.getElementById(`${app}Status`);
666+
const instancesElement = document.getElementById(`${app}Instances`);
667+
668+
if (!statusElement) return;
669+
670+
if (totalInstances === 0) {
671+
statusElement.textContent = 'Not configured';
672+
statusElement.className = 'status-badge offline';
673+
if (instancesElement) {
674+
instancesElement.textContent = 'No instances';
675+
}
676+
} else if (onlineInstances === totalInstances) {
677+
statusElement.textContent = 'Online';
678+
statusElement.className = 'status-badge online';
679+
if (instancesElement) {
680+
instancesElement.textContent = `${totalInstances} instance${totalInstances > 1 ? 's' : ''}`;
681+
}
682+
} else if (onlineInstances > 0) {
683+
statusElement.textContent = 'Partial';
684+
statusElement.className = 'status-badge checking';
685+
if (instancesElement) {
686+
instancesElement.textContent = `${onlineInstances}/${totalInstances} online`;
687+
}
688+
} else {
689+
statusElement.textContent = 'Offline';
690+
statusElement.className = 'status-badge offline';
691+
if (instancesElement) {
692+
instancesElement.textContent = `${totalInstances} instance${totalInstances > 1 ? 's' : ''} offline`;
693+
}
694+
}
695+
},
696+
697+
// Load recent activity
698+
loadRecentActivity: function() {
699+
const activityList = document.getElementById('recentActivity');
700+
if (!activityList) return;
701+
702+
// Simulate loading recent activity (replace with actual API call)
703+
setTimeout(() => {
704+
const activities = [
705+
{ icon: 'fas fa-download', text: 'Sonarr: Downloaded "The Office S09E23"', time: '2 minutes ago', type: 'success' },
706+
{ icon: 'fas fa-search', text: 'Radarr: Searching for "Inception (2010)"', time: '5 minutes ago', type: 'info' },
707+
{ icon: 'fas fa-music', text: 'Lidarr: Added "Pink Floyd - Dark Side of the Moon"', time: '12 minutes ago', type: 'success' },
708+
{ icon: 'fas fa-exclamation-triangle', text: 'Prowlarr: Indexer "TorrentLeech" failed health check', time: '18 minutes ago', type: 'warning' },
709+
{ icon: 'fas fa-book', text: 'Readarr: Imported "Dune by Frank Herbert"', time: '25 minutes ago', type: 'success' }
710+
];
711+
712+
activityList.innerHTML = activities.map(activity => `
713+
<div class="activity-item ${activity.type}">
714+
<i class="${activity.icon} activity-icon"></i>
715+
<div class="activity-content">
716+
<span class="activity-text">${activity.text}</span>
717+
<span class="activity-time">${activity.time}</span>
718+
</div>
719+
</div>
720+
`).join('');
721+
}, 1000);
722+
},
723+
724+
// Load configuration summary
725+
loadConfigurationSummary: function() {
726+
const configuredAppsElement = document.getElementById('configuredAppsCount');
727+
const totalInstancesElement = document.getElementById('totalInstancesCount');
728+
const activeConnectionsElement = document.getElementById('activeConnectionsCount');
729+
730+
if (!configuredAppsElement) return;
731+
732+
const apps = ['sonarr', 'radarr', 'lidarr', 'readarr', 'whisparr', 'eros', 'prowlarr'];
733+
let configuredApps = 0;
734+
let totalInstances = 0;
735+
let activeConnections = 0;
736+
737+
const promises = apps.map(app => {
738+
return HuntarrUtils.fetchWithTimeout(`./api/settings/${app}`)
739+
.then(response => response.json())
740+
.then(settings => {
741+
if (settings.instances && settings.instances.length > 0) {
742+
configuredApps++;
743+
totalInstances += settings.instances.length;
744+
745+
// Count active connections (simplified)
746+
settings.instances.forEach(instance => {
747+
if (instance.url && instance.api_key) {
748+
activeConnections++;
749+
}
750+
});
751+
}
752+
})
753+
.catch(() => {});
754+
});
755+
756+
Promise.all(promises).then(() => {
757+
if (configuredAppsElement) configuredAppsElement.textContent = configuredApps;
758+
if (totalInstancesElement) totalInstancesElement.textContent = totalInstances;
759+
if (activeConnectionsElement) activeConnectionsElement.textContent = activeConnections;
760+
});
761+
},
762+
763+
// Start auto-refresh intervals
764+
startAutoRefresh: function() {
765+
// Refresh status every 30 seconds
766+
this.statusCheckInterval = setInterval(() => {
767+
this.checkAllAppStatus();
768+
}, 30000);
769+
770+
// Refresh activity every 60 seconds
771+
this.activityRefreshInterval = setInterval(() => {
772+
this.loadRecentActivity();
773+
}, 60000);
774+
},
775+
776+
// Stop auto-refresh intervals
777+
stopAutoRefresh: function() {
778+
if (this.statusCheckInterval) {
779+
clearInterval(this.statusCheckInterval);
780+
this.statusCheckInterval = null;
781+
}
782+
if (this.activityRefreshInterval) {
783+
clearInterval(this.activityRefreshInterval);
784+
this.activityRefreshInterval = null;
785+
}
786+
}
787+
};
788+
789+
// Global functions for dashboard actions
790+
function navigateToApp(app) {
791+
console.log(`Navigating to ${app} app`);
792+
window.location.hash = `#${app}`;
793+
}
794+
795+
function refreshAppStatus() {
796+
console.log('Refreshing all app status...');
797+
if (typeof appsDashboard !== 'undefined') {
798+
appsDashboard.checkAllAppStatus();
799+
appsDashboard.loadConfigurationSummary();
800+
}
801+
}
802+
803+
function refreshAllApps() {
804+
console.log('Refreshing all apps...');
805+
refreshAppStatus();
806+
if (typeof appsDashboard !== 'undefined') {
807+
appsDashboard.loadRecentActivity();
808+
}
809+
}
810+
811+
function globalSearch() {
812+
console.log('Opening global search...');
813+
// Implement global search functionality
814+
alert('Global search functionality coming soon!');
815+
}
816+
817+
function systemMaintenance() {
818+
console.log('Opening system maintenance...');
819+
// Implement system maintenance functionality
820+
alert('System maintenance functionality coming soon!');
821+
}
822+
823+
// Initialize dashboard when Apps section is shown
824+
document.addEventListener('DOMContentLoaded', function() {
825+
// Check if we're on the apps section and initialize dashboard
826+
const observer = new MutationObserver(function(mutations) {
827+
mutations.forEach(function(mutation) {
828+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
829+
const appsSection = document.getElementById('appsSection');
830+
if (appsSection && appsSection.classList.contains('active')) {
831+
if (typeof appsDashboard !== 'undefined') {
832+
appsDashboard.init();
833+
}
834+
} else {
835+
if (typeof appsDashboard !== 'undefined') {
836+
appsDashboard.stopAutoRefresh();
837+
}
838+
}
839+
}
840+
});
841+
});
842+
843+
const appsSection = document.getElementById('appsSection');
844+
if (appsSection) {
845+
observer.observe(appsSection, { attributes: true });
846+
847+
// Initialize immediately if already active
848+
if (appsSection.classList.contains('active')) {
849+
if (typeof appsDashboard !== 'undefined') {
850+
appsDashboard.init();
851+
}
852+
}
853+
}
854+
});
855+
567856
// Apps module is now initialized per-app when navigating to individual app sections
568857
// No automatic initialization needed

frontend/static/js/new-main.js

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -634,20 +634,11 @@ let huntarrUI = {
634634
// Show history view
635635
this.showRequestarrView('history');
636636
} else if (section === 'apps') {
637-
console.log('[huntarrUI] Switching to apps section');
638-
document.getElementById('appsSection').classList.add('active');
639-
document.getElementById('appsSection').style.display = 'block';
640-
if (document.getElementById('appsNav')) document.getElementById('appsNav').classList.add('active');
641-
newTitle = 'Apps';
642-
this.currentSection = 'apps';
643-
644-
// Switch to Apps sidebar
645-
console.log('[huntarrUI] About to call showAppsSidebar()');
646-
this.showAppsSidebar();
647-
console.log('[huntarrUI] Called showAppsSidebar()');
648-
649-
// Set localStorage to maintain Apps sidebar preference
650-
localStorage.setItem('huntarr-apps-sidebar', 'true');
637+
console.log('[huntarrUI] Apps section requested - redirecting to Sonarr by default');
638+
// Instead of showing apps dashboard, redirect to Sonarr
639+
this.switchSection('sonarr');
640+
window.location.hash = '#sonarr';
641+
return;
651642
} else if (section === 'sonarr' && document.getElementById('sonarrSection')) {
652643
document.getElementById('sonarrSection').classList.add('active');
653644
document.getElementById('sonarrSection').style.display = 'block';

0 commit comments

Comments
 (0)