Skip to content

Commit c73013c

Browse files
committed
Implementaion of Features and Fixes
1 parent 6e2cdb0 commit c73013c

File tree

10 files changed

+497
-302
lines changed

10 files changed

+497
-302
lines changed

backend/requirements.txt

Lines changed: 232 additions & 232 deletions
Large diffs are not rendered by default.

docs/INSTALL_GUIDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ poetry --version # Should show v2.0.0 or above
3232
3. **Install Python dependencies**
3333
```sh
3434
poetry env use python3.10
35+
poetry lock --no-update
3536
poetry install --with dev
3637
```
3738

frontend/.env example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
VITE_SUPABASE_URL=YOUR SUPABASE URL
2-
2+
VITE_BASE_URL = http://localhost:5173/
33
VITE_SUPABASE_KEY=YOUR SUPABASE ANON KEY

frontend/src/App.tsx

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useState } from 'react';
22
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
33
import { AnimatePresence } from 'framer-motion';
4-
import { Toaster } from 'react-hot-toast';
4+
import toast, { Toaster } from 'react-hot-toast';
55
import Sidebar from './components/layout/Sidebar';
66
import Dashboard from './components/dashboard/Dashboard';
77
import BotIntegrationPage from './components/integration/BotIntegrationPage';
@@ -16,7 +16,7 @@ import ProfilePage from './components/pages/ProfilePage';
1616
import SignUpPage from './components/pages/SignUpPage';
1717
import { supabase } from './lib/supabaseClient';
1818
import ForgotPasswrdPage from './components/pages/ForgotPasswrdPage';
19-
import ResetPasswrdPage from './components/pages/ResetPasswrdPage';
19+
import ResetPasswordPage from './components/pages/ResetPasswordPage';
2020

