package server // Shell is the data every top-level page hands the shared application // shell (docs/design-system.md, "Application shell"): the top bar with its // account menu (partials/shell_topbar.html) and, on the member surface, the // rail (partials/shell_rail_member.html). One value per page, so the shell // is written once and both surfaces render it the same way // (chrome-conventions: "One shell for both surfaces"; "Session controls // live in the account menu on both surfaces"). type Shell struct { // Surface is ShellMember or ShellOperator. The operator surface adds // the muted "Operator" label after the brand; the member surface // renders the member rail. Surface string // Name, Username, and Email are the session's identity claims. Name is // empty when the identity provider supplied none. Name string Username string Email string // KeycloakAccountURL is the identity provider's account console, the // "Identity and Access" destination. KeycloakAccountURL string // IsOperator gates the member rail's "Operator panel" surface switch. IsOperator bool // Active names the member rail entry to mark active: "dashboard", // "products", or "billing". The operator rail keeps its own // ActiveCapability logic in operator.html and ignores this. Active string } // The two surfaces the shell renders. const ( ShellMember = "member" ShellOperator = "operator" ) // Label is the account menu's trigger text: the person's display name, the // username when the identity provider supplied no name, and a neutral word // when it supplied neither, so the trigger is never blank. func (s Shell) Label() string { if s.Name != "" { return s.Name } if s.Username != "" { return s.Username } return "Account" } // IsOperatorSurface reports whether the shell renders the operator surface. func (s Shell) IsOperatorSurface() bool { return s.Surface == ShellOperator } // Shell derives the operator surface's shell from the page data, which // already carries the person's identity and the account console URL. // Every operator page (this package's and an integration's via // BuildOperatorPageData) gets the same shell without touching its // constructor. func (d OperatorPageData) Shell() Shell { return Shell{ Surface: ShellOperator, Name: d.Name, Username: d.Username, Email: d.Email, KeycloakAccountURL: d.KeycloakAccountURL, IsOperator: true, } }