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

refactor(ui/box-selector): replace all selectors [EE-3856] (#7902)

This commit is contained in:
Chaim Lev-Ari 2023-02-07 09:03:57 +05:30 committed by GitHub
parent c9253319d9
commit 2dddc1c6b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 1267 additions and 1011 deletions

View file

@ -35,12 +35,14 @@ export const BoxSelectorAngular: IComponentOptions = {
on-change="$ctrl.handleChange"
options="$ctrl.options"
radio-name="$ctrl.radioName"
slim="$ctrl.slim"
></box-selector-react>`,
bindings: {
value: '<',
onChange: '<',
options: '<',
radioName: '<',
slim: '<',
},
require: {
formCtrl: '^form',

View file

@ -8,10 +8,12 @@ import { BoxSelectorAngular } from './BoxSelectorAngular';
export { buildOption } from './utils';
const BoxSelectorReact = react2angular(BoxSelector, [
'isMulti',
'value',
'onChange',
'options',
'radioName',
'slim',
]);
export const boxSelectorModule = angular

View file

@ -4,11 +4,11 @@ import { BoxSelectorOption } from '@@/BoxSelector/types';
import { IconProps } from '@@/Icon';
export function buildOption<T extends number | string>(
id: string,
id: BoxSelectorOption<T>['id'],
icon: IconProps['icon'],
label: string,
description: string,
value: T,
label: BoxSelectorOption<T>['label'],
description: BoxSelectorOption<T>['description'],
value: BoxSelectorOption<T>['value'],
feature?: FeatureId
): BoxSelectorOption<T> {
return { id, icon, label, description, value, feature };

View file

@ -15,57 +15,16 @@
</div>
<!-- !access-control-switch -->
<!-- restricted-access -->
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_administrators" ng-model="$ctrl.formData.Ownership" value="administrators" />
<label for="access_administrators" data-cy="portainer-selectAdminAccess">
<div class="boxselector_header">
<pr-icon icon="'eye-off'"></pr-icon>
Administrators
</div>
<p class="boxselector_content">I want to restrict the management of this resource to administrators only</p>
</label>
</div>
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_restricted" ng-model="$ctrl.formData.Ownership" value="restricted" />
<label for="access_restricted" data-cy="portainer-selectRestrictedAccess">
<div class="boxselector_header">
<pr-icon icon="'users'"></pr-icon>
Restricted
</div>
<p class="boxselector_content"> I want to restrict the management of this resource to a set of users and/or teams </p>
</label>
</div>
<div ng-if="!$ctrl.isAdmin">
<input type="radio" id="access_private" ng-model="$ctrl.formData.Ownership" value="private" />
<label for="access_private">
<div class="boxselector_header">
<pr-icon icon="'eye-off'"></pr-icon>
Private
</div>
<p> I want to restrict this resource to be manageable by myself only </p>
</label>
</div>
<div ng-if="!$ctrl.isAdmin && $ctrl.availableTeams.length > 0">
<input type="radio" id="access_restricted" ng-model="$ctrl.formData.Ownership" value="restricted" />
<label for="access_restricted">
<div class="boxselector_header">
<pr-icon icon="'users'"></pr-icon>
Restricted
</div>
<p ng-if="$ctrl.availableTeams.length === 1">
I want any member of my team (<b>{{ $ctrl.availableTeams[0].Name }}</b
>) to be able to manage this resource
</p>
<p ng-if="$ctrl.availableTeams.length > 1"> I want to restrict the management of this resource to one or more of my teams </p>
</label>
</div>
</div>
</div>
</div>
<access-type-selector
ng-if="$ctrl.formData.AccessControlEnabled"
value="$ctrl.formData.Ownership"
on-change="($ctrl.onChangeOwnership)"
is-admin="$ctrl.isAdmin"
name="Ownership"
teams="$ctrl.availableTeams"
></access-type-selector>
<!-- restricted-access -->
<!-- authorized-teams -->
<div

View file

@ -21,6 +21,13 @@ angular.module('portainer.app').controller('porAccessControlFormController', [
ctrl.availableTeams = [];
ctrl.availableUsers = [];
ctrl.onChangeEnablement = onChangeEnablement;
ctrl.onChangeOwnership = onChangeOwnership;
function onChangeOwnership(ownership) {
onChange({ Ownership: ownership });
}
function setOwnership(resourceControl, isAdmin) {
if (isAdmin && resourceControl.Ownership === RCO.PRIVATE) {
ctrl.formData.Ownership = RCO.RESTRICTED;
@ -88,15 +95,11 @@ angular.module('portainer.app').controller('porAccessControlFormController', [
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve access control information');
});
}
this.onChangeEnablement = function (enable) {
$scope.$evalAsync(() => {
ctrl.formData.AccessControlEnabled = enable;
if (enable) {
ctrl.formData.Ownership = isAdmin ? RCO.ADMINISTRATORS : RCO.PRIVATE;
}
});
};
function onChangeEnablement(enable) {
const isAdmin = Authentication.isAdmin();
onChange({ AccessControlEnabled: enable, Ownership: isAdmin ? RCO.ADMINISTRATORS : RCO.PRIVATE });
}
},
]);

View file

@ -22,55 +22,16 @@
</span>
</div>
</div>
<div class="form-group"></div>
<!-- endpoint-tls-mode -->
<div class="form-group" ng-if="$ctrl.formData.TLS">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="tls_client_ca" ng-model="$ctrl.formData.TLSMode" value="tls_client_ca" />
<label for="tls_client_ca">
<div class="boxselector_header">
<pr-icon icon="'shield'"></pr-icon>
TLS with server and client verification
</div>
<p>Use client certificates and server verification</p>
</label>
</div>
<div>
<input type="radio" id="tls_client_noca" ng-model="$ctrl.formData.TLSMode" value="tls_client_noca" />
<label for="tls_client_noca">
<div class="boxselector_header">
<pr-icon icon="'shield'"></pr-icon>
TLS with client verification only
</div>
<p>Use client certificates without server verification</p>
</label>
</div>
<div>
<input type="radio" id="tls_ca" ng-model="$ctrl.formData.TLSMode" value="tls_ca" />
<label for="tls_ca">
<div class="boxselector_header">
<pr-icon icon="'shield'"></pr-icon>
TLS with server verification only
</div>
<p>Only verify the server certificate</p>
</label>
</div>
<div>
<input type="radio" id="tls_only" ng-model="$ctrl.formData.TLSMode" value="tls_only" />
<label for="tls_only">
<div class="boxselector_header">
<pr-icon icon="'shield'"></pr-icon>
TLS only
</div>
<p>No server/client verification</p>
</label>
</div>
</div>
</div>
</div>
<!-- !endpoint-tls-mode -->
<box-selector
ng-if="$ctrl.formData.TLS"
slim="true"
radio-name="'tls_mode'"
options="$ctrl.tlsOptions"
value="$ctrl.formData.TLSMode"
on-change="($ctrl.onChangeTLSMode)"
></box-selector>
<div class="col-sm-12 form-section-title" ng-if="$ctrl.formData.TLS && $ctrl.formData.TLSMode !== 'tls_only'"> Required TLS files </div>
<!-- tls-file-upload -->
<div ng-if="$ctrl.formData.TLS">

View file

@ -1,13 +1,30 @@
import { tlsOptions } from './tls-options';
angular.module('portainer.app').controller('porEndpointSecurityController', [
'$scope',
function ($scope) {
var ctrl = this;
ctrl.onToggleTLS = function (newValue) {
this.tlsOptions = tlsOptions;
function onChange(values) {
$scope.$evalAsync(() => {
ctrl.formData.TLS = newValue;
ctrl.formData = {
...ctrl.formData,
...values,
};
});
};
}
ctrl.onChangeTLSMode = onChangeTLSMode;
function onChangeTLSMode(mode) {
onChange({ TLSMode: mode });
}
ctrl.onToggleTLS = onToggleTLS;
function onToggleTLS(newValue) {
onChange({ TLS: newValue });
}
this.$onInit = $onInit;
function $onInit() {

View file

@ -0,0 +1,38 @@
import { Shield } from 'lucide-react';
import { BoxSelectorOption } from '@@/BoxSelector';
export const tlsOptions: ReadonlyArray<BoxSelectorOption<string>> = [
{
id: 'tls_client_ca',
value: 'tls_client_ca',
icon: Shield,
iconType: 'badge',
label: 'TLS with server and client verification',
description: 'Use client certificates and server verification',
},
{
id: 'tls_client_noca',
value: 'tls_client_noca',
icon: Shield,
iconType: 'badge',
label: 'TLS with client verification only',
description: 'Use client certificates without server verification',
},
{
id: 'tls_ca',
value: 'tls_ca',
icon: Shield,
iconType: 'badge',
label: 'TLS with server verification only',
description: 'Only verify the server certificate',
},
{
id: 'tls_only',
value: 'tls_only',
icon: Shield,
iconType: 'badge',
label: 'TLS only',
description: 'No server/client verification',
},
] as const;

View file

@ -18,6 +18,7 @@ import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/Inte
import { PorAccessControlFormTeamSelector } from '@/react/portainer/access-control/PorAccessControlForm/TeamsSelector';
import { PorAccessControlFormUserSelector } from '@/react/portainer/access-control/PorAccessControlForm/UsersSelector';
import { PorAccessManagementUsersSelector } from '@/react/portainer/access-control/AccessManagement/PorAccessManagementUsersSelector';
import { AccessTypeSelector } from '@/react/portainer/access-control/EditDetails/AccessTypeSelector';
import { PageHeader } from '@@/PageHeader';
import { TagSelector } from '@@/TagSelector';
@ -66,6 +67,17 @@ export const componentsModule = angular
'tagButton',
r2a(TagButton, ['value', 'label', 'title', 'onRemove'])
)
.component(
'accessTypeSelector',
r2a(AccessTypeSelector, [
'isAdmin',
'isPublicVisible',
'name',
'onChange',
'value',
'teams',
])
)
.component(
'portainerTooltip',
r2a(Tooltip, ['message', 'position', 'className', 'setHtmlMessage'])

View file

@ -15,42 +15,15 @@
<!-- build-method -->
<div ng-if="!$ctrl.state.fromStack">
<div class="col-sm-12 form-section-title"> Build method </div>
<div class="form-group">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="method_editor" ng-model="$ctrl.state.Method" value="editor" ng-change="$ctrl.onChangeMethod()" />
<label for="method_editor">
<div class="boxselector_header">
<pr-icon icon="'edit'"></pr-icon>
Web editor
</div>
<p>Use our Web editor</p>
</label>
</div>
<div>
<input type="radio" id="method_upload" ng-model="$ctrl.state.Method" value="upload" ng-change="$ctrl.onChangeMethod()" />
<label for="method_upload">
<div class="boxselector_header">
<pr-icon icon="'upload'"></pr-icon>
Upload
</div>
<p>Upload from your computer</p>
</label>
</div>
<div>
<input type="radio" id="method_repository" ng-model="$ctrl.state.Method" value="repository" ng-change="$ctrl.onChangeMethod()" />
<label for="method_repository">
<div class="boxselector_header">
<pr-icon icon="'git-pull-request'"></pr-icon>
Repository
</div>
<p>Use a git repository</p>
</label>
</div>
</div>
</div>
</div>
<box-selector
slim="true"
options="$ctrl.buildMethods"
value="$ctrl.state.Method"
on-change="($ctrl.onChangeMethod)"
radio-name="'buildMethod'"
slim="true"
></box-selector>
</div>
<!-- !build-method -->
<!-- web-editor -->

View file

@ -3,14 +3,29 @@ import { AccessControlFormData } from 'Portainer/components/accessControlForm/po
import { TEMPLATE_NAME_VALIDATION_REGEX } from '@/constants';
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { editor, upload, git } from '@@/BoxSelector/common-options/build-methods';
class CreateCustomTemplateViewController {
/* @ngInject */
constructor($async, $state, $window, Authentication, ModalService, CustomTemplateService, FormValidator, Notifications, ResourceControlService, StackService, StateManager) {
constructor(
$async,
$state,
$scope,
$window,
Authentication,
ModalService,
CustomTemplateService,
FormValidator,
Notifications,
ResourceControlService,
StackService,
StateManager
) {
Object.assign(this, {
$async,
$state,
$window,
$scope,
Authentication,
ModalService,
CustomTemplateService,
@ -21,6 +36,8 @@ class CreateCustomTemplateViewController {
StateManager,
});
this.buildMethods = [editor, upload, git];
this.isTemplateVariablesEnabled = isBE;
this.formValues = {
@ -85,10 +102,13 @@ class CreateCustomTemplateViewController {
return this.$async(this.createCustomTemplateAsync);
}
onChangeMethod() {
this.formValues.FileContent = '';
this.formValues.Variables = [];
this.selectedTemplate = null;
onChangeMethod(method) {
return this.$scope.$evalAsync(() => {
this.formValues.FileContent = '';
this.formValues.Variables = [];
this.selectedTemplate = null;
this.state.Method = method;
});
}
async createCustomTemplateAsync() {

View file

@ -16,22 +16,8 @@
<!-- !name-input -->
<!-- build-method -->
<div class="col-sm-12 form-section-title"> Profile configuration </div>
<div class="form-group">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="method_editor" ng-model="state.method" value="editor" />
<label for="method_editor">
<div class="boxselector_header">
<pr-icon icon="'edit'"></pr-icon>
Web editor
</div>
<p>Use our Web editor</p>
</label>
</div>
</div>
</div>
</div>
<box-selector slim="true" options="buildMethods" value="state.method"></box-selector>
<!-- !build-method -->
<web-editor-form

View file

@ -1,10 +1,13 @@
import angular from 'angular';
import { editor } from '@@/BoxSelector/common-options/build-methods';
import { createProfile } from 'Portainer/hostmanagement/fdo/fdo.service';
angular.module('portainer.app').controller('AddProfileController', AddProfileController);
export default function AddProfileController($scope, $async, $state, $window, ModalService, Authentication, Notifications) {
$scope.buildMethods = [editor];
$scope.formValues = {
name: '',
profileFileContent: '',

View file

@ -16,23 +16,9 @@
<!-- !name-input -->
<!-- build-method -->
<div class="col-sm-12 form-section-title"> Profile configuration </div>
<div class="form-group"></div>
<div class="form-group">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="method_editor" ng-model="state.method" value="editor" />
<label for="method_editor">
<div class="boxselector_header">
<pr-icon icon="'edit'"></pr-icon>
Web editor
</div>
<p>Use our Web editor</p>
</label>
</div>
</div>
</div>
</div>
<box-selector slim="true" options="buildMethods" value="state.method"></box-selector>
<!-- !build-method -->
<web-editor-form

View file

@ -1,9 +1,12 @@
import angular from 'angular';
import { editor } from '@@/BoxSelector/common-options/build-methods';
import { getProfile, updateProfile } from 'Portainer/hostmanagement/fdo/fdo.service';
angular.module('portainer.app').controller('EditProfileController', EditProfileController);
export default function EditProfileController($scope, $async, $state, $window, ModalService, Authentication, Notifications) {
$scope.buildMethods = [editor];
$scope.formValues = {
name: '',
profileFileContent: '',

View file

@ -131,37 +131,9 @@
</span>
</div>
</div>
<!-- !note -->
<div class="form-group">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="restore_file" checked="checked" />
<label for="restore_file" data-cy="init-selectLocalFile">
<div class="boxselector_header">
<pr-icon icon="'upload'"></pr-icon>
Upload backup file
</div>
<p></p>
</label>
</div>
<div>
<input type="radio" id="restore_s3" disabled />
<label for="restore_s3" class="boxselector_disabled">
<div class="boxselector_header">
<pr-icon icon="'download'"></pr-icon>
Retrieve from S3
</div>
<p
>This feature is available in
<a class="hyperlink" href="https://www.portainer.io/business-upsell?from=restore-s3-form" target="_blank"> Portainer Business Edition</a></p
>
</label>
</div>
</div>
</div>
</div>
<!-- note -->
<box-selector slim="true" options="restoreOptions" value="formValues.restoreFormType" on-change="(onChangeRestoreType)" radio-name="'restore-type'"></box-selector>
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted"> You can upload a backup file from your computer. </span>

View file

@ -1,4 +1,5 @@
import { getEnvironments } from '@/react/portainer/environments/environment.service';
import { restoreOptions } from './restore-options';
angular.module('portainer.app').controller('InitAdminController', [
'$scope',
@ -11,6 +12,8 @@ angular.module('portainer.app').controller('InitAdminController', [
'BackupService',
'StatusService',
function ($scope, $state, Notifications, Authentication, StateManager, SettingsService, UserService, BackupService, StatusService) {
$scope.restoreOptions = restoreOptions;
$scope.uploadBackup = uploadBackup;
$scope.logo = StateManager.getState().application.logo;
@ -35,6 +38,13 @@ angular.module('portainer.app').controller('InitAdminController', [
$scope.state.showRestorePortainer = !$scope.state.showRestorePortainer;
};
$scope.onChangeRestoreType = onChangeRestoreType;
function onChangeRestoreType(value) {
$scope.$evalAsync(() => {
$scope.formValues.restoreFormType = value;
});
}
$scope.createAdminUser = function () {
var username = $scope.formValues.Username;
var password = $scope.formValues.Password;

View file

@ -0,0 +1,23 @@
import { Download, Upload } from 'lucide-react';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { BoxSelectorOption } from '@@/BoxSelector';
export const restoreOptions: ReadonlyArray<BoxSelectorOption<string>> = [
{
id: 'restore_file',
value: 'file',
icon: Upload,
iconType: 'badge',
label: 'Upload backup file',
},
{
id: 'restore_s3',
value: 's3',
icon: Download,
iconType: 'badge',
label: 'Retrieve from S3',
feature: FeatureId.S3_RESTORE,
},
] as const;

View file

@ -278,7 +278,7 @@
<form class="form-horizontal" ng-submit="backupPortainer()" name="backupPortainerForm">
<div class="col-sm-12 form-section-title"> Backup configuration </div>
<box-selector options="backupOptions" value="formValues.backupFormType" on-change="(onBackupOptionsChange)" radio-name="'backupOptions'"></box-selector>
<box-selector slim="true" options="backupOptions" value="formValues.backupFormType" on-change="(onBackupOptionsChange)" radio-name="'backupOptions'"></box-selector>
<div ng-if="formValues.backupFormType === BACKUP_FORM_TYPES.S3">
<!-- Schedule automatic backups -->

View file

@ -7,6 +7,7 @@ import { RepositoryMechanismTypes } from '@/kubernetes/models/deploy';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { renderTemplate } from '@/react/portainer/custom-templates/components/utils';
import { editor, upload, git, customTemplate } from '@@/BoxSelector/common-options/build-methods';
angular
.module('portainer.app')
@ -37,6 +38,7 @@ angular
$scope.isTemplateVariablesEnabled = isBE;
$scope.buildAnalyticsProperties = buildAnalyticsProperties;
$scope.stackWebhookFeature = FeatureId.STACK_WEBHOOK;
$scope.buildMethods = [editor, upload, git, customTemplate];
$scope.STACK_NAME_VALIDATION_REGEX = STACK_NAME_VALIDATION_REGEX;
$scope.isAdmin = Authentication.isAdmin();
@ -83,6 +85,13 @@ angular
});
$scope.onChangeFormValues = onChangeFormValues;
$scope.onBuildMethodChange = onBuildMethodChange;
function onBuildMethodChange(value) {
$scope.$evalAsync(() => {
$scope.state.Method = value;
});
}
$scope.onEnableWebhookChange = function (enable) {
$scope.$evalAsync(() => {

View file

@ -49,52 +49,9 @@
</div>
<!-- build-method -->
<div class="col-sm-12 form-section-title"> Build method </div>
<div class="form-group">
<div class="col-sm-12">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="method_editor" ng-model="state.Method" value="editor" />
<label for="method_editor">
<div class="boxselector_header">
<pr-icon icon="'edit'"></pr-icon>
Web editor
</div>
<p>Use our Web editor</p>
</label>
</div>
<div>
<input type="radio" id="method_upload" ng-model="state.Method" value="upload" />
<label for="method_upload">
<div class="boxselector_header">
<pr-icon icon="'upload'"></pr-icon>
Upload
</div>
<p>Upload from your computer</p>
</label>
</div>
<div>
<input type="radio" id="method_repository" ng-model="state.Method" value="repository" />
<label for="method_repository">
<div class="boxselector_header">
<pr-icon icon="'git-pull-request'"></pr-icon>
Repository
</div>
<p>Use a git repository</p>
</label>
</div>
<div>
<input type="radio" id="method_template" ng-model="state.Method" value="template" />
<label for="method_template">
<div class="boxselector_header">
<pr-icon icon="'edit'"></pr-icon>
Custom template
</div>
<p>Use a custom template</p>
</label>
</div>
</div>
</div>
</div>
<box-selector slim="true" radio-name="'build-method'" options="buildMethods" value="state.Method" on-change="(onBuildMethodChange)" slim="true"></box-selector>
<!-- !build-method -->
<!-- upload -->