+ );
+};
+
+export default FlatBtnGroup;
+
diff --git a/ui/src/components/flatbtn/flat-btn.tsx b/ui/src/components/flatbtn/flat-btn.tsx
new file mode 100644
index 0000000000..ff16ee6f43
--- /dev/null
+++ b/ui/src/components/flatbtn/flat-btn.tsx
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {HTMLAttributes} from 'react';
+import classNames from 'classnames';
+import {Link} from 'react-router-dom';
+import './style.less';
+
+interface FlatBtnProps extends HTMLAttributes {
+ to?: string;
+ type?: '' | 'danger' | 'warn';
+ disabled?: boolean;
+ children?: string | JSX.Element;
+ className?: string;
+ default?: string;
+ key?: string | number;
+ href?: string;
+ [attr: string]: any;
+}
+
+const FlatBtn = (props: FlatBtnProps) => {
+ if (props.to) {
+ return (
+
+ {props.children}
+
+ );
+ }
+ return (
+
+ {props.children}
+
+ );
+};
+
+export default FlatBtn;
+
diff --git a/ui/src/components/flatbtn/index.tsx b/ui/src/components/flatbtn/index.tsx
new file mode 100644
index 0000000000..08b7df3423
--- /dev/null
+++ b/ui/src/components/flatbtn/index.tsx
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import FlatBtn from './flat-btn';
+import FlatBtnGroup from './flat-btn-group';
+
+export { FlatBtn };
+export { FlatBtnGroup };
+
diff --git a/ui/src/components/flatbtn/style.less b/ui/src/components/flatbtn/style.less
new file mode 100644
index 0000000000..5c894ecd34
--- /dev/null
+++ b/ui/src/components/flatbtn/style.less
@@ -0,0 +1,59 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+.flat-btn {
+ display: inline-block;
+ text-decoration: none !important;
+
+ border: none;
+ background: none;
+ text-shadow: none;
+ box-shadow: none;
+ cursor: pointer;
+ outline: none;
+ padding-left: 7px;
+ padding-right: 7px;
+ border-right: 1px dashed #c4c4c4;
+ // &.flat-btn--disabled {
+ // cursor: inherit !important;
+ // color: gray !important;
+ // }
+ &.flat-btn--default {
+ color: #414D5F;
+ cursor: not-allowed;
+ }
+ &:first-child {
+ padding-left: 0;
+ }
+ &:last-child {
+ border-right: 0;
+ }
+ &.btn-danger {
+ color: red;
+ &:hover {
+ background-color: inherit !important;
+ }
+ }
+ &.btn-warning {
+ color: #f0b64b;
+ }
+ .glyphicon-triangle-bottom {
+ color: #666;
+ margin-left: 5px;
+ font-size: 12px;
+ }
+}
diff --git a/ui/src/components/iconfont/index.tsx b/ui/src/components/iconfont/index.tsx
new file mode 100644
index 0000000000..5b02bd1769
--- /dev/null
+++ b/ui/src/components/iconfont/index.tsx
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as React from 'react';
+import { HTMLAttributes } from 'react';
+
+interface IconProps extends HTMLAttributes {
+ type: string;
+}
+
+export const IconFont = (props: IconProps) => {
+ const { className = '', type } = props;
+ return ;
+};
+
+interface SvgIconProps {
+ className?: string;
+ name: string;
+}
+
+export const SvgIcon = (props: SvgIconProps) => {
+ const { className = '', name } = props;
+ return (
+
+ );
+};
+
diff --git a/ui/src/components/loadingwrapper/loadingwrapper.tsx b/ui/src/components/loadingwrapper/loadingwrapper.tsx
new file mode 100644
index 0000000000..16ea14547f
--- /dev/null
+++ b/ui/src/components/loadingwrapper/loadingwrapper.tsx
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { PropsWithChildren } from 'react';
+import { SpinProps } from 'antd/lib/spin';
+import { Spin } from 'antd';
+import { TABLE_DELAY } from 'Constants';
+
+type SpinWithoutSpinningProp = Omit;
+
+interface LoadingWrapperProps extends SpinWithoutSpinningProp {
+ loading?: boolean;
+}
+
+export function LoadingWrapper(props: PropsWithChildren) {
+ let loading = false;
+ if (props.loading) {
+ loading = true;
+ }
+ const spinProps = { ...props };
+ delete spinProps.loading;
+ return (
+
+ {props.children}
+
+ );
+}
+
diff --git a/ui/src/components/table/index.less b/ui/src/components/table/index.less
new file mode 100644
index 0000000000..a417c7d0a2
--- /dev/null
+++ b/ui/src/components/table/index.less
@@ -0,0 +1,35 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+:global(.systemTable){
+ position: relative;
+ display: block;
+ :global(.search){
+ color: #1890ff;
+ position: absolute;
+ top: 12px;
+ left: 98%;
+ z-index: 2;
+ }
+ :global(.searchSystem){
+ top: -33px;
+ right: 91px;
+ z-index: 2;
+ color: #1890ff;
+ position: absolute;
+ }
+}
diff --git a/ui/src/components/table/index.tsx b/ui/src/components/table/index.tsx
new file mode 100644
index 0000000000..cdccdb435e
--- /dev/null
+++ b/ui/src/components/table/index.tsx
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState,useEffect} from 'react';
+import {Table, Popover, Input} from 'antd';
+import {FilterFilled} from '@ant-design/icons';
+import {getColumns, filterTableData} from './table.utils.tsx';
+import './index.less';
+
+export default function SortFilterTable(props: any) {
+ const {isFilter=false, isSort=false, allTableData, isInner, isSystem=false} = props;
+ const [tableData, setTableData] = useState([]);
+ const [localColumns, setColumns] = useState([]);
+ // function onChange(pagination, filters, sorter, extra) {
+ // console.log('params', pagination, filters, sorter, extra);
+ // }
+ function changeTableData(e){
+ const localData = filterTableData(allTableData.rows,e.target.value);
+ setTableData(localData);
+ }
+ const content = (
+ changeTableData(e)}/>
+ );
+ useEffect(() => {
+ if(allTableData.rows&&allTableData.column_names){
+ setColumns(getColumns(allTableData.column_names, isSort, isInner, allTableData.href_columns||allTableData.href_column));
+ setTableData(allTableData.rows);
+ }
+ }, [allTableData]);
+
+ return(
+
+ {isFilter?
+
+ :''}
+
`${range[0]}-${range[1]} of ${total} items`,
+ showSizeChanger:true,
+ showQuickJumper:true,
+ hideOnSinglePage:true,
+ }}
+ />
+
+ );
+}
+
+
diff --git a/ui/src/components/table/table.utils.tsx b/ui/src/components/table/table.utils.tsx
new file mode 100644
index 0000000000..e1e6a0e33b
--- /dev/null
+++ b/ui/src/components/table/table.utils.tsx
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import {Link} from 'react-router-dom';
+function sortItems(a: any,b: any, item: string) {
+ if(typeof a[item] === 'number' && typeof b[item] === 'number'){
+ return a[item]-b[item];
+ }
+ return a[item].localeCompare(b[item]);
+}
+function getLinkItem(text, record, index, isInner, item, hrefColumn){
+ if (isInner && hrefColumn && (hrefColumn.indexOf(item) !== -1)&&record.__hrefPaths) {
+ if (record.__hrefPaths[hrefColumn.indexOf(item)].includes('http')) {
+ return {text};
+ }
+ return {text};
+ }
+ return text === '\\N' ? '-' : text;
+}
+export function getColumns(params: string[], isSort: boolean, isInner, hrefColumn) {
+ if(!params||params.length === 0){return [];}
+ let arr = params.map(item=> {
+ if (isSort) {
+ return {
+ title: item,
+ dataIndex: item,
+ className: 'pr-25',
+ sorter: (a,b)=>sortItems(a, b, item),
+ render:(text, record, index)=>getLinkItem(text,record, index, isInner, item, hrefColumn),
+ };
+ }
+ return {
+ title: item,
+ dataIndex: item,
+ className: 'pr-25',
+ render:(text, record, index)=>getLinkItem(text, record, index, isInner, item, hrefColumn),
+ };
+ });
+ return arr;
+}
+export function filterTableData(data: any, target: string) {
+ const res: any[]=[];
+ data.forEach(item=>{
+ const flag: boolean = (Object.values(item)).some(value=>{
+ if (typeof(value) === 'object') { return false };
+ return (value+'').includes(target);
+ });
+ if (flag) {
+ res.push(item);
+ }
+ });
+ return res;
+}
+
diff --git a/ui/src/components/text-with-icon/text-with-icon.tsx b/ui/src/components/text-with-icon/text-with-icon.tsx
new file mode 100644
index 0000000000..8bc8d0937f
--- /dev/null
+++ b/ui/src/components/text-with-icon/text-with-icon.tsx
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Row } from "antd";
+import React, { ComponentProps } from "react";
+import { FlatBtn } from "Components/flatbtn";
+
+interface TextWithIconProps extends ComponentProps {
+ iconPosition?: "left" | "right";
+ colorWithIcon?: boolean,
+ icon: any;
+ isButton?: boolean;
+ text: string;
+ color?: string;
+}
+
+export function TextWithIcon(props: TextWithIconProps) {
+ const { iconPosition = "left", colorWithIcon = true, icon, text, color = "#1890ff", isButton = false, style, ...rest } = props;
+ const children = props.children ? props.children : text;
+ const styles = { alignItems: "center", ...style };
+ if (colorWithIcon) {
+ styles.color = color;
+ }
+ return (
+
+ {iconPosition === "left" && {icon}}
+ {isButton ? (
+ props.onClick(true)}>{text}
+ ) : (
+ {children}
+ )}
+ {iconPosition === "right" && {icon}}
+
+ );
+
+}
diff --git a/ui/src/constants.ts b/ui/src/constants.ts
new file mode 100644
index 0000000000..5f8d20d198
--- /dev/null
+++ b/ui/src/constants.ts
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+export const API_BASE = '/api/meta/namespaces/';
+
+export const PAGE_SIDE_DEFAULT_WIDTH = 240;
+
+export const TABLE_DELAY = 150;
diff --git a/ui/src/favicon.ico b/ui/src/favicon.ico
new file mode 100644
index 0000000000..c79ff458a2
Binary files /dev/null and b/ui/src/favicon.ico differ
diff --git a/ui/src/global.d.ts b/ui/src/global.d.ts
new file mode 100644
index 0000000000..87c80d1d06
--- /dev/null
+++ b/ui/src/global.d.ts
@@ -0,0 +1,21 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+declare module '*.svg';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.less';
diff --git a/ui/src/i18n.tsx b/ui/src/i18n.tsx
new file mode 100644
index 0000000000..006938b699
--- /dev/null
+++ b/ui/src/i18n.tsx
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import LanguageDetector from 'i18next-browser-languagedetector';
+import i18n from 'i18next';
+import enUsTrans from '../public/locales/en-us.json';
+import zhCnTrans from '../public/locales/zh-cn.json';
+import {
+ initReactI18next
+} from 'react-i18next';
+
+i18n.use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ resources: {
+ en: {
+ translation: enUsTrans
+ },
+ zh: {
+ translation: zhCnTrans
+ }
+ },
+ fallbackLng: 'en',
+ debug: false,
+ interpolation: {
+ escapeValue: false
+ }
+ });
+
+export default i18n;
+
diff --git a/ui/src/index.html b/ui/src/index.html
new file mode 100644
index 0000000000..a7a39a3255
--- /dev/null
+++ b/ui/src/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Apache Doris
+
+
+
+
+
+
+
+
diff --git a/ui/src/index.less b/ui/src/index.less
new file mode 100644
index 0000000000..86c63e497e
--- /dev/null
+++ b/ui/src/index.less
@@ -0,0 +1,226 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+@import 'less/variable';
+@import '~antd/lib/style/themes/default.less';
+
+html,
+body,
+:global(#root) {
+ height: 100%;
+}
+
+:global(input[readonly]:not(.ant-cascader-input)) {
+ border: none !important;
+}
+
+:global(.url) {
+ word-wrap: break-word;
+ word-break: break-all;
+}
+
+/* navbar */
+:global {
+ .ant-row {
+ display: flex;
+ }
+ .ant-table-cell {
+ .ant-legacy-form-item {
+ margin: 0 !important;
+ }
+ }
+ .ant-form-item {
+ margin-bottom: 24px !important;
+ }
+ .ant-form-item-explain, .ant-form-item-extra {
+ margin-top: 0 !important;
+ }
+ .ant-select-selection-search-input {
+ padding-right: 0 !important;
+ }
+ .ant-tree-node-content-wrapper {
+ display: flex !important;
+ }
+ .ant-layout-header{
+ z-index: 5!important;
+ box-sizing: border-box;
+ border: 1px solid #f0f0f0;
+ line-height: 61px !important;
+ background-color: #fff !important;
+ }
+ .ant-form-item-label>label.ant-form-item-required:not(.ant-form-item-required-mark-optional):before{
+ position: absolute;
+ left: 100%;
+ top: 10px;
+ }
+ // .ant-menu-inline .ant-menu-item-selected:after, .ant-menu-inline .ant-menu-selected:after {
+ // display: none !important;
+ // }
+ // .ant-menu-dark.ant-menu-horizontal > .ant-menu-item,
+ // .ant-menu-dark.ant-menu-horizontal > .ant-menu-submenu {
+ // top: -1px !important;
+ // border-bottom: 2px solid transparent !important;
+ // // border-bottom-width: 2px !important;
+ // }
+ // .ant-menu-dark.ant-menu-horizontal > .ant-menu-item-selected,
+ // .ant-menu-dark.ant-menu-horizontal > .ant-menu-submenu-selected {
+ // border-bottom: 2px solid @primary-color !important;
+ // background: transparent !important;
+ // color: #608bff !important;
+ // }
+ // .ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected,
+ // .ant-menu.ant-menu-dark .ant-menu-item-selected {
+ // background: transparent !important;
+ // }
+ .react-resizable-handle {
+ background-image: none !important;
+ height: 1px !important;
+ padding: 0 !important;
+ width: 100% !important;
+ background-color: #DCDCDC;
+ cursor: row-resize !important;
+ }
+ .react-resizable-handle:after {
+ display: block;
+ content: '';
+ height: 7px;
+ width: 30px;
+ margin-left: calc((100% - 30px) / 2);
+ margin-top: -3px;
+ border: 1px solid #DCDCDC;
+ border-left: none;
+ border-right: none;
+ cursor: row-resize !important;
+ }
+ .react-resizable-handle-w {
+ left: 0 !important;
+ top: 0 !important;
+ transform: rotate(0deg) !important;
+ height: 100% !important;
+ width: 1px !important;
+ &:after {
+ display: block;
+ content: "";
+ height: 30px;
+ width: 7px;
+ margin-left: -3px;
+ margin-top: 144px;
+ border: 1px solid #dcdcdc;
+ border-top: none;
+ border-bottom: none;
+ cursor: col-resize !important;
+ }
+ }
+ .react-resizable-handle-e {
+ right: 0 !important;
+ top: 0 !important;
+ transform: rotate(0deg) !important;
+ height: 100% !important;
+ width: 1px !important;
+ position: relative;
+ &:after {
+ display: block;
+ position: absolute;
+ content: "";
+ height: 30px;
+ width: 7px;
+ margin-left: -3px;
+ top: calc(50% - 15px);
+ border: 1px solid #dcdcdc;
+ border-top: none;
+ border-bottom: none;
+ cursor: col-resize !important;
+ }
+ }
+}
+
+:global {
+ .ant-card-body {
+ padding: 24px 30px !important;
+ }
+}
+
+/* dropdown ---START--- */
+:global(.ant-dropdown-menu) {
+ padding: @spacer 0 !important;
+ border-radius: 2px !important;
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1) !important;
+}
+:global(.ant-dropdown-menu-item) {
+ padding: 0 2*@spacer !important;
+ margin: 0 !important;
+ line-height: 30px !important;
+ border-radius: 0 !important;
+ // text-align: right;
+ font-size: @font-size-sm !important;
+ > a {
+ border-radius: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ }
+ &:hover {
+ background: #f9f9f9 !important;
+ }
+}
+/* dropdown ---END--- */
+
+:global(.form-item-appendix) {
+ position: absolute;
+ right: -3*@spacer;
+}
+
+:global(.page-wrapper) {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 3*@spacer;
+}
+
+:global(.page-list-top) {
+ display: flex;
+ justify-content: space-between;
+ margin: 2*@spacer 0 @spacer;
+}
+
+:global(.list-selected-count) {
+ display: inline-block;
+ width: 2.5*@spacer;
+ text-align: center;
+}
+
+:global(.CodeMirror) {
+ line-height: 20px;
+}
+
+:global {
+ .ml {
+ margin-right: 10px;
+ }
+}
+a:-webkit-any-link {
+ text-decoration: none;
+}
+:global {
+ .iconfont {
+ font-size: 16px;
+ font-style: normal;
+ }
+}
+:global {
+ .ant-tree {
+ background-color: transparent !important;
+ }
+}
diff --git a/ui/src/index.tsx b/ui/src/index.tsx
new file mode 100644
index 0000000000..9bfa6402cc
--- /dev/null
+++ b/ui/src/index.tsx
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @file test cron
+ * @author lpx
+ * @since 2020/08/19
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './i18n';
+import App from './App';
+import './index.less';
+
+ReactDOM.render(, document.getElementById('root'));
+
diff --git a/ui/src/interfaces/http.interface.ts b/ui/src/interfaces/http.interface.ts
new file mode 100644
index 0000000000..25af6a0063
--- /dev/null
+++ b/ui/src/interfaces/http.interface.ts
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+export interface Result {
+ data: T;
+ code: number;
+ count: number;
+ msg: string;
+}
\ No newline at end of file
diff --git a/ui/src/less/variable.less b/ui/src/less/variable.less
new file mode 100644
index 0000000000..2076fa3682
--- /dev/null
+++ b/ui/src/less/variable.less
@@ -0,0 +1,42 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+ @spacer: 8px;
+ @padding-top: 3*@spacer;
+ @menu-item-padding-space: 4*@spacer;
+
+ @sider-tree-padding: @padding-top 4*@spacer;
+ @tree-sider-width: 240px;
+ @layout-header-height: 60px;
+ @layout-sider-background: #fafafb;
+
+ @content-padding: @padding-top 5*@spacer;
+
+ @content-title-font-size: 16px;
+ @content-padding-left: 4*@spacer;
+ @content-padding-top: @padding-top;
+
+ @table-header-font-weight: normal;
+
+ @btn-font-size-default: 12px;
+
+ @font-size-base: 14px;
+ @font-size-lg: @font-size-base + 2px;
+ @font-size-sm: @font-size-base - 2px;
+
+ @navbar-bg: #1b1b1b;
+
diff --git a/ui/src/pages/backend/index.tsx b/ui/src/pages/backend/index.tsx
new file mode 100644
index 0000000000..4445997292
--- /dev/null
+++ b/ui/src/pages/backend/index.tsx
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import {Typography, Divider} from 'antd';
+const {Title, Paragraph, Text} = Typography;
+export default function Backend(params: any) {
+ return(
+
+ Known Backends(8)
+
+ TODO
+
+
+ );
+}
+
diff --git a/ui/src/pages/configuration/index.tsx b/ui/src/pages/configuration/index.tsx
new file mode 100644
index 0000000000..8c29fd7680
--- /dev/null
+++ b/ui/src/pages/configuration/index.tsx
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect} from 'react';
+import {Typography, Button, Row, Col} from 'antd';
+const {Title} = Typography;
+import {getConfig} from 'Utils/api';
+import Table from 'Src/components/table';
+export default function Configuration(params: any) {
+ const [allTableData, setAllTableData] = useState({});
+ const getConfigData = function(){
+ getConfig({}).then(res=>{
+ if (res && res.msg === 'success') {
+ setAllTableData(res.data);
+ }
+ })
+ .catch(err=>{
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ });
+ };
+ useEffect(() => {
+ getConfigData();
+ }, []);
+
+ return(
+
+ Configure Info
+
+
+ );
+}
+
diff --git a/ui/src/pages/ha/index.tsx b/ui/src/pages/ha/index.tsx
new file mode 100644
index 0000000000..7f661e1619
--- /dev/null
+++ b/ui/src/pages/ha/index.tsx
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import React from 'react';
+import {Typography, Divider} from 'antd';
+import Table from 'Src/components/table';
+const {Title, Text, Paragraph} = Typography;
+export default function Ha(params: any) {
+ return(
+
+ Frontend Role
+
+ MASTER
+
+ Current Journal Id
+
+ MASTER
+
+ Can Read
+
+ MASTER
+
+ Electable nodes
+
+ MASTER
+
+ Observer nodes
+
+ MASTER
+
+ Checkpoint Info
+
+ MASTER
+
+ Database names
+
+ MASTER
+
+ Allowed Frontends
+
+ MASTER
+
+ Removed Frontends
+
+ MASTER
+
+
+ );
+}
+
diff --git a/ui/src/pages/help/index.tsx b/ui/src/pages/help/index.tsx
new file mode 100644
index 0000000000..c7c6f88c0d
--- /dev/null
+++ b/ui/src/pages/help/index.tsx
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import {Typography, Divider, Row, Col, Input, Button} from 'antd';
+const {Title, Paragraph, Text} = Typography;
+import Table from 'Src/components/table';
+
+export default function Home(params: any) {
+ return(
+
+ Help Info
+ This page lists the help info, like 'help contents' in Mysql client.
+
+
+
+ Fuzzy Matching Topic(By Keyword)
+ Find only one category, so show you the detail info below.
+
Find 1 sub topics.
+ {/*
*/}
+ Find 2 sub categories.
+ {/*
*/}
+
+ );
+}
+
diff --git a/ui/src/pages/home/index.tsx b/ui/src/pages/home/index.tsx
new file mode 100644
index 0000000000..4ea95a060e
--- /dev/null
+++ b/ui/src/pages/home/index.tsx
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect} from 'react';
+import {Typography, Divider, BackTop, Spin} from 'antd';
+const {Title, Paragraph, Text} = Typography;
+import {getHardwareInfo} from 'Utils/api';
+
+export default function Home(params: any) {
+ const [hardwareData , setHardwareData] = useState({});
+ const getConfigData = function(){
+ getHardwareInfo().then(res=>{
+ if (res && res.msg === 'success') {
+ setHardwareData(res.data);
+ }
+ })
+ .catch(err=>{
+ setHardwareData({
+ VersionInfo:{},
+ HarewareInfo:{},
+ });
+ });
+ };
+ function getItems(data, flag){
+ let arr = [];
+ for (const i in data) {
+ if (flag) {
+ arr.push(
+
+ )
+ } else {
+ arr.push(
+
{i + ' : ' + data[i]}
+ )
+ }
+ }
+ return arr;
+ }
+ function createMarkup(key,data) {
+ return {__html:key + ' : ' + String(data)};
+ }
+ useEffect(() => {
+ getConfigData();
+ }, []);
+ return(
+
+ Version
+
+ {...getItems(hardwareData.VersionInfo, false)}
+
+
+ Hardware Info
+
+ {...getItems(hardwareData.HarewareInfo, true)}
+
+
+ {hardwareData.HarewareInfo?'':}
+
+ );
+}
+
diff --git a/ui/src/pages/layout/index.css b/ui/src/pages/layout/index.css
new file mode 100644
index 0000000000..9bb8cd8ce6
--- /dev/null
+++ b/ui/src/pages/layout/index.css
@@ -0,0 +1,34 @@
+
+/* .ant-layout-header .logo {
+ width: 120px;
+ height: 31px;
+ margin: 16px 24px 16px 0;
+ float: left;
+} */
+.ant-layout-header .logo img {
+ width: 120px;
+ height: 31px;
+}
+.site-layout .site-layout-background {
+ background: #fff;
+}
+.ant-menu-dark.ant-menu-horizontal {
+ height: 64px;
+}
+.userSet {
+ height: 56px;
+ float: right;
+}
+
+.userSet > span {
+ display: inline-block;
+ height: 100%;
+ padding: 0 12px;
+ /* color: rgba(255, 255, 255, 0.65); */
+}
+
+.userSet .avatar {
+ width: 24px;
+ height: 24px;
+ margin-right: 7px;
+}
diff --git a/ui/src/pages/layout/index.less b/ui/src/pages/layout/index.less
new file mode 100644
index 0000000000..d70f7a8e2a
--- /dev/null
+++ b/ui/src/pages/layout/index.less
@@ -0,0 +1,27 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+.logo{
+ width: 143px;
+ height: 50px;
+ float: left;
+ margin-left: -23px;
+ margin-top: 6px;
+ cursor: pointer;
+ background-size: 143px 50px;
+ background-image: url(../../../public/img/logo.png);
+}
\ No newline at end of file
diff --git a/ui/src/pages/layout/index.tsx b/ui/src/pages/layout/index.tsx
new file mode 100644
index 0000000000..466cf95d40
--- /dev/null
+++ b/ui/src/pages/layout/index.tsx
@@ -0,0 +1,135 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @file test cron
+ * @author lpx
+ * @since 2020/08/19
+ */
+import React, {useState} from 'react';
+import {Layout, Menu, Dropdown, message} from 'antd';
+import { CaretDownOutlined, LogoutOutlined} from '@ant-design/icons';
+import {renderRoutes} from 'react-router-config';
+import {useHistory} from 'react-router-dom';
+import {useTranslation} from 'react-i18next';
+import routes from 'Src/router';
+import {logOut} from 'Utils/api';
+import './index.css';
+import styles from './index.less';
+const {Header, Content, Footer} = Layout;
+
+function Layouts(props: any) {
+ let { t } = useTranslation();
+ const [route, setRoute] = useState(props.route.routes);
+ const [current, setCurrent] = useState(props.location.pathname);
+ const history = useHistory();
+ //Jump page
+ function handleClick(e) {
+ setCurrent(e.key);
+ if (e.key === '/System' ) {
+ history.push(`${e.key}?path=/`);
+ return;
+ }
+ if (location.pathname === e.key) {
+ location.reload();
+ }
+ history.push(e.key);
+ if(location.pathname.includes('Playground')){
+ location.reload();
+ }
+ }
+ function clearAllCookie() {
+ var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
+ if(keys) {
+ for(var i = keys.length; i--;)
+ document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString()
+ }
+ }
+ function onLogout(){
+ logOut().then((res)=>{
+ localStorage.setItem('username','');
+ clearAllCookie();
+ message.success(t('exitSuccessfully'))
+ history.push('/login');
+ })
+ }
+ const menu = (
+
+ );
+ return (
+
+
+
+
+
+ {/* */}
+
+
+ );
+}
+
+export default Layouts;/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
diff --git a/ui/src/pages/login/cover.less b/ui/src/pages/login/cover.less
new file mode 100644
index 0000000000..4c71831610
--- /dev/null
+++ b/ui/src/pages/login/cover.less
@@ -0,0 +1,39 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+:global(.login) {
+ /deep/ :global(.ant-form-item-label){
+ label{
+ color: #999;
+ }
+ }
+ :global(.ant-btn){
+ margin-top: 20px;
+ }
+ :global(.ant-input){
+ border: none;
+ color: #999;
+ border-radius: 10px;
+ background: #f6f7fb;
+ }
+ :global(.ant-input-password){
+ border: none;
+ color: #999;
+ border-radius: 10px;
+ background: #f6f7fb;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/pages/login/index.less b/ui/src/pages/login/index.less
new file mode 100644
index 0000000000..24408158be
--- /dev/null
+++ b/ui/src/pages/login/index.less
@@ -0,0 +1,43 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+.background{
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ justify-content: center;
+ background-image: url('../../../public/img/background.png');
+ background-size: 100% 100%;
+}
+.login-form{
+ width: 350px;
+ height: 330px;
+ border-radius: 10px;
+ margin-bottom: 15%;
+ padding: 40px 35px 15px;
+ background-color: #fff;
+ box-shadow: 0 0 20px 2px rgba(0,0,0,.1);
+}
+.logo{
+ width: 150px;
+ height: 60px;
+ margin-bottom: 15px;
+ background-image: url('../../../public/img/logo.png');
+ background-size: 100% 100%;
+}
\ No newline at end of file
diff --git a/ui/src/pages/login/index.tsx b/ui/src/pages/login/index.tsx
new file mode 100644
index 0000000000..402e38c441
--- /dev/null
+++ b/ui/src/pages/login/index.tsx
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React,{useState} from 'react';
+import {Form, Input, Button, Checkbox} from 'antd';
+import request from 'Utils/request';
+import {useHistory} from 'react-router-dom';
+import {useTranslation} from 'react-i18next';
+import styles from './index.less';
+import './cover.less';
+function Login(){
+ let { t } = useTranslation();
+ const [username, setUsername] = useState();
+ const history = useHistory();
+ const layout = {
+ labelCol: {span: 8},
+ wrapperCol: {span: 24},
+ layout='vertical',
+ };
+ const tailLayout = {
+ wrapperCol: {span: 24},
+ };
+
+ interface Result {
+ content: T;
+ status: string;
+ partialResults: boolean;
+ msg: string;
+ code: number;
+ }
+ function login(data: any): Promise> {
+ return request('/rest/v1/login', {
+ method: 'POST',
+ headers:{Authorization: data.password?`Basic ${btoa(data.username+':'+data.password)}`:`Basic ${btoa(data.username+':')}`},
+ });
+ }
+ const onFinish = values => {
+ login(values).then(res=>{
+ if(res.code===200){
+ history.push('/home');
+ localStorage.setItem('username', username)
+ }
+ });
+ };
+
+ const onFinishFailed = errorInfo => {
+ console.log('Failed:', errorInfo);
+ };
+ // 878CB1
+ // 31395B
+ return (
+
+ );
+}
+
+export default Login;
+
diff --git a/ui/src/pages/logs/index.tsx b/ui/src/pages/logs/index.tsx
new file mode 100644
index 0000000000..a4024de8e2
--- /dev/null
+++ b/ui/src/pages/logs/index.tsx
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React,{useState, useEffect, useRef} from 'react';
+import {Typography, Divider, Row, Col, Input, BackTop} from 'antd';
+const {Title, Paragraph, Text} = Typography;
+import {getLog} from 'Src/utils/api';
+const {Search} = Input;
+import {Result} from '@src/interfaces/http.interface';
+export default function Logs(params: any) {
+ const container = useRef();
+ const [LogConfiguration, setLogConfiguration] = useState({});
+ const [LogContents, setLogContents] = useState({});
+ function getLogData(data){
+ getLog(data).then(res=>{
+ if(res.data){
+ if(res.data.LogConfiguration){
+ setLogConfiguration(res.data.LogConfiguration);
+ }
+ if(res.data.LogContents){
+ container.current.innerHTML=res.data.LogContents.log;
+ setLogContents(res.data.LogContents);
+ }
+ }
+ });
+ }
+ useEffect(() => {
+ getLogData({});
+ }, []);
+ return(
+
+ Log Configuration
+
+
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/adhoc.context.ts b/ui/src/pages/playground/adhoc.context.ts
new file mode 100644
index 0000000000..ef589c7fc9
--- /dev/null
+++ b/ui/src/pages/playground/adhoc.context.ts
@@ -0,0 +1,20 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import React from 'react';
+
+export const QueueContext = React.createContext({});
\ No newline at end of file
diff --git a/ui/src/pages/playground/adhoc.data.ts b/ui/src/pages/playground/adhoc.data.ts
new file mode 100644
index 0000000000..2b8d25f738
--- /dev/null
+++ b/ui/src/pages/playground/adhoc.data.ts
@@ -0,0 +1,50 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+export const CODEMIRROR_OPTIONS = {
+ mode: 'sql',
+ theme: 'material',
+ lineNumbers: true,
+ fullScreen: false,
+ matchBrackets: true,
+ indentUnit: 4,
+ autofocus: true,
+ extraKeys: {
+ 'Alt-/': 'autocomplete',
+ },
+};
+
+export const QUERY_TABTYPE = {
+ key1: '11133sdfsd',
+ key2: '2222sdfsdd',
+ key3: '3321fdsfsd',
+};
+export enum TabPaneType {
+ Result = 'result',
+ History = 'history',
+ Structure = 'structure',
+ Favorite = 'favorite'
+}
+
+export enum AdhocContentRouteKeyEnum {
+ Result = 'result',
+ Structure = 'structure',
+ History = 'history',
+ Favorite = 'favorite',
+ Default = 'default'
+}
\ No newline at end of file
diff --git a/ui/src/pages/playground/adhoc.subject.ts b/ui/src/pages/playground/adhoc.subject.ts
new file mode 100644
index 0000000000..dd9b69135b
--- /dev/null
+++ b/ui/src/pages/playground/adhoc.subject.ts
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import {Subject} from 'rxjs';
+
+// 什么时候在编辑器中打开语句
+export const openInEditorSubject = new Subject();
+
+// Run SQL Success
+export const runSQLSuccessSubject = new Subject();
diff --git a/ui/src/pages/playground/content/components/data-prev.tsx b/ui/src/pages/playground/content/components/data-prev.tsx
new file mode 100644
index 0000000000..55a1aa52f8
--- /dev/null
+++ b/ui/src/pages/playground/content/components/data-prev.tsx
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React,{useState,useEffect} from 'react';
+import {AdHocAPI} from 'Utils/api';
+import {getDbName} from 'Utils/utils';
+import {Row, Empty} from 'antd';
+import {FlatBtn} from 'Components/flatbtn';
+import {useTranslation} from 'react-i18next';
+export function DataPrev(props: any) {
+ let { t } = useTranslation();
+ const {db_name,tbl_name} = getDbName();
+ const [tableData,setTableData] = useState([]);
+ function toQuery(): void {
+ AdHocAPI.doQuery({
+ db_name,
+ body:{stmt:`SELECT * FROM ${db_name}.${tbl_name} LIMIT 10`},
+ }).then(res=>{
+ if (res && res.data) {
+ setTableData(res.data);
+ }
+ })
+ .catch(()=>{
+ setTableData([]);
+ });
+ }
+ useEffect(()=>{
+ toQuery();
+ },[location.pathname]);
+ return (
+
+ );
+}
+
+
diff --git a/ui/src/pages/playground/content/components/history-query.tsx b/ui/src/pages/playground/content/components/history-query.tsx
new file mode 100644
index 0000000000..89ceb2a971
--- /dev/null
+++ b/ui/src/pages/playground/content/components/history-query.tsx
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React,{useState,SyntheticEvent} from 'react';
+import {ResizableBox} from 'react-resizable';
+// const ResizableBox = require('react-resizable').ResizableBox;
+import styles from './index.less';
+require('react-resizable/css/styles.css');
+// import VirtualList from 'Components/VirtualList/index';
+export function HistoryQuery(props: any) {
+ // const { match } = props;
+ // const queueInfo = { ...useContext(QueueContext) };
+ const [historyBoxWidth,setHistoryBoxWidth] = useState(300);
+ // const historyBoxWidth = +(localStorage.getItem('historyBoxWidth')||300);
+
+ const onResize=function(e: SyntheticEvent, data: any) {
+ const width = data.size.width || 300;
+ setHistoryBoxWidth(width);
+ // localStorage.setItem('historyBoxWidth', width);
+ };
+ const initData = {
+ recommendHouse: '名仕公馆',
+ recommendReason: '开发商租赁保障,价格低,位置优越',
+ };
+ const array = [];
+ for(let i = 0; i < 999; i++){
+ array.push(initData);
+ }
+ const resource = array;
+ const renderTable =(item,index)=>{return (
);};
+ return (
+
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/content/components/index.less b/ui/src/pages/playground/content/components/index.less
new file mode 100644
index 0000000000..8cca8bd057
--- /dev/null
+++ b/ui/src/pages/playground/content/components/index.less
@@ -0,0 +1,20 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+.importForm{
+ padding: 30px;
+}
diff --git a/ui/src/pages/playground/content/content-result.tsx b/ui/src/pages/playground/content/content-result.tsx
new file mode 100644
index 0000000000..68f806d756
--- /dev/null
+++ b/ui/src/pages/playground/content/content-result.tsx
@@ -0,0 +1,210 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useEffect, useState} from 'react';
+import {
+ Tabs,
+ Row,
+ Card,
+ Col,
+ Progress,
+ Popover,
+ Pagination,
+} from 'antd';
+import {TabPaneType} from '../adhoc.data';
+import { useLocation } from 'react-router-dom'
+import {FlatBtn} from 'Components/flatbtn';
+import {
+ DownloadOutlined,
+ CloseCircleFilled,
+} from '@ant-design/icons';
+import SyntaxHighlighter from 'react-syntax-highlighter';
+import {docco} from 'react-syntax-highlighter/dist/esm/styles/hljs';
+import {TextWithIcon} from 'Components/text-with-icon/text-with-icon';
+import {LoadingWrapper} from 'Components/loadingwrapper/loadingwrapper';
+import {useTranslation} from 'react-i18next';
+
+export function AdhocContentResult(props) {
+ let { t } = useTranslation();
+ let location = useLocation()
+ const jobId = props.match.params.jobId;
+ const [showSaveResultModal, setShowSaveResultModal] = useState(false);
+ const [tableData, setTableDate] = useState([]);
+ const [resStatus, setResStatus] = useState([]);
+ // const [pageSize, setPageSize] = useState(20);
+ const [total, setTotal] = useState([]);
+ const [runningQueryInfo, setRunningQueryInfo] = useState({});
+ useEffect(() => {
+ const runningQueryInfo = location.state;
+ if (runningQueryInfo.msg=='success') {
+ runningQueryInfo.status = 'success';
+ } else {
+ runningQueryInfo.status = 'failed';
+ }
+ if (runningQueryInfo.data?.type === 'exec_status') {
+ setResStatus(runningQueryInfo.data.status)
+ } else {
+ const tableData = runningQueryInfo.data?(runningQueryInfo.data?.data).slice(0,20):[];
+ setTableDate(tableData);
+ setTotal(runningQueryInfo.data?.data.length);
+ }
+ setRunningQueryInfo(runningQueryInfo);
+ },[location.state]);
+
+ function renderRunStatus() {
+ return (
+ <>
+
+
+ {t('runSuccessfully')}
+
+ >
+ );
+ }
+ function getELe(data) {
+ let arr = []
+ for (const i in data) {
+ arr.push(
+
+
+ `Total ${total} items`}
+ />
+
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/content/content-structure.tsx b/ui/src/pages/playground/content/content-structure.tsx
new file mode 100644
index 0000000000..e2866c8282
--- /dev/null
+++ b/ui/src/pages/playground/content/content-structure.tsx
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState,useEffect} from 'react';
+import {Tabs, Row, Table} from 'antd';
+import {TabPaneType,QUERY_TABTYPE} from '../adhoc.data';
+import React from 'react';
+import {FlatBtn} from 'Components/flatbtn';
+import {TABLE_DELAY} from 'Constants';
+import {useRequest} from '@umijs/hooks';
+import {AdHocAPI} from 'Utils/api';
+import {getDbName} from 'Utils/utils';
+import {Result} from '@src/interfaces/http.interface';
+import {DataPrev} from './components/data-prev';
+import getColumns from './getColumns';
+import {useHistory} from 'react-router-dom';
+import {useTranslation} from 'react-i18next';
+export function ContentStructure(props: any) {
+ let { t } = useTranslation()
+ const {db_name,tbl_name}=getDbName();
+ const [columns, setColumns] = useState([])
+ const getTableInfoRequest = useRequest>(
+ () => {
+ const param = {
+ db_name,
+ tbl_name,
+ };
+ return AdHocAPI.getDatabaseList(param);
+ },
+ {
+ refreshDeps: [location.pathname],
+ },
+ );
+ const cols = getTableInfoRequest?.data?.data?.[tbl_name]?.schema;
+ useEffect(() => {
+ if (cols) {
+ setColumns(getColumns(cols[0], props, false))
+ } else {
+ setColumns([])
+ }
+ }, [cols])
+
+ const history = useHistory();
+ return (
+ {if (key ===QUERY_TABTYPE.key3) {history.push('/Playground/import/'+db_name+'-'+tbl_name);}}}
+ >
+
+
+
+ {t('database')}: {db_name} {t('table')}: {tbl_name}
+
+
+ props.queryTable(`${db_name}.${tbl_name}`)
+ }
+ >
+ {t('queryForm')}
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/content/getColumns.tsx b/ui/src/pages/playground/content/getColumns.tsx
new file mode 100644
index 0000000000..8ea6dc3b41
--- /dev/null
+++ b/ui/src/pages/playground/content/getColumns.tsx
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+
+export default function getColumns(data, fa, isAction){
+ if(!data){
+ return []
+ }
+ let arr = []
+ for(let i in data){
+ if(i === 'key'){
+ continue
+ }
+ if ( i === 'Field' && fa && !isAction ){
+ arr.push({
+ title: 'Field',
+ dataIndex: 'Field',
+ key: 'Field',
+ render: Field => (
+ fa.handleNameClicked(Field)}
+ >
+ {Field}
+
+ ),
+ })
+ } else {
+ arr.push({
+ title: i,
+ dataIndex: i,
+ key: i,
+ render: i => (
+
+ {i === '\t'?'\\t':i}
+
+ ),
+ })
+ }
+ }
+ if(isAction){
+ arr.push(
+ {
+ title: 'Action',
+ dataIndex: '',
+ key: 'x',
+ render: (text, record, index) => {
+ return {fa(record, index, e)}}>Delete
+ },
+ },
+ )
+ }
+ return arr;
+}
+
diff --git a/ui/src/pages/playground/content/index.less b/ui/src/pages/playground/content/index.less
new file mode 100644
index 0000000000..6a60d496d1
--- /dev/null
+++ b/ui/src/pages/playground/content/index.less
@@ -0,0 +1,42 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+@import '~antd/lib/style/themes/default.less';
+
+.adhoc-code {
+ height: 100%;
+ padding-bottom: 60px;
+ &-operator {
+ margin-top: 10px;
+ }
+}
+
+.adhoc-content-favorite {
+ &-edit-icon {
+ margin-left: 4px;
+ cursor: pointer;
+ }
+ &-edit-icon:hover {
+ color: @primary-color;
+ }
+ &-code {
+ margin-top: 10px;
+ // > pre {
+ // margin-bottom: 0 !important;
+ // }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/pages/playground/content/index.tsx b/ui/src/pages/playground/content/index.tsx
new file mode 100644
index 0000000000..61e4b28b32
--- /dev/null
+++ b/ui/src/pages/playground/content/index.tsx
@@ -0,0 +1,241 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect, useContext, SyntheticEvent} from 'react';
+// import {Controlled as CodeMirror} from 'react-codemirror2';
+import styles from './index.less';
+import {
+ CODEMIRROR_OPTIONS,
+ AdhocContentRouteKeyEnum,
+} from '../adhoc.data';
+import {Button, Row, Col, message} from 'antd';
+import {PlayCircleFilled} from '@ant-design/icons';
+import {Switch, Route, Redirect} from 'react-router';
+import {AdhocContentResult} from './content-result';
+import {useRequest} from '@umijs/hooks';
+import {AdHocAPI} from 'Utils/api';
+import {Result} from '@src/interfaces/http.interface';
+import {isSuccess, getDbName, getTimeNow} from 'Utils/utils';
+import {CodeMirrorWithFullscreen} from 'Components/codemirror-with-fullscreen/codemirror-with-fullscreen';
+import {
+ openInEditorSubject,
+ runSQLSuccessSubject,
+} from '../adhoc.subject';
+import {FlatBtn} from 'Components/flatbtn';
+import {ContentStructure} from './content-structure';
+import sqlFormatter from 'sql-formatter';
+import {useTranslation} from 'react-i18next';
+import {ResizableBox} from 'react-resizable';
+require('react-resizable/css/styles.css');
+let editorInstance: any;
+let isQueryTableClicked = false;
+let isFieldNameInserted = false;
+
+export function AdHocContent(props: any) {
+ let { t } = useTranslation();
+ const {match} = props;
+ const [loading, setLoading] = useState({
+ favoriteConfirm: false,
+ });
+ const [code, setCode] = useState('');
+ const editorAreaHeight = +(localStorage.getItem('editorAreaHeight') || 300);
+ const beginTime = getTimeNow();
+ const runQuery = useRequest>(
+ () =>
+ AdHocAPI.doQuery({
+ db_name:getDbName().db_name,
+ body:{stmt:code},
+ }),
+ {
+ manual: true,
+ onSuccess: res => {
+ const endTime = getTimeNow();
+ const {db_name, tbl_name} = getDbName();
+ if (isSuccess(res)) {
+ res.sqlCode = code;
+ res = {...res, db_name, tbl_name, beginTime, endTime}
+ props.history.push({pathname:`/Playground/result/${db_name}-${tbl_name}`,state: res});
+ runSQLSuccessSubject.next(true);
+ } else {
+ res.sqlCode = code;
+ res = {...res, db_name, tbl_name, beginTime, endTime}
+ props.history.push({pathname:`/Playground/result/${db_name}-${tbl_name}`,state: res});
+ runSQLSuccessSubject.next(false);
+ }
+ },
+ onError: () => {
+ message.error(t('errMsg'));
+ runSQLSuccessSubject.next(false);
+ },
+ },
+ );
+
+ useEffect(() => {
+ const subscription = openInEditorSubject.subscribe(code => {
+ setCode(code);
+ setEditorCursor(false);
+ });
+ return () => subscription.unsubscribe();
+ }, []);
+
+ const handleChange = (_editor, _data, value) => {
+ setCode(value);
+ };
+
+
+ function handleQueryTable(tableName: string) {
+ isQueryTableClicked = true;
+ setCode(`SELECT FROM ${tableName}`);
+ setEditorCursor(false);
+ }
+
+ function setEditorCursor(insert = false) {
+ setTimeout(() => {
+ editorInstance.focus();
+ editorInstance.setCursor({line: 0, ch: 7});
+ isFieldNameInserted = insert;
+ }, 0);
+ }
+
+
+ function handleRunQuery() {
+ runQuery.run();
+ }
+
+ function handleNameClicked(fieldName: string) {
+ const currentPos = editorInstance.getCursor();
+ const insertFieldName = isFieldNameInserted
+ ? `, ${fieldName}`
+ : `${fieldName}`;
+ editorInstance.replaceRange(insertFieldName, currentPos);
+ setTimeout(() => {
+ editorInstance.focus();
+ isFieldNameInserted = true;
+ }, 0);
+ }
+ return (
+
+ );
+}
+
diff --git a/ui/src/pages/playground/data-import/import-func.tsx b/ui/src/pages/playground/data-import/import-func.tsx
new file mode 100644
index 0000000000..58a3b26e33
--- /dev/null
+++ b/ui/src/pages/playground/data-import/import-func.tsx
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Item from "antd/lib/list/Item";
+
+export function getAllTableData(max, arr) {
+ if (!max || !arr) {
+ return [];
+ }
+ return arr.map((item,i)=>{
+ item.length = max;
+ item.key = i;
+ return item;
+ });
+}
+
diff --git a/ui/src/pages/playground/data-import/import-result.tsx b/ui/src/pages/playground/data-import/import-result.tsx
new file mode 100644
index 0000000000..ffef384394
--- /dev/null
+++ b/ui/src/pages/playground/data-import/import-result.tsx
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import {Modal} from 'antd';
+export function ImportResult(data,callback) {
+ const arr = [];
+ for (const i in data) {
+ arr.push(
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/index.less b/ui/src/pages/playground/index.less
new file mode 100644
index 0000000000..e9f640bf92
--- /dev/null
+++ b/ui/src/pages/playground/index.less
@@ -0,0 +1,68 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+@import '../../less/variable';
+@import '~antd/lib/style/themes/default.less';
+.content {
+ padding: 3*@spacer;
+ background-color: #fff;
+ // max-width: 1200px;
+}
+.adhoc {
+ display: flex;
+ height: 100%;
+ -webkit-box-orient: horizontal;
+ -webkit-box-direction: normal;
+ flex-direction: row;
+}
+
+.breadcrumb {
+ margin-bottom: 3*@spacer!important;
+}
+
+.section {
+ margin-left: 30px;
+}
+:global {
+ .warning {
+ color: red;
+ }
+ .info {
+ font-size: 12px;
+ margin-left: 10px;
+ }
+}
+.wrap{
+ /deep/ .react-resizable-handle{
+ left: 0;
+ right: initial;
+ height: 100%!important;
+ width: 1px!important;
+ &:after{
+ display: block;
+ content: "";
+ height: 30px;
+ width: 7px;
+ margin-left: -3px;
+ margin-top: 144px;
+ border: 1px solid #dcdcdc;
+ border-top: none;
+ border-bottom: none;
+ cursor: col-resize!important;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/pages/playground/index.tsx b/ui/src/pages/playground/index.tsx
new file mode 100644
index 0000000000..0d16808984
--- /dev/null
+++ b/ui/src/pages/playground/index.tsx
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import {PageSide} from './page-side/index';
+import {AdHocTree} from './tree/index';
+import {Layout} from 'antd';
+import styles from './index.less';
+import {AdHocContent} from './content';
+const Content = Layout.Content;
+function QueryContent(props) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+export default QueryContent;
+
diff --git a/ui/src/pages/playground/page-side/index.less b/ui/src/pages/playground/page-side/index.less
new file mode 100644
index 0000000000..0a80f52056
--- /dev/null
+++ b/ui/src/pages/playground/page-side/index.less
@@ -0,0 +1,23 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+.side{
+ flex: 0 0 300px;
+ height: 100%;
+ background: #fff;
+ min-height: calc(100vh - 60px);
+}
\ No newline at end of file
diff --git a/ui/src/pages/playground/page-side/index.tsx b/ui/src/pages/playground/page-side/index.tsx
new file mode 100644
index 0000000000..570584b3d4
--- /dev/null
+++ b/ui/src/pages/playground/page-side/index.tsx
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect, SyntheticEvent} from 'react';
+import {ResizableBox} from 'react-resizable';
+require('react-resizable/css/styles.css');
+import styles from './index.less';
+
+export function PageSide(props: any) {
+ const {children} = props;
+ const [sideBoxWidth,setSideBoxWidth] = useState(300);
+
+ const onResize=function(e: SyntheticEvent, data: any) {
+ const width = data.size.width || 300;
+ setSideBoxWidth(width);
+ };
+ return (
+
+
+ {children}
+
+
+
+ );
+}
+
diff --git a/ui/src/pages/playground/router.ts b/ui/src/pages/playground/router.ts
new file mode 100644
index 0000000000..9d2190bb79
--- /dev/null
+++ b/ui/src/pages/playground/router.ts
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import {renderRoutes} from 'react-router-config';
+function Playground(props) {
+ return (
+ renderRoutes(props.route.routes)
+ );
+}
+
+export default Playground;
diff --git a/ui/src/pages/playground/tree/index.tsx b/ui/src/pages/playground/tree/index.tsx
new file mode 100644
index 0000000000..1baccbad45
--- /dev/null
+++ b/ui/src/pages/playground/tree/index.tsx
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState,useEffect} from 'react';
+import {Tree, Spin, Space} from 'antd';
+import {TableOutlined, HddOutlined} from '@ant-design/icons';
+import {AdHocAPI} from 'Utils/api';
+import {
+ AdhocContentRouteKeyEnum,
+} from '../adhoc.data';
+interface DataNode {
+ title: string;
+ key: string;
+ isLeaf?: boolean;
+ children?: DataNode[];
+}
+
+const initTreeDate: DataNode[] = [];
+
+function updateTreeData(list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] {
+ return list.map(node => {
+ if (node.key === key) {
+ return {
+ ...node,
+ children,
+ };
+ } else if (node.children) {
+ return {
+ ...node,
+ children: updateTreeData(node.children, key, children),
+ };
+ }
+ return node;
+ });
+}
+export function AdHocTree(props: any) {
+
+ const [treeData, setTreeData] = useState(initTreeDate);
+ const [loading, setLoading] = useState(true);
+ useEffect(() => {
+ AdHocAPI.getDatabaseList().then(res=>{
+ if (res.msg === 'success' && Array.isArray(res.data)) {
+ const treeData = res.data.map((item,index)=>{
+ return {
+ title: item,
+ key: `1-${index}-${item}`,
+ icon: ,
+ };
+ });
+ setTreeData(treeData);
+ }
+ setLoading(false);
+ });
+ }, []);
+ function onLoadData({key, children}) {
+ const [storey, index, db_name, tbl_name] = key.split('-');
+ const param = {
+ db_name,
+ tbl_name,
+ };
+ return AdHocAPI.getDatabaseList(param).then(res=>{
+ if (res.msg=='success' && Array.isArray(res.data)) {
+ const treeData = res.data.map((item,index)=>{
+ if(storey==1){
+ return {
+ title: item,
+ key: `2-${index}-${param.db_name}-${item}`,
+ icon: ,
+ isLeaf: true,
+ };
+ }
+
+ });
+ setTreeData(origin =>
+ updateTreeData(origin, key, treeData),
+ );
+ }
+ });
+
+ }
+ function handleTreeSelect(
+ keys: React.ReactText[],
+ info: any,
+ path: AdhocContentRouteKeyEnum = AdhocContentRouteKeyEnum.Result,
+ ) {
+ if (keys.length > 0) {
+ props.history.push(`/Playground/${path}/${keys[0].split(':')[1]}`);
+ }
+ }
+ return (
+ <>
+
+
+ handleTreeSelect(
+ selectedKeys,
+ info,
+ AdhocContentRouteKeyEnum.Structure )
+ }
+ />
+ >
+ );
+
+}
+
diff --git a/ui/src/pages/query-profile/index.tsx b/ui/src/pages/query-profile/index.tsx
new file mode 100644
index 0000000000..815dbc24ba
--- /dev/null
+++ b/ui/src/pages/query-profile/index.tsx
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect, useRef} from 'react';
+import {Typography, Button, Row, Col} from 'antd';
+const {Text, Title, Paragraph} = Typography;
+import {queryProfile} from 'Utils/api';
+import Table from 'Src/components/table';
+import {useHistory} from 'react-router-dom';
+export default function QueryProfile(params: any) {
+ // const [parentUrl, setParentUrl] = useState('');
+ const container = useRef();
+ const [allTableData, setAllTableData] = useState({});
+ const [profile, setProfile] = useState();
+ const history = useHistory();
+ const doQueryProfile = function(){
+ const param = {
+ path:location.pathname.slice(14),
+ };
+ queryProfile(param).then(res=>{
+ if (res && res.msg === 'success') {
+ if(!res.data.column_names){
+ setProfile(res.data);
+ container.current.innerHTML=res.data;
+ }else{
+ setProfile('');
+ setAllTableData(res.data);
+ }
+ } else {
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ }
+ })
+ .catch(err=>{
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ });
+ };
+ useEffect(() => {
+ doQueryProfile();
+ }, [location.pathname]);
+ function goPrev(){
+ if (location.pathname === '/QueryProfile/') {return;}
+ history.push('/QueryProfile/');
+ }
+ return(
+
+ Finished Queries
+
+
+
This table lists the latest 100 queries
+
+ {profile?:''}
+
+
+ {
+ profile
+ ?
+ {/* {profile} */}
+
+ :
+ }
+
+
+ );
+}
+
diff --git a/ui/src/pages/session/index.tsx b/ui/src/pages/session/index.tsx
new file mode 100644
index 0000000000..7b49775af0
--- /dev/null
+++ b/ui/src/pages/session/index.tsx
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {useState, useEffect} from 'react';
+import {Typography, Button, Row, Col} from 'antd';
+const {Text, Title, Paragraph} = Typography;
+import {getSession} from 'Utils/api';
+import Table from 'Src/components/table';
+// import {useHistory} from 'react-router-dom';
+export default function Session(params: any) {
+ // const [parentUrl, setParentUrl] = useState('');
+ const [allTableData, setAllTableData] = useState({});
+ // const history = useHistory();
+ const getSessionData = function(){
+ getSession({}).then(res=>{
+ if (res && res.msg === 'success') {
+ setAllTableData(res.data);
+ }
+ })
+ .catch(err=>{
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ });
+ };
+ useEffect(() => {
+ getSessionData();
+ }, []);
+ return(
+
+ Session Info
+
+
+ This page lists the session info, there are {allTableData?.rows?.length} active sessions.
+
+
+
+
+ );
+}
+
diff --git a/ui/src/pages/system/index.tsx b/ui/src/pages/system/index.tsx
new file mode 100644
index 0000000000..6af60da5a9
--- /dev/null
+++ b/ui/src/pages/system/index.tsx
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import React, {useState, useEffect} from 'react';
+import {Typography, Button, Row, Col} from 'antd';
+const {Text, Title, Paragraph} = Typography;
+import {getSystem} from 'Utils/api';
+import Table from 'Src/components/table';
+import {useHistory} from 'react-router-dom';
+export default function System(params: any) {
+ const [parentUrl, setParentUrl] = useState('');
+ const [allTableData, setAllTableData] = useState({});
+
+ const history = useHistory();
+ const getSystemData = function(){
+ const param = {
+ path:location.search,
+ };
+ getSystem(param).then(res=>{
+ if (res && res.msg === 'success') {
+ setAllTableData(res.data);
+ setParentUrl(res.data.parent_url);
+ } else {
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ }
+ })
+ .catch(err=>{
+ setAllTableData({
+ column_names:[],
+ rows:[],
+ });
+ });
+ };
+ useEffect(() => {
+ getSystemData();
+ }, [location.search]);
+ function goPrev(){
+ if (parentUrl === '/rest/v1/system') {
+ history.push('/System?path=/');
+ return;
+ }
+ if (parentUrl) {
+ history.push(parentUrl.split('v1/')[1]);
+ }
+ }
+ return(
+
+ System Info
+ This page lists the system info, like /proc in Linux.
+
+
+
+
+
Current path: {location.search.split('=')[1]}
+
+
+
+
+
+
+ );
+}
+
diff --git a/ui/src/router/index.ts b/ui/src/router/index.ts
new file mode 100644
index 0000000000..c94d28bfb8
--- /dev/null
+++ b/ui/src/router/index.ts
@@ -0,0 +1,114 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import asyncComponent from '../utils/lazy';
+const Playground = asyncComponent(() => import('../pages/playground'));
+const QueryRouter = asyncComponent(() => import('../pages/playground/router'));
+const Login = asyncComponent(() => import('../pages/login'));
+const Layout = asyncComponent(() => import('../pages/layout'));
+const Home = asyncComponent(() => import('../pages/home'));
+const System = asyncComponent(() => import('../pages/system'));
+const Backend = asyncComponent(() => import('../pages/backend'));
+const Logs = asyncComponent(() => import('../pages/logs'));
+const QueryProfile = asyncComponent(() => import('../pages/query-profile'));
+const Session = asyncComponent(() => import('../pages/session'));
+const Configuration = asyncComponent(() => import('../pages/configuration'));
+const Ha = asyncComponent(() => import('../pages/ha'));
+const Help = asyncComponent(() => import('../pages/help'));
+const DataImport = asyncComponent(() => import('../pages/playground/data-import'));
+
+export default {
+ routes: [
+ {
+ path: '/login',
+ component: Login,
+ title: '登录',
+ },
+ {
+ path: '/',
+ component: Layout,
+ routes: [
+ {
+ path: '/home',
+ component: Home,
+ title: '首页',
+ },
+ {
+ path: '/Playground',
+ component: QueryRouter,
+ title: 'Playground',
+ routes: [
+ {
+ path: '/Playground/import',
+ component: DataImport,
+ },
+ {
+ path: '/Playground',
+ component: Playground,
+ },
+ ],
+ },
+ {
+ path: '/System',
+ component: System,
+ title: 'System',
+ search: '?path=/',
+ },
+ // {
+ // path: '/menu/backend',
+ // component: Backend,
+ // title: 'backend',
+ // },
+ {
+ path: '/Log',
+ component: Logs,
+ title: 'Log',
+ },
+ {
+ path: '/QueryProfile',
+ component: QueryProfile,
+ title: 'QueryProfile',
+ },
+ {
+ path: '/Session',
+ component: Session,
+ title: 'Session',
+ },
+ {
+ path: '/Configuration',
+ component: Configuration,
+ title: 'Configuration',
+ },
+ // {
+ // path: '/ha',
+ // component: Ha,
+ // title: 'ha',
+ // },
+ // {
+ // path: '/help',
+ // component: Help,
+ // title: 'help',
+ // },
+ ],
+ },
+ {
+ path: '/',
+ redirect: '/home',
+ component: Layout,
+ },
+ ],
+};
\ No newline at end of file
diff --git a/ui/src/router/renderRouter.tsx b/ui/src/router/renderRouter.tsx
new file mode 100644
index 0000000000..a3f8ad2e6e
--- /dev/null
+++ b/ui/src/router/renderRouter.tsx
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { Route, Redirect, Switch } from 'react-router-dom';
+import router from '.';
+let isLogin = document.cookie;
+const renderRoutes = (routes, authPath = '/login') => {
+ if(routes){
+ return (
+
+ {routes.map((route, i) => (
+ {
+ if(props.location.pathname === '/'){
+ return ;
+ }
+ if (isLogin) {
+ return route.render ? (
+ route.render({ ...props, route: route })
+ ) : (
+
+ )
+ } else {
+ isLogin = '1';
+ return ;
+ }}
+ }
+ />
+ ))}
+
+ )
+ }
+ return null
+}
+
+export default renderRoutes;
+
diff --git a/ui/src/utils/api.ts b/ui/src/utils/api.ts
new file mode 100644
index 0000000000..cf7c9a814f
--- /dev/null
+++ b/ui/src/utils/api.ts
@@ -0,0 +1,127 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import {API_BASE} from 'Constants';
+import request from 'Utils/request';
+import {Result} from '@src/interfaces/http.interface';
+//logout
+export function logOut(): Promise> {
+ return request(`/rest/v1/logout`,{
+ method: 'POST',
+ });
+}
+//home
+export function getHardwareInfo(): Promise> {
+ return request(`/rest/v1/hardware_info/fe/`,{
+ method: 'GET',
+ });
+}
+//system
+export function getSystem(data: any): Promise> {
+ return request(`/rest/v1/system${data.path}`,{
+ method: 'GET',
+ });
+}
+//log
+export function getLog(data: any): Promise> {
+ let localUrl = '/rest/v1/log';
+ if(data.add_verbose){
+ localUrl = `/rest/v1/log?add_verbose=${data.add_verbose}`;
+ }
+ if (data.del_verbose) {
+ localUrl = `/rest/v1/log?del_verbose=${data.del_verbose}`;
+ }
+ // if (data.add_verbose && data.del_verbose) {
+ // localUrl += `/rest/v1/log?add_verbose=${data.add_verbose}&&del_verbose=${data.del_verbose}`;
+ // }
+ return request(localUrl,{
+ method: (data.add_verbose || data.del_verbose)?'POST':'GET',
+ });
+}
+//query_profile
+export function queryProfile(data: any): Promise> {
+ let LocalUrl = '/rest/v1/query_profile/';
+ if(data.path){
+ LocalUrl = `/rest/v1/query_profile/${data.path}`;
+ }
+ return request(LocalUrl);
+}
+//session
+export function getSession(data: any): Promise> {
+ return request('/rest/v1/session');
+}
+//config
+export function getConfig(data: any): Promise> {
+ return request('rest/v1/config/fe/');
+}
+//query begin
+export function getDatabaseList(data: any): Promise> {
+ let reURL = `${API_BASE}default_cluster/databases`;
+ if(data){
+ if(data.db_name){
+ reURL += `/${data.db_name}/tables`;
+ }
+ if(data.db_name&&data.tbl_name){
+ reURL += `/${data.tbl_name}/schema`;
+ }
+ }
+ return request(reURL);
+}
+export function doQuery(data: any): Promise> {
+ return request(`/api/query/default_cluster/${data.db_name}`, {
+ method: 'POST',...data,
+ });
+}
+export function doUp(data: any): Promise> {
+ let localHeader = {
+ label: data.label,
+ columns: data.columns,
+ column_separator: data. column_separator
+ }
+ if(!localHeader.columns){
+ delete localHeader.columns
+ }
+ return request(`/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}`, {
+ method: 'PUT',headers:localHeader,
+ });
+}
+export function getUploadData(data: any): Promise> {
+ let localUrl = `/api/default_cluster/${data.db_name}/${data.tbl_name}/upload`
+ if(data.preview){
+ localUrl = `/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}&preview=true`
+ }
+ return request(localUrl,{
+ method: 'GET',
+ });
+}
+export function deleteUploadData(data: any): Promise> {
+ return request(`/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}`,{
+ method: 'DELETE',
+ });
+}
+//query end
+export const AdHocAPI = {
+ getDatabaseList,
+ doQuery,
+ doUp,
+ getLog,
+ queryProfile,
+ logOut,
+ getHardwareInfo,
+ getUploadData,
+ deleteUploadData
+};
\ No newline at end of file
diff --git a/ui/src/utils/lazy.tsx b/ui/src/utils/lazy.tsx
new file mode 100644
index 0000000000..df9d483c60
--- /dev/null
+++ b/ui/src/utils/lazy.tsx
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, {Component} from 'react';
+
+const asyncComponent = importComponent => {
+ return class extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ component: null
+ };
+ }
+ componentDidMount() {
+ importComponent()
+ .then(cmp => {
+ this.setState({component: cmp.default});
+ });
+ }
+ render() {
+ const C = this.state.component;
+ return C ? : null;
+ }
+ };
+};
+
+export default asyncComponent;
+
diff --git a/ui/src/utils/request.tsx b/ui/src/utils/request.tsx
new file mode 100644
index 0000000000..9a7cf7ba16
--- /dev/null
+++ b/ui/src/utils/request.tsx
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @file test cron
+ * @author lpx
+ * @since 2020/08/19
+ */
+import React from 'react';
+import {message, Modal} from 'antd';
+import {ExclamationCircleOutlined} from '@ant-design/icons';
+import {Trans} from 'react-i18next';
+
+function checkStatus(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response;
+ }
+ if (response.status === 401) {
+ Modal.confirm({
+ title: tips,
+ icon: ,
+ content: loginExpirtMsg,
+ onOk() {
+ window.location.href = window.location.origin + '/login';
+ },
+ onCancel() {
+ //
+ }
+ });
+ return;
+ }
+ const error = new Error(response.statusText);
+ error.response = response;
+ message.error(response.statusText);
+ throw error;
+}
+
+/**
+ * Requests a URL, returning a promise.
+ *
+ * @param {string} url The URL we want to request
+ * @param {Object} options The parameters to be passed; options may not be passed when GET, the default is {}
+ * @param {string} options POST, DELETE, GET, PUT
+ * @param {Object} options Parameters that need to be passed to the backend, such as {id: 1}
+ * @param {boolean} options Whether to upload a file, if you upload a file, you do not need the default headers and convert the body to a string
+ * @param {boolean} tipSuccess false: Do not show successful update true: show successful update
+ * @param {boolean} tipError false: Don't prompt error true: display error message
+ * @param {boolean} fullResponse false: Whether to return all requested information
+ * @return {Object}
+ */
+export default async function request(url, options = {}, tipSuccess = false, tipError = true, fullResponse = false) {
+ const newOptions = {credentials: 'include', ...options};
+ if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
+ newOptions.headers = newOptions.isUpload
+ ? {
+ ...newOptions.headers
+ }
+ : {
+ 'Content-Type': 'application/json; charset=utf-8',
+ ...newOptions.headers
+ };
+ }
+ if (typeof newOptions.body === 'object' && !newOptions.isUpload) {
+ newOptions.body = JSON.stringify(newOptions.body);
+ }
+ const response = await fetch(url, newOptions);
+ if (
+ response.url.includes('dataIntegrationApi')
+ && (
+ newOptions.method === 'PUT'
+ || newOptions.method === 'POST'
+ || newOptions.method === 'DELETE'
+ )
+ ) {
+ return response;
+ }
+ checkStatus(response);
+
+ if (options && options.download) {
+ return response.blob();
+ }
+ const data = await response.json();
+ if ('code' in data || 'msg' in data) {
+ const code = data.code;
+ const msg = data.msg;
+ if (msg === 'success' || code === 0 || code === 200) {
+ if (tipSuccess) {
+ message.success(successfulOperation, msg);
+ }
+ } else if (tipError && code !== 0 && msg !== 'success') {
+ message.error(msg);
+ }
+ }
+
+ if (fullResponse) {
+ return {data, response};
+ }
+ return data;
+}
+
diff --git a/ui/src/utils/utils.ts b/ui/src/utils/utils.ts
new file mode 100644
index 0000000000..3355540c64
--- /dev/null
+++ b/ui/src/utils/utils.ts
@@ -0,0 +1,78 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * @file test cron
+ * @author lpx
+ * @since 2020/08/19
+ */
+function isSuccess(response) {
+ if (!response) {
+ return false;
+ }
+
+ let status = response.status;
+ if (status == null) {
+ status = response.msg;
+ }
+
+ if (isNaN(status)) {
+ return status === 'success';
+ }
+ return status === 0;
+}
+function getDbName(params) {
+ const infoArr = location.pathname.split('-');
+ const db_name = infoArr[0].split('/')[3];
+ const tbl_name = infoArr[1];
+ const res = {};
+ res.db_name = db_name;
+ res.tbl_name = tbl_name;
+ return res;
+}
+function getTimeNow() {
+ let dateNow = new Date();
+ let fmt = 'yyyy-MM-dd hh:mm:ss';
+ var o = {
+ 'M+': dateNow.getMonth() + 1,
+ 'd+': dateNow.getDate(),
+ 'h+': dateNow.getHours(),
+ 'm+': dateNow.getMinutes(),
+ 's+': dateNow.getSeconds(),
+ 'q+': Math.floor((dateNow.getMonth() + 3) / 3),
+ 'S': dateNow.getMilliseconds()
+ };
+ if (/(y+)/.test(fmt)) {
+ fmt = fmt.replace(
+ RegExp.$1,
+ (dateNow.getFullYear() + '').substr(4 - RegExp.$1.length)
+ );
+ }
+ for (var k in o) {
+ if (new RegExp('(' + k + ')').test(fmt)) {
+ fmt = fmt.replace(
+ RegExp.$1,
+ RegExp.$1.length === 1
+ ? o[k]
+ : ('00' + o[k]).substr(('' + o[k]).length)
+ );
+ }
+ }
+ return fmt;
+}
+
+module.exports = {isSuccess, getDbName, getTimeNow};
\ No newline at end of file
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
new file mode 100644
index 0000000000..0f07e067e8
--- /dev/null
+++ b/ui/tsconfig.json
@@ -0,0 +1,59 @@
+{
+ "compilerOptions": {
+ "outDir": "dist",
+ "module": "commonjs",
+ "target": "es5",
+ "lib": ["es7", "dom", "es2015.iterable", "es2019"],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "forceConsistentCasingInFileNames": false,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": false,
+ "strictNullChecks": true,
+ "esModuleInterop": true,
+ "suppressImplicitAnyIndexErrors": true,
+ "noUnusedLocals": true,
+ "allowSyntheticDefaultImports": true,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noImplicitUseStrict": true,
+ "alwaysStrict": false,
+ "downlevelIteration": true,
+ "baseUrl": "./",
+ "paths": {
+ "Components/*": ["src/components/*"],
+ "Src": ["src"],
+ "Utils/*": ["src/utils/*"],
+ "Models": ["src/models"],
+ "Services": ["src/services"],
+ "Constants": ["src/constants"],
+ "@hooks/*": ["src/hooks/*"],
+ "@src/*": ["src/*"]
+ },
+ "typeRoots": [
+ "./node_modules/@types",
+ "custom_typings",
+ "./typings/**/*.d.ts"
+ ]
+ },
+ "exclude": [
+ "build",
+ "scripts",
+ "webpack",
+ "jest",
+ "bin",
+ "runtime",
+ "view",
+ "public_gen",
+ "public",
+ "node_modules",
+ "coverage",
+ ".vscode"
+ ],
+ "includes": ["src/**/*.ts", "src/**/*.tsx", "global.d.ts"],
+ "types": ["typePatches"]
+ }
+
\ No newline at end of file
diff --git a/ui/webpack.config.js b/ui/webpack.config.js
new file mode 100644
index 0000000000..9b84a5dc72
--- /dev/null
+++ b/ui/webpack.config.js
@@ -0,0 +1,30 @@
+/**
+ * @file test cron
+ * @author lpx
+ * @since 2020/08/19
+ */
+const devConfig = require('./config/webpack.dev');
+const prodConfig = require('./config/webpack.prod');
+
+const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
+
+const smp = new SpeedMeasurePlugin();
+
+let config = devConfig;
+
+switch (process.env.NODE_ENV) {
+ case 'prod':
+ case 'production':
+ config = prodConfig;
+ break;
+ case 'dev':
+ case 'development':
+ config = devConfig;
+ break;
+ default:
+ config = devConfig;
+}
+
+const webpackConfig = config;
+
+module.exports = smp.wrap(webpackConfig);