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 ( +