Files
Stirling-PDF/frontend/src/saas/components/shared/ManageBillingButton.tsx
T
James BruntonandGitHub fa8c52b2be Add SaaS frontend code (#5879)
# Description of Changes
Adds the code for the SaaS frontend as proprietary code to the OSS repo.
This version of the code is adapted from 22/1/2026, which was the last
SaaS version based on the 'V2' design. This will move us closer to being
able to have the OSS products understand whether the user has a SaaS
account, and provide the correct UI in those cases.
2026-03-11 11:53:54 +00:00

65 lines
1.7 KiB
TypeScript

import { useState } from 'react';
import { supabase } from '@app/auth/supabase';
import { Button } from '@mantine/core';
import { usePlans } from '@app/hooks/usePlans';
interface TrialStatus {
isTrialing: boolean;
trialEnd: string;
daysRemaining: number;
hasPaymentMethod: boolean;
hasScheduledSub: boolean;
}
export function ManageBillingButton({
returnUrl = typeof window !== 'undefined' ? window.location.href : '/',
children = 'Manage billing',
trialStatus,
}: {
returnUrl?: string;
children?: React.ReactNode;
trialStatus?: TrialStatus;
}) {
const [loading, setLoading] = useState(false);
const [err, setErr] = useState<string | null>(null);
const { data } = usePlans();
// Hide for free plan users
if (!data || data.currentPlan.id === 'free') {
return null;
}
// Hide for trial users who haven't scheduled a subscription yet
if (trialStatus?.isTrialing && !trialStatus.hasScheduledSub) {
return null;
}
const onClick = async () => {
setLoading(true);
setErr(null);
try {
const { data, error } = await supabase.functions.invoke('manage-billing', {
body: {
name: 'Functions',
return_url: returnUrl},
})
if (error) throw error;
if (!data || 'error' in data) throw new Error((data as any)?.error ?? 'No portal URL');
window.location.href = (data as any).url;
} catch (e: any) {
setErr(e.message ?? 'Could not open billing portal');
} finally {
setLoading(false);
}
};
return (
<div>
<Button onClick={onClick} disabled={loading} className="px-4 py-2 rounded bg-black text-white">
{loading ? 'Opening…' : children}
</Button>
{err && <div className="mt-2 text-red-600">{err}</div>}
</div>
);
}