fix(quotas): unifier le décompte IA (BYOK, rollback) et combler les fuites
Centralise la réserve via ai-quota, corrige admin unavailable (-1), brancher les routes sans quota et le host-pays brainstorm, avec usage-meter élargi, noms de clusters, MCP et ajustements dashboard/insights. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -62,7 +62,7 @@ export function BillingPlans() {
|
||||
queryFn: async () => {
|
||||
const search = typeof window !== 'undefined' ? window.location.search : '';
|
||||
const res = await fetch(`/api/billing/status${search}`);
|
||||
if (!res.ok) throw new Error('Failed to fetch billing status');
|
||||
if (!res.ok) throw new Error(t('billing.fetchStatusFailed'));
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
@@ -71,7 +71,7 @@ export function BillingPlans() {
|
||||
queryKey: ['usage', 'current'],
|
||||
queryFn: async () => {
|
||||
const res = await fetch('/api/usage/current');
|
||||
if (!res.ok) throw new Error('Failed to fetch quotas');
|
||||
if (!res.ok) throw new Error(t('billing.fetchQuotasFailed'));
|
||||
return res.json();
|
||||
},
|
||||
refetchInterval: 30000,
|
||||
@@ -122,7 +122,7 @@ export function BillingPlans() {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[BillingPlans] checkout error:', err);
|
||||
toast.error('Failed to start checkout. Please try again.');
|
||||
toast.error(t('billing.checkoutFailed'));
|
||||
} finally {
|
||||
setCheckoutLoading(null);
|
||||
}
|
||||
@@ -139,20 +139,20 @@ export function BillingPlans() {
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
toast.error(data.error || 'Failed to open billing portal.');
|
||||
toast.error(data.error || t('billing.portalFailed'));
|
||||
return;
|
||||
}
|
||||
window.location.href = data.url;
|
||||
} catch (err) {
|
||||
console.error('[BillingPlans] portal error:', err);
|
||||
toast.error('Failed to open billing portal.');
|
||||
toast.error(t('billing.portalFailed'));
|
||||
} finally {
|
||||
setPortalLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelSubscription = async () => {
|
||||
const confirmMsg = t('billing.cancelConfirm') || "Êtes-vous sûr de vouloir résilier votre abonnement ? Vous conserverez vos accès Pro/Business jusqu'à la fin de la période en cours.";
|
||||
const confirmMsg = t('billing.cancelConfirm');
|
||||
if (!window.confirm(confirmMsg)) {
|
||||
return;
|
||||
}
|
||||
@@ -165,15 +165,15 @@ export function BillingPlans() {
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
toast.error(data.error || 'Failed to cancel subscription.');
|
||||
toast.error(data.error || t('billing.cancelFailed'));
|
||||
return;
|
||||
}
|
||||
toast.success(t('billing.cancelSuccess') || "Votre abonnement a été résilié avec succès. Il prendra fin à la fin de la période de facturation en cours.");
|
||||
toast.success(t('billing.cancelSuccess'));
|
||||
queryClient.invalidateQueries({ queryKey: ['billing', 'status'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['usage', 'current'] });
|
||||
} catch (err) {
|
||||
console.error('[BillingPlans] cancel error:', err);
|
||||
toast.error('Failed to cancel subscription.');
|
||||
toast.error(t('billing.cancelFailed'));
|
||||
} finally {
|
||||
setCancelLoading(false);
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export function BillingPlans() {
|
||||
name: t('billing.proPlan'),
|
||||
price: status?.prices?.PRO?.[interval]?.display ??
|
||||
(interval === 'month' ? (t('billing.proPrice') || '9,90€') : (t('billing.proAnnualPrice') || '99€')),
|
||||
period: interval === 'month' ? '/mois' : '/an',
|
||||
period: interval === 'month' ? t('billing.perMonth') : t('billing.perYear'),
|
||||
description: t('billing.proDescription') || 'Pour les consultants et créateurs exigeants.',
|
||||
features: [
|
||||
t('billing.proFeature1') || 'Notes illimitées',
|
||||
@@ -252,8 +252,7 @@ export function BillingPlans() {
|
||||
name: t('billing.businessPlan'),
|
||||
price: status?.prices?.BUSINESS?.[interval]?.display ??
|
||||
(interval === 'month' ? (t('billing.businessPrice') || '29,90€') : (t('billing.businessAnnualPrice') || '299€')),
|
||||
period: interval === 'month' ? '/mois' : '/an',
|
||||
description: t('billing.businessDescription') || 'Pour les équipes et chefs de produit.',
|
||||
period: interval === 'month' ? t('billing.perMonth') : t('billing.perYear'),
|
||||
features: [
|
||||
t('billing.businessFeature1') || '10 Collaborateurs inclus',
|
||||
t('billing.businessFeature2') || 'BYOK (13 fournisseurs)',
|
||||
@@ -560,7 +559,7 @@ export function BillingPlans() {
|
||||
)}
|
||||
>
|
||||
{t('billing.annual')}
|
||||
<span className="ms-1 text-primary/80 dark:text-primary">{t('billing.save')} ~17%</span>
|
||||
<span className="ms-1 text-primary/80 dark:text-primary">{t('billing.savePercent')}</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user