mirror of
https://github.com/portainer/portainer.git
synced 2026-08-07 09:04:48 +00:00
chore(react2angular): remove orphaned bridges [BE-13186] (#3142)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,7 @@ import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { DockerfileDetails } from '@/react/docker/images/ItemView/DockerfileDetails';
|
||||
import { HealthStatus } from '@/react/docker/containers/ItemView/HealthStatus';
|
||||
import { GpusList } from '@/react/docker/host/SetupView/GpusList';
|
||||
import { InsightsBox } from '@/react/components/InsightsBox';
|
||||
import { BetaAlert } from '@/react/portainer/environments/update-schedules/common/BetaAlert';
|
||||
import { EventsDatatable } from '@/react/docker/events/EventsDatatables';
|
||||
import { AgentHostBrowser } from '@/react/docker/host/BrowseView/AgentHostBrowser';
|
||||
import { AgentVolumeBrowser } from '@/react/docker/volumes/BrowseView/AgentVolumeBrowser';
|
||||
import { ProcessesDatatable } from '@/react/docker/containers/StatsView/ProcessesDatatable';
|
||||
@@ -35,22 +31,10 @@ const ngModule = angular
|
||||
templatesModule,
|
||||
])
|
||||
.component('dockerfileDetails', r2a(DockerfileDetails, ['image']))
|
||||
.component('dockerHealthStatus', r2a(HealthStatus, ['health']))
|
||||
.component(
|
||||
'gpusList',
|
||||
r2a(withControlledInput(GpusList), ['value', 'onChange'])
|
||||
)
|
||||
.component(
|
||||
'insightsBox',
|
||||
r2a(InsightsBox, [
|
||||
'header',
|
||||
'content',
|
||||
'insightCloseId',
|
||||
'type',
|
||||
'className',
|
||||
])
|
||||
)
|
||||
.component('betaAlert', r2a(BetaAlert, ['className', 'message', 'isHtml']))
|
||||
.component(
|
||||
'agentHostBrowserReact',
|
||||
r2a(withUIRouter(withCurrentUser(AgentHostBrowser)), [
|
||||
@@ -84,7 +68,6 @@ const ngModule = angular
|
||||
'dockerContainerProcessesDatatable',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(ProcessesDatatable))), [])
|
||||
)
|
||||
.component('dockerEventsDatatable', r2a(EventsDatatable, ['dataset']))
|
||||
.component(
|
||||
'dockerSecretsDatatable',
|
||||
r2a(withUIRouter(withCurrentUser(SecretsDatatable)), [
|
||||
|
||||
@@ -3,12 +3,8 @@ import angular from 'angular';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { AssociatedEdgeEnvironmentsSelector } from '@/react/edge/components/AssociatedEdgeEnvironmentsSelector';
|
||||
import { EdgeAsyncIntervalsForm } from '@/react/edge/components/EdgeAsyncIntervalsForm';
|
||||
import { EdgeCheckinIntervalField } from '@/react/edge/components/EdgeCheckInIntervalField';
|
||||
import { EdgeScriptForm } from '@/react/edge/components/EdgeScriptForm';
|
||||
import { EdgeGroupsSelector } from '@/react/edge/edge-stacks/components/EdgeGroupsSelector';
|
||||
import { AssociatedEdgeGroupEnvironmentsSelector } from '@/react/edge/components/AssociatedEdgeGroupEnvironmentsSelector';
|
||||
|
||||
const ngModule = angular
|
||||
.module('portainer.edge.react.components', [])
|
||||
@@ -32,45 +28,6 @@ const ngModule = angular
|
||||
'asyncMode',
|
||||
'showMetaFields',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'edgeCheckinIntervalField',
|
||||
r2a(withReactQuery(EdgeCheckinIntervalField), [
|
||||
'value',
|
||||
'onChange',
|
||||
'isDefaultHidden',
|
||||
'tooltip',
|
||||
'label',
|
||||
'readonly',
|
||||
'size',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'edgeAsyncIntervalsForm',
|
||||
r2a(withReactQuery(EdgeAsyncIntervalsForm), [
|
||||
'values',
|
||||
'onChange',
|
||||
'isDefaultHidden',
|
||||
'readonly',
|
||||
'fieldSettings',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'associatedEdgeEnvironmentsSelector',
|
||||
r2a(withReactQuery(AssociatedEdgeEnvironmentsSelector), [
|
||||
'onChange',
|
||||
'value',
|
||||
'error',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'associatedEdgeGroupEnvironmentsSelector',
|
||||
r2a(withReactQuery(AssociatedEdgeGroupEnvironmentsSelector), [
|
||||
'onChange',
|
||||
'value',
|
||||
'error',
|
||||
'edgeGroupId',
|
||||
])
|
||||
);
|
||||
|
||||
export const componentsModule = ngModule.name;
|
||||
|
||||
@@ -3,16 +3,11 @@ import angular from 'angular';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { NodeApplicationsDatatable } from '@/react/kubernetes/cluster/NodeView/NodeApplicationsDatatable/NodeApplicationsDatatable';
|
||||
import { ResourceEventsDatatable } from '@/react/kubernetes/components/EventsDatatable/ResourceEventsDatatable';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
|
||||
export const clusterManagementModule = angular
|
||||
.module('portainer.kubernetes.react.components.clusterManagement', [])
|
||||
.component(
|
||||
'kubernetesNodeApplicationsDatatable',
|
||||
r2a(withUIRouter(withCurrentUser(NodeApplicationsDatatable)), [])
|
||||
)
|
||||
.component(
|
||||
'resourceEventsDatatable',
|
||||
r2a(
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { IngressClassDatatableAngular } from '@/react/kubernetes/cluster/ingressClass/IngressClassDatatable/IngressClassDatatableAngular';
|
||||
import { NamespacesSelector } from '@/react/kubernetes/cluster/RegistryAccessView/NamespacesSelector';
|
||||
import { NamespaceAccessUsersSelector } from '@/react/kubernetes/namespaces/AccessView/NamespaceAccessUsersSelector';
|
||||
import { KubeServicesForm } from '@/react/kubernetes/applications/CreateView/application-services/KubeServicesForm';
|
||||
import { kubeServicesValidation } from '@/react/kubernetes/applications/CreateView/application-services/kubeServicesValidation';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import {
|
||||
ApplicationSummaryWidget,
|
||||
ApplicationDetailsWidget,
|
||||
ApplicationEventsDatatable,
|
||||
} from '@/react/kubernetes/applications/DetailsView';
|
||||
import { ApplicationContainersDatatable } from '@/react/kubernetes/applications/DetailsView/ApplicationContainersDatatable';
|
||||
import {
|
||||
PlacementFormSection,
|
||||
placementValidation,
|
||||
@@ -22,7 +14,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 { NodesDatatable } from '@/react/kubernetes/cluster/HomeView/NodesDatatable';
|
||||
import { StackName } from '@/react/kubernetes/DeployView/StackName/StackName';
|
||||
import { StackNameLabelInsight } from '@/react/kubernetes/DeployView/StackName/StackNameLabelInsight';
|
||||
import { SecretsFormSection } from '@/react/kubernetes/applications/components/ConfigurationsFormSection/SecretsFormSection';
|
||||
@@ -59,33 +50,17 @@ import { EnvironmentVariablesFormSection } from '@/react/kubernetes/applications
|
||||
import { kubeEnvVarValidationSchema } from '@/react/kubernetes/applications/components/EnvironmentVariablesFormSection/kubeEnvVarValidationSchema';
|
||||
import { IntegratedAppsDatatable } from '@/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable';
|
||||
import { K8sRegistryAccessNotice } from '@/react/kubernetes/components/K8sRegistryAccessNotice';
|
||||
import { HelmTemplates } from '@/react/kubernetes/helm/HelmTemplates/HelmTemplates';
|
||||
import { SecretDetailsTable } from '@/react/kubernetes/configs/secrets/ItemView/SecretDetailsTable';
|
||||
import { KubernetesSummaryView } from '@/react/kubernetes/summary/KubernetesSummaryView';
|
||||
import { SecretItemTabsWidget } from '@/react/kubernetes/configs/secrets/ItemView/SecretItemTabsWidget';
|
||||
|
||||
import { namespacesModule } from './namespaces';
|
||||
import { clusterManagementModule } from './clusterManagement';
|
||||
import { registriesModule } from './registries';
|
||||
|
||||
export const ngModule = angular
|
||||
.module('portainer.kubernetes.react.components', [
|
||||
namespacesModule,
|
||||
clusterManagementModule,
|
||||
registriesModule,
|
||||
])
|
||||
.component(
|
||||
'ingressClassDatatable',
|
||||
r2a(IngressClassDatatableAngular, [
|
||||
'onChangeControllers',
|
||||
'description',
|
||||
'ingressControllers',
|
||||
'initialIngressControllers',
|
||||
'allowNoneIngressClass',
|
||||
'isLoading',
|
||||
'view',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'namespacesSelector',
|
||||
r2a(NamespacesSelector, [
|
||||
@@ -99,22 +74,6 @@ export const ngModule = angular
|
||||
'allowSelectAll',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'namespaceAccessUsersSelector',
|
||||
r2a(NamespaceAccessUsersSelector, [
|
||||
'inputId',
|
||||
'onChange',
|
||||
'options',
|
||||
'value',
|
||||
'dataCy',
|
||||
'placeholder',
|
||||
'name',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'kubeNodesDatatable',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(NodesDatatable))), [])
|
||||
)
|
||||
.component(
|
||||
'accessPolicyFormSection',
|
||||
r2a(DataAccessPolicyFormSection, [
|
||||
@@ -166,36 +125,6 @@ export const ngModule = angular
|
||||
'isComposeFormat',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'applicationSummaryWidget',
|
||||
r2a(
|
||||
withUIRouter(withReactQuery(withCurrentUser(ApplicationSummaryWidget))),
|
||||
[]
|
||||
)
|
||||
)
|
||||
.component(
|
||||
'applicationContainersDatatable',
|
||||
r2a(
|
||||
withUIRouter(
|
||||
withReactQuery(withCurrentUser(ApplicationContainersDatatable))
|
||||
),
|
||||
[]
|
||||
)
|
||||
)
|
||||
.component(
|
||||
'applicationDetailsWidget',
|
||||
r2a(
|
||||
withUIRouter(withReactQuery(withCurrentUser(ApplicationDetailsWidget))),
|
||||
[]
|
||||
)
|
||||
)
|
||||
.component(
|
||||
'applicationEventsDatatable',
|
||||
r2a(
|
||||
withUIRouter(withReactQuery(withCurrentUser(ApplicationEventsDatatable))),
|
||||
[]
|
||||
)
|
||||
)
|
||||
.component(
|
||||
'applicationSummarySection',
|
||||
r2a(
|
||||
@@ -214,24 +143,6 @@ export const ngModule = angular
|
||||
'dataCy',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'helmTemplatesView',
|
||||
r2a(withUIRouter(withCurrentUser(HelmTemplates)), [
|
||||
'onSelectHelmChart',
|
||||
'namespace',
|
||||
'name',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'secretDetailsTable',
|
||||
r2a(withUIRouter(withCurrentUser(withReactQuery(SecretDetailsTable))), [
|
||||
'name',
|
||||
'namespace',
|
||||
'secretTypeLabel',
|
||||
'isSystem',
|
||||
'registryId',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'secretItemTabsWidget',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(SecretItemTabsWidget))), [
|
||||
|
||||
@@ -1,13 +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 { NamespacesDatatable } from '@/react/kubernetes/namespaces/ListView/NamespacesDatatable';
|
||||
|
||||
export const namespacesModule = angular
|
||||
.module('portainer.kubernetes.react.components.namespaces', [])
|
||||
.component(
|
||||
'kubernetesNamespacesDatatable',
|
||||
r2a(withUIRouter(withCurrentUser(NamespacesDatatable)), [])
|
||||
).name;
|
||||
@@ -1,69 +0,0 @@
|
||||
<form class="form-horizontal" name="endpointGroupForm" ng-if="$ctrl.model">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="group_name" class="col-sm-3 col-lg-2 control-label required text-left">Name</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" name="group_name" ng-model="$ctrl.model.Name" placeholder="e.g. my-group" required auto-focus data-cy="group-name-input" />
|
||||
<div class="help-block" ng-show="endpointGroupForm.group_name.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="endpointGroupForm.group_name.$error">
|
||||
<p ng-message="required" class="vertical-center">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
|
||||
This field is required.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- description-input -->
|
||||
<div class="form-group">
|
||||
<label for="group_description" class="col-sm-3 col-lg-2 control-label text-left">Description</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="group_description"
|
||||
ng-model="$ctrl.model.Description"
|
||||
placeholder="e.g. production environments..."
|
||||
data-cy="group-description-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !description-input -->
|
||||
<div class="col-sm-12 form-section-title"> Metadata </div>
|
||||
|
||||
<tag-selector ng-if="$ctrl.model.TagIds" value="$ctrl.model.TagIds" on-change="($ctrl.onChangeTags)" allow-create="$ctrl.state.allowCreateTag"> </tag-selector>
|
||||
|
||||
<div class="form-group" ng-if="$ctrl.model.Id !== 1">
|
||||
<associated-endpoints-selector value="$ctrl.associatedEndpoints" on-change="($ctrl.onChangeEnvironments)"></associated-endpoints-selector>
|
||||
</div>
|
||||
|
||||
<div class="-mx-[15px]">
|
||||
<group-association-table
|
||||
ng-if="$ctrl.model.Id === 1"
|
||||
title="'Unassociated environments'"
|
||||
empty-content-message="'No environment available'"
|
||||
query="$ctrl.unassociatedQuery"
|
||||
></group-association-table>
|
||||
</div>
|
||||
|
||||
<!-- actions -->
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="$ctrl.formAction()"
|
||||
ng-disabled="$ctrl.actionInProgress || !endpointGroupForm.$valid"
|
||||
button-spinner="$ctrl.actionInProgress"
|
||||
>
|
||||
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
||||
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
@@ -1,31 +0,0 @@
|
||||
import angular from 'angular';
|
||||
|
||||
class GroupFormController {
|
||||
/* @ngInject */
|
||||
constructor($async, $scope, GroupService, Notifications, Authentication) {
|
||||
this.$async = $async;
|
||||
this.$scope = $scope;
|
||||
this.GroupService = GroupService;
|
||||
this.Notifications = Notifications;
|
||||
this.Authentication = Authentication;
|
||||
|
||||
this.state = {
|
||||
allowCreateTag: this.Authentication.isAdmin(),
|
||||
};
|
||||
|
||||
this.unassociatedQuery = {
|
||||
groupIds: [1],
|
||||
};
|
||||
|
||||
this.onChangeTags = this.onChangeTags.bind(this);
|
||||
}
|
||||
|
||||
onChangeTags(value) {
|
||||
return this.$scope.$evalAsync(() => {
|
||||
this.model.TagIds = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('portainer.app').controller('GroupFormController', GroupFormController);
|
||||
export default GroupFormController;
|
||||
@@ -1,7 +1,6 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { CustomTemplatesVariablesDefinitionField } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesDefinitionField';
|
||||
import { CustomTemplatesVariablesField } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesField';
|
||||
import { withControlledInput } from '@/react-tools/withControlledInput';
|
||||
|
||||
@@ -18,15 +17,6 @@ export const ngModule = angular
|
||||
'errors',
|
||||
])
|
||||
)
|
||||
.component('customTemplatesVariablesField', VariablesFieldAngular)
|
||||
.component(
|
||||
'customTemplatesVariablesDefinitionField',
|
||||
r2a(withControlledInput(CustomTemplatesVariablesDefinitionField), [
|
||||
'onChange',
|
||||
'value',
|
||||
'errors',
|
||||
'isVariablesNamesFromParent',
|
||||
])
|
||||
);
|
||||
.component('customTemplatesVariablesField', VariablesFieldAngular);
|
||||
|
||||
export const customTemplatesModule = ngModule.name;
|
||||
|
||||
@@ -11,32 +11,21 @@ import { withControlledInput } from '@/react-tools/withControlledInput';
|
||||
import {
|
||||
EnvironmentVariablesFieldset,
|
||||
EnvironmentVariablesPanel,
|
||||
StackEnvironmentVariablesPanel,
|
||||
envVarValidation,
|
||||
} from '@@/form-components/EnvironmentVariablesFieldset';
|
||||
import { Icon } from '@@/Icon';
|
||||
import { ReactQueryDevtoolsWrapper } from '@@/ReactQueryDevtoolsWrapper';
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { TagSelector } from '@@/TagSelector';
|
||||
import { Loading } from '@@/Widget/Loading';
|
||||
import { PasswordCheckHint } from '@@/PasswordCheckHint';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
import { TableColumnHeaderAngular } from '@@/datatables/TableHeaderCell';
|
||||
import { DashboardItem } from '@@/DashboardItem';
|
||||
import { SearchBar } from '@@/datatables/SearchBar';
|
||||
import { FallbackImage } from '@@/FallbackImage';
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
import { TeamsSelector } from '@@/TeamsSelector';
|
||||
import { TerminalTooltip } from '@@/TerminalTooltip';
|
||||
import { Terminal } from '@@/Terminal/Terminal';
|
||||
import { PortainerSelect } from '@@/form-components/PortainerSelect';
|
||||
import { Slider } from '@@/form-components/Slider';
|
||||
import { TagButton } from '@@/TagButton';
|
||||
import { BETeaserButton } from '@@/BETeaserButton';
|
||||
import { CodeEditor } from '@@/CodeEditor';
|
||||
import { HelpLink } from '@@/HelpLink';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { InlineLoader } from '@@/InlineLoader/InlineLoader';
|
||||
|
||||
import { fileUploadField } from './file-upload-field';
|
||||
import { switchField } from './switch-field';
|
||||
@@ -68,15 +57,6 @@ export const ngModule = angular
|
||||
rbacModule,
|
||||
stacksModule,
|
||||
])
|
||||
.component(
|
||||
'tagSelector',
|
||||
r2a(withUIRouter(withReactQuery(TagSelector)), [
|
||||
'allowCreate',
|
||||
'onChange',
|
||||
'value',
|
||||
'errors',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'beTeaserButton',
|
||||
r2a(BETeaserButton, [
|
||||
@@ -89,11 +69,6 @@ export const ngModule = angular
|
||||
'data-cy',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'tagButton',
|
||||
r2a(TagButton, ['value', 'label', 'title', 'onRemove'])
|
||||
)
|
||||
|
||||
.component(
|
||||
'portainerTooltip',
|
||||
r2a(Tooltip, ['message', 'position', 'className', 'setHtmlMessage', 'size'])
|
||||
@@ -130,64 +105,11 @@ export const ngModule = angular
|
||||
'showTitle',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'fallbackImage',
|
||||
r2a(FallbackImage, ['src', 'fallbackIcon', 'alt', 'className'])
|
||||
)
|
||||
.component('prIcon', r2a(Icon, ['className', 'icon', 'mode', 'size', 'spin']))
|
||||
.component(
|
||||
'reactQueryDevTools',
|
||||
r2a(withReactQuery(ReactQueryDevtoolsWrapper), [])
|
||||
)
|
||||
.component(
|
||||
'helpLink',
|
||||
r2a(withUIRouter(withReactQuery(HelpLink)), [
|
||||
'docLink',
|
||||
'target',
|
||||
'children',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'dashboardItem',
|
||||
r2a(DashboardItem, [
|
||||
'icon',
|
||||
'type',
|
||||
'value',
|
||||
'to',
|
||||
'params',
|
||||
'children',
|
||||
'pluralType',
|
||||
'isLoading',
|
||||
'isRefetching',
|
||||
'data-cy',
|
||||
'iconClass',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'datatableSearchbar',
|
||||
r2a(SearchBar, [
|
||||
'data-cy',
|
||||
'onChange',
|
||||
'value',
|
||||
'placeholder',
|
||||
'children',
|
||||
'className',
|
||||
])
|
||||
)
|
||||
.component('badgeIcon', r2a(BadgeIcon, ['icon', 'size', 'iconClass']))
|
||||
.component(
|
||||
'teamsSelector',
|
||||
r2a(TeamsSelector, [
|
||||
'onChange',
|
||||
'value',
|
||||
'dataCy',
|
||||
'inputId',
|
||||
'name',
|
||||
'placeholder',
|
||||
'teams',
|
||||
'disabled',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porSelect',
|
||||
r2a(PortainerSelect, [
|
||||
@@ -213,19 +135,6 @@ export const ngModule = angular
|
||||
'onBlur',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porSlider',
|
||||
r2a(Slider, [
|
||||
'min',
|
||||
'max',
|
||||
'step',
|
||||
'value',
|
||||
'onChange',
|
||||
'visibleTooltip',
|
||||
'dataCy',
|
||||
'disabled',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'reactCodeEditor',
|
||||
r2a(CodeEditor, [
|
||||
@@ -257,10 +166,6 @@ export const ngModule = angular
|
||||
'childrenWrapperClassName',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'inlineLoader',
|
||||
r2a(InlineLoader, ['children', 'className', 'size'])
|
||||
)
|
||||
.component('annotationsBeTeaser', r2a(AnnotationsBeTeaser, []))
|
||||
.component(
|
||||
'shellTerminal',
|
||||
@@ -290,17 +195,3 @@ withFormValidation(
|
||||
['explanation', 'showHelpMessage', 'isFoldable'],
|
||||
envVarValidation
|
||||
);
|
||||
|
||||
withFormValidation(
|
||||
ngModule,
|
||||
withUIRouter(
|
||||
withReactQuery(
|
||||
withControlledInput(StackEnvironmentVariablesPanel, {
|
||||
values: 'onChange',
|
||||
})
|
||||
)
|
||||
),
|
||||
'stackEnvironmentVariablesPanel',
|
||||
['showHelpMessage', 'isFoldable'],
|
||||
envVarValidation
|
||||
);
|
||||
|
||||
@@ -2,27 +2,11 @@ import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { RepositoriesDatatable } from '@/react/portainer/registries/repositories/ListView/RepositoriesDatatable';
|
||||
import { TagsDatatable } from '@/react/portainer/registries/repositories/ItemView/TagsDatatable/TagsDatatable';
|
||||
import { GitlabProjectTable } from '@/react/portainer/registries/CreateView/GitlabProjectsTable/GitlabProjectsTable';
|
||||
import { RegistryFormDockerhub } from '@/react/portainer/registries/CreateView/RegistryFormDockerhub/RegistryFormDockerhub';
|
||||
|
||||
export const registriesModule = angular
|
||||
.module('portainer.app.react.components.registries', [])
|
||||
.component(
|
||||
'registryRepositoriesDatatable',
|
||||
r2a(withUIRouter(withReactQuery(RepositoriesDatatable)), ['dataset'])
|
||||
)
|
||||
.component(
|
||||
'registriesRepositoryTagsDatatable',
|
||||
r2a(withUIRouter(withReactQuery(TagsDatatable)), [
|
||||
'dataset',
|
||||
'advancedFeaturesAvailable',
|
||||
'onRemove',
|
||||
'onRetag',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'gitlabProjectSelector',
|
||||
r2a(GitlabProjectTable, ['dataset', 'onChange', 'value'])
|
||||
|
||||
@@ -4,14 +4,8 @@ import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/Inte
|
||||
import { AuthenticationMethodSelector } from '@/react/portainer/settings/AuthenticationView/AuthenticationMethodSelector';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { LDAPUsersTable } from '@/react/portainer/settings/AuthenticationView/LDAPAuth/LDAPUsersTable';
|
||||
import { LDAPGroupsTable } from '@/react/portainer/settings/AuthenticationView/LDAPAuth/LDAPGroupsTable';
|
||||
import { ApplicationSettingsPanel } from '@/react/portainer/settings/SettingsView/ApplicationSettingsPanel';
|
||||
import { KubeSettingsPanel } from '@/react/portainer/settings/SettingsView/KubeSettingsPanel';
|
||||
import { HelmCertPanel } from '@/react/portainer/settings/SettingsView/HelmCertPanel';
|
||||
import { HiddenContainersPanel } from '@/react/portainer/settings/SettingsView/HiddenContainersPanel/HiddenContainersPanel';
|
||||
import { SSLSettingsPanelWrapper } from '@/react/portainer/settings/SettingsView/SSLSettingsPanel/SSLSettingsPanel';
|
||||
import { AuthStyleField } from '@/react/portainer/settings/AuthenticationView/OAuth';
|
||||
import { AutoUserProvisionToggle } from '@/react/portainer/settings/AuthenticationView/AutoUserProvisionToggle/AutoUserProvisionToggle';
|
||||
import { LdapSettingsTestLogin } from '@/react/portainer/settings/AuthenticationView/LDAPAuth/LdapSettingsTestLogin/LdapSettingsTestLogin';
|
||||
@@ -53,23 +47,6 @@ export const settingsModule = angular
|
||||
'limitedFeatureId',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'applicationSettingsPanel',
|
||||
r2a(withReactQuery(ApplicationSettingsPanel), ['onSuccess', 'settings'])
|
||||
)
|
||||
.component(
|
||||
'sslSettingsPanel',
|
||||
r2a(withReactQuery(SSLSettingsPanelWrapper), [])
|
||||
)
|
||||
.component('helmCertPanel', r2a(withReactQuery(HelmCertPanel), []))
|
||||
.component(
|
||||
'hiddenContainersPanel',
|
||||
r2a(withUIRouter(withReactQuery(HiddenContainersPanel)), [])
|
||||
)
|
||||
.component(
|
||||
'kubeSettingsPanel',
|
||||
r2a(withUIRouter(withReactQuery(KubeSettingsPanel)), ['settings'])
|
||||
)
|
||||
.component(
|
||||
'oauthAuthStyle',
|
||||
r2a(AuthStyleField, [
|
||||
|
||||
@@ -4,18 +4,11 @@ import { r2a } from '@/react-tools/react2angular';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { EffectiveAccessViewerDatatable } from '@/react/portainer/users/RolesView/AccessViewer/EffectiveAccessViewerDatatable';
|
||||
import { EffectiveAccessViewer } from '@/react/portainer/users/RolesView/AccessViewer/EffectiveAccessViewer';
|
||||
import { RbacRolesDatatable } from '@/react/portainer/users/RolesView/RbacRolesDatatable';
|
||||
|
||||
export const usersModule = angular
|
||||
.module('portainer.app.react.components.users', [])
|
||||
.component(
|
||||
'effectiveAccessViewerDatatable',
|
||||
r2a(withUIRouter(withCurrentUser(EffectiveAccessViewerDatatable)), [
|
||||
'dataset',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'effectiveAccessViewer',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(EffectiveAccessViewer))), [
|
||||
|
||||
@@ -7,7 +7,6 @@ import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { CreateUserAccessToken } from '@/react/portainer/account/CreateAccessTokenView';
|
||||
import { EdgeComputeSettingsView } from '@/react/portainer/settings/EdgeComputeView/EdgeComputeSettingsView';
|
||||
import { BackupSettingsPanel } from '@/react/portainer/settings/SettingsView/BackupSettingsView/BackupSettingsPanel';
|
||||
import { SettingsView } from '@/react/portainer/settings/SettingsView/SettingsView';
|
||||
import { CreateHelmRepositoriesView } from '@/react/portainer/account/helm-repositories/CreateHelmRepositoryView';
|
||||
|
||||
@@ -53,11 +52,6 @@ export const viewsModule = angular
|
||||
['onSubmit', 'settings']
|
||||
)
|
||||
)
|
||||
|
||||
.component(
|
||||
'backupSettingsPanel',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(BackupSettingsPanel))), [])
|
||||
)
|
||||
.component(
|
||||
'settingsView',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(SettingsView))), [])
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { array, object, string } from 'yup';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withControlledInput } from '@/react-tools/withControlledInput';
|
||||
|
||||
import { InputList } from '@@/form-components/InputList';
|
||||
import { ItemProps } from '@@/form-components/InputList/InputList';
|
||||
import { InputGroup } from '@@/form-components/InputGroup';
|
||||
@@ -69,8 +66,3 @@ export function gpusListValidation() {
|
||||
});
|
||||
return array().of(gpuShape).default([]);
|
||||
}
|
||||
|
||||
export const GpusListAngular = r2a(withControlledInput(GpusList), [
|
||||
'value',
|
||||
'onChange',
|
||||
]);
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
import { useMemo } from 'react';
|
||||
import { compact } from 'lodash';
|
||||
|
||||
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
import { EventsDatatable } from '../../components/EventsDatatable';
|
||||
import { useEvents } from '../../queries/useEvents';
|
||||
import { AppKind } from '../types';
|
||||
import { useApplication } from '../queries/useApplication';
|
||||
import { useApplicationServices } from '../queries/useApplicationServices';
|
||||
import { useApplicationPods } from '../queries/useApplicationPods';
|
||||
|
||||
const storageKey = 'k8sAppEventsDatatable';
|
||||
const settingsStore = createStore(storageKey, { id: 'Date', desc: true });
|
||||
|
||||
export function ApplicationEventsDatatable() {
|
||||
const tableState = useTableState(settingsStore, storageKey);
|
||||
const {
|
||||
params: {
|
||||
namespace,
|
||||
name,
|
||||
'resource-type': appKind,
|
||||
endpointId: environmentId,
|
||||
},
|
||||
} = useCurrentStateAndParams();
|
||||
|
||||
const { relatedEvents, isInitialLoading } = useApplicationEvents(
|
||||
environmentId,
|
||||
namespace,
|
||||
name,
|
||||
appKind,
|
||||
{
|
||||
autoRefreshRate: tableState.autoRefreshRate,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<EventsDatatable
|
||||
dataset={relatedEvents}
|
||||
tableState={tableState}
|
||||
isLoading={isInitialLoading}
|
||||
data-cy="k8sAppDetail-eventsTable"
|
||||
noWidget
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function useApplicationEvents(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
name: string,
|
||||
appKind?: AppKind,
|
||||
options?: { autoRefreshRate?: number; yaml?: boolean }
|
||||
) {
|
||||
const { data: application, ...applicationQuery } = useApplication(
|
||||
environmentId,
|
||||
namespace,
|
||||
name,
|
||||
appKind
|
||||
);
|
||||
const servicesQuery = useApplicationServices(
|
||||
environmentId,
|
||||
namespace,
|
||||
name,
|
||||
application
|
||||
);
|
||||
const podsQuery = useApplicationPods(
|
||||
environmentId,
|
||||
namespace,
|
||||
name,
|
||||
application
|
||||
);
|
||||
|
||||
// related events are events that have the application id, or the id of a service or pod from the application
|
||||
const relatedUids = useMemo(() => {
|
||||
const serviceIds = compact(
|
||||
servicesQuery.data?.map((service) => service?.metadata?.uid)
|
||||
);
|
||||
const podIds = compact(podsQuery.data?.map((pod) => pod?.metadata?.uid));
|
||||
return [application?.metadata?.uid, ...serviceIds, ...podIds];
|
||||
}, [application?.metadata?.uid, podsQuery.data, servicesQuery.data]);
|
||||
|
||||
const relatedUidsSet = useMemo(() => new Set(relatedUids), [relatedUids]);
|
||||
const { data: events, ...eventsQuery } = useEvents(environmentId, {
|
||||
namespace,
|
||||
queryOptions: {
|
||||
autoRefreshRate: options?.autoRefreshRate
|
||||
? options.autoRefreshRate * 1000
|
||||
: undefined,
|
||||
select: (data) =>
|
||||
data.filter((event) => relatedUidsSet.has(event.involvedObject.uid)),
|
||||
},
|
||||
});
|
||||
|
||||
const isInitialLoading =
|
||||
applicationQuery.isInitialLoading ||
|
||||
servicesQuery.isInitialLoading ||
|
||||
podsQuery.isInitialLoading ||
|
||||
eventsQuery.isInitialLoading;
|
||||
|
||||
return { relatedEvents: events || [], isInitialLoading };
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
export { ApplicationSummaryWidget } from './ApplicationSummaryWidget';
|
||||
export { ApplicationDetailsWidget } from './ApplicationDetailsWidget/ApplicationDetailsWidget';
|
||||
export { ApplicationEventsDatatable } from './ApplicationEventsDatatable';
|
||||
|
||||
-269
@@ -1,269 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Route from '@/assets/ico/route.svg?c';
|
||||
|
||||
import { confirm } from '@@/modals/confirm';
|
||||
import { ModalType } from '@@/modals';
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { Button, ButtonGroup } from '@@/buttons';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
|
||||
import { IngressControllerClassMap } from '../types';
|
||||
|
||||
import { columns } from './columns';
|
||||
|
||||
const storageKey = 'ingressClasses';
|
||||
const settingsStore = createPersistedStore(storageKey, 'name');
|
||||
|
||||
interface Props {
|
||||
onChangeControllers: (
|
||||
controllerClassMap: IngressControllerClassMap[]
|
||||
) => void; // angular function to save the ingress class list
|
||||
description: string;
|
||||
ingressControllers: IngressControllerClassMap[] | undefined;
|
||||
initialIngressControllers: IngressControllerClassMap[] | undefined;
|
||||
allowNoneIngressClass: boolean;
|
||||
isLoading: boolean;
|
||||
view: string;
|
||||
}
|
||||
|
||||
// This is a legacy component that has more state logic than the new one, for angular views
|
||||
// Delete this component when the namespace edit view is migrated to react
|
||||
export function IngressClassDatatableAngular({
|
||||
onChangeControllers,
|
||||
description,
|
||||
initialIngressControllers,
|
||||
ingressControllers,
|
||||
allowNoneIngressClass,
|
||||
isLoading,
|
||||
view,
|
||||
}: Props) {
|
||||
const tableState = useTableState(settingsStore, storageKey);
|
||||
|
||||
const [ingControllerFormValues, setIngControllerFormValues] = useState(
|
||||
ingressControllers || []
|
||||
);
|
||||
|
||||
// set the ingress controller form values when the ingress controller list changes
|
||||
// and the ingress controller form values are not set
|
||||
useEffect(() => {
|
||||
if (
|
||||
ingressControllers &&
|
||||
ingControllerFormValues.length !== ingressControllers.length
|
||||
) {
|
||||
setIngControllerFormValues(ingressControllers);
|
||||
}
|
||||
}, [ingressControllers, ingControllerFormValues]);
|
||||
|
||||
useEffect(() => {
|
||||
if (allowNoneIngressClass === undefined || isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newIngFormValues: IngressControllerClassMap[];
|
||||
const isCustomTypeExist = ingControllerFormValues.some(
|
||||
(ic) => ic.Type === 'custom'
|
||||
);
|
||||
if (allowNoneIngressClass) {
|
||||
newIngFormValues = [...ingControllerFormValues];
|
||||
// add the ingress controller type 'custom' with a 'none' ingress class name
|
||||
if (!isCustomTypeExist) {
|
||||
newIngFormValues.push({
|
||||
Name: 'none',
|
||||
ClassName: 'none',
|
||||
Type: 'custom',
|
||||
Availability: true,
|
||||
New: false,
|
||||
Used: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
newIngFormValues = ingControllerFormValues.filter(
|
||||
(ingController) => ingController.ClassName !== 'none'
|
||||
);
|
||||
}
|
||||
setIngControllerFormValues(newIngFormValues);
|
||||
onChangeControllers(newIngFormValues);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [allowNoneIngressClass, onChangeControllers]);
|
||||
|
||||
return (
|
||||
<div className="-mx-[15px]">
|
||||
<Datatable
|
||||
settingsManager={tableState}
|
||||
dataset={ingControllerFormValues || []}
|
||||
columns={columns}
|
||||
isLoading={isLoading}
|
||||
title="Ingress Controllers"
|
||||
titleIcon={Route}
|
||||
getRowId={(row) => `${row.Name}-${row.ClassName}-${row.Type}`}
|
||||
renderTableActions={(selectedRows) => renderTableActions(selectedRows)}
|
||||
description={renderIngressClassDescription()}
|
||||
data-cy="k8s-ingress-classes-datatable"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
function renderTableActions(selectedRows: IngressControllerClassMap[]) {
|
||||
return (
|
||||
<div className="flex items-start">
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={
|
||||
selectedRows.filter((row) => row.Availability === true).length ===
|
||||
0
|
||||
}
|
||||
color="dangerlight"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
updateIngressControllers(
|
||||
selectedRows,
|
||||
ingControllerFormValues || [],
|
||||
false
|
||||
)
|
||||
}
|
||||
data-cy="k8s-disallow-selected-ingress-controllers-button"
|
||||
>
|
||||
Disallow selected
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
selectedRows.filter((row) => row.Availability === false)
|
||||
.length === 0
|
||||
}
|
||||
color="default"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
updateIngressControllers(
|
||||
selectedRows,
|
||||
ingControllerFormValues || [],
|
||||
true
|
||||
)
|
||||
}
|
||||
data-cy="k8s-allow-selected-ingress-controllers-button"
|
||||
>
|
||||
Allow selected
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderIngressClassDescription() {
|
||||
return (
|
||||
<div className="text-muted flex w-full flex-col !text-xs">
|
||||
<div className="mt-1">{description}</div>
|
||||
{initialIngressControllers &&
|
||||
ingControllerFormValues &&
|
||||
isUnsavedChanges(
|
||||
initialIngressControllers,
|
||||
ingControllerFormValues
|
||||
) && <TextTip>Unsaved changes.</TextTip>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function updateIngressControllers(
|
||||
selectedRows: IngressControllerClassMap[],
|
||||
ingControllerFormValues: IngressControllerClassMap[],
|
||||
availability: boolean
|
||||
) {
|
||||
const updatedIngressControllers = getUpdatedIngressControllers(
|
||||
selectedRows,
|
||||
ingControllerFormValues || [],
|
||||
availability
|
||||
);
|
||||
|
||||
if (ingressControllers && ingressControllers.length) {
|
||||
const newAllowed = updatedIngressControllers.map(
|
||||
(ingController) => ingController.Availability
|
||||
);
|
||||
if (view === 'namespace') {
|
||||
setIngControllerFormValues(updatedIngressControllers);
|
||||
onChangeControllers(updatedIngressControllers);
|
||||
return;
|
||||
}
|
||||
|
||||
const usedControllersToDisallow = ingressControllers.filter(
|
||||
(ingController, index) => {
|
||||
// if any of the current controllers are allowed, and are used, then become disallowed, then add the controller to a new list
|
||||
if (
|
||||
ingController.Availability &&
|
||||
ingController.Used &&
|
||||
!newAllowed[index]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
if (usedControllersToDisallow.length > 0) {
|
||||
const confirmed = await confirm({
|
||||
title: 'Disallow in-use ingress controllers?',
|
||||
modalType: ModalType.Warn,
|
||||
message: (
|
||||
<div>
|
||||
<p>
|
||||
There are ingress controllers you want to disallow that are in
|
||||
use:
|
||||
</p>
|
||||
<ul className="ml-6">
|
||||
{usedControllersToDisallow.map((controller) => (
|
||||
<li key={controller.ClassName}>{controller.ClassName}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p>
|
||||
No new ingress rules can be created for the disallowed
|
||||
controllers.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
confirmButton: buildConfirmButton('Disallow', 'warning'),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
setIngControllerFormValues(updatedIngressControllers);
|
||||
onChangeControllers(updatedIngressControllers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isUnsavedChanges(
|
||||
oldIngressControllers: IngressControllerClassMap[],
|
||||
newIngressControllers: IngressControllerClassMap[]
|
||||
) {
|
||||
if (oldIngressControllers.length !== newIngressControllers.length) {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < newIngressControllers.length; i += 1) {
|
||||
if (
|
||||
oldIngressControllers[i]?.Availability !==
|
||||
newIngressControllers[i]?.Availability
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getUpdatedIngressControllers(
|
||||
selectedRows: IngressControllerClassMap[],
|
||||
allRows: IngressControllerClassMap[],
|
||||
allow: boolean
|
||||
) {
|
||||
const selectedRowClassNames = selectedRows.map((row) => row.ClassName);
|
||||
const updatedIngressControllers = allRows?.map((row) => {
|
||||
if (selectedRowClassNames.includes(row.ClassName)) {
|
||||
return { ...row, Availability: allow };
|
||||
}
|
||||
return row;
|
||||
});
|
||||
return updatedIngressControllers;
|
||||
}
|
||||
Reference in New Issue
Block a user