Skip to content

[6.x] Render standalone CP pages with Inertia and Blade - #19931

Draft
brianjhanson wants to merge 7 commits into
6.xfrom
feature/legacy-page-styling
Draft

brianjhanson wants to merge 7 commits into
6.xfrom
feature/legacy-page-styling

Conversation

@brianjhanson

@brianjhanson brianjhanson commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

Follow-up to #19884, which moved most control panel screens into the Inertia shell. A handful of pages still drew their own Twig documents. This ports three of them, gives the control panel its own error page, and adds workbench pages for previewing what's left.

Ported to Inertia

  • Plugin installer: BaseUpdaterController::index() now renders updater/Index, the page the updater, plugin remover and config sync already used. Their identical index() overrides are gone. _special/updater.twig is removed. The installer's return URL is now a full CP URL, since the Vue page navigates to it directly.
  • Email taken: VerifyEmailController renders a new auth/EmailTaken page through renderViewWithFallback(), like the other auth pages.
  • Licensing issues: EnforceLicenses renders a new licensing/Issues page, still with a 402 and no-cache headers. The countdown, the shun-cookie request and the Refresh button are ported from the jQuery version.

Control panel error pages

  • CP requests that expect HTML now get resources/views/errors/cp.blade.php, a standalone page with inline styles and no JS. It's served through a renderable registered in AppServiceProvider (CpErrorPage).
  • Site requests are unchanged and still use the site's own errors/* templates. CP errors used to show Laravel's built-in page, or the site's own template when one existed.
  • In production an unexpected exception gets a generic 500 without its message. Debug mode still shows the exception page, and JSON responses are untouched.
  • The Twig error templates (errors/*.twig, error.twig) weren't rendered by anything and are removed.

Standalone page styling

  • _layouts/base now gives its body the cp-legacy-screen class, the scope bridged screens already use, so standalone Twig pages pick up the same compat styles. legacy.ts now imports legacy.css, which adds the message-page layout.
  • The can't-run page uses utility classes in place of the old bullets list, and the error page is restyled.
  • _special/login.twig is removed. Only the login modal included it, and 6.x has since removed that.

Workbench

New sections under Debug:

  • Special Pages: the pages still drawn by Twig, with sample data, grouped by the layout they extend:
    • Messages (_layouts/message): database update, can't run, set password, verify email.
    • Auth pages: 2FA setup.
    • Preview: standalone preview.
  • Ported Pages: each ported page in each of its states. The updater scenarios run against fake steps, so nothing is installed.
  • Error Pages: real errors, raised so they go through the exception handler.

Not included

  • Database update, can't run, 2FA setup and standalone preview are still Twig.
  • An Inertia visit that lands on a CP error still shows the error page in a modal.
  • A CP URL with no matching route still returns an empty 404 from the template-route fallback, so it never reaches the new error page.

Note for review

VerifyEmailController only reaches the email-taken page when Users::verifyEmailForUser() throws InvalidElementException, which it currently never does, because it saves the user model directly. The new test forces that exception. Verifying an email that another user already has seems to go through today, which may deserve its own issue.

Tests

  • Updated InstallControllerTest and EnforceLicensesTest for the Inertia responses.
  • Added an email-taken case to VerifyEmailControllerTest, plus CpErrorPageTest.
  • The full suite passes (11,452 tests), as does the adapter suite.

🤖 Generated with Claude Code

The plugin installer, email-taken and licensing pages were standalone Twig
documents. They now render through Inertia: the installer through the same
`updater/Index` page as the other updaters, and the others as new
`auth/EmailTaken` and `licensing/Issues` pages.

Control panel errors render a Craft-owned Blade page instead of Laravel's
default or the site's own error templates, which still apply to site
requests. The unused Twig error templates are removed.

The workbench gets Debug sections for the pages still drawn by Twig, the
ported pages in each of their states, and real error pages, plus an overlay
that draws the nav flyouts' safe areas, which `HoverIntentGroup::safeAreas()`
now exposes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

📚 Storybook previews

@craftcms/ui — open Storybook

No changed components detected in this Storybook.

resources/js — open Storybook

No changed components detected in this Storybook.

brianjhanson and others added 6 commits October 9, 2026 17:14
It was only there to debug the flyout closing early, which is fixed
separately, so it and the `safeAreas()` method it read from can go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the legacy `Craft.AuthManager` and `Craft.AssetImageEditor` call
`users/login-modal` and `assets/image-editor`; the control panel itself uses
the Vue elevated-session host and image editor dialog. The endpoints, their
templates, and their tests now live in yii2-adapter, at the same URLs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yling

6.x removed Craft.AuthManager, the legacy AssetImageEditor, and the
users/login-modal action outright, so the adapter copies of the login modal
and image editor endpoints go too. assets/image-editor stays in core, where
6.x now uses it to serve the new image editor's settings. login.twig is
deleted, since the login modal was the only thing including it.

This branch has not been deployed

No deployments
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.

1 participant