Download apps/dashboard/src/components/forms/api-key-form.tsx from AUXteam/Midday: direct link, hf CLI and curl.
- Browser
- Download file 6.46 kB
-
https://huggingface.co/spaces/AUXteam/Midday/resolve/main/apps/dashboard/src/components/forms/api-key-form.tsx
- Command line
-
hf download hf://spaces/AUXteam/Midday/apps/dashboard/src/components/forms/api-key-form.tsx
-
curl -L -o api-key-form.tsx https://huggingface.co/spaces/AUXteam/Midday/resolve/main/apps/dashboard/src/components/forms/api-key-form.tsx
6.46 kB
| "use client"; | |
| import { | |
| SCOPES, | |
| type Scope, | |
| type ScopePreset, | |
| scopePresets, | |
| scopesToName, | |
| } from "@api/utils/scopes"; | |
| import { AnimatedSizeContainer } from "@midday/ui/animated-size-container"; | |
| import { | |
| Form, | |
| FormControl, | |
| FormField, | |
| FormItem, | |
| FormLabel, | |
| FormMessage, | |
| } from "@midday/ui/form"; | |
| import { Input } from "@midday/ui/input"; | |
| import { SubmitButton } from "@midday/ui/submit-button"; | |
| import { Tabs, TabsList, TabsTrigger } from "@midday/ui/tabs"; | |
| import { useMutation, useQueryClient } from "@tanstack/react-query"; | |
| import { useEffect, useState } from "react"; | |
| import { z } from "zod/v3"; | |
| import { useZodForm } from "@/hooks/use-zod-form"; | |
| import { useTokenModalStore } from "@/store/token-modal"; | |
| import { useTRPC } from "@/trpc/client"; | |
| import { RESOURCES } from "@/utils/scopes"; | |
| import { ScopeSelector } from "../scope-selector"; | |
| const formSchema = z.object({ | |
| id: z.string().optional(), | |
| name: z.string().min(2, { | |
| message: "Team name must be at least 2 characters.", | |
| }), | |
| scopes: z.array(z.enum(SCOPES)).default(["apis.all"]), | |
| }); | |
| type Props = { | |
| onSuccess: (key: string | null) => void; | |
| }; | |
| export function ApiKeyForm({ onSuccess }: Props) { | |
| const { data } = useTokenModalStore(); | |
| const [preset, setPreset] = useState<ScopePreset>(() => | |
| data?.scopes | |
| ? (scopesToName(data.scopes).preset as ScopePreset) | |
| : "all_access", | |
| ); | |
| const trpc = useTRPC(); | |
| const queryClient = useQueryClient(); | |
| const upsertApiKeyMutation = useMutation( | |
| trpc.apiKeys.upsert.mutationOptions({ | |
| onSuccess: (data) => { | |
| queryClient.invalidateQueries({ | |
| queryKey: trpc.apiKeys.get.queryKey(), | |
| }); | |
| onSuccess(data.key); | |
| }, | |
| }), | |
| ); | |
| const form = useZodForm(formSchema, { | |
| defaultValues: { | |
| id: data?.id ?? undefined, | |
| name: data?.name ?? "", | |
| scopes: (data?.scopes as Scope[]) ?? ["apis.all"], | |
| }, | |
| }); | |
| // Effect to ensure proper initialization when editing existing API key | |
| useEffect(() => { | |
| if (data?.scopes) { | |
| const detectedPreset = scopesToName(data.scopes).preset as ScopePreset; | |
| setPreset(detectedPreset); | |
| // If it's restricted, make sure the form has the correct scopes | |
| if (detectedPreset === "restricted") { | |
| form.setValue("scopes", data.scopes as Scope[], { shouldDirty: true }); | |
| } | |
| } | |
| }, [data?.scopes, form]); | |
| // Update form scopes based on preset | |
| const updateScopesFromPreset = (newPreset: ScopePreset) => { | |
| let newScopes: Scope[] = []; | |
| switch (newPreset) { | |
| case "all_access": | |
| newScopes = ["apis.all"]; | |
| break; | |
| case "read_only": | |
| newScopes = ["apis.read"]; | |
| break; | |
| case "restricted": { | |
| // Keep existing scopes when switching to restricted mode | |
| const currentScopes = form.getValues("scopes"); | |
| // Get all valid scopes from RESOURCES | |
| const validScopes = RESOURCES.flatMap((resource) => | |
| resource.scopes.map((scope) => scope.scope), | |
| ); | |
| // Only keep scopes that are defined in RESOURCES | |
| newScopes = currentScopes.filter((scope): scope is Scope => | |
| validScopes.some((validScope) => validScope === scope), | |
| ); | |
| break; | |
| } | |
| } | |
| form.setValue("scopes", newScopes, { shouldDirty: true }); | |
| }; | |
| const handlePresetChange = (value: string) => { | |
| const scopePreset = value as ScopePreset; | |
| setPreset(scopePreset); | |
| updateScopesFromPreset(scopePreset); | |
| }; | |
| const handleResourceScopeChange = (resourceKey: string, scope: string) => { | |
| if (preset !== "restricted") return; | |
| const currentScopes = form.getValues("scopes"); | |
| const resource = RESOURCES.find((r) => r.key === resourceKey); | |
| if (!resource) return; | |
| // Remove any existing scopes for this resource | |
| const filteredScopes = currentScopes.filter( | |
| (currentScope) => !resource.scopes.some((s) => s.scope === currentScope), | |
| ); | |
| // Add the new scope if it's not empty | |
| const newScopes = scope | |
| ? [...filteredScopes, scope as Scope] | |
| : filteredScopes; | |
| form.setValue("scopes", newScopes, { shouldDirty: true }); | |
| }; | |
| function onSubmit(values: z.infer<typeof formSchema>) { | |
| upsertApiKeyMutation.mutate({ | |
| id: values.id, | |
| name: values.name, | |
| scopes: values.scopes, | |
| }); | |
| } | |
| return ( | |
| <Form {...form}> | |
| <form onSubmit={form.handleSubmit(onSubmit)}> | |
| <FormField | |
| control={form.control} | |
| name="name" | |
| render={({ field }) => ( | |
| <FormItem> | |
| <FormLabel>Name</FormLabel> | |
| <FormControl> | |
| <Input | |
| autoFocus | |
| className="mt-2" | |
| autoComplete="off" | |
| autoCapitalize="none" | |
| autoCorrect="off" | |
| spellCheck="false" | |
| {...field} | |
| /> | |
| </FormControl> | |
| <FormMessage /> | |
| </FormItem> | |
| )} | |
| /> | |
| <Tabs | |
| value={preset} | |
| className="mt-4 w-full" | |
| onValueChange={handlePresetChange} | |
| > | |
| <TabsList className="w-full flex"> | |
| {scopePresets.map((scope) => ( | |
| <TabsTrigger | |
| value={scope.value} | |
| className="flex-1" | |
| key={scope.value} | |
| > | |
| {scope.label} | |
| </TabsTrigger> | |
| ))} | |
| </TabsList> | |
| </Tabs> | |
| <p className="text-sm text-[#878787] mt-4"> | |
| This API key will have{" "} | |
| <span className="font-semibold"> | |
| {scopePresets.find((scope) => scope.value === preset)?.description} | |
| </span> | |
| . | |
| </p> | |
| <AnimatedSizeContainer height className="mt-4"> | |
| {preset === "restricted" && ( | |
| <ScopeSelector | |
| selectedScopes={form.watch("scopes")} | |
| onResourceScopeChange={handleResourceScopeChange} | |
| description="Select which scopes this API key can access." | |
| height="max-h-[300px]" | |
| /> | |
| )} | |
| </AnimatedSizeContainer> | |
| <SubmitButton | |
| className="mt-6 w-full" | |
| type="submit" | |
| disabled={!form.formState.isDirty} | |
| isSubmitting={upsertApiKeyMutation.isPending} | |
| > | |
| {data?.id ? "Update" : "Create"} | |
| </SubmitButton> | |
| </form> | |
| </Form> | |
| ); | |
| } | |