combine aliases page with members page
This commit is contained in:
parent
dafc341091
commit
4f70c62ab2
|
@ -20,6 +20,20 @@ type Members struct {
|
|||
db *sql.DB
|
||||
}
|
||||
|
||||
func getAliases(mEntry *models.Member) []roomdb.Alias {
|
||||
if mEntry.R == nil || mEntry.R.Aliases == nil {
|
||||
return make([]roomdb.Alias, 0)
|
||||
}
|
||||
var aliases = make([]roomdb.Alias, len(mEntry.R.Aliases))
|
||||
for j, aEntry := range mEntry.R.Aliases {
|
||||
aliases[j].ID = aEntry.ID
|
||||
aliases[j].Feed = aEntry.R.Member.PubKey.FeedRef
|
||||
aliases[j].Name = aEntry.Name
|
||||
aliases[j].Signature = aEntry.Signature
|
||||
}
|
||||
return aliases
|
||||
}
|
||||
|
||||
func (m Members) Add(ctx context.Context, pubKey refs.FeedRef, role roomdb.Role) (int64, error) {
|
||||
var newID int64
|
||||
err := transact(m.db, func(tx *sql.Tx) error {
|
||||
|
@ -61,9 +75,10 @@ func (m Members) GetByID(ctx context.Context, mid int64) (roomdb.Member, error)
|
|||
return roomdb.Member{}, err
|
||||
}
|
||||
return roomdb.Member{
|
||||
ID: entry.ID,
|
||||
Role: roomdb.Role(entry.Role),
|
||||
PubKey: entry.PubKey.FeedRef,
|
||||
ID: entry.ID,
|
||||
Role: roomdb.Role(entry.Role),
|
||||
PubKey: entry.PubKey.FeedRef,
|
||||
Aliases: getAliases(entry),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -82,16 +97,18 @@ func (m Members) GetByFeed(ctx context.Context, h refs.FeedRef) (roomdb.Member,
|
|||
|
||||
// List returns a list of all the feeds.
|
||||
func (m Members) List(ctx context.Context) ([]roomdb.Member, error) {
|
||||
all, err := models.Members().All(ctx, m.db)
|
||||
all, err := models.Members(qm.Load("Aliases")).All(ctx, m.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var members = make([]roomdb.Member, len(all))
|
||||
for i, listEntry := range all {
|
||||
members[i].ID = listEntry.ID
|
||||
members[i].Role = roomdb.Role(listEntry.Role)
|
||||
members[i].PubKey = listEntry.PubKey.FeedRef
|
||||
for i, entry := range all {
|
||||
members[i].ID = entry.ID
|
||||
members[i].Role = roomdb.Role(entry.Role)
|
||||
members[i].PubKey = entry.PubKey.FeedRef
|
||||
members[i].Aliases = getAliases(entry)
|
||||
println(len(members[i].Aliases))
|
||||
}
|
||||
|
||||
return members, nil
|
||||
|
|
|
@ -28,9 +28,10 @@ type Alias struct {
|
|||
|
||||
// Member holds all the information an internal user of the room has.
|
||||
type Member struct {
|
||||
ID int64
|
||||
Role Role
|
||||
PubKey refs.FeedRef
|
||||
ID int64
|
||||
Role Role
|
||||
PubKey refs.FeedRef
|
||||
Aliases []Alias
|
||||
}
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=Role
|
||||
|
|
|
@ -25,25 +25,6 @@ type aliasesHandler struct {
|
|||
|
||||
const redirectToAliases = "/admin/aliases"
|
||||
|
||||
func (h aliasesHandler) overview(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
lst, err := h.db.List(req.Context())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reverse the slice to provide recent-to-oldest results
|
||||
for i, j := 0, len(lst)-1; i < j; i, j = i+1, j-1 {
|
||||
lst[i], lst[j] = lst[j], lst[i]
|
||||
}
|
||||
|
||||
pageData, err := paginate(lst, len(lst), req.URL.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pageData, nil
|
||||
}
|
||||
|
||||
func (h aliasesHandler) revokeConfirm(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
if req.Method != "GET" {
|
||||
return nil, weberrors.ErrBadRequest{Where: "HTTP Method", Details: fmt.Errorf("expected GET request")}
|
||||
|
|
|
@ -25,7 +25,6 @@ var HTMLTemplates = []string{
|
|||
"admin/dashboard.tmpl",
|
||||
"admin/menu.tmpl",
|
||||
|
||||
"admin/aliases.tmpl",
|
||||
"admin/aliases-revoke-confirm.tmpl",
|
||||
|
||||
"admin/denied-keys.tmpl",
|
||||
|
@ -37,7 +36,8 @@ var HTMLTemplates = []string{
|
|||
|
||||
"admin/notice-edit.tmpl",
|
||||
|
||||
"admin/members.tmpl",
|
||||
"admin/member.tmpl",
|
||||
"admin/member-list.tmpl",
|
||||
"admin/members-remove-confirm.tmpl",
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,6 @@ func Handler(
|
|||
r: r,
|
||||
db: dbs.Aliases,
|
||||
}
|
||||
mux.HandleFunc("/aliases", r.HTML("admin/aliases.tmpl", ah.overview))
|
||||
mux.HandleFunc("/aliases/revoke/confirm", r.HTML("admin/aliases-revoke-confirm.tmpl", ah.revokeConfirm))
|
||||
mux.HandleFunc("/aliases/revoke", ah.revoke)
|
||||
|
||||
|
@ -110,7 +109,8 @@ func Handler(
|
|||
r: r,
|
||||
db: dbs.Members,
|
||||
}
|
||||
mux.HandleFunc("/members", r.HTML("admin/members.tmpl", mh.overview))
|
||||
mux.HandleFunc("/member", r.HTML("admin/member.tmpl", mh.details))
|
||||
mux.HandleFunc("/members", r.HTML("admin/member-list.tmpl", mh.overview))
|
||||
mux.HandleFunc("/members/add", mh.add)
|
||||
mux.HandleFunc("/members/change-role", mh.changeRole)
|
||||
mux.HandleFunc("/members/remove/confirm", r.HTML("admin/members-remove-confirm.tmpl", mh.removeConfirm))
|
||||
|
|
|
@ -104,7 +104,8 @@ func (h membersHandler) changeRole(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, req, redirectToMembers, http.StatusTemporaryRedirect)
|
||||
memberDetailsURL := fmt.Sprint("/admin/member?id=", memberID)
|
||||
http.Redirect(w, req, memberDetailsURL, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
func (h membersHandler) overview(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
|
@ -129,6 +130,28 @@ func (h membersHandler) overview(rw http.ResponseWriter, req *http.Request) (int
|
|||
return pageData, nil
|
||||
}
|
||||
|
||||
func (h membersHandler) details(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
id, err := strconv.ParseInt(req.URL.Query().Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
err = weberrors.ErrBadRequest{Where: "ID", Details: err}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
member, err := h.db.GetByID(req.Context(), id)
|
||||
if err != nil {
|
||||
err = weberrors.ErrBadRequest{Where: "ID", Details: err}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roles := []roomdb.Role{roomdb.RoleMember, roomdb.RoleModerator, roomdb.RoleAdmin}
|
||||
|
||||
return map[string]interface{}{
|
||||
"Member": member,
|
||||
"AllRoles": roles,
|
||||
csrf.TemplateTag: csrf.TemplateField(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h membersHandler) removeConfirm(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
id, err := strconv.ParseInt(req.URL.Query().Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
|
|
|
@ -37,10 +37,6 @@ AuthFallbackInstruct = "This method is an acceptable fallback, if you have a use
|
|||
AdminDashboardTitle = "Dashboard"
|
||||
AdminDashboardWelcome = "Welcome to your dashboard"
|
||||
|
||||
AdminAliasesTitle = "Aliases"
|
||||
AdminAliasesWelcome = "Here you can see and revoke the registered aliases of this room."
|
||||
AdminAliasesRevoke = "Revoke"
|
||||
|
||||
AdminDeniedKeysTitle = "Banned"
|
||||
AdminDeniedKeysWelcome = "This page can be used to ban SSB IDs so that they can't access the room any more."
|
||||
AdminDeniedKeysAdd = "Add"
|
||||
|
@ -53,9 +49,17 @@ AdminDeniedKeysRemoveConfirmTitle = "Confirm member removal"
|
|||
AdminMembersTitle = "Members"
|
||||
AdminMembersWelcome = "Here you can see all the members of the room and ways to add new ones (by their SSB ID) or remove exising ones."
|
||||
AdminMembersAdd = "Add"
|
||||
AdminMembersRemove = "Remove"
|
||||
AdminMembersRemoveConfirmWelcome = "Are you sure you want to remove this member? They will lose their alias, if they have one."
|
||||
|
||||
AdminMembersRemoveConfirmTitle = "Confirm member removal"
|
||||
AdminMembersRemoveConfirmWelcome = "Are you sure you want to remove this member? They will lose their alias, if they have one."
|
||||
|
||||
AdminMemberDetailsTitle = "Member details"
|
||||
AdminMemberDetailsSSBID = "SSB Identifier"
|
||||
AdminMemberDetailsRole = "Permission level"
|
||||
AdminMemberDetailsAliases = "Aliases"
|
||||
AdminMemberDetailsAliasRevoke = "Revoke"
|
||||
AdminMemberDetailsExclusion = "Exclusion from this room"
|
||||
AdminMemberDetailsRemove = "Remove member"
|
||||
|
||||
AdminInvitesTitle = "Invites"
|
||||
AdminInvitesWelcome = "Create invite tokens for people who are not yet members of this room. At the same time as you create an invite, you can also (optionally) attach an alias to it so that the person who receives the invite can automatically use that claim that alias for themselves."
|
||||
|
@ -69,7 +73,6 @@ AdminInviteRevoke = "Revoke"
|
|||
AdminInviteRevokeConfirmTitle = "Confirm invite revocation"
|
||||
AdminInviteRevokeConfirmWelcome = "Are you sure you want to remove this invite? If you already sent it out, they will not be able to use it."
|
||||
|
||||
|
||||
# TODO: add placeholder support to the template helpers (https://github.com/ssb-ngi-pointer/go-ssb-room/issues/60)
|
||||
AdminInviteCreatedBy = "Created by:"
|
||||
AdminInviteSuggestedAliasIs = "The suggested alias is:"
|
||||
|
|
|
@ -9,7 +9,6 @@ const (
|
|||
AdminDashboard = "admin:dashboard"
|
||||
AdminMenu = "admin:menu"
|
||||
|
||||
AdminAliasesOverview = "admin:aliases:overview"
|
||||
AdminAliasesRevokeConfirm = "admin:aliases:revoke:confirm"
|
||||
AdminAliasesRevoke = "admin:aliases:revoke"
|
||||
|
||||
|
@ -18,6 +17,8 @@ const (
|
|||
AdminDeniedKeysRemoveConfirm = "admin:denied-keys:remove:confirm"
|
||||
AdminDeniedKeysRemove = "admin:denied-keys:remove"
|
||||
|
||||
AdminMemberDetails = "admin:member:details"
|
||||
|
||||
AdminMembersOverview = "admin:members:overview"
|
||||
AdminMembersAdd = "admin:members:add"
|
||||
AdminMembersChangeRole = "admin:members:change-role"
|
||||
|
@ -44,7 +45,6 @@ func Admin(m *mux.Router) *mux.Router {
|
|||
m.Path("/dashboard").Methods("GET").Name(AdminDashboard)
|
||||
m.Path("/menu").Methods("GET").Name(AdminMenu)
|
||||
|
||||
m.Path("/aliases").Methods("GET").Name(AdminAliasesOverview)
|
||||
m.Path("/aliases/revoke/confirm").Methods("GET").Name(AdminAliasesRevokeConfirm)
|
||||
m.Path("/aliases/revoke").Methods("POST").Name(AdminAliasesRevoke)
|
||||
|
||||
|
@ -53,6 +53,8 @@ func Admin(m *mux.Router) *mux.Router {
|
|||
m.Path("/denied/remove/confirm").Methods("GET").Name(AdminDeniedKeysRemoveConfirm)
|
||||
m.Path("/denied/remove").Methods("POST").Name(AdminDeniedKeysRemove)
|
||||
|
||||
m.Path("/member").Methods("GET").Name(AdminMemberDetails)
|
||||
|
||||
m.Path("/members").Methods("GET").Name(AdminMembersOverview)
|
||||
m.Path("/members/add").Methods("POST").Name(AdminMembersAdd)
|
||||
m.Path("/members/change-role").Methods("POST").Name(AdminMembersChangeRole)
|
||||
|
|
|
@ -5,7 +5,10 @@ module.exports = {
|
|||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
extend: {
|
||||
backgroundColor: ['odd'],
|
||||
zIndex: ['hover'],
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
{{ define "title" }}{{i18n "AdminAliasesTitle"}}{{ end }}
|
||||
{{ define "content" }}
|
||||
<h1
|
||||
class="text-3xl tracking-tight font-black text-black mt-2 mb-4"
|
||||
>{{i18n "AdminAliasesTitle"}}</h1>
|
||||
|
||||
<p id="welcome" class="my-2">{{i18n "AdminAliasesWelcome"}}</p>
|
||||
|
||||
<p
|
||||
id="aliasCount"
|
||||
class="text-lg font-bold my-2"
|
||||
>{{i18npl "ListCount" .Count}}</p>
|
||||
|
||||
<ul id="theList" class="divide-y pb-4">
|
||||
{{range .Entries}}
|
||||
<li class="flex flex-row items-center h-12">
|
||||
<span
|
||||
class="font-mono truncate flex-auto text-gray-600 tracking-wider"
|
||||
>{{.Name}}</span>
|
||||
<span
|
||||
class="font-mono truncate flex-auto text-gray-600 tracking-wider"
|
||||
>{{.Feed.Ref}}</span>
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:aliases:revoke:confirm" "id" .ID}}"
|
||||
class="pl-4 w-20 py-2 text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>{{i18n "AdminAliasesRevoke"}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{$pageNums := .Paginator.PageNums}}
|
||||
{{$view := .View}}
|
||||
{{if gt $pageNums 1}}
|
||||
<div class="flex flex-row justify-center">
|
||||
{{if not .FirstInView}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page=1"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>1</a>
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
{{end}}
|
||||
|
||||
{{range $view.Pages}}
|
||||
{{if le . $pageNums}}
|
||||
{{if eq . $view.Current}}
|
||||
<span
|
||||
class="px-3 py-2 cursor-default text-gray-500 border-2 border-transparent"
|
||||
>{{.}}</span>
|
||||
{{else}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{.}}"
|
||||
class="rounded px-3 py-2 mx-1 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{.}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if not .LastInView}}
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{$view.Last}}"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{$view.Last}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,96 @@
|
|||
{{ define "title" }}{{i18n "AdminMembersTitle"}}{{ end }}
|
||||
{{ define "content" }}
|
||||
<h1
|
||||
class="text-3xl tracking-tight font-black text-black mt-2 mb-4"
|
||||
>{{i18n "AdminMembersTitle"}}</h1>
|
||||
|
||||
<p id="welcome" class="my-2">{{i18n "AdminMembersWelcome"}}</p>
|
||||
|
||||
<form
|
||||
id="add-entry"
|
||||
action="{{urlTo "admin:members:add"}}"
|
||||
method="POST"
|
||||
>
|
||||
{{ .csrfField }}
|
||||
<label class="block mt-6 mb-1 text-sm text-gray-500">Add a new member</label>
|
||||
<div class="flex flex-row items-center justify-start mb-6">
|
||||
<input
|
||||
type="text"
|
||||
name="pub_key"
|
||||
placeholder="{{i18n "PubKeyRefPlaceholder"}}"
|
||||
class="w-8/12 self-stretch shadow rounded border border-transparent h-10 p-1 pl-4 font-mono truncate text-purple-600 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent">
|
||||
<button
|
||||
type="submit"
|
||||
class="ml-4 h-10 shadow rounded px-6 text-purple-600 ring-1 ring-purple-400 bg-white hover:bg-purple-600 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
>{{i18n "AdminMembersAdd"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p
|
||||
id="membersCount"
|
||||
class="text-lg font-bold my-2 ml-4"
|
||||
>{{i18npl "MemberCount" .Count}}</p>
|
||||
|
||||
<ul id="theList" class="pb-4">
|
||||
{{range $index, $member := .Entries}}
|
||||
<li class="odd:bg-gray-100 rounded-lg relative z-0 hover:z-10 hover:bg-white hover:shadow-md">
|
||||
<a href="{{urlTo "admin:member:details" "id" $member.ID}}" class="group flex flex-row items-center h-16 px-4 rounded-lg">
|
||||
<div class="flex flex-col flex-1 justify-center max-w-full">
|
||||
<span class="font-mono truncate text-gray-600 group-hover:text-gray-800">{{$member.PubKey.Ref}}</span>
|
||||
|
||||
<div class="inline-block h-6">
|
||||
{{range $member.Aliases}}
|
||||
<span class="mr-1 text-purple-800 bg-purple-100 rounded-lg px-2">{{.Name}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg class="w-6 h-6 text-gray-400 group-hover:text-purple-600" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{$pageNums := .Paginator.PageNums}}
|
||||
{{$view := .View}}
|
||||
{{if gt $pageNums 1}}
|
||||
<div class="flex flex-row justify-center">
|
||||
{{if not .FirstInView}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page=1"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>1</a>
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
{{end}}
|
||||
|
||||
{{range $view.Pages}}
|
||||
{{if le . $pageNums}}
|
||||
{{if eq . $view.Current}}
|
||||
<span
|
||||
class="px-3 py-2 cursor-default text-gray-500 border-2 border-transparent"
|
||||
>{{.}}</span>
|
||||
{{else}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{.}}"
|
||||
class="rounded px-3 py-2 mx-1 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{.}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if not .LastInView}}
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{$view.Last}}"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{$view.Last}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -0,0 +1,77 @@
|
|||
{{ define "title" }}{{i18n "AdminMemberDetailsTitle"}}{{ end }}
|
||||
{{ define "content" }}
|
||||
<h1
|
||||
class="text-3xl tracking-tight font-black text-black mt-2 mb-4"
|
||||
>{{i18n "AdminMemberDetailsTitle"}}</h1>
|
||||
|
||||
<label class="mt-2 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsSSBID"}}</label>
|
||||
<p class="mb-8 font-mono font-bold tracking-wider truncate text-gray-900">{{.Member.PubKey.Ref}}</p>
|
||||
|
||||
<label class="mt-2 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsRole"}}</label>
|
||||
<details class="mb-8 self-start w-40">
|
||||
<summary class="px-3 py-1 rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
|
||||
{{range $.AllRoles}}
|
||||
{{if eq . $.Member.Role}}
|
||||
{{i18n .String}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</summary>
|
||||
|
||||
<div class="absolute z-10 bg-white w-40 mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
|
||||
{{range $.AllRoles}}
|
||||
{{if ne . $.Member.Role}}
|
||||
<form
|
||||
id="change-role"
|
||||
action="{{urlTo "admin:members:change-role" "id" $.Member.ID}}"
|
||||
method="POST"
|
||||
>
|
||||
{{$.csrfField}}
|
||||
<input type="hidden" name="role" value="{{.}}">
|
||||
<input
|
||||
type="submit"
|
||||
value="{{i18n .String}}"
|
||||
class="pl-10 pr-3 py-2 w-full text-left bg-white text-gray-700 hover:text-gray-900 hover:bg-gray-50 cursor-pointer"
|
||||
/>
|
||||
</form>
|
||||
{{else}}
|
||||
<div
|
||||
class="pr-3 py-2 text-gray-600 flex flex-row items-center cursor-default"
|
||||
>
|
||||
<div class="w-10 flex flex-row items-center justify-center">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{i18n .String}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{{$aliasCount := len .Member.Aliases}} {{if gt $aliasCount 0}}
|
||||
<label class="mt-2 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsAliases"}}</label>
|
||||
<div class="grid grid-cols-2 gap-y-2 gap-x-4 self-start">
|
||||
{{range .Member.Aliases}}
|
||||
<div class="flex flex-row items-center justify-start">
|
||||
<a
|
||||
href="/alias/{{.Name}}"
|
||||
class="underline text-purple-800 bg-purple-100 rounded-lg px-2 py-1"
|
||||
>{{.Name}}</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:aliases:revoke:confirm" "id" .ID}}"
|
||||
class="w-20 py-2 text-sm text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>({{i18n "AdminMemberDetailsAliasRevoke"}})</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<label class="mt-10 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsExclusion"}}</label>
|
||||
<a
|
||||
href="{{urlTo "admin:members:remove:confirm" "id" .Member.ID}}"
|
||||
class="mb-8 self-start shadow rounded px-3 py-1 text-red-600 ring-1 ring-red-400 bg-white hover:bg-red-600 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-red-400 cursor-pointer"
|
||||
>{{i18n "AdminMemberDetailsRemove"}}</a>
|
||||
|
||||
{{end}}
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<button
|
||||
type="submit"
|
||||
class="shadow rounded px-4 h-8 text-gray-100 bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-600 focus:ring-opacity-50"
|
||||
class="shadow rounded px-4 h-8 text-gray-100 bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-600 focus:ring-opacity-50"
|
||||
>{{i18n "GenericConfirm"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
{{ define "title" }}{{i18n "AdminMembersTitle"}}{{ end }}
|
||||
{{ define "content" }}
|
||||
<h1
|
||||
class="text-3xl tracking-tight font-black text-black mt-2 mb-4"
|
||||
>{{i18n "AdminMembersTitle"}}</h1>
|
||||
|
||||
<p id="welcome" class="my-2">{{i18n "AdminMembersWelcome"}}</p>
|
||||
|
||||
<p
|
||||
id="membersCount"
|
||||
class="text-lg font-bold my-2"
|
||||
>{{i18npl "MemberCount" .Count}}</p>
|
||||
|
||||
<ul id="theList" class="divide-y pb-4">
|
||||
<form
|
||||
id="add-entry"
|
||||
action="{{urlTo "admin:members:add"}}"
|
||||
method="POST"
|
||||
>
|
||||
{{ .csrfField }}
|
||||
<div class="flex flex-row items-center h-12">
|
||||
<input
|
||||
type="text"
|
||||
name="pub_key"
|
||||
placeholder="{{i18n "PubKeyRefPlaceholder"}}"
|
||||
class="font-mono truncate w-1/2 ml-3 tracking-wider h-12 text-gray-900 focus:outline-none focus:ring-1 focus:ring-green-500 focus:border-transparent placeholder-gray-300"
|
||||
>
|
||||
<input
|
||||
type="submit"
|
||||
value="{{i18n "AdminMembersAdd"}}"
|
||||
class="pl-4 w-20 py-2 text-center text-green-500 hover:text-green-600 font-bold bg-transparent cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
{{range $index, $member := .Entries}}
|
||||
<li class="flex flex-row items-center h-12">
|
||||
|
||||
<form
|
||||
class="change-member-role"
|
||||
action="{{urlTo "admin:members:change-role" "id" $member.ID}}"
|
||||
method="POST"
|
||||
>
|
||||
{{ $.csrfField }}
|
||||
<select name="role">
|
||||
{{range $.AllRoles}}
|
||||
<option
|
||||
value="{{.}}"
|
||||
{{if eq . $member.Role}}
|
||||
selected
|
||||
{{else}}
|
||||
class="changed"
|
||||
{{end}}
|
||||
>{{i18n .String}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<!-- TODO: I'd like to keep this button hidden until the option is changed.
|
||||
butt i'm not sure that's possible without client side javascript..?
|
||||
https://stackoverflow.com/questions/16015933/how-can-i-show-a-hidden-div-when-a-select-option-is-selected
|
||||
-->
|
||||
<input type="submit" value="{{i18n "GenericSave"}}">
|
||||
</form>
|
||||
|
||||
<span
|
||||
class="font-mono truncate flex-auto text-gray-600 text-xs"
|
||||
>{{$member.PubKey.Ref}}</span>
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:members:remove:confirm" "id" $member.ID}}"
|
||||
class="pl-4 w-20 py-2 text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>{{i18n "AdminMembersRemove"}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{$pageNums := .Paginator.PageNums}}
|
||||
{{$view := .View}}
|
||||
{{if gt $pageNums 1}}
|
||||
<div class="flex flex-row justify-center">
|
||||
{{if not .FirstInView}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page=1"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>1</a>
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
{{end}}
|
||||
|
||||
{{range $view.Pages}}
|
||||
{{if le . $pageNums}}
|
||||
{{if eq . $view.Current}}
|
||||
<span
|
||||
class="px-3 py-2 cursor-default text-gray-500 border-2 border-transparent"
|
||||
>{{.}}</span>
|
||||
{{else}}
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{.}}"
|
||||
class="rounded px-3 py-2 mx-1 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{.}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if not .LastInView}}
|
||||
<span
|
||||
class="px-3 py-2 text-gray-400 border-2 border-transparent"
|
||||
>..</span>
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}?page={{$view.Last}}"
|
||||
class="rounded px-3 py-2 text-pink-600 border-transparent hover:border-pink-400 border-2"
|
||||
>{{$view.Last}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -19,15 +19,6 @@
|
|||
</svg>{{i18n "NavAdminDashboard"}}
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:aliases:overview"}}"
|
||||
class="{{if current_page_is "admin:aliases:overview"}}bg-gray-300 {{else}}hover:bg-gray-200 {{end}}pr-1 pl-2 py-3 sm:py-1 rounded-md flex flex-row items-center font-semibold text-sm text-gray-700 hover:text-gray-800 truncate"
|
||||
>
|
||||
<svg class="text-green-600 w-4 h-4 mr-1" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M23,12L20.56,9.22L20.9,5.54L17.29,4.72L15.4,1.54L12,3L8.6,1.54L6.71,4.72L3.1,5.53L3.44,9.21L1,12L3.44,14.78L3.1,18.47L6.71,19.29L8.6,22.47L12,21L15.4,22.46L17.29,19.28L20.9,18.46L20.56,14.78L23,12M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9L10,17Z" />
|
||||
</svg>{{i18n "AdminAliasesTitle"}}
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:members:overview"}}"
|
||||
class="{{if current_page_is "admin:members:overview"}}bg-gray-300 {{else}}hover:bg-gray-200 {{end}}pr-1 pl-2 py-3 sm:py-1 rounded-md flex flex-row items-center font-semibold text-sm text-gray-700 hover:text-gray-800 truncate"
|
||||
|
@ -37,7 +28,6 @@
|
|||
</svg>{{i18n "AdminMembersTitle"}}
|
||||
</a>
|
||||
|
||||
|
||||
<a
|
||||
href="{{urlTo "admin:invites:overview"}}"
|
||||
class="{{if current_page_is "admin:invites:overview"}}bg-gray-300 {{else}}hover:bg-gray-200 {{end}}pr-1 pl-2 py-3 sm:py-1 rounded-md flex flex-row items-center font-semibold text-sm text-gray-700 hover:text-gray-800 truncate"
|
||||
|
|
Loading…
Reference in New Issue