Skip to content

Improvements/p2p ramp - #715

Merged
joemarct merged 41 commits into
masterfrom
improvements/p2p-ramp
Jun 16, 2026
Merged

joemarct merged 41 commits into
masterfrom
improvements/p2p-ramp

Conversation

@joemarct

Copy link
Copy Markdown
Member

Summary

This PR consolidates multiple fixes and enhancements across the P2P Ramp module to improve stability, UI consistency, and user experience.

Key Changes

Performance & Data Fetching

  • Eliminated duplicate API calls on the P2P order page, arbiter appeal page, and chat members fetch by debouncing WS handlers, serializing fetches, and using stable route keys to prevent remount cascades
  • Prevented concurrent loadData calls by guarding against parallel invocations and passing contractBalance directly to EscrowTransfer to avoid redundant balance lookups
  • Optimized PaymentConfirmation to accept pre-loaded order/contract data, avoiding an extra fetchOrderDetail call when data is already available

New Components

  • Extracted PeerInfo component (src/components/ramp/fiat/PeerInfo.vue) for reusable peer identity display across the P2P flow
  • Added ReportDialog with category selection and description support for reporting users
  • Added FeedbackForm for post-trade feedback with pre-composed chips

UI/UX Improvements

  • Trade stats display: Shows completed/failed counts and completion percentage on ad listings and trade info cards
  • Reported user handling: Greyscales/blurs content for reported users, keeps Reported badge visible in red
  • Appeal form fixes: Restricts reason selection to single choice, shows correct appeal statement based on filer identity
  • Casing consistency: Normalized header titles, confirmation labels, and button text to sentence case across the module
  • DragSlide overlay: Teleported DragSlide to body so it properly overlays above scroll containers in the appeal form
  • Centered PeerInfo content in UserProfileDialog for better visual alignment

i18n

  • Added translation entries for all new UI strings across 15+ locales (af, ar, de, en-us, es, es-ar, fr, ha, id, it, ja, ko, nl, pt, pt-br, ru, tl, zh-cn, zh-tw)

Bug Fixes

  • Restored ad.owner fallback in TradeInfoCard counterparty computed property
  • Fixed peer trade stats not rendering on store form page by awaiting the fetch call
  • Fixed NaN guard in formatCompletionRate
  • Fixed pre-existing syntax error in UserProfileDialog

Files Changed

48 files changed, 1459 insertions(+), 501 deletions(-)

Testing

  • Verify order page loads without duplicate network requests
  • Confirm trade stats display correctly on ad listings and trade cards
  • Test report user flow with category selection
  • Verify appeal form shows correct statements per filer role
  • Check all new i18n strings render in each supported locale

joemarct added 30 commits June 13, 2026 15:49
- Add TradesCompleted and TradesFailed i18n keys to all languages
- Display completed trades (green) and failed trades (red) alongside trade count and completion percentage
- Fix ad owner stat paths in profile.vue (ad.owner.* instead of ad.*)
- 'Escrow bch' → 'Escrow BCH'
- 'verifying ESCROW' → 'Verifying Escrow'
- 'verifying RELEASE' → 'Verifying Release'
…k chips

- Fix 'Rate your Experience' -> 'Rate Your Experience'
- Add 10 clickable chips with pre-composed feedback options
- Green chips for positive, orange for slightly negative
- Chips populate the comment field when clicked
Switch from long-form to compact display:
  3 completed · 1 failed · 75% completion

Drop the 'X trades' prefix and parenthetical notation
in favor of colored labels separated by middots
- Report button in profile page and user profile dialog
- Visible only when viewing another user (not self)
- Confirm dialog before submitting report
- Success/error notifications with i18n messages
- 6 new i18n keys for report flow
- Add isReported() helper checking reported_at within 24h
- Apply .reported-blur CSS (filter: blur, opacity, no-select)
- Blur rating, completed/failed counts, and completion percentage
- Applied in StoreListings, profile page, and UserProfileDialog
Use filter: grayscale(1) instead of blur(4px) for reported users.
Stars turn grey, text fades — properly 'greyed out' look.
Backend:
- Add REASON_CHOICES and reason CharField to Report model
- Validate and save reason from request.data in report view
- Add migration 0239 for reason field

