{{define "admin_users"}} People · llmgw {{/* Forms here post and get the whole page back, which works with no script at all. htmx, when it loads, swaps only #people from that page instead of reloading it, so an open project stays open and the page does not jump. */}}

People

{{len .Pending}} waiting for approval · {{len .Accounts}} accounts · {{len .Projects}} projects

{{if .Error}}{{end}} {{if .Message}}

{{.Message}}

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

Waiting for approval

{{range .Pending}}{{end}}
EmailNameSigned upPut them in
{{.Email}}{{.Name}}{{ago .CreatedAt}} {{if $.Projects}}
{{else}}Create a project first, below.{{end}}
{{end}}

Organizations, teams and projects

A person is in a project, a project in a team, a team in an organization{{if .HasQuotas}}, and quotas can be set on any of them{{end}}. Open a project to see 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}}
rename
{{if not $org.Teams}}
{{end}}
{{range $team := $org.Teams}}
{{$team.Name}} {{len $team.Projects}} project{{if ne (len $team.Projects) 1}}s{{end}}
rename
{{if gt (len $.Orgs) 1}}
move
{{end}} {{if not $team.Projects}}
{{end}}
{{range $p := $team.Projects}}
{{$p.Name}} {{$p.Users}} {{if eq $p.Users 1}}person{{else}}people{{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 $.People $p.ID}} {{range .}}{{template "user-row-actions" (dict "U" . "D" $ "Open" $p.ID)}}{{end}}
PersonStateLast sign-inMove to
{{else}}

Nobody is in this project.

{{end}} {{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}}
{{end}}
{{end}}

All accounts

{{if .Accounts}} {{range .Accounts}}{{template "user-row-actions" (dict "U" . "D" $ "Open" "" "Path" true)}}{{end}}
PersonStateProjectLast sign-inMove to
{{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}} {{define "user-row-actions"}}{{$u := .U}}{{$d := .D}} {{$u.Email}}{{if $u.IsAdmin}} admin{{end}} {{$u.State}} {{if .Path}}{{or $u.ProjectPath "—"}}{{end}} {{ago $u.LastLoginAt}}
{{if ne $u.ID $d.AdminID}} {{if eq (printf "%s" $u.State) "active"}}
{{else}}
{{end}}
{{else}}you{{end}} {{end}}