feat: initial microservices project structure
Services: - api: FastAPI gateway with JWT auth, async endpoints, WebSocket - worker-gpu: CUDA sentence-transformers, FAISS IVFFlat, Ollama LLM - worker-indexer: Winnowing+MinHash plagiarism detection, PDF/DOCX extraction - worker-notifier: SMTP email notifications - worker-gost: GOST 7.1-2003 and GOST R 7.0.5-2008 formatting Infrastructure: - docker-compose.yml (production) + docker-compose.dev.yml (hot reload) - Nginx reverse proxy + WebSocket support - PostgreSQL 16 with Alembic migrations - Elasticsearch 8 with Russian/English analyzers - MinIO, RabbitMQ, Redis, Ollama Frontend: - React 18 + Vite + TypeScript + TailwindCSS + Zustand + React Query v5 - 9 pages: Home, Search, Cabinet, Task, Check, Bibliography, Pricing, Login, Register Scripts: - Parser stubs: OpenAlex, КиберЛенинка, arXiv (Phase 0 - to be filled) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
122
services/frontend/src/pages/Pricing.tsx
Normal file
122
services/frontend/src/pages/Pricing.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
import { clsx } from 'clsx';
|
||||
import { PLAN_LIMITS } from '../types';
|
||||
|
||||
const PLAN_ORDER = ['free', 'student', 'premium', 'science'] as const;
|
||||
|
||||
const PLAN_FEATURES = {
|
||||
free: [
|
||||
'10 поисков в день',
|
||||
'3 краткие изложения в месяц',
|
||||
'1 проверка плагиата в месяц',
|
||||
'1 задача одновременно',
|
||||
'ГОСТ библиография',
|
||||
],
|
||||
student: [
|
||||
'Безлимитный поиск',
|
||||
'30 кратких изложений в месяц',
|
||||
'10 проверок плагиата в месяц',
|
||||
'2 задачи одновременно',
|
||||
'ГОСТ библиография',
|
||||
'Email уведомления',
|
||||
],
|
||||
premium: [
|
||||
'Безлимитный поиск',
|
||||
'Безлимитные изложения',
|
||||
'50 проверок плагиата в месяц',
|
||||
'5 задач одновременно',
|
||||
'ГОСТ библиография',
|
||||
'Приоритетная очередь',
|
||||
],
|
||||
science: [
|
||||
'Безлимитный поиск',
|
||||
'Безлимитные изложения',
|
||||
'Безлимитные проверки плагиата',
|
||||
'10 задач одновременно',
|
||||
'ГОСТ библиография',
|
||||
'Максимальный приоритет',
|
||||
'API доступ',
|
||||
],
|
||||
};
|
||||
|
||||
const POPULAR_PLAN = 'student';
|
||||
|
||||
export function Pricing() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-3">Тарифные планы</h1>
|
||||
<p className="text-gray-500 max-w-xl mx-auto">
|
||||
Начните бесплатно. Обновите план для расширенных возможностей.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{PLAN_ORDER.map((planKey) => {
|
||||
const plan = PLAN_LIMITS[planKey];
|
||||
const features = PLAN_FEATURES[planKey];
|
||||
const isPopular = planKey === POPULAR_PLAN;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={planKey}
|
||||
className={clsx(
|
||||
'relative bg-white rounded-2xl border-2 p-6 flex flex-col',
|
||||
isPopular ? 'border-brand-500 shadow-lg shadow-brand-100' : 'border-gray-100'
|
||||
)}
|
||||
>
|
||||
{isPopular && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
||||
<span className="px-3 py-1 bg-brand-600 text-white text-xs font-medium rounded-full">
|
||||
Популярный
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-4">
|
||||
<h3 className="text-base font-semibold text-gray-900">{plan.name}</h3>
|
||||
<div className="mt-2">
|
||||
{plan.price === 0 ? (
|
||||
<span className="text-3xl font-bold text-gray-900">Бесплатно</span>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-3xl font-bold text-gray-900">{plan.price}₽</span>
|
||||
<span className="text-gray-400 text-sm">/мес</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-2.5 flex-1 mb-6">
|
||||
{features.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2">
|
||||
<Check className="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" />
|
||||
<span className="text-sm text-gray-600">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
className={clsx(
|
||||
'w-full py-2.5 rounded-xl text-sm font-medium transition-colors',
|
||||
isPopular
|
||||
? 'bg-brand-600 text-white hover:bg-brand-700'
|
||||
: plan.price === 0
|
||||
? 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
: 'border border-brand-200 text-brand-700 hover:bg-brand-50'
|
||||
)}
|
||||
>
|
||||
{plan.price === 0 ? 'Начать бесплатно' : 'Выбрать план'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-xl p-6 text-center text-sm text-gray-500">
|
||||
Оплата через российские системы. При вопросах пишите на noreply@jze9.ru
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user