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:
Carl-Gerhard Lindesvärd
2025-11-11 11:09:11 +01:00
committed by GitHub
parent bbd30ca6e0
commit 9cafd61b25
57 changed files with 1642 additions and 1269 deletions

View File

@@ -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 />;
}