Frontend:
- Create ReportDialog component with radio options and caption descriptions
- Replace confirm dialog with reason selection in profile.vue and UserProfileDialog.vue
- Send reason in POST body to report endpoint
- Add REPORTED badge next to username in StoreListings, profile, and UserProfileDialog
- Add i18n keys for report reasons across all 19 language files
- Add completed/failed trades and completion rate display
- Add REPORTED badge after name and online dot indicator
- Add greyed-out styling for reported users
- Reposition REPORTED badge in profile.vue after online indicator
- Change 'BUYING FROM'/'SELLING TO' to 'Buying From'/'Selling To' in TradeInfoCard and OrderListings
- Change 'BUY BCH WITH'/'SELL BCH FOR' to 'Buy BCH With'/'Sell BCH For' in order-form
- Change 'REPORTED' badge to 'Reported' in StoreListings, profile, UserProfileDialog, TradeInfoCard
- Change 'PAYMENT METHODS' to 'Payment Methods' in StandByDisplay
- Change 'CONFIRM AD DETAILS' fallback to 'Confirm Ad Details' in DisplayConfirmation
- Change 'Swipe To Escrow' to 'Swipe to Escrow' in EscrowTransfer
- Fix ALL-CAPS translation keys: ('BUYER') to ('Buyer'), ('SELLER') to ('Seller'), ('REVIEWS') to ('Reviews'), ('ADS') to ('Ads'), ('MIN') to ('Min'), ('MAX') to ('Max'), ('BUY') to ('Buy'), ('SELL') to ('Sell'), ('PAYMENTMETHODS') to ('PaymentMethods'), ('AMOUNT') to ('Amount')
- Fix hardcoded 'ADS' button to use ('Ads') in UserProfileDialog
- Pass marketPrice from order-form to TradeInfoCard
- Calculate percentage difference between ad price and market price
- Display green (+) or red (-) percentage indicator next to price
- Add marketPriceLoading state in order-form.vue
- Pass marketPriceLoading prop to TradeInfoCard
- Show q-skeleton while market price is loading
- Hide percentage until market price is available
- Add q-tooltip on price percentage span explaining it's the percentage difference from market price
- Add PriceVsMarketTooltip i18n key to all 19 language files and phrases.js
- PaymentConfirmation: use order data from parent prop instead of re-fetching
  on mount; extracted processPaymentMethods() for reuse
- Store contractBalance in order.vue and pass to child components to avoid
  redundant /balance/bch/ calls in PaymentConfirmation, StandByDisplay,
  and VerifyTransaction
- Cache chat members response in fetchChatUnread() and reuse in
  loadChatData() to eliminate duplicate /chat/members/full_info/ call
- Skip generateContract() in loadData() when escrowContract already exists
  (avoids duplicate /contract/fees/ and /contract/ calls for ESCRW_PN status)
- Move escrowContract guard before API calls in generateContract() to
  prevent redundant fetches when contract already exists
- Debounce WebSocket fetchOrder() calls to prevent rapid-fire duplicates
…crowTransfer

- Add loadingData guard in loadData() to prevent second call if
  refreshPage/relogged fires during initial page load
- Guard refreshPage() with loadingData to skip redundant loads
- Pass contractBalance in escrowTransferData and use it in
  EscrowTransfer to avoid redundant /balance/bch/ call
Using $route.params.order instead of $route.path prevents
remounting when URL is normalized (e.g. trailing slash changes),
which would cause duplicate API calls via mounted() -> loadData()
…inate duplicates

- Convert fetchOrder() from .then() to async/await so handleNewStatus()
  is properly awaited (prevents race condition where loadData() continues
  before updateStateByStatus() sets contractBalance and state)
- Move fetchChatUnread() from fetchOrder() to loadData() where it's
  awaited BEFORE activeTab potentially switches to 'chat' tab, ensuring
  chatMembersRaw is cached before loadChatData() runs
…scade

Changed router-view keys from $route.path to $route.name in:
- exchange/index.vue (top level)
- peer_to_peer/index.vue (middle level)
- orders.vue already uses $route.params.order (leaf level)

This prevents URL path normalization (e.g. trailing slash changes) from
destroying and remounting the entire component subtree, which was causing
duplicate API calls via mounted() -> loadData().
- Add dedup for concurrent getBalance calls in RampContract
- Add cooldown to prevent websocket-triggered duplicate fetchOrder
- Skip updateStateByStatus when status value hasn't changed
- Label fetchOrder callers for better traceability
…t NaN

- Fetch counterparty trade stats (completed_trades, failed_trades,
  completion_rate, reported_at) from peer API in order page
- Guard formatCompletionRate with fallback to 0 in all locations
…P2P order page

The trade stats (completed trades, failed trades, completion percentage) were
showing as 'completed · failed · 0% completion' instead of actual values like
'2 completed · 4 failed · 33% completion'.

Root causes:
1. Vue I18n's () treats undefined interpolation params as empty strings,
   so {count: undefined} renders the {count} placeholder as blank
2. The old fetchCounterpartyTradeStats used Vue 2's this.() to mutate
   ad/owner objects directly, but the TradeInfoCard component reads counterparty
   data from order.members - a different object entirely
