Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add separate admin list for past events #150

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions packages/ilmomasiina-frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
"login.errors.401.description": "Wrong username or password",
"login.errors.default.description": "Login failed",
"adminEvents.title": "Manage events",
"adminEvents.title.past": "Past events",
"adminEvents.loadError.default.description": "Failed to load events. $t(errors.contactAdmin)",
"adminEvents.nav.users": "Users",
"adminEvents.nav.auditLog": "Audit log",
"adminEvents.nav.newEvent": "+ New Event",
"adminEvents.nav.past": "Past events",
"adminEvents.nav.upcoming": "Upcoming events",
"adminEvents.noEvents.past": "No past events to show. Would you like to <1>view upcoming events</1> or <3>create a new event</3>?",
"adminEvents.noEvents.upcoming": "No upcoming events to show. Would you like to <1>view past events</1> or <3>create a new event</3>?",
"adminEvents.column.name": "Name",
"adminEvents.column.date": "Date",
"adminEvents.column.status": "Status",
Expand Down
5 changes: 5 additions & 0 deletions packages/ilmomasiina-frontend/src/locales/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
"login.errors.401.description": "Väärä käyttäjänimi tai salasana",
"login.errors.default.description": "Kirjautuminen epäonnistui",
"adminEvents.title": "Hallinta",
"adminEvents.title.past": "Menneet tapahtumat",
"adminEvents.loadError.default.description": "Tapahtumien lataaminen epäonnistui. $t(errors.contactAdmin)",
"adminEvents.nav.users": "Käyttäjät",
"adminEvents.nav.auditLog": "Toimintoloki",
"adminEvents.nav.newEvent": "+ Uusi tapahtuma",
"adminEvents.nav.past": "Menneet tapahtumat",
"adminEvents.nav.upcoming": "Tulevat tapahtumat",
"adminEvents.noEvents.past": "Ei menneitä tapahtumia. Haluatko <1>katsella tulevia tapahtumia</1> tai <3>luoda uuden tapahtuman</3>?",
"adminEvents.noEvents.upcoming": "Ei tulevia tapahtumia. Haluatko <1>katsella menneitä tapahtumia</1> tai <3>luoda uuden tapahtuman</3>?",
"adminEvents.column.name": "Nimi",
"adminEvents.column.date": "Ajankohta",
"adminEvents.column.status": "Tila",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { AdminEventListItem as AdminEventListItemSchema } from "@tietokilta
import { deleteEvent, getAdminEvents } from "../../modules/adminEvents/actions";
import appPaths from "../../paths";
import { useTypedDispatch } from "../../store/reducers";
import { isEventInPast } from "../../utils/eventState";
import { isEventHiddenFromUsersDueToAge } from "../../utils/eventState";

