diff --git a/TODO.md b/TODO.md
index 80cc591d3..ebcce550e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -17,7 +17,7 @@ Calendar
- [x] Add winners page
- [ ] E2E test add winners page
- [x] Winners on the event info page
-- [ ] There should be a banner on the list page if you have past tournaments to report
+- [x] There should be a banner on the list page if you have past tournaments to report
- [x] On the user page tab showing past results
- [ ] Translations
diff --git a/app/components/Alert.tsx b/app/components/Alert.tsx
new file mode 100644
index 000000000..4d0af01c2
--- /dev/null
+++ b/app/components/Alert.tsx
@@ -0,0 +1,16 @@
+import type * as React from "react";
+import { AlertIcon } from "./icons/Alert";
+
+export function Alert({
+ children,
+ textClassName,
+}: {
+ children: React.ReactNode;
+ textClassName?: string;
+}) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/app/db/models/calendar.server.ts b/app/db/models/calendar.server.ts
index 549ee56e0..0bca230d6 100644
--- a/app/db/models/calendar.server.ts
+++ b/app/db/models/calendar.server.ts
@@ -502,3 +502,36 @@ export function findBadgesById(eventId: CalendarEvent["id"]) {
Pick
>;
}
+
+const eventsToReportStm = sql.prepare(`
+ select
+ "CalendarEvent"."id",
+ "CalendarEvent"."name",
+ (select max("startTime")
+ from "CalendarEventDate"
+ where "eventId" = "CalendarEvent"."id") as "startTime"
+ from "CalendarEvent"
+ where
+ "CalendarEvent"."authorId" = $authorId
+ and
+ "startTime" > $lowerLimitTime
+ and
+ "startTime" < $upperLimitTime
+ and
+ "CalendarEvent"."participantCount" is null
+`);
+
+export function eventsToReport(authorId?: CalendarEvent["authorId"]) {
+ if (!authorId) return [];
+
+ const oneMonthAgo = new Date();
+ oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
+
+ return (
+ eventsToReportStm.all({
+ authorId,
+ upperLimitTime: dateToDatabaseTimestamp(new Date()),
+ lowerLimitTime: dateToDatabaseTimestamp(oneMonthAgo),
+ }) as Array>
+ ).map((row) => ({ id: row.id, name: row.name }));
+}
diff --git a/app/routes/calendar/index.tsx b/app/routes/calendar/index.tsx
index 7e7d74017..6f6b498a0 100644
--- a/app/routes/calendar/index.tsx
+++ b/app/routes/calendar/index.tsx
@@ -8,10 +8,11 @@ import React from "react";
import { Flipped, Flipper } from "react-flip-toolkit";
import { useTranslation } from "react-i18next";
import { z } from "zod";
+import { Alert } from "~/components/Alert";
import { LinkButton } from "~/components/Button";
import { db } from "~/db";
import { useIsMounted } from "~/hooks/useIsMounted";
-import { useUser } from "~/modules/auth";
+import { getUser, useUser } from "~/modules/auth";
import { i18next } from "~/modules/i18n";
import styles from "~/styles/calendar.css";
import { joinListToNaturalString } from "~/utils/arrays";
@@ -22,7 +23,7 @@ import {
} from "~/utils/dates";
import { discordFullName, makeTitle } from "~/utils/strings";
import type { Unpacked } from "~/utils/types";
-import { resolveBaseUrl } from "~/utils/urls";
+import { calendarReportWinnersPage, resolveBaseUrl } from "~/utils/urls";
import { actualNumber } from "~/utils/zod";
import { Tags } from "./components/Tags";
@@ -55,6 +56,7 @@ const loaderSearchParamsSchema = z.object({
});
export const loader = async ({ request }: LoaderArgs) => {
+ const user = await getUser(request);
const t = await i18next.getFixedT(request);
const url = new URL(request.url);
const parsedParams = loaderSearchParamsSchema.safeParse({
@@ -87,6 +89,7 @@ export const loader = async ({ request }: LoaderArgs) => {
}),
weeks: closeByWeeks({ week: displayedWeek, year: displayedYear }),
events: fetchEventsOfWeek({ week: displayedWeek, year: displayedYear }),
+ eventsToReport: db.calendarEvents.eventsToReport(user?.id),
title: makeTitle([`Week ${displayedWeek}`, t("pages.calendar")]),
});
};
@@ -143,6 +146,7 @@ export default function CalendarPage() {
return (
+