3. The third argument to () was being ignored (Vue I18n doesn't support it)

Fixes:
- Use ?? 0 fallback for completed_trades, failed_trades, and completion_rate
  in all () calls so undefined values show '0' instead of blank
- Replace this.() approach with passing counterpartyPeerData as a prop
  to TradeInfoCard, which merges it with the order member data
- Derive counterparty from order.members (buyer/seller) instead of
  order.owner/ad.owner for consistency with other P2P components
- Remove unused third argument from all () calls

Affected components:
- TradeInfoCard.vue (order page counterparty display)
- UserProfileDialog.vue (peer profile dialog)
- profile.vue (peer profile page - user stats & ad owner stats)
- StoreListings.vue (marketplace listing owner stats)
- AdListings.vue (ad management listing stats)
- order.vue (fetchCounterpartyTradeStats refactor)
Create a PeerInfo component that encapsulates peer display logic:
- Name (clickable or plain text, with truncation option)
- Online status dot (conditional)
- Rating stars with numeric value
- Trade stats (completed · failed · completion%)
- Last online timestamp
- Reported badge (within 24h) with reported-greyed styling

Refactored components to use PeerInfo:
- TradeInfoCard: counterparty, orderOwner, adOwner sections
- UserProfileDialog: user stats section
- StoreListings: listing owner info

PeerInfo props allow fine-grained control:
- showName, showOnlineStatus, showRating, showTradeStats
- showLastOnline, showReportedBadge, clickableName
- truncateName, largeName, alignRight

Kept profile.vue and AdListings.vue as-is since their layouts
are too different from the standard peer info pattern.
…tainer

The reported-greyed class applies grayscale+opacity which made the
red Reported badge appear greyed out. Moved the badge outside the
name-row div so it stays visually red.
When type='ad' (store form page), order.members is null so counterparty
was returning null, causing PeerInfo to show nothing. Fall back to
ad.owner when order members aren't available, matching original behavior.
On the store form page (type='ad'), order.members is null so
counterparty falls back to ad.owner which lacks trade stats.
Added fetchAdOwnerPeerData() to fetch peer data from /peer/:id/
and pass it as counterpartyPeerData prop to TradeInfoCard.
Also updated counterparty computed to merge counterpartyPeerData
with ad.owner when order members are unavailable.
The async call was fired without await, causing isloaded=true before
the peer data arrived. The component rendered with ad.owner (which lacks
trade stats) and never updated. Awaiting ensures peer data is available
before the component renders.
…eDialog syntax error

- order-form.vue, order.vue: use spread operator on peer API response
  to ensure reactive tracking of completed_trades/failed_trades fields
- UserProfileDialog.vue: remove duplicate '},' that prematurely closed
  methods object, causing vue-loader compile failure
- Add centered prop to PeerInfo that applies text-center on root,
  justify-center on name-row and rating row
- Pass centered prop in UserProfileDialog to fix broken layout
joemarct added 5 commits June 14, 2026 15:36
- Change selectedReasons array to selectedReason single value
- Remove updateAppealReasons toggle method
- Submit reasons as single-element array for API compatibility
- Replace hardcoded 'You have appealed' with dynamic appealSubject
- Determine appellant via appeal.owner.id vs current user
- Omit recipient phrase when appellant is the natural beneficiary
  (buyer appealing release, seller appealing refund)
- Capitalize appellant label: 'The buyer', 'The seller'
…footer menu

- Remove AppealFooterMenu from arbiter shell (index.vue)
- Remove footer-related data, events, and methods
- Add profile icon to HeaderNav top-right-menu slot on appeals.vue and appeal.vue
- Add _loadingData guard on loadData() to prevent concurrent calls
- Add _fetchAppealInProgress guard on fetchAppeal()
- Add 10s websocket cooldown for fetchAppeal()
- Label fetchAppeal callers for traceability
- Wrap RampDragSlide in <Teleport to="body"> to escape the scroll container
- Reduce scroll container bottom padding from 180px to 20px (no longer needed)
@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: improvements/p2p-ramp

Overall Assessment: Approve with minor suggestions

This is a well-structured PR that delivers substantial improvements to the P2P Ramp module. The performance optimizations, new reusable components, and UI/UX enhancements are solid. The i18n coverage is comprehensive across all 19 locales. Below are detailed findings.


✅ Strengths

1. Performance & Data Fetching Improvements

  • order.vue: Excellent use of wsFetchingOrder and _fetchOrderInProgress flags to prevent concurrent fetchOrder calls. The 10-second websocket cooldown is a good balance between responsiveness and avoiding API spam.
  • contract.js: The getBalance deduplication via _getBalancePromise and _getBalanceKey is a clean, robust pattern for serializing concurrent balance lookups.
  • PaymentConfirmation.vue: Properly accepts pre-loaded contractBalance and order data, avoiding redundant fetches when parent already has the data.

2. New Components

  • PeerInfo.vue: Well-designed reusable component with clear props API. The reported-greyed CSS class correctly applies grayscale, opacity, and pointer-events: none to prevent interaction with reported users.
  • ReportDialog.vue: Clean dialog with proper q-radio binding and emits the selected reason. Good use of v-model + update:modelValue pattern.

3. i18n Consistency

  • All new UI strings (ReportUser, TradesCompleted, TradesFailed, CompletionPercentage, ReportReason*, FastTransaction, etc.) are present in all 19 locale files.
  • AppealForm.vue dynamically selects the correct translation key (AppealFormReasonOpt1Seller vs AppealFormReasonOpt1Buyer) based on filer identity — this is the correct approach for the stated requirement.

4. Bug Fixes

  • TradeInfoCard.vue: The ad.owner fallback in counterparty computed property is correctly restored.
  • formatCompletionRate in PeerInfo.vue uses Math.floor(value || 0).toString() which properly guards against NaN and undefined.
  • AppealForm.vue: selectedReason is now a single value (not an array), correctly enforcing single-choice reason selection.

⚠️ Issues Found

High Priority

1. UserProfileDialog.vue — Undefined this.userId (Line 337)

// In fetchAds() method:
params.to_peer = this.userId  // ❌ this.userId does not exist in component

This should be this.user?.id or removed entirely. The to_peer parameter is not valid for the /ramp-p2p/ad/ endpoint anyway (it uses owner_id), so this line appears to be a copy-paste error from fetchReviews(). This will cause a runtime error or incorrect parameter.

2. PaymentConfirmation.vue — showErrorDialog never sets errorDialogActive = true (Line 1143)

showErrorDialog (error) {
  if (!this.errorDialogActive) {
    this.errorDialogActive = false  // ❌ Should be `true`
    this.$q.notify({ ... })
  }
}

Because errorDialogActive is never set to true, the guard if (!this.errorDialogActive) is always true, allowing multiple error notifications to stack. This is a pre-existing bug but was not fixed in this PR.

Medium Priority

3. Debug logs in production code
Multiple console.log, console.trace, and commented-out debug statements remain in:

  • order.vue (lines 786, 788, 794, 973, 979, 1014, 2002, 2006)
  • contract.js (line 108: console.trace)
  • PaymentConfirmation.vue (several console.log statements)

These should be removed or wrapped in a debug flag before production release.

4. PaymentConfirmation.vue — Inconsistent fiatAmount data access
The fiatAmount computed uses this.data.order?.ad?.fiat_currency?.symbol, but the parent (order.vue) passes ad as a separate key in paymentConfirmationData. If the order object does not include the ad snapshot, this will produce undefined. Consider using this.data?.ad?.fiat_currency?.symbol for consistency with the parent data structure.

5. AdListings.vue — Inconsistent completion rate formatting

// AdListings.vue line 114:
Number(listing.completion_rate?.toFixed(2) ?? 0)

This returns a Number (e.g., 95), while PeerInfo.vue uses Math.floor(value || 0).toString() which returns a String. The CompletionPercentage i18n string uses {percentage} interpolation, so both work, but the inconsistency is worth noting. PeerInfo.vue also truncates with Math.floor while AdListings.vue rounds to 2 decimals.

Low Priority

6. i18n translations still in English
Many non-English locales (e.g., ja, fr, pt-br) have English fallback values for the new keys like ReportReasonInactiveDesc, CouldCommunicateBetter, etc. This is acceptable as a stopgap but should be flagged for follow-up translation work.

7. Promise chain pattern in AppealForm.vue

.then(vm.addArbiterToChat())

addArbiterToChat() does not return its inner promise, so the .then() chain does not wait for it. This is a pre-existing pattern, not introduced by this PR.


📋 Recommendations

  1. Fix this.userId in UserProfileDialog.vue before merging — this is a definite runtime bug.
  2. Fix errorDialogActive = true in PaymentConfirmation.vue — this affects user experience when errors occur.
  3. Clean up debug logs in order.vue, contract.js, and PaymentConfirmation.vue.
  4. Consider standardizing completion_rate formatting between AdListings.vue and PeerInfo.vue (either both use Math.floor or both use Number with fixed decimals).
  5. Verify the fiatAmount currency symbol source in PaymentConfirmation.vue to ensure it matches the parent data structure.

Files with the most impact

File Assessment
src/pages/exchange/peer_to_peer/order.vue ✅ Major improvements (WS debouncing, chat, skeletons)
src/components/ramp/fiat/PaymentConfirmation.vue ✅ Good optimization, minor bug (errorDialogActive)
src/components/ramp/fiat/PeerInfo.vue ✅ Clean new component
src/components/ramp/fiat/ReportDialog.vue ✅ Clean new component
src/components/ramp/fiat/TradeInfoCard.vue ✅ Good bug fix (ad.owner fallback)
src/components/ramp/fiat/dialogs/UserProfileDialog.vue ⚠️ Contains this.userId bug
src/exchange/contract.js ✅ Good balance deduplication

Verdict: The PR is ready to merge after fixing the two high-priority issues (this.userId and errorDialogActive). The performance improvements and new components are high-quality additions to the codebase.

New%20session%20-%202026-06-15T01%3A21%3A28.761Z
opencode session  |  github run

- Remove invalid to_peer param in UserProfileDialog.fetchAds()
- Fix errorDialogActive never set to true in PaymentConfirmation
- Remove debug console.log/trace statements from production code
- Add fallback for fiatAmount symbol in PaymentConfirmation
- Align completion rate formatting between AdListings and PeerInfo
- Remove debug console.log from AdListings and contract.js
@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: improvements/p2p-ramp

Overall Assessment: Approve

This is a solid, well-structured PR that delivers meaningful improvements to the P2P Ramp module. The performance optimizations, new reusable components, and UI/UX enhancements are all high-quality. I verified that all high-priority issues from the previous review were promptly fixed in commit 469d0a9.


✅ Verified Fixes (from previous review)

1. UserProfileDialog.vue — fetchAds() parameter fixed

  • The invalid params.to_peer = this.userId line was removed. The method now correctly uses owner_id: vm.user.id for the /ramp-p2p/ad/ endpoint.

2. PaymentConfirmation.vue — errorDialogActive bug fixed

  • showErrorDialog() now correctly sets this.errorDialogActive = true before showing the notification, preventing stacked error dialogs.

3. PaymentConfirmation.vue — fiatAmount symbol fallback added

  • The computed now falls back from this.data.order?.ad?.fiat_currency?.symbol to this.data?.ad?.fiat_currency?.symbol, matching the parent data structure.

4. AdListings.vue — Completion rate formatting aligned

  • Now uses formatCompletionRate(listing.completion_rate) with Math.floor(value || 0), consistent with PeerInfo.vue.

5. Debug logs cleaned from flagged files

  • order.vue: Removed console.trace from mounted(), console.log from beforeUnmount(), and console.log from getBackNavigationPath
  • contract.js: Removed console.log and console.trace from getBalance() deduplication logic
  • PaymentConfirmation.vue: Removed console.log from releaseBch() success handler
  • AdListings.vue: Removed console.log from getFiatCurrencies() and checkAdLimit()

✅ Strengths

1. Performance & Data Fetching

  • order.vue: The wsFetchingOrder and _fetchOrderInProgress flags, plus the 10-second websocket cooldown, effectively prevent duplicate API calls.
  • contract.js: The _getBalancePromise / _getBalanceKey deduplication pattern is a clean, robust way to serialize concurrent balance lookups.
  • PaymentConfirmation.vue: Properly accepts pre-loaded contractBalance and order data, avoiding redundant fetchOrderDetail calls.

2. New Components

  • PeerInfo.vue: Clean, reusable component with well-designed props. The reported-greyed CSS class correctly applies grayscale, opacity, and pointer-events: none.
  • ReportDialog.vue: Proper q-radio binding with clear category selection. Good use of v-model + update:modelValue pattern.
  • FeedbackForm.vue: Well-implemented with pre-composed feedback chips.

3. UI/UX Improvements

  • Trade stats display on ad listings and trade cards is a valuable addition.
  • Reported user handling (greyscale/blur) correctly preserves the red "Reported" badge visibility.
  • Appeal form correctly restricts reason selection to single choice and shows the correct statement based on filer identity.

4. i18n

  • All new UI strings (ReportUser, TradesCompleted, TradesFailed, CompletionPercentage, ReportReason*, FastTransaction, etc.) are present in all 19 locale files.
  • The AppealForm.vue dynamically selects the correct translation key (AppealFormReasonOpt1Seller vs AppealFormReasonOpt1Buyer) based on filer identity.

⚠️ Minor Remaining Issues

1. Debug logs still present in some modified files
These should be removed before merge:

  • src/components/ramp/fiat/TradeInfoCard.vue:198

    console.log('[TradeInfoCard counterparty] ad.owner:', owner, 'peerData:', this.counterpartyPeerData, 'result:', result)
  • src/components/ramp/fiat/StandByDisplay.vue:594

    console.log('appeal:', vm.appeal)
  • src/components/ramp/fiat/dialogs/FeedbackForm.vue:217, 316, 352, 382

    console.log(error.response)
    console.log(response.data)
    console.log(response.data)
    console.log()  // empty log

2. AppealForm.vue — .then() chain does not wait for addArbiterToChat

.then(vm.addArbiterToChat())

addArbiterToChat() does not return its inner promise, so the .then() chain does not properly wait for it. This is a pre-existing pattern, not introduced by this PR, but worth noting.


📋 Recommendations

  1. Remove the remaining 6 debug console.log statements listed above before merging.
  2. Consider fixing the AppealForm.vue promise chain by returning the promise from addArbiterToChat():
    addArbiterToChat () {
      const vm = this
      // ... existing code ...
      return vm.fetchOrderMembers(vm.order.id)
        .then(members => {
          // ...
          return updateChatMembers(chatRef, arbiterMembers)
        })
    }

Files with the most impact

File Assessment
src/pages/exchange/peer_to_peer/order.vue ✅ Major improvements (WS debouncing, chat pagination, skeleton loaders)
src/components/ramp/fiat/PaymentConfirmation.vue ✅ Good optimization, previous bug fixed
src/components/ramp/fiat/PeerInfo.vue ✅ Clean new component
src/components/ramp/fiat/ReportDialog.vue ✅ Clean new component
src/components/ramp/fiat/TradeInfoCard.vue ✅ Good bug fix (ad.owner fallback), minor debug log remaining
src/components/ramp/fiat/dialogs/UserProfileDialog.vue ✅ Previous bug fixed
src/exchange/contract.js ✅ Good balance deduplication

Verdict: The PR is ready to merge after removing the 6 remaining debug console.log statements. The performance improvements and new components are excellent additions to the codebase. All high-priority issues from the previous review cycle have been properly resolved.

New%20session%20-%202026-06-15T01%3A27%3A15.098Z
opencode session  |  github run

@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: improvements/p2p-ramp

Overall Assessment: Approve

I have verified the current state of the PR after the previous review cycle and fix commits. All high-priority issues have been properly resolved, and the remaining debug logs flagged in the last review have been removed.


✅ Verified Fixes

1. UserProfileDialog.vue — Invalid parameter removed

  • fetchAds() now correctly uses owner_id: vm.user.id without the invalid params.to_peer = this.userId line.

2. PaymentConfirmation.vue — errorDialogActive bug fixed

  • showErrorDialog() now correctly sets this.errorDialogActive = true, preventing stacked error notifications.

3. PaymentConfirmation.vue — fiatAmount currency symbol fallback

  • The computed property now falls back from this.data.order?.ad?.fiat_currency?.symbol to this.data?.ad?.fiat_currency?.symbol, matching the parent data structure.

4. AdListings.vue — Completion rate formatting

  • Now uses formatCompletionRate(listing.completion_rate) instead of the previous Number(listing.completion_rate?.toFixed(2) ?? 0).

5. Debug logs cleaned

  • order.vue: Removed debug logs from mounted(), beforeUnmount(), and getBackNavigationPath
  • contract.js: Removed debug logs from getBalance() deduplication logic
  • PaymentConfirmation.vue: Removed debug logs from releaseBch() success handler
  • AdListings.vue: Removed debug logs from getFiatCurrencies() and checkAdLimit()
  • TradeInfoCard.vue: Removed debug log from counterparty computed property
  • StandByDisplay.vue: Removed debug log from fetchAppeal()
  • FeedbackForm.vue: Removed debug logs from error handlers and success callbacks

6. AppealForm.vue — Promise chain fixed

  • addArbiterToChat() now properly returns its promise chain
  • The caller now uses .then(() => vm.addArbiterToChat()) instead of .then(vm.addArbiterToChat()), ensuring the chain waits for completion

✅ Code Quality Verification

Performance & Data Fetching

  • order.vue: The _fetchOrderInProgress flag and _fetchOrderLastTime 10-second cooldown correctly prevent duplicate API calls and websocket spam.
  • contract.js: The _getBalancePromise / _getBalanceKey deduplication pattern is clean and robust.

New Components

  • PeerInfo.vue: Clean reusable component with proper props API, reported-greyed CSS class correctly applies grayscale, opacity, and pointer-events: none.
  • ReportDialog.vue: Proper q-radio binding with clear category selection and v-model + update:modelValue pattern.
  • FeedbackForm.vue: Well-implemented with pre-composed feedback chips.

i18n

  • All new UI strings are present across all 19 locale files.

⚠️ Minor Remaining Issues (Non-blocking)

1. formatCompletionRate inconsistency

  • AdListings.vue returns Math.floor(value || 0) (Number)
  • PeerInfo.vue and UserProfileDialog.vue return Math.floor(value || 0).toString() (String)

Functionally harmless for i18n interpolation, but worth standardizing in a follow-up.

2. Pre-existing debug logs in other modified files
Files like VerifyTransaction.vue, DisplayConfirmation.vue, and MiscDialogs.vue contain pre-existing console.log statements. These were not introduced by this PR and do not block merge.


📋 Final Verdict

This PR is ready to merge. All high-priority issues from the previous review cycle have been resolved. The performance improvements, new components, and UI/UX enhancements are solid additions to the P2P Ramp module.

New%20session%20-%202026-06-15T01%3A30%3A23.693Z
opencode session  |  github run

@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: improvements/p2p-ramp

Overall Assessment: Approve with minor cleanup needed

This PR delivers substantial improvements to the P2P Ramp module. The performance optimizations (WS deduplication, serialized balance fetches), new reusable components (PeerInfo, ReportDialog), and UI/UX enhancements are well-executed. All high-priority issues from previous review cycles have been properly resolved.


✅ Verified Fixes (from previous review cycles)

  1. UserProfileDialog.vue — Invalid params.to_peer = this.userId removed. fetchAds() now correctly uses owner_id: vm.user.id.
  2. PaymentConfirmation.vue — showErrorDialog() now correctly sets this.errorDialogActive = true, preventing stacked error notifications.
  3. PaymentConfirmation.vue — fiatAmount computed property now falls back from this.data.order?.ad?.fiat_currency?.symbol to this.data?.ad?.fiat_currency?.symbol.
  4. AdListings.vue — Now uses formatCompletionRate() with Math.floor(value || 0).toString(), consistent with PeerInfo.vue.
  5. AppealForm.vue — addArbiterToChat() now returns its promise chain; caller uses .then(() => vm.addArbiterToChat()).
  6. Debug logs removed from order.vue, contract.js, PaymentConfirmation.vue, AdListings.vue, TradeInfoCard.vue, StandByDisplay.vue, and FeedbackForm.vue.

⚠️ Remaining Issues (should be fixed before merge)

1. Debug logs added in this PR — 8 instances

These were added during development but should be removed before production release:

File Line Content
src/pages/apps/exchange/index.vue ~79 console.log('[exchange/index.vue] $route watcher:', ...)
src/pages/apps/exchange/index.vue ~88 console.log('[exchange/index.vue] continue watcher fired:', ...)
src/pages/apps/exchange/index.vue ~94 console.trace('[exchange/index.vue] mounted() called:', ...)
src/pages/apps/exchange/index.vue ~139 console.log('[exchange/index.vue] getUser success:', ...)
src/pages/apps/exchange/index.vue ~149 console.log('[exchange/index.vue] getUser error:', ...)
src/pages/apps/exchange/peer_to_peer/index.vue ~89 console.log('[p2p/index.vue] mounted(), route:', ...)
src/pages/apps/exchange/peer_to_peer/orders.vue ~26 console.log('[orders.vue] created(), route:', ...)
src/pages/apps/exchange/peer_to_peer/orders.vue ~59 console.trace('[orders.vue] mounted(), route:', ...)
src/pages/apps/exchange/peer_to_peer/orders.vue ~69 console.log('[orders.vue] beforeUnmount(), route:', ...)

2. profile.vue — Inconsistent completion rate formatting

Line 151 uses user.completion_rate.toFixed(1):

{{ $t('CompletionPercentage', { percentage: user.completion_rate ? user.completion_rate.toFixed(1) : '0' }) }}

But the same file defines formatCompletionRate() at line 587 (used at line 231) which returns Math.floor(value || 0).toString(). This produces inconsistent display (e.g., "95.3%" vs "95%").

Fix: Replace line 151 with:

{{ $t('CompletionPercentage', { percentage: formatCompletionRate(user.completion_rate) }) }}

✅ Strengths

  • Performance: _fetchOrderInProgress / _fetchOrderLastTime cooldowns in order.vue and _getBalancePromise deduplication in contract.js effectively prevent API spam.
  • New Components: PeerInfo.vue is a clean, reusable abstraction with proper props API and reported-greyed CSS. ReportDialog.vue has clean v-model binding.
  • i18n: All new strings (ReportUser, TradesCompleted, TradesFailed, CompletionPercentage, etc.) are present across all 19 locale files.
  • UI/UX: Trade stats display, reported user greyscale handling, appeal form single-choice enforcement, and sentence case normalization are all solid improvements.

📋 Recommendations

  1. Remove the 9 debug console.log / console.trace statements listed above.
  2. Fix the profile.vue completion rate inconsistency by using formatCompletionRate(user.completion_rate) instead of toFixed(1).
  3. Merge after the two cleanup items above.

Verdict: The PR is ready to merge after removing the debug logs and fixing the profile.vue inconsistency. The performance improvements and new components are excellent additions to the P2P Ramp module.

New%20session%20-%202026-06-15T01%3A34%3A06.479Z
opencode session  |  github run

joemarct and others added 2 commits June 15, 2026 10:24
Co-authored-by: joemarct <joemarct@users.noreply.github.com>
@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: improvements/p2p-ramp

Overall Assessment: Approve

I verified the current state of this PR after the previous review cycles and applied additional fixes for issues that were missed. This is a solid, well-structured PR delivering meaningful improvements to the P2P Ramp module.


Changes I Applied

  1. profile.vue — Removed invalid params.to_peer = this.userId

    • this.userId does not exist in the profile.vue component, causing undefined to be sent as a query parameter. This line also incorrectly used to_peer for the /ramp-p2p/ad/ endpoint (which uses owner_id). Removed.
  2. order-form.vue — Removed debug console.log from createGroupChat()

    • console.log('createGroupChat', orderId, members, createdAt) was added during development and should not be in production code.
  3. profile.vue & order-form.vue — Standardized error logging

    • Changed console.log to console.error in handleRequestError default cases for consistency with the rest of the codebase.

Verified Fixes (from previous review cycles)

  • ✅ UserProfileDialog.vue — Invalid params.to_peer = this.userId was removed
  • ✅ PaymentConfirmation.vue — errorDialogActive correctly set to true
  • ✅ PaymentConfirmation.vue — fiatAmount currency symbol fallback is correct
  • ✅ AdListings.vue — Uses formatCompletionRate() with Math.floor(value || 0).toString()
  • ✅ AppealForm.vue — addArbiterToChat() returns its promise chain; caller uses .then(() => vm.addArbiterToChat())
  • ✅ Debug logs removed from order.vue, contract.js, PaymentConfirmation.vue, AdListings.vue, TradeInfoCard.vue, StandByDisplay.vue, and FeedbackForm.vue

Code Quality Assessment

Performance & Data Fetching

  • order.vue: The _fetchOrderInProgress flag and _fetchOrderLastTime 10-second cooldown correctly prevent duplicate API calls and websocket spam.
  • contract.js: The _getBalancePromise / _getBalanceKey deduplication pattern is clean and robust.

New Components

  • PeerInfo.vue: Clean, reusable component with proper props API. The reported-greyed CSS class correctly applies grayscale, opacity, and pointer-events: none.
  • ReportDialog.vue: Proper q-radio binding with clear category selection and v-model + update:modelValue pattern.
  • FeedbackForm.vue: Well-implemented with pre-composed feedback chips.

i18n

  • All new UI strings (ReportUser, TradesCompleted, TradesFailed, CompletionPercentage, ReportReason*, FastTransaction, etc.) are present across all 19 locale files.
  • AppealForm.vue dynamically selects the correct translation key based on filer identity.

⚠️ Minor Non-blocking Notes

  1. EscrowTransfer.vue, AppealDetail.vue, and MiscDialogs.vue contain pre-existing console.log statements that were not introduced by this PR and do not block merge.
  2. formatCompletionRate returns String in some places and Number (then coerced to string) in others — functionally harmless but worth standardizing in a follow-up.

Final Verdict

This PR is ready to merge. All high-priority issues have been resolved. The performance improvements, new reusable components, and UI/UX enhancements are excellent additions to the P2P Ramp module.

New%20session%20-%202026-06-15T02%3A25%3A24.440Z
opencode session  |  github run

@opencode-agent

Copy link
Copy Markdown
Contributor

User opencode-agent[bot] does not have write permissions

github run

@github-actions

Copy link
Copy Markdown

APIError: Insufficient balance. Manage your billing here: https://opencode.ai/workspace/wrk_01KHR5A6MHE7QYY19ZJ7XHBY32/billing

opencode session  |  github run

@github-actions

Copy link
Copy Markdown

PR Review: improvements/p2p-ramp

Overall Assessment: Approve

This PR delivers substantial, well-executed improvements to the P2P Ramp module. After reading the full diff and verifying the current state of all flagged files, I can confirm that all high-priority issues from previous review cycles have been properly resolved. The performance optimizations, new reusable components, and UI/UX enhancements are solid additions to the codebase.


✅ Verified Fixes (from previous review cycles)

Issue File Status
Invalid params.to_peer = this.userId UserProfileDialog.vue ✅ Removed; fetchAds() now uses owner_id: vm.user.id
errorDialogActive never set to true PaymentConfirmation.vue ✅ Fixed; guard now works correctly
fiatAmount currency symbol fallback PaymentConfirmation.vue ✅ Falls back from data.order?.ad?.fiat_currency?.symbol to data?.ad?.fiat_currency?.symbol
Completion rate formatting inconsistency AdListings.vue, profile.vue ✅ All files now use Math.floor(value || 0).toString()
AppealForm.vue promise chain AppealForm.vue ✅ addArbiterToChat() returns its promise; caller uses .then(() => vm.addArbiterToChat())
Invalid params.to_peer = this.userId profile.vue ✅ Removed
Debug log in createGroupChat() order-form.vue ✅ Removed
Debug logs in order.vue, contract.js, PaymentConfirmation.vue, AdListings.vue, TradeInfoCard.vue, StandByDisplay.vue, FeedbackForm.vue Various ✅ Removed

✅ Strengths

1. Performance & Data Fetching

  • order.vue: The _fetchOrderInProgress flag and _fetchOrderLastTime 10-second cooldown correctly prevent duplicate API calls and websocket spam.
  • arbiter/appeal.vue: The _fetchAppealInProgress flag and _fetchAppealLastTime cooldown provide the same protection for the arbiter appeal page.
  • contract.js: The _getBalancePromise / _getBalanceKey deduplication pattern is a clean, robust way to serialize concurrent balance lookups.

2. New Components

  • PeerInfo.vue: Clean, reusable component with a well-designed props API. The reported-greyed CSS class correctly applies grayscale, opacity, and pointer-events: none to prevent interaction with reported users while preserving the red "Reported" badge.
  • ReportDialog.vue: Proper q-radio binding with clear category selection and v-model + update:modelValue pattern.
  • FeedbackForm.vue: Well-implemented with pre-composed feedback chips.

3. UI/UX Improvements

  • Trade stats display on ad listings and trade cards is a valuable addition.
  • Reported user handling (greyscale/blur) correctly preserves the red "Reported" badge visibility.
  • Appeal form correctly restricts reason selection to single choice and shows the correct statement based on filer identity.
  • Sentence case normalization across headers, labels, and buttons improves consistency.

4. i18n

  • All new UI strings (ReportUser, TradesCompleted, TradesFailed, CompletionPercentage, ReportReason*, FastTransaction, etc.) are present across all 19 locale files.
  • AppealForm.vue dynamically selects the correct translation key (AppealFormReasonOpt1Seller vs AppealFormReasonOpt1Buyer) based on filer identity.

⚠️ Minor Notes (Non-blocking)

1. Pre-existing debug logs in modified files
The following console.log statements were not introduced by this PR and are acceptable as pre-existing artifacts, but should be cleaned up in a future housekeeping pass:

  • src/components/ramp/fiat/EscrowTransfer.vue (lines 317, 323, 326) — BCH escrow flow logs
  • src/components/ramp/appeal/AppealDetail.vue (lines 271, 396, 428) — Appeal action / release-refund logs

2. PaymentConfirmation.vue — Contract balance display when balance is zero
Line 45 uses v-if="contractBalance", which hides the balance when it is 0. It should use v-if="contractBalance !== null" to correctly show "0 BCH" when the contract is empty. This is a pre-existing UX bug.

3. PaymentConfirmation.vue — fetchOrderDetail null reference risk
fetchOrderDetail() accesses vm.data.order.id directly. If data.order is ever falsy, this will throw. In current usage the parent always passes order, so this is not exercised, but a defensive check would be safer.

4. PaymentConfirmation.vue — sendConfirmPayment does not re-throw
The .catch(error => { this.showErrorDialog(error) }) in sendConfirmPayment swallows the error and returns undefined, causing response.data to throw on the next line. This is a pre-existing bug.

5. order.vue — loadData catch block leaves skeleton on error
If loadData() throws before isloaded = true, the skeleton loader remains indefinitely. The catch block only logs to console.error. This is a pre-existing UX issue.


📋 Final Verdict

This PR is ready to merge. All high-priority issues from the previous review cycle have been properly resolved. The performance improvements, new reusable components, and UI/UX enhancements are excellent additions to the P2P Ramp module. The remaining notes are minor, pre-existing issues that do not block merge.

New%20session%20-%202026-06-15T06%3A46%3A52.330Z
opencode session  |  github run

@joemarct
joemarct merged commit bca9c84 into master Jun 16, 2026
1 of 2 checks passed
@joemarct
joemarct deleted the improvements/p2p-ramp branch June 16, 2026 05:41
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.

1 participant