Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add search with fuse.js #15

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions data/groups/bristol-ultimate-development/details.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
},
"location": {
"address": "Shine Sports Hall, Brecon Road, Henleaze, BS9 4DT",
"latitude": "51.4847",
"longitude": "-2.6066",
"latitude": "51.486401",
"longitude": "-2.6140988",
"googleMapsLink": "https://maps.app.goo.gl/EA2VJFazA1jL9VAe9"
},
"cost": {
Expand Down
2 changes: 1 addition & 1 deletion data/groups/west-country-leders/details.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"links": [
{
"type": "Discord",
"url": "https://discord.com/invite/BUGXEyvwTc"
"url": "https://discord.gg/yP55Z56E"
}
]
}
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"dotenv": "16.4.5",
"eslint": "8.56.0",
"eslint-config-next": "14.2.3",
"fuse.js": "7.0.0",
"leaflet": "1.9.4",
"next": "14.2.13",
"prettier": "3.2.4",
Expand Down
10 changes: 8 additions & 2 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ export const Footer = () => {
<p>
This website is a non-profit, volunteer run initiative.
<br />
If you&apos;d like to leave feedback,{' '}
<Link url="/feedback" className={styles.link}>
please use this form
Leave feedback here
</Link>{' '}
or send an email to{' '}
<Link
url="mailto:[email protected]"
className={styles.link}
>
[email protected]
</Link>
.
</p>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/components/GroupListingMap/GroupListingMap.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
}

@include media-query(large) {
margin-bottom: $grid * 3;
margin-bottom: $grid * 4;
}
}

.toggleButton {
@include focus-outline;
@include font-size(sm);

padding: $grid;
display: flex;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 42 additions & 10 deletions src/pages/Index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@

.hero {
margin-bottom: 0;

@include media-query(medium) {
margin-bottom: $grid * 2;
}

@include media-query(large) {
margin-bottom: $grid * 2;
}
}

.title {
Expand Down Expand Up @@ -109,7 +101,7 @@
}

.filterAccordion {
margin-bottom: $grid * 2;
margin-bottom: $grid * 1;
max-width: $max-content-width;
}

Expand All @@ -130,6 +122,7 @@

width: 290px;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: $grid * 0.5;
Expand Down Expand Up @@ -159,7 +152,20 @@
padding: $grid 0;

@include media-query(medium) {
padding: $grid * 2 0 0 0;
padding: $grid * 2 0 $grid * 1 0;
}
}

.filterActionsContainer {
display: flex;
flex-direction: column;
gap: $grid;
margin-bottom: $grid;

@include media-query(medium) {
align-items: center;
flex-direction: row;
margin-bottom: $grid * 2;
}
}

Expand All @@ -180,3 +186,29 @@
margin-top: $grid * 4;
}
}

.searchAndFilterContainer {
display: flex;
flex-direction: column;
gap: $grid;
margin-top: $grid * 2;

@include media-query(medium) {
flex-direction: row;
}
}

.searchInput {
@include focus-outline;
@include font-size(sm);

width: 290px;
padding: $grid * 0.5;
border: 1px solid var(--color--black);
border-radius: 4px;

@include media-query(medium) {
width: 100%;
max-width: 40ch;
}
}
87 changes: 63 additions & 24 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useMemo } from 'react';
import fs from 'fs';
import Fuse from 'fuse.js';
import { join } from 'path';
import GroupListingFeed from '@/components/GroupListingFeed';
import GroupListingMap from '@/components/GroupListingMap';
Expand All @@ -23,11 +24,11 @@ const generateListOfGroupTags = (events: any) => {
}, {});
};

export default function Home({ groups }: any) {
export default function Home({ groups }: { groups: Group[] }) {
const [selectedGroupTags, setSelectedGroupTags] = useState<string[]>([]);
const [selectedWeekday, setSelectedWeekday] = useState<string>('All');
const [filterIsOpen, setFilterIsOpen] = useState(false);

const [searchQuery, setSearchQuery] = useState<string>('');
const groupTags = useMemo(() => generateListOfGroupTags(groups), [groups]);

const handleGroupFilterClicked = (tag: string) => {
Expand All @@ -43,15 +44,43 @@ export default function Home({ groups }: any) {
const clearAllFilters = () => {
setSelectedGroupTags([]);
setSelectedWeekday('All');
setSearchQuery('');
};

const toggleFilter = () => {
setFilterIsOpen(!filterIsOpen);
};

const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(event.target.value);
};

const searchOptions = {
includeScore: true,
minMatchCharLength: 3,
useAnd: false,
threshold: 0.2,
matchAllTokens: false,
tokenize: true,
keys: ['name', 'description', 'tags', 'details'],
};

const fuse = new Fuse(groups, searchOptions);

const filteredGroups = useMemo(() => {
if (searchQuery.length > 2) {
const searchResults = fuse.search(searchQuery);
const searchResultGroups = searchResults.map(
(result: any) => result.item,
);
return filterGroups(
searchResultGroups,
selectedGroupTags,
selectedWeekday,
);
}
return filterGroups(groups, selectedGroupTags, selectedWeekday);
}, [groups, selectedGroupTags, selectedWeekday]);
}, [groups, selectedGroupTags, selectedWeekday, searchQuery]);

const filteredGroupsContainRegularLocation =
filteredGroups.regularGroups.some((group: Group) => {
Expand All @@ -73,7 +102,14 @@ export default function Home({ groups }: any) {
A collection of local groups that meet up regularly and are
open to newcomers.
</p>
<div className={styles.filterAccordion}>
<div className={styles.searchAndFilterContainer}>
<input
className={styles.searchInput}
type="text"
placeholder="Search"
onChange={handleSearch}
value={searchQuery}
/>
<button
className={styles.filterAccordionToggle}
onClick={toggleFilter}
Expand All @@ -84,6 +120,8 @@ export default function Home({ groups }: any) {
pointDownwards={!filterIsOpen}
/>
</button>
</div>
<div className={styles.filterAccordion}>
{filterIsOpen && (
<div className={styles.filterContent}>
<div className={styles.groupFilterOptionsContainer}>
Expand Down Expand Up @@ -167,29 +205,30 @@ export default function Home({ groups }: any) {
))}
</div>
</div>

<FilteredGroupsShownMessage
filteredGroups={filteredGroups}
numberOfPossibleGroups={groups.length}
/>

{(selectedGroupTags.length !== 0 ||
selectedWeekday !== 'All') && (
<button
className={
styles.clearSelectedTagsButton
}
onClick={clearAllFilters}
>
Clear all filters
</button>
)}
</div>
</div>
)}
</div>
</div>

<div className={styles.filterActionsContainer}>
<FilteredGroupsShownMessage
filteredGroups={filteredGroups}
numberOfPossibleGroups={groups.length}
/>

{(selectedGroupTags.length !== 0 ||
selectedWeekday !== 'All' ||
searchQuery !== '') && (
<button
className={styles.clearSelectedTagsButton}
onClick={clearAllFilters}
>
Clear all filters
</button>
)}
</div>

{filteredGroupsContainRegularLocation && (
<GroupListingMap
groups={filteredGroups.regularGroups}
Expand All @@ -209,9 +248,9 @@ export default function Home({ groups }: any) {
<h2>Ad-hoc groups</h2>
<p className={styles.description}>
These groups may host an event on the day
you&apos;ve specified. Please check the groups
website, social media page or group chat for
more information.{' '}
you&apos;ve specified, on an ad-hoc basis.
Please check the groups website, social media
page or group chat for more information.{' '}
</p>
</div>
<GroupListingFeed
Expand Down
17 changes: 9 additions & 8 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ export const filterGroups = (
selectedGroupTags: string[],
selectedWeekday: string,
) => {
const regularGroups = groups.filter((event: any) => {
const regularGroups = groups.filter((group: Group) => {
// Find the groups that have a matching tag
let matchingTagFound = event.tags?.some((tag: string) =>
let matchingTagFound = group.tags?.some((tag: string) =>
selectedGroupTags.includes(tag),
);

Expand All @@ -63,15 +63,16 @@ export const filterGroups = (
// If no weekday is selected, all groups will be shown
if (selectedWeekday === 'All') {
matchingWeekdaySelected = true;
} else if (event.events) {
} else if (group.events) {
// If the group has event information,
// check if it has an event on the selected weekday
matchingWeekdaySelected = event.events.some(
matchingWeekdaySelected = group.events.some(
(e: any) => e.time.weekday === selectedWeekday,
);
}

const adHocGroup = event.type === 'Discord' || event.type === 'Ad-hoc';
// If a group doesn't have any regular events, assume it is ad-hoc
const adHocGroup = group.events === undefined;

if (adHocGroup) {
return false;
Expand All @@ -80,16 +81,16 @@ export const filterGroups = (
return matchingTagFound && matchingWeekdaySelected;
});

const adHocGroups = groups.filter((event: any) => {
const adHocGroups = groups.filter((group: Group) => {
// Find the groups that have a matching tag
let matchingTagFound = event.tags?.some((tag: string) =>
let matchingTagFound = group.tags?.some((tag: string) =>
selectedGroupTags.includes(tag),
);

// If no tags are filtered by, all ad-hoc groups will be shown
if (selectedGroupTags.length === 0) matchingTagFound = true;

const adHocGroup = event.type === 'Discord' || event.type === 'Ad-hoc';
const adHocGroup = group.events === undefined;

return matchingTagFound && adHocGroup;
});
Expand Down
Loading