Add TG banner
This commit is contained in:
80
frontend/src/components/TelegramBotBanner.tsx
Normal file
80
frontend/src/components/TelegramBotBanner.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { useAuthStore } from '@/store/auth'
|
||||||
|
import { NeonButton } from '@/components/ui'
|
||||||
|
import { Bot, Bell, X } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'telegram_banner_dismissed'
|
||||||
|
|
||||||
|
export function TelegramBotBanner() {
|
||||||
|
const user = useAuthStore((state) => state.user)
|
||||||
|
const [dismissed, setDismissed] = useState(() => {
|
||||||
|
return sessionStorage.getItem(STORAGE_KEY) === 'true'
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleDismiss = () => {
|
||||||
|
sessionStorage.setItem(STORAGE_KEY, 'true')
|
||||||
|
setDismissed(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't show if user already has Telegram linked or dismissed
|
||||||
|
if (user?.telegram_id || dismissed) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative rounded-2xl overflow-hidden">
|
||||||
|
{/* Background image */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-cover bg-center"
|
||||||
|
style={{ backgroundImage: 'url(/telegram_bot_banner.png)' }}
|
||||||
|
/>
|
||||||
|
{/* Overlay */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-dark-900/95 via-dark-900/80 to-dark-900/60" />
|
||||||
|
|
||||||
|
{/* Close button */}
|
||||||
|
<button
|
||||||
|
onClick={handleDismiss}
|
||||||
|
className="absolute top-3 right-3 p-1.5 text-white bg-dark-700/70 hover:bg-dark-600 rounded-lg transition-colors z-10"
|
||||||
|
title="Скрыть"
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="relative p-6 pr-12 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 rounded-xl bg-[#2AABEE]/20 border border-[#2AABEE]/30 flex items-center justify-center flex-shrink-0">
|
||||||
|
<Bot className="w-6 h-6 text-[#2AABEE]" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-1">
|
||||||
|
Привяжите Telegram-бота
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 text-sm max-w-md">
|
||||||
|
Получайте уведомления о событиях марафона, новых заданиях и результатах прямо в Telegram
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-4 mt-2 text-xs text-gray-500">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Bell className="w-3 h-3" />
|
||||||
|
Мгновенные уведомления
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Bot className="w-3 h-3" />
|
||||||
|
Удобное управление
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ml-16 sm:ml-0">
|
||||||
|
<Link to="/profile">
|
||||||
|
<NeonButton color="neon" size="sm">
|
||||||
|
Привязать
|
||||||
|
</NeonButton>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import { ru } from 'date-fns/locale'
|
import { ru } from 'date-fns/locale'
|
||||||
|
import { TelegramBotBanner } from '@/components/TelegramBotBanner'
|
||||||
|
|
||||||
export function MarathonPage() {
|
export function MarathonPage() {
|
||||||
const { id } = useParams<{ id: string }>()
|
const { id } = useParams<{ id: string }>()
|
||||||
@@ -316,6 +317,9 @@ export function MarathonPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Telegram Bot Banner */}
|
||||||
|
<TelegramBotBanner />
|
||||||
|
|
||||||
{/* Active event banner */}
|
{/* Active event banner */}
|
||||||
{marathon.status === 'active' && activeEvent?.event && (
|
{marathon.status === 'active' && activeEvent?.event && (
|
||||||
<EventBanner activeEvent={activeEvent} onRefresh={refreshEvent} />
|
<EventBanner activeEvent={activeEvent} onRefresh={refreshEvent} />
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { marathonsApi } from '@/api'
|
|||||||
import type { MarathonListItem } from '@/types'
|
import type { MarathonListItem } from '@/types'
|
||||||
import { NeonButton, GlassCard, StatsCard } from '@/components/ui'
|
import { NeonButton, GlassCard, StatsCard } from '@/components/ui'
|
||||||
import { Plus, Users, Calendar, Loader2, Trophy, Gamepad2, ChevronRight, Hash, Sparkles } from 'lucide-react'
|
import { Plus, Users, Calendar, Loader2, Trophy, Gamepad2, ChevronRight, Hash, Sparkles } from 'lucide-react'
|
||||||
|
import { TelegramBotBanner } from '@/components/TelegramBotBanner'
|
||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import { ru } from 'date-fns/locale'
|
import { ru } from 'date-fns/locale'
|
||||||
|
|
||||||
@@ -145,6 +146,11 @@ export function MarathonsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Telegram Bot Banner */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<TelegramBotBanner />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Join marathon */}
|
{/* Join marathon */}
|
||||||
{showJoinSection && (
|
{showJoinSection && (
|
||||||
<GlassCard className="mb-8 animate-slide-in-down" variant="neon">
|
<GlassCard className="mb-8 animate-slide-in-down" variant="neon">
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export const useAuthStore = create<AuthState>()(
|
|||||||
|
|
||||||
logout: () => {
|
logout: () => {
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
|
sessionStorage.removeItem('telegram_banner_dismissed')
|
||||||
set({
|
set({
|
||||||
user: null,
|
user: null,
|
||||||
token: null,
|
token: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user