feat(ui): ui-improvements-helm EE-3476 (#7344)

* feat(ui): helm views ui update EE-3476
pull/7381/head
Ali 2022-08-01 19:13:58 +12:00 committed by GitHub
parent e28a1491d4
commit 57e53d1a21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 283 additions and 119 deletions

View File

@ -99,6 +99,9 @@ pr-icon {
} }
.icon-nested-gray { .icon-nested-gray {
display: flex;
justify-content: center;
align-items: center;
height: 30px; height: 30px;
width: 30px; width: 30px;
padding: 5px; padding: 5px;
@ -109,6 +112,9 @@ pr-icon {
} }
.icon-nested-blue { .icon-nested-blue {
display: flex;
justify-content: center;
align-items: center;
height: 30px; height: 30px;
width: 30px; width: 30px;
padding: 5px; padding: 5px;

View File

@ -96,7 +96,7 @@ div.input-mask {
} }
.widget .widget-header { .widget .widget-header {
color: var(--text-widget-header-color); color: var(--text-widget-header-color);
padding: 10px 15px; padding: 20px 20px 10px 20px;
line-height: 30px; line-height: 30px;
font-weight: 500; font-weight: 500;
} }

View File

@ -234,6 +234,27 @@ json-tree .branch-preview {
background-color: var(--bg-progress-color); background-color: var(--bg-progress-color);
} }
.ui-select-search,
.ui-select-toggle {
height: 30px;
min-width: 260px;
padding: 4px 12px;
}
.ui-select-toggle {
justify-content: flex-start !important;
}
.ui-select-match-text {
display: flex;
flex-direction: row-reverse;
align-items: center;
}
.ui-select-match-text > a {
verical-align: middle;
}
.ui-select-bootstrap .ui-select-choices-row > span { .ui-select-bootstrap .ui-select-choices-row > span {
color: var(--text-ui-select-color); color: var(--text-ui-select-color);
} }

View File

@ -1,3 +1,3 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="auto" height="auto" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.5 25.0002V43.7552C37.5 46.0507 37.5 47.1985 37.2138 48.2621C36.9603 49.2044 36.5432 50.095 35.9816 50.893C35.3477 51.7938 34.4659 52.5286 32.7025 53.9981L17.2975 66.8356C15.5341 68.3051 14.6523 69.0399 14.0184 69.9406C13.4568 70.7387 13.0397 71.6292 12.7862 72.5716C12.5 73.6352 12.5 74.783 12.5 77.0785V78.3335C12.5 83.0006 12.5 85.3342 13.4083 87.1168C14.2072 88.6848 15.4821 89.9596 17.0501 90.7586C18.8327 91.6668 21.1662 91.6668 25.8333 91.6668H74.1667C78.8338 91.6668 81.1673 91.6668 82.9499 90.7586C84.5179 89.9596 85.7928 88.6848 86.5917 87.1168C87.5 85.3342 87.5 83.0006 87.5 78.3335V77.0785C87.5 74.783 87.5 73.6352 87.2138 72.5716C86.9603 71.6292 86.5432 70.7387 85.9816 69.9406C85.3477 69.0399 84.4659 68.3051 82.7025 66.8356L67.2975 53.9981C65.5341 52.5286 64.6523 51.7938 64.0184 50.893C63.4568 50.095 63.0397 49.2044 62.7861 48.2621C62.5 47.1985 62.5 46.0507 62.5 43.7552V25.0002M34.5833 25.0002H65.4167C66.5834 25.0002 67.1668 25.0002 67.6125 24.7731C68.0045 24.5734 68.3232 24.2546 68.5229 23.8626C68.75 23.417 68.75 22.8336 68.75 21.6668V11.6668C68.75 10.5001 68.75 9.91667 68.5229 9.47102C68.3232 9.07901 68.0045 8.7603 67.6125 8.56057C67.1668 8.3335 66.5834 8.3335 65.4167 8.3335H34.5833C33.4166 8.3335 32.8332 8.3335 32.3875 8.56057C31.9955 8.7603 31.6768 9.07901 31.4771 9.47102C31.25 9.91667 31.25 10.5001 31.25 11.6668V21.6668C31.25 22.8336 31.25 23.417 31.4771 23.8626C31.6768 24.2546 31.9955 24.5734 32.3875 24.7731C32.8332 25.0002 33.4166 25.0002 34.5833 25.0002ZM22.9167 70.8335H77.0833C79.0194 70.8335 79.9874 70.8335 80.7924 70.9936C84.0982 71.6512 86.6823 74.2353 87.3399 77.5411C87.5 78.3461 87.5 79.3141 87.5 81.2502C87.5 83.1862 87.5 84.1543 87.3399 84.9593C86.6823 88.265 84.0982 90.8492 80.7924 91.5067C79.9874 91.6668 79.0194 91.6668 77.0833 91.6668H22.9167C20.9806 91.6668 20.0126 91.6668 19.2076 91.5067C15.9018 90.8492 13.3177 88.265 12.6601 84.9593C12.5 84.1543 12.5 83.1862 12.5 81.2502C12.5 79.3141 12.5 78.3461 12.6601 77.5411C13.3177 74.2353 15.9018 71.6512 19.2076 70.9936C20.0126 70.8335 20.9806 70.8335 22.9167 70.8335Z" stroke="black" stroke-width="8.2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M37.5 25.0002V43.7552C37.5 46.0507 37.5 47.1985 37.2138 48.2621C36.9603 49.2044 36.5432 50.095 35.9816 50.893C35.3477 51.7938 34.4659 52.5286 32.7025 53.9981L17.2975 66.8356C15.5341 68.3051 14.6523 69.0399 14.0184 69.9406C13.4568 70.7387 13.0397 71.6292 12.7862 72.5716C12.5 73.6352 12.5 74.783 12.5 77.0785V78.3335C12.5 83.0006 12.5 85.3342 13.4083 87.1168C14.2072 88.6848 15.4821 89.9596 17.0501 90.7586C18.8327 91.6668 21.1662 91.6668 25.8333 91.6668H74.1667C78.8338 91.6668 81.1673 91.6668 82.9499 90.7586C84.5179 89.9596 85.7928 88.6848 86.5917 87.1168C87.5 85.3342 87.5 83.0006 87.5 78.3335V77.0785C87.5 74.783 87.5 73.6352 87.2138 72.5716C86.9603 71.6292 86.5432 70.7387 85.9816 69.9406C85.3477 69.0399 84.4659 68.3051 82.7025 66.8356L67.2975 53.9981C65.5341 52.5286 64.6523 51.7938 64.0184 50.893C63.4568 50.095 63.0397 49.2044 62.7861 48.2621C62.5 47.1985 62.5 46.0507 62.5 43.7552V25.0002M34.5833 25.0002H65.4167C66.5834 25.0002 67.1668 25.0002 67.6125 24.7731C68.0045 24.5734 68.3232 24.2546 68.5229 23.8626C68.75 23.417 68.75 22.8336 68.75 21.6668V11.6668C68.75 10.5001 68.75 9.91667 68.5229 9.47102C68.3232 9.07901 68.0045 8.7603 67.6125 8.56057C67.1668 8.3335 66.5834 8.3335 65.4167 8.3335H34.5833C33.4166 8.3335 32.8332 8.3335 32.3875 8.56057C31.9955 8.7603 31.6768 9.07901 31.4771 9.47102C31.25 9.91667 31.25 10.5001 31.25 11.6668V21.6668C31.25 22.8336 31.25 23.417 31.4771 23.8626C31.6768 24.2546 31.9955 24.5734 32.3875 24.7731C32.8332 25.0002 33.4166 25.0002 34.5833 25.0002ZM22.9167 70.8335H77.0833C79.0194 70.8335 79.9874 70.8335 80.7924 70.9936C84.0982 71.6512 86.6823 74.2353 87.3399 77.5411C87.5 78.3461 87.5 79.3141 87.5 81.2502C87.5 83.1862 87.5 84.1543 87.3399 84.9593C86.6823 88.265 84.0982 90.8492 80.7924 91.5067C79.9874 91.6668 79.0194 91.6668 77.0833 91.6668H22.9167C20.9806 91.6668 20.0126 91.6668 19.2076 91.5067C15.9018 90.8492 13.3177 88.265 12.6601 84.9593C12.5 84.1543 12.5 83.1862 12.5 81.2502C12.5 79.3141 12.5 78.3461 12.6601 77.5411C13.3177 74.2353 15.9018 71.6512 19.2076 70.9936C20.0126 70.8335 20.9806 70.8335 22.9167 70.8335Z" stroke="currentColor" stroke-width="8.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

10
app/assets/ico/vendor/helm.svg vendored Normal file
View File

@ -0,0 +1,10 @@
<svg width="auto" height="auto" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2026_8347)">
<path d="M2.03394 12.0799L21.9659 12.0799M4.05923 7.91314C4.44428 7.25611 4.90783 6.65064 5.437 6.10964M5.437 6.10964C7.12201 4.38695 9.4724 3.31777 12.0725 3.31777C14.6331 3.31777 16.9516 4.35473 18.6308 6.03161M5.437 6.10964L3.87003 4.54238M5.437 6.10964L5.44586 6.1185M18.6308 6.03161C19.1922 6.59221 19.6821 7.22434 20.0858 7.91314M18.6308 6.03161L20.1869 4.47547M18.6308 6.03161L18.6243 6.03807M11.9948 1.04492L11.9948 3.31804M19.969 16.069C19.584 16.726 19.1204 17.3315 18.5912 17.8725M18.5912 17.8725C16.9062 19.5952 14.5559 20.6643 11.9557 20.6643C9.39512 20.6643 7.07669 19.6274 5.39746 17.9505M18.5912 17.8725L20.1582 19.4397M18.5912 17.8725L18.5824 17.8636M5.39746 17.9505C4.83608 17.3899 4.34614 16.7578 3.94248 16.069M5.39746 17.9505L3.84132 19.5066M5.39746 17.9505L5.40392 17.944M12.0335 22.9372L12.0335 20.6641" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</g>
<defs>
<clipPath id="clip0_2026_8347">
<rect width="24" height="24" fill="white" transform="translate(24) rotate(90)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,5 +1,10 @@
<rd-widget> <rd-widget>
<rd-widget-header icon="fa-dharmachakra" title-text="Additional repositories"></rd-widget-header> <div class="toolBar px-5 pt-5">
<div class="toolBarTitle">
<pr-icon icon="'svg-helm'" class-name="'icon-nested-blue'" mode="'primary'"></pr-icon>
Additional repositories
</div>
</div>
<rd-widget-body> <rd-widget-body>
<div class="actionBar"> <div class="actionBar">
<form class="form-horizontal" name="addUserHelmRepoForm"> <form class="form-horizontal" name="addUserHelmRepoForm">
@ -7,7 +12,7 @@
<span class="col-sm-12 text-muted small"> Add a Helm repository. All Helm charts in the repository will be added to the list. </span> <span class="col-sm-12 text-muted small"> Add a Helm repository. All Helm charts in the repository will be added to the list. </span>
</div> </div>
<div class="form-group"> <div class="form-group mb-2">
<div class="col-sm-12"> <div class="col-sm-12">
<input <input
type="url" type="url"
@ -22,16 +27,20 @@
</div> </div>
<div class="form-group nomargin" ng-show="addUserHelmRepoForm.repo.$invalid"> <div class="form-group nomargin" ng-show="addUserHelmRepoForm.repo.$invalid">
<div class="col-sm-12 small text-warning"> <div class="small">
<div ng-messages="addUserHelmRepoForm.repo.$error"> <div ng-messages="addUserHelmRepoForm.repo.$error">
<p ng-message="pattern"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A valid URL beginning with http(s) is required.</p> <p class="vertical-center" ng-message="pattern"
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> A valid URL beginning with http(s) is required.</p
>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group" ng-show="$ctrl.doesRepoExist()"> <div class="form-group nomargin" ng-show="$ctrl.doesRepoExist()">
<div class="col-sm-12 small text-warning"> <div class="small">
<div> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Helm repo already exists. </div> <div ng-messages="addUserHelmRepoForm.repo.$error">
<p class="vertical-center"><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> Helm repo already exists.</p>
</div>
</div> </div>
</div> </div>
@ -39,7 +48,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<button <button
type="button" type="button"
class="btn btn-sm btn-default nomargin" class="btn btn-primary btn-sm nomargin"
ng-click="$ctrl.addRepository()" ng-click="$ctrl.addRepository()"
ng-disabled="$ctrl.state.isAddingRepo || addUserHelmRepoForm.repo.$invalid || $ctrl.doesRepoExist()" ng-disabled="$ctrl.state.isAddingRepo || addUserHelmRepoForm.repo.$invalid || $ctrl.doesRepoExist()"
analytics-on analytics-on

