feat(frontend): Wallet top-up and auto-refill (#9819)

### Changes 🏗️

- Add top-up and auto-refill tabs in the Wallet
- Add shadcn `tabs` component
- Disable increase/decrease spinner buttons on number inputs across
Platform (moved css from `customnode.css` to `globals.css`

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Incorrect values are detected properly
  - [x] Top-up works
  - [x] Setting auto-refill works
This commit is contained in:
Krzysztof Czerwinski
2025-04-18 11:44:54 +02:00
committed by GitHub
parent d1730d7b1d
commit e8bbd945f2
10 changed files with 484 additions and 31 deletions

View File

@@ -42,6 +42,7 @@
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.4",
"@radix-ui/react-toast": "^1.2.5",
"@radix-ui/react-tooltip": "^1.1.7",
"@sentry/nextjs": "^9",

View File

@@ -144,3 +144,13 @@
text-wrap: balance;
}
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}

View File

@@ -118,8 +118,7 @@ export default function CreditsPage() {
{topupStatus === "success" && (
<span className="text-green-500">
Your payment was successful. Your credits will be updated
shortly. You can click the refresh icon 🔄 in case it is not
updated.
shortly. Try refreshing the page in case it is not updated.
</span>
)}
{topupStatus === "cancel" && (

View File

@@ -14,6 +14,7 @@ import { useOnboarding } from "../onboarding/onboarding-provider";
import { useCallback, useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import * as party from "party-js";
import WalletRefill from "./WalletRefill";
export default function Wallet() {
const { credits, formatCredits, fetchCredits } = useCredits({
@@ -86,7 +87,7 @@ export default function Wallet() {
"rounded-xl border-zinc-200 bg-zinc-50 shadow-[0_3px_3px] shadow-zinc-300",
)}
>
<div>
{/* Header */}
<div className="mx-1 flex items-center justify-between border-b border-zinc-300 pb-2">
<span className="font-poppins font-medium text-zinc-900">
Your wallet
@@ -101,11 +102,16 @@ export default function Wallet() {
</PopoverClose>
</div>
</div>
<p className="mx-1 mt-3 font-inter text-xs text-muted-foreground text-zinc-400">
<ScrollArea className="max-h-[85vh] overflow-y-auto">
{/* Top ups */}
<WalletRefill />
{/* Tasks */}
<p className="mx-1 mt-4 font-sans text-xs font-medium text-violet-700">
Onboarding tasks
</p>
<p className="mx-1 my-1 font-sans text-xs font-normal text-zinc-500">
Complete the following tasks to earn more credits!
</p>
</div>
<ScrollArea className="max-h-[80vh] overflow-y-auto">
<TaskGroups />
</ScrollArea>
</PopoverContent>

View File

@@ -0,0 +1,265 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "../ui/input";
import Link from "next/link";
import { useToast, useToastOnFail } from "../ui/use-toast";
import useCredits from "@/hooks/useCredits";
import { useCallback, useEffect, useState } from "react";
const topUpSchema = z.object({
amount: z
.number({ coerce: true, invalid_type_error: "Enter top-up amount" })
.min(5, "Top-ups start at $5. Please enter a higher amount."),
});
const autoRefillSchema = z
.object({
threshold: z
.number({ coerce: true, invalid_type_error: "Enter min. balance" })
.min(
5,
"Looks like your balance is too low for auto-refill. Try $5 or more.",
),
refillAmount: z
.number({ coerce: true, invalid_type_error: "Enter top-up amount" })
.min(5, "Top-ups start at $5. Please enter a higher amount."),
})
.refine((data) => data.refillAmount >= data.threshold, {
message:
"Your refill amount must be equal to or greater than the balance you entered above.",
path: ["refillAmount"],
});
export default function WalletRefill() {
const { toast } = useToast();
const toastOnFail = useToastOnFail();
const { requestTopUp, autoTopUpConfig, updateAutoTopUpConfig } = useCredits({
fetchInitialAutoTopUpConfig: true,
});
const [isLoading, setIsLoading] = useState(false);
const topUpForm = useForm<z.infer<typeof topUpSchema>>({
resolver: zodResolver(topUpSchema),
});
const autoRefillForm = useForm<z.infer<typeof autoRefillSchema>>({
resolver: zodResolver(autoRefillSchema),
});
console.log("autoRefillForm");
// Pre-fill the auto-refill form with existing values
useEffect(() => {
const values = autoRefillForm.getValues();
if (
autoTopUpConfig &&
autoTopUpConfig.amount > 0 &&
autoTopUpConfig.threshold > 0 &&
!autoRefillForm.getFieldState("threshold").isTouched &&
!autoRefillForm.getFieldState("refillAmount").isTouched
) {
autoRefillForm.setValue("threshold", autoTopUpConfig.threshold / 100);
autoRefillForm.setValue("refillAmount", autoTopUpConfig.amount / 100);
}
}, [autoTopUpConfig, autoRefillForm]);
const submitTopUp = useCallback(
async (data: z.infer<typeof topUpSchema>) => {
setIsLoading(true);
await requestTopUp(data.amount * 100).catch(
toastOnFail("request top-up"),
);
setIsLoading(false);
},
[requestTopUp, toastOnFail],
);
const submitAutoTopUpConfig = useCallback(
async (data: z.infer<typeof autoRefillSchema>) => {
setIsLoading(true);
await updateAutoTopUpConfig(data.refillAmount * 100, data.threshold * 100)
.then(() => {
toast({ title: "Auto top-up config updated! 🎉" });
})
.catch(toastOnFail("update auto top-up config"));
setIsLoading(false);
},
[updateAutoTopUpConfig, toast, toastOnFail],
);
return (
<div className="mx-1 border-b border-zinc-300">
<p className="mx-0 mt-4 font-sans text-xs font-medium text-violet-700">
Add credits to your balance
</p>
<p className="mx-0 my-1 font-sans text-xs font-normal text-zinc-500">
Choose a one-time top-up or set up automatic refills
</p>
<Tabs
defaultValue="top-up"
className="mb-6 mt-4 flex w-full flex-col items-center"
>
<TabsList className="mx-auto">
<TabsTrigger value="top-up">One-time top up</TabsTrigger>
<TabsTrigger value="auto-refill">Auto-refill</TabsTrigger>
</TabsList>
<div className="mt-4 w-full rounded-lg px-5 outline outline-1 outline-offset-2 outline-zinc-200">
<TabsContent value="top-up" className="flex flex-col">
<div className="mt-2 justify-start font-sans text-sm font-medium leading-snug text-zinc-900">
One-time top-up
</div>
<div className="mt-1 justify-start font-sans text-xs font-normal leading-tight text-zinc-500">
Enter an amount (min. $5) and add credits instantly.
</div>
<Form {...topUpForm}>
<form onSubmit={topUpForm.handleSubmit(submitTopUp)}>
<FormField
control={topUpForm.control}
name="amount"
render={({ field }) => (
<FormItem className="mb-6 mt-4">
<FormLabel className="font-sans text-sm font-medium leading-snug text-zinc-800">
Amount
</FormLabel>
<FormControl>
<>
<Input
className={cn(
"mt-2 rounded-3xl border-0 bg-white py-2 pl-6 pr-4 font-sans outline outline-1 outline-zinc-300",
"focus:outline-2 focus:outline-offset-0 focus:outline-violet-700",
)}
type="number"
step="1"
{...field}
/>
<span className="absolute left-10 -translate-y-9 text-sm text-zinc-500">
$
</span>
</>
</FormControl>
<FormMessage className="mt-2 font-sans text-xs font-normal leading-tight" />
</FormItem>
)}
/>
<button
className={cn(
"mb-2 inline-flex h-10 w-24 items-center justify-center rounded-3xl bg-zinc-800 px-4 py-2",
"font-sans text-sm font-medium leading-snug text-white",
"transition-colors duration-200 hover:bg-zinc-700 disabled:bg-zinc-500",
)}
type="submit"
disabled={isLoading}
>
Top up
</button>
</form>
</Form>
</TabsContent>
<TabsContent value="auto-refill" className="flex flex-col">
<div className="justify-start font-sans text-sm font-medium leading-snug text-zinc-900">
Auto-refill
</div>
<div className="mt-1 justify-start font-sans text-xs font-normal leading-tight text-zinc-500">
Choose a one-time top-up or set up automatic refills.
</div>
<Form {...autoRefillForm}>
<form
onSubmit={autoRefillForm.handleSubmit(submitAutoTopUpConfig)}
>
<FormField
control={autoRefillForm.control}
name="threshold"
render={({ field }) => (
<FormItem className="mb-6 mt-4">
<FormLabel className="font-sans text-sm font-medium leading-snug text-zinc-800">
Refill when balance drops below:
</FormLabel>
<FormControl>
<>
<Input
className={cn(
"mt-2 rounded-3xl border-0 bg-white py-2 pl-6 pr-4 font-sans outline outline-1 outline-zinc-300",
"focus:outline-2 focus:outline-offset-0 focus:outline-violet-700",
)}
type="number"
step="1"
{...field}
/>
<span className="absolute left-10 -translate-y-9 text-sm text-zinc-500">
$
</span>
</>
</FormControl>
<FormMessage className="mt-2 font-sans text-xs font-normal leading-tight" />
</FormItem>
)}
/>
<FormField
control={autoRefillForm.control}
name="refillAmount"
render={({ field }) => (
<FormItem className="mb-6">
<FormLabel className="font-sans text-sm font-medium leading-snug text-zinc-800">
Add this amount:
</FormLabel>
<FormControl>
<>
<Input
className={cn(
"mt-2 rounded-3xl border-0 bg-white py-2 pl-6 pr-4 font-sans outline outline-1 outline-zinc-300",
"focus:outline-2 focus:outline-offset-0 focus:outline-violet-700",
)}
type="number"
step="1"
{...field}
/>
<span className="absolute left-10 -translate-y-9 text-sm text-zinc-500">
$
</span>
</>
</FormControl>
<FormMessage className="mt-2 font-sans text-xs font-normal leading-tight" />
</FormItem>
)}
/>
<button
className={cn(
"mb-4 inline-flex h-10 w-40 items-center justify-center rounded-3xl bg-zinc-800 px-4 py-2",
"font-sans text-sm font-medium leading-snug text-white",
"transition-colors duration-200 hover:bg-zinc-700 disabled:bg-zinc-500",
)}
type="submit"
disabled={isLoading}
>
Enable Auto-refill
</button>
</form>
</Form>
</TabsContent>
<div className="mb-3 justify-start font-sans text-xs font-normal leading-tight">
<span className="text-zinc-500">
To update your billing details, head to{" "}
</span>
<Link
href="/profile/credits"
className="cursor-pointer text-zinc-800 underline"
>
Billing settings
</Link>
</div>
</div>
</Tabs>
</div>
);
}

View File

@@ -147,13 +147,3 @@
.custom-switch {
padding-left: 2px;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}

View File

@@ -0,0 +1,76 @@
"use client";
import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "@/lib/utils";
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 w-80 items-center justify-center rounded-3xl bg-gray-100 p-[5px] text-neutral-500 dark:bg-neutral-800 dark:text-neutral-400",
className,
)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-start justify-center gap-2.5 whitespace-nowrap rounded-2xl px-3 py-2 text-center font-sans text-xs font-medium leading-tight text-gray-500 ring-offset-white transition-all",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
"data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-subtle",
className,
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300",
className,
)}
{...props}
/>
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent };
<div className="inline-flex h-10 w-80 items-start justify-start rounded-3xl bg-gray-100 p-[5px]">
<div
data-state="selected"
className="flex flex-1 items-start justify-start gap-2.5 rounded-2xl bg-background px-3 py-1.5 shadow-subtle"
>
<div className="flex-1 justify-start text-center font-['Geist'] text-xs font-medium leading-tight text-foreground">
\ One-time top up
</div>
</div>
<div
data-state="unselected"
className="flex flex-1 items-start justify-start gap-2.5 rounded-sm px-3 py-1.5"
>
<div className="flex-1 justify-start text-center font-['Geist'] text-xs font-medium leading-tight text-gray-500">
Auto-refill
</div>
</div>
</div>;

View File

@@ -131,9 +131,7 @@ export default function useCredits({
}
const value = Math.abs(credit);
const sign = credit < 0 ? "-" : "";
const precision =
2 - (value % 100 === 0 ? 1 : 0) - (value % 10 === 0 ? 1 : 0);
return `${sign}$${(value / 100).toFixed(precision)}`;
return `${sign}$${(value / 100).toFixed(2)}`;
}, []);
return {

View File

@@ -117,6 +117,9 @@ const config = {
// Add a full radius for pill-shaped buttons
full: "9999px",
},
boxShadow: {
subtle: "0px 1px 2px 0px rgba(0,0,0,0.05)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },

View File

@@ -2158,6 +2158,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3"
integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==
"@radix-ui/primitive@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.2.tgz#83f415c4425f21e3d27914c12b3272a32e3dae65"
integrity sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==
"@radix-ui/react-alert-dialog@^1.1.5":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz#52187fdaa5110ed6749e75974e90c3505f788c5d"
@@ -2225,11 +2230,26 @@
"@radix-ui/react-primitive" "2.0.2"
"@radix-ui/react-slot" "1.1.2"
"@radix-ui/react-collection@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.3.tgz#cfd46dcea5a8ab064d91798feeb46faba4032930"
integrity sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==
dependencies:
"@radix-ui/react-compose-refs" "1.1.2"
"@radix-ui/react-context" "1.1.2"
"@radix-ui/react-primitive" "2.0.3"
"@radix-ui/react-slot" "1.2.0"
"@radix-ui/react-compose-refs@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec"
integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==
"@radix-ui/react-compose-refs@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30"
integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==
"@radix-ui/react-context-menu@^2.2.5":
version "2.2.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.2.6.tgz#752fd1d91f92bba287ef2b558770f4ca7d74523e"
@@ -2247,6 +2267,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a"
integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
"@radix-ui/react-context@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36"
integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==
"@radix-ui/react-dialog@1.1.6", "@radix-ui/react-dialog@^1.1.2":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz#65b4465e99ad900f28a98eed9a94bb21ec644bf7"
@@ -2272,6 +2297,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc"
integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==
"@radix-ui/react-direction@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14"
integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==
"@radix-ui/react-dismissable-layer@1.1.5":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz#96dde2be078c694a621e55e047406c58cd5fe774"
@@ -2322,6 +2352,13 @@
dependencies:
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-id@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7"
integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==
dependencies:
"@radix-ui/react-use-layout-effect" "1.1.1"
"@radix-ui/react-label@^2.1.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.2.tgz#994a5d815c2ff46e151410ae4e301f1b639f9971"
@@ -2406,6 +2443,14 @@
"@radix-ui/react-compose-refs" "1.1.1"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.3.tgz#ce3400caec9892ceb862f96ddaa2add080c09b90"
integrity sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==
dependencies:
"@radix-ui/react-compose-refs" "1.1.2"
"@radix-ui/react-use-layout-effect" "1.1.1"
"@radix-ui/react-primitive@2.0.2", "@radix-ui/react-primitive@^2.0.0":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz#ac8b7854d87b0d7af388d058268d9a7eb64ca8ef"
@@ -2413,6 +2458,13 @@
dependencies:
"@radix-ui/react-slot" "1.1.2"
"@radix-ui/react-primitive@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz#13c654dc4754558870a9c769f6febe5980a1bad8"
integrity sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==
dependencies:
"@radix-ui/react-slot" "1.2.0"
"@radix-ui/react-radio-group@^1.2.1":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.2.3.tgz#f60f58179cce716ccdb5c3d53a2eca97e4efd520"
@@ -2444,6 +2496,21 @@
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-roving-focus@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.3.tgz#c992b9d30c795f5f5a668853db8f4a6e07b7284d"
integrity sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==
dependencies:
"@radix-ui/primitive" "1.1.2"
"@radix-ui/react-collection" "1.1.3"
"@radix-ui/react-compose-refs" "1.1.2"
"@radix-ui/react-context" "1.1.2"
"@radix-ui/react-direction" "1.1.1"
"@radix-ui/react-id" "1.1.1"
"@radix-ui/react-primitive" "2.0.3"
"@radix-ui/react-use-callback-ref" "1.1.1"
"@radix-ui/react-use-controllable-state" "1.1.1"
"@radix-ui/react-scroll-area@^1.2.1":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz#6a9a7897add739ce84b517796ee345d495893d3f"
@@ -2500,6 +2567,13 @@
dependencies:
"@radix-ui/react-compose-refs" "1.1.1"
"@radix-ui/react-slot@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz#57727fc186ddb40724ccfbe294e1a351d92462ba"
integrity sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==
dependencies:
"@radix-ui/react-compose-refs" "1.1.2"
"@radix-ui/react-switch@^1.1.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.3.tgz#cb6386909d1d3f65a2b81a3b15da8c91d18f49b0"
@@ -2513,6 +2587,20 @@
"@radix-ui/react-use-previous" "1.1.0"
"@radix-ui/react-use-size" "1.1.0"
"@radix-ui/react-tabs@^1.1.4":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.4.tgz#2e43f3ef3450143281e7c1491da1e5d7941b9826"
integrity sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==
dependencies:
"@radix-ui/primitive" "1.1.2"
"@radix-ui/react-context" "1.1.2"
"@radix-ui/react-direction" "1.1.1"
"@radix-ui/react-id" "1.1.1"
"@radix-ui/react-presence" "1.1.3"
"@radix-ui/react-primitive" "2.0.3"
"@radix-ui/react-roving-focus" "1.1.3"
"@radix-ui/react-use-controllable-state" "1.1.1"
"@radix-ui/react-toast@^1.2.5":
version "1.2.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.6.tgz#f8d4bb2217851d221d700ac48fbe866b35023361"
@@ -2554,6 +2642,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1"
integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==
"@radix-ui/react-use-callback-ref@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40"
integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==
"@radix-ui/react-use-controllable-state@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0"
@@ -2561,6 +2654,13 @@
dependencies:
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-controllable-state@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz#ec9c572072a6f269df7435c1652fbeebabe0f0c1"
integrity sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==
dependencies:
"@radix-ui/react-use-callback-ref" "1.1.1"
"@radix-ui/react-use-escape-keydown@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754"
@@ -2573,6 +2673,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27"
integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==
"@radix-ui/react-use-layout-effect@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e"
integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==
"@radix-ui/react-use-previous@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c"