1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 13:55:21 +02:00

feat(edge/jobs): migrate item view to react [EE-2220] (#11887)
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled

This commit is contained in:
Chaim Lev-Ari 2024-06-06 21:07:39 +03:00 committed by GitHub
parent 62c2bf86aa
commit eb6d251a73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 778 additions and 886 deletions

View file

@ -1,206 +0,0 @@
<form class="form-horizontal -mt-4" name="edgeJobForm">
<div class="col-sm-12 form-section-title"> Edge job configuration </div>
<!-- name-input -->
<div class="form-group mt-4">
<label for="edgejob_name" class="col-sm-2 control-label required text-left">Name </label>
<div class="col-sm-10">
<input
type="text"
data-cy="edgejob-name-input"
class="form-control"
ng-model="$ctrl.model.Name"
ng-pattern="/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/"
id="edgejob_name"
name="edgejob_name"
placeholder="e.g. backup-app-prod"
required
auto-focus
/>
<div class="help-block" ng-show="edgeJobForm.edgejob_name.$invalid">
<div class="small text-warning">
<div ng-messages="edgeJobForm.edgejob_name.$error">
<p ng-message="required" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> This field is required.
</p>
<p ng-message="pattern" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> Allowed characters are: [a-zA-Z0-9_.-]
</p>
</div>
</div>
</div>
</div>
</div>
<!-- !name-input -->
<!-- cron-input -->
<!-- edge-job-method-select -->
<div class="col-sm-12 form-section-title"> Edge job configuration </div>
<box-selector slim="true" radio-name="'configurationold'" value="$ctrl.formValues.cronMethod" options="$ctrl.cronMethods" on-change="($ctrl.onCronMethodChange)"></box-selector>
<!-- !edge-job-method-select -->
<!-- basic-edge-job -->
<div ng-if="$ctrl.formValues.cronMethod === 'basic'">
<div class="form-group">
<label for="recurring" class="col-sm-2 control-label text-left">Recurring Edge job</label>
<div class="col-sm-10">
<label class="switch">
<input type="checkbox" name="recurring" ng-model="$ctrl.model.Recurring" id="recurring" data-cy="recurring-edge-job-checkbox" />
<span class="slider round"></span>
</label>
</div>
</div>
<!-- not-recurring -->
<div ng-if="!$ctrl.model.Recurring">
<div class="form-group">
<label for="edgejob_cron" class="col-sm-2 control-label text-left">Schedule date</label>
<div class="col-sm-10">
<input class="form-control" moment-picker ng-model="$ctrl.formValues.datetime" format="YYYY-MM-DD HH:mm" data-cy="edge-job-date-time-picker" />
</div>
<div class="col-sm-12 small text-muted mt-2.5"> Time should be set according to the chosen environments' timezone. </div>
<div ng-show="edgeJobForm.datepicker.$invalid">
<div class="col-sm-12 small text-warning">
<div ng-messages="edgeJobForm.datepicker.$error">
<p ng-message="required" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> This field is required.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- !not-recurring -->
<!-- recurring -->
<div ng-if="$ctrl.model.Recurring">
<div class="form-group">
<label for="edgejob_value" class="col-sm-2 control-label text-left">Edge job time</label>
<div class="col-sm-10">
<select
id="edgejob_value"
data-cy="edge-job-time-select"
name="edgejob_value"
class="form-control"
ng-model="$ctrl.formValues.scheduleValue"
ng-options="value.displayed for value in $ctrl.scheduleValues"
required
></select>
</div>
<div ng-show="edgeJobForm.edgejob_value.$invalid">
<div class="col-sm-12 small text-warning">
<div ng-messages="edgeJobForm.edgejob_value.$error">
<p ng-message="required" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> This field is required.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- !recurring -->
</div>
<!-- !basic-edge-job -->
<!-- advanced-schedule -->
<div ng-if="$ctrl.formValues.cronMethod === 'advanced'">
<div class="form-group">
<label for="edgejob_cron" class="col-sm-2 control-label text-left">Cron rule</label>
<div class="col-sm-10">
<input
type="text"
data-cy="edge-job-cron-input"
class="form-control"
ng-model="$ctrl.model.CronExpression"
id="edgejob_cron"
name="edgejob_cron"
placeholder="e.g. 0 2 * * *"
required
ng-pattern="$ctrl.cronRegex"
/>
<div class="help-block" ng-show="edgeJobForm.edgejob_cron.$invalid && edgeJobForm.edgejob_cron.$dirty">
<div class="small text-warning">
<div ng-messages="edgeJobForm.edgejob_cron.$error">
<p ng-message="required" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> This field is required.
</p>
<p ng-message="pattern" class="vertical-center">
<span><pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-warning'"></pr-icon></span> This field format is invalid.
</p>
</div>
</div>
</div>
</div>
<div class="col-sm-12 small text-muted mt-2.5"> Time should be set according to the chosen environments' timezone. </div>
</div>
</div>
<!-- !advanced-schedule -->
<!-- execution-method -->
<div ng-if="!$ctrl.model.Id">
<div class="col-sm-12 form-section-title"> Job content </div>
<box-selector value="$ctrl.formValues.method" options="$ctrl.buildMethods" radio-name="'buildMethodolds'" on-change="($ctrl.onBuildMethodChange)" slim="true"></box-selector>
</div>
<!-- !execution-method -->
<!-- web-editor -->
<!-- TODO use web-editor-form component -->
<div ng-show="$ctrl.formValues.method === 'editor'">
<div class="col-sm-12 form-section-title"> Web editor </div>
<div class="form-group">
<div class="col-sm-12">
<code-editor
identifier="execute-edge-job-editor"
placeholder="Define or paste the content of your script file here"
on-change="($ctrl.editorUpdate)"
value="$ctrl.model.FileContent"
shell="true"
></code-editor>
</div>
</div>
</div>
<!-- !web-editor -->
<!-- upload -->
<div ng-show="$ctrl.formValues.method === 'upload'">
<div class="col-sm-12 form-section-title"> Upload </div>
<div class="form-group">
<span class="col-sm-12 text-muted small"> You can upload a script file from your computer. </span>
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-sm btn-primary" ngf-select ng-model="$ctrl.model.File">Select file</button>
<span class="space-left">
{{ $ctrl.model.File.name }}
<span ng-if="!$ctrl.model.File"><pr-icon icon="'x'" class-name="'icon icon-danger'"></pr-icon></span>
</span>
</div>
</div>
</div>
<!-- !upload -->
<edge-groups-selector ng-if="$ctrl.model.EdgeGroups" value="$ctrl.model.EdgeGroups" on-change="($ctrl.onChangeGroups)"></edge-groups-selector>
<div class="col-sm-12 form-section-title"> Target environments </div>
<!-- node-selection -->
<associated-edge-environments-selector value="$ctrl.model.Endpoints" on-change="($ctrl.onChangeEnvironments)"></associated-edge-environments-selector>
<!-- !node-selection -->
<!-- actions -->
<div class="col-sm-12 form-section-title"> Actions </div>
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
class="btn btn-primary btn-sm"
ng-disabled="$ctrl.actionInProgress || !edgeJobForm.$valid
|| ($ctrl.model.Endpoints.length === 0 && $ctrl.model.EdgeGroups.length === 0)
|| ($ctrl.formValues.method === 'upload' && !$ctrl.model.File)
|| ($ctrl.formValues.method === 'editor' && !$ctrl.model.FileContent)
"
ng-click="$ctrl.action()"
button-spinner="$ctrl.actionInProgress"
>
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
<span ng-show="$ctrl.actionInProgress">In progress...</span>
</button>
<span class="text-danger space-left" ng-if="$ctrl.state.formValidationError"> {{ $ctrl.state.formValidationError }} </span>
</div>
</div>
<!-- !actions -->
</form>

View file

@ -1,142 +0,0 @@
import moment from 'moment';
import { editor, upload } from '@@/BoxSelector/common-options/build-methods';
import { cronMethodOptions } from '@/react/edge/edge-jobs/CreateView/cron-method-options';
export class EdgeJobFormController {
/* @ngInject */
constructor($async, $scope) {
this.$scope = $scope;
this.$async = $async;
this.cronMethods = cronMethodOptions.map((o) => ({ ...o, id: o.id + '-old' }));
this.buildMethods = [editor, upload].map((o) => ({ ...o, id: o.id + '-old' }));
this.state = {
formValidationError: '',
};
this.scheduleValues = [
{
displayed: 'Every hour',
cron: '0 * * * *',
},
{
displayed: 'Every 2 hours',
cron: '0 */2 * * *',
},
{
displayed: 'Every day',
cron: '0 0 * * *',
},
];
this.formValues = {
datetime: moment(),
scheduleValue: this.scheduleValues[0],
cronMethod: 'basic',
method: 'editor',
};
// see https://regexr.com/573i2
this.cronRegex =
/(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ){4,6}((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*))/;
this.action = this.action.bind(this);
this.editorUpdate = this.editorUpdate.bind(this);
this.onChangeEnvironments = this.onChangeEnvironments.bind(this);
this.onChangeGroups = this.onChangeGroups.bind(this);
this.onChange = this.onChange.bind(this);
this.onCronMethodChange = this.onCronMethodChange.bind(this);
this.onBuildMethodChange = this.onBuildMethodChange.bind(this);
}
onChange(values) {
this.$scope.$evalAsync(() => {
this.formValues = {
...this.formValues,
...values,
};
});
}
onBuildMethodChange(value) {
this.onChange({ method: value });
}
onCronMethodChange(value) {
this.onChange({ cronMethod: value });
}
onChangeModel(model) {
const defaultTime = moment().add('hours', 1);
const scheduled = this.scheduleValues.find((v) => v.cron === model.CronExpression);
this.formValues = {
datetime: model.CronExpression ? cronToDatetime(model.CronExpression, defaultTime) : defaultTime,
scheduleValue: scheduled || this.scheduleValues[0],
cronMethod: model.Recurring && !scheduled ? 'advanced' : 'basic',
method: this.formValues.method,
};
}
onChangeGroups(groups) {
return this.$scope.$evalAsync(() => {
this.model.EdgeGroups = groups ? groups : [];
});
}
action() {
this.state.formValidationError = '';
if (this.formValues.method === 'editor' && this.model.FileContent === '') {
this.state.formValidationError = 'Script file content must not be empty';
return;
}
if (this.formValues.cronMethod === 'basic') {
if (!this.model.Recurring && (this.formValues.datetime === undefined || !this.formValues.datetime.isValid())) {
this.state.formValidationError = 'Schedule date must not be empty';
return;
} else if (!this.model.Recurring) {
this.model.CronExpression = datetimeToCron(this.formValues.datetime);
} else {
this.model.CronExpression = this.formValues.scheduleValue.cron;
}
} else {
this.model.Recurring = true;
}
this.formAction(this.formValues.method);
}
editorUpdate(value) {
this.model.FileContent = value;
this.isEditorDirty = true;
}
onChangeEnvironments(value) {
return this.$scope.$evalAsync(() => {
this.model.Endpoints = value;
});
}
$onInit() {
this.onChangeModel(this.model);
}
}
function cronToDatetime(cron, defaultTime = moment()) {
var strings = cron.split(' ');
if (strings.length > 4) {
strings = strings.slice(0, 4);
} else {
return defaultTime;
}
return moment(cron, 'm H D M');
}
function datetimeToCron(datetime) {
var date = moment(datetime);
return [date.minutes(), date.hours(), date.date(), date.month() + 1, '*'].join(' ');
}

View file

@ -1,20 +0,0 @@
import angular from 'angular';
import { EdgeJobFormController } from './edgeJobFormController';
angular.module('portainer.edge').component('edgeJobForm', {
templateUrl: './edgeJobForm.html',
controller: EdgeJobFormController,
bindings: {
model: '=',
groups: '<',
tags: '<',
edgeGroups: '<',
addLabelAction: '<',
removeLabelAction: '<',
formAction: '<',
formActionLabel: '@',
actionInProgress: '<',
isEditorDirty: '=',
},
});