Skip to content

Commit

Permalink
2823 add missing filters events (#3065)
Browse files Browse the repository at this point in the history
* πŸ› Populated events filters using static fetching

* πŸ”₯ Removed unused EventCategories import

* πŸ› Fixed event filters not working

* ✨ Enabled event category filtering at the database layer

* ⚑ Fixed static site generation for events

* πŸ› Fixed wrong image displaying when toggling event filters

* πŸ› Fixed links to canberra UG thumbnail

* Removed console logs

* πŸ› Updated brisbane 2018 opening party to match use existing opening party field

* ✨ Added similar category fetching for events & massaged events into fitting categories

* πŸ› Updated 2013 Mobile Development webinar to fit into Mobile category

* πŸ› Fixed uncategorized events

* πŸ”‡ Removed console log

* πŸ’„ Fixed pop in animation

* πŸ› Added missing set hook for event title

* ♻️ Updated event category fetch to use existing variable for overriding page size in Tina

* βž• Added import statement for event max override

* βž– removed unused import statements

* πŸ” Removed redundant fuzzy search for "Mobile Development" (Mobile development events were renamed => Mobile)

* 🎨 Updated getFilterState to const because it is never reassigned

* 🎨 Added jsonLd prop back to event

* 🚨 Fixed linting errors

* 🚨 Fixed build and errors

* πŸ”’Comitting updated tina lock file

* removed empty useEffect in component filter

* Added parentheses around if in useEffect

* ⚑ Improved event transition render logic

* Fixed events flickering while fetching more events

* πŸ› Fixed flickering when loading events

* πŸ”‡ Removed console logging

* 🚨 Fixed linting errors

* 🚨 Fixed linting issues with useReducer

* πŸ› Fixed events flickering when page first loads

* 🚨 Fixed linting warnings

* 🚨 Removed explicit any from array comparison function

* πŸ“’ Added TODO for cleaning up event list

* πŸ“’ Added TODO for improving event transition performance

---------

Co-authored-by: Calinator444 <[email protected]>
  • Loading branch information
Calinator444 and Calinator444 authored Sep 5, 2024
1 parent dd3e3cc commit a94270e
Show file tree
Hide file tree
Showing 16 changed files with 361 additions and 156 deletions.
11 changes: 11 additions & 0 deletions components/filter/FilterBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EventCategories } from "hooks/useFetchEvents";
import { MdLiveHelp } from "react-icons/md";
import { FilterGroup, type FilterGroupProps } from "./FilterGroup";

Expand Down Expand Up @@ -37,3 +38,13 @@ export const FilterBlock = ({
</div>
);
};

export type EventFilterAllCategories = {
past: EventFilterCategories;
upcoming: EventFilterCategories;
};

export type EventFilterCategories = {
technologies: EventCategories;
categories: EventCategories;
};
9 changes: 5 additions & 4 deletions components/filter/FilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ export interface FilterGroupProps {
selected: number;
setSelected: (index: number) => void;
allText: string;
options: {
label: string | Readonly<string>;
count: number;
}[];
options: Option[];
}

export type Option = {
label: string | Readonly<string>;
count: number;
};
export const FilterGroup = ({
selected,
setSelected,
Expand Down
234 changes: 162 additions & 72 deletions components/filter/events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

import { Tab, Transition } from "@headlessui/react";
import Image from "next/image";
import { Fragment, useState } from "react";
import { Fragment, useEffect, useMemo, useReducer, useState } from "react";
import { FaSpinner } from "react-icons/fa";
import type { Event, WithContext } from "schema-dts";
import { TinaMarkdown, TinaMarkdownContent } from "tinacms/dist/rich-text";

import { useEvents } from "../../hooks/useEvents";
import {
useFetchFutureEvents,
Expand All @@ -19,13 +18,18 @@ import { CustomLink } from "../customLink";
import { EventsRelativeBox } from "../events/eventsRelativeBox";
import { CITY_MAP } from "../util/constants/country";
import { sswOrganisation } from "../util/constants/json-ld";
import { FilterBlock } from "./FilterBlock";
import { EventFilterAllCategories, FilterBlock } from "./FilterBlock";
import { FilterGroupProps } from "./FilterGroup";

const EVENTS_JSON_LD_LIMIT = 5;

export const DEFAULT_TECHNOLOGY_FITLER = undefined;
export const DEFAULT_CATEGORY_FILTER = undefined;

interface EventsFilterProps {
sidebarBody: TinaMarkdownContent;
defaultToPastTab?: boolean;
filterCategories: EventFilterAllCategories;
}

export type EventTrimmed = {
Expand All @@ -47,31 +51,39 @@ export type EventTrimmed = {
};

export const EventsFilter = ({
filterCategories,
sidebarBody,
defaultToPastTab,
}: EventsFilterProps) => {
const [pastSelected, setPastSelected] = useState<boolean>(defaultToPastTab);
const { past, upcoming } = filterCategories;
const { filters: futureFilters } = useEvents(upcoming);
const { filters: pastFilters } = useEvents(past);
const pastSelectedFilters = useMemo<SelectedFilters>(() => {
const filters = getFilterState(pastFilters);
return filters;
}, [pastFilters]);

const futureSelectedFilters = useMemo<SelectedFilters>(() => {
const filters = getFilterState(futureFilters);
return filters;
}, [futureFilters]);

const {
futureEvents,
fetchFutureNextPage,
hasMoreFuturePages,
isFetchingFuturePages,
isLoadingFuturePages,
} = useFetchFutureEvents();
const { filters: futureFilters, filteredEvents: filteredFutureEvents } =
useEvents(futureEvents);
} = useFetchFutureEvents(futureSelectedFilters);

const {
pastEvents,
fetchNextPastPage,
hasMorePastPages,
isFetchingPastPages,
isLoadingPastPages,
} = useFetchPastEvents(true);

const { filters: pastFilters, filteredEvents: pastFilteredEvents } =
useEvents(pastEvents);
} = useFetchPastEvents(pastSelectedFilters);

return (
<FilterBlock
Expand All @@ -94,7 +106,6 @@ export const EventsFilter = ({
<Tab.Panel>
<EventsList
events={futureEvents}
filteredEvents={filteredFutureEvents}
isUpcoming
isLoading={isLoadingFuturePages}
/>
Expand All @@ -108,11 +119,7 @@ export const EventsFilter = ({
)}
</Tab.Panel>
<Tab.Panel>
<EventsList
events={pastEvents}
filteredEvents={pastFilteredEvents}
isLoading={isLoadingPastPages}
/>
<EventsList events={pastEvents} isLoading={isLoadingPastPages} />
{hasMorePastPages && (
<LoadMore
load={() => {
Expand Down Expand Up @@ -140,73 +147,134 @@ const EventTab = ({ children }: { children: React.ReactNode }) => {

interface EventsListProps {
events: EventTrimmed[];
filteredEvents: EventTrimmed[];
isUpcoming?: boolean;
isLoading?: boolean;
}

const EventsList = ({
events,
filteredEvents,
isUpcoming,
isLoading,
}: EventsListProps) => {
const eventsReducer = (state, action) => {
if (!state.visible && arraysEqual(state.firstEvents, action.payload)) {
return state;
}
if (state.visible && arraysEqual(state.secondEvents, action.payload)) {
return state;
}
switch (action.type) {
case "SET_EVENTS":
return state.visible
? { ...state, firstEvents: action.payload, visible: !state.visible }
: { ...state, secondEvents: action.payload, visible: !state.visible };
default:
return state;
}
};

// TODO: Compare arrays by reference instead of value https://github.com/SSWConsulting/SSW.Website/issues/3066
const arraysEqual = (arr1: EventTrimmed[], arr2: EventTrimmed[]): boolean => {
if (arr1.length !== arr2.length) return false;
return arr1.every(
(value: EventTrimmed, index: number) => value.id === arr2[index].id
);
};

const initialState = {
firstEvents: [],
secondEvents: [],
isFetching: false,
visible: true,
};

const EventsList = ({ events, isUpcoming, isLoading }: EventsListProps) => {
const [state, dispatch] = useReducer(eventsReducer, initialState);
useEffect(() => {
dispatch({ type: "SET_EVENTS", payload: events });
}, [events]);

return (
<div>
{!isLoading ? (
{isLoading ? (
<LoadingIcon />
) : (
<>
{filteredEvents.length > 0 ? (
events?.map((event, index) => {
let eventJsonLd: WithContext<Event> = undefined;

if (index < EVENTS_JSON_LD_LIMIT && isUpcoming) {
eventJsonLd = {
"@context": "https://schema.org",
"@type": "Event",
name: event.title,
image: event.thumbnail,
startDate: event.startDateTime?.toISOString(),
endDate: event.endDateTime?.toISOString(),
location: {
"@type": "Place",
address: {
"@type": "PostalAddress",
addressLocality: CITY_MAP[event.city]?.name,
addressRegion: CITY_MAP[event.city]?.state,
addressCountry: CITY_MAP[event.city]?.country,
},
name: CITY_MAP[event.city]?.name,
url: CITY_MAP[event.city]?.url,
},
eventStatus: "https://schema.org/EventScheduled",
eventAttendanceMode:
"https://schema.org/MixedEventAttendanceMode",
organizer: sswOrganisation,
};
}

return (
<Event
key={index}
visible={!!filteredEvents?.find((e) => e.id === event.id)}
event={event}
jsonLd={eventJsonLd}
/>
);
})
) : (
<h3>No events found matching the filters</h3>
)}
<LoadedEvents
visible={!state.visible}
events={state.firstEvents}
isUpcoming={isUpcoming}
></LoadedEvents>
<LoadedEvents
visible={state.visible}
events={state.secondEvents}
isUpcoming={isUpcoming}
></LoadedEvents>
</>
) : (
<p className="flex flex-row text-xl">
<FaSpinner className="m-icon animate-spin" /> Loading Events...
</p>
)}
</div>
);
};

type AllEventsProps = {
events: EventTrimmed[];
isUpcoming: boolean;
visible: boolean;
};

const LoadingIcon: React.FC = () => {
return (
<p className="flex flex-row text-xl">
<FaSpinner className="m-icon animate-spin" /> Loading Events...
</p>
);
};

const LoadedEvents: React.FC<AllEventsProps> = ({
visible,
events,
isUpcoming,
}) => {
return (
<>
{events.length > 0
? events?.map((event, index) => {
let eventJsonLd: WithContext<Event> = undefined;

if (index < EVENTS_JSON_LD_LIMIT && isUpcoming) {
eventJsonLd = {
"@context": "https://schema.org",
"@type": "Event",
name: event.title,
image: event.thumbnail,
startDate: event.startDateTime?.toISOString(),
endDate: event.endDateTime?.toISOString(),
location: {
"@type": "Place",
address: {
"@type": "PostalAddress",
addressLocality: CITY_MAP[event.city]?.name,
addressRegion: CITY_MAP[event.city]?.state,
addressCountry: CITY_MAP[event.city]?.country,
},
name: CITY_MAP[event.city]?.name,
url: CITY_MAP[event.city]?.url,
},
eventStatus: "https://schema.org/EventScheduled",
eventAttendanceMode:
"https://schema.org/MixedEventAttendanceMode",
organizer: sswOrganisation,
};
}
return (
<Event
visible={visible}
key={index}
jsonLd={eventJsonLd}
event={event}
/>
);
})
: visible && <h3>No events found matching the filters</h3>}
</>
);
};

interface EventProps {
visible?: boolean;
event: EventTrimmed;
Expand All @@ -220,7 +288,11 @@ const Event = ({ visible, event, jsonLd }: EventProps) => {
to Tina cloud. Images that aren't synced will 404.
*/
const [thumbnail, setFallbackImage] = useState(event.thumbnail);
const [thumbnail, setFallbackImage] = useState("");
useEffect(() => {
setFallbackImage(event.thumbnail);
}, [event.thumbnail]);

const handleImageError = () => {
const tinaUrl = /https:\/\/assets\.tina\.io\/[^/]+\/(.*)/;
const match = event.thumbnail.match(tinaUrl);
Expand All @@ -245,7 +317,7 @@ const Event = ({ visible, event, jsonLd }: EventProps) => {
<>
<Transition
className="mb-15 border-b-1 bg-white pb-8"
show={!!visible}
show={visible}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
Expand Down Expand Up @@ -362,3 +434,21 @@ export const LoadMore = ({ load, isLoading }: LoadMoreProps) => {
</div>
);
};

type SelectedFilters = {
category: string;
technology: string;
};

const getFilterState = (filterGroup: FilterGroupProps[]): SelectedFilters => {
const technologyGroup = filterGroup[0];
const categoryGroup = filterGroup[1];

const { selected: technologyIndex, options: technologyOptions } =
technologyGroup;
const { selected: categoryIndex, options: categoryOptions } = categoryGroup;
return {
category: categoryOptions[categoryIndex]?.label,
technology: technologyOptions[technologyIndex]?.label,
};
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Canberra .NET User Group",
"url": "/ssw/NETUG/Canberra.aspx",
"thumbnail": "/images/thumbs/thumb_canberraUG.jpg",
"thumbnail": "/images/Newsletters/thumb_canberraUG.jpg",
"thumbnailDescription": "Canberra .NET User Group ",
"presenterName": "George Doubinski",
"startDateTime": "2013-10-14T07:00:00Z",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Canberra .NET User Group",
"url": "/ssw/NETUG/Canberra.aspx",
"thumbnail": "/images/thumbs/thumb_canberraUG.jpg",
"thumbnail": "/images/Newsletters/thumb_canberraUG.jpg",
"thumbnailDescription": "/ssw/EventsCalendar/Images/thumb_canberraUG.jpg",
"presenterName": "TJ Gokcen",
"startDateTime": "2013-11-18T07:00:00Z",
Expand Down
2 changes: 1 addition & 1 deletion content/events-calendar/2013/Canberra-NET-User-Group.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Canberra .NET User Group",
"url": "/ssw/NETUG/Canberra.aspx",
"thumbnail": "/images/thumbs/thumb_canberraUG.jpg",
"thumbnail": "/images/Newsletters/thumb_canberraUG.jpg",
"thumbnailDescription": "Canberra .NET User Group ",
"presenterName": "Adam Cogan ",
"presenterProfileUrl": "https://sswcom.sharepoint.com/sites/Events/AboutUs/Employees/Pages/Adam.aspx",
Expand Down
Loading

0 comments on commit a94270e

Please sign in to comment.