mirror of https://github.com/portainer/portainer
refactor(app): convert tag-selector to react [EE-2983] (#6783)
parent
75d854e6ad
commit
d52417c14f
|
@ -3,6 +3,7 @@ import '../app/assets/css';
|
||||||
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
|
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
|
||||||
import { initialize as initMSW, mswDecorator } from 'msw-storybook-addon';
|
import { initialize as initMSW, mswDecorator } from 'msw-storybook-addon';
|
||||||
import { handlers } from '@/setup-tests/server-handlers';
|
import { handlers } from '@/setup-tests/server-handlers';
|
||||||
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||||
|
|
||||||
// Initialize MSW
|
// Initialize MSW
|
||||||
initMSW({
|
initMSW({
|
||||||
|
@ -31,11 +32,17 @@ export const parameters = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const testQueryClient = new QueryClient({
|
||||||
|
defaultOptions: { queries: { retry: false } },
|
||||||
|
});
|
||||||
|
|
||||||
export const decorators = [
|
export const decorators = [
|
||||||
(Story) => (
|
(Story) => (
|
||||||
|
<QueryClientProvider client={testQueryClient}>
|
||||||
<UIRouter plugins={[pushStateLocationPlugin]}>
|
<UIRouter plugins={[pushStateLocationPlugin]}>
|
||||||
<Story />
|
<Story />
|
||||||
</UIRouter>
|
</UIRouter>
|
||||||
|
</QueryClientProvider>
|
||||||
),
|
),
|
||||||
mswDecorator,
|
mswDecorator,
|
||||||
];
|
];
|
||||||
|
|
|
@ -807,13 +807,6 @@ json-tree .branch-preview {
|
||||||
}
|
}
|
||||||
/* !spinkit override */
|
/* !spinkit override */
|
||||||
|
|
||||||
/* uib-typeahead override */
|
|
||||||
#scrollable-dropdown-menu .dropdown-menu {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
/* !uib-typeahead override */
|
|
||||||
|
|
||||||
.kubectl-shell {
|
.kubectl-shell {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -93,12 +93,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<tag-selector ng-if="$ctrl.tags.length" tags="$ctrl.tags" model="$ctrl.model.TagIds"></tag-selector>
|
<tag-selector ng-if="$ctrl.model.TagIds" value="$ctrl.model.TagIds" on-change="($ctrl.onChangeTags)"> </tag-selector>
|
||||||
<div ng-if="$ctrl.tags && !$ctrl.tags.length" class="col-sm-12 small text-muted">
|
|
||||||
No tags available. Head over to the <a ui-sref="portainer.tags">Tags view</a> to add tags
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-12 form-section-title"> Associated environments by tags </div>
|
<div class="col-sm-12 form-section-title"> Associated environments by tags </div>
|
||||||
<div class="col-sm-12 form-group">
|
<div class="col-sm-12 form-group">
|
||||||
<group-association-table
|
<group-association-table
|
||||||
|
|
|
@ -6,6 +6,7 @@ export class EdgeGroupFormController {
|
||||||
constructor(EndpointService, $async, $scope) {
|
constructor(EndpointService, $async, $scope) {
|
||||||
this.EndpointService = EndpointService;
|
this.EndpointService = EndpointService;
|
||||||
this.$async = $async;
|
this.$async = $async;
|
||||||
|
this.$scope = $scope;
|
||||||
|
|
||||||
this.endpoints = {
|
this.endpoints = {
|
||||||
state: {
|
state: {
|
||||||
|
@ -22,6 +23,7 @@ export class EdgeGroupFormController {
|
||||||
this.dissociateEndpoint = this.dissociateEndpoint.bind(this);
|
this.dissociateEndpoint = this.dissociateEndpoint.bind(this);
|
||||||
this.getDynamicEndpointsAsync = this.getDynamicEndpointsAsync.bind(this);
|
this.getDynamicEndpointsAsync = this.getDynamicEndpointsAsync.bind(this);
|
||||||
this.getDynamicEndpoints = this.getDynamicEndpoints.bind(this);
|
this.getDynamicEndpoints = this.getDynamicEndpoints.bind(this);
|
||||||
|
this.onChangeTags = this.onChangeTags.bind(this);
|
||||||
|
|
||||||
$scope.$watch(
|
$scope.$watch(
|
||||||
() => this.model,
|
() => this.model,
|
||||||
|
@ -34,6 +36,12 @@ export class EdgeGroupFormController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChangeTags(value) {
|
||||||
|
return this.$scope.$evalAsync(() => {
|
||||||
|
this.model.TagIds = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
associateEndpoint(endpoint) {
|
associateEndpoint(endpoint) {
|
||||||
if (!_.includes(this.model.Endpoints, endpoint.Id)) {
|
if (!_.includes(this.model.Endpoints, endpoint.Id)) {
|
||||||
this.model.Endpoints = [...this.model.Endpoints, endpoint.Id];
|
this.model.Endpoints = [...this.model.Endpoints, endpoint.Id];
|
||||||
|
|
|
@ -8,7 +8,6 @@ angular.module('portainer.edge').component('edgeGroupForm', {
|
||||||
bindings: {
|
bindings: {
|
||||||
model: '<',
|
model: '<',
|
||||||
groups: '<',
|
groups: '<',
|
||||||
tags: '<',
|
|
||||||
formActionLabel: '@',
|
formActionLabel: '@',
|
||||||
formAction: '<',
|
formAction: '<',
|
||||||
actionInProgress: '<',
|
actionInProgress: '<',
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
form-action-label="Add edge group"
|
form-action-label="Add edge group"
|
||||||
form-action="$ctrl.createGroup"
|
form-action="$ctrl.createGroup"
|
||||||
groups="$ctrl.endpointGroups"
|
groups="$ctrl.endpointGroups"
|
||||||
tags="$ctrl.tags"
|
|
||||||
model="$ctrl.model"
|
model="$ctrl.model"
|
||||||
on-change-tags="($ctrl.onChangeTags)"
|
|
||||||
></edge-group-form>
|
></edge-group-form>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
export class CreateEdgeGroupController {
|
export class CreateEdgeGroupController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(EdgeGroupService, GroupService, TagService, Notifications, $state, $async) {
|
constructor(EdgeGroupService, GroupService, Notifications, $state, $async) {
|
||||||
this.EdgeGroupService = EdgeGroupService;
|
this.EdgeGroupService = EdgeGroupService;
|
||||||
this.GroupService = GroupService;
|
this.GroupService = GroupService;
|
||||||
this.TagService = TagService;
|
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$async = $async;
|
this.$async = $async;
|
||||||
|
@ -26,8 +25,8 @@ export class CreateEdgeGroupController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async $onInit() {
|
async $onInit() {
|
||||||
const [tags, endpointGroups] = await Promise.all([this.TagService.tags(), this.GroupService.groups()]);
|
const endpointGroups = await this.GroupService.groups();
|
||||||
this.tags = tags;
|
|
||||||
this.endpointGroups = endpointGroups;
|
this.endpointGroups = endpointGroups;
|
||||||
this.state.loaded = true;
|
this.state.loaded = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
form-action="$ctrl.updateGroup"
|
form-action="$ctrl.updateGroup"
|
||||||
endpoints="$ctrl.endpoints"
|
endpoints="$ctrl.endpoints"
|
||||||
groups="$ctrl.endpointGroups"
|
groups="$ctrl.endpointGroups"
|
||||||
tags="$ctrl.tags"
|
|
||||||
model="$ctrl.model"
|
model="$ctrl.model"
|
||||||
></edge-group-form>
|
></edge-group-form>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
export class EditEdgeGroupController {
|
export class EditEdgeGroupController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(EdgeGroupService, GroupService, TagService, Notifications, $state, $async) {
|
constructor(EdgeGroupService, GroupService, Notifications, $state, $async) {
|
||||||
this.EdgeGroupService = EdgeGroupService;
|
this.EdgeGroupService = EdgeGroupService;
|
||||||
this.GroupService = GroupService;
|
this.GroupService = GroupService;
|
||||||
this.TagService = TagService;
|
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$async = $async;
|
this.$async = $async;
|
||||||
|
@ -18,13 +17,12 @@ export class EditEdgeGroupController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async $onInit() {
|
async $onInit() {
|
||||||
const [tags, endpointGroups, group] = await Promise.all([this.TagService.tags(), this.GroupService.groups(), this.EdgeGroupService.group(this.$state.params.groupId)]);
|
const [endpointGroups, group] = await Promise.all([this.GroupService.groups(), this.EdgeGroupService.group(this.$state.params.groupId)]);
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
this.Notifications.error('Failed to find edge group', {});
|
this.Notifications.error('Failed to find edge group', {});
|
||||||
this.$state.go('edge.groups');
|
this.$state.go('edge.groups');
|
||||||
}
|
}
|
||||||
this.tags = tags;
|
|
||||||
this.endpointGroups = endpointGroups;
|
this.endpointGroups = endpointGroups;
|
||||||
this.model = group;
|
this.model = group;
|
||||||
this.state.loaded = true;
|
this.state.loaded = true;
|
||||||
|
|
|
@ -9,13 +9,11 @@ angular.module('portainer.app').component('groupForm', {
|
||||||
pageType: '@',
|
pageType: '@',
|
||||||
model: '=',
|
model: '=',
|
||||||
availableEndpoints: '=',
|
availableEndpoints: '=',
|
||||||
availableTags: '<',
|
|
||||||
associatedEndpoints: '=',
|
associatedEndpoints: '=',
|
||||||
addLabelAction: '<',
|
addLabelAction: '<',
|
||||||
removeLabelAction: '<',
|
removeLabelAction: '<',
|
||||||
formAction: '<',
|
formAction: '<',
|
||||||
formActionLabel: '@',
|
formActionLabel: '@',
|
||||||
actionInProgress: '<',
|
actionInProgress: '<',
|
||||||
onCreateTag: '<',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,17 +23,9 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- !description-input -->
|
<!-- !description-input -->
|
||||||
<div class="col-sm-12 form-section-title"> Metadata </div>
|
<div class="col-sm-12 form-section-title"> Metadata </div>
|
||||||
<!-- tags -->
|
|
||||||
<div class="form-group">
|
<tag-selector ng-if="$ctrl.model.TagIds" value="$ctrl.model.TagIds" on-change="($ctrl.onChangeTags)" allow-create="$ctrl.state.allowCreateTag"> </tag-selector>
|
||||||
<tag-selector
|
|
||||||
ng-if="$ctrl.model && $ctrl.availableTags"
|
|
||||||
tags="$ctrl.availableTags"
|
|
||||||
model="$ctrl.model.TagIds"
|
|
||||||
allow-create="$ctrl.state.allowCreateTag"
|
|
||||||
on-create="($ctrl.onCreateTag)"
|
|
||||||
></tag-selector>
|
|
||||||
</div>
|
|
||||||
<!-- !tags -->
|
|
||||||
<!-- environments -->
|
<!-- environments -->
|
||||||
<div ng-if="$ctrl.model.Id !== 1">
|
<div ng-if="$ctrl.model.Id !== 1">
|
||||||
<div class="col-sm-12 form-section-title"> Associated environments </div>
|
<div class="col-sm-12 form-section-title"> Associated environments </div>
|
||||||
|
|
|
@ -3,8 +3,9 @@ import angular from 'angular';
|
||||||
|
|
||||||
class GroupFormController {
|
class GroupFormController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($q, EndpointService, GroupService, Notifications, Authentication) {
|
constructor($q, $scope, EndpointService, GroupService, Notifications, Authentication) {
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
|
this.$scope = $scope;
|
||||||
this.EndpointService = EndpointService;
|
this.EndpointService = EndpointService;
|
||||||
this.GroupService = GroupService;
|
this.GroupService = GroupService;
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
|
@ -13,6 +14,13 @@ class GroupFormController {
|
||||||
this.associateEndpoint = this.associateEndpoint.bind(this);
|
this.associateEndpoint = this.associateEndpoint.bind(this);
|
||||||
this.dissociateEndpoint = this.dissociateEndpoint.bind(this);
|
this.dissociateEndpoint = this.dissociateEndpoint.bind(this);
|
||||||
this.getPaginatedEndpointsByGroup = this.getPaginatedEndpointsByGroup.bind(this);
|
this.getPaginatedEndpointsByGroup = this.getPaginatedEndpointsByGroup.bind(this);
|
||||||
|
this.onChangeTags = this.onChangeTags.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeTags(value) {
|
||||||
|
return this.$scope.$evalAsync(() => {
|
||||||
|
this.model.TagIds = value;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
angular.module('portainer.app').component('tagSelector', {
|
|
||||||
templateUrl: './tagSelector.html',
|
|
||||||
controller: 'TagSelectorController',
|
|
||||||
bindings: {
|
|
||||||
tags: '<',
|
|
||||||
model: '=',
|
|
||||||
onCreate: '<',
|
|
||||||
allowCreate: '<',
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,35 +0,0 @@
|
||||||
<div ng-show="$ctrl.model.length > 0" class="col-sm-12" style="padding: 0; margin-bottom: 15px">
|
|
||||||
<label class="col-sm-3 col-lg-2 control-label text-left"> Selected tags </label>
|
|
||||||
<div class="col-sm-9 col-lg-10" style="padding-top: 4px">
|
|
||||||
<span class="tag space-right interactive" ng-repeat="tag in $ctrl.state.selectedTags" ng-click="$ctrl.removeTag(tag)">
|
|
||||||
{{ tag.Name }}
|
|
||||||
<a title="Remove tag" ng-click="$ctrl.removeTag(tag)" style="margin-left: 2px">
|
|
||||||
<span class="fa fa-trash-alt white-icon" aria-hidden="true"></span>
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-12" style="padding: 0">
|
|
||||||
<label for="tags" class="col-sm-3 col-lg-2 control-label text-left"> Tags </label>
|
|
||||||
<div class="col-sm-9 col-lg-10" id="scrollable-dropdown-menu" ng-if="$ctrl.allowCreate || $ctrl.tags.length > 0">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
ng-model="$ctrl.state.selectedValue"
|
|
||||||
id="tags"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Select tags..."
|
|
||||||
uib-typeahead="tag.Id as tag.Name for tag in $ctrl.filterTags($viewValue)"
|
|
||||||
typeahead-on-select="$ctrl.selectTag($item, $model, $label)"
|
|
||||||
typeahead-no-results="$ctrl.state.noResult"
|
|
||||||
typeahead-show-hint="true"
|
|
||||||
typeahead-min-length="0"
|
|
||||||
data-cy="tags-tagInput"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-9 col-lg-10" ng-if="!$ctrl.allowCreate && $ctrl.tags.length === 0">
|
|
||||||
<span class="small text-muted"> No tags available. </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-offset-3 col-lg-offset-2 col-sm-12" ng-if="!$ctrl.allowCreate && $ctrl.state.noResult" style="margin-top: 2px">
|
|
||||||
<span class="small text-muted"> No tags matching your filter. </span>
|
|
||||||
</div>
|
|
|
@ -1,62 +0,0 @@
|
||||||
import angular from 'angular';
|
|
||||||
import _ from 'lodash-es';
|
|
||||||
|
|
||||||
class TagSelectorController {
|
|
||||||
/* @ngInject */
|
|
||||||
constructor() {
|
|
||||||
this.state = {
|
|
||||||
selectedValue: '',
|
|
||||||
selectedTags: [],
|
|
||||||
noResult: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTag(tag) {
|
|
||||||
_.remove(this.model, (id) => tag.Id === id);
|
|
||||||
_.remove(this.state.selectedTags, { Id: tag.Id });
|
|
||||||
}
|
|
||||||
|
|
||||||
selectTag($item) {
|
|
||||||
this.state.selectedValue = '';
|
|
||||||
if ($item.create && this.allowCreate) {
|
|
||||||
this.onCreate($item.value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.state.selectedTags.push($item);
|
|
||||||
this.model.push($item.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
filterTags(searchValue) {
|
|
||||||
let filteredTags = _.filter(this.tags, (tag) => !_.includes(this.model, tag.Id));
|
|
||||||
if (!searchValue) {
|
|
||||||
return filteredTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
const exactTag = _.find(this.tags, (tag) => tag.Name === searchValue);
|
|
||||||
filteredTags = _.filter(filteredTags, (tag) => _.includes(tag.Name.toLowerCase(), searchValue.toLowerCase()));
|
|
||||||
if (exactTag || !this.allowCreate) {
|
|
||||||
return filteredTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredTags.concat({ Name: `Create "${searchValue}"`, create: true, value: searchValue });
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSelectedTags(model, tags) {
|
|
||||||
this.state.selectedTags = _.map(model, (id) => _.find(tags, (t) => t.Id === id));
|
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
this.generateSelectedTags(this.model, this.tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
$onChanges({ tags, model }) {
|
|
||||||
const tagsValue = tags && tags.currentValue ? tags.currentValue : this.tags;
|
|
||||||
const modelValue = model && model.currentValue ? model.currentValue : this.model;
|
|
||||||
if (modelValue && tagsValue) {
|
|
||||||
this.generateSelectedTags(modelValue, tagsValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TagSelectorController;
|
|
||||||
angular.module('portainer.app').controller('TagSelectorController', TagSelectorController);
|
|
|
@ -1,6 +1,11 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
export const componentsModule = angular.module(
|
import { r2a } from '@/react-tools/react2angular';
|
||||||
'portainer.app.react.components',
|
import { TagSelector } from '@/react/components/TagSelector';
|
||||||
[]
|
|
||||||
).name;
|
export const componentsModule = angular
|
||||||
|
.module('portainer.app.react.components', [])
|
||||||
|
.component(
|
||||||
|
'tagSelector',
|
||||||
|
r2a(TagSelector, ['allowCreate', 'onChange', 'value'])
|
||||||
|
).name;
|
||||||
|
|
|
@ -13,7 +13,7 @@ export async function getTags() {
|
||||||
|
|
||||||
export async function createTag(name: string) {
|
export async function createTag(name: string) {
|
||||||
try {
|
try {
|
||||||
const { data: tag } = await axios.post(buildUrl(), { name });
|
const { data: tag } = await axios.post<Tag>(buildUrl(), { name });
|
||||||
return tag;
|
return tag;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw parseAxiosError(err as Error, 'Unable to create tag');
|
throw parseAxiosError(err as Error, 'Unable to create tag');
|
||||||
|
|
|
@ -188,12 +188,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !group -->
|
<!-- !group -->
|
||||||
<!-- tags -->
|
|
||||||
<div class="form-group">
|
<tag-selector ng-if="formValues" value="formValues.TagIds" allow-create="state.allowCreateTag" on-change="(onChangeTags)"> </tag-selector>
|
||||||
<tag-selector ng-if="formValues && availableTags" tags="availableTags" model="formValues.TagIds" allow-create="state.allowCreateTag" on-create="(onCreateTag)">
|
|
||||||
</tag-selector>
|
|
||||||
</div>
|
|
||||||
<!-- !tags -->
|
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
|
|
@ -28,6 +28,12 @@ angular
|
||||||
|
|
||||||
$scope.profiles = [];
|
$scope.profiles = [];
|
||||||
|
|
||||||
|
$scope.onChangeTags = function onChangeTags(value) {
|
||||||
|
return $scope.$evalAsync(() => {
|
||||||
|
$scope.formValues.TagIds = value;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.onVoucherFilesChange = function () {
|
$scope.onVoucherFilesChange = function () {
|
||||||
if ($scope.formValues.VoucherFiles.length < 1) {
|
if ($scope.formValues.VoucherFiles.length < 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -53,20 +59,6 @@ angular
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
|
||||||
return $async(onCreateTagAsync, tagName);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function onCreateTagAsync(tagName) {
|
|
||||||
try {
|
|
||||||
const tag = await TagService.createTag(tagName);
|
|
||||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
|
||||||
$scope.formValues.TagIds = $scope.formValues.TagIds.concat(tag.Id);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error('Failure', err, 'Unable to create tag');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.createEndpointAndConfigureDevice = function () {
|
$scope.createEndpointAndConfigureDevice = function () {
|
||||||
return $async(async () => {
|
return $async(async () => {
|
||||||
$scope.state.actionInProgress = true;
|
$scope.state.actionInProgress = true;
|
||||||
|
@ -133,11 +125,9 @@ angular
|
||||||
|
|
||||||
$q.all({
|
$q.all({
|
||||||
groups: GroupService.groups(),
|
groups: GroupService.groups(),
|
||||||
tags: TagService.tags(),
|
|
||||||
})
|
})
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
$scope.groups = data.groups;
|
$scope.groups = data.groups;
|
||||||
$scope.availableTags = data.tags;
|
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to load groups');
|
Notifications.error('Failure', err, 'Unable to load groups');
|
||||||
|
|
|
@ -17,7 +17,6 @@ angular
|
||||||
clipboard,
|
clipboard,
|
||||||
EndpointService,
|
EndpointService,
|
||||||
GroupService,
|
GroupService,
|
||||||
TagService,
|
|
||||||
SettingsService,
|
SettingsService,
|
||||||
Notifications,
|
Notifications,
|
||||||
Authentication,
|
Authentication,
|
||||||
|
@ -91,20 +90,12 @@ angular
|
||||||
$scope.formValues.URL = '';
|
$scope.formValues.URL = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
$scope.onChangeTags = function onChangeTags(value) {
|
||||||
return $async(onCreateTagAsync, tagName);
|
return $scope.$evalAsync(() => {
|
||||||
|
$scope.formValues.TagIds = value;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function onCreateTagAsync(tagName) {
|
|
||||||
try {
|
|
||||||
const tag = await TagService.createTag(tagName);
|
|
||||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
|
||||||
$scope.formValues.TagIds = $scope.formValues.TagIds.concat(tag.Id);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error('Failure', err, 'Unable to create tag');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChangeCheckInInterval(value) {
|
function onChangeCheckInInterval(value) {
|
||||||
setFieldValue('EdgeCheckinInterval', value);
|
setFieldValue('EdgeCheckinInterval', value);
|
||||||
}
|
}
|
||||||
|
@ -310,12 +301,10 @@ angular
|
||||||
function initView() {
|
function initView() {
|
||||||
$q.all({
|
$q.all({
|
||||||
groups: GroupService.groups(),
|
groups: GroupService.groups(),
|
||||||
tags: TagService.tags(),
|
|
||||||
settings: SettingsService.settings(),
|
settings: SettingsService.settings(),
|
||||||
})
|
})
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
$scope.groups = data.groups;
|
$scope.groups = data.groups;
|
||||||
$scope.availableTags = data.tags;
|
|
||||||
|
|
||||||
const settings = data.settings;
|
const settings = data.settings;
|
||||||
|
|
||||||
|
|
|
@ -436,12 +436,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !group -->
|
<!-- !group -->
|
||||||
<!-- tags -->
|
|
||||||
<div class="form-group">
|
<tag-selector ng-if="formValues" value="formValues.TagIds" allow-create="state.allowCreateTag" on-change="(onChangeTags)"> </tag-selector>
|
||||||
<tag-selector ng-if="formValues && availableTags" tags="availableTags" model="formValues.TagIds" allow-create="state.allowCreateTag" on-create="(onCreateTag)">
|
|
||||||
</tag-selector>
|
|
||||||
</div>
|
|
||||||
<!-- !tags -->
|
|
||||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<div class="col-sm-12 form-section-title" style="margin-top: 25px"> Join token </div>
|
<div class="col-sm-12 form-section-title" style="margin-top: 25px"> Join token </div>
|
||||||
<p>
|
<p>
|
||||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
||||||
For those prestaging the edge agent, use the following join token to associate the Edge agent with this environment.
|
For those pre-staging the edge agent, use the following join token to associate the Edge agent with this environment.
|
||||||
</p>
|
</p>
|
||||||
<p> You can read more about pre-staging in the userguide available <a href="https://downloads.portainer.io/edge_agent_guide.pdf">here.</a> </p>
|
<p> You can read more about pre-staging in the userguide available <a href="https://downloads.portainer.io/edge_agent_guide.pdf">here.</a> </p>
|
||||||
<div style="margin-top: 10px; overflow-wrap: break-word">
|
<div style="margin-top: 10px; overflow-wrap: break-word">
|
||||||
|
@ -159,11 +159,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !group -->
|
<!-- !group -->
|
||||||
<!-- tags -->
|
|
||||||
<div class="form-group">
|
<tag-selector ng-if="endpoint" value="endpoint.TagIds" allow-create="state.allowCreate" on-change="(onChangeTags)"></tag-selector>
|
||||||
<tag-selector ng-if="endpoint && availableTags" tags="availableTags" model="endpoint.TagIds" on-create="(onCreateTag)" allow-create="state.allowCreate"></tag-selector>
|
|
||||||
</div>
|
|
||||||
<!-- !tags -->
|
|
||||||
<!-- endpoint-security -->
|
<!-- endpoint-security -->
|
||||||
<div ng-if="endpointType === 'remote' && !state.azureEndpoint && !state.kubernetesEndpoint && !state.edgeEndpoint && endpoint.Type !== 6">
|
<div ng-if="endpointType === 'remote' && !state.azureEndpoint && !state.kubernetesEndpoint && !state.edgeEndpoint && endpoint.Type !== 6">
|
||||||
<div class="col-sm-12 form-section-title"> Security </div>
|
<div class="col-sm-12 form-section-title"> Security </div>
|
||||||
|
|
|
@ -20,7 +20,7 @@ function EndpointController(
|
||||||
clipboard,
|
clipboard,
|
||||||
EndpointService,
|
EndpointService,
|
||||||
GroupService,
|
GroupService,
|
||||||
TagService,
|
|
||||||
Notifications,
|
Notifications,
|
||||||
Authentication,
|
Authentication,
|
||||||
SettingsService,
|
SettingsService,
|
||||||
|
@ -28,6 +28,7 @@ function EndpointController(
|
||||||
) {
|
) {
|
||||||
$scope.onChangeCheckInInterval = onChangeCheckInInterval;
|
$scope.onChangeCheckInInterval = onChangeCheckInInterval;
|
||||||
$scope.setFieldValue = setFieldValue;
|
$scope.setFieldValue = setFieldValue;
|
||||||
|
$scope.onChangeTags = onChangeTags;
|
||||||
|
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
uploadInProgress: false,
|
uploadInProgress: false,
|
||||||
|
@ -51,26 +52,12 @@ function EndpointController(
|
||||||
$('#copyNotificationEdgeKey').show().fadeOut(2500);
|
$('#copyNotificationEdgeKey').show().fadeOut(2500);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
|
||||||
return $async(onCreateTagAsync, tagName);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.onToggleAllowSelfSignedCerts = function onToggleAllowSelfSignedCerts(checked) {
|
$scope.onToggleAllowSelfSignedCerts = function onToggleAllowSelfSignedCerts(checked) {
|
||||||
return $scope.$evalAsync(() => {
|
return $scope.$evalAsync(() => {
|
||||||
$scope.state.allowSelfSignedCerts = checked;
|
$scope.state.allowSelfSignedCerts = checked;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function onCreateTagAsync(tagName) {
|
|
||||||
try {
|
|
||||||
const tag = await TagService.createTag(tagName);
|
|
||||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
|
||||||
$scope.endpoint.TagIds = $scope.endpoint.TagIds.concat(tag.Id);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error('Failue', err, 'Unable to create tag');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.onDisassociateEndpoint = async function () {
|
$scope.onDisassociateEndpoint = async function () {
|
||||||
ModalService.confirmDisassociate((confirmed) => {
|
ModalService.confirmDisassociate((confirmed) => {
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
|
@ -98,6 +85,10 @@ function EndpointController(
|
||||||
setFieldValue('EdgeCheckinInterval', value);
|
setFieldValue('EdgeCheckinInterval', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChangeTags(value) {
|
||||||
|
setFieldValue('TagIds', value);
|
||||||
|
}
|
||||||
|
|
||||||
function setFieldValue(name, value) {
|
function setFieldValue(name, value) {
|
||||||
return $scope.$evalAsync(() => {
|
return $scope.$evalAsync(() => {
|
||||||
$scope.endpoint = {
|
$scope.endpoint = {
|
||||||
|
@ -229,12 +220,7 @@ function EndpointController(
|
||||||
async function initView() {
|
async function initView() {
|
||||||
return $async(async () => {
|
return $async(async () => {
|
||||||
try {
|
try {
|
||||||
const [endpoint, groups, tags, settings] = await Promise.all([
|
const [endpoint, groups, settings] = await Promise.all([EndpointService.endpoint($transition$.params().id), GroupService.groups(), SettingsService.settings()]);
|
||||||
EndpointService.endpoint($transition$.params().id),
|
|
||||||
GroupService.groups(),
|
|
||||||
TagService.tags(),
|
|
||||||
SettingsService.settings(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (endpoint.URL.indexOf('unix://') === 0 || endpoint.URL.indexOf('npipe://') === 0) {
|
if (endpoint.URL.indexOf('unix://') === 0 || endpoint.URL.indexOf('npipe://') === 0) {
|
||||||
$scope.endpointType = 'local';
|
$scope.endpointType = 'local';
|
||||||
|
@ -254,7 +240,6 @@ function EndpointController(
|
||||||
$scope.endpoint = endpoint;
|
$scope.endpoint = endpoint;
|
||||||
$scope.initialTagIds = endpoint.TagIds.slice();
|
$scope.initialTagIds = endpoint.TagIds.slice();
|
||||||
$scope.groups = groups;
|
$scope.groups = groups;
|
||||||
$scope.availableTags = tags;
|
|
||||||
|
|
||||||
configureState();
|
configureState();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { EndpointGroupDefaultModel } from '../../../models/group';
|
import { EndpointGroupDefaultModel } from '../../../models/group';
|
||||||
|
|
||||||
angular.module('portainer.app').controller('CreateGroupController', function CreateGroupController($async, $scope, $state, GroupService, TagService, Notifications) {
|
angular.module('portainer.app').controller('CreateGroupController', function CreateGroupController($async, $scope, $state, GroupService, Notifications) {
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
actionInProgress: false,
|
actionInProgress: false,
|
||||||
};
|
};
|
||||||
|
@ -28,31 +28,10 @@ angular.module('portainer.app').controller('CreateGroupController', function Cre
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
|
||||||
return $async(onCreateTagAsync, tagName);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function onCreateTagAsync(tagName) {
|
|
||||||
try {
|
|
||||||
const tag = await TagService.createTag(tagName);
|
|
||||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
|
||||||
$scope.model.TagIds = $scope.model.TagIds.concat(tag.Id);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error('Failue', err, 'Unable to create tag');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initView() {
|
function initView() {
|
||||||
TagService.tags()
|
|
||||||
.then((tags) => {
|
|
||||||
$scope.availableTags = tags;
|
|
||||||
$scope.associatedEndpoints = [];
|
$scope.associatedEndpoints = [];
|
||||||
$scope.model = new EndpointGroupDefaultModel();
|
$scope.model = new EndpointGroupDefaultModel();
|
||||||
$scope.loaded = true;
|
$scope.loaded = true;
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
Notifications.error('Failure', err, 'Unable to retrieve tags');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initView();
|
initView();
|
||||||
|
|
|
@ -12,14 +12,12 @@
|
||||||
page-type="create"
|
page-type="create"
|
||||||
model="model"
|
model="model"
|
||||||
available-endpoints="availableEndpoints"
|
available-endpoints="availableEndpoints"
|
||||||
available-tags="availableTags"
|
|
||||||
associated-endpoints="associatedEndpoints"
|
associated-endpoints="associatedEndpoints"
|
||||||
add-label-action="addLabel"
|
add-label-action="addLabel"
|
||||||
remove-label-action="removeLabel"
|
remove-label-action="removeLabel"
|
||||||
form-action="create"
|
form-action="create"
|
||||||
form-action-label="Create the group"
|
form-action-label="Create the group"
|
||||||
action-in-progress="state.actionInProgress"
|
action-in-progress="state.actionInProgress"
|
||||||
on-create-tag="(onCreateTag)"
|
|
||||||
></group-form>
|
></group-form>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
|
|
|
@ -12,14 +12,12 @@
|
||||||
page-type="edit"
|
page-type="edit"
|
||||||
model="group"
|
model="group"
|
||||||
available-endpoints="availableEndpoints"
|
available-endpoints="availableEndpoints"
|
||||||
available-tags="availableTags"
|
|
||||||
associated-endpoints="associatedEndpoints"
|
associated-endpoints="associatedEndpoints"
|
||||||
add-label-action="addLabel"
|
add-label-action="addLabel"
|
||||||
remove-label-action="removeLabel"
|
remove-label-action="removeLabel"
|
||||||
form-action="update"
|
form-action="update"
|
||||||
form-action-label="Update the group"
|
form-action-label="Update the group"
|
||||||
action-in-progress="state.actionInProgress"
|
action-in-progress="state.actionInProgress"
|
||||||
on-create-tag="(onCreateTag)"
|
|
||||||
></group-form>
|
></group-form>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
angular.module('portainer.app').controller('GroupController', function GroupController($q, $async, $scope, $state, $transition$, GroupService, TagService, Notifications) {
|
angular.module('portainer.app').controller('GroupController', function GroupController($q, $scope, $state, $transition$, GroupService, Notifications) {
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
actionInProgress: false,
|
actionInProgress: false,
|
||||||
};
|
};
|
||||||
|
@ -20,30 +20,14 @@ angular.module('portainer.app').controller('GroupController', function GroupCont
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
|
||||||
return $async(onCreateTagAsync, tagName);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function onCreateTagAsync(tagName) {
|
|
||||||
try {
|
|
||||||
const tag = await TagService.createTag(tagName);
|
|
||||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
|
||||||
$scope.group.TagIds = $scope.group.TagIds.concat(tag.Id);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error('Failue', err, 'Unable to create tag');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initView() {
|
function initView() {
|
||||||
var groupId = $transition$.params().id;
|
var groupId = $transition$.params().id;
|
||||||
|
|
||||||
$q.all({
|
$q.all({
|
||||||
group: GroupService.group(groupId),
|
group: GroupService.group(groupId),
|
||||||
tags: TagService.tags(),
|
|
||||||
})
|
})
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
$scope.group = data.group;
|
$scope.group = data.group;
|
||||||
$scope.availableTags = data.tags;
|
|
||||||
$scope.loaded = true;
|
$scope.loaded = true;
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
|
|
|
@ -62,18 +62,19 @@ export function TagSelector({ value, allowCreate = false, onChange }: Props) {
|
||||||
{value.length > 0 && (
|
{value.length > 0 && (
|
||||||
<FormControl label="Selected tags">
|
<FormControl label="Selected tags">
|
||||||
{selectedTags.map((tag) => (
|
{selectedTags.map((tag) => (
|
||||||
<span className="tag space-right interactive" key={tag.value}>
|
|
||||||
{tag.label}
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
title="Remove tag"
|
title="Remove tag"
|
||||||
className={clsx(styles.removeTagBtn, 'space-left')}
|
className={clsx(styles.removeTagBtn, 'space-left', 'tag')}
|
||||||
onClick={() => handleRemove(tag.value)}
|
onClick={() => handleRemove(tag.value)}
|
||||||
|
key={tag.value}
|
||||||
>
|
>
|
||||||
<i className="fa fa-trash-alt white-icon" aria-hidden="true" />
|
{tag.label}
|
||||||
|
<i
|
||||||
|
className="fa fa-trash-alt white-icon space-left"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
|
||||||
))}
|
))}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -12,6 +12,11 @@ import { createMockTeams, createMockUsers } from '../react-tools/test-mocks';
|
||||||
|
|
||||||
import { azureHandlers } from './setup-handlers/azure';
|
import { azureHandlers } from './setup-handlers/azure';
|
||||||
|
|
||||||
|
const tags: Tag[] = [
|
||||||
|
{ ID: 1, Name: 'tag1' },
|
||||||
|
{ ID: 2, Name: 'tag2' },
|
||||||
|
];
|
||||||
|
|
||||||
const licenseInfo: LicenseInfo = {
|
const licenseInfo: LicenseInfo = {
|
||||||
nodes: 1000,
|
nodes: 1000,
|
||||||
type: LicenseType.Subscription,
|
type: LicenseType.Subscription,
|
||||||
|
@ -48,11 +53,11 @@ export const handlers = [
|
||||||
};
|
};
|
||||||
return res(ctx.json(group));
|
return res(ctx.json(group));
|
||||||
}),
|
}),
|
||||||
rest.get('/api/tags', (req, res, ctx) => {
|
rest.get('/api/tags', (req, res, ctx) => res(ctx.json(tags))),
|
||||||
const tags: Tag[] = [
|
rest.post<{ name: string }>('/api/tags', (req, res, ctx) => {
|
||||||
{ ID: 1, Name: 'tag1' },
|
const tagName = req.body.name;
|
||||||
{ ID: 2, Name: 'tag2' },
|
const tag = { ID: tags.length + 1, Name: tagName };
|
||||||
];
|
tags.push(tag);
|
||||||
return res(ctx.json(tags));
|
return res(ctx.json(tag));
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in New Issue