diff --git a/src/features/admin/AdminLayout.server.js b/src/features/admin/AdminLayout.server.js index 5741634..e544160 100644 --- a/src/features/admin/AdminLayout.server.js +++ b/src/features/admin/AdminLayout.server.js @@ -1,6 +1,6 @@ import AdminShell from './components/AdminShell.js'; import { protectAdmin } from './protect.js'; -import { buildNavigationSections } from './navigation.js'; +import { buildNavigationSections, buildBottomNavItems } from './navigation.js'; import { logoutAction } from '@zen/core/features/auth/actions'; import { getAppName } from '@zen/core'; import './widgets/index.server.js'; @@ -9,6 +9,7 @@ export default async function AdminLayout({ children }) { const session = await protectAdmin(); const appName = getAppName(); const navigationSections = buildNavigationSections('/'); + const bottomNavItems = buildBottomNavItems('/'); return ( {children} diff --git a/src/features/admin/AdminPage.client.js b/src/features/admin/AdminPage.client.js index a701505..888f0bf 100644 --- a/src/features/admin/AdminPage.client.js +++ b/src/features/admin/AdminPage.client.js @@ -5,9 +5,10 @@ import './pages/DashboardPage.client.js'; import './pages/UsersPage.client.js'; import './pages/RolesPage.client.js'; import './pages/ProfilePage.client.js'; +import './pages/SettingsPage.client.js'; import './widgets/index.client.js'; -export default function AdminPageClient({ params, user, widgetData }) { +export default function AdminPageClient({ params, user, widgetData, appConfig }) { const parts = params?.admin || []; const [first] = parts; @@ -17,10 +18,11 @@ export default function AdminPageClient({ params, user, widgetData }) { if (!page) return null; const { Component } = page; - // Le tableau de bord reçoit les données collectées côté serveur ; les - // autres pages ne connaissent pas le widget data. if (slug === 'dashboard') { return ; } + if (slug === 'settings') { + return ; + } return ; } diff --git a/src/features/admin/AdminPage.server.js b/src/features/admin/AdminPage.server.js index 1adb7c3..ab77ac0 100644 --- a/src/features/admin/AdminPage.server.js +++ b/src/features/admin/AdminPage.server.js @@ -1,17 +1,20 @@ import AdminPageClient from './AdminPage.client.js'; import { protectAdmin } from './protect.js'; import { collectWidgetData } from './registry.js'; +import { getAppConfig, getPublicBaseUrl } from '@zen/core'; export default async function AdminPage({ params }) { const resolvedParams = await params; const session = await protectAdmin(); const widgetData = await collectWidgetData(); + const appConfig = { ...getAppConfig(), siteUrl: getPublicBaseUrl() }; return ( ); } diff --git a/src/features/admin/components/AdminShell.js b/src/features/admin/components/AdminShell.js index 2684dc4..9eedbd0 100644 --- a/src/features/admin/components/AdminShell.js +++ b/src/features/admin/components/AdminShell.js @@ -4,7 +4,7 @@ import { useState } from 'react'; import AdminSidebar from './AdminSidebar.js'; import AdminTop from './AdminTop.js'; -export default function AdminShell({ children, user, onLogout, appName, navigationSections }) { +export default function AdminShell({ children, user, onLogout, appName, navigationSections, bottomNavItems }) { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); return ( @@ -14,6 +14,7 @@ export default function AdminShell({ children, user, onLogout, appName, navigati setIsMobileMenuOpen={setIsMobileMenuOpen} appName={appName} navigationSections={navigationSections} + bottomNavItems={bottomNavItems} />
{ +const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledModules, navigationSections: serverNavigationSections, bottomNavItems = [] }) => { const pathname = usePathname(); const [collapsedSections, setCollapsedSections] = useState(() => { @@ -190,9 +190,31 @@ const AdminSidebar = ({ isMobileMenuOpen, setIsMobileMenuOpen, appName, enabledM {/* Navigation */} -
); diff --git a/src/features/admin/navigation.js b/src/features/admin/navigation.js index 4869130..ef70f50 100644 --- a/src/features/admin/navigation.js +++ b/src/features/admin/navigation.js @@ -12,6 +12,7 @@ registerNavSection({ id: 'system', title: 'Utilisateurs', icon: 'UserMulti registerNavItem({ id: 'dashboard', label: 'Tableau de bord', icon: 'DashboardSquare03Icon', href: '/admin/dashboard', sectionId: 'dashboard', order: 10 }); registerNavItem({ id: 'users', label: 'Utilisateurs', icon: 'UserMultiple02Icon', href: '/admin/users', sectionId: 'system', order: 10 }); registerNavItem({ id: 'roles', label: 'Rôles', icon: 'Crown03Icon', href: '/admin/roles', sectionId: 'system', order: 20 }); +registerNavItem({ id: 'settings', label: 'Paramètres', icon: 'Settings02Icon', href: '/admin/settings', position: 'bottom', order: 10 }); /** * Build sections for AdminSidebar. Items are sérialisables (pas de composants), @@ -19,7 +20,7 @@ registerNavItem({ id: 'roles', label: 'Rôles', icon: 'Crown03Icon' */ export function buildNavigationSections(pathname) { const sections = getNavSections(); - const items = getNavItems(); + const items = getNavItems().filter(item => item.position !== 'bottom'); const bySection = new Map(); for (const item of items) { @@ -37,3 +38,18 @@ export function buildNavigationSections(pathname) { .filter(s => bySection.has(s.id)) .map(s => ({ id: s.id, title: s.title, icon: s.icon, items: bySection.get(s.id) })); } + +/** + * Build the list of bottom-pinned nav items for AdminSidebar. + */ +export function buildBottomNavItems(pathname) { + return getNavItems() + .filter(item => item.position === 'bottom') + .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) + .map(item => ({ + name: item.label, + href: item.href, + icon: item.icon, + current: pathname === item.href || pathname.startsWith(item.href + '/'), + })); +} diff --git a/src/features/admin/pages/SettingsPage.client.js b/src/features/admin/pages/SettingsPage.client.js new file mode 100644 index 0000000..184c324 --- /dev/null +++ b/src/features/admin/pages/SettingsPage.client.js @@ -0,0 +1,92 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { registerPage } from '../registry.js'; +import AdminHeader from '../components/AdminHeader.js'; +import { Card, Input, Select, TabNav } from '@zen/core/shared/components'; +import { applyTheme, getStoredTheme } from '@zen/core/themes'; + +const TABS = [ + { id: 'general', label: 'Général' }, + { id: 'appearance', label: 'Apparence' }, +]; + +const THEME_OPTIONS = [ + { value: 'light', label: 'Mode clair' }, + { value: 'dark', label: 'Mode sombre' }, + { value: 'auto', label: 'Thème système' }, +]; + +const SettingsPage = ({ appConfig = {} }) => { + const [activeTab, setActiveTab] = useState('general'); + const [theme, setTheme] = useState('auto'); + + useEffect(() => { + setTheme(getStoredTheme()); + }, []); + + const handleThemeChange = (value) => { + setTheme(value); + applyTheme(value); + }; + + return ( +
+ + +
+ + + {activeTab === 'general' && ( + +
+ + + + +
+
+ )} + + {activeTab === 'appearance' && ( + +
+