diff --git a/app/assets/css/app.css b/app/assets/css/app.css
index b38aaae88..8d946337d 100644
--- a/app/assets/css/app.css
+++ b/app/assets/css/app.css
@@ -626,88 +626,6 @@ ul.sidebar .sidebar-list .sidebar-sublist a.active {
margin-left: 21px;
}
-.boxselector_wrapper {
- display: flex;
- flex-flow: row wrap;
- margin: 0.5rem;
-}
-
-.boxselector_wrapper > div {
- flex: 1;
- padding: 0.5rem;
-}
-
-.boxselector_wrapper .boxselector_header {
- font-size: 14px;
- margin-bottom: 5px;
- font-weight: bold;
-}
-
-.boxselector_header .fa,
-.fab {
- font-weight: normal;
-}
-
-.boxselector_wrapper input[type='radio'] {
- display: none;
-}
-
-.boxselector_wrapper input[type='radio']:not(:disabled) ~ label {
- cursor: pointer;
-}
-
-.boxselector_wrapper label {
- font-weight: normal;
- font-size: 12px;
- display: block;
- background: white;
- border: 1px solid #333333;
- border-radius: 2px;
- padding: 10px 10px 0 10px;
- text-align: center;
- box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
- position: relative;
-}
-.boxselector_wrapper label.boxselector_disabled {
- background: #cacaca;
- border-color: #787878;
- color: #787878;
- cursor: not-allowed;
-}
-
-.boxselector_wrapper input[type='radio']:checked + label {
- background: #337ab7;
- color: white;
- padding-top: 2rem;
- border-color: #337ab7;
-}
-
-.boxselector_wrapper input[type='radio']:checked + label::after {
- color: #337ab7;
- font-family: 'Font Awesome 5 Free';
- border: 2px solid #337ab7;
- content: '\f00c';
- font-size: 16px;
- font-weight: bold;
- position: absolute;
- top: -15px;
- left: 50%;
- transform: translateX(-50%);
- height: 30px;
- width: 30px;
- line-height: 26px;
- text-align: center;
- border-radius: 50%;
- background: white;
- box-shadow: 0 2px 5px -2px rgba(0, 0, 0, 0.25);
-}
-
-@media only screen and (max-width: 700px) {
- .boxselector_wrapper {
- flex-direction: column;
- }
-}
-
.visualizer_container {
display: flex;
flex-direction: row;
diff --git a/app/kubernetes/views/deploy/deploy.html b/app/kubernetes/views/deploy/deploy.html
index 69904ae60..9ce051b75 100644
--- a/app/kubernetes/views/deploy/deploy.html
+++ b/app/kubernetes/views/deploy/deploy.html
@@ -22,110 +22,24 @@
-
+
diff --git a/app/kubernetes/views/deploy/deployController.js b/app/kubernetes/views/deploy/deployController.js
index 21d754b82..a22329aaa 100644
--- a/app/kubernetes/views/deploy/deployController.js
+++ b/app/kubernetes/views/deploy/deployController.js
@@ -1,8 +1,9 @@
import angular from 'angular';
import _ from 'lodash-es';
import stripAnsi from 'strip-ansi';
-import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, KubernetesDeployRequestMethods } from 'Kubernetes/models/deploy';
+import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, KubernetesDeployRequestMethods } from 'Kubernetes/models/deploy';
+import { buildOption } from '@/portainer/components/box-selector';
class KubernetesDeployController {
/* @ngInject */
constructor($async, $state, $window, ModalService, Notifications, EndpointProvider, KubernetesResourcePoolService, StackService) {
@@ -15,11 +16,37 @@ class KubernetesDeployController {
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.StackService = StackService;
+ this.deployOptions = [
+ buildOption('method_kubernetes', 'fa fa-cubes', 'Kubernetes', 'Kubernetes manifest format', KubernetesDeployManifestTypes.KUBERNETES),
+ buildOption('method_compose', 'fa fa-docker', 'Compose', 'docker-compose format', KubernetesDeployManifestTypes.COMPOSE),
+ ];
+
+ this.methodOptions = [
+ buildOption('method_repo', 'fab fa-github', 'Git Repository', 'Use a git repository', KubernetesDeployBuildMethods.GIT),
+ buildOption('method_editor', 'fa fa-edit', 'Web editor', 'Use our Web editor', KubernetesDeployBuildMethods.WEB_EDITOR),
+ ];
+
+ this.state = {
+ DeployType: KubernetesDeployManifestTypes.KUBERNETES,
+ BuildMethod: KubernetesDeployBuildMethods.GIT,
+ tabLogsDisabled: true,
+ activeTab: 0,
+ viewReady: false,
+ isEditorDirty: false,
+ };
+
+ this.formValues = {};
+ this.ManifestDeployTypes = KubernetesDeployManifestTypes;
+ this.BuildMethods = KubernetesDeployBuildMethods;
+ this.endpointId = this.EndpointProvider.endpointID();
+
this.onInit = this.onInit.bind(this);
this.deployAsync = this.deployAsync.bind(this);
- this.editorUpdate = this.editorUpdate.bind(this);
- this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
+ this.onChangeFileContent = this.onChangeFileContent.bind(this);
this.getNamespacesAsync = this.getNamespacesAsync.bind(this);
+ this.onChangeFormValues = this.onChangeFormValues.bind(this);
+ this.onRepoUrlChange = this.onRepoUrlChange.bind(this);
+ this.onRepoRefChange = this.onRepoRefChange.bind(this);
}
disableDeploy() {
@@ -33,13 +60,24 @@ class KubernetesDeployController {
return isGitFormInvalid || isWebEditorInvalid || _.isEmpty(this.formValues.Namespace) || this.state.actionInProgress;
}
- async editorUpdateAsync(cm) {
- this.formValues.EditorContent = cm.getValue();
- this.state.isEditorDirty = true;
+ onChangeFormValues(values) {
+ this.formValues = {
+ ...this.formValues,
+ ...values,
+ };
}
- editorUpdate(cm) {
- return this.$async(this.editorUpdateAsync, cm);
+ onRepoUrlChange(value) {
+ this.onChangeFormValues({ RepositoryURL: value });
+ }
+
+ onRepoRefChange(value) {
+ this.onChangeFormValues({ RepositoryReferenceName: value });
+ }
+
+ onChangeFileContent(value) {
+ this.formValues.EditorContent = value;
+ this.state.isEditorDirty = true;
}
displayErrorLog(log) {
@@ -120,20 +158,6 @@ class KubernetesDeployController {
}
}
async onInit() {
- this.state = {
- DeployType: KubernetesDeployManifestTypes.KUBERNETES,
- BuildMethod: KubernetesDeployBuildMethods.GIT,
- tabLogsDisabled: true,
- activeTab: 0,
- viewReady: false,
- isEditorDirty: false,
- };
-
- this.formValues = {};
- this.ManifestDeployTypes = KubernetesDeployManifestTypes;
- this.BuildMethods = KubernetesDeployBuildMethods;
- this.endpointId = this.EndpointProvider.endpointID();
-
await this.getNamespaces();
this.state.viewReady = true;
diff --git a/app/portainer/components/box-selector/box-selector-item/box-selector-item.html b/app/portainer/components/box-selector/box-selector-item/box-selector-item.html
new file mode 100644
index 000000000..398f65563
--- /dev/null
+++ b/app/portainer/components/box-selector/box-selector-item/box-selector-item.html
@@ -0,0 +1,10 @@
+
+
+
+
+ {{ $ctrl.option.description }}
+
+
diff --git a/app/portainer/components/box-selector/box-selector-item/index.js b/app/portainer/components/box-selector/box-selector-item/index.js
new file mode 100644
index 000000000..979f91116
--- /dev/null
+++ b/app/portainer/components/box-selector/box-selector-item/index.js
@@ -0,0 +1,11 @@
+import angular from 'angular';
+
+angular.module('portainer.app').component('boxSelectorItem', {
+ templateUrl: './box-selector-item.html',
+ bindings: {
+ radioName: '@',
+ isChecked: '<',
+ option: '<',
+ onChange: '<',
+ },
+});
diff --git a/app/portainer/components/box-selector/box-selector.controller.js b/app/portainer/components/box-selector/box-selector.controller.js
new file mode 100644
index 000000000..3b3c60d49
--- /dev/null
+++ b/app/portainer/components/box-selector/box-selector.controller.js
@@ -0,0 +1,17 @@
+export default class BoxSelectorController {
+ constructor() {
+ this.isChecked = this.isChecked.bind(this);
+ this.change = this.change.bind(this);
+ }
+
+ change(value) {
+ this.ngModel = value;
+ if (this.onChange) {
+ this.onChange(value);
+ }
+ }
+
+ isChecked(value) {
+ return this.ngModel === value;
+ }
+}
diff --git a/app/portainer/components/box-selector/box-selector.css b/app/portainer/components/box-selector/box-selector.css
new file mode 100644
index 000000000..b359cb4df
--- /dev/null
+++ b/app/portainer/components/box-selector/box-selector.css
@@ -0,0 +1,82 @@
+.boxselector_wrapper {
+ display: flex;
+ flex-flow: row wrap;
+ margin: 0.5rem;
+}
+
+.boxselector_wrapper > div,
+.boxselector_wrapper box-selector-item {
+ flex: 1;
+ padding: 0.5rem;
+}
+
+.boxselector_wrapper .boxselector_header {
+ font-size: 14px;
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+
+.boxselector_header .fa,
+.fab {
+ font-weight: normal;
+}
+
+.boxselector_wrapper input[type='radio'] {
+ display: none;
+}
+
+.boxselector_wrapper input[type='radio']:not(:disabled) ~ label {
+ cursor: pointer;
+}
+
+.boxselector_wrapper label {
+ font-weight: normal;
+ font-size: 12px;
+ display: block;
+ background: white;
+ border: 1px solid #333333;
+ border-radius: 2px;
+ padding: 10px 10px 0 10px;
+ text-align: center;
+ box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
+ position: relative;
+}
+.boxselector_wrapper label.boxselector_disabled {
+ background: #cacaca;
+ border-color: #787878;
+ color: #787878;
+ cursor: not-allowed;
+}
+
+.boxselector_wrapper input[type='radio']:checked + label {
+ background: #337ab7;
+ color: white;
+ padding-top: 2rem;
+ border-color: #337ab7;
+}
+
+.boxselector_wrapper input[type='radio']:checked + label::after {
+ color: #337ab7;
+ font-family: 'Font Awesome 5 Free';
+ border: 2px solid #337ab7;
+ content: '\f00c';
+ font-size: 16px;
+ font-weight: bold;
+ position: absolute;
+ top: -15px;
+ left: 50%;
+ transform: translateX(-50%);
+ height: 30px;
+ width: 30px;
+ line-height: 26px;
+ text-align: center;
+ border-radius: 50%;
+ background: white;
+ box-shadow: 0 2px 5px -2px rgba(0, 0, 0, 0.25);
+}
+
+@media only screen and (max-width: 700px) {
+ .boxselector_wrapper {
+ flex-direction: column;
+ }
+}
diff --git a/app/portainer/components/box-selector/box-selector.html b/app/portainer/components/box-selector/box-selector.html
new file mode 100644
index 000000000..78452bb71
--- /dev/null
+++ b/app/portainer/components/box-selector/box-selector.html
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/portainer/components/box-selector/index.js b/app/portainer/components/box-selector/index.js
new file mode 100644
index 000000000..70ae7d768
--- /dev/null
+++ b/app/portainer/components/box-selector/index.js
@@ -0,0 +1,20 @@
+import angular from 'angular';
+
+import './box-selector.css';
+
+import controller from './box-selector.controller';
+
+angular.module('portainer.app').component('boxSelector', {
+ templateUrl: './box-selector.html',
+ controller,
+ bindings: {
+ radioName: '@',
+ ngModel: '=',
+ options: '<',
+ onChange: '<',
+ },
+});
+
+export function buildOption(id, icon, label, description, value) {
+ return { id, icon, label, description, value };
+}