Skip to content

Commit 3ab3ef8

Browse files
feat: add link to provisioner jobs and daemons (#17509)
Close #17314 **Demo** https://github.com/user-attachments/assets/db37aa67-4755-4b72-a54d-2c3f0c297b7d **Changes** - Added the `xs` button variant - Display all the daemons - idle and offline - and set a size limit to 100 results (explanation in the demo) - Filter daemons and jobs by ID
1 parent 5ca90ae commit 3ab3ef8

16 files changed

+244
-75
lines changed

site/src/api/api.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,17 @@ export class MissingBuildParameters extends Error {
396396
}
397397

398398
export type GetProvisionerJobsParams = {
399-
status?: TypesGen.ProvisionerJobStatus;
399+
status?: string;
400+
limit?: number;
401+
// IDs separated by comma
402+
ids?: string;
403+
};
404+
405+
export type GetProvisionerDaemonsParams = {
406+
// IDs separated by comma
407+
ids?: string;
408+
// Stringified JSON Object
409+
tags?: string;
400410
limit?: number;
401411
};
402412

@@ -711,22 +721,13 @@ class ApiMethods {
711721
return response.data;
712722
};
713723

714-
/**
715-
* @param organization Can be the organization's ID or name
716-
* @param tags to filter provisioner daemons by.
717-
*/
718724
getProvisionerDaemonsByOrganization = async (
719725
organization: string,
720-
tags?: Record<string, string>,
726+
params?: GetProvisionerDaemonsParams,
721727
): Promise<TypesGen.ProvisionerDaemon[]> => {
722-
const params = new URLSearchParams();
723-
724-
if (tags) {
725-
params.append("tags", JSON.stringify(tags));
726-
}
727-
728728
const response = await this.axios.get<TypesGen.ProvisionerDaemon[]>(
729-
`/api/v2/organizations/${organization}/provisionerdaemons?${params}`,
729+
`/api/v2/organizations/${organization}/provisionerdaemons`,
730+
{ params },
730731
);
731732
return response.data;
732733
};

site/src/api/queries/organizations.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { API, type GetProvisionerJobsParams } from "api/api";
1+
import {
2+
API,
3+
type GetProvisionerDaemonsParams,
4+
type GetProvisionerJobsParams,
5+
} from "api/api";
26
import type {
37
CreateOrganizationRequest,
48
GroupSyncSettings,
@@ -164,16 +168,17 @@ export const organizations = () => {
164168

165169
export const getProvisionerDaemonsKey = (
166170
organization: string,
167-
tags?: Record<string, string>,
168-
) => ["organization", organization, tags, "provisionerDaemons"];
171+
params?: GetProvisionerDaemonsParams,
172+
) => ["organization", organization, "provisionerDaemons", params];
169173

170174
export const provisionerDaemons = (
171175
organization: string,
172-
tags?: Record<string, string>,
176+
params?: GetProvisionerDaemonsParams,
173177
) => {
174178
return {
175-
queryKey: getProvisionerDaemonsKey(organization, tags),
176-
queryFn: () => API.getProvisionerDaemonsByOrganization(organization, tags),
179+
queryKey: getProvisionerDaemonsKey(organization, params),
180+
queryFn: () =>
181+
API.getProvisionerDaemonsByOrganization(organization, params),
177182
};
178183
};
179184

site/src/components/Button/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { forwardRef } from "react";
88
import { cn } from "utils/cn";
99

1010
export const buttonVariants = cva(
11-
`inline-flex items-center justify-center gap-1 whitespace-nowrap
11+
`inline-flex items-center justify-center gap-1 whitespace-nowrap font-sans
1212
border-solid rounded-md transition-colors
1313
text-sm font-semibold font-medium cursor-pointer no-underline
1414
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link
@@ -30,6 +30,7 @@ export const buttonVariants = cva(
3030
size: {
3131
lg: "min-w-20 h-10 px-3 py-2 [&_svg]:size-icon-lg",
3232
sm: "min-w-20 h-8 px-2 py-1.5 text-xs [&_svg]:size-icon-sm",
33+
xs: "min-w-8 py-1 px-2 text-2xs rounded-md",
3334
icon: "size-8 px-1.5 [&_svg]:size-icon-sm",
3435
"icon-lg": "size-10 px-2 [&_svg]:size-icon-lg",
3536
},

site/src/pages/CreateTokenPage/CreateTokenForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
119119

120120
{lifetimeDays === "custom" && (
121121
<TextField
122-
data-chromatic="ignore"
123122
type="date"
124123
label="Expires on"
125124
defaultValue={dayjs().add(expDays, "day").format("YYYY-MM-DD")}
@@ -130,6 +129,7 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
130129
setExpDays(lt);
131130
}}
132131
inputProps={{
132+
"data-chromatic": "ignore",
133133
min: dayjs().add(1, "day").format("YYYY-MM-DD"),
134134
max: maxTokenLifetime
135135
? dayjs()

site/src/pages/OrganizationSettingsPage/CustomRolesPage/PermissionPillsList.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const meta: Meta<typeof PermissionPillsList> = {
1515
],
1616
parameters: {
1717
chromatic: {
18-
diffThreshold: 0.5,
18+
diffThreshold: 0.6,
1919
},
2020
},
2121
};

site/src/pages/OrganizationSettingsPage/OrganizationProvisionerJobsPage/JobRow.tsx

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ import {
1515
ProvisionerTruncateTags,
1616
} from "modules/provisioners/ProvisionerTags";
1717
import { type FC, useState } from "react";
18+
import { Link as RouterLink } from "react-router-dom";
1819
import { cn } from "utils/cn";
1920
import { relativeTime } from "utils/time";
2021
import { CancelJobButton } from "./CancelJobButton";
2122

2223
type JobRowProps = {
2324
job: ProvisionerJob;
25+
defaultIsOpen: boolean;
2426
};
2527

26-
export const JobRow: FC<JobRowProps> = ({ job }) => {
28+
export const JobRow: FC<JobRowProps> = ({ job, defaultIsOpen = false }) => {
2729
const metadata = job.metadata;
28-
const [isOpen, setIsOpen] = useState(false);
30+
const [isOpen, setIsOpen] = useState(defaultIsOpen);
2931
const queue = {
3032
size: job.queue_size,
3133
position: job.queue_position,
@@ -114,19 +116,36 @@ export const JobRow: FC<JobRowProps> = ({ job }) => {
114116
: "[]"}
115117
</dd>
116118

117-
<dt>Completed by provisioner:</dt>
118-
<dd>{job.worker_id}</dd>
119+
{job.worker_id && (
120+
<>
121+
<dt>Completed by provisioner:</dt>
122+
<dd className="flex items-center gap-2">
123+
<span>{job.worker_id}</span>
124+
<Button size="xs" variant="outline" asChild>
125+
<RouterLink
126+
to={`../provisioners?${new URLSearchParams({ ids: job.worker_id })}`}
127+
>
128+
View provisioner
129+
</RouterLink>
130+
</Button>
131+
</dd>
132+
</>
133+
)}
119134

120135
<dt>Associated workspace:</dt>
121136
<dd>{job.metadata.workspace_name ?? "null"}</dd>
122137

123138
<dt>Creation time:</dt>
124139
<dd data-chromatic="ignore">{job.created_at}</dd>
125140

126-
<dt>Queue:</dt>
127-
<dd>
128-
{job.queue_position}/{job.queue_size}
129-
</dd>
141+
{job.queue_position > 0 && (
142+
<>
143+
<dt>Queue:</dt>
144+
<dd>
145+
{job.queue_position}/{job.queue_size}
146+
</dd>
147+
</>
148+
)}
130149

131150
<dt>Tags:</dt>
132151
<dd>

site/src/pages/OrganizationSettingsPage/OrganizationProvisionerJobsPage/OrganizationProvisionerJobsPage.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ const OrganizationProvisionerJobsPage: FC = () => {
1111
const { organization } = useOrganizationSettings();
1212
const [searchParams, setSearchParams] = useSearchParams();
1313
const filter = {
14-
status: searchParams.get("status") || "",
14+
status: searchParams.get("status") ?? "",
15+
ids: searchParams.get("ids") ?? "",
1516
};
16-
const queryParams = {
17-
...filter,
18-
limit: 100,
19-
} as GetProvisionerJobsParams;
2017
const {
2118
data: jobs,
2219
isLoadingError,
2320
refetch,
2421
} = useQuery({
25-
...provisionerJobs(organization?.id || "", queryParams),
22+
...provisionerJobs(organization?.id ?? "", {
23+
...filter,
24+
limit: 100,
25+
}),
2626
enabled: organization !== undefined,
2727
});
2828

site/src/pages/OrganizationSettingsPage/OrganizationProvisionerJobsPage/OrganizationProvisionerJobsPageView.stories.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const meta: Meta<typeof OrganizationProvisionerJobsPageView> = {
2121
args: {
2222
organization: MockOrganization,
2323
jobs: MockProvisionerJobs,
24-
filter: { status: "" },
24+
filter: { status: "", ids: "" },
2525
onRetry: fn(),
2626
},
2727
};
@@ -81,8 +81,8 @@ export const Empty: Story = {
8181
export const OnFilter: Story = {
8282
render: function FilterWithState({ ...args }) {
8383
const [jobs, setJobs] = useState<ProvisionerJob[]>([]);
84-
const [filter, setFilter] = useState({ status: "pending" });
85-
const handleFilterChange = (newFilter: { status: string }) => {
84+
const [filter, setFilter] = useState({ status: "pending", ids: "" });
85+
const handleFilterChange = (newFilter: { status: string; ids: string }) => {
8686
setFilter(newFilter);
8787
const filteredJobs = MockProvisionerJobs.filter((job) =>
8888
newFilter.status ? job.status === newFilter.status : true,
@@ -109,3 +109,13 @@ export const OnFilter: Story = {
109109
await userEvent.click(option);
110110
},
111111
};
112+
113+
export const FilterByID: Story = {
114+
args: {
115+
jobs: [MockProvisionerJob],
116+
filter: {
117+
ids: MockProvisionerJob.id,
118+
status: "",
119+
},
120+
},
121+
};

site/src/pages/OrganizationSettingsPage/OrganizationProvisionerJobsPage/OrganizationProvisionerJobsPageView.tsx

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
ProvisionerJob,
44
ProvisionerJobStatus,
55
} from "api/typesGenerated";
6+
import { Badge } from "components/Badge/Badge";
67
import { Button } from "components/Button/Button";
78
import { EmptyState } from "components/EmptyState/EmptyState";
89
import { Link } from "components/Link/Link";
@@ -33,6 +34,13 @@ import {
3334
TableHeader,
3435
TableRow,
3536
} from "components/Table/Table";
37+
import {
38+
Tooltip,
39+
TooltipContent,
40+
TooltipProvider,
41+
TooltipTrigger,
42+
} from "components/Tooltip/Tooltip";
43+
import { XIcon } from "lucide-react";
3644
import type { FC } from "react";
3745
import { Helmet } from "react-helmet-async";
3846
import { docs } from "utils/docs";
@@ -64,6 +72,7 @@ const StatusFilters: ProvisionerJobStatus[] = [
6472

6573
type JobProvisionersFilter = {
6674
status: string;
75+
ids: string;
6776
};
6877

6978
type OrganizationProvisionerJobsPageViewProps = {
@@ -110,30 +119,62 @@ const OrganizationProvisionerJobsPageView: FC<
110119
</SettingsHeaderDescription>
111120
</SettingsHeader>
112121

113-
<Select
114-
value={filter.status}
115-
onValueChange={(status) => {
116-
onFilterChange({ status: status as ProvisionerJobStatus });
117-
}}
118-
>
119-
<SelectTrigger className="w-[180px]" data-testid="status-filter">
120-
<SelectValue placeholder="All statuses" />
121-
</SelectTrigger>
122-
<SelectContent>
123-
<SelectGroup>
124-
{StatusFilters.map((status) => (
125-
<SelectItem key={status} value={status}>
126-
<StatusIndicator variant={variantByStatus[status]}>
127-
<StatusIndicatorDot />
128-
<span className="block first-letter:uppercase">
129-
{status}
130-
</span>
131-
</StatusIndicator>
132-
</SelectItem>
133-
))}
134-
</SelectGroup>
135-
</SelectContent>
136-
</Select>
122+
<div className="flex items-center gap-2">
123+
{filter.ids && (
124+
<div className="relative">
125+
<Badge className="h-10 text-sm pl-3 pr-10 font-mono">
126+
{filter.ids}
127+
</Badge>
128+
<div className="size-10 flex items-center justify-center absolute top-0 right-0">
129+
<TooltipProvider>
130+
<Tooltip>
131+
<TooltipTrigger asChild>
132+
<Button
133+
size="icon"
134+
variant="subtle"
135+
onClick={() => {
136+
onFilterChange({ ...filter, ids: "" });
137+
}}
138+
>
139+
<span className="sr-only">Clear ID</span>
140+
<XIcon />
141+
</Button>
142+
</TooltipTrigger>
143+
<TooltipContent>Clear ID</TooltipContent>
144+
</Tooltip>
145+
</TooltipProvider>
146+
</div>
147+
</div>
148+
)}
149+
150+
<Select
151+
value={filter.status}
152+
onValueChange={(status) => {
153+
onFilterChange({
154+
...filter,
155+
status,
156+
});
157+
}}
158+
>
159+
<SelectTrigger className="w-[180px]" data-testid="status-filter">
160+
<SelectValue placeholder="All statuses" />
161+
</SelectTrigger>
162+
<SelectContent>
163+
<SelectGroup>
164+
{StatusFilters.map((status) => (
165+
<SelectItem key={status} value={status}>
166+
<StatusIndicator variant={variantByStatus[status]}>
167+
<StatusIndicatorDot />
168+
<span className="block first-letter:uppercase">
169+
{status}
170+
</span>
171+
</StatusIndicator>
172+
</SelectItem>
173+
))}
174+
</SelectGroup>
175+
</SelectContent>
176+
</Select>
177+
</div>
137178

138179
<Table className="mt-6">
139180
<TableHeader>
@@ -149,7 +190,13 @@ const OrganizationProvisionerJobsPageView: FC<
149190
<TableBody>
150191
{jobs ? (
151192
jobs.length > 0 ? (
152-
jobs.map((j) => <JobRow key={j.id} job={j} />)
193+
jobs.map((j) => (
194+
<JobRow
195+
defaultIsOpen={filter.ids.includes(j.id)}
196+
key={j.id}
197+
job={j}
198+
/>
199+
))
153200
) : (
154201
<TableRow>
155202
<TableCell colSpan={999}>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy