diff --git a/src/App.js b/src/App.js index 9d9aa1c..8a5f603 100644 --- a/src/App.js +++ b/src/App.js @@ -1,18 +1,126 @@ import React, { useState } from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; -import Home from './components/Home'; -import PostDetail from './components/PostDetail'; +import PostList from './components/PostList'; +import NewPostForm from './components/NewPostForm'; +import Modal from './components/Modal'; +import './App.scss'; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faCaretDown, faCaretUp} from "@fortawesome/free-solid-svg-icons"; + +const App = () => { + const [posts, setPosts] = useState([]); + const [selectedPost, setSelectedPost] = useState(null); + const [comment, setComment] = useState(''); + + const addPost = (text) => { + const newPost = { id: Date.now(), text, comments: [], upvotes: 0, downvotes: 0 }; + setPosts([newPost, ...posts]); + }; + + const addComment = (postId, commentText) => { + const newComment = { id: Date.now(), text: commentText, upvotes: 0, downvotes: 0 }; + setPosts(posts.map(post => + post.id === postId ? { ...post, comments: [...post.comments, newComment] } : post + )); + }; + + const upvotePost = (postId) => { + setPosts(posts.map(post => + post.id === postId ? { ...post, upvotes: post.upvotes + 1 } : post + )); + }; + + const downvotePost = (postId) => { + setPosts(posts.map(post => + post.id === postId ? { ...post, downvotes: post.downvotes + 1 } : post + )); + }; + + const upvoteComment = (postId, commentId) => { + setPosts(posts.map(post => + post.id === postId ? { + ...post, + comments: post.comments.map(comment => + comment.id === commentId ? { ...comment, upvotes: comment.upvotes + 1 } : comment + ) + } : post + )); + }; + + const downvoteComment = (postId, commentId) => { + setPosts(posts.map(post => + post.id === postId ? { + ...post, + comments: post.comments.map(comment => + comment.id === commentId ? { ...comment, downvotes: comment.downvotes + 1 } : comment + ) + } : post + )); + }; + + const handlePostClick = (post) => { + setSelectedPost(post); + }; + + const handleCommentSubmit = (e) => { + e.preventDefault(); + if (comment.trim() && selectedPost) { + addComment(selectedPost.id, comment); + setComment(''); + } + }; + + const closeModal = () => { + setSelectedPost(null); + setComment(''); + }; -function App() { return ( - - - } /> - } /> - - +
+

Jodel Clone

+ + + + {selectedPost && ( +
+

{selectedPost.text}

+
+ setComment(e.target.value)} + placeholder="Kommentieren..." + /> + +
+
+ {selectedPost.comments.map((comment) => ( +
+

{comment.text}

+
+ + {comment.upvotes - comment.downvotes} + +
+
+ ))} +
+
+ )} +
+
); } - -export default App; +export default App; \ No newline at end of file diff --git a/src/App.scss b/src/App.scss index 0c38b59..f4a13ea 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,33 +1,160 @@ body { font-family: Arial, sans-serif; + background-color: #f6f7f9; + color: #4a4a4a; + padding: 20px; } -.vote { - margin-left: 10px; - display: inline-block; - font-size: 20px; - color: #999; -} - -.vote:hover { - cursor: pointer; -} - -.container { - max-width: 600px; +.app { + max-width: 800px; margin: 0 auto; + padding: 20px; + background-color: white; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } -.post { - background-color: #fff; - border: 1px solid #ddd; - padding: 10px; +h1 { + color: #ff9908; + text-align: center; + font-weight: normal; margin-bottom: 20px; } -.comment { - background-color: #fff; - border: 1px solid #ddd; - padding: 10px; +.new-post-form { + display: flex; + flex-direction: column; margin-bottom: 20px; + + input[type="text"] { + padding: 15px; + margin-bottom: 10px; + border: 1px solid #ddd; + border-radius: 10px; + font-size: 1em; + } + + button { + padding: 15px; + background-color: #ff9908; + color: white; + border: none; + border-radius: 10px; + font-size: 1em; + cursor: pointer; + + &:hover { + background-color: #e68a00; + } + } +} + +.post-list { + .post { + display: flex; + justify-content: space-between; + align-items: flex-start; + border: 1px solid #ddd; + margin: 10px 0; + padding: 15px; + border-radius: 10px; + background-color: #fff; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); + + .post-content { + flex: 1; + margin-right: 20px; + } + + .votes { + display: flex; + flex-direction: column; + align-items: center; + + button { + padding: 10px; + margin-bottom: 5px; + background-color: transparent; + color: #ff9908; + border: none; + cursor: pointer; + font-size: 1.5em; + + &:hover { + color: #e68a00; + } + } + + span { + font-size: 1.2em; + margin-bottom: 5px; + } + } + + .comment-form { + display: flex; + margin-top: 10px; + + input[type="text"] { + padding: 10px; + flex: 1; + border: 1px solid #ddd; + border-radius: 10px; + margin-right: 10px; + font-size: 1em; + } + + button { + padding: 10px; + background-color: #ff9908; + color: white; + border: none; + border-radius: 10px; + cursor: pointer; + font-size: 1em; + + &:hover { + background-color: #e68a00; + } + } + } + + .comments { + margin-top: 10px; + + .comment { + padding: 10px; + border: 1px solid #eee; + border-radius: 10px; + background-color: #fafafa; + margin-bottom: 10px; + + .votes { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 5px; + + button { + padding: 10px; + margin-bottom: 5px; + background-color: transparent; + color: #ff9908; + border: none; + cursor: pointer; + font-size: 1.5em; + + &:hover { + color: #e68a00; + } + } + + span { + font-size: 1em; + margin-bottom: 5px; + } + } + } + } + } } diff --git a/src/components/Home.js b/src/components/Home.js deleted file mode 100644 index cf26b80..0000000 --- a/src/components/Home.js +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from 'react'; -import WritePost from './WritePost'; -import '../App.scss'; -import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import {faCaretDown, faCaretUp} from "@fortawesome/free-solid-svg-icons"; // Importiere das Styling - -function Home() { - const [posts, setPosts] = useState([]); - - const handlePost = function(content) { - const newPost = { - id: posts.length + 1, - content: content, - upvotes: 0, - downvotes: 0 - }; - setPosts([...posts, newPost]); - }; - - return ( -
{/* Verwende die Klasse container */} -

Jodel Clone

- - {posts.map(post => ( -
{/* Verwende die Klasse post */} -

{post.content}

-
- console.log("upvote")} /> - console.log("downvote")} /> - {post.upvotes - post.downvotes} -
-
- ))} -
- ); -} - -export default Home; \ No newline at end of file diff --git a/src/components/Modal.js b/src/components/Modal.js new file mode 100644 index 0000000..8023ebe --- /dev/null +++ b/src/components/Modal.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import './Modal.scss'; + +const Modal = ({ isOpen, onClose, children }) => { + if (!isOpen) return null; + + return ( +
+
+ + {children} +
+
+ ); +}; + +export default Modal; diff --git a/src/components/Modal.scss b/src/components/Modal.scss new file mode 100644 index 0000000..64d70e5 --- /dev/null +++ b/src/components/Modal.scss @@ -0,0 +1,34 @@ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; +} + +.modal { + background: white; + padding: 20px; + border-radius: 10px; + width: 90%; + max-width: 600px; + position: relative; +} + +.close-button { + position: absolute; + top: 10px; + right: 10px; + background: transparent; + border: none; + font-size: 1.5em; + cursor: pointer; + + &:hover { + color: #ff9908; + } +} diff --git a/src/components/NewPostForm.js b/src/components/NewPostForm.js new file mode 100644 index 0000000..081ab7e --- /dev/null +++ b/src/components/NewPostForm.js @@ -0,0 +1,27 @@ +import React, { useState } from 'react'; + +const NewPostForm = ({ addPost }) => { + const [text, setText] = useState(''); + + const handleSubmit = (e) => { + e.preventDefault(); + if (text.trim()) { + addPost(text); + setText(''); + } + }; + + return ( +
+ setText(e.target.value)} + placeholder="Schreibe einen neuen Jodel..." + /> + +
+ ); +}; + +export default NewPostForm; diff --git a/src/components/Post.js b/src/components/Post.js index 4e992bf..43b73e0 100644 --- a/src/components/Post.js +++ b/src/components/Post.js @@ -1,34 +1,24 @@ -import React, { useState } from 'react'; +import React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCaretUp, faCaretDown } from '@fortawesome/free-solid-svg-icons'; -function Post({ post }) { - const [upvotes, setUpvotes] = useState(post.upvotes); - const [downvotes, setDownvotes] = useState(post.downvotes); - - const handleUpvote = () => { - // Logic to handle upvote - setUpvotes(upvotes + 1); - }; - - const handleDownvote = () => { - // Logic to handle downvote - setDownvotes(downvotes + 1); - }; - +const Post = ({ post, upvotePost, downvotePost }) => { return (
-

{post.content}

-
- - {upvotes} +
+

{post.text}

-
- - {downvotes} +
+ + {post.upvotes - post.downvotes} +
); -} +}; -export default Post; \ No newline at end of file +export default Post; diff --git a/src/components/PostDetail.js b/src/components/PostDetail.js deleted file mode 100644 index 72280d8..0000000 --- a/src/components/PostDetail.js +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useState } from 'react'; -import WriteComment from './WriteComment'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCaretUp, faCaretDown } from '@fortawesome/free-solid-svg-icons'; -import '../App.scss'; // Importiere das Styling - -function PostDetail() { - const [comments, setComments] = useState([]); - - const handleComment = function(comment) { - setComments([...comments, comment]); - }; - - const calculateVotes = function(comment) { - return comment.upvotes - comment.downvotes; - }; - - const handleVote = function(index, type) { - const updatedComments = [...comments]; - if (type === 'upvote') { - updatedComments[index].upvotes++; - } else if (type === 'downvote') { - updatedComments[index].downvotes++; - } - setComments(updatedComments); - }; - - return ( -
{/* Verwende die Klasse container */} -

