diff --git a/app/components/layout/nav-items.json b/app/components/layout/nav-items.json index 3f7b5ba1f..a31567fa2 100644 --- a/app/components/layout/nav-items.json +++ b/app/components/layout/nav-items.json @@ -6,6 +6,7 @@ { "name": "builds", "url": "builds" }, { "name": "analyzer", "url": "analyzer" }, { "name": "calendar", "url": "calendar" }, + { "name": "maps", "url": "maps" }, { "name": "badges", "url": "badges" }, { "name": "plus", diff --git a/app/routes/calendar/$id/index.tsx b/app/routes/calendar/$id/index.tsx index 2d7b14fcb..5e17d4188 100644 --- a/app/routes/calendar/$id/index.tsx +++ b/app/routes/calendar/$id/index.tsx @@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next"; import { z } from "zod"; import { Avatar } from "~/components/Avatar"; import { LinkButton } from "~/components/Button"; +import { Image } from "~/components/Image"; import { Main } from "~/components/Main"; import { Section } from "~/components/Section"; import { db } from "~/db"; @@ -23,21 +24,28 @@ import { canEditCalendarEvent, canReportCalendarEventWinners, } from "~/permissions"; -import styles from "~/styles/calendar-event.css"; +import calendarStyles from "~/styles/calendar-event.css"; +import mapsStyles from "~/styles/maps.css"; import { databaseTimestampToDate } from "~/utils/dates"; import { notFoundIfFalsy } from "~/utils/remix"; import { discordFullName, makeTitle, placementString } from "~/utils/strings"; import { calendarEditPage, calendarReportWinnersPage, + mapsPage, + navIconUrl, resolveBaseUrl, userPage, } from "~/utils/urls"; import { actualNumber, id } from "~/utils/zod"; +import { MapPoolSelector } from "../components/MapPoolSelector"; import { Tags } from "../components/Tags"; export const links: LinksFunction = () => { - return [{ rel: "stylesheet", href: styles }]; + return [ + { rel: "stylesheet", href: calendarStyles }, + { rel: "stylesheet", href: mapsStyles }, + ]; }; export const meta: MetaFunction = (args) => { @@ -52,7 +60,7 @@ export const meta: MetaFunction = (args) => { }; export const handle = { - i18n: "calendar", + i18n: ["calendar", "game-misc"], }; export const loader = async ({ params, request }: LoaderArgs) => { @@ -67,6 +75,7 @@ export const loader = async ({ params, request }: LoaderArgs) => { badgePrizes: db.calendarEvents.findBadgesByEventId(parsedParams.id), title: makeTitle([event.name, t("pages.calendar")]), results: db.calendarEvents.findResultsByEventId(parsedParams.id), + mapPool: db.calendarEvents.findMapPoolByEventId(parsedParams.id), }); }; @@ -159,6 +168,7 @@ export default function CalendarEventPage() { + ); @@ -222,6 +232,30 @@ function Results() { ); } +function MapPoolInfo() { + const { t } = useTranslation(["calendar"]); + const data = useLoaderData(); + + if (!data.mapPool) return null; + + return ( +
+
+ + + + {t("calendar:createMapList")} + +
+
+ ); +} + function Description() { const { t } = useTranslation(); const data = useLoaderData(); diff --git a/app/routes/calendar/components/MapPoolSelector.tsx b/app/routes/calendar/components/MapPoolSelector.tsx new file mode 100644 index 000000000..b47ae737c --- /dev/null +++ b/app/routes/calendar/components/MapPoolSelector.tsx @@ -0,0 +1,97 @@ +import clsx from "clsx"; +import { useTranslation } from "react-i18next"; +import { Image } from "~/components/Image"; +import type { ModeShort, StageId } from "~/modules/in-game-lists"; +import { modes, stageIds } from "~/modules/in-game-lists"; +import { type MapPool } from "~/modules/map-pool-serializer"; +import { modeImageUrl, stageImageUrl } from "~/utils/urls"; + +// xxx: maybe better mobile layout? +export function MapPoolSelector({ + mapPool, + handleMapPoolChange, +}: { + mapPool: MapPool; + handleMapPoolChange?: ({ + mode, + stageId, + }: { + mode: ModeShort; + stageId: StageId; + }) => void; +}) { + const { t } = useTranslation(["game-misc", "calendar"]); + + const isPresentational = !handleMapPoolChange; + + const stageRowIsVisible = (stageId: StageId) => { + if (!isPresentational) return true; + + return modes.some((mode) => mapPool[mode.short].includes(stageId)); + }; + + return ( +
+ {stageIds.filter(stageRowIsVisible).map((stageId) => ( +
+ +
+
{t(`game-misc:STAGE_${stageId}`)}
+
+ {modes.map((mode) => { + const selected = (mapPool[mode.short] as StageId[]).includes( + stageId + ); + + if (isPresentational && !selected) return null; + if (isPresentational && selected) { + return ( + {mode.long} + ); + } + + return ( + + ); + })} +
+
+
+ ))} +
+ ); +} diff --git a/app/routes/calendar/new.tsx b/app/routes/calendar/new.tsx index 396254612..7334520d2 100644 --- a/app/routes/calendar/new.tsx +++ b/app/routes/calendar/new.tsx @@ -1,32 +1,52 @@ -import { Form, useLoaderData } from "@remix-run/react"; -import { Label } from "~/components/Label"; -import { Main } from "~/components/Main"; -import * as React from "react"; -import type { Badge as BadgeType, CalendarEventTag } from "~/db/types"; -import { CALENDAR_EVENT } from "~/constants"; -import { Button } from "~/components/Button"; import type { SerializeFrom } from "@remix-run/node"; import { json, redirect, - type MetaFunction, type ActionFunction, type LinksFunction, type LoaderArgs, + type MetaFunction, } from "@remix-run/node"; -import calendarNewStyles from "~/styles/calendar-new.css"; -import mapsStyles from "~/styles/maps.css"; +import { Form, useLoaderData } from "@remix-run/react"; import clsx from "clsx"; +import * as React from "react"; import { useTranslation } from "react-i18next"; +import { z } from "zod"; +import { Badge } from "~/components/Badge"; +import { Button } from "~/components/Button"; +import { DateInput } from "~/components/DateInput"; +import { FormMessage } from "~/components/FormMessage"; import { TrashIcon } from "~/components/icons/Trash"; import { Input } from "~/components/Input"; -import { FormMessage } from "~/components/FormMessage"; -import { useIsMounted } from "~/hooks/useIsMounted"; -import { Tags } from "./components/Tags"; +import { Label } from "~/components/Label"; +import { Main } from "~/components/Main"; +import { Toggle } from "~/components/Toggle"; +import { CALENDAR_EVENT } from "~/constants"; import { db } from "~/db"; +import type { Badge as BadgeType, CalendarEventTag } from "~/db/types"; +import { useIsMounted } from "~/hooks/useIsMounted"; import { requireUser } from "~/modules/auth"; -import { Badge } from "~/components/Badge"; -import { z } from "zod"; +import { i18next } from "~/modules/i18n"; +import type { ModeShort, StageId } from "~/modules/in-game-lists"; +import { + mapPoolToSerializedString, + serializedStringToMapPool, + type MapPool, +} from "~/modules/map-pool-serializer"; +import { canEditCalendarEvent } from "~/permissions"; +import calendarNewStyles from "~/styles/calendar-new.css"; +import mapsStyles from "~/styles/maps.css"; +import { + databaseTimestampToDate, + dateToDatabaseTimestamp, +} from "~/utils/dates"; +import { + badRequestIfFalsy, + parseRequestFormData, + validate, +} from "~/utils/remix"; +import { makeTitle } from "~/utils/strings"; +import { calendarEventPage } from "~/utils/urls"; import { actualNumber, date, @@ -37,29 +57,8 @@ import { safeJSONParse, toArray, } from "~/utils/zod"; -import { - badRequestIfFalsy, - parseRequestFormData, - validate, -} from "~/utils/remix"; -import { - databaseTimestampToDate, - dateToDatabaseTimestamp, -} from "~/utils/dates"; -import { calendarEventPage, modeImageUrl, stageImageUrl } from "~/utils/urls"; -import { makeTitle } from "~/utils/strings"; -import { i18next } from "~/modules/i18n"; -import { canEditCalendarEvent } from "~/permissions"; -import { DateInput } from "~/components/DateInput"; -import type { ModeShort, StageId } from "~/modules/in-game-lists"; -import { modes, stageIds } from "~/modules/in-game-lists"; -import { Image } from "~/components/Image"; -import { - type MapPool, - serializedStringToMapPool, -} from "~/modules/map-pool-serializer"; -import { mapPoolToSerializedString } from "~/modules/map-pool-serializer"; -import { Toggle } from "~/components/Toggle"; +import { MapPoolSelector } from "./components/MapPoolSelector"; +import { Tags } from "./components/Tags"; const MIN_DATE = new Date(Date.UTC(2015, 4, 28)); @@ -235,7 +234,7 @@ export default function CalendarNewEventPage() { - + @@ -534,7 +533,7 @@ const DEFAULT_MAP_POOL = { RM: [], TW: [], }; -function MapPoolSelector() { +function MapPoolSection() { const { t } = useTranslation(["game-misc", "calendar"]); const data = useLoaderData(); @@ -578,56 +577,10 @@ function MapPoolSelector() {
{includeMapPool && ( -
- {stageIds.map((stageId) => ( -
- -
-
{t(`game-misc:STAGE_${stageId}`)}
-
- {modes.map((mode) => { - const selected = ( - mapPool[mode.short] as StageId[] - ).includes(stageId); - - return ( - - ); - })} -
-
-
- ))} -
+ )}
diff --git a/app/styles/calendar-event.css b/app/styles/calendar-event.css index eb6d0c311..b56e58163 100644 --- a/app/styles/calendar-event.css +++ b/app/styles/calendar-event.css @@ -17,6 +17,24 @@ height: 1.25rem; } +.event__map-pool-section { + display: flex; + max-width: 32rem; + flex-direction: column; + padding: var(--s-2); + margin: 0 auto; + gap: var(--s-4); +} + +.event__create-map-list-link { + display: flex; + width: max-content; + align-items: center; + margin: 0 auto; + font-size: var(--fonts-xs); + gap: var(--s-1-5); +} + .event__author { display: flex; align-items: center; diff --git a/public/img/layout/maps.avif b/public/img/layout/maps.avif new file mode 100644 index 000000000..28e0f6fb2 Binary files /dev/null and b/public/img/layout/maps.avif differ diff --git a/public/img/layout/maps.png b/public/img/layout/maps.png new file mode 100644 index 000000000..f04def871 Binary files /dev/null and b/public/img/layout/maps.png differ diff --git a/public/locales/en/calendar.json b/public/locales/en/calendar.json index ee0fd6c37..ea894544c 100644 --- a/public/locales/en/calendar.json +++ b/public/locales/en/calendar.json @@ -8,6 +8,7 @@ "participatedCount": "{{count}} teams participated", "members": "Members", "results": "Results", + "createMapList": "Create map list", "forms.dates": "Dates", "forms.bracketUrl": "Bracket URL", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index d89e4d4e6..33cd2a53d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -8,6 +8,7 @@ "pages.faq": "FAQ", "pages.builds": "Builds", "pages.analyzer": "Build Analyzer", + "pages.maps": "Map Lists", "header.profile": "Profile", "header.logout": "Log out",