This commit is contained in:
Tomas Dvorak
2026-01-26 08:13:18 +01:00
parent aa036b6550
commit dfc079288f
505 changed files with 95755 additions and 5712 deletions
Binary file not shown.
+103
View File
@@ -0,0 +1,103 @@
// Admin Sidebar Scroll Test - Run directly in browser console
// Copy and paste this entire script into the browser console on any admin page
(function adminScrollTest() {
console.clear();
console.log('=== ADMIN SIDEBAR SCROLL TEST ===\n');
// Find sidebar
const sidebar = document.querySelector('[data-sidebar="true"]');
if (!sidebar) {
console.error('❌ No sidebar found. Make sure you are on an admin page (/admin/*)');
return;
}
console.log('✅ Sidebar found');
console.log('📏 Current scroll:', sidebar.scrollTop);
console.log('📏 Scroll height:', sidebar.scrollHeight);
console.log('📏 Client height:', sidebar.clientHeight);
console.log('📏 Scrollable:', sidebar.scrollHeight > sidebar.clientHeight ? '✅ YES' : '❌ NO');
// Check if scrollable
if (sidebar.scrollHeight <= sidebar.clientHeight) {
console.log('\n⚠️ Sidebar is NOT scrollable!');
console.log(' To test scroll retention, the sidebar needs to be scrollable.');
console.log(' Try: 1) Reducing browser window height, or 2) Zoom in (Ctrl +)');
return;
}
console.log('\n🧪 Testing scroll retention...');
// Clear any existing scroll data
sessionStorage.removeItem('admin-sidebar-scroll');
sessionStorage.removeItem('admin-sidebar-scroll-emergency');
delete window.__adminSidebarScrollTarget;
// Scroll to a test position (middle of scrollable area)
const maxScroll = sidebar.scrollHeight - sidebar.clientHeight;
const testScroll = Math.floor(maxScroll * 0.5);
console.log('📍 Scrolling to test position:', testScroll, 'px');
sidebar.scrollTop = testScroll;
setTimeout(() => {
const actualScroll = sidebar.scrollTop;
console.log('📍 Current position after scroll:', actualScroll, 'px');
// Check if scroll was saved
const savedData = sessionStorage.getItem('admin-sidebar-scroll');
const emergencyData = sessionStorage.getItem('admin-sidebar-scroll-emergency');
const globalTarget = window.__adminSidebarScrollTarget;
console.log('\n💾 Storage check:');
console.log(' Main saved data:', savedData);
console.log(' Emergency saved:', emergencyData);
console.log(' Global target:', globalTarget);
console.log('\n🚀 NOW CLICK ANY ADMIN NAVIGATION LINK');
console.log(' The test will automatically detect navigation and check scroll preservation');
// Intercept navigation to test
let navDetected = false;
const originalPushState = history.pushState;
history.pushState = function(...args) {
if (!navDetected) {
navDetected = true;
const scrollBefore = sidebar.scrollTop;
console.log('\n🚦 Navigation detected!');
console.log(' Scroll position before navigation:', scrollBefore, 'px');
// Check scroll preservation at multiple intervals
const checkPoints = [100, 300, 600, 1000];
checkPoints.forEach((delay, index) => {
setTimeout(() => {
const scrollAfter = sidebar.scrollTop;
const difference = Math.abs(scrollAfter - scrollBefore);
console.log(` 📍 Check ${index + 1} (${delay}ms): ${scrollAfter}px (diff: ${difference}px)`);
if (delay === 1000) {
if (difference < 10) {
console.log('\n✅ SUCCESS: Scroll position preserved!');
console.log(' The admin sidebar scroll retention is working correctly.');
} else {
console.log('\n❌ FAILURE: Scroll position was reset');
console.log(' Difference:', difference, 'px');
console.log('\n🔍 Debugging info:');
console.log(' - Check browser console for any scroll-related errors');
console.log(' - Verify AdminScrollManager is loaded');
console.log(' - Check if CSS is interfering');
}
// Restore original history function
history.pushState = originalPushState;
}
}, delay);
});
}
return originalPushState.apply(this, args);
};
}, 500);
})();
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128px" height="128px"><path fill="#F7F7FB" d="M109.7,110H18.2c-5.1,0-9.2-4.1-9.2-9.2V22.6c0-2.5,2.1-4.6,4.6-4.6H96c2.5,0,4.6,2.1,4.6,4.6v23.1v55.2C100.6,105.9,104.7,110,109.7,110L109.7,110c5.1,0,9.2-4.1,9.2-9.2V50.2c0-2.5-2.1-4.6-4.6-4.6h-4.6"/><path fill="#DEDFE6" d="M23 31.9h63.1c1.7 0 3 1.3 3 3v16.9c0 1.7-1.3 3-3 3H23c-1.7 0-3-1.3-3-3V34.9C20 33.2 21.3 31.9 23 31.9zM109.7 110L109.7 110c5.1 0 9.2-4.1 9.2-9.2V50.2c0-2.5-2.1-4.6-4.6-4.6h-13.8v55.2C100.6 105.9 104.7 110 109.7 110z"/><path fill="#464C55" d="M109.7,113H18.2C11.5,113,6,107.5,6,100.8V22.6c0-4.2,3.4-7.6,7.6-7.6H96c4.2,0,7.6,3.4,7.6,7.6v78.3c0,3.4,2.8,6.2,6.2,6.2s6.2-2.8,6.2-6.2V50.2c0-0.9-0.7-1.6-1.6-1.6h-4.6c-1.7,0-3-1.3-3-3s1.3-3,3-3h4.6c4.2,0,7.6,3.4,7.6,7.6v50.6C121.9,107.6,116.4,113,109.7,113z M13.6,21c-0.9,0-1.6,0.7-1.6,1.6v78.3c0,3.4,2.8,6.2,6.2,6.2h81.1c-1.1-1.8-1.7-3.9-1.7-6.2V22.6c0-0.9-0.7-1.6-1.6-1.6L13.6,21z"/><path fill="#DEDFE6" d="M41.2 72.9H23c-1.7 0-3-1.3-3-3s1.3-3 3-3h18.2c1.7 0 3 1.3 3 3S42.9 72.9 41.2 72.9zM41.2 98.7H23c-1.7 0-3-1.3-3-3s1.3-3 3-3h18.2c1.7 0 3 1.3 3 3S42.9 98.7 41.2 98.7zM41.2 85.7H23c-1.7 0-3-1.3-3-3s1.3-3 3-3h18.2c1.7 0 3 1.3 3 3S42.9 85.7 41.2 85.7z"/><path fill="#464C55" d="M86,99.1H58c-1.7,0-3-1.3-3-3V68.6c0-1.7,1.3-3,3-3h28c1.7,0,3,1.3,3,3v27.5C89,97.7,87.7,99.1,86,99.1z M61,93.1h22V71.6H61V93.1z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

