Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 36 additions & 51 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { AnimatePresence } from 'framer-motion';
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { Toaster } from 'react-hot-toast';
import Sidebar from './components/layout/Sidebar';
import Dashboard from './components/dashboard/Dashboard';
Expand All @@ -13,10 +12,10 @@ import SupportPage from './components/pages/SupportPage';
import LandingPage from './components/landing/LandingPage';
import LoginPage from './components/pages/LoginPage';
import ProfilePage from './components/pages/ProfilePage';
import { AnimatePresence } from 'framer-motion';

function App() {
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [activePage, setActivePage] = useState('landing'); // Default to landing page
const [repoData, setRepoData] = useState<any>(null); // Store fetched repo stats
const [isAuthenticated, setIsAuthenticated] = useState(false);

Expand All @@ -36,34 +35,29 @@ function App() {
const handleLogout = () => {
setIsAuthenticated(false);
localStorage.removeItem('isAuthenticated');
setActivePage('landing');
setRepoData(null);
};

const renderPage = () => {
switch (activePage) {
case 'landing':
return <LandingPage setRepoData={setRepoData} setActivePage={setActivePage} />;
case 'dashboard':
return <Dashboard repoData={repoData} />;
case 'integration':
return <BotIntegrationPage />;
case 'contributors':
return <ContributorsPage repoData={repoData} />;
case 'analytics':
return <AnalyticsPage repoData={repoData} />;
case 'prs':
return <PullRequestsPage repoData={repoData} />;
case 'support':
return <SupportPage />;
case 'settings':
return <SettingsPage />;
case 'profile':
return <ProfilePage />;
default:
return <Dashboard repoData={repoData} />;
}
};
const ProtectedLayout = () => (
<div className="flex">
<Sidebar
isOpen={isSidebarOpen}
setIsOpen={setIsSidebarOpen}
onLogout={handleLogout}
/>
<main
className={`transition-all duration-300 flex-1 ${
isSidebarOpen ? 'ml-64' : 'ml-20'
}`}
>
<div className="p-8">
<AnimatePresence mode="wait">
<Outlet />
</AnimatePresence>
</div>
</main>
</div>
);

return (
<Router>
Expand All @@ -81,35 +75,26 @@ function App() {
}
/>
<Route
path="/*"
path="/"
element={
isAuthenticated ? (
<div className="flex">
<Sidebar
isOpen={isSidebarOpen}
setIsOpen={setIsSidebarOpen}
activePage={activePage}
setActivePage={setActivePage}
/>
<main
className={`transition-all duration-300 flex-1 ${
isSidebarOpen ? 'ml-64' : 'ml-20'
}`}
>
<div className="p-8">
<AnimatePresence mode="wait">{renderPage()}</AnimatePresence>
</div>
</main>
</div>
) : (
<Navigate to="/login" replace />
)
isAuthenticated ? <ProtectedLayout /> : <Navigate to="/login" replace />
}
/>
>
<Route index element={<LandingPage setRepoData={setRepoData} />} />
<Route path="dashboard" element={<Dashboard repoData={repoData} />} />
<Route path="integration" element={<BotIntegrationPage />} />
<Route path="contributors" element={<ContributorsPage repoData={repoData} />} />
<Route path="analytics" element={<AnalyticsPage repoData={repoData} />} />
<Route path="prs" element={<PullRequestsPage repoData={repoData} />} />
<Route path="support" element={<SupportPage />} />
<Route path="settings" element={<SettingsPage />} />
<Route path="profile" element={<ProfilePage />} />
</Route>
</Routes>
</div>
</Router>
);
}

export default App;

82 changes: 48 additions & 34 deletions frontend/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,72 @@
import React from 'react';
import { NavLink } from 'react-router-dom';
import {
LayoutDashboard,
Bot,
Github,
MessageSquare,
Users,
Activity,
GitPullRequest,
MessageCircleQuestion,
Menu,
Settings,
User
User,
LogOut
} from 'lucide-react';

interface SidebarProps {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
activePage: string;
setActivePage: (page: string) => void;
onLogout: () => void;
}

const Sidebar: React.FC<SidebarProps> = ({ isOpen, setIsOpen, activePage, setActivePage }) => (
<div className={`fixed top-0 left-0 h-full bg-gray-900 text-white transition-all duration-300 ease-in-out ${isOpen ? 'w-64' : 'w-20'}`}>
<div className="p-4 flex items-center justify-between">
<h2 className={`font-bold text-xl ${!isOpen && 'hidden'}`}>Devr.AI</h2>
<button onClick={() => setIsOpen(!isOpen)} className="p-2 hover:bg-gray-800 rounded-lg">
<Menu size={20} />
</button>
const navItems = [
{ icon: <LayoutDashboard size={20} />, label: 'Dashboard', path: '/dashboard' },
{ icon: <Bot size={20} />, label: 'Bot Integration', path: '/integration' },
{ icon: <Users size={20} />, label: 'Contributors', path: '/contributors' },
{ icon: <Activity size={20} />, label: 'Analytics', path: '/analytics' },
{ icon: <GitPullRequest size={20} />, label: 'Pull Requests', path: '/prs' },
{ icon: <MessageCircleQuestion size={20} />, label: 'Support', path: '/support' },
{ icon: <Settings size={20} />, label: 'Settings', path: '/settings' },
{ icon: <User size={20} />, label: 'Profile', path: '/profile' },
];

const Sidebar: React.FC<SidebarProps> = ({ isOpen, setIsOpen, onLogout }) => (
<div className={`fixed top-0 left-0 h-full bg-gray-900 text-white transition-all duration-300 ease-in-out ${isOpen ? 'w-64' : 'w-20'} flex flex-col`}>
<div>
<div className="p-4 flex items-center justify-between">
<h2 className={`font-bold text-xl ${!isOpen && 'hidden'}`}>Devr.AI</h2>
<button onClick={() => setIsOpen(!isOpen)} className="p-2 hover:bg-gray-800 rounded-lg">
<Menu size={20} />
</button>
</div>

<nav className="mt-8">
{navItems.map((item) => (
<NavLink
key={item.path}
to={item.path}
className={({ isActive }) =>
`w-full flex items-center px-4 py-3 text-gray-300 hover:bg-gray-800 hover:text-green-400 transition-colors ${
isActive ? 'bg-gray-800 text-green-400' : ''
}`
}
>
{item.icon}
<span className={`ml-4 ${!isOpen && 'hidden'}`}>{item.label}</span>
</NavLink>
))}
</nav>
</div>

<nav className="mt-8">
{[
{ icon: <LayoutDashboard size={20} />, label: 'Dashboard', id: 'dashboard' },
{ icon: <Bot size={20} />, label: 'Bot Integration', id: 'integration' },
{ icon: <Users size={20} />, label: 'Contributors', id: 'contributors' },
{ icon: <Activity size={20} />, label: 'Analytics', id: 'analytics' },
{ icon: <GitPullRequest size={20} />, label: 'Pull Requests', id: 'prs' },
{ icon: <MessageCircleQuestion size={20} />, label: 'Support', id: 'support' },
{ icon: <Settings size={20} />, label: 'Settings', id: 'settings' },
{ icon: <User size={20} />, label: 'Profile', id: 'profile' },
].map((item) => (
<div className="mt-auto p-4">
<button
key={item.id}
onClick={() => setActivePage(item.id)}
className={`w-full flex items-center px-4 py-3 text-gray-300 hover:bg-gray-800 hover:text-green-400 transition-colors ${
activePage === item.id ? 'bg-gray-800 text-green-400' : ''
}`}
onClick={onLogout}
className={`w-full flex items-center px-4 py-3 text-gray-300 hover:bg-gray-800 hover:text-red-500 transition-colors`}
>
{item.icon}
<span className={`ml-4 ${!isOpen && 'hidden'}`}>{item.label}</span>
<LogOut size={20} />
<span className={`ml-4 ${!isOpen && 'hidden'}`}>Logout</span>
</button>
))}
</nav>
</div>
</div>
);

export default Sidebar;
export default Sidebar;