feat: Services UI — nested nav, per-service pages, settings migration
Rename Store → Services: ServicesIndex.jsx shows built-in core services (Email, Calendar, Files) with Manage links, plus the existing add-on store below. New service sub-pages at /services/email|calendar|files serve both admin and peer roles. Admins see connection info, service status, users list, and an inline config form (port/data-dir). Peers see connection info and their personal credentials fetched from peerAPI. Navigation restructured: a Services parent item expands to show the three sub-pages via a collapsible sidebar group (ChevronDown toggle). Both admin and peer navigation include the Services group. Sidebar extracted NavItem/NavList components to eliminate the duplicate mobile/ desktop rendering. Settings.jsx drops EmailForm, CalendarForm, FilesForm and their SERVICE_DEFS entries. Port conflict detection and per-service validation logic extracted to utils/serviceConfig.js, shared by Settings and the new service pages. Service form flushers are registered without cleanup so the Apply banner saves dirty config even when the user navigates away from a service page before clicking Apply. Legacy routes /email, /calendar, /files, /store redirect to their new canonical paths. GET /api/config now includes installed_services so the nav can derive which add-ons are installed without a separate store fetch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,7 @@ def get_config():
|
||||
'vip_webdav': _ips['vip_webdav'],
|
||||
}
|
||||
config['service_configs'] = service_configs
|
||||
config['installed_services'] = config_manager.get_installed_services()
|
||||
config['domain_mode'] = identity.get('domain_mode', 'lan')
|
||||
config['domain_name'] = identity.get('domain_name', '')
|
||||
config['effective_domain'] = config_manager.get_effective_domain()
|
||||
|
||||
Reference in New Issue
Block a user