mirror of https://github.com/portainer/portainer
feat(ui): ui improvements create template EE-3628 (#7352)
parent
75c1b485ab
commit
e1c7079c81
|
@ -9,8 +9,8 @@ class KubeCreateCustomTemplateViewController {
|
||||||
Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications, ResourceControlService });
|
Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications, ResourceControlService });
|
||||||
|
|
||||||
this.methodOptions = [
|
this.methodOptions = [
|
||||||
buildOption('method_editor', 'fa fa-edit', 'Web editor', 'Use our Web editor', 'editor'),
|
buildOption('method_editor', 'svg-custom', 'Web editor', 'Use our Web editor', 'editor'),
|
||||||
buildOption('method_upload', 'fa fa-upload', 'Upload', 'Upload from your computer', 'upload'),
|
buildOption('method_upload', 'svg-upload', 'Upload', 'Upload from your computer', 'upload'),
|
||||||
];
|
];
|
||||||
|
|
||||||
this.templates = null;
|
this.templates = null;
|
||||||
|
@ -97,7 +97,12 @@ class KubeCreateCustomTemplateViewController {
|
||||||
|
|
||||||
this.state.actionInProgress = true;
|
this.state.actionInProgress = true;
|
||||||
try {
|
try {
|
||||||
const customTemplate = await this.createCustomTemplateByMethod(method, this.formValues);
|
const formValues = { ...this.formValues, Variables: null };
|
||||||
|
if (this.formValues.Variables.length > 0) {
|
||||||
|
formValues.Variables = JSON.stringify(this.formValues.Variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
const customTemplate = await this.createCustomTemplateByMethod(method, formValues);
|
||||||
|
|
||||||
const accessControlData = this.formValues.AccessControlData;
|
const accessControlData = this.formValues.AccessControlData;
|
||||||
const userDetails = this.Authentication.getUserDetails();
|
const userDetails = this.Authentication.getUserDetails();
|
||||||
|
|
|
@ -11,23 +11,25 @@
|
||||||
<div class="col-sm-12 form-section-title"> Build method </div>
|
<div class="col-sm-12 form-section-title"> Build method </div>
|
||||||
<box-selector radio-name="'method'" value="$ctrl.state.method" options="$ctrl.methodOptions" on-change="($ctrl.onChangeMethod)"></box-selector>
|
<box-selector radio-name="'method'" value="$ctrl.state.method" options="$ctrl.methodOptions" on-change="($ctrl.onChangeMethod)"></box-selector>
|
||||||
|
|
||||||
<web-editor-form
|
<div class="mt-4">
|
||||||
ng-if="$ctrl.state.method === 'editor'"
|
<web-editor-form
|
||||||
identifier="template-creation-editor"
|
ng-if="$ctrl.state.method === 'editor'"
|
||||||
value="$ctrl.formValues.FileContent"
|
identifier="template-creation-editor"
|
||||||
on-change="($ctrl.onChangeFileContent)"
|
value="$ctrl.formValues.FileContent"
|
||||||
ng-required="true"
|
on-change="($ctrl.onChangeFileContent)"
|
||||||
yml="true"
|
ng-required="true"
|
||||||
placeholder="# Define or paste the content of your manifest file here"
|
yml="true"
|
||||||
>
|
placeholder="# Define or paste the content of your manifest file here"
|
||||||
<editor-description>
|
>
|
||||||
<div>Templates allow deploying any kind of Kubernetes resource (Deployment, Secret, ConfigMap...)</div>
|
<editor-description>
|
||||||
<div>
|
<p>Templates allow deploying any kind of Kubernetes resource (Deployment, Secret, ConfigMap...)</p>
|
||||||
You can get more information about Kubernetes file format in the
|
<p>
|
||||||
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
|
You can get more information about Kubernetes file format in the
|
||||||
</div>
|
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
|
||||||
</editor-description>
|
</p>
|
||||||
</web-editor-form>
|
</editor-description>
|
||||||
|
</web-editor-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<file-upload-form ng-if="$ctrl.state.method === 'upload'" file="$ctrl.formValues.File" on-change="($ctrl.onChangeFile)" ng-required="true">
|
<file-upload-form ng-if="$ctrl.state.method === 'upload'" file="$ctrl.formValues.File" on-change="($ctrl.onChangeFile)" ng-required="true">
|
||||||
<file-upload-description> You can upload a Manifest file from your computer. </file-upload-description>
|
<file-upload-description> You can upload a Manifest file from your computer. </file-upload-description>
|
||||||
|
@ -48,7 +50,7 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm !ml-0"
|
||||||
ng-disabled="!$ctrl.state.isTemplateValid ||$ctrl.state.actionInProgress || $ctrl.form.$invalid || ($ctrl.state.method === 'editor' && !$ctrl.formValues.FileContent)"
|
ng-disabled="!$ctrl.state.isTemplateValid ||$ctrl.state.actionInProgress || $ctrl.form.$invalid || ($ctrl.state.method === 'editor' && !$ctrl.formValues.FileContent)"
|
||||||
ng-click="$ctrl.createCustomTemplate()"
|
ng-click="$ctrl.createCustomTemplate()"
|
||||||
button-spinner="$ctrl.state.actionInProgress"
|
button-spinner="$ctrl.state.actionInProgress"
|
||||||
|
|
|
@ -66,11 +66,11 @@
|
||||||
<!-- restricted-access -->
|
<!-- restricted-access -->
|
||||||
<!-- authorized-teams -->
|
<!-- authorized-teams -->
|
||||||
<div
|
<div
|
||||||
class="form-group"
|
class="form-group mt-4"
|
||||||
ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && ($ctrl.isAdmin || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1))"
|
ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && ($ctrl.isAdmin || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1))"
|
||||||
>
|
>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<label for="group-access" class="control-label text-left">
|
<label for="group-access" class="control-label text-left col-sm-3 col-sm-2 !p-0">
|
||||||
Authorized teams
|
Authorized teams
|
||||||
<portainer-tooltip
|
<portainer-tooltip
|
||||||
ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length > 0"
|
ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length > 0"
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<!-- authorized-users -->
|
<!-- authorized-users -->
|
||||||
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && $ctrl.isAdmin">
|
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && $ctrl.isAdmin">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<label for="group-access" class="control-label text-left">
|
<label for="group-access" class="control-label text-left col-sm-3 col-sm-2 !p-0">
|
||||||
Authorized users
|
Authorized users
|
||||||
<portainer-tooltip
|
<portainer-tooltip
|
||||||
ng-if="$ctrl.isAdmin && $ctrl.availableUsers.length > 0"
|
ng-if="$ctrl.isAdmin && $ctrl.availableUsers.length > 0"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<ng-form name="commonCustomTemplateForm">
|
<ng-form name="commonCustomTemplateForm">
|
||||||
<!-- title-input -->
|
<!-- title-input -->
|
||||||
<div class="form-group">
|
<div class="form-group mb-0">
|
||||||
<label for="template_title" class="col-sm-3 col-lg-2 control-label text-left"> Title </label>
|
<label for="template_title" class="col-sm-3 col-lg-2 control-label text-left"> Title </label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
@ -14,39 +14,37 @@
|
||||||
auto-focus
|
auto-focus
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
<span class="help-block">
|
||||||
</div>
|
<div ng-show="commonCustomTemplateForm.template_title.$invalid">
|
||||||
<div class="form-group" ng-show="commonCustomTemplateForm.template_title.$invalid">
|
<div class="mt-2 small text-muted">
|
||||||
<div class="col-sm-12 small text-warning">
|
<div ng-messages="commonCustomTemplateForm.template_title.$error">
|
||||||
<div ng-messages="commonCustomTemplateForm.template_title.$error">
|
<p class="vertical-center" ng-message="required"> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> Title is required. </p>
|
||||||
<p ng-message="pattern">
|
<p class="vertical-center" ng-message="pattern">
|
||||||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
<pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon>
|
||||||
<span>This field must consist of lower case alphanumeric characters, '_' or '-' (e.g. 'my-name', or 'abc-123').</span>
|
This field must consist of lower case alphanumeric characters, '_' or '-' (e.g. 'my-name', or 'abc-123').
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" ng-show="commonCustomTemplateForm.template_title.$invalid">
|
</span>
|
||||||
<div class="col-sm-12 small text-warning">
|
|
||||||
<div ng-messages="commonCustomTemplateForm.template_title.$error">
|
|
||||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Title is required.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !title-input -->
|
<!-- !title-input -->
|
||||||
|
|
||||||
<!-- description-input -->
|
<!-- description-input -->
|
||||||
<div class="form-group">
|
<div class="form-group mb-0">
|
||||||
<label for="description" class="col-sm-3 col-lg-2 control-label text-left">Description</label>
|
<label for="description" class="col-sm-3 col-lg-2 control-label text-left">Description</label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<input type="text" class="form-control" id="description" ng-model="$ctrl.formValues.Description" name="description" required />
|
<input type="text" class="form-control" id="description" ng-model="$ctrl.formValues.Description" name="description" required />
|
||||||
</div>
|
<span class="help-block">
|
||||||
</div>
|
<div class="mt-2 small text-muted">
|
||||||
<div class="form-group" ng-show="commonCustomTemplateForm.description.$invalid">
|
<div ng-show="commonCustomTemplateForm.description.$invalid">
|
||||||
<div class="col-sm-12 small text-warning">
|
<div ng-messages="commonCustomTemplateForm.description.$error">
|
||||||
<div ng-messages="commonCustomTemplateForm.description.$error">
|
<p class="vertical-center" ng-message="required"> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> Description is required.</p>
|
||||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Description is required.</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !description-input -->
|
<!-- !description-input -->
|
||||||
|
@ -54,7 +52,7 @@
|
||||||
<!-- note-input -->
|
<!-- note-input -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="note" class="col-sm-3 col-lg-2 control-label text-left">Note</label>
|
<label for="note" class="col-sm-3 col-lg-2 control-label text-left">Note</label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<input type="text" class="form-control" id="note" ng-model="$ctrl.formValues.Note" />
|
<input type="text" class="form-control" id="note" ng-model="$ctrl.formValues.Note" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,7 +61,7 @@
|
||||||
<!-- icon-url-input -->
|
<!-- icon-url-input -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="icon-url" class="col-sm-3 col-lg-2 control-label text-left">Icon URL</label>
|
<label for="icon-url" class="col-sm-3 col-lg-2 control-label text-left">Icon URL</label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<input type="text" class="form-control" id="icon-url" ng-model="$ctrl.formValues.Logo" />
|
<input type="text" class="form-control" id="icon-url" ng-model="$ctrl.formValues.Logo" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,7 +70,7 @@
|
||||||
<!-- platform-input -->
|
<!-- platform-input -->
|
||||||
<div ng-if="$ctrl.showPlatformField" class="form-group">
|
<div ng-if="$ctrl.showPlatformField" class="form-group">
|
||||||
<label for="platform" class="col-sm-3 col-lg-2 control-label text-left">Platform</label>
|
<label for="platform" class="col-sm-3 col-lg-2 control-label text-left">Platform</label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<select class="form-control" ng-model="$ctrl.formValues.Platform" ng-options="+(opt.value) as opt.label for opt in $ctrl.platformTypes"> </select>
|
<select class="form-control" ng-model="$ctrl.formValues.Platform" ng-options="+(opt.value) as opt.label for opt in $ctrl.platformTypes"> </select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,7 +79,7 @@
|
||||||
<!-- platform-input -->
|
<!-- platform-input -->
|
||||||
<div ng-if="$ctrl.showTypeField" class="form-group">
|
<div ng-if="$ctrl.showTypeField" class="form-group">
|
||||||
<label for="platform" class="col-sm-3 col-lg-2 control-label text-left">Type</label>
|
<label for="platform" class="col-sm-3 col-lg-2 control-label text-left">Type</label>
|
||||||
<div class="col-sm-9 col-lg-10">
|
<div class="col-sm-8">
|
||||||
<select class="form-control" ng-model="$ctrl.formValues.Type" ng-options="+(opt.value) as opt.label for opt in $ctrl.templateTypes"> </select>
|
<select class="form-control" ng-model="$ctrl.formValues.Type" ng-options="+(opt.value) as opt.label for opt in $ctrl.templateTypes"> </select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm !ml-0"
|
||||||
ng-disabled="$ctrl.state.actionInProgress || customTemplateForm.$invalid
|
ng-disabled="$ctrl.state.actionInProgress || customTemplateForm.$invalid
|
||||||
|| ($ctrl.state.Method === 'editor' && !$ctrl.formValues.FileContent)
|
|| ($ctrl.state.Method === 'editor' && !$ctrl.formValues.FileContent)
|
||||||
|| ($ctrl.state.Method === 'upload' && !$ctrl.formValues.File)
|
|| ($ctrl.state.Method === 'upload' && !$ctrl.formValues.File)
|
||||||
|
|
|
@ -18,6 +18,7 @@ export function AddButton({ label, onClick, className, disabled }: Props) {
|
||||||
className,
|
className,
|
||||||
'label',
|
'label',
|
||||||
'label-default',
|
'label-default',
|
||||||
|
'vertical-center',
|
||||||
'interactive',
|
'interactive',
|
||||||
'vertical-center',
|
'vertical-center',
|
||||||
styles.addButton
|
styles.addButton
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ChangeEvent, createRef } from 'react';
|
import { ChangeEvent, createRef } from 'react';
|
||||||
|
|
||||||
import { Button } from '@@/buttons';
|
import { Button } from '@@/buttons';
|
||||||
|
import { Icon } from '@@/Icon';
|
||||||
|
|
||||||
import styles from './FileUploadField.module.css';
|
import styles from './FileUploadField.module.css';
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ export function FileUploadField({
|
||||||
const fileRef = createRef<HTMLInputElement>();
|
const fileRef = createRef<HTMLInputElement>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="file-upload-field">
|
<div className="file-upload-field vertical-center">
|
||||||
<input
|
<input
|
||||||
id={inputId}
|
id={inputId}
|
||||||
ref={fileRef}
|
ref={fileRef}
|
||||||
|
@ -44,12 +45,8 @@ export function FileUploadField({
|
||||||
{title}
|
{title}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<span className="space-left">
|
<span className="vertical-center">
|
||||||
{value ? (
|
{value ? value.name : <Icon icon="x" feather mode="danger" />}
|
||||||
value.name
|
|
||||||
) : (
|
|
||||||
<i className="fa fa-times red-icon" aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -94,7 +94,7 @@ export function InputList<T = DefaultType>({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{textTip && (
|
{textTip && (
|
||||||
<div className="col-sm-12 my-5">
|
<div className="col-sm-12 mt-5">
|
||||||
<TextTip color="blue">{textTip}</TextTip>
|
<TextTip color="blue">{textTip}</TextTip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -131,6 +131,7 @@ export function InputList<T = DefaultType>({
|
||||||
size="small"
|
size="small"
|
||||||
disabled={disabled || index === 0}
|
disabled={disabled || index === 0}
|
||||||
onClick={() => handleMoveUp(index)}
|
onClick={() => handleMoveUp(index)}
|
||||||
|
className="vertical-center btn-only-icon"
|
||||||
>
|
>
|
||||||
<Icon icon="arrow-up" feather />
|
<Icon icon="arrow-up" feather />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -139,6 +140,7 @@ export function InputList<T = DefaultType>({
|
||||||
type="button"
|
type="button"
|
||||||
disabled={disabled || index === value.length - 1}
|
disabled={disabled || index === value.length - 1}
|
||||||
onClick={() => handleMoveDown(index)}
|
onClick={() => handleMoveDown(index)}
|
||||||
|
className="vertical-center btn-only-icon"
|
||||||
>
|
>
|
||||||
<Icon icon="arrow-down" feather />
|
<Icon icon="arrow-down" feather />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -146,12 +148,12 @@ export function InputList<T = DefaultType>({
|
||||||
)}
|
)}
|
||||||
{!readOnly && (
|
{!readOnly && (
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="dangerlight"
|
||||||
size="medium"
|
size="small"
|
||||||
onClick={() => handleRemoveItem(key, item)}
|
onClick={() => handleRemoveItem(key, item)}
|
||||||
disabled={disabled}
|
className="vertical-center btn-only-icon"
|
||||||
>
|
>
|
||||||
<Icon icon="trash-2" feather />
|
<Icon icon="trash-2" feather size="md" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,7 +27,7 @@ export function CustomTemplatesVariablesDefinitionField({
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<InputList
|
<InputList
|
||||||
label="Variables Definition"
|
label="Variables definition"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
renderItem={(item, onChange, error) => (
|
renderItem={(item, onChange, error) => (
|
||||||
|
|
Loading…
Reference in New Issue