1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

refactor(apps): migrate applications view to react [r8s-124] (#28)

This commit is contained in:
Ali 2024-10-25 12:28:05 +13:00 committed by GitHub
parent cc75167437
commit 959c527be7
42 changed files with 1378 additions and 1293 deletions

View file

@ -207,7 +207,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
const applications = {
name: 'kubernetes.applications',
url: '/applications',
url: '/applications?tab',
views: {
'content@': {
component: 'kubernetesApplicationsView',
@ -233,7 +233,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
const application = {
name: 'kubernetes.applications.application',
url: '/:namespace/:name?resource-type&tab',
url: '/:namespace/:name?resource-type',
views: {
'content@': {
component: 'applicationDetailsView',

View file

@ -1,21 +0,0 @@
import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { ApplicationsDatatable } from '@/react/kubernetes/applications/ListView/ApplicationsDatatable/ApplicationsDatatable';
export const applicationsModule = angular
.module('portainer.kubernetes.react.components.applications', [])
.component(
'kubernetesApplicationsDatatable',
r2a(withUIRouter(withCurrentUser(ApplicationsDatatable)), [
'namespace',
'namespaces',
'onNamespaceChange',
'onRefresh',
'onRemove',
'hideStacks',
])
).name;

View file

@ -23,7 +23,6 @@ import { ApplicationSummarySection } from '@/react/kubernetes/applications/compo
import { withFormValidation } from '@/react-tools/withFormValidation';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { YAMLInspector } from '@/react/kubernetes/components/YAMLInspector';
import { ApplicationsStacksDatatable } from '@/react/kubernetes/applications/ListView/ApplicationsStacksDatatable';
import { NodesDatatable } from '@/react/kubernetes/cluster/HomeView/NodesDatatable';
import { StackName } from '@/react/kubernetes/DeployView/StackName/StackName';
import { StackNameLabelInsight } from '@/react/kubernetes/DeployView/StackName/StackNameLabelInsight';
@ -61,14 +60,12 @@ import { EnvironmentVariablesFormSection } from '@/react/kubernetes/applications
import { kubeEnvVarValidationSchema } from '@/react/kubernetes/applications/components/EnvironmentVariablesFormSection/kubeEnvVarValidationSchema';
import { IntegratedAppsDatatable } from '@/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable';
import { applicationsModule } from './applications';
import { namespacesModule } from './namespaces';
import { clusterManagementModule } from './clusterManagement';
import { registriesModule } from './registries';
export const ngModule = angular
.module('portainer.kubernetes.react.components', [
applicationsModule,
namespacesModule,
clusterManagementModule,
registriesModule,
@ -208,15 +205,6 @@ export const ngModule = angular
['formValues', 'oldFormValues']
)
)
.component(
'kubernetesApplicationsStacksDatatable',
r2a(withUIRouter(withCurrentUser(ApplicationsStacksDatatable)), [
'onRemove',
'namespace',
'namespaces',
'onNamespaceChange',
])
)
.component(
'kubernetesIntegratedApplicationsDatatable',
r2a(withUIRouter(withCurrentUser(IntegratedAppsDatatable)), [

View file

@ -11,6 +11,7 @@ import { ServicesView } from '@/react/kubernetes/services/ServicesView';
import { ConsoleView } from '@/react/kubernetes/applications/ConsoleView';
import { ConfigmapsAndSecretsView } from '@/react/kubernetes/configs/ListView/ConfigmapsAndSecretsView';
import { CreateNamespaceView } from '@/react/kubernetes/namespaces/CreateView/CreateNamespaceView';
import { ApplicationsView } from '@/react/kubernetes/applications/ListView/ApplicationsView';
import { ApplicationDetailsView } from '@/react/kubernetes/applications/DetailsView/ApplicationDetailsView';
import { ConfigureView } from '@/react/kubernetes/cluster/ConfigureView';
import { NamespacesView } from '@/react/kubernetes/namespaces/ListView/NamespacesView';
@ -55,6 +56,10 @@ export const viewsModule = angular
[]
)
)
.component(
'kubernetesApplicationsView',
r2a(withUIRouter(withReactQuery(withCurrentUser(ApplicationsView))), [])
)
.component(
'applicationDetailsView',
r2a(

View file

@ -1,59 +0,0 @@
<page-header ng-if="ctrl.state.viewReady" title="'Application list'" breadcrumbs="['Applications']" reload="true"></page-header>
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<div class="row">
<div class="col-sm-12" data-cy="k8sApp-appList">
<rd-widget>
<rd-widget-body classes="no-padding">
<uib-tabset active="ctrl.state.activeTab" justified="true" type="pills" ng-if="!ctrl.deploymentOptions.hideStacksFunctionality">
<uib-tab index="0" classes="btn-sm" select="ctrl.selectTab(0)">
<uib-tab-heading class="vertical-center"> <pr-icon icon="'box'"></pr-icon> Applications </uib-tab-heading>
<kubernetes-applications-datatable
dataset="ctrl.state.applications"
on-refresh="(ctrl.getApplications)"
namespaces="ctrl.state.namespaces"
namespace="ctrl.state.namespaceName"
on-namespace-change="(ctrl.onChangeNamespaceDropdown)"
is-loading="ctrl.state.isAppsLoading"
on-remove="(ctrl.removeAction)"
hide-stacks="ctrl.deploymentOptions.hideStacksFunctionality"
>
</kubernetes-applications-datatable>
</uib-tab>
<uib-tab index="2" classes="btn-sm" select="ctrl.selectTab(2)">
<uib-tab-heading class="vertical-center"> <pr-icon icon="'list'"></pr-icon> Stacks </uib-tab-heading>
<kubernetes-applications-stacks-datatable
dataset="ctrl.state.stacks"
on-refresh="(ctrl.getApplications)"
on-remove="(ctrl.removeStacksAction)"
namespaces="ctrl.state.namespaces"
namespace="ctrl.state.namespaceName"
is-loading="ctrl.state.isAppsLoading"
on-namespace-change="(ctrl.onChangeNamespaceDropdown)"
show-system="ctrl.state.isSystemResources"
set-system-resources="(ctrl.setSystemResources)"
>
</kubernetes-applications-stacks-datatable>
</uib-tab>
</uib-tabset>
<kubernetes-applications-datatable
ng-if="ctrl.deploymentOptions.hideStacksFunctionality"
dataset="ctrl.state.applications"
on-refresh="(ctrl.getApplications)"
namespaces="ctrl.state.namespaces"
namespace="ctrl.state.namespaceName"
on-namespace-change="(ctrl.onChangeNamespaceDropdown)"
is-loading="ctrl.state.isAppsLoading"
on-remove="(ctrl.removeAction)"
hide-stacks="ctrl.deploymentOptions.hideStacksFunctionality"
>
</kubernetes-applications-datatable>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>

View file

@ -1,9 +0,0 @@
angular.module('portainer.kubernetes').component('kubernetesApplicationsView', {
templateUrl: './applications.html',
controller: 'KubernetesApplicationsController',
controllerAs: 'ctrl',
bindings: {
$transition$: '<',
endpoint: '<',
},
});

View file

@ -1,181 +0,0 @@
import angular from 'angular';
import _ from 'lodash-es';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
import { KubernetesPortainerApplicationStackNameLabel } from 'Kubernetes/models/application/models';
import { getDeploymentOptions } from '@/react/portainer/environments/environment.service';
import { getStacksFromApplications } from '@/react/kubernetes/applications/ListView/ApplicationsStacksDatatable/getStacksFromApplications';
import { getApplications } from '@/react/kubernetes/applications/application.queries.ts';
import { getNamespaces } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
class KubernetesApplicationsController {
/* @ngInject */
constructor(
$async,
$state,
$scope,
Authentication,
Notifications,
KubernetesApplicationService,
EndpointService,
HelmService,
KubernetesConfigurationService,
LocalStorage,
StackService,
KubernetesNamespaceService
) {
this.$async = $async;
this.$state = $state;
this.$scope = $scope;
this.Authentication = Authentication;
this.Notifications = Notifications;
this.KubernetesApplicationService = KubernetesApplicationService;
this.HelmService = HelmService;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.Authentication = Authentication;
this.LocalStorage = LocalStorage;
this.StackService = StackService;
this.KubernetesNamespaceService = KubernetesNamespaceService;
this.onInit = this.onInit.bind(this);
this.removeAction = this.removeAction.bind(this);
this.removeActionAsync = this.removeActionAsync.bind(this);
this.removeStacksAction = this.removeStacksAction.bind(this);
this.removeStacksActionAsync = this.removeStacksActionAsync.bind(this);
this.onPublishingModeClick = this.onPublishingModeClick.bind(this);
this.onChangeNamespaceDropdown = this.onChangeNamespaceDropdown.bind(this);
}
selectTab(index) {
this.LocalStorage.storeActiveTab('applications', index);
}
async removeStacksActionAsync(selectedItems) {
let actionCount = selectedItems.length;
for (const stack of selectedItems) {
try {
const isAppFormCreated = stack.Applications.some((x) => !x.ApplicationKind);
if (isAppFormCreated) {
const promises = _.map(stack.Applications, (app) => this.KubernetesApplicationService.delete(app));
await Promise.all(promises);
}
await this.StackService.removeKubernetesStacksByName(stack.Name, stack.ResourcePool, false, this.endpoint.Id);
this.Notifications.success('Stack successfully removed', stack.Name);
_.remove(this.state.stacks, { Name: stack.Name });
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to remove stack');
} finally {
--actionCount;
if (actionCount === 0) {
this.$state.reload(this.$state.current);
}
}
}
}
removeStacksAction(selectedItems) {
return this.$async(this.removeStacksActionAsync, selectedItems);
}
async removeActionAsync(selectedItems) {
let actionCount = selectedItems.length;
for (const application of selectedItems) {
try {
if (application.ApplicationType === KubernetesApplicationTypes.Helm) {
await this.HelmService.uninstall(this.endpoint.Id, application);
} else {
if (application.Metadata.labels && application.Metadata.labels[KubernetesPortainerApplicationStackNameLabel]) {
// remove stack if no app left in the stack
const appsInNamespace = await getApplications(this.endpoint.Id, { namespace: application.ResourcePool, withDependencies: false });
const stacksInNamespace = getStacksFromApplications(appsInNamespace);
const stack = stacksInNamespace.find((x) => x.Name === application.StackName);
if (stack.Applications.length === 0 && application.StackId) {
await this.StackService.remove({ Id: application.StackId }, false, this.endpoint.Id);
}
}
await this.KubernetesApplicationService.delete(application);
}
this.Notifications.success('Application successfully removed', application.Name);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to remove application');
} finally {
--actionCount;
if (actionCount === 0) {
this.$state.reload(this.$state.current);
}
}
}
}
removeAction(selectedItems) {
this.$async(() => this.removeActionAsync(selectedItems));
}
onPublishingModeClick(application) {
this.state.activeTab = 1;
_.forEach(this.state.ports, (item) => {
item.Expanded = false;
item.Highlighted = false;
if (item.Name === application.Name && item.Ports.length > 1) {
item.Expanded = true;
item.Highlighted = true;
}
});
}
onChangeNamespaceDropdown(namespaceName) {
return this.$async(async () => {
this.state.namespaceName = namespaceName;
// save the selected namespaceName in local storage with the key 'kubernetes_namespace_filter_${environmentId}_${userID}'
this.LocalStorage.storeNamespaceFilter(this.endpoint.Id, this.user.ID, namespaceName);
});
}
async onInit() {
this.state = {
activeTab: this.LocalStorage.getActiveTab('applications'),
currentName: this.$state.$current.name,
isAdmin: this.Authentication.isAdmin(),
viewReady: false,
applications: [],
stacks: [],
ports: [],
namespaces: [],
namespaceName: '',
};
this.deploymentOptions = await getDeploymentOptions();
this.user = this.Authentication.getUserDetails();
this.state.namespaces = await getNamespaces(this.endpoint.Id);
const savedNamespace = this.LocalStorage.getNamespaceFilter(this.endpoint.Id, this.user.ID); // could be null if not found, and '' if all namepsaces is selected
const preferredNamespace = savedNamespace === null ? 'default' : savedNamespace;
this.state.namespaces = this.state.namespaces.filter((n) => n.Status.phase === 'Active');
this.state.namespaces = _.sortBy(this.state.namespaces, 'Name');
// set all namespaces ('') if there are no namespaces, or if all namespaces is selected
if (!this.state.namespaces.length || preferredNamespace === '') {
this.state.namespaceName = '';
} else {
// otherwise, set the preferred namespaceName if it exists, otherwise set the first namespaceName
this.state.namespaceName = this.state.namespaces.find((n) => n.Name === preferredNamespace) ? preferredNamespace : this.state.namespaces[0].Name;
}
this.state.viewReady = true;
}
$onInit() {
return this.$async(this.onInit);
}
$onDestroy() {
if (this.state.currentName !== this.$state.$current.name) {
this.LocalStorage.storeActiveTab('applications', 0);
}
}
}
export default KubernetesApplicationsController;
angular.module('portainer.kubernetes').controller('KubernetesApplicationsController', KubernetesApplicationsController);