fix: dashboard only shows email/calendar/files if installed
Unit Tests / test (push) Successful in 11m25s

Fetches /api/services/active on load; service status cards and quick-
access links for email, calendar, files, and webmail are suppressed
until the service is installed via the Store. Core services (WireGuard,
Routing, Network) always show. Fixes #setup_complete gate on dev stack.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:38:16 -04:00
parent 03a67ad922
commit 0ed8669aec
+20 -15
View File
@@ -24,14 +24,16 @@ function Dashboard({ isOnline }) {
const { domain = 'cell', cell_name = 'mycell', effective_domain, domain_mode = 'lan' } = useConfig();
const svcDomain = (domain_mode !== 'lan' && effective_domain) ? effective_domain : domain;
const proto = domain_mode === 'lan' ? 'http' : 'https';
const SERVICES = [
{ name: 'Cell Home', url: domain_mode === 'lan' ? `http://${cell_name}.${domain}` : `https://${svcDomain}`, desc: 'Main UI — no login needed' },
{ name: 'Calendar', url: `${proto}://calendar.${svcDomain}`, desc: 'Use your configured account credentials' },
{ name: 'Files', url: `${proto}://files.${svcDomain}`, desc: 'Use your configured account credentials' },
{ name: 'Webmail', url: `${proto}://mail.${svcDomain}`, desc: 'Use your configured account credentials' },
const ALL_SERVICE_LINKS = [
{ id: null, name: 'Cell Home', url: domain_mode === 'lan' ? `http://${cell_name}.${domain}` : `https://${svcDomain}`, desc: 'Main UI — no login needed' },
{ id: 'calendar', name: 'Calendar', url: `${proto}://calendar.${svcDomain}`, desc: 'Use your configured account credentials' },
{ id: 'files', name: 'Files', url: `${proto}://files.${svcDomain}`, desc: 'Use your configured account credentials' },
{ id: 'email', name: 'Webmail', url: `${proto}://mail.${svcDomain}`, desc: 'Use your configured account credentials' },
];
const SERVICES = ALL_SERVICE_LINKS.filter(s => s.id === null || activeServiceIds.has(s.id));
const [cellStatus, setCellStatus] = useState(null);
const [servicesStatus, setServicesStatus] = useState(null);
const [activeServiceIds, setActiveServiceIds] = useState(new Set());
const [isLoading, setIsLoading] = useState(true);
const [serviceControls, setServiceControls] = useState({});
@@ -177,22 +179,25 @@ function Dashboard({ isOnline }) {
}
try {
const [statusResponse, servicesResponse] = await Promise.all([
const [statusResponse, servicesResponse, activeResponse] = await Promise.all([
cellAPI.getStatus(),
servicesAPI.getAllStatus()
servicesAPI.getAllStatus(),
servicesAPI.listActive().catch(() => ({ data: [] })),
]);
setCellStatus(statusResponse.data);
// Transform services data to match expected structure
const installed = new Set((activeResponse.data || []).map(s => s.id));
setActiveServiceIds(installed);
const servicesData = servicesResponse.data;
const transformedServices = {
wireguard: servicesData.wireguard || { running: false, status: 'offline' },
email: servicesData.email || { running: false, status: 'offline' },
calendar: servicesData.calendar || { running: false, status: 'offline' },
files: servicesData.files || { running: false, status: 'offline' },
routing: servicesData.routing || { running: false, status: 'offline' },
network: servicesData.network || { running: false, status: 'offline' }
network: servicesData.network || { running: false, status: 'offline' },
...(installed.has('email') && { email: servicesData.email || { running: false, status: 'offline' } }),
...(installed.has('calendar') && { calendar: servicesData.calendar || { running: false, status: 'offline' } }),
...(installed.has('files') && { files: servicesData.files || { running: false, status: 'offline' } }),
};
setServicesStatus(transformedServices);
@@ -296,11 +301,11 @@ function Dashboard({ isOnline }) {
<h2 className="text-lg font-semibold text-gray-900 mb-4">Services Status</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{renderServiceCard('wireguard', <Shield className="h-6 w-6 text-primary-500" />, 'WireGuard', servicesStatus.wireguard)}
{renderServiceCard('email', <Mail className="h-6 w-6 text-primary-500" />, 'Email', servicesStatus.email)}
{renderServiceCard('calendar', <Calendar className="h-6 w-6 text-primary-500" />, 'Calendar', servicesStatus.calendar)}
{renderServiceCard('files', <FolderOpen className="h-6 w-6 text-primary-500" />, 'Files', servicesStatus.files)}
{renderServiceCard('routing', <Wifi className="h-6 w-6 text-primary-500" />, 'Routing', servicesStatus.routing)}
{renderServiceCard('network', <Server className="h-6 w-6 text-primary-500" />, 'Network', servicesStatus.network)}
{servicesStatus.email && renderServiceCard('email', <Mail className="h-6 w-6 text-primary-500" />, 'Email', servicesStatus.email)}
{servicesStatus.calendar && renderServiceCard('calendar', <Calendar className="h-6 w-6 text-primary-500" />, 'Calendar', servicesStatus.calendar)}
{servicesStatus.files && renderServiceCard('files', <FolderOpen className="h-6 w-6 text-primary-500" />, 'Files', servicesStatus.files)}
</div>
</div>
)}