Skip to content

Conversation

@aristath
Copy link
Member

@aristath aristath commented Dec 10, 2025

Rewriting the plugin, from PHP + Vanilla-JS to React.

Playground link

@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 2025

Test on Playground
Test this pull request on the Playground
or download the zip

@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 2025

✅ Code Coverage Report

PHP Coverage (PHPUnit)

Metric Value
Current 52.83% 📊
Base 31.49%
Change 📈 21.34%

✅ PHP coverage meets threshold (40%)

JavaScript Coverage (Jest)

Metric Value
Current 42.19% 📊
Base 42.19%
Change 📈 0.00%

✅ Jest coverage meets threshold (40%)


🎉 Great job maintaining/improving PHP coverage!

📊 File-level Coverage Changes (51 files)

🆕 New Files

Class Coverage Lines
🟢 Progress_Planner\Rest\Activities 100.00% 51/51
🟢 Progress_Planner\Rest\Badge_Stats 98.44% 63/64
🔴 Progress_Planner\Rest\Data_Collectors 26.03% 19/73
🔴 Progress_Planner\Rest\Email_Sending_Config 28.95% 11/38
🔴 Progress_Planner\Rest\Email_Test 37.31% 25/67
🟢 Progress_Planner\Rest\Page_Settings 83.33% 10/12
🔴 Progress_Planner\Rest\Plugin_Installer 27.97% 40/143
🔴 Progress_Planner\Rest\Popover_Actions 32.38% 34/105
🔴 Progress_Planner\Rest\Subscribe 52.38% 44/84
🔴 Progress_Planner\Rest\Task_Evaluation 41.57% 143/344
🔴 Progress_Planner\Rest\Timezone_Options 33.33% 12/36
🔴 Progress_Planner\Rest\Updates 13.33% 10/75
🔴 Progress_Planner\Rest\Upgrade_Tasks_Config 24.44% 11/45
🟢 Progress_Planner\Rest\Widgets\Activity_Scores 100.00% 147/147
🟢 Progress_Planner\Rest\Widgets\Content_Activity 98.61% 142/144
🟡 Progress_Planner\Rest\Widgets\Whats_New 73.22% 134/183
🔴 Progress_Planner\Rest\Wizard_Config 11.88% 12/101
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\AIOSEO_Options 0.00% 0/37
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\Old_Posts_For_Review 0.00% 0/63
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\PHP_Version 0.00% 0/1
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\Permalink_Has_Date 0.00% 0/7
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\WP_Debug 0.00% 0/7
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\Yoast_Options 0.00% 0/16
🔴 Progress_Planner\Suggested_Tasks\Data_Collector\Yoast_Premium_Status 0.00% 0/20
🔴 Progress_Planner\Utils\Plugin_Utils 0.00% 0/15

📈 Coverage Improved

Class Before After Change
Progress_Planner\Admin\Enqueue 0.43% 74.21% +73.78%
Progress_Planner\Admin\Page 31.29% 90.75% +59.46%
Progress_Planner\Activities\Suggested_Task 44.44% 83.33% +38.89%
Progress_Planner\Lessons 0.00% 35.14% +35.14%
Progress_Planner\Badges 66.10% 100.00% +33.90%
Progress_Planner\Rest\Recommendations_Controller 66.67% 100.00% +33.33%
Progress_Planner\Base 44.51% 61.24% +16.73%
Progress_Planner\Settings 42.11% 55.26% +13.15%
Progress_Planner\Page_Types 52.68% 62.95% +10.27%
Progress_Planner\Suggested_Tasks 5.22% 15.21% +9.99%
Progress_Planner\Goals\Goal_Recurring 91.49% 100.00% +8.51%
Progress_Planner\Admin\Page_Settings 38.71% 46.77% +8.06%
Progress_Planner\UI\Branding 33.33% 39.80% +6.47%
Progress_Planner\Plugin_Upgrade_Tasks 7.41% 11.32% +3.91%
Progress_Planner\UI\Chart 89.29% 92.86% +3.57%
Progress_Planner\Utils\Onboard 4.30% 7.53% +3.23%

📉 Coverage Decreased

Class Before After Change
Progress_Planner\Suggested_Tasks\Data_Collector\Inactive_Plugins 81.25% 0.00% -81.25%
Progress_Planner\Suggested_Tasks\Data_Collector\Post_Tag_Count 80.00% 0.00% -80.00%
Progress_Planner\Utils\System_Status 91.95% 83.44% -8.51%
Progress_Planner\Onboard_Wizard 56.60% 48.91% -7.69%
Progress_Planner\Update\Update_130 88.16% 84.42% -3.74%
Progress_Planner\Suggested_Tasks\Data_Collector\Data_Collector_Manager 64.29% 61.11% -3.18%
Progress_Planner\Admin\Editor 3.85% 2.22% -1.63%
Progress_Planner\Activities\Query 59.35% 58.41% -0.94%
Progress_Planner\Update\Update_190 71.77% 71.54% -0.23%
Progress_Planner\Utils\Plugin_Migration_Helpers 97.37% 97.22% -0.15%
ℹ️ About this report
  • PHP tests run with Xdebug coverage
  • Jest tests run with built-in coverage
  • Security tests excluded from PHP coverage
  • Coverage calculated from line coverage percentages

aristath and others added 27 commits December 12, 2025 12:44
Changes from develop:
- Added get_license_key() method to Base class
- Moved branded site registration to run earlier in constructor
- Standardized license key access across PHP files

Conflict resolution:
- Removed classes/admin/widgets/class-challenge.php (migrated to React)
- Removed views/admin-page-header.php (now DashboardHeader.js)

Localization fix:
- Updated class-page.php to use get_license_key() for React
- Fixed trailing whitespace in class-timezone-options.php
Removed unused PHP popover rendering methods that were left behind
after migrating to React popovers:

- Set_Page_Task: Removed enqueue_scripts(), print_popover_form_contents(),
  and associated static properties
- Set_Valuable_Post_Types: Removed print_popover_form_contents() and
  print_popover_instructions()

These methods called non-existent parent methods (get_enqueue_data,
print_submit_button) causing PHPStan errors. The functionality is now
handled by React components in assets/src/components/Popovers/.
Removed title rendering from DashboardWidgets wrapper component.
Widgets now only render their own semantic <h2> titles, eliminating
the duplicate title that was being added by the wrapper.
- Create dispatchGridResize utility to centralize grid resize event dispatching
- Create WidgetHeader component for consistent widget titles with tooltips
- Update SuggestedTasks, TodoWidget to use gridResize utility (replaced 12 instances)
- Update useCelebration hook to use gridResize utility
- Update ActivityScores, ContentBadges, StreakBadges to use WidgetHeader

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create LoadingState, ErrorState, EmptyState shared components
- Update SimpleBadgeWidget to use shared state components
- Update ActivityScores to use shared state components
- Update ContentActivity to use shared state components (removes duplicate LoadingSpinner/ErrorDisplay)
- Update MonthlyBadges to use shared state components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create useApiData hook for centralized REST API fetching with loading/error states
- Update ActivityScores to use useApiData hook
- Update ContentActivity to use useApiData hook
- Update WhatsNew to use useApiData hook

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update ContentActivity to use WidgetHeader
- Update WhatsNew to use WidgetHeader
- Update MonthlyBadges to use WidgetHeader
- Update SuggestedTasks to use WidgetHeader
- Update TodoWidget to use WidgetHeader
- Remove empty DashboardHeader and Tour folders

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Delete empty CSS files: activity-scores.css, content-activity.css
- Migrate whats-new.css hover states to React (BlogPostImage, PostTitleLink, FooterLink components)
- Delete whats-new.css after migration
- Add migration documentation headers to remaining CSS files explaining what must stay in CSS:
  - badge-streak.css: PHP popover styles
  - monthly-badges.css: PHP popovers, media queries, :has() selectors
  - suggested-tasks.css: PHP interactive popovers, form inputs, :has() selectors
  - todo.css: PHP delete-all popover, :has() for golden/silver tasks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update admin-page.php to always render React root div
- React Dashboard component handles privacy policy state internally
- Remove welcome.css and onboard.css (styles already inline in Welcome.js)
- Delete views/welcome.php PHP template (no longer needed)
- Remove conditional CSS enqueues from class-page.php
- Clean up unused widget style handles (already deleted CSS files)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The welcome page was blank because dashboard.js was only enqueued
when privacy policy was already accepted. Now dashboard.js is always
loaded since React handles both welcome and dashboard states.

