Files
2026-06-26 11:18:07 +03:00

172 lines
6.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Bot, ChevronRight, Loader2, Plus } from 'lucide-react';
import { usePublicSettings } from '@/components/id/public-settings-provider';
import { Button } from '@/components/ui/button';
import { BotCreateMiniAppContent } from '@/app/mini-apps/bot-create/content';
import { BotManageMiniAppContent } from '@/app/mini-apps/bot-manage/content';
import { useMiniAppAuth } from '@/hooks/use-mini-app-auth';
import { fetchMyBots, getApiErrorMessage, type ManagedBot } from '@/lib/api';
import { useToast } from '@/components/id/toast-provider';
type View = 'list' | 'create' | 'manage';
export function BotFatherMiniAppContent() {
const searchParams = useSearchParams();
const router = useRouter();
const { projectName } = usePublicSettings();
const { showToast } = useToast();
const { effectiveToken, canUse, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
const initialBotId = searchParams.get('botId');
const initialMode = searchParams.get('mode');
const [view, setView] = useState<View>(() => {
if (initialBotId) return 'manage';
if (initialMode === 'create') return 'create';
return 'list';
});
const [selectedBotId, setSelectedBotId] = useState<string | null>(initialBotId);
const [bots, setBots] = useState<ManagedBot[]>([]);
const [loadingBots, setLoadingBots] = useState(false);
const syncUrl = useCallback(
(nextView: View, botId?: string | null) => {
const params = new URLSearchParams();
if (nextView === 'create') params.set('mode', 'create');
if (nextView === 'manage' && botId) params.set('botId', botId);
const query = params.toString();
router.replace(query ? `/mini-apps/bot-father?${query}` : '/mini-apps/bot-father');
},
[router]
);
const loadBots = useCallback(async () => {
if (!effectiveToken) return;
setLoadingBots(true);
try {
const response = await fetchMyBots(effectiveToken);
setBots((response.bots ?? []).filter((bot) => !bot.isSystemBot));
} catch (error) {
showToast(getApiErrorMessage(error, 'Не удалось загрузить список ботов') ?? 'Ошибка');
} finally {
setLoadingBots(false);
}
}, [effectiveToken, showToast]);
useEffect(() => {
if (!canUse) return;
if (view === 'list') void loadBots();
}, [canUse, loadBots, view]);
const selectedBot = useMemo(
() => bots.find((bot) => bot.id === selectedBotId) ?? null,
[bots, selectedBotId]
);
function openManage(botId: string) {
setSelectedBotId(botId);
setView('manage');
syncUrl('manage', botId);
}
function openCreate() {
setView('create');
syncUrl('create');
}
function backToList() {
setSelectedBotId(null);
setView('list');
syncUrl('list');
void loadBots();
}
if ((isLoading || waitingForBridge || !authReady) && !effectiveToken) {
return (
<div className="flex min-h-screen items-center justify-center p-6 text-center text-sm text-[#667085]">
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Загрузка...
</div>
);
}
if (!canUse) {
return (
<div className="flex min-h-screen items-center justify-center p-6 text-center text-sm text-[#667085]">
Войдите в {projectName}, чтобы управлять ботами
</div>
);
}
if (view === 'create') {
return (
<BotCreateMiniAppContent
onBack={backToList}
onCreated={(botId) => openManage(botId)}
/>
);
}
if (view === 'manage' && selectedBotId) {
return <BotManageMiniAppContent botId={selectedBotId} onBack={backToList} botHint={selectedBot?.username} />;
}
return (
<div className="min-h-screen bg-[#17212b] p-4 text-white">
<div className="mx-auto max-w-md space-y-4">
<div className="rounded-[24px] bg-[#242f3d] p-5 shadow-xl">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[#3390ec]/20 text-[#3390ec]">
<Bot className="h-6 w-6" />
</div>
<div>
<h1 className="text-lg font-semibold">BotFather</h1>
<p className="text-sm text-[#8b93a7]">Создание и настройка ваших ботов</p>
</div>
</div>
</div>
<Button className="h-12 w-full rounded-xl text-base" onClick={openCreate}>
<Plus className="mr-2 h-5 w-5" />
Создать нового бота
</Button>
<div className="rounded-[24px] bg-[#242f3d] p-4 shadow-xl">
<p className="mb-3 text-sm font-medium text-[#c5cad3]">Мои боты</p>
{loadingBots ? (
<div className="flex items-center justify-center gap-2 py-8 text-sm text-[#8b93a7]">
<Loader2 className="h-4 w-4 animate-spin" />
Загрузка...
</div>
) : bots.length === 0 ? (
<p className="py-6 text-center text-sm text-[#8b93a7]">У вас пока нет ботов</p>
) : (
<div className="space-y-2">
{bots.map((bot) => (
<button
key={bot.id}
type="button"
onClick={() => openManage(bot.id)}
className="flex w-full items-center gap-3 rounded-2xl bg-[#17212b] px-4 py-3 text-left transition hover:bg-[#1c2733]"
>
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[#3390ec]/15 text-[#3390ec]">
<Bot className="h-5 w-5" />
</div>
<div className="min-w-0 flex-1">
<p className="truncate font-medium">{bot.name}</p>
<p className="truncate text-sm text-[#8b93a7]">@{bot.username}</p>
</div>
<ChevronRight className="h-5 w-5 shrink-0 text-[#667085]" />
</button>
))}
</div>
)}
</div>
</div>
</div>
);
}