mirror of https://github.com/kubevela/velaux.git
Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
|
9676d17d62 | |
|
2014a92a46 | |
|
59552e913e | |
|
fc01f2d0de | |
|
c894697eab | |
|
6e233efbd7 | |
|
d7821a8bd2 | |
|
c221fa0c35 | |
|
09a2ce95f7 | |
|
e3604c1efd | |
|
50a750b9dc | |
|
1c086118c3 | |
|
1e5c64af81 | |
|
3ea7661fe7 |
|
@ -2,9 +2,14 @@ name: staticcheck
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, release-* ]
|
branches:
|
||||||
|
- main
|
||||||
|
- release-*
|
||||||
|
workflow_dispatch: {}
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main, release-* ]
|
branches:
|
||||||
|
- main
|
||||||
|
- release-*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect-noop:
|
detect-noop:
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
# CONTRIBUTING Guide
|
# CONTRIBUTING Guide
|
||||||
|
|
||||||
## About velaux
|
## About VelaUX
|
||||||
|
|
||||||
KubeVela control plane dashboard. Designed as an extensible, application-oriented delivery control panel.
|
The [KubeVela](https://github.com/oam-dev/kubevela) User Experience (UX) Dashboard, it provides out-of-box application delivery and management platform.
|
||||||
|
|
||||||
To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/oam-dev/kubevela/blob/master/CODE_OF_CONDUCT.md).
|
To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/oam-dev/kubevela/blob/master/CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
This document is a guide to help you through the process of contributing to velaux.
|
This document is a guide to help you through the process of contributing to VelaUX.
|
||||||
|
|
||||||
## Become a contributor
|
## Become a contributor
|
||||||
|
|
||||||
You can contribute to velaux in several ways. Here are some examples:
|
You can contribute to VelaUX in several ways. Here are some examples:
|
||||||
|
|
||||||
* Contribute to the velaux codebase.
|
* Contribute to the VelaUX codebase.
|
||||||
* Report and triage bugs.
|
* Report and triage bugs.
|
||||||
* Write technical documentation and blog posts, for users and contributors.
|
* Write technical documentation and blog posts, for users and contributors.
|
||||||
* Organize meetups and user groups in your local area.
|
* Organize meetups and user groups in your local area.
|
||||||
* Help others by answering questions about velaux.
|
* Help others by answering questions about VelaUX.
|
||||||
|
|
||||||
For more ways to contribute, check out the [Open Source Guides](https://opensource.guide/how-to-contribute/).
|
For more ways to contribute, check out the [Open Source Guides](https://opensource.guide/how-to-contribute/).
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ Unsure where to begin contributing to KubeVela? Start by browsing issues labeled
|
||||||
- [Good first issue](https://github.com/oam-dev/velaux/labels/good%20first%20issue) issues are generally straightforward to complete.
|
- [Good first issue](https://github.com/oam-dev/velaux/labels/good%20first%20issue) issues are generally straightforward to complete.
|
||||||
- [Help wanted](https://github.com/oam-dev/velaux/labels/help%20wanted) issues are problems we would like the community to help us with regardless of complexity.
|
- [Help wanted](https://github.com/oam-dev/velaux/labels/help%20wanted) issues are problems we would like the community to help us with regardless of complexity.
|
||||||
|
|
||||||
If you're looking to make a code change, see how to set up your environment for [frontend development](docs/contributing/frontend.md) and [backend development](docs/contributing/backend.md).
|
If you're looking to make a code change, see [how to set up your environment](docs/contributing/velaux.md).
|
||||||
|
|
||||||
When you're ready to contribute, it's time to [Create a pull request](https://github.com/oam-dev/kubevela/blob/master/contribute/create-pull-request.md).
|
When you're ready to contribute, it's time to [Create a pull request](https://github.com/oam-dev/kubevela/blob/master/contribute/create-pull-request.md).
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||

|

|
||||||
|
|
||||||
# Velaux
|
# VelaUX
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ echo "BASE_DOMAIN='http://127.0.0.1:8000'" > .env
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
- Slack: [CNCF Slack](https://slack.cncf.io/) #kubevela channel (*English*)
|
- Slack: [CNCF Slack](https://slack.cncf.io/) #kubevela channel (*English*)
|
||||||
- Gitter: [oam-dev](https://gitter.im/oam-dev/community) (*English*)
|
|
||||||
- [DingTalk Group](https://page.dingtalk.com/wow/dingtalk/act/en-home): `23310022` (*Chinese*)
|
- [DingTalk Group](https://page.dingtalk.com/wow/dingtalk/act/en-home): `23310022` (*Chinese*)
|
||||||
- Wechat Group (*Chinese*) : Broker wechat to add you into the user group.
|
- Wechat Group (*Chinese*) : Broker wechat to add you into the user group.
|
||||||
|
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
# How to Develop Backend Services
|
|
||||||
|
|
||||||
## Preparation
|
|
||||||
|
|
||||||
- Install [go](https://golang.org/dl/)
|
|
||||||
- Install [yarn](https://yarnpkg.com/)
|
|
||||||
- Install [protoc](https://grpc.io/docs/protoc-installation/) and [protoc-gen-go](https://grpc.io/docs/languages/go/quickstart/#prerequisites)
|
|
||||||
- Install [mongodb](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition)
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
In [frontend development](./frontend.md), developers will use mock backend services first.
|
|
||||||
But eventually the production rollout of velacp will have both frontend and backend services served.
|
|
||||||
|
|
||||||
Developing backend services in velacp has a well-defined architecture and systematic process. We will walk through the steps in the following.
|
|
||||||
|
|
||||||
Let's assume we are building a new service "Cluster".
|
|
||||||
|
|
||||||
The process goes as:
|
|
||||||
|
|
||||||
1. All public APIs, including records stored in database, should be defined in protobuf. In `pkg/proto/`, add a new protobuf definition:
|
|
||||||
|
|
||||||
```
|
|
||||||
touch pkg/proto/cluster.proto
|
|
||||||
```
|
|
||||||
|
|
||||||
Add definitions for cluster service API types:
|
|
||||||
|
|
||||||
```protobuf
|
|
||||||
message Cluster {
|
|
||||||
string name = 1;
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Generate go code:
|
|
||||||
|
|
||||||
```
|
|
||||||
make proto
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Add the service endpoints. First create a new service in `pkg/rest/services/`:
|
|
||||||
|
|
||||||
```
|
|
||||||
touch pkg/rest/services//cluster.go
|
|
||||||
```
|
|
||||||
|
|
||||||
Implement the services:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ClusterService struct {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClusterService(store storeadapter.ClusterStore) *ClusterService {
|
|
||||||
return &ClusterService{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ClusterService) GetClusters(c echo.Context) error {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The new service needs to be registered to in `pkg/rest/rest_server.go`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *restServer) registerServices() {
|
|
||||||
...
|
|
||||||
clusterService := services.NewClusterService(storeadapter.NewClusterStore(s.ds))
|
|
||||||
s.server.GET("/api/clusters", clusterService.GetClusters)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. There is a generic datastore interface defined in `pkg/datastore/datastore.go`. Its mongo backend is implemented in `pkg/datastore/mongodb/mongodb.go`. For each service, you will implement a more specific store adapter to handle its own types and special logic, e.g. ClusterStore.
|
|
||||||
|
|
||||||
All specific store adapter is defined in `pkg/datastore/storeadapter/`. Create one for ClusterStore:
|
|
||||||
|
|
||||||
```
|
|
||||||
touch pkg/datastore/storeadapter/clusterstore.go
|
|
||||||
```
|
|
||||||
|
|
||||||
We can see its interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ClusterStore interface {
|
|
||||||
PutCluster(cluster *model.Cluster) error
|
|
||||||
ListClusters() ([]*model.Cluster, error)
|
|
||||||
DelCluster(name string) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Its returned model, i.e. `model.Cluster`, is defined in the `pkg/proto/model/cluster.proto`.
|
|
||||||
|
|
||||||
1. Once the code is done, build it:
|
|
||||||
|
|
||||||
```
|
|
||||||
make build
|
|
||||||
```
|
|
||||||
|
|
||||||
This will include the frontend as well.
|
|
||||||
|
|
||||||
1. Start [mongodb](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#run-mongodb-community-edition).
|
|
||||||
Ensure env `MONGO_URL` is set, e.g. "127.0.0.1:27017".
|
|
||||||
|
|
||||||
1. Start velacp:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
_bin/velacp server \
|
|
||||||
--db-url=${MONGO_URL} \
|
|
||||||
--db-name=vela
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now test the APIs and UIs.
|
|
|
@ -1,85 +0,0 @@
|
||||||
# How to Develop UI Components
|
|
||||||
|
|
||||||
## Preparation
|
|
||||||
|
|
||||||
- Install [yarn](https://yarnpkg.com/)
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
The UI code is under ui/ . First, go to the folder:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd ui/
|
|
||||||
```
|
|
||||||
|
|
||||||
Developing UI components in velacp has a well-defined architecture and systematic process. We will walk through the steps in the following.
|
|
||||||
|
|
||||||
Let's assume we are building a new UI page "Cluster".
|
|
||||||
|
|
||||||
The process goes as:
|
|
||||||
|
|
||||||
1. In `config/routes.ts`, add your route and component.
|
|
||||||
|
|
||||||
1. Add a new component in src/pages/:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir src/pages/Cluster/
|
|
||||||
```
|
|
||||||
|
|
||||||
Develop and add your component code there.
|
|
||||||
|
|
||||||
1. For remote services, create interfaces in `services/kubevela`:
|
|
||||||
|
|
||||||
```
|
|
||||||
touch clusterapi.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Add your API calls there:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { request } from "umi";
|
|
||||||
|
|
||||||
export async function listClusterNames() {
|
|
||||||
return request<{ clusters: string[] }>('/api/clusternames');
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
For API types, define in `src/services/kubevela/typings.d.ts`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
declare namespace API {
|
|
||||||
export type ApplicationType = {
|
|
||||||
name: string;
|
|
||||||
desc?: string;
|
|
||||||
updatedAt?: number; // unix milliseconds
|
|
||||||
|
|
||||||
components?: ComponentType[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
They should belong to API global namespace.
|
|
||||||
|
|
||||||
1. To mock the backend APIs, create mock servers in `mock/`:
|
|
||||||
|
|
||||||
```
|
|
||||||
touch mock/cluster.tx
|
|
||||||
```
|
|
||||||
|
|
||||||
Expose the mock services:
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
"GET /api/clusternames": getClusterNames,
|
|
||||||
"GET /api/clusters": getClusters,
|
|
||||||
"POST /api/clusters": postClusters,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Run the UI to see the result:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# run `yarn` first if you haven't installed dependencies
|
|
||||||
yarn start
|
|
||||||
```
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Contribute VelaUX
|
||||||
|
|
||||||
|
Before start, please make sure you have already started the vela api server environment.
|
||||||
|
|
||||||
|
If your api server address is 'http://127.0.0.1:8000', configure the api server address:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
echo "BASE_DOMAIN='http://127.0.0.1:8000'" > .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you have installed [yarn](https://classic.yarnpkg.com/en/docs/install).
|
||||||
|
|
||||||
|
Run this project:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
yarn install
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
* Check the code style
|
||||||
|
|
||||||
|
```shell
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
* Test the code
|
||||||
|
|
||||||
|
```shell
|
||||||
|
yarn test
|
||||||
|
```
|
|
@ -1,96 +0,0 @@
|
||||||
# Environment
|
|
||||||
|
|
||||||
User scenarios:
|
|
||||||
|
|
||||||
- Users want to define base application template and add patches based on environment. For example, in dev environment users would use ephemeral disk, while in prod environment use persistent volumes.
|
|
||||||
- Users want to define environments as the shared-bases for applications. For example, applications might need shared definitions, secrets, health checks, network gateways, etc.
|
|
||||||
|
|
||||||
To support the above scenarios, we will add the following concepts:
|
|
||||||
|
|
||||||
- AppTemplate that defines the template base as well as the env-based patches:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: example-app-template
|
|
||||||
|
|
||||||
base: # the base template
|
|
||||||
components:
|
|
||||||
- name: backend
|
|
||||||
settings:
|
|
||||||
cmd:
|
|
||||||
- /bin/myservice
|
|
||||||
traits:
|
|
||||||
- name: logging
|
|
||||||
properties:
|
|
||||||
rotate: 1d
|
|
||||||
patch: # kustomize-style overlay patch to base template based on env matching
|
|
||||||
- envs:
|
|
||||||
- dev # the name of the Environment
|
|
||||||
components:
|
|
||||||
- name: backend
|
|
||||||
settings:
|
|
||||||
image: dev/myimage
|
|
||||||
traits:
|
|
||||||
- name: scaler
|
|
||||||
properties:
|
|
||||||
replicas: 1
|
|
||||||
- envs:
|
|
||||||
- prod-beijing
|
|
||||||
- prod-shenzhen
|
|
||||||
components:
|
|
||||||
- name: backend
|
|
||||||
settings:
|
|
||||||
image: production/myimage
|
|
||||||
traits:
|
|
||||||
- name: scaler
|
|
||||||
properties:
|
|
||||||
replicas: 10
|
|
||||||
```
|
|
||||||
|
|
||||||
- Environment that defines a shared-base for applications:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: prod
|
|
||||||
|
|
||||||
clusters: # deploy to the following clusters
|
|
||||||
- prod-cluster
|
|
||||||
|
|
||||||
secrets: # The secrets that will be created for applications if not existed.
|
|
||||||
- name: redis
|
|
||||||
data:
|
|
||||||
url: redis-url
|
|
||||||
password: redis-password
|
|
||||||
|
|
||||||
definitions: # The definitions that will be created for applications if not existed.
|
|
||||||
- type: Component
|
|
||||||
name: function
|
|
||||||
source:
|
|
||||||
git: catalog-url
|
|
||||||
path: pacakge/function
|
|
||||||
- type: Trait
|
|
||||||
name: logging
|
|
||||||
source:
|
|
||||||
git: catalog-url
|
|
||||||
path: package/logging
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note: we will define a definition catalog/package format: https://github.com/hongchaodeng/catalog-example
|
|
||||||
|
|
||||||
With the above concepts, the user story goes as:
|
|
||||||
|
|
||||||
- The operations/admin team sets up environments first
|
|
||||||
- The developer users prepare app templates and individual patches for each environment that will have apps to deploy to.
|
|
||||||
- Users choose a template, then choose an environment, and deploy!
|
|
||||||
- velacp will render the final Application based on the template and env
|
|
||||||
- velacp will prepare the necessary secrets, definitions in the environment for the applications
|
|
||||||
- The Application can use any of the definitions, secrets in this env.
|
|
||||||
|
|
||||||
```
|
|
||||||
Applicaton deployment workflow:
|
|
||||||
|
|
||||||
Env -> Controller -> (Secrets + Definitions)
|
|
||||||
AppTemplate + Env -> velacp -> Application -> Controller
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- AppTemplate will be implemented in velacp. Environment will be implemented as a CRD.
|
|
|
@ -1,133 +0,0 @@
|
||||||
# Environment
|
|
||||||
|
|
||||||
DEPRECATED!!!
|
|
||||||
|
|
||||||
## 1. Introduction
|
|
||||||
|
|
||||||
An environment is a shared infra-base consisting of the same clusters, packages (equiv. capabilities), etc.
|
|
||||||
to which the applications are deployed.
|
|
||||||
|
|
||||||
Below are two examples of `staging-env` and `prod-env` environments:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: staging-env
|
|
||||||
|
|
||||||
clusters:
|
|
||||||
- name: staging-cluster
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- catalog: staging-catalog
|
|
||||||
package: inmem-logging
|
|
||||||
|
|
||||||
---
|
|
||||||
name: prod-env
|
|
||||||
|
|
||||||
clusters:
|
|
||||||
- name: prod-cluster
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- catalog: prod-catalog
|
|
||||||
package: loki-logging
|
|
||||||
```
|
|
||||||
|
|
||||||
We are going to explain what an environment includes in the following.
|
|
||||||
|
|
||||||
## 2. Clusters
|
|
||||||
|
|
||||||
An environment consists of a group of clusters. For example, multiple small clusters from different data centers could form a staging environment to simplify management and serve maximum availability. While each production environment might consist of only one cluster and users need to calibrate high availability for their apps.
|
|
||||||
|
|
||||||
When deploying apps to an environment, you might specify only the environment which will deploy to all clusters. Or you might pick some clusters within an environment.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# When deploying an app you could choose env and select clusters within the env.
|
|
||||||
name: example-app
|
|
||||||
env: env-1
|
|
||||||
clusters:
|
|
||||||
- cluster-1
|
|
||||||
```
|
|
||||||
|
|
||||||
The clusters that environments reference to are abstraction over a k8s cluster. It could be an existing one which should have credentials set, or a need-to-be-created one that VelaCP can call over some cloud providers (e.g. ACK, GKE, EKS) to create one.
|
|
||||||
|
|
||||||
### Cluster Setup Workflow
|
|
||||||
|
|
||||||
Before creating any Environment, users need to create Clusters first. Here is an example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: prod-cluster
|
|
||||||
spec:
|
|
||||||
external: # This is pointing to externally managed clusters without VelaCP reconciling
|
|
||||||
kubeconfig: "..."
|
|
||||||
managed: # This would trigger cluster reconciler in VelaCP to create and manage a cluster
|
|
||||||
provider: ack
|
|
||||||
parameters:
|
|
||||||
master:
|
|
||||||
instanceType: ecs.g6.large
|
|
||||||
worker:
|
|
||||||
instanceType: ecs.g6.small
|
|
||||||
replicas: 3
|
|
||||||
cni: terway
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the cluster is setup, then create environments to reference it:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: prod-env
|
|
||||||
clusters:
|
|
||||||
- prod-cluster
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Packages
|
|
||||||
|
|
||||||
Within an environment, deployment on all clusters should be consistent. Thus, all packages should be installed the same across clusters.
|
|
||||||
To provide such guarantee, environment includes `packages` list to ensure those dependencies are declared and installed consistently.
|
|
||||||
|
|
||||||
A package is an abstraction over manifests to prepare infrastructure environments. It could be Helm Charts, Kube resources, or Terraform resources. You can use it to set up Operators (Prometheus, ELK, Istio), RBAC rules, OAM Definitions, etc.
|
|
||||||
|
|
||||||
### Package Setup Workflow
|
|
||||||
|
|
||||||
First of all, the packages should be uploaded to a remote store. We support two kinds of storage backends:
|
|
||||||
|
|
||||||
- Git.
|
|
||||||
- Object storage.
|
|
||||||
|
|
||||||
The catalog and package structure must follow predefined format. Here is an example of the structure format: [catalog-example](https://github.com/hongchaodeng/catalog-example).
|
|
||||||
|
|
||||||
Before creating the Environment, create the Catalogs first. Here is an example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: prod-catalog
|
|
||||||
spec:
|
|
||||||
git:
|
|
||||||
url: https://github.com/hongchaodeng/catalog-example
|
|
||||||
rootdir: catalog/
|
|
||||||
oss:
|
|
||||||
url: https://oss.aliyun.com/bucket_name/
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally create the environment:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: prod-env
|
|
||||||
clusters:
|
|
||||||
- prod-cluster
|
|
||||||
packages:
|
|
||||||
- catalog: prod-catalog
|
|
||||||
package: grafana
|
|
||||||
```
|
|
||||||
|
|
||||||
By doing this, the environment reconciler from VelaCP would retrieve the packages from the catalog, and then:
|
|
||||||
|
|
||||||
- if the package is not installed, install the package
|
|
||||||
- if the package has been installed and version is older, upgrade the package
|
|
||||||
|
|
||||||
Note that the pacakge could be of type of Helm Chart or Kube resources or Terraform resources, no worry about that.
|
|
||||||
VelaCP will take care of them under the hood and use corresponding tooling to do the installation:
|
|
||||||
|
|
||||||
- Kube resources: same as `kubectl apply`
|
|
||||||
- Helm chart: same as `helm install`/`helm upgrade`
|
|
||||||
- Terraform resources: same as `terraform apply`
|
|
||||||
|
|
||||||
## 4. Env-based Config Patch
|
|
||||||
|
|
||||||
You can also do per-environment configuration management based on app templates.
|
|
||||||
Check out [this doc](env_based_patch.md)
|
|
Binary file not shown.
Before Width: | Height: | Size: 227 KiB |
Binary file not shown.
Before Width: | Height: | Size: 220 KiB |
Binary file not shown.
Before Width: | Height: | Size: 91 KiB |
Binary file not shown.
Before Width: | Height: | Size: 80 KiB |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "valaux",
|
"name": "valaux",
|
||||||
"version": "1.3.1",
|
"version": "1.3.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node scripts/start.js",
|
"start": "node scripts/start.js",
|
||||||
|
|
|
@ -480,3 +480,6 @@ a {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,20 @@ export type Props = {
|
||||||
const Item: React.FC<Props> = (props) => {
|
const Item: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Row style={{ marginBottom: props.marginBottom || '16px' }}>
|
<Row style={{ marginBottom: props.marginBottom || '16px' }}>
|
||||||
<Col span={props.labelSpan ? props.labelSpan : 8}>
|
<Col
|
||||||
|
span={props.labelSpan ? props.labelSpan : 8}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyItems: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span style={{ fontSize: '14px', color: '#a6a6a6' }}>{props.label}:</span>
|
<span style={{ fontSize: '14px', color: '#a6a6a6' }}>{props.label}:</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={props.labelSpan ? 24 - props.labelSpan : 16} style={{ fontSize: '14px' }}>
|
<Col
|
||||||
|
span={props.labelSpan ? 24 - props.labelSpan : 16}
|
||||||
|
style={{ fontSize: '14px', display: 'flex' }}
|
||||||
|
>
|
||||||
{props.value}
|
{props.value}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import { routerRedux } from 'dva/router';
|
import { routerRedux } from 'dva/router';
|
||||||
import { Field, Grid, Radio, Input, Message } from '@b-design/ui';
|
import { Field, Grid, Radio, Input, Message, Icon, Switch } from '@b-design/ui';
|
||||||
import { Dialog, Card, Button, Form } from '@b-design/ui';
|
import { Dialog, Card, Button, Form } from '@b-design/ui';
|
||||||
import Translation from '../../components/Translation';
|
import Translation from '../../components/Translation';
|
||||||
import locale from '../../utils/locale';
|
import locale from '../../utils/locale';
|
||||||
|
@ -10,6 +10,7 @@ import type { SystemInfo } from '../../interface/system';
|
||||||
import type { LoginUserInfo } from '../../interface/user';
|
import type { LoginUserInfo } from '../../interface/user';
|
||||||
import { updateSystemInfo } from '../../api/config';
|
import { updateSystemInfo } from '../../api/config';
|
||||||
import { checkPermission } from '../../utils/permission';
|
import { checkPermission } from '../../utils/permission';
|
||||||
|
import Item from '../Item';
|
||||||
const { Col, Row } = Grid;
|
const { Col, Row } = Grid;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -164,10 +165,10 @@ class PlatformSetting extends React.Component<Props, State> {
|
||||||
return domain;
|
return domain;
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { onClose, platformSetting } = this.props;
|
const { onClose, platformSetting, systemInfo } = this.props;
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
visible={platformSetting}
|
visible={platformSetting}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
title={i18n.t('Platform Setting')}
|
title={i18n.t('Platform Setting')}
|
||||||
|
@ -191,7 +192,7 @@ class PlatformSetting extends React.Component<Props, State> {
|
||||||
<Form field={this.field} labelCol={{ fixedSpan: 8 }} wrapperCol={{ span: 16 }}>
|
<Form field={this.field} labelCol={{ fixedSpan: 8 }} wrapperCol={{ span: 16 }}>
|
||||||
<Card
|
<Card
|
||||||
style={{ marginBottom: '16px' }}
|
style={{ marginBottom: '16px' }}
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
contentHeight="200px"
|
contentHeight="200px"
|
||||||
title={<Translation>User authentication configuration</Translation>}
|
title={<Translation>User authentication configuration</Translation>}
|
||||||
>
|
>
|
||||||
|
@ -229,14 +230,33 @@ class PlatformSetting extends React.Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* <Card
|
<Card
|
||||||
style={{ marginBottom: '16px' }}
|
style={{ marginBottom: '16px' }}
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
contentHeight="200px"
|
contentHeight="200px"
|
||||||
title={<Translation>User experience improvement plan</Translation>}
|
title={
|
||||||
|
<span>
|
||||||
|
<Translation>User experience improvement plan</Translation>
|
||||||
|
<a target="_blank" href="https://kubevela.io/docs/reference/user-improvement-plan">
|
||||||
|
<Icon style={{ marginLeft: '4px' }} type="help" />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Form.Item />
|
<Item
|
||||||
</Card> */}
|
label={i18n.t('Contribution')}
|
||||||
|
value={
|
||||||
|
<Switch
|
||||||
|
size="medium"
|
||||||
|
{...this.field.init('enableCollection', {
|
||||||
|
rules: [{ required: true }],
|
||||||
|
initValue: systemInfo.enableCollection,
|
||||||
|
})}
|
||||||
|
checked={this.field.getValue('enableCollection')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</Form>
|
</Form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,7 @@ class StatusShow extends React.Component<Props> {
|
||||||
const { applicationStatus, onClose, loading, title } = this.props;
|
const { applicationStatus, onClose, loading, title } = this.props;
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
visible={true}
|
visible={true}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
title={title}
|
title={title}
|
||||||
|
@ -42,11 +42,11 @@ class StatusShow extends React.Component<Props> {
|
||||||
>
|
>
|
||||||
<Loading visible={loading} style={{ width: '100%' }}>
|
<Loading visible={loading} style={{ width: '100%' }}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
contentHeight="200px"
|
contentHeight="200px"
|
||||||
title={<Translation>Applied Resources</Translation>}
|
title={<Translation>Applied Resources</Translation>}
|
||||||
>
|
>
|
||||||
<Table locale={locale.Table} dataSource={applicationStatus?.appliedResources}>
|
<Table locale={locale().Table} dataSource={applicationStatus?.appliedResources}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
width="150px"
|
width="150px"
|
||||||
|
@ -76,12 +76,12 @@ class StatusShow extends React.Component<Props> {
|
||||||
</Card>
|
</Card>
|
||||||
<If condition={applicationStatus?.conditions}>
|
<If condition={applicationStatus?.conditions}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
style={{ marginTop: '8px' }}
|
style={{ marginTop: '8px' }}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
title={<Translation>Conditions</Translation>}
|
title={<Translation>Conditions</Translation>}
|
||||||
>
|
>
|
||||||
<Table locale={locale.Table} dataSource={applicationStatus?.conditions}>
|
<Table locale={locale().Table} dataSource={applicationStatus?.conditions}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
width="150px"
|
width="150px"
|
||||||
dataIndex="type"
|
dataIndex="type"
|
||||||
|
@ -118,13 +118,13 @@ class StatusShow extends React.Component<Props> {
|
||||||
</If>
|
</If>
|
||||||
<If condition={applicationStatus?.services}>
|
<If condition={applicationStatus?.services}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
title={<Translation>Component Status</Translation>}
|
title={<Translation>Component Status</Translation>}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
className="customTable"
|
className="customTable"
|
||||||
dataSource={applicationStatus?.services}
|
dataSource={applicationStatus?.services}
|
||||||
>
|
>
|
||||||
|
|
|
@ -373,7 +373,7 @@ class UISchema extends Component<Props, State> {
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
disabled={disableEdit}
|
disabled={disableEdit}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
{...init(param.jsonKey, {
|
{...init(param.jsonKey, {
|
||||||
initValue: initValue,
|
initValue: initValue,
|
||||||
rules: convertRule(param.validate),
|
rules: convertRule(param.validate),
|
||||||
|
|
|
@ -127,7 +127,7 @@ class Group extends React.Component<Props, State> {
|
||||||
this.setState({ enable: event, closed: false, checked: false });
|
this.setState({ enable: event, closed: false, checked: false });
|
||||||
this.removeJsonKeyValue();
|
this.removeJsonKeyValue();
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -93,7 +93,7 @@ class HelmChartSelect extends Component<Props, State> {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
value={value}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
/>
|
/>
|
||||||
</Loading>
|
</Loading>
|
||||||
);
|
);
|
||||||
|
|
|
@ -101,7 +101,7 @@ class HelmChartVersionSelect extends Component<Props, State> {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
value={value}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
/>
|
/>
|
||||||
</Loading>
|
</Loading>
|
||||||
);
|
);
|
||||||
|
|
|
@ -92,13 +92,24 @@ class HelmRepoSelect extends Component<Props, State> {
|
||||||
return findSecretObj?.secretName || '';
|
return findSecretObj?.secretName || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
convertHelmRepositoryOptions(data: HelmRepo[]): { label: string; value: string }[] {
|
||||||
|
return (data || []).map((item: { url: string; secretName?: string }) => {
|
||||||
|
let label = item.url;
|
||||||
|
if (item.secretName) {
|
||||||
|
label = `(${item.secretName}) ${item.url}`;
|
||||||
|
}
|
||||||
|
return { label: label, value: item.url };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { disabled, value } = this.props;
|
const { disabled, value } = this.props;
|
||||||
const { repos, loading, inputRepo } = this.state;
|
const { repos, loading, inputRepo } = this.state;
|
||||||
const dataSource = repos.map((repo) => repo.url);
|
const dataSource = repos;
|
||||||
if (inputRepo) {
|
if (inputRepo) {
|
||||||
dataSource.unshift(inputRepo);
|
dataSource.unshift({ url: inputRepo, type: 'helm' });
|
||||||
}
|
}
|
||||||
|
const transDataSource = this.convertHelmRepositoryOptions(dataSource);
|
||||||
return (
|
return (
|
||||||
<Loading visible={loading} style={{ width: '100%' }}>
|
<Loading visible={loading} style={{ width: '100%' }}>
|
||||||
<Select
|
<Select
|
||||||
|
@ -110,8 +121,8 @@ class HelmRepoSelect extends Component<Props, State> {
|
||||||
onSearch={this.onSearch}
|
onSearch={this.onSearch}
|
||||||
followTrigger={true}
|
followTrigger={true}
|
||||||
value={value}
|
value={value}
|
||||||
dataSource={dataSource}
|
dataSource={transDataSource}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
/>
|
/>
|
||||||
</Loading>
|
</Loading>
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,19 +23,30 @@ type Props = {
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
|
selectValues?: {
|
||||||
|
[propName: string]: any;
|
||||||
|
};
|
||||||
|
changeEntryKey?: {
|
||||||
|
[propName: string]: any;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
|
valueType: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Objects = {
|
||||||
|
[propName: string]: any;
|
||||||
|
};
|
||||||
function getEmptyItem() {
|
function getEmptyItem() {
|
||||||
return {
|
return {
|
||||||
key: Date.now().toString(),
|
key: Date.now().toString(),
|
||||||
label: '',
|
label: '',
|
||||||
value: '',
|
value: '',
|
||||||
|
valueType: 'string',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,20 +59,24 @@ class KV extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
this.form = new Field(this, {
|
this.form = new Field(this, {
|
||||||
onChange: (name: string, value: string) => {
|
onChange: (name: string, value: string) => {
|
||||||
this.submit();
|
|
||||||
const { keyOptions } = this.props;
|
const { keyOptions } = this.props;
|
||||||
|
const { selectValues } = this.state;
|
||||||
if (keyOptions && name.indexOf('envKey-') > -1) {
|
if (keyOptions && name.indexOf('envKey-') > -1) {
|
||||||
const itemKey = name.substring(name.indexOf('-') + 1);
|
const itemKey = name.substring(name.indexOf('-') + 1);
|
||||||
this.form.setValue('envValue-' + itemKey, keyOptions[value]);
|
this.form.setValue('envValue-' + itemKey, keyOptions[value]);
|
||||||
const { items } = this.state;
|
const { items } = this.state;
|
||||||
|
const newSelectValues: Objects = {};
|
||||||
const newItems = items.map((item) => {
|
const newItems = items.map((item) => {
|
||||||
if (item.key == itemKey) {
|
if (item.key == itemKey) {
|
||||||
item.value = keyOptions[value];
|
item.value = keyOptions[value];
|
||||||
|
newSelectValues[item.key] = value;
|
||||||
|
item.valueType = this.getValueType(keyOptions[value]);
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
this.setState({ items: newItems });
|
this.setState({ items: newItems, selectValues: { ...selectValues, ...newSelectValues } });
|
||||||
}
|
}
|
||||||
|
this.submit();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -74,15 +89,23 @@ class KV extends Component<Props, State> {
|
||||||
const { value } = this.props;
|
const { value } = this.props;
|
||||||
const { items } = this.state;
|
const { items } = this.state;
|
||||||
const newItems = [...items];
|
const newItems = [...items];
|
||||||
|
const selectValues: Objects = {};
|
||||||
if (value) {
|
if (value) {
|
||||||
for (const label in value) {
|
for (const label in value) {
|
||||||
const key = Date.now().toString() + label;
|
const key = Date.now().toString() + label;
|
||||||
newItems.push({ key: key, label: label, value: value[label] });
|
newItems.push({
|
||||||
|
key: key,
|
||||||
|
label: label,
|
||||||
|
value: value[label],
|
||||||
|
valueType: this.getValueType(value[label]),
|
||||||
|
});
|
||||||
this.form.setValue('envKey-' + key, label);
|
this.form.setValue('envKey-' + key, label);
|
||||||
this.form.setValue('envValue-' + key, value[label]);
|
this.form.setValue('envValue-' + key, value[label]);
|
||||||
|
selectValues[key] = label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({ items: newItems });
|
|
||||||
|
this.setState({ items: newItems, selectValues: selectValues });
|
||||||
};
|
};
|
||||||
|
|
||||||
addItem() {
|
addItem() {
|
||||||
|
@ -92,6 +115,7 @@ class KV extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
|
this.updateEntryKey();
|
||||||
const values: any = this.form.getValues();
|
const values: any = this.form.getValues();
|
||||||
const items: Map<string, Item> = new Map();
|
const items: Map<string, Item> = new Map();
|
||||||
Object.keys(values).map((key) => {
|
Object.keys(values).map((key) => {
|
||||||
|
@ -99,7 +123,7 @@ class KV extends Component<Props, State> {
|
||||||
const index = key.replace('envKey-', '');
|
const index = key.replace('envKey-', '');
|
||||||
let item = items.get(index);
|
let item = items.get(index);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
item = { key: '', label: '' };
|
item = { key: '', label: '', valueType: 'string' };
|
||||||
}
|
}
|
||||||
item.label = values[key];
|
item.label = values[key];
|
||||||
items.set(index, item);
|
items.set(index, item);
|
||||||
|
@ -109,7 +133,7 @@ class KV extends Component<Props, State> {
|
||||||
const index = key.replace('envValue-', '');
|
const index = key.replace('envValue-', '');
|
||||||
let item = items.get(index);
|
let item = items.get(index);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
item = { key: '', label: '' };
|
item = { key: '', label: '', valueType: 'string' };
|
||||||
}
|
}
|
||||||
item.value = values[key];
|
item.value = values[key];
|
||||||
items.set(index, item);
|
items.set(index, item);
|
||||||
|
@ -125,6 +149,17 @@ class KV extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateEntryKey = () => {
|
||||||
|
const values: Objects = this.form.getValues();
|
||||||
|
const { changeEntryKey } = this.state;
|
||||||
|
for (const key in changeEntryKey) {
|
||||||
|
if (`envKey-${key}` in values) {
|
||||||
|
this.form.remove('envKey-' + key);
|
||||||
|
this.form.setValue('envKey-' + key, changeEntryKey[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
remove(key: any) {
|
remove(key: any) {
|
||||||
const { items } = this.state;
|
const { items } = this.state;
|
||||||
items.forEach((item, i) => {
|
items.forEach((item, i) => {
|
||||||
|
@ -138,27 +173,70 @@ class KV extends Component<Props, State> {
|
||||||
this.submit();
|
this.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
onSearch = (key: string, value: string) => {
|
||||||
const { items } = this.state;
|
const { items, selectValues, changeEntryKey } = this.state;
|
||||||
const { id, additional, additionalParameter, keyOptions } = this.props;
|
const newSelectValues: Objects = {};
|
||||||
const { init } = this.form;
|
items.forEach((item) => {
|
||||||
let valueType = 'string';
|
if (item.key === key) {
|
||||||
if (additional && additionalParameter) {
|
item.value = value;
|
||||||
// TODO: current only support one parameter
|
newSelectValues[item.key] = value;
|
||||||
if (additionalParameter.uiType == 'Number') {
|
this.form.setValue('envKey-' + key, value);
|
||||||
valueType = 'number';
|
this.form.remove('envValue-' + key);
|
||||||
}
|
}
|
||||||
if (additionalParameter.uiType == 'Switch') {
|
return item;
|
||||||
valueType = 'boolean';
|
});
|
||||||
|
const newChangeEntryKey = {
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
this.setState({
|
||||||
|
selectValues: { ...selectValues, ...newSelectValues },
|
||||||
|
changeEntryKey: { ...changeEntryKey, ...newChangeEntryKey },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
findKey = (key: string) => {
|
||||||
|
const { selectValues } = this.state;
|
||||||
|
return (selectValues && selectValues[key]) || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
getValueType = (value: any) => {
|
||||||
|
const findValueType = this.matchOutSideValueType();
|
||||||
|
const valueTypeAdditionalParam = ['number', 'boolean'];
|
||||||
|
if (valueTypeAdditionalParam.includes(findValueType)) {
|
||||||
|
return findValueType;
|
||||||
|
} else {
|
||||||
|
if (value != undefined) {
|
||||||
|
return typeof value;
|
||||||
|
} else {
|
||||||
|
return 'string';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
matchOutSideValueType = () => {
|
||||||
|
const { additional, additionalParameter } = this.props;
|
||||||
|
const outSideValueType = [
|
||||||
|
{ uiType: 'Number', valueType: 'number' },
|
||||||
|
{ uiType: 'Switch', valueType: 'boolean' },
|
||||||
|
];
|
||||||
|
if (additional && additionalParameter && additionalParameter.uiType) {
|
||||||
|
const matchValueTypeObj = _.find(outSideValueType, (item) => {
|
||||||
|
return item.uiType === additionalParameter.uiType;
|
||||||
|
});
|
||||||
|
return (matchValueTypeObj && matchValueTypeObj.valueType) || 'string';
|
||||||
|
} else {
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { items } = this.state;
|
||||||
|
const { id, keyOptions } = this.props;
|
||||||
|
const { init } = this.form;
|
||||||
const dataSource = keyOptions ? Object.keys(keyOptions) : [];
|
const dataSource = keyOptions ? Object.keys(keyOptions) : [];
|
||||||
return (
|
return (
|
||||||
<div id={id}>
|
<div id={id}>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
if (item.value != undefined) {
|
|
||||||
valueType = typeof item.value;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Row key={item.key} gutter="20">
|
<Row key={item.key} gutter="20">
|
||||||
<Col span={10}>
|
<Col span={10}>
|
||||||
|
@ -171,7 +249,11 @@ class KV extends Component<Props, State> {
|
||||||
{...init(`envKey-${item.key}`)}
|
{...init(`envKey-${item.key}`)}
|
||||||
label={'Key'}
|
label={'Key'}
|
||||||
placeholder={i18n.t('Please select')}
|
placeholder={i18n.t('Please select')}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
|
onSearch={(value: string) => {
|
||||||
|
this.onSearch(item.key, value);
|
||||||
|
}}
|
||||||
|
value={this.findKey(item.key)}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<If condition={!keyOptions}>
|
<If condition={!keyOptions}>
|
||||||
|
@ -187,17 +269,17 @@ class KV extends Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={10}>
|
<Col span={10}>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<If condition={valueType == 'number' || valueType == 'string'}>
|
<If condition={item.valueType == 'number' || item.valueType == 'string'}>
|
||||||
<Input
|
<Input
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
htmlType={valueType == 'number' ? 'number' : ''}
|
htmlType={item.valueType == 'number' ? 'number' : ''}
|
||||||
{...init(`envValue-${item.key}`)}
|
{...init(`envValue-${item.key}`)}
|
||||||
label={'Value'}
|
label={'Value'}
|
||||||
className="full-width"
|
className="full-width"
|
||||||
placeholder={i18n.t('Please input or select key')}
|
placeholder={i18n.t('Please input or select key')}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<If condition={valueType == 'boolean'}>
|
<If condition={item.valueType == 'boolean'}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<span style={{ lineHeight: '36px', marginRight: '16px' }}>Value:</span>
|
<span style={{ lineHeight: '36px', marginRight: '16px' }}>Value:</span>
|
||||||
<Switch
|
<Switch
|
||||||
|
|
|
@ -27,7 +27,7 @@ class SecretKeySelect extends React.Component<Props, State> {
|
||||||
const { onChange, value, secretKeys, id, disabled } = this.props;
|
const { onChange, value, secretKeys, id, disabled } = this.props;
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -65,7 +65,7 @@ class SecretSelect extends React.Component<Props, State> {
|
||||||
const filters = secrets?.filter((secret) => secret.metadata.labels['app.oam.dev/sync-alias']);
|
const filters = secrets?.filter((secret) => secret.metadata.labels['app.oam.dev/sync-alias']);
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
value={value}
|
value={value}
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -3,10 +3,20 @@ export interface SystemInfo {
|
||||||
velaVersion: string;
|
velaVersion: string;
|
||||||
gitVersion: string;
|
gitVersion: string;
|
||||||
};
|
};
|
||||||
createTime: string;
|
installTime: string;
|
||||||
enableCollection: boolean;
|
enableCollection: boolean;
|
||||||
loginType?: boolean;
|
loginType?: boolean;
|
||||||
installID: string;
|
platformID: string;
|
||||||
|
statisticInfo: {
|
||||||
|
clusterCount?: string;
|
||||||
|
appCount?: string;
|
||||||
|
enableAddonList?: Record<string, string>;
|
||||||
|
componentDefinitionTopList?: string[];
|
||||||
|
traitDefinitionTopList?: string[];
|
||||||
|
workflowDefinitionTopList?: string[];
|
||||||
|
policyDefinitionTopList?: string[];
|
||||||
|
updateTime: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateSystemInfo {
|
export interface UpdateSystemInfo {
|
||||||
|
|
|
@ -102,7 +102,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
createApplicationEnv(params)
|
createApplicationEnv(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Bind Environment Success</Translation>);
|
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||||
this.props.onOK();
|
this.props.onOK();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -115,7 +115,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
updateApplicationEnv(params)
|
updateApplicationEnv(params)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Bind Environment Success</Translation>);
|
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||||
this.props.onOK();
|
this.props.onOK();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -178,7 +178,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Dialog
|
<Dialog
|
||||||
visible={true}
|
visible={true}
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
style={{ width: '600px' }}
|
style={{ width: '600px' }}
|
||||||
isFullScreen={true}
|
isFullScreen={true}
|
||||||
|
@ -211,7 +211,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
name="name"
|
name="name"
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
disabled={isEdit ? true : false}
|
disabled={isEdit ? true : false}
|
||||||
dataSource={envOption}
|
dataSource={envOption}
|
||||||
maxLength={32}
|
maxLength={32}
|
||||||
|
|
|
@ -54,7 +54,7 @@ class DeployConfigDialog extends Component<Props, State> {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Dialog
|
<Dialog
|
||||||
visible={true}
|
visible={true}
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'commonDialog deployConfig'}
|
className={'commonDialog deployConfig'}
|
||||||
style={{ width: '600px' }}
|
style={{ width: '600px' }}
|
||||||
isFullScreen={true}
|
isFullScreen={true}
|
||||||
|
|
|
@ -86,7 +86,7 @@ class ApplicationHeader extends Component<Props, State> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDeploy(workflowName, true);
|
this.onDeploy(workflowName, true);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
|
@ -190,7 +190,7 @@ class ApplicationHeader extends Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
<Col xl={12} m={12} s={24} className="padding16">
|
<Col xl={12} m={12} s={24} className="padding16">
|
||||||
<Card locale={locale.Card}>
|
<Card locale={locale().Card}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={6} style={{ padding: '22px 0' }}>
|
<Col span={6} style={{ padding: '22px 0' }}>
|
||||||
<NumItem
|
<NumItem
|
||||||
|
@ -221,7 +221,7 @@ class ApplicationHeader extends Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={12} m={12} s={24} className="padding16">
|
<Col xl={12} m={12} s={24} className="padding16">
|
||||||
<If condition={!records || (Array.isArray(records) && records.length === 0)}>
|
<If condition={!records || (Array.isArray(records) && records.length === 0)}>
|
||||||
<Card locale={locale.Card}>
|
<Card locale={locale().Card}>
|
||||||
<Empty
|
<Empty
|
||||||
message={<Translation>There is no running workflow</Translation>}
|
message={<Translation>There is no running workflow</Translation>}
|
||||||
iconWidth={'30px'}
|
iconWidth={'30px'}
|
||||||
|
|
|
@ -74,7 +74,7 @@ class Menu extends Component<Props, any> {
|
||||||
}
|
}
|
||||||
const activeKey = currentPath.substring(currentPath.lastIndexOf('/') + 1);
|
const activeKey = currentPath.substring(currentPath.lastIndexOf('/') + 1);
|
||||||
return (
|
return (
|
||||||
<Card locale={locale.Card} contentHeight="100px" className="app-menu">
|
<Card locale={locale().Card} contentHeight="100px" className="app-menu">
|
||||||
{activeItems.map((item) => {
|
{activeItems.map((item) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { Grid, Form, Input, Field, Button, Message, Icon, Dialog } from '@b-design/ui';
|
||||||
|
import { updateUser } from '../../../../api/users';
|
||||||
|
import type { LoginUserInfo } from '../../../../interface/user';
|
||||||
|
import { checkUserPassword, checkUserEmail } from '../../../../utils/common';
|
||||||
|
import Translation from '../../../../components/Translation';
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
userInfo?: LoginUserInfo;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
isLoading: boolean;
|
||||||
|
isLookPassword: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EditPlatFormUserDialog extends Component<Props, State> {
|
||||||
|
field: Field;
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.field = new Field(this);
|
||||||
|
this.state = {
|
||||||
|
isLoading: false,
|
||||||
|
isLookPassword: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateUser = async () => {
|
||||||
|
this.field.validate((error: any, values: any) => {
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { userInfo } = this.props;
|
||||||
|
const { email, password } = values;
|
||||||
|
const params = {
|
||||||
|
name: userInfo?.name || '',
|
||||||
|
alias: userInfo?.alias || '',
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
this.setState({
|
||||||
|
isLoading: true,
|
||||||
|
});
|
||||||
|
updateUser(params)
|
||||||
|
.then((res) => {
|
||||||
|
if (res) {
|
||||||
|
Message.success(<Translation>User updated successfully</Translation>);
|
||||||
|
this.props.onClose();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
showTitle() {
|
||||||
|
return i18n.t('Reset the password and email for the administrator account');
|
||||||
|
}
|
||||||
|
|
||||||
|
showClickButtons = () => {
|
||||||
|
const { isLoading } = this.state;
|
||||||
|
return [
|
||||||
|
<Button type="primary" onClick={this.onUpdateUser} loading={isLoading}>
|
||||||
|
{i18n.t('Update')}
|
||||||
|
</Button>,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClickLook = () => {
|
||||||
|
this.setState({
|
||||||
|
isLookPassword: !this.state.isLookPassword,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const init = this.field.init;
|
||||||
|
const { Row, Col } = Grid;
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
fixedSpan: 6,
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
span: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Dialog
|
||||||
|
visible={true}
|
||||||
|
title={this.showTitle()}
|
||||||
|
style={{ width: '600px' }}
|
||||||
|
onOk={this.onUpdateUser}
|
||||||
|
footerActions={['ok']}
|
||||||
|
>
|
||||||
|
<Form {...formItemLayout} field={this.field}>
|
||||||
|
<Row>
|
||||||
|
<Col span={24} style={{ padding: '0 8px' }}>
|
||||||
|
<FormItem label={<Translation>Password</Translation>} required>
|
||||||
|
<Input
|
||||||
|
name="password"
|
||||||
|
htmlType={this.state.isLookPassword ? 'passwordInput' : 'password'}
|
||||||
|
addonTextAfter={
|
||||||
|
<Icon
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
type="eye-fill"
|
||||||
|
onClick={this.handleClickLook}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={i18n.t('Please input the password').toString()}
|
||||||
|
{...init('password', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: checkUserPassword,
|
||||||
|
message: (
|
||||||
|
<Translation>
|
||||||
|
Password should be 8-16 bits and contain at least one number and one
|
||||||
|
letter
|
||||||
|
</Translation>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col span={24} style={{ padding: '0 8px' }}>
|
||||||
|
<FormItem label={<Translation>Email</Translation>} required>
|
||||||
|
<Input
|
||||||
|
name="email"
|
||||||
|
placeholder={i18n.t('Please input a email').toString()}
|
||||||
|
{...init('email', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: checkUserEmail,
|
||||||
|
message: <Translation>Please input a valid email</Translation>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
</Dialog>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPlatFormUserDialog;
|
|
@ -6,13 +6,16 @@ import SwitchLanguage from '../../components/SwitchButton/index';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import logo from '../../assets/kubevela-logo-white.png';
|
import logo from '../../assets/kubevela-logo-white.png';
|
||||||
|
import axios from 'axios';
|
||||||
import type { SystemInfo } from '../../interface/system';
|
import type { SystemInfo } from '../../interface/system';
|
||||||
import type { LoginUserInfo } from '../../interface/user';
|
import type { LoginUserInfo } from '../../interface/user';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import Translation from '../../components/Translation';
|
import Translation from '../../components/Translation';
|
||||||
import Permission from '../../components/Permission';
|
import Permission from '../../components/Permission';
|
||||||
import PlatformSetting from '../../components/PlatformSetting';
|
import PlatformSetting from '../../components/PlatformSetting';
|
||||||
|
import EditPlatFormUserDialog from './components/EditPlatFormUserDialog';
|
||||||
|
import { getBrowserNameAndVersion, isAdminUserCheck } from '../../utils/utils';
|
||||||
|
import { getData, setData } from '../../utils/cache';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dispatch: ({}) => {};
|
dispatch: ({}) => {};
|
||||||
|
@ -22,16 +25,22 @@ type Props = {
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
platformSetting: boolean;
|
platformSetting: boolean;
|
||||||
|
isEditAdminUser: boolean;
|
||||||
|
userInfo?: LoginUserInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TelemetryDataCollectionKey = 'telemetryDataCollection';
|
||||||
|
const TelemetryDataCollectionServer = 'https://telemetry.kubevela.net/collecting';
|
||||||
@connect((store: any) => {
|
@connect((store: any) => {
|
||||||
return { ...store.user };
|
return { ...store.user };
|
||||||
})
|
})
|
||||||
class TopBar extends Component<Props, State> {
|
class TopBar extends Component<Props, State> {
|
||||||
loadCount: number;
|
loadCount: number;
|
||||||
constructor(props: any) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
platformSetting: false,
|
platformSetting: false,
|
||||||
|
isEditAdminUser: false,
|
||||||
};
|
};
|
||||||
this.loadCount = 0;
|
this.loadCount = 0;
|
||||||
}
|
}
|
||||||
|
@ -44,12 +53,65 @@ class TopBar extends Component<Props, State> {
|
||||||
loadSystemInfo = () => {
|
loadSystemInfo = () => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'user/getSystemInfo',
|
type: 'user/getSystemInfo',
|
||||||
|
callback: () => {
|
||||||
|
this.telemetryDataCollection();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
telemetryDataCollection = async () => {
|
||||||
|
const { systemInfo } = this.props;
|
||||||
|
if (!getData(TelemetryDataCollectionKey) && systemInfo?.enableCollection) {
|
||||||
|
try {
|
||||||
|
axios
|
||||||
|
.post(TelemetryDataCollectionServer, this.buildTelemetryData())
|
||||||
|
.catch()
|
||||||
|
.then(() => {
|
||||||
|
this.setCache();
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
buildTelemetryData = () => {
|
||||||
|
const { systemInfo } = this.props;
|
||||||
|
return {
|
||||||
|
platformID: systemInfo?.platformID,
|
||||||
|
installTime: systemInfo?.installTime,
|
||||||
|
version:
|
||||||
|
(systemInfo?.systemVersion?.velaVersion || '') +
|
||||||
|
+'/' +
|
||||||
|
(systemInfo?.systemVersion?.gitVersion || ''),
|
||||||
|
clusterCount: systemInfo?.statisticInfo.clusterCount || '',
|
||||||
|
appCount: systemInfo?.statisticInfo.appCount || '',
|
||||||
|
enableAddonList: systemInfo?.statisticInfo.enableAddonList || {},
|
||||||
|
componentDefinitionTopList: systemInfo?.statisticInfo.componentDefinitionTopList,
|
||||||
|
traitDefinitionTopList: systemInfo?.statisticInfo.traitDefinitionTopList,
|
||||||
|
workflowStepDefinitionTopList: systemInfo?.statisticInfo.workflowDefinitionTopList,
|
||||||
|
policyDefinitionTopList: systemInfo?.statisticInfo.policyDefinitionTopList,
|
||||||
|
browserInfo: {
|
||||||
|
language: navigator.language,
|
||||||
|
nameAndVersion: getBrowserNameAndVersion(),
|
||||||
|
screenWidth: window.screen.width,
|
||||||
|
screenHeight: window.screen.height,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
setCache = () => {
|
||||||
|
const now = new Date();
|
||||||
|
now.setHours(now.getHours() + 24);
|
||||||
|
setData(TelemetryDataCollectionKey, 'true', now);
|
||||||
|
};
|
||||||
|
|
||||||
loadUserInfo = () => {
|
loadUserInfo = () => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'user/getLoginUserInfo',
|
type: 'user/getLoginUserInfo',
|
||||||
|
callback: (res: LoginUserInfo) => {
|
||||||
|
this.setState({ userInfo: res }, () => {
|
||||||
|
this.isEditPlatForm();
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,10 +129,24 @@ class TopBar extends Component<Props, State> {
|
||||||
this.setState({ platformSetting: true });
|
this.setState({ platformSetting: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isEditPlatForm = () => {
|
||||||
|
const { userInfo } = this.state;
|
||||||
|
const isAdminUser = isAdminUserCheck(userInfo);
|
||||||
|
if (isAdminUser && userInfo && !userInfo.email) {
|
||||||
|
this.setState({
|
||||||
|
isEditAdminUser: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onCloseEditAdminUser = () => {
|
||||||
|
this.setState({ isEditAdminUser: false });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
const { userInfo, systemInfo, dispatch } = this.props;
|
const { systemInfo, dispatch } = this.props;
|
||||||
const { platformSetting } = this.state;
|
const { platformSetting, isEditAdminUser, userInfo } = this.state;
|
||||||
return (
|
return (
|
||||||
<div className="layout-topbar" id="layout-topbar">
|
<div className="layout-topbar" id="layout-topbar">
|
||||||
<Row className="nav-wrapper">
|
<Row className="nav-wrapper">
|
||||||
|
@ -197,6 +273,9 @@ class TopBar extends Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</If>
|
</If>
|
||||||
|
<If condition={isEditAdminUser}>
|
||||||
|
<EditPlatFormUserDialog userInfo={userInfo} onClose={this.onCloseEditAdminUser} />
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@
|
||||||
"Auto-refresh": "自动刷新",
|
"Auto-refresh": "自动刷新",
|
||||||
"Baseline Config": "基础配置",
|
"Baseline Config": "基础配置",
|
||||||
"Bind Environment": "绑定环境",
|
"Bind Environment": "绑定环境",
|
||||||
"Bind Environment Success": "绑定环境成功",
|
"Environment bound successfully": "绑定环境成功",
|
||||||
"Bucket": "Bucket",
|
"Bucket": "Bucket",
|
||||||
"CPU": "CPU",
|
"CPU": "CPU",
|
||||||
"Cancel": "取消",
|
"Cancel": "取消",
|
||||||
|
@ -418,7 +418,8 @@
|
||||||
"Container": "容器",
|
"Container": "容器",
|
||||||
"Logs": "日志",
|
"Logs": "日志",
|
||||||
"Select Pod": "查询实例",
|
"Select Pod": "查询实例",
|
||||||
"Select Container": "查询容器",
|
"Select Container": "选择容器",
|
||||||
|
"Select Component": "选择组件",
|
||||||
"Component Config": "组件配置",
|
"Component Config": "组件配置",
|
||||||
"New Component": "新增组件",
|
"New Component": "新增组件",
|
||||||
"Components": "组件",
|
"Components": "组件",
|
||||||
|
@ -515,5 +516,18 @@
|
||||||
"The email address of administrator is empty": "管理员的电子邮件地址为空",
|
"The email address of administrator is empty": "管理员的电子邮件地址为空",
|
||||||
"Please set a email address for the administrator, it must same as the SSO account.": "请为管理员设置电子邮件地址, 它必须与SSO帐户相同",
|
"Please set a email address for the administrator, it must same as the SSO account.": "请为管理员设置电子邮件地址, 它必须与SSO帐户相同",
|
||||||
"No dex connector configurations": "没有dex连接器配置",
|
"No dex connector configurations": "没有dex连接器配置",
|
||||||
"Before enabling SSO, you must add at least one dex connector configuration.": "在启用SSO之前, 必须至少添加一个dex连接器配置."
|
"Before enabling SSO, you must add at least one dex connector configuration.": "在启用SSO之前, 必须至少添加一个dex连接器配置.",
|
||||||
}
|
"Are you sure you want to reclaim the current environment?": "您确定要回收当前环境吗?",
|
||||||
|
"Are you sure you want to delete the current environment binding?": "是否确实要删除当前环境绑定?",
|
||||||
|
"Recycle application environment success": "回收应用程序环境成功",
|
||||||
|
"Environment binding deleted successfully": "成功移除环境绑定",
|
||||||
|
"Retry": "重试",
|
||||||
|
"Password should be 8-16 bits and contain at least one number and one letter": "密码应为8-16位, 并至少包含一个数字和一个字母",
|
||||||
|
"Please input a valid email": "请输入有效的电子邮件地址",
|
||||||
|
"Please input a email": "请输入电子邮箱地址",
|
||||||
|
"User updated successfully": "用户更新成功",
|
||||||
|
"Reset the password and email for the administrator account": "重置管理员帐户的密码和电子邮件地址",
|
||||||
|
"Please input the alias": "请输入别名",
|
||||||
|
"User experience improvement plan": "用户体验提升计划",
|
||||||
|
"Contribution": "贡献"
|
||||||
|
}
|
|
@ -26,18 +26,24 @@ const user: any = {
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
*getLoginUserInfo(
|
*getLoginUserInfo(
|
||||||
action: { payload: { projectName: string } },
|
action: { payload: Record<string, never>; callback: (data: LoginUserInfo) => void },
|
||||||
{ call, put }: { call: any; put: any },
|
{ call, put }: { call: any; put: any },
|
||||||
) {
|
) {
|
||||||
const result: LoginUserInfo = yield call(getLoginUserInfo, action.payload);
|
const result: LoginUserInfo = yield call(getLoginUserInfo, action.payload);
|
||||||
yield put({ type: 'updateUserInfo', payload: result || {} });
|
yield put({ type: 'updateUserInfo', payload: result || {} });
|
||||||
|
if (action.callback && result) {
|
||||||
|
action.callback(result);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
*getSystemInfo(
|
*getSystemInfo(
|
||||||
action: { payload: { projectName: string } },
|
action: { payload: Record<string, never>; callback: (data: SystemInfo) => void },
|
||||||
{ call, put }: { call: any; put: any },
|
{ call, put }: { call: any; put: any },
|
||||||
) {
|
) {
|
||||||
const result: SystemInfo = yield call(loadSystemInfo, action.payload);
|
const result: SystemInfo = yield call(loadSystemInfo, action.payload);
|
||||||
yield put({ type: 'updateSystemInfo', payload: result || {} });
|
yield put({ type: 'updateSystemInfo', payload: result || {} });
|
||||||
|
if (result && action.callback) {
|
||||||
|
action.callback(result);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,7 +65,7 @@ class CardContent extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Col xl={6} m={8} s={12} xxs={24} className={`card-content-wraper`} key={name}>
|
<Col xl={6} m={8} s={12} xxs={24} className={`card-content-wraper`} key={name}>
|
||||||
<Card locale={locale.Card} contentHeight="auto">
|
<Card locale={locale().Card} contentHeight="auto">
|
||||||
<a onClick={() => clickAddon(name)}>
|
<a onClick={() => clickAddon(name)}>
|
||||||
<div className="cluster-card-top flexcenter">
|
<div className="cluster-card-top flexcenter">
|
||||||
<If condition={icon && icon != 'none'}>
|
<If condition={icon && icon != 'none'}>
|
||||||
|
|
|
@ -164,7 +164,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
content:
|
content:
|
||||||
'Please make sure that the Addon is no longer in use and the related application has been recycled.',
|
'Please make sure that the Addon is no longer in use and the related application has been recycled.',
|
||||||
onOk: this.disableAddon,
|
onOk: this.disableAddon,
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -450,7 +450,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
</If>
|
</If>
|
||||||
<If condition={addonDetailInfo?.dependencies}>
|
<If condition={addonDetailInfo?.dependencies}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
title={<Translation>Dependencies</Translation>}
|
title={<Translation>Dependencies</Translation>}
|
||||||
>
|
>
|
||||||
|
@ -479,7 +479,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
<If condition={addonDetailInfo?.definitions}>
|
<If condition={addonDetailInfo?.definitions}>
|
||||||
<Card
|
<Card
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
title={<Translation>Definitions</Translation>}
|
title={<Translation>Definitions</Translation>}
|
||||||
style={{ marginTop: '16px' }}
|
style={{ marginTop: '16px' }}
|
||||||
>
|
>
|
||||||
|
@ -488,7 +488,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
Enable the addon to obtain the following extension capabilities
|
Enable the addon to obtain the following extension capabilities
|
||||||
</Translation>
|
</Translation>
|
||||||
</Message>
|
</Message>
|
||||||
<Table locale={locale.Table} dataSource={addonDetailInfo?.definitions}>
|
<Table locale={locale().Table} dataSource={addonDetailInfo?.definitions}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
align="left"
|
align="left"
|
||||||
|
@ -514,7 +514,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
</If>
|
</If>
|
||||||
<Card
|
<Card
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
title={<Translation>Readme</Translation>}
|
title={<Translation>Readme</Translation>}
|
||||||
style={{ marginTop: '16px' }}
|
style={{ marginTop: '16px' }}
|
||||||
>
|
>
|
||||||
|
|
|
@ -159,7 +159,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDeleteRegistry(name);
|
this.onDeleteRegistry(name);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -211,7 +211,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className="commonDialog"
|
className="commonDialog"
|
||||||
title={<Translation>Registry Management</Translation>}
|
title={<Translation>Registry Management</Translation>}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
@ -247,7 +247,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Table locale={locale.Table} dataSource={registryDataSource}>
|
<Table locale={locale().Table} dataSource={registryDataSource}>
|
||||||
<Table.Column width="150px" title={<Translation>Name</Translation>} dataIndex="name" />
|
<Table.Column width="150px" title={<Translation>Name</Translation>} dataIndex="name" />
|
||||||
<Table.Column width="80px" title={<Translation>Type</Translation>} dataIndex="type" />
|
<Table.Column width="80px" title={<Translation>Type</Translation>} dataIndex="type" />
|
||||||
<Table.Column title={<Translation>URL</Translation>} dataIndex="url" />
|
<Table.Column title={<Translation>URL</Translation>} dataIndex="url" />
|
||||||
|
@ -296,7 +296,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
||||||
help={<Translation>The addon registry type</Translation>}
|
help={<Translation>The addon registry type</Translation>}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
{...init('type', {
|
{...init('type', {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,7 +59,7 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
||||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={this.handleChangRegistry}
|
onChange={this.handleChangRegistry}
|
||||||
|
|
|
@ -153,8 +153,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success({
|
Message.success({
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
title: i18n.t('Success'),
|
content: i18n.t('Component created successfully'),
|
||||||
content: i18n.t('Create component success.'),
|
|
||||||
});
|
});
|
||||||
this.props.onComponentOK();
|
this.props.onComponentOK();
|
||||||
}
|
}
|
||||||
|
@ -373,7 +372,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
showSearch
|
showSearch
|
||||||
disabled={isEditComponent ? true : false}
|
disabled={isEditComponent ? true : false}
|
||||||
className="select"
|
className="select"
|
||||||
|
|
|
@ -31,7 +31,7 @@ class ComponentsList extends Component<Props> {
|
||||||
this.props.onDeleteComponent(name || '');
|
this.props.onDeleteComponent(name || '');
|
||||||
},
|
},
|
||||||
onClose: () => {},
|
onClose: () => {},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class ComponentsList extends Component<Props> {
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
{(components || []).map((item: ApplicationComponentBase) => (
|
{(components || []).map((item: ApplicationComponentBase) => (
|
||||||
<Col xl={8} m={12} s={24} key={item.name} className="padding16">
|
<Col xl={8} m={12} s={24} key={item.name} className="padding16">
|
||||||
<Card locale={locale.Card} contentHeight="auto">
|
<Card locale={locale().Card} contentHeight="auto">
|
||||||
<div className="components-list-nav">
|
<div className="components-list-nav">
|
||||||
<Permission
|
<Permission
|
||||||
request={{
|
request={{
|
||||||
|
|
|
@ -34,7 +34,7 @@ class TraitsList extends Component<Props> {
|
||||||
this.props.onDeleteTrait(traitType || '');
|
this.props.onDeleteTrait(traitType || '');
|
||||||
},
|
},
|
||||||
onClose: () => {},
|
onClose: () => {},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class TraitsList extends Component<Props> {
|
||||||
const { changeTraitStats } = this.props;
|
const { changeTraitStats } = this.props;
|
||||||
return (
|
return (
|
||||||
<Col xl={12} m={12} s={24} className="padding16 card-trait-wrapper">
|
<Col xl={12} m={12} s={24} className="padding16 card-trait-wrapper">
|
||||||
<Card locale={locale.Card}>
|
<Card locale={locale().Card}>
|
||||||
<div className="traits-list-nav">
|
<div className="traits-list-nav">
|
||||||
<div
|
<div
|
||||||
className="traits-list-title"
|
className="traits-list-title"
|
||||||
|
@ -75,7 +75,7 @@ class TraitsList extends Component<Props> {
|
||||||
const { Col } = Grid;
|
const { Col } = Grid;
|
||||||
return (
|
return (
|
||||||
<Col xl={12} m={12} s={24} className="padding16 card-add-wrapper">
|
<Col xl={12} m={12} s={24} className="padding16 card-add-wrapper">
|
||||||
<Card locale={locale.Card}>
|
<Card locale={locale().Card}>
|
||||||
<div className="traits-add-operation">
|
<div className="traits-add-operation">
|
||||||
<Icon
|
<Icon
|
||||||
type="plus-circle"
|
type="plus-circle"
|
||||||
|
|
|
@ -245,7 +245,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
<FormItem label={<Translation>Type</Translation>} required>
|
<FormItem label={<Translation>Type</Translation>} required>
|
||||||
<Select
|
<Select
|
||||||
name="type"
|
name="type"
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
dataSource={[{ label: 'On Webhook Event', value: 'webhook' }]}
|
dataSource={[{ label: 'On Webhook Event', value: 'webhook' }]}
|
||||||
{...init('type', {
|
{...init('type', {
|
||||||
initValue: 'webhook',
|
initValue: 'webhook',
|
||||||
|
@ -266,7 +266,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
<FormItem label={<Translation>PayloadType</Translation>} required>
|
<FormItem label={<Translation>PayloadType</Translation>} required>
|
||||||
<Select
|
<Select
|
||||||
name="payloadType"
|
name="payloadType"
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
dataSource={payloadTypeOption}
|
dataSource={payloadTypeOption}
|
||||||
{...init('payloadType', {
|
{...init('payloadType', {
|
||||||
initValue: 'custom',
|
initValue: 'custom',
|
||||||
|
@ -288,7 +288,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
<FormItem label={<Translation>Execution workflow</Translation>} required>
|
<FormItem label={<Translation>Execution workflow</Translation>} required>
|
||||||
<Select
|
<Select
|
||||||
name="workflowName"
|
name="workflowName"
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
dataSource={workflowOption}
|
dataSource={workflowOption}
|
||||||
{...init('workflowName', {
|
{...init('workflowName', {
|
||||||
rules: [
|
rules: [
|
||||||
|
|
|
@ -52,7 +52,7 @@ class TriggerList extends Component<Props, State> {
|
||||||
this.props.onDeleteTrigger(token || '');
|
this.props.onDeleteTrigger(token || '');
|
||||||
},
|
},
|
||||||
onClose: () => {},
|
onClose: () => {},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class TriggerList extends Component<Props, State> {
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
{(triggers || []).map((item: Trigger) => (
|
{(triggers || []).map((item: Trigger) => (
|
||||||
<Col xl={8} m={12} s={24} key={item.type} className="padding16">
|
<Col xl={8} m={12} s={24} key={item.type} className="padding16">
|
||||||
<Card free={true} style={{ padding: '16px' }} locale={locale.Card}>
|
<Card free={true} style={{ padding: '16px' }} locale={locale().Card}>
|
||||||
<div className="trigger-list-nav">
|
<div className="trigger-list-nav">
|
||||||
<div className="trigger-list-title">
|
<div className="trigger-list-title">
|
||||||
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
||||||
|
@ -198,7 +198,7 @@ class TriggerList extends Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
<If condition={showTrigger}>
|
<If condition={showTrigger}>
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className="commonDialog"
|
className="commonDialog"
|
||||||
visible={true}
|
visible={true}
|
||||||
onClose={this.closeWebhook}
|
onClose={this.closeWebhook}
|
||||||
|
|
|
@ -393,7 +393,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} className="padding16">
|
<Col span={24} className="padding16">
|
||||||
<Card locale={locale.Card} contentHeight="auto">
|
<Card locale={locale().Card} contentHeight="auto">
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
<Col m={12} xs={24}>
|
<Col m={12} xs={24}>
|
||||||
<Item
|
<Item
|
||||||
|
|
|
@ -116,7 +116,7 @@ class ContainerLog extends Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className="commonDialog logDialog"
|
className="commonDialog logDialog"
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
visible={true}
|
visible={true}
|
||||||
footerActions={[]}
|
footerActions={[]}
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
|
|
|
@ -92,40 +92,40 @@ class Header extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
recycleEnv = async () => {
|
recycleEnv = async () => {
|
||||||
Dialog.confirm({
|
Dialog.confirm({
|
||||||
content: 'Are you sure you want to reclaim the current environment?',
|
content: i18n.t('Are you sure you want to reclaim the current environment?'),
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
const { applicationDetail, envName, refresh } = this.props;
|
const { applicationDetail, envName, refresh } = this.props;
|
||||||
if (applicationDetail) {
|
if (applicationDetail) {
|
||||||
recycleApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
recycleApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
||||||
(re) => {
|
(re) => {
|
||||||
if (re) {
|
if (re) {
|
||||||
Message.success('recycle applicationn environment success');
|
Message.success(i18n.t('Recycle application environment success'));
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
deleteEnv = async () => {
|
deleteEnv = async () => {
|
||||||
Dialog.confirm({
|
Dialog.confirm({
|
||||||
content: 'Are you sure you want to delete the current environment binding?',
|
content: i18n.t('Are you sure you want to delete the current environment binding?'),
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
const { applicationDetail, envName, updateEnvs } = this.props;
|
const { applicationDetail, envName, updateEnvs } = this.props;
|
||||||
if (applicationDetail) {
|
if (applicationDetail) {
|
||||||
deleteApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
deleteApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
||||||
(re) => {
|
(re) => {
|
||||||
if (re) {
|
if (re) {
|
||||||
Message.success('delete applicationn environment binding success');
|
Message.success(i18n.t('Environment binding deleted successfully'));
|
||||||
updateEnvs();
|
updateEnvs();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ class Header extends Component<Props, State> {
|
||||||
<Row className="border-radius-8">
|
<Row className="border-radius-8">
|
||||||
<Col span="4" style={{ marginBottom: '16px' }}>
|
<Col span="4" style={{ marginBottom: '16px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
onChange={this.handleTargetChange}
|
onChange={this.handleTargetChange}
|
||||||
dataSource={targetOptions}
|
dataSource={targetOptions}
|
||||||
|
@ -184,7 +184,7 @@ class Header extends Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col span="4" style={{ marginBottom: '16px', paddingLeft: '16px' }}>
|
<Col span="4" style={{ marginBottom: '16px', paddingLeft: '16px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
onChange={this.handleComponentChange}
|
onChange={this.handleComponentChange}
|
||||||
dataSource={componentOptions}
|
dataSource={componentOptions}
|
||||||
|
|
|
@ -288,7 +288,7 @@ class PodDetail extends React.Component<Props, State> {
|
||||||
hasBorder={false}
|
hasBorder={false}
|
||||||
primaryKey="name"
|
primaryKey="name"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
>
|
>
|
||||||
{containerColumns &&
|
{containerColumns &&
|
||||||
containerColumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
containerColumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
|
@ -300,7 +300,7 @@ class PodDetail extends React.Component<Props, State> {
|
||||||
hasBorder={false}
|
hasBorder={false}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
primaryKey="time"
|
primaryKey="time"
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
>
|
>
|
||||||
{eventCloumns &&
|
{eventCloumns &&
|
||||||
eventCloumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
eventCloumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
|
|
|
@ -109,12 +109,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
||||||
if (re) {
|
if (re) {
|
||||||
const status: ApplicationStatus = re.status;
|
const status: ApplicationStatus = re.status;
|
||||||
if (status && status.appliedResources) {
|
if (status && status.appliedResources) {
|
||||||
const services = status.appliedResources.filter(
|
this.loadApplicationEndpoints();
|
||||||
(resource) => resource.kind == 'Service',
|
|
||||||
);
|
|
||||||
if (services) {
|
|
||||||
this.loadApplicationEndpoints();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -220,19 +215,13 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
||||||
|
|
||||||
loadAppInstances = async () => {
|
loadAppInstances = async () => {
|
||||||
this.setState({ podList: [] });
|
this.setState({ podList: [] });
|
||||||
const { applicationDetail, envbinding, applicationStatus } = this.props;
|
const { applicationDetail, envbinding } = this.props;
|
||||||
const {
|
const {
|
||||||
params: { appName, envName },
|
params: { appName, envName },
|
||||||
} = this.props.match;
|
} = this.props.match;
|
||||||
const { target, componentName } = this.state;
|
const { target, componentName } = this.state;
|
||||||
const envs = envbinding.filter((item) => item.name == envName);
|
const envs = envbinding.filter((item) => item.name == envName);
|
||||||
if (
|
if (applicationDetail && applicationDetail.name && envs.length > 0) {
|
||||||
applicationDetail &&
|
|
||||||
applicationDetail.name &&
|
|
||||||
envs.length > 0 &&
|
|
||||||
applicationStatus &&
|
|
||||||
applicationStatus.services?.length
|
|
||||||
) {
|
|
||||||
if (applicationDetail.applicationType == 'common') {
|
if (applicationDetail.applicationType == 'common') {
|
||||||
const param = {
|
const param = {
|
||||||
appName: envs[0].appDeployName || appName,
|
appName: envs[0].appDeployName || appName,
|
||||||
|
@ -452,7 +441,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
this.setState({ deployLoading: false });
|
this.setState({ deployLoading: false });
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
|
@ -541,7 +530,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
||||||
<If condition={applicationStatus}>
|
<If condition={applicationStatus}>
|
||||||
<If condition={applicationDetail?.applicationType == 'common'}>
|
<If condition={applicationDetail?.applicationType == 'common'}>
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
className="podlist-table-wraper"
|
className="podlist-table-wraper"
|
||||||
size="medium"
|
size="medium"
|
||||||
primaryKey={'primaryKey'}
|
primaryKey={'primaryKey'}
|
||||||
|
@ -560,7 +549,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
||||||
<If condition={applicationDetail?.applicationType == 'cloud'}>
|
<If condition={applicationDetail?.applicationType == 'cloud'}>
|
||||||
<Table
|
<Table
|
||||||
size="medium"
|
size="medium"
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
className="customTable"
|
className="customTable"
|
||||||
dataSource={cloudInstance}
|
dataSource={cloudInstance}
|
||||||
primaryKey={'instanceName'}
|
primaryKey={'instanceName'}
|
||||||
|
|
|
@ -390,7 +390,7 @@ class AppDialog extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
showSearch
|
showSearch
|
||||||
className="select"
|
className="select"
|
||||||
{...init(`componentType`, {
|
{...init(`componentType`, {
|
||||||
|
@ -435,7 +435,7 @@ class AppDialog extends React.Component<Props, State> {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
dataSource={envOptions}
|
dataSource={envOptions}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -92,7 +92,7 @@ class CardContent extends React.Component<Props, State> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDeleteAppPlan(item.name);
|
this.onDeleteAppPlan(item.name);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -145,7 +145,7 @@ class CardContent extends React.Component<Props, State> {
|
||||||
className={`card-content-wrapper`}
|
className={`card-content-wrapper`}
|
||||||
key={`${item.name}`}
|
key={`${item.name}`}
|
||||||
>
|
>
|
||||||
<Card locale={locale.Card} contentHeight="auto">
|
<Card locale={locale().Card} contentHeight="auto">
|
||||||
<Link to={`/applications/${name}/config`}>
|
<Link to={`/applications/${name}/config`}>
|
||||||
<div className="appplan-card-top flexcenter">
|
<div className="appplan-card-top flexcenter">
|
||||||
<If condition={icon && icon != 'none'}>
|
<If condition={icon && icon != 'none'}>
|
||||||
|
|
|
@ -75,7 +75,7 @@ class ProjectForm extends React.Component<Props, State> {
|
||||||
<div className="cluster-container">
|
<div className="cluster-container">
|
||||||
<Select
|
<Select
|
||||||
disabled={disable}
|
disabled={disable}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="cluster-params-input"
|
className="cluster-params-input"
|
||||||
mode="single"
|
mode="single"
|
||||||
dataSource={projectList}
|
dataSource={projectList}
|
||||||
|
|
|
@ -114,7 +114,7 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
||||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={this.onChangeProject}
|
onChange={this.onChangeProject}
|
||||||
|
@ -127,7 +127,7 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={this.onChangeEnv}
|
onChange={this.onChangeEnv}
|
||||||
|
|
|
@ -119,6 +119,8 @@ class ContainerLog extends Component<Props, State> {
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,9 +61,9 @@ class ApplicationLog extends React.Component<Props, State> {
|
||||||
this.loadApplicationStatus();
|
this.loadApplicationStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: any) {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
const { params } = nextProps.match;
|
const { params } = nextProps.match;
|
||||||
if (params.envName !== this.state.envName) {
|
if (params.envName !== this.state.envName || this.props.envbinding != nextProps.envbinding) {
|
||||||
this.setState({ envName: params.envName }, () => {
|
this.setState({ envName: params.envName }, () => {
|
||||||
this.loadApplicationStatus();
|
this.loadApplicationStatus();
|
||||||
});
|
});
|
||||||
|
@ -84,15 +84,14 @@ class ApplicationLog extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
loadPodInstance = async () => {
|
loadPodInstance = async () => {
|
||||||
const { applicationDetail, envbinding, applicationStatus } = this.props;
|
const { envbinding } = this.props;
|
||||||
const { appName, envName } = this.state;
|
const { appName, envName, activeComponentName } = this.state;
|
||||||
const envs = envbinding.filter((item) => item.name === envName);
|
const envs = envbinding.filter((item) => item.name === envName);
|
||||||
if (applicationDetail?.name && applicationStatus?.services?.length && envs.length > 0) {
|
if (envs.length > 0 && envs[0]) {
|
||||||
const componentName = applicationStatus.services[0].name;
|
|
||||||
const param = {
|
const param = {
|
||||||
appName: envs[0].appDeployName || appName,
|
appName: envs[0].appDeployName || appName,
|
||||||
appNs: envs[0].appDeployNamespace,
|
appNs: envs[0].appDeployNamespace,
|
||||||
name: componentName,
|
componentName: activeComponentName,
|
||||||
cluster: '',
|
cluster: '',
|
||||||
clusterNs: '',
|
clusterNs: '',
|
||||||
};
|
};
|
||||||
|
@ -102,12 +101,10 @@ class ApplicationLog extends React.Component<Props, State> {
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
podList: res.podList,
|
podList: res.podList,
|
||||||
pod: res.podList[0] || {},
|
activePodName: '',
|
||||||
activePodName: res.podList[0]?.metadata.name,
|
|
||||||
activeComponentName: res.podList[0]?.component,
|
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.loadPodDetail();
|
this.handlePodNameChange(res.podList[0].metadata.name);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,18 +130,26 @@ class ApplicationLog extends React.Component<Props, State> {
|
||||||
Message.warning(res.error);
|
Message.warning(res.error);
|
||||||
} else if (res) {
|
} else if (res) {
|
||||||
const activeContainerName = (res.containers?.[0] && res.containers[0]?.name) || '';
|
const activeContainerName = (res.containers?.[0] && res.containers[0]?.name) || '';
|
||||||
this.setState({
|
this.setState(
|
||||||
containers: res.containers,
|
{
|
||||||
activeContainerName,
|
containers: res.containers,
|
||||||
isActiveContainerNameDisabled: activeContainerName ? false : true,
|
},
|
||||||
});
|
() => {
|
||||||
|
this.handleContainerNameChange(activeContainerName);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handleComponentNameChange = (value: any) => {
|
handleComponentNameChange = (value: string) => {
|
||||||
this.setState({ activeComponentName: value });
|
this.setState(
|
||||||
|
{ activeComponentName: value, activePodName: '', activeContainerName: '' },
|
||||||
|
() => {
|
||||||
|
this.loadPodInstance();
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
handlePodNameChange = (value: any) => {
|
handlePodNameChange = (value: any) => {
|
||||||
const { podList } = this.state;
|
const { podList } = this.state;
|
||||||
|
@ -167,26 +172,10 @@ class ApplicationLog extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getComponentNameList = () => {
|
getComponentNameList = () => {
|
||||||
const { podList } = this.state;
|
|
||||||
const { components } = this.props;
|
const { components } = this.props;
|
||||||
const componentNameAlias: any = {};
|
return components?.map((c) => {
|
||||||
components?.map((c) => {
|
return { label: c.alias || c.name, value: c.name };
|
||||||
componentNameAlias[c.name] = c.alias || c.name;
|
|
||||||
});
|
});
|
||||||
if (podList && podList.length != 0) {
|
|
||||||
const componentNameList: { label: string; value: string }[] = [];
|
|
||||||
podList.forEach((item) => {
|
|
||||||
if (item.component) {
|
|
||||||
componentNameList.push({
|
|
||||||
label: componentNameAlias[item.component] || item.component,
|
|
||||||
value: item.component,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return componentNameList;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getPodNameList = () => {
|
getPodNameList = () => {
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Hearder extends React.Component<Props, State> {
|
||||||
<Row className="border-radius-8">
|
<Row className="border-radius-8">
|
||||||
<Col span="6" style={{ padding: '0 8px' }}>
|
<Col span="6" style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="small"
|
size="small"
|
||||||
onChange={this.handleChangeEnv}
|
onChange={this.handleChangeEnv}
|
||||||
|
@ -73,7 +73,7 @@ class Hearder extends React.Component<Props, State> {
|
||||||
|
|
||||||
<Col span="6" style={{ padding: '0 8px' }}>
|
<Col span="6" style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="small"
|
size="small"
|
||||||
onChange={this.handleChangeStatus}
|
onChange={this.handleChangeStatus}
|
||||||
|
|
|
@ -123,7 +123,7 @@ class TableList extends Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div className="table-version-list margin-top-20">
|
<div className="table-version-list margin-top-20">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
primaryKey={'version'}
|
primaryKey={'version'}
|
||||||
className="customTable"
|
className="customTable"
|
||||||
rowHeight={40}
|
rowHeight={40}
|
||||||
|
|
|
@ -125,7 +125,7 @@ class ApplicationRevisionList extends React.Component<Props, State> {
|
||||||
getRevisionList={this.getRevisionList}
|
getRevisionList={this.getRevisionList}
|
||||||
/>
|
/>
|
||||||
<Pagination
|
<Pagination
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
className="revison-pagenation"
|
className="revison-pagenation"
|
||||||
hideOnlyOnePage={true}
|
hideOnlyOnePage={true}
|
||||||
total={this.state.revisionsListTotal}
|
total={this.state.revisionsListTotal}
|
||||||
|
|
|
@ -251,7 +251,7 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
this.setState({ deployLoading: false });
|
this.setState({ deployLoading: false });
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
|
@ -304,11 +304,11 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
<Loading visible={loading} style={{ width: '100%' }}>
|
<Loading visible={loading} style={{ width: '100%' }}>
|
||||||
<If condition={applicationStatus}>
|
<If condition={applicationStatus}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
contentHeight="200px"
|
contentHeight="200px"
|
||||||
title={<Translation>Applied Resources</Translation>}
|
title={<Translation>Applied Resources</Translation>}
|
||||||
>
|
>
|
||||||
<Table locale={locale.Table} dataSource={resources}>
|
<Table locale={locale().Table} dataSource={resources}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
dataIndex="cluster"
|
dataIndex="cluster"
|
||||||
title={<Translation>Cluster</Translation>}
|
title={<Translation>Cluster</Translation>}
|
||||||
|
@ -325,9 +325,9 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
userInfo,
|
userInfo,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return <Link to="/clusters">{v}</Link>;
|
return <Link to="/clusters">{clusterName}</Link>;
|
||||||
}
|
}
|
||||||
return <span>{v}</span>;
|
return <span>{clusterName}</span>;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
|
@ -355,7 +355,11 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
if (row.latest) {
|
if (row.latest) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Icon style={{ color: 'green', marginRight: '8px' }} type="NEW" />
|
<Icon
|
||||||
|
style={{ color: 'green', marginRight: '8px' }}
|
||||||
|
type="NEW"
|
||||||
|
title="latest version resource"
|
||||||
|
/>
|
||||||
<Link to={`/applications/${applicationDetail?.name}/revisions`}>{v}</Link>
|
<Link to={`/applications/${applicationDetail?.name}/revisions`}>{v}</Link>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -369,12 +373,12 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
</Card>
|
</Card>
|
||||||
<If condition={componentStatus}>
|
<If condition={componentStatus}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
title={<Translation>Component Status</Translation>}
|
title={<Translation>Component Status</Translation>}
|
||||||
>
|
>
|
||||||
<Table locale={locale.Table} className="customTable" dataSource={componentStatus}>
|
<Table locale={locale().Table} className="customTable" dataSource={componentStatus}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
align="left"
|
align="left"
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
|
@ -413,12 +417,12 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
||||||
</If>
|
</If>
|
||||||
<If condition={applicationStatus?.conditions}>
|
<If condition={applicationStatus?.conditions}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale.Card}
|
locale={locale().Card}
|
||||||
style={{ marginTop: '8px' }}
|
style={{ marginTop: '8px' }}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
title={<Translation>Conditions</Translation>}
|
title={<Translation>Conditions</Translation>}
|
||||||
>
|
>
|
||||||
<Table locale={locale.Table} dataSource={applicationStatus?.conditions}>
|
<Table locale={locale().Table} dataSource={applicationStatus?.conditions}>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
width="150px"
|
width="150px"
|
||||||
dataIndex="type"
|
dataIndex="type"
|
||||||
|
|
|
@ -101,7 +101,7 @@ class WorkflowComponent extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ class WorkflowForm extends Component<Props, State> {
|
||||||
<Col span={24} style={{ padding: '0 8px' }}>
|
<Col span={24} style={{ padding: '0 8px' }}>
|
||||||
<FormItem label={<Translation>Workflow Type</Translation>} required disabled={edit}>
|
<FormItem label={<Translation>Workflow Type</Translation>} required disabled={edit}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="select"
|
className="select"
|
||||||
placeholder={t('Please select').toString()}
|
placeholder={t('Please select').toString()}
|
||||||
{...init(`type`, {
|
{...init(`type`, {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { loginSSO } from '../../api/authentication';
|
import { loginSSO } from '../../api/authentication';
|
||||||
import querystring from 'query-string';
|
import querystring from 'query-string';
|
||||||
|
import { Dialog, Button } from '@b-design/ui';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
location: {
|
location: {
|
||||||
|
@ -38,13 +40,29 @@ export default class CallBackPage extends React.Component<Props> {
|
||||||
this.props.history.push('/');
|
this.props.history.push('/');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
setTimeout(() => {
|
let customErrorMessage = '';
|
||||||
this.props.history.push('/login');
|
if (err.BusinessCode) {
|
||||||
}, 3000);
|
customErrorMessage = `${err.Message}(${err.BusinessCode})`;
|
||||||
|
} else {
|
||||||
|
customErrorMessage = 'Please check the network or contact the administrator!';
|
||||||
|
}
|
||||||
|
return Dialog.alert({
|
||||||
|
title: i18n.t('Dex Error'),
|
||||||
|
content: `${i18n.t(customErrorMessage)}`,
|
||||||
|
closeable: true,
|
||||||
|
closeMode: [],
|
||||||
|
footer: <Button onClick={this.handleClickRetry}>{i18n.t('Retry')}</Button>,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleClickRetry = () => {
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
localStorage.removeItem('refreshToken');
|
||||||
|
window.location.href = '/login';
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ class AddClustDialog extends React.Component<Props, State> {
|
||||||
const valueInfo = cluster.kubeConfig || values.kubeConfig || '';
|
const valueInfo = cluster.kubeConfig || values.kubeConfig || '';
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
title={
|
title={
|
||||||
editMode ? (
|
editMode ? (
|
||||||
|
|
|
@ -74,7 +74,7 @@ class CardContent extends React.Component<Props, State> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDeleteCluster(item.name);
|
this.onDeleteCluster(item.name);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -271,7 +271,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className="dialog-cluoudService-wraper"
|
className="dialog-cluoudService-wraper"
|
||||||
title={<Translation>Connect Kubernetes Cluster From Cloud</Translation>}
|
title={<Translation>Connect Kubernetes Cluster From Cloud</Translation>}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
@ -298,7 +298,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
||||||
<Form {...formItemLayout} field={this.field} className="cloud-server-wraper">
|
<Form {...formItemLayout} field={this.field} className="cloud-server-wraper">
|
||||||
<FormItem label={<Translation>Provider</Translation>} required={true}>
|
<FormItem label={<Translation>Provider</Translation>} required={true}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
dataSource={providerList}
|
dataSource={providerList}
|
||||||
|
@ -348,7 +348,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
<If condition={!choseInput}>
|
<If condition={!choseInput}>
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
dataSource={cloudClusters}
|
dataSource={cloudClusters}
|
||||||
hasBorder={false}
|
hasBorder={false}
|
||||||
loading={tableLoading}
|
loading={tableLoading}
|
||||||
|
@ -356,7 +356,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
||||||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
</Table>
|
</Table>
|
||||||
<Pagination
|
<Pagination
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
hideOnlyOnePage={true}
|
hideOnlyOnePage={true}
|
||||||
total={total}
|
total={total}
|
||||||
size="small"
|
size="small"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Message, Grid, Dialog, Form, Input, Field, Select, Loading } from '@b-design/ui';
|
import { Message, Grid, Dialog, Form, Input, Field, Select, Loading, Button } from '@b-design/ui';
|
||||||
import { checkName } from '../../../../utils/common';
|
import { checkName } from '../../../../utils/common';
|
||||||
import type { Target } from '../../../../interface/target';
|
import type { Target } from '../../../../interface/target';
|
||||||
import Translation from '../../../../components/Translation';
|
import Translation from '../../../../components/Translation';
|
||||||
|
@ -25,6 +25,7 @@ type State = {
|
||||||
targets?: Target[];
|
targets?: Target[];
|
||||||
namespaces?: { label: string; value: string }[];
|
namespaces?: { label: string; value: string }[];
|
||||||
targetLoading: boolean;
|
targetLoading: boolean;
|
||||||
|
submitLoading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EnvDialog extends React.Component<Props, State> {
|
class EnvDialog extends React.Component<Props, State> {
|
||||||
|
@ -41,6 +42,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
this.state = {
|
this.state = {
|
||||||
targetLoading: false,
|
targetLoading: false,
|
||||||
|
submitLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +86,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.setState({ submitLoading: true });
|
||||||
const { isEdit } = this.props;
|
const { isEdit } = this.props;
|
||||||
const { name, alias, description, targets, namespace, project } = values;
|
const { name, alias, description, targets, namespace, project } = values;
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -97,6 +100,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
updateEnv(params).then((res) => {
|
updateEnv(params).then((res) => {
|
||||||
|
this.setState({ submitLoading: false });
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Environment updated successfully</Translation>);
|
Message.success(<Translation>Environment updated successfully</Translation>);
|
||||||
this.props.onOK();
|
this.props.onOK();
|
||||||
|
@ -105,6 +109,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
createEnv(params).then((res) => {
|
createEnv(params).then((res) => {
|
||||||
|
this.setState({ submitLoading: false });
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Environment created successfully</Translation>);
|
Message.success(<Translation>Environment created successfully</Translation>);
|
||||||
this.props.onOK();
|
this.props.onOK();
|
||||||
|
@ -161,7 +166,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { visible, isEdit, projects } = this.props;
|
const { visible, isEdit, projects } = this.props;
|
||||||
const { targetLoading } = this.state;
|
const { targetLoading, submitLoading } = this.state;
|
||||||
const projectList = (projects || []).map((project) => {
|
const projectList = (projects || []).map((project) => {
|
||||||
return {
|
return {
|
||||||
label: project.alias ? `${project.alias}(${project.name})` : project.name,
|
label: project.alias ? `${project.alias}(${project.name})` : project.name,
|
||||||
|
@ -171,7 +176,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
height="auto"
|
height="auto"
|
||||||
title={
|
title={
|
||||||
|
@ -188,6 +193,13 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
onCancel={this.onClose}
|
onCancel={this.onClose}
|
||||||
onClose={this.onClose}
|
onClose={this.onClose}
|
||||||
footerActions={['cancel', 'ok']}
|
footerActions={['cancel', 'ok']}
|
||||||
|
footer={
|
||||||
|
<div>
|
||||||
|
<Button onClick={this.onOk} type="primary" loading={submitLoading}>
|
||||||
|
<Translation>Confirm</Translation>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
footerAlign="center"
|
footerAlign="center"
|
||||||
>
|
>
|
||||||
<Form {...formItemLayout} field={this.field}>
|
<Form {...formItemLayout} field={this.field}>
|
||||||
|
@ -297,7 +309,7 @@ class EnvDialog extends React.Component<Props, State> {
|
||||||
<Loading visible={targetLoading} style={{ width: '100%' }}>
|
<Loading visible={targetLoading} style={{ width: '100%' }}>
|
||||||
<FormItem label={<Translation>Target</Translation>} required>
|
<FormItem label={<Translation>Target</Translation>} required>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="select"
|
className="select"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder={i18n.t('Please select a target').toString()}
|
placeholder={i18n.t('Please select a target').toString()}
|
||||||
|
|
|
@ -138,7 +138,7 @@ class TableList extends Component<Props> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDelete(record);
|
this.onDelete(record);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -173,7 +173,7 @@ class TableList extends Component<Props> {
|
||||||
return (
|
return (
|
||||||
<div className="table-delivery-list margin-top-20">
|
<div className="table-delivery-list margin-top-20">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
className="customTable"
|
className="customTable"
|
||||||
size="medium"
|
size="medium"
|
||||||
dataSource={list}
|
dataSource={list}
|
||||||
|
|
|
@ -78,7 +78,7 @@ class Namespace extends React.Component<Props, State> {
|
||||||
<If condition={!showNameSpaceInput}>
|
<If condition={!showNameSpaceInput}>
|
||||||
<div className="cluster-container">
|
<div className="cluster-container">
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="cluster-params-input"
|
className="cluster-params-input"
|
||||||
mode="single"
|
mode="single"
|
||||||
dataSource={namespaces}
|
dataSource={namespaces}
|
||||||
|
|
|
@ -136,7 +136,7 @@ class targetList extends React.Component<Props, State> {
|
||||||
<Pagination
|
<Pagination
|
||||||
className="delivery-target-pagenation"
|
className="delivery-target-pagenation"
|
||||||
total={envTotal}
|
total={envTotal}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={this.state.pageSize}
|
pageSize={this.state.pageSize}
|
||||||
current={this.state.page}
|
current={this.state.page}
|
||||||
|
|
|
@ -249,7 +249,7 @@ class CreateIntegration extends React.Component<Props, State> {
|
||||||
required={true}
|
required={true}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
showSearch
|
showSearch
|
||||||
className="select"
|
className="select"
|
||||||
placeholder={i18n.t('Please select').toString()}
|
placeholder={i18n.t('Please select').toString()}
|
||||||
|
|
|
@ -74,6 +74,10 @@ class Integrations extends Component<Props, State> {
|
||||||
this.setState({
|
this.setState({
|
||||||
list: res,
|
list: res,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
list: [],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
@ -104,7 +108,7 @@ class Integrations extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -218,7 +222,7 @@ class Integrations extends Component<Props, State> {
|
||||||
</Button>
|
</Button>
|
||||||
</Permission>
|
</Permission>
|
||||||
</div>
|
</div>
|
||||||
<Table locale={locale.Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
<Table locale={locale().Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
||||||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
height: 100vh;
|
height: calc(100vh - 48px);
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
.login-wrapper {
|
.login-wrapper {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
|
@ -60,3 +60,47 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-topbar {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
background-color: #0064c8;
|
||||||
|
background-image: var(--btn-pure-primary-bg-image);
|
||||||
|
box-shadow: 5px 0 8px #888;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
.nav-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 9px;
|
||||||
|
border-radius: 3px;
|
||||||
|
img {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
float: right;
|
||||||
|
height: 48px;
|
||||||
|
margin-left: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
.vela-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import { Card, Button, Input, Form, Field, Icon } from '@b-design/ui';
|
import { Card, Button, Input, Form, Field, Icon, Grid } from '@b-design/ui';
|
||||||
import { getDexConfig, loginLocal, getLoginType } from '../../api/authentication';
|
import { getDexConfig, loginLocal, getLoginType } from '../../api/authentication';
|
||||||
import Translation from '../../components/Translation';
|
import Translation from '../../components/Translation';
|
||||||
import { checkName, checkUserPassword } from '../../utils/common';
|
import { checkName, checkUserPassword } from '../../utils/common';
|
||||||
|
import SwitchLanguage from '../../components/SwitchButton/index';
|
||||||
import Logo from '../../assets/kubevela-logo.png';
|
import Logo from '../../assets/kubevela-logo.png';
|
||||||
|
import LogoWhite from '../../assets/kubevela-logo-white.png';
|
||||||
|
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
|
@ -130,80 +133,102 @@ export default class LoginPage extends Component<Props, State> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const { loginType, loginErrorMessage } = this.state;
|
const { loginType, loginErrorMessage } = this.state;
|
||||||
|
const { Row, Col } = Grid;
|
||||||
return (
|
return (
|
||||||
<div className="full">
|
<Fragment>
|
||||||
<div className="login-wrapper">
|
<div className="login-topbar">
|
||||||
<If condition={loginType === 'dex'}>
|
<Row className="nav-wrapper">
|
||||||
<div />
|
<Col span="4" className="logo">
|
||||||
</If>
|
<img src={LogoWhite} title={'Make shipping applications more enjoyable.'} />
|
||||||
<If condition={loginType === 'local'}>
|
</Col>
|
||||||
<div className="login-card-wrapper">
|
<div style={{ flex: '1 1 0%' }} />
|
||||||
<Card contentHeight={'auto'}>
|
<div className="right">
|
||||||
<div className="logo-img-wrapper">
|
<div className="vela-item">
|
||||||
<img src={Logo} />
|
<a title="KubeVela Documents" href="https://kubevela.io" target="_blank">
|
||||||
</div>
|
<Icon size={14} type="help1" />
|
||||||
<h3 className="login-title-description">
|
</a>
|
||||||
<Translation>Make shipping applications more enjoyable</Translation>
|
</div>
|
||||||
</h3>
|
<div className="vela-item">
|
||||||
<Form onSubmit={this.handleSubmit} {...formItemLayout} field={this.field}>
|
<SwitchLanguage />
|
||||||
<FormItem
|
</div>
|
||||||
label={<Translation className="label-title">Username</Translation>}
|
|
||||||
labelAlign="top"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
name="username"
|
|
||||||
placeholder={i18n.t('Please input the username').toString()}
|
|
||||||
{...init('username', {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
pattern: checkName,
|
|
||||||
message: <Translation>Please input the username</Translation>,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={<Translation className="label-title">Password</Translation>}
|
|
||||||
labelAlign="top"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
name="password"
|
|
||||||
htmlType="password"
|
|
||||||
placeholder={i18n.t('Please input the password').toString()}
|
|
||||||
{...init('password', {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
pattern: checkUserPassword,
|
|
||||||
message: (
|
|
||||||
<Translation>
|
|
||||||
The password should be 8-16 bits and contain at least one number and
|
|
||||||
one letter
|
|
||||||
</Translation>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</Form>
|
|
||||||
<If condition={loginErrorMessage}>
|
|
||||||
<div className="logo-error-wrapper">
|
|
||||||
<Icon type="warning1" /> <Translation>{loginErrorMessage}</Translation>
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
<Button type="primary" onClick={this.handleSubmit}>
|
|
||||||
<Translation>Sign in</Translation>
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div className="full">
|
||||||
|
<div className="login-wrapper">
|
||||||
|
<If condition={loginType === 'dex'}>
|
||||||
|
<div />
|
||||||
|
</If>
|
||||||
|
<If condition={loginType === 'local'}>
|
||||||
|
<div className="login-card-wrapper">
|
||||||
|
<Card contentHeight={'auto'}>
|
||||||
|
<div className="logo-img-wrapper">
|
||||||
|
<img src={Logo} />
|
||||||
|
</div>
|
||||||
|
<h3 className="login-title-description">
|
||||||
|
<Translation>Make shipping applications more enjoyable</Translation>
|
||||||
|
</h3>
|
||||||
|
<Form onSubmit={this.handleSubmit} {...formItemLayout} field={this.field}>
|
||||||
|
<FormItem
|
||||||
|
label={<Translation className="label-title">Username</Translation>}
|
||||||
|
labelAlign="top"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
name="username"
|
||||||
|
placeholder={i18n.t('Please input the username').toString()}
|
||||||
|
{...init('username', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: checkName,
|
||||||
|
message: <Translation>Please input the username</Translation>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
label={<Translation className="label-title">Password</Translation>}
|
||||||
|
labelAlign="top"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
name="password"
|
||||||
|
htmlType="password"
|
||||||
|
placeholder={i18n.t('Please input the password').toString()}
|
||||||
|
{...init('password', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: checkUserPassword,
|
||||||
|
message: (
|
||||||
|
<Translation>
|
||||||
|
The password should be 8-16 bits and contain at least one number
|
||||||
|
and one letter
|
||||||
|
</Translation>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
<If condition={loginErrorMessage}>
|
||||||
|
<div className="logo-error-wrapper">
|
||||||
|
<Icon type="warning1" /> <Translation>{loginErrorMessage}</Translation>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
<Button type="primary" onClick={this.handleSubmit}>
|
||||||
|
<Translation>Sign in</Translation>
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={this.onChangeEnv}
|
onChange={this.onChangeEnv}
|
||||||
|
@ -124,7 +124,7 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="single"
|
mode="single"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={this.onChangeTarget}
|
onChange={this.onChangeTarget}
|
||||||
|
|
|
@ -181,7 +181,7 @@ class MemberDialog extends React.Component<Props, State> {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="tag"
|
mode="tag"
|
||||||
dataSource={rolesList}
|
dataSource={rolesList}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -133,7 +133,7 @@ class ProjectMembers extends Component<Props, State> {
|
||||||
.catch();
|
.catch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ class ProjectMembers extends Component<Props, State> {
|
||||||
|
|
||||||
<section className="margin-top-20 member-list-wrapper">
|
<section className="margin-top-20 member-list-wrapper">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
dataSource={memberList}
|
dataSource={memberList}
|
||||||
hasBorder={false}
|
hasBorder={false}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
@ -294,7 +294,7 @@ class ProjectMembers extends Component<Props, State> {
|
||||||
<Pagination
|
<Pagination
|
||||||
className="margin-top-20 text-align-right"
|
className="margin-top-20 text-align-right"
|
||||||
total={total}
|
total={total}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
hideOnlyOnePage={true}
|
hideOnlyOnePage={true}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
|
|
|
@ -149,7 +149,7 @@ class ProjectRoles extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class General extends Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="general-wrapper">
|
<div className="general-wrapper">
|
||||||
<Card locale={locale.Card} contentHeight="auto" className="card-wrapper">
|
<Card locale={locale().Card} contentHeight="auto" className="card-wrapper">
|
||||||
<section className="card-title-wrapper">
|
<section className="card-title-wrapper">
|
||||||
<span className="card-title">
|
<span className="card-title">
|
||||||
<Translation>General</Translation>
|
<Translation>General</Translation>
|
||||||
|
|
|
@ -122,7 +122,7 @@ class Integrations extends Component<Props, State> {
|
||||||
</section>
|
</section>
|
||||||
<section className="card-content-table">
|
<section className="card-content-table">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
dataSource={configList}
|
dataSource={configList}
|
||||||
hasBorder={true}
|
hasBorder={true}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
|
|
@ -104,7 +104,7 @@ class Targets extends Component<Props, State> {
|
||||||
</Permission>
|
</Permission>
|
||||||
</section>
|
</section>
|
||||||
<section className="card-content-table">
|
<section className="card-content-table">
|
||||||
<Table locale={locale.Table} dataSource={list} hasBorder={true} loading={isLoading}>
|
<Table locale={locale().Table} dataSource={list} hasBorder={true} loading={isLoading}>
|
||||||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
</Table>
|
</Table>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -104,7 +104,7 @@ class Projects extends Component<Props, State> {
|
||||||
.catch();
|
.catch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -253,14 +253,14 @@ class Projects extends Component<Props, State> {
|
||||||
</Permission>,
|
</Permission>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Table locale={locale.Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
<Table locale={locale().Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
||||||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
className="margin-top-20 text-align-right"
|
className="margin-top-20 text-align-right"
|
||||||
total={total}
|
total={total}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
current={page}
|
current={page}
|
||||||
|
|
|
@ -197,7 +197,7 @@ class RolesDialog extends React.Component<Props, State> {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="tag"
|
mode="tag"
|
||||||
dataSource={permPoliciesList}
|
dataSource={permPoliciesList}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -93,7 +93,7 @@ class Roles extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -231,14 +231,14 @@ class Roles extends Component<Props, State> {
|
||||||
</Permission>,
|
</Permission>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Table locale={locale.Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
<Table locale={locale().Table} dataSource={list} hasBorder={false} loading={isLoading}>
|
||||||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
className="margin-top-20 text-align-right"
|
className="margin-top-20 text-align-right"
|
||||||
total={total}
|
total={total}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
hideOnlyOnePage={true}
|
hideOnlyOnePage={true}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
|
|
|
@ -103,7 +103,7 @@ class TableList extends Component<Props> {
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.onDelete(record);
|
this.onDelete(record);
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -137,7 +137,7 @@ class TableList extends Component<Props> {
|
||||||
return (
|
return (
|
||||||
<div className="table-delivery-list margin-top-20">
|
<div className="table-delivery-list margin-top-20">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
className="customTable"
|
className="customTable"
|
||||||
size="medium"
|
size="medium"
|
||||||
dataSource={list}
|
dataSource={list}
|
||||||
|
|
|
@ -100,7 +100,7 @@ class Namespace extends React.Component<Props, State> {
|
||||||
<div>
|
<div>
|
||||||
<div className="cluster-container">
|
<div className="cluster-container">
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="cluster-params-input"
|
className="cluster-params-input"
|
||||||
mode="single"
|
mode="single"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -122,7 +122,7 @@ class Namespace extends React.Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'namespaceDialogWraper'}
|
className={'namespaceDialogWraper'}
|
||||||
title={<Translation>Create Namespace</Translation>}
|
title={<Translation>Create Namespace</Translation>}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|
|
@ -241,7 +241,7 @@ class DeliveryDialog extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
locale={locale.Dialog}
|
locale={locale().Dialog}
|
||||||
className={'commonDialog'}
|
className={'commonDialog'}
|
||||||
height="auto"
|
height="auto"
|
||||||
title={
|
title={
|
||||||
|
@ -346,7 +346,7 @@ class DeliveryDialog extends React.Component<Props, State> {
|
||||||
<Col span={12} style={{ padding: '0 8px' }}>
|
<Col span={12} style={{ padding: '0 8px' }}>
|
||||||
<FormItem label={<Translation>Cluster</Translation>} required>
|
<FormItem label={<Translation>Cluster</Translation>} required>
|
||||||
<Select
|
<Select
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
className="select"
|
className="select"
|
||||||
disabled={isEdit}
|
disabled={isEdit}
|
||||||
placeholder={t('Please select').toString()}
|
placeholder={t('Please select').toString()}
|
||||||
|
|
|
@ -147,7 +147,7 @@ class targetList extends React.Component<Props, State> {
|
||||||
<Pagination
|
<Pagination
|
||||||
className="delivery-target-pagenation"
|
className="delivery-target-pagenation"
|
||||||
total={total}
|
total={total}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={this.state.pageSize}
|
pageSize={this.state.pageSize}
|
||||||
current={this.state.page}
|
current={this.state.page}
|
||||||
|
|
|
@ -253,7 +253,7 @@ class CreateUser extends React.Component<Props, State> {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
locale={locale.Select}
|
locale={locale().Select}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
dataSource={rolesListSelect}
|
dataSource={rolesListSelect}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Grid, Icon, Input } from '@b-design/ui';
|
import { Grid, Icon, Input } from '@b-design/ui';
|
||||||
import i18n from '../../../../i18n';
|
import i18n from '../../../../i18n';
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
|
@ -74,4 +75,4 @@ class SelectSearch extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectSearch;
|
export default withTranslation()(SelectSearch);
|
||||||
|
|
|
@ -193,7 +193,7 @@ class Users extends Component<Props, State> {
|
||||||
.catch();
|
.catch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
locale: locale.Dialog,
|
locale: locale().Dialog,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ class Users extends Component<Props, State> {
|
||||||
|
|
||||||
<section className="margin-top-20 user-list-wrapper">
|
<section className="margin-top-20 user-list-wrapper">
|
||||||
<Table
|
<Table
|
||||||
locale={locale.Table}
|
locale={locale().Table}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
hasBorder={false}
|
hasBorder={false}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
@ -456,7 +456,7 @@ class Users extends Component<Props, State> {
|
||||||
<Pagination
|
<Pagination
|
||||||
className="margin-top-20 text-align-right"
|
className="margin-top-20 text-align-right"
|
||||||
total={total}
|
total={total}
|
||||||
locale={locale.Pagination}
|
locale={locale().Pagination}
|
||||||
hideOnlyOnePage={true}
|
hideOnlyOnePage={true}
|
||||||
size="medium"
|
size="medium"
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
|
|
|
@ -18,7 +18,7 @@ export function isMock() {
|
||||||
export function getDomain(): { MOCK: string | undefined; APIBASE: string | undefined } {
|
export function getDomain(): { MOCK: string | undefined; APIBASE: string | undefined } {
|
||||||
const { MOCK, BASE_DOMAIN } = process.env;
|
const { MOCK, BASE_DOMAIN } = process.env;
|
||||||
return {
|
return {
|
||||||
MOCK: MOCK,
|
MOCK: MOCK || '',
|
||||||
APIBASE: BASE_DOMAIN || '',
|
APIBASE: BASE_DOMAIN || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1,189 @@
|
||||||
export default {
|
import { getLanguage } from '../utils/common';
|
||||||
Timeline: {
|
const localeData = {
|
||||||
expand: 'Expand',
|
en: {
|
||||||
fold: 'Fold',
|
Timeline: {
|
||||||
},
|
expand: 'Expand',
|
||||||
Balloon: {
|
fold: 'Fold',
|
||||||
close: 'Close',
|
},
|
||||||
},
|
Balloon: {
|
||||||
Card: {
|
close: 'Close',
|
||||||
expand: 'Expand',
|
},
|
||||||
fold: 'Fold',
|
Card: {
|
||||||
},
|
expand: 'Expand',
|
||||||
Dialog: {
|
fold: 'Fold',
|
||||||
close: 'Close',
|
},
|
||||||
ok: 'Confirm',
|
Dialog: {
|
||||||
cancel: 'Cancel',
|
close: 'Close',
|
||||||
},
|
ok: 'Confirm',
|
||||||
Drawer: {
|
|
||||||
close: 'Close',
|
|
||||||
},
|
|
||||||
Message: {
|
|
||||||
closeAriaLabel: 'Close',
|
|
||||||
},
|
|
||||||
Pagination: {
|
|
||||||
prev: 'Prev',
|
|
||||||
next: 'Next',
|
|
||||||
goTo: 'Go To',
|
|
||||||
page: 'Page',
|
|
||||||
go: 'Go',
|
|
||||||
total: 'Page {current} of {total} pages.',
|
|
||||||
labelPrev: 'Prev page, current page {current}',
|
|
||||||
labelNext: 'Next page, current page {current}',
|
|
||||||
inputAriaLabel: 'Please enter the page to jump to',
|
|
||||||
selectAriaLabel: 'Please select page size',
|
|
||||||
pageSize: 'Page Size:',
|
|
||||||
},
|
|
||||||
Input: {
|
|
||||||
clear: 'Clear',
|
|
||||||
},
|
|
||||||
List: {
|
|
||||||
empty: 'No Data',
|
|
||||||
},
|
|
||||||
Select: {
|
|
||||||
selectPlaceholder: 'Please select',
|
|
||||||
autoCompletePlaceholder: 'Please enter',
|
|
||||||
notFoundContent: 'No Options',
|
|
||||||
maxTagPlaceholder: '{selected}/{total} items have been selected.',
|
|
||||||
selectAll: 'Select All',
|
|
||||||
},
|
|
||||||
Table: {
|
|
||||||
empty: 'No Data',
|
|
||||||
ok: 'Confirm',
|
|
||||||
reset: 'Reset',
|
|
||||||
asc: 'Asc',
|
|
||||||
desc: 'Desc',
|
|
||||||
expanded: 'Expanded',
|
|
||||||
folded: 'Folded',
|
|
||||||
filter: 'Filter',
|
|
||||||
selectAll: 'Select All',
|
|
||||||
},
|
|
||||||
Upload: {
|
|
||||||
card: {
|
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
|
},
|
||||||
|
Drawer: {
|
||||||
|
close: 'Close',
|
||||||
|
},
|
||||||
|
Message: {
|
||||||
|
closeAriaLabel: 'Close',
|
||||||
|
},
|
||||||
|
Pagination: {
|
||||||
|
prev: 'Prev',
|
||||||
|
next: 'Next',
|
||||||
|
goTo: 'Go To',
|
||||||
|
page: 'Page',
|
||||||
|
go: 'Go',
|
||||||
|
total: 'Page {current} of {total} pages.',
|
||||||
|
labelPrev: 'Prev page, current page {current}',
|
||||||
|
labelNext: 'Next page, current page {current}',
|
||||||
|
inputAriaLabel: 'Please enter the page to jump to',
|
||||||
|
selectAriaLabel: 'Please select page size',
|
||||||
|
pageSize: 'Page Size:',
|
||||||
|
},
|
||||||
|
Input: {
|
||||||
|
clear: 'Clear',
|
||||||
|
},
|
||||||
|
List: {
|
||||||
|
empty: 'No Data',
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
selectPlaceholder: 'Please select',
|
||||||
|
autoCompletePlaceholder: 'Please enter',
|
||||||
|
notFoundContent: 'No Options',
|
||||||
|
maxTagPlaceholder: '{selected}/{total} items have been selected.',
|
||||||
|
selectAll: 'Select All',
|
||||||
|
},
|
||||||
|
Table: {
|
||||||
|
empty: 'No Data',
|
||||||
|
ok: 'Confirm',
|
||||||
|
reset: 'Reset',
|
||||||
|
asc: 'Asc',
|
||||||
|
desc: 'Desc',
|
||||||
|
expanded: 'Expanded',
|
||||||
|
folded: 'Folded',
|
||||||
|
filter: 'Filter',
|
||||||
|
selectAll: 'Select All',
|
||||||
|
},
|
||||||
|
Upload: {
|
||||||
|
card: {
|
||||||
|
cancel: 'Cancel',
|
||||||
|
delete: 'Delete',
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
delete: 'Delete',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: {
|
||||||
|
buttonText: 'Search',
|
||||||
|
},
|
||||||
|
Tag: {
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
},
|
},
|
||||||
upload: {
|
Switch: {
|
||||||
delete: 'Delete',
|
on: 'On',
|
||||||
|
off: 'Off',
|
||||||
|
},
|
||||||
|
Tab: {
|
||||||
|
closeAriaLabel: 'Close',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Search: {
|
zh: {
|
||||||
buttonText: 'Search',
|
Timeline: {
|
||||||
},
|
expand: '展开',
|
||||||
Tag: {
|
fold: '收起',
|
||||||
delete: 'Delete',
|
},
|
||||||
},
|
Balloon: {
|
||||||
Switch: {
|
close: '关闭',
|
||||||
on: 'On',
|
},
|
||||||
off: 'Off',
|
Card: {
|
||||||
},
|
expand: '展开',
|
||||||
Tab: {
|
fold: '收起',
|
||||||
closeAriaLabel: 'Close',
|
},
|
||||||
|
Dialog: {
|
||||||
|
close: '关闭',
|
||||||
|
ok: '确认',
|
||||||
|
cancel: '取消',
|
||||||
|
},
|
||||||
|
Drawer: {
|
||||||
|
close: '关闭',
|
||||||
|
},
|
||||||
|
Message: {
|
||||||
|
closeAriaLabel: '关闭标签',
|
||||||
|
},
|
||||||
|
Pagination: {
|
||||||
|
prev: '前一页',
|
||||||
|
next: '下一页',
|
||||||
|
goTo: '去往',
|
||||||
|
page: '分页',
|
||||||
|
go: '去',
|
||||||
|
total: 'Page {current} of {total} pages.',
|
||||||
|
labelPrev: '前一页, 当前页 {current}',
|
||||||
|
labelNext: '下一页, 当前页 {current}',
|
||||||
|
inputAriaLabel: '请输入要跳转到的页面',
|
||||||
|
selectAriaLabel: '请选择页面展示的数量',
|
||||||
|
pageSize: '每页显示多少条:',
|
||||||
|
},
|
||||||
|
Input: {
|
||||||
|
clear: '清空',
|
||||||
|
},
|
||||||
|
List: {
|
||||||
|
empty: '没有数据',
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
selectPlaceholder: '请选择',
|
||||||
|
autoCompletePlaceholder: '请输入',
|
||||||
|
notFoundContent: '没有下拉项',
|
||||||
|
maxTagPlaceholder: '{selected}/{total} 条目已选择.',
|
||||||
|
selectAll: '全选',
|
||||||
|
},
|
||||||
|
Table: {
|
||||||
|
empty: '没有数据',
|
||||||
|
ok: '确认',
|
||||||
|
reset: '重置',
|
||||||
|
asc: '生序',
|
||||||
|
desc: '降序',
|
||||||
|
expanded: '展开',
|
||||||
|
folded: '收起',
|
||||||
|
filter: '过滤',
|
||||||
|
selectAll: '全选',
|
||||||
|
},
|
||||||
|
Upload: {
|
||||||
|
card: {
|
||||||
|
cancel: '取消',
|
||||||
|
delete: '删除',
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
delete: '删除',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: {
|
||||||
|
buttonText: '搜索',
|
||||||
|
},
|
||||||
|
Tag: {
|
||||||
|
delete: '删除',
|
||||||
|
},
|
||||||
|
Switch: {
|
||||||
|
on: '打开',
|
||||||
|
off: '关闭',
|
||||||
|
},
|
||||||
|
Tab: {
|
||||||
|
closeAriaLabel: '关闭',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
class SingletonLocal {
|
||||||
|
private constructor() {}
|
||||||
|
private static instance: SingletonLocal | null = null;
|
||||||
|
public static getInstance(): SingletonLocal {
|
||||||
|
this.instance = this.instance || new SingletonLocal();
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
private local: any;
|
||||||
|
public setLocal(value: any) {
|
||||||
|
this.local = value;
|
||||||
|
}
|
||||||
|
public getLocal() {
|
||||||
|
return () => {
|
||||||
|
const language = getLanguage();
|
||||||
|
return this.local[language] ?? this.local.en;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SingletonLocal.getInstance().setLocal(localeData);
|
||||||
|
export default SingletonLocal.getInstance().getLocal();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import type { Endpoint } from '../interface/observation';
|
import type { Endpoint } from '../interface/observation';
|
||||||
import type { ComponentDefinitionsBase } from '../interface/application';
|
import type { ComponentDefinitionsBase } from '../interface/application';
|
||||||
|
import type { LoginUserInfo } from '../interface/user';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
type SelectGroupType = {
|
type SelectGroupType = {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -142,3 +144,60 @@ export function getSelectLabel(
|
||||||
export function getMatchParamObj(match: { params: any }, name: string) {
|
export function getMatchParamObj(match: { params: any }, name: string) {
|
||||||
return match.params && match.params[name];
|
return match.params && match.params[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAdminUserCheck(userInfo: LoginUserInfo | undefined) {
|
||||||
|
const platformPermissions = userInfo?.platformPermissions || [];
|
||||||
|
const findAdminUser = _.find(platformPermissions, (item) => {
|
||||||
|
return item.name === 'admin';
|
||||||
|
});
|
||||||
|
if (findAdminUser) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get browser name agent version
|
||||||
|
* return browser name version
|
||||||
|
* */
|
||||||
|
export function getBrowserNameAndVersion() {
|
||||||
|
const agent = navigator.userAgent.toLowerCase();
|
||||||
|
const regStr_ie = /msie [\d.]+/gi;
|
||||||
|
const regStr_ff = /firefox\/[\d.]+/gi;
|
||||||
|
const regStr_chrome = /chrome\/[\d.]+/gi;
|
||||||
|
const regStr_saf = /safari\/[\d.]+/gi;
|
||||||
|
let browserNV: any;
|
||||||
|
//IE
|
||||||
|
if (agent.indexOf('msie') > 0) {
|
||||||
|
browserNV = agent.match(regStr_ie);
|
||||||
|
}
|
||||||
|
//firefox
|
||||||
|
if (agent.indexOf('firefox') > 0) {
|
||||||
|
browserNV = agent.match(regStr_ff);
|
||||||
|
}
|
||||||
|
//Chrome
|
||||||
|
if (agent.indexOf('chrome') > 0) {
|
||||||
|
browserNV = agent.match(regStr_chrome);
|
||||||
|
}
|
||||||
|
//Safari
|
||||||
|
if (agent.indexOf('safari') > 0 && agent.indexOf('chrome') < 0) {
|
||||||
|
browserNV = agent.match(regStr_saf);
|
||||||
|
}
|
||||||
|
browserNV = browserNV.toString();
|
||||||
|
//other
|
||||||
|
if ('' == browserNV) {
|
||||||
|
browserNV = 'Is not a standard browser';
|
||||||
|
}
|
||||||
|
//Here does not display "/"
|
||||||
|
if (browserNV.indexOf('firefox') != -1 || browserNV.indexOf('chrome') != -1) {
|
||||||
|
browserNV = browserNV.replace('/', '');
|
||||||
|
}
|
||||||
|
//Here does not display space
|
||||||
|
if (browserNV.indexOf('msie') != -1) {
|
||||||
|
//msie replace IE & trim space
|
||||||
|
browserNV = browserNV.replace('msie', 'ie').replace(/\s/g, '');
|
||||||
|
}
|
||||||
|
//return eg:ie9.0 firefox34.0 chrome37.0
|
||||||
|
return browserNV;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue