mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
dev day #69
This commit is contained in:
@@ -38,14 +38,14 @@ import {
|
||||
Select
|
||||
} from '@chakra-ui/react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { TeamLogo } from '../../components/common/TeamLogo';
|
||||
import AdminLayout from '../../layouts/AdminLayout';
|
||||
import { putMatchOverride, patchMatchOverride, searchClubs, uploadImage, fetchLogoAsBlob, uploadToLogaSportcreative } from '../../services/adminMatches';
|
||||
import { putMatchOverride, patchMatchOverride, searchClubs, uploadImage, fetchLogoAsBlob, uploadToLogaSportcreative, fetchTeamLogoOverrides } from '../../services/adminMatches';
|
||||
import { getPublicSettings } from '../../services/settings';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { parse } from 'date-fns';
|
||||
import { assetUrl } from '../../utils/url';
|
||||
import { batchFetchLogosFromSportLogosAPI } from '../../utils/sportLogosAPI';
|
||||
|
||||
const MatchesAdminPage = () => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -63,6 +63,60 @@ const MatchesAdminPage = () => {
|
||||
notes: '',
|
||||
});
|
||||
|
||||
const { data: overrides = {} } = useQuery({
|
||||
queryKey: ['teamLogoOverrides'],
|
||||
queryFn: fetchTeamLogoOverrides,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const normalizeName = (s: string) => {
|
||||
let out = String(s || '');
|
||||
out = out
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase();
|
||||
out = out.replace(/[\u2012\u2013\u2014\u2015\u2212]/g, '-');
|
||||
const orgPhrases = [
|
||||
'fotbalovy klub',
|
||||
'sportovni klub',
|
||||
'telovychovna jednota',
|
||||
'skolni sportovni klub',
|
||||
'fotbal',
|
||||
'futsal',
|
||||
];
|
||||
for (const phrase of orgPhrases) {
|
||||
const re = new RegExp(`(^|\\b)${phrase}(\\b|$)`, 'g');
|
||||
out = out.replace(re, ' ');
|
||||
}
|
||||
out = out.replace(/\b(1\.)?\s*(sfc|afc|fc|fk|mfk|tj|sk|afk)\b\.?/g, ' ');
|
||||
out = out.replace(/[\.,!;:()\[\]{}]/g, ' ');
|
||||
out = out.replace(/\s+/g, ' ').trim();
|
||||
return out;
|
||||
};
|
||||
|
||||
const byName: Record<string, string> = (overrides as any)?.by_name || {};
|
||||
const byNameNormalized = useMemo(() => {
|
||||
const idx: Record<string, string> = {};
|
||||
for (const k of Object.keys(byName)) idx[normalizeName(k)] = byName[k];
|
||||
return idx;
|
||||
}, [byName]);
|
||||
|
||||
const [sportLogosMap, setSportLogosMap] = useState<Record<string, string>>({});
|
||||
|
||||
|
||||
const getLogo = (teamName?: string, teamId?: string, facrOriginal?: string) => {
|
||||
if (!teamName) return assetUrl('/dist/img/logo-club-empty.svg') as string;
|
||||
if (teamId && sportLogosMap[String(teamId)]) return sportLogosMap[String(teamId)];
|
||||
let overrideUrl = byName[teamName];
|
||||
if (!overrideUrl) overrideUrl = byNameNormalized[normalizeName(teamName)];
|
||||
if (overrideUrl) {
|
||||
if (overrideUrl.startsWith('/')) return assetUrl(overrideUrl) as string;
|
||||
return overrideUrl;
|
||||
}
|
||||
if (facrOriginal) return facrOriginal;
|
||||
return '/dist/img/logo-club-empty.svg';
|
||||
};
|
||||
|
||||
// External logo upload helpers/state
|
||||
const [homeExternalTeamId, setHomeExternalTeamId] = useState<string>('');
|
||||
const [awayExternalTeamId, setAwayExternalTeamId] = useState<string>('');
|
||||
@@ -137,7 +191,24 @@ const MatchesAdminPage = () => {
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!Array.isArray(matches) || matches.length === 0) return;
|
||||
const ids = new Set<string>();
|
||||
for (const m of matches as any[]) {
|
||||
if (m.home_id) ids.add(String(m.home_id));
|
||||
if (m.away_id) ids.add(String(m.away_id));
|
||||
}
|
||||
if (ids.size === 0) return;
|
||||
(async () => {
|
||||
try {
|
||||
const map = await batchFetchLogosFromSportLogosAPI(Array.from(ids));
|
||||
setSportLogosMap(map);
|
||||
} catch (e) {
|
||||
console.warn('Failed to batch fetch logos:', e);
|
||||
}
|
||||
})();
|
||||
}, [matches]);
|
||||
|
||||
// Filters
|
||||
const [teamFilter, setTeamFilter] = useState('');
|
||||
const [dateFrom, setDateFrom] = useState<string>(''); // YYYY-MM-DD
|
||||
@@ -870,12 +941,11 @@ const MatchesAdminPage = () => {
|
||||
</Td>
|
||||
<Td>
|
||||
<HStack spacing={2}>
|
||||
<TeamLogo
|
||||
teamId={m.home_id}
|
||||
teamName={m.home || m.home_team || ''}
|
||||
facrLogo={m.home_logo_url}
|
||||
size="custom"
|
||||
<Image
|
||||
src={getLogo(m.home || m.home_team || '', m.home_id, m.home_logo_url)}
|
||||
alt={m.home || m.home_team || ''}
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
<Text fontWeight={isPast ? 'normal' : 'medium'}>{m.home || m.home_team || ''}</Text>
|
||||
<Button size="xs" variant="outline" onClick={() => openEdit(m, 'home')} borderRadius="md" _hover={{ borderColor: 'brand.primary', color: 'brand.primary' }}>Tým</Button>
|
||||
@@ -888,12 +958,11 @@ const MatchesAdminPage = () => {
|
||||
</Td>
|
||||
<Td>
|
||||
<HStack spacing={2}>
|
||||
<TeamLogo
|
||||
teamId={m.away_id}
|
||||
teamName={m.away || m.away_team || ''}
|
||||
facrLogo={m.away_logo_url}
|
||||
size="custom"
|
||||
<Image
|
||||
src={getLogo(m.away || m.away_team || '', m.away_id, m.away_logo_url)}
|
||||
alt={m.away || m.away_team || ''}
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
<Text fontWeight={isPast ? 'normal' : 'medium'}>{m.away || m.away_team || ''}</Text>
|
||||
<Button size="xs" variant="outline" onClick={() => openEdit(m, 'away')} borderRadius="md" _hover={{ borderColor: 'brand.primary', color: 'brand.primary' }}>Tým</Button>
|
||||
|
||||
Reference in New Issue
Block a user