Skip to content

Commit

Permalink
ui: Toggle for read-only view (#16279)
Browse files Browse the repository at this point in the history
* ui: model update for specification

* style: add styling for select

* style: add styling for select

* refact: add spec to view

* refact: update component API

* test: refactor for new UI state

* refact: clean conditional

* refact: update component API for prop

* chore: correct naming

* chore:  remove `fn` helper

Co-authored-by: Phil Renaud <[email protected]>

* update `default` Mirage scenario (#16496)

* chore: update mirage scenario:

* ui: conditionally render toggle button (#16497)

* chore: update css variable name (#16498)

---------

Co-authored-by: Phil Renaud <[email protected]>
  • Loading branch information
ChaiWithJai and philrenaud committed Apr 26, 2023
1 parent f12c957 commit 8ae49a2
Show file tree
Hide file tree
Showing 12 changed files with 1,025 additions and 185 deletions.
72 changes: 70 additions & 2 deletions ui/app/components/job-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,40 @@ export default class JobEditor extends Component {

@tracked error = null;
@tracked planOutput = null;
@tracked isEditing;
@tracked view;

constructor() {
super(...arguments);
this.isEditing = !!(this.args.context === 'new');
this.view = this.args.specification ? 'job-spec' : 'full-definition';
}

toggleEdit(bool) {
this.isEditing = bool || !this.isEditing;
}

@action
edit() {
this.args.job.set(
'_newDefinition',
JSON.stringify(this.args.definition, null, 2)
);
this.toggleEdit(true);
}

@action
onCancel() {
this.toggleEdit(false);
}

get stage() {
return this.planOutput ? 'plan' : 'editor';
if (this.planOutput) return 'review';
if (this.isEditing) return 'edit';
else return 'read';
}

@localStorageProperty('nomadMessageJobPlan', true) showPlanMessage;
@localStorageProperty('nomadMessageJobPlan', true) shouldShowPlanMessage;

@(task(function* () {
this.reset();
Expand Down Expand Up @@ -100,4 +128,44 @@ export default class JobEditor extends Component {
const [file] = event.target.files;
reader.readAsText(file);
}

@action
toggleView() {
const opposite = this.view === 'job-spec' ? 'full-definition' : 'job-spec';
this.view = opposite;
}

get definition() {
if (this.view === 'full-definition') {
return this.args.definition;
} else {
return this.args.specification;
}
}

get data() {
return {
cancelable: this.args.cancelable,
definition: this.definition,
hasSpecification: !!this.args.specification,
job: this.args.job,
planOutput: this.planOutput,
shouldShowPlanMessage: this.shouldShowPlanMessage,
view: this.view,
};
}

get fns() {
return {
onCancel: this.onCancel,
onEdit: this.edit,
onPlan: this.plan,
onReset: this.reset,
onSaveAs: this.args.handleSaveAsTemplate,
onSubmit: this.submit,
onToggle: this.toggleView,
onUpdate: this.updateCode,
onUpload: this.uploadJobSpec,
};
}
}
15 changes: 3 additions & 12 deletions ui/app/controllers/jobs/job/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,11 @@ import { inject as service } from '@ember/service';
export default class DefinitionController extends Controller.extend(
WithNamespaceResetting
) {
@alias('model.job') job;
@alias('model.definition') definition;
@service router;

isEditing = false;

edit() {
this.job.set('_newDefinition', JSON.stringify(this.definition, null, 2));
this.set('isEditing', true);
}
@alias('model.job') job;
@alias('model.specification') specification;

onCancel() {
this.set('isEditing', false);
}
@service router;

onSubmit() {
this.router.transitionTo('jobs.job', this.job.idWithNamespace);
Expand Down
15 changes: 12 additions & 3 deletions ui/app/routes/jobs/job/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@
import Route from '@ember/routing/route';

export default class DefinitionRoute extends Route {
model() {
async model() {
const job = this.modelFor('jobs.job');
if (!job) return;

return job.fetchRawDefinition().then((definition) => ({
const definition = await job.fetchRawDefinition();

const hasSpecification = !!definition?.Specification;

const specification = hasSpecification
? await new Blob([definition?.Specification?.Definition]).text()
: null;

return {
job,
definition,
}));
specification,
};
}

resetController(controller, isExiting) {
Expand Down
26 changes: 26 additions & 0 deletions ui/app/styles/components/codemirror.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,29 @@ header.run-job-header {
display: flex;
justify-content: space-between;
}

.job-definition-toggle {
border: 1px solid $grey-blue;
background: rgba(0, 0, 0, 0.05);
border-radius: 2px;
display: grid;
gap: 0.5rem;
grid-template-columns: 1fr 1fr;
padding: 0.25rem 0.5rem;
margin: 0rem 1rem;

button {
height: auto;
padding: 0 0.5rem;
background: transparent;
transition: 0.1s;

&:hover {
background: rgba(255, 255, 255, 0.5);
}

&.is-active {
background: $white;
}
}
}
132 changes: 1 addition & 131 deletions ui/app/templates/components/job-editor.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<p data-test-error-message>{{this.error.message}}</p>
</div>
{{/if}}
{{#if (eq this.stage "editor")}}

<header class="run-job-header">
<h1 class="title is-3">Run a job</h1>
Expand All @@ -19,134 +18,5 @@
</p>
</header>

<div class="boxed-section">
<div class="boxed-section-head">
Job Definition
{{#if @cancelable}}
<button
class="button is-light is-compact pull-right"
onclick={{action @onCancel}}
data-test-cancel-editing
type="button"
>Cancel</button>
{{/if}}
</div>
<div class="boxed-section-body is-full-bleed">
<div
data-test-editor
{{code-mirror
screenReaderLabel="Job definition"
content=@job._newDefinition
theme="hashi"
onUpdate=this.updateCode
mode="javascript"
}}
/>
</div>
</div>

<div class="is-associative buttonset">
<Hds::Button
{{on "click" (perform this.plan)}}
disabled={{or this.plan.isRunning (not @job._newDefinition)}}
data-test-plan
@text="Plan"
/>
{{#if @job.isNew}}
<Hds::ButtonSet>
<label class="job-spec-upload hds-button hds-button--color-secondary hds-button--size-medium">
<div class="hds-button__text">Upload file</div>
<input type="file" onchange={{action this.uploadJobSpec}} accept=".hcl,.json,.nomad" />
</label>
{{#if (can "write variable" path="*" namespace="*")}}
<Hds::Button @icon="file-plus" @text="Save as template" @color="tertiary" @route="jobs.run.templates.new" {{on "click" @handleSaveAsTemplate}} data-test-save-as-template />
<Hds::Button @icon="file-text" @text="Choose from a template" @color="tertiary" @route="jobs.run.templates" data-test-choose-template />
{{/if}}
</Hds::ButtonSet>
{{/if}}
</div>
{{/if}}

{{#if (eq this.stage "plan")}}
{{#if this.showPlanMessage}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4" data-test-plan-help-title>Job Plan</h3>
<p data-test-plan-help-message>This is the impact running this job
will have on your cluster.</p>
</div>
<div class="column is-centered is-minimum">
<Hds::Button @text="Okay" {{on "click" (toggle-action "showPlanMessage" this)}} data-test-plan-help-dismiss />
</div>
</div>
</div>
{{/if}}
<div class="boxed-section">
<div class="boxed-section-head">Job Plan</div>
<div class="boxed-section-body is-dark">
<JobDiff
data-test-plan-output
@diff={{this.planOutput.diff}}
@verbose={{false}}
/>
</div>
</div>
<div
class="boxed-section
{{if this.planOutput.failedTGAllocs 'is-warning' 'is-primary'}}"
data-test-dry-run-message
>
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
<div class="boxed-section-body" data-test-dry-run-body>
{{#if this.planOutput.failedTGAllocs}}
{{#each this.planOutput.failedTGAllocs as |placementFailure|}}
<PlacementFailure @failedTGAlloc={{placementFailure}} />
{{/each}}
{{else}}
All tasks successfully allocated.
{{/if}}
</div>
</div>
{{#if
(and
this.planOutput.preemptions.isFulfilled this.planOutput.preemptions.length
)
}}
<div class="boxed-section is-warning" data-test-preemptions>
<div class="boxed-section-head" data-test-preemptions-title>
Preemptions (if you choose to run this job, these allocations will be
stopped)
</div>
<div class="boxed-section-body" data-test-preemptions-body>
<ListTable
@source={{this.planOutput.preemptions}}
@class="allocations is-isolated"
as |t|
>
<t.head>
<th class="is-narrow"></th>
<th>ID</th>
<th>Task Group</th>
<th>Created</th>
<th>Modified</th>
<th>Status</th>
<th>Version</th>
<th>Node</th>
<th>Volume</th>
<th>CPU</th>
<th>Memory</th>
</t.head>
<t.body as |row|>
<AllocationRow @allocation={{row.model}} @context="job" />
</t.body>
</ListTable>
</div>
</div>
{{/if}}
<Hds::ButtonSet class="is-associative">
<Hds::Button @text="Run" {{on "click" (perform this.submit)}} disabled={{this.submit.isRunning}} data-test-run />
<Hds::Button @color="secondary" @text="Cancel" {{on "click" this.reset}} disabled={{this.submit.isRunning}} data-test-cancel />
</Hds::ButtonSet>
{{/if}}
{{component (concat 'job-editor/' this.stage) data=this.data fns=this.fns}}
</div>
47 changes: 47 additions & 0 deletions ui/app/templates/components/job-editor/edit.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div class="boxed-section">
<div class="boxed-section-head">
Job Definition
{{#if @data.cancelable}}
<button
class="button is-light is-compact pull-right"
onclick={{@fns.onCancel}}
type="button"
data-test-cancel-editing
>
Cancel
</button>
{{/if}}
</div>
<div class="boxed-section-body is-full-bleed">
<div
data-test-editor
{{code-mirror
screenReaderLabel="Job definition"
content=@data.job._newDefinition
theme="hashi"
onUpdate=@fns.onUpdate
mode="javascript"
}}
/>
</div>
</div>
<div class="is-associative buttonset">
<Hds::Button
{{on "click" (perform @fns.onPlan)}}
disabled={{or @fns.onPlan.isRunning (not @data.job._newDefinition)}}
data-test-plan
@text="Plan"
/>
{{#if @data.job.isNew}}
<Hds::ButtonSet>
<label class="job-spec-upload hds-button hds-button--color-secondary hds-button--size-medium">
<div class="hds-button__text">Upload file</div>
<input type="file" onchange={{action @fns.onUpload}} accept=".hcl,.json,.nomad" />
</label>
{{#if (can "write variable" path="*" namespace="*")}}
<Hds::Button @icon="file-plus" @text="Save as template" @color="tertiary" @route="jobs.run.templates.new" {{on "click" @fns.onSaveAs}} data-test-save-as-template />
<Hds::Button @icon="file-text" @text="Choose from a template" @color="tertiary" @route="jobs.run.templates" data-test-choose-template />
{{/if}}
</Hds::ButtonSet>
{{/if}}
</div>
Loading

0 comments on commit 8ae49a2

Please sign in to comment.