Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions components/step-flow-dialog-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { TxnStep } from "@/types/transaction";
import { StepFlowDialog } from "./ui/murphy";

export default function StepFlowDialogPreview() {
const [showDialog, setShowDialog] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const [dialogType, setDialogType] = useState<
"nft-mint" | "token-swap" | "staking"
>("nft-mint");

const workflows = {
"nft-mint": {
title: "Mint NFT Process",
description: "Complete the following steps to mint your NFT",
steps: [
{
id: "1",
title: "Upload Metadata",
description: "Upload image and metadata to IPFS",
status: "pending" as const,
},
{
id: "2",
title: "Create Mint Account",
description: "Create NFT mint account on Solana",
status: "pending" as const,
},
{
id: "3",
title: "Mint Token",
description: "Mint NFT to your wallet",
status: "pending" as const,
},
{
id: "4",
title: "Verify Ownership",
description: "Verify NFT in your wallet",
status: "pending" as const,
},
],
},
"token-swap": {
title: "Token Swap Process",
description: "Swap your tokens through the following steps",
steps: [
{
id: "1",
title: "Get Quote",
description: "Calculate swap rates and fees",
status: "pending" as const,
},
{
id: "2",
title: "Approve Spending",
description: "Approve token spending limit",
status: "pending" as const,
},
{
id: "3",
title: "Execute Swap",
description: "Perform the token exchange",
status: "pending" as const,
},
],
},
staking: {
title: "Staking Process",
description: "Stake your tokens to earn rewards",
steps: [
{
id: "1",
title: "Select Validator",
description: "Choose a validator to stake with",
status: "pending" as const,
},
{
id: "2",
title: "Create Stake Account",
description: "Create a new stake account",
status: "pending" as const,
},
{
id: "3",
title: "Delegate Stake",
description: "Delegate tokens to validator",
status: "pending" as const,
},
{
id: "4",
title: "Activate Stake",
description: "Wait for stake activation",
status: "pending" as const,
},
],
},
};

const updateStepStatus = (stepIndex: number, status: TxnStep["status"]) => {
const currentWorkflow = workflows[dialogType];
return currentWorkflow.steps.map((step, index) => {
if (index < stepIndex) return { ...step, status: "completed" as const };
if (index === stepIndex) return { ...step, status };
return { ...step, status: "pending" as const };
});
};

const [currentSteps, setCurrentSteps] = useState<TxnStep[]>(
workflows["nft-mint"].steps
);

const openDialog = (type: keyof typeof workflows) => {
setDialogType(type);
setCurrentStep(0);
setCurrentSteps(workflows[type].steps);
setShowDialog(true);
};

const handleNext = async () => {
await new Promise((resolve) => setTimeout(resolve, 1500));

const nextStep = currentStep + 1;
setCurrentSteps(updateStepStatus(currentStep, "completed"));

if (nextStep < workflows[dialogType].steps.length) {
setCurrentStep(nextStep);
setCurrentSteps(updateStepStatus(nextStep, "active"));
}
};

const handlePrevious = () => {
const prevStep = Math.max(currentStep - 1, 0);
setCurrentStep(prevStep);
setCurrentSteps(updateStepStatus(prevStep, "active"));
};

const handleCancel = () => {
setShowDialog(false);
setCurrentStep(0);
setCurrentSteps(workflows[dialogType].steps);
};

return (
<div className="flex flex-col items-center justify-center gap-6 p-6 bg-background text-foreground rounded-lg">
<div className="text-center space-y-1">
<p className="text-sm text-muted-foreground">
Choose a workflow to simulate
</p>
</div>

<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 w-full max-w-md">
<Button
onClick={() => openDialog("nft-mint")}
className="bg-purple-600 hover:bg-purple-700 text-white"
>
NFT Minting
</Button>
<Button
onClick={() => openDialog("token-swap")}
className="bg-blue-600 hover:bg-blue-700 text-white"
>
Token Swap
</Button>
<Button
onClick={() => openDialog("staking")}
className="bg-green-600 hover:bg-green-700 text-white"
>
Staking
</Button>
</div>

<StepFlowDialog
open={showDialog}
onOpenChange={setShowDialog}
title={workflows[dialogType].title}
description={workflows[dialogType].description}
steps={currentSteps}
currentStep={currentStep}
onNext={handleNext}
onPrevious={handlePrevious}
onCancel={handleCancel}
canGoPrevious={currentStep > 0}
canGoNext={currentStep < workflows[dialogType].steps.length - 1}
/>
</div>
);
}
93 changes: 93 additions & 0 deletions components/success-dialog-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { SuccessDialog } from "./ui/murphy";

