refactor: full codebase cleanup, dependency updates, and lint fixes

Phase 0: Remove ~920 lines of dead code (ThemeBentoPicker, PromoDiscountBadge,
AdminLayout, SettingsSidebar, MovingGradient, EmptyState, miniapp API, unused
types/functions/transitions/skeleton helpers). Fix API barrel file (add 20 missing exports).

Phase 1: Add ErrorBoundary (app/page/widget levels), centralize constants,
add axios timeout, fix staleTime:0 in React Query.

Phase 2: Consolidate hexToHsl, extract email validation, fix duplicate code.

Phase 3: Fix auth race condition (await checkAdminStatus), memoize useCurrency,
add WebSocket message validation.

Phase 4: Extract useBranding, useFeatureFlags, useScrollRestoration from AppShell.

Phase 5: Remove all eslint-disable react-hooks/exhaustive-deps (14 total),
simplify logger, remove deprecated hooks (useBackButton, useTelegramDnd,
useTelegramWebApp).

Dependencies: React 19, react-router 7, zustand 5, i18next 25, react-i18next 16,
eslint-plugin-react-refresh 0.5. Remove unused @lottiefiles/dotlottie-react.
Convert vite manualChunks to function-based approach for react-router v7 compat.

Lint: Fix logger.ts no-unused-expressions, fix react-refresh/only-export-components
in 6 Radix primitive files (const re-exports → direct re-exports), fix
WebSocketProvider exhaustive-deps. Result: 0 errors, 0 warnings.

Add CLAUDE.md to .gitignore.
This commit is contained in:
c0mrade
2026-02-06 01:35:12 +03:00
parent c5cad20a6f
commit 562ab7abf7
118 changed files with 1243 additions and 2746 deletions

View File

@@ -7,6 +7,7 @@ import {
safeRedirectToLogin,
} from '../utils/token';
import { useBlockingStore } from '../store/blocking';
import { API } from '../config/constants';
const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';
@@ -57,6 +58,7 @@ const getTelegramInitData = (): string | null => {
export const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: API.TIMEOUT_MS,
headers: {
'Content-Type': 'application/json',
},

View File

@@ -13,6 +13,7 @@ export interface ContestInfo {
export interface ContestGameData {
round_id: number;
game_type: string;
// Dynamic game data from backend - shape depends on game_type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
game_data: Record<string, any>;
instructions: string;

View File

@@ -10,3 +10,22 @@ export { promoApi } from './promo';
export { notificationsApi } from './notifications';
export { infoApi } from './info';
export { adminSettingsApi } from './adminSettings';
export { adminApi, statsApi } from './admin';
export { adminAppsApi } from './adminApps';
export { adminBroadcastsApi } from './adminBroadcasts';
export { adminEmailTemplatesApi } from './adminEmailTemplates';
export { adminPaymentMethodsApi } from './adminPaymentMethods';
export { adminPaymentsApi } from './adminPayments';
export { adminRemnawaveApi } from './adminRemnawave';
export { adminUsersApi } from './adminUsers';
export { banSystemApi } from './banSystem';
export { brandingApi } from './branding';
export { campaignsApi } from './campaigns';
export { currencyApi } from './currency';
export { promocodesApi } from './promocodes';
export { promoOffersApi } from './promoOffers';
export { serversApi } from './servers';
export { tariffsApi } from './tariffs';
export { themeColorsApi } from './themeColors';
export { ticketNotificationsApi } from './ticketNotifications';
export { wheelApi, adminWheelApi } from './wheel';

View File

@@ -1,44 +0,0 @@
import axios, { AxiosError } from 'axios';
export interface MiniappCreatePaymentPayload {
initData: string;
method: string;
amountKopeks?: number | null;
option?: string | null;
}
export interface MiniappCreatePaymentResponse {
payment_url: string;
amount_kopeks?: number;
extra?: Record<string, unknown>;
}
export const miniappApi = {
// Create payment inside Telegram Mini App (same flow as miniapp/index.html)
createPayment: async (
payload: MiniappCreatePaymentPayload,
): Promise<MiniappCreatePaymentResponse> => {
try {
const response = await axios.post<MiniappCreatePaymentResponse>(
'/miniapp/payments/create',
{
initData: payload.initData || '',
method: payload.method,
amountKopeks: payload.amountKopeks ?? null,
option: payload.option ?? null,
},
{
headers: { 'Content-Type': 'application/json' },
},
);
return response.data;
} catch (error) {
const axiosError = error as AxiosError<{ detail?: string; message?: string }>;
const message =
axiosError.response?.data?.detail ||
axiosError.response?.data?.message ||
'Failed to create payment';
throw new Error(message);
}
},
};

View File

@@ -9,6 +9,7 @@ export interface PromoOffer {
is_active: boolean;
is_claimed: boolean;
claimed_at: string | null;
// Dynamic extra data from backend
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extra_data: Record<string, any> | null;
}

View File

@@ -31,6 +31,7 @@ export interface PromoOffer {
claimed_at: string | null;
is_active: boolean;
effect_type: string;
// Dynamic extra data from backend - shape depends on effect_type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extra_data: Record<string, any>;
created_at: string;