Post Detail

-

Comments

- {comments.map((comment, index) => ( -
{/* Verwende die Klasse comment */} -

{comment.content}

-
- handleVote(index, 'upvote')} /> - handleVote(index, 'downvote')} /> - {calculateVotes(comment)} -
-
- ))} - -
- ); -} - -export default PostDetail; diff --git a/src/components/PostList.js b/src/components/PostList.js new file mode 100644 index 0000000..d92ae25 --- /dev/null +++ b/src/components/PostList.js @@ -0,0 +1,20 @@ +import React from 'react'; +import Post from './Post'; + +const PostList = ({ posts, upvotePost, downvotePost, onPostClick }) => { + return ( +
+ {posts.map(post => ( +
onPostClick(post)}> + +
+ ))} +
+ ); +}; + +export default PostList; diff --git a/src/components/WriteComment.js b/src/components/WriteComment.js deleted file mode 100644 index 8d9b1b3..0000000 --- a/src/components/WriteComment.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react'; - -function WriteComment({ onComment }) { - const [content, setContent] = useState(''); - - const handleChange = function(e) { - setContent(e.target.value); - }; - - const handleSubmit = function(e) { - e.preventDefault(); - onComment({ content: content, upvotes: 0, downvotes: 0 }); - setContent(''); - }; - - return ( -
-

Write a Comment

-
-