Files
bedolaga-cabinet/src/pages/AdminApps.tsx
c0mrade 562ab7abf7 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.
2026-02-06 16:55:55 +03:00

120 lines
4.3 KiB
TypeScript

import { useNavigate } from 'react-router';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { adminAppsApi } from '../api/adminApps';
import { usePlatform } from '../platform/hooks/usePlatform';
export default function AdminApps() {
const { t } = useTranslation();
const navigate = useNavigate();
const queryClient = useQueryClient();
const { capabilities } = usePlatform();
// RemnaWave status
const { data: status } = useQuery({
queryKey: ['remnawave-status'],
queryFn: adminAppsApi.getRemnaWaveStatus,
staleTime: 60000,
});
// Available configs
const { data: configs, isLoading: isLoadingConfigs } = useQuery({
queryKey: ['remnawave-configs-list'],
queryFn: adminAppsApi.listRemnaWaveConfigs,
staleTime: 30000,
});
// Set UUID mutation
const setUuidMutation = useMutation({
mutationFn: adminAppsApi.setRemnaWaveUuid,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['remnawave-status'] });
queryClient.invalidateQueries({ queryKey: ['remnawave-config'] });
queryClient.invalidateQueries({ queryKey: ['appConfig'] });
},
});
const currentUuid = status?.config_uuid || '';
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center gap-3">
{!capabilities.hasBackButton && (
<button
onClick={() => navigate('/admin')}
className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600"
>
<svg
className="h-5 w-5 text-dark-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
</svg>
</button>
)}
<h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">{t('admin.apps.title')}</h1>
</div>
{/* Status card */}
<div className="card p-4">
<div className="flex items-center gap-3">
<div
className={`h-3 w-3 rounded-full ${status?.enabled ? 'bg-success-400' : 'bg-dark-600'}`}
/>
<span className="text-sm font-medium text-dark-200">
{status?.enabled
? t('admin.apps.remnaWaveConnected', 'RemnaWave connected')
: t('admin.apps.remnaWaveDisconnected', 'RemnaWave not connected')}
</span>
</div>
{status?.config_uuid && (
<div className="mt-2 truncate font-mono text-xs text-dark-500">
UUID: {status.config_uuid}
</div>
)}
</div>
{/* Available configs */}
<div className="space-y-3">
<h2 className="text-sm font-semibold text-dark-300">
{t('admin.apps.availableConfigs', 'Available configs')}
</h2>
{isLoadingConfigs ? (
<div className="flex items-center justify-center py-8">
<div className="h-6 w-6 animate-spin rounded-full border-2 border-accent-500 border-t-transparent" />
</div>
) : configs && configs.length > 0 ? (
<div className="space-y-2">
{configs.map((config) => (
<button
key={config.uuid}
onClick={() => {
if (config.uuid !== currentUuid) {
setUuidMutation.mutate(config.uuid);
}
}}
className={`w-full rounded-lg border p-4 text-left transition-colors ${
currentUuid === config.uuid
? 'border-accent-500 bg-accent-500/10'
: 'border-dark-700 bg-dark-800/50 hover:border-dark-600'
}`}
>
<div className="font-medium text-dark-100">{config.name}</div>
<div className="mt-1 font-mono text-xs text-dark-500">{config.uuid}</div>
</button>
))}
</div>
) : (
<div className="card py-8 text-center text-sm text-dark-500">
{t('admin.apps.noConfigs', 'No configs available')}
</div>
)}
</div>
</div>
);
}