fix: resolve bugs and improve frontend performance

- Fix register not resetting isLoading on success (causing login page to hang)
- Fix leaderboard streaks 400 error by forcing all_time timeframe
- Reorder routes so static paths match before dynamic practice/:sheetId
- Lazy-load QuestMap + Three.js (saves ~350KB gzip on initial load)
- Move KaTeX CSS to lazy import (only loads on math pages)
- Remove 28 duplicate Google Font @import lines from component CSS
- Add font preconnect + single stylesheet link in index.html
- Replace 8 unsafe JSON.parse(localStorage) calls with Zustand selectors
- Add global ErrorBoundary to prevent full-app crashes
- Extract arcTheme utilities to break static import cycle with QuestMap
- Merge Three.js + Troika into single chunk to fix circular dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 08:41:13 +06:00
parent ebbad9bc9e
commit e4c86d473c
34 changed files with 259 additions and 206 deletions

View File

@ -17,6 +17,7 @@ import {
import { useNavigate } from "react-router-dom";
import { api } from "../../../utils/api";
import { type PracticeSheet } from "../../../types/sheet";
import { useAuthStore } from "../../../stores/authStore";
/* ─────────────────────────────────────────────
Ambient decoration
@ -415,7 +416,6 @@ const EmptyState = ({ query }: { query: string }) => (
Styles
───────────────────────────────────────────── */
const STYLES = `
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap');
:root { --content-max: 1100px; }
@ -700,6 +700,7 @@ type ViewMode = "standard" | "compact";
export const PracticeSheetList = () => {
const navigate = useNavigate();
const token = useAuthStore((state) => state.token);
const [sheets, setSheets] = useState<PracticeSheet[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@ -707,13 +708,8 @@ export const PracticeSheetList = () => {
const [viewMode, setViewMode] = useState<ViewMode>("compact");
useEffect(() => {
setLoading(true);
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) return;
const {
state: { token },
} = JSON.parse(authStorage);
if (!token) return;
setLoading(true);
api
.getPracticeSheets(token, 1, 10)
.then((data) => {