Skip to content
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
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# 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.
My own version of Twitter. Consists of a main form with an input, and below the answers in the form are shown.
Happy, pastel lights colors were used to build the web.

## 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?
- My third project using React and building different components. Playing around with importing and exporting, writng a clear and easy to follow code using this Java Script library.
- Second project with the State Hook and the first with the Effect Hook.
- Fatching data with a public API (this API was shared with other students for the sue of their project).
- User can write their own happy thoughts and like some of the already published: when the user clicks the heart button on a thought, it sends a POST request (with no body) to the API URL, and the icon changes the color.

## 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://flourishing-creponne-c72c1d.netlify.app/
18 changes: 18 additions & 0 deletions code/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"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",
Expand Down
2 changes: 1 addition & 1 deletion code/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Technigo React App</title>
<title>🐶🌈 Happy Thoughts </title>
</head>

<body>
Expand Down
101 changes: 97 additions & 4 deletions code/src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,102 @@
import React from 'react';
/* eslint no-underscore-dangle: 0 */
import React, { useState, useEffect } from 'react';
import ThoughtList from 'components/ThoughtList';
import MainForm from 'components/MainForm';
import Header from 'components/Header';
import Footer from 'components/Footer';

export const App = () => {
const [thoughtList, setThoughtList] = useState([]);
const [loading, setLoading] = useState(false);
const [newThought, setNewThought] = useState('');

const fetchThoughts = () => {
setLoading(true);
fetch('https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts')
.then((res) => {
return res.json();
})
.then((data) => {
setThoughtList(data);
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}

useEffect(() => {
fetchThoughts();
}, []);

/* eslint-disable no-unused-vars */
const handleNewThoughtChange = (event) => {
setNewThought(event.target.value);
}

const handleFormCleanup = () => {
setNewThought('');
setLoading(false);
}

const onFormSubmit = (event) => {
event.preventDefault();

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

fetch('https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts', options)
.then((res) => res.json())
.then(() => fetchThoughts())
.finally(() => handleFormCleanup());
}

const handleLikes = (_id) => {
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}

fetch(`https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts/${_id}/like`, options)
.then((res) => {
return res.json();
})
.then((data) => {
const updatedThoughtList = thoughtList.map((thought) => {
if (thought._id === data._id) {
return {
...thought,
hearts: data.hearts
};
}
return thought;
});
setThoughtList(updatedThoughtList);
})
.catch((error) => console.error(error));
}

return (
<div>
Find me in src/app.js!
<div className="outer-wrapper">
<Header />
<div className="inner-wrapper">
<MainForm
newThought={newThought}
setNewThought={setNewThought}
onFormSubmit={onFormSubmit} />
<ThoughtList
loading={loading}
thoughtList={thoughtList}
handleLikes={handleLikes} />
</div>
<Footer />
</div>
);
}
}
Binary file added code/src/assets/cat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/src/assets/dog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/src/assets/githubb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/src/assets/nj.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/src/assets/sun.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/src/assets/woman.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions code/src/components/Footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import GitIcon from '../assets/githubb.png';

const Footer = () => {
return (
<footer>
<div className="links-container">
<a href="https://github.com/majazimnoch"><img src={GitIcon} className="icon-contact" alt="Github icon" /></a>
</div>
<div className="credits-container">
<p>Website designed by <span className="color1"> Maja Zimnoch </span>
Copy link

Choose a reason for hiding this comment

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

I though this looked really cool on the page

</p>
</div>
</footer>
)
}

export default Footer
18 changes: 18 additions & 0 deletions code/src/components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import Picture from '../assets/dog.png';

const Header = () => {
return (
<div className="header">
<div className="image-container">
<img
src={Picture}
alt="Happy Dog"
className="picture" />
</div>
</div>

);
}

export default Header;
56 changes: 56 additions & 0 deletions code/src/components/MainForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable max-len */
/* eslint-disable jsx-a11y/label-has-associated-control */
import React, { useState } from 'react';

const MainForm = ({ newThought, setNewThought, onFormSubmit }) => {
const [charCount, setCharCount] = useState(0);

const handleNewThoughtChange = (event) => {
const inputText = event.target.value;
setNewThought(inputText);
setCharCount(inputText.length);
};

const isOverLimit = charCount > 140;

const resetCharCount = () => {
setCharCount(0);
};

return (
<form
className="thought-form"
onSubmit={(event) => {
event.preventDefault();
onFormSubmit(event);
resetCharCount(); // Reset the character count after submitting the form
}}
// The h2 element has an aria-hidden prop set to true.
// This is used to hide the heading from screen readers, since the heading
// text is already included in the label.
// The label element also has a class name of sr-only.
// This is a utility class that is used to visually hide the label while still making it available to screen readers.
aria-label="Happy Thoughts Form">
<h2 className="header-form" aria-hidden="true">What&apos;s making you happy right now?</h2>
<label htmlFor="happy-thought-input" className="sr-only"> Share your happy thought!
</label>
<textarea
id="happy-thought-input"
placeholder="Type your idea here"
className="text-area"
value={newThought}
onChange={handleNewThoughtChange}
maxLength={1000}
aria-label="Happy thought input"
aria-invalid={isOverLimit} />
<div className="char-count" style={{ color: isOverLimit ? 'red' : 'inherit' }}>
{isOverLimit ? <span>{charCount}</span> : <span>{charCount}</span>}/140
</div>
<button className="form-btn" type="submit" disabled={isOverLimit}>
💙 Send Happy Thought! 💙
</button>
</form>
);
};

export default MainForm;
39 changes: 39 additions & 0 deletions code/src/components/ThoughtList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint no-underscore-dangle: 0 */
import React from 'react';
import { formatDistanceToNow } from 'date-fns';

const ThoughtList = ({ loading, thoughtList, handleLikes }) => {
if (loading) {
return <h3 className="loading">Tokenizing real life...</h3>
}
return (
<section className="thought-list">
{thoughtList.map((thought) => (
<div className="single-thought" key={thought._id}>
<h2>{thought.message}</h2>
<div className="like-and-time">
<div className="btn-and-counter">
<button
aria-label="Like-button"
type="button"
className="like-btn"
onClick={() => { handleLikes(thought._id, thought.hearts); }}
style={{ background: thought.hearts >= 1 ? '#9383C5' : '#56E3FA' }}>
💜
</button>
<span className="counter">x {thought.hearts}</span>
</div>
<p className="time">{formatDistanceToNow(
new Date(thought.createdAt),
Date.now(),
{ addSuffix: true }
)} ago
</p>
</div>
</div>
))}
</section>
)
}

export default ThoughtList;
Loading