diff --git a/README.md b/README.md index 9ea4e26b3..7b2f15b9c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Happy Thoughts -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +A project done in React, with the purpose of displaying API:s in both GET and POST. ## 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 struggled a lot with this project. I had a hard time structuring the code in a way that felt logical to me. After consulting chat-GPT and viewing a few other students projects I finally found my way. +If I had more time I would find a way to align the timer better. It frustrates me that it's not properly justified to the right, the alignment shifts depending on the length of the sentence. +I would also implement a window alert. I chose not to use one now, since I didn't have time to figure out how to only show it if the user tries to submit with the wrong length, it kept showing on every keystroke from 1-4. ## 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. +https://sunnyhappythoughts.netlify.app/ diff --git a/code/package-lock.json b/code/package-lock.json index f7e97e1e9..39582bd6d 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -9,12 +9,14 @@ "version": "1.0.0", "dependencies": { "babel-eslint": "^10.1.0", + "date-fns": "^2.29.3", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", + "moment": "^2.29.4", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -6205,6 +6207,18 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -12155,6 +12169,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -21996,6 +22018,11 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -26401,6 +26428,11 @@ "minimist": "^1.2.6" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/code/package.json b/code/package.json index 68869f589..9d3e846a2 100644 --- a/code/package.json +++ b/code/package.json @@ -4,12 +4,14 @@ "private": true, "dependencies": { "babel-eslint": "^10.1.0", + "date-fns": "^2.29.3", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", + "moment": "^2.29.4", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/code/public/favicon-32.png b/code/public/favicon-32.png new file mode 100644 index 000000000..e075f9931 Binary files /dev/null and b/code/public/favicon-32.png differ diff --git a/code/public/favicon-57.png b/code/public/favicon-57.png new file mode 100644 index 000000000..82672f808 Binary files /dev/null and b/code/public/favicon-57.png differ diff --git a/code/public/index.html b/code/public/index.html index e6730aa66..fefc5cde1 100644 --- a/code/public/index.html +++ b/code/public/index.html @@ -4,6 +4,15 @@ + + + + + + + + + - Technigo React App + Sunny Happy Thoughts diff --git a/code/public/screenshot.png b/code/public/screenshot.png new file mode 100644 index 000000000..89b049947 Binary files /dev/null and b/code/public/screenshot.png differ diff --git a/code/src/App.js b/code/src/App.js index f2007d229..b511df488 100644 --- a/code/src/App.js +++ b/code/src/App.js @@ -1,9 +1,68 @@ -import React from 'react'; +/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-unused-vars */ +import React, { useState, useEffect } from 'react'; +import { SubmitThoughts } from 'components/SubmitThoughts'; +import { ListThoughts } from 'components/ListThoughts'; export const App = () => { + const [thoughtsList, setThoughtsList] = useState([]) + const [submitThoughts, setSubmitThoughts] = useState('') + const [loading, setLoading] = useState(false) + + const fetchThoughts = () => { + setLoading(true) + fetch('https://project-happy-thoughts-api-l8j3.onrender.com/thoughts') + .then((response) => response.json()) + .then((data) => setThoughtsList(data.response)) + .catch((error) => console.log(error)) + .finally(() => { setLoading(false) }) + } + + useEffect(() => { + fetchThoughts() + }, []) + + const handleThoughtChange = (event) => { + setSubmitThoughts(event.target.value) + } + + const handleFormSubmit = (event) => { + event.preventDefault() + setLoading(true) + + const options = { + method: 'POST', + body: JSON.stringify({ message: submitThoughts }), + headers: { 'Content-Type': 'application/json' } + } + fetch('https://project-happy-thoughts-api-l8j3.onrender.com/thoughts', options) + .then((response) => response.json()) + .then((data) => { setThoughtsList([data.response, ...thoughtsList]) }) + .catch((error) => console.log(error)) + .finally(() => { setLoading(false); setSubmitThoughts('') }) + } + + const handleLikeChange = (thoughtId) => { + const options = { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + } + fetch(`https://project-happy-thoughts-api-l8j3.onrender.com/thoughts/${thoughtId}/like`, options) + .then((response) => response.json()) + .catch((error) => console.log(error)) + .finally(() => fetchThoughts('')) + } + return ( -
- Find me in src/app.js! -
+
+ + +
); -} +} \ No newline at end of file diff --git a/code/src/components/ListThoughts.js b/code/src/components/ListThoughts.js new file mode 100644 index 000000000..842745985 --- /dev/null +++ b/code/src/components/ListThoughts.js @@ -0,0 +1,36 @@ +/* eslint-disable no-underscore-dangle */ +import React from 'react' +import moment from 'moment' + +export const ListThoughts = ({ loading, thoughtsList, handleLikeChange }) => { + if (loading) { + return

Loading...

+ } + const getTimeAgo = (createdAt) => { + const timeAgo = moment(createdAt).fromNow(); + return timeAgo; + } + return ( +
+ {thoughtsList.map((thought) => { + return ( +
+

{thought.message}

+
+ +

x {thought.hearts}

+
+

{getTimeAgo(thought.createdAt)}

+
+
+
+ ) + })} +
+ ) +} diff --git a/code/src/components/SubmitThoughts.js b/code/src/components/SubmitThoughts.js new file mode 100644 index 000000000..a250cd042 --- /dev/null +++ b/code/src/components/SubmitThoughts.js @@ -0,0 +1,34 @@ +/* eslint-disable react/no-unescaped-entities */ +import React, { useState } from 'react' + +export const SubmitThoughts = ({ handleFormSubmit, submitThoughts, handleThoughtChange }) => { + const turnCounterRed = submitThoughts.length > 140 ? 'counter-red' : 'counter'; + const [errorAlert, setErrorAlert] = useState('') + const handleLengthError = (event) => { + const textLength = event.target.value.length + if (textLength < 5) { + setErrorAlert('Your message needs to be at least 5 characters long. 😊') + } else if (textLength > 140) { + setErrorAlert('Whoa Nelly! Save the essay for your portfolio. 😄') + } else { + setErrorAlert('') + } + handleThoughtChange(event) + } + return ( +
+

What's making you happy right now?

+
+