diff --git a/backend/api/user_login.py b/backend/api/user_login.py
index 1a8becb..4cd8ff5 100644
--- a/backend/api/user_login.py
+++ b/backend/api/user_login.py
@@ -18,7 +18,9 @@ class LoginView(APIView):
user = authenticate(username=username, password=password)
if user:
token, _ = Token.objects.get_or_create(user=user)
- return Response({"token": token.key})
+ response = Response({"token": token.key}, status=status.HTTP_200_OK)
+ response.set_cookie("token", token.key)
+ return response
else:
return Response(
{"error": "Invalid credentials"},
diff --git a/backend/peer_notes/settings.py b/backend/peer_notes/settings.py
index 474b470..2418f69 100644
--- a/backend/peer_notes/settings.py
+++ b/backend/peer_notes/settings.py
@@ -134,3 +134,7 @@ STATIC_URL = "static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = "api.PeerUser"
CORS_ALLOW_ALL_ORIGINS = True
+# CORS_ALLOWED_ORIGINS = [
+# "http://localhost:5173"
+# ]
+CORS_ALLOW_CREDENTIALS = True
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index d74e05d..b76376c 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -9,7 +9,8 @@
"version": "0.0.0",
"dependencies": {
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.22.3"
},
"devDependencies": {
"@types/react": "^18.2.64",
@@ -569,6 +570,14 @@
"node": ">= 8"
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
+ "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
@@ -2373,6 +2382,36 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
+ "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
+ "dependencies": {
+ "@remix-run/router": "1.15.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
+ "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
+ "dependencies": {
+ "@remix-run/router": "1.15.3",
+ "react-router": "6.22.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index c0de196..f3ae3f2 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,7 +11,8 @@
},
"dependencies": {
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.22.3"
},
"devDependencies": {
"@types/react": "^18.2.64",
diff --git a/frontend/src/App.css b/frontend/src/App.css
deleted file mode 100644
index e69de29..0000000
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 8d4280a..73dfb70 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,22 +1,56 @@
-import './App.css'
+import "./styles/App.css";
+import { Link, useNavigate } from 'react-router-dom';
import MainSearch from './components/MainSearch';
+import { SessionContext, destroySessionCookie, getSessionCookie } from "./contexts/session";
+import { useState, useEffect, useContext } from "react";
-function App() {
+export default function App() {
+ const [session, setSession] = useState(getSessionCookie());
+ const navigate = useNavigate();
+
+ // Redirect to login if session is undefined
+ useEffect(
+ () => {
+ setSession(getSessionCookie());
+ if (session === undefined) {
+ navigate("/login");
+ }
+ },
+ [session, navigate]
+ );
return (
<>
-
-

-
-
+
+
+
+

+
+
+
>
)
}
+function Navbar() {
+ const session = useContext(SessionContext);
-export default App
+ const links = session ? (
+ <>
+ Home
+ Logout
+ >
+ ): (
+ Login
+ )
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/MainSearch.tsx b/frontend/src/components/MainSearch.tsx
index 2558f8f..1f7fe48 100644
--- a/frontend/src/components/MainSearch.tsx
+++ b/frontend/src/components/MainSearch.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import '../screens/MainScreenWrapper.css';
+import '../styles/MainScreenWrapper.css';
const MainSearch: React.FC = () => {
return (
diff --git a/frontend/src/contexts/session.ts b/frontend/src/contexts/session.ts
new file mode 100644
index 0000000..ce77016
--- /dev/null
+++ b/frontend/src/contexts/session.ts
@@ -0,0 +1,17 @@
+import React from "react";
+
+type SessionContextType = string | undefined;
+
+export const getSessionCookie = () => {
+ const sessionCookie = document.cookie
+ .split('; ')
+ .find(row => row.startsWith('token='))
+ ?.split('=')[1];
+ return sessionCookie;
+}
+export const destroySessionCookie = () => {
+ document.cookie = "token=; Max-Age=0;"
+}
+
+export const SessionContext = React.createContext(getSessionCookie());
+
diff --git a/frontend/src/hooks/sessionRedirect.ts b/frontend/src/hooks/sessionRedirect.ts
new file mode 100644
index 0000000..c90d30a
--- /dev/null
+++ b/frontend/src/hooks/sessionRedirect.ts
@@ -0,0 +1,14 @@
+import { useEffect, useContext } from "react";
+import { useNavigate } from "react-router-dom";
+import { SessionContext } from "../contexts/session";
+
+export const useSessionRedirect = () => {
+ const session = useContext(SessionContext);
+ const navigate = useNavigate();
+ // Redirect to home if session is defined
+ useEffect(() => {
+ if (session !== undefined) {
+ navigate('/');
+ }
+ }, [session, navigate])
+}
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 3d7150d..14705c2 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -2,9 +2,33 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
+import {
+ createBrowserRouter,
+ RouterProvider,
+} from "react-router-dom";
+import Login from './screens/Login.tsx';
+import Signup from './screens/Signup.tsx';
+
+const router = createBrowserRouter([
+ {
+ path: "/",
+ element: ,
+ children: [
+ // add more routes here
+ ]
+ },
+ {
+ path: "/login",
+ element: ,
+ },
+ {
+ path: "/signup",
+ element: ,
+ },
+]);
ReactDOM.createRoot(document.getElementById('root')!).render(
-
+
,
)
diff --git a/frontend/src/screens/Login.tsx b/frontend/src/screens/Login.tsx
new file mode 100644
index 0000000..23a1cd5
--- /dev/null
+++ b/frontend/src/screens/Login.tsx
@@ -0,0 +1,48 @@
+import { Link, useNavigate } from "react-router-dom"
+import "../styles/Login.css"
+import { FormEvent } from "react";
+import { useSessionRedirect } from "../hooks/sessionRedirect";
+
+export default function Login() {
+ const navigate = useNavigate();
+
+ useSessionRedirect();
+
+ function handleSubmit(e: FormEvent) {
+ e.preventDefault();
+ const formData = new FormData(e.target as HTMLFormElement);
+ const username = formData.get('username');
+ const password = formData.get('password');
+ fetch('http://localhost:8000/api/login/', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ username, password }),
+ })
+ .then(() => {
+ navigate('/');
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ });
+ }
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/screens/Signup.tsx b/frontend/src/screens/Signup.tsx
new file mode 100644
index 0000000..9022619
--- /dev/null
+++ b/frontend/src/screens/Signup.tsx
@@ -0,0 +1,51 @@
+import { Link, useNavigate } from "react-router-dom";
+import "../styles/Login.css"
+import { FormEvent } from "react";
+import { useSessionRedirect } from "../hooks/sessionRedirect";
+export default function Signup() {
+ const navigate = useNavigate();
+ useSessionRedirect();
+
+ function handleSubmit(e: FormEvent) {
+ e.preventDefault();
+ const formData = new FormData(e.target as HTMLFormElement);
+ const email = formData.get('email');
+ const username = formData.get('username');
+ const password = formData.get('password');
+ fetch('http://localhost:8000/api/signup/', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ email, username, password }),
+ })
+ .then(response => response.json())
+ .then(() => {
+ navigate('/login');
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ });
+ }
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/styles/App.css b/frontend/src/styles/App.css
new file mode 100644
index 0000000..ee9db94
--- /dev/null
+++ b/frontend/src/styles/App.css
@@ -0,0 +1,10 @@
+nav {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 2rem;
+ border: 3px solid #f1f1f1;
+ border-radius: 5px;
+ color: black;
+ background-color: white;
+}
\ No newline at end of file
diff --git a/frontend/src/styles/Login.css b/frontend/src/styles/Login.css
new file mode 100644
index 0000000..3fbe8c4
--- /dev/null
+++ b/frontend/src/styles/Login.css
@@ -0,0 +1,42 @@
+form {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 0.5rem;
+}
+main {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+h1 {
+ text-align: center;
+}
+
+.form-input {
+ display: flex;
+ flex-direction: column;
+}
+
+ .login-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: #B1D4E0;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* shadow */;
+ width: 40vh;
+ }
+
+ .submit-button {
+ padding: 8px 16px;
+ margin-top: 20px; /* Adds space above the button */
+ background-color: #2E8BC0; /* Example background color */
+ color: white; /* Text color */
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
+ }
\ No newline at end of file
diff --git a/frontend/src/screens/MainScreenWrapper.css b/frontend/src/styles/MainScreenWrapper.css
similarity index 100%
rename from frontend/src/screens/MainScreenWrapper.css
rename to frontend/src/styles/MainScreenWrapper.css