refactor(client): delete the UO client half (phase 3, slice 3)
35 files and 5,332 lines out — twelve public pages, seven admin views, two player views, eight components, the two `data/` leaves and the three `lib/` ones, plus the two tests that came with them. §2.7.1's estimate of 51 files / ~3,700 lines was measured differently and is corrected in the docs PR. The seams core keeps, each smaller than what it replaced: Nine rows leave the public header and six leave the admin sidebar, and both lists are now free of `feature` gates and of `IconShard`. `moduleTitle` already handled a module page's heading, so the six TITLES entries and the `/admin/characters` branch of `sectionTitle` simply go. `/player` had `PlayerCharacters` as its index — a UO page — and rather than name a replacement or invent a landing screen it now resolves to the first row of the portal nav this viewer can reach (`firstDestinationFor`, beside `allowedPathsFor` and reading the BASE nav for the same reason: an override is presentation and where everybody lands is behaviour). With the module installed that is still Characters, so a player's first screen after signing in does not change. Deliberately generic and deliberately not in the portal layout — the admin index is the same question with a hardcoded answer, and if the two logged-in areas ever become one this is what serves both. `game_account_signup` goes with the rest of core's UO prose: the mode list, the derived public flag, the validation and a Site Settings field whose help text named Bridge.cfg. The row itself is untouched and module-uo reads it through ctx.settings — the data stays, the semantics move. KNOWN BREAK, accepted by the org lead: the shipped Android app reads `gameAccountSignup` off `/public/settings` (PublicDto.kt:80). The field has a `= false` default so nothing crashes; the app silently stops offering game-account creation until it reads the module's `/public/shard/features` instead. Out of scope here, recorded in the Android plan, and it lands well before this workstream's cutover reaches `main`. 620 server + 161 client tests. Manifest 158 public + 2 internal, unchanged; routes.guards unchanged. The OpenAPI spec loses exactly one property, and only because it was hand-written in swagger.js — regeneration alone would have left the spec documenting a field core no longer returns. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -62,3 +62,39 @@ export function isAllowedPath(pathname, allowed) {
|
||||
exact ? pathname === to : pathname === to || pathname.startsWith(`${to}/`),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The first place in this nav a viewer with this role can actually go.
|
||||
*
|
||||
* Added in Phase 3 slice 3, for the player portal, whose index route was
|
||||
* `PlayerCharacters` — a UO page. When it left, `/player` had nothing behind it,
|
||||
* and the three ways out were: redirect somewhere fixed, invent a core landing
|
||||
* page, or resolve the index from the nav the viewer already has. This is the
|
||||
* third, and it is the only one that keeps today's behaviour — with the module
|
||||
* installed the first row is still Characters, so a player still lands on their
|
||||
* characters after signing in, and with nothing installed they land on Account.
|
||||
*
|
||||
* **From the BASE nav, never the override-merged one**, the same rule
|
||||
* `allowedPathsFor` follows and for a sharper version of the same reason: an
|
||||
* override is presentation, and a landing page is behaviour. An admin reordering
|
||||
* the sidebar must not silently change where everybody arrives, and — more to
|
||||
* the point — must not be able to move it somewhere a role cannot follow.
|
||||
*
|
||||
* Deliberately generic, and deliberately in this file rather than in the portal
|
||||
* layout. The admin area has the same shape of question (its index is a
|
||||
* hardcoded Dashboard), and the direction of travel is one logged-in area that
|
||||
* shows the right things for the viewer's permissions rather than two that
|
||||
* duplicate each other. When that happens this is the function it needs, and it
|
||||
* already answers for both nav shapes.
|
||||
*
|
||||
* @param {Array} baseNav flat or grouped, before overrides
|
||||
* @param {string} role
|
||||
* @param {string} fallback where to go when the viewer can see nothing at all
|
||||
*/
|
||||
export function firstDestinationFor(baseNav, role, fallback) {
|
||||
const items = (Array.isArray(baseNav) ? baseNav : []).flatMap((entry) =>
|
||||
entry && Array.isArray(entry.items) ? entry.items : [entry],
|
||||
)
|
||||
const first = items.find((item) => item && item.to && navItemVisibleTo(item, role))
|
||||
return first ? first.to : fallback
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user