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:
@@ -1,13 +1,32 @@
|
||||
import { Outlet } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Window } from '@olly/modern-sk';
|
||||
import { SubNav, type SubNavItem } from '../../components/common/SubNav';
|
||||
|
||||
/**
|
||||
* `/admin` — A9 admin shell (admin-gated). Secondary nav + nested `<Outlet/>`
|
||||
* for users/sources/instance. `/admin` redirects to `/admin/users`.
|
||||
*/
|
||||
export function AdminPage() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const items: SubNavItem[] = [
|
||||
{ to: '/admin/users', label: t('admin.tabs.users') },
|
||||
{ to: '/admin/sources', label: t('admin.tabs.sources') },
|
||||
{ to: '/admin/instance', label: t('admin.tabs.instance') },
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '1.5rem' }}>
|
||||
<Window title={t('pages.admin')}>
|
||||
<p style={{ color: 'var(--color-text-2)' }}>{t('common.comingSoon')}</p>
|
||||
</Window>
|
||||
<div
|
||||
style={{
|
||||
padding: '1.5rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1.25rem',
|
||||
}}
|
||||
>
|
||||
<h1 className="page-title">{t('pages.admin')}</h1>
|
||||
<SubNav items={items} />
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Window } from '@olly/modern-sk';
|
||||
|
||||
function StubPanel({ title }: { title: string }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Window title={title}>
|
||||
<p style={{ color: 'var(--color-text-2)', margin: 0 }}>
|
||||
{t('common.comingSoon')}
|
||||
</p>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
|
||||
/** `/admin/users` — user list (add/remove). Scaffold. */
|
||||
export function AdminUsers() {
|
||||
const { t } = useTranslation();
|
||||
return <StubPanel title={t('admin.tabs.users')} />;
|
||||
}
|
||||
|
||||
/** `/admin/users/:userId` — per-user permissions / reset password / status. Scaffold. */
|
||||
export function AdminUserDetail() {
|
||||
const { t } = useTranslation();
|
||||
return <StubPanel title={t('admin.userDetail')} />;
|
||||
}
|
||||
|
||||
/** `/admin/sources` — pluggable source management (creds/cookies/status). Scaffold. */
|
||||
export function AdminSources() {
|
||||
const { t } = useTranslation();
|
||||
return <StubPanel title={t('admin.tabs.sources')} />;
|
||||
}
|
||||
|
||||
/** `/admin/instance` — service health, ML_SERVICE_URL, reindex. Scaffold. */
|
||||
export function AdminInstance() {
|
||||
const { t } = useTranslation();
|
||||
return <StubPanel title={t('admin.tabs.instance')} />;
|
||||
}
|
||||
Reference in New Issue
Block a user