Skip to content

Commit b414acf

Browse files
committed
start adding ui elements
1 parent 76722a7 commit b414acf

File tree

9 files changed

+144
-32
lines changed

9 files changed

+144
-32
lines changed

coderd/database/modelqueries.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
7575
rows, err := q.db.QueryContext(ctx, query,
7676
arg.Deleted,
7777
arg.OrganizationID,
78+
arg.OrganizationName,
7879
arg.ExactName,
7980
arg.FuzzyName,
8081
pq.Array(arg.IDs),

coderd/database/queries.sql.go

Lines changed: 25 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/templates.sql

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,23 @@ WHERE
2222
organization_id = @organization_id
2323
ELSE true
2424
END
25+
-- Filter by organization_name
26+
AND CASE
27+
WHEN @organization_name :: text != '' THEN
28+
LOWER("organization_name") = LOWER(@organization_name)
29+
ELSE true
30+
END
2531
-- Filter by exact name
2632
AND CASE
2733
WHEN @exact_name :: text != '' THEN
2834
LOWER("name") = LOWER(@exact_name)
2935
ELSE true
3036
END
31-
-- Filter by name, matching on substring
32-
AND CASE
33-
WHEN @fuzzy_name :: text != '' THEN
34-
lower(name) ILIKE '%' || lower(@fuzzy_name) || '%'
35-
ELSE true
37+
-- Filter by name, matching on substring
38+
AND CASE
39+
WHEN @fuzzy_name :: text != '' THEN
40+
lower(name) ILIKE '%' || lower(@fuzzy_name) || '%'
41+
ELSE true
3642
END
3743
-- Filter by ids
3844
AND CASE

coderd/searchquery/search.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,12 @@ func Templates(ctx context.Context, db database.Store, query string) (database.G
198198

199199
parser := httpapi.NewQueryParamParser()
200200
filter := database.GetTemplatesWithFilterParams{
201-
FuzzyName: parser.String(values, "", "name"),
202-
Deleted: parser.Boolean(values, false, "deleted"),
203-
ExactName: parser.String(values, "", "exact_name"),
204-
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
205-
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
201+
Deleted: parser.Boolean(values, false, "deleted"),
202+
OrganizationName: parser.String(values, "", "organization"),
203+
ExactName: parser.String(values, "", "exact_name"),
204+
FuzzyName: parser.String(values, "", "name"),
205+
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
206+
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
206207
}
207208

208209
// Convert the "organization" parameter to an organization uuid. This can require

site/src/api/api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,13 @@ class ApiMethods {
651651
return response.data;
652652
};
653653

654+
getMyOrganizations = async (): Promise<TypesGen.Organization[]> => {
655+
const response = await this.axios.get<TypesGen.Organization[]>(
656+
"/api/v2/users/me/organizations",
657+
);
658+
return response.data;
659+
};
660+
654661
/**
655662
* @param organization Can be the organization's ID or name
656663
*/
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type { FC } from "react";
2+
import { useSearchParams } from "react-router-dom";
3+
import {
4+
Filter,
5+
MenuSkeleton,
6+
SearchFieldSkeleton,
7+
useFilter,
8+
} from "components/Filter/filter";
9+
import { useFilterMenu } from "components/Filter/menu";
10+
import {
11+
SelectFilter,
12+
SelectFilterOption,
13+
} from "components/Filter/SelectFilter";
14+
import { API } from "api/api";
15+
import { UserAvatar } from "components/UserAvatar/UserAvatar";
16+
import { docs } from "utils/docs";
17+
import { Organization } from "api/typesGenerated";
18+
19+
export const TemplatesFilter: FC = () => {
20+
const searchParamsResult = useSearchParams();
21+
const filter = useFilter({
22+
fallbackFilter: "deprecated:false",
23+
searchParamsResult,
24+
onUpdate: () => {}, // reset pagination
25+
});
26+
27+
const organizationMenu = useFilterMenu({
28+
onChange: (option) =>
29+
filter.update({ ...filter.values, template: option?.value }),
30+
value: filter.values.organization,
31+
id: "status",
32+
getSelectedOption: async () => {
33+
if (!filter.values.organization) {
34+
return null;
35+
}
36+
37+
const org = await API.getOrganization(filter.values.organization);
38+
return orgOption(org);
39+
},
40+
getOptions: async () => {
41+
const orgs = await API.getMyOrganizations();
42+
return orgs.map(orgOption);
43+
},
44+
});
45+
46+
return (
47+
<Filter
48+
presets={[
49+
{ query: "", name: "All templates" },
50+
{ query: "deprecated", name: "Deprecated templates" },
51+
]}
52+
learnMoreLink={docs("/templates#template-filtering")}
53+
isLoading={false}
54+
filter={filter}
55+
options={
56+
<>
57+
<SelectFilter
58+
placeholder="All organizations"
59+
label="Select an organization"
60+
options={organizationMenu.searchOptions}
61+
selectedOption={organizationMenu.selectedOption ?? undefined}
62+
onSelect={organizationMenu.selectOption}
63+
/>
64+
</>
65+
}
66+
skeleton={
67+
<>
68+
<SearchFieldSkeleton />
69+
<MenuSkeleton />
70+
</>
71+
}
72+
/>
73+
);
74+
};
75+
76+
const orgOption = (org: Organization): SelectFilterOption => ({
77+
label: org.display_name || org.name,
78+
value: org.name,
79+
startIcon: (
80+
<UserAvatar
81+
key={org.id}
82+
size="sm"
83+
username={org.display_name}
84+
avatarURL={org.icon}
85+
/>
86+
),
87+
});

site/src/pages/TemplatesPage/TemplatesPageView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
formatTemplateActiveDevelopers,
4747
} from "utils/templates";
4848
import { EmptyTemplates } from "./EmptyTemplates";
49+
import { TemplatesFilter } from "./TemplatesFilter";
4950

5051
export const Language = {
5152
developerCount: (activeCount: number): string => {
@@ -220,6 +221,8 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
220221
)}
221222
</PageHeader>
222223

224+
<TemplatesFilter />
225+
223226
{error ? (
224227
<ErrorAlert error={error} />
225228
) : (

site/src/pages/WorkspacesPage/WorkspacesPageView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import StopOutlined from "@mui/icons-material/StopOutlined";
66
import LoadingButton from "@mui/lab/LoadingButton";
77
import Button from "@mui/material/Button";
88
import Divider from "@mui/material/Divider";
9-
import type { ComponentProps } from "react";
9+
import type { ComponentProps, FC } from "react";
1010
import type { UseQueryResult } from "react-query";
1111
import { hasError, isApiValidationError } from "api/errors";
1212
import type { Template, Workspace } from "api/typesGenerated";
@@ -65,7 +65,7 @@ export interface WorkspacesPageViewProps {
6565
canChangeVersions: boolean;
6666
}
6767

68-
export const WorkspacesPageView = ({
68+
export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({
6969
workspaces,
7070
error,
7171
limit,
@@ -86,7 +86,7 @@ export const WorkspacesPageView = ({
8686
templatesFetchStatus,
8787
canCreateTemplate,
8888
canChangeVersions,
89-
}: WorkspacesPageViewProps) => {
89+
}) => {
9090
// Let's say the user has 5 workspaces, but tried to hit page 100, which does
9191
// not exist. In this case, the page is not valid and we want to show a better
9292
// error message.

site/src/pages/WorkspacesPage/filter/menus.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ export const useTemplateFilterMenu = ({
4343
template.display_name.toLowerCase().includes(query.toLowerCase()),
4444
);
4545
return filteredTemplates.map((template) => ({
46-
label:
47-
template.display_name !== "" ? template.display_name : template.name,
46+
label: template.display_name || template.name,
4847
value: template.name,
4948
startIcon: <TemplateAvatar size="xs" template={template} />,
5049
}));

0 commit comments

Comments
 (0)