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 (
+
+ );
+};
+
+export default SettingsPage;
+
+registerPage({ slug: 'settings', title: 'Paramètres', Component: SettingsPage });
diff --git a/src/shared/components/TabNav.js b/src/shared/components/TabNav.js
new file mode 100644
index 0000000..565decc
--- /dev/null
+++ b/src/shared/components/TabNav.js
@@ -0,0 +1,28 @@
+'use client';
+
+import React from 'react';
+
+const TabNav = ({ tabs = [], activeTab, onTabChange }) => {
+ return (
+
+ {tabs.map((tab) => {
+ const isActive = tab.id === activeTab;
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export default TabNav;
diff --git a/src/shared/components/index.js b/src/shared/components/index.js
index 7d464ed..e879040 100644
--- a/src/shared/components/index.js
+++ b/src/shared/components/index.js
@@ -29,3 +29,4 @@ export { default as TagInput } from './TagInput';
export { default as UserAvatar } from './UserAvatar';
export { default as ColorPicker } from './ColorPicker.client';
export { default as RelativeDate } from './RelativeDate.client';
+export { default as TabNav } from './TabNav';