diff --git a/polylan_submitter/games/admin.py b/polylan_submitter/games/admin.py index 3eb9d65..65510c2 100644 --- a/polylan_submitter/games/admin.py +++ b/polylan_submitter/games/admin.py @@ -7,7 +7,7 @@ from django_object_actions import ( from submissions.tasks import schedule_scope_sync, stop_scope_sync -from .models import EventGame, Game +from .models import EventGame, Game, PlanningSlot from .tasks import refresh_steam_games_task @@ -90,3 +90,10 @@ class EventGameAdmin(DjangoObjectActions, admin.ModelAdmin): stopped = sum(stop_scope_sync(event_game.id) for event_game in queryset) self.message_user(request, f"Stopped scope sync for {stopped} EventGame(s).") + + +@admin.register(PlanningSlot) +class PlanningSlotAdmin(admin.ModelAdmin): + list_display = ["title", "event", "begin", "end", "column", "is_multi_anims"] + list_filter = ["event", "is_multi_anims"] + search_fields = ["title"] diff --git a/polylan_submitter/games/api.py b/polylan_submitter/games/api.py index c49efd8..e4f1479 100644 --- a/polylan_submitter/games/api.py +++ b/polylan_submitter/games/api.py @@ -3,7 +3,8 @@ from typing import List from ninja import Router from .models import Game -from .schemas import GameOut, SteamGameOut +from .schemas import GameOut, PlanningSlotOut, SteamGameOut +from .services.planning import planning_with_games from .tasks import schedule_steam_refresh router = Router() @@ -28,3 +29,9 @@ def list_event_steam_games(request, event_path: str): return Game.objects.enabled_for_event(request.event).filter( steam_app_id__isnull=False ) + + +@event_router.get("/events/{event_path}/planning", response=List[PlanningSlotOut]) +def list_event_planning(request, event_path: str): + """The event planning, with the covers of the games we know.""" + return planning_with_games(request.event.planning_slots.all()) diff --git a/polylan_submitter/games/management/commands/import_planning.py b/polylan_submitter/games/management/commands/import_planning.py new file mode 100644 index 0000000..1e5af5d --- /dev/null +++ b/polylan_submitter/games/management/commands/import_planning.py @@ -0,0 +1,41 @@ +import json +import sys + +from django.core.management.base import BaseCommand, CommandError +from django.db import transaction + +from events.models import Event +from games.services.planning import import_planning + + +class Command(BaseCommand): + help = ( + "Import an event planning from the legacy PolyLAN planning JSON " + "(slots missing from the file are deleted)" + ) + + def add_arguments(self, parser): + parser.add_argument("event_path", help="Path of the event, e.g. polylan-43") + parser.add_argument("json_file", help="Planning JSON file, '-' for stdin") + + def handle(self, *args, **options): + try: + event = Event.objects.get(path=options["event_path"]) + except Event.DoesNotExist: + raise CommandError(f"No event with path {options['event_path']!r}") + + if options["json_file"] == "-": + data = json.load(sys.stdin) + else: + with open(options["json_file"], encoding="utf-8") as f: + data = json.load(f) + + entries = data["list"] if isinstance(data, dict) else data + with transaction.atomic(): + created, updated, deleted = import_planning(event, entries) + + self.stdout.write( + self.style.SUCCESS( + f"Planning of {event}: {created} created, {updated} updated, {deleted} deleted" + ) + ) diff --git a/polylan_submitter/games/migrations/0012_planningslot.py b/polylan_submitter/games/migrations/0012_planningslot.py new file mode 100644 index 0000000..c923fd1 --- /dev/null +++ b/polylan_submitter/games/migrations/0012_planningslot.py @@ -0,0 +1,53 @@ +# Generated by Django 6.0.5 on 2026-10-16 03:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0003_event_is_available"), + ("games", "0011_game_library_image"), + ] + + operations = [ + migrations.CreateModel( + name="PlanningSlot", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("source_pk", models.PositiveIntegerField()), + ("title", models.CharField(max_length=255)), + ("begin", models.DateTimeField()), + ("end", models.DateTimeField()), + ("column", models.PositiveSmallIntegerField(default=0)), + ("color", models.CharField(blank=True, default="", max_length=7)), + ("is_multi_anims", models.BooleanField(default=False)), + ("game_names", models.JSONField(blank=True, default=list)), + ( + "event", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="planning_slots", + to="events.event", + ), + ), + ], + options={ + "ordering": ["begin", "column"], + "constraints": [ + models.UniqueConstraint( + fields=("event", "source_pk"), name="unique_event_planning_slot" + ) + ], + }, + ), + ] diff --git a/polylan_submitter/games/models.py b/polylan_submitter/games/models.py index 7ef1d7b..265eb94 100644 --- a/polylan_submitter/games/models.py +++ b/polylan_submitter/games/models.py @@ -42,6 +42,19 @@ class Game(models.Model): return f"{self.name} ({self.steam_app_id})" return self.name + @property + def store_url(self) -> str: + if not self.steam_app_id: + return "" + return f"https://store.steampowered.com/app/{self.steam_app_id}" + + @property + def library_image_url(self) -> str: + """Vertical cover, like in the Steam library. Empty if there's none.""" + if self.library_image or not self.steam_app_id: + return self.library_image + return f"https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/{self.steam_app_id}/library_600x900.jpg" + class EventGame(models.Model): event = models.ForeignKey( @@ -76,3 +89,37 @@ class EventGame(models.Model): def __str__(self) -> str: return f"{self.game.name} @ {self.event.name} ({'enabled' if self.enabled else 'disabled'})" + + +class PlanningSlot(models.Model): + """One slot of the event planning, imported from the legacy PolyLAN planning + (see the import_planning command).""" + + event = models.ForeignKey( + Event, + on_delete=models.CASCADE, + related_name="planning_slots", + ) + # pk of the slot in the legacy planning, to update it on re-import + source_pk = models.PositiveIntegerField() + title = models.CharField(max_length=255) + begin = models.DateTimeField() + end = models.DateTimeField() + column = models.PositiveSmallIntegerField(default=0) + color = models.CharField(max_length=7, blank=True, default="") + # Multi-anims (e.g. "Fils rouges") run several games over the whole event + is_multi_anims = models.BooleanField(default=False) + # Names of the games run in this slot (the legacy "aandts") + game_names = models.JSONField(default=list, blank=True) + + class Meta: + ordering = ["begin", "column"] + constraints = [ + models.UniqueConstraint( + fields=["event", "source_pk"], + name="unique_event_planning_slot", + ), + ] + + def __str__(self) -> str: + return f"{self.title} @ {self.event.name} ({self.begin:%a %H:%M})" diff --git a/polylan_submitter/games/schemas.py b/polylan_submitter/games/schemas.py index e0a25ee..8364ca7 100644 --- a/polylan_submitter/games/schemas.py +++ b/polylan_submitter/games/schemas.py @@ -36,7 +36,7 @@ class SteamGameOut(Schema): @staticmethod def resolve_store_url(obj) -> str: - return f"https://store.steampowered.com/app/{obj.steam_app_id}" + return obj.store_url @staticmethod def resolve_header_image(obj) -> str: @@ -47,11 +47,7 @@ class SteamGameOut(Schema): @staticmethod def resolve_library_image(obj) -> str: - # Vertical capsule, like in the Steam library - return ( - obj.library_image - or f"https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/{obj.steam_app_id}/library_600x900.jpg" - ) + return obj.library_image_url @staticmethod def resolve_short_description(obj) -> str: @@ -80,3 +76,23 @@ class SteamGameOut(Schema): @staticmethod def resolve_updated_at(obj) -> Optional[datetime]: return obj.steam_info_updated_at + + +class PlanningGameOut(Schema): + name: str + # Empty when the name didn't match any of our games + library_image: str = "" + store_url: str = "" + steam_app_id: Optional[int] = None + + +class PlanningSlotOut(Schema): + id: int + title: str + begin: datetime + end: datetime + column: int + color: str + is_multi_anims: bool + # Single slot: the slot's game. Multi-anims: each of their games. + games: list[PlanningGameOut] diff --git a/polylan_submitter/games/services/planning.py b/polylan_submitter/games/services/planning.py new file mode 100644 index 0000000..2fbe83a --- /dev/null +++ b/polylan_submitter/games/services/planning.py @@ -0,0 +1,88 @@ +import re +import unicodedata + +from ..models import Game, PlanningSlot + + +def normalize_name(name: str) -> str: + """'Towerfall: Ascension' and 'TowerFall Ascension' are the same game: + no accents, no case, letters and digits only.""" + name = unicodedata.normalize("NFKD", name) + name = "".join(c for c in name if not unicodedata.combining(c)) + return re.sub(r"[^0-9a-z]", "", name.casefold()) + + +def _game_out(name: str, game: Game | None) -> dict: + if game is None: + return {"name": name} + return { + "name": game.name, + "library_image": game.library_image_url, + "store_url": game.store_url, + "steam_app_id": game.steam_app_id, + } + + +def planning_with_games(slots) -> list[dict]: + """Slots as PlanningSlotOut dicts, with their games matched by name to ours.""" + games_by_name = {normalize_name(game.name): game for game in Game.objects.all()} + + def match(name: str) -> Game | None: + return games_by_name.get(normalize_name(name)) + + out = [] + for slot in slots: + if slot.is_multi_anims: + games = [_game_out(name, match(name)) for name in slot.game_names] + else: + # The title is the usual match, the legacy game names are a fallback + # ("Golf it!" slot running the "Golf It!" game) + game = next( + filter(None, map(match, [slot.title, *slot.game_names])), None + ) + games = [_game_out(slot.title, game)] + + out.append( + { + "id": slot.id, + "title": slot.title, + "begin": slot.begin, + "end": slot.end, + "column": slot.column, + "color": slot.color, + "is_multi_anims": slot.is_multi_anims, + "games": games, + } + ) + return out + + +def import_planning(event, entries: list[dict]) -> tuple[int, int, int]: + """Sync the event planning with the legacy planning entries (its "list"). + Returns (created, updated, deleted).""" + created = updated = 0 + for entry in entries: + _, was_created = PlanningSlot.objects.update_or_create( + event=event, + source_pk=entry["pk"], + defaults={ + "title": entry["title"], + "begin": entry["begin"], + "end": entry["end"], + "column": entry.get("colonneS") or 0, + "color": entry.get("color_code") or "", + "is_multi_anims": bool(entry.get("is_multi_anims")), + "game_names": [a["name"] for a in entry.get("aandts") or []], + }, + ) + if was_created: + created += 1 + else: + updated += 1 + + deleted, _ = ( + PlanningSlot.objects.filter(event=event) + .exclude(source_pk__in=[entry["pk"] for entry in entries]) + .delete() + ) + return created, updated, deleted diff --git a/polylan_submitter/polylan_submitter/urls.py b/polylan_submitter/polylan_submitter/urls.py index fc8e468..f700e17 100644 --- a/polylan_submitter/polylan_submitter/urls.py +++ b/polylan_submitter/polylan_submitter/urls.py @@ -56,6 +56,12 @@ def games_home(request: HttpRequest, event_path: str): return render(request, "app.html") +@login_required +def planning_home(request: HttpRequest, event_path: str): + get_object_or_404(Event, path=event_path) + return render(request, "app.html") + + @login_required @require_game_enabled(OPUS_MAGNUM_APP_ID) def opus_magnum_home(request: HttpRequest, event_path: str): @@ -102,6 +108,7 @@ urlpatterns = [ path("cas/logout/", SimpleCASLogoutView.as_view(), name="cas_ng_logout"), path("api/", api.urls), path("/games", games_home, name="games.home"), + path("/planning", planning_home, name="planning.home"), path("/market", market_home, name="market.home"), path("/opus-magnum", opus_magnum_home, name="opus-magnum.home"), path("/noita", noita_home, name="noita.home"), diff --git a/polylan_submitter/src/Home.vue b/polylan_submitter/src/Home.vue index 55dff42..dbe98fb 100644 --- a/polylan_submitter/src/Home.vue +++ b/polylan_submitter/src/Home.vue @@ -4,7 +4,7 @@ import { useRouter } from "vue-router"; import { useI18n } from "vue-i18n"; import { useTitle } from "@vueuse/core"; import { useQuery } from "@tanstack/vue-query"; -import { eventGamesQuery } from "@/queries"; +import { eventGamesQuery, eventPlanningQuery } from "@/queries"; import type { GamesApiListEventGamesResponse } from "./api/types.gen"; interface Props { @@ -19,6 +19,8 @@ useTitle(computed(() => t("home.title"))); const { data: games, isPending: loading } = useQuery(eventGamesQuery(props.eventPath)); const hasSteamGames = computed(() => games.value?.some((game) => game.steam_app_id) ?? false); +const { data: planning } = useQuery(eventPlanningQuery(props.eventPath)); +const hasPlanning = computed(() => !!planning.value?.length); const imageErrors = ref>(new Set()); const getHeaderImage = (game: GamesApiListEventGamesResponse[number]) => { @@ -86,9 +88,13 @@ const goToGame = (game: GamesApiListEventGamesResponse[number]) => { - -
- + +
+ + + {{ t("home.planning") }} + + {{ t("home.getTheGames") }} diff --git a/polylan_submitter/src/Planning.vue b/polylan_submitter/src/Planning.vue new file mode 100644 index 0000000..a8f5504 --- /dev/null +++ b/polylan_submitter/src/Planning.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/polylan_submitter/src/api/@tanstack/vue-query.gen.ts b/polylan_submitter/src/api/@tanstack/vue-query.gen.ts index 41c950a..990bf3d 100644 --- a/polylan_submitter/src/api/@tanstack/vue-query.gen.ts +++ b/polylan_submitter/src/api/@tanstack/vue-query.gen.ts @@ -3,8 +3,8 @@ import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/vue-query'; import { client } from '../client.gen'; -import { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from '../sdk.gen'; -import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiMyStatsData, ClockApiMyStatsResponse, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListGamesData, GamesApiListGamesResponse, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiCloseMarketData, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiResolveMarketData, MarketApiResolveMarketResponse, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileResponse, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetStatsData, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionResponse, PlaceApiCanvasData, PlaceApiEvolutionGifData, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionResponse, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropResponse, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionResponse, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrResponse, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesResponse, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusResponse } from '../types.gen'; +import { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventPlanning, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from '../sdk.gen'; +import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiMyStatsData, ClockApiMyStatsResponse, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventPlanningData, GamesApiListEventPlanningResponse, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListGamesData, GamesApiListGamesResponse, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiCloseMarketData, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiResolveMarketData, MarketApiResolveMarketResponse, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileResponse, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetStatsData, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionResponse, PlaceApiCanvasData, PlaceApiEvolutionGifData, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionResponse, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropResponse, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionResponse, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrResponse, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesResponse, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusResponse } from '../types.gen'; export type QueryKey = [ Pick & { @@ -551,6 +551,26 @@ export const gamesApiListEventSteamGamesOptions = (options: Options) => createQueryKey('gamesApiListEventPlanning', options); + +/** + * List Event Planning + * + * The event planning, with the covers of the games we know. + */ +export const gamesApiListEventPlanningOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await gamesApiListEventPlanning({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: gamesApiListEventPlanningQueryKey(options) +}); + export const submissionsApiListMyScopesQueryKey = (options: Options) => createQueryKey('submissionsApiListMyScopes', options); /** diff --git a/polylan_submitter/src/api/index.ts b/polylan_submitter/src/api/index.ts index d862635..3b9552a 100644 --- a/polylan_submitter/src/api/index.ts +++ b/polylan_submitter/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from './sdk.gen'; -export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, BetHistoryPointSchema, ClientOptions, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponse, ClockApiMyStatsResponses, ClockStateOut, GameOut, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventGamesResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, ImageCropIn, ImageCropOut, Input, LeaderboardEntryOut, LeaderboardOut, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponse, MarketApiResolveMarketResponses, MarketListSchema, MarketOptionSchema, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PlayerStatsOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResolveMarketSchema, ResultsOut, ScopeOut, SteamCollectionItemOut, SteamCollectionOut, SteamGameOut, SubmissionFileOut, SubmissionIn, SubmissionOut, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponse, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponse, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponse, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponse, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponse, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponse, SubmissionsApiSyncScopesStatusResponses, SyncScopesIn, SyncScopesOut, SyncScopesStatusOut, SyncScopesStopOut, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserBetCreateSchema, UserBetSchema, UserDisplayOut, UserInfoOut, UserPointsSchema, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; +export { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventPlanning, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from './sdk.gen'; +export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, BetHistoryPointSchema, ClientOptions, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponse, ClockApiMyStatsResponses, ClockStateOut, GameOut, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventGamesResponses, GamesApiListEventPlanningData, GamesApiListEventPlanningResponse, GamesApiListEventPlanningResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, ImageCropIn, ImageCropOut, Input, LeaderboardEntryOut, LeaderboardOut, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponse, MarketApiResolveMarketResponses, MarketListSchema, MarketOptionSchema, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PlanningGameOut, PlanningSlotOut, PlayerStatsOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResolveMarketSchema, ResultsOut, ScopeOut, SteamCollectionItemOut, SteamCollectionOut, SteamGameOut, SubmissionFileOut, SubmissionIn, SubmissionOut, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponse, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponse, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponse, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponse, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponse, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponse, SubmissionsApiSyncScopesStatusResponses, SyncScopesIn, SyncScopesOut, SyncScopesStatusOut, SyncScopesStopOut, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserBetCreateSchema, UserBetSchema, UserDisplayOut, UserInfoOut, UserPointsSchema, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; diff --git a/polylan_submitter/src/api/sdk.gen.ts b/polylan_submitter/src/api/sdk.gen.ts index 6ff9b47..bd6ca18 100644 --- a/polylan_submitter/src/api/sdk.gen.ts +++ b/polylan_submitter/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client'; import { client } from './client.gen'; -import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, ClockApiCurrentStateData, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponses, GamesApiListEventGamesData, GamesApiListEventGamesResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponses, MarketApiBetHistoryData, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponses, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponses } from './types.gen'; +import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, ClockApiCurrentStateData, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponses, GamesApiListEventGamesData, GamesApiListEventGamesResponses, GamesApiListEventPlanningData, GamesApiListEventPlanningResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponses, MarketApiBetHistoryData, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponses, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponses } from './types.gen'; export type Options = Options2 & { /** @@ -203,6 +203,13 @@ export const gamesApiListEventGames = (opt */ export const gamesApiListEventSteamGames = (options: Options) => (options.client ?? client).get({ url: '/api/events/{event_path}/games/steam', ...options }); +/** + * List Event Planning + * + * The event planning, with the covers of the games we know. + */ +export const gamesApiListEventPlanning = (options: Options) => (options.client ?? client).get({ url: '/api/events/{event_path}/planning', ...options }); + /** * List My Scopes */ diff --git a/polylan_submitter/src/api/types.gen.ts b/polylan_submitter/src/api/types.gen.ts index f258123..6bae7ea 100644 --- a/polylan_submitter/src/api/types.gen.ts +++ b/polylan_submitter/src/api/types.gen.ts @@ -986,6 +986,66 @@ export type SteamGameOut = { updated_at?: string | null; }; +/** + * PlanningGameOut + */ +export type PlanningGameOut = { + /** + * Name + */ + name: string; + /** + * Library Image + */ + library_image?: string; + /** + * Store Url + */ + store_url?: string; + /** + * Steam App Id + */ + steam_app_id?: number | null; +}; + +/** + * PlanningSlotOut + */ +export type PlanningSlotOut = { + /** + * Id + */ + id: number; + /** + * Title + */ + title: string; + /** + * Begin + */ + begin: string; + /** + * End + */ + end: string; + /** + * Column + */ + column: number; + /** + * Color + */ + color: string; + /** + * Is Multi Anims + */ + is_multi_anims: boolean; + /** + * Games + */ + games: Array; +}; + /** * ScopeOut */ @@ -1997,6 +2057,29 @@ export type GamesApiListEventSteamGamesResponses = { export type GamesApiListEventSteamGamesResponse = GamesApiListEventSteamGamesResponses[keyof GamesApiListEventSteamGamesResponses]; +export type GamesApiListEventPlanningData = { + body?: never; + path: { + /** + * Event Path + */ + event_path: string; + }; + query?: never; + url: '/api/events/{event_path}/planning'; +}; + +export type GamesApiListEventPlanningResponses = { + /** + * Response + * + * OK + */ + 200: Array; +}; + +export type GamesApiListEventPlanningResponse = GamesApiListEventPlanningResponses[keyof GamesApiListEventPlanningResponses]; + export type SubmissionsApiListMyScopesData = { body?: never; path: { diff --git a/polylan_submitter/src/components/PlanningCover.vue b/polylan_submitter/src/components/PlanningCover.vue new file mode 100644 index 0000000..7a25746 --- /dev/null +++ b/polylan_submitter/src/components/PlanningCover.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/polylan_submitter/src/locales/en.json b/polylan_submitter/src/locales/en.json index 903982d..632c4be 100644 --- a/polylan_submitter/src/locales/en.json +++ b/polylan_submitter/src/locales/en.json @@ -66,6 +66,7 @@ "subtitle": "Choose a game and submit your best solutions", "submitResults": "Submit results", "getTheGames": "Get the games", + "planning": "Planning", "footer": "Select a game above to begin submitting" }, "games": { @@ -79,6 +80,11 @@ "play": "Play", "showPrices": "Show prices" }, + "planning": { + "header": "Planning", + "title": "Planning", + "empty": "No planning for this event yet" + }, "clock": { "pageTitle": "Beat the Clock", "header": "Beat the Clock", diff --git a/polylan_submitter/src/queries.ts b/polylan_submitter/src/queries.ts index 874036c..487c16e 100644 --- a/polylan_submitter/src/queries.ts +++ b/polylan_submitter/src/queries.ts @@ -3,6 +3,7 @@ import { persistQueryClient } from "@tanstack/query-persist-client-core"; import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; import { gamesApiListEventGamesOptions, + gamesApiListEventPlanningOptions, polylanSubmitterApiGetUserInfoOptions, polylanSubmitterApiGetUserInfoQueryKey, } from "@/api/@tanstack/vue-query.gen"; @@ -25,6 +26,12 @@ export const eventGamesQuery = (eventPath: string) => ({ staleTime: 5 * 60 * 1000, }); +// Imported by staff a handful of times per event. +export const eventPlanningQuery = (eventPath: string) => ({ + ...gamesApiListEventPlanningOptions({ path: { event_path: eventPath } }), + staleTime: 5 * 60 * 1000, +}); + const PERSISTED_QUERIES = new Set(["polylanSubmitterApiGetUserInfo", "gamesApiListEventGames"]); // Restores user + games from localStorage before the app mounts, so pages diff --git a/polylan_submitter/src/router/index.ts b/polylan_submitter/src/router/index.ts index 4ada90b..747f7f4 100644 --- a/polylan_submitter/src/router/index.ts +++ b/polylan_submitter/src/router/index.ts @@ -5,6 +5,7 @@ import Home from "@/Home.vue"; import Market from "@/Market.vue"; import Noita from "@/Noita.vue"; import OpusMagnum from "@/OpusMagnum.vue"; +import Planning from "@/Planning.vue"; import Place from "@/Place.vue"; import PlaceEmbed from "@/PlaceEmbed.vue"; import Submissions from "@/Submissions.vue"; @@ -45,6 +46,13 @@ export const router = createRouter({ props: true, meta: { titleKey: "games.header", getBackRoute: eventHome }, }, + { + path: "/:eventPath/planning", + name: "planning", + component: Planning, + props: true, + meta: { titleKey: "planning.header", getBackRoute: eventHome }, + }, { path: "/:eventPath/market", name: "market", diff --git a/polylan_submitter/test/data.js b/polylan_submitter/test/data.js new file mode 100644 index 0000000..d0d9df7 --- /dev/null +++ b/polylan_submitter/test/data.js @@ -0,0 +1 @@ +window.PLANNING = {"list": [{"title": "Pokémon Showdown", "is_multi_anims": false, "begin": "2026-10-25T09:00:00+00:00", "end": "2026-10-25T11:00:00+00:00", "colonneS": 1, "color_code": "#9058f9", "aandts": [], "pk": 2113}, {"title": "Factorio", "is_multi_anims": false, "begin": "2026-10-24T21:30:00+00:00", "end": "2026-10-24T23:30:00+00:00", "colonneS": 1, "color_code": "#dc8add", "aandts": [], "pk": 2152}, {"title": "Échecs", "is_multi_anims": false, "begin": "2026-10-24T16:00:00+00:00", "end": "2026-10-24T18:00:00+00:00", "colonneS": 4, "color_code": "#38ffde", "aandts": [], "pk": 2139}, {"title": "Super Battle Golf", "is_multi_anims": false, "begin": "2026-10-24T16:00:00+00:00", "end": "2026-10-24T18:00:00+00:00", "colonneS": 0, "color_code": "#bd00ff", "aandts": [], "pk": 2146}, {"title": "Sanic Ball", "is_multi_anims": false, "begin": "2026-10-24T00:00:00+00:00", "end": "2026-10-24T02:00:00+00:00", "colonneS": 0, "color_code": "#9058f9", "aandts": [], "pk": 2114}, {"title": "Straftat", "is_multi_anims": false, "begin": "2026-10-25T12:00:00+00:00", "end": "2026-10-25T14:00:00+00:00", "colonneS": 1, "color_code": "#9058f9", "aandts": [], "pk": 2147}, {"title": "Blobby Volley 2", "is_multi_anims": false, "begin": "2026-10-25T00:00:00+00:00", "end": "2026-10-25T02:00:00+00:00", "colonneS": 1, "color_code": "#58f995", "aandts": [], "pk": 2115}, {"title": "Fall Guys", "is_multi_anims": false, "begin": "2026-10-23T19:00:00+00:00", "end": "2026-10-23T21:00:00+00:00", "colonneS": 0, "color_code": "#ff3739", "aandts": [{"pk": 2133, "name": "Fall Guys"}], "pk": 2118}, {"title": "Lethal Company", "is_multi_anims": false, "begin": "2026-10-24T00:00:00+00:00", "end": "2026-10-24T02:00:00+00:00", "colonneS": 1, "color_code": "#9058f9", "aandts": [], "pk": 2148}, {"title": "Puyo Puyo — Tetris", "is_multi_anims": false, "begin": "2026-10-24T11:00:00+00:00", "end": "2026-10-24T13:00:00+00:00", "colonneS": 3, "color_code": "#ff3739", "aandts": [], "pk": 2141}, {"title": "Crochet", "is_multi_anims": false, "begin": "2026-10-24T08:00:00+00:00", "end": "2026-10-24T13:00:00+00:00", "colonneS": 4, "color_code": "#ff3739", "aandts": [], "pk": 2151}, {"title": "BAP BAP", "is_multi_anims": false, "begin": "2026-10-24T08:00:00+00:00", "end": "2026-10-24T10:00:00+00:00", "colonneS": 0, "color_code": "#9058f9", "aandts": [], "pk": 2117}, {"title": "Trackmania", "is_multi_anims": false, "begin": "2026-10-24T13:30:00+00:00", "end": "2026-10-24T15:30:00+00:00", "colonneS": 0, "color_code": "#ff7800", "aandts": [{"pk": 2129, "name": "Trackmania"}], "pk": 2123}, {"title": "Mario Kart 8 Deluxe", "is_multi_anims": false, "begin": "2026-10-25T09:00:00+00:00", "end": "2026-10-25T11:00:00+00:00", "colonneS": 3, "color_code": "#ff3739", "aandts": [], "pk": 2142}, {"title": "Warsow", "is_multi_anims": false, "begin": "2026-10-23T19:00:00+00:00", "end": "2026-10-23T21:00:00+00:00", "colonneS": 1, "color_code": "#9058f9", "aandts": [], "pk": 2149}, {"title": "Rocket League", "is_multi_anims": false, "begin": "2026-10-25T09:00:00+00:00", "end": "2026-10-25T14:00:00+00:00", "colonneS": 0, "color_code": "#58f995", "aandts": [], "pk": 2119}, {"title": "Fils rouges", "is_multi_anims": true, "begin": "2026-10-23T19:00:00+00:00", "end": "2026-10-25T15:00:00+00:00", "colonneS": 5, "color_code": "#ff3739", "aandts": [{"pk": 2119, "name": "Slay the Spire 2"}, {"pk": 2120, "name": "Peak"}, {"pk": 2131, "name": "r/place"}], "pk": 2143}, {"title": "MicroWorks", "is_multi_anims": false, "begin": "2026-10-24T11:00:00+00:00", "end": "2026-10-24T13:00:00+00:00", "colonneS": 0, "color_code": "#9058f9", "aandts": [], "pk": 2145}, {"title": "Tricky Towers", "is_multi_anims": false, "begin": "2026-10-23T16:00:00+00:00", "end": "2026-10-23T18:00:00+00:00", "colonneS": 1, "color_code": "#bd00ff", "aandts": [], "pk": 2150}, {"title": "League of Legends (5v5 / Arena / ARAM)", "is_multi_anims": false, "begin": "2026-10-24T13:30:00+00:00", "end": "2026-10-24T18:00:00+00:00", "colonneS": 1, "color_code": "#58f995", "aandts": [], "pk": 2120}, {"title": "R.E.P.O.", "is_multi_anims": false, "begin": "2026-10-25T00:00:00+00:00", "end": "2026-10-25T02:00:00+00:00", "colonneS": 0, "color_code": "#9058f9", "aandts": [], "pk": 2144}, {"title": "Geoguessr", "is_multi_anims": false, "begin": "2026-10-24T19:00:00+00:00", "end": "2026-10-24T21:00:00+00:00", "colonneS": 0, "color_code": "#ff7800", "aandts": [{"pk": 2130, "name": "Geoguessr"}], "pk": 2124}, {"title": "Meccha Chameleon", "is_multi_anims": false, "begin": "2026-10-24T19:00:00+00:00", "end": "2026-10-24T21:00:00+00:00", "colonneS": 1, "color_code": "#ff3739", "aandts": [], "pk": 2125}, {"title": "ROUNDS", "is_multi_anims": false, "begin": "2026-10-23T21:30:00+00:00", "end": "2026-10-23T23:30:00+00:00", "colonneS": 3, "color_code": "#dc8add", "aandts": [], "pk": 2134}, {"title": "You Suck At Parking", "is_multi_anims": false, "begin": "2026-10-23T16:00:00+00:00", "end": "2026-10-23T18:00:00+00:00", "colonneS": 0, "color_code": "#9058f9", "aandts": [], "pk": 2126}, {"title": "Teamfight Tactics", "is_multi_anims": false, "begin": "2026-10-24T08:00:00+00:00", "end": "2026-10-24T13:00:00+00:00", "colonneS": 1, "color_code": "#d7ff0f", "aandts": [], "pk": 2127}, {"title": "Jass", "is_multi_anims": false, "begin": "2026-10-25T09:00:00+00:00", "end": "2026-10-25T11:00:00+00:00", "colonneS": 4, "color_code": "#d7ff0f", "aandts": [], "pk": 2128}, {"title": "Golf it!", "is_multi_anims": false, "begin": "2026-10-24T21:30:00+00:00", "end": "2026-10-24T23:30:00+00:00", "colonneS": 0, "color_code": "#ff7800", "aandts": [{"pk": 2134, "name": "Golf It!"}], "pk": 2132}, {"title": "Loups-Garous", "is_multi_anims": false, "begin": "2026-10-23T19:00:00+00:00", "end": "2026-10-23T21:00:00+00:00", "colonneS": 4, "color_code": "#ff3739", "aandts": [], "pk": 2133}, {"title": "UNRAILED 2", "is_multi_anims": false, "begin": "2026-10-24T21:30:00+00:00", "end": "2026-10-24T23:30:00+00:00", "colonneS": 3, "color_code": "#dc8add", "aandts": [], "pk": 2135}, {"title": "Skull King", "is_multi_anims": false, "begin": "2026-10-25T12:00:00+00:00", "end": "2026-10-25T14:00:00+00:00", "colonneS": 4, "color_code": "#62a0ea", "aandts": [], "pk": 2130}, {"title": "Skribbl", "is_multi_anims": false, "begin": "2026-10-23T21:30:00+00:00", "end": "2026-10-23T23:30:00+00:00", "colonneS": 0, "color_code": "#58f995", "aandts": [], "pk": 2131}, {"title": "Towerfall: Ascension", "is_multi_anims": false, "begin": "2026-10-23T19:00:00+00:00", "end": "2026-10-23T21:00:00+00:00", "colonneS": 3, "color_code": "#d7ff0f", "aandts": [], "pk": 2137}, {"title": "Cosmoteer", "is_multi_anims": false, "begin": "2026-10-24T08:00:00+00:00", "end": "2026-10-24T10:00:00+00:00", "colonneS": 3, "color_code": "#ff7800", "aandts": [{"pk": 2128, "name": "Cosmoteer"}], "pk": 2138}]}; diff --git a/polylan_submitter/test/index.html b/polylan_submitter/test/index.html new file mode 100644 index 0000000..ee372e8 --- /dev/null +++ b/polylan_submitter/test/index.html @@ -0,0 +1,168 @@ + + + + + +Games Planning Preview + + + +
+

Get the games

+

Don't own them yet? Grab them on Steam

+
Show prices
+
+
+ + + + +