feat: Implement host authentication and settings management
Introduces a new authentication flow for the host, requiring a password to access host-specific features. This commit also adds a dedicated settings section within the host view, allowing administrators to configure essential application parameters such as the API key for AI question generation and the join URL for players. The backend has been updated to include a new `settings` table in the database to persist these configurations. The Gemini service is refactored to accept the API key as a parameter, enhancing flexibility and security. UI components like `HostView` and `App.tsx` are modified to integrate the new authentication and settings management functionalities. Key changes include: - Password-based authentication for host access. - A new settings interface for API key and join URL management. - Database schema update with a `settings` table. - Refactoring `geminiService` to accept API key. - UI adjustments for login and settings screens.
This commit is contained in:
@@ -1,24 +1,68 @@
|
||||
import React, { useState, useEffect, Suspense } from 'react';
|
||||
import { GameProvider, useGame } from './context/GameContext';
|
||||
import { Monitor, Smartphone, LayoutDashboard, Loader2, RefreshCw } from 'lucide-react';
|
||||
import { Monitor, Smartphone, LayoutDashboard, Loader2, RefreshCw, Lock } from 'lucide-react';
|
||||
|
||||
// Lazy load components to reduce initial bundle size
|
||||
const HostView = React.lazy(() => import('./components/HostView').then(module => ({ default: module.HostView })));
|
||||
const PlayerView = React.lazy(() => import('./components/PlayerView').then(module => ({ default: module.PlayerView })));
|
||||
const SpectatorView = React.lazy(() => import('./components/SpectatorView').then(module => ({ default: module.SpectatorView })));
|
||||
|
||||
// Inner Component to access Context
|
||||
const LoginScreen: React.FC = () => {
|
||||
const { login } = useGame();
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError('');
|
||||
const success = await login(password);
|
||||
if (!success) {
|
||||
setError('Invalid Password');
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full bg-slate-900">
|
||||
<div className="bg-white p-8 rounded-xl shadow-2xl max-w-sm w-full">
|
||||
<div className="flex justify-center mb-6 text-indigo-600">
|
||||
<Lock size={48} />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-center text-slate-800 mb-6">Host Access</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full p-3 border border-slate-300 rounded-lg mb-4 text-slate-800 focus:ring-2 focus:ring-indigo-500 outline-none"
|
||||
placeholder="Enter Admin Password"
|
||||
autoFocus
|
||||
/>
|
||||
{error && <p className="text-red-500 text-sm mb-4 text-center font-bold">{error}</p>}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full bg-indigo-600 text-white py-3 rounded-lg font-bold hover:bg-indigo-700 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Verifying...' : 'Login'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AppContent: React.FC = () => {
|
||||
const { setIsHost, isSyncing, isHost } = useGame();
|
||||
const { setIsHost, isSyncing, isAuthenticated } = useGame();
|
||||
const [view, setView] = useState<'HOST' | 'PLAYER' | 'SPECTATOR'>('SPECTATOR');
|
||||
|
||||
// Simple hash routing for demo purposes
|
||||
useEffect(() => {
|
||||
const handleHashChange = () => {
|
||||
const hash = window.location.hash;
|
||||
if (hash === '#host') {
|
||||
setView('HOST');
|
||||
setIsHost(true); // Promote this session to Host Authority
|
||||
setIsHost(true);
|
||||
}
|
||||
else if (hash === '#player') {
|
||||
setView('PLAYER');
|
||||
@@ -42,7 +86,8 @@ const AppContent: React.FC = () => {
|
||||
|
||||
const renderView = () => {
|
||||
switch (view) {
|
||||
case 'HOST': return <HostView />;
|
||||
case 'HOST':
|
||||
return isAuthenticated ? <HostView /> : <LoginScreen />;
|
||||
case 'PLAYER': return <PlayerView />;
|
||||
case 'SPECTATOR': return <SpectatorView />;
|
||||
default: return <SpectatorView />;
|
||||
@@ -127,4 +172,4 @@ const NavButton: React.FC<NavButtonProps> = ({ active, onClick, icon, label }) =
|
||||
</button>
|
||||
);
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
Reference in New Issue
Block a user