Skip to content

Commit

Permalink
Filter calendar events by tags (#1972)
Browse files Browse the repository at this point in the history
* backend

* Tournament docs only visible when adding tournament

* Progress

* Finish?

* Fix
  • Loading branch information
Sendouc authored Nov 25, 2024
1 parent 6d654ba commit dd53b32
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 78 deletions.
6 changes: 2 additions & 4 deletions app/db/seed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { mySlugify } from "~/utils/urls";
import type { SeedVariation } from "~/features/api-private/routes/seed";
import * as BuildRepository from "~/features/builds/BuildRepository.server";
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
import { tags } from "~/features/calendar/calendar-constants";
import { persistedTags } from "~/features/calendar/calendar-constants";
import * as LFGRepository from "~/features/lfg/LFGRepository.server";
import { TIMEZONES } from "~/features/lfg/lfg-constants";
import * as PlusSuggestionRepository from "~/features/plus-suggestions/PlusSuggestionRepository.server";
Expand Down Expand Up @@ -698,9 +698,7 @@ function calendarEvents() {
const userIds = userIdsInRandomOrder();

for (let id = 1; id <= AMOUNT_OF_CALENDAR_EVENTS; id++) {
const shuffledTags = shuffle(Object.keys(tags)).filter(
(tag) => tag !== "BADGE",
);
const shuffledTags = shuffle(Object.keys(persistedTags));

sql
.prepare(
Expand Down
6 changes: 5 additions & 1 deletion app/db/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { tags } from "~/features/calendar/calendar-constants";
import type {
persistedTags,
tags,
} from "~/features/calendar/calendar-constants";
import type { TieredSkill } from "~/features/mmr/tiered.server";
import type { TEAM_MEMBER_ROLES } from "~/features/team";
import type {
Expand Down Expand Up @@ -135,6 +138,7 @@ export interface CalendarEvent {
tournamentId: number | null;
}

export type PersistedCalendarEventTag = keyof typeof persistedTags;
export type CalendarEventTag = keyof typeof tags;

export interface CalendarEventDate {
Expand Down
7 changes: 6 additions & 1 deletion app/features/api-public/routes/calendar.$year.$week.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,10 @@ function fetchEventsOfWeek(args: { week: number; year: number }) {
const endTime = new Date(startTime);
endTime.setDate(endTime.getDate() + 7);

return CalendarRepository.findAllBetweenTwoTimestamps({ startTime, endTime });
return CalendarRepository.findAllBetweenTwoTimestamps({
startTime,
endTime,
tagsToFilterBy: [],
onlyTournaments: false,
});
}
40 changes: 33 additions & 7 deletions app/features/calendar/CalendarRepository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { sql } from "kysely";
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
import { db } from "~/db/sql";
import type { DB, Tables, TournamentSettings } from "~/db/tables";
import type { CalendarEventTag } from "~/db/types";
import type { CalendarEventTag, PersistedCalendarEventTag } from "~/db/types";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
import * as Progression from "~/features/tournament-bracket/core/Progression";
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
Expand Down Expand Up @@ -143,11 +143,15 @@ export type FindAllBetweenTwoTimestampsItem = Unwrapped<
export async function findAllBetweenTwoTimestamps({
startTime,
endTime,
tagsToFilterBy,
onlyTournaments,
}: {
startTime: Date;
endTime: Date;
tagsToFilterBy: Array<PersistedCalendarEventTag>;
onlyTournaments: boolean;
}) {
const rows = await db
let query = db
.selectFrom("CalendarEvent")
.innerJoin(
"CalendarEventDate",
Expand Down Expand Up @@ -211,8 +215,17 @@ export async function findAllBetweenTwoTimestamps({
"<=",
dateToDatabaseTimestamp(endTime),
)
.orderBy("CalendarEventDate.startTime", "asc")
.execute();
.orderBy("CalendarEventDate.startTime", "asc");

for (const tag of tagsToFilterBy) {
query = query.where("CalendarEvent.tags", "like", `%${tag}%`);
}

if (onlyTournaments) {
query = query.where("CalendarEvent.tournamentId", "is not", null);
}

const rows = await query.execute();

return Promise.all(
rows
Expand Down Expand Up @@ -305,17 +318,30 @@ async function tournamentParticipantCount({
export async function startTimesOfRange({
startTime,
endTime,
tagsToFilterBy,
onlyTournaments,
}: {
startTime: Date;
endTime: Date;
tagsToFilterBy: Array<PersistedCalendarEventTag>;
onlyTournaments: boolean;
}) {
const rows = await db
let query = db
.selectFrom("CalendarEventDate")
.innerJoin("CalendarEvent", "CalendarEvent.id", "CalendarEventDate.eventId")
.select(["startTime"])
.where("startTime", ">=", dateToDatabaseTimestamp(startTime))
.where("startTime", "<=", dateToDatabaseTimestamp(endTime))
.execute();
.where("startTime", "<=", dateToDatabaseTimestamp(endTime));

for (const tag of tagsToFilterBy) {
query = query.where("CalendarEvent.tags", "like", `%${tag}%`);
}

if (onlyTournaments) {
query = query.where("CalendarEvent.tournamentId", "is not", null);
}

const rows = await query.execute();
return rows.map((row) => row.startTime);
}

Expand Down
18 changes: 8 additions & 10 deletions app/features/calendar/actions/calendar.new.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ActionFunction } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import { z } from "zod";
import { TOURNAMENT_STAGE_TYPES } from "~/db/tables";
import type { CalendarEventTag } from "~/db/types";
import type { CalendarEventTag, PersistedCalendarEventTag } from "~/db/types";
import { requireUser } from "~/features/auth/core/user.server";
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server";
Expand Down Expand Up @@ -212,6 +212,12 @@ export const bracketProgressionSchema = z.preprocess(
),
);

export const calendarEventTagSchema = z
.string()
.refine((val) =>
CALENDAR_EVENT.PERSISTED_TAGS.includes(val as PersistedCalendarEventTag),
);

export const newCalendarEventActionSchema = z
.object({
eventToEditId: z.preprocess(actualNumber, id.nullish()),
Expand Down Expand Up @@ -252,15 +258,7 @@ export const newCalendarEventActionSchema = z
),
tags: z.preprocess(
processMany(safeJSONParse, removeDuplicates),
z
.array(
z
.string()
.refine((val) =>
CALENDAR_EVENT.TAGS.includes(val as CalendarEventTag),
),
)
.nullable(),
z.array(calendarEventTagSchema).nullable(),
),
badges: z.preprocess(
processMany(safeJSONParse, removeDuplicates),
Expand Down
19 changes: 14 additions & 5 deletions app/features/calendar/calendar-constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type { CalendarEventTag } from "~/db/types";
import type { CalendarEventTag, PersistedCalendarEventTag } from "~/db/types";

export const tags = {
BADGE: {
color: "#000",
},
export const persistedTags = {
SPECIAL: {
color: "#CE93D8",
},
Expand Down Expand Up @@ -60,6 +57,13 @@ export const tags = {
},
};

export const tags = {
...persistedTags,
BADGE: {
color: "#000",
},
};

export const CALENDAR_EVENT = {
NAME_MIN_LENGTH: 2,
NAME_MAX_LENGTH: 100,
Expand All @@ -68,6 +72,11 @@ export const CALENDAR_EVENT = {
DISCORD_INVITE_CODE_MAX_LENGTH: 50,
BRACKET_URL_MAX_LENGTH: 200,
MAX_AMOUNT_OF_DATES: 5,
/** Calendar event tag that is persisted in the database */
PERSISTED_TAGS: Object.keys(
persistedTags,
) as Array<PersistedCalendarEventTag>,
/** Calendar event tag, both those persisted in the database and those that are computed */
TAGS: Object.keys(tags) as Array<CalendarEventTag>,
AVATAR_SIZE: 512,
};
Expand Down
25 changes: 13 additions & 12 deletions app/features/calendar/routes/calendar.new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,17 @@ export default function CalendarNewEventPage() {
<h1 className="text-lg">
{data.isAddingTournament ? "New tournament" : "New calendar event"}
</h1>
<a
href={CREATING_TOURNAMENT_DOC_LINK}
className="text-lg text-bold"
title="Documentation about creating tournaments"
target="_blank"
rel="noopener noreferrer"
>
?
</a>
{data.isAddingTournament ? (
<a
href={CREATING_TOURNAMENT_DOC_LINK}
className="text-lg text-bold"
title="Documentation about creating tournaments"
target="_blank"
rel="noopener noreferrer"
>
?
</a>
) : null}
</div>
{data.isAddingTournament ? <TemplateTournamentForm /> : null}
<EventForm key={baseEvent?.eventId} />
Expand Down Expand Up @@ -572,9 +574,8 @@ function TagsAdder() {
const [tags, setTags] = React.useState(baseEvent?.tags ?? []);
const id = React.useId();

const tagsForSelect = CALENDAR_EVENT.TAGS.filter(
// @ts-expect-error TODO: fix this (5.5 version)
(tag) => !tags.includes(tag) && tag !== "BADGE",
const tagsForSelect = CALENDAR_EVENT.PERSISTED_TAGS.filter(
(tag) => !tags.includes(tag),
);

return (
Expand Down
Loading

0 comments on commit dd53b32

Please sign in to comment.