bug fixes and first try of github docker
This commit is contained in:
+102
-1
@@ -1,5 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import ChessGame from "@/components/ChessGame";
|
||||
import StartScreen from "@/components/StartScreen";
|
||||
import { Personality } from "@/lib/personalities";
|
||||
|
||||
type ViewState = 'start' | 'game';
|
||||
|
||||
export default function Home() {
|
||||
return <ChessGame />;
|
||||
const router = useRouter();
|
||||
const [view, setView] = useState<ViewState>('start');
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// Game Initialization State
|
||||
const [gameProps, setGameProps] = useState<{
|
||||
initialFen?: string;
|
||||
initialPersonality: Personality;
|
||||
initialColor: 'white' | 'black';
|
||||
} | null>(null);
|
||||
|
||||
const [hasSavedGame, setHasSavedGame] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check for API Key
|
||||
const apiKey = localStorage.getItem("gemini_api_key");
|
||||
if (!apiKey) {
|
||||
router.push("/settings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for saved game
|
||||
const savedGame = localStorage.getItem("chess_tutor_save");
|
||||
if (savedGame) {
|
||||
setHasSavedGame(true);
|
||||
}
|
||||
|
||||
setMounted(true);
|
||||
}, [router]);
|
||||
|
||||
const handleStartGame = (options: {
|
||||
personality: Personality;
|
||||
color: 'white' | 'black' | 'random';
|
||||
fen?: string;
|
||||
}) => {
|
||||
const color = options.color === 'random'
|
||||
? (Math.random() < 0.5 ? 'white' : 'black')
|
||||
: options.color;
|
||||
|
||||
setGameProps({
|
||||
initialFen: options.fen,
|
||||
initialPersonality: options.personality,
|
||||
initialColor: color
|
||||
});
|
||||
setView('game');
|
||||
};
|
||||
|
||||
const handleResumeGame = () => {
|
||||
const savedGame = localStorage.getItem("chess_tutor_save");
|
||||
if (savedGame) {
|
||||
try {
|
||||
const data = JSON.parse(savedGame);
|
||||
if (data.fen && data.selectedPersonality) {
|
||||
setGameProps({
|
||||
initialFen: data.fen,
|
||||
initialPersonality: data.selectedPersonality,
|
||||
initialColor: data.playerColor || 'white'
|
||||
});
|
||||
setView('game');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to resume game:", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBackToMenu = () => {
|
||||
setView('start');
|
||||
// Re-check saved game status as it might have changed
|
||||
const savedGame = localStorage.getItem("chess_tutor_save");
|
||||
setHasSavedGame(!!savedGame);
|
||||
};
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<main>
|
||||
{view === 'start' && (
|
||||
<StartScreen
|
||||
onStartGame={handleStartGame}
|
||||
onResumeGame={handleResumeGame}
|
||||
hasSavedGame={hasSavedGame}
|
||||
/>
|
||||
)}
|
||||
{view === 'game' && gameProps && (
|
||||
<ChessGame
|
||||
initialFen={gameProps.initialFen}
|
||||
initialPersonality={gameProps.initialPersonality}
|
||||
initialColor={gameProps.initialColor}
|
||||
onBack={handleBackToMenu}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Header from "@/components/Header";
|
||||
import { useTranslation } from "@/lib/i18n/useTranslation";
|
||||
import { SupportedLanguage } from "@/lib/i18n/translations";
|
||||
import { ArrowLeft, Save } from "lucide-react";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
const [language, setLanguage] = useState<SupportedLanguage>('en');
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// Load settings on mount
|
||||
useEffect(() => {
|
||||
const storedKey = localStorage.getItem("gemini_api_key");
|
||||
const storedLang = localStorage.getItem("chess_tutor_language");
|
||||
|
||||
if (storedKey) setApiKey(storedKey);
|
||||
if (storedLang) setLanguage(storedLang as SupportedLanguage);
|
||||
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const t = useTranslation(language);
|
||||
|
||||
const handleSave = () => {
|
||||
if (apiKey.trim()) {
|
||||
localStorage.setItem("gemini_api_key", apiKey.trim());
|
||||
} else {
|
||||
localStorage.removeItem("gemini_api_key");
|
||||
}
|
||||
|
||||
localStorage.setItem("chess_tutor_language", language);
|
||||
|
||||
// Go back to home
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header language={language} />
|
||||
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-4">
|
||||
<div className="max-w-2xl mx-auto pt-8">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-8 space-y-8">
|
||||
<div className="flex items-center gap-4 border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<button
|
||||
onClick={() => router.push("/")}
|
||||
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
>
|
||||
<ArrowLeft size={24} className="text-gray-600 dark:text-gray-300" />
|
||||
</button>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
{t.start.settings}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Language Selection */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t.start.language}
|
||||
</label>
|
||||
<div className="flex gap-3">
|
||||
{(['en', 'de', 'fr', 'it'] as SupportedLanguage[]).map((lang) => (
|
||||
<button
|
||||
key={lang}
|
||||
onClick={() => setLanguage(lang)}
|
||||
className={`px-4 py-2 rounded-lg border text-sm font-medium transition-all ${language === lang
|
||||
? 'bg-blue-600 text-white border-blue-600 shadow-md'
|
||||
: 'bg-gray-50 dark:bg-gray-700 border-gray-200 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200'
|
||||
}`}
|
||||
>
|
||||
{lang.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* API Key Input */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t.start.apiKey}
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
<input
|
||||
type="password"
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
placeholder={t.start.apiKeyPlaceholder}
|
||||
className="w-full p-3 border rounded-lg dark:bg-gray-700 dark:border-gray-600 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 outline-none transition-all"
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{t.start.apiKeyRequired} <a href="https://aistudio.google.com/app/apikey" target="_blank" rel="noreferrer" className="text-blue-600 hover:underline">{t.start.getApiKey}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-gray-200 dark:border-gray-700 flex justify-end">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium shadow-md transition-transform transform hover:scale-[1.02]"
|
||||
>
|
||||
<Save size={20} />
|
||||
{t.common?.save || "Save Settings"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user