Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
23 changes: 21 additions & 2 deletions frontend/src/Pages/EventsPage/EventsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, EventDto[]>>({});
const [showSpinner, setShowSpinner] = useState<boolean>(true);
const [venues, setVenues] = useState<string[] | null>([]);
const [categories, setCategories] = useState<EventCategoryValue[]>([]);
const [selectedVenue, setSelectedVenue] = useState<string | null>(null);
const [selectedCategory, setSelectedCategory] = useState<EventCategoryValue | null>(null);

useTitle(t(KEY.common_events));

Expand All @@ -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) => {
Expand All @@ -29,7 +40,15 @@ export function EventsPage() {

return (
<Page loading={showSpinner}>
<EventsList events={events} />
<EventsList
events={events}
categories={categories}
venues={venues}
selectedVenue={selectedVenue}
setSelectedVenue={setSelectedVenue}
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
/>
</Page>
);
}
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<string, EventDto[]>;
categories: EventCategoryValue[] | null;
venues: string[] | null;
selectedVenue: string | null;
setSelectedVenue: SetState<string | null>;
selectedCategory: EventCategoryValue | null;
setSelectedCategory: SetState<EventCategoryValue | null>;
};

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<string | null>[] = (categories ?? []).map((category) => {
return { label: category, value: category } as DropdownOption<string>;
});
const venueOptions: DropdownOption<string | null>[] = (venues ?? []).map((venue) => {
return { label: venue, value: venue } as DropdownOption<string>;
});

const eventColumns = [
{ content: t(KEY.common_title), sortable: true },
{ content: t(KEY.common_date), sortable: true },
Expand All @@ -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;
});
}
Comment thread
hei98 marked this conversation as resolved.
Outdated

Expand Down Expand Up @@ -132,6 +157,18 @@ export function EventsList({ events }: EventsListProps) {
/>
</span>
)}
Comment thread
hei98 marked this conversation as resolved.
Outdated
<Dropdown
options={categoryOptions}
onChange={(val) => setSelectedCategory(val as EventCategoryValue)}
className={styles.element}
nullOption={{ label: lowerCapitalize(`${t(KEY.common_choose)} ${t(KEY.event_type)}`) }}
/>
<Dropdown
options={venueOptions}
onChange={(val) => setSelectedVenue(val as string)}
className={styles.element}
nullOption={{ label: lowerCapitalize(`${t(KEY.common_choose)} ${t(KEY.common_venue)}`) }}
/>
</div>

{/* TODO translate */}
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,9 @@ export async function putInformationPage(
return response;
}

export async function getEventsPerDay(): Promise<EventDto[]> {
export async function getEventsPerDay(): Promise<Record<string, EventDto[]>> {
const url = BACKEND_DOMAIN + ROUTES.backend.samfundet__eventsperday;
const response = await axios.get<EventDto[]>(url, { withCredentials: true });

const response = await axios.get<Record<string, EventDto[]>>(url, { withCredentials: true });
return response.data;
}

Expand Down