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.
119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
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
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|