diff --git a/src/App.css b/src/App.css index d97beb4e..daeb9579 100644 --- a/src/App.css +++ b/src/App.css @@ -71,4 +71,11 @@ .purple { color: purple +} + +.liked-count { + text-align: center; + font-size: 2rem; + font-weight: bold; + margin: 0.5rem 0 0; } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 14a7f684..6e5f9541 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,30 @@ -import './App.css'; +import "./App.css"; +import { useState } from "react"; +import ChatLog from "./components/ChatLog"; +import entriesData from "./data/messages.json"; const App = () => { + const [entries, setEntries] = useState(entriesData); + + const likedCount = entries.reduce((sum, e) => sum + (e.liked ? 1 : 0), 0); + + function handleToggleLike(entryId) { + setEntries((prev) => + prev.map((e) => + e.id === entryId ? { ...e, liked: !e.liked } : e + ) + ); + } + return (

Application title

+

{likedCount} ❤️s

+
- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index fe05efa0..b44d485c 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,21 +1,39 @@ -import './ChatEntry.css'; +import PropTypes from "prop-types"; +import TimeStamp from "./TimeStamp"; +import "./ChatEntry.css"; + +const ChatEntry = ({ id, sender, body, timeStamp, liked = false, onToggleLike }) => { + const showLikeButton = + typeof id === "number" && typeof onToggleLike === "function"; -const ChatEntry = () => { return ( - // Replace the outer tag name with a semantic element that fits our use case - -

Replace with name of sender

+
+

{sender}

+
-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- +

{body}

+ +

+ +

+ + {showLikeButton && ( + + )}
- +
); }; ChatEntry.propTypes = { - // Fill with correct proptypes + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + id: PropTypes.number, + liked: PropTypes.bool, + onToggleLike: PropTypes.func, }; -export default ChatEntry; +export default ChatEntry; \ No newline at end of file diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 00000000..b6491169 --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,48 @@ +import PropTypes from "prop-types"; +import ChatEntry from "./ChatEntry"; +import "./ChatLog.css"; + +function ChatLog({ entries, onToggleLike }) { + return ( +
+ {entries.map((entry) => ( + + ))} +
+ ); +} + +ChatLog.propTypes = { + entries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, + }) + ).isRequired, + onToggleLike: PropTypes.func.isRequired, +}; + +ChatLog.propTypes = { + entries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool, + }) + ).isRequired, +}; + +export default ChatLog; \ No newline at end of file