From 028a8a093f5c7f548fc72dff194e1eccd078f253 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Thu, 31 Mar 2022 16:07:15 +0700 Subject: [PATCH] fix: replace /stripe/subscription requests --- .../Subscription/Subscription.types.ts | 1 + .../BillingSettings/ProjectsSummary.tsx | 31 ++----- studio/components/to-be-cleaned/Usage.tsx | 85 +++--------------- studio/hooks/queries/index.ts | 2 + studio/lib/constants/infrastructure.ts | 1 + .../project/[ref]/settings/billing/index.tsx | 86 ++++--------------- 6 files changed, 39 insertions(+), 167 deletions(-) diff --git a/studio/components/interfaces/Billing/Subscription/Subscription.types.ts b/studio/components/interfaces/Billing/Subscription/Subscription.types.ts index 84d96bc2e7e..ed09a01a208 100644 --- a/studio/components/interfaces/Billing/Subscription/Subscription.types.ts +++ b/studio/components/interfaces/Billing/Subscription/Subscription.types.ts @@ -12,4 +12,5 @@ export interface StripeProduct { name: string prod_id: string unit_amount: number + supabase_prod_id: string } diff --git a/studio/components/interfaces/Organization/BillingSettings/ProjectsSummary.tsx b/studio/components/interfaces/Organization/BillingSettings/ProjectsSummary.tsx index c2160a844d3..193749b8ce7 100644 --- a/studio/components/interfaces/Organization/BillingSettings/ProjectsSummary.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/ProjectsSummary.tsx @@ -1,14 +1,11 @@ import Link from 'next/link' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' -import { FC, useState, useEffect } from 'react' +import { FC, useEffect } from 'react' import { IconChevronRight, IconLoader } from '@supabase/ui' -import { useStore } from 'hooks' -import { API_URL } from 'lib/constants' -import { post } from 'lib/common/fetch' +import { useProjectSubscription, useStore } from 'hooks' import Panel from 'components/to-be-cleaned/Panel' -import { StripeSubscription } from 'components/interfaces/Billing' dayjs.extend(utc) @@ -18,35 +15,19 @@ interface ProjectSummaryProps { const ProjectSummary: FC = ({ project }) => { const { ui } = useStore() - - const [loading, setLoading] = useState(false) - const [subscription, setSubscription] = useState() + const { subscription, isLoading: loading, error } = useProjectSubscription(project.ref) const currentPeriodStart = subscription?.billing?.current_period_start ?? 0 const currentPeriodEnd = subscription?.billing?.current_period_end ?? 0 useEffect(() => { - getSubscription() - }, []) - - const getSubscription = async () => { - try { - setLoading(true) - const { data: subscription, error }: { data: StripeSubscription; error: any } = await post( - `${API_URL}/stripe/subscription`, - { subscription_id: project.subscription_id } - ) - if (error) throw error - setSubscription(subscription) - } catch (error: any) { + if (error) { ui.setNotification({ category: 'error', - message: `Failed to get project subscription: ${error.message}`, + message: `Failed to get project subscription: ${error?.message ?? 'unknown'}`, }) - } finally { - setLoading(false) } - } + }, [error]) return (
diff --git a/studio/components/to-be-cleaned/Usage.tsx b/studio/components/to-be-cleaned/Usage.tsx index 6b1c8384693..f442d03903b 100644 --- a/studio/components/to-be-cleaned/Usage.tsx +++ b/studio/components/to-be-cleaned/Usage.tsx @@ -2,8 +2,8 @@ import React, { FC, useEffect, useState } from 'react' import useSWR from 'swr' import { Loading, Typography } from '@supabase/ui' -import { useStore } from 'hooks' -import { API_URL } from 'lib/constants' +import { useProjectSubscription, useStore } from 'hooks' +import { API_URL, PRICING_TIER_PRODUCT_IDS } from 'lib/constants' import { get, post } from 'lib/common/fetch' import SparkBar from 'components/ui/SparkBar' @@ -193,45 +193,19 @@ const usageLimits = { const ProjectUsage: FC = ({ projectRef, subscription_id }) => { const { ui } = useStore() const { data: stats, error: usageError } = useSWR(`${API_URL}/projects/${projectRef}/usage`, get) - - const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) - const [subscription, setSubscription] = useState(null) + const { subscription, isLoading: loading, error } = useProjectSubscription(projectRef) useEffect(() => { - let cancel = false - - /** - * Get subscription information to decide - */ - const getSubscription = async () => { - try { - setLoading(true) - setError(null) - const { data, error } = await post(`${API_URL}/stripe/subscription`, { - subscription_id: subscription_id, - }) - if (!cancel) setSubscription(data) - if (error) throw error - } catch (error: any) { - ui.setNotification({ - category: 'error', - message: `Failed to get subscription: ${error.message}`, - }) - if (!cancel) setError(error) - } finally { - if (!cancel) setLoading(false) - } + if (error) { + ui.setNotification({ + category: 'error', + message: `Failed to get project subscription: ${error?.message ?? 'unknown'}`, + }) } + }, [error]) - getSubscription() - - return () => { - cancel = true - } - }, [subscription_id]) - - const tier = subscription && subscription.tier.unit_amount > 0 ? 'pro' : 'free' + const tier = + subscription?.tier?.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.PRO ? 'pro' : 'free' return ( @@ -296,41 +270,10 @@ export default ProjectUsage export const ProjectUsageMinimal: FC = ({ projectRef, subscription_id, filter }) => { const { data: stats, error: usageError } = useSWR(`${API_URL}/projects/${projectRef}/usage`, get) + const { subscription, isLoading: loading, error } = useProjectSubscription(projectRef) - const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) - const [subscription, setSubscription] = useState(null) - - useEffect(() => { - let cancel = false - - /** - * Get subscription information to decide - */ - const getSubscription = async () => { - try { - setLoading(true) - setError(null) - const { data, error } = await post(`${API_URL}/stripe/subscription`, { - subscription_id: subscription_id, - }) - if (!cancel) setSubscription(data) - if (error) throw error - } catch (error: any) { - if (!cancel) setError(error) - } finally { - if (!cancel) setLoading(false) - } - } - - getSubscription() - - return () => { - cancel = true - } - }, [subscription_id]) - - const tier = subscription && subscription.tier.unit_amount > 0 ? 'pro' : 'free' + const tier = + subscription?.tier?.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.PRO ? 'pro' : 'free' return ( diff --git a/studio/hooks/queries/index.ts b/studio/hooks/queries/index.ts index ab446d6f884..3ada4f98180 100644 --- a/studio/hooks/queries/index.ts +++ b/studio/hooks/queries/index.ts @@ -3,3 +3,5 @@ export * from './useOrganizationDetail' export * from './useProfile' export * from './useProjectAuthConfig' export * from './useSubscriptionStats' +export * from './useProjectSubscription' +export * from './useProjectPaygStatistics' diff --git a/studio/lib/constants/infrastructure.ts b/studio/lib/constants/infrastructure.ts index b1d2c65cb83..04b34ce9650 100644 --- a/studio/lib/constants/infrastructure.ts +++ b/studio/lib/constants/infrastructure.ts @@ -28,6 +28,7 @@ export const PRICING_TIER_LABELS = { export const PRICING_TIER_PRODUCT_IDS = { FREE: 'tier_free', PRO: 'tier_pro', + PAYG: 'tier_payg', } export const PRICING_TIER_FREE_KEY = 'FREE' diff --git a/studio/pages/project/[ref]/settings/billing/index.tsx b/studio/pages/project/[ref]/settings/billing/index.tsx index 0214685bea1..0acccb90b46 100644 --- a/studio/pages/project/[ref]/settings/billing/index.tsx +++ b/studio/pages/project/[ref]/settings/billing/index.tsx @@ -2,27 +2,12 @@ import dayjs from 'dayjs' import { FC, useEffect, useState } from 'react' import { observer } from 'mobx-react-lite' import { Typography, Loading, IconArrowRight } from '@supabase/ui' -import { get as _get, maxBy } from 'lodash' -import { Dictionary } from '@supabase/grid' -import { useStore, withAuth } from 'hooks' -import { post, get } from 'lib/common/fetch' -import { - API_URL, - DATE_FORMAT, - STRIPE_PRODUCT_IDS, - TIME_PERIODS_REPORTS, - TIME_PERIODS_BILLING, -} from 'lib/constants' +import { useProjectPaygStatistics, useProjectSubscription, useStore, withAuth } from 'hooks' +import { STRIPE_PRODUCT_IDS, TIME_PERIODS_REPORTS, TIME_PERIODS_BILLING } from 'lib/constants' import { SettingsLayout } from 'components/layouts' import LoadingUI from 'components/ui/Loading' -import { - PAYGUsage, - Subscription, - StripeSubscription, - chargeableProducts, - Invoices, -} from 'components/interfaces/Billing' +import { PAYGUsage, Subscription, Invoices } from 'components/interfaces/Billing' import ProjectUsage from 'components/to-be-cleaned/Usage' import DateRangePicker from 'components/to-be-cleaned/DateRangePicker' @@ -50,66 +35,25 @@ type SettingsProps = { } const Settings: FC = ({ project }) => { const { ui } = useStore() - const projectRef = ui.selectedProject?.ref - - const [loading, setLoading] = useState(true) - const [subscription, setSubscription] = useState() - const [paygStats, setPaygStats] = useState>() + const { + subscription, + isLoading: loading, + error, + } = useProjectSubscription(ui.selectedProject?.ref) + const { paygStats } = useProjectPaygStatistics( + ui.selectedProject?.ref, + subscription?.tier?.supabase_prod_id + ) const [dateRange, setDateRange] = useState() useEffect(() => { - if (projectRef) { - getSubscription() - } - }, [projectRef]) - - const getSubscription = async () => { - try { - setLoading(true) - const { data: subscription, error }: { data: StripeSubscription; error: any } = await post( - `${API_URL}/stripe/subscription`, - { - subscription_id: project.subscription_id, - } - ) - if (error) throw error - setSubscription(subscription) - - if (subscription.tier.prod_id === STRIPE_PRODUCT_IDS.PAYG) { - fetchPaygStatistics() - } - } catch (error: any) { + if (error) { ui.setNotification({ - error, category: 'error', - message: `Failed to get subscription: ${error.message}`, + message: `Failed to get project subscription: ${error?.message ?? 'unknown'}`, }) - } finally { - setLoading(false) } - } - - const fetchPaygStatistics = async () => { - const startDate = dayjs().utc().startOf('month').format(DATE_FORMAT) - const endDate = dayjs().utc().endOf('month').format(DATE_FORMAT) - const attributes = - 'total_db_size_bytes,total_db_egress_bytes,total_storage_size_bytes,total_storage_egress' - const url = `${API_URL}/projects/${ - project.ref - }/daily-stats?attribute=${attributes}&startDate=${encodeURIComponent( - startDate - )}&endDate=${encodeURIComponent(endDate)}&interval='1d'` - - const { data } = await get(url) - - const paygStats: any = {} - chargeableProducts.forEach((product: any) => { - product.features.forEach((feature: any) => { - paygStats[feature.attribute] = _get(maxBy(data, feature.attribute), feature.attribute) - }) - }) - setPaygStats(paygStats) - } + }, [error]) if (!subscription) { return