Files
nomad/ui/app/templates/jobs/index.hbs
Aimee Ukasick a30cb2f137 Update UI, code comment, and README links to docs, tutorials (#26429)
* Update UI, code comment, and README links to docs, tutorials

* fix typo in ephemeral disks learn more link url

* feedback on typo

Co-authored-by: Tim Gross <tgross@hashicorp.com>

---------

Co-authored-by: Tim Gross <tgross@hashicorp.com>
2025-08-06 09:40:23 -05:00

373 lines
14 KiB
Handlebars
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
{{page-title "Jobs"}}
<section class="section">
{{#if this.showingCachedJobs}}
<Hds::Alert @type="inline" @color="warning" id="jobs-list-cache-warning" as |A|>
<A.Title>Error fetching jobs — shown jobs are cached</A.Title>
<A.Description>Jobs shown are cached and may be out of date. This is often due to a short timeout in proxy configurations.</A.Description>
{{#if this.watchJobIDs.isRunning}}
<A.Button data-test-pause-fetching @text="Stop polling for job updates" @color="secondary" {{on "click" this.pauseJobFetching}} />
{{/if}}
<A.Button data-test-restart-fetching @text="Manually fetch jobs" @color="secondary" {{on "click" this.restartJobList}} />
<A.LinkStandalone @size="medium" @color="primary" @icon="learn-link" @iconPosition="trailing" @text="Tutorial: Configure reverse proxy for Nomad's web UI" @href="https://developer.hashicorp.com/nomad/docs/deploy/clusters/reverse-proxy-ui#extend-connection-timeout" />
</Hds::Alert>
{{/if}}
<Hds::PageHeader id="jobs-list-header" as |PH|>
<PH.Actions id="jobs-list-actions">
<Hds::SegmentedGroup as |S|>
<JobSearchBox
@searchText={{this.rawSearchText}}
@onSearchTextChange={{queue
this.updateSearchText
this.updateFilter
}}
@S={{S}}
/>
{{#each this.filterFacets as |group|}}
<S.Dropdown data-test-facet={{group.label}} @height="300px" as |dd|>
<dd.ToggleButton
@text={{group.label}}
@color="secondary"
@count={{get (filter-by "checked" true group.options) "length"}}
/>
{{#if group.filterable}}
<dd.Header @hasDivider={{true}}>
<Hds::Form::TextInput::Base
@type="search"
placeholder="Filter {{group.label}}"
@value={{group.filter}}
{{autofocus}}
{{on "input" (action (mut group.filter) value="target.value") }}
data-test-namespace-filter-searchbox
/>
</dd.Header>
{{#each (get this (concat "filtered" (capitalize group.label) "Options")) as |option|}}
<dd.Checkbox
{{on "change" (fn this.toggleOption option)}}
@value={{option.key}}
checked={{option.checked}}
data-test-dropdown-option={{option.key}}
>
{{option.key}}
</dd.Checkbox>
{{else}}
<dd.Generic data-test-dropdown-empty>
No {{group.label}} filters match {{group.filter}}
</dd.Generic>
{{/each}}
{{else}}
{{#each group.options as |option|}}
<dd.Checkbox
{{on "change" (fn this.toggleOption option)}}
@value={{option.key}}
checked={{option.checked}}
data-test-dropdown-option={{option.key}}
>
{{option.key}}
</dd.Checkbox>
{{else}}
<dd.Generic data-test-dropdown-empty>
No {{group.label}} filters
</dd.Generic>
{{/each}}
{{/if}}
</S.Dropdown>
{{/each}}
{{#if this.filter}}
<S.Button
@text="Reset Filters"
@color="critical"
@icon="delete"
@size="medium"
{{on "click" (action this.resetFilters)}}
/>
{{/if}}
</Hds::SegmentedGroup>
{{#if this.pendingJobIDDiff}}
<Hds::Button
@size="medium"
@text="Updates Pending"
@color="primary"
@icon="sync"
{{on "click" (perform this.updateJobList)}}
data-test-updates-pending-button
/>
{{/if}}
<div
{{keyboard-shortcut
label="Run Job"
pattern=(array "r" "u" "n")
action=(action this.goToRun)
}}
>
<Hds::Button
data-test-run-job
@text="Run Job"
disabled={{not (can "run job")}}
title={{if (can "run job") null "You dont have permission to run jobs"}}
@route="jobs.run"
/>
</div>
</PH.Actions>
</Hds::PageHeader>
{{#if this.isForbidden}}
<ForbiddenMessage />
{{else if this.jobs.length}}
<Hds::Table
@model={{this.jobs}}
@columns={{this.tableColumns}}
@valign="middle"
>
<:body as |B|>
{{!-- TODO: use <JobRow> --}}
<B.Tr
{{keyboard-shortcut
enumerated=true
action=(action "gotoJob" B.data)
}}
{{on "click" (action this.gotoJob B.data)}}
class="job-row is-interactive {{if B.data.assumeGC "assume-gc"}}"
data-test-job-row={{B.data.plainId}}
data-test-modify-index={{B.data.modifyIndex}}
>
{{!-- {{#each this.tableColumns as |column|}}
<B.Td>{{get B.data (lowercase column.label)}}</B.Td>
{{/each}} --}}
<B.Td data-test-job-name>
{{#if B.data.assumeGC}}
{{B.data.name}}
{{else}}
<LinkTo
@route="jobs.job.index"
@model={{B.data.idWithNamespace}}
class="is-primary"
>
{{B.data.name}}
{{#if B.data.isPack}}
<span data-test-pack-tag class="tag is-pack">
<Hds::Icon @name="box" @color="faint" />
<span>Pack</span>
</span>
{{/if}}
</LinkTo>
{{/if}}
</B.Td>
{{#if this.system.shouldShowNamespaces}}
<B.Td data-test-job-namespace>{{B.data.namespace.id}}</B.Td>
{{/if}}
<B.Td data-test-job-status>
<div class="status-cell">
{{#if (not (eq B.data.childStatuses null))}}
{{#if B.data.childStatusBreakdown.running}}
<Hds::Badge @icon="corner-down-right" @text="{{B.data.childStatusBreakdown.running}} running {{pluralize "job" B.data.childStatusBreakdown.running}}" @color="highlight" @size="large" />
{{else if B.data.childStatusBreakdown.pending}}
<Hds::Badge @icon="corner-down-right" @text="{{B.data.childStatusBreakdown.pending}} pending {{pluralize "job" B.data.childStatusBreakdown.pending}}" @color="neutral" @size="large" />
{{else if B.data.childStatusBreakdown.dead}}
<Hds::Badge @icon="corner-down-right" @text="{{B.data.childStatusBreakdown.dead}} completed {{pluralize "job" B.data.childStatusBreakdown.dead}}" @color="neutral" @size="large" />
{{else if (not B.data.childStatuses.length)}}
<Hds::Badge @icon="corner-down-right" @text="No child jobs" @color="neutral" @size="large" />
{{/if}}
{{else}}
<Hds::Badge @text="{{capitalize B.data.aggregateAllocStatus.label}}" @color={{B.data.aggregateAllocStatus.state}} @size="large" />
{{/if}}
{{#if B.data.hasPausedTask}}
<Hds::TooltipButton @text="At least one task is paused" @placement="right" data-test-paused-task-indicator>
<Hds::Badge @text="At least one task is paused" @icon="delay" @isIconOnly={{true}} @color="highlight" @size="large" />
</Hds::TooltipButton>
{{/if}}
</div>
</B.Td>
<B.Td data-test-job-type={{B.data.type}}>
{{B.data.type}}
</B.Td>
{{#if this.system.shouldShowNodepools}}
<B.Td data-test-job-node-pool>{{B.data.nodePool}}</B.Td>
{{/if}}
<B.Td>
<div class="job-status-panel compact">
{{#unless B.data.assumeGC}}
{{#if (not (eq B.data.childStatuses null))}}
{{#if B.data.childStatuses.length}}
<Hds::Link::Standalone @icon="chevron-right" @text="View {{B.data.childStatuses.length}} child {{pluralize "job" B.data.childStatuses.length}}" @route="jobs.job.index" @model={{B.data.idWithNamespace}} @iconPosition="trailing" />
{{else}}
--
{{/if}}
{{else}}
<JobStatus::AllocationStatusRow
@allocBlocks={{B.data.allocBlocks}}
@steady={{true}}
@compact={{true}}
@runningAllocs={{B.data.allocBlocks.running.healthy.nonCanary.length}}
@groupCountSum={{B.data.expectedRunningAllocCount}}
/>
{{/if}}
{{/unless}}
</div>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
<section id="jobs-list-pagination">
<div class="nav-buttons">
<span {{keyboard-shortcut
label="First Page"
pattern=(array "{" "{")
action=(action this.handlePageChange "first")}}
>
<Hds::Button
@text="First"
@color="tertiary"
@size="small"
@icon="chevrons-left"
@iconPosition="leading"
disabled={{not this.cursorAt}}
data-test-pager="first"
{{on "click" (action this.handlePageChange "first")}}
/>
</span>
<span {{keyboard-shortcut
label="Previous Page"
pattern=(array "[" "[")
action=(action this.handlePageChange "prev")}}
>
<Hds::Button
@text="Previous"
@color="tertiary"
@size="small"
@icon="chevron-left"
@iconPosition="leading"
disabled={{not this.cursorAt}}
data-test-pager="previous"
{{on "click" (action this.handlePageChange "prev")}}
/>
</span>
<span {{keyboard-shortcut
label="Next Page"
pattern=(array "]" "]")
action=(action this.handlePageChange "next")}}
>
<Hds::Button
@text="Next"
@color="tertiary"
@size="small"
@icon="chevron-right"
@iconPosition="trailing"
disabled={{not this.nextToken}}
data-test-pager="next"
{{on "click" (action this.handlePageChange "next")}}
/>
</span>
<span {{keyboard-shortcut
label="Last Page"
pattern=(array "}" "}")
action=(action this.handlePageChange "last")}}
>
<Hds::Button
@text="Last"
@color="tertiary"
@icon="chevrons-right"
@iconPosition="trailing"
@size="small"
disabled={{not this.nextToken}}
data-test-pager="last"
{{on "click" (action this.handlePageChange "last")}}
/>
</span>
</div>
<div class="page-size">
<PageSizeSelect @onChange={{this.handlePageSizeChange}} />
</div>
</section>
{{else}}
<Hds::ApplicationState data-test-empty-jobs-list as |A|>
{{#if this.filter}}
<A.Header data-test-empty-jobs-list-headline @title="No Matches" />
<A.Body>
{{this.humanizedFilterError}}
<br><br>
{{#if this.model.error.correction}}
Did you mean
<Hds::Button
data-test-filter-correction
@text={{this.model.error.correction.correctKey}}
@isInline={{true}}
@color="tertiary"
@icon="bulb"
@iconPosition="trailing"
{{on "click" (action this.correctFilterKey this.model.error.correction)}}
/>?
{{else if this.model.error.suggestion}}
<ul>
{{#each this.model.error.suggestion as |suggestion|}}
<li><Hds::Button
data-test-filter-suggestion
@text={{suggestion.key}}
@isInline={{true}}
@size="small"
@color="tertiary"
@icon="bulb"
{{on "click" (action this.suggestFilter suggestion)}}
/></li>
{{/each}}
</ul>
{{else}}
{{!-- This is the "Nothing was found for your otherwise valid filter" option. Give them suggestions --}}
Did you know: you can try using filter expressions to search through your jobs.
Try <Hds::Button
data-test-filter-random-suggestion
@text={{this.exampleFilter}}
@isInline={{true}}
@color="tertiary"
@icon="bulb"
@iconPosition="trailing"
{{on "click" (action this.suggestFilter
(hash example=this.exampleFilter)
)}}
/>
{{/if}}
</A.Body>
<A.Footer as |F|>
<Hds::Button
@text="Reset Filters"
@color="tertiary"
@icon="arrow-left"
@size="small"
{{on "click" (action this.resetFilters)}}
/>
<F.LinkStandalone @icon="docs" @text="Learn more about Filter Expressions" @href="https://developer.hashicorp.com/nomad/api-docs#creating-expressions" @iconPosition="trailing" />
<F.LinkStandalone @icon="plus" @text="Run a New Job" @route="jobs.run"
@iconPosition="trailing" />
</A.Footer>
{{else}}
<A.Header data-test-empty-jobs-list-headline @title="No Jobs" />
<A.Body
@text="No jobs found."
/>
<A.Footer as |F|>
<F.LinkStandalone @icon="plus" @text="Run a New Job" @route="jobs.run"
@iconPosition="trailing" />
</A.Footer>
{{/if}}
</Hds::ApplicationState>
{{/if}}
</section>