diff --git a/.eslintrc.yml b/.eslintrc.yml index 5ccead36c..cc4e73a73 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -88,6 +88,8 @@ overrides: 'jsx-a11y/label-has-associated-control': ['error', { 'assert': 'either' }] 'react/function-component-definition': ['error', { 'namedComponents': 'function-declaration' }] 'react/jsx-no-bind': off + 'no-await-in-loop': 'off' + 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }] - files: - app/**/*.test.* extends: diff --git a/app/assets/css/app.css b/app/assets/css/app.css index b043f74ba..7a8122f14 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -82,6 +82,13 @@ input[type='checkbox'] { margin-top: -1px; } +.md-checkbox input[type='checkbox']:indeterminate + label:before { + content: '-'; + align-content: center; + line-height: 16px; + text-align: center; +} + a[ng-click] { cursor: pointer; } @@ -885,3 +892,7 @@ json-tree .branch-preview { word-break: break-all; white-space: normal; } + +.space-x-1 { + margin-left: 0.25rem; +} diff --git a/app/assets/css/index.js b/app/assets/css/index.js index c48ded8c2..9cd17ecec 100644 --- a/app/assets/css/index.js +++ b/app/assets/css/index.js @@ -13,6 +13,7 @@ import 'angular-loading-bar/build/loading-bar.css'; import 'angular-moment-picker/dist/angular-moment-picker.min.css'; import 'angular-multiselect/isteven-multi-select.css'; import 'spinkit/spinkit.min.css'; +import '@reach/menu-button/styles.css'; import './rdash.css'; import './app.css'; diff --git a/app/docker/__module.js b/app/docker/__module.js index a34a8e4cd..828862870 100644 --- a/app/docker/__module.js +++ b/app/docker/__module.js @@ -1,4 +1,8 @@ -angular.module('portainer.docker', ['portainer.app']).config([ +import angular from 'angular'; + +import containersModule from './containers'; + +angular.module('portainer.docker', ['portainer.app', containersModule]).config([ '$stateRegistryProvider', function ($stateRegistryProvider) { 'use strict'; diff --git a/app/docker/components/container-quick-actions/ContainerQuickActions.module.css b/app/docker/components/container-quick-actions/ContainerQuickActions.module.css new file mode 100644 index 000000000..dfc0a60d1 --- /dev/null +++ b/app/docker/components/container-quick-actions/ContainerQuickActions.module.css @@ -0,0 +1,3 @@ +.root { + display: inline-flex; +} diff --git a/app/docker/components/container-quick-actions/ContainerQuickActions.tsx b/app/docker/components/container-quick-actions/ContainerQuickActions.tsx new file mode 100644 index 000000000..41e411bef --- /dev/null +++ b/app/docker/components/container-quick-actions/ContainerQuickActions.tsx @@ -0,0 +1,140 @@ +import clsx from 'clsx'; + +import { Authorized } from '@/portainer/hooks/useUser'; +import { Link } from '@/portainer/components/Link'; +import { react2angular } from '@/react-tools/react2angular'; +import { DockerContainerStatus } from '@/docker/containers/types'; + +import styles from './ContainerQuickActions.module.css'; + +interface QuickActionsState { + showQuickActionAttach: boolean; + showQuickActionExec: boolean; + showQuickActionInspect: boolean; + showQuickActionLogs: boolean; + showQuickActionStats: boolean; +} + +interface Props { + taskId?: string; + containerId?: string; + nodeName: string; + state: QuickActionsState; + status: DockerContainerStatus; +} + +export function ContainerQuickActions({ + taskId, + containerId, + nodeName, + state, + status, +}: Props) { + if (taskId) { + return ; + } + + const isActive = ['starting', 'running', 'healthy', 'unhealthy'].includes( + status + ); + + return ( +
+ {state.showQuickActionLogs && ( + + +