Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite3e82d7

Browse files
committed
Add filter search on Users page
1 parent3312c81 commite3e82d7

File tree

14 files changed

+108
-53
lines changed

14 files changed

+108
-53
lines changed

‎site/src/api/api.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
importaxiosfrom"axios"
2-
import{getApiKey,getWorkspacesURL,login,logout}from"./api"
2+
import{getApiKey,getURLWithSearchParams,login,logout}from"./api"
33
import*asTypesGenfrom"./typesGenerated"
44

55
describe("api.ts",()=>{
@@ -114,16 +114,19 @@ describe("api.ts", () => {
114114
})
115115
})
116116

117-
describe("getWorkspacesURL",()=>{
118-
it.each<[TypesGen.WorkspaceFilter|undefined,string]>([
119-
[undefined,"/api/v2/workspaces"],
117+
describe("getURLWithSearchParams",()=>{
118+
it.each<[string,TypesGen.WorkspaceFilter|TypesGen.UsersRequest|undefined,string]>([
119+
["/api/v2/workspaces",undefined,"/api/v2/workspaces"],
120120

121-
[{q:""},"/api/v2/workspaces"],
122-
[{q:"owner:1"},"/api/v2/workspaces?q=owner%3A1"],
121+
["/api/v2/workspaces",{q:""},"/api/v2/workspaces"],
122+
["/api/v2/workspaces",{q:"owner:1"},"/api/v2/workspaces?q=owner%3A1"],
123123

124-
[{q:"owner:me"},"/api/v2/workspaces?q=owner%3Ame"],
125-
])(`getWorkspacesURL(%p) returns %p`,(filter,expected)=>{
126-
expect(getWorkspacesURL(filter)).toBe(expected)
124+
["/api/v2/workspaces",{q:"owner:me"},"/api/v2/workspaces?q=owner%3Ame"],
125+
126+
["/api/v2/users",{q:"status:active"},"/api/v2/users?q=status%3Aactive"],
127+
["/api/v2/users",{q:""},"/api/v2/users"],
128+
])(`getURLWithSearchParams(%p) returns %p`,(basePath,filter,expected)=>{
129+
expect(getURLWithSearchParams(basePath,filter)).toBe(expected)
127130
})
128131
})
129132
})

‎site/src/api/api.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ export const getApiKey = async (): Promise<TypesGen.GenerateAPIKeyResponse> => {
6262
returnresponse.data
6363
}
6464

65-
exportconstgetUsers=async():Promise<TypesGen.User[]>=>{
66-
constresponse=awaitaxios.get<TypesGen.User[]>("/api/v2/users?q=status:active,suspended")
65+
exportconstgetUsers=async(filter?:TypesGen.UsersRequest):Promise<TypesGen.User[]>=>{
66+
consturl=getURLWithSearchParams("/api/v2/users",filter)
67+
constresponse=awaitaxios.get<TypesGen.User[]>(url)
6768
returnresponse.data
6869
}
6970

@@ -115,8 +116,10 @@ export const getWorkspace = async (
115116
returnresponse.data
116117
}
117118

118-
exportconstgetWorkspacesURL=(filter?:TypesGen.WorkspaceFilter):string=>{
119-
constbasePath="/api/v2/workspaces"
119+
exportconstgetURLWithSearchParams=(
120+
basePath:string,
121+
filter?:TypesGen.WorkspaceFilter|TypesGen.UsersRequest,
122+
):string=>{
120123
constsearchParams=newURLSearchParams()
121124

122125
if(filter?.q&&filter.q!==""){
@@ -129,7 +132,7 @@ export const getWorkspacesURL = (filter?: TypesGen.WorkspaceFilter): string => {
129132
}
130133

131134
exportconstgetWorkspaces=async(filter?:TypesGen.WorkspaceFilter):Promise<TypesGen.Workspace[]>=>{
132-
consturl=getWorkspacesURL(filter)
135+
consturl=getURLWithSearchParams("/api/v2/workspaces",filter)
133136
constresponse=awaitaxios.get<TypesGen.Workspace[]>(url)
134137
returnresponse.data
135138
}

‎site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import{ComponentMeta,Story}from"@storybook/react"
2-
import{workspaceFilterQuery}from"../../util/workspace"
2+
import{workspaceFilterQuery}from"../../util/filters"
33
import{SearchBarWithFilter,SearchBarWithFilterProps}from"./SearchBarWithFilter"
44

55
exportdefault{

‎site/src/pages/UsersPage/UsersPage.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { useActor, useSelector } from "@xstate/react"
22
importReact,{useContext,useEffect}from"react"
33
import{Helmet}from"react-helmet"
44
import{useNavigate}from"react-router"
5+
import{useSearchParams}from"react-router-dom"
56
import{ConfirmDialog}from"../../components/ConfirmDialog/ConfirmDialog"
67
import{ResetPasswordDialog}from"../../components/ResetPasswordDialog/ResetPasswordDialog"
8+
import{userFilterQuery}from"../../util/filters"
79
import{pageTitle}from"../../util/page"
810
import{selectPermissions}from"../../xServices/auth/authSelectors"
911
import{XServiceContext}from"../../xServices/StateContext"
@@ -25,6 +27,7 @@ export const UsersPage: React.FC = () => {
2527
const{ users, getUsersError, userIdToSuspend, userIdToActivate, userIdToResetPassword, newUserPassword}=
2628
usersState.context
2729
constnavigate=useNavigate()
30+
const[searchParams,setSearchParams]=useSearchParams()
2831
constuserToBeSuspended=users?.find((u)=>u.id===userIdToSuspend)
2932
constuserToBeActivated=users?.find((u)=>u.id===userIdToActivate)
3033
constuserToResetPassword=users?.find((u)=>u.id===userIdToResetPassword)
@@ -40,8 +43,13 @@ export const UsersPage: React.FC = () => {
4043

4144
// Fetch users on component mount
4245
useEffect(()=>{
43-
usersSend("GET_USERS")
44-
},[usersSend])
46+
constfilter=searchParams.get("filter")
47+
constquery=filter!==null ?filter :userFilterQuery.active
48+
usersSend({
49+
type:"GET_USERS",
50+
query,
51+
})
52+
},[searchParams,usersSend])
4553

4654
// Fetch roles on component mount
4755
useEffect(()=>{
@@ -85,6 +93,11 @@ export const UsersPage: React.FC = () => {
8593
isLoading={isLoading}
8694
canEditUsers={canEditUsers}
8795
canCreateUser={canCreateUser}
96+
filter={usersState.context.filter}
97+
onFilter={(query)=>{
98+
searchParams.set("filter",query)
99+
setSearchParams(searchParams)
100+
}}
88101
/>
89102

90103
<ConfirmDialog

‎site/src/pages/UsersPage/UsersPageView.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@ import * as TypesGen from "../../api/typesGenerated"
55
import{ErrorSummary}from"../../components/ErrorSummary/ErrorSummary"
66
import{Margins}from"../../components/Margins/Margins"
77
import{PageHeader,PageHeaderTitle}from"../../components/PageHeader/PageHeader"
8+
import{SearchBarWithFilter}from"../../components/SearchBarWithFilter/SearchBarWithFilter"
89
import{UsersTable}from"../../components/UsersTable/UsersTable"
10+
import{userFilterQuery}from"../../util/filters"
911

1012
exportconstLanguage={
1113
pageTitle:"Users",
1214
createButton:"New user",
15+
activeUsersFilterName:"Active users",
16+
allUsersFilterName:"All users",
1317
}
1418

1519
exportinterfaceUsersPageViewProps{
1620
users?:TypesGen.User[]
1721
roles?:TypesGen.Role[]
22+
filter?:string
1823
error?:unknown
1924
isUpdatingUserRoles?:boolean
2025
canEditUsers?:boolean
@@ -25,6 +30,7 @@ export interface UsersPageViewProps {
2530
onActivateUser:(user:TypesGen.User)=>void
2631
onResetUserPassword:(user:TypesGen.User)=>void
2732
onUpdateUserRoles:(user:TypesGen.User,roles:TypesGen.Role["name"][])=>void
33+
onFilter:(query:string)=>void
2834
}
2935

3036
exportconstUsersPageView:FC<UsersPageViewProps>=({
@@ -40,7 +46,14 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
4046
canEditUsers,
4147
canCreateUser,
4248
isLoading,
49+
filter,
50+
onFilter,
4351
})=>{
52+
constpresetFilters=[
53+
{query:userFilterQuery.active,name:Language.activeUsersFilterName},
54+
{query:userFilterQuery.all,name:Language.allUsersFilterName},
55+
]
56+
4457
return(
4558
<Margins>
4659
<PageHeader
@@ -55,6 +68,8 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
5568
<PageHeaderTitle>Users</PageHeaderTitle>
5669
</PageHeader>
5770

71+
<SearchBarWithFilterfilter={filter}onFilter={onFilter}presetFilters={presetFilters}/>
72+
5873
{error ?(
5974
<ErrorSummaryerror={error}/>
6075
) :(

‎site/src/pages/WorkspacesPage/WorkspacesPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { useMachine } from "@xstate/react"
22
import{FC,useEffect}from"react"
33
import{Helmet}from"react-helmet"
44
import{useSearchParams}from"react-router-dom"
5+
import{workspaceFilterQuery}from"../../util/filters"
56
import{pageTitle}from"../../util/page"
6-
import{workspaceFilterQuery}from"../../util/workspace"
77
import{workspacesMachine}from"../../xServices/workspaces/workspacesXService"
88
import{WorkspacesPageView}from"./WorkspacesPageView"
99

‎site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ComponentMeta, Story } from "@storybook/react"
22
import{spawn}from"xstate"
33
import{ProvisionerJobStatus,WorkspaceTransition}from"../../api/typesGenerated"
44
import{MockWorkspace}from"../../testHelpers/entities"
5-
import{workspaceFilterQuery}from"../../util/workspace"
5+
import{workspaceFilterQuery}from"../../util/filters"
66
import{workspaceItemMachine,WorkspaceItemMachineRef}from"../../xServices/workspaces/workspacesXService"
77
import{WorkspacesPageView,WorkspacesPageViewProps}from"./WorkspacesPageView"
88

‎site/src/pages/WorkspacesPage/WorkspacesPageView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import {
3131
HelpTooltipText,
3232
HelpTooltipTitle,
3333
}from"../../components/Tooltips/HelpTooltip/HelpTooltip"
34-
import{getDisplayStatus,workspaceFilterQuery}from"../../util/workspace"
34+
import{workspaceFilterQuery}from"../../util/filters"
35+
import{getDisplayStatus}from"../../util/workspace"
3536
import{WorkspaceItemMachineRef}from"../../xServices/workspaces/workspacesXService"
3637

3738
dayjs.extend(relativeTime)

‎site/src/util/filters.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import*asTypesGenfrom"../api/typesGenerated"
2+
import{queryToFilter}from"./filters"
3+
4+
describe("queryToFilter",()=>{
5+
it.each<[string|undefined,TypesGen.WorkspaceFilter|TypesGen.UsersRequest]>([
6+
[undefined,{}],
7+
["",{q:""}],
8+
["asdkfvjn",{q:"asdkfvjn"}],
9+
["owner:me",{q:"owner:me"}],
10+
["owner:me owner:me2",{q:"owner:me owner:me2"}],
11+
["me/dev",{q:"me/dev"}],
12+
["me/",{q:"me/"}],
13+
[" key:val owner:me ",{q:"key:val owner:me"}],
14+
])(`query=%p, filter=%p`,(query,filter)=>{
15+
expect(queryToFilter(query)).toEqual(filter)
16+
})
17+
})

‎site/src/util/filters.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import*asTypesGenfrom"../api/typesGenerated"
2+
3+
exportconstqueryToFilter=(query?:string):TypesGen.WorkspaceFilter|TypesGen.UsersRequest=>{
4+
constpreparedQuery=query?.trim().replace(/+/g," ")
5+
return{
6+
q:preparedQuery,
7+
}
8+
}
9+
10+
exportconstworkspaceFilterQuery={
11+
me:"owner:me",
12+
all:"",
13+
}
14+
15+
exportconstuserFilterQuery={
16+
active:"status:active",
17+
all:"",
18+
}

‎site/src/util/workspace.test.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
importdayjsfrom"dayjs"
22
import*asTypesGenfrom"../api/typesGenerated"
33
import*asMocksfrom"../testHelpers/entities"
4-
import{defaultWorkspaceExtension,isWorkspaceDeleted,isWorkspaceOn,workspaceQueryToFilter}from"./workspace"
4+
import{defaultWorkspaceExtension,isWorkspaceDeleted,isWorkspaceOn}from"./workspace"
55

66
describe("util > workspace",()=>{
77
describe("isWorkspaceOn",()=>{
@@ -101,18 +101,4 @@ describe("util > workspace", () => {
101101
expect(defaultWorkspaceExtension(dayjs(startTime))).toEqual(request)
102102
})
103103
})
104-
describe("workspaceQueryToFilter",()=>{
105-
it.each<[string|undefined,TypesGen.WorkspaceFilter]>([
106-
[undefined,{}],
107-
["",{q:""}],
108-
["asdkfvjn",{q:"asdkfvjn"}],
109-
["owner:me",{q:"owner:me"}],
110-
["owner:me owner:me2",{q:"owner:me owner:me2"}],
111-
["me/dev",{q:"me/dev"}],
112-
["me/",{q:"me/"}],
113-
[" key:val owner:me ",{q:"key:val owner:me"}],
114-
])(`query=%p, filter=%p`,(query,filter)=>{
115-
expect(workspaceQueryToFilter(query)).toEqual(filter)
116-
})
117-
})
118104
})

‎site/src/util/workspace.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,3 @@ export const defaultWorkspaceExtension = (__startDate?: dayjs.Dayjs): TypesGen.P
292292
deadline:fourHoursFromNow.format(),
293293
}
294294
}
295-
296-
exportconstworkspaceQueryToFilter=(query?:string):TypesGen.WorkspaceFilter=>{
297-
constpreparedQuery=query?.trim().replace(/+/g," ")
298-
return{
299-
q:preparedQuery,
300-
}
301-
}
302-
303-
exportconstworkspaceFilterQuery={
304-
me:"owner:me",
305-
all:"",
306-
}

‎site/src/xServices/users/usersXService.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
}from"../../api/errors"
1111
import*asTypesGenfrom"../../api/typesGenerated"
1212
import{displayError,displaySuccess}from"../../components/GlobalSnackbar/utils"
13+
import{queryToFilter}from"../../util/filters"
1314
import{generateRandomString}from"../../util/random"
1415

1516
exportconstLanguage={
@@ -28,6 +29,7 @@ export const Language = {
2829
exportinterfaceUsersContext{
2930
// Get users
3031
users?:TypesGen.User[]
32+
filter?:string
3133
getUsersError?:Error|unknown
3234
createUserErrorMessage?:string
3335
createUserFormErrors?:FieldErrors
@@ -47,7 +49,7 @@ export interface UsersContext {
4749
}
4850

4951
exporttypeUsersEvent=
50-
|{type:"GET_USERS"}
52+
|{type:"GET_USERS";query:string}
5153
|{type:"CREATE";user:TypesGen.CreateUserRequest}
5254
|{type:"CANCEL_CREATE_USER"}
5355
// Suspend events
@@ -97,7 +99,10 @@ export const usersMachine = createMachine(
9799
states:{
98100
idle:{
99101
on:{
100-
GET_USERS:"gettingUsers",
102+
GET_USERS:{
103+
actions:"assignFilter",
104+
target:"gettingUsers",
105+
},
101106
CREATE:"creatingUser",
102107
CANCEL_CREATE_USER:{actions:["clearCreateUserError"]},
103108
SUSPEND_USER:{
@@ -242,7 +247,10 @@ export const usersMachine = createMachine(
242247
},
243248
error:{
244249
on:{
245-
GET_USERS:"gettingUsers",
250+
GET_USERS:{
251+
actions:"assignFilter",
252+
target:"gettingUsers",
253+
},
246254
},
247255
},
248256
},
@@ -252,7 +260,7 @@ export const usersMachine = createMachine(
252260
// Passing API.getUsers directly does not invoke the function properly
253261
// when it is mocked. This happen in the UsersPage tests inside of the
254262
// "shows a success message and refresh the page" test case.
255-
getUsers:()=>API.getUsers(),
263+
getUsers:(context)=>API.getUsers(queryToFilter(context.filter)),
256264
createUser:(_,event)=>API.createUser(event.user),
257265
suspendUser:(context)=>{
258266
if(!context.userIdToSuspend){
@@ -297,6 +305,9 @@ export const usersMachine = createMachine(
297305
assignUsers:assign({
298306
users:(_,event)=>event.data,
299307
}),
308+
assignFilter:assign({
309+
filter:(_,event)=>event.query,
310+
}),
300311
assignGetUsersError:assign({
301312
getUsersError:(_,event)=>event.data,
302313
}),

‎site/src/xServices/workspaces/workspacesXService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as API from "../../api/api"
33
import{getErrorMessage}from"../../api/errors"
44
import*asTypesGenfrom"../../api/typesGenerated"
55
import{displayError,displayMsg,displaySuccess}from"../../components/GlobalSnackbar/utils"
6-
import{workspaceQueryToFilter}from"../../util/workspace"
6+
import{queryToFilter}from"../../util/filters"
77

88
/**
99
* Workspace item machine
@@ -316,7 +316,7 @@ export const workspacesMachine = createMachine(
316316
}),
317317
},
318318
services:{
319-
getWorkspaces:(context)=>API.getWorkspaces(workspaceQueryToFilter(context.filter)),
319+
getWorkspaces:(context)=>API.getWorkspaces(queryToFilter(context.filter)),
320320
},
321321
},
322322
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp