Scaffold global navigation aligned to routes plan
Build out the full web route map from music-selfhost-routes.md as scaffolding (no functionality on new screens): - Full route tree: /login, /albums/:id, /artists/:id, /playlists(+detail), /discover, /upload, metadata editor (single + batch), /storage/maintenance, /queue, nested /settings and /admin, and a 404. - Sidebar rebuilt to the A1 spec with permission-gated Discover/Upload. - ProtectedRoute gains requirePermission; Permission exported. - AppShell wraps Outlet in a Suspense boundary for lazy routes. - Reusable Placeholder + SubNav; Settings/Admin become nested layouts. - Settings/Profile: wired language + theme selectors. - Remove orphaned Home feature (web has no Home; / -> /library) and the now-unused house icon + nav.home keys. - i18n keys (en + ru) and CSS for page-title/sub-nav. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+109
-51
@@ -1,14 +1,38 @@
|
||||
import { lazy } from 'react';
|
||||
import { Routes, Route, Navigate } from 'react-router';
|
||||
import { AppShell } from '../components/layout/AppShell';
|
||||
import { ProtectedRoute } from './ProtectedRoute';
|
||||
|
||||
// Public (outside the shell)
|
||||
import { ConnectPage } from '../features/connect/ConnectPage';
|
||||
import { HomePage } from '../features/home/HomePage';
|
||||
import { LoginPage } from '../features/auth/LoginPage';
|
||||
|
||||
// Core screens (eager — first paint inside the shell)
|
||||
import { LibraryPage } from '../features/library/LibraryPage';
|
||||
import { AlbumDetailPage } from '../features/album-detail/AlbumDetailPage';
|
||||
import { ArtistDetailPage } from '../features/artist-detail/ArtistDetailPage';
|
||||
import { PlaylistsPage } from '../features/playlists/PlaylistsPage';
|
||||
import { PlaylistDetailPage } from '../features/playlist-detail/PlaylistDetailPage';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { LoadingSkeleton } from '../components/common/LoadingSkeleton';
|
||||
|
||||
// Settings / Admin layouts + panels (small, eager)
|
||||
import { SettingsPage } from '../features/settings/SettingsPage';
|
||||
import {
|
||||
ProfileSettings,
|
||||
PlaybackSettings,
|
||||
ScrobblingSettings,
|
||||
InstanceSettings,
|
||||
} from '../features/settings/panels';
|
||||
import { AdminPage } from '../features/admin/AdminPage';
|
||||
import {
|
||||
AdminUsers,
|
||||
AdminUserDetail,
|
||||
AdminSources,
|
||||
AdminInstance,
|
||||
} from '../features/admin/panels';
|
||||
|
||||
import { NotFoundPage } from '../features/not-found/NotFoundPage';
|
||||
|
||||
// Secondary screens — lazily loaded (Suspense boundary lives in AppShell)
|
||||
const SearchDownloadPage = lazy(() =>
|
||||
import('../features/search-download/SearchDownloadPage').then((m) => ({
|
||||
default: m.SearchDownloadPage,
|
||||
@@ -19,30 +43,36 @@ const DownloadsManagerPage = lazy(() =>
|
||||
default: m.DownloadsManagerPage,
|
||||
})),
|
||||
);
|
||||
const UploadPage = lazy(() =>
|
||||
import('../features/upload/UploadPage').then((m) => ({ default: m.UploadPage })),
|
||||
);
|
||||
const MetadataEditorPage = lazy(() =>
|
||||
import('../features/metadata-editor/MetadataEditorPage').then((m) => ({
|
||||
default: m.MetadataEditorPage,
|
||||
})),
|
||||
);
|
||||
const StoragePage = lazy(() =>
|
||||
import('../features/storage/StoragePage').then((m) => ({
|
||||
default: m.StoragePage,
|
||||
})),
|
||||
);
|
||||
const AdminPage = lazy(() =>
|
||||
import('../features/admin/AdminPage').then((m) => ({ default: m.AdminPage })),
|
||||
);
|
||||
const SettingsPage = lazy(() =>
|
||||
import('../features/settings/SettingsPage').then((m) => ({
|
||||
default: m.SettingsPage,
|
||||
const StorageMaintenancePage = lazy(() =>
|
||||
import('../features/storage/StorageMaintenancePage').then((m) => ({
|
||||
default: m.StorageMaintenancePage,
|
||||
})),
|
||||
);
|
||||
|
||||
const Fallback = () => (
|
||||
<div style={{ padding: '2rem' }}>
|
||||
<LoadingSkeleton />
|
||||
</div>
|
||||
const QueuePage = lazy(() =>
|
||||
import('../features/queue/QueuePage').then((m) => ({ default: m.QueuePage })),
|
||||
);
|
||||
|
||||
export function AppRoutes() {
|
||||
return (
|
||||
<Routes>
|
||||
{/* Public */}
|
||||
<Route path="/connect" element={<ConnectPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
|
||||
{/* Authenticated shell */}
|
||||
<Route
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
@@ -50,57 +80,85 @@ export function AppRoutes() {
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<HomePage />} />
|
||||
<Route index element={<Navigate to="/library" replace />} />
|
||||
|
||||
{/* Library */}
|
||||
<Route path="/library" element={<LibraryPage />} />
|
||||
<Route path="/library/albums/:albumId" element={<AlbumDetailPage />} />
|
||||
<Route path="/albums/:albumId" element={<AlbumDetailPage />} />
|
||||
<Route path="/artists/:artistId" element={<ArtistDetailPage />} />
|
||||
|
||||
{/* Playlists */}
|
||||
<Route path="/playlists" element={<PlaylistsPage />} />
|
||||
<Route path="/playlists/:playlistId" element={<PlaylistDetailPage />} />
|
||||
|
||||
{/* Discover & downloads (permission-gated) */}
|
||||
<Route
|
||||
path="/library/playlists/:playlistId"
|
||||
element={<PlaylistDetailPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/search"
|
||||
path="/discover"
|
||||
element={
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<ProtectedRoute requirePermission="download">
|
||||
<SearchDownloadPage />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/downloads"
|
||||
element={
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<ProtectedRoute requirePermission="download">
|
||||
<DownloadsManagerPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/storage"
|
||||
element={
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<StoragePage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<SettingsPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/admin/*"
|
||||
element={
|
||||
<ProtectedRoute requireAdmin>
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<AdminPage />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Upload & metadata */}
|
||||
<Route
|
||||
path="/upload"
|
||||
element={
|
||||
<ProtectedRoute requirePermission="upload">
|
||||
<UploadPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/tracks/:trackId/metadata"
|
||||
element={<MetadataEditorPage />}
|
||||
/>
|
||||
<Route path="/metadata/batch" element={<MetadataEditorPage batch />} />
|
||||
|
||||
{/* Storage */}
|
||||
<Route path="/storage" element={<StoragePage />} />
|
||||
<Route path="/storage/maintenance" element={<StorageMaintenancePage />} />
|
||||
|
||||
{/* Queue (narrow viewports) */}
|
||||
<Route path="/queue" element={<QueuePage />} />
|
||||
|
||||
{/* Settings */}
|
||||
<Route path="/settings" element={<SettingsPage />}>
|
||||
<Route index element={<Navigate to="/settings/profile" replace />} />
|
||||
<Route path="profile" element={<ProfileSettings />} />
|
||||
<Route path="playback" element={<PlaybackSettings />} />
|
||||
<Route path="scrobbling" element={<ScrobblingSettings />} />
|
||||
<Route path="instance" element={<InstanceSettings />} />
|
||||
</Route>
|
||||
|
||||
{/* Admin (admin-gated) */}
|
||||
<Route
|
||||
path="/admin"
|
||||
element={
|
||||
<ProtectedRoute requireAdmin>
|
||||
<AdminPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<Navigate to="/admin/users" replace />} />
|
||||
<Route path="users" element={<AdminUsers />} />
|
||||
<Route path="users/:userId" element={<AdminUserDetail />} />
|
||||
<Route path="sources" element={<AdminSources />} />
|
||||
<Route path="instance" element={<AdminInstance />} />
|
||||
</Route>
|
||||
|
||||
{/* 404 */}
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/library" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user