diff --git a/package.json b/package.json index 3b929aa95..5db6dfe31 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ }, "resolutions": { "@types/react": "17.0.14", - "@types/react-dom": "17.0.14" + "@types/react-dom": "17.0.14", + "@velaux-plugins/vela-adopter": "portal:/Users/qiaozp/repo/vela-adopter" }, "devDependencies": { "@babel/core": "7.20.5", diff --git a/packages/velaux-ui/src/layout/AppRootPage/index.less b/packages/velaux-ui/src/layout/AppRootPage/index.less new file mode 100644 index 000000000..546b03e27 --- /dev/null +++ b/packages/velaux-ui/src/layout/AppRootPage/index.less @@ -0,0 +1,16 @@ +.info-item { + white-space: nowrap; + min-width: 80px; +} +.page-header{ + .page-header-logo { + min-width: 50px; + } + .page-header-title{ + margin-top: 0px; + } + .basic-info{ + width: fit-content; + font-size: 18px; + } +} diff --git a/packages/velaux-ui/src/layout/AppRootPage/index.tsx b/packages/velaux-ui/src/layout/AppRootPage/index.tsx index 7af0bdd51..1857e1989 100644 --- a/packages/velaux-ui/src/layout/AppRootPage/index.tsx +++ b/packages/velaux-ui/src/layout/AppRootPage/index.tsx @@ -3,12 +3,17 @@ import * as React from 'react'; import { Translation } from '../../components/Translation'; import { locationService } from '../../services/LocationService'; import { getPluginSrv, importAppPagePlugin } from '../../services/PluginService'; +import { Box, Button, Divider, Grid, Loading, Tab } from "@alifd/next"; +import './index.less' +import { connect } from "dva"; interface Props { pluginId: string; + dispatch: any; + loading: any; } -export function AppRootPage({ pluginId }: Props) { +function RootPage({ pluginId }: Props) { const [app, setApp] = React.useState(); React.useEffect(() => { loadAppPlugin(pluginId, setApp); @@ -28,7 +33,7 @@ export function AppRootPage({ pluginId }: Props) { ); } -export function AppConfigPage({ pluginId }: Props) { +function ConfigPage({ pluginId, dispatch, loading }: Props) { const [app, setApp] = React.useState(); React.useEffect(() => { loadAppPlugin(pluginId, setApp); @@ -40,11 +45,90 @@ export function AppConfigPage({ pluginId }: Props) { ) } + const meta = app.meta + const { Col, Row } = Grid + + const isEnabled = meta.enabled + const isInstalled = !!meta.info + + const enablePlugin = (id: string) => { + console.log('dispatch', dispatch) + dispatch({ + type: 'plugins/enablePlugin', + payload: { id }, + callback: () => { + location.reload() + } + }); + } + const disablePlugin = (id: string) => { + dispatch({ + type: 'plugins/disablePlugin', + payload: { id }, + callback: () => { + location.reload() + } + }); + } + const pluginLoading = loading.models.plugins || false return ( -
- -
+ <> + + + + + + +

{meta.name}

+ + +
Version
+
{meta.info.version}
+ + + + + + +
Author
+
{meta.info.author?.name ?? "Unknown"}
+
+ + + + + +
Status
+
{ + meta.enabled ? Enabled : Disabled + }
+ +
+

{meta.info.description ?? "No descriptions"}

+
+ + + {isInstalled && !isEnabled && + + + + } + { + isInstalled && isEnabled && + + + + } + + +
+ + + + + + ) } @@ -67,3 +151,10 @@ async function loadAppPlugin( console.log(err) } } + +const mapStateToProps = (store: any) => ({ + ...store.plugins, loading: store.loading +}) + +export const AppConfigPage = connect(mapStateToProps)(ConfigPage) +export const AppRootPage = connect(mapStateToProps)(RootPage) diff --git a/packages/velaux-ui/src/model/addons.js b/packages/velaux-ui/src/model/addons.js index 5ccdbfdf1..baa4a8d44 100644 --- a/packages/velaux-ui/src/model/addons.js +++ b/packages/velaux-ui/src/model/addons.js @@ -38,12 +38,18 @@ export default { let uxPlugins = [] for (const addon of result.addons) { // TODO: remove this hack - if (addon.name === 'example'&& addon.uxPlugins) { + if (addon.name === 'example' && addon.uxPlugins) { addon.uxPlugins['node-dashboard'] = "https://kubevela-docs.oss-accelerate.aliyuncs.com/binary/example/node-dashboad.tar.gz" } if (addon.uxPlugins && Object.keys(addon.uxPlugins).length > 0) { for (const [key, value] of Object.entries(addon.uxPlugins)) { uxPlugins.push({id: key, url: value}) + // Add ten test data + if (key === 'node-dashboard') { + for (let i = 0; i < 10; i++) { + uxPlugins.push({id: key + i, url: value}) + } + } } } } diff --git a/packages/velaux-ui/src/model/plugins.js b/packages/velaux-ui/src/model/plugins.js index 654929244..a7fb2358f 100644 --- a/packages/velaux-ui/src/model/plugins.js +++ b/packages/velaux-ui/src/model/plugins.js @@ -57,16 +57,22 @@ export default { addBatchPluginToCache(state, {type, payload}) { // add the plugin to pluginList if not exist const pluginList = state.pluginList; + const enabledPlugins = state.enabledPlugins // make a copy to newPluginList const newPluginList = pluginList.slice(); for (const plugin of payload) { if (!newPluginList.find(p => p.id === plugin.id)) { newPluginList.push(plugin); + if(plugin.enabled){ + enabledPlugins.push(plugin); + } } } + console.log(newPluginList,enabledPlugins); return { ...state, pluginList: newPluginList, + enabledPlugins: enabledPlugins, } }, removePluginDetail(state, {type, payload}) { @@ -111,7 +117,7 @@ export default { * getPluginList(action, {call, put}) { const res = yield call(getPluginList, action.payload); if (res) { - yield put({type: 'updatePluginList', payload: res}); + yield put({type: 'addBatchPluginToCache', payload: res.plugins}); } }, * detailPlugin(action, {call, put}) { diff --git a/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.less b/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.less new file mode 100644 index 000000000..61e8393af --- /dev/null +++ b/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.less @@ -0,0 +1,25 @@ +.plugin-card { + color: black; + height: fit-content; + background: #eeeeee; + border: 0; + + :hover { + background-color: #e0e0e0; + } + .plugin-card-content{ + margin-top: 10px; + display: flex; + flex-direction: column; + justify-content: space-between; + + .plugin-desc{ + height: 70px; + overflow: hidden; + text-overflow: ellipsis; + } + } + .install-btn{ + float: right; + } +} diff --git a/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.tsx b/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.tsx new file mode 100644 index 000000000..c29e8f087 --- /dev/null +++ b/packages/velaux-ui/src/pages/Addons/components/plugin-card/index.tsx @@ -0,0 +1,187 @@ +import React from 'react'; +import { FaLink } from 'react-icons/fa'; + +import './index.less'; + +import { Card, Grid, Tag } from '@alifd/next'; + +type State = { + iconValid: boolean +}; + +type Props = { + id: string + enabled?: boolean + installed?: boolean + icon?: string + description?: string + url?: string + + // default empty array + tags: string[] + history?: { + push: (path: string, state?: any) => void; + location: { + pathname: string; + }; + }; +}; + + +class PluginCard extends React.Component { + static defaultProps = { + tags: [], + } + + constructor(props: Props) { + super(props); + this.state = { + iconValid: true + }; + } + + checkImage = (icon?: string) => { + if (icon && icon !== 'none' && icon !== '') { + const img = new Image(); + img.src = icon; + img.onload = () => { + this.setState((preState) => { + return { ...preState, iconValid: true }; + }); + } + img.onerror = () => { + this.setState((preState) => { + return { ...preState, iconValid: false }; + }); + } + } else { + this.setState((preState) => { + return { ...preState, iconValid: false }; + }); + } + }; + + componentDidMount() { + this.checkImage(this.props.icon); + } + + componentDidUpdate(prevProps: Readonly) { + if (prevProps.icon !== this.props.icon) { + this.checkImage(this.props.icon); + } + } + + handleGoToPage = (id: string) => { + this.props.history?.push(`/plugins/${id}`) + } + + handleGoToPluginConfig = (id: string) => { + this.props.history?.push(`/plugin-config/${id}`) + } + + + render() { + const { Row, Col } = Grid; + const { + id, + icon, + tags, + description, + enabled, + installed, + url, + } = this.props; + console.log(url) + + const nameUpper = (name: string) => { + return name + .split('-') + .map((sep) => { + if (sep.length > 0) { + return sep.toUpperCase()[0]; + } + return sep; + }) + .toString() + .replace(',', ''); + }; + + const renderIcon = (name: string, icon?: string) => { + if (this.state.iconValid) { + return ; + } else { + return ( +
+ {nameUpper(name)} +
+ ); + } + } + + if (enabled && !tags.some((t) => t == "enabled")) { + tags.unshift("enabled") + } + if (installed && !enabled && !tags.some((t) => t == "installed")) { + tags.unshift("installed") + } + + return ( +
+ this.handleGoToPluginConfig(id)}> + + + +
+ {renderIcon(id, icon)} +
+ + + {enabled && +
{ + e.stopPropagation(); + this.handleGoToPage(id) + }}> + {id} + + } + { + !enabled &&
{id}
+ } + + +
+ +

{description ? description : "No descriptions"}

+
+ +
+ {tags.map((t: string) => { + return ( + {t} + ); + } + )} +
+
+
+ + +
+ + ); + } +} + +export default PluginCard; diff --git a/packages/velaux-ui/src/pages/Addons/components/plugin/index.tsx b/packages/velaux-ui/src/pages/Addons/components/plugin/index.tsx index 59407c8f4..139de13f2 100644 --- a/packages/velaux-ui/src/pages/Addons/components/plugin/index.tsx +++ b/packages/velaux-ui/src/pages/Addons/components/plugin/index.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { connect } from 'dva'; import './index.less'; -import { Box, Button, Dialog, List, Message } from "@alifd/next"; +import { Grid, Message } from "@alifd/next"; import type { KeyValue, PluginMeta, } from '@velaux/data'; -import { PluginType } from "@velaux/data"; -import PluginConfig from "../plugin-config"; import i18n from "../../../../i18n"; +import { If } from "../../../../components/If"; import Empty from "../../../../components/Empty"; +import PluginCard from "../plugin-card"; type State = { iconValid: KeyValue; @@ -17,7 +17,7 @@ type State = { type Props = { dispatch: ({}) => {}; - pluginList?: PluginMeta[]; + pluginList: PluginMeta[]; enabledPlugins?: PluginMeta[]; errorMessage?: string; history?: { @@ -28,6 +28,17 @@ type Props = { }; }; +function pluginEnabled(p: PluginMeta) { + return p.enabled; +} + +function pluginInstalled(p: PluginMeta) { + return !!p.info +} + +function pluginUninstalled(p: PluginMeta) { + return !pluginInstalled(p) +} @connect((store: any) => { return { ...store.plugins }; @@ -41,6 +52,10 @@ class Plugin extends React.Component { }; } + static defaultProps = { + pluginList: [], + } + installPlugin(id: string, url: string) { this.props.dispatch({ type: 'plugins/installPlugin', @@ -93,6 +108,7 @@ class Plugin extends React.Component { } }); } + this.sortedPlugins() } componentDidUpdate(prevProps: Readonly) { @@ -130,105 +146,59 @@ class Plugin extends React.Component { } }; - handleConfigClose = () => { - const plugin = this.state.currentPlugin - if (!plugin || !plugin.id) { - return - } - this.props.dispatch({ - type: 'plugins/detailPlugin', - payload: { id: plugin.id }, - }) - this.setState({ - showConfig: false, - }) - - } - - handleGoToPage = (plugin: PluginMeta) => { - const { id } = plugin; - this.props.history?.push(`/plugins/${id}`) - } - handleGoToPluginConfig = (plugin: PluginMeta) => { - const { id } = plugin; - this.props.history?.push(`/plugin-config/${id}`) + sortedPlugins = () => { + // put enabled plugin first, then installed plugin, then others + const { pluginList } = this.props + let enabledPlugins = pluginList.filter(pluginEnabled) + let uninstalledPlugins = pluginList.filter(pluginUninstalled) + let installedPlugins = pluginList.filter((p) => { + return pluginInstalled(p) && !pluginEnabled(p) + }) + return [...enabledPlugins, ...installedPlugins, ...uninstalledPlugins] } render() { const { pluginList, enabledPlugins } = this.props; - const { currentPlugin, showConfig } = this.state; + console.log(pluginList) + const { Row, Col } = Grid; return (
- this.setState({ showConfig: false })} - style={{ width: '80%', height: '80%', overflow: 'auto' }} - closeMode={['close', 'esc', 'mask']} - > - {(() => { - if (currentPlugin) { - return - } - return - })()} - - - {pluginList?.map((plugin) => { - const isEnabled = enabledPlugins?.some((p) => p.id === plugin.id); - const isInstalled = !!plugin.info; - - return ( - -
- { - if (plugin.type === PluginType.PageApp) { - this.handleGoToPage(plugin) - } - }}> -
{plugin.id}
-
-
- - {isInstalled && isEnabled && ( - - )} - - {!isInstalled && ( - - )} - - {isInstalled && !isEnabled && ( - - )} - - {isInstalled && isEnabled && ( - - )} - - {isInstalled && !isEnabled && ( - - )} - - -
-
-
- ); - })} -
+ +
+ + {pluginList && this.sortedPlugins().map((plugin: PluginMeta, index: number) => { + return ( + +
+ p.id === plugin.id)} + installed={!!plugin.info} + description={plugin.info?.description} + tags={[]} + history={this.props.history} + url={plugin.url} + /> +
+ + ) + }) + } +
+
+ +
+ + + +
); - } - ; + }; } export default Plugin; + diff --git a/packages/velaux-ui/src/pages/Addons/index.tsx b/packages/velaux-ui/src/pages/Addons/index.tsx index d8bca51ab..502b4b0c4 100644 --- a/packages/velaux-ui/src/pages/Addons/index.tsx +++ b/packages/velaux-ui/src/pages/Addons/index.tsx @@ -147,6 +147,7 @@ class Addons extends React.Component { plugin } = this.props; + console.log(loading) const addonLoading = loading.models.addons; const pluginLoading = loading.models.plugins; const { showAddonDetail, addonName, showRegistryManage, tagList, selectTags } = this.state; diff --git a/packages/velaux-ui/src/services/plugin/PluginCache.ts b/packages/velaux-ui/src/services/plugin/PluginCache.ts index 18fc6b58f..354140bf8 100644 --- a/packages/velaux-ui/src/services/plugin/PluginCache.ts +++ b/packages/velaux-ui/src/services/plugin/PluginCache.ts @@ -1,6 +1,6 @@ import { PluginMeta } from '@velaux/data'; import { getBackendSrv } from '../BackendService'; -import {managePlugin} from "../../api/productionLink"; +import { managePlugin } from "../../api/productionLink"; const cache: Record = {}; const initializedAt: number = Date.now(); diff --git a/yarn.lock b/yarn.lock index 338ddddfa..65d770495 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6287,67 +6287,6 @@ __metadata: languageName: unknown linkType: soft -"@velaux-plugins/vela-adopter@workspace:plugins/vela-adopter": - version: 0.0.0-use.local - resolution: "@velaux-plugins/vela-adopter@workspace:plugins/vela-adopter" - dependencies: - "@alifd/next": 1.26.14 - "@babel/core": ^7.16.7 - "@grafana/tsconfig": ^1.2.0-rc1 - "@kubernetes/client-node": 0.18.0 - "@swc/core": ^1.2.144 - "@swc/helpers": ^0.3.6 - "@swc/jest": ^0.2.20 - "@testing-library/jest-dom": ^5.16.2 - "@testing-library/react": ^12.1.3 - "@types/glob": ^8.0.0 - "@types/jest": ^27.4.1 - "@types/lodash": latest - "@types/node": ^17.0.19 - "@types/react": ^16.3.0 - "@types/react-cookies": ^0.1.0 - "@types/react-copy-to-clipboard": ^5.0.2 - "@types/react-dom": ^17.0.0 - "@types/react-router": ^5.1.20 - "@types/react-router-dom": ^5.3.3 - "@typescript-eslint/eslint-plugin": ^4.33.0 - "@typescript-eslint/parser": ^4.33.0 - "@velaux/data": 0.0.4 - "@velaux/ui": 0.0.4 - copy-webpack-plugin: ^10.0.0 - css-loader: 6.7.1 - eslint: ^7.32.0 - eslint-config-prettier: ^8.3.0 - eslint-plugin-jsdoc: ^36.1.0 - eslint-plugin-prettier: ^4.0.0 - eslint-plugin-react: ^7.26.1 - eslint-plugin-react-hooks: ^4.2.0 - eslint-webpack-plugin: ^3.1.1 - fork-ts-checker-webpack-plugin: ^7.2.0 - glob: ^8.0.3 - identity-obj-proxy: 3.0.0 - jest: 27.5.0 - less-loader: 11.1.0 - mini-css-extract-plugin: 2.7.5 - prettier: ^2.5.0 - react: 17.0.2 - react-dom: 17.0.2 - react-scripts: ^3.4.0 - replace-in-file-webpack-plugin: ^1.0.6 - sass: 1.55.0 - sass-loader: 13.1.0 - style-loader: 3.3.1 - swc-loader: ^0.1.15 - ts-node: ^10.5.0 - tsconfig-paths: ^3.12.0 - tslib: 2.5.0 - typescript: ^4.4.0 - webpack: ^5.69.1 - webpack-cli: ^4.9.2 - webpack-livereload-plugin: ^3.0.2 - languageName: unknown - linkType: soft - "@velaux/data@0.0.4, @velaux/data@^0.0.4, @velaux/data@workspace:packages/velaux-data": version: 0.0.0-use.local resolution: "@velaux/data@workspace:packages/velaux-data"