New reverse withdrawal react#2970
Conversation
WalkthroughAdds a Reverse Withdrawal admin feature: new PHP page class and service registration, makes REST trn_date optional with a default, introduces React routes/pages (list + transaction), add-modal, DataView header support, SCSS tweaks, panel-switcher key, and a modal prop for confirm disabling. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Admin
participant UI as ReverseWithdrawalPage
participant Modal as AddReverseWithdrawModal
participant API as dokan/v1 API
rect rgba(0,128,96,0.06)
Admin->>UI: Open /reverse-withdrawal
UI->>API: GET /reverse-withdrawal/stores-balance?filters...
API-->>UI: 200 OK (body + KPI headers)
UI->>Admin: Render KPIs, filters, grid
end
rect rgba(0,96,160,0.06)
Admin->>UI: Click "Add New"
UI->>Modal: Open modal
Admin->>Modal: Submit form (vendor, trn_type, trn_id, debit/credit, note)
Modal->>API: POST /reverse-withdrawal/transactions (payload)
API-->>Modal: 200 OK
Modal->>UI: Emit refresh
UI->>API: Re-fetch /stores-balance
API-->>UI: Updated data + KPI headers
UI->>Admin: Update view
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
includes/Admin/Dashboard/Pages/ReverseWithdrawal.php(1 hunks)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php(2 hunks)src/admin/dashboard/components/Dashboard.tsx(2 hunks)src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
includes/Admin/Dashboard/Dashboard.php (1)
Dashboard(14-658)includes/Admin/Dashboard/Pages/ReverseWithdrawal.php (1)
ReverseWithdrawal(5-66)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (2)
Filter(60-60)DataViews(59-59)
includes/Admin/Dashboard/Pages/ReverseWithdrawal.php (2)
includes/Admin/Dashboard/Pages/AbstractPage.php (1)
AbstractPage(8-59)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (1)
register(51-58)
⏰ 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: e2e tests (2, 3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: api tests (1, 1)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
87f4064 to
49eb668
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (2)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
8-20: MissingonSavecallback to refresh parent data.The component signature doesn't accept an
onSaveprop, so the parent's callback is dropped and the page data won't refresh after a successful save. This issue was already flagged in a previous review.src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
174-180: Modal integration issue already flagged.The
handleModalSavecallback is defined here butAddReverseWithdrawModaldoesn't accept anonSaveprop, so this callback is never invoked. This was already flagged in the previous review of the modal component.
🧹 Nitpick comments (4)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
15-15: Consider alphabetical ordering of imports.The import is correct, but for consistency and maintainability, consider placing it in alphabetical order with the other page imports (between
ProFeaturesandSetupGuide).
43-43: Remove redundant inline comment.The inline comment is self-explanatory from the code itself and inconsistent with other entries in the array.
Apply this diff:
- ReverseWithdrawal::class, // Added ReverseWithdrawal page service + ReverseWithdrawal::class,src/admin/dashboard/pages/reverse-withdrawal/index.tsx (2)
183-189: Remove unnecessary memoization wrapper.The
memoizedSetFilterArgscallback doesn't provide any real benefit sincesetFilterArgsfromuseStateis already stable, and React'ssetStatenatively handles both function and value forms. This adds unnecessary complexity.- // Memoize setFilterArgs to prevent unnecessary re-renders of StoreFilter - const memoizedSetFilterArgs = useCallback((newArgs) => { - if (typeof newArgs === 'function') { - setFilterArgs(newArgs); - } else { - setFilterArgs(newArgs); - } - }, []); - // Move DateFilter outside or memoize it const DateFilter = useCallback(() => {Then update line 272:
- <StoreFilter key="store_filter" filterArgs={filterArgs} setFilterArgs={memoizedSetFilterArgs} />, + <StoreFilter key="store_filter" filterArgs={filterArgs} setFilterArgs={setFilterArgs} />,
227-227: Add space afterifkeyword.Minor style inconsistency.
- if(tempAfter && tempBefore) { + if (tempAfter && tempBefore) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
includes/Admin/Dashboard/Pages/ReverseWithdrawal.php(1 hunks)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php(2 hunks)src/admin/dashboard/components/Dashboard.tsx(2 hunks)src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/admin/dashboard/components/Dashboard.tsx
- includes/Admin/Dashboard/Pages/ReverseWithdrawal.php
🧰 Additional context used
🧬 Code graph analysis (3)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
includes/Admin/Dashboard/Dashboard.php (1)
Dashboard(14-658)includes/Admin/Dashboard/Pages/ReverseWithdrawal.php (1)
ReverseWithdrawal(5-66)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (2)
Filter(60-60)DataViews(59-59)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
⏰ 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 (2)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (2)
17-58: Well-structured memoized component.The
StoreFiltercomponent properly usesmemo,useCallback, anduseMemoto optimize re-renders. Good practice settingdisplayNamefor debugging.
276-345: Main page structure looks good.The component structure, KPI cards, filters, and data grid are well-organized and follow good practices for layout and user interaction.
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
| {transectionType === 'manual_product' && ( | ||
| <div> | ||
| <h3 className="text-base font-medium mb-3"> | ||
| {__('Select Product', 'dokan-lite')} | ||
| </h3> | ||
| <ProductAsyncSelect | ||
| value={selectedProduct} | ||
| onChange={setSelectedProduct} | ||
| placeholder={__('Search', 'dokan-lite')} | ||
| isClearable | ||
| prefetch | ||
| extraQuery={{ | ||
| ...(vendorsData?.value ? { id: vendorsData.value } : {}), | ||
| post_status: 'publish', | ||
| }} | ||
| noOptionsMessage={() => | ||
| !vendorsData | ||
| ? __('Please select a vendor first', 'dokan-lite') | ||
| : __('No products found', 'dokan-lite') | ||
| } | ||
| /> | ||
| {errors.trId && ( | ||
| <span className="text-red-500 text-sm mt-1 block"> | ||
| {__('Please select a product', 'dokan-lite')} | ||
| </span> | ||
| )} | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
Verify the id parameter for vendor filtering.
The extraQuery passes id: vendorsData.value to filter products by vendor. Please verify that the API endpoint expects id as the vendor filter parameter (rather than vendor_id or seller_id).
🏁 Script executed:
#!/bin/bash
# Search for ProductAsyncSelect component definition and usage patterns
ast-grep --pattern 'ProductAsyncSelect'
# Search for product endpoint query parameter handling
rg -nP --type=ts --type=js -C3 'extraQuery.*vendor|extraQuery.*seller' -g '!node_modules'Length of output: 562
---
🏁 Script executed:
```shell
#!/bin/bash
# Get the ProductAsyncSelect component implementation to understand its parameters
cat src/components/ProductAsyncSelect.tsx
Length of output: 10695
🏁 Script executed:
#!/bin/bash
# Get more context around the ProductAsyncSelect usage in AddReverseWithdrawModal
sed -n '160,190p' src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsxLength of output: 1334
🏁 Script executed:
#!/bin/bash
# Search for product API endpoint definitions to understand query parameters
rg -nP --type=ts --type=js -C2 'dokan.*product|endpoint.*product' -g '!node_modules' | head -50Length of output: 3787
🏁 Script executed:
#!/bin/bash
# Search for where the product endpoint is called to see what parameters are expected
rg -nP --type=ts --type=js 'dokan/v1/products' -g '!node_modules' -C3Length of output: 13014
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for other usages of ProductAsyncSelect to see what extraQuery parameters they use
rg -nP --type=ts --type=js 'ProductAsyncSelect' -A 15 -g '!node_modules' | grep -A 15 'extraQuery'
Length of output: 5700
🏁 Script executed:
#!/bin/bash
# Search for REST API patterns with 'id' parameter used for vendor/seller filtering
rg -nP --type=ts --type=js '[\?&]id=' -C 2 -g '!node_modules' | head -60Length of output: 486
🏁 Script executed:
#!/bin/bash
# Search for WordPress Dokan product query examples in backend or documentation
rg -nP --type=php --type=json 'seller_id|vendor_id' -C 2 -g '!node_modules' | head -40Length of output: 3448
🏁 Script executed:
#!/bin/bash
# Search for how OrderAsyncSelect is called to see the extraQuery pattern for orders
rg -nP --type=ts --type=js 'OrderAsyncSelect' -A 15 -g '!node_modules' | grep -B 2 -A 10 'extraQuery'Length of output: 5552
Change id to seller_id in ProductAsyncSelect extraQuery to match API expectations.
The extraQuery uses id: vendorsData.value to filter products by vendor, but the orders component on the same page correctly uses seller_id: vendorsData.value. The product API elsewhere in the codebase also filters by seller_id (see src/stores/vendors/resolvers.ts:46). This inconsistency will likely cause the product filtering to fail.
extraQuery={{
- ...(vendorsData?.value ? { id: vendorsData.value } : {}),
+ ...(vendorsData?.value ? { seller_id: vendorsData.value } : {}),
post_status: 'publish',
}}📝 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.
| {transectionType === 'manual_product' && ( | |
| <div> | |
| <h3 className="text-base font-medium mb-3"> | |
| {__('Select Product', 'dokan-lite')} | |
| </h3> | |
| <ProductAsyncSelect | |
| value={selectedProduct} | |
| onChange={setSelectedProduct} | |
| placeholder={__('Search', 'dokan-lite')} | |
| isClearable | |
| prefetch | |
| extraQuery={{ | |
| ...(vendorsData?.value ? { id: vendorsData.value } : {}), | |
| post_status: 'publish', | |
| }} | |
| noOptionsMessage={() => | |
| !vendorsData | |
| ? __('Please select a vendor first', 'dokan-lite') | |
| : __('No products found', 'dokan-lite') | |
| } | |
| /> | |
| {errors.trId && ( | |
| <span className="text-red-500 text-sm mt-1 block"> | |
| {__('Please select a product', 'dokan-lite')} | |
| </span> | |
| )} | |
| </div> | |
| )} | |
| {transectionType === 'manual_product' && ( | |
| <div> | |
| <h3 className="text-base font-medium mb-3"> | |
| {__('Select Product', 'dokan-lite')} | |
| </h3> | |
| <ProductAsyncSelect | |
| value={selectedProduct} | |
| onChange={setSelectedProduct} | |
| placeholder={__('Search', 'dokan-lite')} | |
| isClearable | |
| prefetch | |
| extraQuery={{ | |
| ...(vendorsData?.value ? { seller_id: vendorsData.value } : {}), | |
| post_status: 'publish', | |
| }} | |
| noOptionsMessage={() => | |
| !vendorsData | |
| ? __('Please select a vendor first', 'dokan-lite') | |
| : __('No products found', 'dokan-lite') | |
| } | |
| /> | |
| {errors.trId && ( | |
| <span className="text-red-500 text-sm mt-1 block"> | |
| {__('Please select a product', 'dokan-lite')} | |
| </span> | |
| )} | |
| </div> | |
| )} |
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
around lines 161 to 188, the ProductAsyncSelect extraQuery uses id:
vendorsData.value which is inconsistent with the API and other components;
change the key from id to seller_id (i.e., use seller_id: vendorsData.value when
vendorsData?.value exists) so the products are correctly filtered by vendor,
leaving the rest of the extraQuery (post_status: 'publish') unchanged and
keeping the conditional presence check for vendorsData.
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
cce4699 to
3228751
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (7)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (2)
131-168: UpdateuseCallbackdependencies to include date parameters.The
fetchDatafunction usesdateAfteranddateBeforefrom its closure (default parameter values on line 131) but doesn't include them in the dependency array on line 168. This causes stale closure issues where the callback won't update when the date values change.Apply this diff:
- }, [view.page, view.perPage, filterArgs]); + }, [view.page, view.perPage, filterArgs, dateAfter, dateBefore]);
192-268: ExtractDateFilteras a separate component to avoid reconciliation issues.Defining a component inside another component (even with
useCallback) is an anti-pattern. When the parent re-renders and dependencies change, React creates a new component function, causing unmounts/remounts that lose internal state and break user interactions.Move
DateFilteroutsideReverseWithdrawalPageas a standalone memoized component:+const DateFilter = memo(({ dateAfter, dateAfterText, dateBefore, dateBeforeText, focusedInput, setDateAfter, setDateAfterText, setDateBefore, setDateBeforeText, setFocusedInput, fetchData }) => { + const [tempAfter, setTempAfter] = useState(dateAfter); + const [tempAfterText, setTempAfterText] = useState(dateAfterText); + const [tempBefore, setTempBefore] = useState(dateBefore); + const [tempBeforeText, setTempBeforeText] = useState(dateBeforeText); + const [tempFocused, setTempFocused] = useState(focusedInput); + + // ... rest of the component logic +}); + +DateFilter.displayName = 'DateFilter'; + const ReverseWithdrawalPage = () => { // ... - const DateFilter = useCallback(() => { - // ... - }, [dateAfter, dateAfterText, dateBefore, dateBeforeText, focusedInput, fetchData]);Then pass props explicitly:
- <DateFilter key="date_filter" />, + <DateFilter + key="date_filter" + dateAfter={dateAfter} + dateAfterText={dateAfterText} + dateBefore={dateBefore} + dateBeforeText={dateBeforeText} + focusedInput={focusedInput} + setDateAfter={setDateAfter} + setDateAfterText={setDateAfterText} + setDateBefore={setDateBefore} + setDateBeforeText={setDateBeforeText} + setFocusedInput={setFocusedInput} + fetchData={fetchData} + />,src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (5)
8-8: Add missingonSaveprop to enable page refresh.The component only destructures
openandonClose, dropping theonSavecallback passed from the parent page. Without this, successful submissions don't trigger a data refresh, leaving the dashboard stale.Apply this diff:
-const AddReverseWithdrawModal = ({ open, onClose }) => { +const AddReverseWithdrawModal = ({ open, onClose, onSave }) => {Then update the success handler (see comment on lines 69-86).
9-11: Fix typo: "transectionType" → "transactionType".The state variable is consistently misspelled as "transectionType" instead of "transactionType" throughout the file.
Apply a global find-and-replace:
- const [transectionType, setTransectionType] = useState< + const [transactionType, setTransactionType] = useState< 'manual_product' | 'manual_order' | 'other' >('manual_product');(Apply to all 12 occurrences in the file)
69-86: Improve error handling and remove debug code.Multiple issues in this block:
- Missing
onSavecall: After successful POST, the callback isn't invoked, so the parent page never refreshes.- Console.log in production: Debug logging should be removed (line 79).
- Silent error handling: Errors are re-thrown without user feedback, leaving the modal open with no indication of what failed.
Apply these fixes:
try { const response = await apiFetch({ path: 'dokan/v1/reverse-withdrawal/transactions', method: 'POST', data: payload, }); resetForm(); - onClose(); - console.log('Reverse withdrawal created successfully:', response); + onSave?.(response); + onClose(); } catch (error) { - console.error('Error saving reverse withdrawal:', error); - throw error; + setErrors({ submit: __('Failed to create reverse withdrawal. Please try again.', 'dokan-lite') }); } finally { setLoading(false); }
161-188: Changeidtoseller_idin ProductAsyncSelect extraQuery.The ProductAsyncSelect uses
id: vendorsData.valueto filter products by vendor (line 173), but this is inconsistent with the OrderAsyncSelect on line 204 which correctly usesseller_id: vendorsData.value. The product API expectsseller_idfor vendor filtering.Apply this diff:
extraQuery={{ - ...(vendorsData?.value ? { id: vendorsData.value } : {}), + ...(vendorsData?.value ? { seller_id: vendorsData.value } : {}), post_status: 'publish', }}
305-320: Fix dynamic height class for Tailwind JIT compatibility.The template literal in the className on line 311 (
h-[${transectionType === 'other' ? '718px' : '818px'}]) won't work with Tailwind JIT mode because the complete class names must be statically detectable at build time.Apply this diff:
- className={`w-full sm:w-[620px] max-w-[95%] h-[${transectionType === 'other' ? '718px' : '818px'}] max-h-[96vh] flex flex-col`} + className={`w-full sm:w-[620px] max-w-[95%] ${transectionType === 'other' ? 'h-[718px]' : 'h-[818px]'} max-h-[96vh] flex flex-col`}
🧹 Nitpick comments (1)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
182-189: Simplify memoizedSetFilterArgs or remove it.The
memoizedSetFilterArgswrapper is unnecessary becausesetFilterArgs(fromuseState) is already stable. Additionally, both branches of the conditional execute identical code, making the condition redundant.You can either:
- Remove the wrapper and pass
setFilterArgsdirectly toStoreFilter- Or simplify to just pass through the argument:
const memoizedSetFilterArgs = useCallback((newArgs) => { - if (typeof newArgs === 'function') { - setFilterArgs(newArgs); - } else { - setFilterArgs(newArgs); - } + setFilterArgs(newArgs); }, []);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
includes/Admin/Dashboard/Pages/ReverseWithdrawal.php(1 hunks)includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php(2 hunks)src/admin/dashboard/components/Dashboard.tsx(2 hunks)src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/admin/dashboard/components/Dashboard.tsx
- includes/Admin/Dashboard/Pages/ReverseWithdrawal.php
🧰 Additional context used
🧬 Code graph analysis (3)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
includes/Admin/Dashboard/Dashboard.php (1)
Dashboard(14-658)includes/Admin/Dashboard/Pages/ReverseWithdrawal.php (1)
ReverseWithdrawal(5-66)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (2)
Filter(60-60)DataViews(59-59)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
⏰ 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: api tests (1, 1)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: e2e tests (2, 3)
🔇 Additional comments (14)
includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php (2)
15-15: LGTM! Import follows the established pattern.The import statement correctly references the new ReverseWithdrawal page class and follows the same pattern as other dashboard page imports.
43-43: LGTM! Service registration is correct.The ReverseWithdrawal page is properly registered in the services array, allowing the Dashboard class to discover and integrate it into the admin dashboard menu and routing.
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (6)
1-13: LGTM! Imports are appropriate.All imports are properly used throughout the component and include necessary dependencies for the reverse withdrawal page functionality.
14-58: LGTM! Helper function and StoreFilter are well-structured.The
pricehelper is concise, andStoreFilteris properly extracted as a memoized component with appropriate use ofuseCallbackanduseMemoto prevent unnecessary re-renders.
60-89: LGTM! State management is comprehensive.The state structure appropriately covers all aspects of the page including statistics, data grid state, filters, and modal visibility.
91-129: LGTM! Field definitions are properly memoized.The field configurations are well-structured with custom renderers, and the date field includes appropriate validation before formatting.
170-180: LGTM! Effect and modal handler are properly structured.The
useEffectcorrectly triggers initial data fetch, andhandleModalSaveappropriately refreshes data after successful submission.
276-345: LGTM! Component render structure is well-organized.The JSX layout effectively presents the page header, KPI cards, filter controls, data grid, and modal integration with appropriate conditional rendering.
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (6)
1-6: LGTM! Imports are appropriate.All necessary dependencies are imported for the modal functionality.
21-48: LGTM! Validation logic is comprehensive.The validation covers all required fields with appropriate conditional checks based on transaction type.
50-67: LGTM! Payload construction is correct.The payload properly maps form state to API requirements, including conditional
trn_idand appropriate debit/credit formatting.
88-118: LGTM! Form handlers are well-structured.Reset and change handlers appropriately manage form state, including clearing dependent selections when vendors or transaction types change.
120-136: LGTM! Vendor selection UI is properly configured.The vendor selector includes appropriate prefetch, clearable options, and error feedback.
225-302: LGTM! Form fields are well-structured.The withdrawal type selector, amount input, and notes textarea include appropriate validation feedback and user-friendly UI elements like the currency prefix and info tooltips.
* enhance: update admin data-table ui. * enhance: update reverse withdrawal's code. * enhance: update reverse withdrawal modal UI styles. * enhance: improve reverse withdrawal UI and functionality with updated styles and layout. * enhance: add support for reverse withdrawal, update texts, and clean up unused code
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
160-161: Consider logging fetch errors for debugging.The empty catch block silently suppresses errors. While setting empty data is a reasonable fallback, logging would help diagnose API issues.
- } catch { + } catch (error) { + console.error('Failed to fetch reverse withdrawal data:', error); setData( [] ); } finally {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
includes/Admin/Dashboard/Pages/ReverseWithdrawal.php(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)src/admin/dashboard/style.scss(1 hunks)src/admin/panel-switcher/PanelSwitch.tsx(1 hunks)src/components/dataviews/AdminDataViewTable.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- includes/Admin/Dashboard/Pages/ReverseWithdrawal.php
🧰 Additional context used
🧬 Code graph analysis (2)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
⏰ 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 (2, 3)
- GitHub Check: e2e tests (1, 3)
🔇 Additional comments (4)
src/components/dataviews/AdminDataViewTable.tsx (1)
216-220: LGTM!The conditional header rendering is clean and properly guarded with a null check. The placement above the tabs is logical for presenting page-level context.
src/admin/panel-switcher/PanelSwitch.tsx (1)
17-24: LGTM!Adding 'reverse-withdrawal' to the supported keys aligns with the new admin page being introduced. The change follows the established pattern.
src/admin/dashboard/style.scss (1)
72-88: LGTM!The styling adjustments for the reverse withdrawal data view are isolated and appropriate. The 75% width allocation for the first column and border removal for the first tab button are reasonable layout choices.
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
94-104: Empty string in fields array may not be ideal but behavior is unclear without library verification.The pattern you've flagged exists consistently across multiple pages (
reverse-withdrawal,withdraw, andRequestList), not just this file. However,vendors.tsxuses a different approach with a static array that doesn't include thetitleFieldat all.The empty string approach may be silently handled by the WordPress DataViews library or could potentially cause issues. Since the DataViews component is from an external library (
@wordpress/dataviews/wp), its behavior with empty strings cannot be verified from the codebase alone.The cleaner approach would match
vendors.tsx—usingfilter()instead of mapping to empty strings—but this requires confirmation that it won't break existing functionality.Please verify:
- Test if empty strings in the
fieldsarray cause any rendering or data issues in the current implementation- If no issues exist, the pattern is acceptable but inconsistent with
vendors.tsx; consider documenting why this page uses dynamic mapping vs. static arrays- If issues exist, align with the
vendors.tsxpattern usingfilter()instead
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (4)
15-15: Hook modal success so the page refreshes.The component signature still doesn't accept an
onSavecallback, so after a successful POST the parent page cannot refresh its data. This was flagged in previous reviews.
16-18: Fix typo: "transectionType" → "transactionType".The state variable remains misspelled throughout the file. This was flagged in previous reviews.
93-95: Improve error handling for better UX.The catch block re-throws the error without providing user feedback, leaving the modal open with no indication of what went wrong. This was flagged in previous reviews.
232-237: Changeidtoseller_idin ProductAsyncSelect extraQuery.Line 234 uses
id: vendorsData.valuewhile the OrderAsyncSelect correctly usesseller_id: vendorsData.valueon line 271. This inconsistency was flagged in previous reviews.
🧹 Nitpick comments (1)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
47-49: Consider validating amount is a positive number.The validation only checks if
withdrawalAmountis truthy, but doesn't verify it's a valid positive number. This could allow invalid values like negative numbers or non-numeric strings to be submitted.Apply this diff to add number validation:
if ( ! withdrawalAmount ) { newErrors.withdrawalAmount = true; + } else if ( isNaN( Number( withdrawalAmount ) ) || Number( withdrawalAmount ) <= 0 ) { + newErrors.withdrawalAmount = true; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/admin/dashboard/pages/reverse-withdrawal/index.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T06:19:41.376Z
Learnt from: mrabbani
Repo: getdokan/dokan PR: 2891
File: src/admin/dashboard/pages/vendors-single/InformationTabs/WithdrawTab.tsx:58-59
Timestamp: 2025-09-19T06:19:41.376Z
Learning: The Dokan codebase uses 'dokan-lite' as the text domain for WordPress internationalization (__() function calls).
Applied to files:
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
🧬 Code graph analysis (1)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
⏰ 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: api tests (1, 1)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
🔇 Additional comments (1)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
423-437: LGTM! Modal setup is correct.The DokanModal is properly configured with all necessary props, and the className concatenation is handled correctly.
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
Outdated
Show resolved
Hide resolved
* feat: Add new Reverse Withdrawal Transaction page with filters, stats, and routing integration * refactor: Move reverse withdrawal creation logic to AddReverseWithdrawModal component
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (2)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (2)
26-28: RenametransectionType→transactionTypefor clarityThe state and related handlers are consistently misspelled as
transectionType. This hurts readability and can be confusing long‑term.A simple find‑and‑replace to
transactionType/setTransactionTypewould clarify intent.Also applies to: 141-147, 217-238
41-68: Handle API errors in the modal instead of just logging and re‑throwingOn failure, the catch block only logs to the console and re‑throws. The modal stays open but the user receives no feedback and the thrown error may bubble in unexpected ways.
Recommend:
- Remove the
console.log(noconsole.*in production code).- Capture the error into component state (e.g.,
errors.submit) or trigger a global toast/notice so the user sees a clear message.- Do not re‑throw from here; rely on the UI error path instead.
Also applies to: 86-115
🧹 Nitpick comments (6)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (3)
153-161: Debounced amount state can be stale at submit time
DokanPriceInput’sonChangefeeds a 500 ms‑debounceddebouncedWithdrawAmount, and both the displayedvalueand the payload (withdrawalAmountRaw) come from that debounced state. If an admin edits the amount and immediately clicks “Add New”, you may post the previous amount.Consider keeping a non‑debounced state for the actual submitted value (and optionally debounce only any heavy side‑effects), or drop the debounce if it isn’t needed.
Also applies to: 382-417
382-417: Avoid hard‑coded$currency symbolThe amount input overlays a literal
$prefix while also usingDokanPriceInput, which is already currency‑aware. This can:
- Show the wrong symbol in non‑USD stores.
- Potentially duplicate the symbol if
DokanPriceInputrenders one.Prefer pulling the symbol from the store settings (or letting
DokanPriceInputhandle it) instead of hard‑coding$.If
DokanPriceInputalready renders the store currency symbol, remove the manual$span to avoid duplication.
39-40: Strongly typeerrorsfor better safety
errorsis initialized as a bare{}and later accessed with properties likeerrors.vendorId,errors.trId, etc. Without an explicit type, this bypasses TypeScript’s safety and can hide typos in error keys.Defining an interface, e.g.:
type FormErrors = { vendorId?: boolean; trId?: boolean; withdrawalAmount?: boolean; withdrawalNote?: boolean; submit?: string; }; const [ errors, setErrors ] = useState<FormErrors>( {} );would make access and updates much safer.
Also applies to: 118-128
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
171-180: Consider server‑side pagination instead ofper_page: -1The transactions fetch uses
per_page: -1and then setspaginationInfo={{ totalItems, totalPages: 1 }}. For vendors with many transactions, this can become a heavy request and slow UI.If the API supports it, consider:
- Using
per_page: view.perPageand a realtotalPagesfrom headers or data length.- Wiring
view.pageintoqueryArgsandpaginationInfo.This would keep the UI responsive on large datasets.
Also applies to: 487-488
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (2)
201-332: Optional: actually memoizefilterFieldsto avoid recreating inline components
filterFieldsis re‑declared on every render, and each field contains inline JSX with closures over component state. The comment says “Memoize filter fields,” but nouseMemois used.This is not a correctness bug, but you could wrap it in
useMemowith appropriate deps (date/vender/filter state) to reduce unnecessary recalculations and avoid potential re‑mounts if the DataViews implementation compares fields by reference.
430-438: Confirm that closing the modal only on success matches desired UX
onCreatedboth closes the modal and triggersfetchData, whileonClosesimply hides it. If the POST fails inside the modal,onCreatedis never called and the modal remains open (which is good), but combined with the current modal error handling (no user‑visible error from the child), admins may not realize something went wrong.Once
AddReverseWithdrawModalsurfaces a clear error, this pattern is fine; otherwise, consider also showing a toast here whenonCreatedis not invoked due to failure.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/admin/dashboard/components/Dashboard.tsx(2 hunks)src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/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
🧠 Learnings (1)
📚 Learning: 2025-09-19T06:19:41.376Z
Learnt from: mrabbani
Repo: getdokan/dokan PR: 2891
File: src/admin/dashboard/pages/vendors-single/InformationTabs/WithdrawTab.tsx:58-59
Timestamp: 2025-09-19T06:19:41.376Z
Learning: The Dokan codebase uses 'dokan-lite' as the text domain for WordPress internationalization (__() function calls).
Applied to files:
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
🧬 Code graph analysis (3)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx (1)
types/externals.d.ts (1)
DokanModal(65-65)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
🪛 Biome (2.1.2)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx
[error] 248-248: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 250-250: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 254-254: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 255-255: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
⏰ 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 (3, 3)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: api tests (1, 1)
🔇 Additional comments (1)
src/admin/dashboard/components/Dashboard.tsx (1)
13-15: The review comment is incorrect—the import path is valid and will resolve properly.The
'admin'alias is configured intsconfig.jsonas"admin/*": ["./src/admin/*"], and the file./src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsxexists at the exact location the alias resolves to. While the import style is inconsistent with the relative path on the line above, it is functionally correct and will not fail.No changes are required.
Likely an incorrect or invalid review comment.
| const AddReverseWithdrawModal = ( { | ||
| open, | ||
| onClose, | ||
| onCreated, | ||
| }: { | ||
| open: boolean; | ||
| onClose: () => void; | ||
| onCreated?: ( args?: any ) => void; | ||
| } ) => { |
There was a problem hiding this comment.
Guard optional onCreated before calling
onCreated is typed as optional but is invoked unconditionally in handleConfirm. If this modal is ever used without an onCreated prop, it will throw at runtime.
Consider guarding the call:
- onCreated( { payload } );
+ onCreated?.( { payload } );Also applies to: 104-106
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
around lines 17-25 (and also apply to the call sites at lines 104-106), the prop
onCreated is declared optional but is invoked unconditionally; update the
implementation to guard the call by checking the existence of onCreated before
invoking it (e.g., use if (onCreated) onCreated(...) or onCreated?.(...)) at
each call site so the modal does not throw when onCreated is not supplied.
| { transectionType === 'manual_product' && ( | ||
| <div className="space-y-2.5"> | ||
| { /* eslint-disable-next-line jsx-a11y/label-has-associated-control */ } | ||
| <label className="text-[#25252D] font-medium text-sm"> | ||
| { __( 'Select Product', 'dokan-lite' ) } | ||
| </label> | ||
| <ProductAsyncSelect | ||
| value={ selectedProduct } | ||
| onChange={ setSelectedProduct } | ||
| placeholder={ __( 'Search', 'dokan-lite' ) } | ||
| isClearable | ||
| prefetch | ||
| extraQuery={ { | ||
| ...( vendorsData?.value && { | ||
| author: vendorsData.value, | ||
| } ), | ||
| post_status: 'publish', | ||
| } } | ||
| disabled={ ! vendorsData } | ||
| className={ '!rounded-lg' } |
There was a problem hiding this comment.
🧩 Analysis chain
Verify correct vendor filter key for ProductAsyncSelect (author vs seller_id)
The product selector sends extraQuery={{ ...(vendorsData?.value && { author: vendorsData.value }), post_status: 'publish' }}. Other reverse‑withdrawal endpoints use vendor_id / seller_id (e.g., the order selector just below uses seller_id).
Please confirm that the products endpoint actually expects author here; if it instead expects something like seller_id or vendor_id, product filtering by vendor will silently fail.
Would you run a quick search in this repo for other ProductAsyncSelect usages and for the dokan/v1/products endpoint to confirm which query key is used for vendor filtering?
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for all ProductAsyncSelect usages to see which query parameters are used
rg -n "ProductAsyncSelect" --type=tsx --type=ts -A 10 | head -100
Length of output: 27
🏁 Script executed:
#!/bin/bash
# Search for the dokan products endpoint handler
rg -n "dokan/v1/products" --type=php --type=ts --type=tsx -B 2 -A 5 | head -100Length of output: 27
🏁 Script executed:
#!/bin/bash
# Search for "seller_id" to see how it's used in similar contexts
rg -n "seller_id" --type=tsx --type=ts -B 2 -A 2 | head -80Length of output: 27
🏁 Script executed:
#!/bin/bash
# Search for the order selector in the same file to see how it filters
rg -n "OrderAsyncSelect\|seller_id.*post_status" src/admin/dashboard/pages/reverse-withdrawal/ --type=tsx -B 3 -A 3Length of output: 27
🏁 Script executed:
#!/bin/bash
# Look for ProductAsyncSelect component definition
fd "ProductAsyncSelect" --type=tsx --type=ts --exec cat {} \;Length of output: 170
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for ProductAsyncSelect usage without strict type filter
rg -n "ProductAsyncSelect" -A 12 src/ 2>/dev/null | head -120
Length of output: 7369
🏁 Script executed:
#!/bin/bash
# Search for seller_id usage in src directory
rg -n "seller_id" -B 2 -A 2 src/ 2>/dev/null | head -100Length of output: 3565
🏁 Script executed:
#!/bin/bash
# Find all files with ProductAsyncSelect in src
fd -e js -e ts -e jsx -e tsx "^ProductAsyncSelect" src/ 2>/dev/nullLength of output: 37
🏁 Script executed:
#!/bin/bash
# Look for the AddReverseWithdrawModal file directly and check context
cat src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx 2>/dev/null | head -300Length of output: 11375
🏁 Script executed:
#!/bin/bash
# Search for dokan products endpoint references
rg -n "products.*endpoint\|dokan.*products\|extraQuery" src/ 2>/dev/null | head -80Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for OrderAsyncSelect to compare filter patterns
rg -n "OrderAsyncSelect" -A 8 src/ 2>/dev/nullLength of output: 5209
---
🏁 Script executed:
```shell
#!/bin/bash
# Look at ProductAsyncSelect.tsx to see how queries are constructed
cat src/components/ProductAsyncSelect.tsx
Length of output: 10760
🏁 Script executed:
#!/bin/bash
# Get the complete AddReverseWithdrawModal.tsx to see OrderAsyncSelect full config
cat src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx | tail -150Length of output: 7042
🏁 Script executed:
#!/bin/bash
# Search for any dokan/v2/products references or products endpoint handlers
rg -n "dokan/v2/products\|/products.*extraQuery\|author.*product" src/ 2>/dev/null | head -50Length of output: 0
🏁 Script executed:
#!/bin/bash
# Look for WordPress REST API product query parameter handling
rg -n "author.*query\|WP_Query.*author" --type=php 2>/dev/null | head -30Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search in entire codebase for "author" usage in extraQuery contexts
rg -n "extraQuery.*author\|author.*extraQuery" 2>/dev/nullLength of output: 0
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for backend/PHP files that might define products endpoint
fd -e php --max-depth 5 "routes\|products" . 2>/dev/null | head -20
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Look for any dokan routes or API handler files
find . -name "*.php" -path "*routes*" -o -name "*.php" -path "*api*" 2>/dev/null | head -20Length of output: 33
🏁 Script executed:
#!/bin/bash
# Check if there are test files showing correct parameters
rg -l "seller_id.*product\|product.*seller_id" --type=php 2>/dev/null | head -10Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for all places where the /dokan/v2/products endpoint is referenced or tested
rg -n "v2/products\|v1/products" . 2>/dev/null | head -40Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check for README or documentation about API parameters
fd -e md -e txt "README\|CHANGELOG\|API\|api" . 2>/dev/null | xargs grep -l "seller_id\|author" 2>/dev/null | head -10Length of output: 0
Fix ProductAsyncSelect vendor filter parameter from author to seller_id
Line 262 uses incorrect parameter name: author: vendorsData.value should be seller_id: vendorsData.value. The OrderAsyncSelect immediately below (line 302) correctly uses seller_id, and other product endpoint calls in the codebase (e.g., src/stores/vendors/resolvers.ts:46) also use seller_id. Using author will silently fail to filter products by vendor.
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
around lines 248 to 267, the ProductAsyncSelect extraQuery uses the wrong vendor
filter key (`author: vendorsData.value`) which fails to filter products; change
that key to `seller_id: vendorsData.value` to match the OrderAsyncSelect and
other product endpoint usages, ensuring the vendor filter is applied when
vendorsData is present.
| const fetchData = useCallback( async () => { | ||
| setData( [] ); | ||
| setTotalItems( 0 ); | ||
| setIsLoading( true ); | ||
| try { | ||
| const queryArgs = { | ||
| per_page: view.perPage, | ||
| page: view.page, | ||
| orderby: ( view as any )?.sort?.field || 'added', | ||
| order: ( view as any )?.sort?.direction || 'desc', | ||
| ...filterArgs, | ||
| }; |
There was a problem hiding this comment.
Update fetchData dependencies so sort changes trigger refetch
fetchData builds orderby and order from view.sort, but the dependency array only includes view.page, view.perPage, and filterArgs. If the user changes sort without changing page/perPage, the closure will still use the old sort and useEffect won’t re‑run.
Consider:
- }, [ view.page, view.perPage, filterArgs ] );
+ }, [ view, filterArgs ] );(or add view.sort explicitly) so any sort change results in a new request and consistent data.
Also applies to: 164-168, 399-413
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/reverse-withdrawal/index.tsx around lines 118-129
(and similarly at 164-168 and 399-413), fetchData reads orderby/order from
view.sort but the effect/dependencies omit view.sort so sort changes don’t
trigger a refetch; update the dependency arrays (or the useCallback
dependencies) to include view.sort (or view.sort.field and view.sort.direction)
so changes to sort cause fetchData to be recreated and the useEffect to re-run,
ensuring the request uses the current sort values.
| const fetchTransactions = useCallback( async () => { | ||
| if ( ! vendorId ) { | ||
| return; | ||
| } | ||
|
|
||
| // Don't fetch until initialization is complete | ||
| if ( ! isInitialized ) { | ||
| return; | ||
| } | ||
|
|
||
| setIsLoading( true ); | ||
| setData( [] ); | ||
| try { | ||
| const queryArgs: any = { | ||
| orderby: ( view as any )?.sort?.field || 'added', | ||
| order: ( view as any )?.sort?.direction || 'desc', | ||
| vendor_id: vendorId, | ||
| per_page: -1, | ||
| ...filterArgs, | ||
| }; | ||
|
|
There was a problem hiding this comment.
Include view sort in fetchTransactions dependencies so sorting works correctly
fetchTransactions reads view.sort.field and view.sort.direction from closure, but the hook’s dependency array only includes view.page, view.perPage, filterArgs, and isInitialized. If the user changes sort without changing page/perPage, the callback won’t update and the API will still use the previous sort.
Safer to depend on the full view object (or at least view.sort) so sort changes trigger a new fetch:
- }, [ vendorId, view.page, view.perPage, filterArgs, isInitialized ] );
+ }, [ vendorId, view, filterArgs, isInitialized ] );(or add view.sort explicitly if you want to be more granular).
Also applies to: 211-241, 481-515
| const clearSingleFilter = ( filterId ) => { | ||
| const args = { ...filterArgs }; | ||
| switch ( filterId ) { | ||
| case 'date-range': | ||
| // Reset to default 30 days window like Vue | ||
| const end = new Date(); | ||
| end.setHours( 23, 59, 59, 0 ); | ||
| const start = new Date(); | ||
| start.setDate( start.getDate() - 29 ); | ||
| start.setHours( 0, 0, 0, 0 ); | ||
|
|
||
| const from = dateI18n( 'Y-m-d H:i:s', start ); | ||
| const to = dateI18n( 'Y-m-d H:i:s', end ); | ||
|
|
||
| setDateAfter( start ); | ||
| setDateAfterText( from ); | ||
| setDateBefore( end ); | ||
| setDateBeforeText( to ); | ||
| args.trn_date = { from, to }; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| setFilterArgs( args ); | ||
| }; |
There was a problem hiding this comment.
Wrap case 'date-range' declarations in a block to fix noSwitchDeclarations lint error
The case 'date-range' branch declares const end, const start, const from, and const to directly in the switch. Biome flags this (noSwitchDeclarations) because those bindings are in scope for all subsequent cases.
Wrap the case body in a block:
- switch ( filterId ) {
- case 'date-range':
- // Reset to default 30 days window like Vue
- const end = new Date();
- end.setHours( 23, 59, 59, 0 );
- const start = new Date();
- start.setDate( start.getDate() - 29 );
- start.setHours( 0, 0, 0, 0 );
-
- const from = dateI18n( 'Y-m-d H:i:s', start );
- const to = dateI18n( 'Y-m-d H:i:s', end );
-
- setDateAfter( start );
- setDateAfterText( from );
- setDateBefore( end );
- setDateBeforeText( to );
- args.trn_date = { from, to };
- break;
+ switch ( filterId ) {
+ case 'date-range': {
+ // Reset to default 30 days window like Vue
+ const end = new Date();
+ end.setHours( 23, 59, 59, 0 );
+ const start = new Date();
+ start.setDate( start.getDate() - 29 );
+ start.setHours( 0, 0, 0, 0 );
+
+ const from = dateI18n( 'Y-m-d H:i:s', start );
+ const to = dateI18n( 'Y-m-d H:i:s', end );
+
+ setDateAfter( start );
+ setDateAfterText( from );
+ setDateBefore( end );
+ setDateBeforeText( to );
+ args.trn_date = { from, to };
+ break;
+ }
default:
break;
}📝 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 clearSingleFilter = ( filterId ) => { | |
| const args = { ...filterArgs }; | |
| switch ( filterId ) { | |
| case 'date-range': | |
| // Reset to default 30 days window like Vue | |
| const end = new Date(); | |
| end.setHours( 23, 59, 59, 0 ); | |
| const start = new Date(); | |
| start.setDate( start.getDate() - 29 ); | |
| start.setHours( 0, 0, 0, 0 ); | |
| const from = dateI18n( 'Y-m-d H:i:s', start ); | |
| const to = dateI18n( 'Y-m-d H:i:s', end ); | |
| setDateAfter( start ); | |
| setDateAfterText( from ); | |
| setDateBefore( end ); | |
| setDateBeforeText( to ); | |
| args.trn_date = { from, to }; | |
| break; | |
| default: | |
| break; | |
| } | |
| setFilterArgs( args ); | |
| }; | |
| const clearSingleFilter = ( filterId ) => { | |
| const args = { ...filterArgs }; | |
| switch ( filterId ) { | |
| case 'date-range': { | |
| // Reset to default 30 days window like Vue | |
| const end = new Date(); | |
| end.setHours( 23, 59, 59, 0 ); | |
| const start = new Date(); | |
| start.setDate( start.getDate() - 29 ); | |
| start.setHours( 0, 0, 0, 0 ); | |
| const from = dateI18n( 'Y-m-d H:i:s', start ); | |
| const to = dateI18n( 'Y-m-d H:i:s', end ); | |
| setDateAfter( start ); | |
| setDateAfterText( from ); | |
| setDateBefore( end ); | |
| setDateBeforeText( to ); | |
| args.trn_date = { from, to }; | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| setFilterArgs( args ); | |
| }; |
🧰 Tools
🪛 Biome (2.1.2)
[error] 248-248: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 250-250: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 254-254: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 255-255: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🤖 Prompt for AI Agents
In src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx
around lines 243 to 267, the declarations inside the case 'date-range' (const
end, start, from, to) are made directly in the switch and trigger the
noSwitchDeclarations lint error; wrap the entire case 'date-range' body in its
own block ({ ... }) so those const bindings are scoped to that case only,
leaving the rest of the switch unchanged and keeping the setFilterArgs(args)
call after the switch.
…WithdrawalTransaction components
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
180-225: Include fullview(or at leastview.sort) infetchTransactionsdependencies so sorting works correctly
fetchTransactionsbuildsorderby/orderfromview.sort:const queryArgs: any = { orderby: ( view as any )?.sort?.field || 'added', order: ( view as any )?.sort?.direction || 'desc', vendor_id: vendorId, per_page: -1, ...filterArgs, };but the hook dependencies only include
view.page,view.perPage, andfilterArgs. If the user changes sort without changing page/perPage, the closure keeps using the old sort and the effect won’t re‑run, so the table doesn’t actually resort on the server.Updating the deps to include the sort state fixes this:
- }, [ vendorId, view.page, view.perPage, filterArgs ] ); + }, [ vendorId, view, filterArgs ] );(or depend specifically on
view.sortif you want to be more granular).src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
117-167: UpdatefetchDatadependencies so sort changes (and other view changes) trigger a refetch
fetchDataconstructs query args usingview.sort.field/view.sort.direction:const queryArgs = { per_page: view.perPage, page: view.page, orderby: ( view as any )?.sort?.field || 'added', order: ( view as any )?.sort?.direction || 'desc', ...filterArgs, };but the
useCallbackonly depends onview.page,view.perPage, andfilterArgs. If the user adjusts sorting without changing page/perPage, the request will still use the previous sort and the effect watchingfetchDatawon’t fire.Safer to depend on the full
viewobject (or at leastview.sort) so any sort or layout change is reflected:- }, [ view.page, view.perPage, filterArgs ] ); + }, [ view, filterArgs ] );This keeps the backend data consistent with the current table view.
🧹 Nitpick comments (1)
includes/REST/ReverseWithdrawalController.php (1)
230-246: Verify newtrn_datedefaulting semantics and avoid surprising changes for partial rangesController now always normalizes
trn_date:$default_transaction_date = Helper::get_default_transaction_date(); $request_params = $request->get_params(); // ... $request_params['trn_date'] = [ 'from' => $request_params['trn_date']['from'] ?? $default_transaction_date['from'], 'to' => $request_params['trn_date']['to'] ?? $default_transaction_date['to'], ];and the route schema marks
trn_dateas optional with nested defaults.This is good for the new UI (it can omit
trn_dateentirely and still get a sane last‑X‑days window), but it also subtly changes API behavior for callers that previously sent only afromor only atodate – those now get the default on the missing side instead of an open‑ended range.If open‑ended ranges should remain supported for API consumers, consider only applying the defaults when
trn_dateis absent or bothfromandtoare empty, e.g.:- // Set default trn_date if not set. - $request_params['trn_date'] = [ - 'from' => $request_params['trn_date']['from'] ?? $default_transaction_date['from'], - 'to' => $request_params['trn_date']['to'] ?? $default_transaction_date['to'], - ]; + // Set default trn_date only when no bounds are provided. + if ( + empty( $request_params['trn_date']['from'] ) + && empty( $request_params['trn_date']['to'] ) + ) { + $request_params['trn_date'] = $default_transaction_date; + }If the intent is to always clamp to a bounded range for this endpoint, then this change looks fine but should be called out explicitly in API/feature docs so consumers know open‑ended ranges are no longer possible.
Also applies to: 633-671
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
includes/REST/ReverseWithdrawalController.php(2 hunks)src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
includes/REST/ReverseWithdrawalController.php (3)
includes/ReverseWithdrawal/Helper.php (2)
Helper(18-778)get_default_transaction_date(344-349)includes/ReverseWithdrawal/Manager.php (1)
Manager(20-789)includes/functions.php (1)
dokan_get_current_user_id(48-61)
⏰ 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: api tests (1, 1)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (3, 3)
|
Please Check this Bug |
…WithdrawalTransaction components
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
118-168: Ensure sort changes trigger data refetch infetchData
fetchDatabuildsorderby/orderfromview.sort, but the hook’s dependency array only includesview.page,view.perPage, andfilterArgs. Changing sort alone won’t recreatefetchDataor re-run the effect, so the API keeps using stale sort values.Consider depending on the full
view(or at leastview.sort) so sort changes refetch data:- }, [ view.page, view.perPage, filterArgs ] ); + }, [ view, filterArgs ] );This keeps behaviour for page/perPage while making sort reactive.
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
181-226: Include view sort infetchTransactionsdependencies to avoid stale sorting
fetchTransactionsreadsview.sort.field/view.sort.directionbut the dependency array only includesvendorId,view.page,view.perPage, andfilterArgs. Sort changes won’t recreate the callback or trigger the effect, so the API keeps using the previous sort.Update dependencies so sort changes refetch:
- }, [ vendorId, view.page, view.perPage, filterArgs ] ); + }, [ vendorId, view, filterArgs ] );This also removes unused deps (
view.page/view.perPagearen’t used in the query).
🧹 Nitpick comments (2)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
210-323: AlignfilterFieldsimplementation with the “memoize” intentThe comment says “Memoize filter fields to prevent recreation”, but
filterFieldsis a plain array literal, so it’s rebuilt on every render (including the inlineVendorAsyncSelectandDateRangePickerelements).Either remove/adjust the comment, or actually memoize:
-import DateRangePicker from '@src/components/DateRangePicker'; +import DateRangePicker from '@src/components/DateRangePicker'; @@ - // Memoize filter fields to prevent recreation - const filterFields = [ + // Memoize filter fields to avoid unnecessary re-renders + const filterFields = useMemo( + () => [ { id: 'vendor', @@ { id: 'date-range', @@ - }, - ]; + }, + ], [ + vendorFilter, + filterArgs, + dateAfter, + dateAfterText, + dateBefore, + dateBeforeText, + focusedInput, + ] );(You’d also need to import
useMemofrom@wordpress/element.)src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
302-400: Consider extracting a shared date‑range filter abstractionThe DateRangePicker + SimpleInput +
trn_datewiring here is almost identical to the date‑range filter inreverse-withdrawal/index.tsx. Duplicating this logic in two pages makes future fixes (formatting, default ranges, edge cases) harder.Consider extracting a small shared helper (component or hook) that encapsulates:
- Local date state mapping (after/before, text, focusedInput)
- Building/clearing
trn_dateinsidefilterArgs- Display formatting via
displayDateRangeThen use it in both pages to keep behaviour consistent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx(1 hunks)src/admin/dashboard/pages/reverse-withdrawal/index.tsx(1 hunks)src/components/modals/DokanModal.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/admin/dashboard/pages/reverse-withdrawal/AddReverseWithdrawModal.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
⏰ 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: api tests (1, 1)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (1, 3)
🔇 Additional comments (3)
src/components/modals/DokanModal.tsx (1)
35-36: Confirm button disable flag is correctly integrated
confirmButtonDisabledis added to the props, defaulted tofalse, and correctly combined withisSubmitting/loadingin the confirm button’sdisabledandloadingbehaviour. No functional or API regression concerns from this change.Also applies to: 61-62, 225-229
src/admin/dashboard/pages/reverse-withdrawal/index.tsx (1)
348-419: Overall DataViews wiring looks consistentHeader, stats cards, and
DataViewsconfiguration (namespace, pagination, filters, tabs, andonChangeView) are wired coherently and match the rest of the page state shape. No blocking issues spotted in this section.src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
260-465: Transactions page layout and DataViews integration look solidHeader/back button, info cards, and
DataViewssetup (namespace, fields, filters, tabs, pagination) form a coherent transactions view and align with the reverse‑withdrawal list page patterns. No additional functional issues spotted here beyond thefetchTransactionsdependency concern already noted.
|
Please Check this Bug |
…for consistent styling
…or improved layout consistency
…or consistent flex styling
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
181-226: Includeview.sortin dependencies to ensure sorting works correctly.This is a continuation of the previous review comment. The
fetchTransactionscallback readsview.sort.fieldandview.sort.direction(lines 190-191) but only depends on[vendorId, view.page, view.perPage, filterArgs]. When the user changes sort order without changing page or perPage, the callback won't update and will continue using stale sort values.- }, [ vendorId, view.page, view.perPage, filterArgs ] ); + }, [ vendorId, view.page, view.perPage, view.sort, filterArgs ] );Alternatively, depend on the entire
viewobject if you prefer:- }, [ vendorId, view.page, view.perPage, filterArgs ] ); + }, [ vendorId, view, filterArgs ] );Additional concern: Lines 189-191 use type assertions (
as any) that bypass TypeScript's type checking. Consider defining a proper type for the view state or using the correct type from the DataViews library.
🧹 Nitpick comments (2)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (2)
86-137: Extract common monetary value rendering logic to reduce duplication.The render functions for
debit,credit, andbalancefields share nearly identical logic (empty check, parseFloat, NaN check, negative value handling). This duplication increases maintenance burden and risk of inconsistency.Consider extracting a shared helper:
+const renderMonetaryValue = ( value ) => { + if ( value === '' ) { + return <span>--</span>; + } + const amt = parseFloat( value ); + if ( isNaN( amt ) ) { + return <span>--</span>; + } + return amt < 0 ? ( + <div className="flex">( { price( Math.abs( amt ) ) } )</div> + ) : ( + <div className="flex">{ price( amt ) }</div> + ); +}; + const fields = [ // ... other fields { id: 'debit', label: __( 'Debit', 'dokan-lite' ), - render: ( { item } ) => { - if ( item.debit === '' ) { - return <span>--</span>; - } - const amt = parseFloat( item.debit ); - if ( isNaN( amt ) ) { - return <span>--</span>; - } - return amt < 0 ? ( - <div className="flex">( { price( Math.abs( amt ) ) } )</div> - ) : ( - <div className="flex">{ price( amt ) }</div> - ); - }, + render: ( { item } ) => renderMonetaryValue( item.debit ), }, { id: 'credit', label: __( 'Credit', 'dokan-lite' ), - render: ( { item } ) => { - if ( item.credit === '' ) { - return <span>--</span>; - } - const amt = parseFloat( item.credit ); - if ( isNaN( amt ) ) { - return <span>--</span>; - } - return amt < 0 ? ( - <div className="flex">( { price( Math.abs( amt ) ) } )</div> - ) : ( - <div className="flex">{ price( amt ) }</div> - ); - }, + render: ( { item } ) => renderMonetaryValue( item.credit ), }, { id: 'balance', label: __( 'Balance', 'dokan-lite' ), - render: ( { item } ) => { - if ( item.balance === '' ) { - return <span>--</span>; - } - const amt = parseFloat( item.balance ); - if ( isNaN( amt ) ) { - return <span>--</span>; - } - return amt < 0 ? ( - <div className="flex">( { price( Math.abs( amt ) ) } )</div> - ) : ( - <div className="flex">{ price( amt ) }</div> - ); - }, + render: ( { item } ) => renderMonetaryValue( item.balance ), }, ];
141-148: Initializesortproperty in view state for consistency and type safety.The
fetchTransactionscallback (lines 190-191) accessesview.sort.fieldandview.sort.direction, but the initialviewstate doesn't include asortproperty. While the code safely falls back using optional chaining and defaults, explicitly initializingsortimproves clarity and type safety.const [ view, setView ] = useState( { perPage: 100, page: 1, search: '', type: 'table', layout: { density: 'comfortable' }, fields: fields.map( ( f ) => f.id ), + sort: { + field: 'trn_date', + direction: 'desc', + }, } );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/admin/dashboard/pages/reverse-withdrawal/ReverseWithdrawalTransaction.tsx (1)
types/externals.d.ts (1)
DataViews(59-59)
⏰ 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 (3, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: api tests (1, 1)
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
Filters
Bug Fixes
Style
Other
✏️ Tip: You can customize this high-level summary in your review settings.