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:
|
||||
push:
|
||||
branches: [ main, release-* ]
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches: [ main, release-* ]
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
detect-noop:
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
# 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).
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
* Write technical documentation and blog posts, for users and contributors.
|
||||
* 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/).
|
||||
|
||||
|
@ -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.
|
||||
- [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).
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||

|
||||
|
||||
# Velaux
|
||||
# VelaUX
|
||||
|
||||
## Overview
|
||||
|
||||
|
@ -22,7 +22,6 @@ echo "BASE_DOMAIN='http://127.0.0.1:8000'" > .env
|
|||
## Community
|
||||
|
||||
- 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*)
|
||||
- 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",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
|
|
|
@ -480,3 +480,6 @@ a {
|
|||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -12,10 +12,20 @@ export type Props = {
|
|||
const Item: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<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>
|
||||
</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}
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'dva';
|
||||
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 Translation from '../../components/Translation';
|
||||
import locale from '../../utils/locale';
|
||||
|
@ -10,6 +10,7 @@ import type { SystemInfo } from '../../interface/system';
|
|||
import type { LoginUserInfo } from '../../interface/user';
|
||||
import { updateSystemInfo } from '../../api/config';
|
||||
import { checkPermission } from '../../utils/permission';
|
||||
import Item from '../Item';
|
||||
const { Col, Row } = Grid;
|
||||
|
||||
type Props = {
|
||||
|
@ -164,10 +165,10 @@ class PlatformSetting extends React.Component<Props, State> {
|
|||
return domain;
|
||||
};
|
||||
render() {
|
||||
const { onClose, platformSetting } = this.props;
|
||||
const { onClose, platformSetting, systemInfo } = this.props;
|
||||
return (
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
visible={platformSetting}
|
||||
className={'commonDialog'}
|
||||
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 }}>
|
||||
<Card
|
||||
style={{ marginBottom: '16px' }}
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
contentHeight="200px"
|
||||
title={<Translation>User authentication configuration</Translation>}
|
||||
>
|
||||
|
@ -229,14 +230,33 @@ class PlatformSetting extends React.Component<Props, State> {
|
|||
</Row>
|
||||
</Card>
|
||||
|
||||
{/* <Card
|
||||
<Card
|
||||
style={{ marginBottom: '16px' }}
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
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 />
|
||||
</Card> */}
|
||||
<Item
|
||||
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>
|
||||
</Dialog>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@ class StatusShow extends React.Component<Props> {
|
|||
const { applicationStatus, onClose, loading, title } = this.props;
|
||||
return (
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
visible={true}
|
||||
className={'commonDialog'}
|
||||
title={title}
|
||||
|
@ -42,11 +42,11 @@ class StatusShow extends React.Component<Props> {
|
|||
>
|
||||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
contentHeight="200px"
|
||||
title={<Translation>Applied Resources</Translation>}
|
||||
>
|
||||
<Table locale={locale.Table} dataSource={applicationStatus?.appliedResources}>
|
||||
<Table locale={locale().Table} dataSource={applicationStatus?.appliedResources}>
|
||||
<Table.Column
|
||||
dataIndex="name"
|
||||
width="150px"
|
||||
|
@ -76,12 +76,12 @@ class StatusShow extends React.Component<Props> {
|
|||
</Card>
|
||||
<If condition={applicationStatus?.conditions}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Conditions</Translation>}
|
||||
>
|
||||
<Table locale={locale.Table} dataSource={applicationStatus?.conditions}>
|
||||
<Table locale={locale().Table} dataSource={applicationStatus?.conditions}>
|
||||
<Table.Column
|
||||
width="150px"
|
||||
dataIndex="type"
|
||||
|
@ -118,13 +118,13 @@ class StatusShow extends React.Component<Props> {
|
|||
</If>
|
||||
<If condition={applicationStatus?.services}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Component Status</Translation>}
|
||||
>
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
dataSource={applicationStatus?.services}
|
||||
>
|
||||
|
|
|
@ -373,7 +373,7 @@ class UISchema extends Component<Props, State> {
|
|||
>
|
||||
<Select
|
||||
disabled={disableEdit}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
|
|
|
@ -127,7 +127,7 @@ class Group extends React.Component<Props, State> {
|
|||
this.setState({ enable: event, closed: false, checked: false });
|
||||
this.removeJsonKeyValue();
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -93,7 +93,7 @@ class HelmChartSelect extends Component<Props, State> {
|
|||
disabled={disabled}
|
||||
value={value}
|
||||
dataSource={dataSource}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
|
|
|
@ -101,7 +101,7 @@ class HelmChartVersionSelect extends Component<Props, State> {
|
|||
disabled={disabled}
|
||||
value={value}
|
||||
dataSource={dataSource}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
|
|
|
@ -92,13 +92,24 @@ class HelmRepoSelect extends Component<Props, State> {
|
|||
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() {
|
||||
const { disabled, value } = this.props;
|
||||
const { repos, loading, inputRepo } = this.state;
|
||||
const dataSource = repos.map((repo) => repo.url);
|
||||
const dataSource = repos;
|
||||
if (inputRepo) {
|
||||
dataSource.unshift(inputRepo);
|
||||
dataSource.unshift({ url: inputRepo, type: 'helm' });
|
||||
}
|
||||
const transDataSource = this.convertHelmRepositoryOptions(dataSource);
|
||||
return (
|
||||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
<Select
|
||||
|
@ -110,8 +121,8 @@ class HelmRepoSelect extends Component<Props, State> {
|
|||
onSearch={this.onSearch}
|
||||
followTrigger={true}
|
||||
value={value}
|
||||
dataSource={dataSource}
|
||||
locale={locale.Select}
|
||||
dataSource={transDataSource}
|
||||
locale={locale().Select}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
|
|
|
@ -23,19 +23,30 @@ type Props = {
|
|||
|
||||
type State = {
|
||||
items: Item[];
|
||||
selectValues?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
changeEntryKey?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
};
|
||||
|
||||
type Item = {
|
||||
key: string;
|
||||
label: string;
|
||||
value?: any;
|
||||
valueType: any;
|
||||
};
|
||||
|
||||
type Objects = {
|
||||
[propName: string]: any;
|
||||
};
|
||||
function getEmptyItem() {
|
||||
return {
|
||||
key: Date.now().toString(),
|
||||
label: '',
|
||||
value: '',
|
||||
valueType: 'string',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -48,20 +59,24 @@ class KV extends Component<Props, State> {
|
|||
};
|
||||
this.form = new Field(this, {
|
||||
onChange: (name: string, value: string) => {
|
||||
this.submit();
|
||||
const { keyOptions } = this.props;
|
||||
const { selectValues } = this.state;
|
||||
if (keyOptions && name.indexOf('envKey-') > -1) {
|
||||
const itemKey = name.substring(name.indexOf('-') + 1);
|
||||
this.form.setValue('envValue-' + itemKey, keyOptions[value]);
|
||||
const { items } = this.state;
|
||||
const newSelectValues: Objects = {};
|
||||
const newItems = items.map((item) => {
|
||||
if (item.key == itemKey) {
|
||||
item.value = keyOptions[value];
|
||||
newSelectValues[item.key] = value;
|
||||
item.valueType = this.getValueType(keyOptions[value]);
|
||||
}
|
||||
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 { items } = this.state;
|
||||
const newItems = [...items];
|
||||
const selectValues: Objects = {};
|
||||
if (value) {
|
||||
for (const label in value) {
|
||||
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('envValue-' + key, value[label]);
|
||||
selectValues[key] = label;
|
||||
}
|
||||
}
|
||||
this.setState({ items: newItems });
|
||||
|
||||
this.setState({ items: newItems, selectValues: selectValues });
|
||||
};
|
||||
|
||||
addItem() {
|
||||
|
@ -92,6 +115,7 @@ class KV extends Component<Props, State> {
|
|||
}
|
||||
|
||||
submit() {
|
||||
this.updateEntryKey();
|
||||
const values: any = this.form.getValues();
|
||||
const items: Map<string, Item> = new Map();
|
||||
Object.keys(values).map((key) => {
|
||||
|
@ -99,7 +123,7 @@ class KV extends Component<Props, State> {
|
|||
const index = key.replace('envKey-', '');
|
||||
let item = items.get(index);
|
||||
if (!item) {
|
||||
item = { key: '', label: '' };
|
||||
item = { key: '', label: '', valueType: 'string' };
|
||||
}
|
||||
item.label = values[key];
|
||||
items.set(index, item);
|
||||
|
@ -109,7 +133,7 @@ class KV extends Component<Props, State> {
|
|||
const index = key.replace('envValue-', '');
|
||||
let item = items.get(index);
|
||||
if (!item) {
|
||||
item = { key: '', label: '' };
|
||||
item = { key: '', label: '', valueType: 'string' };
|
||||
}
|
||||
item.value = values[key];
|
||||
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) {
|
||||
const { items } = this.state;
|
||||
items.forEach((item, i) => {
|
||||
|
@ -138,27 +173,70 @@ class KV extends Component<Props, State> {
|
|||
this.submit();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { items } = this.state;
|
||||
const { id, additional, additionalParameter, keyOptions } = this.props;
|
||||
const { init } = this.form;
|
||||
let valueType = 'string';
|
||||
if (additional && additionalParameter) {
|
||||
// TODO: current only support one parameter
|
||||
if (additionalParameter.uiType == 'Number') {
|
||||
valueType = 'number';
|
||||
onSearch = (key: string, value: string) => {
|
||||
const { items, selectValues, changeEntryKey } = this.state;
|
||||
const newSelectValues: Objects = {};
|
||||
items.forEach((item) => {
|
||||
if (item.key === key) {
|
||||
item.value = value;
|
||||
newSelectValues[item.key] = value;
|
||||
this.form.setValue('envKey-' + key, value);
|
||||
this.form.remove('envValue-' + key);
|
||||
}
|
||||
if (additionalParameter.uiType == 'Switch') {
|
||||
valueType = 'boolean';
|
||||
return item;
|
||||
});
|
||||
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) : [];
|
||||
return (
|
||||
<div id={id}>
|
||||
{items.map((item) => {
|
||||
if (item.value != undefined) {
|
||||
valueType = typeof item.value;
|
||||
}
|
||||
return (
|
||||
<Row key={item.key} gutter="20">
|
||||
<Col span={10}>
|
||||
|
@ -171,7 +249,11 @@ class KV extends Component<Props, State> {
|
|||
{...init(`envKey-${item.key}`)}
|
||||
label={'Key'}
|
||||
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 condition={!keyOptions}>
|
||||
|
@ -187,17 +269,17 @@ class KV extends Component<Props, State> {
|
|||
</Col>
|
||||
<Col span={10}>
|
||||
<Form.Item>
|
||||
<If condition={valueType == 'number' || valueType == 'string'}>
|
||||
<If condition={item.valueType == 'number' || item.valueType == 'string'}>
|
||||
<Input
|
||||
disabled={this.props.disabled}
|
||||
htmlType={valueType == 'number' ? 'number' : ''}
|
||||
htmlType={item.valueType == 'number' ? 'number' : ''}
|
||||
{...init(`envValue-${item.key}`)}
|
||||
label={'Value'}
|
||||
className="full-width"
|
||||
placeholder={i18n.t('Please input or select key')}
|
||||
/>
|
||||
</If>
|
||||
<If condition={valueType == 'boolean'}>
|
||||
<If condition={item.valueType == 'boolean'}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span style={{ lineHeight: '36px', marginRight: '16px' }}>Value:</span>
|
||||
<Switch
|
||||
|
|
|
@ -27,7 +27,7 @@ class SecretKeySelect extends React.Component<Props, State> {
|
|||
const { onChange, value, secretKeys, id, disabled } = this.props;
|
||||
return (
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
onChange={onChange}
|
||||
defaultValue={value}
|
||||
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']);
|
||||
return (
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
onChange={this.onChange}
|
||||
value={value}
|
||||
id={id}
|
||||
|
|
|
@ -3,10 +3,20 @@ export interface SystemInfo {
|
|||
velaVersion: string;
|
||||
gitVersion: string;
|
||||
};
|
||||
createTime: string;
|
||||
installTime: string;
|
||||
enableCollection: 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 {
|
||||
|
|
|
@ -102,7 +102,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
|||
createApplicationEnv(params)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Message.success(<Translation>Bind Environment Success</Translation>);
|
||||
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||
this.props.onOK();
|
||||
}
|
||||
})
|
||||
|
@ -115,7 +115,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
|||
updateApplicationEnv(params)
|
||||
.then((res: any) => {
|
||||
if (res) {
|
||||
Message.success(<Translation>Bind Environment Success</Translation>);
|
||||
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||
this.props.onOK();
|
||||
}
|
||||
})
|
||||
|
@ -178,7 +178,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
|||
<React.Fragment>
|
||||
<Dialog
|
||||
visible={true}
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'commonDialog'}
|
||||
style={{ width: '600px' }}
|
||||
isFullScreen={true}
|
||||
|
@ -211,7 +211,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
|||
>
|
||||
<Select
|
||||
name="name"
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
disabled={isEdit ? true : false}
|
||||
dataSource={envOption}
|
||||
maxLength={32}
|
||||
|
|
|
@ -54,7 +54,7 @@ class DeployConfigDialog extends Component<Props, State> {
|
|||
<React.Fragment>
|
||||
<Dialog
|
||||
visible={true}
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'commonDialog deployConfig'}
|
||||
style={{ width: '600px' }}
|
||||
isFullScreen={true}
|
||||
|
|
|
@ -86,7 +86,7 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
onOk: () => {
|
||||
this.onDeploy(workflowName, true);
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
} else {
|
||||
handleError(err);
|
||||
|
@ -190,7 +190,7 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
</Row>
|
||||
<Row wrap={true}>
|
||||
<Col xl={12} m={12} s={24} className="padding16">
|
||||
<Card locale={locale.Card}>
|
||||
<Card locale={locale().Card}>
|
||||
<Row>
|
||||
<Col span={6} style={{ padding: '22px 0' }}>
|
||||
<NumItem
|
||||
|
@ -221,7 +221,7 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
</Col>
|
||||
<Col xl={12} m={12} s={24} className="padding16">
|
||||
<If condition={!records || (Array.isArray(records) && records.length === 0)}>
|
||||
<Card locale={locale.Card}>
|
||||
<Card locale={locale().Card}>
|
||||
<Empty
|
||||
message={<Translation>There is no running workflow</Translation>}
|
||||
iconWidth={'30px'}
|
||||
|
|
|
@ -74,7 +74,7 @@ class Menu extends Component<Props, any> {
|
|||
}
|
||||
const activeKey = currentPath.substring(currentPath.lastIndexOf('/') + 1);
|
||||
return (
|
||||
<Card locale={locale.Card} contentHeight="100px" className="app-menu">
|
||||
<Card locale={locale().Card} contentHeight="100px" className="app-menu">
|
||||
{activeItems.map((item) => {
|
||||
return (
|
||||
<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 { connect } from 'dva';
|
||||
import logo from '../../assets/kubevela-logo-white.png';
|
||||
|
||||
import axios from 'axios';
|
||||
import type { SystemInfo } from '../../interface/system';
|
||||
import type { LoginUserInfo } from '../../interface/user';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import Translation from '../../components/Translation';
|
||||
import Permission from '../../components/Permission';
|
||||
import PlatformSetting from '../../components/PlatformSetting';
|
||||
import EditPlatFormUserDialog from './components/EditPlatFormUserDialog';
|
||||
import { getBrowserNameAndVersion, isAdminUserCheck } from '../../utils/utils';
|
||||
import { getData, setData } from '../../utils/cache';
|
||||
|
||||
type Props = {
|
||||
dispatch: ({}) => {};
|
||||
|
@ -22,16 +25,22 @@ type Props = {
|
|||
|
||||
type State = {
|
||||
platformSetting: boolean;
|
||||
isEditAdminUser: boolean;
|
||||
userInfo?: LoginUserInfo;
|
||||
};
|
||||
|
||||
const TelemetryDataCollectionKey = 'telemetryDataCollection';
|
||||
const TelemetryDataCollectionServer = 'https://telemetry.kubevela.net/collecting';
|
||||
@connect((store: any) => {
|
||||
return { ...store.user };
|
||||
})
|
||||
class TopBar extends Component<Props, State> {
|
||||
loadCount: number;
|
||||
constructor(props: any) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
platformSetting: false,
|
||||
isEditAdminUser: false,
|
||||
};
|
||||
this.loadCount = 0;
|
||||
}
|
||||
|
@ -44,12 +53,65 @@ class TopBar extends Component<Props, State> {
|
|||
loadSystemInfo = () => {
|
||||
this.props.dispatch({
|
||||
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 = () => {
|
||||
this.props.dispatch({
|
||||
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 });
|
||||
};
|
||||
|
||||
isEditPlatForm = () => {
|
||||
const { userInfo } = this.state;
|
||||
const isAdminUser = isAdminUserCheck(userInfo);
|
||||
if (isAdminUser && userInfo && !userInfo.email) {
|
||||
this.setState({
|
||||
isEditAdminUser: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onCloseEditAdminUser = () => {
|
||||
this.setState({ isEditAdminUser: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { Row, Col } = Grid;
|
||||
const { userInfo, systemInfo, dispatch } = this.props;
|
||||
const { platformSetting } = this.state;
|
||||
const { systemInfo, dispatch } = this.props;
|
||||
const { platformSetting, isEditAdminUser, userInfo } = this.state;
|
||||
return (
|
||||
<div className="layout-topbar" id="layout-topbar">
|
||||
<Row className="nav-wrapper">
|
||||
|
@ -197,6 +273,9 @@ class TopBar extends Component<Props, State> {
|
|||
/>
|
||||
)}
|
||||
</If>
|
||||
<If condition={isEditAdminUser}>
|
||||
<EditPlatFormUserDialog userInfo={userInfo} onClose={this.onCloseEditAdminUser} />
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
"Auto-refresh": "自动刷新",
|
||||
"Baseline Config": "基础配置",
|
||||
"Bind Environment": "绑定环境",
|
||||
"Bind Environment Success": "绑定环境成功",
|
||||
"Environment bound successfully": "绑定环境成功",
|
||||
"Bucket": "Bucket",
|
||||
"CPU": "CPU",
|
||||
"Cancel": "取消",
|
||||
|
@ -418,7 +418,8 @@
|
|||
"Container": "容器",
|
||||
"Logs": "日志",
|
||||
"Select Pod": "查询实例",
|
||||
"Select Container": "查询容器",
|
||||
"Select Container": "选择容器",
|
||||
"Select Component": "选择组件",
|
||||
"Component Config": "组件配置",
|
||||
"New Component": "新增组件",
|
||||
"Components": "组件",
|
||||
|
@ -515,5 +516,18 @@
|
|||
"The email address of administrator is empty": "管理员的电子邮件地址为空",
|
||||
"Please set a email address for the administrator, it must same as the SSO account.": "请为管理员设置电子邮件地址, 它必须与SSO帐户相同",
|
||||
"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: {
|
||||
*getLoginUserInfo(
|
||||
action: { payload: { projectName: string } },
|
||||
action: { payload: Record<string, never>; callback: (data: LoginUserInfo) => void },
|
||||
{ call, put }: { call: any; put: any },
|
||||
) {
|
||||
const result: LoginUserInfo = yield call(getLoginUserInfo, action.payload);
|
||||
yield put({ type: 'updateUserInfo', payload: result || {} });
|
||||
if (action.callback && result) {
|
||||
action.callback(result);
|
||||
}
|
||||
},
|
||||
*getSystemInfo(
|
||||
action: { payload: { projectName: string } },
|
||||
action: { payload: Record<string, never>; callback: (data: SystemInfo) => void },
|
||||
{ call, put }: { call: any; put: any },
|
||||
) {
|
||||
const result: SystemInfo = yield call(loadSystemInfo, action.payload);
|
||||
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 (
|
||||
<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)}>
|
||||
<div className="cluster-card-top flexcenter">
|
||||
<If condition={icon && icon != 'none'}>
|
||||
|
|
|
@ -164,7 +164,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
content:
|
||||
'Please make sure that the Addon is no longer in use and the related application has been recycled.',
|
||||
onOk: this.disableAddon,
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
</If>
|
||||
<If condition={addonDetailInfo?.dependencies}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Dependencies</Translation>}
|
||||
>
|
||||
|
@ -479,7 +479,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
<If condition={addonDetailInfo?.definitions}>
|
||||
<Card
|
||||
contentHeight="auto"
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
title={<Translation>Definitions</Translation>}
|
||||
style={{ marginTop: '16px' }}
|
||||
>
|
||||
|
@ -488,7 +488,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
Enable the addon to obtain the following extension capabilities
|
||||
</Translation>
|
||||
</Message>
|
||||
<Table locale={locale.Table} dataSource={addonDetailInfo?.definitions}>
|
||||
<Table locale={locale().Table} dataSource={addonDetailInfo?.definitions}>
|
||||
<Table.Column
|
||||
dataIndex="name"
|
||||
align="left"
|
||||
|
@ -514,7 +514,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
</If>
|
||||
<Card
|
||||
contentHeight="auto"
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
title={<Translation>Readme</Translation>}
|
||||
style={{ marginTop: '16px' }}
|
||||
>
|
||||
|
|
|
@ -159,7 +159,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
|||
onOk: () => {
|
||||
this.onDeleteRegistry(name);
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
@ -211,7 +211,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
|||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className="commonDialog"
|
||||
title={<Translation>Registry Management</Translation>}
|
||||
autoFocus={true}
|
||||
|
@ -247,7 +247,7 @@ class RegistryManageDialog extends React.Component<Props, State> {
|
|||
</div>
|
||||
</Col>
|
||||
</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="80px" title={<Translation>Type</Translation>} dataIndex="type" />
|
||||
<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>}
|
||||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
{...init('type', {
|
||||
rules: [
|
||||
{
|
||||
|
|
|
@ -59,7 +59,7 @@ class SelectSearch extends React.Component<Props, State> {
|
|||
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
onChange={this.handleChangRegistry}
|
||||
|
|
|
@ -153,8 +153,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
if (res) {
|
||||
Message.success({
|
||||
duration: 4000,
|
||||
title: i18n.t('Success'),
|
||||
content: i18n.t('Create component success.'),
|
||||
content: i18n.t('Component created successfully'),
|
||||
});
|
||||
this.props.onComponentOK();
|
||||
}
|
||||
|
@ -373,7 +372,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
}
|
||||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
showSearch
|
||||
disabled={isEditComponent ? true : false}
|
||||
className="select"
|
||||
|
|
|
@ -31,7 +31,7 @@ class ComponentsList extends Component<Props> {
|
|||
this.props.onDeleteComponent(name || '');
|
||||
},
|
||||
onClose: () => {},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ class ComponentsList extends Component<Props> {
|
|||
<Row wrap={true}>
|
||||
{(components || []).map((item: ApplicationComponentBase) => (
|
||||
<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">
|
||||
<Permission
|
||||
request={{
|
||||
|
|
|
@ -34,7 +34,7 @@ class TraitsList extends Component<Props> {
|
|||
this.props.onDeleteTrait(traitType || '');
|
||||
},
|
||||
onClose: () => {},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ class TraitsList extends Component<Props> {
|
|||
const { changeTraitStats } = this.props;
|
||||
return (
|
||||
<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-title"
|
||||
|
@ -75,7 +75,7 @@ class TraitsList extends Component<Props> {
|
|||
const { Col } = Grid;
|
||||
return (
|
||||
<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">
|
||||
<Icon
|
||||
type="plus-circle"
|
||||
|
|
|
@ -245,7 +245,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
|||
<FormItem label={<Translation>Type</Translation>} required>
|
||||
<Select
|
||||
name="type"
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
dataSource={[{ label: 'On Webhook Event', value: 'webhook' }]}
|
||||
{...init('type', {
|
||||
initValue: 'webhook',
|
||||
|
@ -266,7 +266,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
|||
<FormItem label={<Translation>PayloadType</Translation>} required>
|
||||
<Select
|
||||
name="payloadType"
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
dataSource={payloadTypeOption}
|
||||
{...init('payloadType', {
|
||||
initValue: 'custom',
|
||||
|
@ -288,7 +288,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
|||
<FormItem label={<Translation>Execution workflow</Translation>} required>
|
||||
<Select
|
||||
name="workflowName"
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
dataSource={workflowOption}
|
||||
{...init('workflowName', {
|
||||
rules: [
|
||||
|
|
|
@ -52,7 +52,7 @@ class TriggerList extends Component<Props, State> {
|
|||
this.props.onDeleteTrigger(token || '');
|
||||
},
|
||||
onClose: () => {},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -108,7 +108,7 @@ class TriggerList extends Component<Props, State> {
|
|||
<Row wrap={true}>
|
||||
{(triggers || []).map((item: Trigger) => (
|
||||
<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-title">
|
||||
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
||||
|
@ -198,7 +198,7 @@ class TriggerList extends Component<Props, State> {
|
|||
</Row>
|
||||
<If condition={showTrigger}>
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className="commonDialog"
|
||||
visible={true}
|
||||
onClose={this.closeWebhook}
|
||||
|
|
|
@ -393,7 +393,7 @@ class ApplicationConfig extends Component<Props, State> {
|
|||
</Row>
|
||||
<Row>
|
||||
<Col span={24} className="padding16">
|
||||
<Card locale={locale.Card} contentHeight="auto">
|
||||
<Card locale={locale().Card} contentHeight="auto">
|
||||
<Row wrap={true}>
|
||||
<Col m={12} xs={24}>
|
||||
<Item
|
||||
|
|
|
@ -116,7 +116,7 @@ class ContainerLog extends Component<Props, State> {
|
|||
return (
|
||||
<Dialog
|
||||
className="commonDialog logDialog"
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
visible={true}
|
||||
footerActions={[]}
|
||||
onClose={this.props.onClose}
|
||||
|
|
|
@ -92,40 +92,40 @@ class Header extends Component<Props, State> {
|
|||
};
|
||||
recycleEnv = async () => {
|
||||
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: () => {
|
||||
const { applicationDetail, envName, refresh } = this.props;
|
||||
if (applicationDetail) {
|
||||
recycleApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
||||
(re) => {
|
||||
if (re) {
|
||||
Message.success('recycle applicationn environment success');
|
||||
Message.success(i18n.t('Recycle application environment success'));
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
deleteEnv = async () => {
|
||||
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: () => {
|
||||
const { applicationDetail, envName, updateEnvs } = this.props;
|
||||
if (applicationDetail) {
|
||||
deleteApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
||||
(re) => {
|
||||
if (re) {
|
||||
Message.success('delete applicationn environment binding success');
|
||||
Message.success(i18n.t('Environment binding deleted successfully'));
|
||||
updateEnvs();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -173,7 +173,7 @@ class Header extends Component<Props, State> {
|
|||
<Row className="border-radius-8">
|
||||
<Col span="4" style={{ marginBottom: '16px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
onChange={this.handleTargetChange}
|
||||
dataSource={targetOptions}
|
||||
|
@ -184,7 +184,7 @@ class Header extends Component<Props, State> {
|
|||
</Col>
|
||||
<Col span="4" style={{ marginBottom: '16px', paddingLeft: '16px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
onChange={this.handleComponentChange}
|
||||
dataSource={componentOptions}
|
||||
|
|
|
@ -288,7 +288,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
hasBorder={false}
|
||||
primaryKey="name"
|
||||
loading={loading}
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
>
|
||||
{containerColumns &&
|
||||
containerColumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||
|
@ -300,7 +300,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
hasBorder={false}
|
||||
loading={loading}
|
||||
primaryKey="time"
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
>
|
||||
{eventCloumns &&
|
||||
eventCloumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||
|
|
|
@ -109,12 +109,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
if (re) {
|
||||
const status: ApplicationStatus = re.status;
|
||||
if (status && status.appliedResources) {
|
||||
const services = status.appliedResources.filter(
|
||||
(resource) => resource.kind == 'Service',
|
||||
);
|
||||
if (services) {
|
||||
this.loadApplicationEndpoints();
|
||||
}
|
||||
this.loadApplicationEndpoints();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -220,19 +215,13 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
|
||||
loadAppInstances = async () => {
|
||||
this.setState({ podList: [] });
|
||||
const { applicationDetail, envbinding, applicationStatus } = this.props;
|
||||
const { applicationDetail, envbinding } = this.props;
|
||||
const {
|
||||
params: { appName, envName },
|
||||
} = this.props.match;
|
||||
const { target, componentName } = this.state;
|
||||
const envs = envbinding.filter((item) => item.name == envName);
|
||||
if (
|
||||
applicationDetail &&
|
||||
applicationDetail.name &&
|
||||
envs.length > 0 &&
|
||||
applicationStatus &&
|
||||
applicationStatus.services?.length
|
||||
) {
|
||||
if (applicationDetail && applicationDetail.name && envs.length > 0) {
|
||||
if (applicationDetail.applicationType == 'common') {
|
||||
const param = {
|
||||
appName: envs[0].appDeployName || appName,
|
||||
|
@ -452,7 +441,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
onCancel: () => {
|
||||
this.setState({ deployLoading: false });
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
} else {
|
||||
handleError(err);
|
||||
|
@ -541,7 +530,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
<If condition={applicationStatus}>
|
||||
<If condition={applicationDetail?.applicationType == 'common'}>
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
className="podlist-table-wraper"
|
||||
size="medium"
|
||||
primaryKey={'primaryKey'}
|
||||
|
@ -560,7 +549,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
<If condition={applicationDetail?.applicationType == 'cloud'}>
|
||||
<Table
|
||||
size="medium"
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
dataSource={cloudInstance}
|
||||
primaryKey={'instanceName'}
|
||||
|
|
|
@ -390,7 +390,7 @@ class AppDialog extends React.Component<Props, State> {
|
|||
}
|
||||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
showSearch
|
||||
className="select"
|
||||
{...init(`componentType`, {
|
||||
|
@ -435,7 +435,7 @@ class AppDialog extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="multiple"
|
||||
dataSource={envOptions}
|
||||
/>
|
||||
|
|
|
@ -92,7 +92,7 @@ class CardContent extends React.Component<Props, State> {
|
|||
onOk: () => {
|
||||
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`}
|
||||
key={`${item.name}`}
|
||||
>
|
||||
<Card locale={locale.Card} contentHeight="auto">
|
||||
<Card locale={locale().Card} contentHeight="auto">
|
||||
<Link to={`/applications/${name}/config`}>
|
||||
<div className="appplan-card-top flexcenter">
|
||||
<If condition={icon && icon != 'none'}>
|
||||
|
|
|
@ -75,7 +75,7 @@ class ProjectForm extends React.Component<Props, State> {
|
|||
<div className="cluster-container">
|
||||
<Select
|
||||
disabled={disable}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="cluster-params-input"
|
||||
mode="single"
|
||||
dataSource={projectList}
|
||||
|
|
|
@ -114,7 +114,7 @@ class SelectSearch extends React.Component<Props, State> {
|
|||
<Row className="app-select-wrapper border-radius-8" wrap={true}>
|
||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
onChange={this.onChangeProject}
|
||||
|
@ -127,7 +127,7 @@ class SelectSearch extends React.Component<Props, State> {
|
|||
</Col>
|
||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
onChange={this.onChangeEnv}
|
||||
|
|
|
@ -119,6 +119,8 @@ class ContainerLog extends Component<Props, State> {
|
|||
.finally(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
} else {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -61,9 +61,9 @@ class ApplicationLog extends React.Component<Props, State> {
|
|||
this.loadApplicationStatus();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: any) {
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
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.loadApplicationStatus();
|
||||
});
|
||||
|
@ -84,15 +84,14 @@ class ApplicationLog extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
loadPodInstance = async () => {
|
||||
const { applicationDetail, envbinding, applicationStatus } = this.props;
|
||||
const { appName, envName } = this.state;
|
||||
const { envbinding } = this.props;
|
||||
const { appName, envName, activeComponentName } = this.state;
|
||||
const envs = envbinding.filter((item) => item.name === envName);
|
||||
if (applicationDetail?.name && applicationStatus?.services?.length && envs.length > 0) {
|
||||
const componentName = applicationStatus.services[0].name;
|
||||
if (envs.length > 0 && envs[0]) {
|
||||
const param = {
|
||||
appName: envs[0].appDeployName || appName,
|
||||
appNs: envs[0].appDeployNamespace,
|
||||
name: componentName,
|
||||
componentName: activeComponentName,
|
||||
cluster: '',
|
||||
clusterNs: '',
|
||||
};
|
||||
|
@ -102,12 +101,10 @@ class ApplicationLog extends React.Component<Props, State> {
|
|||
this.setState(
|
||||
{
|
||||
podList: res.podList,
|
||||
pod: res.podList[0] || {},
|
||||
activePodName: res.podList[0]?.metadata.name,
|
||||
activeComponentName: res.podList[0]?.component,
|
||||
activePodName: '',
|
||||
},
|
||||
() => {
|
||||
this.loadPodDetail();
|
||||
this.handlePodNameChange(res.podList[0].metadata.name);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
@ -133,18 +130,26 @@ class ApplicationLog extends React.Component<Props, State> {
|
|||
Message.warning(res.error);
|
||||
} else if (res) {
|
||||
const activeContainerName = (res.containers?.[0] && res.containers[0]?.name) || '';
|
||||
this.setState({
|
||||
containers: res.containers,
|
||||
activeContainerName,
|
||||
isActiveContainerNameDisabled: activeContainerName ? false : true,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
containers: res.containers,
|
||||
},
|
||||
() => {
|
||||
this.handleContainerNameChange(activeContainerName);
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
};
|
||||
handleComponentNameChange = (value: any) => {
|
||||
this.setState({ activeComponentName: value });
|
||||
handleComponentNameChange = (value: string) => {
|
||||
this.setState(
|
||||
{ activeComponentName: value, activePodName: '', activeContainerName: '' },
|
||||
() => {
|
||||
this.loadPodInstance();
|
||||
},
|
||||
);
|
||||
};
|
||||
handlePodNameChange = (value: any) => {
|
||||
const { podList } = this.state;
|
||||
|
@ -167,26 +172,10 @@ class ApplicationLog extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
getComponentNameList = () => {
|
||||
const { podList } = this.state;
|
||||
const { components } = this.props;
|
||||
const componentNameAlias: any = {};
|
||||
components?.map((c) => {
|
||||
componentNameAlias[c.name] = c.alias || c.name;
|
||||
return components?.map((c) => {
|
||||
return { label: c.alias || c.name, value: 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 = () => {
|
||||
|
|
|
@ -59,7 +59,7 @@ class Hearder extends React.Component<Props, State> {
|
|||
<Row className="border-radius-8">
|
||||
<Col span="6" style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="small"
|
||||
onChange={this.handleChangeEnv}
|
||||
|
@ -73,7 +73,7 @@ class Hearder extends React.Component<Props, State> {
|
|||
|
||||
<Col span="6" style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="small"
|
||||
onChange={this.handleChangeStatus}
|
||||
|
|
|
@ -123,7 +123,7 @@ class TableList extends Component<Props, State> {
|
|||
return (
|
||||
<div className="table-version-list margin-top-20">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
primaryKey={'version'}
|
||||
className="customTable"
|
||||
rowHeight={40}
|
||||
|
|
|
@ -125,7 +125,7 @@ class ApplicationRevisionList extends React.Component<Props, State> {
|
|||
getRevisionList={this.getRevisionList}
|
||||
/>
|
||||
<Pagination
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
className="revison-pagenation"
|
||||
hideOnlyOnePage={true}
|
||||
total={this.state.revisionsListTotal}
|
||||
|
|
|
@ -251,7 +251,7 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
onCancel: () => {
|
||||
this.setState({ deployLoading: false });
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
} else {
|
||||
handleError(err);
|
||||
|
@ -304,11 +304,11 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
<If condition={applicationStatus}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
contentHeight="200px"
|
||||
title={<Translation>Applied Resources</Translation>}
|
||||
>
|
||||
<Table locale={locale.Table} dataSource={resources}>
|
||||
<Table locale={locale().Table} dataSource={resources}>
|
||||
<Table.Column
|
||||
dataIndex="cluster"
|
||||
title={<Translation>Cluster</Translation>}
|
||||
|
@ -325,9 +325,9 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
userInfo,
|
||||
)
|
||||
) {
|
||||
return <Link to="/clusters">{v}</Link>;
|
||||
return <Link to="/clusters">{clusterName}</Link>;
|
||||
}
|
||||
return <span>{v}</span>;
|
||||
return <span>{clusterName}</span>;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
|
@ -355,7 +355,11 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
if (row.latest) {
|
||||
return (
|
||||
<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>
|
||||
</span>
|
||||
);
|
||||
|
@ -369,12 +373,12 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
</Card>
|
||||
<If condition={componentStatus}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Component Status</Translation>}
|
||||
>
|
||||
<Table locale={locale.Table} className="customTable" dataSource={componentStatus}>
|
||||
<Table locale={locale().Table} className="customTable" dataSource={componentStatus}>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="name"
|
||||
|
@ -413,12 +417,12 @@ class ApplicationMonitor extends React.Component<Props, State> {
|
|||
</If>
|
||||
<If condition={applicationStatus?.conditions}>
|
||||
<Card
|
||||
locale={locale.Card}
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Conditions</Translation>}
|
||||
>
|
||||
<Table locale={locale.Table} dataSource={applicationStatus?.conditions}>
|
||||
<Table locale={locale().Table} dataSource={applicationStatus?.conditions}>
|
||||
<Table.Column
|
||||
width="150px"
|
||||
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' }}>
|
||||
<FormItem label={<Translation>Workflow Type</Translation>} required disabled={edit}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="select"
|
||||
placeholder={t('Please select').toString()}
|
||||
{...init(`type`, {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { loginSSO } from '../../api/authentication';
|
||||
import querystring from 'query-string';
|
||||
import { Dialog, Button } from '@b-design/ui';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
type Props = {
|
||||
location: {
|
||||
|
@ -38,13 +40,29 @@ export default class CallBackPage extends React.Component<Props> {
|
|||
this.props.history.push('/');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setTimeout(() => {
|
||||
this.props.history.push('/login');
|
||||
}, 3000);
|
||||
.catch((err) => {
|
||||
let customErrorMessage = '';
|
||||
if (err.BusinessCode) {
|
||||
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() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ class AddClustDialog extends React.Component<Props, State> {
|
|||
const valueInfo = cluster.kubeConfig || values.kubeConfig || '';
|
||||
return (
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'commonDialog'}
|
||||
title={
|
||||
editMode ? (
|
||||
|
|
|
@ -74,7 +74,7 @@ class CardContent extends React.Component<Props, State> {
|
|||
onOk: () => {
|
||||
this.onDeleteCluster(item.name);
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -271,7 +271,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className="dialog-cluoudService-wraper"
|
||||
title={<Translation>Connect Kubernetes Cluster From Cloud</Translation>}
|
||||
autoFocus={true}
|
||||
|
@ -298,7 +298,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
<Form {...formItemLayout} field={this.field} className="cloud-server-wraper">
|
||||
<FormItem label={<Translation>Provider</Translation>} required={true}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
dataSource={providerList}
|
||||
|
@ -348,7 +348,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
|
||||
<If condition={!choseInput}>
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
dataSource={cloudClusters}
|
||||
hasBorder={false}
|
||||
loading={tableLoading}
|
||||
|
@ -356,7 +356,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
{columns && columns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||
</Table>
|
||||
<Pagination
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
hideOnlyOnePage={true}
|
||||
total={total}
|
||||
size="small"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 type { Target } from '../../../../interface/target';
|
||||
import Translation from '../../../../components/Translation';
|
||||
|
@ -25,6 +25,7 @@ type State = {
|
|||
targets?: Target[];
|
||||
namespaces?: { label: string; value: string }[];
|
||||
targetLoading: boolean;
|
||||
submitLoading: boolean;
|
||||
};
|
||||
|
||||
class EnvDialog extends React.Component<Props, State> {
|
||||
|
@ -41,6 +42,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
});
|
||||
this.state = {
|
||||
targetLoading: false,
|
||||
submitLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
if (error) {
|
||||
return;
|
||||
}
|
||||
this.setState({ submitLoading: true });
|
||||
const { isEdit } = this.props;
|
||||
const { name, alias, description, targets, namespace, project } = values;
|
||||
const params = {
|
||||
|
@ -97,6 +100,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
|
||||
if (isEdit) {
|
||||
updateEnv(params).then((res) => {
|
||||
this.setState({ submitLoading: false });
|
||||
if (res) {
|
||||
Message.success(<Translation>Environment updated successfully</Translation>);
|
||||
this.props.onOK();
|
||||
|
@ -105,6 +109,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
});
|
||||
} else {
|
||||
createEnv(params).then((res) => {
|
||||
this.setState({ submitLoading: false });
|
||||
if (res) {
|
||||
Message.success(<Translation>Environment created successfully</Translation>);
|
||||
this.props.onOK();
|
||||
|
@ -161,7 +166,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
const { visible, isEdit, projects } = this.props;
|
||||
const { targetLoading } = this.state;
|
||||
const { targetLoading, submitLoading } = this.state;
|
||||
const projectList = (projects || []).map((project) => {
|
||||
return {
|
||||
label: project.alias ? `${project.alias}(${project.name})` : project.name,
|
||||
|
@ -171,7 +176,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'commonDialog'}
|
||||
height="auto"
|
||||
title={
|
||||
|
@ -188,6 +193,13 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
onCancel={this.onClose}
|
||||
onClose={this.onClose}
|
||||
footerActions={['cancel', 'ok']}
|
||||
footer={
|
||||
<div>
|
||||
<Button onClick={this.onOk} type="primary" loading={submitLoading}>
|
||||
<Translation>Confirm</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
footerAlign="center"
|
||||
>
|
||||
<Form {...formItemLayout} field={this.field}>
|
||||
|
@ -297,7 +309,7 @@ class EnvDialog extends React.Component<Props, State> {
|
|||
<Loading visible={targetLoading} style={{ width: '100%' }}>
|
||||
<FormItem label={<Translation>Target</Translation>} required>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="select"
|
||||
mode="multiple"
|
||||
placeholder={i18n.t('Please select a target').toString()}
|
||||
|
|
|
@ -138,7 +138,7 @@ class TableList extends Component<Props> {
|
|||
onOk: () => {
|
||||
this.onDelete(record);
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
@ -173,7 +173,7 @@ class TableList extends Component<Props> {
|
|||
return (
|
||||
<div className="table-delivery-list margin-top-20">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
size="medium"
|
||||
dataSource={list}
|
||||
|
|
|
@ -78,7 +78,7 @@ class Namespace extends React.Component<Props, State> {
|
|||
<If condition={!showNameSpaceInput}>
|
||||
<div className="cluster-container">
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="cluster-params-input"
|
||||
mode="single"
|
||||
dataSource={namespaces}
|
||||
|
|
|
@ -136,7 +136,7 @@ class targetList extends React.Component<Props, State> {
|
|||
<Pagination
|
||||
className="delivery-target-pagenation"
|
||||
total={envTotal}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
size="medium"
|
||||
pageSize={this.state.pageSize}
|
||||
current={this.state.page}
|
||||
|
|
|
@ -249,7 +249,7 @@ class CreateIntegration extends React.Component<Props, State> {
|
|||
required={true}
|
||||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
showSearch
|
||||
className="select"
|
||||
placeholder={i18n.t('Please select').toString()}
|
||||
|
|
|
@ -74,6 +74,10 @@ class Integrations extends Component<Props, State> {
|
|||
this.setState({
|
||||
list: res,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
list: [],
|
||||
});
|
||||
}
|
||||
})
|
||||
.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>
|
||||
</Permission>
|
||||
</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'} />)}
|
||||
</Table>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
height: 100vh;
|
||||
height: calc(100vh - 48px);
|
||||
background: #f7f7f7;
|
||||
.login-wrapper {
|
||||
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 { 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 Translation from '../../components/Translation';
|
||||
import { checkName, checkUserPassword } from '../../utils/common';
|
||||
import SwitchLanguage from '../../components/SwitchButton/index';
|
||||
import Logo from '../../assets/kubevela-logo.png';
|
||||
import LogoWhite from '../../assets/kubevela-logo-white.png';
|
||||
|
||||
import i18n from '../../i18n';
|
||||
import './index.less';
|
||||
|
||||
|
@ -130,80 +133,102 @@ export default class LoginPage extends Component<Props, State> {
|
|||
},
|
||||
};
|
||||
const { loginType, loginErrorMessage } = this.state;
|
||||
const { Row, Col } = Grid;
|
||||
return (
|
||||
<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>
|
||||
<Fragment>
|
||||
<div className="login-topbar">
|
||||
<Row className="nav-wrapper">
|
||||
<Col span="4" className="logo">
|
||||
<img src={LogoWhite} title={'Make shipping applications more enjoyable.'} />
|
||||
</Col>
|
||||
<div style={{ flex: '1 1 0%' }} />
|
||||
<div className="right">
|
||||
<div className="vela-item">
|
||||
<a title="KubeVela Documents" href="https://kubevela.io" target="_blank">
|
||||
<Icon size={14} type="help1" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="vela-item">
|
||||
<SwitchLanguage />
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
</Row>
|
||||
</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}>
|
||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
onChange={this.onChangeEnv}
|
||||
|
@ -124,7 +124,7 @@ class SelectSearch extends React.Component<Props, State> {
|
|||
</Col>
|
||||
<Col xl={6} m={8} s={12} xxs={24} style={{ padding: '0 8px' }}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="single"
|
||||
size="large"
|
||||
onChange={this.onChangeTarget}
|
||||
|
|
|
@ -181,7 +181,7 @@ class MemberDialog extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="tag"
|
||||
dataSource={rolesList}
|
||||
/>
|
||||
|
|
|
@ -133,7 +133,7 @@ class ProjectMembers extends Component<Props, State> {
|
|||
.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">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
dataSource={memberList}
|
||||
hasBorder={false}
|
||||
loading={isLoading}
|
||||
|
@ -294,7 +294,7 @@ class ProjectMembers extends Component<Props, State> {
|
|||
<Pagination
|
||||
className="margin-top-20 text-align-right"
|
||||
total={total}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
hideOnlyOnePage={true}
|
||||
size="medium"
|
||||
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 (
|
||||
<Fragment>
|
||||
<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">
|
||||
<span className="card-title">
|
||||
<Translation>General</Translation>
|
||||
|
|
|
@ -122,7 +122,7 @@ class Integrations extends Component<Props, State> {
|
|||
</section>
|
||||
<section className="card-content-table">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
dataSource={configList}
|
||||
hasBorder={true}
|
||||
loading={isLoading}
|
||||
|
|
|
@ -104,7 +104,7 @@ class Targets extends Component<Props, State> {
|
|||
</Permission>
|
||||
</section>
|
||||
<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'} />)}
|
||||
</Table>
|
||||
</section>
|
||||
|
|
|
@ -104,7 +104,7 @@ class Projects extends Component<Props, State> {
|
|||
.catch();
|
||||
}
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -253,14 +253,14 @@ class Projects extends Component<Props, State> {
|
|||
</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'} />)}
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
className="margin-top-20 text-align-right"
|
||||
total={total}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
size="medium"
|
||||
pageSize={pageSize}
|
||||
current={page}
|
||||
|
|
|
@ -197,7 +197,7 @@ class RolesDialog extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="tag"
|
||||
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>,
|
||||
]}
|
||||
/>
|
||||
<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'} />)}
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
className="margin-top-20 text-align-right"
|
||||
total={total}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
hideOnlyOnePage={true}
|
||||
size="medium"
|
||||
pageSize={pageSize}
|
||||
|
|
|
@ -103,7 +103,7 @@ class TableList extends Component<Props> {
|
|||
onOk: () => {
|
||||
this.onDelete(record);
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
@ -137,7 +137,7 @@ class TableList extends Component<Props> {
|
|||
return (
|
||||
<div className="table-delivery-list margin-top-20">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
size="medium"
|
||||
dataSource={list}
|
||||
|
|
|
@ -100,7 +100,7 @@ class Namespace extends React.Component<Props, State> {
|
|||
<div>
|
||||
<div className="cluster-container">
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="cluster-params-input"
|
||||
mode="single"
|
||||
disabled={disabled}
|
||||
|
@ -122,7 +122,7 @@ class Namespace extends React.Component<Props, State> {
|
|||
</div>
|
||||
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'namespaceDialogWraper'}
|
||||
title={<Translation>Create Namespace</Translation>}
|
||||
autoFocus={true}
|
||||
|
|
|
@ -241,7 +241,7 @@ class DeliveryDialog extends React.Component<Props, State> {
|
|||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
locale={locale().Dialog}
|
||||
className={'commonDialog'}
|
||||
height="auto"
|
||||
title={
|
||||
|
@ -346,7 +346,7 @@ class DeliveryDialog extends React.Component<Props, State> {
|
|||
<Col span={12} style={{ padding: '0 8px' }}>
|
||||
<FormItem label={<Translation>Cluster</Translation>} required>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
className="select"
|
||||
disabled={isEdit}
|
||||
placeholder={t('Please select').toString()}
|
||||
|
|
|
@ -147,7 +147,7 @@ class targetList extends React.Component<Props, State> {
|
|||
<Pagination
|
||||
className="delivery-target-pagenation"
|
||||
total={total}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
size="medium"
|
||||
pageSize={this.state.pageSize}
|
||||
current={this.state.page}
|
||||
|
|
|
@ -253,7 +253,7 @@ class CreateUser extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
locale={locale.Select}
|
||||
locale={locale().Select}
|
||||
mode="multiple"
|
||||
dataSource={rolesListSelect}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Grid, Icon, Input } from '@b-design/ui';
|
||||
import i18n from '../../../../i18n';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import './index.less';
|
||||
|
||||
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();
|
||||
}
|
||||
},
|
||||
locale: locale.Dialog,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -445,7 +445,7 @@ class Users extends Component<Props, State> {
|
|||
|
||||
<section className="margin-top-20 user-list-wrapper">
|
||||
<Table
|
||||
locale={locale.Table}
|
||||
locale={locale().Table}
|
||||
dataSource={dataSource}
|
||||
hasBorder={false}
|
||||
loading={isLoading}
|
||||
|
@ -456,7 +456,7 @@ class Users extends Component<Props, State> {
|
|||
<Pagination
|
||||
className="margin-top-20 text-align-right"
|
||||
total={total}
|
||||
locale={locale.Pagination}
|
||||
locale={locale().Pagination}
|
||||
hideOnlyOnePage={true}
|
||||
size="medium"
|
||||
pageSize={pageSize}
|
||||
|
|
|
@ -18,7 +18,7 @@ export function isMock() {
|
|||
export function getDomain(): { MOCK: string | undefined; APIBASE: string | undefined } {
|
||||
const { MOCK, BASE_DOMAIN } = process.env;
|
||||
return {
|
||||
MOCK: MOCK,
|
||||
MOCK: MOCK || '',
|
||||
APIBASE: BASE_DOMAIN || '',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,83 +1,189 @@
|
|||
export default {
|
||||
Timeline: {
|
||||
expand: 'Expand',
|
||||
fold: 'Fold',
|
||||
},
|
||||
Balloon: {
|
||||
close: 'Close',
|
||||
},
|
||||
Card: {
|
||||
expand: 'Expand',
|
||||
fold: 'Fold',
|
||||
},
|
||||
Dialog: {
|
||||
close: 'Close',
|
||||
ok: 'Confirm',
|
||||
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: {
|
||||
import { getLanguage } from '../utils/common';
|
||||
const localeData = {
|
||||
en: {
|
||||
Timeline: {
|
||||
expand: 'Expand',
|
||||
fold: 'Fold',
|
||||
},
|
||||
Balloon: {
|
||||
close: 'Close',
|
||||
},
|
||||
Card: {
|
||||
expand: 'Expand',
|
||||
fold: 'Fold',
|
||||
},
|
||||
Dialog: {
|
||||
close: 'Close',
|
||||
ok: 'Confirm',
|
||||
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',
|
||||
},
|
||||
upload: {
|
||||
delete: 'Delete',
|
||||
Switch: {
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
},
|
||||
Tab: {
|
||||
closeAriaLabel: 'Close',
|
||||
},
|
||||
},
|
||||
Search: {
|
||||
buttonText: 'Search',
|
||||
},
|
||||
Tag: {
|
||||
delete: 'Delete',
|
||||
},
|
||||
Switch: {
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
},
|
||||
Tab: {
|
||||
closeAriaLabel: 'Close',
|
||||
zh: {
|
||||
Timeline: {
|
||||
expand: '展开',
|
||||
fold: '收起',
|
||||
},
|
||||
Balloon: {
|
||||
close: '关闭',
|
||||
},
|
||||
Card: {
|
||||
expand: '展开',
|
||||
fold: '收起',
|
||||
},
|
||||
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 { ComponentDefinitionsBase } from '../interface/application';
|
||||
import type { LoginUserInfo } from '../interface/user';
|
||||
import _ from 'lodash';
|
||||
|
||||
type SelectGroupType = {
|
||||
label: string;
|
||||
|
@ -142,3 +144,60 @@ export function getSelectLabel(
|
|||
export function getMatchParamObj(match: { params: any }, name: string) {
|
||||
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