fix nav expand
This commit is contained in:
@@ -271,7 +271,6 @@ export default function App() {
|
||||
return <NewOrganization {...commonProps} />;
|
||||
|
||||
// Updated content routes section
|
||||
// In App.tsx - Update the content routes section
|
||||
case "/content":
|
||||
case "/content/profiler":
|
||||
case "/content/blogs":
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { NotificationPanel, Notification } from '../NotificationPanel';
|
||||
import {
|
||||
Home,
|
||||
FileText,
|
||||
GraduationCap,
|
||||
Users,
|
||||
Calendar,
|
||||
Globe,
|
||||
BarChart3,
|
||||
Settings,
|
||||
Menu,
|
||||
import {
|
||||
Home,
|
||||
FileText,
|
||||
GraduationCap,
|
||||
Users,
|
||||
Calendar,
|
||||
Globe,
|
||||
BarChart3,
|
||||
Settings,
|
||||
Menu,
|
||||
ChevronLeft,
|
||||
LogOut,
|
||||
User,
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
ChevronDown,
|
||||
Target
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
BreadcrumbSeparator,
|
||||
} from '../ui/breadcrumb';
|
||||
import { Avatar, AvatarFallback } from '../ui/avatar';
|
||||
import {
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
@@ -112,10 +112,10 @@ const navigationItems: NavigationItem[] = [
|
||||
route: '/content',
|
||||
children: [
|
||||
{
|
||||
id: 'profiler',
|
||||
label: 'Profiler',
|
||||
icon: Target,
|
||||
route: '/content'
|
||||
id: 'profiler',
|
||||
label: 'Profiler',
|
||||
icon: Target,
|
||||
route: '/content/profiler'
|
||||
},
|
||||
{
|
||||
id: 'blogs',
|
||||
@@ -257,7 +257,7 @@ const useAutoSave = (
|
||||
// Save to localStorage for persistence
|
||||
localStorage.setItem(`autosave_${userId}_${currentRoute}`, JSON.stringify(autoSaveData));
|
||||
onAutoSave(formData);
|
||||
|
||||
|
||||
// Show subtle toast notification
|
||||
toast.success("Draft saved", { duration: 1000 });
|
||||
}, SESSION_CONFIG.AUTO_SAVE_INTERVAL);
|
||||
@@ -301,7 +301,7 @@ const useSessionTimeout = (onLogout: () => void) => {
|
||||
|
||||
// Activity listeners
|
||||
const resetOnActivity = () => resetTimers();
|
||||
|
||||
|
||||
// Listen for user activity
|
||||
const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
|
||||
events.forEach(event => {
|
||||
@@ -320,12 +320,12 @@ const useSessionTimeout = (onLogout: () => void) => {
|
||||
}, [onLogout]);
|
||||
};
|
||||
|
||||
export function AuthenticatedLayout({
|
||||
children,
|
||||
currentRoute,
|
||||
onNavigate,
|
||||
onLogout,
|
||||
user,
|
||||
export function AuthenticatedLayout({
|
||||
children,
|
||||
currentRoute,
|
||||
onNavigate,
|
||||
onLogout,
|
||||
user,
|
||||
breadcrumbs = [],
|
||||
formData,
|
||||
onAutoSave,
|
||||
@@ -350,10 +350,10 @@ export function AuthenticatedLayout({
|
||||
// Auto-expand parent items when on child routes
|
||||
useEffect(() => {
|
||||
const newExpandedItems = new Set<string>();
|
||||
|
||||
|
||||
navigationItems.forEach(item => {
|
||||
if (item.children) {
|
||||
const isChildActive = item.children.some(child =>
|
||||
const isChildActive = item.children.some(child =>
|
||||
isActiveRoute(child.route)
|
||||
);
|
||||
if (isChildActive) {
|
||||
@@ -361,13 +361,14 @@ export function AuthenticatedLayout({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setExpandedItems(newExpandedItems);
|
||||
}, [currentRoute]);
|
||||
|
||||
|
||||
// Notification handlers
|
||||
const handleMarkAsRead = (notificationId: string) => {
|
||||
setLocalNotifications(prev =>
|
||||
setLocalNotifications(prev =>
|
||||
prev.map(n => n.id === notificationId ? { ...n, read: true } : n)
|
||||
);
|
||||
if (onMarkNotificationAsRead) {
|
||||
@@ -408,22 +409,32 @@ export function AuthenticatedLayout({
|
||||
|
||||
const isActiveRoute = (route: string) => {
|
||||
if (!currentRoute || !route) return false;
|
||||
|
||||
|
||||
// Exact match
|
||||
if (currentRoute === route) return true;
|
||||
|
||||
|
||||
// User routes
|
||||
if (route === '/users/individual' && currentRoute.startsWith('/users/')) return true;
|
||||
|
||||
// Content routes - match both parent and children
|
||||
if (route === '/content' && currentRoute.startsWith('/content/')) return true;
|
||||
|
||||
|
||||
// Content section — highlight both parent and child routes
|
||||
if (route === '/content') {
|
||||
return currentRoute.startsWith('/content');
|
||||
}
|
||||
|
||||
// Child route check
|
||||
if (route.startsWith('/content/')) {
|
||||
return currentRoute === route || currentRoute.startsWith(route + '/');
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const getActiveParent = (items: NavigationItem[]): string | null => {
|
||||
for (const item of items) {
|
||||
// Check if this item is active
|
||||
if (isActiveRoute(item.route)) return item.id;
|
||||
|
||||
// Check if any children are active
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
if (isActiveRoute(child.route)) return item.id;
|
||||
@@ -453,31 +464,43 @@ export function AuthenticatedLayout({
|
||||
const expanded = isExpanded(item.id);
|
||||
const Icon = item.icon;
|
||||
|
||||
// const handleItemClick = () => {
|
||||
// if (hasChildren && !isChild) {
|
||||
// // Always expand when parent clicked
|
||||
// const newExpandedItems = new Set(expandedItems);
|
||||
// newExpandedItems.add(item.id);
|
||||
// setExpandedItems(newExpandedItems);
|
||||
|
||||
// // Navigate to parent route (like /content)
|
||||
// onNavigate(item.route);
|
||||
// } else {
|
||||
// // Navigate directly for child items
|
||||
// onNavigate(item.route);
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleItemClick = () => {
|
||||
if (hasChildren && !isChild) {
|
||||
// Toggle dropdown for parent items
|
||||
// Toggle dropdown open/close when clicking parent
|
||||
toggleExpanded(item.id);
|
||||
// If not active and has children, navigate to first child
|
||||
if (!isActive && item.children && item.children.length > 0) {
|
||||
onNavigate(item.children[0].route);
|
||||
}
|
||||
} else {
|
||||
// Navigate directly for child items or items without children
|
||||
onNavigate(item.route);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div key={item.id}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full justify-start min-h-[44px] ${
|
||||
isActive
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: expanded && !isChild
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: 'hover:bg-accent hover:text-accent-foreground'
|
||||
} ${isChild ? 'ml-4 text-sm' : ''} ${hasChildren ? 'pr-2' : ''}`}
|
||||
className={`w-full justify-start min-h-[44px] ${isActive
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: expanded && !isChild
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: 'hover:bg-accent hover:text-accent-foreground'
|
||||
} ${isChild ? 'ml-4 text-sm' : ''} ${hasChildren ? 'pr-2' : ''}`}
|
||||
onClick={handleItemClick}
|
||||
>
|
||||
<Icon className={`${sidebarCollapsed ? 'mx-auto' : 'mr-2'} h-4 w-4 flex-shrink-0`} />
|
||||
@@ -485,14 +508,13 @@ export function AuthenticatedLayout({
|
||||
<>
|
||||
<span className="truncate flex-1 text-left">{item.label}</span>
|
||||
{hasChildren && (
|
||||
<ChevronDown className={`h-4 w-4 ml-auto transition-transform ${
|
||||
expanded ? 'rotate-180' : ''
|
||||
}`} />
|
||||
<ChevronDown className={`h-4 w-4 ml-auto transition-transform ${expanded ? 'rotate-180' : ''
|
||||
}`} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
|
||||
{/* Render children if dropdown is expanded and not collapsed */}
|
||||
{item.children && expanded && !sidebarCollapsed && (
|
||||
<div className="mt-1 space-y-1">
|
||||
@@ -511,7 +533,7 @@ export function AuthenticatedLayout({
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
<BreadcrumbLink
|
||||
onClick={() => onNavigate('/dashboard')}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
@@ -523,7 +545,7 @@ export function AuthenticatedLayout({
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
{crumb.href && index < breadcrumbs.length - 1 ? (
|
||||
<BreadcrumbLink
|
||||
<BreadcrumbLink
|
||||
onClick={() => onNavigate(crumb.href!)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
@@ -537,7 +559,7 @@ export function AuthenticatedLayout({
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
|
||||
{/* Notification Bell */}
|
||||
<NotificationPanel
|
||||
notifications={localNotifications}
|
||||
@@ -559,7 +581,7 @@ export function AuthenticatedLayout({
|
||||
return (
|
||||
<div className="flex h-screen bg-background">
|
||||
{/* Left Sidebar */}
|
||||
<div
|
||||
<div
|
||||
className={`${sidebarCollapsed ? 'w-[72px]' : 'w-[240px]'} bg-sidebar border-r border-sidebar-border flex flex-col transition-all duration-200`}
|
||||
role="navigation"
|
||||
aria-label="Main navigation"
|
||||
@@ -568,9 +590,9 @@ export function AuthenticatedLayout({
|
||||
<div className="p-4 border-b border-sidebar-border" style={{ backgroundColor: 'var(--color-brand-primary)' }}>
|
||||
<div className="flex items-center justify-between">
|
||||
{!sidebarCollapsed && (
|
||||
<img
|
||||
src={klcLogoDark}
|
||||
alt="Kautilya Leadership Centre"
|
||||
<img
|
||||
src={klcLogoDark}
|
||||
alt="Kautilya Leadership Centre"
|
||||
className="h-8"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -132,25 +132,25 @@ export function ContentManager({
|
||||
|
||||
const getCurrentContentType = () => {
|
||||
console.log('🔄 getCurrentContentType called with currentRoute:', currentRoute);
|
||||
|
||||
|
||||
// Find exact route match first
|
||||
const exactMatch = contentTypes.find(type => currentRoute === type.route);
|
||||
if (exactMatch) {
|
||||
console.log('✅ Exact route match found:', exactMatch.id);
|
||||
return exactMatch;
|
||||
}
|
||||
|
||||
|
||||
// Find partial match for nested routes
|
||||
const partialMatch = contentTypes.find(type =>
|
||||
currentRoute.startsWith(type.route + '/') ||
|
||||
const partialMatch = contentTypes.find(type =>
|
||||
currentRoute.startsWith(type.route + '/') ||
|
||||
currentRoute.includes(type.id)
|
||||
);
|
||||
|
||||
|
||||
if (partialMatch) {
|
||||
console.log('✅ Partial route match found:', partialMatch.id);
|
||||
return partialMatch;
|
||||
}
|
||||
|
||||
|
||||
console.log('⚠️ No match found, defaulting to profiler');
|
||||
return contentTypes[0]; // Default to profiler
|
||||
};
|
||||
@@ -159,34 +159,33 @@ export function ContentManager({
|
||||
console.log('📌 Current content type:', currentContentType.label);
|
||||
|
||||
const getBreadcrumbs = () => {
|
||||
const breadcrumbs = [
|
||||
{ label: "Content", href: "/content" }
|
||||
];
|
||||
const breadcrumbs = [{ label: "Content", href: "/content" }];
|
||||
|
||||
// Add specific content type to breadcrumbs if not on the main content page
|
||||
if (currentRoute !== "/content") {
|
||||
// Always show current content type
|
||||
if (currentContentType) {
|
||||
breadcrumbs.push({ label: currentContentType.label });
|
||||
}
|
||||
|
||||
return breadcrumbs;
|
||||
};
|
||||
|
||||
|
||||
const renderContent = () => {
|
||||
console.log('🎨 Rendering content for:', currentContentType.id);
|
||||
|
||||
|
||||
switch (currentContentType.id) {
|
||||
case "profiler":
|
||||
return <ProfilerTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "blogs":
|
||||
return <BlogsTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "faqs":
|
||||
return <FAQTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "webcasts":
|
||||
return <WebcastsTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "training-materials":
|
||||
return (
|
||||
<TrainingMaterialsTab
|
||||
@@ -196,16 +195,16 @@ export function ContentManager({
|
||||
setActiveInnerTab={setActiveInnerTab}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
case "reading-materials":
|
||||
return <ReadingMaterialsTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "podcasts":
|
||||
return <PodcastsTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "case-studies":
|
||||
return <CaseStudiesTab onNavigate={onNavigate} user={user} />;
|
||||
|
||||
|
||||
case "klc-archives":
|
||||
return (
|
||||
<KLCContentArchiveTab
|
||||
@@ -215,7 +214,7 @@ export function ContentManager({
|
||||
setActiveInnerTab={setActiveInnerTab}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
default:
|
||||
console.log('⚠️ Defaulting to profiler tab');
|
||||
return <ProfilerTab onNavigate={onNavigate} user={user} />;
|
||||
@@ -237,7 +236,7 @@ export function ContentManager({
|
||||
{currentContentType.label}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
{currentContentType.id === "profiler"
|
||||
{currentContentType.id === "profiler"
|
||||
? "Manage all content types and profiler submissions with approval workflow"
|
||||
: `Manage and organize ${currentContentType.label.toLowerCase()}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user