2121
function App() {
2222
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
@@ -26,18 +26,62 @@ function App() {
2626

2727
//Auto login if user has already logged in
2828
useEffect(() => {
29-
supabase.auth.getSession().then(({ data }) => {
30-
if (data.session) {
31-
setIsAuthenticated(true);
29+
supabase.auth.getSession().then(({ data, error }) => {
30+
if (error) {
31+
toast.error('User Login Failed');
32+
console.error('Error checking session:', error);
33+
return;
3234
}
35+
setIsAuthenticated(!!data.session);
3336
});
37+
38+
const { data: subscription } = supabase.auth.onAuthStateChange(
39+
(event, session) => {
40+
console.log("Auth event:", event, session);
41+
switch(event){
42+
case "SIGNED_IN":
43+
setIsAuthenticated(true);
44+
toast.success("Signed in!");
45+
break;
46+
47+
case "SIGNED_OUT":
48+
setIsAuthenticated(false);
49+
setActivePage("landing");
50+
setRepoData(null);
51+
toast.success("Signed out!");
52+
break;
53+
54+
case "PASSWORD_RECOVERY":
55+
toast("Check your email to reset your password.");
56+
break;
57+
case "TOKEN_REFRESHED":
58+
console.log("Session refreshed");
59+
break;
60+
case "USER_UPDATED":
61+
console.log("User updated", session?.user);
62+
break;
63+
}
64+
}
65+
);
66+
67+
return () => {
68+
subscription.subscription.unsubscribe();
69+
};
3470
}, []);
3571

72+
3673
const handleLogin = () => {
3774
setIsAuthenticated(true);
3875
};
3976

40-
const handleLogout = () => {
77+
const handleLogout = async () => {
78+
const { error } = await supabase.auth.signOut();
79+
if (error) {
80+
toast.error('Logout failed');
81+
console.error('Error during logout:', error);
82+
return;
83+
}
84+
toast.success('Signed out!');
4185
setIsAuthenticated(false);
4286
setActivePage('landing');
4387
setRepoData(null);
@@ -62,7 +106,7 @@ function App() {
62106
case 'settings':
63107
return <SettingsPage />;
64108
case 'profile':
65-
return <ProfilePage />;
109+
return <ProfilePage onSignOut={handleLogout} />;
66110
default:
67111
return <Dashboard repoData={repoData} />;
68112
}
@@ -96,7 +140,7 @@ function App() {
96140
<Route
97141
path="/reset-password"
98142
element={
99-
<ResetPasswrdPage/>
143+
<ResetPasswordPage/>
100144
}
101145
/>
102146
<Route

frontend/src/components/pages/ForgotPasswrdPage.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState, ReactNode, FormEvent } from "react";
22
import { motion } from "framer-motion";
33
import { useNavigate } from 'react-router-dom';
4-
import { toast, Toaster } from "react-hot-toast";
4+
import { toast} from "react-hot-toast";
55
import { supabase } from "../../lib/supabaseClient";
66

77

@@ -54,25 +54,32 @@ export default function ForgotPasswrdPage() {
5454
const handleAuth = async (e: FormEvent<HTMLFormElement>) => {
5555
e.preventDefault();
5656
const formData = new FormData(e.currentTarget);
57-
const email = formData.get('email') as string;
58-
setIsLoading(true);
59-
const { error } = await supabase.auth.resetPasswordForEmail(email, {
60-
redirectTo: 'http://localhost:5173/reset-password',
61-
});
62-
console.log(error)
63-
setIsLoading(false);
64-
if (error) {
65-
toast.error(error.message || "An unknown error occurred!");
57+
const email = String(formData.get('email') || '').trim();
58+
if (!email) {
59+
toast.error("Please enter your email address.");
6660
return;
6761
}
68-
setMailPage(true);
62+
setIsLoading(true);
63+
try {
64+
const base = import.meta.env.VITE_BASE_URL || window.location.origin;
65+
const redirectTo = new URL('/reset-password', base).toString();
66+
const { error } = await supabase.auth.resetPasswordForEmail(email, { redirectTo });
67+
if (error) {
68+
console.error('resetPasswordForEmail failed', error);
69+
}
70+
setMailPage(true);
71+
} catch (err) {
72+
console.error('resetPasswordForEmail unexpected error', err);
73+
toast.error("Something went wrong. Please try again.");
74+
} finally {
75+
setIsLoading(false);
76+
}
6977
};
7078

7179
return (
7280
<AuthLayout>
7381

7482
<div className="bg-gray-900 p-8 rounded-xl border border-gray-800">
75-
<Toaster position="top-right" />
7683
{!mailPage ? (
7784
<>
7885
<motion.div
@@ -116,7 +123,7 @@ export default function ForgotPasswrdPage() {
116123
<p className="text-center text-gray-400 text-sm">
117124
<button
118125
type="button"
119-
onClick={() => navigate('/signup')}
126+
onClick={() => navigate('/login')}
120127
className="text-gray-400 hover:text-gray-300 font-medium"
121128
>
122129
Back to Sign In

frontend/src/components/pages/ProfilePage.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useState } from 'react';
22
import { motion } from 'framer-motion';
33
import { toast } from 'react-hot-toast';
4-
import { User, Mail, Building, Globe, Github, Twitter, Edit, Camera, Save } from 'lucide-react';
4+
import { User, Mail, Building, Globe, Github, Twitter, Edit, Camera, Save, DoorClosed } from 'lucide-react';
55

6-
const ProfilePage = () => {
6+
const ProfilePage = ({onSignOut}:{onSignOut:()=>void}) => {
77
const [isEditing, setIsEditing] = useState(false);
88
const [profile, setProfile] = useState({
99
name: 'Sarah Chen',
@@ -179,7 +179,19 @@ const ProfilePage = () => {
179179
className="w-full bg-gray-800 rounded-lg p-3 text-white focus:outline-none focus:ring-2 focus:ring-green-500 disabled:opacity-50"
180180
/>
181181
</div>
182-
182+
<div className="mt-8 flex justify-end">
183+
<motion.button
184+
whileHover={{ scale: 1.05 }}
185+
whileTap={{ scale: 0.95 }}
186+
onClick={() => {
187+
onSignOut();
188+
}}
189+
className="px-4 py-2 bg-red-500 hover:bg-red-600 rounded-lg transition-colors flex items-center"
190+
>
191+
<DoorClosed size={18} className="mr-2" />
192+
Sign Out
193+
</motion.button>
194+
</div>
183195
{isEditing && (
184196
<div className="mt-8 flex justify-end space-x-4">
185197
<motion.button

frontend/src/components/pages/ResetPasswrdPage.tsx renamed to frontend/src/components/pages/ResetPasswordPage.tsx

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { useState, ReactNode, FormEvent, useEffect } from "react";
1+
import { useState, useEffect } from "react";
2+
import type { ReactNode, FormEvent } from "react";
23
import { motion } from "framer-motion";
34
import { useNavigate } from 'react-router-dom';
4-
import { toast, Toaster } from "react-hot-toast";
5+
import { toast} from "react-hot-toast";
56
import { supabase } from "../../lib/supabaseClient";
67
import {
78
Settings,
@@ -46,7 +47,7 @@ const InputField = ({ icon: Icon, ...props }: InputFieldProps) => (
4647

4748

4849

49-
export default function ResetPasswrdPage() {
50+
export default function ResetPasswordPage() {
5051
const navigate = useNavigate();
5152
const [isLoading, setIsLoading] = useState<boolean>(false);
5253

@@ -57,20 +58,34 @@ export default function ResetPasswrdPage() {
5758
const accessToken = params.get('access_token');
5859
const refreshToken = params.get('refresh_token');
5960

61+
const clearUrlHash = () => {
62+
if (window.location.hash) {
63+
window.history.replaceState({}, document.title, window.location.pathname + window.location.search);
64+
}
65+
};
66+
6067
if (accessToken && refreshToken) {
61-
supabase.auth.setSession({
62-
access_token: accessToken,
63-
refresh_token: refreshToken,
64-
})
65-
.then(({ error }) => {
68+
(async () => {
69+
try {
70+
const { error } = await supabase.auth.setSession({
71+
access_token: accessToken,
72+
refresh_token: refreshToken,
73+
});
6674
if (error) {
67-
toast.error("Error setting session:" + error.message);
68-
navigate('/login');
75+
toast.error("Error setting session: " + error.message);
76+
navigate('/login', { replace: true });
6977
}
70-
});
78+
} catch {
79+
toast.error("Error setting session");
80+
navigate('/login', { replace: true });
81+
} finally {
82+
clearUrlHash();
83+
}
84+
})();
7185
} else {
72-
toast.error("Access Denied");
73-
navigate('/login');
86+
toast.error("Access denied");
87+
navigate('/login', { replace: true });
88+
clearUrlHash();
7489
}
7590
}, [navigate]);
7691

@@ -84,20 +99,44 @@ export default function ResetPasswrdPage() {
8499
toast.error("Passwords doesn't match. Try Again");
85100
return;
86101
}
87-
setIsLoading(true);
88-
const { error } = await supabase.auth.updateUser({ password: password })
89-
setIsLoading(false);
90-
if (error) {
91-
toast.error(error.message || "An unknown error occurred!");
102+
if (password.length < 8) {
103+
toast.error("Password must be at least 8 characters long.");
92104
return;
93105
}
94-
navigate("/");
95-
};
96-
106+
if (!/[A-Z]/.test(password)) {
107+
toast.error("Password must contain at least one uppercase letter.");
108+
return;
109+
}
110+
if (!/[a-z]/.test(password)) {
111+
toast.error("Password must contain at least one lowercase letter.");
112+
return;
113+
}
114+
if (!/[0-9]/.test(password)) {
115+
toast.error("Password must contain at least one number.");
116+
return;
117+
}
118+
if (!/[^A-Za-z0-9]/.test(password)) {
119+
toast.error("Password must contain at least one special character.");
120+
return;
121+
}
122+
setIsLoading(true);
123+
try {
124+
const { error } = await supabase.auth.updateUser({ password });
125+
if (error) {
126+
toast.error(error.message || "An unknown error occurred!");
127+
return;
128+
}
129+
toast.success("Password updated successfully.");
130+
navigate("/");
131+
} catch {
132+
toast.error("Unexpected error updating password.");
133+
} finally {
134+
setIsLoading(false);
135+
}
136+
}
97137
return (
98138
<AuthLayout>
99139
<div className="bg-gray-900 p-8 rounded-xl border border-gray-800">
100-
<Toaster position="top-right" />
101140
<>
102141
<motion.div
103142
initial={{ opacity: 0 }}
@@ -148,7 +187,7 @@ export default function ResetPasswrdPage() {
148187
<p className="text-center text-gray-400 text-sm">
149188
<button
150189
type="button"
151-
onClick={() => navigate('/signup')}
190+
onClick={() => navigate('/login')}
152191
className="text-gray-400 hover:text-gray-300 font-medium"
153192
>
154193
Back to Sign In

0 commit comments

Comments
 (0)