Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { GlobalStyle } from "./styles/GlobalStyles";
import { Signup } from "./pages/SignupPage";
import { useEffect, useState } from "react";
import { apiUrl } from "../api";
import { Navbar } from "./components/Navbar";

export const App = () => {
const [user, setUser] = useState(null)
const [data, setData] = useState(null);

// TODO remove?
useEffect(() => {
const fetchData = async () => {
try {
Expand All @@ -29,16 +32,41 @@ export const App = () => {
fetchData();
}, []);

useEffect(() => {
const accessToken = localStorage.getItem('accessToken')
const userId = localStorage.getItem('userId')
const userEmail = localStorage.getItem('userEmail')
const userName = localStorage.getItem('userName')

if (accessToken && userId) {
setUser({
accessToken,
userId,
email: userEmail,
name: userName
})
}
}, [])

const handleLogout = () => {
setUser(null)
localStorage.removeItem('accessToken')
localStorage.removeItem('userId')
localStorage.removeItem('userEmail')
localStorage.removeItem('userName')
}

return (
<>
<GlobalStyle />
<Navbar onLogout={handleLogout} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/feed" element={<FriendFeed />} />
<Route path="/quests" element={<Quests />} />
<Route path="/rewards" element={<Rewards />} />
<Route path="/profile" element={<UserProfile />} />
<Route path="/profile" element={<UserProfile user={user}/>} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
Expand Down
92 changes: 86 additions & 6 deletions frontend/src/components/CreateQuest.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,96 @@
import styled from 'styled-components'
import { useState } from 'react'
import { apiUrl } from '../../api'

// component for creating quest
export const CreateQuest = () => {
// state variables for form inputs
const [message, setMessage] = useState('')
const [timeNeeded, setTimeNeeded] = useState('')
const [category, setCategory] = useState('')
const [error, setError] = useState(null)

// Handle form submission
const handleSubmit = async (event) => {
event.preventDefault()
setError(null)

// get the users accesstoken from localStorage (saved during login/signup)
const accessToken = localStorage.getItem('accessToken')

if (!accessToken) {
setError('You must be logged in to create a quest')
return
}

try {
// POST request for creating a new quest
const response = await fetch(apiUrl + '/quests', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': accessToken
},
body: JSON.stringify({
message,
timeNeeded: Number(timeNeeded),
category: [category]
})
})

const data = await response.json()

if (!response.ok) {
setError(data.message || 'Failed to create quest')
return
}

console.log('Quest created:', data)

// After form i submitted, clear the input field
setMessage('')
setTimeNeeded('')
setCategory('')
} catch (error) {
console.error('Error:', error)
setError('Something went wrong. Please try again.')
}
}

return (
<Form>
<Form onSubmit={handleSubmit}>
<Label>
Create new quest
<input type="text" placeholder='Text input' />

{error && <p>{error}</p>}

<input
type="text"
placeholder='What do you need to do?'
value={message}
onChange={(event) => setMessage(event.target.value)}/>

<select name="category" id="QuestCategory" >
<select
name="category"
value={category}
onChange={(event) => setCategory(event.target.value)}
>
<option value="">Select a category</option>
<option value="cleaning">Cleaning</option>
<option value="exercise">Exercise</option>
<option value="work">Work</option>
<option value="personal">Personal</option>
</select>

<select name="time" id="QuestTime" >
<option value="">Time needed</option>
<select
name="time"
value={timeNeeded}
onChange={(event) => setTimeNeeded(event.target.value)}
>
<option value="10">10 min</option>
<option value="20">20 min</option>
<option value="30">30 min</option>
<option value="60">1 hour</option>
</select>

<button type="submit">Add a quest</button>
Expand All @@ -29,4 +107,6 @@ const Form = styled.form`
const Label = styled.label`
display: flex;
flex-direction: column;
`
`

// TODO: Add authentication check - redirect to login if no accessToken
6 changes: 4 additions & 2 deletions frontend/src/components/LoginForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export const LoginForm = () => {
localStorage.setItem('accessToken', data.accessToken)
// Store the user ID
localStorage.setItem('userId', data.id)
// store user email
localStorage.setItem('userEmail', email)

// Clear the form inputs
setName('')
Expand All @@ -64,10 +66,10 @@ export const LoginForm = () => {
<>
<Form onSubmit={handleSubmit}>
<h4>Welcome Back! Log in now:</h4>
<label>
{/* <label>
Username
<input type="text" placeholder="username" onChange={event => setName(event.target.value)}/>
</label>
</label> */}
<label>
email
<input type="email" placeholder="email" onChange={event => setEmail(event.target.value)}/>
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/components/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { Link } from 'react-router-dom'
import { Link, useNavigate } from 'react-router-dom'
import styled from 'styled-components'

export const Navbar = () => {
export const Navbar = ({ onLogout }) => {
const navigate = useNavigate()

const handleLogoutClick = () => {
onLogout() // Call the logout function
navigate('/') // Redirect to homepage
}

return (
<>
<Nav>
Expand All @@ -10,7 +17,7 @@ export const Navbar = () => {
<StyledLink to='/feed'>Friends</StyledLink>
<StyledLink to='/about'>About</StyledLink>
<StyledLink to='/login'>Log in</StyledLink>
<button>Log out</button>
<button onClick={handleLogoutClick}>Log out</button>
</Nav>
</>
)
Expand All @@ -26,4 +33,4 @@ const Nav = styled.nav`
const StyledLink = styled(Link)`
text-decoration: none;
`
// TODO: decide: Should navbar be hamburger menu on phone size?
// TODO: change to hamburger on small screens
1 change: 0 additions & 1 deletion frontend/src/components/QuestLibrary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ export const QuestLibrary = () => {
)
}

// TODO Decide: QuestLibrary hardcoded as list at the moment. Keep that way? Or create Library task card and map over?
85 changes: 84 additions & 1 deletion frontend/src/components/QuestList.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
import { apiUrl } from "../../api";
import styled from 'styled-components'
import { useState, useEffect } from 'react'
import { QuestCard } from "./cards/QuestCard";

export const QuestList = () => {
return <p>quest list</p>;
const [quests, setQuests] = useState([])
const [error, setError] = useState(null)


useEffect(() => {
const getQuests = async () => {
try {
const accessToken = localStorage.getItem('accessToken')

const response = await fetch(apiUrl + '/quests/all', {
headers: {
'Authorization': accessToken
}
})

if (!response.ok) {
throw new Error('failed to fetch quests')
}

const data = await response.json()
setQuests(data.response)
} catch (error) {
console.error(error.message)
setError(error.message)
}
}

getQuests()
}, [])

// Delete quests
const handleDelete = async (questId) => {
try {
const accessToken = localStorage.getItem('accessToken')

const response = await fetch(apiUrl + `/quests/${questId}`, {
method: 'DELETE',
headers: {
'Authorization': accessToken
}
})

if (!response.ok) {
throw new Error('Failed to delete quest')
}

// remove from state
setQuests(quests.filter(quest => quest._id !== questId))
} catch (error) {
console.error('Error deleting quest:', error)
}
}

return (
<>
<p>Your quests:</p>
{quests.map((quest) => (
<QuestCard
key={quest._id}
id={quest._id}
message={quest.message}
category={quest.category}
timeNeeded={quest.timeNeeded}
onDelete={handleDelete}
/>
))}
</>
)
};

const Div = styled.div`
display: flex;
flex-direction: column;
background-color: var(--primary-color);
margin: 10px;
border-radius: 12px;
`

// 1. GET request from api to get all quests quests/all
// 2. map over quests, for each quest create a display of message, time and catagory
7 changes: 6 additions & 1 deletion frontend/src/components/SignupForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const SignupForm = () => {
localStorage.setItem('accessToken', data.accessToken)
// Store the user ID
localStorage.setItem('userId', data.id)
// Store username
localStorage.setItem('userName', name)
// store email
localStorage.setItem('userEmail', email)

// Clear the form inputs
setName('')
Expand Down Expand Up @@ -91,4 +95,5 @@ const Form = styled.form`
border-radius: 12px;
`

// TODO: This is only basic form and step 1/2. Add step 2 (add code sent to email to verify)
// TODO: This is only basic form and step 1/2. Add step 2 (add code sent to email to verify)
// TODO: when user signed up, also log in
10 changes: 5 additions & 5 deletions frontend/src/components/cards/QuestCard.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import styled from 'styled-components'

export const QuestCard = () => {
export const QuestCard = ({ message, category, timeNeeded, onDelete, id }) => {
return (
<Div>
<div>
<h4>Name</h4>
<h4>{message}</h4>
</div>
<div>
<p>Task:</p>
<p>Time spent:</p>
<p>Kudos: 🙌</p>
<p>Category: {category}</p>
<p>Time: {timeNeeded} min</p>
<button onClick={() => onDelete(id)}>Delete</button>
</div>
</Div>
)
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/pages/AboutPage.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Navbar } from '../components/Navbar'

export const About = () => {
return (
<>
<Navbar />
<p>What does the app do</p>
<p>How to get started</p>
</>
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/pages/FriendFeedPage.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { QuestCard } from '../components/cards/QuestCard'
import { Navbar } from '../components/Navbar'

export const FriendFeed = () => {
return (
<>
<Navbar />
<h2>My friends finished quests</h2>
<form>
<label>
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Navbar } from "../components/Navbar";
import { Header } from "../components/Header";
import styled from "styled-components";
import { CreateQuest } from "../components/CreateQuest";
Expand All @@ -8,7 +7,6 @@ import { apiUrl } from "../../api";
export const Home = () => {
return (
<>
<Navbar />
<Header />
<Div>
<p>Motivational things</p>
Expand Down
Loading