diff --git a/frontend/package.json b/frontend/package.json index 476a72d..901a83a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", diff --git a/frontend/src/components/settings/AccessCodeSection.tsx b/frontend/src/components/settings/AccessCodeSection.tsx index 0dada1f..5135307 100644 --- a/frontend/src/components/settings/AccessCodeSection.tsx +++ b/frontend/src/components/settings/AccessCodeSection.tsx @@ -1,20 +1,88 @@ +import { useEffect, useState } from "react"; import { Settings } from "../../types"; +import { Button } from "../ui/button"; import { Input } from "../ui/input"; import { Label } from "../ui/label"; +import useThrottle from "../../hooks/useThrottle"; +import { Progress } from "../ui/progress"; +import { PICO_BACKEND_FORM_SECRET } from "../../config"; interface Props { settings: Settings; setSettings: React.Dispatch>; } +interface UsageResponse { + used_credits: number; + total_credits: number; + is_valid: boolean; +} + +enum FetchState { + EMPTY = "EMPTY", + LOADING = "LOADING", + INVALID = "INVALID", + VALID = "VALID", +} + function AccessCodeSection({ settings, setSettings }: Props) { + const [isLoading, setIsLoading] = useState(false); + const [isValid, setIsValid] = useState(false); + const [usedCredits, setUsedCredits] = useState(0); + const [totalCredits, setTotalCredits] = useState(0); + const throttledAccessCode = useThrottle(settings.accessCode || "", 500); + + const fetchState = (() => { + if (!settings.accessCode) return FetchState.EMPTY; + if (isLoading) return FetchState.LOADING; + if (!isValid) return FetchState.INVALID; + return FetchState.VALID; + })(); + + async function fetchUsage(accessCode: string) { + const res = await fetch( + "https://backend.buildpicoapps.com/screenshot_to_code/get_access_code_usage", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + access_code: accessCode, + secret: PICO_BACKEND_FORM_SECRET, + }), + } + ); + const usage = (await res.json()) as UsageResponse; + + if (!usage.is_valid) { + setIsValid(false); + } else { + setIsValid(true); + setUsedCredits(usage.used_credits); + setTotalCredits(usage.total_credits); + } + + setIsLoading(false); + } + + useEffect(() => { + // Don't do anything if access code is empty + if (!throttledAccessCode) return; + + setIsLoading(true); + setIsValid(true); + + // Wait for 500 ms before fetching usage + setTimeout(async () => { + await fetchUsage(throttledAccessCode); + }, 500); + }, [throttledAccessCode]); + return (
+ + {fetchState === "EMPTY" && ( +
+ + + +
+ )} + + {fetchState === "LOADING" && ( +
+ Loading... +
+ )} + + {fetchState === "INVALID" && ( + <> +
+ Invalid access code +
+ + )} + + {fetchState === "VALID" && ( + <> + +
+ + {usedCredits} out of {totalCredits} credits used + + + + +
+ + )}
); } diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx new file mode 100644 index 0000000..3fd47ad --- /dev/null +++ b/frontend/src/components/ui/progress.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 16ffdbe..d2a33d1 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -869,6 +869,15 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-progress@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-progress/-/react-progress-1.0.3.tgz#8380272fdc64f15cbf263a294dea70a7d5d9b4fa" + integrity sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-roving-focus@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"