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

refactor(docker): migrate dashboard to react [EE-2191] (#11574)
Some checks are pending
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run

This commit is contained in:
Chaim Lev-Ari 2024-05-20 09:34:51 +03:00 committed by GitHub
parent 2669a44d79
commit 014a590704
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1297 additions and 507 deletions

View file

@ -150,8 +150,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([
url: '/dashboard',
views: {
'content@': {
templateUrl: './views/dashboard/dashboard.html',
controller: 'DashboardController',
component: 'dockerDashboardView',
},
},
data: {

View file

@ -1,7 +0,0 @@
angular.module('portainer.docker').component('dashboardClusterAgentInfo', {
templateUrl: './dashboardClusterAgentInfo.html',
controller: 'DashboardClusterAgentInfoController',
bindings: {
endpointId: '<',
},
});

View file

@ -1,20 +0,0 @@
<rd-widget>
<rd-widget-header icon="gauge" title-text="Cluster information"></rd-widget-header>
<rd-widget-body classes="!px-5 !py-0">
<table class="table">
<tbody>
<tr>
<td>Nodes in the cluster</td>
<td>{{ $ctrl.agentCount }}</td>
</tr>
<tr>
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a ui-sref="docker.swarm.visualizer" class="vertical-center"><pr-icon icon="'trello'" class-name="'icon'"></pr-icon>Go to cluster visualizer</a>
</div>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>

View file

@ -1,17 +0,0 @@
angular.module('portainer.docker').controller('DashboardClusterAgentInfoController', [
'AgentService',
'Notifications',
function (AgentService, Notifications) {
var ctrl = this;
this.$onInit = function () {
AgentService.agents(ctrl.endpointId)
.then(function success(data) {
ctrl.agentCount = data.length;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve agent information');
});
};
},
]);

View file

@ -3,15 +3,18 @@ import angular from 'angular';
import { ItemView as NetworksItemView } from '@/react/docker/networks/ItemView';
import { r2a } from '@/react-tools/react2angular';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { withReactQuery } from '@/react-tools/withReactQuery';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { DashboardView } from '@/react/docker/DashboardView/DashboardView';
import { containersModule } from './containers';
export const viewsModule = angular
.module('portainer.docker.react.views', [containersModule])
.component(
'dockerDashboardView',
r2a(withUIRouter(withCurrentUser(DashboardView)), [])
)
.component(
'networkDetailsView',
r2a(withUIRouter(withReactQuery(withCurrentUser(NetworksItemView))), [])
r2a(withUIRouter(withCurrentUser(NetworksItemView)), [])
).name;

View file

@ -1,117 +0,0 @@
<page-header title="'Dashboard'" breadcrumbs="['Environment summary']"> </page-header>
<div class="row" ng-if="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
<div class="col-sm-12">
<dashboard-cluster-agent-info endpoint-id="endpoint.Id"></dashboard-cluster-agent-info>
</div>
</div>
<information-panel
ng-if="
!applicationState.UI.dismissedInfoPanels['docker-dashboard-info-01'] &&
!applicationState.endpoint.mode.agentProxy &&
applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'
"
title-text="Information"
dismiss-action="dismissInformationPanel('docker-dashboard-info-01')"
>
<span class="small">
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
Portainer is connected to a node that is part of a Swarm cluster. Some resources located on other nodes in the cluster might not be available for management, have a look at
<help-link doc-link="'/admin/environments/add/swarm/agent'" target="'_blank'" children="'our agent setup'"></help-link> for more details.
</p>
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'WORKER'">
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
Portainer is connected to a worker node. Swarm management features will not be available.
</p>
</span>
</information-panel>
<div ng-if="info">
<div class="row" ng-if="(!applicationState.endpoint.mode.agentProxy || applicationState.endpoint.mode.provider !== 'DOCKER_SWARM_MODE') && info && endpoint">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="gauge" title-text="Environment info"></rd-widget-header>
<rd-widget-body classes="!px-5 !py-0">
<table class="table">
<tbody>
<tr>
<td>Environment</td>
<td>
{{ endpoint.Name }}
<span class="small text-muted space-left">
<pr-icon icon="'cpu'"></pr-icon> {{ endpoint.Snapshots[0].TotalCPU }} <pr-icon icon="'svg-memory'"></pr-icon>
{{ endpoint.Snapshots[0].TotalMemory | humansize }}
</span>
<span class="small text-muted">
- {{ info.Swarm && info.Swarm.NodeID !== '' ? 'Swarm' : 'Standalone' }} {{ info.ServerVersion }}
<span ng-if="endpoint.Type === 2">
<pr-icon icon="'zap'"></pr-icon>
Agent</span
></span
>
</td>
</tr>
<tr ng-if="showEnvUrl">
<td>URL</td>
<td>{{ endpoint.URL | stripprotocol }}</td>
</tr>
<tr>
<td>{{ endpoint.Gpus.length <= 1 ? 'GPU' : 'GPUs' }}</td>
<td>{{ gpuInfoStr }}</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ endpointTags }}</td>
</tr>
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a ui-sref="docker.swarm.visualizer" class="vertical-center"><pr-icon icon="'trello'" class-name="'icon'"></pr-icon>Go to cluster visualizer</a>
</div>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="mx-4 grid grid-cols-2 gap-3">
<a class="no-link" ui-sref="docker.stacks" ng-if="showStacks">
<dashboard-item icon="'layers'" type="'Stack'" value="stackCount"></dashboard-item>
</a>
<div ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<a class="no-link" ui-sref="docker.services">
<dashboard-item icon="'shuffle'" type="'Service'" value="serviceCount"></dashboard-item>
</a>
</div>
<a class="no-link" ng-if="containers" ui-sref="docker.containers">
<dashboard-item icon="'box'" type="'Container'" value="containers.length" children="containerStatusComponent"></dashboard-item>
</a>
<a class="no-link" ng-if="images" ui-sref="docker.images">
<dashboard-item icon="'list'" type="'Image'" value="images.length" children="imagesTotalSizeComponent"></dashboard-item>
</a>
<a class="no-link" ui-sref="docker.volumes">
<dashboard-item icon="'database'" type="'Volume'" value="volumeCount"></dashboard-item>
</a>
<a class="no-link" ui-sref="docker.networks">
<dashboard-item icon="'Network'" type="'Network'" value="networkCount"></dashboard-item>
</a>
<div>
<dashboard-item
ng-if="endpoint.EnableGPUManagement && applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'"
icon="'cpu'"
type="'GPU'"
value="endpoint.Gpus.length"
></dashboard-item>
</div>
</div>
</div>

View file

@ -1,141 +0,0 @@
import angular from 'angular';
import _ from 'lodash';
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
import { useContainerStatusComponent } from '@/react/docker/DashboardView/ContainerStatus';
import { useImagesTotalSizeComponent } from '@/react/docker/DashboardView/ImagesTotalSize';
angular.module('portainer.docker').controller('DashboardController', [
'$scope',
'$q',
'Authentication',
'ContainerService',
'ImageService',
'NetworkService',
'VolumeService',
'SystemService',
'ServiceService',
'StackService',
'Notifications',
'StateManager',
'TagService',
'endpoint',
function (
$scope,
$q,
Authentication,
ContainerService,
ImageService,
NetworkService,
VolumeService,
SystemService,
ServiceService,
StackService,
Notifications,
StateManager,
TagService,
endpoint
) {
$scope.dismissInformationPanel = function (id) {
StateManager.dismissInformationPanel(id);
};
$scope.showStacks = false;
$scope.buildGpusStr = function (gpuUseSet) {
var gpusAvailable = new Object();
for (let i = 0; i < ($scope.endpoint.Gpus || []).length; i++) {
if (!gpuUseSet.has($scope.endpoint.Gpus[i].name)) {
var exist = false;
for (let gpuAvailable in gpusAvailable) {
if ($scope.endpoint.Gpus[i].value == gpuAvailable) {
gpusAvailable[gpuAvailable] += 1;
exist = true;
}
}
if (exist === false) {
gpusAvailable[$scope.endpoint.Gpus[i].value] = 1;
}
}
}
var retStr = Object.keys(gpusAvailable).length
? _.join(
_.map(Object.keys(gpusAvailable), (gpuAvailable) => {
var _str = gpusAvailable[gpuAvailable];
_str += ' x ';
_str += gpuAvailable;
return _str;
}),
' + '
)
: 'none';
return retStr;
};
async function initView() {
const endpointMode = $scope.applicationState.endpoint.mode;
$scope.endpoint = endpoint;
$scope.showStacks = await shouldShowStacks();
$scope.showEnvUrl = endpoint.Type !== PortainerEndpointTypes.EdgeAgentOnDockerEnvironment && endpoint.Type !== PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment;
$q.all({
containers: ContainerService.containers(endpoint.Id, 1),
images: ImageService.images(),
volumes: VolumeService.volumes(),
networks: NetworkService.networks(true, true, true),
services: endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER' ? ServiceService.services() : [],
stacks: StackService.stacks(true, endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER', endpoint.Id),
info: SystemService.info(),
tags: TagService.tags(),
})
.then(function success(data) {
$scope.containers = data.containers;
$scope.containerStatusComponent = useContainerStatusComponent(data.containers);
$scope.images = data.images;
$scope.imagesTotalSizeComponent = useImagesTotalSizeComponent(imagesTotalSize(data.images));
$scope.volumeCount = data.volumes.length;
$scope.networkCount = data.networks.length;
$scope.serviceCount = data.services.length;
$scope.stackCount = data.stacks.length;
$scope.info = data.info;
$scope.gpuInfoStr = $scope.buildGpusStr(new Set());
$scope.gpuUseAll = _.get($scope, 'endpoint.Snapshots[0].GpuUseAll', false);
$scope.gpuUseList = _.get($scope, 'endpoint.Snapshots[0].GpuUseList', []);
$scope.gpuFreeStr = 'all';
if ($scope.gpuUseAll == true) $scope.gpuFreeStr = 'none';
else $scope.gpuFreeStr = $scope.buildGpusStr(new Set($scope.gpuUseList));
$scope.endpointTags = endpoint.TagIds.length
? _.join(
_.filter(
_.map(endpoint.TagIds, (id) => {
const tag = data.tags.find((tag) => tag.Id === id);
return tag ? tag.Name : '';
}),
Boolean
),
', '
)
: '-';
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load dashboard data');
});
}
async function shouldShowStacks() {
const isAdmin = Authentication.isAdmin();
return isAdmin || endpoint.SecuritySettings.allowStackManagementForRegularUsers;
}
initView();
},
]);
function imagesTotalSize(images) {
return images.reduce((acc, image) => acc + image.Size, 0);
}