View File

@ -1,12 +1,19 @@
<!-- helm chart --> <!-- helm chart -->
<div ng-class="{ 'blocklist-item--selected': $ctrl.model.Selected }" class="blocklist-item template-item" ng-click="$ctrl.onSelect($ctrl.model)"> <div ng-class="{ 'blocklist-item--selected': $ctrl.model.Selected }" class="blocklist-item template-item mx-[10px]" ng-click="$ctrl.onSelect($ctrl.model)">
<div class="blocklist-item-box"> <div class="blocklist-item-box">
<!-- helmchart-image --> <!-- helmchart-image -->
<span ng-if="$ctrl.model.icon"> <span ng-if="$ctrl.model.icon">
<img class="blocklist-item-logo" ng-src="{{ $ctrl.model.icon }}" /> <fallback-image
src="$ctrl.model.icon"
fallback-icon="'svg-helm'"
class-name="'blocklist-item-logo h-16 w-auto'"
fallback-class-name="'icon-nested-blue !h-12 !w-12'"
fallback-mode="'primary'"
size="'xl'"
></fallback-image>
</span> </span>
<span class="blocklist-item-logo" ng-if="!$ctrl.model.icon"> <span class="blocklist-item-logo vertical-center" ng-if="!$ctrl.model.icon">
<i class="fa fa-dharmachakra fa-4x blue-icon" aria-hidden="true"></i> <pr-icon icon="'svg-helm'" mode="'primary'" class-name="'icon-nested-blue'"></pr-icon>
</span> </span>
<!-- !helmchart-image --> <!-- !helmchart-image -->
<!-- helmchart-details --> <!-- helmchart-details -->
@ -18,8 +25,8 @@
{{ $ctrl.model.name }} {{ $ctrl.model.name }}
</span> </span>
<span class="space-left blocklist-item-subtitle"> <span class="space-left blocklist-item-subtitle">
<span> <span class="vertical-center">
<i class="fa fa-dharmachakra" aria-hidden="true"></i> <pr-icon icon="'svg-helm'" mode="'primary'"></pr-icon>
</span> </span>
<span> Helm </span> <span> Helm </span>
</span> </span>

View File

@ -1,28 +1,14 @@
<div class="datatable"> <div class="datatable">
<rd-widget> <rd-widget>
<rd-widget-body classes="no-padding"> <rd-widget-body classes="no-padding">
<div class="toolBar"> <div class="toolBar vertical-center !gap-x-5 !gap-y-1 flex-wrap w-full relative">
<div class="toolBarTitle"> <pr-icon icon="$ctrl.titleIcon" class-name="space-right"></pr-icon> {{ $ctrl.titleText }} </div> <div class="toolBarTitle vertical-center">
<pr-icon icon="$ctrl.titleIcon" feather="true" mode="'primary'" class-name="'icon-nested-blue space-right'"></pr-icon>
{{ $ctrl.titleText }}
</div> </div>
<div class="actionBar"> <div class="searchBar vertical-center !mr-0">
<div> <pr-icon icon="'search'" feather="true" class="searchIcon"></pr-icon>
<span style="width: 25%">
<ui-select ng-model="$ctrl.state.selectedCategory" theme="bootstrap">
<ui-select-match placeholder="Select a category">
<a class="btn btn-xs btn-link pull-right" ng-click="$ctrl.clearCategory()"><i class="glyphicon glyphicon-remove"></i></a>
<span>{{ $select.selected }}</span>
</ui-select-match>
<ui-select-choices repeat="category in ($ctrl.state.categories | filter: $select.search)">
<span>{{ category }}</span>
</ui-select-choices>
</ui-select>
</span>
</div>
</div>
<div class="searchBar" style="border-top: 2px solid #f6f6f6">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input <input
type="text" type="text"
class="searchInput" class="searchInput"
@ -33,6 +19,20 @@
ng-model-options="{ debounce: 300 }" ng-model-options="{ debounce: 300 }"
/> />
</div> </div>
<div>
<ui-select ng-model="$ctrl.state.selectedCategory" theme="bootstrap" append-to-body="true">
<ui-select-match placeholder="Select a category">
<a class="btn btn-xs btn-link pull-right vertical-center" ng-click="$ctrl.clearCategory()">
<pr-icon icon="'x'" feather="true"></pr-icon>
</a>
<span class="vertical-center">{{ $select.selected }}</span>
</ui-select-match>
<ui-select-choices repeat="category in ($ctrl.state.categories | filter: $select.search)">
<span>{{ category }}</span>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="blocklist"> <div class="blocklist">
<helm-templates-list-item <helm-templates-list-item

