feat: Dokan widthraw admin list page.#2882
Conversation
…package-using-webpack' into feat/dokan-widthraw-admin-list
…package-using-webpack' into feat/dokan-widthraw-admin-list
…ing, and eligibility conditions - Improved handling for `dokanAdminDashboard.currency` fallback in `Accounting.ts`. - Implemented `processDetails` for detailed render of withdrawal methods. - Replaced custom buttons with `DokanButton` for better consistency. - Added eligibility conditions for bulk actions (approve, cancel, delete). - Introduced `formatPrice` utility for consistent price formatting. - Updated table styling with new borders and rounded corners in SCSS. - Corrected TSPath alias in `tsconfig.json`.
…ancellation, and deletion
|
Caution Review failedThe pull request is closed. WalkthroughAdds Withdraw and Vendors admin pages and routes, new ExportController and WithdrawExportController REST endpoints, frontend Withdraw and Vendors React pages, dataview UI components/styles, TypeScript configs/types, accounting currency fallbacks, and registration of new dashboard services and assets. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Admin
participant Router as Admin Router
participant UI as WithdrawPage (React)
participant WithdrawAPI as Withdraw REST (dokan/v2)
participant ExportAPI as Export Controller (dokan/v1/reports/withdraws)
Admin->>Router: Navigate /withdraw
Router->>UI: Mount WithdrawPage
UI->>WithdrawAPI: GET /dokan/v2/withdraws?per_page=&page=&status=
WithdrawAPI-->>UI: 200 OK + X-WP-Total, X-WP-TotalPages
UI->>Admin: Render tabs, filters, table
rect rgba(200,230,255,0.18)
Admin->>UI: Click Export
UI->>ExportAPI: POST /dokan/v1/reports/withdraws/export
ExportAPI-->>UI: 202 Accepted + { export_id }
loop Polling
UI->>ExportAPI: GET /dokan/v1/reports/withdraws/export/{export_id}/status
ExportAPI-->>UI: 200 OK (pending|completed)
end
UI->>ExportAPI: GET /dokan/v1/reports/withdraws/export/{export_id}/download
ExportAPI-->>UI: 200 OK (file download)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (8)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (11)
src/admin/dashboard/style.scss (1)
18-87: Consider consolidating duplicate selectors and improving maintainability.The styling is comprehensive, but there are several areas for improvement:
- Duplicate selectors: Lines 18-65 and 67-77 both target
#dokan-admin-dashboardseparately- Redundant no-results styling: Lines 56-64 style
.dataviews-no-resultstwice with different properties- Missing organization: Related styles are scattered across multiple blocks
Apply this diff to consolidate and organize the styles:
-#dokan-admin-dashboard { - .dokan-dashboard-datatable { - .dataviews-wrapper { - .dataviews__view-actions { - display: none; - } - } - } - - .dataviews-view-table__actions-column { - .components-button { - &:hover { - background: transparent; - } - - &:focus { - box-shadow: none; - } - } - } - - .dataviews-view-table__row { - td { - vertical-align: middle; - } - } - - table.dataviews-view-table { - border: 1px solid #E9E9E9; - th { - background-color: #f9fafb; - border-bottom: 1px solid #E9E9E9; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - } - } - - .dataviews-wrapper { - .dataviews-no-results { - color: #6b7280; - } - - .dataviews-no-results { - padding-top: 1rem; - padding-bottom: 1rem; - } - } -} - -#dokan-admin-dashboard { - .dataviews-view-table { - .dataviews-view-table__actions-column { - .components-button { - &:first-child { - margin-right: 16px; - } - } - } - } -} - -#dokan-admin-dashboard { - .dataviews-wrapper { - background-color: #ffffff; - .dataviews-loading { - padding-top: 1rem; - padding-bottom: 1rem; - } - } -} +#dokan-admin-dashboard { + // DataTable wrapper + .dokan-dashboard-datatable { + .dataviews-wrapper { + .dataviews__view-actions { + display: none; + } + } + } + + // DataViews wrapper + .dataviews-wrapper { + background-color: #ffffff; + + .dataviews-no-results { + color: #6b7280; + padding: 1rem 0; + } + + .dataviews-loading { + padding: 1rem 0; + } + } + + // Table styling + table.dataviews-view-table { + border: 1px solid #E9E9E9; + + th { + background-color: #f9fafb; + border-bottom: 1px solid #E9E9E9; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + } + + .dataviews-view-table__row { + td { + vertical-align: middle; + } + } + + // Actions column styling + .dataviews-view-table__actions-column { + .components-button { + &:hover { + background: transparent; + } + + &:focus { + box-shadow: none; + } + + &:first-child { + margin-right: 16px; + } + } + } +}tsconfig.json (1)
36-36: Consider enabling noUncheckedIndexedAccess for better type safety.Setting
noUncheckedIndexedAccesstofalsedisables an important TypeScript check that helps catch potential runtime errors when accessing array/object properties. Consider enabling this for better type safety.- "noUncheckedIndexedAccess": false, + "noUncheckedIndexedAccess": true,src/utilities/Accounting.ts (2)
25-27: Consider extracting currency data resolution logic.The fallback pattern is repeated for each currency property. Consider extracting this into a helper function to reduce code duplication and improve maintainability.
const getCurrencyData = () => { return window?.dokanFrontend?.currency || window?.dokanAdminDashboard?.currency; }; export const formatPrice = ( price: number | string = '', currencySymbol = '', precision = null, thousand = '', decimal = '', format = '' ): string | number => { if (!window.accounting) { console.warn('Woocommerce Accounting Library Not Found'); return price; } const currency = getCurrencyData(); if (!currency) { console.warn('Dokan Currency Data Not Found'); return price; } return window.accounting.formatMoney( price, currencySymbol || currency.symbol, precision || currency.precision, thousand || currency.thousand, decimal || currency.decimal, format || currency.format ); };Also applies to: 31-33, 37-39, 43-45, 49-51
84-91: Fix formatting and consider extracting currency data logic.The parameters are inconsistently formatted, and the same currency data fallback pattern is repeated.
return window.accounting.formatNumber( value, - // @ts-ignore - window?.dokanFrontend?.currency.precision || - window?.dokanAdminDashboard?.currency.precision, - window?.dokanFrontend?.currency.thousand || - window?.dokanAdminDashboard?.currency.thousand, - window?.dokanFrontend?.currency.decimal || - window?.dokanAdminDashboard?.currency.decimal + currency.precision, + currency.thousand, + currency.decimal );src/admin/dashboard/components/Dashboard.tsx (1)
7-7: Consider lazy-loading WithdrawPage to keep the admin bundle leanRoute-level code-splitting can reduce initial load.
-import WithdrawPage from '../pages/withdraw'; +import { lazy, Suspense } from 'react'; +const WithdrawPage = lazy(() => import('../pages/withdraw'));Wrap render where used:
- element: <WithdrawPage />, + element: ( + <Suspense fallback={null}> + <WithdrawPage /> + </Suspense> + ),includes/Admin/Dashboard/Pages/Withdraw.php (1)
21-29: Capability handling may be misleadingReturning
'capability' => $capabilityhere doesn’t affect enforcement becauseDashboard::register_menu()uses its own$capabilitywhen building submenu links. If Withdraw needs stricter/different caps, wire it throughregister_menu()(e.g., honor$menu_args['capability']).Would you like a patch to make
register_menu()respect per-page capabilities?src/admin/dashboard/pages/withdraw/index.tsx (5)
560-565: Remove unused variable
updateDatais set but never used.- try { - const updateData = {}; - if ( action === 'approved' ) { - updateData.status = action; - } else if ( action === 'cancelled' ) { - updateData.status = action; - } + try {
504-512: Status filter:isNoneoperator unsupported in query buildingUI exposes
isNone, but fetch only handlesisAny. Either implement exclusion logic or removeisNoneto avoid a broken UX.Example (if backend supports exclusion):
view.filters.forEach( ( filter ) => { if ( filter.field === 'status' && filter.operator === 'isAny' ) { queryArgs.status = filter.value?.join( ',' ); + } else if ( + filter.field === 'status' && + filter.operator === 'isNone' + ) { + queryArgs.status__not_in = filter.value?.join( ',' ); } } );Or restrict operators:
- filterBy: { - operators: [ 'isAny', 'isNone' ], - }, + filterBy: { operators: [ 'isAny' ] },Also applies to: 145-146
389-391: Unimplemented PayPal bulk actionCallback is empty; clicking will do nothing. Hide until implemented or wire to export/masspay endpoint.
Would you like me to add a CSV download for PayPal IDs (selected rows) behind a capability check, or remove the action for now?
112-118: Improve avatar img accessibility and performanceProvide a meaningful alt fallback and lazy-load the image.
- <img + <img src={ item.user.gravatar } - alt={ item.user?.store_name || '' } + alt={ item.user?.store_name || __( 'Vendor avatar', 'dokan-lite' ) } + loading="lazy" className="w-8 h-8 rounded-sm" />
1008-1013: Modal primary action label ‘Update’ is misleadingThe View modal does not update anything. Either implement update or change the confirm label to “Close” and hide the cancel button.
- onConfirm={ closeModal } - dialogTitle={ <></> } - confirmButtonText={ __( 'Update', 'dokan-lite' ) } - hideCancelButton={ false } - cancelButtonText={ __( 'Cancel', 'dokan-lite' ) } + onConfirm={ closeModal } + dialogTitle={ <></> } + confirmButtonText={ __( 'Close', 'dokan-lite' ) } + hideCancelButton={ true }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
includes/Admin/Dashboard/Dashboard.php(2 hunks)includes/Admin/Dashboard/Pages/Withdraw.php(1 hunks)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php(2 hunks)includes/REST/WithdrawControllerV2.php(1 hunks)src/admin/dashboard/components/Dashboard.tsx(2 hunks)src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)src/admin/dashboard/style.scss(1 hunks)src/utilities/Accounting.ts(3 hunks)tsconfig.json(1 hunks)types/externals.d.ts(1 hunks)types/globals.d.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
includes/REST/WithdrawControllerV2.php (1)
includes/REST/WithdrawController.php (1)
register_routes(42-176)
includes/Admin/Dashboard/Pages/Withdraw.php (3)
includes/Admin/Dashboard/Dashboard.php (4)
Dashboard(13-409)settings(131-233)scripts(242-249)styles(258-265)includes/Admin/Dashboard/Pages/AbstractPage.php (1)
AbstractPage(8-59)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (1)
register(29-36)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
includes/Admin/Dashboard/Dashboard.php (1)
Dashboard(13-409)includes/Admin/Dashboard/Pages/Withdraw.php (1)
Withdraw(5-66)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (5)
formatPrice(79-79)DokanTab(61-61)Filter(60-60)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: api tests (1, 1)
- GitHub Check: e2e tests (3, 3)
🔇 Additional comments (10)
includes/REST/WithdrawControllerV2.php (1)
27-27: LGTM! Parent route registration ensures complete API availability.Calling
parent::register_routes()correctly ensures that all base class routes (fromWithdrawController) are available alongside the V2-specific routes, providing a complete withdrawal API for admin functionality.includes/Admin/Dashboard/Dashboard.php (2)
302-304: Good defensive programming for dependency management.The safeguard to automatically include 'dokan-react-components' ensures the admin dashboard script loads correctly even if the dependency is missing from the asset file, preventing runtime errors in the React components.
317-317: Correct dependency declaration for CSS loading order.Adding 'dokan-react-components' as a dependency for the admin dashboard stylesheet ensures proper loading sequence and prevents styling conflicts.
tsconfig.json (1)
14-20: Aliases in tsconfig.json match webpack.config.js. TSConfigpathsalign with thealiassection in webpack.config.js; no further action required.src/utilities/Accounting.ts (1)
16-22: LGTM! Robust fallback mechanism for currency data.The dual-source approach correctly handles both frontend and admin dashboard contexts, ensuring consistent currency formatting across different parts of the application.
src/admin/dashboard/components/Dashboard.tsx (1)
29-33: Route registration looks consistent with PHP page id/route
id: 'withdraw'andpath: '/withdraw'align with the PHP page. LGTM.Manually hit wp-admin admin.php?page=dokan#/withdraw to confirm the page loads and nav highlights correctly.
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (1)
9-24: Service registration is correctImporting and adding
Withdraw::classensures hooks are registered and the page is enlisted. LGTM.includes/Admin/Dashboard/Pages/Withdraw.php (1)
63-65: Confirm no page-specific assets/settings are required
register()is a no-op andsettings()returns an empty array. Verify the React page does not expectdokanAdminDashboardSettings.withdrawvalues or extra assets.If needed, we can add typed settings now and localize via
Dashboard::settings().src/admin/dashboard/pages/withdraw/index.tsx (2)
533-548: Verify header names for counts (‘Completed’ vs ‘Approved’)You read
X-Status-Completedand map toapproved. Confirm backend headers; if it returnsX-Status-Approvedin some versions, consider reading both to harden.Proposed hardening:
- const completedCount = parseInt( - response.headers.get( 'X-Status-Completed' ) || 0 - ); + const completedCount = parseInt( + response.headers.get( 'X-Status-Approved' ) ?? + response.headers.get( 'X-Status-Completed' ) ?? + 0 + );
1212-1219: Use ReactclassNameprop instead ofclass
React ignores theclassattribute in JSX; replace it withclassNameon<SimpleRadio>(and verify if per-option props should beoptionClassName/selectedOptionClassName). (geeksforgeeks.org)- <SimpleRadio - name="action" - options={ options } - class={ 'px-0' } - optionClass={ 'px-0' } - selectedOptionClass={ 'px-0' } - /> + <SimpleRadio + name="action" + options={ options } + className="px-0" + optionClass="px-0" + selectedOptionClass="px-0" + />Confirm the exact prop names in
@getdokan/dokan-ui; if they’reoptionClassName/selectedOptionClassName, update accordingly.
| const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; | ||
|
|
There was a problem hiding this comment.
Stop using RawHTML for prices to prevent XSS; render as text
formatPrice returns plain text; RawHTML is unnecessary and risky.
-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>;
+const price = ( amount ) => <span>{ formatPrice( amount ) }</span>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; | |
| const price = ( amount ) => <span>{ formatPrice( amount ) }</span>; |
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/withdraw/index.tsx around lines 48-49, the price
renderer uses RawHTML which is unnecessary and poses XSS risk; replace RawHTML
with plain text rendering. Change the price function to return formatted text
(e.g., a string or a React element like a span) containing formatPrice(amount)
without dangerously inserting HTML, ensuring the value is rendered as safe text.
| render: ( { item } ) => { | ||
| const statusColors = { | ||
| pending: 'bg-yellow-100 text-yellow-800', | ||
| approved: 'bg-green-100 text-green-800', | ||
| }; | ||
| return ( | ||
| <span | ||
| className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ | ||
| statusColors[ item.status ] || | ||
| 'bg-gray-100 text-gray-800' | ||
| }` } | ||
| > | ||
| { | ||
| WITHDRAW_STATUSES.find( | ||
| ( status ) => status.value === item.status | ||
| )?.label | ||
| } | ||
| </span> | ||
| ); | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Normalize backend status 'completed' to UI 'approved'
Prevents blank labels/colors when API returns completed while UI expects approved.
- render: ( { item } ) => {
- const statusColors = {
- pending: 'bg-yellow-100 text-yellow-800',
- approved: 'bg-green-100 text-green-800',
- };
+ render: ( { item } ) => {
+ const normalized = item.status === 'completed' ? 'approved' : item.status;
+ const statusColors = {
+ pending: 'bg-yellow-100 text-yellow-800',
+ approved: 'bg-green-100 text-green-800',
+ cancelled: 'bg-gray-100 text-gray-800',
+ };
return (
<span
className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${
- statusColors[ item.status ] ||
+ statusColors[ normalized ] ||
'bg-gray-100 text-gray-800'
}` }
>
- {
- WITHDRAW_STATUSES.find(
- ( status ) => status.value === item.status
- )?.label
- }
+ { WITHDRAW_STATUSES.find( (s) => s.value === normalized )?.label || normalized }
</span>
);
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| render: ( { item } ) => { | |
| const statusColors = { | |
| pending: 'bg-yellow-100 text-yellow-800', | |
| approved: 'bg-green-100 text-green-800', | |
| }; | |
| return ( | |
| <span | |
| className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ | |
| statusColors[ item.status ] || | |
| 'bg-gray-100 text-gray-800' | |
| }` } | |
| > | |
| { | |
| WITHDRAW_STATUSES.find( | |
| ( status ) => status.value === item.status | |
| )?.label | |
| } | |
| </span> | |
| ); | |
| }, | |
| }, | |
| render: ({ item }) => { | |
| // Normalize backend 'completed' to UI 'approved' | |
| const normalized = item.status === 'completed' ? 'approved' : item.status; | |
| const statusColors = { | |
| pending: 'bg-yellow-100 text-yellow-800', | |
| approved: 'bg-green-100 text-green-800', | |
| cancelled:'bg-gray-100 text-gray-800', | |
| }; | |
| return ( | |
| <span | |
| className={`inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ | |
| statusColors[normalized] || | |
| 'bg-gray-100 text-gray-800' | |
| }`} | |
| > | |
| {WITHDRAW_STATUSES.find(s => s.value === normalized)?.label || normalized} | |
| </span> | |
| ); | |
| }, |
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/withdraw/index.tsx around lines 147 to 167, the UI
shows blank labels/colors when the backend returns status 'completed' because
the code only recognizes 'approved'; normalize 'completed' to 'approved' before
using statusColors and the WITHDRAW_STATUSES lookup. Update the render function
to map item.status === 'completed' to 'approved' (e.g., const uiStatus =
item.status === 'completed' ? 'approved' : item.status) and use uiStatus for
both statusColors[uiStatus] and the WITHDRAW_STATUSES.find(...) comparison so
labels and colors display correctly.
| <Tooltip content={ <RawHTML>{ full }</RawHTML> }> | ||
| <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> | ||
| <RawHTML> | ||
| { truncate ? truncate( full, 120 ) : full } | ||
| </RawHTML> | ||
| </p> |
There was a problem hiding this comment.
Sanitize details tooltip and cell; avoid RawHTML
Render as text and preserve newlines with CSS. Current RawHTML risks XSS from user-supplied payment details.
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }>
+ <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }>
<p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600">
- <RawHTML>
- { truncate ? truncate( full, 120 ) : full }
- </RawHTML>
+ <span>{ truncate ? truncate( full, 120 ) : full }</span>
</p>
</Tooltip>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Tooltip content={ <RawHTML>{ full }</RawHTML> }> | |
| <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> | |
| <RawHTML> | |
| { truncate ? truncate( full, 120 ) : full } | |
| </RawHTML> | |
| </p> | |
| <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> | |
| <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> | |
| <span>{ truncate ? truncate( full, 120 ) : full }</span> | |
| </p> | |
| </Tooltip> |
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/withdraw/index.tsx around lines 212 to 217, the
code uses RawHTML for both the Tooltip content and cell rendering which risks
XSS; replace RawHTML with plain text rendering and preserve newlines via CSS.
Specifically, pass the raw string (escaped by React when rendered as text) into
Tooltip and the <p> instead of RawHTML, apply a utility class like
"whitespace-pre-wrap break-words" (or equivalent) to preserve newlines and wrap
text, and keep using truncate(full, 120) when needed but ensure truncate returns
a plain string; remove any dangerouslySetInnerHTML/RawHTML usage. Ensure Tooltip
content receives the plain full string so no HTML is injected.
| declare module 'jquery' { | ||
| const jQuery: JQueryStatic; | ||
| export = jQuery; | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Avoid hard dependency on JQueryStatic
JQueryStatic requires @types/jquery; without it, tsc will fail.
Run to verify presence:
If missing, either add it or relax the type:
declare module 'jquery' {
- const jQuery: JQueryStatic;
+ const jQuery: any;
export = jQuery;
}🏁 Script executed:
#!/bin/bash
jq -r '.devDependencies["@types/jquery"] // .dependencies["@types/jquery"] // "MISSING"' package.jsonLength of output: 111
Install @types/jquery or relax jQuery type
In types/externals.d.ts (lines 4–7), you’re using JQueryStatic but @types/jquery is not in package.json, causing tsc failures. Either add @types/jquery to dependencies/devDependencies or update the declaration:
declare module 'jquery' {
- const jQuery: JQueryStatic;
+ const jQuery: any;
export = jQuery;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| declare module 'jquery' { | |
| const jQuery: JQueryStatic; | |
| export = jQuery; | |
| } | |
| // types/externals.d.ts | |
| declare module 'jquery' { | |
| const jQuery: any; | |
| export = jQuery; | |
| } |
🤖 Prompt for AI Agents
In types/externals.d.ts around lines 4 to 7, the ambient declaration references
JQueryStatic which requires @types/jquery to be installed; either add
"@types/jquery" to your project (preferably as a devDependency) and run your
package manager to install it, or change the declaration to remove the
JQueryStatic dependency (e.g., declare module 'jquery' { const jQuery: any;
export = jQuery; }) so TypeScript no longer requires the missing types; update
package.json and reinstall if adding the types, or replace the type with any in
the file to relax the type reference.
| declare module 'moment' { | ||
| import moment from 'moment'; | ||
| export = moment; | ||
| } | ||
|
|
||
| // Chart.js - mapped to global Chart | ||
| declare module 'chart.js' { | ||
| import Chart from 'chart.js'; | ||
| export = Chart; | ||
| } |
There was a problem hiding this comment.
Do not import from a module inside its own ambient declaration (moment, chart.js)
import ... from 'moment' within declare module 'moment' (same for chart.js) can cause declaration merging/duplication issues.
Use simple externs:
declare module 'moment' {
- import moment from 'moment';
- export = moment;
+ const moment: any;
+ export = moment;
}
declare module 'chart.js' {
- import Chart from 'chart.js';
- export = Chart;
+ const Chart: any;
+ export = Chart;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| declare module 'moment' { | |
| import moment from 'moment'; | |
| export = moment; | |
| } | |
| // Chart.js - mapped to global Chart | |
| declare module 'chart.js' { | |
| import Chart from 'chart.js'; | |
| export = Chart; | |
| } | |
| declare module 'moment' { | |
| const moment: any; | |
| export = moment; | |
| } | |
| // Chart.js - mapped to global Chart | |
| declare module 'chart.js' { | |
| const Chart: any; | |
| export = Chart; | |
| } |
🤖 Prompt for AI Agents
In types/externals.d.ts around lines 10 to 19, the ambient module declarations
import from the same module (e.g., "import moment from 'moment'") which can
cause declaration merging/duplication; replace those import-based declarations
with simple extern declarations that don't import the module itself — for
example declare module 'moment' { const moment: any; export = moment; } and
declare module 'chart.js' { const Chart: any; export = Chart; } so the files
provide plain extern types without internal imports.
| // WooCommerce packages - mapped to global wc object | ||
| declare module '@woocommerce/blocks-registry' { | ||
| const registry: any; | ||
| export = registry; | ||
| } | ||
|
|
||
| declare module '@woocommerce/settings' { | ||
| const settings: any; | ||
| export = settings; | ||
| } | ||
|
|
||
| declare module '@woocommerce/block-data' { | ||
| const blockData: any; | ||
| export = blockData; | ||
| } | ||
|
|
||
| declare module '@woocommerce/shared-context' { | ||
| const sharedContext: any; | ||
| export = sharedContext; | ||
| } | ||
|
|
||
| declare module '@woocommerce/shared-hocs' { | ||
| const sharedHocs: any; | ||
| export = sharedHocs; | ||
| } | ||
|
|
||
| declare module '@woocommerce/price-format' { | ||
| const priceFormat: any; | ||
| export = priceFormat; | ||
| } | ||
|
|
||
| declare module '@woocommerce/blocks-checkout' { | ||
| const blocksCheckout: any; | ||
| export = blocksCheckout; | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify WooCommerce externals match actual imports
Ensure these module IDs reflect what the code imports to avoid unresolved modules at build time.
🏁 Script executed:
#!/bin/bash
# Used modules in src
used=$(rg -nP --type=ts --type=tsx -o "'@woocommerce/[^']+'" src | sed "s/.*'//;s/'.*//" | sort -u)
# Declared externals here
declared=$(sed -n "s/^declare module '\([^']*\)'.*/\1/p" types/externals.d.ts | grep '^@woocommerce/' | sort -u)
echo "Used-only (not declared):"
comm -23 <(echo "$used") <(echo "$declared") || true
echo "Declared-only (not used):"
comm -13 <(echo "$used") <(echo "$declared") || trueLength of output: 1156
🏁 Script executed:
#!/bin/bash
set -e
# Collect used WooCommerce imports in TS/TSX files
used=$(grep -RhoP "@woocommerce/[^'\" ]+" src --include "*.ts" --include "*.tsx" | sort -u)
# Collect declared externals in types/externals.d.ts
declared=$(grep -Po "(?<=declare module ')[^']+" types/externals.d.ts | grep '^@woocommerce/' | sort -u)
echo "Used-only (not declared):"
comm -23 <(echo "$used") <(echo "$declared") || true
echo "Declared-only (not used):"
comm -13 <(echo "$used") <(echo "$declared") || trueLength of output: 1511
Sync WooCommerce externals with actual imports
- In types/externals.d.ts, add missing declarations for:
• @woocommerce/admin-layout
• @woocommerce/components
• @woocommerce/data
• @woocommerce/date
• @woocommerce/experimental
• @woocommerce/navigation - Remove stale declarations for:
• @woocommerce/block-data
• @woocommerce/blocks-checkout
• @woocommerce/blocks-registry
• @woocommerce/price-format
• @woocommerce/settings
• @woocommerce/shared-context
• @woocommerce/shared-hocs
| declare module '@dokan/hooks' { | ||
| export const useWindowDimensions: any; | ||
| export const useCurrentUser: any; | ||
| export const useCustomerSearch: any; | ||
| export const ViewportDimensions: any; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
ViewportDimensions should be a type, not a value
Avoid declaring it as a const; declare a type to enable import type usage.
declare module '@dokan/hooks' {
- export const useWindowDimensions: any;
+ export function useWindowDimensions(): ViewportDimensions;
export const useCurrentUser: any;
export const useCustomerSearch: any;
- export const ViewportDimensions: any;
+ export type ViewportDimensions = any;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| declare module '@dokan/hooks' { | |
| export const useWindowDimensions: any; | |
| export const useCurrentUser: any; | |
| export const useCustomerSearch: any; | |
| export const ViewportDimensions: any; | |
| } | |
| declare module '@dokan/hooks' { | |
| export function useWindowDimensions(): ViewportDimensions; | |
| export const useCurrentUser: any; | |
| export const useCustomerSearch: any; | |
| export type ViewportDimensions = any; | |
| } |
🤖 Prompt for AI Agents
In types/externals.d.ts around lines 86 to 91, the declaration exports
ViewportDimensions as a value (export const ViewportDimensions: any;) but it
should be a type so consumers can use `import type`; change the export to a type
declaration (export type ViewportDimensions = any;) while leaving the other hook
declarations unchanged.
| var jQuery: JQueryStatic; | ||
| var $: JQueryStatic; | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
jQuery globals rely on JQueryStatic; prefer safer fallback or ensure typings installed
If @types/jquery isn’t installed, these declarations will fail.
Same check as suggested for externals.d.ts.
If needed, relax:
- var jQuery: JQueryStatic;
- var $: JQueryStatic;
+ var jQuery: any;
+ var $: any;🏁 Script executed:
#!/bin/bash
# Check for @types/jquery in package.json
if [ -f package.json ]; then
grep -R '"@types/jquery"' -n package.json || echo "@types/jquery not found in package.json"
else
echo "package.json not found"
fi
# Check tsconfig.json for jquery in compilerOptions.types
if [ -f tsconfig.json ]; then
grep -R '"jquery"' -n tsconfig.json || echo "jquery not found in tsconfig.json"
else
echo "tsconfig.json not found"
fiLength of output: 300
Install jQuery typings or fall back to any
Declarations in types/globals.d.ts (lines 39–41) reference JQueryStatic, but @types/jquery isn’t in package.json and jquery isn’t in tsconfig.json types—this will break builds. Either add @types/jquery to your devDependencies and include "jquery" under compilerOptions.types in tsconfig, or relax the globals:
- var jQuery: JQueryStatic;
- var $: JQueryStatic;
+ var jQuery: any;
+ var $: any;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var jQuery: JQueryStatic; | |
| var $: JQueryStatic; | |
| var jQuery: any; | |
| var $: any; |
🤖 Prompt for AI Agents
In types/globals.d.ts around lines 39 to 41, the file references the
JQueryStatic type but the repo doesn't include @types/jquery or a tsconfig types
entry, which will break builds; fix by either adding @types/jquery to
devDependencies and adding "jquery" to compilerOptions.types in tsconfig.json so
JQueryStatic is available, or replace the strict declarations with a safe
fallback (declare var jQuery: any; declare var $: any;) to avoid the
missing-typing error.
# Conflicts: # includes/Admin/Dashboard/Dashboard.php
- Added `VendorAsyncSelect` and `DateRangePicker` components for improved filtering. - Updated SCSS to fix z-index and add relative positioning. - Refactored filter handling logic for better clarity and usability.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (11)
src/admin/dashboard/pages/withdraw/index.tsx (11)
161-169: Normalize backend 'completed' → UI 'approved' and add 'cancelled' colorPrevents blank label/color and keeps filters consistent.
- getValue: ( { item } ) => item.status, + getValue: ( { item } ) => ( item.status === 'completed' ? 'approved' : item.status ), @@ - render: ( { item } ) => { - const statusColors = { - pending: 'bg-yellow-100 text-yellow-800', - approved: 'bg-green-100 text-green-800', - }; + render: ( { item } ) => { + const normalized = item.status === 'completed' ? 'approved' : item.status; + const statusColors = { + pending: 'bg-yellow-100 text-yellow-800', + approved: 'bg-green-100 text-green-800', + cancelled: 'bg-gray-100 text-gray-800', + }; return ( <span - className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ statusColors[ item.status ] || 'bg-gray-100 text-gray-800' }` } + className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ statusColors[ normalized ] || 'bg-gray-100 text-gray-800' }` } > - { WITHDRAW_STATUSES.find( ( status ) => status.value === item.status )?.label } + { WITHDRAW_STATUSES.find( (s) => s.value === normalized )?.label || normalized } </span> ); },Also applies to: 169-189
271-285: Guard unknown icons from lucide-reactAvoid runtime crash when icon name is invalid.
- const Icon = LucideIcons[ iconName ]; + const Icon = LucideIcons[ iconName as keyof typeof LucideIcons ] as any; @@ - <Icon size={ 16 } className="!fill-none" /> + { Icon ? <Icon size={ 16 } className="!fill-none" /> : null }
371-372: Treat 'completed' as approved in action eligibilityKeeps actions disabled for both approved/completed.
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Also applies to: 392-393
515-586: Stabilize fetching; avoid stale filters/duplicatesMemoize fetchWithdraws and drive it via effect; let handlers only update state.
- const fetchWithdraws = async () => { + const fetchWithdraws = useCallback( async () => { setIsLoading( true ); try { const queryArgs = { per_page: view?.perPage ?? 10, page: view?.page ?? 1, search: view?.search ?? '', status: view?.status === 'all' ? '' : view?.status, ...filterArgs, }; @@ } finally { setIsLoading( false ); } - }; + }, [ view, filterArgs ] );- const handleFilter = () => { - setView( ( prevView ) => ( { - ...prevView, - page: 1, // Reset to first page when applying filters - } ) ); - // fetchWithdraws(); - }; + const handleFilter = () => { + setView( ( prevView ) => ( { ...prevView, page: 1 } ) ); + };- const clearFilter = () => { + const clearFilter = () => { setVendorFilter( null ); setAfter( '' ); setAfterText( '' ); setBefore( '' ); setBeforeText( '' ); setFilterArgs( {} ); - - setView( ( prevView ) => ( { - ...prevView, - page: 1, // Reset to first page when applying filters - } ) ); - // fetchWithdraws(); + setView( ( prevView ) => ( { ...prevView, page: 1 } ) ); };- useEffect( () => { - fetchWithdraws(); - }, [ view ] ); + useEffect( () => { + fetchWithdraws(); + }, [ fetchWithdraws ] );Also applies to: 639-647, 650-663, 665-669
56-57: Stop using RawHTML for prices; render as textRawHTML invites XSS and isn’t needed for plain strings.
-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount ) => <span>{ formatPrice( amount ) }</span>;
234-241: Sanitize “details” rendering; replace RawHTMLUser-provided details must not be injected as HTML.
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> - <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> - </p> - </Tooltip> + <Tooltip content={ <span className="whitespace-pre-line break-words">{ full }</span> }> + <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600 whitespace-pre-line break-words"> + <span>{ truncate ? truncate( full, 120 ) : full }</span> + </p> + </Tooltip>
250-257: Sanitize “note” rendering; replace RawHTMLSame XSS risk for notes.
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> - <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> - </p> - </Tooltip> + <Tooltip content={ <span className="whitespace-pre-line break-words">{ full }</span> }> + <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600 whitespace-pre-line break-words"> + <span>{ truncate ? truncate( full, 120 ) : full }</span> + </p> + </Tooltip>
489-492: Don’t inject empty field id into DataViewsEmpty ids can break column rendering; filter vendor out explicitly.
- fields: fields.map( ( field ) => - field.id !== 'vendor' ? field.id : '' - ), + fields: fields.filter( ( f ) => f.id !== 'vendor' ).map( ( f ) => f.id ),
547-554: Fix REST path: add leading slashPrevents broken requests on some installs.
- path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ),
692-701: Export: whitelist params and add leading slashAvoid leaking internal view keys and fix REST path.
- const path = addQueryArgs( - 'dokan/v2/withdraw', - { ...view, is_export: true } - ); - const res = await apiFetch( { path } ); + const exportArgs: any = { + per_page: view?.perPage ?? 10, + page: view?.page ?? 1, + search: view?.search ?? '', + status: view?.status === 'all' ? '' : view?.status, + ...filterArgs, + is_export: true, + ...( view?.sort?.field ? { orderby: view.sort.field } : {} ), + ...( view?.sort?.direction ? { order: view.sort.direction } : {} ), + }; + const path = addQueryArgs( '/dokan/v2/withdraw', exportArgs ); + const res = await apiFetch( { path } );
1186-1193: Sanitize details in modal; remove RawHTMLRender as text and preserve newlines via CSS.
- <RawHTML> - { processDetails( - item.details, - item.method - ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
🧹 Nitpick comments (3)
src/admin/dashboard/pages/withdraw/index.tsx (3)
1068-1124: Normalize status in modal and use UI labelKeep modal consistent with table/status tabs.
- const statusColors = { + const normalized = item.status === 'completed' ? 'approved' : item.status; + const statusColors = { pending: 'bg-yellow-100 text-yellow-800', approved: 'bg-green-100 text-green-800', cancelled: 'bg-gray-100 text-gray-800', }; @@ - className={ `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${ statusColors[ item.status ] || 'bg-gray-100 text-gray-800' }` } + className={ `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${ statusColors[ normalized ] || 'bg-gray-100 text-gray-800' }` } > - { item.status.charAt( 0 ).toUpperCase() + item.status.slice( 1 ) } + { WITHDRAW_STATUSES.find( (s) => s.value === normalized )?.label || normalized } </span>
819-831: Localize the placeholderEnsure i18n consistency.
- placeholder: 'Select date range', + placeholder: __( 'Select date range', 'dokan-lite' ),
591-596: Remove unused variableupdateData is unused; drop it to reduce noise.
- const updateData = {}; - if ( action === 'approved' ) { - updateData.status = action; - } else if ( action === 'cancelled' ) { - updateData.status = action; - }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
includes/Admin/Dashboard/Dashboard.php(1 hunks)src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)src/admin/dashboard/style.scss(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/admin/dashboard/style.scss
🧰 Additional context used
🧬 Code graph analysis (1)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (5)
formatPrice(79-79)DokanTab(61-61)Filter(60-60)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: api tests (1, 1)
🔇 Additional comments (2)
includes/Admin/Dashboard/Dashboard.php (1)
310-315: CSS dependency order change looks goodRegistering the dashboard CSS after Dokan React Components and WC components should prevent style overrides/regressions.
src/admin/dashboard/pages/withdraw/index.tsx (1)
421-422: Empty PayPal mass payment callbackConfirm whether this is intentionally stubbed; otherwise disable the action until implemented.
Would you like me to wire this to an export endpoint based on payment method and current filters?
| $this->script_key, | ||
| DOKAN_PLUGIN_ASSEST . '/css/dokan-admin-dashboard.css', | ||
| [ 'wc-components' ], | ||
| [ 'dokan-react-components', 'wc-components' ], | ||
| $version | ||
| ); |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify dependency handle is a registered STYLE, not just a script
Ensure a style with handle 'dokan-react-components' is registered before this, otherwise the dependency won’t resolve.
Run:
🏁 Script executed:
#!/bin/bash
# Check that a STYLE handle named 'dokan-react-components' is registered anywhere.
rg -nP "wp_register_style\\(\\s*'dokan-react-components'" -S
rg -nP "wp_enqueue_style\\(\\s*'dokan-react-components'" -S
# Sanity: also list where dokan-admin-dashboard.css is referenced.
rg -nP "dokan-admin-dashboard\\.css" -nLength of output: 114
Register missing style dependency 'dokan-react-components'
No style handle named ‘dokan-react-components’ is registered before being used as a dependency for dokan-admin-dashboard.css (includes/Admin/Dashboard/Dashboard.php:312). Add a wp_register_style or wp_enqueue_style for this handle prior to this enqueue.
🤖 Prompt for AI Agents
In includes/Admin/Dashboard/Dashboard.php around lines 311 to 315, the style
handle 'dokan-react-components' is referenced as a dependency for
dokan-admin-dashboard.css but is never registered; register the
'dokan-react-components' style before enqueuing dokan-admin-dashboard.css by
adding a wp_register_style (or wp_enqueue_style) call that provides the correct
handle, file path (e.g., DOKAN_PLUGIN_ASSEST .
'/css/dokan-react-components.css'), version and optional dependencies, then
proceed to enqueue dokan-admin-dashboard.css as before so the dependency exists
and loads correctly.
| <div className="font-medium text-gray-900"> | ||
| { price( item.receivable || item.amount ) } | ||
| </div> | ||
| ), |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use nullish coalescing to avoid wrong fallback when receivable is 0
If receivable is 0, current code incorrectly falls back to amount.
- { price( item.receivable || item.amount ) }
+ { price( item.receivable ?? item.amount ) }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="font-medium text-gray-900"> | |
| { price( item.receivable || item.amount ) } | |
| </div> | |
| ), | |
| <div className="font-medium text-gray-900"> | |
| { price( item.receivable ?? item.amount ) } | |
| </div> | |
| ), |
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/withdraw/index.tsx around lines 211 to 214, the
current expression uses the || operator so when item.receivable is 0 it
incorrectly falls back to item.amount; replace the fallback logic to use the
nullish coalescing operator (??) so that item.receivable is used when it is 0
but falls back only when it is null or undefined (i.e., call
price(item.receivable ?? item.amount)).
- Introduced `AsyncSelect` with payment method filter support. - Enhanced UI components for filtering, including updated placeholders and button styles. - Added `loadPaymentMethods` function to dynamically fetch payment methods. - Improved filter reset functionality with additional cleanup actions.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (13)
src/admin/dashboard/pages/withdraw/index.tsx (13)
176-196: Normalize 'completed' → 'approved' in table status and add cancelled color- render: ( { item } ) => { - const statusColors = { - pending: 'bg-yellow-100 text-yellow-800', - approved: 'bg-green-100 text-green-800', - }; + render: ( { item } ) => { + const normalized = item.status === 'completed' ? 'approved' : item.status; + const statusColors = { + pending: 'bg-yellow-100 text-yellow-800', + approved: 'bg-green-100 text-green-800', + cancelled: 'bg-gray-100 text-gray-800', + }; return ( <span className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ - statusColors[ item.status ] || + statusColors[ normalized ] || 'bg-gray-100 text-gray-800' }` } > - { - WITHDRAW_STATUSES.find( - ( status ) => status.value === item.status - )?.label - } + { WITHDRAW_STATUSES.find( (s) => s.value === normalized )?.label || normalized } </span> ); },
378-378: Account for 'completed' in eligibility checks- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ), @@ - isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Also applies to: 399-399
1113-1167: Normalize status in modal pill/label (treat 'completed' as 'approved')const statusColors = { pending: 'bg-yellow-100 text-yellow-800', approved: 'bg-green-100 text-green-800', cancelled: 'bg-gray-100 text-gray-800', }; + const normalized = item.status === 'completed' ? 'approved' : item.status; @@ - <span + <span className={ `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${ - statusColors[ - item.status - ] || + statusColors[ normalized ] || 'bg-gray-100 text-gray-800' }` } > - { item.status + { normalized .charAt( 0 ) .toUpperCase() + - item.status.slice( + normalized.slice( 1 ) }
218-221: Use nullish coalescing for payable fallback (handles 0 correctly)- { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) } @@ - { price( - item.receivable || - item.amount - ) } + { price( item.receivable ?? item.amount ) }Also applies to: 1206-1210
279-289: Guard unknown icon names in getActionLabelconst Icon = LucideIcons[ iconName ]; return ( <div className="dokan-layout"> <span className="inline-flex items-center gap-2.5"> - <Icon size={ 16 } className="!fill-none" /> + { Icon ? <Icon size={ 16 } className="!fill-none" /> : null } { label } </span> </div> );
521-593: Stabilize data fetching: memoize fetchWithdraws and update effect deps- const fetchWithdraws = async () => { + const fetchWithdraws = useCallback( async () => { setIsLoading( true ); try { const queryArgs = { per_page: view?.perPage ?? 10, page: view?.page ?? 1, search: view?.search ?? '', status: view?.status === 'all' ? '' : view?.status, ...filterArgs, }; @@ } finally { setIsLoading( false ); } - }; + }, [ view, filterArgs ] );- useEffect( () => { - fetchWithdraws(); - }, [ view ] ); + useEffect( () => { + fetchWithdraws(); + }, [ fetchWithdraws ] );Also applies to: 684-687
3-3: Stop using RawHTML for prices; render as text to avoid XSS
formatPricereturns text. Drop RawHTML import and render safely.-import { RawHTML, useEffect, useState, useCallback } from '@wordpress/element'; +import { useEffect, useState, useCallback } from '@wordpress/element'; @@ -const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount: number | string ) => <span>{ formatPrice( amount ) }</span>;Also applies to: 58-58
241-246: Sanitize Details tooltip/cell; remove RawHTMLRender plain text and preserve newlines via CSS.
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> + <Tooltip content={ <span className="whitespace-pre-line break-words">{ full }</span> }> <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> + <span>{ truncate ? truncate( full, 120 ) : full }</span> </p> </Tooltip>
257-262: Sanitize Note tooltip/cell; remove RawHTML- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> + <Tooltip content={ <span className="whitespace-pre-line break-words">{ full }</span> }> <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> + <span>{ truncate ? truncate( full, 120 ) : full }</span> </p> </Tooltip>
1229-1236: Avoid RawHTML in modal; render details safely- <RawHTML> - { processDetails( - item.details, - item.method - ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
496-499: Fix DataViews fields: avoid empty field id ('')- fields: fields.map( ( field ) => - field.id !== 'vendor' ? field.id : '' - ), + fields: fields.filter( ( f ) => f.id !== 'vendor' ).map( ( f ) => f.id ),
555-555: Fix REST path: add leading slash- path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ),
711-719: Export: whitelist params and fix REST path- const path = addQueryArgs( - 'dokan/v2/withdraw', - { ...view, is_export: true } - ); - const res = await apiFetch( { path } ); + const exportArgs: any = { + per_page: view?.perPage ?? 10, + page: view?.page ?? 1, + search: view?.search ?? '', + status: view?.status === 'all' ? '' : view?.status, + ...filterArgs, + is_export: true, + ...(view?.sort?.field ? { orderby: view.sort.field } : {}), + ...(view?.sort?.direction ? { order: view.sort.direction } : {}), + }; + const path = addQueryArgs( '/dokan/v2/withdraw', exportArgs ); + const res = await apiFetch( { path } );
🧹 Nitpick comments (5)
src/admin/dashboard/pages/withdraw/index.tsx (5)
595-604: Remove dead code in handleBulkAction- try { - const updateData = {}; - if ( action === 'approved' ) { - updateData.status = action; - } else if ( action === 'cancelled' ) { - updateData.status = action; - } + try {
60-67: Harden processDetails against missing/undefined fields-const processDetails = ( data, method: string ) => { +const processDetails = ( data, method: string ) => { + if ( ! data || typeof data !== 'object' ) { + return __( '-', 'dokan-lite' ); + } // get method key details from data then return the values of the key from the data const methodKey = Object.keys( data ).find( ( key ) => key.toLowerCase() === method.toLowerCase() ); if ( methodKey ) { - const methodObject = data[ methodKey ]; + const methodObject = data?.[ methodKey ] || {}; let details = ''; @@ - case 'bank': + case 'bank': details = sprintf( __( 'Account Name: %s, \nAccount Number: %s\nBank Name: %s\nRouting Number: %s\nSwift Code: %s\nIBAN: %s', 'dokan-lite' ), - methodObject.ac_name, - methodObject.ac_number, - methodObject.bank_name, - methodObject.routing_number, - methodObject.swift, - methodObject.iban + methodObject?.ac_name || '-', + methodObject?.ac_number || '-', + methodObject?.bank_name || '-', + methodObject?.routing_number || '-', + methodObject?.swift || '-', + methodObject?.iban || '-' ); break; @@ - case 'paypal': - details = methodObject.email; + case 'paypal': + details = methodObject?.email || ''; break;Also applies to: 74-86
799-799: Localize placeholders- placeholder="Payment Method" + placeholder={ __( 'Payment Method', 'dokan-lite' ) } @@ - placeholder: 'Date', + placeholder: __( 'Date', 'dokan-lite' ),Also applies to: 871-871
427-429: TODO: Implement PayPal mass payment callbackCurrently a no-op; wire up download or disable until available.
141-147: Optional: add width/height and fallback for avatarImproves CLS and avoids broken images.
- <img - src={ item.user.gravatar } - alt={ item.user?.store_name || '' } - className="w-8 h-8 rounded-sm" - /> + <img + src={ item.user.gravatar } + alt={ item.user?.store_name || '' } + width={ 32 } + height={ 32 } + className="w-8 h-8 rounded-sm" + onError={ (e) => (e.currentTarget.style.visibility = 'hidden') } + />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (5)
formatPrice(79-79)DokanTab(61-61)Filter(60-60)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: api tests (1, 1)
🔇 Additional comments (1)
src/admin/dashboard/pages/withdraw/index.tsx (1)
1304-1314: Verify SimpleRadio prop namesComponent props use
class/optionClass; confirm API doesn’t expectclassNamevariants to avoid styling issues.
…ollers - Added `ExportController` and `WithdrawExportController` to manage export functionality. - Registered `WithdrawExportController` with WooCommerce under `withdraws` type. - Created comprehensive usage documentation for the export controller. - Enhanced `REST/Manager` to support export controllers seamlessly.
…integration - Added support for withdraw data export via WooCommerce export system. - Introduced `register_data_endpoint` method to map withdraw export endpoint. - Updated `ExportController` and `WithdrawExportController` for endpoint structure alignment. - Enhanced admin withdraw page with bulk export options and improved pagination.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (12)
src/admin/dashboard/pages/withdraw/index.tsx (12)
58-58: Remove RawHTML in price renderer; render plain text
formatPricereturns text; RawHTML is unnecessary and risky.-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount ) => <span>{ formatPrice( amount ) }</span>;
218-221: Use nullish coalescing for payableIf
receivableis 0,||falls back incorrectly toamount.- { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) }- { price( - item.receivable || - item.amount - ) } + { price( + item.receivable ?? item.amount + ) }Also applies to: 1301-1305
283-289: Guard unknown icons to prevent crashes
LucideIcons[iconName]may be undefined.- const Icon = LucideIcons[ iconName ]; + const Icon = LucideIcons[ iconName ]; return ( <div className="dokan-layout"> <span className="inline-flex items-center gap-2.5"> - <Icon size={ 16 } className="!fill-none" /> + { Icon ? <Icon size={ 16 } className="!fill-none" /> : null } { label } </span> </div> );
498-501: Fix DataViews view.fields: avoid empty string field idEmpty ids can break column rendering; filter out vendor.
- fields: fields.map( ( field ) => - field.id !== 'vendor' ? field.id : '' - ), + fields: fields.filter( ( f ) => f.id !== 'vendor' ).map( ( f ) => f.id ),
523-596: Stabilize fetching; avoid stale filters/duplicate callsMemoize
fetchWithdrawsand depend on it inuseEffect.- const fetchWithdraws = async () => { + const fetchWithdraws = useCallback( async () => { setIsLoading( true ); try { const queryArgs = { per_page: view?.perPage ?? 20, page: view?.page ?? 1, search: view?.search ?? '', status: view?.status === 'all' ? '' : view?.status, ...filterArgs, }; @@ } finally { setIsLoading( false ); } - }; + }, [ view, filterArgs ] );- useEffect( () => { - fetchWithdraws(); - }, [ view ] ); + useEffect( () => { + fetchWithdraws(); + }, [ fetchWithdraws ] );Also applies to: 779-783
806-814: Export: whitelist query params and fix REST pathDon’t leak internal view keys; use server-supported params only and add
/.- const path = addQueryArgs( - 'dokan/v2/withdraw', - { ...view, is_export: true } - ); - const res = await apiFetch( { path } ); + const exportArgs: any = { + per_page: view?.perPage ?? 10, + page: view?.page ?? 1, + search: view?.search ?? '', + status: view?.status === 'all' ? '' : view?.status, + ...filterArgs, + is_export: true, + ...( view?.sort?.field ? { orderby: view.sort.field } : {} ), + ...( view?.sort?.direction ? { order: view.sort.direction } : {} ), + }; + const path = addQueryArgs( '/dokan/v2/withdraw', exportArgs ); + const res = await apiFetch( { path } );
378-378: Eligibility: treat ‘completed’ as approvedBlocks actions on already-completed items.
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Also applies to: 399-399
241-247: XSS: stop injecting Details with RawHTMLRender plain text and preserve newlines with CSS.
- return ( - <Tooltip content={ <RawHTML>{ full }</RawHTML> }> - <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> - </p> - </Tooltip> - ); + return ( + <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> + <p className="m-0 space-x-2 flex flex-wrap max-w-40 leading-6 text-sm text-gray-600 whitespace-pre-line break-words"> + <span>{ truncate ? truncate( full, 120 ) : full }</span> + </p> + </Tooltip> + );
257-263: XSS: stop injecting Note with RawHTMLSame risk as Details; render as text.
- return ( - <Tooltip content={ <RawHTML>{ full }</RawHTML> }> - <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> - </p> - </Tooltip> - ); + return ( + <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> + <p className="m-0 space-x-2 flex flex-wrap max-w-40 leading-6 text-sm text-gray-600 whitespace-pre-line break-words"> + <span>{ truncate ? truncate( full, 120 ) : full }</span> + </p> + </Tooltip> + );
176-195: Normalize status and handle ‘cancelled’; show correct color/labelTreat backend
completedas UIapprovedand add a style forcancelled.- render: ( { item } ) => { - const statusColors = { - pending: 'bg-yellow-100 text-yellow-800', - approved: 'bg-green-100 text-green-800', - }; + render: ( { item } ) => { + const normalized = item.status === 'completed' ? 'approved' : item.status; + const statusColors = { + pending: 'bg-yellow-100 text-yellow-800', + approved: 'bg-green-100 text-green-800', + cancelled: 'bg-gray-100 text-gray-800', + }; return ( <span className={ `inline-flex items-center px-3.5 py-1.5 rounded-md text-xs font-medium ${ - statusColors[ item.status ] || + statusColors[ normalized ] || 'bg-gray-100 text-gray-800' }` } > - { - WITHDRAW_STATUSES.find( - ( status ) => status.value === item.status - )?.label - } + { WITHDRAW_STATUSES.find( (s) => s.value === normalized )?.label || normalized } </span> ); },
556-563: Fix REST path: add leading slashMissing
/can break requests on some installs.- const response = await apiFetch( { - path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + const response = await apiFetch( { + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ), headers: { 'Content-Type': 'application/json', }, parse: false, // Get raw response to access headers } );
1324-1332: XSS in modal: stop using RawHTMLRender details safely; preserve line breaks via CSS.
- <RawHTML> - { processDetails( - item.details, - item.method - ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
🧹 Nitpick comments (13)
src/admin/dashboard/pages/withdraw/index.tsx (6)
894-894: Localize placeholderWrap with
__()for i18n consistency.- placeholder="Payment Method" + placeholder={ __( 'Payment Method', 'dokan-lite' ) }
687-694: Remove dead code: unusedupdateData
updateDatais built but never used.- try { - const updateData = {}; - if ( action === 'approved' ) { - updateData.status = action; - } else if ( action === 'cancelled' ) { - updateData.status = action; - } + try {
1199-1203: Modal UX: confirm button says “Update” but does nothingEither change label to “Close” or hide confirm and keep Cancel.
- onConfirm={ closeModal } - dialogTitle={ <></> } - confirmButtonText={ __( 'Update', 'dokan-lite' ) } - hideCancelButton={ false } + onConfirm={ closeModal } + dialogTitle={ <></> } + confirmButtonText={ __( 'Close', 'dokan-lite' ) } + hideCancelButton={ true }
208-211: Nit: prefer nullish coalescing forchargeAvoid falling back when
chargeis 0.- { price( item.charge || 0 ) } + { price( item.charge ?? 0 ) }
639-683: Optional: clear polling timer on unmountPrevent orphaned timers if the component unmounts during export.
You can keep a ref to the timeout id and clear it in a cleanup:
// at top-level const exportTimerRef = useRef<number | null>( null ); // inside checkStatus setTimeout exportTimerRef.current = window.setTimeout( checkStatus, 5000 ); // on unmount useEffect(() => () => { if ( exportTimerRef.current ) { clearTimeout( exportTimerRef.current ); } }, []);
3-3: Drop RawHTML import after replacing usagesKeep imports minimal.
-import { RawHTML, useEffect, useState, useCallback } from '@wordpress/element'; +import { useEffect, useState, useCallback } from '@wordpress/element';includes/REST/WithdrawExportController.php (3)
104-107: Sanitize and bound pagination params.per_page/page should be absint and bounded to avoid huge offsets or negatives.
Apply:
- $args['limit'] = $request['per_page'] ?? 20; - $args['offset'] = ( ( $request['page'] ?? 1 ) - 1 ) * $args['limit']; + $limit = isset( $request['per_page'] ) ? max( 1, min( 100, absint( $request['per_page'] ) ) ) : 20; + $page = isset( $request['page'] ) ? max( 1, absint( $request['page'] ) ) : 1; + $args['limit'] = $limit; + $args['offset'] = ( $page - 1 ) * $limit;
130-135: Resilient pagination headers.If max_num_pages isn’t provided by all(), fall back to a calculated value.
Apply:
- $response->header( 'X-WP-TotalPages', $withdraws_obj->max_num_pages ); + $max_pages = isset( $withdraws_obj->max_num_pages ) ? (int) $withdraws_obj->max_num_pages : (int) ceil( $total_withdraws / max( 1, (int) $args['limit'] ) ); + $response->header( 'X-WP-TotalPages', $max_pages );
192-198: PHPMD “unused parameter” on $request.Harmless here. Consider adding a short inline ignore or underscore prefix to quiet linters.
Example:
/** @noinspection PhpUnusedParameterInspection */ // phpcs:ignore WordPressVIPMinimum.Hooks.StrictAvoidUnhookedFunctions public function get_items_permissions_check( $request ) {docs/export-controller-usage.md (3)
31-37: Fix rest_base example and declare namespace separately.rest_base shouldn’t include the namespace; keep it relative. This mirrors the controllers in code.
Apply:
class YourCustomExportController extends GenericController implements ExportableInterface { @@ - /** - * Route base for this controller - */ - protected $rest_base = 'dokan/v1/reports/your-report-type'; + /** + * Route base and namespace for this controller + */ + protected $namespace = 'dokan/v1'; + protected $rest_base = 'reports/your-report-type';
175-177: Add language to fenced block (MD040).Apply:
-``` +```http POST /wp-json/dokan/v1/reports/your-report-type/export -``` +```
193-195: Add language to fenced block (MD040).Apply:
-``` +```http GET /wp-json/dokan/v1/reports/your-report-type/export/{export_id}/status -``` +```includes/REST/ExportController.php (1)
68-74: PHPMD “unused parameter” warning.Benign. Optionally prefix $request with underscore or add an inline ignore to appease linters.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
docs/export-controller-usage.md(1 hunks)includes/REST/ExportController.php(1 hunks)includes/REST/Manager.php(3 hunks)includes/REST/WithdrawExportController.php(1 hunks)src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
includes/REST/ExportController.php (1)
includes/REST/WithdrawExportController.php (1)
get_items_permissions_check(192-198)
includes/REST/WithdrawExportController.php (3)
includes/Vendor/Vendor.php (1)
get_name(321-325)includes/Withdraw/Withdraw.php (6)
get_user_id(93-95)get_amount(104-106)get_date(115-117)get_method(137-139)get_details(159-161)get_note(148-150)includes/REST/ExportController.php (1)
get_items_permissions_check(68-74)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (5)
formatPrice(79-79)DokanTab(61-61)Filter(60-60)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
🪛 PHPMD (2.15.0)
includes/REST/ExportController.php
68-68: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
includes/REST/WithdrawExportController.php
192-192: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
🪛 markdownlint-cli2 (0.17.2)
docs/export-controller-usage.md
175-175: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
193-193: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (5)
includes/REST/WithdrawExportController.php (1)
154-160: Good: constrained status enum.The fixed enum reduces invalid queries and simplifies validation.
includes/REST/Manager.php (2)
188-197: Class load in filter callback is fine; keep require_once guarded.The guard avoids autoload assumptions. Looks good.
257-259: Registration in class map looks correct.ExportController and WithdrawExportController are discoverable for route registration.
docs/export-controller-usage.md (1)
246-253: Keep leading slash in custom endpoint example (matches Manager fix).Example already shows leading slash—good. Ensure code follows the same convention.
includes/REST/ExportController.php (1)
33-60: Route registration aligns with Woo export controller.Once rest_base is fixed, both routes look correct with proper permissions and schemas.
| * | ||
| * @var string | ||
| */ | ||
| protected $rest_base = '/reports/(?P<type>[a-z]+)/export'; |
There was a problem hiding this comment.
Remove leading slash from $rest_base to prevent double slashes in registered routes.
register_rest_route prepends '/', so rest_base should be relative.
Apply:
- protected $rest_base = '/reports/(?P<type>[a-z]+)/export';
+ protected $rest_base = 'reports/(?P<type>[a-z]+)/export';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| protected $rest_base = '/reports/(?P<type>[a-z]+)/export'; | |
| protected $rest_base = 'reports/(?P<type>[a-z]+)/export'; |
🤖 Prompt for AI Agents
In includes/REST/ExportController.php around line 26, the $rest_base currently
starts with a leading slash which causes register_rest_route to produce double
slashes; remove the leading slash so $rest_base is relative (e.g. change
"/reports/(?P<type>[a-z]+)/export" to "reports/(?P<type>[a-z]+)/export") and
verify any concatenation when registering the route expects a relative base.
| public function register_data_endpoint( string $endpoint, string $type ): string { | ||
| if ( 'withdraws' === $type ) { | ||
| return 'dokan/v1/reports/withdraws'; | ||
| } | ||
|
|
||
| return $endpoint; | ||
| } |
There was a problem hiding this comment.
Ensure data endpoint starts with a leading slash.
WooCommerce’s filter typically works with a leading-slash path (docs below also show this). Returning a bare path risks malformed URLs.
Apply:
- if ( 'withdraws' === $type ) {
- return 'dokan/v1/reports/withdraws';
- }
+ if ( 'withdraws' === $type ) {
+ return '/dokan/v1/reports/withdraws';
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public function register_data_endpoint( string $endpoint, string $type ): string { | |
| if ( 'withdraws' === $type ) { | |
| return 'dokan/v1/reports/withdraws'; | |
| } | |
| return $endpoint; | |
| } | |
| public function register_data_endpoint( string $endpoint, string $type ): string { | |
| if ( 'withdraws' === $type ) { | |
| return '/dokan/v1/reports/withdraws'; | |
| } | |
| return $endpoint; | |
| } |
🤖 Prompt for AI Agents
In includes/REST/Manager.php around lines 209-215, the register_data_endpoint
function returns paths without a leading slash which can produce malformed URLs;
update the function so the special-case path is returned as
'/dokan/v1/reports/withdraws' (with a leading slash) and normalize the general
return by ensuring $endpoint begins with a leading slash (prepend one if
missing) before returning it.
| protected $namespace = 'dokan/v1'; | ||
| /** | ||
| * Route base. | ||
| * | ||
| * @var string | ||
| */ | ||
| protected $rest_base = '/reports/withdraws'; | ||
|
|
There was a problem hiding this comment.
Remove leading slash from $rest_base to avoid route normalization quirks.
Using a leading slash in rest_base combined with register_rest_route patterns elsewhere can generate double slashes and rely on normalization. Keep rest_base relative.
Apply:
- protected $rest_base = '/reports/withdraws';
+ protected $rest_base = 'reports/withdraws';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| protected $namespace = 'dokan/v1'; | |
| /** | |
| * Route base. | |
| * | |
| * @var string | |
| */ | |
| protected $rest_base = '/reports/withdraws'; | |
| protected $namespace = 'dokan/v1'; | |
| /** | |
| * Route base. | |
| * | |
| * @var string | |
| */ | |
| protected $rest_base = 'reports/withdraws'; |
🤖 Prompt for AI Agents
In includes/REST/WithdrawExportController.php around lines 23 to 30, the
protected $rest_base currently starts with a leading slash which can cause
double-slash route issues; change it to be relative by removing the leading
slash (e.g. 'reports/withdraws') so register_rest_route constructs routes
consistently and avoid normalization quirks; update any usages or
documentation/comments accordingly.
There was a problem hiding this comment.
Pls check this feedback to remove the first the slash(/).
CC: @shohag121 via.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| public function prepare_item_for_export( $item ) { | ||
| $vendor = dokan()->vendor->get( $item['user_id'] ); | ||
|
|
||
| return array( | ||
| 'id' => $item['id'], | ||
| 'user_id' => $item['user_id'], | ||
| 'vendor_name' => $vendor->get_name(), | ||
| 'amount' => $item['amount'], | ||
| 'date' => $item['date'], | ||
| 'status' => $item['status'], | ||
| 'payment_method' => $item['method'], | ||
| 'details' => $item['details'], | ||
| 'note' => $item['note'], | ||
| ); |
There was a problem hiding this comment.
Null-safe vendor name resolution (avoid fatal on missing vendor).
dokan()->vendor->get(...) can be null/invalid. Calling get_name() unguarded risks fatals during export.
Apply:
- $vendor = dokan()->vendor->get( $item['user_id'] );
+ $vendor = dokan()->vendor->get( $item['user_id'] );
+ $vendor_name = ( $vendor && method_exists( $vendor, 'get_name' ) ) ? $vendor->get_name() : '';
...
- 'vendor_name' => $vendor->get_name(),
+ 'vendor_name' => $vendor_name,Optional: micro-cache vendor names to cut repeated lookups during large exports.
// e.g., static $vendor_names = []; $vendor_names[$id] ??= ...🤖 Prompt for AI Agents
In includes/REST/WithdrawExportController.php around lines 56-69, calling
dokan()->vendor->get($item['user_id'])->get_name() is unsafe because get(...)
can return null; guard against null before calling get_name() and fall back to
an empty string or placeholder (e.g., '') for vendor_name. Update the code to
fetch the vendor, check if it's truthy before calling get_name(), and assign a
default when absent; optionally implement a simple static micro-cache keyed by
user_id to avoid repeated get() calls during large exports.
# Conflicts: # src/admin/dashboard/style.scss
…t/dokan-widthraw-admin-list # Conflicts: # includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php # includes/REST/Manager.php
…raw admin UI - Created `AdminDataViewTable` component for reusable, filterable, and responsive data tables. - Integrated new data table in withdraw admin page for better UX. - Added new SCSS styles to support component styling. - Enhanced withdraw page functionality with improved tabs, export options, and filtering.
- Updated `handleBulkAction` to process only items with the `paypal` method before mapping their IDs.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (11)
src/admin/dashboard/pages/withdraw/index.tsx (9)
34-34: Remove RawHTML from price helper to prevent XSS risk.The
formatPriceutility returns plain text, makingRawHTMLunnecessary and dangerous. Render the formatted price as safe text instead.Apply this diff:
-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount ) => <span>{ formatPrice( amount ) }</span>;
147-167: Normalize backend 'completed' status to 'approved' for consistent UI.The backend may return
completedwhile your UI expectsapproved. Without normalization, items withcompletedstatus will display with incorrect colors and labels.Apply this diff:
render: ( { item } ) => { + const normalized = item.status === 'completed' ? 'approved' : item.status; const statusColors = { pending: 'bg-[#FDF2F8] text-[#9D174D]', approved: 'bg-[#D4FBEF] text-[#00563F]', }; return ( <span className={ `inline-flex items-center px-3.5 py-1.5 rounded-full text-xs font-medium ${ - statusColors[ item.status ] || + statusColors[ normalized ] || 'bg-[#F1F1F4] text-[#393939]' }` } > { WITHDRAW_STATUSES.find( - ( status ) => status.value === item.status + ( status ) => status.value === normalized )?.label } </span> ); },
189-192: Use nullish coalescing to handle receivable value of 0 correctly.When
item.receivableis0, the current||operator incorrectly falls back toitem.amount. Use??instead to only fall back when the value isnullorundefined.Apply this diff:
<div className="font-medium text-gray-900"> - { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) } </div>
212-219: Replace RawHTML with safe text rendering to prevent XSS.Using
RawHTMLfor both tooltip and cell content creates an XSS vulnerability. Render the details as plain text and use CSS to preserve formatting.Apply this diff:
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> + <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> + <span>{ truncate ? truncate( full, 120 ) : full }</span> </p> </Tooltip>
370-370: Account for 'completed' status in delete eligibility check.The backend may return
completedinstead ofapproved. The delete action should treat both as ineligible.Apply this diff:
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),
562-568: Add leading slash to REST path for consistent endpoint resolution.The
pathparameter toapiFetchshould start with/to ensure correct absolute endpoint resolution across different WordPress installations.Apply this diff:
const response = await apiFetch( { - path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ), headers: { 'Content-Type': 'application/json', }, parse: false, } );
1321-1328: Replace RawHTML with safe rendering to prevent XSS in modal.Injecting raw HTML to preserve line breaks is unsafe. Use CSS
whitespace-pre-lineor render line-separated elements instead.Apply this diff:
- <RawHTML> - { processDetails( - item.details, - item.method - ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
1373-1382: Account for 'completed' status when showing delete option.The backend may return
completedinstead ofapproved. The delete option should be hidden for both statuses.Apply this diff:
if ( - item.status !== 'approved' + ! [ 'approved', 'completed' ].includes( item.status ) ) { options.push( { label: __( 'Delete', 'dokan-lite' ), value: 'delete', } ); }
509-520: Fix REST path and avoid leaking internal view state in export.Two issues remain:
- The REST path lacks a leading slash, which can break requests on some installations
- Spreading the entire
viewobject sends internal keys (type, layout, titleField, etc.) as query paramsWhitelist only server-supported parameters before calling
addQueryArgs.Apply this diff:
onClick={ async () => { try { - const path = addQueryArgs( 'dokan/v2/withdraw', { - ...view, - is_export: true, - } ); + const exportArgs = { + per_page: view?.perPage ?? 10, + page: view?.page ?? 1, + search: view?.search ?? '', + status: view?.status === 'all' ? '' : view?.status, + ...filterArgs, + is_export: true, + ...(view?.sort?.field ? { orderby: view.sort.field } : {}), + ...(view?.sort?.direction ? { order: view.sort.direction } : {}), + }; + const path = addQueryArgs( '/dokan/v2/withdraw', exportArgs ); const res = await apiFetch( { path } );includes/REST/WithdrawExportController.php (2)
29-29: Remove leading slash from rest_base to avoid route normalization issues.Using a leading slash in
rest_basecan cause double-slash issues when combined withregister_rest_routepatterns. Keep it relative for consistent route construction.Apply this diff:
- protected $rest_base = '/reports/withdraws'; + protected $rest_base = 'reports/withdraws';
57-71: Add null-safe vendor name resolution to prevent fatal errors.
dokan()->vendor->get()can returnnullor an invalid object. Callingget_name()without a guard will cause a fatal error during export.Apply this diff:
public function prepare_item_for_export( $item ) { $vendor = dokan()->vendor->get( $item['user_id'] ); + $vendor_name = ( $vendor && method_exists( $vendor, 'get_name' ) ) + ? $vendor->get_name() + : ''; return array( 'id' => $item['id'], 'user_id' => $item['user_id'], - 'vendor_name' => $vendor->get_name(), + 'vendor_name' => $vendor_name, 'amount' => $item['amount'], 'payable' => $item['payable'], 'date' => $item['date'], 'status' => $item['status'], 'payment_method' => $item['method'], 'details' => $item['details'], 'note' => $item['note'], ); }
🧹 Nitpick comments (2)
package.json (2)
68-68: Pin GitHub dependency to a commit SHA for reproducible builds.
"@getdokan/dokan-ui": "github:getdokan/dokan-ui#dokan-plugin"is mutable. Pin to a specific commit/tag.
87-88: Heavy deps; consider size controls.
d3anddompurifycan bloat bundles. Prefer modular imports or dynamic import where feasible; ensure DOMPurify usage is constrained to trusted contexts.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
includes/REST/WithdrawExportController.php(1 hunks)package.json(3 hunks)src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)src/components/dataviews/AdminDataViewTable.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/dataviews/AdminDataViewTable.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
includes/REST/WithdrawExportController.php (3)
includes/Vendor/Vendor.php (1)
get_name(321-325)includes/Withdraw/Withdraw.php (7)
get_user_id(93-95)get_amount(104-106)get_receivable_amount(192-194)get_date(115-117)get_method(137-139)get_details(159-161)get_note(148-150)includes/REST/ExportController.php (1)
get_items_permissions_check(68-74)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (3)
formatPrice(79-79)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
🪛 GitHub Check: Run PHPCS inspection
includes/REST/WithdrawExportController.php
[warning] 200-200:
Found unknown capability "dokan_view_reports" in function call to current_user_can(). Please check the spelling of the capability. If this is a custom capability, please verify the capability is registered with WordPress via a call to WP_Role(s)->add_cap().
Custom capabilities can be made known to this sniff by setting the "custom_capabilities" property in the PHPCS ruleset.
🪛 PHPMD (2.15.0)
includes/REST/WithdrawExportController.php
199-199: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: api tests (1, 1)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
🔇 Additional comments (3)
package.json (3)
28-29: Tooling bump looks good; confirm environment compatibility.Updates to @wordpress/scripts and dependency-extraction are fine. Please confirm Node version and builds locally/CI with the new toolchain.
59-59: Dev tool addition is fine.
wp-readme-to-markdownas a devDependency is appropriate.
70-86: Verify WordPress Core compatibility and extraction.These @WordPress packages are fairly recent. Ensure:
- Site minimum WP Core provides matching script handles (esp.
@wordpress/dataviews,components,data).- Dependency extraction is enabled and enqueues correct core assets.
Based on learnings
| "tailwind-merge": "^2.6.0", | ||
| "tailwind-scrollbar-hide": "^4.0.0", |
There was a problem hiding this comment.
Duplicate tailwind-merge (dev vs prod) with mismatched versions.
Present in devDependencies (^2.6.0) and dependencies (^2.5.5 at line 96). Keep only one (prefer dependencies if used at runtime) and align versions.
Apply:
"devDependencies": {
- "tailwind-merge": "^2.6.0",
"tailwind-scrollbar-hide": "^4.0.0",
},And bump the runtime one for consistency:
- "tailwind-merge": "^2.5.5",
+ "tailwind-merge": "^2.6.0",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "tailwind-merge": "^2.6.0", | |
| "tailwind-scrollbar-hide": "^4.0.0", | |
| "devDependencies": { | |
| - "tailwind-merge": "^2.6.0", | |
| "tailwind-scrollbar-hide": "^4.0.0", | |
| }, | |
| "dependencies": { | |
| "tailwind-merge": "^2.6.0", | |
| // …other dependencies | |
| }, |
🤖 Prompt for AI Agents
In package.json around lines 42-43 and 96, there is a duplicate tailwind-merge
entry with mismatched versions; remove the duplicate and align versions. If
tailwind-merge is required at runtime keep it in "dependencies" and delete it
from "devDependencies"; set the remaining dependency version to "^2.6.0" (bump
the runtime entry to ^2.6.0 if it's the one kept) so both places are consistent,
then run npm/yarn install to update lockfile.
| "d3": "^7.9.0", | ||
| "dompurify": "^3.2.6", |
There was a problem hiding this comment.
🧩 Analysis chain
React peer deps missing for added React-based libs.
lucide-react, react-router-dom, qrcode.react, and usehooks-ts require react (and react-dom). Neither is listed. Builds may fail or bundle a duplicate React.
Options:
- Prefer WordPress-native alternatives (e.g.,
@wordpress/iconsinstead oflucide-react; avoidreact-router-domif not bundling React), or - Add and align React deps with Core, then configure externals/alias to avoid duplicates.
If proceeding, add:
"dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
"react-router-dom": "^6.27.0",
"usehooks-ts": "^3.1.0"
}And ensure webpack/dependency-extraction maps React correctly (or you’ll ship a second React).
Also applies to: 89-93, 97-97
🏁 Script executed:
#!/bin/bash
# Check if react/react-dom are declared
jq '.dependencies.react, .dependencies["react-dom"]' package.json
# Locate imports that require React presence
rg -nP --type=ts --type=tsx --type=js --type=jsx -C2 \
-e 'from\s+[\"\']react[\"\']' \
-e 'from\s+[\"\']react-router-dom[\"\']' \
-e 'from\s+[\"\']lucide-react[\"\']' \
-e 'from\s+[\"\']qrcode\.react[\"\']' \
-e 'usehooks-ts'Length of output: 79
🏁 Script executed:
#!/bin/bash
echo "Dependencies:"
jq '.dependencies.react, .dependencies["react-dom"]' package.json
echo "DevDependencies:"
jq '.devDependencies.react, .devDependencies["react-dom"]' package.json
echo "Searching for React-related imports:"
for pkg in react react-router-dom lucide-react qrcode.react usehooks-ts; do
echo "=== $pkg ==="
rg -nP --type=js --type=jsx --type=ts --type=tsx -C2 "from\s+['\"]$pkg['\"]" || true
doneLength of output: 328
🏁 Script executed:
#!/bin/bash
echo "Fallback search for React-related imports:"
for pkg in react react-router-dom lucide-react "qrcode.react" usehooks-ts; do
echo "=== $pkg ==="
rg -n -e "import .* from ['\"]$pkg['\"]" -C2 --glob '*.{js,jsx,ts,tsx}' || true
rg -n -e "require\(['\"]$pkg['\"]\)" -C2 --glob '*.{js,jsx,ts,tsx}' || true
doneLength of output: 16052
Add React and React-DOM dependencies and configure externals
- Add
"react": "^18.2.0"and"react-dom": "^18.2.0"to your package.json dependencies. - Configure your bundler (webpack dependency-extraction or externals/alias) to avoid shipping a duplicate React instance.
🤖 Prompt for AI Agents
In package.json around lines 87-88 add "react": "^18.2.0" and "react-dom":
"^18.2.0" to dependencies; then update your bundler config to prevent shipping a
duplicate React instance by marking react and react-dom as externals (or using
the dependency-extraction plugin) or by setting resolve.alias to a single path
to your installed react (ensuring both consumers use the same version), and
confirm any peerDependencies or host app also use the same 18.2.0 version to
avoid multiple React copies.
| const fetchWithdraws = async () => { | ||
| setIsLoading( true ); | ||
| try { | ||
| const queryArgs = { | ||
| per_page: view?.perPage ?? 20, | ||
| page: view?.page ?? 1, | ||
| search: view?.search ?? '', | ||
| status: view?.status === 'all' ? '' : view?.status, | ||
| ...filterArgs, | ||
| }; | ||
|
|
||
| // Handle sorting | ||
| if ( view?.sort?.field ) { | ||
| queryArgs.orderby = view.sort.field; | ||
| } | ||
| if ( view?.sort?.direction ) { | ||
| queryArgs.order = view.sort.direction; | ||
| } | ||
|
|
||
| // Handle filters | ||
| if ( view?.filters ) { | ||
| view.filters.forEach( ( filter ) => { | ||
| if ( | ||
| filter.field === 'status' && | ||
| filter.operator === 'isAny' | ||
| ) { | ||
| queryArgs.status = filter.value?.join( ',' ); | ||
| } | ||
| } ); | ||
| } | ||
|
|
||
| // Fetch data from the REST API | ||
| const response = await apiFetch( { | ||
| path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| parse: false, // Get raw response to access headers | ||
| } ); | ||
|
|
||
| const data = await response.json(); | ||
| const totalItems = parseInt( | ||
| response.headers.get( 'X-WP-Total' ) || 0 | ||
| ); | ||
|
|
||
| setTotalItems( totalItems ); | ||
| setData( data ); | ||
|
|
||
| // Extract status counts from response headers | ||
| const pendingCount = parseInt( | ||
| response.headers.get( 'X-Status-Pending' ) || 0 | ||
| ); | ||
| const completedCount = parseInt( | ||
| response.headers.get( 'X-Status-Completed' ) || 0 | ||
| ); | ||
| const cancelledCount = parseInt( | ||
| response.headers.get( 'X-Status-Cancelled' ) || 0 | ||
| ); | ||
|
|
||
| const counts = { | ||
| all: pendingCount + completedCount + cancelledCount, | ||
| pending: pendingCount, | ||
| approved: completedCount, // 'approved' maps to 'completed' in backend | ||
| cancelled: cancelledCount, | ||
| }; | ||
| setStatusCounts( counts ); | ||
| } catch ( error ) { | ||
| console.error( 'Error fetching withdraws:', error ); | ||
| } finally { | ||
| setIsLoading( false ); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Memoize fetchWithdraws to prevent stale closures and duplicate requests.
The fetchWithdraws function is recreated on every render and can capture stale values of filterArgs. Wrap it in useCallback and update the useEffect dependency to use the memoized version.
Apply this diff:
- const fetchWithdraws = async () => {
+ const fetchWithdraws = useCallback( async () => {
setIsLoading( true );
try {
const queryArgs = {
per_page: view?.perPage ?? 20,
page: view?.page ?? 1,
search: view?.search ?? '',
status: view?.status === 'all' ? '' : view?.status,
...filterArgs,
};
// ... rest of function
} finally {
setIsLoading( false );
}
- };
+ }, [ view, filterArgs ] );
// Fetch withdraws when view changes
useEffect( () => {
fetchWithdraws();
- }, [ view ] );
+ }, [ fetchWithdraws ] );Also applies to: 822-824
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (13)
src/admin/dashboard/pages/withdraw/index.tsx (7)
34-34: Remove unnecessary RawHTML wrapper from price function.
formatPricereturns plain text; wrapping it inRawHTMLis both unnecessary and poses an XSS risk if the function output ever includes user-supplied data.Apply this diff:
-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount ) => formatPrice( amount );
190-190: Use nullish coalescing to handle zero receivable correctly.When
receivableis0, the current||operator incorrectly falls back toamount. Use??instead.Apply this diff:
- { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) }
303-303: Account for 'completed' status in action eligibility checks.The backend may return
completedinstead ofapproved. Treat both as equivalent in eligibility logic.Apply this diff at line 303:
- isEligible: ( item ) => item?.status === 'pending', + isEligible: ( item ) => item?.status === 'pending',Apply this diff at line 325:
- isEligible: ( item ) => item?.status === 'pending', + isEligible: ( item ) => item?.status === 'pending',Apply this diff at line 370:
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Also applies to: 325-325, 370-370
564-564: Add missing leading slash to REST path.The REST path must start with
/for correct endpoint resolution on all WordPress installations.Apply this diff:
const response = await apiFetch( { - path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ), headers: {
1323-1330: Remove RawHTML from modal; render details safely.Injecting HTML via
RawHTMLfor payment details is unsafe. Render as text with CSS line-break preservation.Apply this diff:
- <RawHTML> - { processDetails( - item.details, - item.method - ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
1374-1376: Account for 'completed' in modal delete eligibility.The backend may return
completedinstead ofapproved. Hide delete for both statuses.Apply this diff:
if ( - item.status !== 'approved' + ! [ 'approved', 'completed' ].includes( item.status ) ) {
147-166: Normalize backend 'completed' status to 'approved' for UI display.The backend may return
completedinstead ofapproved, causing blank labels and missing color styling.Apply this diff:
render: ( { item } ) => { + const normalized = item.status === 'completed' ? 'approved' : item.status; const statusColors = { pending: 'bg-[#FDF2F8] text-[#9D174D]', approved: 'bg-[#D4FBEF] text-[#00563F]', + cancelled: 'bg-[#F1F1F4] text-[#393939]', }; return ( <span className={ `inline-flex items-center px-3.5 py-1.5 rounded-full text-xs font-medium ${ - statusColors[ item.status ] || + statusColors[ normalized ] || 'bg-[#F1F1F4] text-[#393939]' }` } > { - WITHDRAW_STATUSES.find( - ( status ) => status.value === item.status - )?.label + WITHDRAW_STATUSES.find( ( s ) => s.value === normalized )?.label || normalized } </span> ); },src/components/dataviews/AdminDataViewTable.tsx (4)
19-19: Avoid importing from internal build-types paths.Importing from
build-types/couples code to internal implementation details that may change without notice across package versions.Apply this diff:
-import { Item } from '@wordpress/components/build-types/navigation/types'; +// Define Item type locally or import from a stable public API +type Item = { id: string; [key: string]: any };
123-133: Fix Rules of Hooks violation: hoist useEffect unconditionally.Calling
useEffectinsideif (responsive)violates React's Rules of Hooks. The effect also usesview/onChangeViewwithout proper dependencies, causing stale closures.Apply this diff:
- if ( responsive ) { - // Set view type `list` for mobile device. - useEffect( - () => - onChangeView( { - ...view, - type: windowWidth <= 768 ? 'list' : 'table', - } ), - [ windowWidth ] - ); - } + useEffect( () => { + if ( ! responsive ) { + return; + } + const nextType = windowWidth <= 768 ? 'list' : 'table'; + onChangeView( ( prevView ) => + prevView.type === nextType + ? prevView + : { ...prevView, type: nextType } + ); + }, [ responsive, windowWidth, onChangeView ] );
149-152: Use namespace-aware key for filter button uniqueness.The hardcoded key
'dokan_admin_dashboard_filter_button'will cause collisions if multipleAdminDataViewTableinstances with different namespaces render on the same page.Apply this diff:
const hasButton = existing.some( ( comp: any ) => comp && - comp.key === 'dokan_admin_dashboard_filter_button' + comp.key === `dokan_admin_${ tableNameSpace }_filter_button` );And update the button key:
const newButton = ( <button type="button" ref={ setButtonRef } - key="dokan_admin_dashboard_filter_button" + key={ `dokan_admin_${ tableNameSpace }_filter_button` } title={ __( 'Filter', 'dokan-lite' ) }Also applies to: 164-164
193-195: Remove or replace literal {item_name} placeholder in data-filter-id.The
data-filter-idcontains a literal{item_name}placeholder that will render verbatim instead of being substituted.Apply this diff to remove it:
data-filter-id={ `dokan_admin_${ snakeCase( namespace - ) }_dataviews_{item_name}` } + ) }_dataviews` }includes/REST/WithdrawExportController.php (2)
29-29: Remove leading slash from $rest_base to avoid route normalization issues.Using a leading slash in
rest_basecombined withregister_rest_routecan generate double slashes. Keep it relative.Apply this diff:
- protected $rest_base = '/reports/withdraws'; + protected $rest_base = 'reports/withdraws';
57-63: Add null-safety check for vendor before calling get_name().
dokan()->vendor->get( $item['user_id'] )can returnnullor an invalid vendor object. Callingget_name()unguarded risks fatal errors during export.Apply this diff:
public function prepare_item_for_export( $item ) { $vendor = dokan()->vendor->get( $item['user_id'] ); + $vendor_name = ( $vendor && method_exists( $vendor, 'get_name' ) ) ? $vendor->get_name() : ''; return array( 'id' => $item['id'], 'user_id' => $item['user_id'], - 'vendor_name' => $vendor->get_name(), + 'vendor_name' => $vendor_name, 'amount' => $item['amount'],
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
includes/REST/WithdrawExportController.php(1 hunks)package.json(3 hunks)src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)src/components/dataviews/AdminDataViewTable.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
includes/REST/WithdrawExportController.php (3)
includes/Vendor/Vendor.php (1)
get_name(321-325)includes/Withdraw/Withdraw.php (7)
get_user_id(93-95)get_amount(104-106)get_receivable_amount(192-194)get_date(115-117)get_method(137-139)get_details(159-161)get_note(148-150)includes/REST/ExportController.php (1)
get_items_permissions_check(68-74)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (3)
formatPrice(79-79)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
src/components/dataviews/AdminDataViewTable.tsx (3)
src/components/AdminFilter.tsx (2)
Field(12-16)AdminFilterProps(18-39)types/externals.d.ts (3)
ViewportDimensions(90-90)kebabCase(81-81)DataViews(59-59)src/components/AdminTab.tsx (1)
AdminTabProps(17-29)
🪛 GitHub Check: Run PHPCS inspection
includes/REST/WithdrawExportController.php
[warning] 200-200:
Found unknown capability "dokan_view_reports" in function call to current_user_can(). Please check the spelling of the capability. If this is a custom capability, please verify the capability is registered with WordPress via a call to WP_Role(s)->add_cap().
Custom capabilities can be made known to this sniff by setting the "custom_capabilities" property in the PHPCS ruleset.
🪛 PHPMD (2.15.0)
includes/REST/WithdrawExportController.php
199-199: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: api tests (1, 1)
| "@wordpress/dependency-extraction-webpack-plugin": "^6.32.0", | ||
| "@wordpress/scripts": "^30.25.0", |
There was a problem hiding this comment.
🧩 Analysis chain
Verify WordPress package versions exist and are compatible.
Several WordPress packages specify versions like ^6.32.0, ^7.32.0, ^30.5.0 which don't match official WordPress release patterns. WordPress 6.x releases are typically 6.5–6.8.
Please verify these package versions are valid:
Also applies to: 71-86
🌐 Web query:
What are the latest stable versions of @wordpress/dependency-extraction-webpack-plugin, @wordpress/scripts, @wordpress/api-fetch, @wordpress/components, and @wordpress/dataviews packages on npm?
💡 Result:
Here are the latest stable npm versions I found:
- @wordpress/dependency-extraction-webpack-plugin — 6.30.0. [1]
- @wordpress/scripts — 30.23.0 (tagged "latest"). [2]
- @wordpress/api-fetch — 7.30.0. [3]
- @wordpress/components — 30.3.0. [4]
- @wordpress/dataviews — 7.0.0. [5]
Sources:
- npm package page for @wordpress/dependency-extraction-webpack-plugin.
- npm versions/tags listing for @wordpress/scripts.
- npm package page for @wordpress/api-fetch.
- npm package page for @wordpress/components.
- npm package page for @wordpress/dataviews.
Align WordPress package versions to published npm releases
- @wordpress/dependency-extraction-webpack-plugin: ^6.32.0 → ^6.30.0
- @wordpress/scripts: ^30.25.0 → ^30.23.0
- @wordpress/api-fetch: ^7.32.0 → ^7.30.0
- @wordpress/components: ^30.5.0 → ^30.3.0
- @wordpress/dataviews: ^7.32.0 → ^7.0.0
| "tailwind-merge": "^2.6.0", | ||
| "tailwind-scrollbar-hide": "^4.0.0", |
There was a problem hiding this comment.
Critical: Remove duplicate tailwind-merge from devDependencies.
tailwind-merge is listed in both devDependencies (v2.6.0) and dependencies (v2.5.5), creating version conflicts. Keep it only in dependencies since it's used at runtime.
Apply this diff:
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@wordpress/dependency-extraction-webpack-plugin": "^6.32.0",
"@wordpress/scripts": "^30.25.0",
"chartjs-adapter-moment": "^1.0.1",
"debounce": "^1.2.1",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"fs-extra": "^10.1.0",
"jed": "^1.1.1",
"less": "^4.1.3",
"less-loader": "^6.0.0",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.7.6",
"papaparse": "^5.4.1",
"replace-in-file": "^6.3.5",
- "tailwind-merge": "^2.6.0",
- "tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^3.3.3",Also applies to: 96-96
🤖 Prompt for AI Agents
In package.json around lines 42-43 (and also line 96), there is a duplicate
"tailwind-merge" entry in devDependencies (v2.6.0) while dependencies already
contains v2.5.5; remove the "tailwind-merge" entry from devDependencies so it
exists only under dependencies (keep the runtime dependency as-is), then update
package-lock.json/yarn.lock by running the package manager install to ensure the
lockfile reflects the single entry.
| <Tooltip content={ <RawHTML>{ full }</RawHTML> }> | ||
| <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> | ||
| <RawHTML> | ||
| { truncate ? truncate( full, 120 ) : full } | ||
| </RawHTML> | ||
| </p> | ||
| </Tooltip> |
There was a problem hiding this comment.
Replace RawHTML with safe text rendering in tooltips.
Using RawHTML for user-supplied payment details and notes creates XSS vulnerabilities. Render as plain text and preserve line breaks with CSS.
Apply this diff for details (lines 212-218):
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }>
+ <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }>
<p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600">
- <RawHTML>
- { truncate ? truncate( full, 120 ) : full }
- </RawHTML>
+ <span>{ truncate ? truncate( full, 120 ) : full }</span>
</p>
</Tooltip>Apply this diff for notes (lines 228-235):
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }>
+ <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }>
<p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600">
- <RawHTML>
- { truncate ? truncate( full, 120 ) : full }
- </RawHTML>
+ <span>{ truncate ? truncate( full, 120 ) : full }</span>
</p>
</Tooltip>Also applies to: 228-235
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/withdraw/index.tsx around lines 212-218 and
228-235, the Tooltip currently uses RawHTML to render user-supplied payment
details and notes which causes XSS risk; replace RawHTML with plain text
rendering and<codegen_instructions>
In src/admin/dashboard/pages/withdraw/index.tsx around lines 212-218 and
228-235, the Tooltip currently uses RawHTML to render user-supplied payment
details and notes which causes XSS risk; replace RawHTML with plain text
rendering (no dangerously-set HTML), render the possibly truncated text
directly, and preserve line breaks using CSS (e.g. a class that applies
white-space: pre-wrap or tailwind’s whitespace-pre-wrap) so content is escaped
and line breaks are kept in both the tooltip content and the <p> display.
| protected $namespace = 'dokan/v1'; | ||
| /** | ||
| * Route base. | ||
| * | ||
| * @var string | ||
| */ | ||
| protected $rest_base = '/reports/withdraws'; | ||
|
|
There was a problem hiding this comment.
Pls check this feedback to remove the first the slash(/).
CC: @shohag121 via.
|
Note: This PR has multiple PR dependencies, so make sure this is marged now. |
|
@shohag121 This PR trigger a fatal error: ` |
- Added `selectedAction` state to manage action selections in the withdraw modal. - Improved action initialization for the "view" modal based on item status. - Simplified conditional rendering and cleaned up modal structure for better readability. - Refined button handling, ensuring appropriate action execution and modal behavior.
|
@dev-shahed Bhai, Updated the PR resolving all the above concerns. |
- Extended `supportedKeys` filter to include 'withdraw' for better panel switch handling.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (14)
src/admin/dashboard/pages/withdraw/index.tsx (14)
34-34: Replace RawHTML with plain text rendering for prices to prevent XSS.
formatPricereturns plain text (string or number), soRawHTMLis unnecessary and introduces XSS risk if the price value is ever user-controlled or manipulated.Apply this diff:
-const price = ( amount ) => <RawHTML>{ formatPrice( amount ) }</RawHTML>; +const price = ( amount ) => <span>{ formatPrice( amount ) }</span>;
147-167: Normalize 'completed' status to 'approved' for consistent UI rendering.The backend may return
status: 'completed', but the UI only defines colors and labels for 'pending', 'approved', and 'cancelled'. When 'completed' is returned, the status badge will show blank or default styling.Apply this diff:
render: ( { item } ) => { + const normalizedStatus = item.status === 'completed' ? 'approved' : item.status; const statusColors = { pending: 'bg-[#FDF2F8] text-[#9D174D]', approved: 'bg-[#D4FBEF] text-[#00563F]', + cancelled: 'bg-[#F1F1F4] text-[#393939]', }; return ( <span className={ `inline-flex items-center px-3.5 py-1.5 rounded-full text-xs font-medium ${ - statusColors[ item.status ] || + statusColors[ normalizedStatus ] || 'bg-[#F1F1F4] text-[#393939]' }` } > { WITHDRAW_STATUSES.find( - ( status ) => status.value === item.status - )?.label + ( status ) => status.value === normalizedStatus + )?.label || normalizedStatus } </span> ); },
189-191: Use nullish coalescing to avoid incorrect fallback when receivable is 0.When
item.receivableis0, the||operator will incorrectly fall back toitem.amount. Use??to only fall back when the value isnullorundefined.Apply this diff:
- { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) }
212-218: Replace RawHTML with safe text rendering to prevent XSS in payment details.User-supplied payment details are rendered using
RawHTML, which creates an XSS vulnerability. Render as plain text and use CSS to preserve line breaks.Apply this diff:
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> + <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> + <span>{ truncate ? truncate( full, 120 ) : full }</span> </p> </Tooltip>
228-235: Replace RawHTML with safe text rendering to prevent XSS in notes.User-supplied notes are rendered using
RawHTML, creating an XSS vulnerability. Render as plain text and use CSS to preserve line breaks.Apply this diff:
- <Tooltip content={ <RawHTML>{ full }</RawHTML> }> + <Tooltip content={ <span className="whitespace-pre-line">{ full }</span> }> <p className="m-0 space-x-2 flex flex-wrap max-w-40 text-wrap leading-6 text-sm text-gray-600"> - <RawHTML> - { truncate ? truncate( full, 120 ) : full } - </RawHTML> + <span>{ truncate ? truncate( full, 120 ) : full }</span> </p> </Tooltip>
249-263: Guard against undefined icon to prevent crashes.
LucideIcons[iconName]can returnundefinedfor unknown icon names, which will crash when attempting to render<Icon />.Apply this diff:
const Icon = LucideIcons[ iconName ]; + if ( ! Icon ) { + return <span>{ label }</span>; + } return ( <div className="dokan-layout"> <span className="inline-flex items-center gap-2.5"> <Icon size={ 16 } className="!fill-none" /> { label } </span> </div> );
349-349: Account for 'completed' status in action eligibility checks.The backend may return
status: 'completed'instead of'approved'. Actions that should be disabled for approved withdrawals must also exclude 'completed' status.Apply this diff at line 349:
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Apply this diff at line 370:
- isEligible: ( item ) => item?.status !== 'approved', + isEligible: ( item ) => ! [ 'approved', 'completed' ].includes( item?.status ),Also applies to: 370-370
493-495: Fix fields mapping: filter out vendor field instead of using empty string.Passing an empty string
''as a field id can break DataViews column rendering. Filter out the 'vendor' field explicitly.Apply this diff:
- fields: fields.map( ( field ) => - field.id !== 'vendor' ? field.id : '' - ), + fields: fields + .filter( ( field ) => field.id !== 'vendor' ) + .map( ( field ) => field.id ),
526-542: Fix REST path and avoid leaking internal view state in export query.The REST path is missing a leading slash (which can break requests), and the entire
viewobject is being spread into query params, including internal keys liketype,layout,titleField, andfieldsthat the backend doesn't expect.Apply this diff:
onClick={ async () => { try { - // Minimal placeholder; backend export flow may vary. - // Attempt to hit export endpoint via same query params. - const path = addQueryArgs( 'dokan/v2/withdraw', { - ...view, - is_export: true, - } ); + const exportArgs = { + per_page: view?.perPage ?? 10, + page: view?.page ?? 1, + search: view?.search ?? '', + status: view?.status === 'all' ? '' : view?.status, + ...filterArgs, + is_export: true, + ...(view?.sort?.field ? { orderby: view.sort.field } : {}), + ...(view?.sort?.direction ? { order: view.sort.direction } : {}), + }; + const path = addQueryArgs( '/dokan/v2/withdraw', exportArgs ); const res = await apiFetch( { path } );
583-583: Fix REST path: add leading slash to prevent broken requests.
apiFetchexpects thepathto start with/. Missing the leading slash can cause requests to fail on some WordPress installations.Apply this diff:
- path: addQueryArgs( 'dokan/v2/withdraw', queryArgs ), + path: addQueryArgs( '/dokan/v2/withdraw', queryArgs ),
550-621: Memoize fetchWithdraws to prevent stale closures and unnecessary re-renders.The
fetchWithdrawsfunction is recreated on every render and can capture stale values offilterArgs. Wrap it inuseCallbackand update theuseEffectdependency.Apply this diff:
- const fetchWithdraws = async () => { + const fetchWithdraws = useCallback( async () => { setIsLoading( true ); try { const queryArgs = { per_page: view?.perPage ?? 20, page: view?.page ?? 1, search: view?.search ?? '', status: view?.status === 'all' ? '' : view?.status, ...filterArgs, }; // ... rest of function body } finally { setIsLoading( false ); } - }; + }, [ view, filterArgs ] ); // Fetch withdraws when view changes useEffect( () => { fetchWithdraws(); - }, [ view ] ); + }, [ fetchWithdraws ] );Also applies to: 843-845
1230-1232: Account for 'completed' status when showing delete action in view modal.The backend may return
status: 'completed'instead of'approved'. The delete option should be hidden for both statuses.Apply this diff:
- if ( item.status !== 'approved' ) { + if ( ! [ 'approved', 'completed' ].includes( item.status ) ) { options.push( { label: __( 'Delete', 'dokan-lite' ), value: 'delete' } ); }
1360-1364: Replace RawHTML with safe text rendering in modal to prevent XSS.Using
RawHTMLto render user-supplied payment details in the modal creates an XSS vulnerability. Render as plain text and preserve line breaks with CSS.Apply this diff:
- <RawHTML> - { processDetails( item.details, item.method ) - .split( '\n' ) - .join( '<br />' ) } - </RawHTML> + <span className="whitespace-pre-line"> + { processDetails( item.details, item.method ) } + </span>
1344-1344: Use nullish coalescing in modal to avoid incorrect fallback when receivable is 0.When
item.receivableis0, the||operator will incorrectly fall back toitem.amount. Use??to only fall back when the value isnullorundefined.Apply this diff:
- { price( item.receivable || item.amount ) } + { price( item.receivable ?? item.amount ) }
🧹 Nitpick comments (2)
src/admin/dashboard/pages/withdraw/index.tsx (2)
655-660: Consider using a toast notification instead of alert() for better UX.Using
alert()blocks the UI and provides a poor user experience. Consider using a toast notification system for error messages.Also applies to: 710-710
944-944: Consider disabling future dates in the date range picker.The
isInvalidDateprop always returnsfalse, allowing users to select future dates. For withdraw history filtering, future dates are typically not valid.Apply this diff:
- isInvalidDate={ () => false } + isInvalidDate={ ( date ) => { + const today = new Date(); + today.setHours( 23, 59, 59, 999 ); + return date > today; + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/admin/dashboard/pages/withdraw/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/admin/dashboard/pages/withdraw/index.tsx (3)
src/utilities/Accounting.ts (1)
formatPrice(3-62)types/externals.d.ts (3)
formatPrice(79-79)DataViews(59-59)DokanModal(65-65)src/utilities/Truncate.ts (1)
truncate(1-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: e2e tests (2, 3)
🔇 Additional comments (1)
src/admin/dashboard/pages/withdraw/index.tsx (1)
36-73: LGTM!The
processDetailsfunction correctly handles different payment methods and returns plain text strings.
* feat: add DokanSelect component with customizable styles and indicators * enhance: update DokanSelect component to improve cursor styles for indicators and options * enhance: update indicator separator visibility in DokanSelect component * enhance: improve component composition and style precedence in DokanSelect * feat: add DokanAsyncSelect and VendorAsyncSelect components for enhanced vendor selection * feat: add ProductAsyncSelect component for enhanced product selection * feat: add WcDateRangePicker component for date range selection * feat: add WpDateTimePicker component for date and time selection * feat: refactor ProductAsyncSelect for improved loading and option handling * feat: add OrderAsyncSelect component for enhanced order selection * feat: add SearchInput component for enhanced search functionality * feat: add CouponAsyncSelect component for coupon selection * feat: add Test component with various Dokan UI elements and functionality * feat: remove DokanSelect export from index.tsx * feat: remove DokanSelect component from Test.tsx * feat: enhance Test component with ProductAsyncSelect and VendorAsyncSelect * feat: remove unused imports from Test component * feat: rename DokanAsyncSelect to AsyncSelect and update imports * feat: rename DokanAsyncSelect to AsyncSelect and update imports * feat: add Select component and update AsyncSelect usage in Test component * feat: refactor AsyncSelect and related components to use BaseSelectProps for improved type safety * feat: enable multi-select functionality in Test component * feat: integrate SimpleInput for date input in WcDateRangePicker, WpDatePicker, and WpDateTimePicker components * feat: add documentation for AsyncSelect, CouponAsyncSelect, OrderAsyncSelect, ProductAsyncSelect, VendorAsyncSelect, WpDateTimePicker, SearchInput, and Select components * feat: add prefetch and strict validation options to AsyncSelect components * feat: update AsyncSelect components to improve fetching logic and add unique instance IDs * feat: enhance AsyncSelect components with unique merging logic for prefetched options * feat: implement skip merging logic for AsyncSelect components on dependency changes * feat: replace Test component with Status component in routing * feat: add Vendors management page and related components * feat: enhance vendor admin list with avatar display and action buttons * feat: update vendor admin list with improved URL handling and status display * feat: add confirmation modal for vendor approval and disable actions * feat: add selection state management for bulk actions in vendor admin list * feat: rename WcDateRangePicker and WpDateTimePicker components to DateRangePicker and DateTimePicker * feat: enhance AsyncSelect components with Tailwind CSS styling and improve option label formatting * feat: update documentation for Props API and enhance WpDatePicker with unique instance IDs * feat: enhance AsyncSelect component with icon support and update documentation * feat: update ProductAsyncSelect endpoint and enhance SearchInput with stable debouncing * feat: remove onClose handler from DateRangePicker component * feat: replace Test component with Status in Dokan routes * Finish select components * feat: enhance AsyncSelect components with onMenuOpen handling and loading optimization * feat: add filter button and toggle functionality to Vendors page * feat: add seller badge and subscription filters to Vendors page * feat: improve loading states and optimize rendering for vendor fields * feat: add SellerBadgeSelect component and update vendor list layout * feat: enhance vendor filtering with dynamic fields and improved query handling * refactor: enhance vendor admin list with dynamic filters and improved UI elements - Updated filters to allow dynamic field injection and better customization. - Improved UI for status tabs and filters with consistent theming. - Refactored filter logic and replaced redundant methods for better clarity. - Integrated search and tab selection into the main table component for enhanced usability. * refactor: remove unused components and redundant state logic in Vendors page - Removed `DokanTab` and `Filter` components. - Deleted `showFilters` state and unnecessary `useEffect` for filter visibility. - Simplified and cleaned up markup and filter methods for improved readability. * feat: add 'vendors' to supported keys in PanelSwitch filter * refactor: Remove unused `shouldRender` state and legacy redirect logic from PanelSwitch and LegacySwitcher * fix: update vendor navigation path in Vendors page * refactor: replace `wp.hooks.applyFilters` with `applyFilters` and update `wp_localize_script` usage - Used `@wordpress/hooks`'s `applyFilters` for consistency. - Replaced `wp_localize_script` with `wp_add_inline_script` for better script handling. --------- Co-authored-by: Md Asif Hossain Nadim <devianadim@gmail.com>
All Submissions:
Changes proposed in this Pull Request:
Related Pull Request(s)
Closes
How to test the changes in this Pull Request:
Changelog entry
Title
Detailed Description of the pull request. What was previous behaviour
and what will be changed in this PR.
Before Changes
Describe the issue before changes with screenshots(s).
After Changes
Describe the issue after changes with screenshot(s).
Feature Video (optional)
Link of detailed video if this PR is for a feature.
PR Self Review Checklist:
FOR PR REVIEWER ONLY:
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Documentation