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') && ( - - )}
)} +
+ + + {(selectedGroupTags.length !== 0 || + selectedWeekday !== 'All' || + searchQuery !== '') && ( + + )} +
+ {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; });