Publication IA: - 4 templates (magazine, brief, essay, simple) avec CSS riche - Rewrite IA (article/exercises/tutorial/reference/mixed) - Modération avec timeout 12s + fallback safe - Quotas publish_enhance par tier (basic=2, pro=15, business=100) - Détection contenu stale (hash) - Migration DB publishedContent/publishedTemplate/publishedSourceHash Fixes: - cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast - _isShared ajouté au type Note (champ virtuel serveur) - callout colors PDF export: extraction fonction pure testable - admin/published: guard note.userId null - Cmd+S fonctionne en mode dialog (pas seulement fullPage) i18n: - 23 clés publish* traduites dans les 15 locales - Extension Web Clipper: 13 locales mise à jour Tests: - callout-colors.test.ts (6 tests) - note-visible-in-view.test.ts (5 tests) - entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs - 199/199 tests passent Tracker: user-stories.md sync avec sprint-status.yaml
204 lines
8.3 KiB
TypeScript
204 lines
8.3 KiB
TypeScript
import React from 'react';
|
|
import { motion } from 'motion/react';
|
|
import { Check, Shield, Zap, Crown, CreditCard, ArrowRight, Activity, Clock } from 'lucide-react';
|
|
|
|
export const BillingTab: React.FC = () => {
|
|
const plans = [
|
|
{
|
|
id: 'free',
|
|
name: 'Plan Basic',
|
|
price: 'Gratuit',
|
|
period: '',
|
|
description: 'Pour découvrir la magie de Memento.',
|
|
features: [
|
|
'100 Notes max',
|
|
'3 Carnets',
|
|
'50 crédits IA (Lifetime)',
|
|
'Recherche sémantique',
|
|
'Historique 7 jours'
|
|
],
|
|
current: true,
|
|
buttonText: 'Plan Actuel',
|
|
buttonClass: 'bg-paper text-concrete cursor-default'
|
|
},
|
|
{
|
|
id: 'pro',
|
|
name: 'Plan Pro',
|
|
price: '9,90€',
|
|
period: '/mois',
|
|
description: 'Pour les consultants et créateurs exigeants.',
|
|
features: [
|
|
'Notes illimitées',
|
|
'BYOK (OpenAI/Anthropic)',
|
|
'200 recherches sémantiques',
|
|
'Agents (12 runs/mois)',
|
|
'Historique 30 jours',
|
|
'Support Email'
|
|
],
|
|
current: false,
|
|
popular: true,
|
|
buttonText: 'Passer au Plan Pro',
|
|
buttonClass: 'bg-accent text-white shadow-xl shadow-accent/20 hover:scale-[1.02] active:scale-95'
|
|
},
|
|
{
|
|
id: 'business',
|
|
name: 'Plan Business',
|
|
price: '29,90€',
|
|
period: '/mois',
|
|
description: 'Pour les équipes et chefs de produit.',
|
|
features: [
|
|
'10 Collaborateurs inclus',
|
|
'BYOK (13 fournisseurs)',
|
|
'1000 recherches sémantiques',
|
|
'Agents (60 runs/mois)',
|
|
'Brainstorm illimité',
|
|
'Accès API'
|
|
],
|
|
current: false,
|
|
buttonText: 'Choisir Plan Business',
|
|
buttonClass: 'bg-ink text-white shadow-xl shadow-ink/20 hover:scale-[1.02] active:scale-95'
|
|
}
|
|
];
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -10 }}
|
|
className="space-y-12"
|
|
>
|
|
<div className="space-y-2">
|
|
<h3 className="text-[10px] font-bold uppercase tracking-[0.4em] text-concrete opacity-60">Gérer votre abonnement et votre facturation</h3>
|
|
</div>
|
|
|
|
{/* Usage Overview */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-3xl p-8 space-y-6">
|
|
<div className="flex items-center gap-4">
|
|
<div className="p-2 bg-accent/10 text-accent rounded-xl">
|
|
<Activity size={20} />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-bold text-ink">Utilisation actuelle</h4>
|
|
<p className="text-[10px] text-concrete uppercase tracking-widest">Période en cours</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between text-[11px] font-medium text-concrete uppercase tracking-wider">
|
|
<span>Crédits IA</span>
|
|
<span>1 / 50 utilisés</span>
|
|
</div>
|
|
<div className="h-2 w-full bg-slate-100 dark:bg-white/5 rounded-full overflow-hidden">
|
|
<div className="h-full bg-accent w-[2%] rounded-full" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between text-[11px] font-medium text-concrete uppercase tracking-wider">
|
|
<span>Notes & Carnets</span>
|
|
<span>12 / 100 notes</span>
|
|
</div>
|
|
<div className="h-2 w-full bg-slate-100 dark:bg-white/5 rounded-full overflow-hidden">
|
|
<div className="h-full bg-ochre w-[12%] rounded-full" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-3xl p-8 space-y-6">
|
|
<div className="flex items-center gap-4">
|
|
<div className="p-2 bg-paper dark:bg-white/10 text-concrete rounded-xl">
|
|
<Clock size={20} />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-bold text-ink">Facturation</h4>
|
|
<p className="text-[10px] text-concrete uppercase tracking-widest">Renouvellement</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<p className="text-xs text-concrete font-light">Votre plan gratuit n'expire jamais. Passez à la vitesse supérieure pour débloquer toute la puissance de Memento.</p>
|
|
<div className="pt-4 flex items-center justify-between border-t border-border/40 mt-4">
|
|
<span className="text-[11px] font-bold text-ink uppercase tracking-widest">Plan Actuel</span>
|
|
<span className="text-[11px] font-bold text-accent uppercase tracking-widest">GRATUIT</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Plan Selection */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
{plans.map((plan) => (
|
|
<div
|
|
key={plan.id}
|
|
className={`relative p-8 rounded-[40px] border transition-all duration-500 overflow-hidden group flex flex-col
|
|
${plan.popular
|
|
? 'bg-white dark:bg-paper border-accent shadow-2xl shadow-accent/10 scale-105 z-10'
|
|
: 'bg-white/40 dark:bg-white/5 border-border hover:border-concrete/30'}`}
|
|
>
|
|
{plan.popular && (
|
|
<div className="absolute top-0 right-0 py-1.5 px-6 bg-accent text-white text-[9px] font-bold uppercase tracking-widest rounded-bl-2xl">
|
|
Recommandé
|
|
</div>
|
|
)}
|
|
|
|
<div className="mb-8 space-y-2">
|
|
<h4 className="text-xl font-serif font-bold text-ink">{plan.name}</h4>
|
|
<div className="flex items-baseline gap-1">
|
|
<span className="text-4xl font-serif font-bold text-ink">{plan.price}</span>
|
|
<span className="text-concrete text-xs font-light italic">{plan.period}</span>
|
|
</div>
|
|
<p className="text-xs text-concrete font-light leading-relaxed pr-4">{plan.description}</p>
|
|
</div>
|
|
|
|
<div className="space-y-4 mb-10 flex-1">
|
|
{plan.features.map((feature, i) => (
|
|
<div key={i} className="flex items-start gap-3">
|
|
<div className={`mt-1 p-0.5 rounded-full ${plan.popular ? 'bg-accent/10 text-accent' : 'bg-concrete/10 text-concrete'}`}>
|
|
<Check size={10} />
|
|
</div>
|
|
<span className="text-xs font-light text-ink/80">{feature}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
className={`w-full py-4 rounded-2xl text-[10px] font-bold uppercase tracking-[0.2em] transition-all duration-300 ${plan.buttonClass}`}
|
|
>
|
|
<div className="flex items-center justify-center gap-2">
|
|
{plan.buttonText}
|
|
{!plan.current && <ArrowRight size={14} />}
|
|
</div>
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Footer Info */}
|
|
<div className="bg-slate-50 dark:bg-black/20 rounded-[32px] p-8 border border-border/40 flex flex-col md:flex-row items-center justify-between gap-8">
|
|
<div className="flex items-center gap-4">
|
|
<div className="p-3 bg-white dark:bg-paper rounded-2xl shadow-sm border border-border">
|
|
<Shield size={24} className="text-accent" />
|
|
</div>
|
|
<div>
|
|
<h5 className="text-sm font-bold text-ink">Transactions sécurisées</h5>
|
|
<p className="text-xs text-concrete font-light">Paiement via Stripe. Annulez à tout moment, sans engagement.</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-6">
|
|
<div className="flex items-center gap-2">
|
|
<Zap size={16} className="text-ochre" />
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-concrete">Activation instantanée</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Crown size={16} className="text-amber-500" />
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-concrete">Garantie satisfait</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
};
|