feat: new billing and restrict access when trial has ended
* fix: simply billing * fix usage graph * imporve billing more + supporter prompt on self-hosting * revert service change * revert query builder * fix: comments
This commit is contained in:
committed by
GitHub
parent
bbd30ca6e0
commit
9cafd61b25
@@ -6,7 +6,7 @@ import { keepPreviousData, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import type { UIMessage } from 'ai';
|
||||
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId_/chat')({
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId/chat')({
|
||||
component: Component,
|
||||
pendingComponent: FullPageLoadingState,
|
||||
head: () => {
|
||||
@@ -32,7 +32,7 @@ import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { Link, createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/dashboards',
|
||||
'/_app/$organizationId/$projectId/dashboards',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -53,7 +53,7 @@ type Layout = {
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/dashboards_/$dashboardId',
|
||||
'/_app/$organizationId/$projectId/dashboards_/$dashboardId',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -6,7 +6,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsIsoDateTime, useQueryState } from 'nuqs';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/events/_tabs/conversions',
|
||||
'/_app/$organizationId/$projectId/events/_tabs/conversions',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsIsoDateTime, useQueryState } from 'nuqs';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/events/_tabs/events',
|
||||
'/_app/$organizationId/$projectId/events/_tabs/events',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/events/_tabs/',
|
||||
'/_app/$organizationId/$projectId/events/_tabs/',
|
||||
)({
|
||||
component: Component,
|
||||
beforeLoad({ params }) {
|
||||
@@ -14,7 +14,7 @@ import type { IChartEvent } from '@openpanel/validation';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/events/_tabs/stats',
|
||||
'/_app/$organizationId/$projectId/events/_tabs/stats',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
|
||||
import { Outlet, createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/events/_tabs',
|
||||
'/_app/$organizationId/$projectId/events/_tabs',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
OverviewFilterButton,
|
||||
OverviewFiltersButtons,
|
||||
} from '@/components/overview/filters/overview-filters-buttons';
|
||||
import { LiveCounter } from '@/components/overview/live-counter';
|
||||
import { OverviewInterval } from '@/components/overview/overview-interval';
|
||||
import OverviewMetrics from '@/components/overview/overview-metrics';
|
||||
import { OverviewRange } from '@/components/overview/overview-range';
|
||||
import { OverviewShare } from '@/components/overview/overview-share';
|
||||
import OverviewTopDevices from '@/components/overview/overview-top-devices';
|
||||
import OverviewTopEvents from '@/components/overview/overview-top-events';
|
||||
import OverviewTopGeo from '@/components/overview/overview-top-geo';
|
||||
import OverviewTopPages from '@/components/overview/overview-top-pages';
|
||||
import OverviewTopSources from '@/components/overview/overview-top-sources';
|
||||
import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId/')({
|
||||
component: ProjectDashboard,
|
||||
head: () => {
|
||||
return {
|
||||
meta: [
|
||||
{
|
||||
title: createProjectTitle(PAGE_TITLES.DASHBOARD),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function ProjectDashboard() {
|
||||
const { projectId } = Route.useParams();
|
||||
return (
|
||||
<div>
|
||||
<div className="col gap-2 p-4">
|
||||
<div className="flex justify-between gap-2">
|
||||
<div className="flex gap-2">
|
||||
<OverviewRange />
|
||||
<OverviewInterval />
|
||||
<OverviewFilterButton mode="events" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<LiveCounter projectId={projectId} />
|
||||
<OverviewShare projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
<OverviewFiltersButtons />
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4 p-4 pt-0">
|
||||
<OverviewMetrics projectId={projectId} />
|
||||
<OverviewTopSources projectId={projectId} />
|
||||
<OverviewTopPages projectId={projectId} />
|
||||
<OverviewTopDevices projectId={projectId} />
|
||||
<OverviewTopEvents projectId={projectId} />
|
||||
<OverviewTopGeo projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/notifications/_tabs/',
|
||||
'/_app/$organizationId/$projectId/notifications/_tabs/',
|
||||
)({
|
||||
component: Component,
|
||||
beforeLoad({ params }) {
|
||||
@@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/notifications/_tabs/notifications',
|
||||
'/_app/$organizationId/$projectId/notifications/_tabs/notifications',
|
||||
)({
|
||||
component: Component,
|
||||
loader: async ({ context, params }) => {
|
||||
@@ -12,7 +12,7 @@ import { PencilRulerIcon, PlusIcon } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/notifications/_tabs/rules',
|
||||
'/_app/$organizationId/$projectId/notifications/_tabs/rules',
|
||||
)({
|
||||
component: Component,
|
||||
loader: async ({ context, params }) => {
|
||||
@@ -5,7 +5,7 @@ import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
|
||||
import { Outlet, createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/notifications/_tabs',
|
||||
'/_app/$organizationId/$projectId/notifications/_tabs',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -24,7 +24,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsInteger, useQueryState } from 'nuqs';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId_/pages')(
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId/pages')(
|
||||
{
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -9,7 +9,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsIsoDateTime, useQueryState } from 'nuqs';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/$profileId/_tabs/events',
|
||||
'/_app/$organizationId/$projectId/profiles/$profileId/_tabs/events',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -11,7 +11,7 @@ import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/$profileId/_tabs/',
|
||||
'/_app/$organizationId/$projectId/profiles/$profileId/_tabs/',
|
||||
)({
|
||||
component: Component,
|
||||
loader: async ({ context, params }) => {
|
||||
@@ -11,7 +11,7 @@ import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { Outlet, createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/$profileId/_tabs',
|
||||
'/_app/$organizationId/$projectId/profiles/$profileId/_tabs',
|
||||
)({
|
||||
component: Component,
|
||||
loader: async ({ context, params }) => {
|
||||
@@ -7,7 +7,7 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/_tabs/anonymous',
|
||||
'/_app/$organizationId/$projectId/profiles/_tabs/anonymous',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -7,7 +7,7 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/_tabs/identified',
|
||||
'/_app/$organizationId/$projectId/profiles/_tabs/identified',
|
||||
)({
|
||||
head: () => {
|
||||
return {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/_tabs/',
|
||||
'/_app/$organizationId/$projectId/profiles/_tabs/',
|
||||
)({
|
||||
component: Component,
|
||||
beforeLoad({ params }) {
|
||||
@@ -6,7 +6,7 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/_tabs/power-users',
|
||||
'/_app/$organizationId/$projectId/profiles/_tabs/power-users',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -5,7 +5,7 @@ import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
|
||||
import { Outlet, createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/_tabs',
|
||||
'/_app/$organizationId/$projectId/profiles/_tabs',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -12,7 +12,7 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/realtime',
|
||||
'/_app/$organizationId/$projectId/realtime',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -33,7 +33,7 @@ import { PlusIcon } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/references',
|
||||
'/_app/$organizationId/$projectId/references',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -4,7 +4,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/reports',
|
||||
'/_app/$organizationId/$projectId/reports',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -7,7 +7,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/reports_/$reportId',
|
||||
'/_app/$organizationId/$projectId/reports_/$reportId',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -13,7 +13,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsString, parseAsStringEnum, useQueryState } from 'nuqs';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/sessions',
|
||||
'/_app/$organizationId/$projectId/sessions',
|
||||
)({
|
||||
component: Component,
|
||||
head: () => {
|
||||
@@ -16,7 +16,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||
import { parseAsIsoDateTime, useQueryState } from 'nuqs';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/sessions_/$sessionId',
|
||||
'/_app/$organizationId/$projectId/sessions_/$sessionId',
|
||||
)({
|
||||
component: Component,
|
||||
loader: async ({ context, params }) => {
|
||||
@@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs/clients',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs/clients',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs/details',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs/details',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs/events',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs/events',
|
||||
)({
|
||||
component: Component,
|
||||
});
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs/imports',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs/imports',
|
||||
)({
|
||||
component: ImportsSettings,
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs/',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs/',
|
||||
)({
|
||||
component: Component,
|
||||
beforeLoad: ({ params }) => {
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/settings/_tabs',
|
||||
'/_app/$organizationId/$projectId/settings/_tabs',
|
||||
)({
|
||||
component: ProjectDashboard,
|
||||
head: () => {
|
||||
@@ -1,19 +1,9 @@
|
||||
import {
|
||||
OverviewFilterButton,
|
||||
OverviewFiltersButtons,
|
||||
} from '@/components/overview/filters/overview-filters-buttons';
|
||||
import { LiveCounter } from '@/components/overview/live-counter';
|
||||
import { OverviewInterval } from '@/components/overview/overview-interval';
|
||||
import OverviewMetrics from '@/components/overview/overview-metrics';
|
||||
import { OverviewRange } from '@/components/overview/overview-range';
|
||||
import { OverviewShare } from '@/components/overview/overview-share';
|
||||
import OverviewTopDevices from '@/components/overview/overview-top-devices';
|
||||
import OverviewTopEvents from '@/components/overview/overview-top-events';
|
||||
import OverviewTopGeo from '@/components/overview/overview-top-geo';
|
||||
import OverviewTopPages from '@/components/overview/overview-top-pages';
|
||||
import OverviewTopSources from '@/components/overview/overview-top-sources';
|
||||
import BillingPrompt from '@/components/organization/billing-prompt';
|
||||
import { useTRPC } from '@/integrations/trpc/react';
|
||||
import { PAGE_TITLES, createProjectTitle } from '@/utils/title';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { FREE_PRODUCT_IDS } from '@openpanel/payments';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { Outlet, createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/$organizationId/$projectId')({
|
||||
component: ProjectDashboard,
|
||||
@@ -26,34 +16,43 @@ export const Route = createFileRoute('/_app/$organizationId/$projectId')({
|
||||
],
|
||||
};
|
||||
},
|
||||
loader: async ({ context, params }) => {
|
||||
await context.queryClient.prefetchQuery(
|
||||
context.trpc.organization.get.queryOptions({
|
||||
organizationId: params.organizationId,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
function ProjectDashboard() {
|
||||
const { projectId } = Route.useParams();
|
||||
return (
|
||||
<div>
|
||||
<div className="col gap-2 p-4">
|
||||
<div className="flex justify-between gap-2">
|
||||
<div className="flex gap-2">
|
||||
<OverviewRange />
|
||||
<OverviewInterval />
|
||||
<OverviewFilterButton mode="events" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<LiveCounter projectId={projectId} />
|
||||
<OverviewShare projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
<OverviewFiltersButtons />
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4 p-4 pt-0">
|
||||
<OverviewMetrics projectId={projectId} />
|
||||
<OverviewTopSources projectId={projectId} />
|
||||
<OverviewTopPages projectId={projectId} />
|
||||
<OverviewTopDevices projectId={projectId} />
|
||||
<OverviewTopEvents projectId={projectId} />
|
||||
<OverviewTopGeo projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
const { organizationId } = Route.useParams();
|
||||
const trpc = useTRPC();
|
||||
const { data: organization } = useSuspenseQuery(
|
||||
trpc.organization.get.queryOptions({
|
||||
organizationId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
organization.subscriptionProductId &&
|
||||
FREE_PRODUCT_IDS.includes(organization.subscriptionProductId)
|
||||
) {
|
||||
return <BillingPrompt organization={organization} type={'freePlan'} />;
|
||||
}
|
||||
|
||||
if (organization.isExpired) {
|
||||
return (
|
||||
<BillingPrompt
|
||||
organization={organization}
|
||||
type={
|
||||
organization.subscriptionStatus === 'trialing'
|
||||
? 'trialEnded'
|
||||
: 'expired'
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { FullPageEmptyState } from '@/components/full-page-empty-state';
|
||||
import FullPageLoadingState from '@/components/full-page-loading-state';
|
||||
import Billing from '@/components/organization/billing';
|
||||
import { BillingFaq } from '@/components/organization/billing-faq';
|
||||
import CurrentSubscription from '@/components/organization/current-subscription';
|
||||
import Usage from '@/components/organization/usage';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import { useTRPC } from '@/integrations/trpc/react';
|
||||
import { PAGE_TITLES, createOrganizationTitle } from '@/utils/title';
|
||||
@@ -22,6 +19,20 @@ export const Route = createFileRoute('/_app/$organizationId/billing')({
|
||||
],
|
||||
};
|
||||
},
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
await Promise.all([
|
||||
context.queryClient.prefetchQuery(
|
||||
context.trpc.subscription.products.queryOptions({
|
||||
organizationId: params.organizationId,
|
||||
}),
|
||||
),
|
||||
context.queryClient.prefetchQuery(
|
||||
context.trpc.subscription.getCurrent.queryOptions({
|
||||
organizationId: params.organizationId,
|
||||
}),
|
||||
),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
function OrganizationPage() {
|
||||
@@ -51,14 +62,7 @@ function OrganizationPage() {
|
||||
className="mb-8"
|
||||
/>
|
||||
|
||||
<div className="flex flex-col-reverse md:flex-row gap-8 max-w-screen-lg">
|
||||
<div className="col gap-8 w-full">
|
||||
<Billing organization={organization} />
|
||||
<Usage organization={organization} />
|
||||
<BillingFaq />
|
||||
</div>
|
||||
<CurrentSubscription organization={organization} />
|
||||
</div>
|
||||
<Billing organization={organization} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import FullPageLoadingState from '@/components/full-page-loading-state';
|
||||
import SupporterPrompt from '@/components/organization/supporter-prompt';
|
||||
import { LinkButton } from '@/components/ui/button';
|
||||
import { useTRPC } from '@/integrations/trpc/react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { FREE_PRODUCT_IDS } from '@openpanel/payments';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
Outlet,
|
||||
@@ -106,24 +106,9 @@ function Component() {
|
||||
</LinkButton>
|
||||
</Alert>
|
||||
)}
|
||||
{organization.subscriptionEndsAt && organization.isExpired && (
|
||||
<Alert
|
||||
title="Subscription expired"
|
||||
description={`Your subscription has expired. You can reactivate it by choosing a new plan below. It expired on ${format(organization.subscriptionEndsAt, 'PPP')}`}
|
||||
>
|
||||
<LinkButton
|
||||
to="/$organizationId/billing"
|
||||
params={{
|
||||
organizationId: organizationId,
|
||||
}}
|
||||
>
|
||||
Reactivate
|
||||
</LinkButton>
|
||||
</Alert>
|
||||
)}
|
||||
{organization.subscriptionEndsAt && organization.isWillBeCanceled && (
|
||||
<Alert
|
||||
title="Subscription will becanceled"
|
||||
title="Subscription will be canceled"
|
||||
description={`You have canceled your subscription. You can reactivate it by choosing a new plan below. It'll expire on ${format(organization.subscriptionEndsAt, 'PPP')}`}
|
||||
>
|
||||
<LinkButton
|
||||
@@ -151,25 +136,8 @@ function Component() {
|
||||
</LinkButton>
|
||||
</Alert>
|
||||
)}
|
||||
{organization.subscriptionProductId &&
|
||||
FREE_PRODUCT_IDS.includes(organization.subscriptionProductId) && (
|
||||
<Alert
|
||||
title="Free plan is removed"
|
||||
description="We've removed the free plan. You can upgrade to a paid plan to continue using OpenPanel."
|
||||
className="bg-orange-400/40 border-orange-400/50"
|
||||
>
|
||||
<LinkButton
|
||||
className="bg-orange-400 text-white hover:bg-orange-400/80"
|
||||
to="/$organizationId/billing"
|
||||
params={{
|
||||
organizationId: organizationId,
|
||||
}}
|
||||
>
|
||||
Upgrade
|
||||
</LinkButton>
|
||||
</Alert>
|
||||
)}
|
||||
<Outlet />
|
||||
<SupporterPrompt />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user