import { useCallback, useEffect, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminLegalPagesApi, type FaqPageItem, type FaqSettingItem, type LegalDisplayMode, } from '../api/adminLegalPages'; import { AdminBackButton } from '../components/admin'; import { Toggle } from '../components/admin/Toggle'; import { useHapticFeedback } from '../platform/hooks/useHaptic'; import { useDestructiveConfirm } from '../platform/hooks/useNativeDialog'; import { cn } from '../lib/utils'; import { ChevronDownIcon, ChevronUpIcon, PlusIcon, TrashIcon } from '@/components/icons'; type LegalTab = 'privacy' | 'offer' | 'rules' | 'faq'; const DISPLAY_MODES: LegalDisplayMode[] = ['bot', 'web', 'both']; function extractErrorDetail(err: unknown): string | null { const error = err as { response?: { data?: { detail?: unknown } } }; const detail = error.response?.data?.detail; return typeof detail === 'string' ? detail : null; } function DisplayModeSelector({ value, onChange, disabled, }: { value: LegalDisplayMode; onChange: (mode: LegalDisplayMode) => void; disabled?: boolean; }) { const { t } = useTranslation(); return (
{DISPLAY_MODES.map((mode) => ( ))}
{disabled && (

{t('admin.legalPages.displayModeLocked')}

)}
); } function LanguageTabs({ languages, active, onChange, }: { languages: string[]; active: string; onChange: (lang: string) => void; }) { const { t } = useTranslation(); return (
{languages.map((lang) => ( ))}
); } function DocumentEditor({ kind, onDirtyChange, }: { kind: 'privacy-policy' | 'public-offer'; onDirtyChange: (dirty: boolean) => void; }) { const { t } = useTranslation(); const queryClient = useQueryClient(); const haptic = useHapticFeedback(); const [displayMode, setDisplayMode] = useState('both'); const [contents, setContents] = useState>({}); const [enabled, setEnabled] = useState>({}); const [activeLang, setActiveLang] = useState('ru'); const [populated, setPopulated] = useState(false); const [saveError, setSaveError] = useState(null); const { data, isLoading, isFetching } = useQuery({ queryKey: ['admin', 'legal-pages', kind], queryFn: () => kind === 'privacy-policy' ? adminLegalPagesApi.getPrivacyPolicy() : adminLegalPagesApi.getPublicOffer(), staleTime: 0, gcTime: 0, }); useEffect(() => { if (!data || isFetching || populated) return; setDisplayMode(data.display_mode); const nextContents: Record = {}; const nextEnabled: Record = {}; for (const item of data.items) { nextContents[item.language] = item.content; nextEnabled[item.language] = item.is_enabled; } setContents(nextContents); setEnabled(nextEnabled); if (data.items.length > 0 && !data.items.some((item) => item.language === 'ru')) { setActiveLang(data.items[0].language); } setPopulated(true); }, [data, isFetching, populated]); const isDirty = populated && !!data && (displayMode !== data.display_mode || data.items.some( (item) => (contents[item.language] ?? '') !== item.content || (enabled[item.language] ?? false) !== item.is_enabled, )); useEffect(() => { onDirtyChange(isDirty); }, [isDirty, onDirtyChange]); const saveMutation = useMutation({ mutationFn: () => { const payload = { ...(data?.display_mode_env_locked ? {} : { display_mode: displayMode }), items: Object.keys(contents).map((language) => ({ language, content: contents[language] ?? '', is_enabled: enabled[language] ?? false, })), }; return kind === 'privacy-policy' ? adminLegalPagesApi.updatePrivacyPolicy(payload) : adminLegalPagesApi.updatePublicOffer(payload); }, onSuccess: () => { haptic.success(); setSaveError(null); queryClient.invalidateQueries({ queryKey: ['admin', 'legal-pages', kind] }); }, onError: (err) => { haptic.error(); setSaveError(extractErrorDetail(err) ?? t('admin.legalPages.saveError')); }, }); if (isLoading || !data) { return
; } const languages = data.items.map((item) => item.language); return (
setEnabled((prev) => ({ ...prev, [activeLang]: !prev[activeLang] }))} aria-label={t('admin.legalPages.enabled')} /> {t('admin.legalPages.enabled')}