feat: add full-page chat route and sidebar menu ordering
This commit is contained in:
@@ -13,7 +13,7 @@ import Tooltip from '@mui/material/Tooltip';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import { useLayout, CHAT_PANEL_MIN_WIDTH, CHAT_PANEL_MAX_WIDTH } from '../../contexts/LayoutContext';
|
||||
import { ChatProvider, useChat } from '../../contexts/ChatContext';
|
||||
import { useChat } from '../../contexts/ChatContext';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { configApi } from '../../services/config';
|
||||
@@ -341,11 +341,7 @@ const ChatPanelInner: React.FC = () => {
|
||||
};
|
||||
|
||||
const ChatPanel: React.FC = () => {
|
||||
return (
|
||||
<ChatProvider>
|
||||
<ChatPanelInner />
|
||||
</ChatProvider>
|
||||
);
|
||||
return <ChatPanelInner />;
|
||||
};
|
||||
|
||||
export default ChatPanel;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useState, ReactNode } from 'react';
|
||||
import { Box, Toolbar } from '@mui/material';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import Header from '../shared/Header';
|
||||
import Sidebar from '../shared/Sidebar';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import Loading from '../shared/Loading';
|
||||
import { LayoutProvider, useLayout, DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED } from '../../contexts/LayoutContext';
|
||||
import { ChatProvider } from '../../contexts/ChatContext';
|
||||
import ChatPanel from '../chat/ChatPanel';
|
||||
|
||||
interface DashboardLayoutProps {
|
||||
@@ -15,6 +17,8 @@ function DashboardLayoutInner({ children }: DashboardLayoutProps) {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const { isLoading } = useAuth();
|
||||
const { sidebarCollapsed, chatPanelOpen, chatPanelWidth } = useLayout();
|
||||
const location = useLocation();
|
||||
const onChatPage = location.pathname === '/chat';
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
setMobileOpen(!mobileOpen);
|
||||
@@ -25,7 +29,7 @@ function DashboardLayoutInner({ children }: DashboardLayoutProps) {
|
||||
}
|
||||
|
||||
const sidebarWidth = sidebarCollapsed ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH;
|
||||
const chatWidth = chatPanelOpen ? chatPanelWidth : 64;
|
||||
const chatWidth = onChatPage ? 0 : (chatPanelOpen ? chatPanelWidth : 64);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
|
||||
@@ -48,7 +52,7 @@ function DashboardLayoutInner({ children }: DashboardLayoutProps) {
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
<ChatPanel />
|
||||
{!onChatPage && <ChatPanel />}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -56,7 +60,9 @@ function DashboardLayoutInner({ children }: DashboardLayoutProps) {
|
||||
function DashboardLayout({ children }: DashboardLayoutProps) {
|
||||
return (
|
||||
<LayoutProvider>
|
||||
<DashboardLayoutInner>{children}</DashboardLayoutInner>
|
||||
<ChatProvider>
|
||||
<DashboardLayoutInner>{children}</DashboardLayoutInner>
|
||||
</ChatProvider>
|
||||
</LayoutProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@ import {
|
||||
LocalShipping,
|
||||
BugReport,
|
||||
BookOnline,
|
||||
Forum,
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useLayout, DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED } from '../../contexts/LayoutContext';
|
||||
import { usePermissionContext } from '../../contexts/PermissionContext';
|
||||
import { vehiclesApi } from '../../services/vehicles';
|
||||
import { preferencesApi } from '../../services/settings';
|
||||
|
||||
export { DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED };
|
||||
|
||||
@@ -69,6 +71,11 @@ const baseNavigationItems: NavigationItem[] = [
|
||||
icon: <DashboardIcon />,
|
||||
path: '/dashboard',
|
||||
},
|
||||
{
|
||||
text: 'Chat',
|
||||
icon: <Forum />,
|
||||
path: '/chat',
|
||||
},
|
||||
{
|
||||
text: 'Kalender',
|
||||
icon: <CalendarMonth />,
|
||||
@@ -169,6 +176,14 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
||||
staleTime: 2 * 60 * 1000,
|
||||
});
|
||||
|
||||
const { data: preferences } = useQuery({
|
||||
queryKey: ['user-preferences'],
|
||||
queryFn: preferencesApi.get,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const menuOrder: string[] = (preferences?.menuOrder as string[] | undefined) ?? [];
|
||||
|
||||
const vehicleSubItems: SubItem[] = useMemo(
|
||||
() =>
|
||||
(vehicleList ?? []).map((v) => ({
|
||||
@@ -220,8 +235,21 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
||||
return item;
|
||||
})
|
||||
.filter((item) => !item.permission || hasPermission(item.permission));
|
||||
|
||||
// Apply custom menu order: items in menuOrder are sorted to their index; rest keep relative order
|
||||
if (menuOrder.length > 0) {
|
||||
items.sort((a, b) => {
|
||||
const aIdx = menuOrder.indexOf(a.path);
|
||||
const bIdx = menuOrder.indexOf(b.path);
|
||||
if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
|
||||
if (aIdx !== -1) return -1;
|
||||
if (bIdx !== -1) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return hasPermission('admin:view') ? [...items, adminItem, adminSettingsItem] : items;
|
||||
}, [vehicleSubItems, hasPermission]);
|
||||
}, [vehicleSubItems, hasPermission, menuOrder]);
|
||||
|
||||
// Expand state for items with sub-items — auto-expand when route matches
|
||||
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>({});
|
||||
|
||||
Reference in New Issue
Block a user