fixed filtering and moved refersh button into header, added css modules for components

This commit is contained in:
David Katrinka 2025-07-24 18:20:24 +02:00
parent d3f26083de
commit 12e5665a1c
10 changed files with 97 additions and 83 deletions

View File

@ -5,61 +5,11 @@
box-sizing: border-box; box-sizing: border-box;
} }
.block{
border-radius: 10px;
background-color: #ffffff;
border: 1px solid #000000;
width: 100%;
padding: 15px;
margin-bottom: 30px;
}
.refresh-btn{
padding: 5px;
background-color: #0000ff;
color: #ffffff;
border-radius: 10px;
}
.refresh-btn:hover{
opacity: 0.7;
cursor: pointer;
}
.post{
display: flex;
border-bottom: 1px solid #b3b3b3;
border-radius: 10px;
margin-bottom: 5px;
cursor: pointer;
}
.post-id{
padding: 5px;
font-size: 24px;
}
.post-body{
padding: 5px;
}
.post:hover{
background-color: #b3b3b3;
}
.post-title{
font-size: 24px;
font-weight: 500;
}
.refresh-and-search{
display: flex;
justify-content: space-between;
align-items: center;
}
.refresh-and-search input{
border-radius: 10px;
padding: 5px;
}

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import './App.css' import './App.css'
import Header from './components/Header' import Header from './components/Header'
import PostList from './components/PostList' import PostList from './components/PostList'
@ -7,7 +7,7 @@ import axios from 'axios'
function App() { function App() {
const [allPosts, setAllPosts] = useState<Post[]>([]);
const [posts, setPosts] = useState<Post[]>([]); const [posts, setPosts] = useState<Post[]>([]);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
@ -16,7 +16,7 @@ function App() {
const fetchPosts = async () => { const fetchPosts = async () => {
const response = await axios.get<Post[]>("https://jsonplaceholder.typicode.com/posts"); const response = await axios.get<Post[]>("https://jsonplaceholder.typicode.com/posts");
setAllPosts(response.data);
setPosts(response.data); setPosts(response.data);
console.log("Posts fetched:", response.data); console.log("Posts fetched:", response.data);
@ -29,20 +29,34 @@ function App() {
}, []) }, [])
useEffect(() => {
const normalize = (str: string) => str.replace(/\s+/g, " ").toLowerCase(); const normalize = (str: string) => str.replace(/\s+/g, " ").toLowerCase();
if (searchTerm) { const filteredPosts = useMemo(() => {
const filteredPosts = allPosts.filter(post => return posts.filter(post => {
normalize(post.body).includes(searchTerm.toLowerCase()) || post.title.toLowerCase().includes(searchTerm.toLowerCase()) if (searchTerm == "") {
); return true;
setPosts(filteredPosts);
} else {
setPosts(allPosts);
} }
}, [searchTerm, allPosts]);
if (normalize(post.body).includes(searchTerm.toLowerCase()) || post.title.toLowerCase().includes(searchTerm.toLowerCase())) {
return true;
} else {
return false;
}
}
);
},[posts, searchTerm]);
@ -51,7 +65,7 @@ function App() {
<Header setRefresh={fetchPosts} searchTerm={searchTerm} setSearchTerm={setSearchTerm} /> <Header setRefresh={fetchPosts} searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<PostList posts={posts} /> <PostList posts={filteredPosts} />
</> </>

View File

@ -0,0 +1,8 @@
.block {
border-radius: 10px;
background-color: #ffffff;
border: 1px solid #000000;
width: 100%;
padding: 15px;
margin-bottom: 30px;
}

View File

@ -1,10 +1,11 @@
import type { BlockProps } from "./types"; import type { BlockProps } from "./types";
import styles from "./Block.module.css";
function Block({title, children}:BlockProps) { function Block({title, children}:BlockProps) {
return ( return (
<div className="block"> <div className={styles.block}>
<h1 className="title">{title}</h1> <h1>{title}</h1>
<div className="block-children"> <div>
{children} {children}
</div> </div>
</div> </div>

View File

@ -0,0 +1,22 @@
.refreshBtn {
padding: 5px;
background-color: #0000ff;
color: #ffffff;
border-radius: 10px;
}
.refreshBtn:hover {
opacity: 0.7;
cursor: pointer;
}
.refreshAndSearch {
display: flex;
justify-content: space-between;
align-items: center;
}
.refreshAndSearch input {
border-radius: 10px;
padding: 5px;
}

View File

@ -1,15 +1,15 @@
import Block from "./Block"; import Block from "./Block";
import RefreshButton from "./RefreshButton";
import type { HeaderProps } from "./types"; import type { HeaderProps } from "./types";
import styles from "./Header.module.css";
function Header({setRefresh, searchTerm, setSearchTerm}:HeaderProps) { function Header({setRefresh, searchTerm, setSearchTerm}:HeaderProps) {
return ( return (
<Block title='Header'> <Block title="Header">
<hr></hr> <hr></hr>
<div className="refresh-and-search"> <div className={styles.refreshAndSearch}>
<RefreshButton setRefresh={setRefresh}/> <button className={styles.refreshBtn} onClick={() => setRefresh()}>Refresh</button>
<input type="text" name="search" id="search" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="search..."/> <input type="text" name="search" id="search" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="search..."/>
</div> </div>

View File

@ -0,0 +1,25 @@
.post {
display: flex;
border-bottom: 1px solid #b3b3b3;
border-radius: 10px;
margin-bottom: 5px;
cursor: pointer;
}
.postId {
padding: 5px;
font-size: 24px;
}
.postBody {
padding: 5px;
}
.post:hover {
background-color: #b3b3b3;
}
.postTitle {
font-size: 24px;
font-weight: 500;
}

View File

@ -1,10 +1,11 @@
import type { postItemProps } from "./types"; import type { postItemProps } from "./types";
import styles from "./PostItem.module.css";
function PostItem({post}:postItemProps){ function PostItem({post}:postItemProps){
return ( return (
<div className="post"> <div className={styles.post}>
<div className="post-id">{post.id}</div> <div className="post-body" onClick={() => alert("clicked on post: " + post.title)}> <span className="post-title">{post.title}</span> <br /> <br />{post.body} </div> <div className={styles.postId}>{post.id}</div> <div className={styles.postBody} onClick={() => alert("clicked on post: " + post.title)}> <span className={styles.postTitle}>{post.title}</span> <br /> <br />{post.body} </div>
</div> </div>
) )
} }

View File

@ -1,9 +0,0 @@
import type { RefreshButtonProps } from "./types";
function RefreshButton({setRefresh}: RefreshButtonProps) {
return (
<button className="refresh-btn" onClick={() => setRefresh()}>Refresh</button>
)
}
export default RefreshButton;

View File

@ -18,3 +18,5 @@ body {