mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
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.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAuth } from '@app/auth/UseSession';
|
||||
import { TrialExpiredModal } from '@app/components/shared/TrialExpiredModal';
|
||||
import StripeCheckout from '@app/components/shared/StripeCheckoutSaas';
|
||||
|
||||
/**
|
||||
* Bootstrap component that shows the trial expired modal when a user's trial has ended
|
||||
* and they haven't added a payment method. Shows once per user per expired trial.
|
||||
*/
|
||||
export default function TrialExpiredBootstrap() {
|
||||
const { user, trialStatus, isPro } = useAuth();
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [checkoutOpened, setCheckoutOpened] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Close modal if user logs out or session expires
|
||||
if (!user) {
|
||||
if (showModal) {
|
||||
console.debug('[TrialExpired] User logged out, closing modal');
|
||||
setShowModal(false);
|
||||
}
|
||||
if (checkoutOpened) {
|
||||
setCheckoutOpened(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only check conditions when auth is fully loaded
|
||||
if (trialStatus === null || isPro === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build localStorage key unique to this user
|
||||
const storageKey = `trialExpiredModalShown_${user.id}`;
|
||||
const hasSeenModal = localStorage.getItem(storageKey) === 'true';
|
||||
|
||||
// If user is currently trialing, clear any previous "seen" flag
|
||||
// This handles the edge case where a user might re-enter a trial
|
||||
if (trialStatus.isTrialing) {
|
||||
if (hasSeenModal) {
|
||||
console.debug('[TrialExpired] User is trialing, clearing seen flag');
|
||||
localStorage.removeItem(storageKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all conditions are met to show the modal
|
||||
const isExpired =
|
||||
trialStatus.status === 'incomplete_expired' || trialStatus.status === 'canceled';
|
||||
const hasNoPayment = !trialStatus.hasPaymentMethod && !trialStatus.hasScheduledSub;
|
||||
const wasDowngraded = !isPro;
|
||||
const trialEndedRecently = trialStatus.daysRemaining === 0;
|
||||
|
||||
const shouldShowModal =
|
||||
isExpired && hasNoPayment && wasDowngraded && trialEndedRecently && !hasSeenModal;
|
||||
|
||||
if (shouldShowModal) {
|
||||
console.debug('[TrialExpired] Showing trial expired modal', {
|
||||
status: trialStatus.status,
|
||||
daysRemaining: trialStatus.daysRemaining,
|
||||
hasPaymentMethod: trialStatus.hasPaymentMethod,
|
||||
hasScheduledSub: trialStatus.hasScheduledSub,
|
||||
isPro,
|
||||
});
|
||||
setShowModal(true);
|
||||
}
|
||||
}, [user, trialStatus, isPro, showModal, checkoutOpened]);
|
||||
|
||||
const handleClose = () => {
|
||||
if (user) {
|
||||
const storageKey = `trialExpiredModalShown_${user.id}`;
|
||||
localStorage.setItem(storageKey, 'true');
|
||||
console.debug('[TrialExpired] Modal dismissed, marking as seen');
|
||||
}
|
||||
setShowModal(false);
|
||||
};
|
||||
|
||||
const handleSubscribe = () => {
|
||||
console.debug('[TrialExpired] User clicked Subscribe to Pro');
|
||||
setCheckoutOpened(true);
|
||||
};
|
||||
|
||||
const handleCheckoutSuccess = () => {
|
||||
console.debug('[TrialExpired] Subscription successful, refreshing page');
|
||||
// Close modal and refresh to update subscription status
|
||||
handleClose();
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleCheckoutClose = () => {
|
||||
console.debug('[TrialExpired] Checkout closed');
|
||||
setCheckoutOpened(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TrialExpiredModal
|
||||
opened={showModal && !checkoutOpened}
|
||||
onClose={handleClose}
|
||||
onSubscribe={handleSubscribe}
|
||||
/>
|
||||
|
||||
{user && (
|
||||
<StripeCheckout
|
||||
opened={checkoutOpened}
|
||||
onClose={handleCheckoutClose}
|
||||
purchaseType="subscription"
|
||||
planId="pro"
|
||||
creditsPack={null}
|
||||
planName="Pro"
|
||||
onSuccess={handleCheckoutSuccess}
|
||||
onError={(error) => console.error('[TrialExpired] Checkout error:', error)}
|
||||
isTrialConversion={false} // Trial already ended, so this is not a conversion
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user