type Props = {
event: AdminEventListItemSchema;
Expand Down Expand Up @@ -46,10 +46,10 @@ const AdminEventListItem = ({ event }: Props) => {
let status;
if (draft) {
status = t("adminEvents.status.draft");
} else if (isEventInPast(event)) {
} else if (isEventHiddenFromUsersDueToAge(event)) {
status = date === null ? t("adminEvents.status.closed") : t("adminEvents.status.ended");
} else if (!listed) {
status = t("adminEvents.status.hidden");
status = <Link to={appPaths.eventDetails(slug)}>{t("adminEvents.status.hidden")}</Link>;
} else {
status = <Link to={appPaths.eventDetails(slug)}>{t("adminEvents.status.published")}</Link>;
}
Expand Down
41 changes: 36 additions & 5 deletions packages/ilmomasiina-frontend/src/routes/AdminEvents/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import React, { BaseSyntheticEvent, useCallback, useEffect, useMemo, useState } from "react";

import { Button, Spinner } from "react-bootstrap";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import { shallowEqual } from "react-redux";
import { Link } from "react-router-dom";

Expand All @@ -10,20 +10,33 @@ import requireAuth from "../../containers/requireAuth";
import { getAdminEvents, resetState } from "../../modules/adminEvents/actions";
import appPaths from "../../paths";
import { useTypedDispatch, useTypedSelector } from "../../store/reducers";
import { isEventInPast } from "../../utils/eventState";
import AdminEventListItem from "./AdminEventListItem";

const AdminEventsList = () => {
const dispatch = useTypedDispatch();
const { events, loadError } = useTypedSelector((state) => state.adminEvents, shallowEqual);
const [showPast, setShowPast] = useState(false);
const { t } = useTranslation();

const togglePast = useCallback((evt: BaseSyntheticEvent) => {
evt.preventDefault();
setShowPast((prev) => !prev);
}, []);

useEffect(() => {
dispatch(getAdminEvents());
return () => {
dispatch(resetState());
};
}, [dispatch]);

const shownEvents = useMemo(() => {
const filtered = events?.filter((event) => isEventInPast(event) === showPast);
// Additionally, reverse events when viewing past events, so the newest event comes first.
return showPast ? filtered?.reverse() : filtered;
}, [events, showPast]);

if (loadError) {
return (
<>
Expand All @@ -33,7 +46,7 @@ const AdminEventsList = () => {
);
}

if (!events) {
if (!shownEvents) {
return (
<>
<h1>{t("adminEvents.title")}</h1>
Expand All @@ -45,7 +58,10 @@ const AdminEventsList = () => {
return (
<>
<nav className="ilmo--title-nav">
<h1>{t("adminEvents.title")}</h1>
<h1>{showPast ? t("adminEvents.title.past") : t("adminEvents.title")}</h1>
<Button variant="secondary" onClick={togglePast}>
{showPast ? t("adminEvents.nav.upcoming") : t("adminEvents.nav.past")}
</Button>
<Button as={Link} variant="secondary" to={appPaths.adminUsersList}>
{t("adminEvents.nav.users")}
</Button>
Expand All @@ -67,9 +83,24 @@ const AdminEventsList = () => {
</tr>
</thead>
<tbody>
{events.map((event) => (
{shownEvents.map((event) => (
<AdminEventListItem key={event.id} event={event} />
))}
{!shownEvents.length && (
<tr>
<td colSpan={5}>
<Trans t={t} i18nKey={showPast ? "adminEvents.noEvents.past" : "adminEvents.noEvents.upcoming"}>
No (type) events to show. Would you like to
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" onClick={togglePast} role="button">
view (other type) events
</a>
or
<Link to={appPaths.adminEditEvent("new")}>create a new event</Link>?
</Trans>
</td>
</tr>
)}
</tbody>
</table>
</>
Expand Down
23 changes: 20 additions & 3 deletions packages/ilmomasiina-frontend/src/utils/eventState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import type { AdminEventListItem } from "@tietokilta/ilmomasiina-models";

// eslint-disable-next-line import/prefer-default-export
/** Determines the effective end date of the event, matching the scope logic in `backend/src/models/event`. */
function getEffectiveEndDate(event: Pick<AdminEventListItem, "date" | "endDate" | "registrationEndDate">) {
const endDates = [event.endDate, event.date, event.registrationEndDate]
.filter((date): date is string => date != null)
.map((date) => new Date(date).getTime());
if (!endDates.length) return null;
return endDates.reduce((lhs, rhs) => Math.max(lhs, rhs));
}

/** Checks whether the event's effective end date is before now. */
export function isEventInPast(event: Pick<AdminEventListItem, "date" | "endDate" | "registrationEndDate">) {
const endDate = event.endDate ?? event.date ?? event.registrationEndDate;
return endDate != null && new Date(endDate) < new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const endDate = getEffectiveEndDate(event);
return endDate != null && endDate < Date.now();
}

/** Checks whether the event is hidden from regular users due to the scope logic in `backend/src/models/event`. */
export function isEventHiddenFromUsersDueToAge(
event: Pick<AdminEventListItem, "date" | "endDate" | "registrationEndDate">,
) {
const endDate = getEffectiveEndDate(event);
return endDate != null && endDate < Date.now() - 7 * 24 * 60 * 60 * 1000;
}