mirror of
https://github.com/Sevichecc/m-oauth.git
synced 2025-04-30 06:59:29 +08:00
feat: copy button
This commit is contained in:
parent
ae0fc58569
commit
c2900df6e9
9 changed files with 102 additions and 45 deletions
10
app/page.tsx
10
app/page.tsx
|
@ -8,7 +8,7 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import DataDisplay from "@/components/DataDisplay";
|
||||
import FormContainer from "@/components/FormContainer";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
|
@ -19,21 +19,15 @@ export default function Home() {
|
|||
<CardTitle>M-OAuth</CardTitle>
|
||||
<CardDescription>Card Description</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<ClientOnly>
|
||||
<InputForm />
|
||||
<FormContainer/>
|
||||
</ClientOnly>
|
||||
</CardContent>
|
||||
{/* <CardFooter>
|
||||
<p>Card Footer</p>
|
||||
</CardFooter> */}
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<DataDisplay />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -1,40 +1,44 @@
|
|||
'use client'
|
||||
"use client";
|
||||
|
||||
import useCreateApp from "@/hooks/useCreateApp";
|
||||
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { AppEntry } from "../lib/types";
|
||||
|
||||
interface DataDisplayProps {
|
||||
appEntry: AppEntry;
|
||||
}
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { AppEntry } from "@/lib/types";
|
||||
import { CopyButton } from "./ui/copybutton";
|
||||
|
||||
const renderTableRow = (label: string, value: string | undefined) => (
|
||||
<TableRow>
|
||||
<TableRow className="group relative">
|
||||
<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>
|
||||
);
|
||||
|
||||
const DataDisplay = () => {
|
||||
const { appEntry } = useCreateApp();
|
||||
|
||||
const DataDisplay = ({ appEntry }: { appEntry: AppEntry }) => {
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Data Name</TableHead>
|
||||
<TableHead className="min-w-[120px]">Type</TableHead>
|
||||
<TableHead>Value</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{renderTableRow("ID", appEntry?.id)}
|
||||
{renderTableRow("Name", appEntry?.name)}
|
||||
{renderTableRow("Website", appEntry?.website || '')}
|
||||
{renderTableRow("Redirect URI", appEntry?.redirectUri)}
|
||||
{renderTableRow("Client ID", appEntry?.clientId)}
|
||||
{renderTableRow("Client Secret", appEntry?.clientSecret)}
|
||||
{renderTableRow("Vapid Key", appEntry?.vapidKey)}
|
||||
{renderTableRow("Website", appEntry?.website || "")}
|
||||
{renderTableRow("Redirect URI", appEntry?.redirect_uri)}
|
||||
{renderTableRow("Client ID", appEntry?.client_id)}
|
||||
{renderTableRow("Client Secret", appEntry?.client_secret)}
|
||||
{renderTableRow("Vapid Key", appEntry?.vapid_key)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
|
|
16
components/FormContainer.tsx
Normal file
16
components/FormContainer.tsx
Normal 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;
|
|
@ -15,9 +15,8 @@ import {
|
|||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
|
||||
import ScopeSection from "@/components/ScopeSection";
|
||||
import ScopeSection from "@/components/scopes/ScopeSection";
|
||||
import { scopesInfo } from "@/lib/utils";
|
||||
import useCreateApp from "@/hooks/useCreateApp";
|
||||
|
||||
const formSchema = z.object({
|
||||
instance: z.string().trim(),
|
||||
|
@ -28,9 +27,11 @@ const formSchema = z.object({
|
|||
});
|
||||
|
||||
export type FormSchema = z.infer<typeof formSchema>;
|
||||
interface InputFormProps {
|
||||
createApp: ({}: FormSchema) => Promise<void>;
|
||||
}
|
||||
|
||||
const InputForm = () => {
|
||||
const { createApp } = useCreateApp();
|
||||
const InputForm: React.FC<InputFormProps> = ({ createApp }) => {
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
|
@ -43,7 +44,10 @@ const InputForm = () => {
|
|||
|
||||
return (
|
||||
<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
|
||||
control={form.control}
|
||||
name="instance"
|
||||
|
@ -132,7 +136,9 @@ const InputForm = () => {
|
|||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit" className="w-ful">Submit</Button>
|
||||
<Button type="submit" className="w-ful">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import ScopeItem from "@/components/ScopeItem";
|
||||
import ScopeItem from "@/components/scopes/ScopeItem";
|
||||
|
||||
import { ScopeInfo } from "@/lib/types";
|
||||
|
40
components/ui/copybutton.tsx
Normal file
40
components/ui/copybutton.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -24,7 +24,6 @@ const useCreateApp = () => {
|
|||
scopes: scopes?.join(" "),
|
||||
};
|
||||
|
||||
console.log("app,", app);
|
||||
try {
|
||||
let request = await fetch(`${instance}/api/v1/apps`, {
|
||||
method: "POST",
|
||||
|
@ -41,14 +40,13 @@ const useCreateApp = () => {
|
|||
} catch (error) {
|
||||
throw new Error((error as MError).error);
|
||||
}
|
||||
},
|
||||
[]
|
||||
},[]
|
||||
);
|
||||
console.log(appEntry);
|
||||
|
||||
return {
|
||||
appEntry,
|
||||
createApp,
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateApp;
|
||||
export default useCreateApp;
|
|
@ -18,10 +18,9 @@ export interface ScopeInfo {
|
|||
id: string;
|
||||
name: string;
|
||||
website: string | null;
|
||||
redirectUri: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
vapidKey: string;
|
||||
redirect_uri: string;
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
vapid_key: string;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue