mirror of https://github.com/jetkvm/kvm.git
feat: add copy to clipboard functionality for MAC address in network settings
This commit is contained in:
parent
76d256b69a
commit
5e06625966
|
|
@ -28,6 +28,7 @@
|
|||
"react": "^19.1.1",
|
||||
"react-animate-height": "^3.2.3",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.9.3",
|
||||
|
|
@ -5856,8 +5857,6 @@
|
|||
"react": "^19.1.1"
|
||||
}
|
||||
},
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.62.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz",
|
||||
|
|
@ -5874,7 +5873,6 @@
|
|||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
>>>>>>> Stashed changes
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import { useCallback, useState } from "react";
|
||||
|
||||
export function useCopyToClipboard(resetInterval = 2000) {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copy = useCallback(async (text: string) => {
|
||||
if (!text) return false;
|
||||
|
||||
let success = false;
|
||||
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.warn("Clipboard API failed:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for insecure contexts
|
||||
if (!success) {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = text;
|
||||
textarea.style.position = "fixed";
|
||||
textarea.style.opacity = "0";
|
||||
document.body.appendChild(textarea);
|
||||
textarea.focus();
|
||||
textarea.select();
|
||||
|
||||
try {
|
||||
success = document.execCommand("copy");
|
||||
} catch (err) {
|
||||
console.error("Fallback copy failed:", err);
|
||||
success = false;
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
|
||||
setIsCopied(success);
|
||||
if (success && resetInterval > 0) {
|
||||
setTimeout(() => setIsCopied(false), resetInterval);
|
||||
}
|
||||
|
||||
return success;
|
||||
}, [resetInterval]);
|
||||
|
||||
return { copy, isCopied };
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import dayjs from "dayjs";
|
|||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FieldValues, FormProvider, useForm } from "react-hook-form";
|
||||
import { LuEthernetPort } from "react-icons/lu";
|
||||
import { LuCopy, LuEthernetPort } from "react-icons/lu";
|
||||
import validator from "validator";
|
||||
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
|
|
@ -24,6 +24,7 @@ import StaticIpv4Card from "../components/StaticIpv4Card";
|
|||
import StaticIpv6Card from "../components/StaticIpv6Card";
|
||||
import { useJsonRpc } from "../hooks/useJsonRpc";
|
||||
import { SettingsItem } from "../components/SettingsItem";
|
||||
import { useCopyToClipboard } from "../components/useCopyToClipBoard";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
|
|
@ -225,6 +226,8 @@ export default function SettingsNetworkRoute() {
|
|||
});
|
||||
};
|
||||
|
||||
const { copy } = useCopyToClipboard();
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormProvider {...formMethods}>
|
||||
|
|
@ -248,19 +251,26 @@ export default function SettingsNetworkRoute() {
|
|||
}
|
||||
/>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<SettingsItem
|
||||
title="MAC Address"
|
||||
description="Hardware identifier for the network interface"
|
||||
>
|
||||
<InputField
|
||||
type="text"
|
||||
size="SM"
|
||||
value={networkState?.mac_address}
|
||||
error={""}
|
||||
readOnly={true}
|
||||
className="dark:!text-opacity-60"
|
||||
/>
|
||||
</SettingsItem>
|
||||
<div className="flex items-center">
|
||||
<GridCard cardClassName="rounded-r-none">
|
||||
<div className=" h-[34px] flex items-center text-xs select-all text-black font-mono dark:text-white px-3 ">
|
||||
{networkState?.mac_address} {" "}
|
||||
</div>
|
||||
</GridCard>
|
||||
<Button className="rounded-l-none border-l-blue-900 dark:border-l-blue-600" size="SM" type="button" theme="primary" LeadingIcon={LuCopy} onClick={async () => {
|
||||
if (await copy(networkState?.mac_address || "")) {
|
||||
notifications.success("MAC address copied to clipboard");
|
||||
} else {
|
||||
notifications.error("Failed to copy MAC address");
|
||||
}
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<SettingsItem title="Hostname" description="Set the device hostname">
|
||||
<InputField
|
||||
size="SM"
|
||||
|
|
|
|||
Loading…
Reference in New Issue