|
| 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