0de85cbc51
Build and push image / build (push) Failing after 15m20s
Archive and answer pages now exist in all six languages and are prerendered instead of rendered per request, and each answer page carries a sentence of real context per player (club, league, nationality, former clubs, honours) plus Article and BreadcrumbList JSON-LD. The sitemap grows from 121 to 691 URLs, every one with hreflang alternates. Adds an optional display name to shared results, so a challenge link reads "<name> challenges you" in the page, the OG image and the head-to-head verdict; the name is sanitised both when written and when parsed back. Adds an opt-in daily reminder (Web Push without payload, so no content encryption) and a PWA install prompt shown once the day is finished, plus first-party event ingestion at /api/events that mirrors GA4 so the funnel survives ad blockers. Wires the missing N8N_PLAYS_WEBHOOK_URL into the deployment along with the new events, push and search-verification secrets, and ships the n8n workflows, the Postgres schema with funnel/retention views, and an IndexNow submitter. Refs #2 #3 #5 #6 #7 #8 #9 #10 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQ1ErZNRUcWgCEkJ9uyrn
64 lines
1.3 KiB
TypeScript
64 lines
1.3 KiB
TypeScript
export type Position = "GK" | "DEF" | "MID" | "WING" | "ST";
|
|
|
|
export type Difficulty = "easy" | "medium" | "hard";
|
|
|
|
export type Feedback = "correct" | "partial" | "wrong" | "empty";
|
|
|
|
export type Player = {
|
|
id: string;
|
|
name: string;
|
|
position: Position;
|
|
nationality: string;
|
|
club: string;
|
|
league: string;
|
|
formerClubs?: string[];
|
|
championsLeagueTitles?: number;
|
|
leagueTitles?: number;
|
|
leagueTitleCountries?: string[];
|
|
ballonDOr?: number;
|
|
goldenBoot?: number;
|
|
worldCupWinner?: boolean;
|
|
copaAmericaWinner?: boolean;
|
|
eurocupWinner?: boolean;
|
|
};
|
|
|
|
export type Puzzle = {
|
|
id: string;
|
|
date: string;
|
|
difficulty: Difficulty;
|
|
solution: Record<Position, string>;
|
|
fixedPosition: Position;
|
|
clues: string[];
|
|
};
|
|
|
|
export type SlotGuess = {
|
|
position: Position;
|
|
playerId: string | null;
|
|
feedback: Feedback;
|
|
};
|
|
|
|
export type Guess = {
|
|
slots: SlotGuess[];
|
|
};
|
|
|
|
export type PuzzleProgress = {
|
|
attempts: Guess[];
|
|
elapsedSeconds: number;
|
|
solved: boolean;
|
|
locked: boolean;
|
|
};
|
|
|
|
export type DailyProgress = Record<Difficulty, PuzzleProgress>;
|
|
|
|
export type GlobalStats = {
|
|
averageSeconds: number;
|
|
bestSeconds: number | null;
|
|
currentStreak: number;
|
|
gamesPlayed: number;
|
|
lastCompletedDate: string | null;
|
|
maxStreak: number;
|
|
perfectDays: number;
|
|
totalSeconds: number;
|
|
winDistribution: Record<number, number>;
|
|
};
|