From 0ba95b0dab50f47414d45290e0e9d8ea91aab551 Mon Sep 17 00:00:00 2001 From: rishi Date: Sun, 1 Dec 2024 16:59:07 +0000 Subject: [PATCH] Basic home, Login, and Auth in client --- client/src/App.jsx | 81 +++++++++++++++---------- client/src/components/Login.jsx | 52 ++++++++++++++++ client/src/contexts/AuthContext.jsx | 46 +++++++++++++++ client/src/main.jsx | 15 ++--- client/src/styles/main.scss | 92 +++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 39 deletions(-) create mode 100644 client/src/components/Login.jsx create mode 100644 client/src/contexts/AuthContext.jsx create mode 100644 client/src/styles/main.scss diff --git a/client/src/App.jsx b/client/src/App.jsx index f67355a..ed66208 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,35 +1,52 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +// src/App.jsx +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import { AuthProvider, useAuth } from './contexts/AuthContext'; +import Login from './components/Login'; +import Dashboard from './components/Dashboard'; +import Editor from './components/Editor'; -function App() { - const [count, setCount] = useState(0) - - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) +function PrivateRoute({ children }) { + const { isAuthenticated } = useAuth(); + return isAuthenticated ? children : ; } -export default App +function App() { + return ( + + +
+ + } /> + + + + } + /> + + + + } + /> + + + + } + /> + } /> + +
+
+
+ ); +} + +export default App; \ No newline at end of file diff --git a/client/src/components/Login.jsx b/client/src/components/Login.jsx new file mode 100644 index 0000000..69c0cb4 --- /dev/null +++ b/client/src/components/Login.jsx @@ -0,0 +1,52 @@ +// src/components/Login.jsx +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '../contexts/AuthContext'; + +export default function Login() { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const { login } = useAuth(); + const navigate = useNavigate(); + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(''); + + const success = await login(username, password); + if (success) { + navigate('/dashboard'); + } else { + setError('Invalid credentials'); + } + }; + + return ( +
+
+

Login

+ {error &&
{error}
} +
+ + setUsername(e.target.value)} + required + /> +
+
+ + setPassword(e.target.value)} + required + /> +
+ +
+
+ ); +} \ No newline at end of file diff --git a/client/src/contexts/AuthContext.jsx b/client/src/contexts/AuthContext.jsx new file mode 100644 index 0000000..45617e5 --- /dev/null +++ b/client/src/contexts/AuthContext.jsx @@ -0,0 +1,46 @@ +// src/contexts/AuthContext.jsx +import { createContext, useContext, useState, useEffect } from 'react'; +import axios from 'axios'; + +const AuthContext = createContext(); + +export function AuthProvider({ children }) { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const token = localStorage.getItem('token'); + setIsAuthenticated(!!token); + setIsLoading(false); + }, []); + + const login = async (username, password) => { + try { + const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/auth/login`, { + username, + password + }); + localStorage.setItem('token', response.data.token); + setIsAuthenticated(true); + return true; + } catch (error) { + console.error('Login error:', error); + return false; + } + }; + + const logout = () => { + localStorage.removeItem('token'); + setIsAuthenticated(false); + }; + + return ( + + {!isLoading && children} + + ); +} + +export function useAuth() { + return useContext(AuthContext); +} \ No newline at end of file diff --git a/client/src/main.jsx b/client/src/main.jsx index b9a1a6d..dca82ab 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,10 +1,11 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' +// src/main.jsx +import React from 'react' +import ReactDOM from 'react-dom/client' import App from './App.jsx' +import './styles/main.scss' -createRoot(document.getElementById('root')).render( - +ReactDOM.createRoot(document.getElementById('root')).render( + - , -) + , +) \ No newline at end of file diff --git a/client/src/styles/main.scss b/client/src/styles/main.scss new file mode 100644 index 0000000..8919b99 --- /dev/null +++ b/client/src/styles/main.scss @@ -0,0 +1,92 @@ +// src/styles/main.scss +$primary-color: #2c3e50; +$secondary-color: #3498db; +$background-color: #f5f6fa; +$text-color: #2c3e50; +$border-color: #dcdde1; +$error-color: #e74c3c; + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + background-color: $background-color; + color: $text-color; + line-height: 1.5; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.login-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + padding: 20px; +} + +.login-form { + background: white; + padding: 30px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 400px; + + h2 { + margin: 0 0 20px; + color: $primary-color; + text-align: center; + } + + .error { + background: lighten($error-color, 35%); + color: $error-color; + padding: 10px; + border-radius: 4px; + margin-bottom: 20px; + text-align: center; + } +} + +.form-group { + margin-bottom: 20px; + + label { + display: block; + margin-bottom: 5px; + color: $primary-color; + } + + input { + width: 100%; + padding: 8px; + border: 1px solid $border-color; + border-radius: 4px; + font-size: 16px; + + &:focus { + outline: none; + border-color: $secondary-color; + } + } +} + +.button { + width: 100%; + padding: 10px; + background: $primary-color; + color: white; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background: darken($primary-color, 10%); + } +} \ No newline at end of file