Skip to content

Commit

Permalink
Merge pull request #414 from Sambashivarao-Boyina/SearchFeature
Browse files Browse the repository at this point in the history
The Search Feature of Skills cirteria implement from one skill to mutliple Skills
  • Loading branch information
madhukalita authored Oct 24, 2024
2 parents ab04ce2 + 4a4e6da commit 530f35c
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 52 deletions.
46 changes: 31 additions & 15 deletions src/Homepage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ function App() {
const [currentPage, setCurrentPage] = useState(1);
const [shuffledProfiles, setShuffledProfiles] = useState([]);
const [loadingProfiles, setLoadingProfiles] = useState(false);
const recordsPerPage = 20;

const recordsPerPage = 20;

const currentUrl = window.location.pathname;
useEffect(() => {
Expand Down Expand Up @@ -67,20 +68,35 @@ function App() {
.replace(/\s+/g, ' ')
.trim();

const normalizedValue = normalizeString(value);

const filteredResults = combinedData.filter((user) => {
if (criteria === 'name') {
return normalizeString(user.name).includes(normalizedValue);
} else if (criteria === 'location') {
return normalizeString(user.location).includes(normalizedValue);
} else if (criteria === 'skill') {
return user.skills.some((skill) => normalizeString(skill).includes(normalizedValue));
}
return false;
});

setProfiles(filteredResults);
if (criteria !== 'skill') {
let normalizedValue = normalizeString(value);

const filteredResults = combinedData.filter((user) => {
if (criteria === 'name') {
return normalizeString(user.name).includes(normalizedValue);
} else if (criteria === 'location') {
return normalizeString(user.location).includes(normalizedValue);
}
return false;
});

setProfiles(filteredResults);
} else if(criteria === "skill") {
// if criteria is skill the it will filter the data
if(value.length > 0){
let setOfSearchSkills = new Set(value.map(skill => skill.toLowerCase())); // Convert searchSkills to lowercase for comparison
const filteredUsers = shuffledProfiles.filter(user =>
user.skills.some(skill => setOfSearchSkills.has(skill.toLowerCase())) // Ensure skill is also lowercase
);
setProfiles(filteredUsers);
} else {
//if skills are empty it will reset the data
setProfiles(shuffledProfiles)
}
} else {
setProfiles(false);
}

setSearching(true);
};

Expand Down
133 changes: 96 additions & 37 deletions src/components/Search/Search.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,78 @@ import React, { useState, useRef, useEffect } from 'react';
import useDebounce from '../../hooks/useDebouncer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMagnifyingGlass, faXmark } from '@fortawesome/free-solid-svg-icons';
import SearchSkillsContainer from './SearchSkillsContainer';

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

const [searchSkills, setSearchSkills] = useState([]);

const normalizeString = (str) =>
str
.toLowerCase()
.replace(/\s*,\s*/g, ' ')
.replace(/\s+/g, ' ')
.trim();

const handleInputChange = (event) => {
setSearchValue(event.target.value);
};

const handleCriteriaChange = (event) => {
if(event.target.value !== "skill") {
handleClearSkills();
}
setSearchCriteria(event.target.value);

};

const debouncedValue = useDebounce(searchValue, 500);

useEffect(() => {
if (debouncedValue !== prevSearchValue) {
if (debouncedValue !== prevSearchValue && searchCriteria !== "skill") {
onSearch({ value: debouncedValue, criteria: searchCriteria });
setPrevSearchValue(debouncedValue);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedValue]);

const handleSearch = () => {
if (searchValue !== prevSearchValue) {
if ((searchValue !== prevSearchValue && searchCriteria !== "skill") || searchValue.trim() === "") {
onSearch({ value: searchValue, criteria: searchCriteria });
setPrevSearchValue(searchValue);
}
};

const handleSearchOnEnter = (e) => {
if (e.keyCode === 13) {
handleSearch();
const handleSearchOnEnter = (event) => {
if (event.keyCode === 13) {
//if searchCriteia is skill then it will add that skill to searchSkills
if (searchCriteria ==='skill') {
let searchvalue = normalizeString(searchValue);
searchvalue = searchvalue.trim();
if (searchvalue.length > 0) {
var set = new Set(searchSkills);
set.add(searchvalue);
setSearchSkills((prev) => [...set]);
}
setSearchValue('');
} else {
handleSearch();
}
}
};

useEffect(() => {
//when new skill is added to searchSkill it will filter the data
if(searchCriteria === "skill") {
onSearch({ value: searchSkills, criteria: searchCriteria });
setPrevSearchValue('');
}
}, [searchSkills]);

const handleSearchButtonClick = () => {
handleSearch();
};
Expand All @@ -53,45 +87,70 @@ function Search({ onSearch }) {
}
};

//Reset the profiles
const handleClearSkills = () => {
setSearchSkills([]);
setSearchValue('');
setPrevSearchValue('');
onSearch({ value: [], criteria: "skill" });
searchInput.current.focus();
};

useEffect(() => {
searchInput.current.focus();
}, []);

return (
<div className="relative flex items-center justify-end space-x-4 pb-6">
<select
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"
value={searchCriteria}
onChange={handleCriteriaChange}
>
<option value="name">Name</option>
<option value="location">Location</option>
<option value="skill">Skill</option>
</select>
<div className="relative w-full">
<input
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"
ref={searchInput}
type="text"
onChange={handleInputChange}
value={searchValue}
placeholder={`Search user by ${searchCriteria}`}
onKeyDown={handleSearchOnEnter}
/>
{searchValue ? (
<FontAwesomeIcon
onClick={handleDeleteButtonClick}
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"
icon={faXmark}
<div className="relative pb-6">
<div className="relative flex items-center justify-end space-x-4 ">
<select
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"
value={searchCriteria}
onChange={handleCriteriaChange}
>
<option value="name">Name</option>
<option value="location">Location</option>
<option value="skill">Skill</option>
</select>
<div className="relative w-full">
<input
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"
ref={searchInput}
type="text"
onChange={handleInputChange}
value={searchValue}
placeholder={`Search user by ${searchCriteria}`}
onKeyDown={handleSearchOnEnter}
/>
) : (
<FontAwesomeIcon
onClick={handleSearchButtonClick}
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"
icon={faMagnifyingGlass}
/>
)}
{searchValue ? (
<FontAwesomeIcon
onClick={handleDeleteButtonClick}
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"
icon={faXmark}
/>
) : (
<FontAwesomeIcon
onClick={handleSearchButtonClick}
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"
icon={faMagnifyingGlass}
/>
)}
</div>
</div>


{/* This block show the skills the user searched */}
{searchCriteria === 'skill' && searchSkills && searchSkills.length > 0 ? (
<>
<button
onClick={handleClearSkills}
className="m-2 cursor-pointer self-end rounded-md bg-white p-2 font-semibold dark:bg-[#1E2A47] dark:text-white"
>
Clear All
</button>
<SearchSkillsContainer searchSkills={searchSkills} setSearchSkills={setSearchSkills} />
</>
) : null}
</div>
);
}
Expand Down
21 changes: 21 additions & 0 deletions src/components/Search/SearchSkill.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FaTimes } from 'react-icons/fa';

//it is the design of each skill box
export default function SearchSkill({ skill, setSearchSkills }) {
const handleRemoveSkill = (value) => {
setSearchSkills((prev) => {
return prev.filter((prevSkill) => prevSkill !== skill);
});
};

return (
<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]">
<p className="mr-4">{skill}</p>
<FaTimes
onClick={() => {
handleRemoveSkill(skill);
}}
/>
</div>
);
}
11 changes: 11 additions & 0 deletions src/components/Search/SearchSkillsContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import SearchSkill from './SearchSkill';

export default function SearchSkillsContainer({ searchSkills, setSearchSkills }) {
return (
<div className="relative mt-2 flex min-h-20 w-full flex-row flex-wrap rounded-lg bg-white p-2 dark:bg-[#1E2A47]">
{searchSkills.map((skill, idx) => {
return <SearchSkill skill={skill} key={idx} setSearchSkills={setSearchSkills} />;
})}
</div>
);
}

0 comments on commit 530f35c

Please sign in to comment.