Extract the top bar and account menu into shell_topbar.html and the member rail into shell_rail_member.html, backed by a single server.Shell value. Move session controls into the account menu, add the mirrored Operator panel/Member dashboard surface switch, and turn the rail into an offcanvas drawer below lg with shell.js closing it on navigation. Update docs, specs, and tests. Unify application shell across both surfaces Extract the top bar and member rail into shared partials and introduce server.Shell as the single data value for page chrome. Move session controls into an account menu, make the rail an offcanvas drawer below lg, and add the mirrored surface switch.
68 lines
2.4 KiB
Go
68 lines
2.4 KiB
Go
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,
|
|
}
|
|
}
|