Skip to content

payment page redesing#1816

Open
anik-fahmid wants to merge 1 commit intoweDevsOfficial:developfrom
anik-fahmid:feat/payment-settings-page-redesign
Open

payment page redesing#1816
anik-fahmid wants to merge 1 commit intoweDevsOfficial:developfrom
anik-fahmid:feat/payment-settings-page-redesign

Conversation

@anik-fahmid
Copy link
Collaborator

@anik-fahmid anik-fahmid commented Feb 20, 2026

feat(payment): redesign payment gateway settings page

  • Restructure payment gateway settings into collapsible accordion panels
  • Add Pro badge (pro-icon) next to Credit Card / Stripe gateway label
  • Disable Stripe accordion trigger for free users with a "Available with WPUF Pro" message
  • Add "Enable Gateway" row with PRO badge for Stripe in accordion header
  • Resolve PRO badge image path dynamically from existing DOM assets
  • Close all other accordions when one is opened (single-expand behavior)
  • Skip accordion expansion click for Pro-gated (disabled) gateways

Summary by CodeRabbit

  • New Features

    • Settings payment section now features a collapsible accordion layout for PayPal, Bank Transfer, Credit Card, and Stripe.
    • Added PRO feature indicators for gated functionality.
  • Style

    • Improved formatting and visual consistency in the settings interface.

@anik-fahmid anik-fahmid requested a review from sapayth February 20, 2026 08:34
@anik-fahmid anik-fahmid self-assigned this Feb 20, 2026
@anik-fahmid anik-fahmid added the needs: dev review This PR needs review by a developer label Feb 20, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Walkthrough

This pull request refactors the payment gateway settings UI in the admin panel by introducing a client-side accordion component that groups PayPal, Bank Transfer, and Credit Card gateway options. It scans the existing payment table, reorganizes rows by gateway type, injects dynamic trigger rows with toggle behavior, adds conditional PRO-gated features for Stripe, and injects styling and PRO badge indicators.

Changes

Cohort / File(s) Summary
Payment Gateway Accordion UI
assets/js/admin/settings.js
Implements client-side accordion for payment gateway grouping, adds gateway discovery and row organization logic, creates dynamic toggle triggers with event handlers, injects accordion styles, and implements PRO badge resolution and conditional Stripe gating. Also includes minor formatting consistency improvements (+312/-17 lines).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Dev Review Done

Suggested reviewers

  • sapayth

Poem

🐰 In settings bright where gateways dwell,
An accordion unfolds its tale so well,
PayPal, Bank, and Stripe in groups so neat,
With PRO badges making the UI complete!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'payment page redesing' [sic] is related to the main changeset but contains a spelling error ('redesing' instead of 'redesign'), and uses imprecise language that could be clearer about the specific changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
assets/js/admin/settings.js (1)

62-74: ⚠️ Potential issue | 🟠 Major

allowGatewaysTr re-surfaces after every search reset.

allowGatewaysTr is hidden using an inline style at Line 127 (allowGatewaysTr.style.display = 'none'), but wpuf_search_reset() at Line 70–72 unconditionally calls row.style.display = '' on every tr in the metabox holder, clearing that inline style and making the original gateway-checkboxes row visible again alongside the newly injected accordion UI.

Fix: use a CSS class to hide the row — inline styles are easily clobbered by the reset.

🐛 Proposed fix

Add the CSS class alongside the injected <style> block (Lines 158–218):

+                    .wpuf-accordion-hidden {
+                        display: none !important;
+                    }

Then replace the inline-style hide:

-                    allowGatewaysTr.style.display = 'none';
+                    allowGatewaysTr.classList.add('wpuf-accordion-hidden');

Also applies to: 127-127

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` around lines 62 - 74, The reset routine
wpuf_search_reset currently clears inline styles on all tr elements which
re-shows the gateway row; stop using inline styles and instead add a dedicated
CSS class (e.g., "wpuf-hidden-gateway") to hide allowGatewaysTr when injecting
the accordion, and update the injected <style> block to include
.wpuf-hidden-gateway { display: none !important; } ; replace the code that sets
allowGatewaysTr.style.display = 'none' with adding that class
(allowGatewaysTr.classList.add('wpuf-hidden-gateway')), and remove any code that
later tries to clear inline display so wpuf_search_reset can safely reset rows
without re-surfacing the gateway row.
🧹 Nitpick comments (3)
assets/js/admin/settings.js (3)

293-293: Hardcoded upgrade URL should be passed via wp_localize_script.

https://wedevs.com/wp-user-frontend-pro/ is hardcoded in JavaScript. Consider passing it from PHP using wp_localize_script (alongside any other JS config) so the URL can be controlled server-side without touching the JS bundle.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` at line 293, The hardcoded upgrade URL inside
the assets/js/admin/settings.js string (see the dummyTd.innerHTML assignment)
should be replaced with a value provided from PHP via wp_localize_script (or
wp_add_inline_script) so the URL can be configured server-side; update the PHP
that enqueues this script to pass an array (e.g., ['upgradeUrl' =>
'https://...']) to the script handle, then modify the JS to read the localized
object (e.g., window.<yourLocalizedObject>.upgradeUrl) instead of the literal
"https://wedevs.com/wp-user-frontend-pro/" in the dummyTd.innerHTML
construction.

