import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import DOMPurify from 'dompurify'
import { infoApi, FaqPage } from '../api/info'
const InfoIcon = () => (
)
const QuestionIcon = () => (
)
const DocumentIcon = () => (
)
const ShieldIcon = () => (
)
const ChevronIcon = ({ expanded }: { expanded: boolean }) => (
)
type TabType = 'faq' | 'rules' | 'privacy' | 'offer'
// Sanitize HTML content to prevent XSS
const sanitizeHtml = (html: string): string => {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'b', 'i', 'u', 'strong', 'em', 'a', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'code', 'pre', 'span', 'div'],
ALLOWED_ATTR: ['href', 'target', 'rel', 'class'],
ALLOW_DATA_ATTR: false,
})
}
// Convert plain text to HTML with proper formatting
const formatContent = (content: string): string => {
if (!content) return ''
// Check if content already has HTML tags
const hasHtmlTags = /<[a-z][\s\S]*>/i.test(content)
if (hasHtmlTags) {
return sanitizeHtml(content)
}
// Convert plain text to formatted HTML
const result = content
.split(/\n\n+/) // Split by double newlines (paragraphs)
.map(paragraph => {
// Check if it's a header (starts with # or numeric like "1.")
if (/^#{1,4}\s/.test(paragraph)) {
const level = paragraph.match(/^(#{1,4})/)?.[1].length || 1
const text = paragraph.replace(/^#{1,4}\s*/, '')
return `${text}`
}
// Check for list items
if (/^[-•]\s/.test(paragraph) || /^\d+[.)]\s/.test(paragraph)) {
const lines = paragraph.split('\n')
const isOrdered = /^\d+[.)]\s/.test(lines[0])
const listItems = lines
.map(line => line.replace(/^[-•]\s*/, '').replace(/^\d+[.)]\s*/, ''))
.filter(line => line.trim())
.map(line => `
${line}`)
.join('')
return isOrdered ? `${listItems}
` : ``
}
// Regular paragraph - handle single line breaks within
const formattedParagraph = paragraph
.split('\n')
.join('
')
return `${formattedParagraph}
`
})
.join('')
return sanitizeHtml(result)
}
export default function Info() {
const { t } = useTranslation()
const [activeTab, setActiveTab] = useState('faq')
const [expandedFaq, setExpandedFaq] = useState(null)
const { data: faqPages, isLoading: faqLoading } = useQuery({
queryKey: ['faq-pages'],
queryFn: infoApi.getFaqPages,
enabled: activeTab === 'faq',
staleTime: 0,
refetchOnMount: 'always',
})
const { data: rules, isLoading: rulesLoading } = useQuery({
queryKey: ['rules'],
queryFn: infoApi.getRules,
enabled: activeTab === 'rules',
staleTime: 0,
refetchOnMount: 'always',
})
const { data: privacy, isLoading: privacyLoading } = useQuery({
queryKey: ['privacy-policy'],
queryFn: infoApi.getPrivacyPolicy,
enabled: activeTab === 'privacy',
staleTime: 0,
refetchOnMount: 'always',
})
const { data: offer, isLoading: offerLoading } = useQuery({
queryKey: ['public-offer'],
queryFn: infoApi.getPublicOffer,
enabled: activeTab === 'offer',
staleTime: 0,
refetchOnMount: 'always',
})
const tabs = [
{ id: 'faq' as TabType, label: t('info.faq'), icon: QuestionIcon },
{ id: 'rules' as TabType, label: t('info.rules'), icon: DocumentIcon },
{ id: 'privacy' as TabType, label: t('info.privacy'), icon: ShieldIcon },
{ id: 'offer' as TabType, label: t('info.offer'), icon: DocumentIcon },
]
const toggleFaq = (id: number) => {
setExpandedFaq(expandedFaq === id ? null : id)
}
const renderContent = () => {
if (activeTab === 'faq') {
if (faqLoading) {
return (
)
}
if (!faqPages || faqPages.length === 0) {
return (
{t('info.noFaq')}
)
}
return (
{faqPages.map((faq: FaqPage) => (
{expandedFaq === faq.id && (
)}
))}
)
}
if (activeTab === 'rules') {
if (rulesLoading) {
return (
)
}
if (!rules?.content) {
return (
{t('info.noContent')}
)
}
return (
{rules.updated_at && (
{t('info.updatedAt')}: {new Date(rules.updated_at).toLocaleDateString()}
)}
)
}
if (activeTab === 'privacy') {
if (privacyLoading) {
return (
)
}
if (!privacy?.content) {
return (
{t('info.noContent')}
)
}
return (
{privacy.updated_at && (
{t('info.updatedAt')}: {new Date(privacy.updated_at).toLocaleDateString()}
)}
)
}
if (activeTab === 'offer') {
if (offerLoading) {
return (
)
}
if (!offer?.content) {
return (
{t('info.noContent')}
)
}
return (
{offer.updated_at && (
{t('info.updatedAt')}: {new Date(offer.updated_at).toLocaleDateString()}
)}
)
}
return null
}
return (
{t('info.title')}
{/* Tabs */}
{tabs.map((tab) => (
))}
{/* Content */}
{renderContent()}
)
}