-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
show free trial dialog only in stripe test mode
- Loading branch information
1 parent
fb759b4
commit cff17fb
Showing
5 changed files
with
207 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
'use client' | ||
|
||
import { Button } from "@/components/ui/button" | ||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" | ||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" | ||
import { DIGGER_FEATURES } from '@/constants' | ||
import { getActiveProductsWithPrices } from '@/data/user/organizations' | ||
import { useSAToastMutation } from '@/hooks/useSAToastMutation' | ||
import { createTrialSubSuccessCB } from '@/lib/payments/paymentGatewayUtils' | ||
import { startTrial } from '@/lib/payments/paymentUtilsServer' | ||
import type { UnwrapPromise } from '@/types' | ||
import { useState } from 'react' | ||
|
||
|
||
|
||
function getProductsSortedByPrice( | ||
activeProducts: UnwrapPromise<ReturnType<typeof getActiveProductsWithPrices>> | ||
) { | ||
if (!activeProducts) return [] | ||
const products = activeProducts.flatMap((product) => { | ||
const prices = Array.isArray(product.prices) ? product.prices : [product.prices] | ||
return prices.map((price) => ({ | ||
...product, | ||
price, | ||
priceString: new Intl.NumberFormat('en-US', { | ||
style: 'currency', | ||
currency: price?.currency ?? undefined, | ||
minimumFractionDigits: 0, | ||
}).format((price?.unit_amount || 0) / 100), | ||
})) | ||
}) | ||
return products | ||
.sort((a, b) => (a?.price?.unit_amount ?? 0) - (b?.price?.unit_amount ?? 0)) | ||
.filter(Boolean) | ||
} | ||
type FreeTrialDialogProps = { | ||
organizationId: string | ||
activeProducts: UnwrapPromise<ReturnType<typeof getActiveProductsWithPrices>> | ||
isOrganizationAdmin: boolean, | ||
defaultOpen?: boolean | ||
} | ||
export function FreeTrialDialog({ organizationId, activeProducts, isOrganizationAdmin, defaultOpen = false }: FreeTrialDialogProps) { | ||
// this should be true | ||
const [open, setOpen] = useState(defaultOpen) | ||
// supabase cannot sort by foreign table, so we do it here | ||
const productsSortedByPrice = getProductsSortedByPrice(activeProducts); | ||
|
||
|
||
const { mutate, isLoading } = useSAToastMutation( | ||
async (priceId: string) => { | ||
return await startTrial(organizationId, priceId) | ||
}, | ||
{ | ||
loadingMessage: 'Starting trial...', | ||
errorMessage: 'Failed to start trial', | ||
successMessage: 'Redirecting to checkout...', | ||
onSuccess(response) { | ||
if (response.status === 'success' && response.data) { | ||
createTrialSubSuccessCB(response.data) | ||
} | ||
}, | ||
} | ||
) | ||
|
||
return ( | ||
<Dialog open={open} onOpenChange={setOpen}> | ||
<DialogContent className="sm:max-w-[900px]"> | ||
<DialogHeader> | ||
<DialogTitle>Start Your Free Trial</DialogTitle> | ||
<DialogDescription> | ||
Your organization doesn't have an active subscription. Choose a plan to start your free trial. | ||
</DialogDescription> | ||
</DialogHeader> | ||
<div className="grid grid-cols-1 md:grid-cols-auto gap-4"> | ||
{productsSortedByPrice.map((product) => ( | ||
<Card key={product.id}> | ||
<CardHeader> | ||
<CardTitle>{product.name}</CardTitle> | ||
<CardDescription>{product.priceString} per {product.price?.interval}</CardDescription> | ||
</CardHeader> | ||
<CardContent> | ||
<ul className="space-y-2"> | ||
{DIGGER_FEATURES.map((feature, index) => { | ||
const FeatureIcon = feature.icon | ||
return ( | ||
<li key={index} className="flex items-start"> | ||
<FeatureIcon className="mr-2 h-4 w-4 mt-1 flex-shrink-0" /> | ||
<div> | ||
<span className="font-semibold">{feature.title}</span> | ||
<p className="text-sm text-muted-foreground">{feature.description}</p> | ||
</div> | ||
</li> | ||
) | ||
})} | ||
</ul> | ||
{ | ||
isOrganizationAdmin ? ( | ||
<Button | ||
className="mt-4 w-full" | ||
onClick={() => mutate(product.price?.id ?? '')} | ||
disabled={isLoading} | ||
> | ||
{isLoading ? 'Starting...' : 'Start Free Trial'} | ||
</Button> | ||
) : ( | ||
<Button | ||
className="mt-4 w-full" | ||
disabled | ||
> | ||
Contact your admin | ||
</Button> | ||
) | ||
} | ||
</CardContent> | ||
</Card> | ||
))} | ||
</div> | ||
|
||
</DialogContent> | ||
</Dialog> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function getIsStripeTestMode() { | ||
return process.env.STRIPE_SECRET_KEY.startsWith('sk_test') || typeof process.env.STRIPE_SECRET_KEY === 'undefined' | ||
} |