diff --git a/components/txn-pending-indication-preview.tsx b/components/txn-pending-indication-preview.tsx
new file mode 100644
index 0000000..75f47cd
--- /dev/null
+++ b/components/txn-pending-indication-preview.tsx
@@ -0,0 +1,180 @@
+"use client";
+
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Plus, X } from "lucide-react";
+import { TxnPendingIndicator } from "./ui/murphy/Txn-Feedback/txn-pending-indicator";
+
+interface PendingTransaction {
+ id: string;
+ signature?: string;
+ description: string;
+ startTime: number;
+}
+
+export default function TxnPendingIndicatorPreview() {
+ const [pendingTransactions, setPendingTransactions] = useState<
+ PendingTransaction[]
+ >([]);
+ const [position, setPosition] = useState<
+ "top-left" | "top-right" | "bottom-left" | "bottom-right"
+ >("bottom-right");
+
+ const addTransaction = (description: string) => {
+ const newTransaction: PendingTransaction = {
+ id: Date.now().toString(),
+ description,
+ startTime: Date.now(),
+ };
+ setPendingTransactions((prev) => [...prev, newTransaction]);
+
+ setTimeout(() => {
+ setPendingTransactions((prev) =>
+ prev.filter((txn) => txn.id !== newTransaction.id)
+ );
+ }, 15000);
+ };
+
+ const removeTransaction = (id: string) => {
+ setPendingTransactions((prev) => prev.filter((txn) => txn.id !== id));
+ };
+
+ const clearAllTransactions = () => {
+ setPendingTransactions([]);
+ };
+
+ const addBatchTransactions = () => {
+ const batchTransactions = [
+ "Transfer to Alice",
+ "Transfer to Bob",
+ "Transfer to Charlie",
+ ].map((desc, index) => ({
+ id: `batch_${Date.now()}_${index}`,
+ description: desc,
+ startTime: Date.now(),
+ }));
+
+ setPendingTransactions((prev) => [...prev, ...batchTransactions]);
+
+ setTimeout(() => {
+ batchTransactions.forEach((txn) => {
+ setPendingTransactions((prev) => prev.filter((t) => t.id !== txn.id));
+ });
+ }, 20000);
+ };
+
+ return (
+
+
+
+
+
+ Example Usage
+
+
+ Add transactions to see the pending indicator appear. It will show
+ in the {position} corner.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Current pending:
+
+ {pendingTransactions.length}
+
+
+
+
+
+
+
+ Position Options
+
+
+ Change the position of the pending indicator on screen
+
+
+
+
+ {(
+ [
+ "top-left",
+ "top-right",
+ "bottom-left",
+ "bottom-right",
+ ] as const
+ ).map((pos) => (
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/ui/murphy/Txn-Feedback/txn-pending-indicator.tsx b/components/ui/murphy/Txn-Feedback/txn-pending-indicator.tsx
new file mode 100644
index 0000000..2691f41
--- /dev/null
+++ b/components/ui/murphy/Txn-Feedback/txn-pending-indicator.tsx
@@ -0,0 +1,120 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Loader2, X } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { cn } from "@/lib/utils";
+
+interface PendingTransaction {
+ id: string;
+ signature?: string;
+ description: string;
+ startTime: number;
+}
+
+interface TxnPendingIndicatorProps {
+ transactions: PendingTransaction[];
+ onCancel?: (id: string) => void;
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
+ className?: string;
+}
+
+export function TxnPendingIndicator({
+ transactions,
+ onCancel,
+ position = "bottom-right",
+ className,
+}: TxnPendingIndicatorProps) {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ useEffect(() => {
+ if (transactions.length === 0) {
+ setIsExpanded(false);
+ }
+ }, [transactions.length]);
+
+ if (transactions.length === 0) return null;
+
+ const positionClasses = {
+ "top-left": "top-4 left-4",
+ "top-right": "top-4 right-4",
+ "bottom-left": "bottom-4 left-4",
+ "bottom-right": "bottom-4 right-4",
+ };
+
+ const getElapsedTime = (startTime: number) => {
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
+ if (elapsed < 60) return `${elapsed}s`;
+ return `${Math.floor(elapsed / 60)}m ${elapsed % 60}s`;
+ };
+
+ return (
+
+ {!isExpanded ? (
+
+ ) : (
+
+
+
Pending Transactions
+
+
+
+
+ {transactions.map((tx) => (
+
+
+
+
+
+ {tx.description}
+
+
+ {getElapsedTime(tx.startTime)}
+
+
+
+ {onCancel && (
+
+ )}
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/components/ui/murphy/index.tsx b/components/ui/murphy/index.tsx
index 987b191..4678eb0 100644
--- a/components/ui/murphy/index.tsx
+++ b/components/ui/murphy/index.tsx
@@ -14,7 +14,7 @@ import CandyMachineForm from "./candy-machine-form";
import CoreCandyMachineForm from "./core-candy-machine-form";
import BubblegumLegacyForm from "./bubblegum-legacy-form";
import ImprovedCNFTManager from "./improved-cnft-manager";
-import CompressedNFTViewer from "./compressed-nft-viewer"
+import CompressedNFTViewer from "./compressed-nft-viewer";
import { CreateMerkleTree } from "./create-merkleTree-form";
import { TokenList } from "./token-list";
import { StakeForm } from "./stake-token-form";
@@ -37,6 +37,7 @@ import { CoreAssetLaunchpad } from "./core-asset-launchpad";
import { HydraFanoutForm } from "./hydra-fanout-form";
import { MPLHybridForm } from "./mpl-hybrid-form";
import { TokenMetadataViewer } from "./token-metadata-viewer";
+import { TxnPendingIndicator } from "./Txn-Feedback/txn-pending-indicator";
export {
ConnectWalletButton,
@@ -78,5 +79,6 @@ export {
TMLaunchpadForm,
HydraFanoutForm,
MPLHybridForm,
- TokenMetadataViewer
+ TokenMetadataViewer,
+ TxnPendingIndicator,
};
diff --git a/content/docs/onchainkit/Txn-Feedback/txn-pending-indicator.mdx b/content/docs/onchainkit/Txn-Feedback/txn-pending-indicator.mdx
new file mode 100644
index 0000000..fd8d37a
--- /dev/null
+++ b/content/docs/onchainkit/Txn-Feedback/txn-pending-indicator.mdx
@@ -0,0 +1,199 @@
+---
+title: Pending Transactions Indicator
+description: Global indicator showing pending transactions with elapsed time
+icon: Clock
+---
+
+import TxnPendingIndicatorPreview from "@/components/txn-pending-indication-preview";
+
+
+
+
+
+## Installation
+
+
+
+
+ Install Pending Transactions Indicator
+
+
+
+
+
+## Basic Usage
+
+```tsx
+"use client";
+
+import { useState } from "react";
+import { TxnPendingIndicator } from "@/components/ui/murphy/Txn-Feedback/txn-pending-indicator";
+import { Button } from "@/components/ui/button";
+
+interface PendingTransaction {
+ id: string;
+ description: string;
+ startTime: number;
+}
+
+export default function MyPage() {
+ const [pendingTxns, setPendingTxns] = useState([]);
+
+ const addTxn = () => {
+ const id = Date.now().toString();
+ const newTxn: PendingTransaction = {
+ id,
+ description: "Simulated Transaction",
+ startTime: Date.now(),
+ };
+
+ setPendingTxns((prev) => [...prev, newTxn]);
+ };
+
+ const cancelTxn = (id: string) => {
+ setPendingTxns((prev) => prev.filter((t) => t.id !== id));
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+```
+
+## Props
+
+ void",
+ default: "undefined",
+ },
+ position: {
+ description: "Position of the indicator on screen",
+ type: "'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'",
+ default: "'bottom-right'",
+ },
+ className: {
+ description: "Additional CSS classes",
+ type: "string",
+ default: "undefined",
+ },
+ }}
+/>
+
+### PendingTransaction Interface
+
+
+
+## Indicator Features
+
+### Collapsible Interface
+
+- Collapsed: Shows count badge with pending transactions
+- Expanded: Shows detailed list of all pending transactions
+- Click to toggle between states
+
+### Elapsed Time Tracking
+
+- Real-time elapsed time display
+- Format: seconds (30s), minutes (2m 30s)
+- Updates automatically every second
+
+### Individual Transaction Management
+
+- Cancel individual transactions
+- Transaction descriptions
+- Unique transaction IDs
+
+### Positioning Options
+
+- **top-left**: Upper left corner
+- **top-right**: Upper right corner
+- **bottom-left**: Lower left corner
+- **bottom-right**: Lower right corner (default)
+
+### Auto-hide Behavior
+
+- Automatically hides when no pending transactions
+- Smooth fade in/out animations
+- Responsive design for mobile devices
+
+## Customization
+
+### Custom Positioning
+
+```tsx
+
+```
+
+### Custom Styling
+
+```tsx
+
+```
+
+### Custom Transaction Display
+
+```tsx
+// Extend the component for custom transaction rendering
+const CustomPendingIndicator = ({ transactions, onCancel }) => {
+ return (
+ ({
+ ...txn,
+ description: `🔄 ${txn.description} (${txn.type})`,
+ }))}
+ onCancel={onCancel}
+ />
+ );
+};
+```
diff --git a/content/docs/onchainkit/meta.json b/content/docs/onchainkit/meta.json
index d2b1c3b..329e6c2 100644
--- a/content/docs/onchainkit/meta.json
+++ b/content/docs/onchainkit/meta.json
@@ -31,6 +31,7 @@
"Metaplex",
"Meteora-DBC",
"ZK-Compression",
- "Jupiter-Recurring"
+ "Jupiter-Recurring",
+ "Txn-Feedback"
]
}