mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-05 03:02:56 +00:00
de day #74
This commit is contained in:
@@ -41,6 +41,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import AdminLayout from '../../layouts/AdminLayout';
|
||||
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';
|
||||
@@ -546,28 +547,24 @@ const MatchesAdminPage = () => {
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(false);
|
||||
const [showScrollHint, setShowScrollHint] = useState(true);
|
||||
const thBg = useColorModeValue('gray.50', 'gray.700');
|
||||
|
||||
// Drag-to-scroll state
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [startX, setStartX] = useState(0);
|
||||
const [scrollLeft, setScrollLeft] = useState(0);
|
||||
const [lastX, setLastX] = useState(0);
|
||||
const [lastTime, setLastTime] = useState(0);
|
||||
const lastXRef = useRef(0);
|
||||
const lastTimeRef = useRef(0);
|
||||
const velocityRef = useRef(0);
|
||||
const animationRef = useRef<number | null>(null);
|
||||
const scrollRaf = useRef<number | null>(null);
|
||||
|
||||
// Color modes for past/future matches
|
||||
const pastMatchBg = useColorModeValue('gray.100', 'gray.700');
|
||||
const futureMatchBg = useColorModeValue('white', 'gray.800');
|
||||
const pastMatchHoverBg = useColorModeValue('gray.200', 'gray.600');
|
||||
const futureMatchHoverBg = useColorModeValue('gray.50', 'gray.700');
|
||||
|
||||
const updateScrollShadow = () => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
setCanScrollLeft(el.scrollLeft > 0);
|
||||
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
||||
const left = el.scrollLeft > 0;
|
||||
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
||||
if (left !== canScrollLeft) setCanScrollLeft(left);
|
||||
if (right !== canScrollRight) setCanScrollRight(right);
|
||||
};
|
||||
|
||||
// Drag-to-scroll handlers
|
||||
@@ -581,8 +578,8 @@ const MatchesAdminPage = () => {
|
||||
setIsDragging(true);
|
||||
setStartX(e.pageX - scrollRef.current.offsetLeft);
|
||||
setScrollLeft(scrollRef.current.scrollLeft);
|
||||
setLastX(e.pageX);
|
||||
setLastTime(Date.now());
|
||||
lastXRef.current = e.pageX;
|
||||
lastTimeRef.current = Date.now();
|
||||
velocityRef.current = 0;
|
||||
scrollRef.current.style.cursor = 'grabbing';
|
||||
scrollRef.current.style.userSelect = 'none';
|
||||
@@ -632,13 +629,13 @@ const MatchesAdminPage = () => {
|
||||
|
||||
// Calculate velocity for momentum
|
||||
const now = Date.now();
|
||||
const timeDelta = now - lastTime;
|
||||
const timeDelta = now - lastTimeRef.current;
|
||||
if (timeDelta > 0) {
|
||||
const currentX = e.pageX;
|
||||
const distance = currentX - lastX;
|
||||
const distance = currentX - lastXRef.current;
|
||||
velocityRef.current = distance / timeDelta * 16; // Normalize to ~60fps
|
||||
setLastX(currentX);
|
||||
setLastTime(now);
|
||||
lastXRef.current = currentX;
|
||||
lastTimeRef.current = now;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -653,8 +650,8 @@ const MatchesAdminPage = () => {
|
||||
setIsDragging(true);
|
||||
setStartX(touch.pageX - scrollRef.current.offsetLeft);
|
||||
setScrollLeft(scrollRef.current.scrollLeft);
|
||||
setLastX(touch.pageX);
|
||||
setLastTime(Date.now());
|
||||
lastXRef.current = touch.pageX;
|
||||
lastTimeRef.current = Date.now();
|
||||
velocityRef.current = 0;
|
||||
if (scrollRef.current) scrollRef.current.style.scrollBehavior = 'auto';
|
||||
};
|
||||
@@ -667,13 +664,13 @@ const MatchesAdminPage = () => {
|
||||
scrollRef.current.scrollLeft = scrollLeft - walk;
|
||||
|
||||
const now = Date.now();
|
||||
const timeDelta = now - lastTime;
|
||||
const timeDelta = now - lastTimeRef.current;
|
||||
if (timeDelta > 0) {
|
||||
const currentX = touch.pageX;
|
||||
const distance = currentX - lastX;
|
||||
const distance = currentX - lastXRef.current;
|
||||
velocityRef.current = distance / timeDelta * 16;
|
||||
setLastX(currentX);
|
||||
setLastTime(now);
|
||||
lastXRef.current = currentX;
|
||||
lastTimeRef.current = now;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -734,6 +731,12 @@ const MatchesAdminPage = () => {
|
||||
const headerText = useColorModeValue('text.onPrimary', 'white');
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const edgeGradientLeft = useColorModeValue('linear(to-r, white, transparent)', 'linear(to-r, gray.800, transparent)');
|
||||
const edgeGradientRight = useColorModeValue('linear(to-l, white, transparent)', 'linear(to-l, gray.800, transparent)');
|
||||
const pastMatchBg = useColorModeValue('gray.100', 'gray.700');
|
||||
const futureMatchBg = useColorModeValue('white', 'gray.800');
|
||||
const pastMatchHoverBg = useColorModeValue('gray.200', 'gray.600');
|
||||
const futureMatchHoverBg = useColorModeValue('gray.50', 'gray.700');
|
||||
|
||||
return (
|
||||
<AdminLayout requireAdmin={false}>
|
||||
@@ -856,12 +859,24 @@ const MatchesAdminPage = () => {
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
onScroll={(e) => {
|
||||
updateScrollShadow();
|
||||
if ((e.currentTarget as HTMLDivElement).scrollLeft > 0 && showScrollHint) setShowScrollHint(false);
|
||||
if (scrollRaf.current == null) {
|
||||
scrollRaf.current = requestAnimationFrame(() => {
|
||||
const el = scrollRef.current;
|
||||
if (el) {
|
||||
updateScrollShadow();
|
||||
if (el.scrollLeft > 0 && showScrollHint) setShowScrollHint(false);
|
||||
}
|
||||
scrollRaf.current = null;
|
||||
});
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
scrollBehavior: 'smooth',
|
||||
transform: 'translateZ(0)',
|
||||
willChange: 'transform',
|
||||
overscrollBehaviorX: 'contain',
|
||||
touchAction: 'pan-x',
|
||||
'th, td': { whiteSpace: 'nowrap' },
|
||||
'::-webkit-scrollbar': { height: '14px' },
|
||||
'::-webkit-scrollbar-thumb': {
|
||||
@@ -885,13 +900,13 @@ const MatchesAdminPage = () => {
|
||||
{/* Gradient edges to indicate horizontal scroll */}
|
||||
{canScrollLeft && (
|
||||
<Box position="sticky" left={0} top={0} bottom={0} w="24px" pointerEvents="none"
|
||||
bgGradient={useColorModeValue('linear(to-r, white, transparent)', 'linear(to-r, gray.800, transparent)')}
|
||||
bgGradient={edgeGradientLeft}
|
||||
zIndex={1}
|
||||
/>
|
||||
)}
|
||||
{canScrollRight && (
|
||||
<Box position="sticky" right={0} top={0} bottom={0} w="24px" pointerEvents="none"
|
||||
bgGradient={useColorModeValue('linear(to-l, white, transparent)', 'linear(to-l, gray.800, transparent)')}
|
||||
bgGradient={edgeGradientRight}
|
||||
zIndex={1}
|
||||
/>
|
||||
)}
|
||||
@@ -945,6 +960,9 @@ const MatchesAdminPage = () => {
|
||||
alt={m.home || m.home_team || ''}
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
draggable={false}
|
||||
/>
|
||||
<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>
|
||||
@@ -962,6 +980,9 @@ const MatchesAdminPage = () => {
|
||||
alt={m.away || m.away_team || ''}
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
draggable={false}
|
||||
/>
|
||||
<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>
|
||||
@@ -1167,6 +1188,7 @@ const MatchesAdminPage = () => {
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
</AdminLayout>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user