mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 23:35:31 +02:00
refactor(app-templates): convert list to react [EE-6205] (#10439)
This commit is contained in:
parent
1fa63f6ab7
commit
14129632a3
22 changed files with 393 additions and 351 deletions
|
@ -1,149 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
angular.module('portainer.app').controller('TemplateListController', TemplateListController);
|
||||
|
||||
function TemplateListController($scope, $async, $state, DatatableService, Notifications, TemplateService) {
|
||||
var ctrl = this;
|
||||
|
||||
this.state = {
|
||||
textFilter: '',
|
||||
selectedCategory: null,
|
||||
categories: [],
|
||||
typeFilters: [],
|
||||
filterByType: null,
|
||||
showContainerTemplates: true,
|
||||
selectedOrderBy: null,
|
||||
orderByFields: [],
|
||||
orderDesc: false,
|
||||
};
|
||||
|
||||
this.onTextFilterChange = function () {
|
||||
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
|
||||
};
|
||||
|
||||
ctrl.filterByTemplateType = function (item) {
|
||||
switch (item.Type) {
|
||||
case 1: // container
|
||||
return ctrl.state.showContainerTemplates;
|
||||
case 2: // swarm stack
|
||||
return ctrl.showSwarmStacks && !ctrl.state.showContainerTemplates;
|
||||
case 3: // docker compose
|
||||
return !ctrl.state.showContainerTemplates || null === ctrl.state.filterByType;
|
||||
case 4: // Edge stack templates
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
ctrl.updateCategories = function () {
|
||||
var availableCategories = [];
|
||||
|
||||
for (var i = 0; i < ctrl.templates.length; i++) {
|
||||
var template = ctrl.templates[i];
|
||||
if (ctrl.filterByTemplateType(template)) {
|
||||
availableCategories = availableCategories.concat(template.Categories);
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.state.categories = _.sortBy(_.uniq(availableCategories));
|
||||
};
|
||||
|
||||
ctrl.filterByCategory = function (item) {
|
||||
if (!ctrl.state.selectedCategory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return _.includes(item.Categories, ctrl.state.selectedCategory);
|
||||
};
|
||||
|
||||
this.duplicateTemplate = duplicateTemplate.bind(this);
|
||||
this.duplicateTemplateAsync = duplicateTemplateAsync.bind(this);
|
||||
function duplicateTemplate(template) {
|
||||
return $async(this.duplicateTemplateAsync, template);
|
||||
}
|
||||
|
||||
async function duplicateTemplateAsync(template) {
|
||||
try {
|
||||
const { FileContent: fileContent } = await TemplateService.templateFile(template.Repository.url, template.Repository.stackfile);
|
||||
let type = 0;
|
||||
if (template.Type === 2) {
|
||||
type = 1;
|
||||
}
|
||||
if (template.Type === 3) {
|
||||
type = 2;
|
||||
}
|
||||
$state.go('docker.templates.custom.new', { fileContent, type });
|
||||
} catch (err) {
|
||||
Notifications.error('Failure', err, 'Failed to duplicate template');
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.changeOrderBy = function (orderField) {
|
||||
$scope.$evalAsync(() => {
|
||||
if (null === orderField) {
|
||||
ctrl.state.selectedOrderBy = null;
|
||||
ctrl.templates = ctrl.initalTemplates;
|
||||
}
|
||||
|
||||
ctrl.state.selectedOrderBy = orderField;
|
||||
ctrl.templates = _.orderBy(ctrl.templates, [getSorter(ctrl.state.selectedOrderBy)], [ctrl.state.orderDesc ? 'desc' : 'asc']);
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.applyTypeFilter = function (type) {
|
||||
$scope.$evalAsync(() => {
|
||||
ctrl.state.filterByType = type;
|
||||
ctrl.state.showContainerTemplates = 'Container' === type || null === type;
|
||||
ctrl.updateCategories();
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.invertOrder = function () {
|
||||
$scope.$evalAsync(() => {
|
||||
ctrl.state.orderDesc = !ctrl.state.orderDesc;
|
||||
ctrl.templates = _.orderBy(ctrl.templates, [getSorter(ctrl.state.selectedOrderBy)], [ctrl.state.orderDesc ? 'desc' : 'asc']);
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.applyCategoriesFilter = function (category) {
|
||||
$scope.$evalAsync(() => {
|
||||
ctrl.state.selectedCategory = category;
|
||||
ctrl.updateCategories();
|
||||
});
|
||||
};
|
||||
|
||||
this.$onInit = function () {
|
||||
if (this.showSwarmStacks) {
|
||||
this.state.showContainerTemplates = false;
|
||||
}
|
||||
this.updateCategories();
|
||||
|
||||
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
|
||||
if (textFilter !== null) {
|
||||
this.state.textFilter = textFilter;
|
||||
}
|
||||
|
||||
this.initalTemplates = this.templates;
|
||||
this.state.orderByFields = ['Title', 'Categories', 'Description'];
|
||||
this.state.typeFilters = ['Container', 'Stack'];
|
||||
};
|
||||
|
||||
function categorySorter(template) {
|
||||
if (template.Categories && template.Categories.length > 0 && template.Categories[0] && template.Categories[0].length > 0) {
|
||||
return template.Categories[0].toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
function getSorter(orderBy) {
|
||||
let sorter;
|
||||
switch (orderBy) {
|
||||
case 'Categories':
|
||||
sorter = categorySorter;
|
||||
break;
|
||||
default:
|
||||
sorter = orderBy;
|
||||
}
|
||||
|
||||
return sorter;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
angular.module('portainer.app').component('templateList', {
|
||||
templateUrl: './templateList.html',
|
||||
controller: 'TemplateListController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
templates: '<',
|
||||
tableKey: '@',
|
||||
selectAction: '<',
|
||||
showSwarmStacks: '<',
|
||||
isSelected: '<',
|
||||
},
|
||||
});
|
|
@ -1,76 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle vertical-center">
|
||||
<div class="widget-icon space-right">
|
||||
<pr-icon icon="$ctrl.titleIcon"></pr-icon>
|
||||
</div>
|
||||
{{ $ctrl.titleText }}
|
||||
</div>
|
||||
<div class="searchBar vertical-center">
|
||||
<pr-icon icon="'search'" class-name="'searchIcon'"></pr-icon>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search for a template..."
|
||||
auto-focus
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
data-cy="template-searchInput"
|
||||
/>
|
||||
</div>
|
||||
<div class="actionBar !gap-3" ng-if="$ctrl.showAddAction">
|
||||
<button type="button" class="btn btn-sm btn-primary vertical-center !ml-0 h-fit" ui-sref="docker.templates.new" data-cy="template-addTemplateButton">
|
||||
<pr-icon icon="'plus'"></pr-icon>Add template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="actionBar">
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<template-list-dropdown
|
||||
options="$ctrl.state.categories"
|
||||
on-change="($ctrl.applyCategoriesFilter)"
|
||||
placeholder="'Category'"
|
||||
value="$ctrl.state.selectedCategory"
|
||||
></template-list-dropdown>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<template-list-dropdown
|
||||
options="$ctrl.state.typeFilters"
|
||||
on-change="($ctrl.applyTypeFilter)"
|
||||
placeholder="'Type'"
|
||||
value="$ctrl.state.filterByType"
|
||||
></template-list-dropdown>
|
||||
</div>
|
||||
<div class="col-sm-3 col-sm-offset-3">
|
||||
<template-list-sort
|
||||
on-change="($ctrl.changeOrderBy)"
|
||||
on-descending="($ctrl.invertOrder)"
|
||||
options="$ctrl.state.orderByFields"
|
||||
sort-by-button="true"
|
||||
sort-by-descending="$ctrl.state.orderDesc"
|
||||
placeholder="'Sort By'"
|
||||
value="$ctrl.state.selectedOrderBy"
|
||||
></template-list-sort>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="blocklist gap-y-2 !px-[20px] !pb-[20px]">
|
||||
<div ng-repeat="template in $ctrl.templates | filter: $ctrl.filterByTemplateType | filter:$ctrl.filterByCategory | filter:$ctrl.state.textFilter">
|
||||
<app-templates-list-item template="template" on-select="($ctrl.selectAction)" on-duplicate="($ctrl.duplicateTemplate)" is-selected="$ctrl.isSelected(template)">
|
||||
</app-templates-list-item>
|
||||
</div>
|
||||
<div ng-if="!$ctrl.templates" class="text-muted text-center"> Loading... </div>
|
||||
<div
|
||||
ng-if="($ctrl.templates | filter: $ctrl.filterByTemplateType | filter: $ctrl.filterByCategory | filter: $ctrl.state.textFilter).length === 0"
|
||||
class="text-muted text-center"
|
||||
>
|
||||
No templates available.
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -7,7 +7,6 @@ import { withControlledInput } from '@/react-tools/withControlledInput';
|
|||
import { CustomTemplatesListItem } from '@/react/portainer/templates/custom-templates/ListView/CustomTemplatesListItem';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { AppTemplatesListItem } from '@/react/portainer/templates/app-templates/AppTemplatesListItem';
|
||||
import {
|
||||
CommonFields,
|
||||
validation as commonFieldsValidation,
|
||||
|
@ -15,6 +14,7 @@ import {
|
|||
import { PlatformField } from '@/react/portainer/custom-templates/components/PlatformSelector';
|
||||
import { TemplateTypeSelector } from '@/react/portainer/custom-templates/components/TemplateTypeSelector';
|
||||
import { withFormValidation } from '@/react-tools/withFormValidation';
|
||||
import { AppTemplatesList } from '@/react/portainer/templates/app-templates/AppTemplatesList';
|
||||
|
||||
import { VariablesFieldAngular } from './variables-field';
|
||||
|
||||
|
@ -47,15 +47,7 @@ export const ngModule = angular
|
|||
'isSelected',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'appTemplatesListItem',
|
||||
r2a(withUIRouter(withCurrentUser(AppTemplatesListItem)), [
|
||||
'onSelect',
|
||||
'template',
|
||||
'isSelected',
|
||||
'onDuplicate',
|
||||
])
|
||||
)
|
||||
|
||||
.component(
|
||||
'customTemplatesPlatformSelector',
|
||||
r2a(PlatformField, ['onChange', 'value'])
|
||||
|
@ -63,6 +55,15 @@ export const ngModule = angular
|
|||
.component(
|
||||
'customTemplatesTypeSelector',
|
||||
r2a(TemplateTypeSelector, ['onChange', 'value'])
|
||||
)
|
||||
.component(
|
||||
'appTemplatesList',
|
||||
r2a(withUIRouter(withCurrentUser(AppTemplatesList)), [
|
||||
'onSelect',
|
||||
'templates',
|
||||
'selectedId',
|
||||
'showSwarmStacks',
|
||||
])
|
||||
);
|
||||
|
||||
withFormValidation(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { commandStringToArray } from '@/docker/helpers/containers';
|
||||
import { TemplateViewModel } from '@/react/portainer/templates/app-templates/template';
|
||||
import { TemplateViewModel } from '@/react/portainer/templates/app-templates/view-model';
|
||||
import { DockerHubViewModel } from 'Portainer/models/dockerhub';
|
||||
|
||||
angular.module('portainer.app').factory('TemplateService', TemplateServiceFactory);
|
||||
|
|
|
@ -270,18 +270,9 @@
|
|||
<!-- container-form -->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<template-list
|
||||
ng-if="templates"
|
||||
title-text="Templates"
|
||||
title-icon="edit"
|
||||
templates="templates"
|
||||
table-key="templates"
|
||||
select-action="selectTemplate"
|
||||
is-selected="isSelected"
|
||||
show-swarm-stacks="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER' && applicationState.endpoint.apiVersion >= 1.25"
|
||||
>
|
||||
</template-list>
|
||||
</div>
|
||||
</div>
|
||||
<app-templates-list
|
||||
templates="templates"
|
||||
on-select="(selectTemplate)"
|
||||
selected-id="state.selectedTemplate.Id"
|
||||
show-swarm-stacks="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER' && applicationState.endpoint.apiVersion >= 1.25"
|
||||
></app-templates-list>
|
||||
|
|
|
@ -224,10 +224,6 @@ angular.module('portainer.app').controller('TemplatesController', [
|
|||
}
|
||||
};
|
||||
|
||||
$scope.isSelected = function (template) {
|
||||
return $scope.state.selectedTemplate && $scope.state.selectedTemplate.Id === template.Id;
|
||||
};
|
||||
|
||||
$scope.unselectTemplate = function () {
|
||||
return $async(async () => {
|
||||
$scope.state.selectedTemplate = null;
|
||||
|
@ -237,7 +233,7 @@ angular.module('portainer.app').controller('TemplatesController', [
|
|||
$scope.selectTemplate = function (template) {
|
||||
return $async(async () => {
|
||||
if ($scope.state.selectedTemplate) {
|
||||
$scope.unselectTemplate($scope.state.selectedTemplate);
|
||||
await $scope.unselectTemplate($scope.state.selectedTemplate);
|
||||
}
|
||||
|
||||
if (template.Network) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue