From fea71bed83844755468223441b35b658e82a9a4a Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 27 Jan 2026 01:22:00 +0300 Subject: [PATCH] Add files via upload --- src/components/admin/AnalyticsTab.tsx | 294 ++++++++++++++++++++++++++ src/components/admin/constants.ts | 1 + src/components/admin/index.ts | 1 + 3 files changed, 296 insertions(+) create mode 100644 src/components/admin/AnalyticsTab.tsx diff --git a/src/components/admin/AnalyticsTab.tsx b/src/components/admin/AnalyticsTab.tsx new file mode 100644 index 0000000..e63e9dd --- /dev/null +++ b/src/components/admin/AnalyticsTab.tsx @@ -0,0 +1,294 @@ +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { brandingApi } from '../../api/branding' +import { CheckIcon, CloseIcon } from './icons' + +export function AnalyticsTab() { + const { t } = useTranslation() + const queryClient = useQueryClient() + + // Editing states + const [editingYandex, setEditingYandex] = useState(false) + const [editingGoogleId, setEditingGoogleId] = useState(false) + const [editingGoogleLabel, setEditingGoogleLabel] = useState(false) + const [yandexValue, setYandexValue] = useState('') + const [googleIdValue, setGoogleIdValue] = useState('') + const [googleLabelValue, setGoogleLabelValue] = useState('') + const [error, setError] = useState(null) + + // Query + const { data: analytics } = useQuery({ + queryKey: ['analytics-counters'], + queryFn: brandingApi.getAnalyticsCounters, + }) + + // Mutation + const updateMutation = useMutation({ + mutationFn: brandingApi.updateAnalyticsCounters, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['analytics-counters'] }) + setError(null) + }, + onError: (err: unknown) => { + const detail = (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail + setError(detail || t('common.error')) + }, + }) + + const handleSaveYandex = () => { + updateMutation.mutate( + { yandex_metrika_id: yandexValue.trim() }, + { onSuccess: () => setEditingYandex(false) }, + ) + } + + const handleSaveGoogleId = () => { + updateMutation.mutate( + { google_ads_id: googleIdValue.trim() }, + { onSuccess: () => setEditingGoogleId(false) }, + ) + } + + const handleSaveGoogleLabel = () => { + updateMutation.mutate( + { google_ads_label: googleLabelValue.trim() }, + { onSuccess: () => setEditingGoogleLabel(false) }, + ) + } + + const yandexActive = Boolean(analytics?.yandex_metrika_id) + const googleActive = Boolean(analytics?.google_ads_id) + + return ( +
+ {/* Error message */} + {error && ( +
+ {error} +
+ )} + + {/* Yandex Metrika */} +
+
+
+
+ + + +
+

+ {t('admin.settings.yandexMetrika')} +

+
+ + + {yandexActive ? t('admin.settings.counterActive') : t('admin.settings.counterInactive')} + +
+

+ {t('admin.settings.yandexMetrikaDesc')} +

+ +
+ + {editingYandex ? ( +
+ setYandexValue(e.target.value.replace(/\D/g, ''))} + placeholder={t('admin.settings.yandexIdPlaceholder')} + className="flex-1 px-4 py-2.5 rounded-xl bg-dark-700 border border-dark-600 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 transition-colors" + autoFocus + /> + + +
+ ) : ( +
+ + {analytics?.yandex_metrika_id || t('admin.settings.notConfigured')} + + +
+ )} +

+ {t('admin.settings.yandexIdHint')} +

+
+
+ + {/* Google Ads */} +
+
+
+
+ + + +
+

+ {t('admin.settings.googleAds')} +

+
+ + + {googleActive ? t('admin.settings.counterActive') : t('admin.settings.counterInactive')} + +
+

+ {t('admin.settings.googleAdsDesc')} +

+ +
+ {/* Conversion ID */} +
+ + {editingGoogleId ? ( +
+ setGoogleIdValue(e.target.value)} + placeholder={t('admin.settings.googleIdPlaceholder')} + className="flex-1 px-4 py-2.5 rounded-xl bg-dark-700 border border-dark-600 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 transition-colors" + autoFocus + /> + + +
+ ) : ( +
+ + {analytics?.google_ads_id || t('admin.settings.notConfigured')} + + +
+ )} +

+ {t('admin.settings.googleIdHint')} +

+
+ + {/* Conversion Label */} +
+ + {editingGoogleLabel ? ( +
+ setGoogleLabelValue(e.target.value)} + placeholder={t('admin.settings.googleLabelPlaceholder')} + className="flex-1 px-4 py-2.5 rounded-xl bg-dark-700 border border-dark-600 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 transition-colors" + autoFocus + /> + + +
+ ) : ( +
+ + {analytics?.google_ads_label || t('admin.settings.notConfigured')} + + +
+ )} +

+ {t('admin.settings.googleLabelHint')} +

+
+
+
+ + {/* Info block */} +
+

+ {t('admin.settings.analyticsHint')} +

+
+
+ ) +} diff --git a/src/components/admin/constants.ts b/src/components/admin/constants.ts index f686581..b74b3ca 100644 --- a/src/components/admin/constants.ts +++ b/src/components/admin/constants.ts @@ -20,6 +20,7 @@ export const MENU_SECTIONS: MenuSection[] = [ { id: 'favorites', iconType: 'star' }, { id: 'branding', iconType: null }, { id: 'theme', iconType: null }, + { id: 'analytics', iconType: null }, ] }, { diff --git a/src/components/admin/index.ts b/src/components/admin/index.ts index 1d6357d..4be4e1f 100644 --- a/src/components/admin/index.ts +++ b/src/components/admin/index.ts @@ -3,6 +3,7 @@ export * from './icons' export * from './Toggle' export * from './SettingInput' export * from './SettingRow' +export * from './AnalyticsTab' export * from './BrandingTab' export * from './ThemeTab' export * from './FavoritesTab'