diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 36ef639..e5a1ff4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,6 +4,7 @@ import { motion, AnimatePresence, MotionConfig } from 'motion/react' import { IconShare2 } from '@tabler/icons-react' import Grid from './components/Grid' import CluePanel from './components/CluePanel' +import ClueNav from './components/ClueNav' import ActiveClue from './components/ActiveClue' import Header from './components/Header' import InfoBar from './components/InfoBar' @@ -309,6 +310,7 @@ export default function App() {

{t('app.crosswordGrid')}

+
{(['auto', 'keyboard', 'stylus'] as const).map(m => ( @@ -729,18 +730,18 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) {

{t('admin.setup.title')}

{t('admin.setup.desc')}

- setUsername(e.target.value)} placeholder={t('admin.username')} className="admin-input" autoComplete="username" /> - setPassword(e.target.value)} placeholder={t('admin.password')} className="admin-input" autoComplete="new-password" /> - setConfirmPassword(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSetup()} placeholder={t('admin.setup.confirmPassword')} className="admin-input" autoComplete="new-password" /> - {setupError &&

{setupError}

} + setUsername(e.target.value)} placeholder={t('admin.username')} aria-label={t('admin.username')} className="admin-input" autoComplete="username" /> + setPassword(e.target.value)} placeholder={t('admin.password')} aria-label={t('admin.password')} className="admin-input" autoComplete="new-password" /> + setConfirmPassword(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSetup()} placeholder={t('admin.setup.confirmPassword')} aria-label={t('admin.setup.confirmPassword')} className="admin-input" autoComplete="new-password" /> + {setupError &&

{setupError}

}
) : (
- {setupSuccess &&

{setupSuccess}

} - setUsername(e.target.value)} onKeyDown={e => e.key === 'Enter' && login()} placeholder={t('admin.username')} className="admin-input" autoComplete="username" /> - setPassword(e.target.value)} onKeyDown={e => e.key === 'Enter' && login()} placeholder={t('admin.password')} className="admin-input" autoComplete="current-password" /> - {loginError &&

{loginError}

} + {setupSuccess &&

{setupSuccess}

} + setUsername(e.target.value)} onKeyDown={e => e.key === 'Enter' && login()} placeholder={t('admin.username')} aria-label={t('admin.username')} className="admin-input" autoComplete="username" /> + setPassword(e.target.value)} onKeyDown={e => e.key === 'Enter' && login()} placeholder={t('admin.password')} aria-label={t('admin.password')} className="admin-input" autoComplete="current-password" /> + {loginError &&

{loginError}

}
)} @@ -772,12 +773,12 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) {
- {notification &&
{notification}
} + {notification &&
{notification}
}
-
- +
0 && selectedPuzzles.size === puzzles.length} onChange={e => { if (e.target.checked) setSelectedPuzzles(new Set(puzzles.map(p => p.id))); else setSelectedPuzzles(new Set()) }} />{t('admin.content.id')}{t('admin.content.type')}{t('admin.content.size')}{t('admin.overview.solves')}{t('admin.content.rating')}{t('admin.content.created')}
{puzzles.map(p => ( @@ -864,7 +865,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { - + ))}
0 && selectedPuzzles.size === puzzles.length} onChange={e => { if (e.target.checked) setSelectedPuzzles(new Set(puzzles.map(p => p.id))); else setSelectedPuzzles(new Set()) }} />{t('admin.content.id')}{t('admin.content.type')}{t('admin.content.size')}{t('admin.overview.solves')}{t('admin.content.rating')}{t('admin.content.created')}
togglePuzzleSelect(p.id)} />{p.solves} {p.rating > 0 ? `${p.rating}/5` : '-'} {new Date(p.created_at).toLocaleDateString()}
@@ -879,7 +880,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { )} - +
0 && selectedCommunity.size === communityPuzzles.length} onChange={e => { if (e.target.checked) setSelectedCommunity(new Set(communityPuzzles.map(p => p.id))); else setSelectedCommunity(new Set()) }} />{t('admin.content.title')}{t('admin.content.author')}{t('admin.content.size')}{t('admin.content.created')}
{communityPuzzles.map(p => ( @@ -887,7 +888,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { - + ))}
0 && selectedCommunity.size === communityPuzzles.length} onChange={e => { if (e.target.checked) setSelectedCommunity(new Set(communityPuzzles.map(p => p.id))); else setSelectedCommunity(new Set()) }} />{t('admin.content.title')}{t('admin.content.author')}{t('admin.content.size')}{t('admin.content.created')}
toggleCommunitySelect(p.id)} />{p.author} {p.width}x{p.height} {new Date(p.created_at).toLocaleDateString()}
@@ -929,7 +930,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) {

{t('admin.content.blockedWords')}

{t('admin.content.blockedWordsDesc')}

- setBlockedWord(e.target.value)} onKeyDown={e => e.key === 'Enter' && addBlockedWord()} className="admin-input" style={{ maxWidth: '16rem' }} /> @@ -941,7 +942,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { {tab === 'users' && tabLoading.users && } {tab === 'users' && !tabLoading.users && (<>
- setSessionSearch(e.target.value)} className="admin-input" style={{ maxWidth: '16rem' }} /> + setSessionSearch(e.target.value)} className="admin-input" style={{ maxWidth: '16rem' }} /> {t('admin.users.userCount', { count: filteredSessions.length })}
@@ -956,10 +957,10 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { {new Date(s.created_at).toLocaleDateString()} {s.banned - ? - : + ? + : } - + ))} @@ -979,7 +980,7 @@ export default function AdminPanel({ onClose }: { onClose: () => void }) { {tab === 'integrations' && !tabLoading.integrations && (<>

{t('admin.integrations.webhooks')}

- setNewWebhook({ ...newWebhook, url: e.target.value })} className="admin-input" /> + setNewWebhook({ ...newWebhook, url: e.target.value })} className="admin-input" />