Skip to content

Commit 3541cc6

Browse files
canicefavourCaniceFavour
andauthored
implement dispute panel with IPFS evidence flow and live arbitration timeline (#550)
Co-authored-by: CaniceFavour <osuochafavouremeka@gmail.com>
1 parent e9f1015 commit 3541cc6

10 files changed

Lines changed: 3163 additions & 131 deletions

File tree

DISPUTE_FLOW_DIAGRAM.md

Lines changed: 467 additions & 0 deletions
Large diffs are not rendered by default.

DISPUTE_FLOW_IMPLEMENTATION.md

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
# Dispute Flow Panel and Timeline Implementation
2+
3+
## Overview
4+
5+
This document describes the comprehensive dispute flow implementation for the invoice detail page at `/invoice/[id]`. The implementation enables parties to monitor arbitration, submit evidence via IPFS, view live vote tallies, and allows authorized arbitrators to vote on dispute outcomes.
6+
7+
## Files Created/Modified
8+
9+
### New Files
10+
11+
1. **`src/components/DisputePanel.tsx`** - Main dispute interface component
12+
2. **`src/components/DisputeTimeline.tsx`** - Chronological event timeline (updated)
13+
3. **`src/lib/ipfs.ts`** - IPFS upload utilities
14+
4. **`src/components/__tests__/DisputePanel.test.tsx`** - Comprehensive test suite
15+
5. **`src/components/__tests__/DisputeTimeline.test.tsx`** - Timeline test suite
16+
6. **`src/lib/__tests__/ipfs.test.ts`** - IPFS utilities test suite
17+
18+
### Modified Files
19+
20+
1. **`src/app/invoice/[id]/page.tsx`** - Integrated DisputePanel component
21+
22+
## Features Implemented
23+
24+
### 1. Dispute Metadata Panel (`DisputePanel.tsx`)
25+
26+
The DisputePanel component renders conditionally when `invoice.status === "Disputed"` and displays:
27+
28+
- **Dispute Information**
29+
- Reason and detailed description
30+
- Initiator wallet address (truncated)
31+
- Timestamp of dispute filing
32+
- List of assigned arbitrators with voting status
33+
34+
- **Live Vote Tally**
35+
- Real-time vote counts (Release vs Refund)
36+
- Visual progress bars showing vote distribution
37+
- Vote percentage calculations
38+
- Number of votes cast vs total arbitrators
39+
40+
- **Resolved Status Badge**
41+
- Shows outcome when dispute is resolved
42+
- Color-coded badges (green for Release, orange for Refund)
43+
44+
### 2. Evidence Submission via IPFS
45+
46+
**Evidence Upload Modal:**
47+
- File selector supporting PDF, images, and documents
48+
- 10MB file size limit with validation
49+
- Accepted file types: `.pdf`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.txt`, `.doc`, `.docx`
50+
- Real-time upload to IPFS with CID generation
51+
- Evidence metadata includes: CID, filename, submitter address, timestamp
52+
53+
**IPFS Integration (`src/lib/ipfs.ts`):**
54+
- `uploadToIpfs()` - Uploads file to IPFS gateway
55+
- `getIpfsUrl()` - Generates IPFS gateway URL
56+
- `isValidCid()` - Validates CID format (v0 and v1)
57+
- `mockIpfsUpload()` - Development fallback when gateway unavailable
58+
- `uploadToIpfsWithFallback()` - Automatic fallback to mock for dev/test
59+
60+
**Evidence Display:**
61+
- List of all submitted evidence with metadata
62+
- Clickable "View" links opening IPFS gateway URLs
63+
- Submitter address and submission date for each piece of evidence
64+
- Evidence submission disabled after dispute resolution
65+
66+
### 3. Arbitrator Voting Controls
67+
68+
**Authorization Checks:**
69+
- Verifies connected wallet is in the appointed arbitrators list
70+
- Checks if arbitrator has already voted
71+
- Shows voting interface only to authorized, non-voted arbitrators
72+
73+
**Voting Actions:**
74+
- **"Approve Release"** button - Votes to release funds (vote = 1)
75+
- **"Reject / Refund"** button - Votes to refund (vote = 0)
76+
- Calls `sdk.voteDispute({ invoiceId, arbitrator, vote })` via Web3 SDK
77+
- Displays loading state during transaction submission
78+
- Shows success toast on vote confirmation
79+
- Triggers invoice data refresh after successful vote
80+
81+
**Post-Vote Display:**
82+
- Shows confirmation message to arbitrators who have already voted
83+
- Disables voting controls after vote is cast
84+
- Updates arbitrator list showing who has voted
85+
86+
### 4. Real-Time State Updates
87+
88+
**Integration with `useInvoiceStream`:**
89+
- The invoice detail page already uses `useInvoiceStream(invoiceId)` hook
90+
- Hook polls for invoice updates every 3 seconds
91+
- Automatically detects status changes and new events
92+
- DisputePanel receives updated invoice prop with latest dispute state
93+
94+
**Refresh Mechanism:**
95+
- `onRefresh` callback provided to DisputePanel
96+
- Called after evidence submission and voting actions
97+
- Manually triggers invoice reload to ensure immediate UI update
98+
- Works in conjunction with the streaming subscription for redundancy
99+
100+
**Live Tally Updates:**
101+
- Vote counts update automatically via invoice stream
102+
- Progress bars animate when new votes arrive
103+
- Arbitrator voting status updates in real-time
104+
105+
### 5. Dispute Lifecycle Timeline (`DisputeTimeline.tsx`)
106+
107+
**Event Types Displayed:**
108+
- **DisputeOpened** - Initial dispute filing with reason
109+
- **EvidenceSubmitted** - Evidence uploads with IPFS links
110+
- **VoteCast** - Individual arbitrator votes (Release/Refund)
111+
- **DisputeResolved** - Final outcome with vote tally
112+
113+
**Timeline Features:**
114+
- Chronological ordering of events (earliest to latest)
115+
- Color-coded event markers (red, blue, indigo, green)
116+
- Event-specific icons (⚠️, 📎, ⚖️, ✓)
117+
- Timestamp formatting with locale-aware dates
118+
- Truncated actor addresses with tooltips
119+
- IPFS evidence links with "View" action
120+
- Vote type badges showing Release/Refund choices
121+
- Final tally display on resolution events
122+
123+
**Data Loading:**
124+
- Fetches events via `splitClient.getDisputeEvents(invoiceId)`
125+
- Shows loading skeleton while fetching
126+
- Graceful error handling with empty state fallback
127+
- Sorts events by timestamp on client side
128+
129+
## Technical Implementation Details
130+
131+
### Type Extensions
132+
133+
The implementation extends the SDK `Invoice` type to include dispute data:
134+
135+
```typescript
136+
interface DisputeMetadata {
137+
reason: string;
138+
description: string;
139+
initiator: string;
140+
timestamp: number;
141+
arbitrators: string[];
142+
evidenceLinks: Array<{
143+
cid: string;
144+
timestamp: number;
145+
submitter: string;
146+
filename: string;
147+
}>;
148+
}
149+
150+
interface DisputeVoteTally {
151+
releaseVotes: number;
152+
refundVotes: number;
153+
votedArbitrators: string[];
154+
}
155+
156+
interface DisputeStatus extends DisputeMetadata, DisputeVoteTally {
157+
resolved: boolean;
158+
outcome?: "Release" | "Refund";
159+
}
160+
```
161+
162+
### SDK Method Assumptions
163+
164+
The implementation assumes the following Web3 SDK methods exist:
165+
166+
```typescript
167+
// Vote on a dispute
168+
sdk.voteDispute({
169+
invoiceId: string,
170+
arbitrator: string,
171+
vote: 0 | 1 // 0 = Refund, 1 = Release
172+
}): Promise<{ txHash: string }>
173+
174+
// Submit evidence
175+
sdk.addDisputeEvidence({
176+
invoiceId: string,
177+
submitter: string,
178+
evidenceCid: string,
179+
filename: string
180+
}): Promise<{ txHash: string }>
181+
182+
// Fetch dispute events
183+
sdk.getDisputeEvents(invoiceId: string): Promise<DisputeEvent[]>
184+
```
185+
186+
### IPFS Configuration
187+
188+
**Environment Variables:**
189+
- `NEXT_PUBLIC_IPFS_GATEWAY` - IPFS API endpoint (default: `https://api.web3.storage`)
190+
- `NEXT_PUBLIC_IPFS_API_KEY` - Authentication token for IPFS service
191+
- `NODE_ENV` - Determines whether to use mock uploads in development
192+
193+
**Mock Mode:**
194+
- Automatically activated when `NODE_ENV === "development"` or no API key configured
195+
- Generates deterministic CIDs based on file hash
196+
- Useful for local development and testing without IPFS infrastructure
197+
198+
## Testing
199+
200+
### Test Coverage
201+
202+
1. **DisputePanel Tests (`DisputePanel.test.tsx`)**
203+
- ✅ Conditional rendering based on dispute status
204+
- ✅ Dispute metadata display (reason, initiator, dates, arbitrators)
205+
- ✅ Vote tally visualization and calculations
206+
- ✅ Evidence upload flow with file validation
207+
- ✅ IPFS CID submission callback
208+
- ✅ Voting authorization checks
209+
- ✅ Vote submission with correct parameters
210+
- ✅ Real-time refresh callbacks
211+
- ✅ Resolved dispute display
212+
- ✅ Timeline chronology validation
213+
214+
2. **DisputeTimeline Tests (`DisputeTimeline.test.tsx`)**
215+
- ✅ Event chronological ordering
216+
- ✅ Event type rendering (DisputeOpened, EvidenceSubmitted, VoteCast, DisputeResolved)
217+
- ✅ Actor address display and truncation
218+
- ✅ Timestamp formatting
219+
- ✅ IPFS evidence links
220+
- ✅ Vote type badges
221+
- ✅ Loading states
222+
- ✅ Empty states
223+
- ✅ Error handling
224+
- ✅ Visual indicators (icons, colors)
225+
226+
3. **IPFS Tests (`ipfs.test.ts`)**
227+
- ✅ File upload to IPFS gateway
228+
- ✅ File size validation (10MB limit)
229+
- ✅ File type validation
230+
- ✅ CID extraction from response
231+
- ✅ API key authentication
232+
- ✅ Error handling (network, API errors)
233+
- ✅ CID format validation (v0 and v1)
234+
- ✅ Mock IPFS deterministic CID generation
235+
- ✅ Gateway URL generation
236+
237+
### Running Tests
238+
239+
```bash
240+
# Run all tests
241+
npm run test
242+
243+
# Run tests in watch mode
244+
npm run test:watch
245+
246+
# Run with coverage
247+
npm run test -- --coverage
248+
249+
# Run specific test file
250+
npm run test DisputePanel.test.tsx
251+
```
252+
253+
## Usage Example
254+
255+
### For Invoice Creators/Payers
256+
257+
When viewing a disputed invoice:
258+
1. Navigate to `/invoice/[id]`
259+
2. See the dispute panel with reason and vote tally
260+
3. Click "Submit Evidence" to upload supporting documents
261+
4. View all submitted evidence with IPFS links
262+
5. Monitor vote progress in real-time
263+
264+
### For Arbitrators
265+
266+
When assigned to a dispute:
267+
1. Navigate to disputed invoice
268+
2. Review dispute reason and evidence
269+
3. See "Arbitrator Actions" section
270+
4. Click "Approve Release" or "Reject / Refund"
271+
5. Confirm transaction in wallet
272+
6. See confirmation that vote was recorded
273+
274+
## Integration Checklist
275+
276+
✅ DisputePanel component created with full features
277+
✅ DisputeTimeline updated with event display
278+
✅ IPFS upload utilities implemented
279+
✅ Evidence upload modal with validation
280+
✅ Arbitrator voting controls
281+
✅ Real-time state updates via useInvoiceStream
282+
✅ Comprehensive test suites written
283+
✅ TypeScript types defined
284+
✅ Accessibility attributes (ARIA labels, roles)
285+
✅ Responsive design (mobile-friendly)
286+
✅ Error handling and loading states
287+
✅ Toast notifications for user feedback
288+
✅ Integration with invoice detail page
289+
290+
## Verification Steps
291+
292+
### 1. Linting
293+
```bash
294+
npm run lint
295+
```
296+
Expected: Zero warnings/errors
297+
298+
### 2. TypeScript Compilation
299+
```bash
300+
npx tsc --noEmit
301+
```
302+
Expected: No type errors
303+
304+
### 3. Build
305+
```bash
306+
npm run build
307+
```
308+
Expected: Successful production build
309+
310+
### 4. Manual Testing
311+
312+
**Test Scenario 1: Dispute Panel Display**
313+
- Create an invoice with status "Disputed"
314+
- Navigate to invoice detail page
315+
- Verify dispute panel renders with all metadata
316+
- Verify vote tally displays correctly
317+
318+
**Test Scenario 2: Evidence Upload**
319+
- Click "Submit Evidence" button
320+
- Select a PDF file < 10MB
321+
- Submit and verify IPFS upload
322+
- Check evidence appears in list with View link
323+
324+
**Test Scenario 3: Arbitrator Voting**
325+
- Connect wallet as assigned arbitrator
326+
- Verify voting controls are visible
327+
- Click "Approve Release" or "Reject / Refund"
328+
- Confirm transaction and verify vote recorded
329+
- Check arbitrator shown as "Voted"
330+
331+
**Test Scenario 4: Real-Time Updates**
332+
- Open dispute in two browser windows
333+
- Vote in one window
334+
- Verify vote tally updates in other window within 3 seconds
335+
336+
## Future Enhancements
337+
338+
1. **Evidence Comments** - Allow annotating evidence submissions
339+
2. **Vote Reasoning** - Require arbitrators to provide vote rationale
340+
3. **Evidence Categories** - Tag evidence by type (contract, communication, etc.)
341+
4. **Arbitrator Chat** - Private discussion channel for arbitrators
342+
5. **Dispute Appeal** - Allow parties to appeal resolved disputes
343+
6. **Multi-round Voting** - Support escalation to larger arbitrator panels
344+
7. **Evidence Verification** - Cryptographic signatures for evidence authenticity
345+
8. **Notification System** - Email/push alerts for new evidence and votes
346+
347+
## Dependencies
348+
349+
- `@stellar-split/sdk` - Web3 SDK for Stellar smart contract interactions
350+
- `@stellar/stellar-sdk` - Stellar blockchain utilities
351+
- `next` 14.2.3 - React framework
352+
- `react` 18.3.0 - UI library
353+
- `lucide-react` - Icon library
354+
- `vitest` - Testing framework
355+
- `@testing-library/react` - React testing utilities
356+
357+
## Environment Setup
358+
359+
Required environment variables:
360+
361+
```env
362+
NEXT_PUBLIC_CONTRACT_ID=<stellar_contract_id>
363+
NEXT_PUBLIC_RPC_URL=https://soroban-testnet.stellar.org
364+
NEXT_PUBLIC_IPFS_GATEWAY=https://api.web3.storage
365+
NEXT_PUBLIC_IPFS_API_KEY=<your_web3_storage_api_key>
366+
```
367+
368+
## Troubleshooting
369+
370+
### IPFS Upload Fails
371+
- Verify `NEXT_PUBLIC_IPFS_API_KEY` is set correctly
372+
- Check IPFS gateway URL is accessible
373+
- In development, mock mode will activate automatically
374+
375+
### Votes Not Appearing
376+
- Ensure wallet is connected
377+
- Verify arbitrator address matches assigned list
378+
- Check transaction was confirmed on blockchain
379+
- Wait up to 3 seconds for streaming update
380+
381+
### Evidence Links Not Working
382+
- Verify CID format is valid (QmXXX... or bXXX...)
383+
- Try alternative gateway: `https://gateway.pinata.cloud/ipfs/{cid}`
384+
- Check IPFS file was properly pinned
385+
386+
## License
387+
388+
This implementation follows the project's existing license.

0 commit comments

Comments
 (0)