{{/* The first tab of the people page: the tree, and everyone in it. */}} {{define "people_body"}} {{if .Error}}{{end}} {{if .Message}}

{{.Message}}

{{end}} {{if .Pending}}

Waiting for approval

{{range .Pending}}{{end}}
EmailNameSigned upApprove, and put them in
{{.Email}}{{.Name}}{{ago .CreatedAt}}
{{end}}

Organizations, teams and projects

A person is in a project, a project in a team, a team in an organization{{if .HasQuotas}}, and limits can be set on any of them{{end}}. Choose a name — or a person in the list below — to see its limits, capacity and use beside the tree; open a project for its people and keys.

{{if not .Tree}}

No organizations yet. Create one, then a team in it, then a project in the team — an account can only be approved into a project.

{{end}} {{range $org := .Tree}}
{{$org.Name}} {{len $org.Teams}} team{{if ne (len $org.Teams) 1}}s{{end}} {{template "scope-members" (dict "Kind" "org" "ID" $org.ID "Name" $org.Name "D" $)}}
rename
{{if not $org.Teams}}
{{end}}
{{range $team := $org.Teams}}
{{$team.Name}} {{len $team.Projects}} project{{if ne (len $team.Projects) 1}}s{{end}} {{template "scope-members" (dict "Kind" "team" "ID" $team.ID "Name" $team.Name "D" $)}}
rename
{{if gt (len $.Orgs) 1}}
move
{{end}} {{if not $team.Projects}}
{{end}}
{{range $p := $team.Projects}}
{{$p.Name}} limits & use {{$p.Users}} member{{if ne $p.Users 1}}s{{end}} · {{$p.ActiveKeys}} key{{if ne $p.ActiveKeys 1}}s{{end}}{{if gt $p.Keys $p.ActiveKeys}} ({{sub $p.Keys $p.ActiveKeys}} revoked){{end}}
rename
{{if gt (len $.Teams) 1}}
move to another team
{{end}} {{if and (eq $p.Users 0) (eq $p.Keys 0)}}
{{else}}Removable once it has no people and never had a key.{{end}}
{{with index $.Members (printf "project:%s" $p.ID)}} {{range .}}{{template "member-row" (dict "U" . "D" $ "Scope" (printf "project:%s" $p.ID) "Open" $p.ID)}}{{end}}
MemberStateLast sign-in
{{else}}

No members of its own. Members of its team or organization can use it too.

{{end}} {{template "add-member" (dict "Kind" "project" "ID" $p.ID "D" $ "Open" $p.ID)}} {{with index $.Keys $p.ID}} {{range .}}{{end}}
KeyOwnerStateLast used
{{.Name}} {{.Prefix}} {{or .OwnerEmail "issued by an operator"}} {{.State}} {{ago .LastUsedAt}} {{if eq .State "active"}}
{{end}}
{{end}}
{{end}} {{/* Folded until wanted: a box open under every team was most of the tree's height and read as something to fill in. */}}
+ project
{{end}}
+ team
{{end}}

All accounts

{{if .Accounts}} {{range .Accounts}}{{template "account-row" (dict "U" . "D" $)}}{{end}}
PersonStateMember ofLast sign-inAdd to

Someone who is a member of nothing uses the gateway on their own: personal keys and the chat page, within their personal limits.

{{else}}

No accounts yet.

{{end}}

Not here on purpose: making someone an admin, resetting a password and issuing a key for someone else. Each turns a stolen admin session into more access than it had, so they need a shell on the control plane — llmgw users promote, llmgw users reset-password, llmgw keys create.

{{end}} {{/* The actions every account row carries: suspend or reinstate, remove. */}} {{define "state-actions"}}{{$u := .U}}{{$d := .D}} {{if ne $u.ID $d.AdminID}} {{/* In a menu: two red links on every row made the list look alarmed, and they are the two things done least often. */}}
⋯
{{else}}you{{end}} {{end}} {{/* A person in the list of every account: where they are, and where else they could be. */}} {{define "account-row"}}{{$u := .U}}{{$d := .D}} {{$u.Email}}{{if $u.IsAdmin}} admin{{end}} {{$u.State}} {{range $i, $m := $u.Memberships}}{{if lt $i 2}}{{with $m}}{{.Path}}{{if eq .Role "manager"}} manager{{end}} {{if eq (printf "%s" .Scope) "org"}}
{{end}}
{{end}}{{end}} {{else}}on their own{{end}} {{/* Two, then a count: a person in six teams was six chips wrapping the row into a paragraph. The rest are named on hover and in their own scopes' member lists, where taking them out is done. */}} {{if gt (len $u.Memberships) 2}}+{{sub (len $u.Memberships) 2}}{{end}} {{ago $u.LastLoginAt}}
{{template "state-actions" (dict "U" $u "D" $d "Open" "")}} {{end}} {{/* A member in a scope's own list: taking them out of here is the action. */}} {{define "member-row"}}{{$u := .U}}{{$d := .D}} {{$u.Email}}{{if $u.IsAdmin}} admin{{end}} {{if gt (len $u.Memberships) 1}}also in {{sub (len $u.Memberships) 1}} more{{end}} {{$u.State}} {{ago $u.LastLoginAt}}
{{end}} {{/* Adding a person to a scope, from the scope. */}} {{define "add-member"}}{{$d := .D}}
{{end}} {{/* An org's or team's own members, folded away until asked for. */}} {{define "scope-members"}}{{$d := .D}}{{$k := printf "%s:%s" .Kind .ID}}
{{len (index $d.Members $k)}} member{{if ne (len (index $d.Members $k)) 1}}s{{end}}
{{with index $d.Members $k}} {{range .}}{{template "member-row" (dict "U" . "D" $d "Scope" $k "Open" "")}}{{end}}
{{else}}

Nobody is a member of {{.Name}} itself; a member here could use every project in it.

{{end}} {{template "add-member" (dict "Kind" .Kind "ID" .ID "D" $d "Open" "")}}
{{end}}