Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jonas Happy Thought App #92

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"date-fns": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
14 changes: 12 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import { HappyThoughts } from './components/HappyThoughts';




export const App = () => {
return <div>Find me in src/app.jsx!</div>;
};
return (
<div>

< HappyThoughts />
</div>
);
};
74 changes: 74 additions & 0 deletions src/components/HappyThoughts.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

import { Header } from "./Header"
import { useState, useEffect } from "react"
import { ThoughtForm } from "./ThoughtForm"
import { ThoughtList } from "./ThoughtList"


export const HappyThoughts = () => {
const [thoughts, setThoughts] = useState([]);
const [newThought, setNewThought] = useState("")

useEffect(() => {
fetch("https://project-happy-thoughts-api-j0eg.onrender.com/thoughts")
.then(res => res.json())
.then((json) => {
setThoughts(json.response)
})
.catch(error => {
console.error('Fel vid hämtning av tankar:', error);
});
}, []);

const handleNewThought = (event) => {
setNewThought(event.target.value)
}

//Function to POST new Happy Thoughts
const onFormSubmit = (event) => {
event.preventDefault()


const PostOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: newThought,
}),
}

fetch(
"https://project-happy-thoughts-api-j0eg.onrender.com/thoughts",
PostOptions
)
.then((res) => res.json())
.then((newThought) => {
setThoughts((previousThoughts) => [
newThought.response,
...previousThoughts,
])
setNewThought("")
})
.catch((error) => {
console.error("Error posting Happy Thought:", error)
})
}

return (
<div>
<Header />
<main className="main-wrapper">
<div className="main-content">
<ThoughtForm
newThought={newThought}
onNewThoughtChange={handleNewThought}
onFormSubmit={onFormSubmit}
/>
<ThoughtList thoughts={thoughts} />
</div>
</main>
</div>
)
}
8 changes: 8 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const Header = () => {
return (
<div className="header">
<h1>Project Happy Thoughts</h1>
<h2>by Jonas</h2>
</div>
);
};
31 changes: 31 additions & 0 deletions src/components/ThoughtForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";


export const ThoughtForm = ({ onFormSubmit, newThought, onNewThoughtChange }) => {

const disableSubmit = newThought.length < 5 || newThought.length > 140;

return (
<form className="form-container" onSubmit={onFormSubmit}>
<label>
<h3>What makes you happy..</h3>
<textarea
className="thought-input"
value={newThought}
onChange={onNewThoughtChange}
placeholder="Write something here"
required
/>
</label>

<button
type="submit"
className="submit-thought"
aria-label="button for submit thought"
disabled={disableSubmit}
>
❤️ Send Happy Thought ❤️
</button>
</form>
);
};
47 changes: 47 additions & 0 deletions src/components/ThoughtLikes.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useState, useEffect } from "react";

export const ThoughtLikes = ({ id, heart }) => {
const [likes, setLikes] = useState(() => {
const savedLikes = localStorage.getItem(`likes-${id}`);
return savedLikes !== null ? Number(savedLikes) : Number(heart) || 0;
});

useEffect(() => {
localStorage.setItem(`likes-${id}`, likes);
}, [likes, id]);

const handleLikes = () => {
fetch(`https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts/${id}/like`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => {
if (!res.ok) {
throw new Error('Network response was not ok');
}
return res.json();
})
.then(() => {
setLikes((prevLikes) => prevLikes + 1);
})
.catch((error) => {
console.error("Error:", error);
});
};

return (
<div className="likes">
<button
onClick={handleLikes}
aria-label="Like-button"
className={likes === 0 ? "heart-button" : "liked-button"}
type="button"
>
❤️
</button>
<p>x {likes}</p>
</div>
);
};
22 changes: 22 additions & 0 deletions src/components/ThoughtList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { formatDistanceToNow } from 'date-fns';
import { ThoughtLikes } from "./ThoughtLikes.jsx";

export const ThoughtList = ({ thoughts }) => {
return (
<section className="thought-section">
{thoughts.map((thought) => (
<div className="thought-wrapper" key={thought._id}>
<div className="input-message">{thought.message}</div>
<div className="info-wrapper">
<ThoughtLikes id={thought._id} hearts={thought.hearts} />
<div className="time">
{formatDistanceToNow(new Date(thought.createdAt), {
addSuffix: true,
})}
</div>
</div>
</div>
))}
</section>
);
};
129 changes: 126 additions & 3 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,130 @@
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

.header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1rem;

}

.main-wrapper {
display: flex;
flex-direction: column;
align-items: center;

}

.form-container,
.thought-wrapper {
background-color: #ebebeb;
border: 1px solid rgb(105, 105, 105);
box-shadow: rgb(0, 0, 0) 8px 8px 0px 0px;
margin: 13px 0 13px 8px;
padding: 1rem;
max-width: 600px;
width: 90%;

}

h3 {
text-align: center;

}





.thought-input {
width: 100%;
height: 80px;
padding: 12px 20px;
border: 2px solid #d6d6d6;
border-radius: 4px;
background-color: #fff;
font-size: 16px;
resize: none;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐️

}

.submit-thought {
border-radius: 26px;
border: none;
background-color: #ffadad;
color: #000;
font-size: 16px;
font-weight: 600;
margin: 8px 0 5px;
padding: 10px 6px;
cursor: pointer;
width: 100%;

}

.thought-section {
padding: 0px;
display: flex;
flex-direction: column;
margin-bottom: 2rem;
}

.heart-button,
.liked-button {
border-radius: 50%;
border: none;
padding: 16px;
cursor: pointer;
}

.heart-button:hover {
background-color: #ffadad;
}

.likes {
display: flex;
align-items: center;
color: rgb(105, 105, 105);
}

.likes>* {
margin-right: 5px;
}

.time {
color: rgb(105, 105, 105);
font-size: 14px;
padding-bottom: 1rem;
}

.info-wrapper {
display: flex;
justify-content: space-between;
align-items: flex-end;
}


@media (max-width: 600px) {
.main-wrapper {
padding: 0.5rem;

}

h1 {
text-align: center;
padding-bottom: 2px;
}

.form-container,
.thought-wrapper {
width: 95%;

}
}