-
Notifications
You must be signed in to change notification settings - Fork 929
feat: display provisioner jobs and daemons for an organization #16532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b6430bb
5d7d58f
643c362
f9db209
6e967f1
943b7d7
2bc6ccf
71f4fe5
f027485
dcf8140
994e186
3083cef
d66141e
49a7ec7
ffee2ed
7802636
4f9030f
22dc7be
0ff39e5
5953960
aabf8df
ed61ce7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,52 @@ | ||
import { buildInfo } from "api/queries/buildInfo"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps I'm missing some context, could you explain why are we removing these pages? Will they be replaced now? PS. It would be convenient to add Changes: to the PR description. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, they will be replaced. |
||
import { provisionerDaemonGroups } from "api/queries/organizations"; | ||
import { | ||
provisionerDaemonGroups, | ||
provisionerJobs, | ||
} from "api/queries/organizations"; | ||
import type { Organization } from "api/typesGenerated"; | ||
import { Avatar } from "components/Avatar/Avatar"; | ||
import { Badge } from "components/Badge/Badge"; | ||
import { Button } from "components/Button/Button"; | ||
import { EmptyState } from "components/EmptyState/EmptyState"; | ||
import { Link } from "components/Link/Link"; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from "components/Table/Table"; | ||
import { TableEmpty } from "components/TableEmpty/TableEmpty"; | ||
import { TableLoader } from "components/TableLoader/TableLoader"; | ||
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs"; | ||
import { | ||
Tooltip, | ||
TooltipContent, | ||
TooltipProvider, | ||
TooltipTrigger, | ||
} from "components/Tooltip/Tooltip"; | ||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; | ||
import { BanIcon } from "lucide-react"; | ||
import { useDashboard } from "modules/dashboard/useDashboard"; | ||
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout"; | ||
import type { FC } from "react"; | ||
import { Helmet } from "react-helmet-async"; | ||
import { useQuery } from "react-query"; | ||
import { useParams } from "react-router-dom"; | ||
import { docs } from "utils/docs"; | ||
import { pageTitle } from "utils/page"; | ||
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; | ||
import { relativeTime } from "utils/time"; | ||
|
||
const OrganizationProvisionersPage: FC = () => { | ||
const { organization: organizationName } = useParams() as { | ||
organization: string; | ||
}; | ||
// const { organization: organizationName } = useParams() as { | ||
// organization: string; | ||
// }; | ||
const { organization } = useOrganizationSettings(); | ||
const { entitlements } = useDashboard(); | ||
const { metadata } = useEmbeddedMetadata(); | ||
const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); | ||
const provisionersQuery = useQuery(provisionerDaemonGroups(organizationName)); | ||
// const { entitlements } = useDashboard(); | ||
// const { metadata } = useEmbeddedMetadata(); | ||
// const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); | ||
// const provisionersQuery = useQuery(provisionerDaemonGroups(organizationName)); | ||
|
||
if (!organization) { | ||
return <EmptyState message="Organization not found" />; | ||
|
@@ -35,14 +62,124 @@ const OrganizationProvisionersPage: FC = () => { | |
)} | ||
</title> | ||
</Helmet> | ||
<OrganizationProvisionersPageView | ||
showPaywall={!entitlements.features.multiple_organizations.enabled} | ||
error={provisionersQuery.error} | ||
buildInfo={buildInfoQuery.data} | ||
provisioners={provisionersQuery.data} | ||
/> | ||
|
||
<div className="flex flex-col gap-12"> | ||
<header className="flex flex-row items-baseline justify-between"> | ||
<div className="flex flex-col gap-2"> | ||
<h1 className="text-3xl m-0">Provisioners</h1> | ||
</div> | ||
</header> | ||
|
||
<main> | ||
<Tabs active="jobs"> | ||
<TabsList> | ||
<TabLink value="jobs" to="?tab=jobs"> | ||
Jobs | ||
</TabLink> | ||
<TabLink value="daemons" to="?tab=daemons"> | ||
Daemons | ||
</TabLink> | ||
</TabsList> | ||
</Tabs> | ||
|
||
<div className="mt-6"> | ||
<JobsTabContent org={organization} /> | ||
</div> | ||
</main> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
type JobsTabContentProps = { | ||
org: Organization; | ||
}; | ||
|
||
const JobsTabContent: FC<JobsTabContentProps> = ({ org }) => { | ||
const { organization } = useOrganizationSettings(); | ||
const { data: jobs, isLoadingError } = useQuery(provisionerJobs(org.id)); | ||
|
||
return ( | ||
<section className="flex flex-col gap-8"> | ||
<p className="text-sm text-content-secondary m-0 mt-2"> | ||
Provisioner Jobs are the individual tasks assigned to Provisioners when | ||
the workspaces are being built.{" "} | ||
<Link href={docs("/admin/provisioners")}>View docs</Link> | ||
</p> | ||
|
||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead>Last seen</TableHead> | ||
<TableHead>Type</TableHead> | ||
<TableHead>Template</TableHead> | ||
<TableHead>Tags</TableHead> | ||
<TableHead>Status</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{jobs ? ( | ||
jobs.length > 0 ? ( | ||
jobs.map(({ metadata, ...job }) => { | ||
if (!metadata) { | ||
throw new Error( | ||
`Metadata is required but it is missing in the job ${job.id}`, | ||
); | ||
} | ||
return ( | ||
<TableRow key={job.id}> | ||
<TableCell className="[&:first-letter]:uppercase"> | ||
{relativeTime(new Date(job.created_at))} | ||
</TableCell> | ||
<TableCell> | ||
<Badge size="sm">{job.type}</Badge> | ||
</TableCell> | ||
<TableCell> | ||
<div className="flex items-center gap-1"> | ||
<Avatar | ||
variant="icon" | ||
src={metadata.template_icon.icon} | ||
fallback={template.display_name || template.name} | ||
/> | ||
{metadata.template_display_name ?? | ||
metadata.template_name} | ||
</div> | ||
</TableCell> | ||
<TableCell> | ||
<Badge size="sm">[foo=bar]</Badge> | ||
</TableCell> | ||
<TableCell>Completed</TableCell> | ||
<TableCell className="text-right"> | ||
<TooltipProvider> | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
aria-label="Cancel job" | ||
size="icon" | ||
variant="outline" | ||
> | ||
<BanIcon /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>Cancel job</TooltipContent> | ||
</Tooltip> | ||
</TooltipProvider> | ||
</TableCell> | ||
</TableRow> | ||
); | ||
}) | ||
) : ( | ||
<TableEmpty message="No provisioner jobs found" /> | ||
) | ||
) : isLoadingError ? ( | ||
<TableEmpty message="Error loading the provisioner jobs" /> | ||
) : ( | ||
<TableLoader /> | ||
)} | ||
</TableBody> | ||
</Table> | ||
</section> | ||
); | ||
}; | ||
|
||
export default OrganizationProvisionersPage; |
Uh oh!
There was an error while loading. Please reload this page.