mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
135 lines
4.2 KiB
TypeScript
135 lines
4.2 KiB
TypeScript
import { assetUrl } from '../utils/url';
|
|
import { API_URL } from './api';
|
|
|
|
export interface RelatedClub {
|
|
id: string;
|
|
name: string;
|
|
logo_url?: string;
|
|
competition?: string;
|
|
last_played_iso?: string;
|
|
matches_played?: number;
|
|
}
|
|
|
|
const normalize = (value: string) =>
|
|
String(value || '')
|
|
.normalize('NFD')
|
|
.replace(/[\u0300-\u036f]/g, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim()
|
|
.toLowerCase();
|
|
|
|
const resolveBackendUrl = (path: string): string => {
|
|
try {
|
|
if (/^https?:\/\//i.test(path)) return path;
|
|
if (path.startsWith('/cache') || path.startsWith('/uploads') || path.startsWith('/api/')) {
|
|
const origin = new URL(API_URL, typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000').origin;
|
|
return new URL(path, origin).toString();
|
|
}
|
|
return path;
|
|
} catch {
|
|
return path;
|
|
}
|
|
};
|
|
|
|
const fetchJSON = async <T>(path: string): Promise<T | null> => {
|
|
try {
|
|
const res = await fetch(resolveBackendUrl(path), { cache: 'no-cache' });
|
|
if (!res.ok) return null;
|
|
return (await res.json()) as T;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const parseDateTime = (dt?: string): Date | null => {
|
|
if (!dt) return null;
|
|
const [datePart, timePart = '00:00'] = String(dt).split(' ');
|
|
const [day, month, year] = (datePart || '').split('.');
|
|
if (!day || !month || !year) return null;
|
|
const iso = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}T${timePart.slice(0, 5)}:00`;
|
|
const d = new Date(iso);
|
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
};
|
|
|
|
let cachePromise: Promise<RelatedClub[]> | null = null;
|
|
|
|
const buildRelatedClubs = (clubInfo: any): RelatedClub[] => {
|
|
if (!clubInfo) return [];
|
|
const ourNameNorm = normalize(clubInfo.name || '');
|
|
const competitions = Array.isArray(clubInfo.competitions) ? clubInfo.competitions : [];
|
|
const map = new Map<string, RelatedClub & { lastPlayedDate?: Date }>();
|
|
|
|
const considerTeam = (team: {
|
|
name?: string;
|
|
id?: string;
|
|
logo?: string;
|
|
competition?: string;
|
|
occurredAt?: Date | null;
|
|
}) => {
|
|
const teamName = team.name?.trim();
|
|
if (!teamName) return;
|
|
const normName = normalize(teamName);
|
|
if (!normName || normName === ourNameNorm) return;
|
|
|
|
const existing = map.get(normName);
|
|
const matchesPlayed = (existing?.matches_played ?? 0) + 1;
|
|
const lastPlayedDate = team.occurredAt && (!existing?.lastPlayedDate || team.occurredAt > existing.lastPlayedDate)
|
|
? team.occurredAt
|
|
: existing?.lastPlayedDate;
|
|
const logoUrl = team.logo || existing?.logo_url;
|
|
const competition = team.competition || existing?.competition;
|
|
|
|
map.set(normName, {
|
|
id: team.id || existing?.id || normName,
|
|
name: teamName,
|
|
logo_url: logoUrl ? assetUrl(logoUrl) || logoUrl : existing?.logo_url,
|
|
competition,
|
|
matches_played: matchesPlayed,
|
|
last_played_iso: lastPlayedDate ? lastPlayedDate.toISOString() : existing?.last_played_iso,
|
|
lastPlayedDate,
|
|
});
|
|
};
|
|
|
|
competitions.forEach((comp: any) => {
|
|
const matches = Array.isArray(comp?.matches) ? comp.matches : [];
|
|
matches.forEach((match: any) => {
|
|
const occurredAt = parseDateTime(match?.date_time);
|
|
considerTeam({
|
|
name: match?.home,
|
|
id: match?.home_id || match?.homeId,
|
|
logo: match?.home_logo_url,
|
|
competition: comp?.name,
|
|
occurredAt,
|
|
});
|
|
considerTeam({
|
|
name: match?.away,
|
|
id: match?.away_id || match?.awayId,
|
|
logo: match?.away_logo_url,
|
|
competition: comp?.name,
|
|
occurredAt,
|
|
});
|
|
});
|
|
});
|
|
|
|
return Array.from(map.values())
|
|
.map(({ lastPlayedDate, ...rest }) => rest)
|
|
.sort((a, b) => {
|
|
const ad = a.last_played_iso ? new Date(a.last_played_iso).getTime() : 0;
|
|
const bd = b.last_played_iso ? new Date(b.last_played_iso).getTime() : 0;
|
|
return bd - ad;
|
|
});
|
|
};
|
|
|
|
const fetchRelatedClubs = async (): Promise<RelatedClub[]> => {
|
|
const clubInfo = await fetchJSON<any>('/cache/prefetch/facr_club_info.json');
|
|
if (!clubInfo) return [];
|
|
return buildRelatedClubs(clubInfo);
|
|
};
|
|
|
|
export const getRelatedClubs = async (): Promise<RelatedClub[]> => {
|
|
if (!cachePromise) {
|
|
cachePromise = fetchRelatedClubs().catch(() => []);
|
|
}
|
|
return cachePromise;
|
|
};
|