diff --git a/src/components/ArbitratorPicker.tsx b/src/components/ArbitratorPicker.tsx
new file mode 100644
index 0000000..1b47a95
--- /dev/null
+++ b/src/components/ArbitratorPicker.tsx
@@ -0,0 +1,101 @@
+"use client";
+
+import { truncateAddress } from "@stellar-split/sdk";
+
+interface Arbitrator {
+ address: string;
+ name: string;
+ resolvedDisputeCount: number | null;
+}
+
+interface Props {
+ /** List of arbitrators to display */
+ arbitrators: Arbitrator[];
+ /** Currently selected arbitrator address */
+ selectedAddress: string;
+ /** Callback when an arbitrator is selected */
+ onSelect: (address: string) => void;
+ /** Whether the component is disabled (e.g., dispute resolved) */
+ disabled?: boolean;
+ /** Heading text (defaults to "Assigned Arbitrators") */
+ heading?: string;
+ /** Show vote status badges (default: false) */
+ showVoteStatus?: boolean;
+ /** List of arbitrators who have voted (used when showVoteStatus is true) */
+ votedArbitrators?: string[];
+}
+
+/**
+ * ArbitratorPicker — displays a selectable list of arbitrators.
+ * Extracted from DisputePanel for reusability and testability.
+ * Can show vote status badges and is fully typed.
+ */
+export default function ArbitratorPicker({
+ arbitrators,
+ selectedAddress,
+ onSelect,
+ disabled = false,
+ heading = "Assigned Arbitrators",
+ showVoteStatus = false,
+ votedArbitrators = [],
+}: Props) {
+ return (
+
+
{heading}
+
+ {arbitrators.length === 0 ? (
+
+ No arbitrators assigned
+
+ ) : (
+ arbitrators.map((arb) => {
+ const isSelected = arb.address === selectedAddress;
+ const hasVoted = votedArbitrators.includes(arb.address);
+
+ return (
+
+ );
+ })
+ )}
+
+
+ );
+}
diff --git a/src/components/DisputePanel.tsx b/src/components/DisputePanel.tsx
index 338c032..a0b4626 100644
--- a/src/components/DisputePanel.tsx
+++ b/src/components/DisputePanel.tsx
@@ -5,6 +5,7 @@ import type { Invoice } from "@stellar-split/sdk";
import { truncateAddress } from "@stellar-split/sdk";
import { uploadToIpfs } from "@/lib/ipfs";
import { getSplitClient } from "@/lib/stellar";
+import ArbitratorPicker from "@/components/ArbitratorPicker";
interface DisputeMetadata {
reason: string;
@@ -281,31 +282,18 @@ export default function DisputePanel({ invoice, publicKey, onRefresh }: Props) {
{/* Arbitrators List */}
-
-
Assigned Arbitrators
-
- {dispute.arbitrators.map((arb, idx) => {
- const voted = dispute.votedArbitrators.includes(arb);
- return (
-
- {truncateAddress(arb)}
-
- {voted ? "✓ Voted" : "Pending"}
-
-
- );
- })}
-
-
+ ({
+ address,
+ name: truncateAddress(address),
+ resolvedDisputeCount: null,
+ }))}
+ selectedAddress=""
+ onSelect={() => {}}
+ disabled={true}
+ showVoteStatus={true}
+ votedArbitrators={dispute.votedArbitrators}
+ />
{/* Evidence Upload Modal */}
diff --git a/src/components/DisputeWizard.tsx b/src/components/DisputeWizard.tsx
index 2e4d449..7057832 100644
--- a/src/components/DisputeWizard.tsx
+++ b/src/components/DisputeWizard.tsx
@@ -11,12 +11,59 @@ const DISPUTE_REASONS = [
"Other",
];
+const TOTAL_STEPS = 4;
+
interface Props {
invoiceId: string;
onSubmit: (reason: string, description: string, arbitratorAddress?: string) => Promise;
onClose: () => void;
}
+/**
+ * StepIndicator — displays dot-based progress through the wizard steps.
+ * Completed steps are filled, the current step is highlighted, upcoming steps are muted.
+ */
+function StepIndicator({ current, total }: { current: number; total: number }) {
+ return (
+
+ );
+}
+
+/**
+ * DisputeWizard — multi-step modal form for filing an on-chain dispute.
+ * Guides the user through: reason → description → arbitrator → confirmation.
+ */
export default function DisputeWizard({ invoiceId, onSubmit, onClose }: Props) {
const [step, setStep] = useState(1);
const [reason, setReason] = useState("");
@@ -78,10 +125,18 @@ export default function DisputeWizard({ invoiceId, onSubmit, onClose }: Props) {
+ {/* Step indicator */}
+
+
+
+ Step {step} of {TOTAL_STEPS}
+
+
+
{/* Step 1: Reason Selection */}
{step === 1 && (
-
Step 1 of 4: Select reason
+
Select reason
{DISPUTE_REASONS.map((r) => (