From 55e01f97915e3190fa3955ba2169c795b1d89dec Mon Sep 17 00:00:00 2001 From: tildetilde Date: Mon, 28 Apr 2025 17:19:58 +0200 Subject: [PATCH 01/32] First layout --- index.html | 7 +- package.json | 5 ++ postcss.config.js | 6 ++ src/App.jsx | 5 -- src/App.tsx | 59 +++++++++++++++++ src/components/ThoughtForm.tsx | 110 ++++++++++++++++++++++++++++++++ src/components/ThoughtList.tsx | 108 +++++++++++++++++++++++++++++++ src/globals.css | 113 +++++++++++++++++++++++++++++++++ src/index.css | 3 - src/main.jsx | 12 ---- src/main.tsx | 10 +++ tailwind.config.ts | 95 +++++++++++++++++++++++++++ 12 files changed, 508 insertions(+), 25 deletions(-) create mode 100644 postcss.config.js delete mode 100644 src/App.jsx create mode 100644 src/App.tsx create mode 100644 src/components/ThoughtForm.tsx create mode 100644 src/components/ThoughtList.tsx create mode 100644 src/globals.css delete mode 100644 src/index.css delete mode 100644 src/main.jsx create mode 100644 src/main.tsx create mode 100644 tailwind.config.ts diff --git a/index.html b/index.html index d4492e94..c81d494c 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,10 @@ - Happy Thoughts + Thankful Thoughts
- + diff --git a/package.json b/package.json index 2f66d295..a5d4886f 100644 --- a/package.json +++ b/package.json @@ -10,18 +10,23 @@ "preview": "vite preview" }, "dependencies": { + "lucide-react": "^0.503.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/js": "^9.21.0", + "@tailwindcss/postcss": "^4.1.4", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.21", "eslint": "^9.21.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "postcss": "^8.5.3", + "tailwindcss": "^3.4.17", "vite": "^6.2.0" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/App.jsx b/src/App.jsx deleted file mode 100644 index 07f2cbdf..00000000 --- a/src/App.jsx +++ /dev/null @@ -1,5 +0,0 @@ -export const App = () => { - return ( -

Happy Thoughts

- ) -} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 00000000..349a21ce --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,59 @@ +"use client"; +import React, { useState } from "react"; +import ThoughtForm from "./components/ThoughtForm"; +import ThoughtList from "./components/ThoughtList"; + +export type Thought = { + id: string; + message: string; + likes: number; + timestamp: Date; +}; + +export default function App() { + const [thoughts, setThoughts] = useState([ + { + id: "1", + message: "I'm happy because we just moved into a new apartment!", + likes: 0, + timestamp: new Date(Date.now() - 30 * 1000), // 30 seconds ago + }, + { + id: "2", + message: "It's my birthday!!!", + likes: 10, + timestamp: new Date(Date.now() - 10 * 60 * 1000), // 10 minutes ago + }, + { + id: "3", + message: "I'm happy because the sun is out :)", + likes: 23, + timestamp: new Date(Date.now() - 15 * 60 * 1000), // 15 minutes ago + }, + ]); + + const addThought = (message: string) => { + const newThought: Thought = { + id: Date.now().toString(), + message, + likes: 0, + timestamp: new Date(), + }; + setThoughts([newThought, ...thoughts]); + }; + + const handleLike = (id: string) => { + setThoughts( + thoughts.map((thought) => + thought.id === id ? { ...thought, likes: thought.likes + 1 } : thought + ) + ); + }; + + return ( +
+ + +
+ ); +} diff --git a/src/components/ThoughtForm.tsx b/src/components/ThoughtForm.tsx new file mode 100644 index 00000000..180ae102 --- /dev/null +++ b/src/components/ThoughtForm.tsx @@ -0,0 +1,110 @@ +"use client"; + +import React from "react"; + +import { useState } from "react"; +import { Heart } from "lucide-react"; + +interface ThoughtFormProps { + onSubmit: (message: string) => void; +} + +export default function ThoughtForm({ onSubmit }: ThoughtFormProps) { + const [message, setMessage] = useState(""); + const [error, setError] = useState(null); + + const MAX_LENGTH = 140; + const MIN_LENGTH = 5; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (message.trim().length < MIN_LENGTH) { + setError( + `Your message is too short. Please write at least ${MIN_LENGTH} characters.` + ); + return; + } + + if (message.length > MAX_LENGTH) { + setError( + `Your message is too long. Please keep it under ${MAX_LENGTH} characters.` + ); + return; + } + + onSubmit(message); + setMessage(""); + setError(null); + }; + + const handleChange = (e: React.ChangeEvent) => { + setMessage(e.target.value); + if (error) setError(null); + }; + + const charactersLeft = MAX_LENGTH - message.length; + const isOverLimit = charactersLeft < 0; + + return ( +
+
+

+ What's making you thankful right now? +

+ +