Files
app_umbrix/components/ReferralModal.tsx
Umbrix Dev b43eb3c724 Реферальная система: БД, API, UI
**База данных:**
- Создана таблица referrals в MariaDB (193.168.175.128)
- Поля: username, referrer_username, referral_count, bonus_days_earned
- Foreign keys к users таблице с CASCADE/SET NULL

**API Endpoints:**
- POST /api/referral/track - отслеживание новых рефералов
  - Автоматический расчёт бонусов: +7 дней за каждого
  - Milestone bonus: +30 дней за каждые 5 рефералов
  - Обновление expire даты реферера в users таблице

- GET /api/referral/stats?username=xxx - статистика
  - Возвращает количество рефералов, бонусные дни
  - Список приглашённых пользователей со статусами

**Интеграция:**
- POST /api/create-user принимает referrerId параметр
- Автоматический вызов /api/referral/track после создания юзера
- Параметр ref из URL при активации trial

**UI:**
- /app/referral/page.tsx - страница статистики
  - 3 KPI карточки: рефералов, бонусных дней, milestone
  - Реферальная ссылка с кнопкой копирования
  - Список приглашённых юзеров с иконками статуса
  - Инфоблок о механике начисления бонусов

**ReferralModal обновлён:**
- Добавлена кнопка «Моя статистика» → /referral
- Перенос Share/Copy кнопок на второй/третий план

**Зависимости:**
- mysql2@3.16.3 - для подключения к MariaDB

**Логика бонусов:**
- +7 дней за каждого успешного реферала
- +30 дней бонус за каждые 5 рефералов (milestone)
- Автоматическое обновление expire поля в users таблице
- Сохранение всех бонусов в bonus_days_earned
2026-02-06 20:51:40 +03:00

146 lines
6.0 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 { X, Copy, Share2, Gift, Users, Award, BarChart3 } from 'lucide-react';
import Link from 'next/link';
interface ReferralModalProps {
isOpen: boolean;
onClose: () => void;
referralUrl: string;
onShare: () => void;
onCopy: () => void;
}
export default function ReferralModal({
isOpen,
onClose,
referralUrl,
onShare,
onCopy
}: ReferralModalProps) {
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-50 flex items-end sm:items-center justify-center"
onClick={onClose}
>
{/* Backdrop */}
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
{/* Modal */}
<div
className="relative w-full max-w-lg bg-[var(--bg-card)] rounded-t-3xl sm:rounded-3xl shadow-2xl border border-slate-700 max-h-[90vh] overflow-y-auto"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="sticky top-0 bg-[var(--bg-card)] border-b border-slate-700 p-6 flex items-center justify-between z-10">
<h2 className="text-xl font-bold flex items-center gap-2">
<Gift className="w-6 h-6 text-[var(--primary)]" />
Пригласи друга
</h2>
<button
onClick={onClose}
className="p-2 hover:bg-slate-700 rounded-full transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
{/* Content */}
<div className="p-6 space-y-6">
{/* Условия программы */}
<div className="space-y-4">
<div className="bg-slate-800/50 border border-slate-700 rounded-xl p-4">
<div className="flex items-start gap-3">
<Award className="w-6 h-6 flex-shrink-0 mt-1" style={{ color: 'var(--primary)' }} />
<div>
<h3 className="font-semibold mb-2" style={{ color: 'var(--primary)' }}>Ты получишь:</h3>
<ul className="space-y-2 text-sm text-slate-300">
<li className="flex items-start gap-2">
<span className="mt-0.5" style={{ color: 'var(--primary)' }}></span>
<span><strong>+7 дней</strong> бесплатно за каждого друга</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-0.5" style={{ color: 'var(--primary)' }}></span>
<span><strong>10% скидка</strong> на продление при оплате</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-0.5" style={{ color: 'var(--primary)' }}></span>
<span>За 5 друзей <strong>1 месяц в подарок</strong></span>
</li>
</ul>
</div>
</div>
</div>
<div className="bg-slate-800/50 border border-slate-700 rounded-xl p-4">
<div className="flex items-start gap-3">
<Users className="w-6 h-6 flex-shrink-0 mt-1" style={{ color: 'var(--primary)' }} />
<div>
<h3 className="font-semibold mb-2" style={{ color: 'var(--primary)' }}>Твой друг получит:</h3>
<ul className="space-y-2 text-sm text-slate-300">
<li className="flex items-start gap-2">
<span className="mt-0.5" style={{ color: 'var(--primary)' }}></span>
<span><strong>7 дней бесплатного</strong> триала</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-0.5" style={{ color: 'var(--primary)' }}></span>
<span><strong>-10% скидка</strong> на первую покупку</span>
</li>
</ul>
</div>
</div>
</div>
</div>
{/* Реферальная ссылка */}
<div className="space-y-3">
<label className="text-sm text-slate-400">Твоя реферальная ссылка:</label>
<div className="flex items-center gap-2 p-3 bg-slate-800 rounded-lg border border-slate-700">
<code className="flex-1 text-sm text-[var(--primary)] truncate">
{referralUrl}
</code>
</div>
</div>
{/* Action buttons */}
<div className="space-y-3">
<Link href="/referral">
<button
onClick={onClose}
className="w-full flex items-center justify-center gap-3 px-6 py-4 rounded-xl font-semibold transition-all shadow-lg hover:opacity-90"
style={{ background: 'var(--primary)', color: 'white' }}
>
<BarChart3 className="w-5 h-5" />
Моя статистика
</button>
</Link>
<button
onClick={onShare}
className="w-full flex items-center justify-center gap-3 px-6 py-4 bg-slate-700 hover:bg-slate-600 rounded-xl font-semibold transition-all"
>
<Share2 className="w-5 h-5" />
Поделиться с друзьями
</button>
<button
onClick={onCopy}
className="w-full flex items-center justify-center gap-3 px-6 py-4 bg-slate-700 hover:bg-slate-600 rounded-xl font-semibold transition-all"
>
<Copy className="w-5 h-5" />
Скопировать ссылку
</button>
</div>
{/* Info */}
<div className="text-xs text-slate-500 text-center">
Бонусы начисляются после активации подписки приглашенным другом
</div>
</div>
</div>
</div>
);
}