mirror of https://github.com/portainer/portainer
fix(stacks): show stack containers [EE-2359] (#6375)
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>pull/6398/head
parent
085762a1f4
commit
584a46d9d4
|
@ -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>
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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' } },
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 ./...",
|
||||
|
|
Loading…
Reference in New Issue