View File

@ -83,7 +83,7 @@ export default class HelmTemplatesController {
} }
async selectHelmChart(chart) { async selectHelmChart(chart) {
this.$anchorScroll('view-top'); window.scrollTo(0, 0);
this.state.showCustomValues = false; this.state.showCustomValues = false;
this.state.chart = chart; this.state.chart = chart;
await this.getHelmValues(); await this.getHelmValues();

View File

@ -2,14 +2,19 @@
<information-panel title-text="Information" ng-if="!$ctrl.state.chart"> <information-panel title-text="Information" ng-if="!$ctrl.state.chart">
<span class="small text-muted"> <span class="small text-muted">
<p> <p class="inline-flex flex-row items-center">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'info'" feather="true" class="mr-1 vertical-center" mode="'primary'"></pr-icon>
This is a first version for Helm charts, for more information see this <a href="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank">blog post</a>. This is a first version for Helm charts, for more information see this&nbsp;<a
</p> class="text-blue-8 hover:underline hover:text-blue-8"
<p ng-if="$ctrl.state.globalRepository === ''"> href="https://www.portainer.io/blog/portainer-now-with-helm-support"
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> target="_blank"
>blog post</a
>.</p
>
<p ng-if="$ctrl.state.globalRepository === ''" class="inline-flex items-center">
<pr-icon icon="'info'" feather="true"></pr-icon>
<span>The Global Helm Repository is not configured.</span> <span>The Global Helm Repository is not configured.</span>
<a ng-if="$ctrl.state.isAdmin" ui-sref="portainer.settings">Configure Global Helm Repository in Settings</a> <a ng-if="$ctrl.state.isAdmin" ui-sref="portainer.settings">Configure Global Helm Repository in Settings</a>.
</p> </p>
</span> </span>
</information-panel> </information-panel>
@ -18,7 +23,16 @@
<!-- helmchart-form --> <!-- helmchart-form -->
<div class="col-sm-12" ng-if="$ctrl.state.chart"> <div class="col-sm-12" ng-if="$ctrl.state.chart">
<rd-widget> <rd-widget>
<rd-widget-custom-header icon="$ctrl.state.chart.icon" title-text="$ctrl.state.chart.name"></rd-widget-custom-header> <div class="toolBarTitle vertical-center pt-5 px-5 text-muted">
<fallback-image
src="$ctrl.state.chart.icon"
fallback-icon="'svg-helm'"
class-name="'h-8 w-8'"
fallback-class-name="'icon-nested-blue'"
fallback-mode="'primary'"
></fallback-image>
{{ $ctrl.state.chart.name }}
</div>
<rd-widget-body classes="padding"> <rd-widget-body classes="padding">
<form class="form-horizontal" name="$ctrl.helmTemplateCreationForm"> <form class="form-horizontal" name="$ctrl.helmTemplateCreationForm">
<!-- description --> <!-- description -->
@ -26,7 +40,7 @@
<div class="col-sm-12 form-section-title"> Description </div> <div class="col-sm-12 form-section-title"> Description </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="template-note" ng-bind-html="$ctrl.state.chart.description"></div> <div class="text-xs text-muted" ng-bind-html="$ctrl.state.chart.description"></div>
</div> </div>
</div> </div>
</div> </div>
@ -46,17 +60,16 @@
></select> ></select>
</div> </div>
</div> </div>
<div class="form-group" ng-if="!$ctrl.state.resourcePool"> <div class="form-group" ng-if="!$ctrl.state.resourcePool">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted vertical-center">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon>
You do not have access to any namespace. Contact your administrator to get access to a namespace. You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div> </div>
</div> </div>
<!-- !namespace-input --> <!-- !namespace-input -->
<!-- name-input --> <!-- name-input -->
<div class="form-group"> <div class="form-group mb-2">
<label for="release_name" class="col-sm-2 control-label text-left">Name</label> <label for="release_name" class="col-sm-2 control-label text-left required">Name</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input <input
type="text" type="text"
@ -70,12 +83,17 @@
</div> </div>
</div> </div>
<div class="form-group" ng-show="$ctrl.helmTemplateCreationForm.release_name.$invalid"> <div class="form-group" ng-show="$ctrl.helmTemplateCreationForm.release_name.$invalid">
<div class="col-sm-12 small text-warning"> <div class="small">
<div ng-messages="$ctrl.helmTemplateCreationForm.release_name.$error"> <div ng-messages="$ctrl.helmTemplateCreationForm.release_name.$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p> <div class="col-sm-2"></div>
<p ng-message="pattern"> <p class="vertical-center col-sm-10 text-muted" ng-message="required">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field must consist of lower case alphanumeric characters or '-', start with an alphabetic <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon>
character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123'). This field is required.
</p>
<p class="vertical-center col-sm-10 text-muted" ng-message="pattern">
<pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon>
This field must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',
or 'abc-123').
</p> </p>
</div> </div>
</div> </div>
@ -83,35 +101,44 @@
<!-- !name-input --> <!-- !name-input -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<a class="small interactive" ng-if="!$ctrl.state.showCustomValues && !$ctrl.state.loadingValues" ng-click="$ctrl.state.showCustomValues = true;"> <button
<i class="fa fa-plus space-right" aria-hidden="true"></i> Show custom values ng-if="!$ctrl.state.showCustomValues && !$ctrl.state.loadingValues"
</a> class="btn btn-xs btn-default vertical-center mr-2 !ml-0"
<span class="small interactive" ng-if="$ctrl.state.loadingValues"> <i class="fa fa-sync-alt space-right" aria-hidden="true"></i> Loading values.yaml... </span> ng-click="$ctrl.state.showCustomValues = true;"
<a class="small interactive" ng-if="$ctrl.state.showCustomValues" ng-click="$ctrl.state.showCustomValues = false;"> >
<i class="fa fa-minus space-right" aria-hidden="true"></i> Hide custom values <pr-icon icon="'plus'" feather="true" class="vertical-center"></pr-icon>
</a> Show custom values
</button>
<span class="small interactive vertical-center" ng-if="$ctrl.state.loadingValues">
<pr-icon icon="'refresh-cw'" class="mr-1" feather="true"></pr-icon>
Loading values.yaml...
</span>
<button ng-if="$ctrl.state.showCustomValues" class="btn btn-xs btn-default vertical-center mr-2 !ml-0" ng-click="$ctrl.state.showCustomValues = false;">
<pr-icon icon="'minus'" feather="true" class="vertical-center"></pr-icon>
Hide custom values
</button>
</div> </div>
</div> </div>
<!-- values override --> <!-- values override -->
<div ng-if="$ctrl.state.showCustomValues"> <div ng-if="$ctrl.state.showCustomValues">
<!-- web-editor --> <!-- web-editor -->
<div> <div>
<div class="col-sm-12 form-section-title"> Web editor </div>
<div class="form-group">
<span class="col-sm-12 text-muted small">
You can get more information about Helm values file format in the
<a href="https://helm.sh/docs/chart_template_guide/values_files/" target="_blank">official documentation</a>.
</span>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<code-editor <web-editor-form
identifier="helm-app-creation-editor" identifier="helm-app-creation-editor"
placeholder="# Define or paste the content of your values yaml file here"
yml="true"
on-change="($ctrl.editorUpdate)"
value="$ctrl.state.values" value="$ctrl.state.values"
></code-editor> on-change="($ctrl.editorUpdate)"
yml="true"
placeholder="# Define or paste the content of your values yaml file here"
>
<editor-description>
You can get more information about Helm values file format in the
<a href="https://helm.sh/docs/chart_template_guide/values_files/" target="_blank" class="text-blue-8 hover:text-blue-8 hover:underline"
>official documentation</a
>.
</editor-description>
</web-editor-form>
</div> </div>
</div> </div>
</div> </div>
@ -124,7 +151,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.appName && $ctrl.state.resourcePool && !$ctrl.state.loadingValues && !$ctrl.state.actionInProgress)" ng-disabled="!($ctrl.state.appName && $ctrl.state.resourcePool && !$ctrl.state.loadingValues && !$ctrl.state.actionInProgress)"
ng-click="$ctrl.installHelmchart()" ng-click="$ctrl.installHelmchart()"
button-spinner="$ctrl.state.actionInProgress" button-spinner="$ctrl.state.actionInProgress"
@ -155,7 +182,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<helm-templates-list <helm-templates-list
title-text="Charts" title-text="Charts"
title-icon="fa-rocket" title-icon="compass"
charts="$ctrl.state.charts" charts="$ctrl.state.charts"
table-key="$ctrl.state.charts" table-key="$ctrl.state.charts"
select-action="$ctrl.selectHelmChart" select-action="$ctrl.selectHelmChart"

