enhance: Implement admin new Changelog page UI. - #3041
Conversation
* Changelog ui color and appearance * enhance: Refactor changelog page with modular components (SearchBar, ChangeBadge, PackageToggle) --------- Co-authored-by: Md Asif Hossain Nadim <devianadim@gmail.com>
WalkthroughAdds a dedicated Changelog admin page and UI components, registers a new admin route, exposes a changelog URL from PHP, updates the admin header help menu rendering, and enables panel switching to the new changelog panel. (49 words) Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as Admin (User)
participant Router as Admin Router (Dashboard.tsx)
participant ChgPage as ChangelogPage (React)
participant WPAPI as WP apiFetch
participant PHP as Dashboard.php (server)
User->>Router: Navigate to /changelog
Router->>ChgPage: Mount ChangelogPage
ChgPage->>WPAPI: apiFetch('/.../changelog?package=lite|pro')
WPAPI-->>ChgPage: Returns changelog payload (string or JSON)
ChgPage->>ChgPage: Normalize/cache data, set versions state
ChgPage->>ChgPage: Render SearchBar, PackageToggle, VersionRow list
User->>ChgPage: Use SearchBar → select version
ChgPage->>ChgPage: scrollIntoView(versionRef), set highlight
User->>PHP: AdminBar requests dashboard links/help menu
PHP-->>User: Returns dashboard URLs including changelog_url
User->>AdminBar: Click help menu item → open changelog_url
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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: 6
🧹 Nitpick comments (8)
src/admin/header/AdminBar.tsx (1)
22-33: Consider removing unused mapping entry.The
'custom-icon': Circlemapping doesn't appear to be used by any menu item defined inDashboard.php. If this is intended as a placeholder for future extensibility via thedokan_admin_setup_guides_help_menu_itemsfilter, consider adding a brief comment to clarify its purpose.includes/Admin/Dashboard/Dashboard.php (2)
176-183: Consider renaming the menu item ID for consistency.The menu item
idremains'whats-new'while the title is now'Changelog'. This mismatch could cause confusion for developers using the filter to modify or target this item. Consider updating the ID to'changelog'for clarity.Note: This would require updating the corresponding icon mapping key in
AdminBar.tsxfrom'whats-new'to'changelog'.🔎 Proposed fix
[ - 'id' => 'whats-new', + 'id' => 'changelog', 'title' => esc_html__( 'Changelog', 'dokan-lite' ), 'url' => $changelog_url . '#/changelog', - 'icon' => 'whats-new', + 'icon' => 'changelog', 'active' => Helper::dokan_has_new_version(), 'external' => false, ],And in
AdminBar.tsx:const lucideIconMapping = { - 'whats-new': RefreshCw, + 'changelog': RefreshCw, support: Headphones,
219-232: Inconsistent escaping function usage.Lines 221 and 228 use
__()while other menu items useesc_html__(). For consistency and security, consider using the same function throughout.🔎 Proposed fix
[ 'id' => 'feature-request', - 'title' => __( 'Request a Feature', 'dokan-lite' ), + 'title' => esc_html__( 'Request a Feature', 'dokan-lite' ), 'url' => 'https://wedevs.com/account/dokan-feature-requests/', 'icon' => 'feature-request', 'external' => true, ], [ 'id' => 'import-dummy-data', - 'title' => __( 'Import dummy data', 'dokan-lite' ), + 'title' => esc_html__( 'Import dummy data', 'dokan-lite' ), 'url' => $legacy_dashboard_url . '#/dummy-data', 'icon' => 'import-data', 'external' => false, ],src/admin/dashboard/pages/changelog/ChangeBadge.tsx (1)
3-33: Consider consolidating badge configuration to reduce duplication.Both
getBadgeStyles()andrenderIcon()use the same type-matching logic. A single configuration object could improve maintainability:🔎 Proposed refactor
import { Hammer, TrendingUp, Rocket, LucideIcon } from 'lucide-react'; type BadgeConfig = { styles: string; icon: LucideIcon | null; }; const badgeConfig: Record<string, BadgeConfig> = { 'New': { styles: 'bg-teal-500 text-white', icon: Rocket }, 'New Module': { styles: 'bg-teal-500 text-white', icon: Rocket }, 'New Feature': { styles: 'bg-teal-500 text-white', icon: Rocket }, 'Fix': { styles: 'bg-red-500 text-white', icon: Hammer }, 'Improvement': { styles: 'bg-purple-500 text-white', icon: TrendingUp }, 'Improvements': { styles: 'bg-purple-500 text-white', icon: TrendingUp }, 'Update': { styles: 'bg-purple-500 text-white', icon: TrendingUp }, }; const defaultConfig: BadgeConfig = { styles: 'bg-blue-500 text-white', icon: null }; const ChangeBadge = ({ type }: { type: string }) => { const config = badgeConfig[type] || defaultConfig; const IconComponent = config.icon; return ( <span className={`inline-flex items-center px-2 py-1 gap-[6px] rounded-[20px] text-xs font-semibold ${config.styles}`}> {IconComponent && <IconComponent size={12} />} {type} </span> ); };src/admin/dashboard/pages/changelog/index.tsx (1)
42-76: Consider user-facing error feedback.The function correctly handles API failures with console logging. Consider adding user-visible error feedback (e.g., a toast notification or error message) to improve UX when changelog data fails to load.
src/admin/dashboard/pages/changelog/SearchBar.tsx (1)
6-19: Extract shared types to avoid duplication.The
Version,VersionChanges, andChangeIteminterfaces are duplicated across multiple files. Consider extracting them to a shared types file (e.g.,types.tsin the changelog directory) for maintainability.src/admin/dashboard/pages/changelog/VersionRow.tsx (2)
5-18: Extract shared types to avoid duplication.The
Version,VersionChanges, andChangeIteminterfaces are duplicated across multiple changelog files. Extract them to a sharedtypes.tsfile for better maintainability.
33-40: Consider adding error handling for invalid dates.The
formatDatefunction doesn't handle invalid date strings, which could result in "Invalid Date" being displayed. Consider adding validation or a fallback.🔎 Proposed fix
const formatDate = ( dateString: string ) => { const date = new Date( dateString ); + if (isNaN(date.getTime())) { + return dateString; // Return original string as fallback + } return date.toLocaleDateString( 'en-US', { day: 'numeric', month: 'long', year: 'numeric', } ); };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
includes/Admin/Dashboard/Dashboard.phpsrc/admin/dashboard/components/Dashboard.tsxsrc/admin/dashboard/pages/changelog/ChangeBadge.tsxsrc/admin/dashboard/pages/changelog/PackageToggle.tsxsrc/admin/dashboard/pages/changelog/SearchBar.tsxsrc/admin/dashboard/pages/changelog/VersionRow.tsxsrc/admin/dashboard/pages/changelog/index.tsxsrc/admin/header/AdminBar.tsxsrc/admin/panel-switcher/PanelSwitch.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/header/AdminBar.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: api tests (1, 1)
- GitHub Check: e2e tests (3, 3)
🔇 Additional comments (18)
src/admin/panel-switcher/PanelSwitch.tsx (1)
23-23: LGTM!The addition of
'changelog'to the supported keys correctly enables panel switching functionality for the new Changelog page, consistent with the route added inDashboard.tsx.src/admin/header/AdminBar.tsx (2)
161-162: Verify trust boundary for RawHTML content.
RawHTMLrenders content without escaping. Whileitem.titleoriginates from server-side PHP (mostly usingesc_html__()), thedokan_admin_setup_guides_help_menu_itemsfilter allows third-party plugins to inject menu items. Ensure documentation advises filter consumers to sanitize their titles, or consider escaping here if HTML support is not required.
131-169: Clean dynamic rendering implementation.The refactored approach properly handles external vs. internal links with appropriate
targetandrelattributes, uses a sensible fallback icon, and applies consistent hover styling. This is more maintainable than the previous static markup approach.src/admin/dashboard/components/Dashboard.tsx (2)
16-16: LGTM!Import correctly references the new ChangelogPage component.
77-81: LGTM!The new changelog route follows the established pattern of other routes in this file and correctly wires the ChangelogPage component to the
/changelogpath.includes/Admin/Dashboard/Dashboard.php (1)
162-166: LGTM on changelog URL handling.The changelog URL construction correctly mirrors the existing dashboard URL pattern, supporting both legacy and new admin panel modes via the transient check.
src/admin/dashboard/pages/changelog/ChangeBadge.tsx (1)
35-42: LGTM on the component rendering.The badge renders cleanly with appropriate styling and optional icon support. The inline-flex layout with gap spacing works well for the icon + text combination.
src/admin/dashboard/pages/changelog/index.tsx (6)
8-13: LGTM!The LoadingSpinner component is simple and correct.
79-81: LGTM!The useEffect correctly triggers data fetching when the active package changes. The caching logic in
fetchChangelogprevents redundant API calls.
84-88: LGTM!Correctly resets UI state when switching between packages.
91-97: LGTM!Standard toggle pattern for managing expanded state.
129-171: LGTM!The conditional rendering logic correctly handles loading, data, and empty states.
173-200: LGTM!The component structure and JSX are well-organized with clear layout sections.
src/admin/dashboard/pages/changelog/SearchBar.tsx (2)
32-45: LGTM!The click-outside detection is correctly implemented with proper cleanup.
48-58: LGTM!The filtering logic correctly handles both search and default display scenarios.
src/admin/dashboard/pages/changelog/PackageToggle.tsx (1)
4-15: LGTM!The conditional rendering based on
hasProis correct.src/admin/dashboard/pages/changelog/VersionRow.tsx (2)
54-80: LGTM with note on change type ordering.The visible changes logic correctly limits items to 5 when collapsed. Note that the order depends on
Object.keys(version.changes), which follows insertion order. Ensure the backend provides changes in the desired display order.
85-108: LGTM!The sticky positioning and layout structure are well-implemented.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
src/admin/dashboard/pages/changelog/VersionRow.tsx (2)
128-145: Remove unnecessary optional chaining onitem.title.Lines 134 and 139 use
item?.titlewith optional chaining, but theChangeIteminterface (lines 5-7) definestitleas a required string property. The optional chaining is unnecessary and should be simplified toitem.title.🔎 Proposed fix
- <li - key={ itemIndex } - className="flex items-start text-sm text-[#575757] mb-0" - > - { item?.title && ( - <span className="mr-2 text-sm text-[#575757]"> - • - </span> - ) } - <span>{ item?.title }</span> - </li> + <li + key={ itemIndex } + className="flex items-start text-sm text-[#575757] mb-0" + > + <span className="mr-2 text-sm text-[#575757]"> + • + </span> + <span>{ item.title }</span> + </li>
148-158: Simplify button styles and remove unnecessary!importantflags.Line 152 contains redundant and conflicting focus styles with
!importantflags (!outline-none,!focus:outline-none,focus:ring-0,focus:border-0). This is a code smell indicating style conflicts in your CSS architecture. Remove the!importantprefixes and consolidate the focus styles while ensuring an accessible focus indicator remains.🔎 Proposed fix
<button onClick={ onToggle } - className="text-sm text-[#7047EB] hover:text-[#5a38bc] font-medium !outline-none !focus:outline-none focus:ring-0 focus:border-0 underline p-6 pt-0" + className="text-sm text-[#7047EB] hover:text-[#5a38bc] font-medium underline p-6 pt-0 outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-[#7047EB] focus-visible:ring-offset-2" >If the
!importantflags are required to override conflicting styles, address the root cause in your CSS architecture instead.src/admin/dashboard/pages/changelog/index.tsx (2)
15-35: Import theVersiontype to fix TypeScript compilation error.Lines 20 and 23 use the
Versiontype inuseState<Version[] | null>, but this type is not imported. TheVersioninterface is defined inVersionRow.tsx(lines 14-18) but is not exported. This will cause a TypeScript compilation error.To fix this, you need to either:
- Export the
Versioninterface fromVersionRow.tsxand import it here- Define a shared types file for common interfaces
- Define the
Versiontype locally in this file🔎 Proposed fix (Option 1: Export from VersionRow)
In
src/admin/dashboard/pages/changelog/VersionRow.tsx, export theVersioninterface:-interface Version { +export interface Version { version: string; released: string; changes: VersionChanges; }Then import it in this file at the top:
import SearchBar from './SearchBar'; import { __ } from '@wordpress/i18n'; import VersionRow from './VersionRow'; +import type { Version } from './VersionRow'; import apiFetch from '@wordpress/api-fetch'; import PackageToggle from './PackageToggle'; import { useState, useEffect, useCallback, useRef } from '@wordpress/element';
100-117: Implement auto-dismiss for highlight state with proper cleanup.The
handleJumpToVersionfunction sets a highlight on the target version but never automatically dismisses it. Users must manually click the overlay (lines 190-195) to clear the highlight. A previous review expected asetTimeoutto auto-dismiss the highlight after 2 seconds, but this functionality is entirely missing from the current implementation.🔎 Proposed fix with timeout cleanup
+import { useState, useEffect, useCallback, useRef } from '@wordpress/element'; + const ChangelogPage = () => { + const highlightTimerRef = useRef<NodeJS.Timeout | null>(null); // ... existing state ... // Handle jump to version with highlight const handleJumpToVersion = ( index: number ) => { const versions = activePackage === 'lite' ? liteVersions : proVersions; if ( ! versions ) { return; } const version = versions[ index ]; const versionKey = `${ activePackage }-${ version.version }`; const element = versionRefs.current[ versionKey ]; + // Clear any existing highlight timer + if (highlightTimerRef.current) { + clearTimeout(highlightTimerRef.current); + } + // Set highlight setHighlightedVersion( versionKey ); // Scroll to element if ( element ) { element.scrollIntoView( { behavior: 'smooth', block: 'start' } ); } + + // Remove highlight after 2 seconds + highlightTimerRef.current = setTimeout( () => { + setHighlightedVersion( null ); + highlightTimerRef.current = null; + }, 2000 ); }; + // Cleanup on unmount + useEffect(() => { + return () => { + if (highlightTimerRef.current) { + clearTimeout(highlightTimerRef.current); + } + }; + }, []);
🧹 Nitpick comments (1)
src/admin/dashboard/pages/changelog/index.tsx (1)
42-76: RefactorfetchChangelogto avoid unnecessary callback recreations.The
useCallbackdependency array (line 75) includesliteVersionsandproVersions, causing the callback to be recreated every time these state values change. Since these dependencies are only used for null-checks (lines 45-50), this creates unnecessary churn. WhenfetchChangelogchanges, theuseEffecton lines 79-81 runs again, potentially causing unnecessary effect executions.🔎 Proposed fix using refs to track loaded state
const ChangelogPage = () => { const [ activePackage, setActivePackage ] = useState< 'lite' | 'pro' >( 'lite' ); const [ liteVersions, setLiteVersions ] = useState< Version[] | null >( null ); const [ proVersions, setProVersions ] = useState< Version[] | null >( null ); + const loadedPackages = useRef< Set<'lite' | 'pro'> >( new Set() ); const [ loading, setLoading ] = useState( false ); // ... rest of state ... // Fetch changelog data const fetchChangelog = useCallback( async ( pkg: 'lite' | 'pro' ) => { // Check if already loaded - if ( pkg === 'lite' && liteVersions !== null ) { - return; - } - if ( pkg === 'pro' && proVersions !== null ) { + if ( loadedPackages.current.has( pkg ) ) { return; } setLoading( true ); try { const response = await apiFetch< string >( { path: `/dokan/v1/admin/changelog/${ pkg }`, } ); const data = typeof response === 'string' ? JSON.parse( response ) : response; if ( pkg === 'lite' ) { setLiteVersions( data ); } else { setProVersions( data ); } + loadedPackages.current.add( pkg ); } catch ( error ) { // eslint-disable-next-line no-console console.error( 'Failed to fetch changelog:', error ); } finally { setLoading( false ); } }, - [ liteVersions, proVersions ] + [] );
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/admin/dashboard/pages/changelog/VersionRow.tsxsrc/admin/dashboard/pages/changelog/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/admin/dashboard/pages/changelog/index.tsx (5)
src/stores/vendors/actions.ts (1)
setLoading(20-25)assets/src/js/product-editor.js (1)
key(1887-1887)src/vendor-dashboard/reports/analytics/settings/historical-data/layout.js (1)
response(144-148)src/vendor-dashboard/reports/dashboard/components/connect/index.js (1)
error(170-170)assets/src/js/setup-wizard/commission/index.js (1)
element(7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e tests (1, 3)
- GitHub Check: e2e tests (2, 3)
- GitHub Check: e2e tests (3, 3)
- GitHub Check: api tests (1, 1)
🔇 Additional comments (10)
src/admin/dashboard/pages/changelog/VersionRow.tsx (5)
1-18: LGTM!The imports and type definitions are well-structured. The
ChangeIteminterface correctly defines bothtitleanddescriptionas required string properties.
20-40: LGTM!The component props are clearly typed, and the date formatting implementation is correct.
42-80: LGTM!The logic for calculating total items and determining visible changes is correct. The truncation strategy is deterministic and ensures exactly 5 items are shown when collapsed.
85-108: LGTM!The left column layout with sticky positioning is well-implemented. The "Latest" badge and date formatting provide clear visual hierarchy.
110-127: LGTM!The card structure with conditional highlight ring and section dividers is implemented correctly. The use of
ChangeBadgefor type indicators maintains consistency.src/admin/dashboard/pages/changelog/index.tsx (5)
8-13: LGTM!The loading spinner implementation is clean and uses standard Tailwind animation utilities.
37-39: LGTM!The Pro availability check safely handles missing global state with optional chaining and a sensible default.
84-97: LGTM!The package toggle and version expansion handlers are implemented correctly with appropriate state resets.
120-166: LGTM!The content rendering logic correctly handles loading, populated, and empty states. The ref callback pattern for scroll targeting is properly implemented.
168-202: LGTM!The main layout is well-structured with clear header and content sections. The overlay for dismissing highlights is properly positioned and interactive.
All Submissions:
Changes proposed in this Pull Request:
Related Pull Request(s)
Closes
How to test the changes in this Pull Request:
Changelog entry
update: Redesigned the Admin Panel "Changelog" page template for a better look and user experience.
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
Style
✏️ Tip: You can customize this high-level summary in your review settings.