mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
dev day #70
This commit is contained in:
@@ -76,8 +76,18 @@ const AuthPage: React.FC = () => {
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
// Redirect to original destination or dashboard
|
||||
navigate(from, { replace: true });
|
||||
// Role-based redirect after login
|
||||
const role = String(user?.role || '').toLowerCase();
|
||||
if (role === 'admin') {
|
||||
navigate('/admin', { replace: true });
|
||||
} else if (role === 'editor') {
|
||||
navigate('/admin', { replace: true });
|
||||
} else if (role === 'user') {
|
||||
navigate('/', { replace: true });
|
||||
} else {
|
||||
// Fallback for unknown roles (e.g., fan): go to frontpage
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: 'Přihlášení selhalo',
|
||||
|
||||
@@ -1580,10 +1580,19 @@ const HomePage: React.FC = () => {
|
||||
(matchingStanding.rows && matchingStanding.rows.length > 0)
|
||||
);
|
||||
|
||||
const showNews = isVisible('news', true);
|
||||
const showTable = isVisible('table', true) && hasStandingsForCurrentTab;
|
||||
const variant = showNews && showTable ? undefined : 'standard';
|
||||
if (!showNews && !showTable) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible('news', true) && (
|
||||
<section data-element="news" className="news-list" style={{ marginTop: 32, ...getStyles('news') }}>
|
||||
<section
|
||||
className="standings"
|
||||
data-variant={variant}
|
||||
style={{ marginTop: 32 }}
|
||||
>
|
||||
{showNews && (
|
||||
<section data-element="news" className="news-list" style={{ ...getStyles('news') }}>
|
||||
<div className="section-head" style={{ marginTop: 0 }}>
|
||||
<h3>Další aktuality</h3>
|
||||
</div>
|
||||
@@ -1610,12 +1619,8 @@ const HomePage: React.FC = () => {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{isVisible('table', true) && hasStandingsForCurrentTab && (
|
||||
<section
|
||||
data-element="table"
|
||||
className="standings"
|
||||
style={{ marginTop: 32, ...getStyles('table') }}
|
||||
>
|
||||
{showTable && (
|
||||
<div data-element="table" style={{ ...getStyles('table') }}>
|
||||
<div className="table-card">
|
||||
<div className="section-head" style={{ marginTop: 0, marginBottom: 12 }}>
|
||||
<h3>Tabulky</h3>
|
||||
@@ -1699,9 +1704,9 @@ const HomePage: React.FC = () => {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</section>
|
||||
);
|
||||
})()}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const OverlayScoreboardPage: React.FC = () => {
|
||||
const { data, isLoading } = useQuery<ScoreboardState>({
|
||||
queryKey: ['public-scoreboard'],
|
||||
queryFn: getPublicScoreboard,
|
||||
refetchInterval: 5000,
|
||||
refetchInterval: 1000,
|
||||
staleTime: 3000,
|
||||
});
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: '',
|
||||
description: '',
|
||||
type: 'single',
|
||||
style: 'auto',
|
||||
status: 'draft',
|
||||
allow_multiple: false,
|
||||
max_choices: 1,
|
||||
@@ -191,6 +192,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: '',
|
||||
description: '',
|
||||
type: 'single',
|
||||
style: 'auto',
|
||||
status: 'draft',
|
||||
allow_multiple: false,
|
||||
max_choices: 1,
|
||||
@@ -221,6 +223,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: 'Hodnocení zápasu',
|
||||
description: 'Ohodnoťte zápas (1 = nejhorší, 5 = nejlepší)',
|
||||
type: 'rating',
|
||||
style: 'rating-stars',
|
||||
status: 'active',
|
||||
allow_multiple: false,
|
||||
max_choices: 1,
|
||||
@@ -239,6 +242,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: 'Hodnocení zápasu (1–10)',
|
||||
description: 'Ohodnoťte zápas (1 = nejhorší, 10 = nejlepší)',
|
||||
type: 'rating',
|
||||
style: 'rating-scale',
|
||||
status: 'active',
|
||||
allow_multiple: false,
|
||||
max_choices: 1,
|
||||
@@ -253,6 +257,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: 'Dorazíš na schůzku?',
|
||||
description: 'Dej nám vědět, zda dorazíš.',
|
||||
type: 'single',
|
||||
style: 'choices-chips',
|
||||
status: 'active',
|
||||
allow_multiple: false,
|
||||
max_choices: 1,
|
||||
@@ -276,6 +281,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
title: poll.title,
|
||||
description: poll.description,
|
||||
type: poll.type,
|
||||
style: (poll as any).style || 'auto',
|
||||
status: poll.status,
|
||||
start_date: poll.start_date,
|
||||
end_date: poll.end_date,
|
||||
@@ -566,7 +572,7 @@ const PollsAdminPage: React.FC = () => {
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<SimpleGrid columns={2} spacing={4} w="full">
|
||||
<SimpleGrid columns={3} spacing={4} w="full">
|
||||
<FormControl>
|
||||
<FormLabel>Typ</FormLabel>
|
||||
<Select
|
||||
@@ -595,6 +601,28 @@ const PollsAdminPage: React.FC = () => {
|
||||
<option value="archived">Archivovaná</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>Styl</FormLabel>
|
||||
<Select
|
||||
value={(formData as any).style || 'auto'}
|
||||
onChange={(e) => setFormData({ ...formData, style: e.target.value as any })}
|
||||
>
|
||||
<option value="auto">Automaticky</option>
|
||||
{formData.type === 'rating' ? (
|
||||
<>
|
||||
<option value="rating-stars">Hvězdičky</option>
|
||||
<option value="rating-scale">Číselná stupnice</option>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<option value="choices-list">Seznam</option>
|
||||
<option value="choices-chips">Štítky</option>
|
||||
<option value="choices-cards">Karty</option>
|
||||
</>
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</SimpleGrid>
|
||||
|
||||
<SimpleGrid columns={2} spacing={4} w="full">
|
||||
|
||||
@@ -39,6 +39,8 @@ import {
|
||||
startTimer,
|
||||
pauseTimer,
|
||||
resetTimer,
|
||||
swapSides,
|
||||
startSecondHalf,
|
||||
} from '@/services/scoreboard';
|
||||
import { useFacrApi } from '@/hooks/useFacrApi';
|
||||
import { SearchResult } from '@/services/facr/types';
|
||||
@@ -434,6 +436,18 @@ const ScoreboardAdminPage: React.FC = () => {
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Fauly domácích</FormLabel>
|
||||
<NumberInput value={state.homeFouls || 0} min={0} max={5} onChange={async (_, n) => setPartial({ homeFouls: Math.max(0, Math.min(5, Number.isFinite(n) ? n : 0)) })}>
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Fauly hostů</FormLabel>
|
||||
<NumberInput value={state.awayFouls || 0} min={0} max={5} onChange={async (_, n) => setPartial({ awayFouls: Math.max(0, Math.min(5, Number.isFinite(n) ? n : 0)) })}>
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Délka poločasu (min)</FormLabel>
|
||||
<NumberInput value={state.halfLength} min={1} max={60} onChange={async (_, n) => setPartial({ halfLength: Number.isFinite(n) ? n : 45 })}>
|
||||
@@ -448,6 +462,17 @@ const ScoreboardAdminPage: React.FC = () => {
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl display="flex" alignItems="center">
|
||||
<FormLabel mb={0}>Přehodit strany (vizuálně)</FormLabel>
|
||||
<Switch isChecked={!!state.sidesFlipped} onChange={async (e) => setPartial({ sidesFlipped: e.target.checked })} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Poločas</FormLabel>
|
||||
<Select value={String(state.half || 1)} onChange={async (e) => setPartial({ half: parseInt(e.target.value, 10) || 1 })}>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
|
||||
@@ -462,6 +487,18 @@ const ScoreboardAdminPage: React.FC = () => {
|
||||
<FormLabel>Barva hostů</FormLabel>
|
||||
<Input type="color" value={state.secondaryColor || '#2563eb'} onChange={async (e) => setPartial({ secondaryColor: e.target.value })} />
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>QR interval (minuty)</FormLabel>
|
||||
<NumberInput value={state.qrEvery || 5} min={1} max={120} onChange={async (_, n) => setPartial({ qrEvery: Math.max(1, Number.isFinite(n) ? n : 5) })}>
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>QR délka zobrazení (sekundy)</FormLabel>
|
||||
<NumberInput value={state.qrDuration || 60} min={5} max={600} onChange={async (_, n) => setPartial({ qrDuration: Math.max(5, Number.isFinite(n) ? n : 60) })}>
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
</FormControl>
|
||||
</SimpleGrid>
|
||||
|
||||
<Divider my={4} />
|
||||
@@ -528,6 +565,16 @@ const ScoreboardAdminPage: React.FC = () => {
|
||||
const s = await getScoreboardState();
|
||||
setState(s);
|
||||
}}>Reset</Button>
|
||||
<Button onClick={async () => {
|
||||
await swapSides();
|
||||
const s = await getScoreboardState();
|
||||
setState(s);
|
||||
}}>Přehodit strany</Button>
|
||||
<Button colorScheme="purple" onClick={async () => {
|
||||
await startSecondHalf();
|
||||
const s = await getScoreboardState();
|
||||
setState(s);
|
||||
}}>Začít 2. poločas</Button>
|
||||
</HStack>
|
||||
</HStack>
|
||||
<HStack mt={3} spacing={3} align="center">
|
||||
|
||||
Reference in New Issue
Block a user