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

Commit95dccf3

Browse files
author
Rafael Rodriguez
authored
feat: add user filter to templates page to filter by template author (#19561)
## SummaryIn this pull request we're adding a user selector dropdown to thetemplates page that allows an admin to select a user. The selected userwill be used in the `author:<username>` filter to filter the templateslist by a template author.Closes:#19547### ChangesAdmin View - Can view all users<img width="1622" height="489" alt="Screenshot 2025-08-26 at 5 24 07 PM"src="https://github.com/user-attachments/assets/f2ace51e-5834-4bed-bd4f-14c6800816f0"/>Admin View - Using the user filterhttps://github.com/user-attachments/assets/b4570cca-6dff-45c1-89ab-844f126bdc0fUser view - Cannot view all users<img width="1617" height="455" alt="Screenshot 2025-08-26 at 5 25 38 PM"src="https://github.com/user-attachments/assets/f8680acb-d463-4a22-826e-053f0e7dbe21"/>### Testing- Added storybook test for viewing the templates page with a userdropdown
1 parent75b38f1 commit95dccf3

File tree

8 files changed

+149
-41
lines changed

8 files changed

+149
-41
lines changed

‎site/src/components/Filter/UserFilter.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { useAuthenticated } from "hooks";
99
importtype{FC}from"react";
1010
import{typeUseFilterMenuOptions,useFilterMenu}from"./menu";
1111

12+
exportconstDEFAULT_USER_FILTER_WIDTH=175;
13+
1214
exportconstuseUserFilterMenu=({
1315
value,
1416
onChange,

‎site/src/pages/AuditPage/AuditFilter.tsx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
SelectFilter,
99
typeSelectFilterOption,
1010
}from"components/Filter/SelectFilter";
11-
import{typeUserFilterMenu,UserMenu}from"components/Filter/UserFilter";
11+
import{
12+
DEFAULT_USER_FILTER_WIDTH,
13+
typeUserFilterMenu,
14+
UserMenu,
15+
}from"components/Filter/UserFilter";
1216
importcapitalizefrom"lodash/capitalize";
1317
import{
1418
typeOrganizationsFilterMenu,
@@ -47,8 +51,7 @@ interface AuditFilterProps {
4751
}
4852

4953
exportconstAuditFilter:FC<AuditFilterProps>=({ filter, error, menus})=>{
50-
constwidth=menus.organization ?175 :undefined;
51-
54+
constwidth=menus.organization ?DEFAULT_USER_FILTER_WIDTH :undefined;
5255
return(
5356
<Filter
5457
learnMoreLink={docs("/admin/security/audit-logs#filtering-logs")}

‎site/src/pages/ConnectionLogPage/ConnectionLogFilter.tsx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
SelectFilter,
99
typeSelectFilterOption,
1010
}from"components/Filter/SelectFilter";
11-
import{typeUserFilterMenu,UserMenu}from"components/Filter/UserFilter";
11+
import{
12+
DEFAULT_USER_FILTER_WIDTH,
13+
typeUserFilterMenu,
14+
UserMenu,
15+
}from"components/Filter/UserFilter";
1216
importcapitalizefrom"lodash/capitalize";
1317
import{
1418
typeOrganizationsFilterMenu,
@@ -42,8 +46,7 @@ export const ConnectionLogFilter: FC<ConnectionLogFilterProps> = ({
4246
error,
4347
menus,
4448
})=>{
45-
constwidth=menus.organization ?175 :undefined;
46-
49+
constwidth=menus.organization ?DEFAULT_USER_FILTER_WIDTH :undefined;
4750
return(
4851
<Filter
4952
learnMoreLink={docs(

‎site/src/pages/TemplatesPage/TemplatesFilter.tsx‎

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
11
import{API}from"api/api";
22
importtype{Organization}from"api/typesGenerated";
33
import{Avatar}from"components/Avatar/Avatar";
4-
import{Filter,MenuSkeleton,typeuseFilter}from"components/Filter/Filter";
4+
import{
5+
Filter,
6+
MenuSkeleton,
7+
typeUseFilterResult,
8+
}from"components/Filter/Filter";
59
import{useFilterMenu}from"components/Filter/menu";
610
import{
711
SelectFilter,
812
typeSelectFilterOption,
913
}from"components/Filter/SelectFilter";
14+
import{useDashboard}from"modules/dashboard/useDashboard";
1015
importtype{FC}from"react";
16+
import{
17+
DEFAULT_USER_FILTER_WIDTH,
18+
typeUserFilterMenu,
19+
UserMenu,
20+
}from"../../components/Filter/UserFilter";
1121

1222
interfaceTemplatesFilterProps{
13-
filter:ReturnType<typeofuseFilter>;
23+
filter:UseFilterResult;
1424
error?:unknown;
25+
26+
userMenu?:UserFilterMenu;
1527
}
1628

1729
exportconstTemplatesFilter:FC<TemplatesFilterProps>=({
1830
filter,
1931
error,
32+
userMenu,
2033
})=>{
34+
const{ showOrganizations}=useDashboard();
35+
constwidth=showOrganizations ?DEFAULT_USER_FILTER_WIDTH :undefined;
2136
constorganizationMenu=useFilterMenu({
2237
onChange:(option)=>
2338
filter.update({ ...filter.values,organization:option?.value}),
@@ -50,15 +65,23 @@ export const TemplatesFilter: FC<TemplatesFilterProps> = ({
5065
filter={filter}
5166
error={error}
5267
options={
53-
<SelectFilter
54-
placeholder="All organizations"
55-
label="Select an organization"
56-
options={organizationMenu.searchOptions}
57-
selectedOption={organizationMenu.selectedOption??undefined}
58-
onSelect={organizationMenu.selectOption}
59-
/>
68+
<>
69+
{userMenu&&<UserMenuwidth={width}menu={userMenu}/>}
70+
<SelectFilter
71+
placeholder="All organizations"
72+
label="Select an organization"
73+
options={organizationMenu.searchOptions}
74+
selectedOption={organizationMenu.selectedOption??undefined}
75+
onSelect={organizationMenu.selectOption}
76+
/>
77+
</>
78+
}
79+
optionsSkeleton={
80+
<>
81+
{userMenu&&<MenuSkeleton/>}
82+
<MenuSkeleton/>
83+
</>
6084
}
61-
optionsSkeleton={<MenuSkeleton/>}
6285
/>
6386
);
6487
};

‎site/src/pages/TemplatesPage/TemplatesPage.tsx‎

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import{workspacePermissionsByOrganization}from"api/queries/organizations";
22
import{templateExamples,templates}from"api/queries/templates";
3-
import{useFilter}from"components/Filter/Filter";
3+
import{typeUseFilterResult,useFilter}from"components/Filter/Filter";
4+
import{useUserFilterMenu}from"components/Filter/UserFilter";
45
import{useAuthenticated}from"hooks";
56
import{useDashboard}from"modules/dashboard/useDashboard";
67
importtype{FC}from"react";
@@ -15,14 +16,12 @@ const TemplatesPage: FC = () => {
1516
const{ showOrganizations}=useDashboard();
1617

1718
const[searchParams,setSearchParams]=useSearchParams();
18-
constfilter=useFilter({
19-
fallbackFilter:"deprecated:false",
19+
constfilterState=useTemplatesFilter({
2020
searchParams,
2121
onSearchParamsChange:setSearchParams,
22-
onUpdate:()=>{},// reset pagination
2322
});
2423

25-
consttemplatesQuery=useQuery(templates({q:filter.query}));
24+
consttemplatesQuery=useQuery(templates({q:filterState.filter.query}));
2625
constexamplesQuery=useQuery({
2726
...templateExamples(),
2827
enabled:permissions.createTemplates,
@@ -47,7 +46,7 @@ const TemplatesPage: FC = () => {
4746
</Helmet>
4847
<TemplatesPageView
4948
error={error}
50-
filter={filter}
49+
filterState={filterState}
5150
showOrganizations={showOrganizations}
5251
canCreateTemplates={permissions.createTemplates}
5352
examples={examplesQuery.data}
@@ -59,3 +58,42 @@ const TemplatesPage: FC = () => {
5958
};
6059

6160
exportdefaultTemplatesPage;
61+
62+
exporttypeTemplateFilterState={
63+
filter:UseFilterResult;
64+
menus:{
65+
user?:ReturnType<typeofuseUserFilterMenu>;
66+
};
67+
};
68+
69+
typeUseTemplatesFilterOptions={
70+
searchParams:URLSearchParams;
71+
onSearchParamsChange:(params:URLSearchParams)=>void;
72+
};
73+
74+
constuseTemplatesFilter=({
75+
searchParams,
76+
onSearchParamsChange,
77+
}:UseTemplatesFilterOptions):TemplateFilterState=>{
78+
constfilter=useFilter({
79+
fallbackFilter:"deprecated:false",
80+
searchParams,
81+
onSearchParamsChange,
82+
});
83+
84+
const{ permissions}=useAuthenticated();
85+
constcanFilterByUser=permissions.viewAllUsers;
86+
constuserMenu=useUserFilterMenu({
87+
value:filter.values.author,
88+
onChange:(option)=>
89+
filter.update({ ...filter.values,author:option?.value}),
90+
enabled:canFilterByUser,
91+
});
92+
93+
return{
94+
filter,
95+
menus:{
96+
user:canFilterByUser ?userMenu :undefined,
97+
},
98+
};
99+
};

‎site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx‎

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,35 @@ import {
33
MockTemplate,
44
MockTemplateExample,
55
MockTemplateExample2,
6+
MockUserOwner,
67
mockApiError,
78
}from"testHelpers/entities";
89
import{withDashboardProvider}from"testHelpers/storybook";
910
importtype{Meta,StoryObj}from"@storybook/react-vite";
10-
import{getDefaultFilterProps}from"components/Filter/storyHelpers";
11+
import{
12+
getDefaultFilterProps,
13+
MockMenu,
14+
}from"components/Filter/storyHelpers";
15+
importtype{TemplateFilterState}from"./TemplatesPage";
1116
import{TemplatesPageView}from"./TemplatesPageView";
1217

18+
constdefaultFilterProps=getDefaultFilterProps<TemplateFilterState>({
19+
query:"deprecated:false",
20+
menus:{
21+
organizations:MockMenu,
22+
},
23+
values:{
24+
author:MockUserOwner.username,
25+
},
26+
});
27+
1328
constmeta:Meta<typeofTemplatesPageView>={
1429
title:"pages/TemplatesPage",
1530
decorators:[withDashboardProvider],
1631
parameters:{chromatic:chromaticWithTablet},
1732
component:TemplatesPageView,
1833
args:{
19-
...getDefaultFilterProps({
20-
query:"deprecated:false",
21-
menus:{},
22-
values:{},
23-
}),
34+
filterState:defaultFilterProps,
2435
},
2536
};
2637

@@ -104,12 +115,32 @@ export const WithFilteredAllTemplates: Story = {
104115
args:{
105116
...WithTemplates.args,
106117
templates:[],
107-
...getDefaultFilterProps({
108-
query:"deprecated:false searchnotfound",
109-
menus:{},
110-
values:{},
111-
used:true,
112-
}),
118+
filterState:{
119+
filter:{
120+
...defaultFilterProps.filter,
121+
query:"deprecated:false searchnotfound",
122+
values:{},
123+
used:true,
124+
},
125+
menus:defaultFilterProps.menus,
126+
},
127+
},
128+
};
129+
130+
exportconstWithUserDropdown:Story={
131+
args:{
132+
...WithTemplates.args,
133+
filterState:{
134+
...defaultFilterProps,
135+
menus:{
136+
user:MockMenu,
137+
},
138+
filter:{
139+
...defaultFilterProps.filter,
140+
query:"author:me",
141+
values:{author:"me"},
142+
},
143+
},
113144
},
114145
};
115146

‎site/src/pages/TemplatesPage/TemplatesPageView.tsx‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { AvatarData } from "components/Avatar/AvatarData";
99
import{AvatarDataSkeleton}from"components/Avatar/AvatarDataSkeleton";
1010
import{DeprecatedBadge}from"components/Badges/Badges";
1111
import{Button}from"components/Button/Button";
12-
importtype{useFilter}from"components/Filter/Filter";
1312
import{
1413
HelpTooltip,
1514
HelpTooltipContent,
@@ -52,6 +51,7 @@ import {
5251
}from"utils/templates";
5352
import{EmptyTemplates}from"./EmptyTemplates";
5453
import{TemplatesFilter}from"./TemplatesFilter";
54+
importtype{TemplateFilterState}from"./TemplatesPage";
5555

5656
constLanguage={
5757
developerCount:(activeCount:number):string=>{
@@ -184,7 +184,7 @@ const TemplateRow: FC<TemplateRowProps> = ({
184184

185185
interfaceTemplatesPageViewProps{
186186
error?:unknown;
187-
filter:ReturnType<typeofuseFilter>;
187+
filterState:TemplateFilterState;
188188
showOrganizations:boolean;
189189
canCreateTemplates:boolean;
190190
examples:TemplateExample[]|undefined;
@@ -194,7 +194,7 @@ interface TemplatesPageViewProps {
194194

195195
exportconstTemplatesPageView:FC<TemplatesPageViewProps>=({
196196
error,
197-
filter,
197+
filterState,
198198
showOrganizations,
199199
canCreateTemplates,
200200
examples,
@@ -229,7 +229,11 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
229229
</PageHeaderSubtitle>
230230
</PageHeader>
231231

232-
<TemplatesFilterfilter={filter}error={error}/>
232+
<TemplatesFilter
233+
filter={filterState.filter}
234+
error={error}
235+
userMenu={filterState.menus.user}
236+
/>
233237
{/* Validation errors are shown on the filter, other errors are an alert box. */}
234238
{hasError(error)&&!isApiValidationError(error)&&(
235239
<ErrorAlerterror={error}/>
@@ -256,7 +260,7 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
256260
<EmptyTemplates
257261
canCreateTemplates={canCreateTemplates}
258262
examples={examples??[]}
259-
isUsingFilter={filter.used}
263+
isUsingFilter={filterState.filter.used}
260264
/>
261265
) :(
262266
templates?.map((template)=>(

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {
33
MenuSkeleton,
44
typeUseFilterResult,
55
}from"components/Filter/Filter";
6-
import{typeUserFilterMenu,UserMenu}from"components/Filter/UserFilter";
6+
import{
7+
DEFAULT_USER_FILTER_WIDTH,
8+
typeUserFilterMenu,
9+
UserMenu,
10+
}from"components/Filter/UserFilter";
711
import{useDashboard}from"modules/dashboard/useDashboard";
812
import{
913
typeOrganizationsFilterMenu,
@@ -96,7 +100,7 @@ export const WorkspacesFilter: FC<WorkspaceFilterProps> = ({
96100
organizationsMenu,
97101
})=>{
98102
const{ entitlements, showOrganizations}=useDashboard();
99-
constwidth=showOrganizations ?175 :undefined;
103+
constwidth=showOrganizations ?DEFAULT_USER_FILTER_WIDTH :undefined;
100104
constpresets=entitlements.features.advanced_template_scheduling.enabled
101105
?PRESETS_WITH_DORMANT
102106
:PRESET_FILTERS;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp