Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions admin/billing/orb.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,9 @@ func getBillingInvoiceFromOrbInvoice(i *orb.Invoice) *Invoice {
}
}

// Mapping of externalID/planName to a type.
// Used in deciding email body in backend.
// Make sure to update web-admin/src/features/billing/plans/utils.ts if this is updated
func getPlanType(externalID string) PlanType {
switch externalID {
case "free_trial":
Expand Down
5 changes: 3 additions & 2 deletions web-admin/src/features/billing/Payment.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
$: neverSubscribed = $categorisedIssues.data?.neverSubscribed;
$: onTrial = !!$categorisedIssues.data?.trial;
$: onEnterprisePlan =
subscription?.plan && isEnterprisePlan(subscription.plan);
$: onManagedPlan = subscription?.plan && isManagedPlan(subscription.plan);
subscription?.plan && isEnterprisePlan(subscription.plan.name);
$: onManagedPlan =
subscription?.plan && isManagedPlan(subscription.plan.name);
$: hidePaymentModule =
neverSubscribed || onTrial || onEnterprisePlan || onManagedPlan;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
} from "@rilldata/web-admin/features/billing/issues/useBillingIssueMessage";
import StartTeamPlanDialog from "@rilldata/web-admin/features/billing/plans/StartTeamPlanDialog.svelte";
import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus";
import { onMount } from "svelte";

export let organization: string;

Expand All @@ -14,7 +15,9 @@
$: ({ showStartTeamPlanDialog, startTeamPlanType, teamPlanEndDate } =
billingCTAHandler);

function showBillingIssueBanner(message: BillingIssueMessage) {
function showBillingIssueBanner(message: BillingIssueMessage | undefined) {
if (!message) return;

eventBus.emit("banner", {
type: message.type,
message: message.title + " " + message.description,
Expand All @@ -33,9 +36,13 @@
});
}

$: if ($billingIssueMessage.data) {
$: showBillingIssueBanner($billingIssueMessage.data);
onMount(() => {
// There is a race condition where BannerCenter is mounted after the above statement is run.
// So call showBillingIssueBanner again to make sure banner is shown.
// TODO: we should probably save the last event args and re-fire them when a listener added
showBillingIssueBanner($billingIssueMessage.data);
}
});
</script>

<StartTeamPlanDialog
Expand Down
17 changes: 8 additions & 9 deletions web-admin/src/features/billing/issues/useBillingIssueMessage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createAdminServiceGetBillingSubscription } from "@rilldata/web-admin/client";
import { createAdminServiceGetOrganization } from "@rilldata/web-admin/client";
import { getMessageForPaymentIssues } from "@rilldata/web-admin/features/billing/issues/getMessageForPaymentIssues";
import { getMessageForCancelledIssue } from "@rilldata/web-admin/features/billing/issues/getMessageForCancelledIssue";
import { getMessageForTrialPlan } from "@rilldata/web-admin/features/billing/issues/getMessageForTrialPlan";
Expand Down Expand Up @@ -30,13 +30,13 @@ export function useBillingIssueMessage(
): CompoundQueryResult<BillingIssueMessage> {
return derived(
[
createAdminServiceGetBillingSubscription(organization),
createAdminServiceGetOrganization(organization),
useCategorisedOrganizationBillingIssues(organization),
areAllProjectsHibernating(organization),
],
([subscriptionResp, categorisedIssuesResp, allProjectsHibernatingResp]) => {
([orgResp, categorisedIssuesResp, allProjectsHibernatingResp]) => {
if (
subscriptionResp.isFetching ||
orgResp.isFetching ||
categorisedIssuesResp.isFetching ||
allProjectsHibernatingResp.isFetching
) {
Expand All @@ -46,14 +46,14 @@ export function useBillingIssueMessage(
};
}
if (
subscriptionResp.error ||
orgResp.error ||
categorisedIssuesResp.error ||
allProjectsHibernatingResp.error
) {
return {
isFetching: false,
error:
subscriptionResp.error ??
orgResp.error ??
categorisedIssuesResp.error ??
allProjectsHibernatingResp.error,
};
Expand All @@ -79,11 +79,10 @@ export function useBillingIssueMessage(

if (
categorisedIssuesResp.data?.payment.length &&
subscriptionResp.data?.subscription
orgResp.data?.organization?.billingPlanName
) {
const paymentIssue = getMessageForPaymentIssues(
!!subscriptionResp.data.subscription.plan &&
!isTeamPlan(subscriptionResp.data.subscription.plan),
!isTeamPlan(orgResp.data.organization.billingPlanName),
categorisedIssuesResp.data.payment,
);
// if we do not have any payment related message to show, skip it
Expand Down
4 changes: 2 additions & 2 deletions web-admin/src/features/billing/plans/Plan.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
$: isTrial = !!$categorisedIssues.data?.trial;
// ended subscription will have a cancelled issue associated with it
$: subHasEnded = !!$categorisedIssues.data?.cancelled;
$: subIsTeamPlan = plan && isTeamPlan(plan);
$: subIsManagedPlan = plan && isManagedPlan(plan);
$: subIsTeamPlan = plan && isTeamPlan(plan.name);
$: subIsManagedPlan = plan && isManagedPlan(plan.name);
$: subIsEnterprisePlan =
plan && !isTrial && !subIsTeamPlan && !subIsManagedPlan;
</script>
Expand Down
2 changes: 1 addition & 1 deletion web-admin/src/features/billing/plans/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function fetchTeamPlan() {
queryFn: () => adminServiceListPublicBillingPlans(),
});

return plansResp.plans?.find(isTeamPlan);
return plansResp.plans?.find((p) => isTeamPlan(p.name ?? ""));
}

/**
Expand Down
26 changes: 14 additions & 12 deletions web-admin/src/features/billing/plans/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import {
type V1BillingPlan,
V1BillingPlanType,
} from "@rilldata/web-admin/client";
import { formatMemorySize } from "@rilldata/web-common/lib/number-formatting/memory-size";
import { DateTime } from "luxon";
import { writable } from "svelte/store";
Expand All @@ -21,20 +17,26 @@ export function formatUsageVsQuota(
return `${formattedUsage} of ${formattedQuota} (${percent}%)`;
}

export function isTrialPlan(plan: V1BillingPlan) {
return plan.planType === V1BillingPlanType.BILLING_PLAN_TYPE_TRIAL;
// Mapping of externalID/planName to a type.
// Used in deciding banner message and to show different billing module in frontend.
// Make sure to update admin/billing/orb.go::getPlanType if this is updated

export function isTrialPlan(planName: string) {
return planName === "free_trial";
}

export function isTeamPlan(plan: V1BillingPlan) {
return plan.planType === V1BillingPlanType.BILLING_PLAN_TYPE_TEAM;
export function isTeamPlan(planName: string) {
return planName === "team";
}

export function isManagedPlan(plan: V1BillingPlan) {
return plan.planType === V1BillingPlanType.BILLING_PLAN_TYPE_MANAGED;
export function isManagedPlan(planName: string) {
return planName === "managed";
}

export function isEnterprisePlan(plan: V1BillingPlan) {
return !isTrialPlan(plan) && !isTeamPlan(plan) && !isManagedPlan(plan);
export function isEnterprisePlan(planName: string) {
return (
!isTrialPlan(planName) && !isTeamPlan(planName) && !isManagedPlan(planName)
);
}

export function getSubscriptionResumedText(endDate: string) {
Expand Down
13 changes: 2 additions & 11 deletions web-admin/src/features/navigation/TopNavigationBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import { useExplore } from "@rilldata/web-common/features/explores/selectors";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import {
createAdminServiceGetBillingSubscription,
createAdminServiceGetCurrentUser,
createAdminServiceListOrganizations as listOrgs,
createAdminServiceListProjectsForOrganization as listProjects,
Expand All @@ -35,10 +34,10 @@
isPublicURLPage,
} from "./nav-utils";

export let manageOrganization: boolean;
export let createMagicAuthTokens: boolean;
export let manageProjectMembers: boolean;
export let organizationLogoUrl: string | undefined = undefined;
export let planDisplayName: string | undefined;

const user = createAdminServiceGetCurrentUser();

Expand Down Expand Up @@ -101,19 +100,11 @@
$: alerts = $alertsQuery.data?.resources ?? [];
$: reports = $reportsQuery.data?.resources ?? [];

$: plan = createAdminServiceGetBillingSubscription(organization, {
query: {
enabled: Boolean(
!!organization && manageOrganization && !onPublicURLPage,
),
select: (data) => data.subscription?.plan,
},
});
$: organizationPaths = organizations.reduce(
(map, { name, displayName }) =>
map.set(name.toLowerCase(), {
label: displayName || name,
pill: $plan?.data?.displayName,
pill: planDisplayName,
}),
new Map<string, PathOption>(),
);
Expand Down
3 changes: 2 additions & 1 deletion web-admin/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
organizationPermissions,
organizationLogoUrl,
organizationFaviconUrl,
planDisplayName,
} = data);
$: ({
params: { organization },
Expand Down Expand Up @@ -111,10 +112,10 @@
{/if}
{#if !isEmbed && !hideTopBar}
<TopNavigationBar
manageOrganization={organizationPermissions?.manageOrg}
createMagicAuthTokens={projectPermissions?.createMagicAuthTokens}
manageProjectMembers={projectPermissions?.manageProjectMembers}
{organizationLogoUrl}
{planDisplayName}
/>

{#if withinOnlyOrg}
Expand Down
4 changes: 4 additions & 0 deletions web-admin/src/routes/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const load = async ({ params, url, route, depends }) => {
let organizationPermissions: V1OrganizationPermissions = {};
let organizationLogoUrl: string | undefined = undefined;
let organizationFaviconUrl: string | undefined = undefined;
let planDisplayName: string | undefined = undefined;
if (organization && !token) {
try {
const organizationResp = await queryClient.fetchQuery(
Expand All @@ -88,6 +89,7 @@ export const load = async ({ params, url, route, depends }) => {
organizationPermissions = organizationResp.permissions ?? {};
organizationLogoUrl = organizationResp.organization?.logoUrl;
organizationFaviconUrl = organizationResp.organization?.faviconUrl;
planDisplayName = organizationResp.organization?.billingPlanDisplayName;
} catch (e: unknown) {
if (!isAxiosError<RpcStatus>(e) || !e.response) {
throw error(500, "Error fetching organization");
Expand All @@ -110,6 +112,7 @@ export const load = async ({ params, url, route, depends }) => {
organizationPermissions,
organizationLogoUrl,
organizationFaviconUrl,
planDisplayName,
projectPermissions: <V1ProjectPermissions>{},
};
}
Expand All @@ -134,6 +137,7 @@ export const load = async ({ params, url, route, depends }) => {
organizationPermissions,
organizationLogoUrl,
organizationFaviconUrl,
planDisplayName,
projectPermissions,
project: proj,
runtime: runtimeData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
$: organization = $page.params.organization;
$: basePage = `/${organization}/-/settings`;
$: onEnterprisePlan =
subscription?.plan && isEnterprisePlan(subscription?.plan);
subscription?.plan?.name && isEnterprisePlan(subscription.plan.name);
$: hideBillingSettings = neverSubscribed;
$: hideUsageSettings = onEnterprisePlan || !billingPortalUrl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const load: PageLoad = async ({ params: { organization }, parent }) => {
}

// Orgs on an Enterprise Plan should not see this page
if (subscription?.plan && isEnterprisePlan(subscription.plan)) {
if (subscription?.plan && isEnterprisePlan(subscription.plan.name)) {
throw error(404, "Page not found");
}
};
Loading