From 2a795423d8ad19e6f1997c60885f44231aeb2f78 Mon Sep 17 00:00:00 2001 From: Van Minh Date: Thu, 4 Sep 2025 22:41:28 +0700 Subject: [PATCH] feat: add transaction progress steps component and documentation --- components/txn-progress-steps-preview.tsx | 207 ++++++++++++++++++ .../Txn-Feedback/txn-progress-steps.tsx | 83 +++++++ components/ui/murphy/index.tsx | 7 +- .../Txn-Feedback/txn-progress-steps.mdx | 159 ++++++++++++++ content/docs/onchainkit/meta.json | 3 +- public/r/txn-progress-steps.json | 16 ++ registry.json | 14 ++ registry/components/txn-progress-steps.json | 14 ++ types/transaction/index.ts | 27 +++ 9 files changed, 526 insertions(+), 4 deletions(-) create mode 100644 components/txn-progress-steps-preview.tsx create mode 100644 components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx create mode 100644 content/docs/onchainkit/Txn-Feedback/txn-progress-steps.mdx create mode 100644 public/r/txn-progress-steps.json create mode 100644 registry/components/txn-progress-steps.json create mode 100644 types/transaction/index.ts diff --git a/components/txn-progress-steps-preview.tsx b/components/txn-progress-steps-preview.tsx new file mode 100644 index 0000000..f635956 --- /dev/null +++ b/components/txn-progress-steps-preview.tsx @@ -0,0 +1,207 @@ +"use client"; + +import { useRef, useState } from "react"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { TxnStep } from "@/types/transaction"; +import { TxnProgressSteps } from "./ui/murphy/Txn-Feedback/txn-progress-steps"; + +export default function TxnProgressStepsPreview() { + const initialSteps: TxnStep[] = [ + { + id: "1", + title: "Connect Wallet", + description: "Connect your Solana wallet", + status: "completed", + }, + { + id: "2", + title: "Prepare Transaction", + description: "Preparing transaction data", + status: "completed", + }, + { + id: "3", + title: "Sign Transaction", + description: "Sign with your wallet", + status: "active", + }, + { + id: "4", + title: "Submit to Network", + description: "Broadcasting to Solana", + status: "pending", + }, + { + id: "5", + title: "Confirmation", + description: "Waiting for confirmation", + status: "pending", + }, + ]; + + const [currentSteps, setCurrentSteps] = useState(initialSteps); + const intervalRef = useRef(null); + + const updateSteps = ( + stepIndex: number, + status: TxnStep["status"] + ): TxnStep[] => { + return initialSteps.map((step, index) => { + if (index < stepIndex) return { ...step, status: "completed" }; + if (index === stepIndex) return { ...step, status }; + return { ...step, status: "pending" }; + }); + }; + + const simulateProgress = () => { + if (intervalRef.current) return; // prevent multiple intervals + + let step = 0; + intervalRef.current = setInterval(() => { + if (step < initialSteps.length) { + setCurrentSteps(updateSteps(step, "active")); + step++; + } else { + setCurrentSteps(updateSteps(initialSteps.length - 1, "completed")); + clearStimulate(); + } + }, 1500); + }; + + const simulateError = () => { + const activeIndex = currentSteps.findIndex( + (step) => step.status === "active" + ); + if (activeIndex === -1) return; + + // Stop progression + clearStimulate(); + + // Set error step + setCurrentSteps((prev) => + prev.map((step, index) => + index === activeIndex ? { ...step, status: "error" } : step + ) + ); + }; + + const clearStimulate = () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; + + const reset = () => { + clearStimulate(); + setCurrentSteps(initialSteps); + }; + + return ( +
+ {/* Horizontal */} + + + Horizontal Layout + + Perfect for desktop interfaces and wide layouts + + + + +
+ + + +
+
+
+ + {/* Vertical */} + + + Vertical Layout + + Ideal for mobile interfaces and detailed descriptions + + + + + + + + {/* Step States */} + + + Step States + + Different visual states for transaction steps + + + +
+

Success Flow

+ +
+
+

Error Flow

+ +
+
+
+
+ ); +} diff --git a/components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx b/components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx new file mode 100644 index 0000000..db6ecea --- /dev/null +++ b/components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx @@ -0,0 +1,83 @@ +"use client"; + +import { Check, Loader2, X } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { TxnStep } from "@/types/transaction"; + +interface TxnProgressStepsProps { + steps: TxnStep[]; + orientation?: "horizontal" | "vertical"; + className?: string; +} + +export function TxnProgressSteps({ + steps, + orientation = "horizontal", + className, +}: TxnProgressStepsProps) { + const isVertical = orientation === "vertical"; + + return ( +
+ {steps.map((step, index) => ( +
+
+ {step.status === "completed" && } + {step.status === "active" && ( + + )} + {step.status === "error" && } + {step.status === "pending" && ( + {index + 1} + )} +
+ +
+
+ {step.title} +
+ {step.description && ( +
+ {step.description} +
+ )} +
+ + {!isVertical && index < steps.length - 1 && ( +
+ )} +
+ ))} +
+ ); +} diff --git a/components/ui/murphy/index.tsx b/components/ui/murphy/index.tsx index 987b191..bdd64de 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,7 +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 { TxnProgressSteps } from "@/components/ui/murphy/Txn-Feedback/txn-progress-steps"; export { ConnectWalletButton, SendTokenForm, @@ -78,5 +78,6 @@ export { TMLaunchpadForm, HydraFanoutForm, MPLHybridForm, - TokenMetadataViewer + TokenMetadataViewer, + TxnProgressSteps, }; diff --git a/content/docs/onchainkit/Txn-Feedback/txn-progress-steps.mdx b/content/docs/onchainkit/Txn-Feedback/txn-progress-steps.mdx new file mode 100644 index 0000000..96276a2 --- /dev/null +++ b/content/docs/onchainkit/Txn-Feedback/txn-progress-steps.mdx @@ -0,0 +1,159 @@ +--- +title: Transaction Progress Steps +description: Visual progress indicator for multi-step transaction workflows +icon: Activity +--- + +import TxnProgressStepsPreview from "@/components/txn-progress-steps-preview"; + + +
+
+ +
+
+
+ +## Installation + + + + + Install Transaction Progress Steps + + + + + +## Basic Usage + +```tsx +"use client"; + +import { TxProgressSteps } from "@/components/ui/murphy/Txn-Feedback/txn-progress-steps"; +import type { TxStep } from "@/types/transaction"; + +export default function MyPage() { + const steps: TxStep[] = [ + { + id: "1", + title: "Connect Wallet", + description: "Connect your Solana wallet", + status: "completed", + }, + { + id: "2", + title: "Prepare Transaction", + description: "Preparing transaction data", + status: "completed", + }, + { + id: "3", + title: "Sign Transaction", + description: "Sign with your wallet", + status: "active", + }, + { + id: "4", + title: "Submit to Network", + description: "Broadcasting to Solana", + status: "pending", + }, + { + id: "5", + title: "Confirmation", + description: "Waiting for confirmation", + status: "pending", + }, + ]; + + return ( +
+

Transaction Progress

+ +
+ ); +} +``` + +## Props + + + +### TxStep Interface + + + +## Customization + +### Custom Styling + +```tsx + +``` + +### Responsive Design + +```tsx + + + +``` 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" ] } diff --git a/public/r/txn-progress-steps.json b/public/r/txn-progress-steps.json new file mode 100644 index 0000000..a72009d --- /dev/null +++ b/public/r/txn-progress-steps.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "txn-progress-steps", + "type": "registry:block", + "title": "A visual step tracker for multi-step transaction workflows.", + "dependencies": [], + "registryDependencies": [], + "files": [ + { + "path": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx", + "content": "\"use client\";\r\n\r\nimport { Check, Loader2, X } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport type { TxnStep } from \"@/types/transaction\";\r\n\r\ninterface TxnProgressStepsProps {\r\n steps: TxnStep[];\r\n orientation?: \"horizontal\" | \"vertical\";\r\n className?: string;\r\n}\r\n\r\nexport function TxnProgressSteps({\r\n steps,\r\n orientation = \"horizontal\",\r\n className,\r\n}: TxnProgressStepsProps) {\r\n const isVertical = orientation === \"vertical\";\r\n\r\n return (\r\n \r\n {steps.map((step, index) => (\r\n \r\n \r\n {step.status === \"completed\" && }\r\n {step.status === \"active\" && (\r\n \r\n )}\r\n {step.status === \"error\" && }\r\n {step.status === \"pending\" && (\r\n {index + 1}\r\n )}\r\n
\r\n\r\n
\r\n \r\n {step.title}\r\n
\r\n {step.description && (\r\n
\r\n {step.description}\r\n
\r\n )}\r\n \r\n\r\n {!isVertical && index < steps.length - 1 && (\r\n
\r\n )}\r\n
\r\n ))}\r\n \r\n );\r\n}\r\n", + "type": "registry:file", + "target": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx" + } + ] +} \ No newline at end of file diff --git a/registry.json b/registry.json index bac943b..dbb1793 100644 --- a/registry.json +++ b/registry.json @@ -1412,6 +1412,20 @@ } ] }, + { + "name": "txn-progress-steps", + "type": "registry:block", + "title": "A visual step tracker for multi-step transaction workflows.", + "registryDependencies": [], + "dependencies": [], + "files": [ + { + "path": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx", + "type": "registry:file", + "target": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx" + } + ] + }, { "name": "txn-settings", "type": "registry:block", diff --git a/registry/components/txn-progress-steps.json b/registry/components/txn-progress-steps.json new file mode 100644 index 0000000..fdec8cb --- /dev/null +++ b/registry/components/txn-progress-steps.json @@ -0,0 +1,14 @@ +{ + "name": "txn-progress-steps", + "type": "registry:block", + "title": "A visual step tracker for multi-step transaction workflows.", + "registryDependencies": [], + "dependencies": [], + "files": [ + { + "path": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx", + "type": "registry:file", + "target": "components/ui/murphy/Txn-Feedback/txn-progress-steps.tsx" + } + ] +} diff --git a/types/transaction/index.ts b/types/transaction/index.ts new file mode 100644 index 0000000..5be4a81 --- /dev/null +++ b/types/transaction/index.ts @@ -0,0 +1,27 @@ +export interface TransactionStatus { + status: + | "idle" + | "preparing" + | "signing" + | "sending" + | "confirming" + | "success" + | "error"; + signature?: string; + error?: string; + step?: number; + totalSteps?: number; +} + +export interface TxnStep { + id: string; + title: string; + description?: string; + status: "pending" | "active" | "completed" | "error"; +} + +export interface TxnFeedbackProps { + status: TransactionStatus; + onRetry?: () => void; + onClose?: () => void; +}