mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
# 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.
65 lines
1.7 KiB
TypeScript
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>
|
|
);
|
|
}
|