-24
View File
@@ -1,24 +0,0 @@
{
"files": {
"main.css": "/static/css/main.3ca1fc6e.css",
"main.js": "/static/js/main.8c4bb3a7.js",
"runtime.js": "/static/js/runtime.18a1ba68.js",
"static/js/453.54292a4b.chunk.js": "/static/js/453.54292a4b.chunk.js",
"static/css/290.1124e12e.css": "/static/css/290.1124e12e.css",
"static/js/290.0640644c.js": "/static/js/290.0640644c.js",
"index.html": "/index.html",
"main.3ca1fc6e.css.map": "/static/css/main.3ca1fc6e.css.map",
"main.8c4bb3a7.js.map": "/static/js/main.8c4bb3a7.js.map",
"runtime.18a1ba68.js.map": "/static/js/runtime.18a1ba68.js.map",
"453.54292a4b.chunk.js.map": "/static/js/453.54292a4b.chunk.js.map",
"290.1124e12e.css.map": "/static/css/290.1124e12e.css.map",
"290.0640644c.js.map": "/static/js/290.0640644c.js.map"
},
"entrypoints": [
"static/js/runtime.18a1ba68.js",
"static/css/290.1124e12e.css",
"static/js/290.0640644c.js",
"static/css/main.3ca1fc6e.css",
"static/js/main.8c4bb3a7.js"
]
}
@@ -0,0 +1,46 @@
// Quick debug to check if AdminScrollManager is working
// Run this in browser console
(function debugAdminScrollManager() {
console.clear();
console.log('=== DEBUGGING AdminScrollManager ===\n');
// Check if AdminScrollManager is loaded
console.log('🔍 Checking AdminScrollManager...');
// Look for AdminScrollManager logs
console.log('📋 Recent console logs:');
const logs = console.logs || [];
console.log(' - Look for "[AdminScrollManager] Component mounted" message');
console.log(' - Look for "[AdminScrollManager] Saved position" message');
console.log(' - Look for "[AdminScrollManager] Restoring position" message');
// Check if AdminLayout is being used
const adminLayout = document.querySelector('.admin-layout');
console.log('\n🏗️ AdminLayout check:');
console.log(' AdminLayout element found:', !!adminLayout);
// Check sidebar
const sidebar = document.querySelector('[data-sidebar="true"]');
console.log('\n📱 Sidebar check:');
console.log(' Sidebar element found:', !!sidebar);
console.log(' Sidebar current scroll:', sidebar?.scrollTop);
// Check if React DevTools shows AdminScrollManager
console.log('\n🔧 Manual test:');
console.log(' 1. Scroll the sidebar to any position');
console.log(' 2. Click an admin navigation link');
console.log(' 3. Watch console for AdminScrollManager messages');
console.log('\n💡 If you don\'t see AdminScrollManager logs, the component is not loading');
// Force a test
if (sidebar) {
console.log('\n🧪 Forcing a scroll test...');
sidebar.scrollTop = 300;
setTimeout(() => {
console.log(' Scroll set to:', sidebar.scrollTop);
console.log(' Now click a navigation link and watch for AdminScrollManager logs');
}, 100);
}
})();
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="144px" height="144px" baseProfile="basic"><path fill="#536dfe" d="M47.496,10.074c-0.508-0.249-0.727,0.226-1.025,0.467c-0.102,0.078-0.188,0.179-0.274,0.272 c-0.743,0.794-1.611,1.315-2.746,1.253c-1.658-0.093-3.074,0.428-4.326,1.696c-0.266-1.564-1.15-2.498-2.495-3.097 c-0.704-0.311-1.416-0.623-1.909-1.3c-0.344-0.482-0.438-1.019-0.61-1.548c-0.11-0.319-0.219-0.646-0.587-0.7 c-0.399-0.062-0.555,0.272-0.712,0.553c-0.626,1.144-0.868,2.405-0.845,3.681c0.055,2.871,1.267,5.159,3.676,6.785 c0.274,0.187,0.344,0.373,0.258,0.646c-0.164,0.56-0.36,1.105-0.532,1.665c-0.11,0.358-0.274,0.436-0.657,0.28 c-1.322-0.552-2.464-1.369-3.473-2.358c-1.713-1.657-3.262-3.486-5.194-4.918c-0.454-0.335-0.907-0.646-1.377-0.942 c-1.971-1.914,0.258-3.486,0.774-3.673c0.54-0.195,0.188-0.864-1.557-0.856c-1.744,0.008-3.34,0.591-5.374,1.369 c-0.297,0.117-0.61,0.202-0.931,0.272c-1.846-0.35-3.763-0.428-5.765-0.202c-3.77,0.42-6.782,2.202-8.996,5.245 c-2.66,3.657-3.285,7.812-2.519,12.147c0.806,4.568,3.137,8.349,6.719,11.306c3.716,3.066,7.994,4.568,12.876,4.28 c2.965-0.171,6.266-0.568,9.989-3.719c0.939,0.467,1.924,0.654,3.559,0.794c1.259,0.117,2.472-0.062,3.411-0.257 c1.471-0.311,1.369-1.673,0.837-1.922C34,36,33.471,35.441,33.471,35.441c2.19-2.591,5.491-5.284,6.782-14.007 c0.102-0.692,0.016-1.128,0-1.689c-0.008-0.342,0.07-0.475,0.462-0.514c1.079-0.125,2.128-0.42,3.09-0.949 c2.793-1.525,3.919-4.031,4.185-7.034C48.028,10.79,47.981,10.315,47.496,10.074z M23.161,37.107 c-4.177-3.284-6.203-4.365-7.04-4.319c-0.782,0.047-0.641,0.942-0.469,1.525c0.18,0.576,0.415,0.973,0.743,1.478 c0.227,0.335,0.383,0.833-0.227,1.206c-1.345,0.833-3.684-0.28-3.794-0.335c-2.722-1.603-4.998-3.72-6.602-6.614 c-1.549-2.786-2.448-5.774-2.597-8.964c-0.039-0.77,0.188-1.043,0.954-1.183c1.009-0.187,2.049-0.226,3.059-0.078 c4.263,0.623,7.893,2.529,10.936,5.548c1.737,1.72,3.051,3.774,4.404,5.782c1.439,2.132,2.988,4.163,4.959,5.828 c0.696,0.584,1.252,1.027,1.783,1.354C27.667,38.515,24.991,38.554,23.161,37.107L23.161,37.107z M25.164,24.228 c0-0.342,0.274-0.615,0.618-0.615c0.078,0,0.149,0.015,0.211,0.039c0.086,0.031,0.164,0.078,0.227,0.148 c0.11,0.109,0.172,0.265,0.172,0.428c0,0.342-0.274,0.615-0.618,0.615S25.164,24.571,25.164,24.228L25.164,24.228z M31.382,27.419 c-0.399,0.163-0.798,0.303-1.181,0.319c-0.595,0.031-1.244-0.21-1.596-0.506c-0.548-0.459-0.939-0.716-1.103-1.517 c-0.07-0.342-0.031-0.872,0.031-1.175c0.141-0.654-0.016-1.074-0.477-1.455c-0.376-0.311-0.853-0.397-1.377-0.397 c-0.196,0-0.375-0.086-0.508-0.156c-0.219-0.109-0.399-0.381-0.227-0.716c0.055-0.109,0.321-0.373,0.383-0.42 c0.712-0.405,1.533-0.272,2.292,0.031c0.704,0.288,1.236,0.817,2.003,1.564c0.782,0.903,0.923,1.152,1.369,1.829 c0.352,0.529,0.673,1.074,0.892,1.696C32.016,26.905,31.844,27.224,31.382,27.419L31.382,27.419z"/></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Player placeholder">
<defs>
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#EDF2F7"/>
<stop offset="1" stop-color="#E2E8F0"/>
</linearGradient>
</defs>
<rect width="256" height="256" rx="24" fill="url(#g)"/>
<circle cx="128" cy="92" r="44" fill="#CBD5E0"/>
<path d="M48 212c0-40 36-64 80-64s80 24 80 64v12H48v-12z" fill="#CBD5E0"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Grok</title><path d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815"></path></svg>

