From 54135278fc93b28b5a6a26ef10f22ae8a2d9d456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20T=C3=BCrker=20Erdem?= Date: Sat, 14 Dec 2024 17:15:42 +0300 Subject: [PATCH 1/2] user search is implemented Co-authored-by: Deniz Co-authored-by: Ersel --- .../SearchResults/SearchResults.tsx | 207 +++++++++++------- .../UserDisplayer/UserDisplayer.module.css | 10 + .../UserDisplayer/UserDisplayer.tsx | 85 +++++++ 3 files changed, 224 insertions(+), 78 deletions(-) create mode 100644 3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.module.css create mode 100644 3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.tsx diff --git a/3Design/frontend/src/components/SearchResults/SearchResults.tsx b/3Design/frontend/src/components/SearchResults/SearchResults.tsx index 6a20cf5d..3da2e5aa 100644 --- a/3Design/frontend/src/components/SearchResults/SearchResults.tsx +++ b/3Design/frontend/src/components/SearchResults/SearchResults.tsx @@ -1,79 +1,130 @@ -import React, { useEffect, useState } from 'react' -import styles from "./SearchResults.module.css"; -import { useParams } from 'react-router-dom'; -import { DPost } from '../interfaces'; -import { Skeleton } from 'antd'; -import GalleryPost from '../GalleryPost/Clickable/GalleryPost'; -import DiscussionPost from '../DiscussionPost/Clickable/DiscussionPost'; -import SideBar from '../SideBar/SideBar'; -import PageHeader from '../PageHeader/PageHeader'; -import axios from 'axios'; - -const SearchResults = () => { - const {query} = useParams(); - const [searchResults, setSearchResults] = useState([]); - const [searchLoading, setSearchLoading] = useState(true); - - if (query == undefined || query == null){ - window.location.href = "/home"; - } - - useEffect(() => { - fetchPostData(); - }, []) - - const fetchPostData = async () => { - try{ - const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/posts?param=${query}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("jwt_token")}` - } - }); - setSearchResults(res.data); - } - catch(e){ - - } - finally{ - setSearchLoading(false); - } - - } - - - return ( - <> - -
- -
-
-
-

Results for: {query}

-
-
- { searchLoading ? - ( -
- -
- ) : - searchResults.length == 0 ? -

No results for this query

: - searchResults.map((item, index) => ( - item.isVisualPost ? - - : - - )) - } -
- -
- - - - ) -} - +import React, { useEffect, useState } from 'react' +import styles from "./SearchResults.module.css"; +import { useParams } from 'react-router-dom'; +import { DPost } from '../interfaces'; +import { Skeleton } from 'antd'; +import GalleryPost from '../GalleryPost/Clickable/GalleryPost'; +import DiscussionPost from '../DiscussionPost/Clickable/DiscussionPost'; +import SideBar from '../SideBar/SideBar'; +import PageHeader from '../PageHeader/PageHeader'; +import axios from 'axios'; +import UserDisplayer from './UserDisplayer/UserDisplayer'; + +interface UserResponse{ + userId : number; + email : string; + nickName : string; + profilePictureUrl : string; + experience : number; + isFollowed : boolean; +} + +const SearchResults = () => { + const {query} = useParams(); + const [searchResults, setSearchResults] = useState([]); + const [searchLoading, setSearchLoading] = useState(true); + const [searchType, setSearchType] = useState(true); + const [userResults, setUserResults] = useState([]); + + if (query == undefined || query == null){ + window.location.href = "/home"; + } + + useEffect(() => { + fetchData(); + }, [searchType]) + + const fetchData = async () => { + setSearchLoading(true); + if (!searchType){ + try{ + const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/users/search?keyword=${query}`, { + headers: { + Authorization: `Bearer ${localStorage.getItem("jwt_token")}` + } + }); + setUserResults(res.data); + } + catch(e){ + + } + finally{ + setSearchLoading(false); + return; + } + } + try{ + const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/posts?param=${query}`, { + headers: { + Authorization: `Bearer ${localStorage.getItem("jwt_token")}` + } + }); + setSearchResults(res.data); + } + catch(e){ + + } + finally{ + setSearchLoading(false); + } + + } + + const searchTypeHandler = (x: boolean) => { + if (searchType == x){ + return; + } + setSearchType(x); + } + + + return ( + <> + +
+ +
+
+
+

Results for: {query}

+
+
+ + +
+
+ { searchLoading ? + ( +
+ +
+ ) : + searchType ? + ( + searchResults.length == 0 ? +

No post results for this query

: + searchResults.map((item, index) => ( + item.isVisualPost ? + + : + + )) + ) : + ( + userResults.length == 0 ? +

No user results for this query

: + userResults.map((item, index) => ( + + )) + ) + } +
+ +
+ + + + ) +} + export default SearchResults \ No newline at end of file diff --git a/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.module.css b/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.module.css new file mode 100644 index 00000000..d73ac79b --- /dev/null +++ b/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.module.css @@ -0,0 +1,10 @@ + +.userCard{ + background-color: white; + padding: 20px; + width: 100%; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1); + display: flex; + gap: 20px; +} \ No newline at end of file diff --git a/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.tsx b/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.tsx new file mode 100644 index 00000000..1dcb6a4d --- /dev/null +++ b/3Design/frontend/src/components/SearchResults/UserDisplayer/UserDisplayer.tsx @@ -0,0 +1,85 @@ +import { Avatar } from '@mui/material'; +import { Button, message } from 'antd'; +import axios from 'axios'; +import React, { useState } from 'react' +import styles from "./UserDisplayer.module.css" +interface UserResponse{ + userId : number; + email : string; + nickName : string; + profilePictureUrl : string; + experience : number; + isFollowed : boolean; +} + +interface Props{ + data: UserResponse +} + +const UserDisplayer = ({data}: Props) => { + const [stateData, setStateData] = useState(data); + const [followRequesting, setFollowRequesting] = useState(false); + + const handleFollowLogic = async (e:any,type:boolean) => { + e.stopPropagation(); + setFollowRequesting(true); + if (type){ + try{ + await axios.post(`${process.env.REACT_APP_API_URL}/api/v1/users/follow`, + null, + { + headers: {Authorization: `Bearer ${localStorage.getItem("jwt_token")}`}, + params: {followedUserId: stateData.userId} + } + ); + setStateData(prev => ({...prev, isFollowed: true})); + } + catch(e){ + message.error("Couldn't follow user."); + setStateData(prev => ({...prev, isFollowed: false})); + } + finally{ + setFollowRequesting(false); + return; + } + } + try{ + await axios.delete(`${process.env.REACT_APP_API_URL}/api/v1/users/unfollow`, + { + headers: {Authorization: `Bearer ${localStorage.getItem("jwt_token")}`}, + params: {followedUserId: stateData.userId} + } + ); + setStateData(prev => ({...prev, isFollowed: false})); + } + catch(e){ + message.error("Couldn't unfollow user."); + setStateData(prev => ({...prev, isFollowed: true})); + } + finally{ + setFollowRequesting(false); + return; + } + } + + return ( +
window.location.href = `/profile/${stateData.userId}`} className={`clickable-post ${styles.userCard}`}> +
+ +

{stateData.nickName}

+
+
+ + { + stateData.userId != parseInt(localStorage.getItem("user_id") ?? "-1") && + (stateData.isFollowed ? + : + + ) + } +
+
+ ) +} + +export default UserDisplayer \ No newline at end of file From 16f54fd01f3af17d4dd56c9f04e97b3fe834553e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20T=C3=BCrker=20Erdem?= Date: Sat, 14 Dec 2024 17:19:40 +0300 Subject: [PATCH 2/2] post search pagination is implemented Co-authored-by: Deniz Co-authored-by: Ersel --- .../SearchResults/SearchResults.tsx | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/3Design/frontend/src/components/SearchResults/SearchResults.tsx b/3Design/frontend/src/components/SearchResults/SearchResults.tsx index 3da2e5aa..8f0e8472 100644 --- a/3Design/frontend/src/components/SearchResults/SearchResults.tsx +++ b/3Design/frontend/src/components/SearchResults/SearchResults.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react' import styles from "./SearchResults.module.css"; -import { useParams } from 'react-router-dom'; +import { useParams, useSearchParams } from 'react-router-dom'; import { DPost } from '../interfaces'; -import { Skeleton } from 'antd'; +import { Pagination, Skeleton } from 'antd'; import GalleryPost from '../GalleryPost/Clickable/GalleryPost'; import DiscussionPost from '../DiscussionPost/Clickable/DiscussionPost'; import SideBar from '../SideBar/SideBar'; @@ -26,6 +26,14 @@ const SearchResults = () => { const [searchType, setSearchType] = useState(true); const [userResults, setUserResults] = useState([]); + const [searchParams] = useSearchParams(); + const passedPageNumber = searchParams.get("p") ?? ""; + let pageNumber = 1; + if (/^\d+$/.test(passedPageNumber)){ + pageNumber = parseInt(passedPageNumber); + } + const pageSize = 3; + if (query == undefined || query == null){ window.location.href = "/home"; } @@ -103,12 +111,16 @@ const SearchResults = () => { ( searchResults.length == 0 ?

No post results for this query

: - searchResults.map((item, index) => ( - item.isVisualPost ? - - : - - )) + <> + {searchResults.slice((pageNumber-1)*pageSize, (pageNumber)*pageSize).map((item, index) => ( + item.isVisualPost ? + + : + + )) + } + window.location.href = `/search/${query}?p=${x}`}/> + ) : ( userResults.length == 0 ?