Skip to content

feat(disputes): dispute resolution with member voting and admin resolution - #250

Open
jajafwangshak86-ops wants to merge 1 commit into
JointSave-org:mainfrom
jajafwangshak86-ops:feat/dispute-resolution
Open

feat(disputes): dispute resolution with member voting and admin resolution#250
jajafwangshak86-ops wants to merge 1 commit into
JointSave-org:mainfrom
jajafwangshak86-ops:feat/dispute-resolution

Conversation

@jajafwangshak86-ops

Copy link
Copy Markdown

Closes #208

Summary

Adds a complete dispute resolution flow for pools: members can file disputes (against another member or an admin action), the pool votes to uphold or dismiss, and the admin can force-resolve with a written note.

Database (20260824010000_dispute_resolution.sql)

  • disputes: filer, optional target member, type (missed_deposit / unfair_penalty / admin_abuse / member_misconduct / other), description (≤2000 chars), up to 3 evidence URLs, status lifecycle open → voting → resolved_upheld / resolved_dismissed / expired, vote counters, resolution note + resolver + timestamp
  • dispute_votes: one vote per wallet per dispute (composite PK)
  • Public-read RLS (writes only via service-role API routes) + realtime publication, matching the repo's existing lockdown style

API

Endpoint Behavior
POST /api/disputes Members file disputes; one active dispute per filer per pool; 72h expiry; evidence URLs validated
GET /api/disputes?pool_id= Pool feed, newest first, optional status filter
POST /api/disputes/[id]/vote Member-only; filer/target excluded; double votes rejected by PK; auto-resolves when one side reaches ⌈members/2⌉
POST /api/disputes/[id]/resolve Pool admin closes any active dispute with required resolution note (tie-breaker / urgent cases)
POST /api/disputes/expire CRON_SECRET-gared job that flips stale disputes to expired — registered in vercel.json

All state changes are mirrored into pool_activity (dispute_filed, dispute_resolved).

Frontend

  • DisputesPanel rendered full-width on the group page: status badges, live voting countdown, vote counter showing threshold, resolution notes
  • FileDisputeDialog: type select, optional target member picker, live character counter, repeatable evidence URL inputs
  • useDisputes: Supabase Realtime subscription keeps the feed live without refresh; optimistic vote updates reconcile against server truth
  • lib/disputes.ts pure helpers covered by 8 node unit tests (registered in test:unit); full en/es translations

Verification (all green locally)

  • pnpm lint, pnpm format:check, test:unit (210 pass incl. new disputes tests), test:components (101), pnpm build, bundle budget check
  • No new TypeScript errors from the disputes files (tsc --noEmit)

Implements issue JointSave-org#208.

Database:
- disputes table (type, target, evidence URLs, status lifecycle
  open -> voting -> resolved_upheld/resolved_dismissed/expired,
  resolution notes, vote counters) and dispute_votes table with one
  vote per wallet per dispute; RLS public-read + realtime publication.
- Disputes auto-expire 72h after filing.

API:
- POST/GET /api/disputes — members file disputes (one active per filer
  per pool, description <=2000 chars, up to 3 validated evidence links)
- POST /api/disputes/[id]/vote — member-only voting; filer and target
  excluded; auto-resolves when one side reaches half the pool (rounded
  up) and logs to pool_activity
- POST /api/disputes/[id]/resolve — pool admin closes any active
  dispute with a required written resolution note
- POST /api/disputes/expire — CRON_SECRET-gared expiry job registered
  in vercel.json

Frontend:
- useDisputes hook: feed loading + Supabase Realtime subscription +
  optimistic voting that reconciles with server truth
- DisputesPanel / DisputeCard / FileDisputeDialog components wired into
  the group page below the main grid; status badges, live countdown,
  quorum-style vote counter, admin resolve dialog
- lib/disputes.ts pure helpers with 8 node unit tests; en/es i18n
@Sendi0011
Sendi0011 self-requested a review August 25, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Build a dispute resolution system for pool members to report and resolve conflicts (missed deposits, unfair penalties)

1 participant