Files
PnvPanel/frontend/src/features/telegram/TelegramLoginButton.tsx
T
Leonid Pershin bea2b5fcf7
CI / Backend (build + test) (push) Successful in 1m24s
CI / Frontend (lint + typecheck + build) (push) Successful in 30s
Refactor VPN configuration handling to remove device limit management
- Updated the VPN configuration commands and handlers to eliminate the device limit parameter, simplifying the configuration process.
- Adjusted related API documentation to reflect the removal of device limit management, clarifying that this setting is now handled directly in the 3x-ui by node administrators.
- Enhanced the overall codebase by removing unnecessary device limit references across various components, ensuring a cleaner and more maintainable code structure.
2026-07-02 23:21:26 +03:00

89 lines
3.4 KiB
TypeScript

import { useEffect, useState } from 'react'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useNavigate } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import { QRCodeSVG } from 'qrcode.react'
import { Send } from 'lucide-react'
import { toast } from '@/shared/ui/toast-store'
import { Button } from '@/shared/ui/button'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/shared/ui/dialog'
import { applyAuthResponse } from '@/features/auth/api'
import { createLoginRequest, getLoginRequestStatus } from './api'
import type { TelegramLoginStatus } from '@/shared/api/types'
const TERMINAL: TelegramLoginStatus[] = ['Rejected', 'Expired', 'Consumed']
export function TelegramLoginButton() {
const { t } = useTranslation()
const navigate = useNavigate()
const [open, setOpen] = useState(false)
const [requestId, setRequestId] = useState<string | null>(null)
const [deepLink, setDeepLink] = useState<string | null>(null)
const startMutation = useMutation({
mutationFn: createLoginRequest,
onSuccess: (data) => {
setRequestId(data.requestId)
setDeepLink(data.deepLink)
setOpen(true)
},
onError: () => toast.error(t('auth.genericError')),
})
const statusQuery = useQuery({
queryKey: ['telegram-login-status', requestId],
queryFn: () => getLoginRequestStatus(requestId!),
enabled: open && !!requestId,
refetchInterval: (query) => {
const status = query.state.data?.status
return status && (status === 'Approved' || TERMINAL.includes(status)) ? false : 2000
},
})
const status = statusQuery.data?.status
useEffect(() => {
if (status !== 'Approved' || !statusQuery.data?.accessToken || !statusQuery.data.user) return
applyAuthResponse({
accessToken: statusQuery.data.accessToken,
expiresAt: statusQuery.data.expiresAt!,
user: statusQuery.data.user,
})
setOpen(false)
void navigate({ to: '/dashboard' })
}, [status, statusQuery.data, navigate])
return (
<>
<Button type="button" variant="outline" className="w-full" onClick={() => startMutation.mutate()} disabled={startMutation.isPending}>
<Send className="h-4 w-4" />
{t('auth.loginViaTelegram')}
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('auth.loginViaTelegram')}</DialogTitle>
</DialogHeader>
<div className="flex flex-col items-center gap-4">
{deepLink && (
<>
<QRCodeSVG value={deepLink} size={200} />
<a href={deepLink} target="_blank" rel="noreferrer" className="text-sm text-primary hover:underline">
{deepLink}
</a>
<p className="text-center text-sm text-muted-foreground">{t('auth.telegramQrHint')}</p>
</>
)}
{!deepLink && <p className="text-sm text-muted-foreground">{t('auth.telegramBotNotConfigured')}</p>}
{status === 'Pending' && <p className="text-sm text-muted-foreground">{t('auth.waitingForConfirmation')}</p>}
{status === 'Rejected' && <p className="text-sm text-red-500">{t('auth.telegramLoginRejected')}</p>}
{status === 'Expired' && <p className="text-sm text-red-500">{t('auth.telegramLoginExpired')}</p>}
</div>
</DialogContent>
</Dialog>
</>
)
}