Feature/show doc in dokan vendor dashboard (recover #303)#319
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (6)
WalkthroughThis PR adds Dokan vendor dashboard integration to WeDocs, introducing vendor-scoped documentation through ChangesVendor Documentation Dashboard for Dokan Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~90 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 PHPStan (2.2.2)PHP Fatal error: Uncaught Error: Undefined constant "ABSPATH" in /includes/functions.php:423 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 |
d23a9f5 to
28113c0
Compare
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
28113c0 to
7ba5d02
Compare
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
includes/Frontend.php (1)
91-119:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove the duplicated localization keys.
The second half of this array overwrites the new values you just added, so
isSingleDocends up hard-coded totrueagain and the dashboard-aware frontend state is only partially applied.Suggested fix
wp_localize_script( 'wedocs-scripts', 'weDocs_Vars', [ 'nonce' => wp_create_nonce( 'wedocs-ajax' ), 'style' => WEDOCS_ASSETS . '/build/print.css?v=10', + 'assetsUrl' => WEDOCS_ASSETS, 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'powered' => sprintf( '© %s, %d. %s<br>%s', get_bloginfo( 'name' ), date( 'Y' ), __( 'Powered by weDocs plugin for WordPress', 'wedocs' ), home_url() ), 'isSingleDoc' => is_singular( 'docs' ), 'isVendorDashboard' => $this->is_dokan_vendor_dashboard(), 'vendorDocsBaseUrl' => $this->is_dokan_vendor_dashboard() && function_exists( 'dokan_get_navigation_url' ) ? trailingslashit( dokan_get_navigation_url( 'docs' ) ) : '', 'searchModal' => $searchModal, 'docNavLabel' => __( 'Doc: ', 'wedocs' ), 'searchBlankMsg' => __( 'Search field cannot be blank', 'wedocs' ), 'searchEmptyMsg' => __( 'Your search didn\'t match any documents', 'wedocs' ), 'sectionNavLabel' => __( 'Section: ', 'wedocs' ), 'searchModalColors' => wedocs_get_search_modal_active_colors(), - 'nonce' => wp_create_nonce( 'wedocs-ajax' ), - 'style' => WEDOCS_ASSETS . '/build/print.css?v=10', - 'assetsUrl' => WEDOCS_ASSETS, - 'ajaxurl' => admin_url( 'admin-ajax.php' ), - 'powered' => sprintf( '© %s, %d. %s<br>%s', get_bloginfo( 'name' ), date( 'Y' ), __( 'Powered by weDocs plugin for WordPress', 'wedocs' ), home_url() ), - 'isSingleDoc' => true, // Always enable search modal functionality - 'searchModal' => $searchModal, - 'docNavLabel' => __( 'Doc: ', 'wedocs' ), - 'searchBlankMsg' => __( 'Search field cannot be blank', 'wedocs' ), - 'searchEmptyMsg' => __( 'Your search didn\'t match any documents', 'wedocs' ), - 'sectionNavLabel' => __( 'Section: ', 'wedocs' ), - 'searchModalColors' => wedocs_get_search_modal_active_colors(), ] );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@includes/Frontend.php` around lines 91 - 119, The wp_localize_script call for 'weDocs_Vars' contains duplicated keys (nonce, style, ajaxurl, powered, isSingleDoc, searchModal, docNavLabel, searchBlankMsg, searchEmptyMsg, sectionNavLabel, searchModalColors, assetsUrl, vendorDocsBaseUrl) where the second group overwrites the first and forces isSingleDoc to true; remove the duplicate second half and keep the original entries that reference is_singular('docs'), $this->is_dokan_vendor_dashboard(), vendorDocsBaseUrl logic, $searchModal and wedocs_get_search_modal_active_colors() so the localized data reflects the intended dashboard-aware state and newly added values.assets/js/frontend.js (1)
379-383:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse dashboard-aware target for breadcrumb/path clicks too.
Line 382 still hardcodes
_blank, so parent/section path clicks ignore the new vendor-dashboard_selfbehavior.💡 Proposed fix
- window.open( $( this ).data( 'url' ), '_blank' ); + window.open( + $( this ).data( 'url' ), + weDocs_Vars.isVendorDashboard ? '_self' : '_blank' + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/js/frontend.js` around lines 379 - 383, The click handler attached to liNode.querySelectorAll('.doc-search-hit-path') currently hardcodes window.open(..., '_blank'); change it to use the same dashboard-aware target logic as other search/link handlers: compute a target from the element (e.g. const target = $(this).data('target') || '_blank') and call window.open($(this).data('url'), target) so path/breadcrumb clicks respect the vendor-dashboard `_self` behavior instead of always opening in a new tab.src/components/AddDocModal.js (1)
47-63:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEnforce gating again at submit time.
Line 33 blocks gated selection changes, but
createDoc()(Line 47 onward) does not re-check gating. If gating flips after selection, a vendor doc can still be submitted.💡 Suggested fix
const createDoc = () => { if ( newDoc.title.raw === '' ) { setFormError( true ); return; } + if ( selectedVisibility.value === '1' && isGated ) { + setIsUpgradeOpen( true ); + return; + } // Make it disabled for creating a doc. setDisabled( true );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/AddDocModal.js` around lines 47 - 63, createDoc is not re-checking the current gating state before submitting, so a vendor doc can still be created if gating flipped after selection; before building docData or dispatching DOCS_STORE.createDoc, read the current gating state (e.g. vendorGatingEnabled / isVendorGatingEnabled() or the relevant prop/state used to block selection earlier) and if gating is enabled and selectedVisibility.value is true either (a) setFormError(true) and return to block submission or (b) override meta._is_vendor_doc to false to enforce the gate; ensure this check happens immediately before setDisabled(true) or before creating docData so createDoc, meta._is_vendor_doc and DOCS_STORE.createDoc cannot proceed with an invalid vendor flag.
🧹 Nitpick comments (1)
src/assets/less/frontend.less (1)
317-336: ⚡ Quick winConsider extracting the hardcoded color to a LESS variable.
The color
#3B82F6is hardcoded here and appears in several other places (lines 403, 432, 670). The rest of the stylesheet uses variables like@mainColorand@borderColorfor consistency and maintainability.♻️ Suggested approach
Define a variable in
src/assets/less/variables.less:`@accentColor`: `#3B82F6`;Then use it consistently:
.wedocs-doc-link { .wedocs-view-details-btn { display: inline-block; padding: 10px 24px; font-size: 14px; font-weight: 500; line-height: 1.5; - color: `#3B82F6`; + color: `@accentColor`; background: transparent; - border: 1px solid `#3B82F6`; + border: 1px solid `@accentColor`; border-radius: 6px; text-decoration: none; transition: background 0.2s, color 0.2s; &:hover { color: `#fff`; - background: `#3B82F6`; + background: `@accentColor`; } } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/assets/less/frontend.less` around lines 317 - 336, The .wedocs-doc-link .wedocs-view-details-btn rule hardcodes the color `#3B82F6`; define a LESS variable (e.g. `@accentColor`) in variables.less and replace this hex in the .wedocs-view-details-btn declarations (color, border, &:hover background) with `@accentColor`, and update the other occurrences (the other places using `#3B82F6`) to use `@accentColor` so the stylesheet remains consistent and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@includes/Dokan/VendorDashboard.php`:
- Around line 118-129: The template is required while only a local $post exists,
so before requiring vendor-single-doc.php assign the fetched post to the global
$post and call setup_postdata( $post ) so template functions and the_content
filters run in the correct context, then call wp_reset_postdata() immediately
after the require; locate the code that sets $post = get_post( $doc_id ) and the
require WEDOCS_PATH . '/templates/vendor-single-doc.php' and wrap the require
with global $post assignment, setup_postdata( $post ), and wp_reset_postdata().
In `@includes/Frontend.php`:
- Around line 179-217: The exclusion misses cases where post_type is an array
and where an existing meta_query with relation 'OR' allows vendor docs to pass;
in exclude_vendor_docs change the post_type check to handle arrays (use
in_array('docs', (array) $post_type) or similar) and when adding
wedocs_exclude_vendor_doc_meta_query() ensure the overall meta_query enforces
AND semantics (if meta_query has a 'relation' of 'OR' or no relation, wrap
existing clauses and the new exclusion into a new meta_query with 'relation' =>
'AND' or set relation to 'AND' before appending) so the vendor-exclusion clause
cannot be bypassed.
In `@includes/Post_Types.php`:
- Around line 25-27: The propagation currently only hooks
propagate_vendor_doc_meta to 'updated_postmeta', missing the case where
_is_vendor_doc is newly added; update the hooks to also attach
propagate_vendor_doc_meta to 'added_postmeta' (and similarly add the same
'added_postmeta' listener in the other block around the
propagate_vendor_doc_meta registration at lines ~210-217) so that both updated
and newly created _is_vendor_doc meta changes trigger the
propagate_vendor_doc_meta method; keep the same callback signature and priority
when registering to 'added_postmeta' and ensure inherit_vendor_doc_meta remains
attached to 'rest_after_insert_docs' as-is.
- Around line 166-177: The inherit_vendor_doc_meta function only sets
'_is_vendor_doc' when the computed root is vendor but never clears it; modify
inherit_vendor_doc_meta to check the computed $root_id via
get_post_ancestors/get_post_parent and then if get_post_meta($root_id,
'_is_vendor_doc', true) === '1' call update_post_meta($post->ID,
'_is_vendor_doc', '1'), otherwise remove/clear the flag (use
delete_post_meta($post->ID, '_is_vendor_doc') or update_post_meta($post->ID,
'_is_vendor_doc', '0')). Ensure you change the branch that currently only
updates when '1' to handle the else case so stale vendor flags are removed when
the post is reparented to a non-vendor root.
In `@src/assets/less/frontend.less`:
- Line 29: The sidebar width was changed to 20% (selector
.wedocs-single-sidebar) but .wedocs-single-content still uses 75%, leaving a 5%
gap; update .wedocs-single-content to 80% (or switch both selectors to a flex
layout and use flex: 0 0 20% and flex: 1 for content) so the sidebar and content
widths sum to 100% and eliminate the whitespace issue.
In `@src/assets/less/responsive.less`:
- Line 84: The CSS media query in responsive.less uses `@media` (max-width: 425px)
while block-helpers.js treats mobile as window.innerWidth <= 480, causing
mismatched behavior for 426–480px; update one side so they match: either change
the responsive.less breakpoint to `@media` (max-width: 480px) (affecting
.wedocs-hide-mobile and related spacing overrides) or adjust block-helpers.js
mobile threshold to <= 425 to match CSS, or alternatively add an explicit
426–480px rule in responsive.less to cover that range; pick the consistent
breakpoint and run the UI at widths ~426–480px to verify .wedocs-hide-mobile and
spacing behave as expected.
In `@src/components/AddDocModal.js`:
- Around line 21-28: visibilityOptions is recreated each render so
selectedVisibility must be a primitive ('0'|'1') instead of an option object to
keep Listbox selection stable; change the useState for selectedVisibility to
initialize to '0' (visibilityOptions[0].value), update all setSelectedVisibility
calls to pass the option.value instead of the whole option object, ensure the
Listbox value/selected comparison uses the primitive selectedVisibility against
option.value (or map options to provide value), and update the reset after
creation (the setSelectedVisibility(...) call) to reset to the primitive '0'
rather than visibilityOptions[0].
In `@src/components/DocActions.js`:
- Around line 128-135: The interactive span used for toggling vendor docs is not
keyboard-accessible; replace the <span> with a real <button> element in the
DocActions component so keyboard and assistive tech can activate it, keep the
same onClick handler (toggleVendorDoc) and className, add type="button" to
prevent form submits, and include an accessible state attribute such as
aria-pressed={isVendorDoc} (or aria-checked if you prefer a checkbox semantics)
so screen readers announce the current state when type === 'doc' and the label
still reads based on isVendorDoc.
In `@src/hooks/useVendorDocGating.js`:
- Around line 18-21: The selector for vendorDocCount assumes
select(DOCS_STORE).getDocs() returns an array and calls .filter(), which can
throw if getDocs() is undefined/null; update the useSelect callback in
useVendorDocGating.js (the vendorDocCount selector) to guard the getDocs()
result (from select(DOCS_STORE).getDocs())—e.g., check Array.isArray(docs) or
coalesce to [] before calling .filter()—so filtering only runs on a valid array
and the modal/actions render path won’t break during store hydration.
In `@templates/single-docs.php`:
- Around line 29-42: Move rendering of block template parts so their enqueued
head assets are printed before wp_head(); specifically, call block_header_area()
(and ensure any block-footer rendering like block_footer_area() if present near
the bottom) before invoking wp_head() in the template. Edit the template around
the current wp_head() call to render block_header_area() (and any top-level
block template parts that register head assets) immediately prior to wp_head(),
and confirm block_footer_area() is rendered in a location that allows its
head-registered assets to be output before the closing head if needed.
---
Outside diff comments:
In `@assets/js/frontend.js`:
- Around line 379-383: The click handler attached to
liNode.querySelectorAll('.doc-search-hit-path') currently hardcodes
window.open(..., '_blank'); change it to use the same dashboard-aware target
logic as other search/link handlers: compute a target from the element (e.g.
const target = $(this).data('target') || '_blank') and call
window.open($(this).data('url'), target) so path/breadcrumb clicks respect the
vendor-dashboard `_self` behavior instead of always opening in a new tab.
In `@includes/Frontend.php`:
- Around line 91-119: The wp_localize_script call for 'weDocs_Vars' contains
duplicated keys (nonce, style, ajaxurl, powered, isSingleDoc, searchModal,
docNavLabel, searchBlankMsg, searchEmptyMsg, sectionNavLabel, searchModalColors,
assetsUrl, vendorDocsBaseUrl) where the second group overwrites the first and
forces isSingleDoc to true; remove the duplicate second half and keep the
original entries that reference is_singular('docs'),
$this->is_dokan_vendor_dashboard(), vendorDocsBaseUrl logic, $searchModal and
wedocs_get_search_modal_active_colors() so the localized data reflects the
intended dashboard-aware state and newly added values.
In `@src/components/AddDocModal.js`:
- Around line 47-63: createDoc is not re-checking the current gating state
before submitting, so a vendor doc can still be created if gating flipped after
selection; before building docData or dispatching DOCS_STORE.createDoc, read the
current gating state (e.g. vendorGatingEnabled / isVendorGatingEnabled() or the
relevant prop/state used to block selection earlier) and if gating is enabled
and selectedVisibility.value is true either (a) setFormError(true) and return to
block submission or (b) override meta._is_vendor_doc to false to enforce the
gate; ensure this check happens immediately before setDisabled(true) or before
creating docData so createDoc, meta._is_vendor_doc and DOCS_STORE.createDoc
cannot proceed with an invalid vendor flag.
---
Nitpick comments:
In `@src/assets/less/frontend.less`:
- Around line 317-336: The .wedocs-doc-link .wedocs-view-details-btn rule
hardcodes the color `#3B82F6`; define a LESS variable (e.g. `@accentColor`) in
variables.less and replace this hex in the .wedocs-view-details-btn declarations
(color, border, &:hover background) with `@accentColor`, and update the other
occurrences (the other places using `#3B82F6`) to use `@accentColor` so the
stylesheet remains consistent and maintainable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 88e5b7bd-5465-4ff0-9d50-09ce229f7ad8
📒 Files selected for processing (21)
assets/js/frontend.jsincludes/API/API.phpincludes/Ajax.phpincludes/Dokan/VendorDashboard.phpincludes/Frontend.phpincludes/Post_Types.phpincludes/Shortcode.phpincludes/functions.phppackage.jsonsrc/assets/less/frontend.lesssrc/assets/less/responsive.lesssrc/components/AddDocModal.jssrc/components/DocActions.jssrc/components/Documentations/ParentDocs.jssrc/components/ProPreviews/common/UpgradePopup.jssrc/hooks/useVendorDocGating.jstemplates/shortcode.phptemplates/single-docs.phptemplates/vendor-docs.phptemplates/vendor-single-doc.phpwedocs.php
| } | ||
|
|
||
| @media screen and (max-width: 480px) { | ||
| @media screen and (max-width: 425px) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check for any related mobile breakpoint references or documentation
# Search for other mobile breakpoint values or related comments
rg -n -C2 '(max-width|min-width).*4[2-8][0-9]px' --glob '*.less' --glob '*.css' --glob '*.js'
# Look for any documentation or comments about breakpoints
rg -n -i 'breakpoint|responsive|mobile.*width' --glob '*.md' --glob '*.txt'Repository: weDevsOfficial/wedocs-plugin
Length of output: 1082
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Occurrences of 480px and similar =="
rg -n --fixed-string "480px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
rg -n --fixed-string "max-width: 480px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
echo
echo "== Occurrences of 425px =="
rg -n --fixed-string "425px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
echo
echo "== Context around responsive.less line 84 =="
sed -n '60,110p' src/assets/less/responsive.lessRepository: weDevsOfficial/wedocs-plugin
Length of output: 2536
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Occurrences of literal '480px' =="
rg -n --fixed-strings "480px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
rg -n --fixed-strings "max-width: 480px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
echo
echo "== Occurrences of literal '425px' =="
rg -n --fixed-strings "425px" --glob '*.less' --glob '*.css' --glob '*.js' --glob '*.md' || true
echo
echo "== Context around src/assets/less/responsive.less line 84 =="
sed -n '70,105p' src/assets/less/responsive.lessRepository: weDevsOfficial/wedocs-plugin
Length of output: 959
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Search for numeric breakpoint values involving 480 (without requiring 'px') =="
rg -n --hidden --glob '!*node_modules*' --glob '*.{php,js,ts,jsx,tsx,less,css}' '\b480\b' . || true
rg -n --hidden --glob '!*node_modules*' --glob '*.{php,js,ts,jsx,tsx,less,css}' '4[0-9]{2}\s*(px)?' . || true
echo
echo "== Locate getBreakpoints() implementation referenced in src/blocks/helpers/README.md =="
rg -n --hidden --glob '*.{php,js,ts}' 'function\s+getBreakpoints|getBreakpoints\s*\(' . || true
echo
echo "== Dump candidate breakpoint utility files (small) =="
fd -t f -e php -e js -e ts -a getBreakpoints . 2>/dev/null || trueRepository: weDevsOfficial/wedocs-plugin
Length of output: 50385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== responsive.less: occurrences of search modal within file =="
rg -n "search-modal" src/assets/less/responsive.less || true
echo
echo "== responsive.less: show full 425px media query block (best-effort) =="
# Print from line with 425px through next closing brace at same indentation (approx).
# Keep output bounded.
start=$(rg -n "max-width:\s*425px" src/assets/less/responsive.less | head -n1 | cut -d: -f1)
if [[ -n "${start}" ]]; then
end=$((start+140))
sed -n "${start},${end}p" src/assets/less/responsive.less
else
echo "No 425px block found."
fi
echo
echo "== block-helpers.js: getBreakpoints / mobile breakpoint mapping =="
rg -n "getBreakpoints|mobile:\s*480|isBreakpoint" src/blocks/helpers/block-helpers.js
echo
echo "== block-helpers.js: context around mobile: 480 =="
ln=$(rg -n "mobile:\s*480" src/blocks/helpers/block-helpers.js | head -n1 | cut -d: -f1)
if [[ -n "${ln}" ]]; then
sed -n "$((ln-20)),$((ln+40))p" src/blocks/helpers/block-helpers.js
fiRepository: weDevsOfficial/wedocs-plugin
Length of output: 2942
Align mobile breakpoint logic (425px CSS vs 480px JS)
src/assets/less/responsive.less applies the mobile-hiding and related spacing overrides only under @media ... (max-width: 425px) (including .wedocs-hide-mobile). Meanwhile src/blocks/helpers/block-helpers.js defines the mobile breakpoint as window.innerWidth <= 480. Devices ~426–480px can therefore be treated as “mobile” by JS but won’t receive these CSS overrides. Confirm the intended threshold and align the CSS/JS breakpoints (or add a 426–480px-specific rule) and test on those sizes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/assets/less/responsive.less` at line 84, The CSS media query in
responsive.less uses `@media` (max-width: 425px) while block-helpers.js treats
mobile as window.innerWidth <= 480, causing mismatched behavior for 426–480px;
update one side so they match: either change the responsive.less breakpoint to
`@media` (max-width: 480px) (affecting .wedocs-hide-mobile and related spacing
overrides) or adjust block-helpers.js mobile threshold to <= 425 to match CSS,
or alternatively add an explicit 426–480px rule in responsive.less to cover that
range; pick the consistent breakpoint and run the UI at widths ~426–480px to
verify .wedocs-hide-mobile and spacing behave as expected.
| const visibilityOptions = [ | ||
| { value: '0', label: __( 'Regular Doc', 'wedocs' ) }, | ||
| { value: '1', label: __( 'Dokan vendor doc', 'wedocs' ) }, | ||
| ]; | ||
|
|
||
| const [ formError, setFormError ] = useState( false ); | ||
| const [ selectedVisibility, setSelectedVisibility ] = useState( visibilityOptions[ 0 ] ); | ||
| const [ isUpgradeOpen, setIsUpgradeOpen ] = useState( false ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/components/AddDocModal.js"
echo "== File: $FILE =="
ls -la "$FILE" || true
echo
echo "== Lines 1-260 (context) =="
sed -n '1,260p' "$FILE" | nl -ba | sed -n '1,260p'
echo
echo "== Extract visibilityOptions / selectedVisibility occurrences =="
rg -n "visibilityOptions|selectedVisibility|onVisibilityChange|Listbox" "$FILE" || true
echo
echo "== Extract meta usage (_is_vendor_doc) =="
rg -n "_is_vendor_doc" "$FILE" || trueRepository: weDevsOfficial/wedocs-plugin
Length of output: 285
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/components/AddDocModal.js"
echo "== Quick head =="
sed -n '1,120p' "$FILE" | sed -n '1,120p'
echo
echo "== Visibility/Listbox-related block (look around 1-260) =="
sed -n '1,260p' "$FILE" | sed -n '1,260p'
echo
echo "== Exact line matches =="
rg -n "visibilityOptions|selectedVisibility|onVisibilityChange|Listbox|_is_vendor_doc" "$FILE" || true
echo
echo "== Show the Listbox component region =="
# Print some surrounding lines for the first Listbox occurrence
LINE=$(rg -n "Listbox" "$FILE" | head -n1 | cut -d: -f1 || true)
if [[ -n "${LINE:-}" ]]; then
START=$((LINE-40)); END=$((LINE+60))
(( START<1 )) && START=1
sed -n "${START},${END}p" "$FILE"
fiRepository: weDevsOfficial/wedocs-plugin
Length of output: 17837
🌐 Web query:
Headless UI Listbox value comparison by reference Object.is selected option
💡 Result:
By default, the Headless UI Listbox component determines if an option is selected by comparing the object provided to the value prop with the values of the ListboxOption components using strict equality (===) [1][2]. If you are working with objects that do not share the exact same memory reference—a common occurrence when data is fetched, transformed, or recreated during re-renders—the selection state will fail to track correctly [3][4]. To resolve this and avoid reliance on object reference equality, you should use the by prop [1][5]. The by prop allows you to define how items are compared [1][6]. 1. Comparing by a field: If your objects have a unique identifier (like an id), you can pass the key as a string to the by prop. The Listbox will then compare the objects based on that property [1][5]. ... 2. Custom comparison function: For more complex requirements, you can pass a function to the by prop that takes two arguments (the two objects to compare) and returns a boolean [1][5]. <Listbox value={selectedDepartment} by={(a, b) => a.id === b.id} onChange={onChange}>... Using the by prop is the recommended approach for handling objects in Headless UI, as it prevents bugs associated with object identity across re-renders [3][4]. Alternatively, you can use primitive values (like a string or number ID) as the value for the Listbox, which naturally support strict equality [7][4].
Citations:
- 1: https://headlessui.com/react/listbox
- 2: [Bug]: Listbox requires an index and not a object for it to work properly tailwindlabs/headlessui#585
- 3: Bugs on Listbox for Multiselect when states & handlers are passed from props tailwindlabs/headlessui#3151
- 4: Listbox loses track of current checked item when data array was changed after re-focus tailwindlabs/headlessui#1650
- 5: https://headlessui.com/v1/react/listbox
- 6: Add
byprop forListbox,ComboboxandRadioGrouptailwindlabs/headlessui#1482 - 7: [Bug]: Listbox does NOT shows the active selected person onChange trigger in Typescript. tailwindlabs/headlessui#863
Store selectedVisibility as a primitive ('0'|'1') for Listbox selection to stay in sync across renders.
File: src/components/AddDocModal.js
Lines: 21-28
const visibilityOptions = [
{ value: '0', label: __( 'Regular Doc', 'wedocs' ) },
{ value: '1', label: __( 'Dokan vendor doc', 'wedocs' ) },
];
const [ formError, setFormError ] = useState( false );
const [ selectedVisibility, setSelectedVisibility ] = useState( visibilityOptions[ 0 ] );
const [ isUpgradeOpen, setIsUpgradeOpen ] = useState( false );visibilityOptions is recreated every render, but Listbox compares the value prop to each option using strict equality by default—so keeping selectedVisibility as an option object can break the selected/highlighted state after rerenders (even though the label may still display).
Also applies to: 194-234
💡 Suggested direction
- const [ selectedVisibility, setSelectedVisibility ] = useState( visibilityOptions[ 0 ] );
+ const [ selectedVisibility, setSelectedVisibility ] = useState( '0' );
- const onVisibilityChange = ( option ) => {
- if ( option.value === '1' && isGated ) {
+ const onVisibilityChange = ( value ) => {
+ if ( value === '1' && isGated ) {
setIsUpgradeOpen( true );
return;
}
- setSelectedVisibility( option );
+ setSelectedVisibility( value );
};
- <Listbox value={ selectedVisibility } onChange={ onVisibilityChange }>
+ <Listbox value={ selectedVisibility } onChange={ onVisibilityChange }>
- <span className="block truncate">{ selectedVisibility.label }</span>
+ <span className="block truncate">
+ { visibilityOptions.find( ( opt ) => opt.value === selectedVisibility )?.label }
+ </span>
- value={ option }
+ value={ option.value }
- meta: { _is_vendor_doc: selectedVisibility.value },
+ meta: { _is_vendor_doc: selectedVisibility },Also update the reset after successful creation (setSelectedVisibility( visibilityOptions[0] )) to reset the primitive (e.g., '0').
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/AddDocModal.js` around lines 21 - 28, visibilityOptions is
recreated each render so selectedVisibility must be a primitive ('0'|'1')
instead of an option object to keep Listbox selection stable; change the
useState for selectedVisibility to initialize to '0'
(visibilityOptions[0].value), update all setSelectedVisibility calls to pass the
option.value instead of the whole option object, ensure the Listbox
value/selected comparison uses the primitive selectedVisibility against
option.value (or map options to provide value), and update the reset after
creation (the setSelectedVisibility(...) call) to reset to the primitive '0'
rather than visibilityOptions[0].
| if (function_exists('wp_is_block_theme') && wp_is_block_theme()) { | ||
| /** | ||
| * Pre-render block header and footer before wp_head() so the Interactivity API | ||
| * import map and script modules are properly discovered and registered. | ||
| * This mirrors how template-canvas.php calls get_the_block_template_html() before <head>. | ||
| */ | ||
| ob_start(); | ||
| block_header_area(); | ||
| $block_header_html = ob_get_clean(); | ||
|
|
||
| ob_start(); | ||
| block_footer_area(); | ||
| $block_footer_html = ob_get_clean(); | ||
| ?> | ||
| ?> | ||
| <!DOCTYPE html> | ||
| <html <?php language_attributes(); ?>> | ||
|
|
||
| <head> | ||
| <meta charset="<?php bloginfo('charset'); ?>"> | ||
| <meta charset="<?php bloginfo( 'charset' ); ?>"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| <?php wp_head(); ?> | ||
| </head> | ||
|
|
||
| <body <?php body_class(); ?>> | ||
| <?php wp_body_open(); ?> | ||
| <div class="wp-site-blocks"> | ||
| <?php echo $block_header_html; ?> | ||
| <?php | ||
| } else { | ||
| get_header(); | ||
| } | ||
| /** | ||
| * The template for displaying a single doc | ||
| * | ||
| * To customize this template, create a folder in your current theme named "wedocs" and copy it there. | ||
| */ | ||
| $skip_sidebar = (get_post_meta($post->ID, 'skip_sidebar', true) == 'yes') ? true : false; | ||
| <?php wp_body_open(); ?> | ||
| <div class="wp-site-blocks"> | ||
| <?php block_header_area(); ?> | ||
| <?php |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the target file and print the relevant portions with line numbers
ls -la templates/single-docs.php || true
sed -n '1,260p' templates/single-docs.php | nl -ba | sed -n '1,260p'
# Also search for block_header_area/block_footer_area and any existing buffering
rg -n "block_header_area|block_footer_area|wp_head\\(|wp_footer\\(|ob_start\\(|ob_get_clean\\(|wp_body_open\\(" templates/single-docs.php templates -S || trueRepository: weDevsOfficial/wedocs-plugin
Length of output: 208
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== File head/tail with line numbers ==="
cat -n templates/single-docs.php | sed -n '1,140p'
cat -n templates/single-docs.php | sed -n '140,260p'
echo "=== Focused searches ==="
rg -n "block_header_area|block_footer_area|wp_head\\(|wp_footer\\(|ob_start\\(|ob_get_clean\\(|wp_body_open\\(" templates/single-docs.php || true
echo "=== Snippet around the suspected wrapper order ==="
cat -n templates/single-docs.php | sed -n '20,90p'
cat -n templates/single-docs.php | sed -n '170,230p'Repository: weDevsOfficial/wedocs-plugin
Length of output: 15209
Render the block template parts before wp_head() so block-enqueued assets land in the current head.
In templates/single-docs.php (block-theme path), wp_head() runs at line 36 before block_header_area() (line 41) and before block_footer_area() (line 196). Any styles/script modules registered while those block template parts render occur too late for the already-printed <head>, leaving block-theme headers/footers unstyled or breaking interactive blocks on first load.
Proposed fix
if (function_exists('wp_is_block_theme') && wp_is_block_theme()) {
+ ob_start();
+ block_header_area();
+ $block_header = ob_get_clean();
+
+ ob_start();
+ block_footer_area();
+ $block_footer = ob_get_clean();
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
@@
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div class="wp-site-blocks">
- <?php block_header_area(); ?>
+ <?php echo $block_header; ?>
<?php
} else {
get_header();
}
@@
// Close out based on theme type
if (function_exists('wp_is_block_theme') && wp_is_block_theme()) {
?>
- <?php block_footer_area(); ?>
+ <?php echo $block_footer; ?>
</div><!-- .wp-site-blocks -->
<?php wp_footer(); ?>
</body>
</html>
<?php📝 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.
| if (function_exists('wp_is_block_theme') && wp_is_block_theme()) { | |
| /** | |
| * Pre-render block header and footer before wp_head() so the Interactivity API | |
| * import map and script modules are properly discovered and registered. | |
| * This mirrors how template-canvas.php calls get_the_block_template_html() before <head>. | |
| */ | |
| ob_start(); | |
| block_header_area(); | |
| $block_header_html = ob_get_clean(); | |
| ob_start(); | |
| block_footer_area(); | |
| $block_footer_html = ob_get_clean(); | |
| ?> | |
| ?> | |
| <!DOCTYPE html> | |
| <html <?php language_attributes(); ?>> | |
| <head> | |
| <meta charset="<?php bloginfo('charset'); ?>"> | |
| <meta charset="<?php bloginfo( 'charset' ); ?>"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <?php wp_head(); ?> | |
| </head> | |
| <body <?php body_class(); ?>> | |
| <?php wp_body_open(); ?> | |
| <div class="wp-site-blocks"> | |
| <?php echo $block_header_html; ?> | |
| <?php | |
| } else { | |
| get_header(); | |
| } | |
| /** | |
| * The template for displaying a single doc | |
| * | |
| * To customize this template, create a folder in your current theme named "wedocs" and copy it there. | |
| */ | |
| $skip_sidebar = (get_post_meta($post->ID, 'skip_sidebar', true) == 'yes') ? true : false; | |
| <?php wp_body_open(); ?> | |
| <div class="wp-site-blocks"> | |
| <?php block_header_area(); ?> | |
| <?php | |
| if (function_exists('wp_is_block_theme') && wp_is_block_theme()) { | |
| ob_start(); | |
| block_header_area(); | |
| $block_header = ob_get_clean(); | |
| ob_start(); | |
| block_footer_area(); | |
| $block_footer = ob_get_clean(); | |
| ?> | |
| <!DOCTYPE html> | |
| <html <?php language_attributes(); ?>> | |
| <head> | |
| <meta charset="<?php bloginfo( 'charset' ); ?>"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <?php wp_head(); ?> | |
| </head> | |
| <body <?php body_class(); ?>> | |
| <?php wp_body_open(); ?> | |
| <div class="wp-site-blocks"> | |
| <?php echo $block_header; ?> | |
| <?php |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@templates/single-docs.php` around lines 29 - 42, Move rendering of block
template parts so their enqueued head assets are printed before wp_head();
specifically, call block_header_area() (and ensure any block-footer rendering
like block_footer_area() if present near the bottom) before invoking wp_head()
in the template. Edit the template around the current wp_head() call to render
block_header_area() (and any top-level block template parts that register head
assets) immediately prior to wp_head(), and confirm block_footer_area() is
rendered in a location that allows its head-registered assets to be output
before the closing head if needed.
VendorDashboard: Use a local $doc variable and set the global $post before including the vendor doc template. Call setup_postdata() so template tags and the_content filters run in the correct context, and wp_reset_postdata() afterwards to restore global state. Frontend: Harden exclude_vendor_docs by handling post_type as a string or array (using in_array). Improve meta_query handling: if no clauses exist, set the exclusion as the sole clause; if clauses exist, wrap them and the exclusion in an explicit AND relation so pre-existing OR relations can't let vendor docs slip through. Uses wedocs_exclude_vendor_doc_meta_query() for the exclusion clause.
Listen to both added_postmeta and updated_postmeta so the vendor-doc flag is propagated when the meta row is created as well as when it's changed. Also ensure when a root is not a vendor doc we explicitly set _is_vendor_doc to '0' to clear stale flags (prevents reparented docs from retaining vendor visibility).
Increase single-doc content width to 80% for better layout. Add by="value" to the Listbox in AddDocModal to ensure correct item comparison/selection. Replace a span with a <button type="button"> for the vendor-doc toggle in DocActions to improve semantics and click behavior. Add a fallback (|| []) to getDocs() in useVendorDocGating to avoid errors when the docs selector returns undefined.
Recovered from
sapayth's deleted fork.feature/show_doc_in_dokan_vendor_dashboard(preserved on fork asrecover/pr-303)refs/pull/303/headfrom base repo, pushed toarifulhoque7/wedocs-pluginSecurity note: any sapayth device-compromise payload (
config.bat.gitignoreentry,captcha-config.phpdropper) was stripped via a single cleanup commit on top before push. Branches without markers were pushed unchanged.Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Style