From 9235e9a6fbb364713105137b6bf5bba9d81ecd4c Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Fri, 16 Feb 2024 21:26:24 +0000 Subject: ui: Change action buttons to show a spinner when the request is loading --- .../app/dashboard/bookmarks/components/AddLink.tsx | 6 ++--- .../dashboard/settings/components/AddApiKey.tsx | 28 ++++++++++++++++++---- .../dashboard/settings/components/DeleteApiKey.tsx | 23 ++++++++++-------- packages/web/components/ui/action-button.tsx | 25 +++++++++++++++++++ packages/web/components/ui/spinner.tsx | 20 ++++++++++++++++ 5 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 packages/web/components/ui/action-button.tsx create mode 100644 packages/web/components/ui/spinner.tsx diff --git a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx index 34f043e7..0ef4d193 100644 --- a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx +++ b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx @@ -1,6 +1,5 @@ "use client"; -import { Button } from "@/components/ui/button"; import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Plus } from "lucide-react"; @@ -10,6 +9,7 @@ import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { toast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; +import { ActionButton } from "@/components/ui/action-button"; const formSchema = z.object({ url: z.string().url({ message: "The link must be a valid URL" }), @@ -62,9 +62,9 @@ export default function AddLink() { ); }} /> - + diff --git a/packages/web/app/dashboard/settings/components/AddApiKey.tsx b/packages/web/app/dashboard/settings/components/AddApiKey.tsx index c438f4b1..27111b87 100644 --- a/packages/web/app/dashboard/settings/components/AddApiKey.tsx +++ b/packages/web/app/dashboard/settings/components/AddApiKey.tsx @@ -29,9 +29,19 @@ import { useForm, SubmitErrorHandler } from "react-hook-form"; import { toast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; import { useState } from "react"; -import { Copy } from "lucide-react"; +import { Check, Copy } from "lucide-react"; +import LoadingSpinner from "@/components/ui/spinner"; +import { ActionButton } from "@/components/ui/action-button"; function ApiKeySuccess({ apiKey }: { apiKey: string }) { + const [isCopied, setCopied] = useState(false); + + const onCopy = () => { + navigator.clipboard.writeText(apiKey); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + return (
@@ -40,8 +50,12 @@ function ApiKeySuccess({ apiKey }: { apiKey: string }) {
-
@@ -104,9 +118,13 @@ function AddApiKeyForm({ onSuccess }: { onSuccess: (key: string) => void }) { ); }} /> - + ); diff --git a/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx b/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx index bc3e3c92..566136af 100644 --- a/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx +++ b/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx @@ -16,6 +16,8 @@ import { import { useRouter } from "next/navigation"; import { toast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; +import { ActionButton } from "@/components/ui/action-button"; +import { useState } from "react"; export default function DeleteApiKey({ name, @@ -24,18 +26,20 @@ export default function DeleteApiKey({ name: string; id: string; }) { + const [isDialogOpen, setDialogOpen] = useState(false); const router = useRouter(); const mutator = api.apiKeys.revoke.useMutation({ onSuccess: () => { toast({ description: "Key was successfully deleted", }); + setDialogOpen(false); router.refresh(); }, }); return ( - + - - - + mutator.mutate({ id })} + > + Delete + diff --git a/packages/web/components/ui/action-button.tsx b/packages/web/components/ui/action-button.tsx new file mode 100644 index 00000000..42e16f65 --- /dev/null +++ b/packages/web/components/ui/action-button.tsx @@ -0,0 +1,25 @@ +import { Button, ButtonProps } from "./button"; +import LoadingSpinner from "./spinner"; + +export function ActionButton({ + children, + loading, + spinner, + disabled, + ...props +}: ButtonProps & { + loading: boolean; + spinner?: React.ReactNode; +}) { + spinner ||= ; + if (disabled !== undefined) { + disabled ||= loading; + } else if (loading) { + disabled = true; + } + return ( + + ); +} diff --git a/packages/web/components/ui/spinner.tsx b/packages/web/components/ui/spinner.tsx new file mode 100644 index 00000000..adcd2807 --- /dev/null +++ b/packages/web/components/ui/spinner.tsx @@ -0,0 +1,20 @@ +import { cn } from "@/lib/utils"; + +export default function LoadingSpinner({ className }: { className?: string }) { + return ( + + + + ); +} -- cgit v1.2.3-70-g09d2