diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/settings/billing/page-client.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/settings/billing/page-client.tsx index 6f4ed8eb4e..59e67d2fae 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/settings/billing/page-client.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/settings/billing/page-client.tsx @@ -3,14 +3,21 @@ import useTags from "@/lib/swr/use-tags"; import useUsers from "@/lib/swr/use-users"; import useWorkspace from "@/lib/swr/use-workspace"; -import { Divider } from "@/ui/shared/icons"; import Infinity from "@/ui/shared/icons/infinity"; import PlanBadge from "@/ui/workspaces/plan-badge"; -import { Button, buttonVariants, InfoTooltip, ProgressBar } from "@dub/ui"; +import { + Button, + buttonVariants, + Icon, + InfoTooltip, + ProgressBar, + useRouterStuff, +} from "@dub/ui"; +import { CircleDollar, CursorRays, Hyperlink } from "@dub/ui/src/icons"; import { cn, getFirstAndLastDay, nFormatter } from "@dub/utils"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; -import { useMemo, useState } from "react"; +import { CSSProperties, useMemo, useState } from "react"; import { toast } from "sonner"; export default function WorkspaceBillingClient() { @@ -60,10 +67,10 @@ export default function WorkspaceBillingClient() { return (
-
-
-

Plan & Usage

-

+

+
+

Plan and Usage

+

You are currently on the{" "} {plan ? ( @@ -113,43 +120,46 @@ export default function WorkspaceBillingClient() { )}

-
- {conversionEnabled && ( - +
+ - )} - = 1000000000) || false} - /> + + {conversionEnabled && ( + + )} +
+
+
+ WIP +
+
-
- = 1000000000) || false} - /> +
-
-
-
+
{plan ? (

{plan === "enterprise" @@ -220,6 +228,101 @@ export default function WorkspaceBillingClient() { ); } +function UsageTabCard({ + id, + icon: Icon, + title, + usage: usageProp, + limit: limitProp, + unit, + root, +}: { + id: string; + icon: Icon; + title: string; + usage?: number; + limit?: number; + unit?: string; + root?: boolean; +}) { + const { searchParams, queryParams } = useRouterStuff(); + + const isActive = + searchParams.get("tab") === id || (!searchParams.get("tab") && root); + + const [usage, limit] = + unit === "$" && usageProp !== undefined && limitProp !== undefined + ? [usageProp / 100, limitProp / 100] + : [usageProp, limitProp]; + + const loading = usage === undefined || limit === undefined; + const unlimited = limit !== undefined && limit >= 1000000000; + const warning = !loading && !unlimited && usage >= limit * 0.9; + const remaining = !loading && !unlimited ? Math.max(0, limit - usage) : 0; + + const prefix = unit || ""; + + return ( + + ); +} + function UsageCategory(data: { title: string; unit: string; @@ -234,26 +337,27 @@ function UsageCategory(data: { usage = usage / 100; usageLimit = usageLimit / 100; } + return ( -

+
-

{title}

+

{title}

{numberOnly ? ( -
+
{usage || usage === 0 ? ( -

+

{nFormatter(usage, { full: true })}

) : ( -
+
)} - + / {usageLimit && usageLimit >= 1000000000 ? ( ) : ( -

+

{nFormatter(usageLimit, { full: true })}

)} diff --git a/packages/ui/src/icons/nucleo/circle-dollar.tsx b/packages/ui/src/icons/nucleo/circle-dollar.tsx new file mode 100644 index 0000000000..2ff132cbfa --- /dev/null +++ b/packages/ui/src/icons/nucleo/circle-dollar.tsx @@ -0,0 +1,56 @@ +import { SVGProps } from "react"; + +export function CircleDollar(props: SVGProps) { + return ( + + + + + + + + + ); +} diff --git a/packages/ui/src/icons/nucleo/index.ts b/packages/ui/src/icons/nucleo/index.ts index 67c87f3d80..f48b7014e7 100644 --- a/packages/ui/src/icons/nucleo/index.ts +++ b/packages/ui/src/icons/nucleo/index.ts @@ -18,6 +18,7 @@ export * from "./check2"; export * from "./checkbox-checked-fill"; export * from "./checkbox-unchecked"; export * from "./circle-check"; +export * from "./circle-dollar"; export * from "./circle-dotted"; export * from "./circle-half-dotted-clock"; export * from "./circle-info";