feat: Phase 4 — dynamic nav + service visibility based on installed services
Unit Tests / test (push) Successful in 11m24s
Unit Tests / test (push) Successful in 11m24s
Email, calendar, and files no longer appear in the nav or as usable pages unless they are installed. The nav refreshes whenever a service is installed or removed via the new pic-services-changed CustomEvent. Changes: - routes/services.py: add GET /api/services/active endpoint - api.js: add servicesAPI.listActive() - App.jsx: replace hardcoded coreServiceChildren with dynamic state fetched from /api/services/active; SERVICE_META maps ids to nav entry shapes - ServiceNotInstalledBanner.jsx: new component — admin gets catalog link, peer gets "contact admin" message - EmailPage/CalendarPage/FilesPage: show banner when service not installed - ServicesIndex.jsx: remove CoreServiceCard + CORE_SERVICES "Built-in" section; rename Remove → Uninstall; dispatch pic-services-changed on install/uninstall success - MyServices.jsx: conditionally render service cards based on active list; placeholder card when absent; page-level notice when nothing is installed - tests/test_services_active_endpoint.py: 4 new endpoint tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,26 @@ def get_services_catalog():
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/api/services/active', methods=['GET'])
|
||||
def get_active_services():
|
||||
"""Return minimal info for all installed services. Used by webui to build nav."""
|
||||
try:
|
||||
from app import service_registry
|
||||
active = service_registry.list_active()
|
||||
return jsonify([
|
||||
{
|
||||
'id': svc['id'],
|
||||
'name': svc.get('name', svc['id']),
|
||||
'subdomain': svc.get('subdomain'),
|
||||
'capabilities': svc.get('capabilities', {}),
|
||||
}
|
||||
for svc in active
|
||||
])
|
||||
except Exception as e:
|
||||
logger.error('get_active_services: %s', e)
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/api/services/catalog/<service_id>', methods=['GET'])
|
||||
def get_service_catalog_entry(service_id: str):
|
||||
"""Return a single service manifest+config, or 404 if unknown."""
|
||||
|
||||
Reference in New Issue
Block a user