diff --git a/.gitignore b/.gitignore index b02a1ff7..f5962297 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ package-lock.json *.njsproj *.sln *.sw? + +.codegpt \ No newline at end of file diff --git a/README.md b/README.md index 6fc6d095..940dc2ba 100644 --- a/README.md +++ b/README.md @@ -5,31 +5,12 @@ # Happy thoughts Project +In this week's project, we have been working with APIs again, this time in React. We should also apply what we learnt about the useEffect function and also been using useState. -In this week's project, you'll be able to practice your React state skills by fetching and posting data to an API. +I have focused on dividing the project into different components and tried to make it as easy and understandable as possible. This time we also had to focus quite a lot on the styling and adapt it to the design we received. I thought it was a bit tricky with the useEffect but I looked a lot at the example we did on the tuesday session as well as the Q&A session on Thursday. I also found it difficult with the time conversion to be able to show the time stamp of the different posts and needed help from chatGPT for that. -## Getting Started with the Project +If I would have more time I would add the function to display max number of characters used. I did however add this as an if/else statement but it is not showing in the text area. -### 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? ### View it live - -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. - -## Instructions - - - See instructions of this project - +https://happythoughtsbyanna.netlify.app/ diff --git a/index.html b/index.html index 21cce4e0..fdea27ac 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,22 @@ - - - - - Happy Thought - Project - Week 7 - - -
- - - + + + + + + + + + + Anna's Happy Thoughts App + + + +
+ + + + \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 1091d431..e22c5b2b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,77 @@ +import { useState, useEffect } from "react" +// Import child components and style components used in the App component +import { Header } from "./components/Header" +import { ThoughtForm } from "./components/ThoughtForm" +import { ThoughtList } from "./components/ThoughtList" +import "./styles/App.css" + +// Define the base URL for the API endpoint to fetch thoughts +const BASE_URL = "https://project-happy-thoughts-api-42bh.onrender.com/thoughts" //From backend Happy Thoughts API + export const App = () => { - return
Find me in src/app.jsx!
; -}; + const [thoughts, setThoughts] = useState([]) // State to store the list of thoughts + const [isLoading, setIsLoading] = useState(true) // State to track the loading status of the app + + useEffect(() => { // useEffect to fetch initial data on component mount + const fetchThoughts = async () => { // Define an asynchronous function to fetch thoughts + try { + // Fetch thoughts data from the API + const response = await fetch(BASE_URL) + const result = await response.json() + + // Set the fetched data to state + setThoughts(result) + } catch (error) { + console.error("Error fetching data:", error) + } finally { + setTimeout(() => setIsLoading(false)) + } + } + // Call the fetch function + fetchThoughts() + }, []) + + // Function to handle adding a new thought to the list + const handleNewThought = (newThought) => { + // Update the state by adding the new thought to the beginning of the array + setThoughts((prevThoughts) => [newThought, ...prevThoughts]) + } + + return ( + // Main container for the app +
+ {/*Header component */} +
+ {/* ThoughtForm component, passing down the function to handle new thoughts */} + + {/* ThoughtList component, passing down the thoughts data */} + {/* Show loading message while data is being fetched */} + {isLoading ? ( +
+

Loading Happy Thoughts...

+
+ ) : ( + + )} +
+ ) +} + + + + + + + + + +{/* Planning of components + +// App Component: Main component that manages state and handles API calls. +// Header Component: Includes the header text and heart icon image and it is imported into and rendered within the App component. +// ThoughtForm Component: A form that allows users to submit new thoughts the form data is passed up to the App component. +// ThoughtHearts Component: Displays each individual thought with a like button and is a child of the ThoughtList component. +// ThoughtList Component: Responsible for displaying a list of thoughts passed down from the App component. + +// Each component has it's separate CSS file*/ } + diff --git a/src/assets/examples/finished-example.png b/src/assets/examples/finished-example.png deleted file mode 100644 index cd845e6f..00000000 Binary files a/src/assets/examples/finished-example.png and /dev/null differ diff --git a/src/assets/happy-thoughts.svg b/src/assets/happy-thoughts.svg deleted file mode 100644 index 63ab9f0d..00000000 --- a/src/assets/happy-thoughts.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/assets/smilingheart.png b/src/assets/smilingheart.png new file mode 100644 index 00000000..f6d72a6e Binary files /dev/null and b/src/assets/smilingheart.png differ diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..e8ca061f --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,13 @@ +import "../styles/Header.css" +import smilingHeart from "../assets/smilingheart.png" + +export const Header = () => { + return ( +
+ Smiling heart image +

Happy Thoughts

+

Created by Anna Hansen

+
+ ) +} + diff --git a/src/components/ThoughtForm.jsx b/src/components/ThoughtForm.jsx new file mode 100644 index 00000000..f61daf20 --- /dev/null +++ b/src/components/ThoughtForm.jsx @@ -0,0 +1,84 @@ +import { useState } from "react" +import "../styles/ThoughtForm.css" + +// eslint-disable-next-line react/prop-types +export const ThoughtForm = ({ onNewThought }) => { + const [newThought, setNewThought] = useState('') // 'newThought' holds the user’s input for the thought message. 'setNewThought' is used to update it. + const [errorMessage, setErrorMessage] = useState('') // 'errorMessage' holds any error messages, if the input doesn’t meet character limits. 'setErrorMessage' updates it. + + const handleSubmit = async (event) => { // This function handles the form submission when the user clicks the submit button. + event.preventDefault() // Prevents the page from refreshing on form submission. + + // Check for minimum character count + if (newThought.length < 5) { + setErrorMessage("Your thought must be at least 5 characters long.") + return // Exit the function early if input is too short. + } + + // Check for maximum character count + else if (newThought.length > 140) { + setErrorMessage("Your thought cannot be more than 140 characters long.") + return // Exit the function early if input is too long. + } + + const URL = "https://project-happy-thoughts-api-42bh.onrender.com/thoughts" ///POST request to add a new thought. From backend Happy Thoughts API. + + try { + const response = await fetch(URL, { // Use fetch to make a POST request to the server to submit the new thought. + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ message: newThought }), // Convert the 'newThought' to JSON and send it in the request body. + }) + + if (response.ok) { // If the response from the server is OK (status code 200-299): + const newThoughtData = await response.json() // Get the newly added thought data from the response. + + + onNewThought(newThoughtData) // Call the onNewThought function from the parent component (App.jsx) with new thought data. + setNewThought("") // Clear input field after successful submission + } else if (response.status === 400) { // If there’s a validation error, show a specific error message. + const errorData = await response.json() + setErrorMessage(errorData.message) // Display error message for invalid data + } else { + console.error( + "Failed to post thought. The message must be between 5 and 140 characters." + ) + } + } catch (error) { // Catch any other errors, such as network issues, and show a generic error message. + console.error("Error posting thought: ", error) + setErrorMessage("Failed to post thought. Please try again.") // Generic error message + } + } + // This is the JSX layout of the ThoughtForm component. + return ( +
+

What is making you happy right now?

+
+ {/* Label for accessibility (hidden visually but helps screen readers) */} + + {/* Text area where users type their thought */} +