mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 19:28:33 +00:00
fix: pricing calculation on billing page
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
|||||||
TOrgPlanDTO,
|
TOrgPlanDTO,
|
||||||
TOrgPlansTableDTO,
|
TOrgPlansTableDTO,
|
||||||
TOrgPmtMethodsDTO,
|
TOrgPmtMethodsDTO,
|
||||||
|
TPlanBillingInfo,
|
||||||
TStartOrgTrialDTO,
|
TStartOrgTrialDTO,
|
||||||
TUpdateOrgBillingDetailsDTO
|
TUpdateOrgBillingDetailsDTO
|
||||||
} from "./license-types";
|
} from "./license-types";
|
||||||
@@ -465,6 +466,21 @@ export const licenseServiceFactory = ({
|
|||||||
return { url };
|
return { url };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getUsageMetrics = async (orgId: string) => {
|
||||||
|
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
||||||
|
orgDAL.countAllOrgMembers(orgId),
|
||||||
|
licenseDAL.countOfOrgIdentities(orgId),
|
||||||
|
projectDAL.countOfOrgProjects(orgId)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
orgMembersUsed,
|
||||||
|
identityUsed,
|
||||||
|
projectCount,
|
||||||
|
totalIdentities: identityUsed + orgMembersUsed
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission({
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actorId,
|
actorId,
|
||||||
@@ -483,10 +499,16 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (instanceType === InstanceType.Cloud) {
|
if (instanceType === InstanceType.Cloud) {
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get<TPlanBillingInfo>(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
||||||
);
|
);
|
||||||
return data;
|
const { identityUsed, orgMembersUsed } = await getUsageMetrics(orgId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
users: orgMembersUsed,
|
||||||
|
identities: identityUsed
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -495,7 +517,9 @@ export const licenseServiceFactory = ({
|
|||||||
interval: "month",
|
interval: "month",
|
||||||
intervalCount: 1,
|
intervalCount: 1,
|
||||||
amount: 0,
|
amount: 0,
|
||||||
quantity: 1
|
quantity: 1,
|
||||||
|
users: 0,
|
||||||
|
identities: 0
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -539,21 +563,6 @@ export const licenseServiceFactory = ({
|
|||||||
throw new Error(`Unsupported instance type for server-based plan table: ${instanceType}`);
|
throw new Error(`Unsupported instance type for server-based plan table: ${instanceType}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUsageMetrics = async (orgId: string) => {
|
|
||||||
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
|
||||||
orgDAL.countAllOrgMembers(orgId),
|
|
||||||
licenseDAL.countOfOrgIdentities(orgId),
|
|
||||||
projectDAL.countOfOrgProjects(orgId)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
orgMembersUsed,
|
|
||||||
identityUsed,
|
|
||||||
projectCount,
|
|
||||||
totalIdentities: identityUsed + orgMembersUsed
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns org current plan feature table
|
// returns org current plan feature table
|
||||||
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission({
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ export type TOfflineLicense = {
|
|||||||
features: TFeatureSet;
|
features: TFeatureSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TPlanBillingInfo = {
|
||||||
|
currentPeriodStart: number;
|
||||||
|
currentPeriodEnd: number;
|
||||||
|
interval: "month" | "year";
|
||||||
|
intervalCount: number;
|
||||||
|
amount: number;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type TFeatureSet = {
|
export type TFeatureSet = {
|
||||||
_id: null;
|
_id: null;
|
||||||
slug: string | null;
|
slug: string | null;
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ export type PlanBillingInfo = {
|
|||||||
interval: "month" | "year";
|
interval: "month" | "year";
|
||||||
intervalCount: number;
|
intervalCount: number;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
|
users: number;
|
||||||
|
identities: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Invoice = {
|
export type Invoice = {
|
||||||
|
|||||||
+22
-5
@@ -1,10 +1,10 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import { Button } from "@app/components/v2";
|
import { Button, Tooltip } from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
OrgPermissionBillingActions,
|
OrgPermissionBillingActions,
|
||||||
OrgPermissionSubjects,
|
OrgPermissionSubjects,
|
||||||
@@ -27,6 +27,9 @@ export const PreviewSection = () => {
|
|||||||
const { subscription } = useSubscription(true);
|
const { subscription } = useSubscription(true);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { data, isPending } = useGetOrgPlanBillingInfo(currentOrg?.id ?? "");
|
const { data, isPending } = useGetOrgPlanBillingInfo(currentOrg?.id ?? "");
|
||||||
|
|
||||||
|
const totalAmount = data?.amount ? data.amount * (data.users + data.identities) : 0;
|
||||||
|
|
||||||
const getOrgTrialUrl = useGetOrgTrialUrl();
|
const getOrgTrialUrl = useGetOrgTrialUrl();
|
||||||
const createCustomerPortalSession = useCreateCustomerPortalSession();
|
const createCustomerPortalSession = useCreateCustomerPortalSession();
|
||||||
|
|
||||||
@@ -190,14 +193,28 @@ export const PreviewSection = () => {
|
|||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{subscription.slug !== "enterprise" ? (
|
||||||
<div className="mr-4 flex-1 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mr-4 flex-1 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<p className="mb-2 text-gray-400">Price</p>
|
<p className="mb-2 text-gray-400">Price</p>
|
||||||
<p className="mb-8 text-2xl font-medium text-mineshaft-50">
|
<p className="mb-8 text-2xl font-medium text-mineshaft-50">
|
||||||
{subscription.status === "trialing"
|
{subscription.status === "trialing" ? (
|
||||||
? "$0.00 / month"
|
"$0.00 / month"
|
||||||
: `${formatAmount(data.amount)} / ${data.interval}`}
|
) : (
|
||||||
|
<>
|
||||||
|
{formatAmount(totalAmount)} / {data.interval}
|
||||||
|
{(subscription.slug === "pro" || subscription.slug === "pro-annual") && (
|
||||||
|
<Tooltip
|
||||||
|
content={`Total price is based on the number of users and machine identities at ${formatAmount(data.amount)} each. You have ${data.users} ${data.users > 1 ? "users" : "user"} and ${data.identities} ${data.identities > 1 ? "machine identities" : "machine identity"}.`}
|
||||||
|
className="max-w-lg"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} className="ml-2" size="xs" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="flex-1 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="flex-1 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<p className="mb-2 text-gray-400">Subscription renews on</p>
|
<p className="mb-2 text-gray-400">Subscription renews on</p>
|
||||||
<p className="mb-8 text-2xl font-medium text-mineshaft-50">
|
<p className="mb-8 text-2xl font-medium text-mineshaft-50">
|
||||||
|
|||||||
Reference in New Issue
Block a user