Skip to content

Commit eb48e20

Browse files
committed
Add Login and Profile Page
1 parent 8d8e646 commit eb48e20

File tree

6 files changed

+484
-21
lines changed

6 files changed

+484
-21
lines changed

frontend/package-lock.json

Lines changed: 68 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"react": "^18.3.1",
1818
"react-dom": "^18.3.1",
1919
"react-hot-toast": "^2.5.2",
20+
"react-router-dom": "^7.3.0",
2021
"recharts": "^2.15.1",
2122
"tailwind-merge": "^3.0.2"
2223
},

frontend/src/App.tsx

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { useState } from 'react';
1+
import React, { useState,useEffect } from 'react';
22
import { AnimatePresence } from 'framer-motion';
3-
3+
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
44
import Sidebar from './components/layout/Sidebar';
55
import Dashboard from './components/dashboard/Dashboard';
66
import BotIntegrationPage from './components/integration/BotIntegrationPage';
@@ -10,12 +10,32 @@ import SettingsPage from './components/pages/SettingsPage';
1010
import AnalyticsPage from './components/pages/AnalyticsPage';
1111
import SupportPage from './components/pages/SupportPage';
1212
import LandingPage from './components/landing/LandingPage';
13+
import LoginPage from './components/pages/LoginPage';
14+
import ProfilePage from './components/pages/ProfilePage';
1315

1416
function App() {
1517
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
1618
const [activePage, setActivePage] = useState('landing'); // Default to landing page
1719
const [repoData, setRepoData] = useState<any>(null); // Store fetched repo stats
20+
const [isAuthenticated, setIsAuthenticated] = useState(false);
21+
22+
useEffect(() => {
23+
const user = localStorage.getItem('isAuthenticated');
24+
if (user === 'true') {
25+
setIsAuthenticated(true);
26+
}
27+
}, []);
1828

29+
const handleLogin = () => {
30+
setIsAuthenticated(true);
31+
localStorage.setItem('isAuthenticated', 'true');
32+
};
33+
34+
// Function to handle logout
35+
const handleLogout = () => {
36+
setIsAuthenticated(false);
37+
localStorage.removeItem('isAuthenticated');
38+
};
1939
const renderPage = () => {
2040
switch (activePage) {
2141
case 'landing':
@@ -34,30 +54,43 @@ function App() {
3454
return <SupportPage />;
3555
case 'settings':
3656
return <SettingsPage />;
57+
case 'profile':
58+
return <ProfilePage />;
3759
default:
3860
return <Dashboard repoData={repoData} />;
3961
}
4062
};
4163

4264
return (
43-
<div className="min-h-screen bg-gray-950 text-white">
44-
{activePage !== 'landing' && (
45-
<Sidebar
46-
isOpen={isSidebarOpen}
47-
setIsOpen={setIsSidebarOpen}
48-
activePage={activePage}
49-
setActivePage={setActivePage}
50-
/>
51-
)}
52-
53-
<main className={`transition-all duration-300 ${isSidebarOpen && activePage !== 'landing' ? 'ml-64' : 'ml-20'}`}>
54-
<div className="p-8">
55-
<AnimatePresence mode="wait">
56-
{renderPage()}
57-
</AnimatePresence>
58-
</div>
59-
</main>
60-
</div>
65+
<Router>
66+
<div className="min-h-screen bg-gray-950 text-white">
67+
<Routes>
68+
{/* If not logged in, redirect all routes to login */}
69+
<Route
70+
path="/login"
71+
element={<LoginPage />}
72+
/>
73+
<Route
74+
path="/"
75+
element={
76+
<>
77+
<Sidebar
78+
isOpen={isSidebarOpen}
79+
setIsOpen={setIsSidebarOpen}
80+
activePage={activePage}
81+
setActivePage={setActivePage}
82+
/>
83+
<main className={`transition-all duration-300 ${isSidebarOpen ? 'ml-64' : 'ml-20'}`}>
84+
<div className="p-8">
85+
<AnimatePresence mode="wait">{renderPage()}</AnimatePresence>
86+
</div>
87+
</main>
88+
</>
89+
}
90+
/>
91+
</Routes>
92+
</div>
93+
</Router>
6194
);
6295
}
6396

frontend/src/components/layout/Sidebar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
GitPullRequest,
1010
MessageCircleQuestion,
1111
Menu,
12-
Settings
12+
Settings,
13+
User
1314
} from 'lucide-react';
1415

