This commit is contained in:
Tomas Dvorak
2025-10-19 17:16:57 +02:00
parent e9a63073e5
commit 77213f4e83
76 changed files with 9728 additions and 935 deletions
+86 -5
View File
@@ -36,6 +36,8 @@ const MatchesPage: React.FC = () => {
const [aliases, setAliases] = useState<CompetitionAlias[]>([]);
const [aliasMap, setAliasMap] = useState<Record<string, { alias: string; original_name?: string; display_order?: number }>>({});
const [sortAscending, setSortAscending] = useState<boolean>(true); // true = oldest first, false = newest first
const [displayedMatchesCount, setDisplayedMatchesCount] = useState<Record<number, number>>({}); // Track displayed matches per competition index
const MATCHES_PER_PAGE = 12; // Number of matches to load initially and per load more click
// Dark mode colors
const bgColor = useColorModeValue('#f8f9fb', '#0f1115');
@@ -78,11 +80,29 @@ const MatchesPage: React.FC = () => {
};
// Sentiment helpers for win/draw/loss detection
const normalize = (s: string) => String(s || '')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
const stripPrefixes = (s: string) => {
let x = normalize(s);
x = x.replace(/\b(mestsky|m\.?f\.?k\.?|mfk|tj|sk|sokol|fotbalovy|fotbalový|fotbalovy\s+klub|fotbalovy\s+klub)\b/g, '');
return x.replace(/\s+/g, ' ').trim();
};
const isClubTeam = (team: string) => {
const normalize = (s: string) => s.toLowerCase().trim();
const a = normalize(team);
const b = normalize(clubName || '');
return a.includes(b) || b.includes(a);
try {
const a = stripPrefixes(team);
const b = stripPrefixes(clubName || '');
if (!a || !b) return false;
// Allow equality or suffix match (handles prefixes like TJ, SK, etc.)
return a === b || a.endsWith(b) || b.endsWith(a);
} catch {
return false;
}
};
const parseScore = (score?: string | null): { h: number; a: number } | null => {
@@ -143,6 +163,27 @@ const MatchesPage: React.FC = () => {
return sorted;
}, [facrCompetitions, sortAscending]);
// Initialize displayed matches count when competitions change
useEffect(() => {
const initialCounts: Record<number, number> = {};
sortedCompetitions.forEach((_, index) => {
if (displayedMatchesCount[index] === undefined) {
initialCounts[index] = MATCHES_PER_PAGE;
}
});
if (Object.keys(initialCounts).length > 0) {
setDisplayedMatchesCount(prev => ({ ...prev, ...initialCounts }));
}
}, [sortedCompetitions.length]);
// Handle load more for a specific competition
const handleLoadMore = (competitionIndex: number) => {
setDisplayedMatchesCount(prev => ({
...prev,
[competitionIndex]: (prev[competitionIndex] || MATCHES_PER_PAGE) + MATCHES_PER_PAGE
}));
};
// Get all upcoming matches for countdown tracking
const upcomingMatches = useMemo(() => {
if (activeTab >= sortedCompetitions.length) return [];
@@ -416,6 +457,7 @@ const MatchesPage: React.FC = () => {
Žádné zápasy k zobrazení
</div>
) : (
<>
<div
style={{
display: 'grid',
@@ -423,7 +465,7 @@ const MatchesPage: React.FC = () => {
gap: 16,
}}
>
{c.matches.map((m: MatchItem, idx: number) => {
{c.matches.slice(0, displayedMatchesCount[compIdx] || MATCHES_PER_PAGE).map((m: MatchItem, idx: number) => {
const matchTime = new Date(`${m.date}T${(m.time || '00:00')}:00`).getTime();
const currentTime = Date.now();
const isFuture = matchTime > currentTime;
@@ -607,6 +649,45 @@ const MatchesPage: React.FC = () => {
);
})}
</div>
{c.matches.length > (displayedMatchesCount[compIdx] || MATCHES_PER_PAGE) && (
<div style={{ textAlign: 'center', marginTop: 32 }}>
<button
onClick={() => handleLoadMore(compIdx)}
style={{
padding: '14px 32px',
background: 'var(--primary-color, #3b82f6)',
color: 'white',
border: 'none',
borderRadius: 12,
fontWeight: 700,
fontSize: '1rem',
cursor: 'pointer',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)',
display: 'inline-flex',
alignItems: 'center',
gap: 10
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 8px 20px rgba(59, 130, 246, 0.4)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(59, 130, 246, 0.3)';
}}
>
<span>Načíst další zápasy</span>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div style={{ marginTop: 12, fontSize: '0.875rem', color: textSecondary, fontWeight: 600 }}>
Zobrazeno {Math.min(displayedMatchesCount[compIdx] || MATCHES_PER_PAGE, c.matches.length)} z {c.matches.length} zápasů
</div>
</div>
)}
</>
)}
</TabPanel>
))}