diff --git a/app/components/EventsList.module.css b/app/components/EventsList.module.css new file mode 100644 index 000000000..237139854 --- /dev/null +++ b/app/components/EventsList.module.css @@ -0,0 +1,8 @@ +.dayHeader { + padding: var(--s-2) var(--s-2) var(--s-1); + font-size: var(--font-2xs); + font-weight: var(--weight-bold); + color: var(--color-text-high); + text-transform: uppercase; + letter-spacing: 0.05em; +} diff --git a/app/components/EventsList.tsx b/app/components/EventsList.tsx new file mode 100644 index 000000000..1e455ce22 --- /dev/null +++ b/app/components/EventsList.tsx @@ -0,0 +1,101 @@ +import { isToday, isTomorrow } from "date-fns"; +import { useTranslation } from "react-i18next"; +import type { SidebarEvent } from "~/features/sidebar/core/sidebar.server"; +import styles from "./EventsList.module.css"; +import { ListLink } from "./SideNav"; + +export function EventsList({ + events, + onClick, +}: { + events: SidebarEvent[]; + onClick?: () => void; +}) { + const { t, i18n } = useTranslation(["front"]); + + if (events.length === 0) { + return ( +
+ {t("front:sideNav.noEvents")} +
+ ); + } + + const getDayKey = (timestamp: number) => { + const date = new Date(timestamp * 1000); + return date.toDateString(); + }; + + const formatDayHeader = (date: Date) => { + if (isToday(date)) { + const rtf = new Intl.RelativeTimeFormat(i18n.language, { + numeric: "auto", + }); + const str = rtf.format(0, "day"); + return str.charAt(0).toUpperCase() + str.slice(1); + } + if (isTomorrow(date)) { + const rtf = new Intl.RelativeTimeFormat(i18n.language, { + numeric: "auto", + }); + const str = rtf.format(1, "day"); + return str.charAt(0).toUpperCase() + str.slice(1); + } + return date.toLocaleDateString(i18n.language, { + weekday: "long", + month: "short", + day: "numeric", + }); + }; + + const formatTime = (date: Date) => { + return date.toLocaleTimeString(i18n.language, { + hour: "numeric", + minute: "2-digit", + }); + }; + + const groupedEvents = events.reduce>( + (acc, event) => { + const key = getDayKey(event.startTime); + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(event); + return acc; + }, + {}, + ); + + const dayKeys = Object.keys(groupedEvents); + + return ( + <> + {dayKeys.map((dayKey) => { + const dayEvents = groupedEvents[dayKey]; + const firstDate = new Date(dayEvents[0].startTime * 1000); + + return ( +
+
{formatDayHeader(firstDate)}
+ {dayEvents.map((event) => ( + + {event.scrimStatus === "booked" + ? t("front:sideNav.scrimVs", { opponent: event.name }) + : event.scrimStatus === "looking" + ? t("front:sideNav.lookingForScrim") + : event.name} + + ))} +
+ ); + })} + + ); +} diff --git a/app/components/MobileNav.module.css b/app/components/MobileNav.module.css index 6efb2c533..60d9e3b8c 100644 --- a/app/components/MobileNav.module.css +++ b/app/components/MobileNav.module.css @@ -299,24 +299,24 @@ } .panelSectionLink { - display: block; + display: flex; + align-items: center; + gap: 2px; + width: fit-content; + margin: var(--s-4) auto; font-size: var(--font-2xs); color: var(--color-text-high); text-decoration: none; - padding: var(--s-3) var(--s-2); - text-align: center; + padding: var(--s-1) var(--s-3); + background-color: var(--color-bg-higher); + border-radius: var(--radius-field); + + & svg { + stroke-width: 3; + } } .panelSectionLink:hover { color: var(--color-text); - text-decoration: underline; -} - -.dayHeader { - padding: var(--s-2) var(--s-2) var(--s-1); - font-size: var(--font-2xs); - font-weight: var(--weight-bold); - color: var(--color-text-high); - text-transform: uppercase; - letter-spacing: 0.05em; + background-color: var(--color-bg-high); } diff --git a/app/components/MobileNav.tsx b/app/components/MobileNav.tsx index edf34e7cd..370a951b5 100644 --- a/app/components/MobileNav.tsx +++ b/app/components/MobileNav.tsx @@ -1,7 +1,7 @@ import clsx from "clsx"; -import { isToday, isTomorrow } from "date-fns"; import { Calendar, + ChevronRight, LogIn, Menu, Settings, @@ -18,12 +18,14 @@ import { useUser } from "~/features/auth/core/user"; import { FriendMenu } from "~/features/friends/components/FriendMenu"; import type { RootLoaderData } from "~/root"; import { + EVENTS_PAGE, FRIENDS_PAGE, navIconUrl, SETTINGS_PAGE, userPage, } from "~/utils/urls"; import { Avatar } from "./Avatar"; +import { EventsList } from "./EventsList"; import { SendouButton } from "./elements/Button"; import { Image } from "./Image"; import { LogInButtonContainer } from "./layout/LogInButtonContainer"; @@ -34,7 +36,6 @@ import { import { navItems } from "./layout/nav-items"; import styles from "./MobileNav.module.css"; import { NotificationDot } from "./NotificationDot"; -import { ListLink } from "./SideNav"; import { StreamListItems } from "./StreamListItems"; type SidebarData = RootLoaderData["sidebar"] | undefined; @@ -302,6 +303,7 @@ function FriendsPanel({ onClick={onClose} > {t("common:actions.viewAll")} + ); @@ -314,91 +316,19 @@ function TourneysPanel({ events: NonNullable["events"]; onClose: () => void; }) { - const { t, i18n } = useTranslation(["front"]); - - const formatDayHeader = (date: Date) => { - if (isToday(date)) { - const rtf = new Intl.RelativeTimeFormat(i18n.language, { - numeric: "auto", - }); - const str = rtf.format(0, "day"); - return str.charAt(0).toUpperCase() + str.slice(1); - } - if (isTomorrow(date)) { - const rtf = new Intl.RelativeTimeFormat(i18n.language, { - numeric: "auto", - }); - const str = rtf.format(1, "day"); - return str.charAt(0).toUpperCase() + str.slice(1); - } - return date.toLocaleDateString(i18n.language, { - weekday: "long", - month: "short", - day: "numeric", - }); - }; - - const formatTime = (date: Date) => { - return date.toLocaleTimeString(i18n.language, { - hour: "numeric", - minute: "2-digit", - }); - }; - - const getDayKey = (timestamp: number) => { - const date = new Date(timestamp * 1000); - return date.toDateString(); - }; - - const groupedEvents = events.reduce>( - (acc, event) => { - const key = getDayKey(event.startTime); - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(event); - return acc; - }, - {}, - ); - - const dayKeys = Object.keys(groupedEvents); + const { t } = useTranslation(["front", "common"]); return ( - {events.length > 0 ? ( - dayKeys.map((dayKey) => { - const dayEvents = groupedEvents[dayKey]; - const firstDate = new Date(dayEvents[0].startTime * 1000); - - return ( -
-
- {formatDayHeader(firstDate)} -
- {dayEvents.map((event) => ( - - {event.scrimStatus === "booked" - ? t("front:sideNav.scrimVs", { opponent: event.name }) - : event.scrimStatus === "looking" - ? t("front:sideNav.lookingForScrim") - : event.name} - - ))} -
- ); - }) - ) : ( -
- {t("front:sideNav.noEvents")} -
- )} + + + {t("common:actions.viewAll")} + +
); } diff --git a/app/components/layout/NotificationPopover.module.css b/app/components/layout/NotificationPopover.module.css index 28693565f..7a5bb8c4b 100644 --- a/app/components/layout/NotificationPopover.module.css +++ b/app/components/layout/NotificationPopover.module.css @@ -44,3 +44,32 @@ .divider { border-color: var(--color-border); } + +.viewAllLink { + display: flex; + align-items: center; + gap: 2px; + width: fit-content; + margin: var(--s-2) auto; + font-size: var(--font-2xs); + color: var(--color-text-high); + text-decoration: none; + padding: var(--s-1) var(--s-3); + background-color: var(--color-bg-higher); + border-radius: var(--radius-field); + + & svg { + stroke-width: 3; + } +} + +@media screen and (max-width: 599px) { + .viewAllLink { + margin-top: var(--s-4); + } +} + +.viewAllLink:hover { + color: var(--color-text); + background-color: var(--color-bg-high); +} diff --git a/app/components/layout/NotificationPopover.tsx b/app/components/layout/NotificationPopover.tsx index f2468e51a..2d365f9b8 100644 --- a/app/components/layout/NotificationPopover.tsx +++ b/app/components/layout/NotificationPopover.tsx @@ -1,7 +1,7 @@ -import { Bell, RefreshCcw } from "lucide-react"; +import { Bell, ChevronRight, RefreshCcw } from "lucide-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; -import { useMatches, useRevalidator } from "react-router"; +import { Link, useMatches, useRevalidator } from "react-router"; import { NotificationItem, NotificationItemDivider, @@ -11,7 +11,7 @@ import { NOTIFICATIONS } from "~/features/notifications/notifications-contants"; import type { RootLoaderData } from "~/root"; import { NOTIFICATIONS_URL } from "~/utils/urls"; import { useMarkNotificationsAsSeen } from "../../features/notifications/notifications-hooks"; -import { LinkButton, SendouButton } from "../elements/Button"; +import { SendouButton } from "../elements/Button"; import styles from "./NotificationPopover.module.css"; @@ -95,15 +95,15 @@ function NotificationsFooter({ onClose }: { onClose?: () => void }) { return (

- - {t("common:notifications.seeAll")} - + {t("common:actions.viewAll")} + +
); } diff --git a/app/components/layout/index.tsx b/app/components/layout/index.tsx index f6a015968..cd949b7fe 100644 --- a/app/components/layout/index.tsx +++ b/app/components/layout/index.tsx @@ -25,7 +25,12 @@ import { useUser } from "~/features/auth/core/user"; import { FriendMenu } from "~/features/friends/components/FriendMenu"; import type { RootLoaderData } from "~/root"; import type { Breadcrumb, SendouRouteHandle } from "~/utils/remix.server"; -import { FRIENDS_PAGE, SETTINGS_PAGE, userPage } from "~/utils/urls"; +import { + EVENTS_PAGE, + FRIENDS_PAGE, + SETTINGS_PAGE, + userPage, +} from "~/utils/urls"; import { Avatar } from "../Avatar"; import { SendouButton } from "../elements/Button"; import { SendouPopover } from "../elements/Popover"; @@ -210,7 +215,17 @@ export function Layout({ const sideNavChildren = ( <> - }> + } + action={ + user ? ( + + {t("common:actions.viewAll")} + + + ) : null + } + > {t("front:sideNav.myCalendar")} {events.length > 0 ? ( diff --git a/app/features/calendar/loaders/events.server.ts b/app/features/calendar/loaders/events.server.ts new file mode 100644 index 000000000..a201e6108 --- /dev/null +++ b/app/features/calendar/loaders/events.server.ts @@ -0,0 +1,32 @@ +import { requireUser } from "~/features/auth/core/user.server"; +import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server"; +import * as ScrimPostRepository from "~/features/scrims/ScrimPostRepository.server"; +import { + scrimToSidebarEvent, + tournamentToSidebarEvent, +} from "~/features/sidebar/core/sidebar.server"; + +export type EventsLoaderData = typeof loader; + +export const loader = async () => { + const user = requireUser(); + + const [tournamentsData, scrimsData] = await Promise.all([ + ShowcaseTournaments.frontPageTournamentsByUserId(user.id), + ScrimPostRepository.findUserScrims(user.id), + ]); + + const registered = tournamentsData.participatingFor + .map(tournamentToSidebarEvent) + .sort((a, b) => a.startTime - b.startTime); + + const hosting = tournamentsData.organizingFor + .map(tournamentToSidebarEvent) + .sort((a, b) => a.startTime - b.startTime); + + const scrims = scrimsData + .map(scrimToSidebarEvent) + .sort((a, b) => a.startTime - b.startTime); + + return { registered, hosting, scrims }; +}; diff --git a/app/features/calendar/routes/events.module.css b/app/features/calendar/routes/events.module.css new file mode 100644 index 000000000..44bb705a9 --- /dev/null +++ b/app/features/calendar/routes/events.module.css @@ -0,0 +1,22 @@ +.eventsListHeader { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--s-2); + margin-block-end: var(--s-2); +} + +.filterRadio { + padding: var(--s-1) var(--s-2); + border-radius: var(--radius-field); + cursor: pointer; + color: var(--color-text-high); + font-weight: var(--weight-semi); + font-size: var(--font-3xs); +} + +.filterRadioSelected { + background-color: var(--color-bg-higher); + color: var(--color-text); +} diff --git a/app/features/calendar/routes/events.tsx b/app/features/calendar/routes/events.tsx new file mode 100644 index 000000000..b8a801022 --- /dev/null +++ b/app/features/calendar/routes/events.tsx @@ -0,0 +1,83 @@ +import clsx from "clsx"; +import { useState } from "react"; +import { Radio, RadioGroup } from "react-aria-components"; +import { useTranslation } from "react-i18next"; +import { Link, useLoaderData } from "react-router"; +import { EventsList } from "~/components/EventsList"; +import { Main } from "~/components/Main"; +import type { SendouRouteHandle } from "~/utils/remix.server"; +import { CALENDAR_PAGE } from "~/utils/urls"; +import type { EventsLoaderData } from "../loaders/events.server"; +import styles from "./events.module.css"; + +export { loader } from "../loaders/events.server"; + +export const handle: SendouRouteHandle = { + i18n: ["calendar"], +}; + +type ViewFilter = "registered" | "hosting" | "scrims"; + +export default function EventsPage() { + const { t } = useTranslation(["calendar"]); + const data = useLoaderData(); + const [filter, setFilter] = useState("registered"); + + const viewLabels: Record = { + registered: t("calendar:events.view.registered"), + hosting: t("calendar:events.view.hosting"), + scrims: t("calendar:events.view.scrims"), + }; + + const shownEvents = + filter === "registered" + ? data.registered + : filter === "hosting" + ? data.hosting + : data.scrims; + + const hasNoEventsAtAll = + data.registered.length === 0 && + data.hosting.length === 0 && + data.scrims.length === 0; + + return ( +
+
+

{t("calendar:events.title")}

+ {hasNoEventsAtAll ? null : ( + setFilter(v as ViewFilter)} + orientation="horizontal" + className="stack horizontal xs" + > + {(["registered", "hosting", "scrims"] as const).map((value) => ( + + {({ isSelected }) => ( + + {viewLabels[value]} + + )} + + ))} + + )} +
+ {hasNoEventsAtAll ? ( +

+ {t("calendar:events.emptyAll")}{" "} + {t("calendar:events.findOnCalendar")} +

+ ) : shownEvents.length === 0 ? ( +

{t("calendar:events.empty")}

+ ) : ( + + )} +
+ ); +} diff --git a/app/features/sidebar/core/sidebar.server.ts b/app/features/sidebar/core/sidebar.server.ts index 5076909a1..0192e1da5 100644 --- a/app/features/sidebar/core/sidebar.server.ts +++ b/app/features/sidebar/core/sidebar.server.ts @@ -12,6 +12,7 @@ import * as FriendRepository from "~/features/friends/FriendRepository.server"; import { resolveFriendActivity } from "~/features/friends/friends-utils.server"; import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server"; import * as LiveStreamRepository from "~/features/live-streams/LiveStreamRepository.server"; +import type { SidebarScrim } from "~/features/scrims/ScrimPostRepository.server"; import * as ScrimPostRepository from "~/features/scrims/ScrimPostRepository.server"; import { getSendouQSidebarStreams } from "~/features/sendouq-streams/core/streams.server"; import type { TournamentTierNumber } from "~/features/tournament/core/tiering"; @@ -81,27 +82,9 @@ export async function resolveSidebarData(userId: number | null) { seenTournamentIds.add(t.id); return true; }) - .map((t) => ({ - id: t.id, - name: t.name, - url: t.url, - logoUrl: t.logoUrl, - startTime: t.startTime, - type: "tournament" as const, - })); + .map(tournamentToSidebarEvent); - const scrimsIconUrl = `${navIconUrl("scrims")}.png`; - const scrimEvents: SidebarEvent[] = scrimsData.map((s) => ({ - id: s.id, - name: s.opponentName ?? "Scrim", - url: s.isAccepted - ? href("/scrims/:id", { id: String(s.id) }) - : href("/scrims"), - logoUrl: s.opponentAvatarUrl ?? scrimsIconUrl, - startTime: s.at, - type: "scrim" as const, - scrimStatus: s.isAccepted ? ("booked" as const) : ("looking" as const), - })); + const scrimEvents: SidebarEvent[] = scrimsData.map(scrimToSidebarEvent); const personalEvents = [...tournamentEvents, ...scrimEvents].sort( (a, b) => a.startTime - b.startTime, @@ -347,3 +330,32 @@ function rowToSidebarFriend( tournamentId: row.tournamentId, }; } + +export function tournamentToSidebarEvent( + t: ShowcaseCalendarEvent, +): SidebarEvent { + return { + id: t.id, + name: t.name, + url: t.url, + logoUrl: t.logoUrl, + startTime: t.startTime, + type: "tournament" as const, + }; +} + +const SCRIMS_ICON_URL = `${navIconUrl("scrims")}.png`; + +export function scrimToSidebarEvent(s: SidebarScrim): SidebarEvent { + return { + id: s.id, + name: s.opponentName ?? "Scrim", + url: s.isAccepted + ? href("/scrims/:id", { id: String(s.id) }) + : href("/scrims"), + logoUrl: s.opponentAvatarUrl ?? SCRIMS_ICON_URL, + startTime: s.at, + type: "scrim" as const, + scrimStatus: s.isAccepted ? ("booked" as const) : ("looking" as const), + }; +} diff --git a/app/routes.ts b/app/routes.ts index 13eadc395..344e559c9 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -44,6 +44,8 @@ export default [ route("/friends", "features/friends/routes/friends.tsx"), + route("/events", "features/calendar/routes/events.tsx"), + route("/suspended", "features/ban/routes/suspended.tsx"), route("/u", "features/user-search/routes/u.tsx"), diff --git a/app/utils/urls.ts b/app/utils/urls.ts index f4e451fc7..ebde668c7 100644 --- a/app/utils/urls.ts +++ b/app/utils/urls.ts @@ -120,6 +120,7 @@ export const SENDOUQ_STREAMS_PAGE = "/q/streams"; export const TIERS_PAGE = "/tiers"; export const SUSPENDED_PAGE = "/suspended"; export const LFG_PAGE = "/lfg"; +export const EVENTS_PAGE = "/events"; export const FRIENDS_PAGE = "/friends"; export const SETTINGS_PAGE = "/settings"; export const LUTI_PAGE = "/luti"; diff --git a/locales/da/calendar.json b/locales/da/calendar.json index a7baefcb6..4ee77f0a3 100644 --- a/locales/da/calendar.json +++ b/locales/da/calendar.json @@ -79,5 +79,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/de/calendar.json b/locales/de/calendar.json index 00f8f418c..5cee7f59f 100644 --- a/locales/de/calendar.json +++ b/locales/de/calendar.json @@ -79,5 +79,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/en/calendar.json b/locales/en/calendar.json index d6a15d3dd..79add1679 100644 --- a/locales/en/calendar.json +++ b/locales/en/calendar.json @@ -79,5 +79,12 @@ "filter.applyAndDefault": "Apply & make default", "forms.draft": "Draft", "forms.draftInfo": "Draft tournaments are hidden and only visible to organizers. The tournament must be opened (by disabling this toggle) before any bracket can be started.", - "forms.draftBracketStartBlocked": "Tournament is in draft mode. Edit the tournament and disable the draft toggle before starting the bracket." + "forms.draftBracketStartBlocked": "Tournament is in draft mode. Edit the tournament and disable the draft toggle before starting the bracket.", + "events.title": "My Events", + "events.view.registered": "Registered", + "events.view.hosting": "Hosting", + "events.view.scrims": "Scrims", + "events.empty": "No events in this category", + "events.emptyAll": "You have no upcoming events.", + "events.findOnCalendar": "Find an event to join on the calendar!" } diff --git a/locales/es-ES/calendar.json b/locales/es-ES/calendar.json index b005cae62..5d090bcac 100644 --- a/locales/es-ES/calendar.json +++ b/locales/es-ES/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/es-US/calendar.json b/locales/es-US/calendar.json index b005cae62..5d090bcac 100644 --- a/locales/es-US/calendar.json +++ b/locales/es-US/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/fr-CA/calendar.json b/locales/fr-CA/calendar.json index 4f84120c7..dc078ffb7 100644 --- a/locales/fr-CA/calendar.json +++ b/locales/fr-CA/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/fr-EU/calendar.json b/locales/fr-EU/calendar.json index 4f84120c7..dc078ffb7 100644 --- a/locales/fr-EU/calendar.json +++ b/locales/fr-EU/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/he/calendar.json b/locales/he/calendar.json index 2a16dd62e..f99a4eed0 100644 --- a/locales/he/calendar.json +++ b/locales/he/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "החל והפוך לברירת מחדל", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/it/calendar.json b/locales/it/calendar.json index 3dbf60a04..f6b586379 100644 --- a/locales/it/calendar.json +++ b/locales/it/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/ja/calendar.json b/locales/ja/calendar.json index a654dd3cf..ee4f47556 100644 --- a/locales/ja/calendar.json +++ b/locales/ja/calendar.json @@ -75,5 +75,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/ko/calendar.json b/locales/ko/calendar.json index f6c4d3aea..4949c5605 100644 --- a/locales/ko/calendar.json +++ b/locales/ko/calendar.json @@ -75,5 +75,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/nl/calendar.json b/locales/nl/calendar.json index 4d196d1c6..d93969574 100644 --- a/locales/nl/calendar.json +++ b/locales/nl/calendar.json @@ -79,5 +79,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/pl/calendar.json b/locales/pl/calendar.json index 40c933639..3783cab0c 100644 --- a/locales/pl/calendar.json +++ b/locales/pl/calendar.json @@ -83,5 +83,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/pt-BR/calendar.json b/locales/pt-BR/calendar.json index cfefc88a9..39d841705 100644 --- a/locales/pt-BR/calendar.json +++ b/locales/pt-BR/calendar.json @@ -81,5 +81,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/ru/calendar.json b/locales/ru/calendar.json index b882eae0f..be936a053 100644 --- a/locales/ru/calendar.json +++ b/locales/ru/calendar.json @@ -83,5 +83,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" } diff --git a/locales/zh/calendar.json b/locales/zh/calendar.json index 639d367cf..1b8170d23 100644 --- a/locales/zh/calendar.json +++ b/locales/zh/calendar.json @@ -75,5 +75,12 @@ "filter.applyAndDefault": "", "forms.draft": "", "forms.draftInfo": "", - "forms.draftBracketStartBlocked": "" + "forms.draftBracketStartBlocked": "", + "events.title": "", + "events.view.registered": "", + "events.view.hosting": "", + "events.view.scrims": "", + "events.empty": "", + "events.emptyAll": "", + "events.findOnCalendar": "" }