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

Gaby's Happy Thoughts Project w11 #109

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
30 changes: 5 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,15 @@
<h1 align="center">
<a href="">
<img src="/src/assets/happy-thoughts.svg" alt="Project Banner Image">
</a>
</h1>

# Happy thoughts Project

In this week's project, you'll be able to practice your React state skills by fetching and posting data to an API.

## Getting Started with the Project

### Dependency Installation & Startup Development Server

Once cloned, navigate to the project's root directory and this project uses npm (Node Package Manager) to manage its dependencies.

The command below is a combination of installing dependencies, opening up the project on VS Code and it will run a development server on your terminal.

```bash
npm i && code . && npm run dev
```

### The Problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
I started by created the components I believed I needed. My goal was to have the App.jsx as clean as possible and let the components handle the tasks: GET, POST and the returns. I realised quite late that I couldnt insert the new post into the Thoughts list without merging most of the code into one component. Tried many different options but still haven't managed figuring it out. So with more time I would probably create more, but simpler components or just divide it differently. I would also have liked to reach more of the strechgoals.

### View it live
I managed quite far with the class material but halfway I got stuck and used ChatGPT, Google and previous students code as my help.

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Something I find very challenged is to plan correctly and I feel that I don't know what is right and wrong and doubt my solutions a lot.

## Instructions
### View it live

<a href="instructions.md">
See instructions of this project
</a>
https://gabyshappythoughts.netlify.app/
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
15 changes: 14 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import React from "react"
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to import React

import { Header } from "./components/header/Header";
import { PostThoughts } from "./components/postThoughts/PostThoughts";
import { Thoughts } from "./components/thoughts/Thoughts"
import { Footer } from "./components/footer/Footer";

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return (
<div>
<Header />
<PostThoughts />
<Thoughts />
<Footer />
</div>
);
};
1 change: 1 addition & 0 deletions src/components/ApiUrl.jsx
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not a component 👀

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const URL = "https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts"
10 changes: 10 additions & 0 deletions src/components/footer/Footer.jsx
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice and clean! ✨

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "./footer.css";

export const Footer = () => {
return (
<div className="footer">
<h4>Thank you for your sparkle</h4>
<h5>© 2024 Copyright - Developed by Gabriella Iofe</h5>
</div>
)
}
19 changes: 19 additions & 0 deletions src/components/footer/footer.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.footer {
display: flex;
flex-direction: column;
align-items: center;
}

h4 {
font-family: Courier New, monospace;
font-size: 16px;
margin-bottom: 5px;

}

h5 {
font-family: Courier New, monospace;
font-size: 11px;
margin-top: auto;
margin-bottom: 10px;
}
11 changes: 11 additions & 0 deletions src/components/header/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "./header.css";

export const Header = () => {
return (
<section className="header">
<h1>Happy Thoughts Project</h1>
<h2>by Gabriella Iofe</h2>
</section>
)

}
20 changes: 20 additions & 0 deletions src/components/header/header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.header {
display: flex;
flex-direction: column;
align-items: center;
}

h1 {
font-family: Courier New, monospace;
font-size: 23px;
font-weight: bold;
margin-bottom: 5px;

}

h2 {
font-family: Courier New, monospace;
font-size: 15px;
margin-top: auto;
margin-bottom: 30px;
}
77 changes: 77 additions & 0 deletions src/components/postThoughts/PostThoughts.jsx
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice error handling, and really good that you remembered the label! However, something more needs to happen when you post a thought:

  • either do an optimistic UI update as described in the instructions
  • or trigger the fetch of thoughts

so that the feed updates with the new thought

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useState } from "react";
import { URL } from "../ApiUrl";
import "./postThoughts.css";



export const PostThoughts = () => {
const [body, setBody] = useState('')
const [loading, setLoading] = useState(false)
const [errorMessage, setErrorMessage] = useState('');


//This functions POSTs a happy thought to the API
const handleSubmit = async (event) => {
event.preventDefault()
setLoading(true) /* Start loading on submit */
setErrorMessage('');

// Validation: Check if message is between 5 and 140 characters
if (body.length < 5 || body.length > 140) {
setErrorMessage("Message must be between 5 and 140 characters.");
setLoading(false);
return;
}

try {
const response = await fetch(URL, {
method: "POST",
headers: {
//this is a way to send extra information to our API.
"content-type": "application/json; charset=utf-8"
},
body: JSON.stringify({message: body})//command to tell API the message (and what i call it in my code "body" should be fetched)
});

if (response.ok) {
const recentBody = await response.json(); //Get the new post
setBody((previousBody) => [recentBody, ...previousBody]); // Update the post state
setBody(''); //this clears the input field after its been posted

} else {
const errorData = await response.json();
setErrorMessage(errorData.message || "An error occurred. Please try again.");
}
} catch (error) {
setErrorMessage("Network error. Please try again later.");
console.log("error:", error);
} finally {
setLoading(false) /* Stops the loading */
}
}


return (
<section className="post-thoughts-container">
<form className="post-thoughts-form" onSubmit={handleSubmit}>
<label>
<h3>What's making you happy right now?</h3>
<textarea
className="textarea"
value={body}
placeholder="Share your happy thought here..."
onChange={(e) => setBody(e.target.value)}
/>
</label>
{errorMessage && <p className="error-message">{errorMessage}</p>}
<button
className="send-button"
type="submit"
disabled={loading}
>
{loading ? "Loading..." : "💖 Send Happy Thought 💖"}{/* Loading? If True show Loading... If False show 💖Send Happy Thought💖*/}
</button>
</form>
</section>
)
}
45 changes: 45 additions & 0 deletions src/components/postThoughts/postThoughts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.post-thoughts-container {
background-color: rgb(239, 236, 237);
border: solid rgb(209, 208, 209) 2px;
padding: 15px;
margin: 0px 15px 25px 15px;
box-shadow: 6px 6px 0px 0px rgb(0, 0, 0);
}

