mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
Move billing components to own files
This commit is contained in:
1 parent
414f6c7854
commit
71de50995f
5 files changed
+175
-109
No files matched your search
@@ -24,12 +24,6 @@ export const generateLogsMenu = (project?: Project): ProductMenuGroup[] => {
|
||||
url: `/project/${ref}/logs/postgres-logs`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Query logs(?)',
|
||||
key: 'api-logs',
|
||||
url: `/project/${ref}/database/insights?`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'PostgREST logs',
|
||||
key: 'postgrest-logs',
|
||||
|
||||
@@ -39,95 +39,25 @@ export const generateSettingsMenu = (ref: string, project?: ProjectBase): Produc
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Project settings',
|
||||
items: [
|
||||
{
|
||||
name: 'Authentication',
|
||||
key: 'auth',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/auth/settings`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Storage',
|
||||
key: 'storage',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/storage/settings`,
|
||||
items: [],
|
||||
// disabled: canReadStorage,
|
||||
},
|
||||
{
|
||||
name: 'Billing & Usage',
|
||||
key: 'billing',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/billing`,
|
||||
items: [],
|
||||
// disabled: canReadInvoices && canReadSubscriptions,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'API',
|
||||
items: [
|
||||
{
|
||||
name: 'API Keys',
|
||||
key: 'generaleeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'API Settings',
|
||||
key: 'databaseeeeeeeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/databaseeeeee`,
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Project keys',
|
||||
items: [
|
||||
{
|
||||
name: 'Roll keys',
|
||||
key: 'generaleeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'JWT',
|
||||
key: 'databaseeeeeeeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/databaseeeeee`,
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Backups?',
|
||||
items: [
|
||||
{
|
||||
name: 'Backups?',
|
||||
key: 'generaleeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`,
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Billing',
|
||||
items: [
|
||||
{
|
||||
name: 'Subscription',
|
||||
key: 'generaleeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`,
|
||||
key: 'billing',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/billing/subscription`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Usage',
|
||||
key: 'databaseeeeeeeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/databaseeeeee`,
|
||||
key: 'usage',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/billing/usage`,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Invoices',
|
||||
key: 'databaseeeeeeeeeee',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/databaseeeeee`,
|
||||
key: 'billing/invoices',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/billing/invoices`,
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FC, useEffect } from 'react'
|
||||
|
||||
import { SettingsLayout } from 'components/layouts'
|
||||
import LoadingUI from 'components/ui/Loading'
|
||||
import OveragesBanner from 'components/ui/OveragesBanner/OveragesBanner'
|
||||
import { useProjectSubscription, useStore } from 'hooks'
|
||||
import { NextPageWithLayout, Project } from 'types'
|
||||
|
||||
import { Invoices } from 'components/interfaces/Billing'
|
||||
|
||||
const ProjectBilling: NextPageWithLayout = () => {
|
||||
const { ui } = useStore()
|
||||
const project = ui.selectedProject
|
||||
|
||||
return (
|
||||
<div className="content h-full w-full overflow-y-auto">
|
||||
<div className="mx-auto w-full">
|
||||
<Settings project={project} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ProjectBilling.getLayout = (page) => (
|
||||
<SettingsLayout title="Billing and Usage">{page}</SettingsLayout>
|
||||
)
|
||||
|
||||
export default observer(ProjectBilling)
|
||||
|
||||
interface SettingsProps {
|
||||
project?: Project
|
||||
}
|
||||
|
||||
const Settings: FC<SettingsProps> = ({ project }) => {
|
||||
const { ui } = useStore()
|
||||
const projectTier = ui.selectedProject?.subscription_tier
|
||||
|
||||
const {
|
||||
subscription,
|
||||
isLoading: loading,
|
||||
error,
|
||||
} = useProjectSubscription(ui.selectedProject?.ref)
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to get project subscription: ${error?.message ?? 'unknown'}`,
|
||||
})
|
||||
}
|
||||
}, [error])
|
||||
|
||||
if (!subscription) {
|
||||
return <LoadingUI />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl space-y-8 p-4">
|
||||
{projectTier !== undefined && <OveragesBanner tier={projectTier} />}
|
||||
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-lg">Invoices</h4>
|
||||
<Invoices projectRef={ui.selectedProject?.ref ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { Loading } from 'ui'
|
||||
|
||||
import { SettingsLayout } from 'components/layouts'
|
||||
import LoadingUI from 'components/ui/Loading'
|
||||
import OveragesBanner from 'components/ui/OveragesBanner/OveragesBanner'
|
||||
import { useProjectPaygStatistics, useProjectSubscription, useStore } from 'hooks'
|
||||
import { NextPageWithLayout, Project } from 'types'
|
||||
|
||||
import { Subscription } from 'components/interfaces/Billing'
|
||||
import { PaygStats } from 'components/interfaces/Billing/PAYGUsage/PAYGUsage.types'
|
||||
|
||||
const ProjectBilling: NextPageWithLayout = () => {
|
||||
const { ui } = useStore()
|
||||
const project = ui.selectedProject
|
||||
|
||||
return (
|
||||
<div className="content h-full w-full overflow-y-auto">
|
||||
<div className="mx-auto w-full">
|
||||
<Settings project={project} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ProjectBilling.getLayout = (page) => (
|
||||
<SettingsLayout title="Billing and Usage">{page}</SettingsLayout>
|
||||
)
|
||||
|
||||
export default observer(ProjectBilling)
|
||||
|
||||
interface SettingsProps {
|
||||
project?: Project
|
||||
}
|
||||
|
||||
const Settings: FC<SettingsProps> = ({ project }) => {
|
||||
const { ui } = useStore()
|
||||
const projectTier = ui.selectedProject?.subscription_tier
|
||||
|
||||
const {
|
||||
subscription,
|
||||
isLoading: loading,
|
||||
error,
|
||||
} = useProjectSubscription(ui.selectedProject?.ref)
|
||||
|
||||
const { paygStats }: { paygStats: PaygStats | undefined } = useProjectPaygStatistics(
|
||||
ui.selectedProject?.ref,
|
||||
subscription?.tier?.supabase_prod_id
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to get project subscription: ${error?.message ?? 'unknown'}`,
|
||||
})
|
||||
}
|
||||
}, [error])
|
||||
|
||||
if (!subscription) {
|
||||
return <LoadingUI />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl space-y-8 p-4">
|
||||
{projectTier !== undefined && <OveragesBanner tier={projectTier} />}
|
||||
<Subscription
|
||||
loading={loading}
|
||||
project={project}
|
||||
subscription={subscription}
|
||||
paygStats={paygStats}
|
||||
currentPeriodStart={subscription?.billing.current_period_start}
|
||||
currentPeriodEnd={subscription?.billing.current_period_end}
|
||||
/>
|
||||
{loading ? (
|
||||
<Loading active={loading}>
|
||||
<div className="mb-8 w-full overflow-hidden rounded border border-panel-border-light dark:border-panel-border-dark">
|
||||
<div className="flex items-center justify-center bg-panel-body-light px-6 py-6 dark:bg-panel-body-dark">
|
||||
<p>Loading usage breakdown</p>
|
||||
</div>
|
||||
</div>
|
||||
</Loading>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+12
-27
@@ -1,20 +1,19 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Loading, IconArrowRight } from 'ui'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { IconArrowRight, Loading } from 'ui'
|
||||
|
||||
import { Project, NextPageWithLayout } from 'types'
|
||||
import { useProjectPaygStatistics, useProjectSubscription, useStore } 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 OveragesBanner from 'components/ui/OveragesBanner/OveragesBanner'
|
||||
import DateRangePicker from 'components/to-be-cleaned/DateRangePicker'
|
||||
import { PAYGUsage, Subscription, Invoices } from 'components/interfaces/Billing'
|
||||
import { PAYGUsage } from 'components/interfaces/Billing'
|
||||
import { PaygStats } from 'components/interfaces/Billing/PAYGUsage/PAYGUsage.types'
|
||||
import ProjectUsage from 'components/interfaces/Settings/ProjectUsageBars/ProjectUsageBars'
|
||||
import { SettingsLayout } from 'components/layouts'
|
||||
import DateRangePicker from 'components/to-be-cleaned/DateRangePicker'
|
||||
import LoadingUI from 'components/ui/Loading'
|
||||
import { useProjectPaygStatistics, useProjectSubscription, useStore } from 'hooks'
|
||||
import { STRIPE_PRODUCT_IDS, TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants'
|
||||
import { NextPageWithLayout, Project } from 'types'
|
||||
|
||||
const ProjectBilling: NextPageWithLayout = () => {
|
||||
const ProjectBillingUsage: NextPageWithLayout = () => {
|
||||
const { ui } = useStore()
|
||||
const project = ui.selectedProject
|
||||
|
||||
@@ -27,11 +26,11 @@ const ProjectBilling: NextPageWithLayout = () => {
|
||||
)
|
||||
}
|
||||
|
||||
ProjectBilling.getLayout = (page) => (
|
||||
ProjectBillingUsage.getLayout = (page) => (
|
||||
<SettingsLayout title="Billing and Usage">{page}</SettingsLayout>
|
||||
)
|
||||
|
||||
export default observer(ProjectBilling)
|
||||
export default observer(ProjectBillingUsage)
|
||||
|
||||
interface SettingsProps {
|
||||
project?: Project
|
||||
@@ -39,7 +38,6 @@ interface SettingsProps {
|
||||
|
||||
const Settings: FC<SettingsProps> = ({ project }) => {
|
||||
const { ui } = useStore()
|
||||
const projectTier = ui.selectedProject?.subscription_tier
|
||||
|
||||
const [dateRange, setDateRange] = useState<any>()
|
||||
|
||||
@@ -69,15 +67,6 @@ const Settings: FC<SettingsProps> = ({ project }) => {
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl space-y-8 p-4">
|
||||
{projectTier !== undefined && <OveragesBanner tier={projectTier} />}
|
||||
<Subscription
|
||||
loading={loading}
|
||||
project={project}
|
||||
subscription={subscription}
|
||||
paygStats={paygStats}
|
||||
currentPeriodStart={subscription?.billing.current_period_start}
|
||||
currentPeriodEnd={subscription?.billing.current_period_end}
|
||||
/>
|
||||
{loading ? (
|
||||
<Loading active={loading}>
|
||||
<div className="mb-8 w-full overflow-hidden rounded border border-panel-border-light dark:border-panel-border-dark">
|
||||
@@ -115,10 +104,6 @@ const Settings: FC<SettingsProps> = ({ project }) => {
|
||||
) : (
|
||||
<ProjectUsage projectRef={project?.ref} />
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-lg">Invoices</h4>
|
||||
<Invoices projectRef={ui.selectedProject?.ref ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user