After

Width:  |  Height:  |  Size: 756 B

@@ -0,0 +1,4 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="#f7fafc"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#a0aec0" font-family="Arial, sans-serif" font-size="14">Club Logo</text>
</svg>

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 2.9 KiB

-1
View File
@@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Oficiální webové stránky fotbalového klubu - aktuality, zápasy, tabulky, hráči a fotogalerie"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Fotbal Club</title><script defer="defer" src="/static/js/runtime.18a1ba68.js"></script><script defer="defer" src="/static/js/290.0640644c.js"></script><script defer="defer" src="/static/js/main.8c4bb3a7.js"></script><link href="/static/css/290.1124e12e.css" rel="stylesheet"><link href="/static/css/main.3ca1fc6e.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
+36
View File
@@ -0,0 +1,36 @@
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Clean white background -->
<rect width="192" height="192" fill="#ffffff"/>
<!-- Enhanced Football/Soccer Ball Design -->
<g transform="translate(96,96)">
<!-- Outer circle with bold stroke -->
<circle cx="0" cy="0" r="48" fill="none" stroke="#000000" stroke-width="3"/>
<!-- Pentagon pattern - cleaner design -->
<g>
<!-- Center pentagon -->
<path d="M 0,-20 L 19,-6 L 12,16 L -12,16 L -19,-6 Z"
fill="#000000" stroke="#000000" stroke-width="2"/>
<!-- Surrounding hexagons with cleaner lines -->
<g stroke="#000000" stroke-width="2" fill="none">
<path d="M 0,-20 L 19,-6 L 19,-32 L 0,-45 L -19,-32 L -19,-6 Z"/>
<path d="M 19,-6 L 12,16 L 34,16 L 41,-6 L 19,-6 Z"/>
<path d="M 12,16 L -12,16 L -19,38 L 3,46 L 25,38 L 12,16 Z"/>
<path d="M -12,16 L -19,-6 L -41,-6 L -34,16 L -12,16 Z"/>
<path d="M -19,-6 L -19,-32 L -41,-32 L -34,-6 L -19,-6 Z"/>
</g>
</g>
<!-- Add subtle texture lines -->
<g stroke="#000000" stroke-width="0.5" opacity="0.3">
<circle cx="0" cy="0" r="35" fill="none"/>
<circle cx="0" cy="0" r="25" fill="none"/>
</g>
</g>
<!-- Enhanced text with better typography -->
<text x="96" y="165" font-family="Arial, sans-serif" font-size="16" font-weight="900"
text-anchor="middle" fill="#000000" letter-spacing="1">FC CLUB</text>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+1 -25
View File
@@ -1,25 +1 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
{"short_name": "Fotbal Club", "name": "Fotbal Club - Oficiální aplikace", "description": "Oficiální webové stránky fotbalového klubu - aktuality, zápasy, tabulky, hráči a fotogalerie", "icons": [{"src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon"}, {"src": "logo192.png", "type": "image/png", "sizes": "192x192"}, {"src": "logo512.png", "type": "image/png", "sizes": "512x512"}], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff"}
Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+42
View File
@@ -0,0 +1,42 @@
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Clean white background -->
<rect width="192" height="192" fill="#ffffff"/>
<!-- Enhanced Player Silhouette -->
<g transform="translate(96,96)">
<!-- Athletic pose silhouette -->
<g fill="#000000">
<!-- Head -->
<circle cx="0" cy="-28" r="14"/>
<!-- Body with athletic build -->
<path d="M -10,-14 L -7,8 L -4,30 L 4,30 L 7,8 L 10,-14 Z"/>
<!-- Defined arms in athletic position -->
<path d="M -10,-10 L -22,-8 L -24,2 L -18,6 L -10,2 M 10,-10 L 22,-8 L 24,2 L 18,6 L 10,2"/>
<!-- Strong legs in running stance -->
<path d="M -4,30 L -8,52 L -14,52 M 4,30 L 8,52 L 14,52"/>
<!-- Muscle definition lines -->
<g stroke="#ffffff" stroke-width="1" fill="none">
<line x1="-7" y1="-5" x2="-5" y2="5"/>
<line x1="7" y1="-5" x2="5" y2="5"/>
<line x1="-4" y1="15" x2="-4" y2="25"/>
<line x1="4" y1="15" x2="4" y2="25"/>
</g>
</g>
<!-- Motion lines -->
<g stroke="#000000" stroke-width="2" opacity="0.3">
<line x1="-30" y1="-20" x2="-20" y2="-18"/>
<line x1="-32" y1="-10" x2="-22" y2="-8"/>
<line x1="30" y1="-20" x2="20" y2="-18"/>
<line x1="32" y1="-10" x2="22" y2="-8"/>
</g>
</g>
<!-- Enhanced typography -->
<text x="96" y="165" font-family="Arial, sans-serif" font-size="14" font-weight="900"
text-anchor="middle" fill="#000000" letter-spacing="2">PLAYER</text>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+76 -8
View File
@@ -1,19 +1,22 @@
/* eslint-disable no-restricted-globals */
// Service Worker for PWA support and offline functionality
const CACHE_VERSION = 'v1.0.0';
const CACHE_VERSION = 'v1.0.2';
const CACHE_NAME = `fotbal-club-cache-${CACHE_VERSION}`;
// Rate limiting for background updates
const BACKGROUND_UPDATE_INTERVAL = 5000; // 5 seconds minimum between updates
const lastBackgroundUpdates = new Map();
// Assets to cache on install
const STATIC_ASSETS = [
'/',
'/index.html',
'/static/css/main.css',
'/static/js/main.js',
'/manifest.json',
'/favicon.ico',
'/logo192.png',
'/logo512.png',
'/robots.txt',
];
// API endpoints to cache
@@ -75,11 +78,27 @@ self.addEventListener('fetch', (event) => {
return;
}
// Only handle same-origin requests
if (url.origin !== self.location.origin) {
return;
}
// Skip admin routes
if (url.pathname.startsWith('/admin')) {
return;
}
// Skip background update requests to prevent infinite loops
if (request.headers.get('X-SW-Background-Update') === 'true') {
return;
}
// Handle SPA navigations with app shell fallback
if (request.mode === 'navigate') {
event.respondWith(handleNavigationRequest(request));
return;
}
// Handle API requests
if (url.pathname.startsWith('/api/')) {
event.respondWith(handleAPIRequest(request));
@@ -96,8 +115,16 @@ async function handleStaticRequest(request) {
// Try cache first
const cachedResponse = await caches.match(request);
if (cachedResponse) {
// Return cached response and update in background
fetchAndUpdateCache(request);
// For static assets with long cache headers, don't update in background
const url = new URL(request.url);
const isStaticAsset = url.pathname.match(/\.(png|jpg|jpeg|gif|svg|webp|ico|woff|woff2|ttf|eot)$/);
const hasLongCache = cachedResponse.headers.get('cache-control')?.includes('max-age=31536000');
// Only update in background if it's not a static asset with long cache
if (!isStaticAsset || !hasLongCache) {
fetchAndUpdateCache(request);
}
return cachedResponse;
}
@@ -114,8 +141,8 @@ async function handleStaticRequest(request) {
} catch (error) {
console.error('[SW] Fetch failed:', error);
// Return offline page if available
const cachedOffline = await caches.match('/offline.html');
// Return app shell (index.html) if available
const cachedOffline = await caches.match('/index.html');
if (cachedOffline) {
return cachedOffline;
}
@@ -129,6 +156,28 @@ async function handleStaticRequest(request) {
}
}
// Handle SPA navigation requests - Network First with index.html fallback
async function handleNavigationRequest(request) {
try {
const networkResponse = await fetch(request);
if (networkResponse && networkResponse.ok) {
return networkResponse;
}
} catch (error) {
console.error('[SW] Navigation fetch failed:', error);
}
// Fallback to cached index.html (app shell)
const cachedIndex = await caches.match('/index.html');
if (cachedIndex) {
return cachedIndex;
}
return new Response('Offline - Please check your connection', {
status: 503,
statusText: 'Service Unavailable',
headers: { 'Content-Type': 'text/plain' },
});
}
// Handle API requests - Network First strategy with cache fallback
async function handleAPIRequest(request) {
try {
@@ -166,9 +215,28 @@ async function handleAPIRequest(request) {
// Update cache in background
async function fetchAndUpdateCache(request) {
try {
const response = await fetch(request);
// Skip if this is already a background update request to prevent infinite loops
if (request.headers.get('X-SW-Background-Update') === 'true') {
return;
}
// Rate limit background updates to prevent excessive requests
const now = Date.now();
const lastUpdate = lastBackgroundUpdates.get(request.url);
if (lastUpdate && (now - lastUpdate) < BACKGROUND_UPDATE_INTERVAL) {
return;
}
lastBackgroundUpdates.set(request.url, now);
// Use fetch with cache: 'no-store' to bypass service worker and avoid infinite loops
const response = await fetch(request.url, {
cache: 'no-store',
headers: { 'X-SW-Background-Update': 'true' }
});
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
// Use the original request as key, but the new response
cache.put(request, response);
}
} catch (error) {
+49
View File
@@ -0,0 +1,49 @@
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Clean white background -->
<rect width="192" height="192" fill="#ffffff"/>
<!-- Enhanced Sponsor/Partner placeholder -->
<g transform="translate(96,96)">
<!-- Premium shield shape with better proportions -->
<g>
<!-- Shadow -->
<path d="M 2,-47 L 37,-27 L 37,12 L 2,47 L -33,12 L -33,-27 Z"
fill="#000000" opacity="0.1"/>
<!-- Main shield -->
<path d="M 0,-45 L 35,-25 L 35,10 L 0,45 L -35,10 L -35,-25 Z"
fill="#ffffff" stroke="#000000" stroke-width="3"/>
<!-- Inner decorative border -->
<path d="M 0,-38 L 28,-20 L 28,8 L 0,38 L -28,8 L -28,-20 Z"
fill="none" stroke="#000000" stroke-width="1.5"/>
<!-- Enhanced star with better geometry -->
<g transform="scale(1.2)">
<path d="M 0,-18 L 5,-5 L 18,-3 L 9,6 L 11,19 L 0,13 L -11,19 L -9,6 L -18,-3 L -5,-5 Z"
fill="#000000"/>
<!-- Inner star detail -->
<path d="M 0,-10 L 2,-3 L 9,-2 L 4,3 L 5,10 L 0,7 L -5,10 L -4,3 L -9,-2 L -2,-3 Z"
fill="#ffffff"/>
</g>
<!-- Ribbon with better design -->
<g>
<path d="M -12,38 L 0,48 L 12,38 L 8,45 L 0,50 L -8,45 Z" fill="#000000"/>
<text x="0" y="44" font-family="Arial, sans-serif" font-size="8" font-weight="bold"
text-anchor="middle" fill="#ffffff">PREMIUM</text>
</g>
</g>
<!-- Decorative corner elements -->
<g stroke="#000000" stroke-width="1.5" fill="none">
<circle cx="-25" cy="-35" r="3"/>
<circle cx="25" cy="-35" r="3"/>
<circle cx="-25" cy="30" r="3"/>
<circle cx="25" cy="30" r="3"/>
</g>
</g>
<!-- Enhanced typography -->
<text x="96" y="165" font-family="Arial, sans-serif" font-size="14" font-weight="900"
text-anchor="middle" fill="#000000" letter-spacing="2">SPONSOR</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,121 +0,0 @@
/*!
* @kurkle/color v0.3.4
* https://github.com/kurkle/color#readme
* (c) 2024 Jukka Kurkela
* Released under the MIT License
*/
/*!
* Chart.js v4.4.1
* https://www.chartjs.org
* (c) 2023 Chart.js Contributors
* Released under the MIT License
*/
/*!
* Quill Editor v1.3.7
* https://quilljs.com/
* Copyright (c) 2014, Jason Chen
* Copyright (c) 2013, salesforce.com
*/
/*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license lucide-react v0.379.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
/**
* @remix-run/router v1.23.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/**
* React Router DOM v6.30.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/**
* React Router v6.30.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
"use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[453],{6453:(e,t,n)=>{n.r(t),n.d(t,{getCLS:()=>y,getFCP:()=>g,getFID:()=>C,getLCP:()=>P,getTTFB:()=>D});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver(function(e){return e.getEntries().map(t)});return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",function(t){t.persisted&&e(t)},!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,d=function(){return"hidden"===document.visibilityState?0:1/0},p=function(){f(function(e){var t=e.timeStamp;v=t},!0)},l=function(){return v<0&&(v=d(),p(),s(function(){setTimeout(function(){v=d(),p()},0)})),{get firstHiddenTime(){return v}}},g=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime<i.firstHiddenTime&&(r.value=e.startTime,r.entries.push(e),n(!0)))},o=window.performance&&performance.getEntriesByName&&performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",a);(o||f)&&(n=m(e,r,t),o&&a(o),s(function(i){r=u("FCP"),n=m(e,r,t),requestAnimationFrame(function(){requestAnimationFrame(function(){r.value=performance.now()-i.timeStamp,n(!0)})})}))},h=!1,T=-1,y=function(e,t){h||(g(function(e){T=e.value}),h=!0);var n,i=function(t){T>-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},d=c("layout-shift",v);d&&(n=m(i,r,t),f(function(){d.takeRecords().map(v),n(!0)}),s(function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)}))},E={passive:!0,capture:!0},w=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,F(removeEventListener),S())},S=function(){if(r>=0&&r<a-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+r};o.forEach(function(t){t(e)}),o=[]}},b=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,E),removeEventListener("pointercancel",i,E)};addEventListener("pointerup",n,E),addEventListener("pointercancel",i,E)}(t,e):L(t,e)}},F=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach(function(t){return e(t,b,E)})},C=function(e,t){var n,a=l(),v=u("FID"),d=function(e){e.startTime<a.firstHiddenTime&&(v.value=e.processingStart-e.startTime,v.entries.push(e),n(!0))},p=c("first-input",d);n=m(e,v,t),p&&f(function(){p.takeRecords().map(d),p.disconnect()},!0),p&&s(function(){var a;v=u("FID"),n=m(e,v,t),o=[],r=-1,i=null,F(addEventListener),a=d,o.push(a),S()})},k={},P=function(e,t){var n,i=l(),r=u("LCP"),a=function(e){var t=e.startTime;t<i.firstHiddenTime&&(r.value=t,r.entries.push(e),n())},o=c("largest-contentful-paint",a);if(o){n=m(e,r,t);var v=function(){k[r.id]||(o.takeRecords().map(a),o.disconnect(),k[r.id]=!0,n(!0))};["keydown","click"].forEach(function(e){addEventListener(e,v,{once:!0,capture:!0})}),f(v,!0),s(function(i){r=u("LCP"),n=m(e,r,t),requestAnimationFrame(function(){requestAnimationFrame(function(){r.value=performance.now()-i.timeStamp,k[r.id]=!0,n(!0)})})})}},D=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0||n.value>performance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",function(){return setTimeout(t,0)})}}}]);
//# sourceMappingURL=453.54292a4b.chunk.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
(()=>{"use strict";var e={},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var a=t[o]={id:o,loaded:!1,exports:{}};return e[o].call(a.exports,a,a.exports,r),a.loaded=!0,a.exports}r.m=e,(()=>{var e=[];r.O=(t,o,n,a)=>{if(!o){var i=1/0;for(d=0;d<e.length;d++){o=e[d][0],n=e[d][1],a=e[d][2];for(var l=!0,u=0;u<o.length;u++)(!1&a||i>=a)&&Object.keys(r.O).every(e=>r.O[e](o[u]))?o.splice(u--,1):(l=!1,a<i&&(i=a));if(l){e.splice(d--,1);var f=n();void 0!==f&&(t=f)}}return t}a=a||0;for(var d=e.length;d>0&&e[d-1][2]>a;d--)e[d]=e[d-1];e[d]=[o,n,a]}})(),r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;r.t=function(o,n){if(1&n&&(o=this(o)),8&n)return o;if("object"===typeof o&&o){if(4&n&&o.__esModule)return o;if(16&n&&"function"===typeof o.then)return o}var a=Object.create(null);r.r(a);var i={};e=e||[null,t({}),t([]),t(t)];for(var l=2&n&&o;("object"==typeof l||"function"==typeof l)&&!~e.indexOf(l);l=t(l))Object.getOwnPropertyNames(l).forEach(e=>i[e]=()=>o[e]);return i.default=()=>o,r.d(a,i),a}})(),r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>"static/js/"+e+".54292a4b.chunk.js",r.miniCssF=e=>{},r.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={},t="frontend:";r.l=(o,n,a,i)=>{if(e[o])e[o].push(n);else{var l,u;if(void 0!==a)for(var f=document.getElementsByTagName("script"),d=0;d<f.length;d++){var c=f[d];if(c.getAttribute("src")==o||c.getAttribute("data-webpack")==t+a){l=c;break}}l||(u=!0,(l=document.createElement("script")).charset="utf-8",l.timeout=120,r.nc&&l.setAttribute("nonce",r.nc),l.setAttribute("data-webpack",t+a),l.src=o),e[o]=[n];var s=(t,r)=>{l.onerror=l.onload=null,clearTimeout(p);var n=e[o];if(delete e[o],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(r)),t)return t(r)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=s.bind(null,l.onerror),l.onload=s.bind(null,l.onload),u&&document.head.appendChild(l)}}})(),r.r=e=>{"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),r.p="/",(()=>{var e={121:0};r.f.j=(t,o)=>{var n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else if(121!=t){var a=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=a);var i=r.p+r.u(t),l=new Error;r.l(i,o=>{if(r.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var a=o&&("load"===o.type?"missing":o.type),i=o&&o.target&&o.target.src;l.message="Loading chunk "+t+" failed.\n("+a+": "+i+")",l.name="ChunkLoadError",l.type=a,l.request=i,n[1](l)}},"chunk-"+t,t)}else e[t]=0},r.O.j=t=>0===e[t];var t=(t,o)=>{var n,a,i=o[0],l=o[1],u=o[2],f=0;if(i.some(t=>0!==e[t])){for(n in l)r.o(l,n)&&(r.m[n]=l[n]);if(u)var d=u(r)}for(t&&t(o);f<i.length;f++)a=i[f],r.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return r.O(d)},o=self.webpackChunkfrontend=self.webpackChunkfrontend||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})(),r.nc=void 0})();
//# sourceMappingURL=runtime.18a1ba68.js.map
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB