Skip to content

Extract admin UI into wp-admin-ui kit; consume from progress-planner#761

Open
jdevalk wants to merge 15 commits intodevelopfrom
admin-ui-pkg-integration
Open

Extract admin UI into wp-admin-ui kit; consume from progress-planner#761
jdevalk wants to merge 15 commits intodevelopfrom
admin-ui-pkg-integration

Conversation

@jdevalk
Copy link
Copy Markdown
Member

@jdevalk jdevalk commented Apr 15, 2026

Summary

Extracts the admin UI layer (page framework, asset enqueuer, widget base, web components, CSS tokens) of progress-planner into a standalone Composer package at ProgressPlanner/wp-admin-ui (private, pinned at v0.1.1) and rewires progress-planner to consume the kit instead of shipping those bits directly.

After this PR, the kit owns:

  • Menu registration + top-level admin page rendering (PageRegistrar).
  • <prpl-gauge>/<prpl-tooltip>/chart/counter/progress-bar web components.
  • CSS tokens (variables-color.css) + layout-only admin.css + grid-masonry.js.
  • The file-header-based AssetEnqueuer (dep resolution, mtime versioning, external-handle callbacks).

Progress-planner keeps all domain logic (task providers, badges, SaaS branding, privacy gate, tour, subscribe popover, JS templates, dashboard widgets). It integrates via Admin_UI_Kit_Integration which attaches the plugin's UI bits to the kit's {asset_prefix}_admin_ui_* hooks.

Why

Reusable admin look-and-feel across other plugins. Everything kit-side is now extractable by any consumer via a vcs Composer repo entry.

Phases (each is its own commit for review)

  1. Dual-load the kit behind a flag.
  2. Route Admin\Enqueue through the kit's AssetEnqueuer.
  3. Progress_Planner\UI\Branding::to_kit_branding() bridge.
  4. Admin\Widgets\Widget extends the kit's base.
  5. Kit renders the real admin page (flag-on path).
  6. Delete duplicated CSS/JS — identical copies fall through to the kit.
  7. Remove the flag: kit always renders.

Test plan

  • composer install picks up progressplanner/wp-admin-ui v0.1.1.
  • /wp-admin/admin.php?page=progress-planner renders with no JS console errors.
  • Widgets (suggested tasks, to-do, monthly badges, streaks, challenge, activity scores, content activity, what's new) all render and behave as before.
  • Notification bubble on the menu item appears when there are pending celebrations.
  • Privacy/welcome gate still shows when privacy policy isn't accepted.
  • Tour button + subscribe popover in the header work.
  • Recommendation scripts on the WP Dashboard load without prplInteractiveTaskFormListener errors.
  • Branding colors (monthly background cream, headings purple, primary red) are preserved.

🤖 Generated with Claude Code

jdevalk added 10 commits April 15, 2026 21:18
Adds the extracted admin-ui kit as a composer dependency (vcs repo,
dev-main) and wires a shadow admin page that boots AdminUI alongside
the existing progress-planner admin page. Behind the
PROGRESS_PLANNER_USE_ADMIN_UI_PKG constant — off by default, so
production behavior is unchanged.

- composer.json: require progressplanner/wp-admin-ui dev-main
- progress-planner.php: conditionally load composer autoload + boot
  shadow page when the flag is defined-and-truthy
- classes/admin/class-admin-ui-pkg-shadow.php: shadow page + smoke-test
  widget rendering <prpl-gauge> via the kit

Verified: with flag off, PHP syntax + composer valid, no behavior change.
With flag on, shadow page at ?page=progress-planner-adminui renders a
gauge inside a proper widget wrapper, confirming the kit autoloads and
works inside progress-planner's PHP environment.

Phase 3 note: the shadow page sets host_assets_path to a non-existent
dir because progress-planner's own asset file headers use qualified
'progress-planner/...' dep strings that the kit's enqueuer can't resolve
yet. That gets fixed in Phase 3 when we set the kit's asset_prefix to
'progress-planner' — then legacy headers will round-trip cleanly.
Progress_Planner\Admin\Enqueue is now a thin adapter around the kit:

- enqueue_style() fully delegates to the kit enqueuer.
- enqueue_script() delegates non-vendor handles to the kit; vendor
  scripts (particles-confetti, driver) keep their special handling
  because their fixed path/version doesn't fit the kit's generic
  {prefix}/{handle} resolution.
- Vendor handles are registered with the kit as externals with a lazy
  resolver callback, so when another script declares them as deps, the
  vendor file is wp_enqueue_script()'d on demand.
- Progress-planner-specific methods (localize_script() with its
  per-handle switch, get_localized_strings(), get_badge_urls(),
  maybe_empty_session_storage()) stay here — they're domain logic, not
  asset mechanics.
- get_file_details() removed; the kit owns resolution now.

The kit's asset_prefix is 'progress-planner' so existing legacy headers
(Dependencies: progress-planner/foo) round-trip cleanly without needing
file edits.

Requires the kit's register_external() accepting a resolver callback
(progressplanner/wp-admin-ui@dev-main).
Adds Progress_Planner\UI\Branding::to_kit_branding() which returns a
\ProgressPlanner\AdminUI\Branding populated with currently-resolved
SaaS values (menu name, submenu name, admin menu icon SVG, logo HTML,
and custom CSS).

The SaaS-driven Progress_Planner\UI\Branding class keeps all its
existing behavior — remote fetches, hostname sniffing, widget filtering
— and this method is purely an output bridge. Kit code (which performs
no remote calls) consumes the VO.

The shadow page now uses to_kit_branding() instead of constructing its
own Branding, exercising the bridge end-to-end.
Progress_Planner\Admin\Widgets\Widget now extends
ProgressPlanner\AdminUI\Widgets\Widget. Rendering mechanics (wrapper
div, CSS class naming, view loading) live in the kit; progress-planner-
specific additions stay here:

- range/frequency getters (bound to the existing <select> UI).
- legacy enqueue_styles/enqueue_scripts paths that use the classic
  'progress-planner/page-widgets/{id}' handle convention.

Added Progress_Planner\Admin\Admin_UI_Instance — a lazy singleton
accessor for the kit's AdminUI instance. Boots with
register_page: false so the kit's PageRegistrar doesn't collide with
progress-planner's own Admin\Page class.

Return-type-compatible overrides in three concrete widgets
(Badge_Streak, Challenge, Todo) to match the kit's signatures.

Requires the kit's Config::$register_page flag
(progressplanner/wp-admin-ui@dev-main bump).
…I_PKG is on

When the flag is on:
- Admin_UI_Instance boots AdminUI with register_page=true, show_range_filter=true,
  menu_slug='progress-planner' — kit's PageRegistrar owns the top-level menu.
- Admin\Page::add_page() short-circuits so we don't register a duplicate menu.
- Admin\Page::enqueue_assets() delegates to a new enqueue_widget_assets()
  method that only enqueues widget-specific bits (tokens + layout + masonry
  are the kit's responsibility now).
- Admin_UI_Pkg_Shadow::boot() registers progress-planner's widgets with the
  kit and hooks legacy UI into the kit's filters/actions:
    * {prefix}_admin_ui_menu_title_suffix → notification-count bubble.
    * {prefix}_admin_ui_before_content    → welcome/privacy gate.
    * {prefix}_admin_ui_header_right      → tour button + subscribe popover.
    * {prefix}_admin_ui_after_widgets     → overlay-close script, JS templates,
      and the legacy progress_planner_admin_page_after_widgets action fires
      as compat for third-party code.

With flag off: nothing routes through the kit's page flow; existing
Admin\Page behavior is unchanged.

Renamed views/admin-page.php → views/legacy-admin-page.php (same for -header)
so the kit's host-first view resolution finds the kit's versions for admin-
page.php while legacy-* stays available for the flag-off code path.

Also:
- Enqueue: pass an after_enqueue_script callback to the kit so
  localize_script() fires for every transitively-enqueued script (fixes
  prplL10nStrings / progressPlannerBadge / prplSuggestedTask not being
  attached to deps resolved by the kit).
- Branding::to_kit_branding() now passes null for all colors so
  progress-planner's variables-color.css wins for token values (requires
  kit bump with nullable Branding colors).
Tagged v0.1.0 on the kit after the phase-1-through-5 integration was
verified working in both flag states (PROGRESS_PLANNER_USE_ADMIN_UI_PKG
on and off) with zero visual regression on the real admin page.

Phase 6 (actually deleting duplicated CSS/JS from progress-planner's
assets so the kit's files become the source of truth) is deferred to a
follow-up branch — this one is the stabilization checkpoint.
- Rename Progress_Planner\Admin\Admin_UI_Pkg_Shadow →
  Admin_UI_Kit_Integration (and the file to match). The 'shadow' name
  was a Phase-2 artifact; post-Phase-5 this class is the integration
  layer between progress-planner and the kit when flag is on.
- Bump progressplanner/wp-admin-ui to v0.1.1 for the proper cycle-safe
  dep resolution (processing vs enqueued two-set tracking), which
  fixed the prplInteractiveTaskFormListener undefined errors on the
  WP dashboard when multiple recommendation scripts share the
  interactive-task dep.

Phase 3C (deleting duplicated CSS/JS from progress-planner/assets so
host-first resolution falls through to the kit's copies) is abandoned
for this round — byte-identical files at vendor/ URLs break rendering
in the Local dev environment for reasons that need investigation.
The kit's copies still ship inside vendor/, they're just not used
unless a host plugin has no own copy.
Root-caused the earlier render regression: Progress_Planner\Admin\Enqueue
wrapped its own AssetEnqueuer with only a single host root and no
package fallback, so when legacy enqueue paths (flag-off) called
enqueue_style('progress-planner/variables-color') and the host file was
gone, resolve() returned null and wp_enqueue_style was never called.

Fix: give progress-planner's kit_enqueuer the same (host, package) root
list as the AdminUI's enqueuer uses. Deletions now fall through to the
kit's copies whether the flag is on or off.

With that landed, the Phase 3C/6 deletions are safe:
- assets/css/variables-color.css (identical to kit)
- assets/css/web-components/prpl-tooltip.css (identical)
- assets/css/web-components/prpl-install-plugin.css (identical)
- assets/js/web-components/prpl-{big-counter,chart-bar,chart-line,gauge-progress-controller,tooltip}.js
  (identical behavior; kit's gauge-progress-controller drops the
   gauge→controller→gauge legacy cycle dep, controller.js deps on
   badge-progress-bar only.)

Kept in progress-planner's assets because the kit versions behave
differently or have domain coupling:
- prpl-gauge.js (host declares prpl-badge dep needed for host widgets)
- prpl-badge-progress-bar.js (same reason)
- prpl-install-plugin.js (kit strips the suggested-task coupling; host
  still uses the old behavior)
- prpl-badge.js, prpl-interactive-task.js, prpl-task-*.js (progress-
  planner-specific entirely)
- admin.css (host is a superset of the kit's layout-only version)
…e page

The flag was the Phase-5 safety net for switching progress-planner to the
extracted wp-admin-ui kit's page rendering. With the kit verified working
in both flag states across all the Phase 1-6 commits, drop the toggle.

Changes:
- progress-planner.php: unconditional composer autoload + unconditional
  Admin_UI_Kit_Integration boot (was flag-gated).
- Admin_UI_Instance: remove kit_renders_page(); boot kit with
  register_page=true always.
- Admin_UI_Kit_Integration::boot(): remove the flag early-return.
- Admin\Page: strip the legacy page ownership — no more add_page(),
  render_page(), enqueue_scripts(), enqueue_styles(), or
  get_notification_counter(). enqueue_assets() always runs the
  widget-only path (kit handles tokens + layout + masonry). Kept:
  get_widgets()/get_widget(), maybe_enqueue_focus_el_script(),
  remove_admin_notices(), clear_activity_scores_cache(), admin_footer().
- Deleted views/legacy-admin-page.php + views/legacy-admin-page-header.php
  (only the flag-off path used them).
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

Composer package changes
Prod Packages Operation Base Target
progressplanner/wp-admin-ui New - v0.1.1

@jdevalk jdevalk requested review from aristath and ilicfilip April 15, 2026 20:33
ilicfilip and others added 5 commits April 16, 2026 09:47
…CS warnings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace with direct enqueue of the base stylesheets (variables-color +
admin) that the dashboard widgets need on the WP Dashboard screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The removed Page::enqueue_styles() also attached branding CSS overrides
via wp_add_inline_style — restore that for WP Dashboard widgets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The suggested-task template uses tooltip-actions classes, so the
tooltip stylesheet is needed on the WP Dashboard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The old progress_planner_admin_page_header_before action no longer fires
since the kit owns the admin page header. Switch to the kit's equivalent
progress-planner_admin_ui_header_before hook.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@ilicfilip ilicfilip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fixes pushed + items needing attention

Fixes pushed (5 commits)

  1. PHPDoc + style fixes (1db545a) — Added proper docblocks to Admin_UI_Instance, removed double blank line in Branding::to_kit_branding(), fixed PHPCS param spacing in Enqueue.

  2. Dashboard widgets broken (5afec8d, 84eb430, 2616438) — Page::enqueue_styles() was removed but Dashboard_Widget_Score and Dashboard_Widget_Todo still called it. Replaced with direct enqueue of variables-color, admin, branding inline CSS, and prpl-tooltip (needed by the suggested-task template).

  3. Playground header notice (44c90aa) — progress_planner_admin_page_header_before action no longer fires since the old admin-page-header.php was deleted. Updated Playground class to hook into the kit's equivalent progress-planner_admin_ui_header_before.

Items needing attention

  • CI is fully red — All jobs fail at composer install because ProgressPlanner/wp-admin-ui is a private repo and CI doesn't have credentials to clone it. You'll need to add a GitHub token (e.g. COMPOSER_AUTH_TOKEN secret) and configure composer config github-oauth.github.com in the workflows before composer install.

  • composer.lock plugin-api-version downgrade — Dropped from 2.9.0 to 2.6.0, suggesting the lock was regenerated with an older Composer version. May cause issues if CI or other devs use Composer 2.9+.

  • Recursive vendor path in kit — The CSS lint hook fails with ENAMETOOLONG because wp-admin-ui/examples/minimal-plugin/ contains a self-referencing vendor path. Worth adding examples/ to .gitattributes export-ignore in the kit repo.

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