1516
interface SidebarProps {
@@ -37,6 +38,7 @@ const Sidebar: React.FC<SidebarProps> = ({ isOpen, setIsOpen, activePage, setAct
3738
{ icon: <GitPullRequest size={20} />, label: 'Pull Requests', id: 'prs' },
3839
{ icon: <MessageCircleQuestion size={20} />, label: 'Support', id: 'support' },
3940
{ icon: <Settings size={20} />, label: 'Settings', id: 'settings' },
41+
{ icon: <User size={20} />, label: 'Profile', id: 'profile' },
4042
].map((item) => (
4143
<button
4244
key={item.id}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import React, { useState } from "react";
2+
import { motion } from "framer-motion";
3+
import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
4+
import { toast } from "react-hot-toast";
5+
import {
6+
LayoutDashboard,
7+
Bot,
8+
Github,
9+
MessageSquare,
10+
Slack,
11+
Users,
12+
Activity,
13+
GitPullRequest,
14+
MessageCircleQuestion,
15+
Menu,
16+
ChevronRight,
17+
Settings,
18+
Shield,
19+
KeyRound,
20+
Mail,
21+
Lock,
22+
User,
23+
LogIn,
24+
UserPlus
25+
} from 'lucide-react';
26+
27+
export default function LoginPage () {
28+
const navigate = useNavigate();
29+
const [isLoading, setIsLoading] = useState(false);
30+
31+
const handleLogin = async (e:any) => {
32+
e.preventDefault();
33+
setIsLoading(true);
34+
35+
// Simulate API call
36+
await new Promise(resolve => setTimeout(resolve, 1500));
37+
38+
setIsLoading(false);
39+
toast.success('Successfully logged in!');
40+
navigate('/');
41+
};
42+
const AuthLayout = ({ children }:any) => (
43+
<div className="min-h-screen bg-gray-950 flex items-center justify-center p-4">
44+
<motion.div
45+
initial={{ opacity: 0, y: 20 }}
46+
animate={{ opacity: 1, y: 0 }}
47+
exit={{ opacity: 0, y: -20 }}
48+
className="w-full max-w-md"
49+
>
50+
{children}
51+
</motion.div>
52+
</div>
53+
);
54+
55+
const InputField = ({ icon: Icon, ...props }:any) => (
56+
<div className="relative">
57+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
58+
<Icon className="h-5 w-5 text-gray-400" />
59+
</div>
60+
<input
61+
{...props}
62+
className="block w-full pl-10 pr-3 py-2 border border-gray-800 rounded-lg bg-gray-900 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
63+
/>
64+
</div>
65+
);
66+
67+
68+
return (
69+
<AuthLayout>
70+
<div className="bg-gray-900 p-8 rounded-xl border border-gray-800">
71+
<motion.div
72+
initial={{ opacity: 0 }}
73+
animate={{ opacity: 1 }}
74+
className="text-center mb-8"
75+
>
76+
<h1 className="text-3xl font-bold text-white mb-2">Welcome back</h1>
77+
<p className="text-gray-400">Sign in to your account</p>
78+
</motion.div>
79+
80+
<form onSubmit={handleLogin} className="space-y-6">
81+
<div className="space-y-4">
82+
<InputField
83+
icon={Mail}
84+
type="email"
85+
placeholder="Email address"
86+
required
87+
/>
88+
<InputField
89+
icon={Lock}
90+
type="password"
91+
placeholder="Password"
92+
required
93+
/>
94+
</div>
95+
96+
<div className="flex items-center justify-between text-sm">
97+
<label className="flex items-center">
98+
<input type="checkbox" className="rounded bg-gray-800 border-gray-700 text-green-500 focus:ring-green-500" />
99+
<span className="ml-2 text-gray-300">Remember me</span>
100+
</label>
101+
<button
102+
type="button"
103+
onClick={() => toast.success('Reset link sent!')}
104+
className="text-green-400 hover:text-green-300"
105+
>
106+
Forgot password?
107+
</button>
108+
</div>
109+
110+
<motion.button
111+
whileHover={{ scale: 1.02 }}
112+
whileTap={{ scale: 0.98 }}
113+
type="submit"
114+
disabled={isLoading}
115+
className="w-full py-3 bg-green-500 hover:bg-green-600 text-white rounded-lg font-medium transition-colors flex items-center justify-center"
116+
>
117+
{isLoading ? (
118+
<motion.div
119+
animate={{ rotate: 360 }}
120+
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
121+
>
122+
<Settings size={20} />
123+
</motion.div>
124+
) : (
125+
<>
126+
<LogIn size={20} className="mr-2" />
127+
Sign In
128+
</>
129+
)}
130+
</motion.button>
131+
132+
<p className="text-center text-gray-400 text-sm">
133+
Don't have an account?{' '}
134+
<button
135+
type="button"
136+
onClick={() => navigate('/signup')}
137+
className="text-green-400 hover:text-green-300 font-medium"
138+
>
139+
Sign up
140+
</button>
141+
</p>
142+
</form>
143+
</div>
144+
</AuthLayout>
145+
);
146+
};
147+
148+
149+
150+

0 commit comments

Comments
 (0)