diff --git a/public/logo_icon.svg b/public/logo_icon.svg new file mode 100755 index 00000000..5e6fc0e6 --- /dev/null +++ b/public/logo_icon.svg @@ -0,0 +1,20 @@ + + + + Rectangle + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Base/Icon/index.scss b/src/components/Base/Icon/index.scss index 645567ad..3182a6d2 100644 --- a/src/components/Base/Icon/index.scss +++ b/src/components/Base/Icon/index.scss @@ -2,15 +2,15 @@ :global { .#{$icon-prefix}-light { - --primary-color: #{$P100}; - --secondary-color: #{$P30}; + --primary-color: #{$P300}; + --secondary-color: #{$P45}; //--primary-opacity: 0.9; //--secondary-opacity: 0.4; } .#{$icon-prefix}-dark { - --primary-color: #{$N500}; - --secondary-color: #{$N65}; + --primary-color: #{$N300}; + --secondary-color: #{$N45}; //--primary-opacity: 0.9; //--secondary-opacity: 0.4; } diff --git a/src/components/Base/Popover/index.scss b/src/components/Base/Popover/index.scss index d0fe7c81..1b2ae9ac 100644 --- a/src/components/Base/Popover/index.scss +++ b/src/components/Base/Popover/index.scss @@ -4,7 +4,7 @@ :global { .pi-popover-popper { min-width: 100px; - padding: 7px 0; + padding: 4px 0; font-size: $body-size; border-radius: 2px; background-color: $N0; diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx index 84ade9ed..144eeaca 100644 --- a/src/components/Header/index.jsx +++ b/src/components/Header/index.jsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { NavLink, withRouter } from 'react-router-dom'; +import { NavLink, withRouter, Link } from 'react-router-dom'; import { observer, inject } from 'mobx-react'; import { translate } from 'react-i18next'; @@ -52,7 +52,7 @@ class Header extends Component { activeClassName={styles.active} isActive={this.isLinkActive.bind(null, 'store')} > - {t('Store')} + {t('App Store')} }> {username} - + ); @@ -103,34 +103,46 @@ class Header extends Component { match, rootStore: { fixNav } } = this.props; - - const { isNormal } = this.props.user; - const logoUrl = !isHome || fixNav ? '/logo_light.svg' : '/logo_dark.svg'; - const needShowSearch = isHome && fixNav; + const logoUrl = fixNav ? '/logo_light.svg' : '/logo_dark.svg'; const appSearch = match.params.search; - return ( -
-
- -
- {isNormal && !isHome && this.renderMenus()} + if (isHome) { + return ( +
+
+ {this.renderMenuBtns()} + {fixNav && ( + + )}
- {needShowSearch && ( - - )} +
+ ); + } + + return ( +
+
+ + + + {this.renderMenus()} + {this.renderMenuBtns()} + + + + {t('UPGRADE_PROVIDER')} +
); diff --git a/src/components/Header/index.scss b/src/components/Header/index.scss index c0f352e0..eee1ea6e 100644 --- a/src/components/Header/index.scss +++ b/src/components/Header/index.scss @@ -21,38 +21,6 @@ $profile-hover-color: #efe6f8; z-index: 99; background: none; - &.deep { - background: linear-gradient(to left, $P75, #854fb9 32%, #484999); - } - - &.deep, &.deepHome { - position: fixed; - - .login{ - color: $N0; - } - - .user [class*='target']{ - color: $N0; - - :global{ - .icon-caret-down svg{ - --primary-color: #{$N0}; - --secondary-color: #{$N0}; - } - } - } - } - - - &.deepHome { - animation-duration: 0.5s; - animation-name: deepHeader; - animation-iteration-count: 1; - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - } - .wrapper { width: $content-width; height: $header-height; @@ -142,60 +110,156 @@ $profile-hover-color: #efe6f8; } } - .user [class*='target']{ + .user { float: right; - margin: 12px 24px; - height: 20px; - width: auto; - line-height: 20px; - opacity: 0.9; - color: $N500; - font-weight: 500; - border: 0 none; - &:hover{ + [class*='target'] { + margin: 16px 0 16px 8px; + height: 16px; + width: auto; + line-height: 16px; + font-size: 14px; + opacity: 0.9; + color: $N500; + font-weight: 500; border: 0 none; - box-shadow: none; - opacity: 1; - } - :global{ - .icon-caret-down{ - position: relative; - top: 3px; - left: 2px; + .icon { + float: right; + margin-left: 2px; svg { --primary-color: #{$N500}; --secondary-color: #{$N500}; } } + + &:hover{ + border: 0 none; + box-shadow: none; + opacity: 1; + } } } - :global{ .pi-popover-popper{ - margin-left: -40px !important; + margin-left: -95px !important; margin-top: 6px !important; } } } -.menuOuter { - .menus{ +.deepInner, +.deepHome { + position: fixed; +} + +.deepHome { + background: linear-gradient(to left, $P75, #854fb9 32%, #484999); + animation-duration: 0.5s; + animation-name: deepHeader; + animation-iteration-count: 1; + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; + + .login { + color: $N0; + } + + .user [class*='target']{ + color: $N0; + + .icon svg{ + --primary-color: #{$N0}; + --secondary-color: #{$N0}; + } + } +} + +.deepInner { + background-color: $N400; + + .logoIcon { float: left; - margin-left: 50px; - a{ + display: inline-block; + margin-top: 12px; + width: 24px; + height: 24px; + border-radius: 2px; + background-color: $N500; + + .icon { + margin: 7px; + } + } + + .menus { + display: inline-block; + margin-left: 8px; + a { display: inline-block; - margin: 0 12px; - padding: 13px 0; - font-size: 14px; - font-weight: 500; - line-height: 20px; - color: $N0; - &.active{ - border-bottom: 2px solid $Y200; + margin: 0 8px; + padding: 16px 0; + font-size: 12px; + line-height: 16px; + color: $N65; + + &:hover { + color: $N30; + } + + &.active { + font-weight: 500; + color: $N10; + } + } + } + + .user [class*='target'] { + font-size: 12px; + color: $N65; + opacity: 1; + + .icon svg{ + --primary-color: #{$N65}; + --secondary-color: #{$N65}; + } + + &:hover { + color: $N30; + + .icon svg { + --primary-color: #{$N30}; + --secondary-color: #{$N30}; } } } + + .mail { + float: right; + margin: 14px 8px; + } + + .upgrade { + float: right; + margin: 16px 8px; + line-height: 16px; + font-size: 12px; + font-weight: 500; + color: $Y100; + + .shield { + float: left; + margin-right: 4px; + svg { + --primary-color: #{$Y100}; + --secondary-color: #{$Y100}; + } + } + + &:hover { + color: $Y65; + } + } } + diff --git a/src/components/Layout/SideNav/index.jsx b/src/components/Layout/SideNav/index.jsx index 9c068a0b..c6ea855f 100644 --- a/src/components/Layout/SideNav/index.jsx +++ b/src/components/Layout/SideNav/index.jsx @@ -7,13 +7,24 @@ import classnames from 'classnames'; import _ from 'lodash'; import { Icon, Popover, Image, Tooltip } from 'components/Base'; +import Status from 'components/Status'; import MenuLayer from 'components/MenuLayer'; -import { subNavMap, getNavs } from './navMap'; +import { subNavMap, getNavs, getDevSubNavs, getBottomNavs } from './navMap'; import styles from './index.scss'; -const keys = ['app', 'review', 'cluster', 'runtime', 'repo', 'categories', 'category', 'user']; +const keys = [ + 'app', + 'review', + 'cluster', + 'runtime', + 'repo', + 'categories', + 'category', + 'user', + 'create' +]; const changeKey = { review: 'app', cluster: 'repo', @@ -32,6 +43,7 @@ const changeKey = { class SideNav extends React.Component { static propTypes = { isScroll: PropTypes.bool, + hasSubNav: PropTypes.bool, className: PropTypes.string }; @@ -46,6 +58,14 @@ class SideNav extends React.Component { } } + becomeDeveloper = isNormal => { + const { rootStore } = this.props; + rootStore.updateUser({ + changedRole: isNormal ? '' : 'user' + }); + location.href = '/dashboard'; + }; + getMatchKey = () => { const { path } = this.props.match; const key = _.find(keys, k => path.indexOf(k) > -1) || 'dashboard'; @@ -53,12 +73,8 @@ class SideNav extends React.Component { return changeKey[key] || key; }; - isLinkActive = (activeName, role) => { - let key = this.getMatchKey(); - if (role === 'developer' && key === 'app') { - key = 'repo'; - } - + isLinkActive = activeName => { + const key = this.getMatchKey(); return activeName === key; }; @@ -67,48 +83,95 @@ class SideNav extends React.Component { return subNavMap[key]; }; - renderNav(role) { - const { t } = this.props; - - let navs = getNavs(role); + renderNav() { + const { user, appStore, history, t } = this.props; + const { role, isDev } = user; + let navs = getNavs[role]; + const { menuApps } = appStore; + let bottomNavs = getBottomNavs; + const { pathname } = history.location; - if (role === 'developer') { - navs = navs.slice(0, 3); + if (isDev) { + navs = menuApps.concat(navs); + bottomNavs = bottomNavs.slice(2); } return ( -
    - {navs.map(nav => ( - -
  • - this.isLinkActive(nav.active, role)} - > - +
    +
      +
    • + + + + +
    • + {navs.map(nav => ( +
    • + + {nav.app_id && ( + -1 + })} + > + + + )} + {nav.iconName && ( + + )} + + +
    • - - ))} -
    + ))} +
