diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9870661..29de04a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,11 @@ on: push: - branches: [main, develop] + branches: [main, dev] pull_request: - branches: [main, develop] + branches: [main, dev] jobs: - lint-and-typecheck: + lint-and-build: runs-on: ubuntu-latest steps: @@ -13,29 +13,12 @@ jobs: run: npm ci - name: Run ESLint - run: npm run lint -- --max-warnings 50 + run: npm run lint - - name: Type check - run: npm run type-check + - name: Run TypeScript check + run: npx tsc --noEmit - build: - runs-on: ubuntu-latest - needs: lint-and-typecheck - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build application + - name: Build run: npm run build env: VITE_API_URL: /api @@ -48,4 +31,4 @@ jobs: with: name: dist path: dist/ - retention-days: 7 \ No newline at end of file + retention-days: 7 diff --git a/src/App.tsx b/src/App.tsx index c99b572..48f2c17 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,8 @@ -import { Routes, Route, Navigate } from 'react-router-dom' +import { Routes, Route, Navigate, useLocation } from 'react-router-dom' import { useAuthStore } from './store/auth' import Layout from './components/layout/Layout' import PageLoader from './components/common/PageLoader' +import { saveReturnUrl } from './utils/token' import Login from './pages/Login' import TelegramCallback from './pages/TelegramCallback' import TelegramRedirect from './pages/TelegramRedirect' @@ -36,13 +37,16 @@ import AdminRemnawave from './pages/AdminRemnawave' function ProtectedRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, isLoading } = useAuthStore() + const location = useLocation() if (isLoading) { return } if (!isAuthenticated) { - return + // Сохраняем текущий URL для возврата после авторизации + saveReturnUrl() + return } return {children} @@ -50,13 +54,16 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) { function AdminRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, isLoading, isAdmin } = useAuthStore() + const location = useLocation() if (isLoading) { return } if (!isAuthenticated) { - return + // Сохраняем текущий URL для возврата после авторизации + saveReturnUrl() + return } if (!isAdmin) { diff --git a/src/pages/AdminApps.tsx b/src/pages/AdminApps.tsx index 2ff0b12..0a25598 100644 --- a/src/pages/AdminApps.tsx +++ b/src/pages/AdminApps.tsx @@ -1,6 +1,7 @@ import { useState, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { adminAppsApi, AppDefinition, @@ -14,6 +15,12 @@ import { } from '../api/adminApps' // Icons +const BackIcon = () => ( + + + +) + const AppsIcon = () => ( @@ -640,7 +647,12 @@ export default function AdminApps() {
- + + +

{t('admin.apps.title')}

diff --git a/src/pages/AdminBroadcasts.tsx b/src/pages/AdminBroadcasts.tsx index 1451d5f..9618bd6 100644 --- a/src/pages/AdminBroadcasts.tsx +++ b/src/pages/AdminBroadcasts.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useMemo } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { adminBroadcastsApi, Broadcast, @@ -10,6 +11,12 @@ import { } from '../api/adminBroadcasts' // Icons +const BackIcon = () => ( + + + +) + const BroadcastIcon = () => ( @@ -618,9 +625,12 @@ export default function AdminBroadcasts() { {/* Header */}
-
- -
+ + +

{t('admin.broadcasts.title')}

{t('admin.broadcasts.subtitle')}

diff --git a/src/pages/AdminCampaigns.tsx b/src/pages/AdminCampaigns.tsx index d3ab946..a7a7fe6 100644 --- a/src/pages/AdminCampaigns.tsx +++ b/src/pages/AdminCampaigns.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { Link } from 'react-router-dom' import { campaignsApi, CampaignListItem, @@ -14,9 +15,9 @@ import { } from '../api/campaigns' // Icons -const CampaignIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -741,9 +742,12 @@ export default function AdminCampaigns() { {/* Header */}
-
- -
+ + +

Рекламные кампании

Управление рекламными ссылками

diff --git a/src/pages/AdminDashboard.tsx b/src/pages/AdminDashboard.tsx index 97b0956..6a8e6c9 100644 --- a/src/pages/AdminDashboard.tsx +++ b/src/pages/AdminDashboard.tsx @@ -1,12 +1,13 @@ import { useState, useEffect } from 'react' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { statsApi, type DashboardStats, type NodeStatus } from '../api/admin' import { useCurrency } from '../hooks/useCurrency' // Icons -const ChartIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -292,9 +293,12 @@ export default function AdminDashboard() { {/* Header */}
-
- -
+ + +

{t('adminDashboard.title')}

{t('adminDashboard.subtitle')}

diff --git a/src/pages/AdminPromoOffers.tsx b/src/pages/AdminPromoOffers.tsx index 3e265b4..fe00d0f 100644 --- a/src/pages/AdminPromoOffers.tsx +++ b/src/pages/AdminPromoOffers.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { Link } from 'react-router-dom' import { promoOffersApi, PromoOfferTemplate, @@ -12,9 +13,9 @@ import { } from '../api/promoOffers' // Icons -const GiftIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -576,9 +577,12 @@ export default function AdminPromoOffers() { {/* Header */}
-
- -
+ + +

Промопредложения

Шаблоны, рассылка и логи предложений

diff --git a/src/pages/AdminPromocodes.tsx b/src/pages/AdminPromocodes.tsx index 12575cd..ab7122d 100644 --- a/src/pages/AdminPromocodes.tsx +++ b/src/pages/AdminPromocodes.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { Link } from 'react-router-dom' import { promocodesApi, PromoCode, @@ -13,9 +14,9 @@ import { } from '../api/promocodes' // Icons -const TicketIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -800,9 +801,12 @@ export default function AdminPromocodes() { {/* Header */}
-
- -
+ + +

Промокоды

Управление промокодами и группами скидок

diff --git a/src/pages/AdminServers.tsx b/src/pages/AdminServers.tsx index 3709ffa..c715d0d 100644 --- a/src/pages/AdminServers.tsx +++ b/src/pages/AdminServers.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { serversApi, ServerListItem, @@ -9,9 +10,9 @@ import { } from '../api/servers' // Icons -const ServerIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -322,9 +323,12 @@ export default function AdminServers() { {/* Header */}
-
- -
+ + +

{t('admin.servers.title')}

{t('admin.servers.subtitle')}

diff --git a/src/pages/AdminSettings.tsx b/src/pages/AdminSettings.tsx index 7dff564..e8cfa56 100644 --- a/src/pages/AdminSettings.tsx +++ b/src/pages/AdminSettings.tsx @@ -1,6 +1,7 @@ import { useState, useMemo, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { adminSettingsApi, SettingDefinition, SettingCategorySummary } from '../api/adminSettings' import { brandingApi } from '../api/branding' import { themeColorsApi } from '../api/themeColors' @@ -10,6 +11,12 @@ import { applyThemeColors } from '../hooks/useThemeColors' import { updateEnabledThemesCache } from '../hooks/useTheme' // Icons +const BackIcon = () => ( + + + +) + const CogIcon = () => ( @@ -1033,7 +1040,12 @@ export default function AdminSettings() {
{/* Header */}
- + + +

{t('admin.settings.title')}

diff --git a/src/pages/AdminTariffs.tsx b/src/pages/AdminTariffs.tsx index 8994e94..7c7d514 100644 --- a/src/pages/AdminTariffs.tsx +++ b/src/pages/AdminTariffs.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { tariffsApi, TariffListItem, @@ -13,9 +14,9 @@ import { } from '../api/tariffs' // Icons -const TariffIcon = () => ( - - +const BackIcon = () => ( + + ) @@ -1187,9 +1188,12 @@ export default function AdminTariffs() { {/* Header */}

-
- -
+ + +

{t('admin.tariffs.title')}

{t('admin.tariffs.subtitle')}

diff --git a/src/pages/AdminTickets.tsx b/src/pages/AdminTickets.tsx index 83f2d9c..9d6b622 100644 --- a/src/pages/AdminTickets.tsx +++ b/src/pages/AdminTickets.tsx @@ -1,9 +1,16 @@ import { useState, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { adminApi, AdminTicket, AdminTicketDetail, AdminTicketMessage } from '../api/admin' import { ticketsApi } from '../api/tickets' +const BackIcon = () => ( + + + +) + function AdminMessageMedia({ message, t }: { message: AdminTicketMessage; t: (key: string) => string }) { const [imageLoaded, setImageLoaded] = useState(false) const [imageError, setImageError] = useState(false) @@ -175,7 +182,15 @@ export default function AdminTickets() { return (
-

{t('admin.tickets.title')}

+
+ + + +

{t('admin.tickets.title')}

+