Fix homepage layout and add Polish language support

- Reorganize StartScreen: Start New Game button always visible at top
- Move unfinished games section below the start button
- Remove '2.' prefix from startGame translations (all languages)
- Update startNewGame translations to be button-appropriate
- Fix TypeScript errors: Update GameImportModal, GameAnalysisModal, and GameOverModal to use SupportedLanguage type instead of hardcoded language union
- Delete local branches: codex/add-polish-as-ui-language and codex/add-option-to-continue-game-from-position
This commit is contained in:
Stefan
2025-12-01 11:49:12 +01:00
parent 13210c61ee
commit 6acc7ecc1c
5 changed files with 37 additions and 34 deletions
+2 -1
View File
@@ -5,12 +5,13 @@ import { Stockfish, StockfishEvaluation } from "@/lib/stockfish";
import { OpeningMetadata, lookupOpening } from "@/lib/openings"; import { OpeningMetadata, lookupOpening } from "@/lib/openings";
import { getGenAIModel } from "@/lib/gemini"; import { getGenAIModel } from "@/lib/gemini";
import { Loader2, X, Brain, Trophy, AlertTriangle } from "lucide-react"; import { Loader2, X, Brain, Trophy, AlertTriangle } from "lucide-react";
import { SupportedLanguage } from "@/lib/i18n/translations";
interface GameAnalysisModalProps { interface GameAnalysisModalProps {
fen: string; fen: string;
stockfish: Stockfish | null; stockfish: Stockfish | null;
apiKey: string | null; apiKey: string | null;
language: 'en' | 'de' | 'fr' | 'it'; language: SupportedLanguage;
onClose: () => void; onClose: () => void;
} }
+2 -1
View File
@@ -4,11 +4,12 @@ import { useState, useEffect } from "react";
import { Chessboard } from "react-chessboard"; import { Chessboard } from "react-chessboard";
import { X, Loader2, Download, ExternalLink } from "lucide-react"; import { X, Loader2, Download, ExternalLink } from "lucide-react";
import { fetchChessComGames, fetchLichessGames, GameMetadata, Platform } from "@/lib/gameImport"; import { fetchChessComGames, fetchLichessGames, GameMetadata, Platform } from "@/lib/gameImport";
import { SupportedLanguage } from "@/lib/i18n/translations";
interface GameImportModalProps { interface GameImportModalProps {
onClose: () => void; onClose: () => void;
onSelectGame: (pgn: string) => void; onSelectGame: (pgn: string) => void;
language: 'en' | 'de' | 'fr' | 'it'; language: SupportedLanguage;
} }
export function GameImportModal({ onClose, onSelectGame, language }: GameImportModalProps) { export function GameImportModal({ onClose, onSelectGame, language }: GameImportModalProps) {
+2 -1
View File
@@ -6,6 +6,7 @@ import { Loader2, X, Trophy, AlertTriangle, RefreshCw } from "lucide-react";
import { StockfishEvaluation } from "@/lib/stockfish"; import { StockfishEvaluation } from "@/lib/stockfish";
import { DetectedTactic } from "@/lib/tacticDetection"; import { DetectedTactic } from "@/lib/tacticDetection";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { SupportedLanguage } from "@/lib/i18n/translations";
export interface MoveHistoryItem { export interface MoveHistoryItem {
moveNumber: number; moveNumber: number;
@@ -50,7 +51,7 @@ interface GameOverModalProps {
winner: "White" | "Black" | "Draw"; winner: "White" | "Black" | "Draw";
history: MoveHistoryItem[]; history: MoveHistoryItem[];
apiKey: string | null; apiKey: string | null;
language: 'en' | 'de' | 'fr' | 'it'; language: SupportedLanguage;
onClose: () => void; onClose: () => void;
onNewGame: () => void; onNewGame: () => void;
} }
+21 -21
View File
@@ -85,11 +85,7 @@ export default function StartScreen({ onStartGame, onResumeGame, savedGames, onD
return t.start.noEvaluation; return t.start.noEvaluation;
}; };
useEffect(() => {
if (!hasSavedGames) {
setShowNewGameOptions(true);
}
}, [hasSavedGames]);
if (!mounted) return null; if (!mounted) return null;
@@ -118,18 +114,24 @@ export default function StartScreen({ onStartGame, onResumeGame, savedGames, onD
</h2> </h2>
<div className="space-y-6"> <div className="space-y-6">
{hasSavedGames && ( {/* Start New Game Button - Always visible at top */}
<div>
<button
onClick={() => setShowNewGameOptions(!showNewGameOptions)}
className="w-full py-4 px-4 bg-blue-600 text-white rounded-xl hover:bg-blue-700 font-semibold shadow-lg transition-transform transform hover:scale-[1.02] flex items-center justify-center gap-2"
>
<Brain size={18} />
{t.start.startNewGame}
</button>
</div>
{/* Unfinished Games Section */}
{hasSavedGames && !showNewGameOptions && (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white"> <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
{t.start.savedGamesTitle} {t.start.savedGamesTitle}
</h3> </h3>
<button
onClick={() => setShowNewGameOptions(true)}
className="text-sm text-blue-600 dark:text-blue-400 hover:underline"
>
{t.start.startNewGame}
</button>
</div> </div>
{sortedSavedGames.length === 0 && ( {sortedSavedGames.length === 0 && (
@@ -210,7 +212,7 @@ export default function StartScreen({ onStartGame, onResumeGame, savedGames, onD
)} )}
{/* New Game Options */} {/* New Game Options */}
{(!hasSavedGames || showNewGameOptions) && ( {showNewGameOptions && (
<div className="space-y-8 animate-in fade-in slide-in-from-top-4 duration-300"> <div className="space-y-8 animate-in fade-in slide-in-from-top-4 duration-300">
{/* Color Selection */} {/* Color Selection */}
<div> <div>
@@ -326,14 +328,12 @@ export default function StartScreen({ onStartGame, onResumeGame, savedGames, onD
)} )}
</div> </div>
{hasSavedGames && ( <button
<button onClick={() => setShowNewGameOptions(false)}
onClick={() => setShowNewGameOptions(false)} className="w-full py-3 text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
className="w-full py-3 text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors" >
> {t.common.cancel}
{t.common.cancel} </button>
</button>
)}
</div> </div>
)} )}
+10 -10
View File
@@ -194,9 +194,9 @@ const en: Translations = {
apiKey: 'Google Gemini API Key', apiKey: 'Google Gemini API Key',
apiKeyPlaceholder: 'AIzaSy...', apiKeyPlaceholder: 'AIzaSy...',
getApiKey: 'Get free key', getApiKey: 'Get free key',
startGame: '2. Start Game', startGame: 'Start Game',
resumeGame: 'Resume Previous Game', resumeGame: 'Resume Previous Game',
startNewGame: 'Start New Game instead...', startNewGame: 'Start New Game',
chooseCoach: 'Choose Your Coach:', chooseCoach: 'Choose Your Coach:',
importPosition: 'Import Position or Game (FEN or PGN)', importPosition: 'Import Position or Game (FEN or PGN)',
importPositionPlaceholder: 'Paste FEN position or PGN game here...', importPositionPlaceholder: 'Paste FEN position or PGN game here...',
@@ -355,9 +355,9 @@ const de: Translations = {
apiKey: 'Google Gemini API-Schlüssel', apiKey: 'Google Gemini API-Schlüssel',
apiKeyPlaceholder: 'AIzaSy...', apiKeyPlaceholder: 'AIzaSy...',
getApiKey: 'Kostenlosen Schlüssel erhalten', getApiKey: 'Kostenlosen Schlüssel erhalten',
startGame: '2. Spiel starten', startGame: 'Spiel starten',
resumeGame: 'Vorheriges Spiel fortsetzen', resumeGame: 'Vorheriges Spiel fortsetzen',
startNewGame: 'Stattdessen neues Spiel starten...', startNewGame: 'Neues Spiel starten',
chooseCoach: 'Wähle deinen Trainer:', chooseCoach: 'Wähle deinen Trainer:',
importPosition: 'Position oder Partie importieren (FEN oder PGN)', importPosition: 'Position oder Partie importieren (FEN oder PGN)',
importPositionPlaceholder: 'FEN-Position oder PGN-Partie hier einfügen...', importPositionPlaceholder: 'FEN-Position oder PGN-Partie hier einfügen...',
@@ -516,9 +516,9 @@ const fr: Translations = {
apiKey: 'Clé API Google Gemini', apiKey: 'Clé API Google Gemini',
apiKeyPlaceholder: 'AIzaSy...', apiKeyPlaceholder: 'AIzaSy...',
getApiKey: 'Obtenir une clé gratuite', getApiKey: 'Obtenir une clé gratuite',
startGame: '2. Démarrer la partie', startGame: 'Démarrer la partie',
resumeGame: 'Reprendre la partie précédente', resumeGame: 'Reprendre la partie précédente',
startNewGame: 'Démarrer une nouvelle partie...', startNewGame: 'Démarrer une nouvelle partie',
chooseCoach: 'Choisissez votre coach :', chooseCoach: 'Choisissez votre coach :',
importPosition: 'Importer une position ou partie (FEN ou PGN)', importPosition: 'Importer une position ou partie (FEN ou PGN)',
importPositionPlaceholder: 'Collez une position FEN ou partie PGN ici...', importPositionPlaceholder: 'Collez une position FEN ou partie PGN ici...',
@@ -677,9 +677,9 @@ const it: Translations = {
apiKey: 'Chiave API Google Gemini', apiKey: 'Chiave API Google Gemini',
apiKeyPlaceholder: 'AIzaSy...', apiKeyPlaceholder: 'AIzaSy...',
getApiKey: 'Ottieni chiave gratuita', getApiKey: 'Ottieni chiave gratuita',
startGame: '2. Inizia partita', startGame: 'Inizia partita',
resumeGame: 'Riprendi partita precedente', resumeGame: 'Riprendi partita precedente',
startNewGame: 'Inizia nuova partita...', startNewGame: 'Inizia nuova partita',
chooseCoach: 'Scegli il tuo allenatore:', chooseCoach: 'Scegli il tuo allenatore:',
importPosition: 'Importa posizione o partita (FEN o PGN)', importPosition: 'Importa posizione o partita (FEN o PGN)',
importPositionPlaceholder: 'Incolla posizione FEN o partita PGN qui...', importPositionPlaceholder: 'Incolla posizione FEN o partita PGN qui...',
@@ -838,9 +838,9 @@ const pl: Translations = {
apiKey: 'Klucz API Google Gemini', apiKey: 'Klucz API Google Gemini',
apiKeyPlaceholder: 'AIzaSy...', apiKeyPlaceholder: 'AIzaSy...',
getApiKey: 'Zdobądź darmowy klucz', getApiKey: 'Zdobądź darmowy klucz',
startGame: '2. Rozpocznij grę', startGame: 'Rozpocznij grę',
resumeGame: 'Wznów poprzednią partię', resumeGame: 'Wznów poprzednią partię',
startNewGame: 'Zacznij nową partię...', startNewGame: 'Rozpocznij nową partię',
chooseCoach: 'Wybierz swojego trenera:', chooseCoach: 'Wybierz swojego trenera:',
importPosition: 'Importuj pozycję lub partię (FEN lub PGN)', importPosition: 'Importuj pozycję lub partię (FEN lub PGN)',
importPositionPlaceholder: 'Wklej tutaj pozycję FEN lub partię PGN...', importPositionPlaceholder: 'Wklej tutaj pozycję FEN lub partię PGN...',