Skip to content

Commit 530f35c

Browse files
authored
Merge pull request #414 from Sambashivarao-Boyina/SearchFeature
The Search Feature of Skills cirteria implement from one skill to mutliple Skills
2 parents ab04ce2 + 4a4e6da commit 530f35c

File tree

4 files changed

+159
-52
lines changed

4 files changed

+159
-52
lines changed

src/Homepage.jsx

+31-15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ function App() {
1717
const [currentPage, setCurrentPage] = useState(1);
1818
const [shuffledProfiles, setShuffledProfiles] = useState([]);
1919
const [loadingProfiles, setLoadingProfiles] = useState(false);
20-
const recordsPerPage = 20;
20+
21+
const recordsPerPage = 20;
2122

2223
const currentUrl = window.location.pathname;
2324
useEffect(() => {
@@ -67,20 +68,35 @@ function App() {
6768
.replace(/\s+/g, ' ')
6869
.trim();
6970

70-
const normalizedValue = normalizeString(value);
71-
72-
const filteredResults = combinedData.filter((user) => {
73-
if (criteria === 'name') {
74-
return normalizeString(user.name).includes(normalizedValue);
75-
} else if (criteria === 'location') {
76-
return normalizeString(user.location).includes(normalizedValue);
77-
} else if (criteria === 'skill') {
78-
return user.skills.some((skill) => normalizeString(skill).includes(normalizedValue));
79-
}
80-
return false;
81-
});
82-
83-
setProfiles(filteredResults);
71+
if (criteria !== 'skill') {
72+
let normalizedValue = normalizeString(value);
73+
74+
const filteredResults = combinedData.filter((user) => {
75+
if (criteria === 'name') {
76+
return normalizeString(user.name).includes(normalizedValue);
77+
} else if (criteria === 'location') {
78+
return normalizeString(user.location).includes(normalizedValue);
79+
}
80+
return false;
81+
});
82+
83+
setProfiles(filteredResults);
84+
} else if(criteria === "skill") {
85+
// if criteria is skill the it will filter the data
86+
if(value.length > 0){
87+
let setOfSearchSkills = new Set(value.map(skill => skill.toLowerCase())); // Convert searchSkills to lowercase for comparison
88+
const filteredUsers = shuffledProfiles.filter(user =>
89+
user.skills.some(skill => setOfSearchSkills.has(skill.toLowerCase())) // Ensure skill is also lowercase
90+
);
91+
setProfiles(filteredUsers);
92+
} else {
93+
//if skills are empty it will reset the data
94+
setProfiles(shuffledProfiles)
95+
}
96+
} else {
97+
setProfiles(false);
98+
}
99+
84100
setSearching(true);
85101
};
86102

src/components/Search/Search.jsx

+96-37
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,78 @@ import React, { useState, useRef, useEffect } from 'react';
22
import useDebounce from '../../hooks/useDebouncer';
33
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
44
import { faMagnifyingGlass, faXmark } from '@fortawesome/free-solid-svg-icons';
5+
import SearchSkillsContainer from './SearchSkillsContainer';
56

67
function Search({ onSearch }) {
78
const [searchValue, setSearchValue] = useState('');
89
const [prevSearchValue, setPrevSearchValue] = useState('');
910
const [searchCriteria, setSearchCriteria] = useState('name');
1011
const searchInput = useRef(null);
1112

13+
const [searchSkills, setSearchSkills] = useState([]);
14+
15+
const normalizeString = (str) =>
16+
str
17+
.toLowerCase()
18+
.replace(/\s*,\s*/g, ' ')
19+
.replace(/\s+/g, ' ')
20+
.trim();
21+
1222
const handleInputChange = (event) => {
1323
setSearchValue(event.target.value);
1424
};
1525

1626
const handleCriteriaChange = (event) => {
27+
if(event.target.value !== "skill") {
28+
handleClearSkills();
29+
}
1730
setSearchCriteria(event.target.value);
31+
1832
};
1933

2034
const debouncedValue = useDebounce(searchValue, 500);
2135

2236
useEffect(() => {
23-
if (debouncedValue !== prevSearchValue) {
37+
if (debouncedValue !== prevSearchValue && searchCriteria !== "skill") {
2438
onSearch({ value: debouncedValue, criteria: searchCriteria });
2539
setPrevSearchValue(debouncedValue);
2640
}
2741
// eslint-disable-next-line react-hooks/exhaustive-deps
2842
}, [debouncedValue]);
2943

3044
const handleSearch = () => {
31-
if (searchValue !== prevSearchValue) {
45+
if ((searchValue !== prevSearchValue && searchCriteria !== "skill") || searchValue.trim() === "") {
3246
onSearch({ value: searchValue, criteria: searchCriteria });
3347
setPrevSearchValue(searchValue);
3448
}
3549
};
3650

37-
const handleSearchOnEnter = (e) => {
38-
if (e.keyCode === 13) {
39-
handleSearch();
51+
const handleSearchOnEnter = (event) => {
52+
if (event.keyCode === 13) {
53+
//if searchCriteia is skill then it will add that skill to searchSkills
54+
if (searchCriteria ==='skill') {
55+
let searchvalue = normalizeString(searchValue);
56+
searchvalue = searchvalue.trim();
57+
if (searchvalue.length > 0) {
58+
var set = new Set(searchSkills);
59+
set.add(searchvalue);
60+
setSearchSkills((prev) => [...set]);
61+
}
62+
setSearchValue('');
63+
} else {
64+
handleSearch();
65+
}
4066
}
4167
};
4268

69+
useEffect(() => {
70+
//when new skill is added to searchSkill it will filter the data
71+
if(searchCriteria === "skill") {
72+
onSearch({ value: searchSkills, criteria: searchCriteria });
73+
setPrevSearchValue('');
74+
}
75+
}, [searchSkills]);
76+
4377
const handleSearchButtonClick = () => {
4478
handleSearch();
4579
};
@@ -53,45 +87,70 @@ function Search({ onSearch }) {
5387
}
5488
};
5589

90+
//Reset the profiles
91+
const handleClearSkills = () => {
92+
setSearchSkills([]);
93+
setSearchValue('');
94+
setPrevSearchValue('');
95+
onSearch({ value: [], criteria: "skill" });
96+
searchInput.current.focus();
97+
};
98+
5699
useEffect(() => {
57100
searchInput.current.focus();
58101
}, []);
59102

60103
return (
61-
<div className="relative flex items-center justify-end space-x-4 pb-6">
62-
<select
63-
className="focus:border-primaryFocus focus:bg-primaryLight dark:focus:border-secondaryFocus dark:focus:bg-secondaryLight h-12 rounded-lg border-2 border-borderSecondary bg-primaryColor px-4 py-3 text-base text-secondaryColor outline-none dark:border-borderColor dark:bg-secondaryColor dark:text-white"
64-
value={searchCriteria}
65-
onChange={handleCriteriaChange}
66-
>
67-
<option value="name">Name</option>
68-
<option value="location">Location</option>
69-
<option value="skill">Skill</option>
70-
</select>
71-
<div className="relative w-full">
72-
<input
73-
className="focus:border-primaryFocus focus:bg-primaryLight dark:focus:border-secondaryFocus dark:focus:bg-secondaryLight h-12 w-full rounded-lg border-2 border-borderSecondary bg-primaryColor px-4 py-3 pr-12 font-spaceMono text-base text-secondaryColor outline-none dark:border-borderColor dark:bg-secondaryColor dark:text-white"
74-
ref={searchInput}
75-
type="text"
76-
onChange={handleInputChange}
77-
value={searchValue}
78-
placeholder={`Search user by ${searchCriteria}`}
79-
onKeyDown={handleSearchOnEnter}
80-
/>
81-
{searchValue ? (
82-
<FontAwesomeIcon
83-
onClick={handleDeleteButtonClick}
84-
className="hover:text-primaryFocus dark:hover:text-secondaryFocus absolute right-4 top-1/2 -translate-y-1/2 scale-125 transform cursor-pointer text-xl text-secondaryColor dark:text-white"
85-
icon={faXmark}
104+
<div className="relative pb-6">
105+
<div className="relative flex items-center justify-end space-x-4 ">
106+
<select
107+
className="focus:border-primaryFocus focus:bg-primaryLight dark:focus:border-secondaryFocus dark:focus:bg-secondaryLight h-12 rounded-lg border-2 border-borderSecondary bg-primaryColor px-4 py-3 text-base text-secondaryColor outline-none dark:border-borderColor dark:bg-secondaryColor dark:text-white"
108+
value={searchCriteria}
109+
onChange={handleCriteriaChange}
110+
>
111+
<option value="name">Name</option>
112+
<option value="location">Location</option>
113+
<option value="skill">Skill</option>
114+
</select>
115+
<div className="relative w-full">
116+
<input
117+
className="focus:border-primaryFocus focus:bg-primaryLight dark:focus:border-secondaryFocus dark:focus:bg-secondaryLight h-12 w-full rounded-lg border-2 border-borderSecondary bg-primaryColor px-4 py-3 pr-12 font-spaceMono text-base text-secondaryColor outline-none dark:border-borderColor dark:bg-secondaryColor dark:text-white"
118+
ref={searchInput}
119+
type="text"
120+
onChange={handleInputChange}
121+
value={searchValue}
122+
placeholder={`Search user by ${searchCriteria}`}
123+
onKeyDown={handleSearchOnEnter}
86124
/>
87-
) : (
88-
<FontAwesomeIcon
89-
onClick={handleSearchButtonClick}
90-
className="hover:text-primaryFocus dark:hover:text-secondaryFocus absolute right-4 top-1/2 -translate-y-1/2 transform cursor-pointer text-xl text-secondaryColor dark:text-white"
91-
icon={faMagnifyingGlass}
92-
/>
93-
)}
125+
{searchValue ? (
126+
<FontAwesomeIcon
127+
onClick={handleDeleteButtonClick}
128+
className="hover:text-primaryFocus dark:hover:text-secondaryFocus absolute right-4 top-1/2 -translate-y-1/2 scale-125 transform cursor-pointer text-xl text-secondaryColor dark:text-white"
129+
icon={faXmark}
130+
/>
131+
) : (
132+
<FontAwesomeIcon
133+
onClick={handleSearchButtonClick}
134+
className="hover:text-primaryFocus dark:hover:text-secondaryFocus absolute right-4 top-1/2 -translate-y-1/2 transform cursor-pointer text-xl text-secondaryColor dark:text-white"
135+
icon={faMagnifyingGlass}
136+
/>
137+
)}
138+
</div>
94139
</div>
140+
141+
142+
{/* This block show the skills the user searched */}
143+
{searchCriteria === 'skill' && searchSkills && searchSkills.length > 0 ? (
144+
<>
145+
<button
146+
onClick={handleClearSkills}
147+
className="m-2 cursor-pointer self-end rounded-md bg-white p-2 font-semibold dark:bg-[#1E2A47] dark:text-white"
148+
>
149+
Clear All
150+
</button>
151+
<SearchSkillsContainer searchSkills={searchSkills} setSearchSkills={setSearchSkills} />
152+
</>
153+
) : null}
95154
</div>
96155
);
97156
}

src/components/Search/SearchSkill.jsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { FaTimes } from 'react-icons/fa';
2+
3+
//it is the design of each skill box
4+
export default function SearchSkill({ skill, setSearchSkills }) {
5+
const handleRemoveSkill = (value) => {
6+
setSearchSkills((prev) => {
7+
return prev.filter((prevSkill) => prevSkill !== skill);
8+
});
9+
};
10+
11+
return (
12+
<div className=" m-2 flex flex-row items-center rounded-md border-2 border-[#00A6FB] bg-[#00A6FB] p-2 text-black transition-colors duration-1000 ease-in-out hover:bg-white hover:text-[#00A6FB] dark:text-white dark:hover:bg-[#141D2F]">
13+
<p className="mr-4">{skill}</p>
14+
<FaTimes
15+
onClick={() => {
16+
handleRemoveSkill(skill);
17+
}}
18+
/>
19+
</div>
20+
);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import SearchSkill from './SearchSkill';
2+
3+
export default function SearchSkillsContainer({ searchSkills, setSearchSkills }) {
4+
return (
5+
<div className="relative mt-2 flex min-h-20 w-full flex-row flex-wrap rounded-lg bg-white p-2 dark:bg-[#1E2A47]">
6+
{searchSkills.map((skill, idx) => {
7+
return <SearchSkill skill={skill} key={idx} setSearchSkills={setSearchSkills} />;
8+
})}
9+
</div>
10+
);
11+
}

0 commit comments

Comments
 (0)