label {
color: black;
}

h3 {
font-family:"Segoe UI", Arial, sans-serif;
font-size: 16px;
}

.textarea {
background-color: white;
height: 50px;
width: 100%;
margin: 10px 0px 10px 0px;
padding: 5px;
font-family: Courier New, monospace;
font-size: 13px;
}

.error-message {}

.send-button {
padding: 9px;
border-radius: 30px;
border: none;
background-color: rgb(250, 144, 160);
font-size: 13px;
font-family: "Segoe UI", Arial, sans-serif;
}

@media only screen and (min-width: 768px) {
.post-thoughts-container {
width: 417px;
margin-left: auto;
margin-right: auto;
}
}
74 changes: 74 additions & 0 deletions src/components/thoughts/Thoughts.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

import { useState,useEffect } from "react"
import { URL } from "../ApiUrl";
import { formatDistance } from 'date-fns';
import "./thoughts.css";

//A function to fetch and display the message object in a list
export const Thoughts = () => {
const [thoughts, setThoughts] = useState([])

useEffect(() => {
const fetchThoughts = async () => {
try {
const response = await fetch(URL)
const data = await response.json()
setThoughts(data)
console.log("Thought is", data)
} catch (error) {
console.log("Error fetching thoughts is:", error)
}
}
fetchThoughts()
}, []);


const addLike = async (postId) => {
try {
fetch(`${URL}/${postId}/like`, {method: "POST"})

//Update the state with a like
setThoughts((prevThoughts) =>
prevThoughts.map((thought) =>
thought._id === postId ? { ...thought, hearts: thought.hearts + 1 } : thought
))
} catch (error) {
console.error("Error liking the post is:", error)
}
}



return (
<section>
<ul className="thoughts-list-container">
{thoughts.map((thought) => (
<li
className="thought-list-item"
key={thought._id} // I tried using both index and thought._id. the second option uses the unique number for each message whereas index creates its own unique numbers.
>
<p className="thought-message">{thought.message}</p>
<div className="time-count-container">
<div>
<button
aria-label={`Like post with message: ${thought.message}`}
className={`like-button ${thought.hearts === 0 ? 'notLikedClass' : 'likedClass'}`}
onClick={() => addLike(thought._id)}
>
<span className="heart-icon" aria-label="Like icon">💖</span> {/* Target heart icon */}
</button>
<span aria-label="Number of likes"> x {thought.hearts}</span> {/* Display likes outside the button */}
</div>
<div className="time-container">
<p>{formatDistance(new Date(thought.createdAt), Date.now(), { addSuffix: true })}</p> {/*npm install date-fns --save to */}
Copy link
Contributor

Choose a reason for hiding this comment

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

</div>
</div>
</li>
))}
</ul>
</section>
);
};



56 changes: 56 additions & 0 deletions src/components/thoughts/thoughts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@


.thoughts-list-container {
list-style: none;
padding: 0;
margin: 0px 15px 0px 15px;
}

.thought-list-item {
border: 1px solid black;
margin-bottom: 25px;
padding: 15px;
box-shadow: 6px 6px 0px 0px rgb(0, 0, 0);
}

.thought-message {
font-family: Courier New, monospace;
margin: 0;
margin-bottom: 5px;
overflow: hidden;

}

.time-count-container {
display: flex;
justify-content: space-between;
align-items: flex-end;

}


.like-button {
border-radius: 20px;
padding: 8px 9px 8px 9px;
border: none;
}

.time-count-container {
font-family: "Segoe UI", Arial, sans-serif;
font-size: 13px;
}

.time-count-container p {
margin: 0;
padding-bottom: 8px;
}



@media only screen and (min-width: 768px) {
.thoughts-list-container {
width: 450px;
margin-left: auto;
margin-right: auto;
}
}
Loading