Files
member-console/internal/server/shell.go
T
cgalo5758 88db730fcc Add dual licensing and SPDX headers
Introduce a commercial license option alongside AGPL-3.0-only, require a
CLA for contributors, and document the terms in COMMERCIAL.md and
NOTICE. Add a script to stamp SPDX headers on Go files and apply it
across the tree.
2026-09-06 02:29:42 -05:00

71 lines
2.5 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
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,
}
}