Skip to content

Commit

Permalink
feat: Remove Free Trial
Browse files Browse the repository at this point in the history
  • Loading branch information
amaury1093 committed Dec 15, 2023
1 parent 85416a6 commit 43563fd
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 69 deletions.
51 changes: 41 additions & 10 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@ import { Link as GLink, Loading, Page, Spacer, Text } from "@geist-ui/react";
import React from "react";

import { StripeMananageButton } from "../components/StripeManageButton";
import { SubGetStarted } from "../components/SubGetStarted/";
import { COMMERCIAL_LICENSE_PRODUCT_ID, productName } from "@/util/subs";
import {
GetStartedFreeTrial,
GetStartedLicense,
GetStartedSaas,
} from "../components/GetStarted";
import {
COMMERCIAL_LICENSE_PRODUCT_ID,
SAAS_100K_PRODUCT_ID,
SAAS_10K_PRODUCT_ID,
productName,
} from "@/util/subs";
import { useUser } from "@/util/useUser";
import { ApiUsage } from "./ApiUsage";
import styles from "./Dashboard.module.css";
import { formatDate } from "@/util/helpers";
import { SubscriptionWithPrice } from "@/supabase/domain.types";

export function Dashboard(): React.ReactElement {
const { userDetails, subscription, userFinishedLoading } = useUser();
const { userDetails, subscription, subscriptionLoaded } = useUser();

if (!userFinishedLoading) {
if (!subscriptionLoaded) {
return (
<Page>
<Loading />
Expand All @@ -26,8 +36,13 @@ export function Dashboard(): React.ReactElement {
<div>
<Text h2>Hello{userDetails?.full_name || ""},</Text>
<Text p>
Thanks for using the Reacher{" "}
{productName(subscription?.prices?.products)}!
<span>
{subscription
? `Thanks for using the Reacher ${productName(
subscription?.prices?.products
)}.`
: "Thank for signing up for Reacher."}
</span>
</Text>
{subscription && (
<>
Expand Down Expand Up @@ -69,10 +84,26 @@ export function Dashboard(): React.ReactElement {

<Spacer y={3} />

{subscription?.prices?.product_id !==
COMMERCIAL_LICENSE_PRODUCT_ID && <ApiUsage />}

<SubGetStarted subscription={subscription} />
{showContent(subscription)}
</Page>
);
}

function showContent(
subscription: SubscriptionWithPrice | null
): React.ReactElement {
switch (subscription?.prices?.product_id) {
case COMMERCIAL_LICENSE_PRODUCT_ID:
return <GetStartedLicense />;
case SAAS_10K_PRODUCT_ID:
case SAAS_100K_PRODUCT_ID:
return (
<>
<ApiUsage />
<GetStartedSaas />
</>
);
default:
return <GetStartedFreeTrial />;
}
}
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export function Footer(): React.ReactElement {

<div className={styles.bottom}>
<Text small>
© Reacher 2020-2022,{" "}
© Reacher 2020-2023,{" "}
<Link
href="https://help.reacher.email/mentions-lgales"
target="_blank"
Expand Down
15 changes: 15 additions & 0 deletions src/components/GetStarted/GetStartedFreeTrial.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Card, Text } from "@geist-ui/react";
import Link from "next/link";
import React from "react";

export function GetStartedFreeTrial(): React.ReactElement {
return (
<Card>
<Text h4>Select a Plan</Text>
<p>
Please head to the <Link href="/pricing">Pricing Page</Link> to
select a plan in order to use Reacher.
</p>
</Card>
);
}
3 changes: 3 additions & 0 deletions src/components/GetStarted/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./GetStartedLicense";
export * from "./GetStartedSaas";
export * from "./GetStartedFreeTrial";
7 changes: 2 additions & 5 deletions src/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ export function Nav(): React.ReactElement {
return (
<header className={styles.container}>
<div>
<a
className="flex"
href={user ? "/dashboard" : "https://reacher.email"}
>
<a className="flex" href={user ? "/" : "https://reacher.email"}>
<Image
alt="Reacher logo"
height={24}
Expand Down Expand Up @@ -89,7 +86,7 @@ export function Nav(): React.ReactElement {
<Spacer x={0.5} />
<Link href="/signup">
<Button auto type="success">
Get Started - 50 free emails
Sign up
</Button>
</Link>
</>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProductCard/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export function ProductCard({
: active
? "Current Plan"
: user
? "Upgrade Plan"
? "Select Plan"
: "Get Started"}
</Button>
}
Expand Down
21 changes: 0 additions & 21 deletions src/components/SubGetStarted/index.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ export * from "./Nav";
export * from "./Footer";
export * from "./Layout";
export * from "./ProductCard";
export * from "./SubGetStarted";
export * from "./SigninLayout";
17 changes: 14 additions & 3 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const getStaticProps: GetStaticProps = async () => {

export default function Index(): React.ReactElement {
const router = useRouter();
const { user, userFinishedLoading } = useUser();
const { user, userFinishedLoading, subscription } = useUser();
const [isRedirecting, setIsRedirecting] = useState(false);
const [emailSent, setEmailSent] = useState(false); // Set if we sent an email upon confirmation. Only do it once.

Expand Down Expand Up @@ -71,9 +71,20 @@ export default function Index(): React.ReactElement {
router.replace("/login").catch(sentryException);
} else if (userFinishedLoading && user) {
setIsRedirecting(true);
router.replace("/dashboard").catch(sentryException);
if (subscription) {
router.replace("/dashboard").catch(sentryException);
} else {
router.replace("/pricing").catch(sentryException);
}
}
}, [isRedirecting, router, userFinishedLoading, user, emailSent]);
}, [
isRedirecting,
router,
userFinishedLoading,
user,
emailSent,
subscription,
]);

return (
<>
Expand Down
59 changes: 37 additions & 22 deletions src/pages/pricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { getActiveProductWithPrices } from "@/util/supabaseClient";
import { useUser } from "@/util/useUser";
import { ProductWithPrice } from "@/supabase/domain.types";
import Link from "next/link";

export const getStaticProps: GetStaticProps = async () => {
const products = await getActiveProductWithPrices();
Expand Down Expand Up @@ -57,6 +56,9 @@ export default function Pricing({
<Text className="text-center" h2>
Pricing
</Text>
<Text p em className="text-center">
All plans include a 30-day money back guarantee.
</Text>

<Spacer y={2} />
<section>
Expand Down Expand Up @@ -122,30 +124,43 @@ export default function Pricing({
title="Can I verify 1 million (or more) emails?"
initialVisible
>
Reacher currently <strong>does not</strong> offer a
SaaS plan for verifying 1 million or more emails. If
you need to verify 1 million or more emails, please
check the Commercial License plan. You will need to
purchase your own servers separately and self-host
Reacher.
If you need to verify 200K, 500K, or 1+ million
emails, please check the{" "}
<strong>Commercial License Plan</strong>. You will
need to purchase your own servers separately and
self-host Reacher.
</Collapse>
<Collapse title="I need to verify one single email, can I use Reacher?">
Yes. Simple{" "}
<Link href="/signup">create an account</Link> and
you can use the textbox to verify your email.
<Collapse title="Can I get a refund?">
Sure. If you use Reacher and find that the
verifications are slow or incorrect, I&apos;ll
refund you the full amount if you email me ✉️{" "}
<a href="mailto:[email protected]">
[email protected]
</a>{" "}
within 30 days of your subscription.
</Collapse>
<Collapse title="Can I get a free trial of the Commercial Plan?">
Yes. Follow these steps in the{" "}
<a
href="https://help.reacher.email/self-host-guide#2a0e764e7cb94933b81c967be334dffd"
target="_blank"
rel="noopener noreferrer"
>
self-host guide
</a>
.
<Collapse title="Can I get a free trial?">
<p>
If you are interested in the SaaS 10K or 100K
plans, then no, Reacher does not offer any free
trial. There is however a 30-day money back
guarantee.
</p>

<p>
If you are interested in the Commercial License,
you can follow these steps in the{" "}
<a
href="https://help.reacher.email/self-host-guide#2a0e764e7cb94933b81c967be334dffd"
target="_blank"
rel="noopener noreferrer"
>
self-host guide
</a>{" "}
for a free trial .
</p>
</Collapse>
<Collapse title="I have another question.">
<Collapse title="Another question?">
Send me an email to{" "}
<a href="mailto:[email protected]">
📧 [email protected]
Expand Down
2 changes: 1 addition & 1 deletion src/util/subs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ if (!SAAS_10K_PRODUCT_ID || !COMMERCIAL_LICENSE_PRODUCT_ID) {

// Get the user-friendly name of a product.
export function productName(product?: Tables<"products">): string {
return product?.name || "Free Trial";
return product?.name || "None";
}

// Return the max monthly calls
Expand Down
12 changes: 8 additions & 4 deletions src/util/useUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface UserContext {
subscription: SubscriptionWithPrice | null;
user: User | null;
userDetails: Tables<"users"> | null;
userLoaded: boolean;
subscriptionLoaded: boolean;
userFinishedLoading: boolean;
}

Expand All @@ -59,7 +59,7 @@ interface UserContextProviderProps {
export const UserContextProvider = (
props: UserContextProviderProps
): React.ReactElement => {
const [userLoaded, setUserLoaded] = useState(false);
const [subscriptionLoaded, setSubscriptionLoaded] = useState(false);
const [userFinishedLoading, setUserFinishedLoading] = useState(false);
const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<User | null>(null);
Expand Down Expand Up @@ -97,6 +97,10 @@ export const UserContextProvider = (
.in("status", ["trialing", "active", "past_due"])
.order("current_period_start", { ascending: false });
useEffect(() => {
setUserDetails(null);
setSubscription(null);
setSubscriptionLoaded(false);
setUserFinishedLoading(false);
if (user) {
Promise.all([getUserDetails(), getSubscription()])
.then(([userDetails, sub]) => {
Expand All @@ -108,7 +112,7 @@ export const UserContextProvider = (
}
setUserDetails(userDetails.data);
setSubscription(sub.data?.[0]);
setUserLoaded(true);
setSubscriptionLoaded(true);
setUserFinishedLoading(true);
})
.catch(sentryException);
Expand All @@ -120,7 +124,7 @@ export const UserContextProvider = (
user,
userDetails,
userFinishedLoading,
userLoaded,
subscriptionLoaded,
subscription,
resetPassword: (email: string) =>
supabase.auth.api.resetPasswordForEmail(email, {
Expand Down
15 changes: 15 additions & 0 deletions supabase/migrations/20231215163735_segments.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Segment#0 defines all users that have made API calls, but did not subscribe
-- to any plan. Try to understand why.
CREATE OR REPLACE VIEW segment0 AS
SELECT
u.email,
COUNT(c.id) as number_of_calls,
u.created_at,
MAX(c.created_at) as last_api_call,
u.id
FROM calls c
LEFT JOIN subscriptions s ON c.user_id = s.user_id
JOIN auth.users u ON u.id = c.user_id
WHERE s.id IS NULL
AND u.created_at > NOW() - INTERVAL '3 day'
GROUP BY (u.id, s.id);

0 comments on commit 43563fd

Please sign in to comment.