Skip to content

Commit

Permalink
Merge pull request #1172 from mattwoberts/member-email-search
Browse files Browse the repository at this point in the history
Member email search
  • Loading branch information
mattwoberts authored Jul 3, 2024
2 parents e55a144 + c815935 commit 3250ad7
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 7 deletions.
11 changes: 10 additions & 1 deletion app/handlers/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/getfider/fider/app/actions"
"github.com/getfider/fider/app/models/cmd"
"github.com/getfider/fider/app/models/entity"
"github.com/getfider/fider/app/models/query"
"github.com/getfider/fider/app/pkg/bus"
"github.com/getfider/fider/app/pkg/web"
Expand Down Expand Up @@ -126,11 +127,19 @@ func ManageMembers() web.HandlerFunc {
return c.Failure(err)
}

// Create an array of UserWithEmail structs from the allUsers.Result
allUsersWithEmail := make([]entity.UserWithEmail, len(allUsers.Result))
for i, user := range allUsers.Result {
allUsersWithEmail[i] = entity.UserWithEmail{
User: user,
}
}

return c.Page(http.StatusOK, web.Props{
Page: "Administration/pages/ManageMembers.page",
Title: "Manage Members · Site Settings",
Data: web.Map{
"users": allUsers.Result,
"users": allUsersWithEmail,
},
})
}
Expand Down
28 changes: 24 additions & 4 deletions app/models/entity/user.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package entity

import "github.com/getfider/fider/app/models/enum"
import (
"encoding/json"

//User represents an user inside our application
"github.com/getfider/fider/app/models/enum"
)

// User represents an user inside our application
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Expand All @@ -16,7 +20,7 @@ type User struct {
Status enum.UserStatus `json:"status"`
}

//HasProvider returns true if current user has registered with given provider
// HasProvider returns true if current user has registered with given provider
func (u *User) HasProvider(provider string) bool {
for _, p := range u.Providers {
if p.Name == provider {
Expand All @@ -36,8 +40,24 @@ func (u *User) IsAdministrator() bool {
return u.Role == enum.RoleAdministrator
}

//UserProvider represents the relationship between an User and an Authentication provide
// UserProvider represents the relationship between an User and an Authentication provide
type UserProvider struct {
Name string
UID string
}

// UserWithEmail is a wrapper around User that includes the email field when marshaling to JSON
type UserWithEmail struct {
*User
}

func (umc UserWithEmail) MarshalJSON() ([]byte, error) {
type Alias User // Prevent recursion
return json.Marshal(&struct {
*Alias
Email string `json:"email"`
}{
Alias: (*Alias)(umc.User),
Email: umc.User.Email,
})
}
55 changes: 55 additions & 0 deletions app/models/entity/user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package entity_test

import (
"encoding/json"
"testing"

"github.com/getfider/fider/app/models/entity"
. "github.com/getfider/fider/app/pkg/assert"
)

func TestUserWithEmail_MarshalJSON(t *testing.T) {

RegisterT(t)
user := entity.UserWithEmail{
User: &entity.User{
ID: 1,
Name: "John Doe",
Email: "[email protected]",
Role: 1,
Status: 1,
},
}

expectedJSON := `{"id":1,"name":"John Doe","role":"visitor","status":"active","email":"[email protected]"}`

jsonData, err := json.Marshal(user)
if err != nil {
t.Errorf("Failed to marshal user to JSON: %v", err)
}

Expect(string(jsonData)).Equals(expectedJSON)

}

func TestUser_MarshalJSON(t *testing.T) {

RegisterT(t)
user := entity.User{
ID: 1,
Name: "John Doe",
Email: "[email protected]",
Role: 1,
Status: 1,
}

expectedJSON := `{"id":1,"name":"John Doe","role":"visitor","status":"active"}`

jsonData, err := json.Marshal(user)
if err != nil {
t.Errorf("Failed to marshal user to JSON: %v", err)
}

Expect(string(jsonData)).Equals(expectedJSON)

}
7 changes: 7 additions & 0 deletions public/components/common/UserName.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
display: inline-flex;
align-items: center;

&--email {
margin-left: 10px;
color: get("colors.gray.600");
font-size: get("font.size.xs");
font-weight: 400;
}

&--staff {
color: get("colors.primary.base");
border-color: get("colors.primary.base");
Expand Down
3 changes: 3 additions & 0 deletions public/components/common/UserName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface UserNameProps {
id: number
name: string
role?: UserRole
email?: string
}
}

Expand All @@ -22,6 +23,8 @@ export const UserName = (props: UserNameProps) => {
return (
<div className={className}>
<span>{props.user.name || "Anonymous"}</span>
<>{props.user.email && <span className="c-username--email">({props.user.email})</span>}</>

{isStaff && (
<div data-tooltip={isStaff ? "Staff" : undefined}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
Expand Down
1 change: 1 addition & 0 deletions public/models/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum TenantStatus {
export interface User {
id: number
name: string
email?: string
role: UserRole
status: UserStatus
avatarURL: string
Expand Down
9 changes: 7 additions & 2 deletions public/pages/Administration/pages/ManageMembers.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default class ManageMembersPage extends AdminBasePage<ManageMembersPagePr
super(props)

const users = this.props.users.sort(this.sortByStaff)

this.state = {
query: "",
users,
Expand All @@ -86,8 +87,12 @@ export default class ManageMembersPage extends AdminBasePage<ManageMembersPagePr
this.handleSearchFilterChanged("")
}

private memberFilter = (query: string, user: User): boolean => {
return user.name.toLowerCase().indexOf(query.toLowerCase()) >= 0 || (user.email && user.email.toLowerCase().indexOf(query.toLowerCase()) >= 0) || false
}

private handleSearchFilterChanged = (query: string) => {
const users = this.props.users.filter((x) => x.name.toLowerCase().indexOf(query.toLowerCase()) >= 0).sort(this.sortByStaff)
const users = this.props.users.filter((x) => this.memberFilter(query, x)).sort(this.sortByStaff)
this.setState({ query, users, visibleUsers: users.slice(0, 10) })
}

Expand Down Expand Up @@ -145,7 +150,7 @@ export default class ManageMembersPage extends AdminBasePage<ManageMembersPagePr
field="query"
icon={this.state.query ? IconX : IconSearch}
onIconClick={this.state.query ? this.clearSearch : undefined}
placeholder="Search for users by name..."
placeholder="Search for users by name / email ..."
value={this.state.query}
onChange={this.handleSearchFilterChanged}
/>
Expand Down

0 comments on commit 3250ad7

Please sign in to comment.