Skip to content

Commit

Permalink
refactor: Use subsAndCalls instead of subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
storm1729 committed Mar 18, 2024
1 parent f752f58 commit 43201aa
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 105 deletions.
37 changes: 13 additions & 24 deletions src/app/[lang]/dashboard/ApiUsage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,38 @@

import { Capacity, Text } from "@geist-ui/react";
import { Loader } from "@geist-ui/react-icons";
import React, { useEffect, useState } from "react";
import { sentryException } from "@/util/sentry";
import { getApiUsage, subApiMaxCalls } from "@/util/subs";
import React from "react";
import { subApiMaxCalls } from "@/util/subs";
import styles from "./ApiUsage.module.css";
import { formatDate } from "@/util/helpers";
import { Dictionary } from "@/dictionaries";
import { SubscriptionWithPrice } from "@/supabase/supabaseServer";
import { createClient } from "@/supabase/client";
import { Tables } from "@/supabase/database.types";

interface ApiUsageProps {
d: Dictionary;
subscription: SubscriptionWithPrice | null;
subAndCalls: Tables<"sub_and_calls">;
}

export function ApiUsage({
subscription,
subAndCalls,
d,
}: ApiUsageProps): React.ReactElement {
const supabase = createClient();
const [apiCalls, setApiCalls] = useState<number | undefined>(undefined); // undefined means loading

useEffect(() => {
getApiUsage(supabase, subscription)
.then(setApiCalls)
.catch(sentryException);
}, [supabase, subscription]);

return (
<section>
<div className={styles.textContainer}>
<Text h5>
{d.dashboard.emails_this_month}
{subscription && (
{subAndCalls.subscription_id && (
<>
{" "}
(
{formatDate(
subscription.current_period_start,
subAndCalls.current_period_start as string,
d.lang
)}{" "}
-{" "}
{formatDate(
subscription.current_period_end,
subAndCalls.current_period_end as string,
d.lang
)}
)
Expand All @@ -54,21 +43,21 @@ export function ApiUsage({

<Text h4>
<Text type="success" span>
{apiCalls === undefined ? (
{subAndCalls.number_of_calls === null ? (
<Loader size={16} />
) : (
apiCalls
subAndCalls.number_of_calls
)}
</Text>
/{subApiMaxCalls(subscription?.prices?.product_id)}
/{subApiMaxCalls(subAndCalls.product_id)}
</Text>
</div>

<Capacity
className={styles.capacity}
value={
((apiCalls || 0) /
subApiMaxCalls(subscription?.prices?.product_id)) *
((subAndCalls.number_of_calls || 0) /
subApiMaxCalls(subAndCalls.product_id)) *
100
}
/>
Expand Down
14 changes: 7 additions & 7 deletions src/app/[lang]/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,40 @@ import { ApiUsage } from "./ApiUsage";
import { Tabs, TabsProps } from "./Tabs";
import { SAAS_10K_PRODUCT_ID } from "@/util/subs";
import { SubscriptionHeader } from "./SubscriptionHeader";
import { SubscriptionWithPrice } from "@/supabase/supabaseServer";
import { Dictionary } from "@/dictionaries";
import { ENABLE_BULK } from "@/util/helpers";
import { Tables } from "@/supabase/database.types";

interface DashboardProps {
children: React.ReactNode;
d: Dictionary;
showApiUsage?: boolean;
subscription: SubscriptionWithPrice | null;
subAndCalls: Tables<"sub_and_calls">;
tab: TabsProps["tab"] | false;
}
export function Dashboard({
children,
d,
showApiUsage = true,
subscription,
subAndCalls,
tab,
}: DashboardProps) {
return (
<Page>
<SubscriptionHeader d={d} subscription={subscription} />
<SubscriptionHeader d={d} subAndCalls={subAndCalls} />
<Spacer h={2} />
{showApiUsage && (
<>
<ApiUsage d={d} subscription={subscription} />
<ApiUsage d={d} subAndCalls={subAndCalls} />
<Spacer h={2} />
</>
)}
{tab !== false && ENABLE_BULK && (
<Tabs
d={d}
bulkDisabled={
!subscription ||
subscription?.prices?.product_id === SAAS_10K_PRODUCT_ID
!subAndCalls.subscription_id ||
subAndCalls.product_id === SAAS_10K_PRODUCT_ID
}
tab={tab}
/>
Expand Down
24 changes: 10 additions & 14 deletions src/app/[lang]/dashboard/SubscriptionHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,33 @@ import { formatDate } from "@/util/helpers";
import { Dictionary } from "@/dictionaries";

import styles from "./SubscriptionHeader.module.css";
import { SubscriptionWithPrice } from "@/supabase/supabaseServer";
import { DLink } from "@/components/DLink";
import { Tables } from "@/supabase/database.types";

interface SubscriptionHeaderProps {
d: Dictionary;
subscription: SubscriptionWithPrice | null;
subAndCalls: Tables<"sub_and_calls">;
}

export function SubscriptionHeader({
d,
subscription,
subAndCalls,
}: SubscriptionHeaderProps): React.ReactElement {
return (
<section className={styles.plan}>
<div>
<h2>{d.dashboard.header.hello}</h2>
<p>
<span>
{subscription
{subAndCalls.subscription_id
? d.dashboard.header.thanks_for_subscription.replace(
"%s",
productName(
subscription?.prices?.product_id,
d
)
productName(subAndCalls.product_id, d)
)
: d.dashboard.header.thanks_for_signup}
</span>
</p>
{subscription && (
{subAndCalls.subscription_id && (
<>
<StripeMananageButton d={d}>
{d.dashboard.header.manage_subscription}
Expand All @@ -55,18 +52,17 @@ export function SubscriptionHeader({
{d.dashboard.header.active_subscription}
</p>
<h3 className="text-right">
{productName(subscription?.prices?.product_id, d)}
{productName(subAndCalls.product_id, d)}
</h3>
{subscription?.status === "active" && subscription?.cancel_at && (
{subAndCalls.status === "active" && subAndCalls.cancel_at && (
<Text p small em className="text-right mt-0">
{d.dashboard.header.plan_ends_on.replace(
"%s",
formatDate(new Date(subscription.cancel_at), d.lang)
formatDate(new Date(subAndCalls.cancel_at), d.lang)
)}
</Text>
)}
{subscription?.prices?.product_id !==
COMMERCIAL_LICENSE_PRODUCT_ID && (
{subAndCalls.product_id !== COMMERCIAL_LICENSE_PRODUCT_ID && (
<div className="text-right">
<DLink
d={d}
Expand Down
6 changes: 3 additions & 3 deletions src/app/[lang]/dashboard/bulk/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { Dashboard } from "../Dashboard";
import { getSession, getSubscription } from "@/supabase/supabaseServer";
import { getSession, getSubAndCalls } from "@/supabase/supabaseServer";
import { dictionary } from "@/dictionaries";
import { cookies } from "next/headers";
import { createClient } from "@/supabase/server";
Expand All @@ -24,7 +24,7 @@ export default async function Bulk({
return redirect(`/${lang}/login`);
}

const subscription = await getSubscription();
const subAndCalls = await getSubAndCalls(session.user.id);
const d = await dictionary(lang);

const cookieStore = cookies();
Expand All @@ -36,7 +36,7 @@ export default async function Bulk({
const bulkJobs = res.data;

return (
<Dashboard d={d} subscription={subscription} tab="bulk">
<Dashboard d={d} subAndCalls={subAndCalls} tab="bulk">
<Csv d={d} />
<Spacer h={2} />
<BulkHistory d={d} initialBulksJobs={bulkJobs} />
Expand Down
6 changes: 3 additions & 3 deletions src/app/[lang]/dashboard/commercial_license/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dictionary } from "@/dictionaries";
import React from "react";
import { GetStartedCommercial } from "./GetStartedCommercial";
import { Dashboard } from "../Dashboard";
import { getSession, getSubscription } from "@/supabase/supabaseServer";
import { getSession, getSubAndCalls } from "@/supabase/supabaseServer";
import { redirect } from "next/navigation";

export default async function CommercialLicensePage({
Expand All @@ -15,13 +15,13 @@ export default async function CommercialLicensePage({
return redirect(`/${lang}/login`);
}

const subscription = await getSubscription();
const subAndCalls = await getSubAndCalls(session.user.id);
const d = await dictionary(lang);

return (
<Dashboard
d={d}
subscription={subscription}
subAndCalls={subAndCalls}
showApiUsage={false}
tab={false}
>
Expand Down
6 changes: 3 additions & 3 deletions src/app/[lang]/dashboard/verify/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Dashboard } from "../Dashboard";
import { GetStartedSaaS } from "./GetStartedSaaS";
import {
getSession,
getSubscription,
getSubAndCalls,
getUserDetails,
} from "@/supabase/supabaseServer";
import { GetStartedApi } from "./GetStartedApi";
Expand All @@ -21,15 +21,15 @@ export default async function VerifySingle({
return redirect(`/${lang}/login`);
}

const subscription = await getSubscription();
const userDetails = await getUserDetails();
const subAndCalls = await getSubAndCalls(session.user.id);
const d = await dictionary(lang);

return (
<Dashboard
showApiUsage={true}
subAndCalls={subAndCalls}
d={d}
subscription={subscription}
tab="verify"
>
<GetStartedSaaS userDetails={userDetails} d={d} />
Expand Down
10 changes: 5 additions & 5 deletions src/app/api/v1/bulk/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { supabaseAdmin } from "@/supabase/supabaseAdmin";
import { sentryException } from "@/util/sentry";
import { ENABLE_BULK, getWebappURL } from "@/util/helpers";
import { isEarlyResponse } from "@/app/api/v0/check_email/checkUserInDb";
import { SAAS_100K_PRODUCT_ID, getApiUsage } from "@/util/subs";
import { SAAS_100K_PRODUCT_ID } from "@/util/subs";
import { Json, Tables } from "@/supabase/database.types";
import { cookies } from "next/headers";
import { createClient } from "@/supabase/server";
import { getSubscription } from "@/supabase/supabaseServer";
import { getSubAndCalls } from "@/supabase/supabaseServer";

interface BulkPayload {
input_type: "array";
Expand Down Expand Up @@ -43,9 +43,9 @@ export const POST = async (req: NextRequest): Promise<Response> => {
}
);
}
const subscripton = await getSubscription();
const subAndCalls = await getSubAndCalls(user.id);

if (subscripton?.prices?.product_id !== SAAS_100K_PRODUCT_ID) {
if (subAndCalls.product_id !== SAAS_100K_PRODUCT_ID) {
return Response.json(
{
error: "Bulk verification is not available on your plan",
Expand All @@ -58,7 +58,7 @@ export const POST = async (req: NextRequest): Promise<Response> => {

const payload: BulkPayload = await req.json();

const callsUsed = await getApiUsage(supabase, subscripton);
const callsUsed = subAndCalls.number_of_calls || 0;

if (payload.input.length + callsUsed > 100_000) {
return Response.json(
Expand Down
4 changes: 4 additions & 0 deletions src/supabase/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,14 @@ export interface Database {
};
sub_and_calls: {
Row: {
cancel_at: string | null;
current_period_end: string | null;
current_period_start: string | null;
number_of_calls: number | null;
product_id: string | null;
status:
| Database["public"]["Enums"]["subscription_status"]
| null;
subscription_id: string | null;
user_id: string | null;
};
Expand Down
30 changes: 30 additions & 0 deletions src/supabase/supabaseServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,33 @@ export const getActiveProductsWithPrices = async () => {
if (error) throw error;
return (data as unknown as ProductWithPrice[]) ?? [];
};

// Get the api calls of a user in the past month/billing period.
// Once we upgrade to postgres 15, we can use the security_invoker = true
// and skip the userId here.
export async function getSubAndCalls(
userId: string
): Promise<Tables<"sub_and_calls">> {
const cookieStore = cookies();
const supabase = createClient(cookieStore);

const { data, error } = await supabase
.from("sub_and_calls")
.select("*")
.eq("user_id", userId);
if (error) {
throw error;
}

if (!data[0]) {
throw new Error(
`No sub_and_calls found for user ${
(await supabase.auth.getUser()).data.user?.id
}.`
);
}

console.log("aaa", data[0]);

return data[0];
}
Loading

0 comments on commit 43201aa

Please sign in to comment.