fix: dashboard only shows email/calendar/files if installed
Unit Tests / test (push) Successful in 11m25s
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:
@@ -24,14 +24,16 @@ function Dashboard({ isOnline }) {
|
|||||||
const { domain = 'cell', cell_name = 'mycell', effective_domain, domain_mode = 'lan' } = useConfig();
|
const { domain = 'cell', cell_name = 'mycell', effective_domain, domain_mode = 'lan' } = useConfig();
|
||||||
const svcDomain = (domain_mode !== 'lan' && effective_domain) ? effective_domain : domain;
|
const svcDomain = (domain_mode !== 'lan' && effective_domain) ? effective_domain : domain;
|
||||||
const proto = domain_mode === 'lan' ? 'http' : 'https';
|
const proto = domain_mode === 'lan' ? 'http' : 'https';
|
||||||
const SERVICES = [
|
const ALL_SERVICE_LINKS = [
|
||||||
{ name: 'Cell Home', url: domain_mode === 'lan' ? `http://${cell_name}.${domain}` : `https://${svcDomain}`, desc: 'Main UI — no login needed' },
|
{ id: null, 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' },
|
{ id: 'calendar', name: 'Calendar', url: `${proto}://calendar.${svcDomain}`, desc: 'Use your configured account credentials' },
|
||||||
{ name: 'Files', url: `${proto}://files.${svcDomain}`, desc: 'Use your configured account credentials' },
|
{ id: 'files', name: 'Files', url: `${proto}://files.${svcDomain}`, desc: 'Use your configured account credentials' },
|
||||||
{ name: 'Webmail', url: `${proto}://mail.${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 [cellStatus, setCellStatus] = useState(null);
|
||||||
const [servicesStatus, setServicesStatus] = useState(null);
|
const [servicesStatus, setServicesStatus] = useState(null);
|
||||||
|
const [activeServiceIds, setActiveServiceIds] = useState(new Set());
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [serviceControls, setServiceControls] = useState({});
|
const [serviceControls, setServiceControls] = useState({});
|
||||||
|
|
||||||
@@ -177,24 +179,27 @@ function Dashboard({ isOnline }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [statusResponse, servicesResponse] = await Promise.all([
|
const [statusResponse, servicesResponse, activeResponse] = await Promise.all([
|
||||||
cellAPI.getStatus(),
|
cellAPI.getStatus(),
|
||||||
servicesAPI.getAllStatus()
|
servicesAPI.getAllStatus(),
|
||||||
|
servicesAPI.listActive().catch(() => ({ data: [] })),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setCellStatus(statusResponse.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 servicesData = servicesResponse.data;
|
||||||
const transformedServices = {
|
const transformedServices = {
|
||||||
wireguard: servicesData.wireguard || { running: false, status: 'offline' },
|
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' },
|
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);
|
setServicesStatus(transformedServices);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch dashboard data:', error);
|
console.error('Failed to fetch dashboard data:', error);
|
||||||
@@ -296,11 +301,11 @@ function Dashboard({ isOnline }) {
|
|||||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">Services Status</h2>
|
<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">
|
<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('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('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)}
|
{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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user