Skip to content

Commit

Permalink
Add search functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
lokeshwar777 committed Aug 22, 2024
1 parent ec83f13 commit 684769a
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 26 deletions.
3 changes: 2 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Routes from "./routes";
import "./App.less";
import { useFirebase, useFirestore } from "react-redux-firebase";
import { useDispatch, useSelector } from "react-redux";
import { getProfileData } from "./store/actions";
import { getProfileData, fetchAndIndexTutorials } from "./store/actions";

const App = () => {
const firebase = useFirebase();
Expand All @@ -19,6 +19,7 @@ const App = () => {

useEffect(() => {
getProfileData(organizations)(firebase, firestore, dispatch);
fetchAndIndexTutorials()(firebase, firestore, dispatch);
}, [organizations, firebase, dispatch]);
return <Routes />;
};
Expand Down
28 changes: 25 additions & 3 deletions src/components/NavBar/new/MainNavbar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function MainNavbar() {
const windowSize = useWindowSize();
const [openDrawer, setOpenDrawer] = useState(false);
const [openMenu, setOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const toggleSlider = () => {
setOpen(!openMenu);
};
Expand All @@ -70,6 +71,16 @@ function MainNavbar() {
const notificationCount = notifications?.filter(
notification => !notification.isRead
).length;
const handleSearchChange = e => {
setSearchQuery(e.target.value);
};
const handleSearch = e => {
e.preventDefault();
if (searchQuery.length > 0) {
history.push(`/search?query=${searchQuery}`);
}
};

return (
<Headroom>
<nav
Expand Down Expand Up @@ -110,17 +121,28 @@ function MainNavbar() {
</Grid>
</Grid>
<Grid item xs={12} md={5}>
<Paper component={"form"} className={classes.root} elevation={0}>
<Paper
component={"form"}
className={classes.root}
elevation={0}
onSubmit={handleSearch}
>
<IconButton
type="submit"
type="button"
aria-label="search"
disableRipple
className={classes.icon}
data-testid="navbarSearch"
onClick={handleSearch}
>
<SearchIcon />
</IconButton>
<InputBase className={classes.input} placeholder="Search..." />
<InputBase
className={classes.input}
value={searchQuery}
placeholder="Search..."
onChange={handleSearchChange}
/>
</Paper>
</Grid>
<Grid
Expand Down
19 changes: 18 additions & 1 deletion src/components/NavBar/new/MiniNavbar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import CloseIcon from "@mui/icons-material/Close";
import SideBar from "../../../SideBar";
import useWindowSize from "../../../../helpers/customHooks/useWindowSize";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { searchFromTutorialsIndex } from "../../../../store/actions";

const useStyles = makeStyles(theme => ({
input: {
Expand Down Expand Up @@ -67,6 +69,7 @@ function MiniNavbar() {
const classes = useStyles();

const history = useHistory();
const dispatch = useDispatch();
const notifications = useSelector(
state => state.notifications.data.notifications
);
Expand Down Expand Up @@ -101,6 +104,17 @@ function MiniNavbar() {
});
};

const [searchQuery, setSearchQuery] = useState("");
const handleSearchChange = e => {
setSearchQuery(e.target.value);
};
const handleSearch = () => {
if (searchQuery.length > 0) {
dispatch(searchFromTutorialsIndex(searchQuery));
history.push(`/search?query=${searchQuery}`);
}
};

useEffect(() => {
window.addEventListener("resize", setDimension);

Expand Down Expand Up @@ -151,11 +165,12 @@ function MiniNavbar() {
<Grid style={{ display: "inline-block" }} item xs={12} md={4}>
<Paper component={"form"} className={classes.root} elevation={0}>
<IconButton
type="submit"
type="button"
aria-label="search"
disableRipple
className={classes.icon}
data-testid="navbarSearch"
onClick={handleSearch}
>
<SearchIcon />
</IconButton>
Expand All @@ -170,6 +185,8 @@ function MiniNavbar() {
}}
className={classes.input}
placeholder="Search..."
value={searchQuery}
onChange={handleSearchChange}
/>
</Paper>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const TutorialCard = ({
{title}
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
{title}
{summary}
</Typography>
{loading ? <Skeleton variant="text" /> : null}
{loading ? <Skeleton variant="text" /> : null}
Expand Down
131 changes: 121 additions & 10 deletions src/components/Tutorials/MyTutorials/Search/SearchResultsComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,135 @@
import Divider from "@mui/material/Divider";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import React from "react";
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { useFirebase, useFirestore } from "react-redux-firebase";
import TutorialCard from "../BaseTutorialsComponent/TutorialCard";
import { searchFromTutorialsIndex } from "../../../../store/actions";
import { getTutorialFeedData } from "../../../../store/actions/tutorialPageActions";
import CardWithPicture from "../../../Card/CardWithPicture";
import CardWithoutPicture from "../../../Card/CardWithoutPicture";

const SearchResultsComponent = ({ results: propResults }) => {
const [results, setResults] = useState([]);
const location = useLocation();
const firebase = useFirebase();
const firestore = useFirestore();
const dispatch = useDispatch();

const tutorials = useSelector(
({
tutorialPage: {
feed: { homepageFeedArray }
}
}) => homepageFeedArray
);

useEffect(() => {
setResults(tutorials);
}, [tutorials]);

useEffect(() => {
const query = new URLSearchParams(location.search).get("query");
let tutorialIdArray = propResults || [];

if (query) {
tutorialIdArray = searchFromTutorialsIndex(query);
tutorialIdArray = tutorialIdArray.map(tutorial => tutorial.ref);
}

const getResults = async () => {
if (tutorialIdArray.length > 0) {
await getTutorialFeedData(tutorialIdArray)(
firebase,
firestore,
dispatch
);
} else {
setResults([]);
}
};

getResults();
}, [location.search, propResults, firebase, firestore, dispatch]);

const SearchResultsComponent = ({ results }) => {
return (
<div>
<Grid container item justify="center">
<Divider variant="middle" />
<Grid item xs={12}>
<Typography align="center"> Search Results</Typography>
<Typography align="center" variant="h4">
Search Results
</Typography>
</Grid>
{propResults && (
<>
<Divider variant="middle" />
<Grid container spacing={3}>
{results.map((tutorial, index) => (
<Grid
item
key={index}
xs={12}
sm={6}
md={3}
lg={2}
xl={2}
className="pr-24"
>
<TutorialCard tutorialData={tutorial} loading={false} />
</Grid>
))}
</Grid>
</>
)}

<Grid
container
spacing={3}
justifyContent="center"
alignItems="center"
sx={{
padding: "1rem",
margin: "0 auto",
width: "100%",
maxWidth: "1200px"
}}
>
{!propResults &&
results.map((tutorial, index) => (
<Grid
item
key={index}
xs={12}
sm={6}
md={6}
lg={6}
xl={6}
sx={{ mb: 2 }}
>
{!tutorial?.featured_image ? (
<CardWithoutPicture tutorial={tutorial} />
) : (
<CardWithPicture tutorial={tutorial} />
)}
</Grid>
))}
</Grid>

<Grid container justifyContent="center">
{results.length === 0 && (
<Typography
variant="h6"
component="p"
color="textSecondary"
sx={{ marginTop: 2 }}
>
No CodeLabz found with the given query.
</Typography>
)}
</Grid>
<Divider variant="middle" />
{results.map((tutorial, index) => (
<Grid xs={12} sm={6} md={3} lg={2} xl={2} className="pr-24">
<TutorialCard key={index} tutorialData={tutorial} loading={false} />
</Grid>
))}
{results.length === 0 && "No CodeLabz with the given query"}

<Divider variant="middle" />
</Grid>
Expand Down
5 changes: 1 addition & 4 deletions src/components/Tutorials/MyTutorials/Search/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ const Header = () => {
if (result.length > 0) {
let tempArray = [];
result.forEach(item => {
tempArray = [
...tempArray,
..._.filter(indexData, ref => ref.tutorial_id === item.ref)
];
tempArray = [...tempArray, item.ref];
});
setViewResults(true);
return setResults(tempArray);
Expand Down
32 changes: 28 additions & 4 deletions src/helpers/elasticlunr.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import elasticlunr from "elasticlunr";

export default class Elasticlunr {
constructor(key, ...fields) {
this.elasticlunr = elasticlunr();
Expand All @@ -8,11 +9,34 @@ export default class Elasticlunr {
});
}

addDocToIndex = doc => {
this.elasticlunr.addDoc(doc);
addDocToIndex(doc) {
try {
this.elasticlunr.addDoc(doc);
} catch (error) {
console.error("Error adding document to index:", error);
}
}

searchFromIndex(query, options = { expand: true }) {
try {
if (typeof query !== "string") {
throw new Error("Query must be a string.");
}

return this.elasticlunr.search(query, options);
} catch (error) {
console.error("Error searching the index:", error);
return [];
}
}

// To get a specific document by its reference key
getDocById = id => {
return this.elasticlunr.documentStore.getDoc(id);
};

searchFromIndex = query => {
return this.elasticlunr.search(query, { expand: true });
// To get all documents in the index
getAllDocs = () => {
return Object.values(this.elasticlunr.documentStore.docs);
};
}
2 changes: 2 additions & 0 deletions src/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import MainNavbar from "./components/NavBar/new/MainNavbar";
import UserDashboard from "./components/UserDashboard";
import TutorialPage from "./components/TutorialPage";
import Notification from "./components/Notification";
import SearchResultsComponent from "./components/Tutorials/MyTutorials/Search/SearchResultsComponent";

const AuthIsLoaded = ({ children }) => {
const profile = useSelector(({ firebase: { profile } }) => profile);
Expand Down Expand Up @@ -157,6 +158,7 @@ const Routes = () => {
path={"/tutorial/:id"}
component={UserIsAllowedUserDashboard(TutorialPage)}
/>
<Route exact path={"/search"} component={SearchResultsComponent} />
<Route
exact
path={"/editor"}
Expand Down
3 changes: 2 additions & 1 deletion src/store/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,6 @@ export {
setTutorialTheme,
updateStepTime,
updateStepTitle,
uploadTutorialImages
uploadTutorialImages,
fetchAndIndexTutorials
} from "./tutorialsActions";
20 changes: 19 additions & 1 deletion src/store/actions/tutorialsActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,26 @@ const tutorials_index = new Elasticlunr(
"summary"
);

export const fetchAndIndexTutorials = () => async (firebase, firestore) => {
try {
const snapshot = await firestore.collection("tutorials").get();
snapshot.forEach(doc => {
const data = doc.data();
const tutorial = { id: doc.id, ...data };
// console.log("Adding tutorial to index:", tutorial);
tutorials_index.addDocToIndex(tutorial);
});

// console.log("All docs in index:", tutorials_index.getAllDocs());
} catch (error) {
console.error("Error fetching or indexing tutorials:", error);
}
};

export const searchFromTutorialsIndex = query => {
return tutorials_index.searchFromIndex(query);
const results = tutorials_index.searchFromIndex(query);
// console.log("searchFromIndex", query, results);
return results;
};

// Gets all the tutorials with this user having edit access
Expand Down

0 comments on commit 684769a

Please sign in to comment.