diff --git a/apps/web/modules/settings/billing/components/BillingCredits.tsx b/apps/web/modules/settings/billing/components/BillingCredits.tsx
index 3abc6a015b..4fb3287015 100644
--- a/apps/web/modules/settings/billing/components/BillingCredits.tsx
+++ b/apps/web/modules/settings/billing/components/BillingCredits.tsx
@@ -1,14 +1,12 @@
"use client";
import { useSession } from "next-auth/react";
-import Link from "next/link";
import { useRouter, usePathname } from "next/navigation";
import { useState, useMemo } from "react";
import { useForm } from "react-hook-form";
import dayjs from "@calcom/dayjs";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
-import ServerTrans from "@calcom/lib/components/ServerTrans";
import { IS_SMS_CREDITS_ENABLED } from "@calcom/lib/constants";
import { downloadAsCsv } from "@calcom/lib/csvUtils";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -24,6 +22,7 @@ import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { MemberInvitationModalWithoutMembers } from "~/ee/teams/components/MemberInvitationModal";
+import { LearnMoreLink } from "~/event-types/components/LearnMoreLink";
import { BillingCreditsSkeleton } from "./BillingCreditsSkeleton";
@@ -42,7 +41,13 @@ type CreditRowProps = {
className?: string;
};
-const CreditRow = ({ label, value, isBold = false, underline, className = "" }: CreditRowProps) => {
+const CreditRow = ({
+ label,
+ value,
+ isBold = false,
+ underline,
+ className = "",
+}: CreditRowProps) => {
const numberFormatter = new Intl.NumberFormat();
return (
+ )}
+ >
+ className={classNames(
+ "text-sm",
+ isBold ? "font-semibold" : "font-medium leading-tight text-subtle"
+ )}
+ >
{label}
+ className={classNames(
+ `text-sm`,
+ isBold ? "font-semibold" : "font-medium leading-tight text-subtle"
+ )}
+ >
{numberFormatter.format(value)}
@@ -73,7 +87,10 @@ const getMonthOptions = (): MonthOption[] => {
let date = dayjs.utc();
let count = 0;
- while ((date.isAfter(minDate) || date.isSame(minDate, "month")) && count < 12) {
+ while (
+ (date.isAfter(minDate) || date.isSame(minDate, "month")) &&
+ count < 12
+ ) {
const startDate = date.startOf("month");
const endDate = date.endOf("month");
options.push({
@@ -96,9 +113,12 @@ export default function BillingCredits() {
const session = useSession();
const orgBranding = useOrgBranding();
const monthOptions = useMemo(() => getMonthOptions(), []);
- const [selectedMonth, setSelectedMonth] = useState(monthOptions[0]);
+ const [selectedMonth, setSelectedMonth] = useState(
+ monthOptions[0]
+ );
const [isDownloading, setIsDownloading] = useState(false);
- const [showMemberInvitationModal, setShowMemberInvitationModal] = useState(false);
+ const [showMemberInvitationModal, setShowMemberInvitationModal] =
+ useState(false);
const utils = trpc.useUtils();
const {
@@ -122,14 +142,26 @@ export default function BillingCredits() {
const tokens = (pathname ?? "").split("/").filter(Boolean);
const settingsIndex = tokens.indexOf("settings");
const isOrgScopedPath =
- settingsIndex >= 0 && ["organizations", "teams"].includes(tokens[settingsIndex + 1]);
+ settingsIndex >= 0 &&
+ ["organizations", "teams"].includes(tokens[settingsIndex + 1]);
- const shouldRender = IS_SMS_CREDITS_ENABLED && !(orgId && !isOrgScopedPath && !orgBranding?.slug);
+ const shouldRender =
+ IS_SMS_CREDITS_ENABLED &&
+ !(orgId && !isOrgScopedPath && !orgBranding?.slug);
- const { data: creditsData, isLoading } = trpc.viewer.credits.getAllCredits.useQuery(
- { teamId },
- { enabled: shouldRender }
+ const { data: creditsData, isLoading } =
+ trpc.viewer.credits.getAllCredits.useQuery(
+ { teamId },
+ { enabled: shouldRender }
+ );
+
+ const { data: teamPlanData } = trpc.viewer.teams.hasTeamPlan.useQuery(
+ undefined,
+ {
+ enabled: shouldRender && !teamId,
+ }
);
+ const isUserInTeam = teamPlanData?.hasTeamPlan ?? false;
if (!shouldRender) return null;
@@ -153,7 +185,9 @@ export default function BillingCredits() {
endDate: selectedMonth.endDate,
});
if (result?.csvData) {
- const filename = `credit-expense-log-${selectedMonth.value.toLowerCase().replace(" ", "-")}.csv`;
+ const filename = `credit-expense-log-${selectedMonth.value
+ .toLowerCase()
+ .replace(" ", "-")}.csv`;
downloadAsCsv(result.csvData, filename);
} else {
showToast(t("error_downloading_expense_log"), "error");
@@ -168,6 +202,11 @@ export default function BillingCredits() {
if (isLoading && teamId) return ;
if (!creditsData) return null;
+ // For personal billing page: hide credits section if user is part of a team and has no personal credits
+ if (!teamId && isUserInTeam && creditsData.credits.additionalCredits <= 0) {
+ return null;
+ }
+
const onSubmit = (data: { quantity: number }) => {
buyCreditsMutation.mutate({ quantity: data.quantity, teamId });
};
@@ -175,15 +214,20 @@ export default function BillingCredits() {
const totalCredits = creditsData.credits.totalMonthlyCredits ?? 0;
const totalUsed = creditsData.credits.totalCreditsUsedThisMonth ?? 0;
- const teamCreditsPercentageUsed = totalCredits > 0 ? (totalUsed / totalCredits) * 100 : 0;
+ const teamCreditsPercentageUsed =
+ totalCredits > 0 ? (totalUsed / totalCredits) * 100 : 0;
const numberFormatter = new Intl.NumberFormat();
return (
<>
-
+
-
{t("credits")}
-
{t("view_and_manage_credits")}
+
+ {t("credits")}
+
+
+ {t("view_and_manage_credits")}
+
@@ -196,20 +240,33 @@ export default function BillingCredits() {
isBold={true}
underline="dashed"
/>
-
+
{/*750 credits per tip*/}
-
-
- {orgSlug ? t("credits_per_tip_org") : t("credits_per_tip_teams")}
+
+
+ {orgSlug
+ ? t("credits_per_tip_org")
+ : t("credits_per_tip_teams")}
-
@@ -227,51 +284,76 @@ export default function BillingCredits() {
*/}
{/*Additional Credits*/}
-