diff --git a/packages/www/components/UsageSummary/index.tsx b/packages/www/components/UsageSummary/index.tsx
index df81d43b0..5b62253f2 100644
--- a/packages/www/components/UsageSummary/index.tsx
+++ b/packages/www/components/UsageSummary/index.tsx
@@ -18,11 +18,7 @@ import { useApi } from "hooks";
import { products } from "@livepeer.studio/api/src/config";
import { QuestionMarkCircledIcon as Help } from "@radix-ui/react-icons";
import { useJune, events } from "hooks/use-june";
-
-const StyledUpcomingIcon = styled(UpcomingIcon, {
- mr: "$2",
- color: "$gray",
-});
+import { Card, CardContent, CardHeader, CardTitle } from "components/ui/card";
export interface OverUsageBill {
transcodingBill: OverUsageItem;
@@ -37,40 +33,23 @@ export interface OverUsageItem {
export const UsageCard = ({ title, usage, limit, loading = false }) => {
return (
-
+
{loading ? (
-
-
-
-
+
+
+
+
) : (
<>
-
-
- {title}
-
-
-
- {usage}
-
+
+ {title}
+
+
+ {usage} minutes
+
>
)}
-
+
);
};
@@ -87,7 +66,7 @@ const UsageSummary = () => {
const [subscription, setSubscription] = useState(null);
const [invoices, setInvoices] = useState(null);
const [overUsageBill, setOverUsageBill] = useState(
- null,
+ null
);
const [upcomingInvoiceTotal, setUpcomingInvoiceTotal] = useState(0);
const [upcomingInvoice, setUpcomingInvoice] = useState(null);
@@ -134,7 +113,7 @@ const UsageSummary = () => {
doGetUsage(
subscription?.current_period_start,
subscription?.current_period_end,
- subscription?.status,
+ subscription?.status
);
};
@@ -160,7 +139,7 @@ const UsageSummary = () => {
TotalUsageMins: Math.max(usage?.TotalUsageMins - limits.transcoding, 0),
DeliveryUsageMins: Math.max(
usage?.DeliveryUsageMins - limits.streaming,
- 0,
+ 0
),
StorageUsageMins: Math.max(usage?.StorageUsageMins - limits.storage, 0),
};
@@ -176,8 +155,8 @@ const UsageSummary = () => {
units: overusage.TotalUsageMins,
total: Number(
(overusage.TotalUsageMins * payAsYouGoData.usage[0].price).toFixed(
- 2,
- ),
+ 2
+ )
),
},
deliveryBill: {
@@ -185,7 +164,7 @@ const UsageSummary = () => {
total: Number(
(
overusage.DeliveryUsageMins * payAsYouGoData.usage[1].price
- ).toFixed(2),
+ ).toFixed(2)
),
},
storageBill: {
@@ -193,7 +172,7 @@ const UsageSummary = () => {
total: Number(
(
overusage.StorageUsageMins * payAsYouGoData.usage[2].price
- ).toFixed(2),
+ ).toFixed(2)
),
},
};
@@ -263,14 +242,14 @@ const UsageSummary = () => {
{subscription && (
{new Date(
- subscription.current_period_start * 1000,
+ subscription.current_period_start * 1000
).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}{" "}
to{" "}
{new Date(
- subscription.current_period_end * 1000,
+ subscription.current_period_end * 1000
).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
diff --git a/packages/www/components/ui/card.tsx b/packages/www/components/ui/card.tsx
new file mode 100644
index 000000000..3c0ac8edd
--- /dev/null
+++ b/packages/www/components/ui/card.tsx
@@ -0,0 +1,82 @@
+import * as React from "react";
+
+import { cn } from "lib/cn";
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+Card.displayName = "Card";
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardHeader.displayName = "CardHeader";
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardTitle.displayName = "CardTitle";
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardDescription.displayName = "CardDescription";
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardContent.displayName = "CardContent";
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardFooter.displayName = "CardFooter";
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardDescription,
+ CardContent,
+};
diff --git a/packages/www/components/ui/chart.tsx b/packages/www/components/ui/chart.tsx
new file mode 100644
index 000000000..1ae71984e
--- /dev/null
+++ b/packages/www/components/ui/chart.tsx
@@ -0,0 +1,370 @@
+import * as React from "react";
+import * as RechartsPrimitive from "recharts";
+
+import { cn } from "lib/cn";
+import { Payload } from "recharts/types/component/DefaultLegendContent";
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const;
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode;
+ icon?: React.ComponentType;
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ );
+};
+
+type ChartContextProps = {
+ config: ChartConfig;
+};
+
+const ChartContext = React.createContext(null);
+
+function useChart() {
+ const context = React.useContext(ChartContext);
+
+ if (!context) {
+ throw new Error("useChart must be used within a ");
+ }
+
+ return context;
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig;
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"];
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId();
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+});
+ChartContainer.displayName = "Chart";
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([_, config]) => config.theme || config.color
+ );
+
+ if (!colorConfig.length) {
+ return null;
+ }
+
+ return (
+