diff --git a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/monitors/[id]/layout.tsx b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/monitors/[id]/layout.tsx
index 9033319cb..cc2214d62 100644
--- a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/monitors/[id]/layout.tsx
+++ b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/monitors/[id]/layout.tsx
@@ -4,6 +4,7 @@ import { Badge } from "@openstatus/ui/src/components/badge";
import { Header } from "@/components/dashboard/header";
import AppPageWithSidebarLayout from "@/components/layout/app-page-with-sidebar-layout";
+import { CopyButton } from "@/components/layout/header/copy-button";
import { JobTypeIconWithTooltip } from "@/components/monitor/job-type-icon-with-tooltip";
import { NotificationIconWithTooltip } from "@/components/monitor/notification-icon-with-tooltip";
import { StatusDotWithTooltip } from "@/components/monitor/status-dot-with-tooltip";
@@ -82,6 +83,7 @@ export default async function Layout(props: {
/>
}
+ actions={}
/>
{children}
diff --git a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/notifications/[id]/layout.tsx b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/notifications/[id]/layout.tsx
index 84b31f950..966081fe7 100644
--- a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/notifications/[id]/layout.tsx
+++ b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/notifications/[id]/layout.tsx
@@ -2,6 +2,7 @@ import { notFound } from "next/navigation";
import { Header } from "@/components/dashboard/header";
import AppPageWithSidebarLayout from "@/components/layout/app-page-with-sidebar-layout";
+import { CopyButton } from "@/components/layout/header/copy-button";
import { api } from "@/trpc/server";
export default async function Layout(props: {
@@ -27,6 +28,7 @@ export default async function Layout(props: {
{notification.provider}}
+ actions={}
/>
{children}
diff --git a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/[id]/layout.tsx b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/[id]/layout.tsx
index 8ea399ab6..73740598d 100644
--- a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/[id]/layout.tsx
+++ b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/[id]/layout.tsx
@@ -6,6 +6,7 @@ import { Button } from "@openstatus/ui/src/components/button";
import { getBaseUrl } from "@/app/status-page/[domain]/utils";
import { Header } from "@/components/dashboard/header";
import AppPageWithSidebarLayout from "@/components/layout/app-page-with-sidebar-layout";
+import { CopyButton } from "@/components/layout/header/copy-button";
import { api } from "@/trpc/server";
export default async function Layout(props: {
@@ -31,8 +32,9 @@ export default async function Layout(props: {
+ actions={[
+ ,
+
- }
+ ,
+ ]}
/>
{children}
diff --git a/apps/web/src/components/data-table/maintenance/data-table-row-actions.tsx b/apps/web/src/components/data-table/maintenance/data-table-row-actions.tsx
index 9b4cf0afe..6d30ded01 100644
--- a/apps/web/src/components/data-table/maintenance/data-table-row-actions.tsx
+++ b/apps/web/src/components/data-table/maintenance/data-table-row-actions.tsx
@@ -25,6 +25,7 @@ import {
} from "@openstatus/ui";
import { LoadingAnimation } from "@/components/loading-animation";
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { toastAction } from "@/lib/toast";
import { api } from "@/trpc/client";
@@ -39,6 +40,7 @@ export function DataTableRowActions({
const router = useRouter();
const [alertOpen, setAlertOpen] = React.useState(false);
const [isPending, startTransition] = React.useTransition();
+ const { copy } = useCopyToClipboard();
async function onDelete() {
startTransition(async () => {
@@ -70,6 +72,15 @@ export function DataTableRowActions({
Edit
+
+ copy(`${maintenance.id}`, {
+ withToast: `Copied ID '${maintenance.id}'`,
+ })
+ }
+ >
+ Copy ID
+
Delete
diff --git a/apps/web/src/components/data-table/monitor/data-table-row-actions.tsx b/apps/web/src/components/data-table/monitor/data-table-row-actions.tsx
index 8a5069c1a..609540a26 100644
--- a/apps/web/src/components/data-table/monitor/data-table-row-actions.tsx
+++ b/apps/web/src/components/data-table/monitor/data-table-row-actions.tsx
@@ -32,6 +32,7 @@ import { toast, toastAction } from "@/lib/toast";
import { api } from "@/trpc/client";
import type { TCPResponse } from "@/app/api/checker/test/tcp/schema";
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
interface DataTableRowActionsProps {
row: Row;
@@ -46,6 +47,7 @@ export function DataTableRowActions({
const router = useRouter();
const [alertOpen, setAlertOpen] = useState(false);
const [isPending, startTransition] = useTransition();
+ const { copy } = useCopyToClipboard();
async function onDelete() {
startTransition(async () => {
@@ -155,6 +157,15 @@ export function DataTableRowActions({
Clone
Test
+
+ copy(`${monitor.id}`, {
+ withToast: `Copied ID '${monitor.id}'`,
+ })
+ }
+ >
+ Copy ID
+
diff --git a/apps/web/src/components/data-table/notification/data-table-row-actions.tsx b/apps/web/src/components/data-table/notification/data-table-row-actions.tsx
index c0547f221..a55e58a61 100644
--- a/apps/web/src/components/data-table/notification/data-table-row-actions.tsx
+++ b/apps/web/src/components/data-table/notification/data-table-row-actions.tsx
@@ -26,6 +26,7 @@ import {
} from "@openstatus/ui";
import { LoadingAnimation } from "@/components/loading-animation";
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { toastAction } from "@/lib/toast";
import { api } from "@/trpc/client";
@@ -40,6 +41,7 @@ export function DataTableRowActions({
const router = useRouter();
const [alertOpen, setAlertOpen] = React.useState(false);
const [isPending, startTransition] = React.useTransition();
+ const { copy } = useCopyToClipboard();
async function onDelete() {
startTransition(async () => {
@@ -73,6 +75,15 @@ export function DataTableRowActions({
Edit
+
+ copy(`${notification.id}`, {
+ withToast: `Copied ID '${notification.id}'`,
+ })
+ }
+ >
+ Copy ID
+
diff --git a/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx b/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx
index 9a2939145..34ecf1bca 100644
--- a/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx
+++ b/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx
@@ -105,6 +105,15 @@ export function DataTableRowActions({
>
Visit
+
+ copy(`${page.id}`, {
+ withToast: `Copied ID '${page.id}'`,
+ })
+ }
+ >
+ Copy ID
+
Create Report
diff --git a/apps/web/src/components/data-table/status-report/data-table-row-actions.tsx b/apps/web/src/components/data-table/status-report/data-table-row-actions.tsx
index 944272c55..fe2148f67 100644
--- a/apps/web/src/components/data-table/status-report/data-table-row-actions.tsx
+++ b/apps/web/src/components/data-table/status-report/data-table-row-actions.tsx
@@ -25,6 +25,7 @@ import {
} from "@openstatus/ui";
import { LoadingAnimation } from "@/components/loading-animation";
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { toastAction } from "@/lib/toast";
import { api } from "@/trpc/client";
@@ -39,6 +40,7 @@ export function DataTableRowActions({
const router = useRouter();
const [alertOpen, setAlertOpen] = React.useState(false);
const [isPending, startTransition] = React.useTransition();
+ const { copy } = useCopyToClipboard();
async function onDelete() {
startTransition(async () => {
@@ -75,6 +77,15 @@ export function DataTableRowActions({
View
+
+ copy(`${statusReport.id}`, {
+ withToast: `Copied ID '${statusReport.id}'`,
+ })
+ }
+ >
+ Copy ID
+
Delete
diff --git a/apps/web/src/components/layout/header/copy-button.tsx b/apps/web/src/components/layout/header/copy-button.tsx
new file mode 100644
index 000000000..c0198bb6c
--- /dev/null
+++ b/apps/web/src/components/layout/header/copy-button.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
+import {
+ Button,
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@openstatus/ui";
+import { Copy } from "lucide-react";
+
+export function CopyButton({ id }: { id: string | number }) {
+ const { copy } = useCopyToClipboard();
+ return (
+
+
+
+
+
+ Copy ID for API usage
+
+
+ );
+}
diff --git a/apps/web/src/hooks/use-copy-to-clipboard.ts b/apps/web/src/hooks/use-copy-to-clipboard.ts
index 121d01c1f..612304fe0 100644
--- a/apps/web/src/hooks/use-copy-to-clipboard.ts
+++ b/apps/web/src/hooks/use-copy-to-clipboard.ts
@@ -10,7 +10,7 @@ export function useCopyToClipboard() {
{
timeout = 3000,
withToast = false,
- }: { timeout?: number; withToast?: boolean },
+ }: { timeout?: number; withToast?: boolean | string },
) => {
if (!navigator?.clipboard) {
console.warn("Clipboard not supported");
@@ -28,7 +28,11 @@ export function useCopyToClipboard() {
}
if (withToast) {
- toast.success("Copied to clipboard");
+ if (typeof withToast === "string") {
+ toast.success(withToast);
+ } else {
+ toast.success("Copied to clipboard");
+ }
}
return true;