From 98c9701c22bc5cfd1d987d1462053d441c122925 Mon Sep 17 00:00:00 2001 From: Lidavic Date: Tue, 10 Mar 2026 19:50:25 +0100 Subject: [PATCH 1/4] front-end filtering, no styling --- frontend/src/Pages/EventsPage/EventsPage.tsx | 23 ++++++++- .../components/EventsList/EventsList.tsx | 49 ++++++++++++++++--- frontend/src/api.ts | 5 +- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/frontend/src/Pages/EventsPage/EventsPage.tsx b/frontend/src/Pages/EventsPage/EventsPage.tsx index b11725965..30cce6efb 100644 --- a/frontend/src/Pages/EventsPage/EventsPage.tsx +++ b/frontend/src/Pages/EventsPage/EventsPage.tsx @@ -3,14 +3,20 @@ import { useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; import { Page } from '~/Components'; import { getEventsPerDay } from '~/api'; +import type { EventDto } from '~/dto'; import { useTitle } from '~/hooks'; import { KEY } from '~/i18n/constants'; +import type { EventCategoryValue } from '~/types'; import { EventsList } from './components/EventsList'; export function EventsPage() { const { t } = useTranslation(); - const [events, setEvents] = useState({}); + const [events, setEvents] = useState>({}); const [showSpinner, setShowSpinner] = useState(true); + const [venues, setVenues] = useState([]); + const [categories, setCategories] = useState([]); + const [selectedVenue, setSelectedVenue] = useState(null); + const [selectedCategory, setSelectedCategory] = useState(null); useTitle(t(KEY.common_events)); @@ -19,6 +25,11 @@ export function EventsPage() { getEventsPerDay() .then((data) => { setEvents(data); + + const allEvents = Object.values(data).flat(); + setVenues([...new Set(allEvents.map((event) => event.location))]); + setCategories([...new Set(allEvents.map((event) => event.category))]); + setShowSpinner(false); }) .catch((error) => { @@ -29,7 +40,15 @@ export function EventsPage() { return ( - + ); } diff --git a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx index 2a4688970..adff9ba31 100644 --- a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx +++ b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx @@ -1,8 +1,8 @@ import { Icon } from '@iconify/react'; import { type ReactNode, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, IconButton, InputField, Link, TimeDisplay } from '~/Components'; -import { eventQuery } from '~/Components/EventQuery/utils'; +import { Button, Dropdown, IconButton, InputField, Link, TimeDisplay } from '~/Components'; +import type { DropdownOption } from '~/Components/Dropdown/Dropdown'; import { ImageCard } from '~/Components/ImageCard'; import { Table, type TableRow } from '~/Components/Table'; import { BACKEND_DOMAIN } from '~/constants'; @@ -11,20 +11,42 @@ import { useDesktop } from '~/hooks'; import { KEY } from '~/i18n/constants'; import { reverse } from '~/named-urls'; import { ROUTES } from '~/routes'; +import type { EventCategoryValue, SetState } from '~/types'; import { COLORS } from '~/types'; -import { dbT } from '~/utils'; +import { dbT, lowerCapitalize } from '~/utils'; import styles from './EventsList.module.scss'; type EventsListProps = { events: Record; + categories: EventCategoryValue[] | null; + venues: string[] | null; + selectedVenue: string | null; + setSelectedVenue: SetState; + selectedCategory: EventCategoryValue | null; + setSelectedCategory: SetState; }; -export function EventsList({ events }: EventsListProps) { +export function EventsList({ + events, + categories, + venues, + selectedVenue, + setSelectedVenue, + selectedCategory, + setSelectedCategory, +}: EventsListProps) { const { t, i18n } = useTranslation(); const [tableView, setTableView] = useState(false); const [query, setQuery] = useState(''); const isDesktop = useDesktop(); + const categoryOptions: DropdownOption[] = (categories ?? []).map((category) => { + return { label: category, value: category } as DropdownOption; + }); + const venueOptions: DropdownOption[] = (venues ?? []).map((venue) => { + return { label: venue, value: venue } as DropdownOption; + }); + const eventColumns = [ { content: t(KEY.common_title), sortable: true }, { content: t(KEY.common_date), sortable: true }, @@ -42,10 +64,13 @@ export function EventsList({ events }: EventsListProps) { const normalizedSearch = query.trim().toLowerCase(); const keywords = normalizedSearch.split(' '); - if (query === '') return eventQuery(allEvents, query); return allEvents.filter((event) => { const title = (dbT(event, 'title', i18n.language) as string)?.toLowerCase() ?? ''; - return keywords.every((kw) => title.includes(kw)); + const matchesSearch = query === '' || keywords.every((kw) => title.includes(kw)); + const matchesVenue = !selectedVenue || event.location === selectedVenue; + const matchesCategory = !selectedCategory || event.category === selectedCategory; + + return matchesSearch && matchesVenue && matchesCategory; }); } @@ -132,6 +157,18 @@ export function EventsList({ events }: EventsListProps) { /> )} + setSelectedCategory(val as EventCategoryValue)} + className={styles.element} + nullOption={{ label: lowerCapitalize(`${t(KEY.common_choose)} ${t(KEY.event_type)}`) }} + /> + setSelectedVenue(val as string)} + className={styles.element} + nullOption={{ label: lowerCapitalize(`${t(KEY.common_choose)} ${t(KEY.common_venue)}`) }} + /> {/* TODO translate */} diff --git a/frontend/src/api.ts b/frontend/src/api.ts index cf4793f97..c270cfafe 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -239,10 +239,9 @@ export async function putInformationPage( return response; } -export async function getEventsPerDay(): Promise { +export async function getEventsPerDay(): Promise> { const url = BACKEND_DOMAIN + ROUTES.backend.samfundet__eventsperday; - const response = await axios.get(url, { withCredentials: true }); - + const response = await axios.get>(url, { withCredentials: true }); return response.data; } From f20f7de93f7f7a4ac9e865bfcf44a30658f5fbf0 Mon Sep 17 00:00:00 2001 From: Lidavic Date: Thu, 19 Mar 2026 14:35:07 +0100 Subject: [PATCH 2/4] update filteredevents to use usememo and remove filterbutton --- .../components/EventsList/EventsList.tsx | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx index c0ed28309..e64261778 100644 --- a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx +++ b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx @@ -1,18 +1,16 @@ import { Icon } from '@iconify/react'; -import { type ReactNode, useState } from 'react'; +import { type ReactNode, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Dropdown, IconButton, InputField, Link, TimeDisplay } from '~/Components'; +import { Button, Dropdown, InputField, Link, TimeDisplay } from '~/Components'; import type { DropdownOption } from '~/Components/Dropdown/Dropdown'; import { ImageCard } from '~/Components/ImageCard'; import { Table, type TableRow } from '~/Components/Table'; import { BACKEND_DOMAIN } from '~/constants'; import type { EventDto } from '~/dto'; -import { useDesktop } from '~/hooks'; import { KEY } from '~/i18n/constants'; import { reverse } from '~/named-urls'; import { ROUTES } from '~/routes'; import type { EventCategoryValue, SetState } from '~/types'; -import { COLORS } from '~/types'; import { dbT, lowerCapitalize } from '~/utils'; import styles from './EventsList.module.scss'; @@ -38,7 +36,6 @@ export function EventsList({ const { t, i18n } = useTranslation(); const [tableView, setTableView] = useState(false); const [query, setQuery] = useState(''); - const isDesktop = useDesktop(); const categoryOptions: DropdownOption[] = (categories ?? []).map((category) => { return { label: category, value: category } as DropdownOption; @@ -59,8 +56,8 @@ export function EventsList({ ]; // TODO debounce and move header/filtering stuff to a separate component - function filteredEvents() { - const allEvents = Object.keys(events).flatMap((k: string) => events[k]); + const filteredEvents = useMemo(() => { + const allEvents = Object.values(events).flat(); const normalizedSearch = query.trim().toLowerCase(); const keywords = normalizedSearch.split(' '); @@ -72,11 +69,11 @@ export function EventsList({ return matchesSearch && matchesVenue && matchesCategory; }); - } + }, [events, query, selectedVenue, selectedCategory, i18n.language]); // TODO improve table view for events function getEventRows(): TableRow[] { - return filteredEvents().map((event) => ({ + return filteredEvents.map((event) => ({ cells: [ { content: ( @@ -104,7 +101,7 @@ export function EventsList({ } function getEventCards(): ReactNode[] { - return filteredEvents().map((event: EventDto) => { + return filteredEvents.map((event: EventDto) => { const time_display = ; return (
@@ -147,16 +144,6 @@ export function EventsList({ onChange={setQuery} value={query} /> - {isDesktop && ( - - alert('TODO legg til tinius sitt filter')} - /> - - )} setSelectedCategory(val as EventCategoryValue)} From 68df93c286b999d7f570c6fd673c269415909715 Mon Sep 17 00:00:00 2001 From: Lidavic Date: Tue, 14 Apr 2026 21:16:16 +0200 Subject: [PATCH 3/4] fix translations for event types --- .../EventsPage/components/EventsList/EventsList.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx index e64261778..bfaa1b9e3 100644 --- a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx +++ b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx @@ -10,8 +10,8 @@ import type { EventDto } from '~/dto'; import { KEY } from '~/i18n/constants'; import { reverse } from '~/named-urls'; import { ROUTES } from '~/routes'; -import type { EventCategoryValue, SetState } from '~/types'; -import { dbT, lowerCapitalize } from '~/utils'; +import { EventCategory, type EventCategoryValue, type SetState } from '~/types'; +import { dbT, getEventCategoryKey, lowerCapitalize } from '~/utils'; import styles from './EventsList.module.scss'; type EventsListProps = { @@ -37,9 +37,10 @@ export function EventsList({ const [tableView, setTableView] = useState(false); const [query, setQuery] = useState(''); - const categoryOptions: DropdownOption[] = (categories ?? []).map((category) => { - return { label: category, value: category } as DropdownOption; - }); + const eventCategoryOptions: DropdownOption[] = Object.values(EventCategory).map((category)=>({ + value: category, label: t(getEventCategoryKey(category)), + })); + const venueOptions: DropdownOption[] = (venues ?? []).map((venue) => { return { label: venue, value: venue } as DropdownOption; }); @@ -145,7 +146,7 @@ export function EventsList({ value={query} /> setSelectedCategory(val as EventCategoryValue)} className={styles.element} nullOption={{ label: lowerCapitalize(`${t(KEY.common_choose)} ${t(KEY.event_type)}`) }} From d9a8e95b5695f471e156a422f089423b9b3f9b3e Mon Sep 17 00:00:00 2001 From: Lidavic Date: Tue, 14 Apr 2026 21:26:52 +0200 Subject: [PATCH 4/4] biome...................... --- .../Pages/EventsPage/components/EventsList/EventsList.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx index bfaa1b9e3..9e671a8a8 100644 --- a/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx +++ b/frontend/src/Pages/EventsPage/components/EventsList/EventsList.tsx @@ -37,8 +37,9 @@ export function EventsList({ const [tableView, setTableView] = useState(false); const [query, setQuery] = useState(''); - const eventCategoryOptions: DropdownOption[] = Object.values(EventCategory).map((category)=>({ - value: category, label: t(getEventCategoryKey(category)), + const eventCategoryOptions: DropdownOption[] = Object.values(EventCategory).map((category) => ({ + value: category, + label: t(getEventCategoryKey(category)), })); const venueOptions: DropdownOption[] = (venues ?? []).map((venue) => {