fix(stacks): show stack containers [EE-2359] (#6375)

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
pull/6398/head
Chaim Lev-Ari 2022-01-13 07:28:49 +02:00 committed by GitHub
parent 085762a1f4
commit 584a46d9d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 52 additions and 65 deletions

View File

@ -50,9 +50,10 @@ import { useColumns } from './columns';
export interface ContainerTableProps {
isAddActionVisible: boolean;
dataset: DockerContainer[];
onRefresh(): Promise<void>;
onRefresh?(): Promise<void>;
isHostColumnVisible: boolean;
autoFocusSearch: boolean;
tableKey?: string;
}
export function ContainersDatatable({
@ -150,7 +151,7 @@ export function ContainersDatatable({
<TableSettingsMenu
quickActions={<QuickActionsSettings actions={actions} />}
>
<ContainersDatatableSettings />
<ContainersDatatableSettings isRefreshVisible={!!onRefresh} />
</TableSettingsMenu>
</TableTitleActions>
</TableTitle>

View File

@ -13,7 +13,11 @@ interface Props extends ContainerTableProps {
endpoint: Environment;
}
export function ContainersDatatableContainer({ endpoint, ...props }: Props) {
export function ContainersDatatableContainer({
endpoint,
tableKey = 'containers',
...props
}: Props) {
const defaultSettings = {
autoRefreshRate: 0,
truncateContainerName: 32,
@ -25,8 +29,8 @@ export function ContainersDatatableContainer({ endpoint, ...props }: Props) {
return (
<EnvironmentProvider environment={endpoint}>
<TableSettingsProvider defaults={defaultSettings} storageKey="containers">
<SearchBarProvider>
<TableSettingsProvider defaults={defaultSettings} storageKey={tableKey}>
<SearchBarProvider storageKey={tableKey}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<ContainersDatatable {...props} />
</SearchBarProvider>
@ -40,13 +44,10 @@ export const ContainersDatatableAngular = react2angular(
[
'endpoint',
'isAddActionVisible',
'containerService',
'httpRequestHelper',
'notifications',
'modalService',
'dataset',
'onRefresh',
'isHostColumnVisible',
'autoFocusSearch',
'tableKey',
]
);

View File

@ -3,10 +3,13 @@ import { useTableSettings } from '@/portainer/components/datatables/components/u
import { Checkbox } from '@/portainer/components/form-components/Checkbox';
import type { ContainersTableSettings } from '@/docker/containers/types';
export function ContainersDatatableSettings() {
const { settings, setTableSettings } = useTableSettings<
ContainersTableSettings
>();
interface Props {
isRefreshVisible: boolean;
}
export function ContainersDatatableSettings({ isRefreshVisible }: Props) {
const { settings, setTableSettings } =
useTableSettings<ContainersTableSettings>();
return (
<>
@ -22,10 +25,12 @@ export function ContainersDatatableSettings() {
}
/>
<TableSettingsMenuAutoRefresh
value={settings.autoRefreshRate}
onChange={handleRefreshRateChange}
/>
{isRefreshVisible && (
<TableSettingsMenuAutoRefresh
value={settings.autoRefreshRate}
onChange={handleRefreshRateChange}
/>
)}
</>
);

View File

@ -30,20 +30,22 @@ const SearchBarContext = createContext<
interface SearchBarProviderProps {
defaultValue?: string;
storageKey: string;
}
export function SearchBarProvider({
children,
storageKey,
defaultValue = '',
}: PropsWithChildren<SearchBarProviderProps>) {
const [value, setValue] = useLocalStorage(
'datatable_text_filter_containers',
const state = useLocalStorage(
`datatable_text_filter_${storageKey}`,
defaultValue,
sessionStorage
);
return (
<SearchBarContext.Provider value={[value, setValue]}>
<SearchBarContext.Provider value={state}>
{children}
</SearchBarContext.Provider>
);

View File

@ -2,7 +2,7 @@ import { useEffect, useCallback, useState } from 'react';
export function useRepeater(
refreshRate: number,
onRefresh: () => Promise<void>
onRefresh?: () => Promise<void>
) {
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
@ -17,7 +17,7 @@ export function useRepeater(
const startRepeater = useCallback(
(refreshRate) => {
if (intervalId) {
if (intervalId || !onRefresh) {
return;
}
@ -27,16 +27,16 @@ export function useRepeater(
}, refreshRate * 1000)
);
},
[intervalId]
[intervalId, onRefresh]
);
useEffect(() => {
if (!refreshRate) {
if (!refreshRate || !onRefresh) {
stopRepeater();
} else {
startRepeater(refreshRate);
}
return stopRepeater;
}, [refreshRate, startRepeater, stopRepeater, intervalId]);
}, [refreshRate, startRepeater, stopRepeater, intervalId, onRefresh]);
}

View File

@ -5,7 +5,7 @@ angular.module('portainer.app').factory('Stack', StackFactory);
/* @ngInject */
function StackFactory($resource, API_ENDPOINT_STACKS) {
return $resource(
API_ENDPOINT_STACKS + '/:id/:action',
API_ENDPOINT_STACKS + '/:id/:action/:subaction',
{},
{
get: { method: 'GET', params: { id: '@id' } },

View File

@ -15,16 +15,14 @@
<!-- tab-info -->
<uib-tab index="0">
<uib-tab-heading> <i class="fa fa-th-list" aria-hidden="true"></i> Stack </uib-tab-heading>
<div style="margin-top: 10px;">
<div style="margin-top: 10px">
<!-- stack-information -->
<div ng-if="external || orphaned">
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="col-sm-12 form-section-title"> Information </div>
<div class="form-group">
<span class="small">
<p class="text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i>
<span ng-if="external">This stack was created outside of Portainer. Control over this stack is limited.</span>
<span ng-if="orphaned">This stack is orphaned. You can reassociate it with the current environment using the "Associate to this environment" feature.</span>
</p>
@ -34,9 +32,7 @@
<!-- !stack-information -->
<!-- stack-details -->
<div>
<div class="col-sm-12 form-section-title">
Stack details
</div>
<div class="col-sm-12 form-section-title"> Stack details </div>
<div class="form-group">
{{ stackName }}
@ -81,12 +77,8 @@
<!-- associate -->
<div ng-if="orphaned">
<div class="col-sm-12 form-section-title">
Associate to this environment
</div>
<p class="small text-muted">
This feature allows you to reassociate this stack to the current environment.
</p>
<div class="col-sm-12 form-section-title"> Associate to this environment </div>
<p class="small text-muted"> This feature allows you to reassociate this stack to the current environment. </p>
<form class="form-horizontal">
<por-access-control-form form-data="formValues.AccessControlData" hide-title="true"></por-access-control-form>
<div class="form-group">
@ -97,13 +89,13 @@
ng-disabled="state.actionInProgress"
ng-click="associateStack()"
button-spinner="state.actionInProgress"
style="margin-left: -5px;"
style="margin-left: -5px"
>
<i class="fa fa-sync" aria-hidden="true" style="margin-right: 3px;"></i>
<i class="fa fa-sync" aria-hidden="true" style="margin-right: 3px"></i>
<span ng-hide="state.actionInProgress">Associate</span>
<span ng-show="state.actionInProgress">Association in progress...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px">{{ state.formValidationError }}</span>
</div>
</div>
</form>
@ -128,12 +120,12 @@
<!-- tab-file -->
<uib-tab index="1" select="showEditor()" ng-if="!external && (!stack.GitConfig || stack.FromAppTemplate)">
<uib-tab-heading> <i class="fa fa-pencil-alt space-right" aria-hidden="true"></i> Editor </uib-tab-heading>
<form class="form-horizontal" ng-if="state.showEditorTab" style="margin-top: 10px;" name="stackUpdateForm">
<form class="form-horizontal" ng-if="state.showEditorTab" style="margin-top: 10px" name="stackUpdateForm">
<div class="form-group">
<span class="col-sm-12 text-muted small" style="margin-bottom: 7px;" ng-if="stackType == 2 && composeSyntaxMaxVersion == 2">
<span class="col-sm-12 text-muted small" style="margin-bottom: 7px" ng-if="stackType == 2 && composeSyntaxMaxVersion == 2">
This stack will be deployed using the equivalent of <code>docker-compose</code>. Only Compose file format version <b>2</b> is supported at the moment.
</span>
<span class="col-sm-12 text-muted small" style="margin-bottom: 7px;" ng-if="stackType == 2 && composeSyntaxMaxVersion > 2">
<span class="col-sm-12 text-muted small" style="margin-bottom: 7px" ng-if="stackType == 2 && composeSyntaxMaxVersion > 2">
This stack will be deployed using <code>docker-compose</code>.
</span>
<span class="col-sm-12 text-muted small">
@ -163,24 +155,20 @@
<!-- !environment-variables -->
<!-- options -->
<div ng-if="stack.Type === 1 && applicationState.endpoint.apiVersion >= 1.27" authorization="PortainerStackUpdate">
<div class="col-sm-12 form-section-title">
Options
</div>
<div class="col-sm-12 form-section-title"> Options </div>
<div class="form-group">
<div class="col-sm-12">
<label for="prune" class="control-label text-left">
Prune services
<portainer-tooltip position="bottom" message="Prune services that are no longer referenced."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;"> <input name="prune" type="checkbox" ng-model="formValues.Prune" /><i></i> </label>
<label class="switch" style="margin-left: 20px"> <input name="prune" type="checkbox" ng-model="formValues.Prune" /><i></i> </label>
</div>
</div>
</div>
<!-- !options -->
<div authorization="PortainerStackUpdate">
<div class="col-sm-12 form-section-title">
Actions
</div>
<div class="col-sm-12 form-section-title"> Actions </div>
<div class="form-group">
<div class="col-sm-12">
<button
@ -207,17 +195,7 @@
<div class="row" ng-if="containers && (!orphaned || orphanedRunning)">
<div class="col-sm-12">
<containers-datatable
title-text="Containers"
title-icon="fa-cubes"
dataset="containers"
table-key="stack-containers"
order-by="Status"
show-host-column="false"
show-add-action="false"
not-auto-focus="true"
endpoint-public-url="endpoint.PublicURL"
></containers-datatable>
<containers-datatable dataset="containers" endpoint="endpoint" table-key="stack-containers"></containers-datatable>
</div>
</div>

View File

@ -38,7 +38,7 @@
"format": "prettier --loglevel warn --write \"**/*.{js,css,html,jsx,tsx,ts}\"",
"lint": "yarn lint:client; yarn lint:server",
"lint:server": "cd api && golangci-lint run -E exportloopref",
"lint:client": "eslint --cache --fix ./**/*.{js,jsx,ts,tsx}",
"lint:client": "eslint --cache --fix './**/*.{js,jsx,ts,tsx}'",
"lint:pr": "make lint-pr",
"test": "yarn test:client; yarn test:server",
"test:server": "cd api && go test ./...",