diff --git a/data/groups/bristol-ultimate-development/details.json b/data/groups/bristol-ultimate-development/details.json
index 1573fd6..e48a3db 100644
--- a/data/groups/bristol-ultimate-development/details.json
+++ b/data/groups/bristol-ultimate-development/details.json
@@ -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": {
diff --git a/data/groups/west-country-leders/details.json b/data/groups/west-country-leders/details.json
index a0ab40d..42f8058 100644
--- a/data/groups/west-country-leders/details.json
+++ b/data/groups/west-country-leders/details.json
@@ -11,7 +11,7 @@
"links": [
{
"type": "Discord",
- "url": "https://discord.com/invite/BUGXEyvwTc"
+ "url": "https://discord.gg/yP55Z56E"
}
]
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bbe3bb3..5f8c79a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,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",
@@ -10805,6 +10806,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/fuse.js": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
+ "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -27542,6 +27552,11 @@
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
},
+ "fuse.js": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
+ "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q=="
+ },
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
diff --git a/package.json b/package.json
index 2fa6bd0..099e94b 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
index e74fe2a..da5bfcc 100644
--- a/src/components/Footer/Footer.tsx
+++ b/src/components/Footer/Footer.tsx
@@ -13,9 +13,15 @@ export const Footer = () => {
This website is a non-profit, volunteer run initiative.
- If you'd like to leave feedback,{' '}
- please use this form
+ Leave feedback here
+ {' '}
+ or send an email to{' '}
+
+ contact@bristol.social
.
diff --git a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-chrome-linux.png b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-chrome-linux.png
index 243e2ed..6199983 100644
Binary files a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-chrome-linux.png and b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-chrome-linux.png differ
diff --git a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-firefox-linux.png b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-firefox-linux.png
index 714f016..7e4d130 100644
Binary files a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-firefox-linux.png and b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-firefox-linux.png differ
diff --git a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-safari-linux.png b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-safari-linux.png
index f970de8..3e0a34a 100644
Binary files a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-safari-linux.png and b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-desktop-safari-linux.png differ
diff --git a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-chrome-linux.png b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-chrome-linux.png
index 63dc4b7..29aa42e 100644
Binary files a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-chrome-linux.png and b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-chrome-linux.png differ
diff --git a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-safari-linux.png b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-safari-linux.png
index 12454ec..decae29 100644
Binary files a/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-safari-linux.png and b/src/components/Footer/tests/Footer.visual.spec.ts-snapshots/Footer-mobile-safari-linux.png differ
diff --git a/src/components/GroupListingMap/GroupListingMap.module.scss b/src/components/GroupListingMap/GroupListingMap.module.scss
index d32d782..3efc909 100644
--- a/src/components/GroupListingMap/GroupListingMap.module.scss
+++ b/src/components/GroupListingMap/GroupListingMap.module.scss
@@ -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;
diff --git a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-chrome-linux.png b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-chrome-linux.png
index edd8dfb..dc079a1 100644
Binary files a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-chrome-linux.png and b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-chrome-linux.png differ
diff --git a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-firefox-linux.png b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-firefox-linux.png
index f5b6959..368734b 100644
Binary files a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-firefox-linux.png and b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-firefox-linux.png differ
diff --git a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-safari-linux.png b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-safari-linux.png
index 16f8ed2..85d6273 100644
Binary files a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-safari-linux.png and b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-desktop-safari-linux.png differ
diff --git a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-chrome-linux.png b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-chrome-linux.png
index 584f2af..01fb37f 100644
Binary files a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-chrome-linux.png and b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-chrome-linux.png differ
diff --git a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-safari-linux.png b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-safari-linux.png
index a1b2193..72f17cb 100644
Binary files a/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-safari-linux.png and b/src/components/GroupListingMap/tests/GroupListingMap.visual.spec.ts-snapshots/GroupListingMap-mobile-safari-linux.png differ
diff --git a/src/pages/Index.module.scss b/src/pages/Index.module.scss
index b9abd9f..27b9ec3 100644
--- a/src/pages/Index.module.scss
+++ b/src/pages/Index.module.scss
@@ -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 {
@@ -109,7 +101,7 @@
}
.filterAccordion {
- margin-bottom: $grid * 2;
+ margin-bottom: $grid * 1;
max-width: $max-content-width;
}
@@ -130,6 +122,7 @@
width: 290px;
display: flex;
+ flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: $grid * 0.5;
@@ -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;
}
}
@@ -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;
+ }
+}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 2797f25..56bc593 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -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';
@@ -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([]);
const [selectedWeekday, setSelectedWeekday] = useState('All');
const [filterIsOpen, setFilterIsOpen] = useState(false);
-
+ const [searchQuery, setSearchQuery] = useState('');
const groupTags = useMemo(() => generateListOfGroupTags(groups), [groups]);
const handleGroupFilterClicked = (tag: string) => {
@@ -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) => {
+ 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) => {
@@ -73,7 +102,14 @@ export default function Home({ groups }: any) {
A collection of local groups that meet up regularly and are
open to newcomers.
-
+
+
+
+
{filterIsOpen && (
@@ -167,29 +205,30 @@ export default function Home({ groups }: any) {
))}
-
-
-
- {(selectedGroupTags.length !== 0 ||
- selectedWeekday !== 'All') && (
-
- Clear all filters
-
- )}
)}
+
+
+
+ {(selectedGroupTags.length !== 0 ||
+ selectedWeekday !== 'All' ||
+ searchQuery !== '') && (
+
+ Clear all filters
+
+ )}
+
+
{filteredGroupsContainRegularLocation && (
Ad-hoc groups
These groups may host an event on the day
- you've specified. Please check the groups
- website, social media page or group chat for
- more information.{' '}
+ you've specified, on an ad-hoc basis.
+ Please check the groups website, social media
+ page or group chat for more information.{' '}
{
- 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),
);
@@ -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;
@@ -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;
});