export default function SuccessDialogPreview() {
const [showDialog, setShowDialog] = useState(false);
const [dialogType, setDialogType] = useState<
"basic" | "token-transfer" | "nft-mint" | "staking"
>("basic");

const dialogExamples = {
basic: {
title: "Transaction Successful! πŸŽ‰",
description: "Your transaction has been confirmed on the blockchain",
signature:
"5VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM7n",
},
"token-transfer": {
title: "Tokens Sent Successfully! πŸ’Έ",
description: "Your tokens have been transferred to the recipient",
signature:
"2B5VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM",
amount: "100",
token: "USDC",
recipient: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
},
"nft-mint": {
title: "NFT Minted Successfully! 🎨",
description: "Your NFT has been minted and added to your wallet",
signature:
"3C6VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM",
amount: "1",
token: "NFT",
},
staking: {
title: "Staking Successful! πŸš€",
description: "Your tokens have been staked and are now earning rewards",
signature:
"4D7VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM",
amount: "500",
token: "SOL",
},
};

const openDialog = (type: keyof typeof dialogExamples) => {
setDialogType(type);
setShowDialog(true);
};

return (
<div className="flex flex-col items-center justify-center py-10 px-4 text-black dark:text-white space-y-4">
<Button
onClick={() => openDialog("basic")}
className="w-64 bg-green-600 hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600"
>
Basic Success Dialog
</Button>
<Button
onClick={() => openDialog("token-transfer")}
className="w-64 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600"
>
Token Transfer Success
</Button>
<Button
onClick={() => openDialog("nft-mint")}
className="w-64 bg-purple-600 hover:bg-purple-700 dark:bg-purple-500 dark:hover:bg-purple-600"
>
NFT Mint Success
</Button>
<Button
onClick={() => openDialog("staking")}
className="w-64 bg-orange-600 hover:bg-orange-700 dark:bg-orange-500 dark:hover:bg-orange-600"
>
Staking Success
</Button>

<SuccessDialog
open={showDialog}
onOpenChange={setShowDialog}
{...dialogExamples[dialogType]}
onViewExplorer={() =>
window.open(
`https://explorer.solana.com/tx/${dialogExamples[dialogType].signature}`,
"_blank"
)
}
onClose={() => setShowDialog(false)}
/>
</div>
);
}
103 changes: 103 additions & 0 deletions components/txn-error-fallback-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { TxnErrorFallback } from "./ui/murphy";

export default function TxnErrorFallbackPreview() {
const [showError, setShowError] = useState(false);
const [errorType, setErrorType] = useState<
"simple" | "with-signature" | "with-logs"
>("simple");

const errorExamples = {
simple: {
error: "Transaction failed: Insufficient funds for transaction fees",
},
"with-signature": {
error: "Transaction failed: Program error occurred during execution",
signature:
"5VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM7n",
},
"with-logs": {
error: "Transaction failed: Custom program error: 0x1771",
signature:
"2B5VfYmGC9L8ty3D4HutfxndoKXGBwXJWKKvxgF7qQzqK8xMjU9v7Rw2sP3nT6hL4jK9mN8bC1dF2eG3hI5jK6lM",
showLogs: true,
logs: [
"Program 11111111111111111111111111111111 invoke [1]",
"Program log: Instruction: Transfer",
"Program log: Error: custom program error: 0x1771",
"Program 11111111111111111111111111111111 consumed 200000 of 200000 compute units",
"Program 11111111111111111111111111111111 failed: custom program error: 0x1771",
],
},
};

const handleRetry = () => {
console.log("Retrying...");
setShowError(false);
setTimeout(() => {
if (Math.random() > 0.5) {
alert("Retry successful!");
} else {
setShowError(true);
}
}, 1500);
};

return (
<div className="p-6 max-w-2xl mx-auto space-y-6">
<div className="space-y-2">
<p className="text-gray-600 dark:text-gray-300">
Simulate different transaction error scenarios
</p>
</div>

<div className="space-y-3">
<Button
onClick={() => {
setErrorType("simple");
setShowError(true);
}}
variant="destructive"
className="w-full"
>
Simple Error
</Button>

<Button
onClick={() => {
setErrorType("with-signature");
setShowError(true);
}}
variant="destructive"
className="w-full"
>
Error with Signature
</Button>

<Button
onClick={() => {
setErrorType("with-logs");
setShowError(true);
}}
variant="destructive"
className="w-full"
>
Error with Logs
</Button>
</div>

{showError && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<TxnErrorFallback
{...errorExamples[errorType]}
onRetry={handleRetry}
onClose={() => setShowError(false)}
/>
</div>
)}
</div>
);
}
Loading