feat: copy button

This commit is contained in:
SevicheCC 2023-06-06 02:47:14 +08:00
parent ae0fc58569
commit c2900df6e9
Signed by: SevicheCC
GPG key ID: C577000000000000
9 changed files with 102 additions and 45 deletions

View file

@ -8,7 +8,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import DataDisplay from "@/components/DataDisplay"; import FormContainer from "@/components/FormContainer";
export default function Home() { export default function Home() {
return ( return (
@ -19,21 +19,15 @@ export default function Home() {
<CardTitle>M-OAuth</CardTitle> <CardTitle>M-OAuth</CardTitle>
<CardDescription>Card Description</CardDescription> <CardDescription>Card Description</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<ClientOnly> <ClientOnly>
<InputForm /> <FormContainer/>
</ClientOnly> </ClientOnly>
</CardContent> </CardContent>
{/* <CardFooter> {/* <CardFooter>
<p>Card Footer</p> <p>Card Footer</p>
</CardFooter> */} </CardFooter> */}
</Card> </Card>
<Card>
<CardContent>
<DataDisplay />
</CardContent>
</Card>
</div> </div>
</main> </main>
); );

View file

@ -1,40 +1,44 @@
'use client' "use client";
import useCreateApp from "@/hooks/useCreateApp"; import {
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; Table,
import { AppEntry } from "../lib/types"; TableBody,
TableCaption,
interface DataDisplayProps { TableCell,
appEntry: AppEntry; TableHead,
} TableHeader,
TableRow,
} from "@/components/ui/table";
import { AppEntry } from "@/lib/types";
import { CopyButton } from "./ui/copybutton";
const renderTableRow = (label: string, value: string | undefined) => ( const renderTableRow = (label: string, value: string | undefined) => (
<TableRow> <TableRow className="group relative">
<TableCell className="font-medium">{label}</TableCell> <TableCell className="font-medium">{label}</TableCell>
<TableCell>{value}</TableCell> <TableCell className="font-mono break-all flex justify-between items-center">
{value}
{value && <CopyButton value={value} className="end-2 shrink-0"/>}
</TableCell>
</TableRow> </TableRow>
); );
const DataDisplay = () => { const DataDisplay = ({ appEntry }: { appEntry: AppEntry }) => {
const { appEntry } = useCreateApp();
return ( return (
<Table> <Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead>Data Name</TableHead> <TableHead className="min-w-[120px]">Type</TableHead>
<TableHead>Value</TableHead> <TableHead>Value</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{renderTableRow("ID", appEntry?.id)} {renderTableRow("ID", appEntry?.id)}
{renderTableRow("Name", appEntry?.name)} {renderTableRow("Name", appEntry?.name)}
{renderTableRow("Website", appEntry?.website || '')} {renderTableRow("Website", appEntry?.website || "")}
{renderTableRow("Redirect URI", appEntry?.redirectUri)} {renderTableRow("Redirect URI", appEntry?.redirect_uri)}
{renderTableRow("Client ID", appEntry?.clientId)} {renderTableRow("Client ID", appEntry?.client_id)}
{renderTableRow("Client Secret", appEntry?.clientSecret)} {renderTableRow("Client Secret", appEntry?.client_secret)}
{renderTableRow("Vapid Key", appEntry?.vapidKey)} {renderTableRow("Vapid Key", appEntry?.vapid_key)}
</TableBody> </TableBody>
</Table> </Table>
); );

View file

@ -0,0 +1,16 @@
"use client";
import InputForm from "@/components/InputForm";
import DataDisplay from "@/components/DataDisplay";
import useCreateApp from "@/hooks/useCreateApp";
const FormContainer = () => {
const { appEntry, createApp } = useCreateApp();
return (
<>
<InputForm createApp={createApp} />
{appEntry && <DataDisplay appEntry={appEntry} />}
</>
);
};
export default FormContainer;

View file

@ -15,9 +15,8 @@ import {
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import ScopeSection from "@/components/ScopeSection"; import ScopeSection from "@/components/scopes/ScopeSection";
import { scopesInfo } from "@/lib/utils"; import { scopesInfo } from "@/lib/utils";
import useCreateApp from "@/hooks/useCreateApp";
const formSchema = z.object({ const formSchema = z.object({
instance: z.string().trim(), instance: z.string().trim(),
@ -28,9 +27,11 @@ const formSchema = z.object({
}); });
export type FormSchema = z.infer<typeof formSchema>; export type FormSchema = z.infer<typeof formSchema>;
interface InputFormProps {
createApp: ({}: FormSchema) => Promise<void>;
}
const InputForm = () => { const InputForm: React.FC<InputFormProps> = ({ createApp }) => {
const { createApp } = useCreateApp();
const form = useForm<FormSchema>({ const form = useForm<FormSchema>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
@ -43,7 +44,10 @@ const InputForm = () => {
return ( return (
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(createApp)} className="space-y-6 flex flex-col"> <form
onSubmit={form.handleSubmit(createApp)}
className="flex flex-col space-y-6 mb-6"
>
<FormField <FormField
control={form.control} control={form.control}
name="instance" name="instance"
@ -132,7 +136,9 @@ const InputForm = () => {
</FormItem> </FormItem>
)} )}
/> />
<Button type="submit" className="w-ful">Submit</Button> <Button type="submit" className="w-ful">
Submit
</Button>
</form> </form>
</Form> </Form>
); );

View file

@ -8,7 +8,7 @@ import {
CollapsibleTrigger, CollapsibleTrigger,
} from "@/components/ui/collapsible"; } from "@/components/ui/collapsible";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import ScopeItem from "@/components/ScopeItem"; import ScopeItem from "@/components/scopes/ScopeItem";
import { ScopeInfo } from "@/lib/types"; import { ScopeInfo } from "@/lib/types";

View file

@ -0,0 +1,40 @@
"use client";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { Check, Copy } from "lucide-react";
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string;
}
const copyToClipboardWithMeta = (value: string, event?: Event) => {
navigator.clipboard.writeText(value);
};
export function CopyButton({ value, className, ...props }: CopyButtonProps) {
const [hasCopied, setHasCopied] = useState(false);
useEffect(() => {
setTimeout(() => {
setHasCopied(false);
}, 2000);
}, [hasCopied]);
return (
<button
className={cn(
"z-20 inline-flex h-8 w-8 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none ",
className
)}
onClick={() => {
copyToClipboardWithMeta(value);
setHasCopied(true);
}}
{...props}
>
<span className="sr-only">Copy</span>
{hasCopied ? <Check className="h-3 w-3" /> : <Copy className="h-3 w-3" />}
</button>
);
}

View file

@ -24,7 +24,6 @@ const useCreateApp = () => {
scopes: scopes?.join(" "), scopes: scopes?.join(" "),
}; };
console.log("app,", app);
try { try {
let request = await fetch(`${instance}/api/v1/apps`, { let request = await fetch(`${instance}/api/v1/apps`, {
method: "POST", method: "POST",
@ -41,10 +40,9 @@ const useCreateApp = () => {
} catch (error) { } catch (error) {
throw new Error((error as MError).error); throw new Error((error as MError).error);
} }
}, },[]
[]
); );
console.log(appEntry);
return { return {
appEntry, appEntry,
createApp, createApp,

View file

@ -18,10 +18,9 @@ export interface ScopeInfo {
id: string; id: string;
name: string; name: string;
website: string | null; website: string | null;
redirectUri: string; redirect_uri: string;
clientId: string; client_id: string;
clientSecret: string; client_secret: string;
vapidKey: string; vapid_key: string;
} }