View File

@ -9,35 +9,37 @@
<div ng-if="$ctrl.state.viewReady"> <div ng-if="$ctrl.state.viewReady">
<div class="row"> <div class="row">
<div class="col-sm-12">
<div class="col-sm-12 form-section-title"> Information </div>
<p class="text-muted">
<i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px"></i>
This is a first version for Helm charts, for more information see this
<a href="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank">blog post</a>.
</p>
</div>
<div class="col-sm-12"> <div class="col-sm-12">
<rd-widget> <rd-widget>
<rd-widget-header icon="fa-dharmachakra" title-text="Release"></rd-widget-header> <div class="toolBar vertical-center !gap-x-5 !gap-y-1 p-5 flex-wrap w-full">
<rd-widget-body classes="no-padding"> <div class="toolBarTitle vertical-center">
<pr-icon icon="'svg-helm'" class-name="'icon-nested-blue'" mode="'primary'"></pr-icon>
Release
</div>
</div>
<div class="toolBarTitle text-muted small vertical-center px-5 !gap-0">
<pr-icon icon="'info'" feather="true" mode="'primary'" class-name="'!mr-1'" class="vertical-center"></pr-icon>
This is a first version for Helm charts, for more information see this&nbsp;
<a href="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank" class="text-blue-8 hover:text-blue-8 hover:underline">blog post</a>.
</div>
<rd-widget-body>
<table class="table"> <table class="table">
<tbody class="release-table"> <tbody class="release-table">
<tr> <tr>
<td>Name</td> <td class="vertical-center !pl-0">Name</td>
<td> <td class="vertical-center !p-2">
{{ $ctrl.state.release.name }} {{ $ctrl.state.release.name }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Chart</td> <td class="vertical-center !pl-0">Chart</td>
<td> <td class="vertical-center !p-2">
{{ $ctrl.state.release.chart }} {{ $ctrl.state.release.chart }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td>App version</td> <td class="vertical-center !pl-0">App version</td>
<td> <td class="vertical-center !p-2">
{{ $ctrl.state.release.app_version }} {{ $ctrl.state.release.app_version }}
</td> </td>
</tr> </tr>

View File

@ -14,6 +14,7 @@ import { Tooltip } from '@@/Tip/Tooltip';
import { TableColumnHeaderAngular } from '@@/datatables/TableHeaderCell'; import { TableColumnHeaderAngular } from '@@/datatables/TableHeaderCell';
import { DashboardItem } from '@@/DashboardItem'; import { DashboardItem } from '@@/DashboardItem';
import { SearchBar } from '@@/datatables/SearchBar'; import { SearchBar } from '@@/datatables/SearchBar';
import { FallbackImage } from '@@/FallbackImage';
import { fileUploadField } from './file-upload-field'; import { fileUploadField } from './file-upload-field';
import { switchField } from './switch-field'; import { switchField } from './switch-field';
@ -50,6 +51,19 @@ export const componentsModule = angular
'pageHeader', 'pageHeader',
r2a(PageHeader, ['title', 'breadcrumbs', 'loading', 'onReload', 'reload']) r2a(PageHeader, ['title', 'breadcrumbs', 'loading', 'onReload', 'reload'])
) )
.component(
'fallbackImage',
r2a(FallbackImage, [
'src',
'fallbackIcon',
'alt',
'size',
'className',
'fallbackMode',
'fallbackClassName',
'feather',
])
)
.component( .component(
'prIcon', 'prIcon',
react2angular(Icon, ['className', 'feather', 'icon', 'mode', 'size']) react2angular(Icon, ['className', 'feather', 'icon', 'mode', 'size'])

View File

@ -0,0 +1,58 @@
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { Icon, IconMode, IconSize } from './Icon';
interface Props {
// props for the image to load
src: string; // a link to an external image
fallbackIcon: string;
alt?: string;
size?: IconSize;
className?: string;
// additional fallback icon props
fallbackMode?: IconMode;
fallbackClassName?: string;
feather?: boolean;
}
export function FallbackImage({
src,
fallbackIcon,
alt,
size,
className,
fallbackMode,
fallbackClassName,
feather,
}: Props) {
const [error, setError] = useState(false);
const classes = clsx(className, { [`icon-${size}`]: size });
useEffect(() => {
setError(false);
}, [src]);
if (!error) {
return (
<img
className={classes}
src={src}
alt={alt}
onError={() => setError(true)}
/>
);
}
// fallback icon if there is an error loading the image
return (
<Icon
icon={fallbackIcon}
feather={feather}
className={fallbackClassName}
size={size}
mode={fallbackMode}
/>
);
}

View File

@ -10,12 +10,7 @@ export interface IconProps {
featherIcon?: boolean; featherIcon?: boolean;
} }
interface Props { export type IconMode =
icon: ReactNode | ComponentType<{ size?: string | number }>;
feather?: boolean;
className?: string;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
mode?:
| 'alt' | 'alt'
| 'primary' | 'primary'
| 'primary-alt' | 'primary-alt'
@ -27,6 +22,15 @@ interface Props {
| 'danger-alt' | 'danger-alt'
| 'success' | 'success'
| 'success-alt'; | 'success-alt';
export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
interface Props {
icon: ReactNode | ComponentType<{ size?: string | number }>;
feather?: boolean;
className?: string;
size?: IconSize;
mode?: IconMode;
} }
export function Icon({ icon, feather, className, mode, size }: Props) { export function Icon({ icon, feather, className, mode, size }: Props) {

View File

@ -59,6 +59,7 @@ import gitlab from '@/assets/ico/vendor/gitlab.svg?c';
import google from '@/assets/ico/vendor/google.svg?c'; import google from '@/assets/ico/vendor/google.svg?c';
import googlecloud from '@/assets/ico/vendor/googlecloud.svg?c'; import googlecloud from '@/assets/ico/vendor/googlecloud.svg?c';
import kubernetes from '@/assets/ico/vendor/kubernetes.svg?c'; import kubernetes from '@/assets/ico/vendor/kubernetes.svg?c';
import helm from '@/assets/ico/vendor/helm.svg?c';
import linode from '@/assets/ico/vendor/linode.svg?c'; import linode from '@/assets/ico/vendor/linode.svg?c';
import microsoft from '@/assets/ico/vendor/microsoft.svg?c'; import microsoft from '@/assets/ico/vendor/microsoft.svg?c';
import nomad from '@/assets/ico/vendor/nomad.svg?c'; import nomad from '@/assets/ico/vendor/nomad.svg?c';
@ -66,6 +67,8 @@ import openldap from '@/assets/ico/vendor/openldap.svg?c';
import proget from '@/assets/ico/vendor/proget.svg?c'; import proget from '@/assets/ico/vendor/proget.svg?c';
import quay from '@/assets/ico/vendor/quay.svg?c'; import quay from '@/assets/ico/vendor/quay.svg?c';
const placeholder = Placeholder;
export const SvgIcons = { export const SvgIcons = {
automode, automode,
darkmode, darkmode,
@ -96,6 +99,7 @@ export const SvgIcons = {
memory, memory,
objectgroup, objectgroup,
palette, palette,
placeholder,
plug, plug,
restore, restore,
restorewindow, restorewindow,
@ -124,6 +128,7 @@ export const SvgIcons = {
google, google,
googlecloud, googlecloud,
kubernetes, kubernetes,
helm,
linode, linode,
microsoft, microsoft,
nomad, nomad,

View File

@ -1,7 +1,8 @@
import { Box, Edit, Layers, Loader, Lock, Server } from 'react-feather'; import { Box, Edit, Layers, Lock, Server } from 'react-feather';
import { EnvironmentId } from '@/portainer/environments/types'; import { EnvironmentId } from '@/portainer/environments/types';
import { Authorized } from '@/portainer/hooks/useUser'; import { Authorized } from '@/portainer/hooks/useUser';
import Helm from '@/assets/ico/vendor/helm.svg?c';
import { DashboardLink } from '../items/DashboardLink'; import { DashboardLink } from '../items/DashboardLink';
import { SidebarItem } from '../SidebarItem'; import { SidebarItem } from '../SidebarItem';
@ -54,7 +55,7 @@ export function KubernetesSidebar({ environmentId }: Props) {
<SidebarItem <SidebarItem
to="kubernetes.templates.helm" to="kubernetes.templates.helm"
params={{ endpointId: environmentId }} params={{ endpointId: environmentId }}
icon={Loader} icon={Helm}
label="Helm" label="Helm"
data-cy="k8sSidebar-helm" data-cy="k8sSidebar-helm"
/> />