Refactor: Split Sidebar.tsx into smaller components
Refactored the Sidebar component into smaller, more manageable files to improve code organization and maintainability. Fix sidebar auto-expansion issue Ensured the sidebar's collapsed state is maintained when navigating between pages.
This commit is contained in:
+107
-129
@@ -1,137 +1,115 @@
|
|||||||
|
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import React from 'react';
|
||||||
import { Toaster as Sonner } from "@/components/ui/sonner";
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { ThemeProvider } from './contexts/ThemeContext';
|
||||||
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
|
import { LanguageProvider } from './contexts/LanguageContext';
|
||||||
import { useEffect, useState } from "react";
|
import { SidebarProvider } from './contexts/SidebarContext';
|
||||||
import { authService } from "@/services/authService";
|
import { ErrorBoundary } from './components/ErrorBoundary';
|
||||||
import { ThemeProvider } from "@/contexts/ThemeContext";
|
import { Toaster } from './components/ui/sonner';
|
||||||
import { LanguageProvider } from "@/contexts/LanguageContext";
|
import { authService } from './services/authService';
|
||||||
|
|
||||||
import Login from "./pages/Login";
|
// Pages
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Index from './pages/Index';
|
||||||
import ServiceDetail from "./pages/ServiceDetail";
|
import Login from './pages/Login';
|
||||||
import Settings from "./pages/Settings";
|
import Dashboard from './pages/Dashboard';
|
||||||
import Profile from "./pages/Profile";
|
import ServiceDetail from './pages/ServiceDetail';
|
||||||
import NotFound from "./pages/NotFound";
|
import Settings from './pages/Settings';
|
||||||
import SslDomain from "./pages/SslDomain";
|
import Profile from './pages/Profile';
|
||||||
import ScheduleIncident from "./pages/ScheduleIncident";
|
import SslDomain from './pages/SslDomain';
|
||||||
import OperationalPage from "./pages/OperationalPage";
|
import ScheduleIncident from './pages/ScheduleIncident';
|
||||||
import PublicStatusPage from "./pages/PublicStatusPage";
|
import OperationalPage from './pages/OperationalPage';
|
||||||
|
import PublicStatusPage from './pages/PublicStatusPage';
|
||||||
// Create a Protected route component
|
import NotFound from './pages/NotFound';
|
||||||
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const isAuthenticated = authService.isAuthenticated();
|
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
|
||||||
return <Navigate to="/login" replace />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{children}</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const App = () => {
|
// Protected Route Component
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const isAuthenticated = authService.isAuthenticated();
|
||||||
useEffect(() => {
|
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
|
||||||
// Check authentication status when the app loads
|
|
||||||
const checkAuth = async () => {
|
|
||||||
try {
|
|
||||||
// Just check the auth state
|
|
||||||
authService.isAuthenticated();
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkAuth();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <div className="flex items-center justify-center h-screen bg-background text-foreground">Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<ThemeProvider>
|
|
||||||
<LanguageProvider>
|
|
||||||
<TooltipProvider>
|
|
||||||
<Toaster />
|
|
||||||
<Sonner />
|
|
||||||
<BrowserRouter>
|
|
||||||
<Routes>
|
|
||||||
<Route path="/login" element={<Login />} />
|
|
||||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
||||||
<Route
|
|
||||||
path="/dashboard"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<Dashboard />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/service/:id"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ServiceDetail />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/settings"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<Settings />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/profile"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<Profile />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/ssl-domain"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<SslDomain />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/schedule-incident"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ScheduleIncident />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/operational-page"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<OperationalPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/* Public status page route */}
|
|
||||||
<Route path="/status/:slug" element={<PublicStatusPage />} />
|
|
||||||
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
|
||||||
<Route path="*" element={<NotFound />} />
|
|
||||||
</Routes>
|
|
||||||
</BrowserRouter>
|
|
||||||
</TooltipProvider>
|
|
||||||
</LanguageProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</QueryClientProvider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<ThemeProvider>
|
||||||
|
<LanguageProvider>
|
||||||
|
<SidebarProvider>
|
||||||
|
<Router>
|
||||||
|
<div className="min-h-screen bg-background text-foreground">
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Index />} />
|
||||||
|
<Route path="/login" element={<Login />} />
|
||||||
|
<Route
|
||||||
|
path="/dashboard"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<Dashboard />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/service/:id"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<ServiceDetail />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/settings"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<Settings />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/profile"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<Profile />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/ssl-domain"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<SslDomain />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/schedule-incident"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<ScheduleIncident />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/operational-page"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<OperationalPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/status/:pageSlug" element={<PublicStatusPage />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</Routes>
|
||||||
|
<Toaster />
|
||||||
|
</div>
|
||||||
|
</Router>
|
||||||
|
</SidebarProvider>
|
||||||
|
</LanguageProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ErrorBoundaryState {
|
||||||
|
hasError: boolean;
|
||||||
|
error?: Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorBoundaryProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||||
|
constructor(props: ErrorBoundaryProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = { hasError: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||||
|
return { hasError: true, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
||||||
|
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.hasError) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||||
|
<div className="text-center p-6">
|
||||||
|
<h2 className="text-2xl font-bold text-destructive mb-4">Something went wrong</h2>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
{this.state.error?.message || 'An unexpected error occurred'}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.reload()}
|
||||||
|
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
||||||
|
>
|
||||||
|
Reload Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,140 +1,22 @@
|
|||||||
import { Globe, Boxes, Radar, Calendar, BarChart2, LineChart, FileText, Settings, User, UserCog, Bell, FileClock, Database, RefreshCw, Info, ChevronDown, BookOpen } from "lucide-react";
|
|
||||||
|
import React from "react";
|
||||||
import { useTheme } from "@/contexts/ThemeContext";
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { SidebarHeader } from "./sidebar/SidebarHeader";
|
||||||
import { useState, useEffect } from "react";
|
import { MainNavigation } from "./sidebar/MainNavigation";
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
import { SettingsPanel } from "./sidebar/SettingsPanel";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
||||||
import { useLanguage } from "@/contexts/LanguageContext";
|
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sidebar = ({
|
export const Sidebar = ({ collapsed }: SidebarProps) => {
|
||||||
collapsed
|
const { theme } = useTheme();
|
||||||
}: SidebarProps) => {
|
|
||||||
const {
|
|
||||||
theme
|
|
||||||
} = useTheme();
|
|
||||||
const {
|
|
||||||
t
|
|
||||||
} = useLanguage();
|
|
||||||
const location = useLocation();
|
|
||||||
const [activeSettingsItem, setActiveSettingsItem] = useState<string | null>("general");
|
|
||||||
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false);
|
|
||||||
|
|
||||||
// Update active settings item based on URL
|
|
||||||
useEffect(() => {
|
|
||||||
if (location.pathname === '/settings') {
|
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
const panel = params.get('panel');
|
|
||||||
if (panel) {
|
|
||||||
setActiveSettingsItem(panel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
const handleSettingsItemClick = (item: string) => {
|
return (
|
||||||
setActiveSettingsItem(item);
|
<div className={`${collapsed ? 'w-16' : 'w-64'} ${theme === 'dark' ? 'bg-[#121212] border-[#1e1e1e]' : 'bg-sidebar border-sidebar-border'} border-r flex flex-col transition-all duration-300 h-full`}>
|
||||||
};
|
<SidebarHeader collapsed={collapsed} />
|
||||||
|
<MainNavigation collapsed={collapsed} />
|
||||||
const getMenuItemClasses = (isActive: boolean) => {
|
<SettingsPanel collapsed={collapsed} />
|
||||||
return `p-2 ${isActive ? theme === 'dark' ? 'bg-[#1a1a1a]' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-[#1a1a1a]' : 'bg-sidebar-accent'}`} rounded-lg flex items-center`;
|
</div>
|
||||||
};
|
);
|
||||||
|
|
||||||
// New larger icon size for the main menu
|
|
||||||
const mainIconSize = "h-6 w-6";
|
|
||||||
|
|
||||||
return <div className={`${collapsed ? 'w-16' : 'w-64'} ${theme === 'dark' ? 'bg-[#121212] border-[#1e1e1e]' : 'bg-sidebar border-sidebar-border'} border-r flex flex-col transition-all duration-300 h-full`}>
|
|
||||||
<div className={`p-4 ${theme === 'dark' ? 'border-[#1e1e1e]' : 'border-sidebar-border'} border-b flex items-center ${collapsed ? 'justify-center' : ''}`}>
|
|
||||||
<div className="h-8 w-8 bg-green-500 rounded flex items-center justify-center mr-2">
|
|
||||||
<span className="text-white font-bold">C</span>
|
|
||||||
</div>
|
|
||||||
{!collapsed && <h1 className="text-xl font-semibold">CheckCle App</h1>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav className="my-2 mx-1 py-1 px-1">
|
|
||||||
<Link to="/dashboard" className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg ${location.pathname === '/dashboard' ? theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'}`} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<Globe className={`${mainIconSize} text-purple-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("uptimeMonitoring")}</span>}
|
|
||||||
</Link>
|
|
||||||
<div className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<Boxes className={`${mainIconSize} text-blue-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("instanceMonitoring")}</span>}
|
|
||||||
</div>
|
|
||||||
<Link to="/ssl-domain" className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg ${location.pathname === '/ssl-domain' ? theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'}`} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<Radar className={`${mainIconSize} text-cyan-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("sslDomain")}</span>}
|
|
||||||
</Link>
|
|
||||||
<Link to="/schedule-incident" className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg ${location.pathname === '/schedule-incident' ? theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'}`} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<Calendar className={`${mainIconSize} text-emerald-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("scheduleIncident")}</span>}
|
|
||||||
</Link>
|
|
||||||
<Link to="/operational-page" className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg ${location.pathname === '/operational-page' ? theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'}`} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<BarChart2 className={`${mainIconSize} text-amber-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("operationalPage")}</span>}
|
|
||||||
</Link>
|
|
||||||
<div className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<LineChart className={`${mainIconSize} text-rose-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("reports")}</span>}
|
|
||||||
</div>
|
|
||||||
<div className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200`}>
|
|
||||||
<FileText className={`${mainIconSize} text-indigo-400`} />
|
|
||||||
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t("apiDocumentation")}</span>}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{!collapsed && <div className={`flex-1 flex flex-col border-t ${theme === 'dark' ? 'border-[#1e1e1e] bg-[#121212]' : 'border-sidebar-border bg-sidebar'} p-4`}>
|
|
||||||
<Collapsible open={settingsPanelOpen} onOpenChange={setSettingsPanelOpen} className="w-full flex flex-col flex-1">
|
|
||||||
<CollapsibleTrigger className={`flex items-center justify-between w-full mb-4 px-2 py-2 rounded-lg ${theme === 'dark' ? 'hover:bg-[#1a1a1a]' : 'hover:bg-sidebar-accent'}`}>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="font-medium tracking-wide">{t("settingPanel")}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Settings className="h-4 w-4 mr-1" />
|
|
||||||
<ChevronDown className={`h-4 w-4 transition-transform duration-200 ${settingsPanelOpen ? 'rotate-180' : ''}`} />
|
|
||||||
</div>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
|
|
||||||
<CollapsibleContent className={`${theme === 'dark' ? 'bg-[#121212]' : 'bg-sidebar'} flex-1 flex flex-col`}>
|
|
||||||
<div className="max-h-[300px] overflow-y-auto custom-scrollbar relative pr-1">
|
|
||||||
<ScrollArea className="h-full">
|
|
||||||
<div className="space-y-2 pr-4">
|
|
||||||
<Link to={`/settings?panel=general`} className={getMenuItemClasses(activeSettingsItem === 'general')} onClick={() => handleSettingsItemClick('general')}>
|
|
||||||
<Settings className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("generalSettings")}</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={`/settings?panel=users`} className={getMenuItemClasses(activeSettingsItem === 'users')} onClick={() => handleSettingsItemClick('users')}>
|
|
||||||
<User className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("userManagement")}</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={`/settings?panel=notifications`} className={getMenuItemClasses(activeSettingsItem === 'notifications')} onClick={() => handleSettingsItemClick('notifications')}>
|
|
||||||
<Bell className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("notificationSettings")}</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={`/settings?panel=templates`} className={getMenuItemClasses(activeSettingsItem === 'templates')} onClick={() => handleSettingsItemClick('templates')}>
|
|
||||||
<BookOpen className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("alertsTemplates")}</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={`/settings?panel=data-retention`} className={getMenuItemClasses(activeSettingsItem === 'data-retention')} onClick={() => handleSettingsItemClick('data-retention')}>
|
|
||||||
<Database className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("dataRetention")}</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={`/settings?panel=about`} className={getMenuItemClasses(activeSettingsItem === 'about')} onClick={() => handleSettingsItemClick('about')}>
|
|
||||||
<Info className="h-4 w-4 mr-2" />
|
|
||||||
<span className="text-sm">{t("aboutSystem")}</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{collapsed && <div className={`border-t ${theme === 'dark' ? 'border-[#1e1e1e] bg-[#121212]' : 'border-sidebar-border bg-sidebar'} p-4 flex justify-center`}>
|
|
||||||
<Link to="/settings">
|
|
||||||
<Settings className={`${mainIconSize} text-purple-400`} />
|
|
||||||
</Link>
|
|
||||||
</div>}
|
|
||||||
</div>;
|
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { MenuItem } from "./MenuItem";
|
||||||
|
import { mainMenuItems } from "./navigationData";
|
||||||
|
|
||||||
|
interface MainNavigationProps {
|
||||||
|
collapsed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MainNavigation: React.FC<MainNavigationProps> = ({ collapsed }) => {
|
||||||
|
return (
|
||||||
|
<nav className="my-2 mx-1 py-1 px-1">
|
||||||
|
{mainMenuItems.map((item) => (
|
||||||
|
<MenuItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
path={item.path}
|
||||||
|
icon={item.icon}
|
||||||
|
translationKey={item.translationKey}
|
||||||
|
color={item.color}
|
||||||
|
hasNavigation={item.hasNavigation}
|
||||||
|
collapsed={collapsed}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
import { LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
|
interface MenuItemProps {
|
||||||
|
id: string;
|
||||||
|
path: string | null;
|
||||||
|
icon: LucideIcon;
|
||||||
|
translationKey: string;
|
||||||
|
color: string;
|
||||||
|
hasNavigation: boolean;
|
||||||
|
collapsed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MenuItem: React.FC<MenuItemProps> = ({
|
||||||
|
id,
|
||||||
|
path,
|
||||||
|
icon: Icon,
|
||||||
|
translationKey,
|
||||||
|
color,
|
||||||
|
hasNavigation,
|
||||||
|
collapsed
|
||||||
|
}) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleClick = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (hasNavigation && path) {
|
||||||
|
// Use navigate instead of window.location to prevent full page reload
|
||||||
|
navigate(path, { replace: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isActive = path && location.pathname === path;
|
||||||
|
const mainIconSize = "h-6 w-6";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${collapsed ? 'p-3' : 'p-2 pl-3'} mb-1 rounded-lg ${isActive ? theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-gray-800' : 'bg-sidebar-accent'}`} flex items-center ${collapsed ? 'justify-center' : ''} transition-colors duration-200 cursor-pointer`}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<Icon className={`${mainIconSize} ${color}`} />
|
||||||
|
{!collapsed && <span className="ml-2.5 font-medium text-foreground tracking-wide text-[15px]">{t(translationKey)}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
import { Settings, ChevronDown } from "lucide-react";
|
||||||
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { settingsMenuItems } from "./navigationData";
|
||||||
|
|
||||||
|
interface SettingsPanelProps {
|
||||||
|
collapsed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsPanel: React.FC<SettingsPanelProps> = ({ collapsed }) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [activeSettingsItem, setActiveSettingsItem] = useState<string | null>("general");
|
||||||
|
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false);
|
||||||
|
|
||||||
|
// Update active settings item based on URL
|
||||||
|
useEffect(() => {
|
||||||
|
if (location.pathname === '/settings') {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const panel = params.get('panel');
|
||||||
|
if (panel) {
|
||||||
|
setActiveSettingsItem(panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
const handleSettingsItemClick = (item: string) => {
|
||||||
|
setActiveSettingsItem(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuItemClick = (path: string, event: React.MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
// Use navigate instead of window.location to prevent full page reload
|
||||||
|
navigate(path, { replace: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMenuItemClasses = (isActive: boolean) => {
|
||||||
|
return `p-2 ${isActive ? theme === 'dark' ? 'bg-[#1a1a1a]' : 'bg-sidebar-accent' : `hover:${theme === 'dark' ? 'bg-[#1a1a1a]' : 'bg-sidebar-accent'}`} rounded-lg flex items-center`;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (collapsed) {
|
||||||
|
const mainIconSize = "h-6 w-6";
|
||||||
|
return (
|
||||||
|
<div className={`border-t ${theme === 'dark' ? 'border-[#1e1e1e] bg-[#121212]' : 'border-sidebar-border bg-sidebar'} p-4 flex justify-center`}>
|
||||||
|
<div
|
||||||
|
onClick={(e) => handleMenuItemClick('/settings', e)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<Settings className={`${mainIconSize} text-purple-400`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex-1 flex flex-col border-t ${theme === 'dark' ? 'border-[#1e1e1e] bg-[#121212]' : 'border-sidebar-border bg-sidebar'} p-4`}>
|
||||||
|
<Collapsible open={settingsPanelOpen} onOpenChange={setSettingsPanelOpen} className="w-full flex flex-col flex-1">
|
||||||
|
<CollapsibleTrigger className={`flex items-center justify-between w-full mb-4 px-2 py-2 rounded-lg ${theme === 'dark' ? 'hover:bg-[#1a1a1a]' : 'hover:bg-sidebar-accent'}`}>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="font-medium tracking-wide">{t("settingPanel")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Settings className="h-4 w-4 mr-1" />
|
||||||
|
<ChevronDown className={`h-4 w-4 transition-transform duration-200 ${settingsPanelOpen ? 'rotate-180' : ''}`} />
|
||||||
|
</div>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
|
||||||
|
<CollapsibleContent className={`${theme === 'dark' ? 'bg-[#121212]' : 'bg-sidebar'} flex-1 flex flex-col`}>
|
||||||
|
<div className="max-h-[300px] overflow-y-auto custom-scrollbar relative pr-1">
|
||||||
|
<ScrollArea className="h-full">
|
||||||
|
<div className="space-y-2 pr-4">
|
||||||
|
{settingsMenuItems.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className={getMenuItemClasses(activeSettingsItem === item.id)}
|
||||||
|
onClick={(e) => {
|
||||||
|
handleMenuItemClick(`/settings?panel=${item.id}`, e);
|
||||||
|
handleSettingsItemClick(item.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<item.icon className="h-4 w-4 mr-2" />
|
||||||
|
<span className="text-sm">{t(item.translationKey)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
|
|
||||||
|
interface SidebarHeaderProps {
|
||||||
|
collapsed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SidebarHeader: React.FC<SidebarHeaderProps> = ({ collapsed }) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`p-4 ${theme === 'dark' ? 'border-[#1e1e1e]' : 'border-sidebar-border'} border-b flex items-center ${collapsed ? 'justify-center' : ''}`}>
|
||||||
|
<div className="h-8 w-8 bg-green-500 rounded flex items-center justify-center mr-2">
|
||||||
|
<span className="text-white font-bold">C</span>
|
||||||
|
</div>
|
||||||
|
{!collapsed && <h1 className="text-xl font-semibold">CheckCle App</h1>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export { SidebarHeader } from './SidebarHeader';
|
||||||
|
export { MainNavigation } from './MainNavigation';
|
||||||
|
export { MenuItem } from './MenuItem';
|
||||||
|
export { SettingsPanel } from './SettingsPanel';
|
||||||
|
export { mainMenuItems, settingsMenuItems } from './navigationData';
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
import { Globe, Boxes, Radar, Calendar, BarChart2, LineChart, FileText, Settings, User, Bell, Database, Info, BookOpen } from "lucide-react";
|
||||||
|
|
||||||
|
export const mainMenuItems = [
|
||||||
|
{
|
||||||
|
id: 'uptime-monitoring',
|
||||||
|
path: '/dashboard',
|
||||||
|
icon: Globe,
|
||||||
|
translationKey: 'uptimeMonitoring',
|
||||||
|
color: 'text-purple-400',
|
||||||
|
hasNavigation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'instance-monitoring',
|
||||||
|
path: null,
|
||||||
|
icon: Boxes,
|
||||||
|
translationKey: 'instanceMonitoring',
|
||||||
|
color: 'text-blue-400',
|
||||||
|
hasNavigation: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ssl-domain',
|
||||||
|
path: '/ssl-domain',
|
||||||
|
icon: Radar,
|
||||||
|
translationKey: 'sslDomain',
|
||||||
|
color: 'text-cyan-400',
|
||||||
|
hasNavigation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'schedule-incident',
|
||||||
|
path: '/schedule-incident',
|
||||||
|
icon: Calendar,
|
||||||
|
translationKey: 'scheduleIncident',
|
||||||
|
color: 'text-emerald-400',
|
||||||
|
hasNavigation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'operational-page',
|
||||||
|
path: '/operational-page',
|
||||||
|
icon: BarChart2,
|
||||||
|
translationKey: 'operationalPage',
|
||||||
|
color: 'text-amber-400',
|
||||||
|
hasNavigation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'reports',
|
||||||
|
path: null,
|
||||||
|
icon: LineChart,
|
||||||
|
translationKey: 'reports',
|
||||||
|
color: 'text-rose-400',
|
||||||
|
hasNavigation: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'api-documentation',
|
||||||
|
path: null,
|
||||||
|
icon: FileText,
|
||||||
|
translationKey: 'apiDocumentation',
|
||||||
|
color: 'text-indigo-400',
|
||||||
|
hasNavigation: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const settingsMenuItems = [
|
||||||
|
{
|
||||||
|
id: 'general',
|
||||||
|
icon: Settings,
|
||||||
|
translationKey: 'generalSettings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'users',
|
||||||
|
icon: User,
|
||||||
|
translationKey: 'userManagement'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notifications',
|
||||||
|
icon: Bell,
|
||||||
|
translationKey: 'notificationSettings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'templates',
|
||||||
|
icon: BookOpen,
|
||||||
|
translationKey: 'alertsTemplates'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'data-retention',
|
||||||
|
icon: Database,
|
||||||
|
translationKey: 'dataRetention'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'about',
|
||||||
|
icon: Info,
|
||||||
|
translationKey: 'aboutSystem'
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
interface SidebarContextType {
|
||||||
|
sidebarCollapsed: boolean;
|
||||||
|
setSidebarCollapsed: (collapsed: boolean) => void;
|
||||||
|
toggleSidebar: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SidebarContext = createContext<SidebarContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const SidebarProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||||
|
|
||||||
|
const toggleSidebar = () => {
|
||||||
|
setSidebarCollapsed(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
sidebarCollapsed,
|
||||||
|
setSidebarCollapsed,
|
||||||
|
toggleSidebar
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</SidebarContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSidebar = () => {
|
||||||
|
const context = useContext(SidebarContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useSidebar must be used within a SidebarProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Header } from "@/components/dashboard/Header";
|
import { Header } from "@/components/dashboard/Header";
|
||||||
import { Sidebar } from "@/components/dashboard/Sidebar";
|
import { Sidebar } from "@/components/dashboard/Sidebar";
|
||||||
@@ -8,11 +8,11 @@ import { serviceService } from "@/services/serviceService";
|
|||||||
import { authService } from "@/services/authService";
|
import { authService } from "@/services/authService";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { LoadingState } from "@/components/services/LoadingState";
|
import { LoadingState } from "@/components/services/LoadingState";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
const currentUser = authService.getCurrentUser();
|
const currentUser = authService.getCurrentUser();
|
||||||
@@ -71,4 +71,4 @@ const Dashboard = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dashboard;
|
export default Dashboard;
|
||||||
@@ -1,14 +1,29 @@
|
|||||||
// Update this page (the content is just a fallback if you fail to update the page)
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { authService } from '@/services/authService';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if user is authenticated and redirect accordingly
|
||||||
|
if (authService.isAuthenticated()) {
|
||||||
|
navigate('/dashboard', { replace: true });
|
||||||
|
} else {
|
||||||
|
navigate('/login', { replace: true });
|
||||||
|
}
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
|
// Show a loading state while redirecting
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-gray-100">
|
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-4xl font-bold mb-4">Welcome to Your Blank App</h1>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"></div>
|
||||||
<p className="text-xl text-gray-600">Start building your amazing project here!</p>
|
<p className="text-muted-foreground">Loading...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Index;
|
export default Index;
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { Header } from "@/components/dashboard/Header";
|
import { Header } from "@/components/dashboard/Header";
|
||||||
import { Sidebar } from "@/components/dashboard/Sidebar";
|
import { Sidebar } from "@/components/dashboard/Sidebar";
|
||||||
import { OperationalPageContent } from '@/components/operational-page/OperationalPageContent';
|
import { OperationalPageContent } from '@/components/operational-page/OperationalPageContent';
|
||||||
import { authService } from "@/services/authService";
|
import { authService } from "@/services/authService";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const OperationalPage = () => {
|
const OperationalPage = () => {
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
const currentUser = authService.getCurrentUser();
|
const currentUser = authService.getCurrentUser();
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import { ProfileContent } from "@/components/profile/ProfileContent";
|
|||||||
import { User } from "@/services/userService";
|
import { User } from "@/services/userService";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const Profile = () => {
|
const Profile = () => {
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
const currentUser = authService.getCurrentUser();
|
const currentUser = authService.getCurrentUser();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Header } from "@/components/dashboard/Header";
|
import { Header } from "@/components/dashboard/Header";
|
||||||
import { Sidebar } from "@/components/dashboard/Sidebar";
|
import { Sidebar } from "@/components/dashboard/Sidebar";
|
||||||
import { useTheme } from "@/contexts/ThemeContext";
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
@@ -8,11 +8,11 @@ import { ScheduleIncidentContent } from "@/components/schedule-incident/Schedule
|
|||||||
import { authService } from "@/services/authService";
|
import { authService } from "@/services/authService";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { initMaintenanceNotifications, stopMaintenanceNotifications } from "@/services/maintenance/maintenanceNotificationService";
|
import { initMaintenanceNotifications, stopMaintenanceNotifications } from "@/services/maintenance/maintenanceNotificationService";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const ScheduleIncident = () => {
|
const ScheduleIncident = () => {
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current theme and language
|
// Get current theme and language
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
@@ -24,12 +24,10 @@ const ScheduleIncident = () => {
|
|||||||
|
|
||||||
// Initialize maintenance notifications
|
// Initialize maintenance notifications
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Initializing maintenance notifications");
|
|
||||||
initMaintenanceNotifications();
|
initMaintenanceNotifications();
|
||||||
|
|
||||||
// Clean up on unmount
|
// Clean up on unmount
|
||||||
return () => {
|
return () => {
|
||||||
console.log("Stopping maintenance notifications");
|
|
||||||
stopMaintenanceNotifications();
|
stopMaintenanceNotifications();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import { NotificationSettings } from "@/components/settings/notification-setting
|
|||||||
import { AlertsTemplates } from "@/components/settings/alerts-templates";
|
import { AlertsTemplates } from "@/components/settings/alerts-templates";
|
||||||
import { AboutSystem } from "@/components/settings/about-system";
|
import { AboutSystem } from "@/components/settings/about-system";
|
||||||
import DataRetentionSettings from "@/components/settings/data-retention/DataRetentionSettings";
|
import DataRetentionSettings from "@/components/settings/data-retention/DataRetentionSettings";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
const currentUser = authService.getCurrentUser();
|
const currentUser = authService.getCurrentUser();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { authService } from "@/services/authService";
|
import { authService } from "@/services/authService";
|
||||||
@@ -8,14 +9,14 @@ import { SSLDomainContent } from "@/components/ssl-domain/SSLDomainContent";
|
|||||||
import { LoadingState } from "@/components/services/LoadingState";
|
import { LoadingState } from "@/components/services/LoadingState";
|
||||||
import { fetchSSLCertificates, shouldRunDailyCheck, checkAllCertificatesAndNotify } from "@/services/sslCertificateService";
|
import { fetchSSLCertificates, shouldRunDailyCheck, checkAllCertificatesAndNotify } from "@/services/sslCertificateService";
|
||||||
import { useLanguage } from "@/contexts/LanguageContext";
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
import { useSidebar } from "@/contexts/SidebarContext";
|
||||||
|
|
||||||
const SslDomain = () => {
|
const SslDomain = () => {
|
||||||
// Get language context for translations
|
// Get language context for translations
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
|
|
||||||
// State for sidebar collapse functionality
|
// Use shared sidebar state
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false);
|
const { sidebarCollapsed, toggleSidebar } = useSidebar();
|
||||||
const toggleSidebar = () => setSidebarCollapsed(prev => !prev);
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
const currentUser = authService.getCurrentUser();
|
const currentUser = authService.getCurrentUser();
|
||||||
|
|||||||
Reference in New Issue
Block a user