Compare commits

..
Author SHA1 Message Date
Andy Lee f120252dc2 style: remove unneeded overflow css
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 21:55:48 +08:00
Andy Lee 497a703513 style: update header tag and align fixed header actions in review migration step
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 21:50:58 +08:00
Andy Lee 425d26f7ea refactor: fixed the table column
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 17:15:20 +08:00
Andy Lee 88ef562807 build: artifact
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 16:57:16 +08:00
Andy Lee 7592f5b64d refactor: customize empty table rows message
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 16:45:43 +08:00
Andy Lee 8779154ef1 refactor: new design for review page
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-23 16:21:11 +08:00
119 changed files with 460 additions and 2581 deletions
+3 -7
View File
@@ -8,16 +8,12 @@
- [ ] Yes, the backend owner is @
- [ ] No, frontend-only.
### Related Issue
### Related Issue #
<!-- Link the issue as harvester/harvester#<issue_number> -->
harvester/harvester#<issue_number>
### Test Steps
<!-- Provide the test steps for reviewer to verify the issue -->
1. Go to ... page
### Test screenshot or video
<!-- Attach screenshot or video of the changes and eventual comparison -->
### Test screenshot or video (Required)
<!-- Attach screenshot or video of the changes and eventual comparison if you find it necessary -->
@@ -1,12 +1,12 @@
name: Build Standalone on PR Merge
on:
workflow_dispatch: # manually trigger on Github Actions only support PR branch pushed to harvester/harvester-ui-extension
push:
branches:
- main
- 'release-harvester-v*'
- '*-dev'
- new_review_page
pull_request:
branches:
- main
@@ -30,35 +30,3 @@ jobs:
id-token: write
with:
CI_BRANCH: ${{github.ref_name}}
comment-on-pr:
name: Comment build result on PR
# Only for manual runs so the existing push / pull_request flows are untouched.
if: github.event_name == 'workflow_dispatch'
needs:
- build
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Add build artifact comment to the PR opened from this branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
# Match scripts/version: sanitize the branch and map main -> latest.
if [ "$BRANCH" = "main" ]; then
DIR="latest"
else
DIR=$(printf '%s' "$BRANCH" | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
fi
URL="https://releases.rancher.com/harvester-ui/dashboard/${DIR}/index.html"
PR=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --base main --head "$BRANCH" --json number --jq '.[0].number // empty')
if [ -z "$PR" ]; then
echo "No open PR found for branch $BRANCH, skipping comment."
exit 0
fi
BODY=$(printf '🚀 **UI Artifact Build Status:** `SUCCESS` ✅\n> ui-index: %s\n> ui-source: external' "$URL")
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$BODY"
Binary file not shown.
-26
View File
@@ -1,26 +0,0 @@
apiVersion: v1
entries:
harvester:
- annotations:
catalog.cattle.io/certified: rancher
catalog.cattle.io/display-name: Harvester
catalog.cattle.io/kube-version: '>= 1.16.0-0'
catalog.cattle.io/namespace: cattle-ui-plugin-system
catalog.cattle.io/os: linux
catalog.cattle.io/permits-os: linux, windows
catalog.cattle.io/rancher-version: '>= 2.15.0-0'
catalog.cattle.io/scope: management
catalog.cattle.io/ui-component: plugins
catalog.cattle.io/ui-extensions-version: '>= 3.0.0 < 4.0.0'
apiVersion: v2
appVersion: 1.10.0-dev
created: "2026-08-17T09:53:47.7476+08:00"
description: Rancher UI Extension for Harvester
digest: 8800a65761d4b9dbdadb9f9050b9053738021c43d02cf7249e6922e230fdd95b
icon: https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg
name: harvester
type: application
urls:
- assets/harvester/harvester-1.10.0-dev.tgz
version: 1.10.0-dev
generated: "2026-08-17T09:53:47.74717+08:00"
-19
View File
@@ -1,19 +0,0 @@
annotations:
catalog.cattle.io/certified: rancher
catalog.cattle.io/namespace: cattle-ui-plugin-system
catalog.cattle.io/os: linux
catalog.cattle.io/permits-os: linux, windows
catalog.cattle.io/scope: management
catalog.cattle.io/ui-component: plugins
catalog.cattle.io/display-name: Harvester
catalog.cattle.io/kube-version: '>= 1.16.0-0'
catalog.cattle.io/rancher-version: '>= 2.15.0-0'
catalog.cattle.io/ui-extensions-version: '>= 3.0.0 < 4.0.0'
apiVersion: v2
appVersion: 1.10.0-dev
description: Rancher UI Extension for Harvester
name: harvester
type: application
version: 1.10.0-dev
icon: >-
https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg
-177
View File
@@ -1,177 +0,0 @@
# harvester-ui-extension
The Harvester UI Extension is a Rancher extension that provides the user interface for [Harvester](https://harvesterhci.io) within the [Rancher Dashboard](https://github.com/rancher/dashboard).
> **Note:**
> This extension is available starting from **Rancher 2.10.0**. Ensure your Rancher version is **2.10.0 or later** to access Harvester integration.
## Installation
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io).
## Development Setup
Ensure **Node.js v20 or later** is installed for development and debugging.
### Standalone Mode
Run the extension standalone with hot reload at `https://localhost:8005`.
```bash
# Install dependencies
yarn install
# Start the development server
RANCHER_ENV=harvester API=https://your-harvester-ip yarn dev
# Example with specific server version
RANCHER_ENV=harvester VUE_APP_SERVER_VERSION=v1.5.0 API=https://192.168.1.123 yarn dev
```
You may also define environment variables in a `.env` file:
```env
RANCHER_ENV=harvester
VUE_APP_SERVER_VERSION=v1.5.0
API=https://192.168.1.123
```
### Rancher Integration Mode
To run as a Rancher extension, follow the [Rancher UI Extension Guide](https://extensions.rancher.io/extensions/next/extensions-getting-started#running-the-app).
```bash
API=https://your-rancher-ip yarn dev
```
## Commit Message Guidelines
This project uses [commit-lint](https://commitlint.js.org/) with [Conventional Commits](https://www.conventionalcommits.org/) to ensure consistent and meaningful commit messages.
### Commit Message Format
All commit messages must follow the conventional commit format:
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
### Supported Types
- **feat**: New features
- **fix**: Bug fixes
- **docs**: Documentation changes
- **style**: Code style changes (formatting, missing semicolons, etc.)
- **refactor**: Code refactoring
- **perf**: Performance improvements
- **test**: Adding or updating tests
- **build**: Build system or external dependencies
- **ci**: CI/CD changes
- **chore**: Other changes that don't modify src or test files
- **revert**: Reverts a previous commit
- **wip**: Work in progress
- **deps**: Dependency updates
- **security**: Security fixes
### Examples
```bash
# Feature
git commit -m "feat: add new virtual machine creation wizard"
# Bug fix
git commit -m "fix: resolve memory leak in VM console"
# Documentation
git commit -m "docs: update installation instructions"
# Breaking change
git commit -m "feat!: change API endpoint structure
BREAKING CHANGE: The /api/v1/vms endpoint has been replaced with /api/v2/vms"
```
### Git Hooks
The project uses [Husky](https://typicode.github.io/husky/) to automatically validate commit messages and run linting before commits:
- **pre-commit**: Runs ESLint to ensure code quality
- **commit-msg**: Validates commit message format using commit-lint
These hooks are automatically installed when you run `yarn install`.
### Manual Validation
You can manually validate commit messages:
```bash
# Validate the last commit
yarn commitlint
# Validate a specific commit
npx commitlint --from <commit-hash>
# Validate a range of commits
npx commitlint --from <start-hash> --to <end-hash>
```
## Branch Structure
- **`main`** – Main development branch
- **`release-harvester-vX.Y`** – Stable release branches per version series
- **`vX.Y-head`** – Testing branches for ongoing changes to extension builds in each release series
> **Note:**
> The `vX.Y-head` branches are auto-generated and kept in sync with release branches. Use these for testing the latest changes in each version series.
## Testing Guidelines
### UI Extension Testing
To validate changes in a release series, switch to the appropriate `vX.Y-head` branch. For main branch testing, use `main-head`.
- Examples:
- Test `1.0.x` series → `v1.0-head`
- Test `1.5.x` series → `v1.5-head`
**Steps:**
1. Navigate to **Rancher UI** → **Local** → **App** → **Repositories**
2. Refresh the Harvester repository using the target `vX.Y-head` branch
3. Go to the **Extensions** page and install the desired version
### Standalone Mode Testing
To test the standalone UI, configure Harvester to load the UI from an external source.
- Examples of `ui-index`:
- Main branch → `https://releases.rancher.com/harvester-ui/dashboard/latest/index.html`
- Release series `1.5.x` → `https://releases.rancher.com/harvester-ui/dashboard/release-harvester-v1.5/index.html`
**Steps:**
1. Go to **Harvester UI** → **Advanced** → **Settings** → **UI**
2. Set **ui-source** to `External`
3. Set **ui-index** to the desired URL
## Contributing
If you want to contribute, start by reading this document, then visit our [Getting Started guide](https://extensions.rancher.io/extensions/next/extensions-getting-started) to learn how to develop and submit changes.
## License
Copyright (c) 2014-2026 [SUSE, LLC.](https://www.suse.com/)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -1,63 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "extension-server.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "extension-server.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "extension-server.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "extension-server.labels" -}}
helm.sh/chart: {{ include "extension-server.chart" . }}
{{ include "extension-server.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | replace "+" "_" | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "extension-server.selectorLabels" -}}
app.kubernetes.io/name: {{ include "extension-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Pkg annotations
*/}}
{{- define "extension-server.pluginMetadata" -}}
{{- with .Values.plugin.metadata }}
{{- range $key, $value := . }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,15 +0,0 @@
apiVersion: catalog.cattle.io/v1
kind: UIPlugin
metadata:
name: {{ include "extension-server.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{ include "extension-server.labels" . | nindent 4 }}
spec:
plugin:
name: {{ include "extension-server.fullname" . }}
version: {{ (semver (default .Chart.AppVersion .Values.plugin.versionOverride)).Original }}
endpoint: {{ .Values.plugin.endpoint | quote }}
compressedEndpoint: {{ .Values.plugin.compressedEndpoint | quote }}
noCache: {{ .Values.plugin.noCache }}
noAuth: {{ .Values.plugin.noAuth }}
metadata: {{ include "extension-server.pluginMetadata" . | indent 6 }}
-16
View File
@@ -1,16 +0,0 @@
nameOverride: ""
fullnameOverride: ""
plugin:
enabled: true
versionOverride: ""
endpoint: >-
https://raw.githubusercontent.com/a110605/harvester-ui-extension/fix_forklift_ns_test/extensions/harvester/1.10.0-dev
compressedEndpoint: >-
https://raw.githubusercontent.com/a110605/harvester-ui-extension/fix_forklift_ns_test/extensions/harvester/1.10.0-dev.tgz
noCache: false
noAuth: false
metadata:
catalog.cattle.io/display-name: Harvester
catalog.cattle.io/kube-version: ">= 1.16.0-0"
catalog.cattle.io/rancher-version: ">= 2.15.0-0"
catalog.cattle.io/ui-extensions-version: ">= 3.0.0 < 4.0.0"
+1 -1
View File
@@ -36,7 +36,7 @@ module.exports = {
'body-leading-blank': [2, 'always'],
'body-max-line-length': [0, 'always', 100],
'footer-leading-blank': [2, 'always'],
'footer-max-line-length': [0, 'always', 100],
'footer-max-line-length': [2, 'always', 100],
},
// Ignore merge commits and revert commits
ignores: [
Binary file not shown.
-76
View File
@@ -1,76 +0,0 @@
plugin/harvester-1.10.0-dev.umd.min.106.js
plugin/harvester-1.10.0-dev.umd.min.106.js.map
plugin/harvester-1.10.0-dev.umd.min.238.js
plugin/harvester-1.10.0-dev.umd.min.238.js.map
plugin/harvester-1.10.0-dev.umd.min.277.js
plugin/harvester-1.10.0-dev.umd.min.277.js.map
plugin/harvester-1.10.0-dev.umd.min.303.js
plugin/harvester-1.10.0-dev.umd.min.303.js.map
plugin/harvester-1.10.0-dev.umd.min.37.js
plugin/harvester-1.10.0-dev.umd.min.37.js.map
plugin/harvester-1.10.0-dev.umd.min.375.js
plugin/harvester-1.10.0-dev.umd.min.375.js.map
plugin/harvester-1.10.0-dev.umd.min.431.js
plugin/harvester-1.10.0-dev.umd.min.431.js.map
plugin/harvester-1.10.0-dev.umd.min.446.js
plugin/harvester-1.10.0-dev.umd.min.446.js.map
plugin/harvester-1.10.0-dev.umd.min.460.js
plugin/harvester-1.10.0-dev.umd.min.460.js.map
plugin/harvester-1.10.0-dev.umd.min.489.js
plugin/harvester-1.10.0-dev.umd.min.489.js.map
plugin/harvester-1.10.0-dev.umd.min.573.js
plugin/harvester-1.10.0-dev.umd.min.573.js.map
plugin/harvester-1.10.0-dev.umd.min.60.js
plugin/harvester-1.10.0-dev.umd.min.60.js.map
plugin/harvester-1.10.0-dev.umd.min.619.js
plugin/harvester-1.10.0-dev.umd.min.619.js.map
plugin/harvester-1.10.0-dev.umd.min.682.js
plugin/harvester-1.10.0-dev.umd.min.682.js.map
plugin/harvester-1.10.0-dev.umd.min.730.js
plugin/harvester-1.10.0-dev.umd.min.730.js.map
plugin/harvester-1.10.0-dev.umd.min.740.js
plugin/harvester-1.10.0-dev.umd.min.740.js.map
plugin/harvester-1.10.0-dev.umd.min.763.js
plugin/harvester-1.10.0-dev.umd.min.763.js.map
plugin/harvester-1.10.0-dev.umd.min.813.js
plugin/harvester-1.10.0-dev.umd.min.813.js.map
plugin/harvester-1.10.0-dev.umd.min.856.js
plugin/harvester-1.10.0-dev.umd.min.856.js.map
plugin/harvester-1.10.0-dev.umd.min.880.js
plugin/harvester-1.10.0-dev.umd.min.880.js.map
plugin/harvester-1.10.0-dev.umd.min.895.js
plugin/harvester-1.10.0-dev.umd.min.895.js.map
plugin/harvester-1.10.0-dev.umd.min.97.js
plugin/harvester-1.10.0-dev.umd.min.97.js.map
plugin/harvester-1.10.0-dev.umd.min.csv.js
plugin/harvester-1.10.0-dev.umd.min.csv.js.map
plugin/harvester-1.10.0-dev.umd.min.detail.js
plugin/harvester-1.10.0-dev.umd.min.detail.js.map
plugin/harvester-1.10.0-dev.umd.min.dialog.js
plugin/harvester-1.10.0-dev.umd.min.dialog.js.map
plugin/harvester-1.10.0-dev.umd.min.edit.js
plugin/harvester-1.10.0-dev.umd.min.edit.js.map
plugin/harvester-1.10.0-dev.umd.min.formatters.js
plugin/harvester-1.10.0-dev.umd.min.formatters.js.map
plugin/harvester-1.10.0-dev.umd.min.js
plugin/harvester-1.10.0-dev.umd.min.js.map
plugin/harvester-1.10.0-dev.umd.min.list.js
plugin/harvester-1.10.0-dev.umd.min.list.js.map
plugin/harvester-1.10.0-dev.umd.min.promptRemove.js
plugin/harvester-1.10.0-dev.umd.min.promptRemove.js.map
plugin/harvester-1.10.0-dev.umd.min.xccdf.js
plugin/harvester-1.10.0-dev.umd.min.xccdf.js.map
plugin/harvester-1.10.0-dev.umd.min.xterm.js
plugin/harvester-1.10.0-dev.umd.min.xterm.js.map
plugin/img/custom.6f45244d.svg
plugin/img/email.b67ceff2.svg
plugin/img/generic-catalog.1842a50e.svg
plugin/img/generic-plugin.f1ebdd74.svg
plugin/img/grafana.2db31bb8.svg
plugin/img/harvester.0168283e.svg
plugin/img/icon.42954c0f.svg
plugin/img/pagerduty.9871fbd3.svg
plugin/img/prometheus.be8ac54d.svg
plugin/img/slack.34dfd73f.svg
plugin/img/webhook.f86c012b.svg
plugin/package.json
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
"use strict";(("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]=("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]||[]).push([[277],{28277:function(e,n,t){t.r(n),t.d(n,{default:function(){return f}});var r=t(9274);function s(e,n,t,s,o,c){const u=(0,r.resolveComponent)("ResourceDetail");return(0,r.openBlock)(),(0,r.createBlock)(u)}var o=t(97867),c={name:"ClusterResourceNamespaceId",components:{ResourceDetail:o.A}},u=t(47433);const a=(0,u.A)(c,[["render",s]]);var f=a}}]);
//# sourceMappingURL=harvester-1.10.0-dev.umd.min.277.js.map
@@ -1 +0,0 @@
{"version":3,"file":"harvester-1.10.0-dev.umd.min.277.js","mappings":"uWAUEA,EAAAA,EAAAA,aAAkBC,E,gBAPpB,GACEC,KAAY,6BACZC,WAAY,CAAEC,eAAcA,EAAAA,I,WCA9B,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,KAEpE,O","sources":["webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/_namespace/_id.vue","webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/_namespace/_id.vue?9724"],"sourcesContent":["<script>\nimport ResourceDetail from '@shell/components/ResourceDetail';\n\nexport default {\n name: 'ClusterResourceNamespaceId',\n components: { ResourceDetail },\n};\n</script>\n\n<template>\n <ResourceDetail />\n</template>\n","import { render } from \"./_id.vue?vue&type=template&id=36f5b1f9\"\nimport script from \"./_id.vue?vue&type=script&lang=js\"\nexport * from \"./_id.vue?vue&type=script&lang=js\"\n\nimport exportComponent from \"../../../../../../../../vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__"],"names":["_createBlock","_component_ResourceDetail","name","components","ResourceDetail","__exports__","render"],"sourceRoot":""}
@@ -1,2 +0,0 @@
"use strict";(("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]=("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]||[]).push([[303],{66303:function(e,n,t){t.r(n),t.d(n,{default:function(){return f}});var r=t(9274);function s(e,n,t,s,o,u){const c=(0,r.resolveComponent)("ResourceDetail");return(0,r.openBlock)(),(0,r.createBlock)(c)}var o=t(97867),u={name:"ClusterResourcedId",components:{ResourceDetail:o.A}},c=t(47433);const a=(0,c.A)(u,[["render",s]]);var f=a}}]);
//# sourceMappingURL=harvester-1.10.0-dev.umd.min.303.js.map
@@ -1 +0,0 @@
{"version":3,"file":"harvester-1.10.0-dev.umd.min.303.js","mappings":"uWAUEA,EAAAA,EAAAA,aAAkBC,E,gBAPpB,GACEC,KAAY,qBACZC,WAAY,CAAEC,eAAcA,EAAAA,I,WCA9B,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,KAEpE,O","sources":["webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/_id.vue","webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/_id.vue?96bb"],"sourcesContent":["<script>\nimport ResourceDetail from '@shell/components/ResourceDetail';\n\nexport default {\n name: 'ClusterResourcedId',\n components: { ResourceDetail },\n};\n</script>\n\n<template>\n <ResourceDetail />\n</template>\n","import { render } from \"./_id.vue?vue&type=template&id=7d01dbfe\"\nimport script from \"./_id.vue?vue&type=script&lang=js\"\nexport * from \"./_id.vue?vue&type=script&lang=js\"\n\nimport exportComponent from \"../../../../../../../vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__"],"names":["_createBlock","_component_ResourceDetail","name","components","ResourceDetail","__exports__","render"],"sourceRoot":""}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
"use strict";(("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]=("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]||[]).push([[446],{36446:function(e,n,t){t.r(n),t.d(n,{default:function(){return i}});var s=t(9274);function r(e,n,t,r,o,u){const c=(0,s.resolveComponent)("ResourceList");return(0,s.openBlock)(),(0,s.createBlock)(c)}var o=t(62348),u={name:"ClusterResourcedList",components:{ResourceList:o.A}},c=t(47433);const f=(0,c.A)(u,[["render",r]]);var i=f}}]);
//# sourceMappingURL=harvester-1.10.0-dev.umd.min.446.js.map
@@ -1 +0,0 @@
{"version":3,"file":"harvester-1.10.0-dev.umd.min.446.js","mappings":"qWAUEA,EAAAA,EAAAA,aAAgBC,E,gBAPlB,GACEC,KAAY,uBACZC,WAAY,CAAEC,aAAYA,EAAAA,I,WCA5B,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,KAEpE,O","sources":["webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/index.vue","webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/index.vue?1ef3"],"sourcesContent":["<script>\nimport ResourceList from '@shell/components/ResourceList';\n\nexport default {\n name: 'ClusterResourcedList',\n components: { ResourceList },\n};\n</script>\n\n<template>\n <ResourceList />\n</template>\n","import { render } from \"./index.vue?vue&type=template&id=00d07840\"\nimport script from \"./index.vue?vue&type=script&lang=js\"\nexport * from \"./index.vue?vue&type=script&lang=js\"\n\nimport exportComponent from \"../../../../../../../vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__"],"names":["_createBlock","_component_ResourceList","name","components","ResourceList","__exports__","render"],"sourceRoot":""}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
(("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]=("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]||[]).push([[60],{45060:function(e,t,n){var o,a,i,r=n(4364);(function(n,r){a=[],o=r,i="function"===typeof o?o.apply(t,a):o,void 0===i||(e.exports=i)})(0,function(){"use strict";function t(e,t){return"undefined"==typeof t?t={autoBom:!1}:"object"!=typeof t&&(r.warn("Deprecated: Expected third argument to be a object"),t={autoBom:!t}),t.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)?new Blob(["\ufeff",e],{type:e.type}):e}function o(e,t,n){var o=new XMLHttpRequest;o.open("GET",e),o.responseType="blob",o.onload=function(){f(o.response,t,n)},o.onerror=function(){r.error("could not download file")},o.send()}function a(e){var t=new XMLHttpRequest;t.open("HEAD",e,!1);try{t.send()}catch(e){}return 200<=t.status&&299>=t.status}function i(e){try{e.dispatchEvent(new MouseEvent("click"))}catch(o){var t=document.createEvent("MouseEvents");t.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),e.dispatchEvent(t)}}var s="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof n.g&&n.g.global===n.g?n.g:void 0,c=s.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),f=s.saveAs||("object"!=typeof window||window!==s?function(){}:"download"in HTMLAnchorElement.prototype&&!c?function(e,t,n){var r=s.URL||s.webkitURL,c=document.createElement("a");t=t||e.name||"download",c.download=t,c.rel="noopener","string"==typeof e?(c.href=e,c.origin===location.origin?i(c):a(c.href)?o(e,t,n):i(c,c.target="_blank")):(c.href=r.createObjectURL(e),setTimeout(function(){r.revokeObjectURL(c.href)},4e4),setTimeout(function(){i(c)},0))}:"msSaveOrOpenBlob"in navigator?function(e,n,r){if(n=n||e.name||"download","string"!=typeof e)navigator.msSaveOrOpenBlob(t(e,r),n);else if(a(e))o(e,n,r);else{var s=document.createElement("a");s.href=e,s.target="_blank",setTimeout(function(){i(s)})}}:function(e,t,n,a){if(a=a||open("","_blank"),a&&(a.document.title=a.document.body.innerText="downloading..."),"string"==typeof e)return o(e,t,n);var i="application/octet-stream"===e.type,r=/constructor/i.test(s.HTMLElement)||s.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent);if((f||i&&r||c)&&"undefined"!=typeof FileReader){var u=new FileReader;u.onloadend=function(){var e=u.result;e=f?e:e.replace(/^data:[^;]*;/,"data:attachment/file;"),a?a.location.href=e:location=e,a=null},u.readAsDataURL(e)}else{var l=s.URL||s.webkitURL,d=l.createObjectURL(e);a?a.location=d:location.href=d,a=null,setTimeout(function(){l.revokeObjectURL(d)},4e4)}});s.saveAs=f.saveAs=f,e.exports=f})}}]);
//# sourceMappingURL=harvester-1.10.0-dev.umd.min.60.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
"use strict";(("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]=("undefined"!==typeof self?self:this)["webpackChunkharvester_1_10_0_dev"]||[]).push([[856],{13856:function(e,t,n){n.r(t),n.d(t,{default:function(){return f}});var r=n(9274);function s(e,t,n,s,o,c){const u=(0,r.resolveComponent)("ResourceDetail");return(0,r.openBlock)(),(0,r.createBlock)(u,{"component-testid":"cluster-manager-create"})}var o=n(97867),c={name:"ClusterResourceCreate",components:{ResourceDetail:o.A}},u=n(47433);const a=(0,u.A)(c,[["render",s]]);var f=a}}]);
//# sourceMappingURL=harvester-1.10.0-dev.umd.min.856.js.map
@@ -1 +0,0 @@
{"version":3,"file":"harvester-1.10.0-dev.umd.min.856.js","mappings":"uWAUEA,EAAAA,EAAAA,aAA4DC,EAAA,CAA5C,mBAAiB,0B,gBAPnC,GACEC,KAAY,wBACZC,WAAY,CAAEC,eAAcA,EAAAA,I,WCA9B,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,KAEpE,O","sources":["webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/create.vue","webpack://harvester-1.10.0-dev/../../node_modules/@rancher/shell/pages/c/_cluster/_product/_resource/create.vue?41a9"],"sourcesContent":["<script>\nimport ResourceDetail from '@shell/components/ResourceDetail';\n\nexport default {\n name: 'ClusterResourceCreate',\n components: { ResourceDetail },\n};\n</script>\n\n<template>\n <ResourceDetail component-testid=\"cluster-manager-create\" />\n</template>\n","import { render } from \"./create.vue?vue&type=template&id=60b40cbd\"\nimport script from \"./create.vue?vue&type=script&lang=js\"\nexport * from \"./create.vue?vue&type=script&lang=js\"\n\nimport exportComponent from \"../../../../../../../vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render]])\n\nexport default __exports__"],"names":["_createBlock","_component_ResourceDetail","name","components","ResourceDetail","__exports__","render"],"sourceRoot":""}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#3D98D3;}
</style>
<path class="st0" d="M44.5300789,20.6782227c-0.1426773-0.6309566-0.6984367-1.0951176-1.3626976-1.0951176
c-0.1240196,0-0.244236,0.0162106-0.3586922,0.046484l0.0096703-0.0022469
c-0.3308601,0.0887699-0.7106476,0.1397476-1.1024437,0.1397476c-2.440136,0-4.4182625-1.9781246-4.4182625-4.4182625
c0-1.2042961,0.481739-2.296093,1.2631836-3.092968l-0.0006828,0.0006838
c0.2468758-0.2515621,0.3993149-0.5965824,0.3993149-0.9772463c0-0.4067383-0.1740227-0.7728519-0.4516602-1.027832
l-0.0009766-0.0009766c-2.0785141-1.9058599-4.5794945-3.3877935-7.3433609-4.2872076l-0.1422844-0.0400391
c-0.125391-0.0409179-0.2697258-0.0645504-0.4197273-0.0645504c-0.6379871,0-1.1758804,0.4281249-1.342186,1.012598
l-0.0023422,0.0097651c-0.5352535,1.8808599-2.2378902,3.234961-4.2569351,3.234961s-3.7215824-1.3541994-4.2493153-3.2038088
l-0.007618-0.0311522c-0.1687489-0.5944333-0.7067375-1.0225587-1.344532-1.0225587
c-0.1499023,0-0.2942371,0.023633-0.4295902,0.0673828l0.0098629-0.0028319
c-2.9060535,0.9396482-5.4071283,2.4213872-7.5004873,4.3408208l0.0149412-0.0134773
c-0.2786131,0.2559576-0.4527349,0.6220713-0.4527349,1.0288086c0,0.3805666,0.152441,0.7255859,0.3995123,0.9773436
l-0.0001955-0.0001945c0.7806644,0.7961903,1.2625008,1.8879881,1.2625008,3.0922852
c0,2.440136-1.9781246,4.4182606-4.4182615,4.4182606c-0.3916993,0-0.7715821-0.0509758-1.1333003-0.1466789l0.0308595,0.0069332
c-0.104785-0.0281239-0.2249026-0.0443344-0.3489256-0.0443344c-0.6641603,0-1.2200193,0.4640617-1.3608398,1.085741
l-0.0017581,0.0092773c-0.2987304,1.2988281-0.4699221,2.7902355-0.4699221,4.3217773
c0,1.5315456,0.1711912,3.0228539,0.4955082,4.4562492l-0.0255861-0.1344738
c0.1426754,0.6309566,0.6984377,1.0951157,1.3625979,1.0951157c0.124023,0,0.2442384-0.0162086,0.3586912-0.0464859
l-0.0097656,0.0021496c0.3308592-0.0887699,0.710742-0.1397457,1.1025391-0.1397457
c2.4401369,0,4.4183598,1.9781265,4.4183598,4.4182625c0,1.2042961-0.4818354,2.296093-1.2631836,3.0930672l0.0006838-0.0006828
c-0.2468748,0.2514648-0.3993158,0.5965843-0.3993158,0.9772453c0,0.4067383,0.1740236,0.77285,0.4517584,1.0279274
l0.0009766,0.0009804c2.078516,1.9058571,4.5794926,3.3876953,7.343359,4.2872047l0.1422844,0.0400391
c0.1254883,0.0410194,0.2697258,0.0645523,0.4197273,0.0645523c0.6378899,0,1.1758785-0.4281235,1.3421879-1.0126953
l0.0023441-0.009861c0.5352535-1.880764,2.2379875-3.2349625,4.2568359-3.2349625s3.7216797,1.3541985,4.2493153,3.2038078
l0.007618,0.0311546c0.1687489,0.5944328,0.7067375,1.0224609,1.3446293,1.0224609
c0.1498051,0,0.2941399-0.0236359,0.4293957-0.0673866l-0.0098648,0.002739
c2.9061527-0.9396515,5.4072247-2.4214859,7.5004864-4.3408241l-0.014843,0.0134773
c0.278614-0.2559547,0.4527321-0.6220703,0.4527321-1.0288086c0-0.3805656-0.1523438-0.7255859-0.3995132-0.9774399
l0.0001984,0.0001945c-0.7806625-0.7962875-1.2625008-1.887989-1.2625008-3.0922852
c0-2.440136,1.9781265-4.4182625,4.4182625-4.4182625c0.3917961,0,0.7715836,0.0509758,1.1333008,0.1466808l-0.0307617-0.0069351
c0.1046867,0.0281239,0.2249031,0.0443363,0.3490257,0.0443363c0.6641579,0,1.2200203-0.4640636,1.3608398-1.085743
l0.0017586-0.0093765c0.2986336-1.2988262,0.4699211-2.7902336,0.4699211-4.3217773s-0.1711884-3.02285-0.4955063-4.4562492
l0.0255852,0.1344719L44.5300789,20.6782227z M25,31.25c-3.4517593,0-6.25-2.7982407-6.25-6.25s2.7982407-6.25,6.25-6.25
s6.25,2.7982407,6.25,6.25l0,0C31.25,28.4517593,28.4517593,31.25,25,31.25L25,31.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 51 51" style="enable-background:new 0 0 51 51;" xml:space="preserve">
<style type="text/css">
.st0{fill:#3D98D3;}
</style>
<g>
<path class="st0" d="M7.5491395,26.9503384c0-10.7451725,7.6761146-18.2928314,19.0600967-18.2928314
c10.4466,0,16.8416233,5.3725863,16.8416233,14.7533474c0,6.4384232-2.8989372,11.1288033-8.2281265,11.1288033
c-1.9615593,0-3.9231167-1.0241776-4.6070576-2.6437626c-1.3644123,1.7046452-3.6662045,2.6437626-6.1832485,2.6437626
c-4.3918076,0-7.8010998-2.3451881-7.8010998-7.6327171c0-5.7978802,3.7946606-10.2330837,9.9779091-10.2330837
c2.4302483,0,5.2423935,0.597147,7.1206303,1.6213226l-0.5554886,9.7209969
c-0.1284561,1.7897034,0.725605,2.6854248,1.919899,2.6854248c2.2601318,0,3.0690575-2.942337,3.0690575-7.2907467
c0-6.9505119-3.7529984-10.1480255-11.5540981-10.1480255c-8.1448059,0-13.7725687,4.9889545-13.7725687,13.6875095
c0,7.2907467,4.4751291,10.9152889,12.4914789,10.9152889c2.4753838,0,5.2042046-0.2985725,7.676115-1.1526337v4.3918076
c-2.2601318,0.8540611-4.777174,1.2376938-7.676115,1.2376938C14.7114305,42.342495,7.5491395,36.9265099,7.5491395,26.9503384z
M28.442337,27.1204548l0.3402348-5.8412762c-0.597147-0.2551765-1.4477329-0.3836327-2.3017921-0.3836327
c-2.6455002,0-4.4786015,2.34519-4.4786015,5.6277618c0,2.5587063,1.0241756,3.7946606,2.9440727,3.7946606
C27.2480431,30.3179684,28.3138809,28.8684978,28.442337,27.1204548z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
<path d="M432.4,135.1c38,0,76,5.9,112.6,17.8c0,98.2,0,196.4,0,294.6c-36.6-11.9-74.6-17.8-112.6-17.8s-76,5.9-112.6,17.8l0,0
c-36.6-11.9-74.6-17.8-112.6-17.8s-76,5.9-112.6,17.8c0-98.2,0-196.4,0-294.6c36.6-11.9,74.6-17.8,112.6-17.8s76,5.9,112.6,17.8l0,0
C356.4,141.1,394.4,135.1,432.4,135.1z M432.4,105.1c-38.2,0-76.1,5.5-112.6,16.4c-36.6-10.9-74.4-16.4-112.6-16.4
c-41.5,0-82.5,6.5-121.9,19.3l-20.7,6.7v357.8l39.3-12.8c33.4-10.9,68.2-16.4,103.4-16.4s69.9,5.5,103.4,16.4l9.3,3l9.3-3
c33.4-10.9,68.2-16.4,103.4-16.4s69.9,5.5,103.4,16.4l39.3,12.8V131.2l-20.7-6.7C514.9,111.6,473.9,105.1,432.4,105.1L432.4,105.1
L432.4,105.1z"/>
<path d="M320,534.9l-3.9-1.3c-35.1-11.4-71.7-17.2-108.7-17.2s-73.6,5.8-108.8,17.2l-7.7-23.8c37.7-12.2,76.9-18.5,116.5-18.5
c38.3,0,76.1,5.8,112.6,17.2c36.5-11.4,74.4-17.2,112.6-17.2c39.6,0,78.8,6.2,116.5,18.5l-7.7,23.8c-35.2-11.4-71.8-17.2-108.8-17.2
s-73.6,5.8-108.8,17.2L320,534.9L320,534.9z"/>
<path d="M307.5,148h25v311h-25V148z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="#777" transform="translate(0, 0) scale(1, 1) "><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></g></svg>

Before

Width:  |  Height:  |  Size: 430 B

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-969.9529" y1="487.6317" x2="-969.9529" y2="470.7364" gradientTransform="matrix(2.7039 0 0 2.7039 2652.6287 -1252.246)">
<stop offset="0" style="stop-color:#FFF200"/>
<stop offset="1" style="stop-color:#F15A29"/>
</linearGradient>
<path class="st0" d="M53,27.1c-0.1-0.8-0.2-1.8-0.5-2.9c-0.3-1.1-0.7-2.2-1.3-3.4c-0.6-1.2-1.4-2.5-2.4-3.7c-0.4-0.5-0.8-1-1.3-1.4
c0.7-2.8-0.9-5.2-0.9-5.2c-2.7-0.2-4.4,0.8-5,1.3c-0.1,0-0.2-0.1-0.3-0.1c-0.5-0.2-0.9-0.4-1.4-0.5c-0.5-0.2-1-0.3-1.5-0.4
c-0.5-0.1-1-0.2-1.5-0.3c-0.1,0-0.2,0-0.3,0C35.3,6.6,32,5,32,5c-3.8,2.4-4.5,5.7-4.5,5.7s0,0.1,0,0.2C27.2,11,27,11,26.8,11.1
c-0.3,0.1-0.6,0.2-0.9,0.3c-0.3,0.1-0.6,0.2-0.9,0.3c-0.6,0.2-1.1,0.5-1.7,0.8c-0.5,0.3-1.1,0.6-1.6,1c-0.1,0-0.1-0.1-0.1-0.1
c-5.2-2-9.8,0.4-9.8,0.4c-0.4,5.5,2.1,9,2.6,9.7c-0.1,0.3-0.2,0.7-0.3,1c-0.4,1.3-0.7,2.5-0.8,3.9c0,0.2,0,0.4-0.1,0.6
C8.4,31.4,7,36.3,7,36.3c4,4.6,8.7,4.9,8.7,4.9c0,0,0,0,0,0c0.6,1.1,1.3,2.1,2.1,3c0.3,0.4,0.7,0.8,1,1.1C17.3,49.5,19,53,19,53
c4.5,0.2,7.4-2,8-2.4c0.4,0.2,0.9,0.3,1.4,0.4c1.4,0.4,2.8,0.6,4.2,0.6c0.4,0,0.7,0,1.1,0h0.2l0.1,0l0.2,0l0.2,0l0,0
c2.1,3,5.8,3.4,5.8,3.4c2.6-2.8,2.8-5.5,2.8-6.1l0,0c0,0,0,0,0,0c0,0,0-0.1,0-0.1l0,0c0,0,0-0.1,0-0.1c0.6-0.4,1.1-0.8,1.6-1.2
c1.1-1,2-2,2.7-3.2c0.1-0.1,0.1-0.2,0.2-0.3c3,0.2,5.1-1.8,5.1-1.8c-0.5-3.1-2.3-4.6-2.6-4.9l0,0c0,0,0,0,0,0c0,0,0,0,0,0l0,0
c0,0,0,0-0.1,0c0-0.2,0-0.4,0-0.6c0-0.3,0-0.7,0-1v-0.2v-0.1V35c0-0.1,0-0.1,0-0.1l0-0.2l0-0.3c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3
l0-0.3l0-0.3c0-0.3-0.1-0.7-0.2-1c-0.3-1.3-0.8-2.6-1.5-3.7c-0.7-1.1-1.5-2.2-2.5-3c-1-0.9-2-1.5-3.2-2c-1.1-0.5-2.3-0.8-3.5-1
c-0.6-0.1-1.2-0.1-1.8-0.1l-0.2,0h-0.1c0,0-0.1,0-0.1,0l-0.1,0l-0.2,0c-0.1,0-0.2,0-0.2,0c-0.3,0-0.6,0.1-0.9,0.1
c-1.2,0.2-2.3,0.6-3.3,1.2c-1,0.6-1.8,1.3-2.5,2.2c-0.7,0.8-1.2,1.8-1.6,2.7c-0.4,1-0.6,1.9-0.6,2.9c0,0.2,0,0.5,0,0.7
c0,0.1,0,0.1,0,0.2l0,0.2c0,0.1,0,0.2,0,0.3c0,0.5,0.1,0.9,0.3,1.4c0.3,0.9,0.7,1.7,1.2,2.4c0.5,0.7,1.1,1.3,1.8,1.7
c0.6,0.4,1.3,0.8,2,1s1.4,0.3,2,0.3c0.1,0,0.2,0,0.2,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0.1,0,0.1,0,0.2,0c0,0,0,0,0.1,0l0.1,0
c0,0,0.1,0,0.1,0c0.1,0,0.2,0,0.2,0c0.1,0,0.2,0,0.2,0c0.1,0,0.3-0.1,0.4-0.1c0.3-0.1,0.5-0.2,0.8-0.3s0.5-0.3,0.7-0.4
c0.1,0,0.1-0.1,0.2-0.1c0.2-0.2,0.3-0.5,0.1-0.7c-0.2-0.2-0.4-0.2-0.6-0.1c-0.1,0-0.1,0.1-0.2,0.1c-0.2,0.1-0.4,0.2-0.6,0.2
c-0.2,0.1-0.4,0.1-0.6,0.2c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1,0-0.2,0s-0.1,0-0.2,0c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0
h0l0,0c0,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.2,0c-0.5-0.1-1-0.2-1.5-0.4c-0.5-0.2-1-0.5-1.4-0.9c-0.4-0.4-0.8-0.8-1.1-1.4
c-0.3-0.5-0.5-1.1-0.6-1.7c0-0.3-0.1-0.6-0.1-0.9c0-0.1,0-0.2,0-0.3c0,0,0,0,0,0v0l0-0.1c0,0,0-0.1,0-0.1c0-0.2,0-0.3,0.1-0.5
c0.2-1.3,0.9-2.6,1.9-3.6c0.3-0.2,0.5-0.5,0.8-0.7c0.3-0.2,0.6-0.4,0.9-0.5c0.3-0.2,0.7-0.3,1-0.4c0.4-0.1,0.7-0.2,1.1-0.2
c0.2,0,0.4,0,0.5,0c0,0,0.1,0,0.1,0l0.1,0l0.1,0c0,0,0,0,0,0h0l0.1,0c0.4,0,0.8,0.1,1.2,0.2c0.8,0.2,1.5,0.5,2.2,0.8
c1.4,0.8,2.6,2,3.3,3.5c0.4,0.7,0.6,1.5,0.8,2.3c0,0.2,0.1,0.4,0.1,0.6l0,0.2l0,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.1,0,0.1v0.1l0,0.2
c0,0.1,0,0.3,0,0.4c0,0.2,0,0.4-0.1,0.7c0,0.2-0.1,0.4-0.1,0.7c0,0.2-0.1,0.4-0.1,0.7c-0.1,0.4-0.2,0.9-0.4,1.3
c-0.3,0.8-0.8,1.6-1.3,2.4c-1.1,1.5-2.5,2.6-4.2,3.4c-0.8,0.4-1.7,0.6-2.6,0.8c-0.4,0.1-0.9,0.1-1.4,0.1l-0.1,0h-0.1H34h-0.2h-0.1
c0.1,0,0,0,0,0h0c-0.2,0-0.5,0-0.7,0c-1-0.1-1.9-0.2-2.8-0.5c-0.9-0.3-1.8-0.6-2.7-1.1c-1.7-0.9-3.2-2.2-4.4-3.7
c-0.6-0.7-1.1-1.6-1.6-2.4s-0.8-1.7-1-2.6c-0.2-0.9-0.4-1.8-0.5-2.8l0-0.2l0,0v0v-0.1l0-0.2v0v-0.1v-0.1l0-0.2v0c0,0,0,0,0,0v-0.1
c0-0.1,0-0.2,0-0.3c0-0.5,0.1-0.9,0.1-1.4c0.1-0.5,0.1-1,0.2-1.4s0.2-0.9,0.3-1.4c0.3-0.9,0.6-1.8,1-2.7c0.8-1.7,1.8-3.2,3-4.4
c0.3-0.3,0.6-0.6,1-0.8c0.3-0.3,0.7-0.5,1-0.7c0.4-0.2,0.7-0.4,1.1-0.6c0.2-0.1,0.4-0.2,0.6-0.3c0.1,0,0.2-0.1,0.3-0.1
c0.1,0,0.2-0.1,0.3-0.1c0.4-0.2,0.8-0.3,1.2-0.4c0.1,0,0.2-0.1,0.3-0.1c0.1,0,0.2-0.1,0.3-0.1C30.8,16,31,16,31.3,16
c0.1,0,0.2,0,0.3-0.1c0.1,0,0.2,0,0.3-0.1c0.1,0,0.2,0,0.3-0.1l0.2,0l0.2,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0c0.1,0,0.3,0,0.4,0
c0.1,0,0.2,0,0.2,0l0.2,0l0.1,0l0.1,0c0.1,0,0.2,0,0.4,0l0.2,0c0,0,0.1,0,0,0h0l0.1,0c0.1,0,0.2,0,0.3,0c0.4,0,0.8,0,1.2,0
c0.8,0,1.6,0.1,2.3,0.3c1.5,0.3,3,0.8,4.3,1.4c1.3,0.6,2.5,1.4,3.5,2.3c0.1,0.1,0.1,0.1,0.2,0.2c0.1,0.1,0.1,0.1,0.2,0.2
c0.1,0.1,0.2,0.2,0.4,0.3s0.2,0.2,0.3,0.3c0.1,0.1,0.2,0.2,0.3,0.3c0.4,0.5,0.8,0.9,1.2,1.4c0.7,0.9,1.3,1.9,1.8,2.7
c0,0.1,0.1,0.1,0.1,0.2c0,0.1,0.1,0.1,0.1,0.2c0.1,0.1,0.1,0.2,0.2,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3
c0.2,0.4,0.3,0.8,0.5,1.2c0.2,0.6,0.4,1.1,0.5,1.6c0,0.2,0.2,0.3,0.4,0.3c0.2,0,0.4-0.2,0.4-0.4C53,28.3,53,27.7,53,27.1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 194.25619 159.08912"><defs><style>.cls-1{fill:#00a383;}.cls-2{fill:#fff;}</style></defs><rect class="cls-1" width="194.25619" height="159.08912" rx="20.56068"/><path class="cls-2" d="M180.4487,81.46673a4.97417,4.97417,0,0,0,.25287-.81463c.01635-.07209.0296-.14435.04284-.21759a4.98972,4.98972,0,0,0,0-1.78c-.01324-.07324-.02649-.14551-.04284-.21759a4.97458,4.97458,0,0,0-.25287-.81464c-.01587-.03815-.03748-.07269-.05432-.11035a4.9932,4.9932,0,0,0-.23017-.46741l-19.7666-34.2373a4.96949,4.96949,0,0,0-.28741-.42926c-.02527-.03491-.04535-.07251-.07154-.10681a4.97881,4.97881,0,0,0-.57611-.62305c-.05584-.05176-.11346-.10089-.17206-.15033a5.00763,5.00763,0,0,0-.71991-.51843l-.00305-.0022-.00274-.00122a5.00234,5.00234,0,0,0-.80945-.36469c-.07269-.02624-.14459-.05175-.2179-.0744a4.97937,4.97937,0,0,0-.82483-.18683c-.04693-.0061-.0935-.00457-.14044-.00927a4.98006,4.98006,0,0,0-.50464-.03351H116.53335a4.96842,4.96842,0,0,0-.504.03345c-.0476.00482-.09478.00329-.14233.00946a4.97566,4.97566,0,0,0-.82226.18627c-.07447.023-.14753.049-.22138.07563a5.00058,5.00058,0,0,0-.80719.36383l-.00281.00128-.00305.0022a5.00874,5.00874,0,0,0-.71991.51843c-.05859.04944-.11615.09845-.172.15027a4.97536,4.97536,0,0,0-.57635.62335c-.026.034-.04584.07123-.07086.10584a4.98409,4.98409,0,0,0-.2879.43L93.88046,74.54449H79.278l13.99317-24.2373h6.4956a5,5,0,0,0,0-10H90.38443a5.0003,5.0003,0,0,0-4.33008,2.5L67.73111,74.54449h-14.602l13.99316-24.2373h6.49561a5,5,0,0,0,0-10H64.2355a5.00031,5.00031,0,0,0-4.33008,2.5L41.58218,74.54449H26.9797l13.99359-24.2373H47.4689a5,5,0,0,0,0-10H38.08658a5.00029,5.00029,0,0,0-4.33008,2.5L13.98941,77.04449a4.98206,4.98206,0,0,0-.22925.46594c-.01715.0384-.03912.07349-.05536.11243a4.974,4.974,0,0,0-.25183.81128c-.01691.07422-.03058.1485-.04413.22388a4.99817,4.99817,0,0,0-.089.88305l-.00036.00342.00036.00342a4.99828,4.99828,0,0,0,.089.88306c.01355.07538.02722.14966.04413.22387a4.97371,4.97371,0,0,0,.25183.81128c.01624.03894.03821.074.05536.11243a4.98084,4.98084,0,0,0,.22925.46594L33.7565,116.28131a5.00031,5.00031,0,0,0,4.33008,2.5H47.4689a5,5,0,1,0,0-10H40.97329L26.97976,84.54449H41.58218l18.32324,31.73682a5.00033,5.00033,0,0,0,4.33008,2.5h9.38233a5,5,0,0,0,0-10H67.12222L53.12912,84.54449h14.602l18.32324,31.73682a5.00032,5.00032,0,0,0,4.33008,2.5h9.38232a5,5,0,0,0,0-10h-6.4956L79.278,84.54449H93.88052l18.32276,31.73682a4.97193,4.97193,0,0,0,.28759.42944c.02521.03479.04517.07233.07135.10657a4.98039,4.98039,0,0,0,.57605.62311c.05591.05188.11371.10095.17237.15051a5.00138,5.00138,0,0,0,.71966.51825l.00305.0022c.02063.0119.04254.01953.0633.03112a4.99593,4.99593,0,0,0,.52386.25788c.15222.06329.30756.11084.46283.15808.051.0155.09961.037.15119.0509a4.96725,4.96725,0,0,0,1.29394.17292l.01508-.001h39.51379l.01508.001a4.96725,4.96725,0,0,0,1.29394-.17292c.05158-.01391.10016-.0354.15119-.0509.15527-.04724.3106-.09479.46282-.15808a4.99511,4.99511,0,0,0,.52387-.25788c.02075-.01159.04266-.01922.06329-.03112l.00312-.0022a5.00713,5.00713,0,0,0,.71856-.51733c.05939-.05011.11774-.09986.17432-.15229a4.97862,4.97862,0,0,0,.57458-.6214c.027-.03546.04773-.07415.0738-.11023a4.98327,4.98327,0,0,0,.2857-.42663l19.7666-34.23682a4.99182,4.99182,0,0,0,.23017-.4674C180.41122,81.53943,180.43283,81.50488,180.4487,81.46673Zm-24.38111,22.31452L150.2941,93.78094l5.33294-9.23645h11.54688ZM105.42776,84.54449h11.54706l5.33289,9.23633-5.77417,10.00061ZM116.5336,55.307l5.77411,10.00031-5.33295,9.23718H105.4277Zm14.43432,33.47382-5.333-9.23633,5.333-9.2373h10.666l5.333,9.2373-5.333,9.23633Zm10.666-28.47363H130.9678l-5.77392-10h22.2135ZM130.96786,98.78082h10.666l5.77362,10.00049H125.19375ZM155.6271,74.54449l-5.333-9.2373,5.77343-10L167.174,74.54449Z"/></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g id="icomoon-ignore">
</g>
<path d="M23,12.5c0,0,0-0.1,0-0.1c0,0,0,0,0,0c0-0.1,0-0.2,0-0.2c0,0,0,0,0,0c0,0,0-0.1,0-0.1c0,0,0,0,0,0c0,0,0,0,0-0.1l-2.6-4.5
c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1-0.1-0.1c0,0,0,0,0,0c0,0-0.1,0-0.1-0.1l0,0l0,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0
c0,0,0,0,0,0c0,0,0,0-0.1,0h-5.2c0,0,0,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0l0,0l0,0c0,0-0.1,0-0.1,0.1
c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1l-2.4,4.2H9.7l1.8-3.2h0.9c0.4,0,0.7-0.3,0.7-0.7c0-0.4-0.3-0.7-0.7-0.7
h-1.2c-0.2,0-0.5,0.1-0.6,0.3l-2.4,4.2H6.2l1.8-3.2h0.9c0.4,0,0.7-0.3,0.7-0.7c0-0.4-0.3-0.7-0.7-0.7H7.7c-0.2,0-0.5,0.1-0.6,0.3
l-2.4,4.2H2.8l1.8-3.2h0.9c0.4,0,0.7-0.3,0.7-0.7S5.8,7.1,5.5,7.1H4.3C4,7.1,3.8,7.2,3.7,7.4l-2.6,4.5c0,0,0,0,0,0.1c0,0,0,0,0,0
c0,0,0,0.1,0,0.1c0,0,0,0,0,0c0,0,0,0.1,0,0.1l0,0l0,0c0,0,0,0.1,0,0.1c0,0,0,0,0,0c0,0,0,0.1,0,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1
l2.6,4.5c0.1,0.2,0.3,0.3,0.6,0.3h1.2c0.4,0,0.7-0.3,0.7-0.7s-0.3-0.7-0.7-0.7H4.6l-1.8-3.2h1.9l2.4,4.2c0.1,0.2,0.3,0.3,0.6,0.3
h1.2c0.4,0,0.7-0.3,0.7-0.7s-0.3-0.7-0.7-0.7H8.1l-1.8-3.2h1.9l2.4,4.2c0.1,0.2,0.3,0.3,0.6,0.3h1.2c0.4,0,0.7-0.3,0.7-0.7
c0-0.4-0.3-0.7-0.7-0.7h-0.9l-1.8-3.2h1.9l2.4,4.2c0,0,0,0,0,0.1c0,0,0,0,0,0c0,0,0,0.1,0.1,0.1c0,0,0,0,0,0c0,0,0.1,0,0.1,0.1l0,0
c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0.1,0,0.1,0,0.2,0l0,0l5.2,0c0.1,0,0.1,0,0.2,0c0,0,0,0,0,0c0,0,0,0,0.1,0
c0,0,0,0,0.1,0c0,0,0,0,0,0l0,0c0,0,0.1,0,0.1-0.1c0,0,0,0,0,0c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0-0.1L23,12.5
C22.9,12.5,22.9,12.5,23,12.5C22.9,12.5,22.9,12.5,23,12.5L23,12.5z M19.7,15.4L19,14.1l0.7-1.2h1.5L19.7,15.4L19.7,15.4z
M13.1,12.9h1.5l0.7,1.2l-0.8,1.3L13.1,12.9z M14.6,9l0.8,1.3l-0.7,1.2h-1.5L14.6,9L14.6,9z M16.5,13.4l-0.7-1.2l0.7-1.2h1.4
l0.7,1.2l-0.7,1.2H16.5z M17.9,9.7h-1.4l-0.8-1.3h2.9L17.9,9.7L17.9,9.7z M16.5,14.8h1.4l0.8,1.3h-2.9L16.5,14.8L16.5,14.8z
M19.7,11.6L19,10.4L19.7,9l1.5,2.5L19.7,11.6L19.7,11.6z"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
<style type="text/css">
.st0{fill:#25C151;}
.st1{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M51.3,55H8.7c-2,0-3.7-1.7-3.7-3.7V8.7C5,6.7,6.7,5,8.7,5h42.6c2,0,3.7,1.7,3.7,3.7v42.6
C55,53.3,53.3,55,51.3,55z"/>
<g>
<g>
<path class="st1" d="M41.4,38.4C41.4,38.4,41.4,38.4,41.4,38.4l-5.6,0c-2,0-3.2-0.8-3.8-1.5c-1.2-1.4-1.3-3.1-1.2-3.6v-6.7
c0-1.9,0.8-3.1,1.5-3.7c1.4-1.3,3.2-1.3,3.6-1.3h7v-6.1h3.6v17.9c0,1.8-0.8,3-1.4,3.6C43.6,38.3,41.8,38.4,41.4,38.4z M34.3,33.5
c0,0.2,0,0.7,0.3,1c0.3,0.3,0.8,0.3,1.2,0.3h5.6c1.4,0,1.4-1.1,1.4-1.4v-8.3l-7.2,0c0,0,0,0,0,0c-0.2,0-0.7,0.1-1,0.3
c-0.2,0.2-0.3,0.6-0.3,1.1L34.3,33.5z"/>
</g>
<g>
<path class="st1" d="M17.2,44.5h-3.6V26.5c0-1.8,0.8-3,1.4-3.6c1.4-1.4,3.3-1.4,3.6-1.4h5.6c2,0,3.2,0.8,3.8,1.5
c1.3,1.4,1.3,3.1,1.2,3.6v6.8c0,1.9-0.8,3.1-1.5,3.7c-1.4,1.3-3.2,1.3-3.6,1.3h-7V44.5z M24.4,34.8c0.2,0,0.7-0.1,1-0.3
c0.2-0.2,0.3-0.6,0.3-1.1l0-6.9c0-0.2,0-0.7-0.3-1c-0.3-0.3-0.8-0.3-1.2-0.3h-5.6c-1.4,0-1.4,1.1-1.4,1.4v8.3L24.4,34.8z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E6532B;}
</style>
<path id="path4486" class="st0" d="M30,5C16.2,5,5,16.2,5,30c0,13.8,11.2,25,25,25s25-11.2,25-25C55,16.2,43.8,5,30,5z M30,51.8
c-3.9,0-7.1-2.6-7.1-5.9h14.2C37.1,49.2,33.9,51.8,30,51.8z M41.7,44H18.3v-4.3h23.5L41.7,44L41.7,44z M41.7,37.5H18.3
c-0.1-0.1-0.2-0.2-0.2-0.3c-2.4-2.9-3-4.4-3.5-6c0-0.1,2.9,0.6,5,1.1c0,0,1.1,0.2,2.6,0.5c-1.5-1.8-2.4-4-2.4-6.3
c0-5,3.8-9.4,2.5-12.9c1.3,0.1,2.8,2.8,2.9,7.1c1.4-2,2-5.6,2-7.8c0-2.3,1.5-5,3-5.1c-1.4,2.2,0.3,4.1,1.9,8.9
c0.6,1.8,0.5,4.8,0.9,6.7c0.1-3.9,0.8-9.7,3.3-11.7c-1.1,2.5,0.2,5.6,1,7.1c1.4,2.4,2.2,4.3,2.2,7.7c0,2.3-0.9,4.5-2.3,6.2
c1.7-0.3,2.8-0.6,2.8-0.6l5.4-1C45.4,31.2,44.7,34.4,41.7,37.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#36C5F0;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2EB67D;}
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#ECB22E;}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#E01E5A;}
</style>
<g>
<path class="st0" d="M19.5139961,5.3530526c-2.2111454,0.0016346-4.0006533,1.7960448-3.9990196,4.0071902
c-0.0016336,2.2111444,1.7895088,4.0055552,4.0006523,4.0071888h4.0006542V9.3618765
C23.5179176,7.1507316,21.7267761,5.3563209,19.5139961,5.3530526C19.5156288,5.3530526,19.5156288,5.3530526,19.5139961,5.3530526
M19.5139961,16.0410709H8.8488569c-2.2111449,0.0016346-4.0022864,1.7960453-4.0006523,4.0071907
c-0.0032687,2.2111435,1.7878733,4.0055561,3.9990187,4.0088234h10.6667728
c2.2111454-0.0016346,4.0022869-1.7960453,4.0006523-4.0071907C23.516283,17.8371162,21.7251415,16.0427055,19.5139961,16.0410709
L19.5139961,16.0410709z"/>
<path class="st1" d="M44.8481941,20.0482616c0.0016365-2.2111454-1.789505-4.0055561-4.0006523-4.0071907
c-2.2111473,0.0016346-4.0022888,1.7960453-4.0006523,4.0071907v4.0088234h4.0006523
C43.0586891,24.0554504,44.8498268,22.2610397,44.8481941,20.0482616z M34.1814232,20.0482616V9.3602428
c0.0016327-2.2095108-1.7878723-4.0039215-3.9990177-4.0071898c-2.2111454,0.0016341-4.0022869,1.7960443-4.0006542,4.0071898
v10.6880188c-0.0032673,2.2111435,1.7878742,4.0055561,3.9990177,4.0088234
C32.3919144,24.0554504,34.1830559,22.2610397,34.1814232,20.0482616z"/>
<path class="st2" d="M30.1807671,45.4331207c2.2111473-0.0016365,4.0022888-1.7960434,4.0006561-4.0071869
c0.0016327-2.2111473-1.7895088-4.005558-4.0006561-4.0071907h-4.0006523v4.0071907
C26.1784801,43.6354408,27.9696217,45.4298553,30.1807671,45.4331207z M30.1807671,34.7434692h10.6667747
c2.2111473-0.0016327,4.002285-1.7960434,4.0006523-4.0071888c0.0032692-2.2111454-1.7878685-4.0055561-3.9990158-4.0088253
H30.1824055c-2.2111454,0.0016346-4.0022869,1.7960453-4.0006542,4.0071888
c-0.0016327,2.2127819,1.7878742,4.0071888,3.9990177,4.0088253H30.1807671z"/>
<path class="st3" d="M4.8482037,30.7362785c-0.0016341,2.2111473,1.7895079,4.0055542,4.0006533,4.0071869
c2.2111444-0.0016327,4.0022869-1.7960434,4.0006523-4.0071869v-4.0071888H8.8488569
C6.6377115,26.7307224,4.8465695,28.525135,4.8482037,30.7362785z M15.5149765,30.7362785v10.6880188
c-0.0032682,2.2111435,1.7878742,4.005558,3.9990196,4.0088234c2.2111454-0.0016365,4.0022869-1.7960434,4.0006523-4.0071869
V30.7395477c0.0032692-2.2111454-1.7878723-4.0055542-3.9990177-4.0088253c-2.2127781,0-4.0022869,1.7944126-4.0006533,4.0055561
c0,0,0,0.0016327,0,0"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#37474F;}
.st1{fill:#E91E63;}
</style>
<path class="st0" d="M35,29c-1.4691162,0-2.7455444,0.8115845-3.44104,2H20v2c0,3.2999878-2.7000122,6-6,6s-6-2.7000122-6-6
c0-2.7000122,1.9000244-5.0999756,4.5-5.7999878l-1-3.9000244C7.0999756,24.5,4,28.4000244,4,33c0,5.5,4.5,10,10,10
c4.7999878,0,8.9000244-3.4000244,9.7999878-8H31.55896c0.6954956,1.1884155,1.9719238,2,3.44104,2c2.2000122,0,4-1.7999878,4-4
S37.2000122,29,35,29z"/>
<path class="st0" d="M35.0999756,23c-1,0-2,0.2000122-3,0.5l-3.6618652-6.4946289C28.7860107,16.4129028,29,15.7330933,29,15
c0-2.2000122-1.7999878-4-4-4s-4,1.7999878-4,4c0,2.1869507,1.7796631,3.9749756,3.9619141,3.9961548l5.4381104,9.803833
l1.6999512-1C33,27.4000244,34,27.0999756,35,27.0999756c3.2999878,0,6,2.7000122,6,6c0,3.3000488-2.7000122,6-6,6
c-1.7999878,0-3.5999756-0.8999634-4.7000122-2.2999878l-3.0999756,2.5C29.0999756,41.5999756,32,43,35,43
c5.5,0,10.0999756-4.5,10.0999756-10S40.5999756,23,35.0999756,23z"/>
<path class="st1" d="M25,5c-5.5,0-10,4.5-10,10c0,2.7999878,1.2000122,5.4000244,3.2000122,7.2999878l-4.0825195,6.7119751
C14.0775757,29.0107422,14.0402222,29,14,29c-2.2000122,0-4,1.7999878-4,4s1.7999878,4,4,4s4-1.7999878,4-4
c0-0.6972656-0.1966553-1.3445435-0.5137329-1.9168701l6.1137085-9.9831543l-1.6999512-1C20.0999756,19,19,17.0999756,19,15
c0-3.2999878,2.7000122-6,6-6s6,2.7000122,6,6c0,0.5-0.0999756,1-0.2000122,1.5l3.9000244,1
C34.9000244,16.7000122,35,15.9000244,35,15C35,9.5,30.5,5,25,5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,20 +0,0 @@
{
"name": "harvester",
"description": "Rancher UI Extension for Harvester",
"version": "1.10.0-dev",
"private": false,
"rancher": true,
"icon": "https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg",
"engines": {
"node": ">=24.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.9",
"@vue/cli-service": "~5.0.9",
"@vue/cli-plugin-typescript": "~5.0.9"
},
"files": [
"**/*"
],
"main": "harvester-1.10.0-dev.umd.min.js"
}
-26
View File
@@ -1,26 +0,0 @@
apiVersion: v1
entries:
harvester:
- annotations:
catalog.cattle.io/certified: rancher
catalog.cattle.io/display-name: Harvester
catalog.cattle.io/kube-version: '>= 1.16.0-0'
catalog.cattle.io/namespace: cattle-ui-plugin-system
catalog.cattle.io/os: linux
catalog.cattle.io/permits-os: linux, windows
catalog.cattle.io/rancher-version: '>= 2.15.0-0'
catalog.cattle.io/scope: management
catalog.cattle.io/ui-component: plugins
catalog.cattle.io/ui-extensions-version: '>= 3.0.0 < 4.0.0'
apiVersion: v2
appVersion: 1.10.0-dev
created: "2026-08-17T09:53:47.7476+08:00"
description: Rancher UI Extension for Harvester
digest: 8800a65761d4b9dbdadb9f9050b9053738021c43d02cf7249e6922e230fdd95b
icon: https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg
name: harvester
type: application
urls:
- assets/harvester/harvester-1.10.0-dev.tgz
version: 1.10.0-dev
generated: "2026-08-17T09:53:47.74717+08:00"
+3 -9
View File
@@ -7,7 +7,7 @@
},
"dependencies": {
"@babel/plugin-transform-class-static-block": "7.29.7",
"@rancher/shell": "3.0.12-rc.7",
"@rancher/shell": "3.0.12-rc.3",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.1",
"@vue-flow/core": "^1.33.5",
@@ -15,12 +15,6 @@
"cache-loader": "^4.1.0",
"color": "4.2.3",
"ip": "2.0.1",
"@xterm/xterm": "6.0.0",
"@xterm/addon-fit": "0.11.0",
"@xterm/addon-search": "0.16.0",
"@xterm/addon-web-links": "0.12.0",
"@xterm/addon-webgl": "0.19.0",
"@xterm/addon-canvas": "0.7.0",
"node-polyfill-webpack-plugin": "^3.0.0",
"elkjs": "^0.11.0",
"vue-draggable-next": "^2.2.1",
@@ -35,12 +29,12 @@
"glob": "7.2.3",
"glob-parent": "6.0.2",
"json5": "2.2.3",
"@types/lodash": "4.17.25",
"@types/lodash": "4.17.24",
"merge": "2.1.1",
"node-forge": "1.4.0",
"nth-check": "2.1.1",
"qs": "6.15.3",
"roarr": "7.21.7",
"roarr": "7.21.6",
"semver": "7.8.5",
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
},
-5
View File
@@ -271,9 +271,4 @@ export default {
.filter-label .v-popper__arrow-container {
display: none;
}
.fixed-header-actions .middle .filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style>
@@ -116,11 +116,6 @@ export default {
.vm-schedule-dropdown .v-popper__arrow-container {
display: none;
}
.fixed-header-actions .middle .vm-schedule-filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style>
<style lang="scss" scoped>
+63 -442
View File
@@ -1,6 +1,7 @@
<script>
import { allHash } from '@shell/utils/promise';
import debounce from 'lodash/debounce';
import Socket, {
EVENT_CONNECTED,
EVENT_CONNECTING,
@@ -21,98 +22,74 @@ export default {
data() {
return {
socket: null,
terminal: null,
textDecoder: null,
fitAddon: null,
searchAddon: null,
webglAddon: null,
onResize: null,
isOpen: false,
isOpening: false,
backlog: [],
firstTime: true,
queue: [],
isDraining: false,
drainScheduled: false,
drainTimer: null,
showSearch: false,
searchQuery: '',
searchOptions: {
caseSensitive: false,
regex: false,
wholeWord: false,
incremental: true,
decorations: {
matchBackground: '#B45309',
matchBorder: '#FDBA74',
matchOverviewRuler: '#FDBA74',
activeMatchBackground: '#EA580C',
activeMatchBorder: '#FED7AA',
activeMatchColorOverviewRuler: '#FED7AA',
},
},
socket: null,
terminal: null,
fitAddon: null,
searchAddon: null,
webglAddon: null,
isOpen: false,
isOpening: false,
backlog: [],
firstTime: true,
queue: []
};
},
computed: {
serialConsoleUrl() {
return this.value?.getSerialConsolePath || '';
},
xtermConfig() {
return {
allowProposedApi: true,
cursorBlink: true,
cursorStyle: 'bar',
cursorWidth: 2,
scrollback: 5000,
useStyle: true,
fontFamily: 'JetBrains Mono, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
fontSize: 16,
lineHeight: 1.3,
fontSize: 12,
};
},
},
watch: {
queue: {
handler: debounce(async function(neu) {
if (neu.length === 0) {
return;
}
const msg = await Promise.all(neu);
(msg || []).forEach((m) => {
this.terminal.write(m);
});
this.queue = [];
}, 10),
deep: true
}
},
beforeUnmount() {
this.close();
},
watch: {
serialConsoleUrl() {
if (this.terminal) {
this.connect();
}
},
},
async mounted() {
this.textDecoder = new TextDecoder('utf-8');
await this.setupTerminal();
await this.connect();
this.onResize = debounce(() => this.fit(), 100);
window.addEventListener('resize', this.onResize);
this.scheduleFit();
},
methods: {
async setupTerminal() {
const docStyle = getComputedStyle(document.querySelector('body'));
const xterm = await import(/* webpackChunkName: "xterm" */ '@xterm/xterm');
const xterm = await import(/* webpackChunkName: "xterm" */ 'xterm');
const addons = await allHash({
fit: import(/* webpackChunkName: "xterm" */ '@xterm/addon-fit'),
webgl: import(/* webpackChunkName: "xterm" */ '@xterm/addon-webgl'),
weblinks: import(/* webpackChunkName: "xterm" */ '@xterm/addon-web-links'),
search: import(/* webpackChunkName: "xterm" */ '@xterm/addon-search'),
canvas: import(/* webpackChunkName: "xterm" */ '@xterm/addon-canvas'),
fit: import(/* webpackChunkName: "xterm" */ 'xterm-addon-fit'),
webgl: import(/* webpackChunkName: "xterm" */ 'xterm-addon-webgl'),
weblinks: import(/* webpackChunkName: "xterm" */ 'xterm-addon-web-links'),
search: import(/* webpackChunkName: "xterm" */ 'xterm-addon-search'),
});
const terminal = new xterm.Terminal({
theme: {
background: docStyle.getPropertyValue('--terminal-bg').trim(),
cursor: docStyle.getPropertyValue('--terminal-text').trim(),
cursor: docStyle.getPropertyValue('--terminal-cursor').trim(),
foreground: docStyle.getPropertyValue('--terminal-text').trim()
},
...this.xtermConfig,
@@ -120,48 +97,25 @@ export default {
this.fitAddon = new addons.fit.FitAddon();
this.searchAddon = new addons.search.SearchAddon();
try {
this.webglAddon = new addons.webgl.WebGlAddon();
} catch (e) {
// Some browsers (Safari) don't support the webgl renderer, so don't use it.
this.webglAddon = null;
}
terminal.loadAddon(this.fitAddon);
terminal.loadAddon(this.searchAddon);
terminal.loadAddon(new addons.weblinks.WebLinksAddon());
terminal.open(this.$refs.xterm);
// The webgl renderer can fail without throwing: the context may be lost
// mid-session, or it can attach but silently never paint, leaving the
// terminal's helper textarea at 0x0. Detect both cases and fall back to the
// canvas renderer so the terminal stays interactive.
try {
this.webglAddon = new addons.webgl.WebglAddon();
this.webglAddon.onContextLoss(() => {
if (!this.isUnmounted && this.terminal) {
this.fallbackToCanvas(terminal, addons);
}
});
if ( this.webglAddon ) {
terminal.loadAddon(this.webglAddon);
// Detect the silent "attaches but never paints" failure after a render frame.
requestAnimationFrame(() => {
if (this.isUnmounted || !this.terminal) {
return;
}
const textarea = this.$refs.xterm?.querySelector('.xterm-helper-textarea');
const hasPainted = textarea && textarea.getBoundingClientRect().height > 0;
if (this.webglAddon && !hasPainted) {
this.fallbackToCanvas(terminal, addons);
this.fit();
}
});
} catch (e) {
// Some browsers (e.g. Safari) don't support the webgl renderer, so don't use it.
this.fallbackToCanvas(terminal, addons);
}
this.fit();
this.flush();
this.scheduleDrainQueue();
terminal.onData((input) => {
const msg = this.str2ab(input);
@@ -169,132 +123,9 @@ export default {
this.write(msg);
});
terminal.attachCustomKeyEventHandler((event) => {
const key = event.key?.toLowerCase();
if ((event.ctrlKey || event.metaKey) && key === 'f') {
event.preventDefault();
this.openSearch();
return false;
}
if (key === 'escape' && this.showSearch) {
event.preventDefault();
this.closeSearch();
return false;
}
return true;
});
this.terminal = terminal;
},
openSearch() {
this.showSearch = true;
this.$nextTick(() => {
const input = this.$refs.searchInput;
if (input) {
input.focus();
input.select();
}
});
},
closeSearch() {
this.showSearch = false;
this.searchQuery = '';
this.clearSearchHighlights();
this.terminal?.focus();
},
clearSearchHighlights() {
this.searchAddon?.clearActiveDecoration();
this.searchAddon?.clearDecorations();
this.terminal?.clearSelection();
// Disposing the search decorations doesn't repaint the webgl/canvas
// layer, so force a refresh to remove the lingering highlight.
if (this.terminal) {
this.terminal.refresh(0, this.terminal.rows - 1);
}
},
findNext() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findNext(this.searchQuery, this.searchOptions);
},
findPrevious() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findPrevious(this.searchQuery, this.searchOptions);
},
onSearchInput() {
if (!this.searchQuery) {
this.clearSearchHighlights();
return;
}
this.findNext();
},
onSearchKeydown(event) {
if (event.key === 'Enter') {
event.preventDefault();
if (event.shiftKey) {
this.findPrevious();
} else {
this.findNext();
}
}
if (event.key === 'Escape') {
event.preventDefault();
this.closeSearch();
}
},
scheduleFit() {
this.$nextTick(() => {
requestAnimationFrame(() => {
this.fit();
// Re-fit after layout settles to avoid undersized rows on first paint.
setTimeout(() => this.fit(), 120);
});
});
},
// Dispose the webgl renderer (if any) and switch to the canvas renderer.
// Used when webgl fails to construct, loses its context, or silently never paints.
fallbackToCanvas(terminal, addons) {
if (this.isUnmounted) {
return;
}
this.webglAddon?.dispose();
this.webglAddon = null;
if (!this.canvasAddon) {
this.canvasAddon = new addons.canvas.CanvasAddon();
terminal.loadAddon(this.canvasAddon);
}
this.fit();
},
str2ab(str) {
const enc = new TextEncoder();
@@ -309,131 +140,12 @@ export default {
}
},
enqueueMessage(messagePromise) {
this.queue.push(messagePromise);
this.scheduleDrainQueue();
},
scheduleDrainQueue() {
if (this.drainScheduled || this.isDraining || !this.terminal) {
return;
}
this.drainScheduled = true;
const shouldDrainImmediately = this.queue.length <= 3;
const runDrain = () => {
this.drainScheduled = false;
this.drainTimer = null;
this.drainQueue();
};
if (shouldDrainImmediately) {
runDrain();
return;
}
this.drainTimer = requestAnimationFrame(runDrain);
},
async decodeMessageData(data) {
if (typeof data === 'string') {
return data;
}
if (!this.textDecoder) {
this.textDecoder = new TextDecoder('utf-8');
}
if (data instanceof Blob) {
const buffer = await data.arrayBuffer();
return this.textDecoder.decode(new Uint8Array(buffer), { stream: true });
}
if (data instanceof ArrayBuffer) {
return this.textDecoder.decode(new Uint8Array(data), { stream: true });
}
if (ArrayBuffer.isView(data)) {
const view = data;
const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
return this.textDecoder.decode(bytes, { stream: true });
}
return String(data || '');
},
async drainQueue() {
if (this.isDraining || !this.terminal) {
return;
}
this.isDraining = true;
try {
while (this.queue.length > 0) {
const pendingMessages = this.queue.splice(0, this.queue.length);
const settledMessages = await Promise.allSettled(pendingMessages);
const messages = settledMessages
.filter((result) => result.status === 'fulfilled')
.map((result) => result.value);
const output = messages.filter(Boolean).join('');
if (output) {
await this.writeOutputInChunks(output);
}
if (this.queue.length > 0) {
await this.nextFrame();
}
}
} finally {
this.isDraining = false;
// If data arrived between the last while-check and releasing the lock,
// immediately schedule another drain so output never gets stuck.
if (this.queue.length > 0 && this.terminal) {
this.scheduleDrainQueue();
}
}
},
async writeOutputInChunks(output) {
if (!output) {
return;
}
const chunkSize = output.length > 20000 ? 16000 : output.length > 8000 ? 8000 : output.length;
if (chunkSize === output.length) {
this.terminal.write(output);
return;
}
for (let index = 0; index < output.length; index += chunkSize) {
this.terminal.write(output.slice(index, index + chunkSize));
if (index + chunkSize < output.length) {
await this.nextFrame();
}
}
},
nextFrame() {
return new Promise((resolve) => requestAnimationFrame(resolve));
},
clear() {
this.terminal.clear();
},
getSocketUrl() {
return this.serialConsoleUrl;
return `${ this.value?.getSerialConsolePath }`;
},
async connect() {
@@ -469,7 +181,6 @@ export default {
this.fit();
this.flush();
}
this.scheduleFit();
if (this.firstTime) {
this.socket.send(this.str2ab('\n'));
@@ -484,7 +195,7 @@ export default {
});
this.socket.addEventListener(EVENT_MESSAGE, (e) => {
this.enqueueMessage(this.decodeMessageData(e.detail.data));
this.queue.push(e.detail.data.text());
});
this.socket.connect();
@@ -507,19 +218,22 @@ export default {
}
this.fitAddon.fit();
const { rows, cols } = this.fitAddon.proposeDimensions();
if ( !this.isOpen ) {
return;
}
const message = JSON.stringify({
Width: cols,
Height: rows
});
this.socket.send(this.str2ab(message));
},
close() {
if (this.onResize) {
window.removeEventListener('resize', this.onResize);
this.onResize = null;
}
if (this.drainTimer) {
cancelAnimationFrame(this.drainTimer);
this.drainTimer = null;
}
if ( this.socket ) {
this.socket.disconnect();
}
@@ -534,44 +248,6 @@ export default {
<template>
<div class="harvester-shell-container">
<div
v-if="showSearch"
class="shell-search"
>
<input
ref="searchInput"
v-model="searchQuery"
type="text"
class="shell-search-input"
placeholder="Search in terminal"
@input="onSearchInput"
@keydown="onSearchKeydown"
>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Shift+Enter"
@click="findPrevious"
>
Prev
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Enter"
@click="findNext"
>
Next
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Esc"
@click="closeSearch"
>
Close
</button>
</div>
<div
ref="xterm"
class="shell-body"
@@ -581,73 +257,18 @@ export default {
</template>
<style lang="scss">
@import '../../../../node_modules/@xterm/xterm/css/xterm.css';
@import '../../../../node_modules/xterm/css/xterm.css';
body, #__nuxt, #__layout, MAIN {
height: 100%;
}
.harvester-shell-container {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
min-height: 0;
overflow: hidden;
.resize-observer {
display: none;
}
.shell-search {
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-end;
position: absolute;
top: 8px;
right: 10px;
z-index: 5;
padding: 8px;
border-radius: 6px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--body-bg) 88%, transparent);
}
.shell-search-input {
min-width: 220px;
max-width: 420px;
flex: 0 1 320px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--body-bg);
color: var(--body-text);
padding: 6px 8px;
}
.shell-search-btn {
min-width: 64px;
}
.shell-body {
flex: 1 1 auto;
.shell-body, .terminal.xterm {
height: 100%;
width: 100%;
min-height: 0;
padding: 0 10px;
box-sizing: border-box;
}
.terminal.xterm,
.terminal.xterm .xterm-viewport,
.terminal.xterm .xterm-screen,
.terminal.xterm .xterm-scrollable-element {
width: 100%;
height: 100%;
}
.terminal.xterm .xterm-scrollable-element {
min-height: 100%;
}
}
</style>
+1 -5
View File
@@ -55,10 +55,6 @@ export default {
const host = window.location.host;
const prefix = window.location.pathname.replace(this.$route.path, '');
const params = this.$route?.params;
const popupWidth = Math.max(800, screen.width - 200);
const popupHeight = Math.max(600, screen.height - 200);
const popupLeft = Math.max(0, window.screenX + Math.floor((window.outerWidth - popupWidth) / 2));
const popupTop = Math.max(0, window.screenY + Math.floor((window.outerHeight - popupHeight) / 2));
const url = `https://${ host }${ prefix }/${ PRODUCT_NAME }/c/${ params.cluster }/console/${ uid }/${ type }`;
@@ -67,7 +63,7 @@ export default {
window.open(
url,
'_blank',
`toolbars=0,width=${ popupWidth },height=${ popupHeight },left=${ popupLeft },top=${ popupTop },noreferrer`
`toolbars=0,width=${ screen.width - 200 },height=${ screen.height - 200 },left=0,top=0,noreferrer`
);
});
},
@@ -7,15 +7,10 @@ import { STORAGE_CLASS, NETWORK_ATTACHMENT } from '@shell/config/types';
import { useI18n } from '@shell/composables/useI18n';
import { randomStr } from '@shell/utils/string';
import { HCI } from '../../types';
import { VOLUME_MODE, ACCESS_MODE } from '../../config/types';
import { FORKLIFT_NAMESPACE } from '../../config/harvester-map';
import { buildNetworkMapEntries, buildStorageMapEntries } from '../../utils/forklift';
import { isInternalStorageClass } from '../../utils/storage-class';
import MappingColumn from './MappingColumn.vue';
import StorageDefaultsModal from './StorageDefaultsModal.vue';
const DEFAULT_VOLUME_MODE = VOLUME_MODE.FILE_SYSTEM;
const DEFAULT_ACCESS_MODE = ACCESS_MODE?.READ_WRITE_MANY ?? 'ReadWriteMany';
const props = defineProps({
providerName: { type: String, default: '' },
@@ -40,10 +35,6 @@ const allStorageMaps = ref([]);
const errors = ref([]);
const loading = ref(true);
// Storage defaults edit modal state.
const showStorageDefaultsModal = ref(false);
const editingStorageEntry = ref(null);
const { networkEntries, storageEntries } = toRefs(props.stepData);
const NAMESPACE = FORKLIFT_NAMESPACE;
@@ -114,7 +105,7 @@ const applyNetworkMapTargets = (mapSpec) => {
});
};
const applyStorageMapTargets = (mapSpec, { markOverridden = false, captureInherited = false } = {}) => {
const applyStorageMapTargets = (mapSpec) => {
if (!mapSpec) {
return;
}
@@ -126,30 +117,6 @@ const applyStorageMapTargets = (mapSpec, { markOverridden = false, captureInheri
if (match?.destination?.storageClass) {
entry.target = match.destination.storageClass;
if (captureInherited) {
entry.inheritedFromProvider = true;
}
if (match.destination.volumeMode) {
entry.volumeMode = match.destination.volumeMode;
if (captureInherited) {
entry.inheritedVolumeMode = match.destination.volumeMode;
}
}
if (match.destination.accessMode) {
entry.accessModes = [match.destination.accessMode];
if (captureInherited) {
entry.inheritedAccessModes = [match.destination.accessMode];
}
}
if (markOverridden) {
entry.overridden = true;
}
}
});
};
@@ -167,39 +134,12 @@ const applyDefaultStorageMap = () => {
(sm) => sm.metadata.name === `${ props.providerName }-storage-map-default`
);
applyStorageMapTargets(defaultMap?.spec?.map, { captureInherited: true });
applyStorageMapTargets(defaultMap?.spec?.map);
};
const allNetworksMapped = computed(() => networkEntries.value.length > 0 && networkEntries.value.every((e) => !!e.target));
const allStorageMapped = computed(() => storageEntries.value.length > 0 && storageEntries.value.every((e) => !!e.target));
// The "Inherited from provider" hint only appears in the migration plan wizard
// (where mappings inherit from the provider default), not on the provider creation page.
const inheritedProviderName = computed(() => (props.useAllProviderData ? '' : props.providerName));
const openStorageDefaults = (entry) => {
editingStorageEntry.value = entry;
showStorageDefaultsModal.value = true;
};
const closeStorageDefaults = () => {
showStorageDefaultsModal.value = false;
editingStorageEntry.value = null;
};
const applyStorageDefaults = ({ volumeMode, accessModes }) => {
if (editingStorageEntry.value) {
editingStorageEntry.value.volumeMode = volumeMode;
editingStorageEntry.value.accessModes = accessModes;
const inheritedVolumeMode = editingStorageEntry.value.inheritedVolumeMode;
const inheritedAccessMode = editingStorageEntry.value.inheritedAccessModes?.[0];
const selectedAccessMode = accessModes?.[0];
editingStorageEntry.value.overridden = volumeMode !== inheritedVolumeMode || selectedAccessMode !== inheritedAccessMode;
}
};
const canSave = computed(() => {
if (props.useAllProviderData) {
return true;
@@ -262,19 +202,13 @@ const buildStorageEntries = () => {
if (ds && ds.id) {
if (!datastoreMap[ds.id]) {
datastoreMap[ds.id] = {
name: ds.name || t('harvester.addons.vmMigration.generic.unknown'),
id: ds.id,
type: ds.type || '',
capacity: 0,
target: '',
volumeMode: DEFAULT_VOLUME_MODE,
accessModes: [DEFAULT_ACCESS_MODE],
inheritedVolumeMode: DEFAULT_VOLUME_MODE,
inheritedAccessModes: [DEFAULT_ACCESS_MODE],
inheritedFromProvider: false,
overridden: false,
usedBy: [],
_key: `stor-${ ds.id }`,
name: ds.name || t('harvester.addons.vmMigration.generic.unknown'),
id: ds.id,
type: ds.type || '',
capacity: 0,
target: '',
usedBy: [],
_key: `stor-${ ds.id }`,
};
}
@@ -304,19 +238,13 @@ const buildNetworkEntriesFromProvider = (networksData) => {
const buildStorageEntriesFromProvider = (datastoresData) => {
storageEntries.value = (Array.isArray(datastoresData) ? datastoresData : []).map((ds) => ({
name: ds.name || ds.id,
id: ds.id || '',
type: ds.type || '',
capacity: ds.capacity || 0,
target: '',
volumeMode: DEFAULT_VOLUME_MODE,
accessModes: [DEFAULT_ACCESS_MODE],
inheritedVolumeMode: DEFAULT_VOLUME_MODE,
inheritedAccessModes: [DEFAULT_ACCESS_MODE],
inheritedFromProvider: false,
overridden: false,
usedBy: [],
_key: `stor-${ ds.id || ds.name }`,
name: ds.name || ds.id,
id: ds.id || '',
type: ds.type || '',
capacity: ds.capacity || 0,
target: '',
usedBy: [],
_key: `stor-${ ds.id || ds.name }`,
}));
};
@@ -533,7 +461,7 @@ const init = async() => {
if (!hasExistingStorageTargets) {
if (props.existingStorageMap?.spec?.map) {
applyStorageMapTargets(props.existingStorageMap.spec.map, { markOverridden: true });
applyStorageMapTargets(props.existingStorageMap.spec.map);
} else {
applyDefaultStorageMap();
}
@@ -583,9 +511,6 @@ init();
:placeholder="t('harvester.addons.vmMigration.configureMappings.storageMapping.placeholder')"
:show-used-by="!useAllProviderData"
:clearable="useAllProviderData"
:show-volume-settings="true"
:inherited-provider-name="inheritedProviderName"
@edit-defaults="openStorageDefaults"
>
<template #source-detail="{ entry }">
<span
@@ -595,19 +520,6 @@ init();
</template>
</MappingColumn>
</div>
<StorageDefaultsModal
v-if="showStorageDefaultsModal && editingStorageEntry"
:storage-class-name="editingStorageEntry.target"
:provider-name="providerName"
:show-inherited="!!inheritedProviderName"
:volume-mode="editingStorageEntry.volumeMode"
:access-modes="editingStorageEntry.accessModes"
:inherited-volume-mode="editingStorageEntry.inheritedVolumeMode"
:inherited-access-modes="editingStorageEntry.inheritedAccessModes"
@apply="applyStorageDefaults"
@close="closeStorageDefaults"
/>
</div>
</template>
@@ -42,7 +42,6 @@ const {
username,
password,
skipTlsVerify,
vddkInitImage,
testPassed,
testResult,
testError,
@@ -98,7 +97,6 @@ watch(selectedProvider, (val) => {
username.value = '';
password.value = '';
skipTlsVerify.value = false;
vddkInitImage.value = '';
createdProvider.value = null;
createdSecret.value = null;
} else {
@@ -109,7 +107,6 @@ watch(selectedProvider, (val) => {
if (provider) {
providerName.value = provider.metadata.name;
url.value = provider.spec?.url || '';
vddkInitImage.value = provider.spec?.settings?.vddkInitImage || '';
const secretRef = provider.spec?.secret;
@@ -242,18 +239,6 @@ const testConnection = async(buttonCb) => {
if (props.editMode && createdProvider.value) {
try {
createdProvider.value.spec.url = url.value;
const vddkImage = vddkInitImage.value?.trim();
if (vddkImage) {
createdProvider.value.spec.settings = {
...(createdProvider.value.spec.settings || {}),
vddkInitImage: vddkImage,
};
} else if (createdProvider.value.spec.settings) {
delete createdProvider.value.spec.settings.vddkInitImage;
}
await createdProvider.value.save();
const secretRef = createdProvider.value.spec?.secret;
@@ -298,28 +283,20 @@ const testConnection = async(buttonCb) => {
const namespace = FORKLIFT_NAMESPACE;
const secretName = `${ providerName.value }-creds-${ randomStr(4).toLowerCase() }`;
const spec = {
type: 'vsphere',
url: url.value,
secret: {
name: secretName,
namespace,
},
};
const vddkImage = vddkInitImage.value?.trim();
if (vddkImage) {
spec.settings = { vddkInitImage: vddkImage };
}
const provider = await store.dispatch(`${ inStore }/create`, {
type: HCI.FORKLIFT_PROVIDER,
metadata: {
name: providerName.value,
namespace,
},
spec,
spec: {
type: 'vsphere',
url: url.value,
secret: {
name: secretName,
namespace,
},
}
});
await provider.save();
@@ -443,28 +420,17 @@ defineExpose({ testConnection, clickTestButton });
/>
</div>
<div class="row">
<div class="col span-6">
<LabeledInput
v-model:value="url"
:label="t('harvester.addons.vmMigration.configureProvider.urlLabel')"
:placeholder="t('harvester.addons.vmMigration.configureProvider.urlPlaceholder')"
:disabled="isExistingProvider && !editMode"
required
/>
<p class="text-deemphasized mt-5">
{{ t('harvester.addons.vmMigration.configureProvider.urlHint') }}
</p>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="vddkInitImage"
:label="t('harvester.addons.vmMigration.configureProvider.vddkImageLabel')"
:placeholder="t('harvester.addons.vmMigration.configureProvider.vddkImagePlaceholder')"
:tooltip="t('harvester.addons.vmMigration.configureProvider.vddkImageTooltip')"
:disabled="isExistingProvider && !editMode"
/>
</div>
<div>
<LabeledInput
v-model:value="url"
:label="t('harvester.addons.vmMigration.configureProvider.urlLabel')"
:placeholder="t('harvester.addons.vmMigration.configureProvider.urlPlaceholder')"
:disabled="isExistingProvider && !editMode"
required
/>
<p class="text-deemphasized mt-5">
{{ t('harvester.addons.vmMigration.configureProvider.urlHint') }}
</p>
</div>
<div class="row">
@@ -3,27 +3,20 @@ import { useStore } from 'vuex';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { RcItemCard } from '@components/RcItemCard';
import { useI18n } from '@shell/composables/useI18n';
import { VOLUME_MODE } from '../../config/types';
const store = useStore();
const { t } = useI18n(store);
const props = defineProps({
title: { type: String, required: true },
description: { type: String, default: '' },
entries: { type: Array, default: () => [] },
options: { type: Array, default: () => [] },
placeholder: { type: String, default: '' },
showUsedBy: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
// Storage-specific: show the volume/access mode defaults row with an Edit action.
showVolumeSettings: { type: Boolean, default: false },
// When set, the defaults row shows an "Inherited from provider" hint (migration plan wizard only).
inheritedProviderName: { type: String, default: '' },
title: { type: String, required: true },
description: { type: String, default: '' },
entries: { type: Array, default: () => [] },
options: { type: Array, default: () => [] },
placeholder: { type: String, default: '' },
showUsedBy: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
});
const emit = defineEmits(['edit-defaults']);
// Only offer "Remove Map" for entries that already have a target selected;
// entries without a selection just show the regular options.
const optionsFor = (entry) => {
@@ -45,13 +38,6 @@ const optionsFor = (entry) => {
...props.options,
];
};
const formatModes = (entry) => {
const volumeMode = entry.volumeMode || VOLUME_MODE.FILE_SYSTEM;
const accessModes = (entry.accessModes || []).join(', ');
return t('harvester.addons.vmMigration.storageDefaults.summary', { volumeMode, accessModes });
};
</script>
<template>
@@ -101,29 +87,6 @@ const formatModes = (entry) => {
/>
</div>
</div>
<div
v-if="showVolumeSettings && entry.target"
class="storage-defaults-row"
>
<div class="storage-defaults">
<div class="storage-defaults-info">
<span class="storage-defaults-summary">{{ formatModes(entry) }}</span>
<span
v-if="inheritedProviderName && entry.inheritedFromProvider && !entry.overridden"
class="text-deemphasized storage-defaults-inherited"
>
{{ t('harvester.addons.vmMigration.storageDefaults.inherited', { provider: inheritedProviderName }) }}
</span>
</div>
<a
role="button"
class="storage-defaults-edit"
@click.prevent="emit('edit-defaults', entry)"
>
{{ t('harvester.addons.vmMigration.storageDefaults.edit') }}
</a>
</div>
</div>
<div v-if="showUsedBy && entry.usedBy && entry.usedBy.length">
<span class="used-by">
{{ t('harvester.addons.vmMigration.generic.usedBy') }} <b>{{ entry.usedBy.join(', ') }}</b>
@@ -210,41 +173,6 @@ const formatModes = (entry) => {
line-height: 20px;
}
.storage-defaults-row {
width: 100%;
}
.storage-defaults {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 8px 12px;
border-radius: 4px;
background-color: var(--body-bg);
.storage-defaults-info {
display: flex;
flex-direction: column;
min-width: 0;
}
.storage-defaults-summary {
font-size: 13px;
line-height: 20px;
}
.storage-defaults-inherited {
font-size: 12px;
line-height: 18px;
}
.storage-defaults-edit {
flex-shrink: 0;
cursor: pointer;
}
}
.bg-light-gray {
background-color: var(--category-active) !important;
border: 0;
@@ -35,8 +35,6 @@ const storageMappings = ref([]);
const planName = ref('');
const targetNamespace = ref('');
const namespaceOptions = ref([]);
const existingProviderNames = ref([]);
const existingPlanNames = ref([]);
const errors = ref([]);
const loading = ref(true);
@@ -46,24 +44,6 @@ targetNamespace.value = props.stepData.targetNamespace || '';
const NS = FORKLIFT_NAMESPACE;
const nameConflictError = computed(() => {
const name = (planName.value || '').trim();
if (!name) {
return '';
}
if (existingProviderNames.value.includes(name)) {
return t('harvester.addons.vmMigration.reviewMigration.planNameProviderConflict', { name });
}
if (existingPlanNames.value.includes(name)) {
return t('harvester.addons.vmMigration.reviewMigration.planNamePlanConflict', { name });
}
return '';
});
watch(planName, (val) => {
props.stepData.planName = val;
}, { immediate: true });
@@ -72,8 +52,8 @@ watch(targetNamespace, (val) => {
props.stepData.targetNamespace = val;
});
watch([planName, targetNamespace, nameConflictError], () => {
emit('ready', !!planName.value && !!targetNamespace.value && !nameConflictError.value);
watch([planName, targetNamespace], ([name, namespace]) => {
emit('ready', !!name && !!namespace);
}, { immediate: true });
const formatNetworkTarget = (target = '') => {
@@ -273,6 +253,19 @@ const startMigrationAction = async() => {
await plan.save();
const planOwnerRef = {
apiVersion: FORKLIFT_API_VERSION,
kind: 'Plan',
name: plan.metadata.name,
uid: plan.metadata.uid,
blockOwnerDeletion: true,
};
networkMap.metadata.ownerReferences = [planOwnerRef];
await networkMap.save();
storageMap.metadata.ownerReferences = [planOwnerRef];
await storageMap.save();
// Kick off the first migration through the model so the Migration payload
// lives in a single place (also reused by the dashboard start/restart action).
await plan.startMigration();
@@ -289,22 +282,6 @@ const init = async() => {
namespaceOptions.value = [];
}
try {
await store.dispatch(`${ inStore }/findAll`, { type: HCI.FORKLIFT_PROVIDER });
} catch (e) {}
existingProviderNames.value = (store.getters[`${ inStore }/all`](HCI.FORKLIFT_PROVIDER) || [])
.map((p) => p.metadata?.name)
.filter(Boolean);
try {
await store.dispatch(`${ inStore }/findAll`, { type: HCI.FORKLIFT_PLAN });
} catch (e) {}
existingPlanNames.value = (store.getters[`${ inStore }/all`](HCI.FORKLIFT_PLAN) || [])
.map((p) => p.metadata?.name)
.filter(Boolean);
if (props.mappingEntries) {
networkMappings.value = (props.mappingEntries.networkEntries || []).map((entry) => ({
source: entry.name || entry.id,
@@ -336,12 +313,6 @@ defineExpose({ startMigration: startMigrationAction });
<div
class="review-migration-content"
>
<Banner
v-if="nameConflictError"
color="error"
:label="nameConflictError"
/>
<!-- Migration Details Summary -->
<div class="migration-details">
<div class="section-header">
@@ -25,9 +25,8 @@ const { discoveredVMs, selectedVMIds, tableRows } = toRefs(props.stepData);
const selectedVMs = ref([]);
const loading = ref(true);
// Restored from stepData so friendly names survive step navigation / remount.
const networkMap = ref(props.stepData.networkMap || {});
const datastoreMap = ref(props.stepData.datastoreMap || {});
const networkMap = ref({});
const datastoreMap = ref({});
const sortableTableRef = ref(null);
const allVMsSelected = ref(false);
const errors = ref([]);
@@ -95,7 +94,7 @@ const showSelectAllBanner = computed(() => {
const theadElement = computed(() => sortableTableRef.value?.$el?.querySelector('thead'));
const headers = ref([
const headers = [
{
name: 'vmName',
labelKey: 'harvester.addons.vmMigration.selectVms.columns.vmName',
@@ -140,7 +139,7 @@ const headers = ref([
subLabel: t('harvester.addons.vmMigration.generic.identifier'),
width: 200,
},
]);
];
const buildTableRows = () => {
return discoveredVMs.value.map((vm) => {
@@ -191,7 +190,6 @@ const buildTableRows = () => {
return {
_original: vm,
_key: vm.id || vm.vmId || vm.metadata?.name,
selectedSort: selectedVMIds.value.has(vm.id) ? 0 : 1,
vmName: vm.name || vm.metadata?.name || '-',
vmId: vm.id || vm.vmId || vm.metadata?.name || '-',
os: vm.guestName || vm.guestOS || vm.os || '-',
@@ -241,10 +239,6 @@ const clearSelection = () => {
if (table) {
table.clearSelection();
if (typeof table.setPage === 'function') {
table.setPage(1);
}
}
});
};
@@ -255,49 +249,6 @@ const selectAllVMs = () => {
selectedVMs.value = discoveredVMs.value.slice();
};
// Moves currently-selected VMs to the first page(s) and re-checks them.
const sortSelectedToFront = () => {
if (selectedVMIds.value.size === 0) {
return;
}
tableRows.value.forEach((row) => {
row.selectedSort = selectedVMIds.value.has(row._original?.id) ? 0 : 1;
});
headers.value = headers.value.map((h) => (
h.name === 'vmName' ? { ...h, sort: ['selectedSort', 'vmName'] } : h
));
skipNextSelectionEvent = true;
nextTick(() => {
const table = sortableTableRef.value;
if (!table) {
skipNextSelectionEvent = false;
return;
}
if (typeof table.changeSort === 'function') {
table.changeSort('vmName', false);
} else if (typeof table.setPage === 'function') {
table.setPage(1);
}
nextTick(() => {
const rowsToReselect = (table.pagedRows || []).filter((row) => selectedVMIds.value.has(row._original?.id));
if (rowsToReselect.length > 0) {
table.update(rowsToReselect, []);
}
skipNextSelectionEvent = false;
});
});
};
watch(
() => sortableTableRef.value?.page,
() => {
@@ -367,9 +318,6 @@ const fetchVMs = async() => {
return map;
}, {});
props.stepData.networkMap = networkMap.value;
props.stepData.datastoreMap = datastoreMap.value;
lastFetchedAt.value = Date.now();
tableRows.value = buildTableRows();
};
@@ -426,10 +374,6 @@ const init = async() => {
tableRows.value = buildTableRows();
loading.value = false;
// Returning to the step with an existing selection: bring selected VMs forward and re-check them.
await nextTick();
sortSelectedToFront();
return;
}
@@ -497,6 +441,7 @@ init();
:row-actions="false"
:groupable="false"
:paging="true"
:rows-per-page="20"
key-field="_key"
@selection="onSelect"
>
@@ -505,20 +450,7 @@ init();
<h3 class="m-0">
{{ t('harvester.addons.vmMigration.selectVms.availableVms') }}
</h3>
<span class="selected-actions">
<span class="text-deemphasized">
{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}
</span>
<template v-if="selectedCount > 0">
<span class="text-deemphasized">|</span>
<a
role="button"
@click.prevent="clearSelection"
>
{{ t('harvester.addons.vmMigration.selectVms.clearAll') }}
</a>
</template>
</span>
<span class="text-deemphasized">{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}</span>
</div>
</template>
<template #cell:vmName="{ row }">
@@ -534,7 +466,7 @@ init();
<template #cell:powerState="{ row }">
<BadgeState
:label="row.powerState"
:color="row.powerStateClass === 'power-on' ? 'bg-success' : 'bg-darker'"
:color="row.powerStateClass === 'power-on' ? 'bg-warning' : 'bg-darker'"
/>
</template>
<template #cell:network="{ row }">
@@ -601,23 +533,6 @@ init();
.text-deemphasized {
font-size: 13px;
}
.selected-actions {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
a {
cursor: pointer;
&.disabled {
color: var(--muted);
cursor: default;
pointer-events: none;
}
}
}
}
.vm-name-cell {
@@ -1,181 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useStore } from 'vuex';
import { Card } from '@components/Card';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import AppModal from '@shell/components/AppModal';
import { useI18n } from '@shell/composables/useI18n';
import { VOLUME_MODE, ACCESS_MODE } from '../../config/types';
const VOLUME_MODE_OPTIONS = [VOLUME_MODE.FILE_SYSTEM, VOLUME_MODE.BLOCK];
const {
READ_WRITE_ONCE = 'ReadWriteOnce',
READ_ONLY_MANY = 'ReadOnlyMany',
READ_WRITE_MANY = 'ReadWriteMany',
READ_WRITE_ONCE_POD = 'ReadWriteOncePod'
} = ACCESS_MODE || {};
const ACCESS_MODE_OPTIONS = [READ_WRITE_ONCE, READ_WRITE_MANY, READ_ONLY_MANY, READ_WRITE_ONCE_POD];
const props = defineProps({
storageClassName: { type: String, default: '' },
providerName: { type: String, default: '' },
showInherited: { type: Boolean, default: false },
volumeMode: { type: String, default: VOLUME_MODE.FILE_SYSTEM },
accessModes: { type: Array, default: () => ['ReadWriteMany'] },
inheritedVolumeMode: { type: String, default: VOLUME_MODE.FILE_SYSTEM },
inheritedAccessModes: { type: Array, default: () => ['ReadWriteMany'] },
});
const emit = defineEmits(['apply', 'close']);
const store = useStore();
const { t } = useI18n(store);
const localVolumeMode = ref(props.volumeMode || VOLUME_MODE.FILE_SYSTEM);
const localAccessMode = ref(props.accessModes?.[0] || READ_WRITE_MANY);
const volumeModeOptions = VOLUME_MODE_OPTIONS.map((value) => ({ label: value, value }));
const accessModeOptions = ACCESS_MODE_OPTIONS.map((value) => ({ label: value, value }));
const apply = () => {
emit('apply', {
volumeMode: localVolumeMode.value,
accessModes: [localAccessMode.value],
});
emit('close');
};
// Repopulate the dropdowns with the provider default values; the user then applies.
const reset = () => {
localVolumeMode.value = props.inheritedVolumeMode || VOLUME_MODE.FILE_SYSTEM;
localAccessMode.value = props.inheritedAccessModes?.[0] || READ_WRITE_MANY;
};
const cancel = () => {
emit('close');
};
</script>
<template>
<app-modal
class="storage-defaults-modal"
name="storageDefaultsDialog"
:width="620"
height="auto"
:click-to-close="false"
@close="cancel"
>
<Card
class="storage-defaults-card"
:show-highlight-border="false"
>
<template #title>
<h4 class="text-default-text">
{{ t('harvester.addons.vmMigration.storageDefaults.title', { name: storageClassName }) }}
</h4>
</template>
<template #body>
<p class="text-deemphasized description">
<template v-if="showInherited">
{{ t('harvester.addons.vmMigration.storageDefaults.descriptionInherited', { name: storageClassName, provider: providerName }) }}
</template>
<template v-else>
{{ t('harvester.addons.vmMigration.storageDefaults.description', { name: storageClassName }) }}
</template>
</p>
<div class="settings-box">
<LabeledSelect
v-model:value="localVolumeMode"
class="volume-mode"
:label="t('harvester.addons.vmMigration.storageDefaults.volumeMode')"
:options="volumeModeOptions"
:searchable="false"
:clearable="false"
/>
<LabeledSelect
v-model:value="localAccessMode"
class="access-mode"
:label="t('harvester.addons.vmMigration.storageDefaults.accessMode')"
:options="accessModeOptions"
:searchable="false"
:clearable="false"
/>
</div>
</template>
<template #actions>
<div class="buttons">
<button
class="btn role-secondary"
@click="cancel"
>
{{ t('harvester.addons.vmMigration.storageDefaults.cancel') }}
</button>
<div class="right-buttons">
<button
v-if="showInherited"
class="btn role-secondary mr-10"
@click="reset"
>
{{ t('harvester.addons.vmMigration.storageDefaults.resetToProviderDefault') }}
</button>
<button
class="btn role-primary"
:disabled="!localAccessMode"
@click="apply"
>
{{ showInherited ? t('harvester.addons.vmMigration.storageDefaults.applyOverride') : t('harvester.addons.vmMigration.storageDefaults.apply') }}
</button>
</div>
</div>
</template>
</Card>
</app-modal>
</template>
<style lang="scss" scoped>
.storage-defaults-modal {
z-index: 45;
}
.storage-defaults-card {
margin: 0;
padding: 20px;
}
.description {
margin-bottom: 16px;
line-height: 20px;
}
.settings-box {
display: flex;
flex-direction: column;
gap: 20px;
padding: 16px;
border-radius: 6px;
background-color: var(--category-active);
}
.volume-mode {
max-width: 280px;
}
.access-mode {
max-width: 280px;
}
.buttons {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.right-buttons {
display: flex;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More