307-365: Accordion button missing aria-expanded (and aria-controls) attributes.

aria-expanded indicates whether a control is expanded or collapsed and should be applied to the focusable, interactive control that toggles the visibility of the object. When the content is visible, the element with role button has aria-expanded set to true; when hidden, it is set to false. Without this, screen readers have no way to announce the open/closed state of each gateway panel.

♻️ Proposed fix
     triggerBtn.type = 'button';
     triggerBtn.className = 'wpuf-accordion-trigger';
     triggerBtn.setAttribute('data-target-group', key);
+    triggerBtn.setAttribute('aria-expanded', 'false');

Then in the click handler, sync the attribute:

-                        this.classList.add('active');
+                        this.classList.add('active');
+                        this.setAttribute('aria-expanded', 'true');
                         document.querySelectorAll('tr[data-group="' + key + '"]').forEach(function (tr) {
                             tr.classList.add('active');
                         });

And when closing all accordions:

         document.querySelectorAll('.wpuf-accordion-trigger').forEach(function (btn) {
             btn.classList.remove('active');
+            btn.setAttribute('aria-expanded', 'false');
             var g = btn.getAttribute('data-target-group');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` around lines 307 - 365, The accordion trigger
(triggerBtn) lacks accessibility attributes; set aria-expanded="false" on
creation and set aria-controls to the id of the rows group (generate a stable id
using the group key, e.g. "wpuf-accordion-" + key) so screen readers can
associate the button with its content; ensure each row in group.rows gets that
same container id or a wrapper element id to reference; in the click handler for
triggerBtn (and in the routine that closes all accordions where you remove the
'active' class from .wpuf-accordion-trigger), update
triggerBtn.setAttribute('aria-expanded', 'true'|'false') accordingly and when
toggling the group update the referenced content's aria-hidden to match the
visibility (true when hidden, false when shown) so aria-expanded and
aria-controls stay in sync with the visible state.

182-196: float: right is a no-op inside a flex container.

.wpuf-accordion-trigger uses display: flex, so float: right on the ::after pseudo-element (Line 190) has no effect — the arrow is already pushed to the right by margin-left: auto on Line 191. The float declaration can be removed to avoid confusion.

♻️ Proposed fix
                     .wpuf-accordion-trigger:after {
                         content: '';
                         display: block;
                         width: 8px;
                         height: 8px;
                         border-right: 2px solid `#777`;
                         border-bottom: 2px solid `#777`;
                         transform: translateY(-2px) rotate(45deg);
-                        float: right;
                         margin-left: auto;
                         transition: transform 0.3s ease;
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` around lines 182 - 196, Remove the redundant
float declaration from the pseudo-element rule: in the
.wpuf-accordion-trigger:after CSS block remove "float: right" (the element is
inside a flex container and already positioned by margin-left: auto), leaving
the rest of the styles (width, height, borders, transform, margin-left: auto,
transition) unchanged; ensure the active state
.wpuf-accordion-trigger.active:after remains as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/js/admin/settings.js`:
- Around line 9-36: The search handler currently sets tr.style.display =
'table-row' for matches using the static NodeList "content", which causes
payment rows (originally hidden by the accordion CSS) to leak into view; to fix,
add a guard like the existing 'wpuf_mails' block for content_id ===
'wpuf_payment' that resets display on its tbody rows (e.g.,
querySelectorAll('tr').forEach(tr => tr.style.display = '')) so payment rows
remain controlled by the accordion; apply this same guard in both places where
the content iteration runs (the search_input keyup handler and the repeated loop
later around the same logic) and keep using the same identifiers: search_input,
content, content_id, value_tab, and 'wpuf_payment'.
- Around line 225-242: The PRO icon resolution is doing a redundant DOM query
and using a fragile split to derive base URLs; update the proIconImg lookup to
only try document.querySelector('.pro-icon img') once (remove the redundant
document.querySelector('span.pro-icon img') fallback) and, when deriving baseUrl
from anyImg.src in the anyImg branch, compute baseUrl using the last occurrence
of "/assets/" (e.g., use src.substring(0, src.lastIndexOf('/assets/')) ) before
building existingProIconSrc; ensure you still fall back to fallbackProIconSvg if
no anyImg is found and keep references to proIconImg, anyImg,
existingProIconSrc, and fallbackProIconSvg unchanged.

---

Outside diff comments:
In `@assets/js/admin/settings.js`:
- Around line 62-74: The reset routine wpuf_search_reset currently clears inline
styles on all tr elements which re-shows the gateway row; stop using inline
styles and instead add a dedicated CSS class (e.g., "wpuf-hidden-gateway") to
hide allowGatewaysTr when injecting the accordion, and update the injected
<style> block to include .wpuf-hidden-gateway { display: none !important; } ;
replace the code that sets allowGatewaysTr.style.display = 'none' with adding
that class (allowGatewaysTr.classList.add('wpuf-hidden-gateway')), and remove
any code that later tries to clear inline display so wpuf_search_reset can
safely reset rows without re-surfacing the gateway row.

---

Nitpick comments:
In `@assets/js/admin/settings.js`:
- Line 293: The hardcoded upgrade URL inside the assets/js/admin/settings.js
string (see the dummyTd.innerHTML assignment) should be replaced with a value
provided from PHP via wp_localize_script (or wp_add_inline_script) so the URL
can be configured server-side; update the PHP that enqueues this script to pass
an array (e.g., ['upgradeUrl' => 'https://...']) to the script handle, then
modify the JS to read the localized object (e.g.,
window.<yourLocalizedObject>.upgradeUrl) instead of the literal
"https://wedevs.com/wp-user-frontend-pro/" in the dummyTd.innerHTML
construction.
- Around line 307-365: The accordion trigger (triggerBtn) lacks accessibility
attributes; set aria-expanded="false" on creation and set aria-controls to the
id of the rows group (generate a stable id using the group key, e.g.
"wpuf-accordion-" + key) so screen readers can associate the button with its
content; ensure each row in group.rows gets that same container id or a wrapper
element id to reference; in the click handler for triggerBtn (and in the routine
that closes all accordions where you remove the 'active' class from
.wpuf-accordion-trigger), update triggerBtn.setAttribute('aria-expanded',
'true'|'false') accordingly and when toggling the group update the referenced
content's aria-hidden to match the visibility (true when hidden, false when
shown) so aria-expanded and aria-controls stay in sync with the visible state.
- Around line 182-196: Remove the redundant float declaration from the
pseudo-element rule: in the .wpuf-accordion-trigger:after CSS block remove
"float: right" (the element is inside a flex container and already positioned by
margin-left: auto), leaving the rest of the styles (width, height, borders,
transform, margin-left: auto, transition) unchanged; ensure the active state
.wpuf-accordion-trigger.active:after remains as-is.

Comment on lines 9 to 36
search_input.addEventListener('keyup', function (e) {
var search_value = e.target.value.toLowerCase();
var value_tab = [];
var value_tab = [];

if ( search_value.length ) {
if (search_value.length) {
close.style.display = 'flex';
content.forEach(function (row, index) {

var content_id = row.closest('div').getAttribute('id');
var tab_id = content_id + '-tab';
var found_value = row.innerText.toLowerCase().includes( search_value );
var tab_id = content_id + '-tab';
var found_value = row.innerText.toLowerCase().includes(search_value);

if ( found_value ){
if (found_value) {
row.closest('tr').style.display = 'table-row';
}else {
} else {
row.closest('tr').style.display = 'none';
}

if ( 'wpuf_mails' === content_id ){
if ('wpuf_mails' === content_id) {
row.closest('tbody').querySelectorAll('tr').forEach(function (tr) {
tr.style.display = '';
});
}

if ( found_value === true && ! value_tab.includes( tab_id ) ) {
if (found_value === true && !value_tab.includes(tab_id)) {
value_tab.push(tab_id);
}
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Search overrides accordion display:none for payment rows.

content is a static NodeList captured at Line 4 before the accordion reorganizes the DOM. The original payment table rows (PayPal settings, Bank Transfer settings, Stripe settings) are part of that snapshot. When a user types a matching term, Lines 21–25 apply tr.style.display = 'table-row', which overrides .wpuf-accordion-row { display: none } — making those rows appear outside their accordion context. The accordion trigger rows are NOT in content (they are created after Line 4), so they won't be shown alongside the leaked content rows.

Lines 27–31 already have a precedent for special-casing a tab; the same pattern is needed for wpuf_payment.

🐛 Proposed fix — add a payment-tab guard parallel to the `wpuf_mails` guard
                    if ('wpuf_mails' === content_id) {
                        row.closest('tbody').querySelectorAll('tr').forEach(function (tr) {
                            tr.style.display = '';
                        });
                    }
+
+                    if ('wpuf_payment' === content_id) {
+                        // Let the accordion handle its own row visibility; skip inline-style overrides
+                        row.closest('tr').style.display = '';
+                    }

Also applies to: 27-31, 130-156

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` around lines 9 - 36, The search handler
currently sets tr.style.display = 'table-row' for matches using the static
NodeList "content", which causes payment rows (originally hidden by the
accordion CSS) to leak into view; to fix, add a guard like the existing
'wpuf_mails' block for content_id === 'wpuf_payment' that resets display on its
tbody rows (e.g., querySelectorAll('tr').forEach(tr => tr.style.display = ''))
so payment rows remain controlled by the accordion; apply this same guard in
both places where the content iteration runs (the search_input keyup handler and
the repeated loop later around the same logic) and keep using the same
identifiers: search_input, content, content_id, value_tab, and 'wpuf_payment'.

Comment on lines +225 to +242
var existingProIconSrc = '';
var proIconImg = document.querySelector('.pro-icon img');
if (!proIconImg) {
// Also try settings fields that may be hidden
proIconImg = document.querySelector('span.pro-icon img');
}
if (proIconImg) {
existingProIconSrc = '<img src="' + proIconImg.getAttribute('src') + '" alt="PRO" style="vertical-align:middle;">';
} else {
// Derive path from any known plugin img on the page, or use absolute path pattern
var anyImg = document.querySelector('img[src*="wp-user-frontend"][src*="assets"]');
if (anyImg) {
var baseUrl = anyImg.getAttribute('src').split('/assets/')[0];
existingProIconSrc = '<img src="' + baseUrl + '/assets/images/pro-badge.svg" alt="PRO" style="vertical-align:middle;">';
} else {
existingProIconSrc = fallbackProIconSvg;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fragile path derivation and redundant query in PRO icon resolution.

Two minor concerns:

  1. Redundant query (Line 229): document.querySelector('span.pro-icon img') is a strict subset of .pro-icon img already tried on Line 226. If Line 226 returns null, Line 229 never returns a different result.

  2. Fragile .split('/assets/')[0] (Line 237): If any image's src URL contains /assets/ in the subdomain or path prefix (e.g., https://cdn-assets.domain.com/plugins/wp-user-frontend/assets/img.png), the split yields an incorrect base. Prefer src.substring(0, src.lastIndexOf('/assets/')) to match the last occurrence, which is more reliable for typical WP plugin URL structures.

♻️ Proposed fix
-                if (!proIconImg) {
-                    // Also try settings fields that may be hidden
-                    proIconImg = document.querySelector('span.pro-icon img');
-                }
                 if (proIconImg) {
-                        var baseUrl = anyImg.getAttribute('src').split('/assets/')[0];
+                        var imgSrc = anyImg.getAttribute('src');
+                        var baseUrl = imgSrc.substring(0, imgSrc.lastIndexOf('/assets/'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/admin/settings.js` around lines 225 - 242, The PRO icon resolution
is doing a redundant DOM query and using a fragile split to derive base URLs;
update the proIconImg lookup to only try document.querySelector('.pro-icon img')
once (remove the redundant document.querySelector('span.pro-icon img') fallback)
and, when deriving baseUrl from anyImg.src in the anyImg branch, compute baseUrl
using the last occurrence of "/assets/" (e.g., use src.substring(0,
src.lastIndexOf('/assets/')) ) before building existingProIconSrc; ensure you
still fall back to fallbackProIconSvg if no anyImg is found and keep references
to proIconImg, anyImg, existingProIconSrc, and fallbackProIconSvg unchanged.

@Rubaiyat-E-Mohammad
Copy link
Contributor

Rubaiyat-E-Mohammad commented Feb 25, 2026

Pro badge is showing, though Pro is off. @anik-fahmid vai
image

@Rubaiyat-E-Mohammad
Copy link
Contributor

duplicate #1821

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug needs: dev review This PR needs review by a developer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants