Skip to content

Registry website: Inertia (React) rebuild from the Waypoints design - #8

Merged
roncodes merged 11 commits into
release/v2.0.0from
feat/website-inertia
Oct 24, 2026
Merged

roncodes merged 11 commits into
release/v2.0.0from
feat/website-inertia

Conversation

@roncodes

Copy link
Copy Markdown
Member

Stacked on #6 (the website backend). Once #6 merges into v2, retarget this PR to v2.

The publisher website and staff review console, built with Inertia v3, React 19, Tailwind v4 and Vite inside the Laravel app, following the Waypoints design canvas. It replaces the Ember prototype, which is archived on archive/website-ember-prototype.

Pages

  • Public: Discover (search, intents, filters, "works with my console"), extension pages with install steps and a version check, publisher profiles.
  • Account: sign in, create account, email code, forgot password, Sign in with GitHub, profile and password, organizations and members, tokens shown once.
  • Studio: inbox, new listing with live package-name availability, the listing editor with a live readiness rail and drag-to-reorder screenshots, uploads, live release checks, editable release notes, submit / withdraw / rejected states, history, insights placeholder.
  • Review console: queue (tabs, filters, J/K, "Update from vX"), the R1 to R6 decision rail with what changed since the previous version, request changes (also on approved releases), publish, suspend and lift, the To publish page.
  • Light and dark themes, phone layouts, error pages rendered through Inertia.

How it works

  • Page controllers in app/Http/Controllers/Web call the /v1 controllers in process, so page props are the API's JSON. Pages write to /v1 with the session cookie and XSRF header.
  • Server-side rendering: npm run build produces a self-contained bootstrap/ssr/ssr.js. The Docker image installs Node and runs it as an s6 service (docker/s6/inertia-ssr). Without it, pages render in the browser.

API additions

Endpoint Purpose
GET /v1/catalog/extensions?console=&compatible=1 Compatibility annotation per listing; filters to listings with a release for that console
POST /v1/auth/password/forgot, POST /v1/auth/password/reset Password reset with a six-digit code; no account enumeration; revokes tokens and sessions
GET /auth/github, GET /auth/github/callback, DELETE /v1/account/github Sign in with GitHub (Socialite), connect and disconnect
GET /v1/publisher/packages/availability Package-name checks; one rule set shared with create
PATCH /v1/publisher/releases/{id} Edit release notes (not when yanked)

Response changes:

  • Publisher release summaries add submitted_at and decided_at.
  • Admin release responses add previous_version and previous.

Migrations:

  • verification_codes.attempts: codes die after five wrong guesses.
  • accounts.github_login, and accounts.github_id becomes unique.

Configuration:

  • New env: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_REDIRECT_URI, all empty in .env.example. The GitHub button only shows once they are set.

Tests

  • Pest: the full suite passes. New tests cover the website pages, catalog compatibility, password reset, GitHub sign-in (Socialite mocked), package availability, release notes and the review console.
  • Vitest: 114 component and page-logic tests.
  • Playwright: smoke test against a seeded server with SSR on and SSR errors failing the request. It covers the catalog, a publisher token shown once, staff review through to the catalog, the theme toggle, and a phone layout.
  • CI: new website (ESLint, Vitest, build) and website-e2e jobs.
  • Docker: the image was built locally. node bootstrap/ssr/ssr.js runs beside php-fpm and nginx, and / is served server-rendered.

Decisions for review

  • Reusing your own package name: the availability check reports a name already used by one of your own listings as not available. Create is idempotent and returns that listing instead.
  • GitHub conflict: connecting a GitHub account that belongs to someone else redirects to the profile with an error toast, not a 409 page.
  • Release notes edits: allowed after publishing, without review (the same as listing copy edits).

…design

The publisher website and staff review console, served by the Laravel app with Inertia v3,
React 19, Tailwind v4 and Vite, replacing the Ember prototype.

- Page controllers (app/Http/Controllers/Web) read through the /v1 API controllers in
  process, so props have the API's JSON shapes; pages write to /v1 with the session cookie
  and XSRF header. Website routes use auth:web; unknown paths render the website 404.
- Public: Discover (search, intents, filters, console compatibility), extension pages with
  install steps and a version check, publisher profiles.
- Account: sign in, create account, email code, profile and password, organizations and
  members, personal tokens shown once.
- Studio: inbox, new listing, the listing editor with a live readiness rail, uploads, live
  release checks, submit / withdraw / rejected states, history, insights placeholder.
- Review console: queue with tabs, filters and J/K, the R1 to R6 decision rail, publish,
  suspend and lift, the To publish page.
- Light and dark themes, phone layouts, error pages through Inertia.
- Vitest component and page-logic tests, Pest page tests, a Playwright smoke test with its
  own seeded server (scripts/e2e-server.sh), CI jobs, and a Docker stage for the assets.
…nd Studio features

Catalog
- GET /v1/catalog/extensions takes console=<version> (each item gets compatibility:
  {console, compatible, newest_compatible}) and compatible=1 (only listings with a published
  release for that console). Discover filters on the server, so counts and pages cover the
  whole catalog; ?compat=any shows every version.

Server-side rendering
- The SSR bundle is self-contained (ssr.noExternal), listens on 127.0.0.1 and takes
  INERTIA_SSR_PORT. The Docker image installs Node and runs it as an s6 service beside nginx
  and php-fpm (docker/s6/inertia-ssr); INERTIA_SSR_ENABLED=false turns it off.
- The Playwright server runs the renderer with SSR errors failing the request; tests wait for
  hydration (html[data-ready]). Write forms are method=post and the search and version check
  forms are real GET forms, so nothing typed before hydration lands in a URL.

Account
- Forgot password: POST /v1/auth/password/forgot (no enumeration) and /reset with a six-digit
  code; resetting revokes every token and session. Codes now die after five wrong guesses.
- Sign in with GitHub (laravel/socialite): match by github_id, then verified email, else a
  new account; connect and disconnect on the profile. accounts.github_id becomes unique.

Studio
- GET /v1/publisher/packages/availability, sharing one rule set (PackageNames) with create;
  live Available chips on the new-listing page.
- submitted_at and decided_at on release summaries: "Submitted N days ago" and "day N".
- PATCH /v1/publisher/releases/{id} edits release notes (not when yanked).
- Drag to reorder screenshots, keeping the buttons for keyboard and screen readers.

Review console
- previous_version (and the previous release) on admin release responses, without N+1:
  "Update from vX" in the queue and what changed since it on the release page.
- Request changes on an approved release, from the release page and To publish.

Also: Vitest timeout 20 s for slow CI runners.
The plugin refuses to start its dev server when CI is set, which failed the
Vitest step. Tests need neither the manifest nor the hot file.
@roncodes
roncodes changed the base branch from feat/publisher-website to release/v2.0.0 October 24, 2026 09:21
@roncodes
roncodes merged commit 6ebdde3 into release/v2.0.0 Oct 24, 2026
6 checks passed
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