- Move enqueue_dashboard_script() call outside conditional
- Delete unused onboard.js (replaced by React Welcome component)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add onboard URLs, user data, and branding to prplDashboardConfig
- Add progressIconHtml, homeUrl, privacyPolicyUrl to branding config
- Add wrapper styles (white background, border, padding) to Welcome.js
- Set icon height to 100px

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SVG elements can't be styled with React inline styles when using
dangerouslySetInnerHTML. Added inline style tag to target the SVG
and set height: 100px.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
aristath and others added 29 commits December 18, 2025 19:26
- Add needsPagination param to fetchTasks() to enable preloading support
- Return full task data from evaluate endpoint (eliminates follow-up fetches)
- Add batch endpoint /tasks/evaluate-batch for creating multiple tasks
- Rewrite taskRegistry with batched evaluation flow:
  - Pre-fetch all existing tasks (all statuses) for completion tracking
  - Render existing tasks immediately
  - Batch create new tasks (5 at a time) with progressive rendering
- Update preload paths for all task fetch scenarios

Performance impact: ~64 API calls reduced to ~8 calls (worst case)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
For existing sites, tasks already in the database were being
re-evaluated with shouldAddTask() which calls data-collectors
(server-side). This made performance WORSE for returning users.

Now the check order is:
1. Skip if completed/snoozed (trash/future status)
2. Return immediately if active task exists (NO evaluation)
3. Only run shouldAddTask() for tasks not in cache

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Data collectors are preloaded but fetchDataCollector was using raw
apiFetch, bypassing the cache layer. Now uses cachedApiFetch which:
- Returns preloaded data instantly on first call
- Caches responses for subsequent calls (5-min TTL)
- Deduplicates concurrent requests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of fetching data collectors one-by-one during sequential
shouldAddTask() calls, pre-fetch ALL 16 data collectors in parallel
during initialization. This populates the cachedApiFetch cache so
subsequent calls from task providers are instant cache hits.

Combined with Fix 1, this eliminates ~10-15 seconds of sequential
API wait time on new sites.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After creating a task, we were making an internal REST API request
to get the full task data (~50-100ms per task). Since we already
have all the data from the creation, build the response directly.

For 10 new tasks, this saves 500-1000ms of latency.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three tasks (SetPageAbout, SetPageContact, SetPageFAQ) all call
the same /page-settings endpoint. Using cachedApiFetch ensures:
- First call uses preloaded data
- Subsequent calls hit the 5-minute cache

Saves 2 unnecessary API calls (~100ms).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The was_task_completed() check was unreachable dead code because:
1. get_post() queries ALL statuses (publish, trash, draft, future, pending)
2. If a task exists in ANY status, we return early at line 213-221
3. was_task_completed() also uses get_post() internally
4. We only reach was_task_completed() when get_post() returned false
5. Therefore was_task_completed() will always return false at that point

Removing this saves one unnecessary database query per task creation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Optimizations in create_tasks_batch():
- Single lock for entire batch (vs per-task locks)
- Bulk existence check with one query (vs per-task queries)
- Pre-fetch all provider terms at once (vs per-task term lookups)
- Direct response building (no internal REST calls)

New helper methods:
- bulk_check_existing_tasks(): Single query with post_name__in
- ensure_provider_terms(): Fetch all terms in one query, create missing
- create_task_post(): Direct post creation bypassing per-task locking
- build_task_response_from_post(): Build response from existing WP_Post

For a batch of 5 tasks, this reduces:
- From ~10 lock operations to 1
- From ~10-15 existence queries to 1
- From ~5-10 term queries to 2-3
- From ~5 internal REST calls to 0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The AJAX handler for task completion was failing for React tasks because:
- get_task_provider() only searches PHP provider instances
- React providers are listed in REACT_PROVIDERS but not instantiated
- This caused "Provider not found" error, preventing activity insertion

Fix:
- Make is_react_provider() public in Tasks_Manager
- Skip PHP provider lookup for React providers in suggested_task_action()
- React providers already check capabilities client-side before display

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two issues fixed:

1. Points not being awarded on task completion
   - sendTaskAction() was looking for prplSuggestedTasksConfig which
     was never set in PHP (only prplDashboardConfig was set)
   - Changed all JS files to use prplDashboardConfig instead

2. Badge gauge not updating after task completion
   - Each widget was a separate webpack entry point with its own
     copy of the Zustand store
   - Added optimization.runtimeChunk: 'single' to webpack config
   - This creates a shared runtime.js that ensures modules are
     instantiated only once across all entry points
   - All widgets now share the same Zustand store instance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set isDismissable = false on 19 React tasks that should not show
the Complete button, matching the PHP base class default behavior.
Only 12 task providers explicitly set this to true in PHP.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Created 11 React task files for Yoast integration
- Added 3 new data collectors (yoast_options, yoast_premium_status, permalink_has_date)
- Refactored Add_Yoast_Providers with FOCUS_TASKS_CONFIG for focus element highlighting
- Added site_logo fallback check for organization logo task (100% feature parity)
- Deleted 14 PHP files (11 providers + 2 base classes + 1 trait)
- Updated REACT_PROVIDERS constant with all Yoast provider IDs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create AIOSEO options data collector for React task evaluation
- Add 6 React task files: OrganizationLogo, ArchiveAuthor, ArchiveDate,
  MediaPages, CrawlFeedAuthors, CrawlFeedComments
- Update REACT_PROVIDERS constant with AIOSEO provider IDs
- Refactor Add_AIOSEO_Providers to only handle allowed options
- Delete old PHP provider files and unused traits
- Fix AIOSEOPopover with correct option names and setting paths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Handle term_exists error gracefully by fetching existing term
- Suppress 409 Conflict errors from batch creation (expected behavior)

The term_exists error occurred when the user provider term already
existed but wasn't returned in the initial fetch. The 409 errors
are expected when multiple widgets try to create tasks simultaneously.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Delete class-collaborator.php provider
- Remove Collaborator import and instantiation from Tasks_Manager
- Change WP-CLI task create default provider_id to 'user'
- Remove 'collaborator' from update-190 priority mapping
- Simplify evaluate_task() in class-tasks.php (remove collaborator special case)
- Update tests to use 'user' provider instead of 'collaborator'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert all 16 popover components to use React.lazy() for code splitting
- Add Suspense wrapper in PopoverManager with loading state
- Create REST API endpoints for EmailSending and UpgradeTasks config
- Migrate MonthlyBadgesPopover from PHP to React
- Use webpackChunkName magic comments for descriptive chunk filenames
- Remove wp_localize_script in favor of REST API for popover data

Bundle size reduced from 162KB to 109KB for widget-suggested-tasks.js
Each popover loads on-demand as a separate chunk (~3-11KB each)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The focus-element feature highlighted settings fields when navigating
from Progress Planner tasks. This legacy functionality is no longer
needed as the React implementation uses popovers instead.

Deleted:
- assets/js/focus-element.js
- assets/js/yoast-focus-element.js
- assets/css/focus-element.css

Modified:
- Removed maybe_enqueue_focus_el_script() from class-page.php
- Simplified class-add-yoast-providers.php
- Removed pp-focus-el URL parameters from React task files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All tasks are now managed by React. Removes:
- Tasks_Manager and Tasks_Interface classes
- PHP task provider classes (Tasks, Tasks_Interactive, User)
- AIOSEO and Yoast provider integrations
- Related test files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Delete legacy JS recommendations (set-page.js, set-valuable-post-types.js)
- Remove unreachable ajax_get_task_template() method
- Simplify get_newly_added_task_providers() (mark as deprecated)
- Remove empty constructor from Branding class
- Remove unused imports from Yoast_Orphaned_Content
- Remove unused .move-up/.move-down CSS selectors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Delete 4 unused PHP view templates:
  - views/setting/radio.php, views/setting/page-select.php
  - views/popovers/parts/badge-streak-badge.php, badge-streak-progressbar.php
- Remove dead monthly badges popover code from tour.js
- Remove 11 unused CSS variables from variables-color.css
- Remove unused .prpl-pp-not-accepted selectors from admin.css

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove Tasks_Interactive import from class-popover-actions.php (class deleted in round 1)
- Remove broken tests from test-class-security.php that referenced deleted Tasks_Interactive class
- Remove unused .prpl-columns-wrapper CSS selector (only -flex variant is used)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove Task_Provider_Test_Trait require from bootstrap (deleted in previous round)
- Delete 10 obsolete test files that tested deleted PHP task providers/traits
- Add REACT_MIGRATED_PROVIDERS blocklist to Update_130 migration
  This prevents migration of tasks whose providers have been migrated to React

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants