diff --git a/app/landing/page.js b/app/landing/page.js new file mode 100644 index 0000000..b1c9b4e --- /dev/null +++ b/app/landing/page.js @@ -0,0 +1,173 @@ +import Link from 'next/link'; +import { Sparkles, Zap, Calendar, BarChart3, MessageCircle, Globe, ArrowRight, Check } from 'lucide-react'; + +const FEATURES = [ + { icon: Zap, title: 'AI генерация постов', desc: 'Claude пишет посты под твою нишу и стиль. Тексты, которые хочется читать.' }, + { icon: Calendar, title: 'Отложенная публикация', desc: 'Планируй контент на неделю вперёд. Автопостинг в нужное время.' }, + { icon: Globe, title: 'Telegram, VK, MAX', desc: 'Один интерфейс для всех платформ. Публикуй везде одновременно.' }, + { icon: Sparkles, title: 'Авто-черновики', desc: 'Каждое утро 3 новых поста на проверку. Ты только одобряешь лучшее.' }, + { icon: BarChart3, title: 'Аналитика канала', desc: 'Видишь что работает. Охват, реакции, лучшее время для публикации.' }, + { icon: MessageCircle, title: 'Inbox и AI-ответы', desc: 'Комментарии приходят в одно место. AI предлагает ответы за тебя.' }, +]; + +const PLANS = [ + { + name: 'Free', price: 0, credits: 50, channels: 1, + features: ['1 канал', '50 кредитов/мес', 'AI генерация постов', 'Планировщик'], + cta: 'Начать бесплатно', ctaHref: '/register', accent: false, + }, + { + name: 'Starter', price: 490, credits: 500, channels: 2, + features: ['2 канала', '500 кредитов/мес', 'Авто-черновики', 'Аналитика', 'Inbox'], + cta: 'Попробовать', ctaHref: '/register', accent: true, + }, + { + name: 'Pro', price: 1490, credits: 2000, channels: 5, + features: ['5 каналов', '2000 кредитов/мес', 'Все платформы', 'Хештеги AI', 'Опросы TG'], + cta: 'Выбрать Pro', ctaHref: '/register', accent: false, + }, + { + name: 'Business', price: 3990, credits: -1, channels: -1, + features: ['Безлимит каналов', 'Безлимит кредитов', 'Приоритетная поддержка', 'API доступ'], + cta: 'Связаться', ctaHref: 'mailto:hello@zeropost.ru', accent: false, + }, +]; + +export default function LandingPage() { + return ( +
+ {/* Nav */} + + + {/* Hero */} +
+
+ AI-контент для Telegram и VK +
+

+ Ведите канал на автопилоте.
+ AI пишет, ты одобряешь. +

+

+ ZeroPost генерирует посты для Telegram и VK, планирует публикации и отвечает на комментарии. + Тратьте 10 минут в день вместо 2 часов. +

+
+ + Начать бесплатно + + + Уже есть аккаунт + +
+

50 кредитов бесплатно · Без карты

+
+ + {/* Features */} +
+

Что умеет ZeroPost

+
+ {FEATURES.map(f => ( +
+ +

{f.title}

+

{f.desc}

+
+ ))} +
+
+ + {/* How it works */} +
+

Как это работает

+
+ {[ + { step: '1', title: 'Добавь канал', desc: 'Подключи Telegram, VK или MAX. Укажи нишу и стиль.' }, + { step: '2', title: 'AI генерирует', desc: 'Каждое утро — свежие черновики. Редактируй, одобряй.' }, + { step: '3', title: 'Публикуй в один клик', desc: 'Запланируй или публикуй сейчас. Всё само.' }, + ].map(s => ( +
+
+ {s.step} +
+

{s.title}

+

{s.desc}

+
+ ))} +
+
+ + {/* Pricing */} +
+

Тарифы

+

Начни бесплатно, масштабируй по мере роста

+
+ {PLANS.map(plan => ( +
+ {plan.accent && ( +
✨ Популярный
+ )} +
{plan.name}
+
+ {plan.price === 0 ? 0₽ : `${plan.price}₽`} + {plan.price > 0 && /мес} +
+
+ {plan.credits === -1 ? '∞ кредитов' : `${plan.credits} кредитов/мес`} +
+
    + {plan.features.map(f => ( +
  • + {f} +
  • + ))} +
+ + {plan.cta} + +
+ ))} +
+
+ + {/* CTA */} +
+

Готовы попробовать?

+

50 бесплатных кредитов. Без карты. Настройка за 5 минут.

+ + Создать аккаунт + +
+ + {/* Footer */} + +
+ ); +} diff --git a/app/page.js b/app/page.js index 8e4d152..22bf152 100644 --- a/app/page.js +++ b/app/page.js @@ -15,7 +15,7 @@ const GOAL_LABELS = { export default async function HomePage() { const user = await requireUser(); - if (!user) redirect('/login'); + if (!user) redirect('/landing'); let channels = []; try { @@ -43,50 +43,47 @@ export default async function HomePage() { {channels.length === 0 ? (
-
- -
-

Пока пусто

-

- Создай первый канал, чтобы начать генерировать посты -

+ +

Нет каналов

+

Добавь первый канал чтобы начать генерировать контент

- Создать канал + Создать первый канал
) : ( -
+
{channels.map(ch => ( - +
-

- {ch.name} -

- - {(ch.goal || '').split(',').map(g => GOAL_LABELS[g.trim()] || g.trim()).join(' · ')} +
+

{ch.name}

+ {ch.tg_username && ( + @{ch.tg_username} + )} +
+ + {ch.platform || 'telegram'}
{ch.niche && ( -

- {ch.niche} -

+

{ch.niche}

)} -
- {ch.audience && ( +
+ {ch.goal && ( - - Есть ЦА + + {GOAL_LABELS[ch.goal] || ch.goal} )} - {ch.style?.example_posts?.length > 0 && ( + {ch.language && ( - - {ch.style.example_posts.length} пример{ch.style.example_posts.length === 1 ? '' : 'а'} + + {ch.language.toUpperCase()} )}
diff --git a/app/register/page.js b/app/register/page.js new file mode 100644 index 0000000..bad3c16 --- /dev/null +++ b/app/register/page.js @@ -0,0 +1,121 @@ +'use client'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import { Loader2, Eye, EyeOff, Sparkles } from 'lucide-react'; + +export default function RegisterPage() { + const router = useRouter(); + const [email, setEmail] = useState(''); + const [pass, setPass] = useState(''); + const [pass2, setPass2] = useState(''); + const [name, setName] = useState(''); + const [show, setShow] = useState(false); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(''); + + async function submit() { + if (!email.trim() || !pass) { setError('Заполните email и пароль'); return; } + if (pass.length < 6) { setError('Пароль минимум 6 символов'); return; } + if (pass !== pass2) { setError('Пароли не совпадают'); return; } + setBusy(true); setError(''); + try { + const res = await fetch('/api/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: email.trim(), password: pass, name: name.trim() || undefined, mode: 'register' }), + }).then(r => r.json()); + if (!res.ok) { setError(res.error || 'Ошибка'); setBusy(false); return; } + router.push(res.isNew ? '/onboarding' : '/'); + } catch { setError('Ошибка соединения'); setBusy(false); } + } + + return ( +
+ {/* Background glow */} +
+
+
+ +
+ {/* Logo */} +
+ + + ZeroPost + +

Создайте аккаунт — это бесплатно

+
+ +
+

Регистрация

+ +
+ + setName(e.target.value)} + placeholder="Алексей" + className="input w-full" autoFocus /> +
+ +
+ + setEmail(e.target.value)} + onKeyDown={e => e.key === 'Enter' && submit()} + placeholder="you@example.com" + className="input w-full" /> +
+ +
+ +
+ setPass(e.target.value)} + placeholder="Минимум 6 символов" + className="input w-full pr-10" /> + +
+
+ +
+ + setPass2(e.target.value)} + onKeyDown={e => e.key === 'Enter' && submit()} + placeholder="Ещё раз" + className="input w-full" /> +
+ + {error &&

{error}

} + + + +
+
+ или +
+
+ + + Уже есть аккаунт? Войти → + +
+ + {/* Бонус */} +
+ 🎁 При регистрации — 50 бесплатных кредитов +
+ +

+ Регистрируясь, вы принимаете{' '} + условия использования +

+
+
+ ); +} diff --git a/components/Header.js b/components/Header.js index c43411f..ff58c54 100644 --- a/components/Header.js +++ b/components/Header.js @@ -2,7 +2,7 @@ import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; -import { Sparkles, LogOut, CalendarDays, Coins, FileText } from 'lucide-react'; +import { Sparkles, LogOut, CalendarDays, Coins, FileText, Settings2 } from 'lucide-react'; import ThemeToggle from './ThemeToggle'; export default function Header({ user }) { @@ -63,3 +63,21 @@ export default function Header({ user }) { ); } + +// Публичный хедер для лендинга — отдельный экспорт +export function PublicHeader() { + return ( +
+
+ + + ZeroPost + +
+ Войти + Начать бесплатно +
+
+
+ ); +}