+
    + {bottomNavs.map( + nav => + nav.iconName === 'human' ? ( +
  • + } className={styles.iconOuter}> + + + + + +
  • + ) : ( +
  • + + + + +
  • + ) + )} +
+
); } renderSubDev() { const { t } = this.props; const { url } = this.props.match; + const { appDetail } = this.props.appStore; if (url === '/dashboard') { return ( @@ -124,51 +187,29 @@ class SideNav extends React.Component { ); } - const { menuApps } = this.props.appStore; - return (
-
{t('My Apps')}
-
- {menuApps.map(app => ( - -1 })} - title={app.name} - to={`/dashboard/app/${app.app_id}`} - > - - {app.name} - - ))} - - - - - - -
- -1 })} - to="/dashboard/repos" - > - {t('Repos')} - -
- {t('Test')} +
+
{appDetail.name}
+
- -1 })} - to="/dashboard/clusters" - > - {t('Clusters')} - - -1 })} - to="/runtimes" - > - {t('Runtimes')} - + + {getDevSubNavs.map(nav => ( +
+
{t(nav.title)}
+ {nav.items.map(item => ( + -1 + })} + to={item.link} + > + {t(item.name)} + + ))} +
+ ))}
); } @@ -180,7 +221,9 @@ class SideNav extends React.Component { return (
-
{t(subNavData.title)}
+
+
{t(subNavData.title)}
+
{subNavData.links.map(link => ( -
- {this.renderNav(role)} - {isDev && this.renderSubDev()} - {isAdmin && this.renderSubAdmin()} -
- - {this.renderHeader()} + {this.renderNav()} + {hasSubNav && isDev && this.renderSubDev()} + {hasSubNav && isAdmin && this.renderSubAdmin()} ); } diff --git a/src/components/Layout/SideNav/index.scss b/src/components/Layout/SideNav/index.scss index 447bc9e3..a908b42d 100644 --- a/src/components/Layout/SideNav/index.scss +++ b/src/components/Layout/SideNav/index.scss @@ -1,179 +1,195 @@ @import '~scss/vars'; -.menu { - display: inline-block; +.nav { position: fixed; left: 0; top: 0; - z-index: 4; - width: $menu-width; + z-index: 3; + width: $menu-nav-width; + height: 100vh; + background-color: $N0; + border-right: 1px solid $N10; + transition: all 0.3s ease-out 0s; - .nav { - float: left; - width: $menu-nav-width; - height: 100vh; - background-image: linear-gradient(to top, $P75, #854fb9 32%, #484999); + &:hover { + width: $menu-width;; - .item { - [class*='target'] { - width: auto; - height: auto; - border: 0 none; - } + .bottomNav { + width: $menu-width; + } + } - [class*='popper']{ - border: 0 none; - &:active, &:hover { - border: 0 none; - } - } + li { + height: 40px; + overflow: hidden; + + a { + color: $N300; } - li{ - height: 48px; + .icon { + float: left; + margin: 10px 22px; + } - >a { - display: inline-block; - height: 48px; - width: 100%; - text-align: center; - } + .title { + margin-left: -4px; + line-height: 40px; + cursor: pointer; + } - .active { - background-color: #343945; - cursor: auto; - } - &:hover { - background-color: #343945; - cursor: auto; + &:hover { + a { + color: $P75; } - transition: all .3s ease-in-out; - :global { - .icon { - margin: 12px; - } - - .icon-op-logo{ - margin: 16px; - } + .icon svg { + --primary-color: #{$N300}; + --secondary-color: #{$P30}; } } } } -.subNav { - float: left; - height: 100vh; - width: $menu-sub-nav-width; - background-color: #343945; - box-shadow: 0 1px 4px 0 rgba(73, 33, 173, 0.07); +.topNav { + >li { + .imageOuter { + float: left; + display: inline-block; + width: 20px; + height: 20px; + margin: 9px 22px; + border: 1px solid $N30; - .title { - margin: 0 24px; - font-size: 16px; - font-weight: 900; - line-height: 48px; - color: $N0; - } + &:hover, &.activeApp { + border-color: $P75; + } - .apps { - margin: 0 24px 12px; + .image { + margin: 2px; + } + } - >a { - display: inline-block; - box-sizing: border-box; - height: 32px; - border-radius: 2px; - border: 1px solid $N400; - background-color: $N400; - text-align: center; - cursor: pointer; - opacity: 0.5; + &:nth-child(1) { + margin-bottom: 14px; + height: 63px; + border-bottom: 1px solid $N10; - &:hover { - opacity: 1; + .icon { + margin: 23px 24px 16px 24px; } - } - .app { - margin-bottom: 8px; - width: 100%; - height: 32px; - padding: 6px 12px; - font-size: 12px; - font-weight: 500; - //line-height: 16px; - color: $N0; - text-align: left; - @include textCut; - - &.active { - border-color: $Y300; - opacity: 1; + .title { + line-height: 64px; + font-size: 16px; + color: $N500; + font-weight: 500; + cursor: default; } + } + } + + :global { + .icon-more{ + transform: rotate(90deg); + } + } +} - img { - margin-right: 4px; - width: 16px; - max-width: 100%; - max-height: 100%; - vertical-align: middle; - border-radius: 50%; + +.bottomNav { + >li { + .iconOuter { + [class*='target'] { + width: auto; + height: auto; + border-radius: 0; + border: 0 none; } - .appName { - vertical-align: middle; + :global{ + .pi-popover-popper{ + margin-left: -10px !important; + margin-bottom: -30px !important; + } } } - .plus { - width: 104px; - line-height: 36px; + .icon svg { + opacity: 0.6; } - .more { - float: right; - width: 40px; - line-height: 36px; + &:last-child { + margin-bottom: 14px; } } +} - .test { - margin: 5px 0 16px; - height: 32px; - border-bottom: 1px solid rgba($N400, 0.4); +@media screen and (min-height: 550px) { + .bottomNav { + position: fixed; + bottom: 0; + left: 0; + z-index: 4; + width: $menu-nav-width; + } +} - .word { - display: inline-block; - position: relative; - bottom: -16px; - left: 22px; - padding: 0 3px; - line-height: 32px; + +.subNav { + position: fixed; + left: $menu-nav-width; + top: 0; + z-index: 2; + height: 100vh; + width: $menu-sub-nav-width; + background-color: $N0; + box-shadow: 0 1px 7px 0 rgba(71, 78, 93, 0.1); + + .title { + margin: 24px 0; + padding-left: 20px; + + .name { + line-height: 16px; + font-size: 16px; + font-weight: 500; + color: $N500; + } + + .status { + display: block; + margin-top: 8px; font-size: 12px; - color: #69748c; - background-color: #343945; + color: $N75; } } + + .subContent { + margin-bottom: 28px; + } + .subTitle { + padding-left:20px; + line-height: 24px; + font-size: 12px; + color: $N65; + } .link { display: block; - padding: 9px 24px; - line-height: 14px; + padding: 0 20px; + line-height: 32px; font-size: 14px; - color: $N30; + color: $N300; &:hover { - color: $N0; + color: $P75; } &.active { font-weight: 500; - border-right: 2px solid $Y200; - color: $N0; + color: $P75; } } - } .header{ @@ -193,7 +209,6 @@ height: 20px; width: auto; line-height: 20px; - opacity: 0.9; color: $N500; font-weight: 500; border: 0 none; @@ -243,6 +258,7 @@ background-color: $N10; color: $N500; } + &.line{ border-bottom: 1px solid $N10; } diff --git a/src/components/Layout/SideNav/navMap.js b/src/components/Layout/SideNav/navMap.js index 129e4b82..a8e36ab8 100644 --- a/src/components/Layout/SideNav/navMap.js +++ b/src/components/Layout/SideNav/navMap.js @@ -15,10 +15,9 @@ export const subNavMap = { user: { title: 'Users', links: [ - { name: 'All Users', link: '/dashboard/users', active: 'user' } - /* { name: 'User Groups', link: '#', active: 'group' }, - { name: 'Roles', link: '#', active: 'role' }, - { name: 'Policy', link: '#', active: 'policy' }*/ + { name: 'All Users', link: '/dashboard/users', active: 'user' }, + { name: 'Roles', link: '#', active: 'role' }, + { name: 'Permission and Policy', link: '#', active: 'policy' } ] }, repo: { @@ -34,35 +33,160 @@ export const subNavMap = { } }; -export const getNavs = role => [ +export const getNavs = { + global_admin: [ + { + link: '/dashboard', + iconName: 'dashboard', + active: 'dashboard', + title: 'My dashboard' + }, + { + link: '/dashboard/apps', + iconName: 'components', + active: 'app', + title: 'App Store' + }, + { + link: '/dashboard/repos', + iconName: 'shield', + active: 'repo', + title: 'App service provider ISV' + }, + { + link: '#', + iconName: 'ticket', + active: '', + title: 'Work list' + }, + { + link: '#', + iconName: 'wallet', + active: '', + title: 'Financial Center' + }, + { + link: '#', + iconName: 'linechart', + active: '', + title: 'Message and monitoring' + }, + { + link: '/dashboard/users', + iconName: 'group', + active: 'user', + title: 'Users' + }, + { + link: '#', + iconName: 'cogwheel', + active: '', + title: 'Settings' + } + ], + developer: [ + { + link: '/dashboard/app/create', + iconName: 'plus-square', + active: 'create', + title: 'Create app' + }, + { + link: '/dev/apps', + iconName: 'more', + active: 'app', + title: 'View all' + } + ] +}; + +export const getBottomNavs = [ + { + link: '#', + iconName: 'magnifier', + active: '', + title: 'Global search' + }, + { + link: '#', + iconName: 'bell', + active: '', + title: 'Alarms' + }, { - link: '/', - iconName: 'op-logo', + link: '#', + iconName: 'mail', active: '', - title: 'Home' + title: 'My news' + }, + { + link: '/profile', + iconName: 'human', + active: 'profile', + title: 'My account' + } +]; + +export const getDevSubNavs = [ + { + title: 'Development', + items: [ + { name: 'Version management', link: '#' }, + { name: 'Audit record', link: '#' }, + { name: 'App information', link: '#' } + ] + }, + { + title: 'Operation and maintenance', + items: [ + { name: 'Monitor', link: '#' }, + { name: 'Event', link: '#' }, + { name: 'App information', link: '#' } + ] + }, + { + title: 'Customer', + items: [ + { name: 'Example', link: '#' }, + { name: 'Work list', link: '#' }, + { name: 'News', link: '#' } + ] + }, + { + title: 'Sandbox', + items: [{ name: 'Example', link: '#' }, { name: 'Environment', link: '#' }] + } +]; + +export const userMeuns = [ + { + name: 'Account info', + link: '/profile', + iconName: 'folder' + }, + { + name: 'Change Password', + link: '/profile', + iconName: 'lock' }, { - link: '/dashboard', - iconName: 'dashboard', - active: 'dashboard', - title: 'Dashboard' + name: 'Notice settings', + link: '#', + iconName: 'loudspeaker' }, { - link: '/dashboard/repos', - iconName: 'appcenter', - active: 'repo', - title: 'Platform' + name: 'Payment', + link: '#', + iconName: 'creditcard' }, { - link: '/dashboard/apps', - iconName: 'components', - active: 'app', - title: role === 'developer' ? 'My Apps' : 'Store' + name: 'SSH Keys', + link: '/ssh_keys', + iconName: 'ssh' }, { - link: '/dashboard/users', - iconName: 'group', - active: 'user', - title: 'Users' + name: 'Log out', + link: '/logout', + iconName: 'logout' } ]; diff --git a/src/components/Layout/TitleBanner.jsx b/src/components/Layout/TitleBanner.jsx index f203a243..0e50d82e 100644 --- a/src/components/Layout/TitleBanner.jsx +++ b/src/components/Layout/TitleBanner.jsx @@ -3,15 +3,20 @@ import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import { observer } from 'mobx-react'; import { translate } from 'react-i18next'; +import { inject } from 'mobx-react'; -import Input from '../Base/Input'; - +import { Image, Icon, Input } from 'components/Base'; import styles from './index.scss'; -import { inject } from 'mobx-react/index'; // translate hoc should place before mobx @translate() -@inject('rootStore') +@inject(({ rootStore }) => ({ + rootStore, + appStore: rootStore.appStore, + clusterDetailStore: rootStore.clusterDetailStore, + runtimeStore: rootStore.runtimeStore, + user: rootStore.user +})) @observer class TitleBanner extends Component { static propTypes = { @@ -33,22 +38,65 @@ class TitleBanner extends Component { this.props.history.push('/'); }; - render() { - const { title, hasSearch, t } = this.props; + renderContent = type => { + const { appStore, clusterDetailStore, runtimeStore } = this.props; + let detail = {}; + let hasImage = false; - return ( -
+ switch (type) { + case 'appDetail': + detail = appStore.appDetail; + hasImage = true; + break; + case 'clusterDetail': + detail = clusterDetailStore.cluster; + break; + case 'runtimeDetail': + detail = runtimeStore.runtimeDetail; + break; + default: + detail = {}; + } + + if (detail.name) { + return (
- {t(title)} - {hasSearch && ( - + {hasImage && ( + + + + )} + {type === 'runtimeDetail' && ( + + + )} +
{detail.name}
+
{detail.description}
+ ); + } + + return null; + }; + + render() { + const { appStore, title, t } = this.props; + const descMap = { + 'App Store': t('APP_STORE_DESC', { total: appStore.storeTotal }), + Purchased: t('PURCHASED_DESC'), + 'My Runtimes': t('MY_RUNTIMES_DESC') + }; + + return ( +
+ {Boolean(descMap[title]) && ( +
+
{t(title)}
+
{descMap[title]}
+
+ )} + {!Boolean(descMap[title]) && this.renderContent(title)}
); } diff --git a/src/components/Layout/index.jsx b/src/components/Layout/index.jsx index 793b7274..bf20c918 100644 --- a/src/components/Layout/index.jsx +++ b/src/components/Layout/index.jsx @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { withRouter } from 'react-router-dom'; import classnames from 'classnames'; import { inject } from 'mobx-react'; import { noop, clone, isEmpty, get } from 'lodash'; @@ -16,7 +17,7 @@ import styles from './index.scss'; user: rootStore.user, sock })) -export default class Layout extends Component { +class Layout extends Component { static propTypes = { className: PropTypes.string, children: PropTypes.node, @@ -86,27 +87,31 @@ export default class Layout extends Component { backBtn, hasSearch, title, - isHome + isHome, + match } = this.props; const { isNormal, isDev, isAdmin } = this.props.user; const hasMenu = (isDev || isAdmin) && !isHome; const { isScroll } = this.state; + const paths = ['/dashboard', '/profile', '/ssh_keys', '/dev/apps']; + const hasSubNav = hasMenu && !paths.includes(match.path); return (
- {hasMenu && } {noNotification ? null : } + {backBtn} + {hasMenu && } {isNormal && !isHome && } - {backBtn} {children} @@ -115,3 +120,5 @@ export default class Layout extends Component { ); } } + +export default withRouter(Layout); diff --git a/src/components/Layout/index.scss b/src/components/Layout/index.scss index 05e44d43..2c942f19 100644 --- a/src/components/Layout/index.scss +++ b/src/components/Layout/index.scss @@ -6,34 +6,43 @@ width: $content-width; margin: 0 auto; - &.hasBack{ + &.hasBack { padding-top: $layout-top - 20px; } - &.hasMenu{ - margin: 56px 20px 32px $menu-width+20px; - width: calc(100% - #{$menu-width} - 40px); + &.hasMenu, + &.hasNav { + margin: 56px 20px 32px $menu-width + 20px; //min-width: 950px; min-width: 992px; min-height: calc(100vh - 100px); padding-top: 0; } - .backBtn{ + &.hasMenu { + width: calc(100% - #{$menu-width} - 40px); + } + + &.hasNav { + width: calc(100% - #{$menu-nav-width} - 40px); + margin-left: $menu-nav-width + 20px; + } + + .backBtn { //width: $content-width; margin: 0 auto 20px; font-size: $size-normal; line-height: 1.43; - a{ + a { color: $P75; } } - &.noTabs{ - padding-top: $header-height+32px; + &.noTabs { + padding-top: $header-height + 32px; } - .detailTab{ + .detailTab { position: absolute; top: 150px; left: 384px; @@ -50,27 +59,60 @@ } } -.titleBanner{ +.titleBanner { position: fixed; top: 48px; left: 0; z-index: 3; width: 100%; height: $title-banner-height; - background-color: $N0; - box-shadow: 0 4px 8px 0 rgba(35, 35, 36, 0.04); + background-color: $N500; - .wrapper{ + .wrapper { width: $content-width; - margin: 20px auto; + margin: 30px auto; + } + + .image, + .icon { + float: left; + margin-right: 12px; + width: 48px; + height: 48px; + } + + .image { + line-height: 48px; + + img { + max-width: 100%; + max-height: 100%; + vertical-align: middle; + } + } + + .icon { + box-sizing: border-box; + padding: 12px; + background-color: $N400; + border-radius: 3px; } - .name{ - font-size: 28px; + .name { + margin-bottom: 4px; + font-size: 20px; font-weight: 500; - line-height: 32px; + line-height: 24px; letter-spacing: 0; - color: #343945; + color: $N0; + } + + .desc { + height: 20px; + line-height: 20px; + font-size: 12px; + color: $N65; + overflow: hidden; } .search { @@ -90,11 +132,11 @@ border-radius: 16px; border: 1px solid transparent; background-color: $N10; - transition: all .3s ease-in-out; + transition: all 0.3s ease-in-out; &::placeholder { font-size: 14px; color: $N45; - line-height: 1.0; + line-height: 1; letter-spacing: 0; } &:focus { @@ -109,12 +151,11 @@ .icon { color: $N65; opacity: 0.5; - &:hover, &:focus { + &:hover, + &:focus { opacity: 1; } } } } } - - diff --git a/src/components/MenuLayer/index.jsx b/src/components/MenuLayer/index.jsx index d0a2bf85..4cc3eeeb 100644 --- a/src/components/MenuLayer/index.jsx +++ b/src/components/MenuLayer/index.jsx @@ -1,13 +1,14 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { NavLink, Link, withRouter } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { observer, inject } from 'mobx-react'; import { translate } from 'react-i18next'; import { Popover, Icon } from 'components/Base'; import styles from './index.scss'; +import { userMeuns } from 'components/Layout/SideNav/navMap'; // translate hoc should place before mobx @translate() @@ -30,44 +31,44 @@ export default class MenuLayer extends Component { }; render() { - const { className, user, t } = this.props; - const { role, isNormal } = user; - const changeWord = isNormal ? t('Back to developer') : t('Back to user'); + const { user, className, t } = this.props; + const { isNormal } = user; + const changeWord = isNormal ? t('Develop Center') : t('App Center'); + const isDeveloper = user.role === 'developer'; return (
    - {role === 'developer' && ( -
  • this.becomeDeveloper(isNormal)} className={styles.line}> - -
  • - )} -
  • - - - {t('Dashboard')} - -
  • - - - {t('Profile')} - -
  • -
  • - - - {t('SSH Keys')} - -
  • -
  • - - - {t('Log out')} - + + + + {user.username} + {isDeveloper && ( + + + + )}
  • + + {isDeveloper && ( +
  • this.becomeDeveloper(isNormal)}> + + +
  • + )} + + {userMeuns.map(item => ( +
  • + + {item.name === 'Log out' && {t(item.name)}} + {item.name !== 'Log out' && {t(item.name)}} +
  • + ))}
); } diff --git a/src/components/MenuLayer/index.scss b/src/components/MenuLayer/index.scss index 5cd6f17a..166a5e42 100644 --- a/src/components/MenuLayer/index.scss +++ b/src/components/MenuLayer/index.scss @@ -1,19 +1,18 @@ -@import "~scss/vars"; +@import '~scss/vars'; .menuLayer { - min-width: 130px; + min-width: 140px; li { + height: 32px; + > a, > label { display: block; - height: 32px; - padding: 0 12px; - //width: 100%; line-height: 32px; - font-size: $font14; + font-size: 12px; font-weight: normal; - color: $N300; + color: $N300 !important; border-radius: 0; cursor: pointer; @@ -23,16 +22,81 @@ } } - &.line{ + .iconImg { + float: left; + margin: 8px 8px 8px 15px; + } + + &:hover { + a { + color: $N500; + } + } + + &:first-child { + padding: 12px 15px 15px; + font-size: 16px; + line-height: 32px; + font-weight: 500; + color: $N500; + border-bottom: 1px solid $N10; + + .userIcon { + float: left; + display: inline-block; + margin-right: 8px; + width: 32px; + height: 32px; + background-color: $N10; + border-radius: 50%; + + .iconImg { + margin: 0; + svg { + --primary-color: #{$N300}; + --secondary-color: #{$N45}; + } + } + } + + .devIconOuter { + display: inline-block; + position: relative; + margin-left: 4px; + width: 12px; + height: 12px; + border-radius: 50%; + background-color: $P75; + + .devIcon { + position: absolute; + left: 2px; + top: 2px; + line-height: 0px; + svg { + --primary-color: #{$N0}; + --secondary-color: #{$N0}; + --primary-opacity: 0.9; + --secondary-opacity: 0.4; + } + } + } + } + + &.dev { + height: 40px; + > label { + line-height: 40px; + } border-bottom: 1px solid $N10; + + .iconImg { + margin: 12px 8px 12px 15px; + } } - .icon { - float: left; - position: relative; - top: 4px; - margin-right: 4px; + &:last-child { + border-top: 1px solid $N10; } } } - diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 59429753..b4f766a1 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -84,5 +84,10 @@ "DEPLOY_NO_RUNTIME_NOTE": "There is currently no available runtime, please go to ", - "HELM_APP_NAME_TIP": "Start with [a-z], with '-', 0~9, lowercase letter in the middle, no more than 14 characters in length." + "HELM_APP_NAME_TIP": "Start with [a-z], with '-', 0~9, lowercase letter in the middle, no more than 14 characters in length.", + + "APP_STORE_DESC": "Openpitrix official store has {{total}} apps", + "PURCHASED_DESC": "All the apps you have purchased will be shown here, including the corresponding instance of the app.", + "MY_RUNTIMES_DESC": "The platform supports multiple cloud environments, at the same time and can be managed here.", + "UPGRADE_PROVIDER": "Upgrade to an「application provider」" } diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 8d705809..64b9d6ad 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -272,7 +272,7 @@ "Tickets": "优惠卷", "Notifications": "通知", "Service Status": "服务状态", - "All Users": "所有用户", + "All Users": "全部用户", "User Groups": "用户分组", "Policy": "政策", "SSH Keys": "SSH密钥", @@ -285,6 +285,7 @@ "Administrator": "管理员", "Developer": "开发者", "Normal User": "普通用户", + "Permission and Policy": "权限与策略", "Email": "邮箱", "delete_user_desc": "您确定要删除该用户吗?", "Organization": "机构", @@ -312,8 +313,13 @@ "Attach": "绑定", "Detach": "解绑", + "App Store": "应用商店", "Purchased": "已部署应用", "My runtimes": "我的环境", + "APP_STORE_DESC": "Openpitrix 官方商店,有{{total}}款应用。", + "PURCHASED_DESC": "所有你购买过的应用都会展示在此,包括应用对应的实例。", + "MY_RUNTIMES_DESC": "平台同时支持多种云环境,可以在这里进行统一管理。", + "UPGRADE_PROVIDER": "升级成为「应用提供商」", "creat_new_app": "创建新应用程序", "creat_new_version": "创建新版本", @@ -376,6 +382,8 @@ "Back to user": "普通用户", "Back to developer": "开发中心", + "App Center": "应用中心", + "Develop Center": "开发中心", "Document": "文档", "Already create new version": "已创建新版本", @@ -400,5 +408,34 @@ "Version should not be empty": "版本不能为空", "Runtime should not be empty": "运行环境不能为空", "Deploy app successfully": "部署应用成功", - "Invalid config file, failed to render page": "无效的配置文件,无法渲染页面" + "Invalid config file, failed to render page": "无效的配置文件,无法渲染页面", + + "QingCloud App Center": "QingCloud 应用中心", + "My dashboard": "我的工作台", + "App service provider ISV": "应用服务商ISV", + "Work list": "工单", + "Financial Center": "财务中心", + "Message and monitoring": "消息与监控", + "Settings": "设置", + "Create app": "创建新应用", + "View all": "查看全部", + "Global search": "全局搜索", + "Alarms": "告警", + "My news": "我的消息", + "My account": "我的帐户", + "Version management": "版本管理", + "Audit record": "审核记录", + "App information": "应用信息", + "Operation and maintenance": "运维", + "Monitor": "监控", + "Event": "事件", + "Customer": "客户", + "News": "消息", + "Sandbox": "沙盒", + "Example": "实例", + "Environment": "环境", + "Account info": "账户信息", + "Change Password": "修改密码", + "Notice settings": "通知设置", + "Payment": "支付" } diff --git a/src/pages/AppDetail/index.jsx b/src/pages/AppDetail/index.jsx index b3dc5c92..3e6826cd 100644 --- a/src/pages/AppDetail/index.jsx +++ b/src/pages/AppDetail/index.jsx @@ -271,7 +271,7 @@ export default class AppDetail extends Component { } diff --git a/src/pages/Dashboard/Clusters/Detail/index.jsx b/src/pages/Dashboard/Clusters/Detail/index.jsx index a0aab7f0..d925d933 100644 --- a/src/pages/Dashboard/Clusters/Detail/index.jsx +++ b/src/pages/Dashboard/Clusters/Detail/index.jsx @@ -357,7 +357,7 @@ export default class ClusterDetail extends Component { className={classnames({ [styles.clusterDetail]: !isNormal })} backBtn={isNormal && } listenToJob={this.listenToJob} - title="Purchased" + title="clusterDetail" > {!isNormal && } diff --git a/src/pages/Dashboard/Runtimes/Detail/index.jsx b/src/pages/Dashboard/Runtimes/Detail/index.jsx index 87f18275..f3ff127d 100644 --- a/src/pages/Dashboard/Runtimes/Detail/index.jsx +++ b/src/pages/Dashboard/Runtimes/Detail/index.jsx @@ -343,7 +343,7 @@ export default class RuntimeDetail extends Component { return ( } listenToJob={this.listenToJob} > diff --git a/src/pages/Store/index.jsx b/src/pages/Store/index.jsx index 0ce5da8f..636c0407 100644 --- a/src/pages/Store/index.jsx +++ b/src/pages/Store/index.jsx @@ -39,6 +39,7 @@ export default class Store extends Component { } await appStore.fetchAll(params); + appStore.storeTotal = appStore.totalCount; appStore.storeApps = appStore.apps.slice(); if (!category && !search) { @@ -114,7 +115,7 @@ export default class Store extends Component { const categoryTitle = get(find(categories, { category_id: category }), 'name', ''); return ( - +