diff --git a/package-lock.json b/package-lock.json index 78daeff22..4ab9aedd6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "tessera", "version": "0.0.0", "dependencies": { + "@supabase/supabase-js": "^2.49.1", "@tailwindcss/vite": "^4.0.9", "axios": "^1.8.1", "firebase": "^11.3.1", @@ -2078,6 +2079,80 @@ "win32" ] }, + "node_modules/@supabase/auth-js": { + "version": "2.68.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.68.0.tgz", + "integrity": "sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.2.tgz", + "integrity": "sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz", + "integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.18.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.49.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.1.tgz", + "integrity": "sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.68.0", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.2", + "@supabase/realtime-js": "2.11.2", + "@supabase/storage-js": "2.7.1" + } + }, "node_modules/@tailwindcss/node": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.9.tgz", @@ -2357,6 +2432,12 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.0.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", @@ -2377,6 +2458,15 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.24.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", @@ -4835,6 +4925,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", @@ -5474,6 +5570,12 @@ "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", "license": "Apache-2.0" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -5497,6 +5599,16 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5540,6 +5652,27 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index a6eecced8..8f02bcb4f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@supabase/supabase-js": "^2.49.1", "@tailwindcss/vite": "^4.0.9", "axios": "^1.8.1", "firebase": "^11.3.1", diff --git a/src/App.tsx b/src/App.tsx index 00fbcc817..e88a556a8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,37 +1,31 @@ import { useContext } from 'react'; import { BrowserRouter as Router, Route, Routes, } from 'react-router-dom'; import NavBar from './components/Navbar'; - -import { UserContext } from './UserContext'; +import Register from './pages/Register'; +import Login from "./pages/Login"; +import PrivateRoute from "./components/PrivateRoute"; function App() { - const userContext = useContext(UserContext); - if (!userContext) { - return
Error: UserContext is not provided
; - } - const { user, login, logout } = userContext; + + // const { user, login, logout } = userContext; return (
- -
- -
- {/*
- {user ? ( -
-

Welcome, {user.name}

- -
- ) : ( - - )} -
*/} -
+ + + } /> + } /> + + + + } + /> - + +
); } diff --git a/src/UserContext.tsx b/src/UserContext.tsx index a09306976..5e05c82d5 100644 --- a/src/UserContext.tsx +++ b/src/UserContext.tsx @@ -1,60 +1,60 @@ -import { createContext, useState, useEffect, ReactNode } from 'react'; -import { signInWithGoogle, signOutFromGoogle } from './firebaseConfig'; - -interface User { - id: string; - name: string; - email: string; -} - -interface UserContextType { - user: User | null; - login: () => void; - logout: () => void; -} - -const UserContext = createContext(undefined); - -const UserProvider = ({ children }: { children: ReactNode }) => { - const [user, setUser] = useState(null); - - useEffect(() => { - const storedUser = localStorage.getItem('user'); - if (storedUser) { - setUser(JSON.parse(storedUser)); - } - }, []); - - const login = async () => { - try { - const result = await signInWithGoogle(); - const userData = { - id: result.user.uid, - name: result.user.displayName || '', - email: result.user.email || '' - }; - setUser(userData); - localStorage.setItem('user', JSON.stringify(userData)); - } catch (error) { - console.error('Error logging in with Google:', error); - } - }; - - const logout = async () => { - try { - await signOutFromGoogle(); - setUser(null); - localStorage.removeItem('user'); - } catch (error) { - console.error('Error logging out:', error); - } - }; - - return ( - - {children} - - ); -}; - -export { UserContext, UserProvider }; \ No newline at end of file +// import { createContext, useState, useEffect, ReactNode } from 'react'; + + +// interface User { +// id: string; +// name: string; +// email: string; +// } + +// interface UserContextType { +// user: User | null; +// login: () => void; +// logout: () => void; +// } + +// const UserContext = createContext(undefined); + +// const UserProvider = ({ children }: { children: ReactNode }) => { +// const [user, setUser] = useState(null); + +// useEffect(() => { +// const storedUser = localStorage.getItem('user'); +// if (storedUser) { +// setUser(JSON.parse(storedUser)); +// } +// }, []); + +// const login = async () => { +// try { +// const result = await signInWithGoogle(); +// const userData = { +// id: result.user.uid, +// name: result.user.displayName || '', +// email: result.user.email || '' +// }; +// setUser(userData); +// localStorage.setItem('user', JSON.stringify(userData)); +// } catch (error) { +// console.error('Error logging in with Google:', error); +// } +// }; + +// const logout = async () => { +// try { +// await signOutFromGoogle(); +// setUser(null); +// localStorage.removeItem('user'); +// } catch (error) { +// console.error('Error logging out:', error); +// } +// }; + +// return ( +// +// {children} +// +// ); +// }; + +// export { UserContext, UserProvider }; \ No newline at end of file diff --git a/src/assets/bird.jpeg b/src/assets/bird.jpeg new file mode 100644 index 000000000..283bc40f9 Binary files /dev/null and b/src/assets/bird.jpeg differ diff --git a/src/components/PrivateRoute.tsx b/src/components/PrivateRoute.tsx new file mode 100644 index 000000000..630cbb5c2 --- /dev/null +++ b/src/components/PrivateRoute.tsx @@ -0,0 +1,26 @@ +import { JSX, useEffect, useState } from "react"; +import { Navigate } from "react-router-dom"; +import { supabase } from "../supabaseClient"; + +function PrivateRoute({ children }: { children: JSX.Element }) { + const [authenticated, setAuthenticated] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const checkSession = async () => { + const { data: { user } } = await supabase.auth.getUser(); + setAuthenticated(!!user); + setLoading(false); + }; + + checkSession(); + }, []); + + if (loading) { + return
Loading...
; + } + + return authenticated ? children : ; +} + +export default PrivateRoute; \ No newline at end of file diff --git a/src/firebaseConfig.ts b/src/firebaseConfig.ts deleted file mode 100644 index 5ac1555a2..000000000 --- a/src/firebaseConfig.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { initializeApp } from 'firebase/app'; -import { getAuth, GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth'; - -const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID -}; - -const app = initializeApp(firebaseConfig); -const auth = getAuth(app); -const provider = new GoogleAuthProvider(); - -const signInWithGoogle = () => { - return signInWithPopup(auth, provider); -}; - -const signOutFromGoogle = () => { - return signOut(auth); -}; - -export { auth, signInWithGoogle, signOutFromGoogle }; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 0fbb6684b..12e8f1dd5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,14 +4,16 @@ import './styles/index.css'; import './styles/App.css'; import './styles/styles.css'; import App from './App'; -import { UserProvider } from './UserContext'; + +import Register from '../src/pages/Register'; +import { BrowserRouter } from 'react-router-dom'; createRoot(document.getElementById('root')!).render( - - + {/* */} + - + ); \ No newline at end of file diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx new file mode 100644 index 000000000..d378d96fe --- /dev/null +++ b/src/pages/Login.tsx @@ -0,0 +1,61 @@ +import React, { useState } from "react"; +import { supabase } from "../supabaseClient"; +import { Link,useNavigate } from "react-router-dom"; + +function Login() { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + const navigate = useNavigate(); + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setMessage(""); + + const { data, error } = await supabase.auth.signInWithPassword({ + email: email, + password: password, + }); + + if (error) { + setMessage(error.message); + return; + } + + if (data) { + navigate("/Home"); + return null; + } + + setEmail(""); + setPassword(""); + }; + + return ( +
+

Register

+

+ {message && {message}} +
+ setEmail(e.target.value)} + value={email} + type="email" + placeholder="Email" + required + /> + setPassword(e.target.value)} + value={password} + type="password" + placeholder="Password" + required + /> + +
+ Don't have an account? + Create an Account. +
+ ); +} + +export default Login \ No newline at end of file diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx new file mode 100644 index 000000000..6ce91e69d --- /dev/null +++ b/src/pages/Register.tsx @@ -0,0 +1,96 @@ +import React, { useState } from "react"; +import { supabase } from "../supabaseClient"; +import { Link } from "react-router-dom"; +import bird from "../assets/bird.jpeg" + +function Register() { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + const [full_name,setName] = useState(""); + + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setMessage(""); + + const { data, error } = await supabase.auth.signUp({ + email: email, + password: password, + options:{ + data:{ + full_name :full_name, + + }, + }, + + }); + + if (error) { + setMessage(error.message); + return; + } + + if (data) { + setMessage("User account created!"); + } + + setEmail(""); + setPassword(""); + setName(""); + + }; + + return ( +
+ +
+ placeholder +
+ +
+ +

+ {message && {message}} +
+

Register

+ setEmail(e.target.value)} + value={email} + type="email" + placeholder="Email" + required + /> + + setPassword(e.target.value)} + value={password} + type="password" + placeholder="Password" + required + /> + + setName(e.target.value)} + value = {full_name} + type = "Name" + placeholder="Name" + required + /> + + + + +
+ Already have an account? + Log in. +
+ +
+ ); +} + +export default Register; \ No newline at end of file diff --git a/src/supabaseClient.ts b/src/supabaseClient.ts new file mode 100644 index 000000000..f8a66f0f4 --- /dev/null +++ b/src/supabaseClient.ts @@ -0,0 +1,6 @@ +import { createClient } from "@supabase/supabase-js"; + +const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL!; +const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY!; + +export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);