Skip to content

Commit

Permalink
feat: Add version audit record
Browse files Browse the repository at this point in the history
  • Loading branch information
dongrui committed Dec 6, 2018
1 parent 3c14060 commit 8e7de15
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 2 deletions.
11 changes: 10 additions & 1 deletion src/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -412,5 +412,14 @@
"Account info": "账户信息",
"Change Password": "修改密码",
"Notice settings": "通知设置",
"Payment": "支付"
"Payment": "支付",

"Record": "记录",
"Version": "版本",
"developer": "开发者",
"global_admin": "管理员",
"Reason": "原因",
"Expand": "展开",
"Collapse": "收起",
"None": ""
}
218 changes: 218 additions & 0 deletions src/pages/Dashboard/Apps/Audits/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import React, { Component, Fragment } from 'react';
import { observer, inject } from 'mobx-react';
import { translate } from 'react-i18next';
import classnames from 'classnames';
import _ from 'lodash';

import { Icon } from 'components/Base';
import Layout from 'components/Layout';
import Status from 'components/Status';

import styles from './index.scss';

@translate()
@inject(({ rootStore }) => ({
rootStore,
appVersionStore: rootStore.appVersionStore,
userStore: rootStore.userStore
}))
@observer
export default class Audits extends Component {
constructor(props) {
super(props);

this.state = {
currentType: ''
};
}

async componentDidMount() {
const { appVersionStore, userStore, match } = this.props;
const { appId } = match.params;

await appVersionStore.fetchAll({ app_id: appId });
const versions = appVersionStore.versions;

// default show last version audit records
const versionId = _.get(versions, '[0].version_id');
await appVersionStore.fetchAudits(appId, versionId);
versions[0].isShowAudits = true;

// query audit relative operators name
const userIds = _.get(appVersionStore.audits, versionId, []).map(
item => item.operator
);
await userStore.fetchAll({ user_id: userIds });
}

componentWillUnmount() {
const { appVersionStore } = this.props;
appVersionStore.reset();
}

changeType = type => {
const { currentType } = this.state;

if (type !== currentType) {
this.setState({ currentType: type });
const { versions } = this.props.appVersionStore;
// after change type, query the type last version audit records
const version = _.find(versions, { type });
this.showAudits(version, true);
}
};

async showAudits(version, isShowAudits) {
const { appVersionStore, userStore } = this.props;
const { fetchAudits, audits } = appVersionStore;
const versionId = version.version_id;
version.isShowAudits = isShowAudits || !version.isShowAudits;

// judge need query audits again
if (!audits[versionId]) {
await fetchAudits(version.app_id, versionId);

// judge need query users again
const oldUserIds = userStore.users.map(item => item.user_id).sort();
const userIds = _.get(appVersionStore.audits, versionId, []).map(
item => item.operator
);
const newUserIds = _.concat(oldUserIds, userIds).sort();

if (!_.isEqual(oldUserIds, _.uniq(newUserIds))) {
await userStore.fetchAll({ user_id: newUserIds });
}
}
}

expandReason(audit) {
audit.isExpand = !audit.isExpand;
}

renderReason(audit) {
const { t } = this.props;

if (!audit.message) {
return null;
}

return (
<div>
{t('Reason')}:
{!audit.isExpand && (
<Fragment>
<span className={styles.hideReason}>&nbsp;{audit.message}</span>
<span
onClick={() => this.expandReason(audit)}
className={styles.expand}
>
{t('Expand')}
</span>
</Fragment>
)}
</div>
);
}

renderAudits(versionId) {
const { appVersionStore, userStore, t } = this.props;
const { audits } = appVersionStore;
const { users } = userStore;
const auditRecords = audits[versionId];

if (!_.isArray(auditRecords)) {
return null;
}

return (
<ul className={styles.auditRecords}>
{auditRecords.map(audit => (
<li
key={audit.status_time}
className={classnames({ [styles.active]: audit.isExpand })}
>
<label className={styles.status}>
<Status type={audit.status} name={audit.status} />
</label>
<label className={styles.operator}>
{t(audit.role)}:&nbsp;{
(_.find(users, { user_id: audit.operator }) || {}).username
}
</label>
<label className={styles.reason}>{this.renderReason(audit)}</label>
<label className={styles.time}>{audit.status_time}</label>
{audit.isExpand && (
<span
onClick={() => this.expandReason(audit)}
className={styles.showReason}
>
{audit.message}
<br />
<span className={styles.collapse}>{t('Collapse')}</span>
</span>
)}
</li>
))}
</ul>
);
}

renderTypes(types, activeType) {
const { t } = this.props;
const typeMap = {
vmbase: 'VM',
helm: 'Helm'
};

return (
<div className={styles.types}>
{types.map(type => (
<label
key={type}
onClick={() => this.changeType(type)}
className={classnames({ [styles.active]: activeType === type })}
>
{typeMap[type] || type || t('None')}
</label>
))}
</div>
);
}

render() {
const { appVersionStore, t } = this.props;
const { currentType } = this.state;
const { versions } = appVersionStore;

const types = _.uniq(versions.map(item => item.type)); // get all unique version types
const activeType = currentType || types[0]; // if currentType value is '', get first value of types
const currentVersions = versions.filter(item => item.type === activeType);

return (
<Layout>
<div className={styles.audits}>
<div className={styles.title}>{t('Record')}</div>

{this.renderTypes(types, activeType)}

{currentVersions.map(version => (
<div className={styles.version} key={version.version_id}>
<div
onClick={() => this.showAudits(version)}
className={styles.name}
>
<Icon
name={version.isShowAudits ? 'caret-down' : 'caret-right'}
size={24}
className={styles.icon}
/>
{t('Version')}&nbsp;{version.name}
</div>
{version.isShowAudits && this.renderAudits(version.version_id)}
</div>
))}
</div>
</Layout>
);
}
}
120 changes: 120 additions & 0 deletions src/pages/Dashboard/Apps/Audits/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@import '~scss/vars';

.audits {
max-width: 950px;
margin: 0 auto;

.title {
margin: 18px 0;
font-size: 16px;
font-weight: 500;
line-height: 28px;
color: $N500;
}

.types {
margin-bottom: 16px;

> label {
display: inline-block;
padding: 9px 12px;
border-radius: 2px;
background-color: $N0;
border: 1px solid $N10;
font-size: 14px;
line-height: 14px;
color: $N300;
cursor: pointer;

&.active {
color: $P75;
border-color: $P75;
}
}
}

.version {
.name {
margin-bottom: 12px;
font-size: 14px;
font-weight: 500;
line-height: 28px;
color: $N500;
cursor: pointer;
}

.icon {
float: left;
svg {
--primary-color: #{$N300};
--secondary-color: #{$N300};
}
}
}
}

.auditRecords {
li {
box-sizing: border-box;
min-height: 48px;
padding: 14px 24px;
line-height: 20px;
border-bottom: 1px solid $N10;
font-size: 12px;

&:hover, &.active {
background-color: $N0;
}

> label {
display: inline-block;
float: left;
height: 20px;
color: $N75;
}
}

.status {
width: 20%;
}

.operator {
width: 20%;
color: $N300;
}

.reason {
width: 45%;
}

.time {
width: 15%;
text-align: right;
}

.hideReason {
display: inline-block;
width: calc(100% - 70px);
@include textCut;
vertical-align: middle;
}

.showReason {
display: inline-block;
margin-top: 12px;
margin-left: 40%;
width: 43%;
}

.expand,
.collapse {
line-height: 20px;
color: $P75;
cursor: pointer;
}

.collapse {
display: inline-block;
margin-top: 8px;
}
}
1 change: 1 addition & 0 deletions src/pages/Dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export AppDetail from './Apps/Detail';
export AppReview from './Apps/Review';
export AppDeploy from './Apps/Deploy';
export MyApps from './Apps/MyApps';
export Audits from './Apps/Audits';
export AuditRecord from './Audit/Record';
export Clusters from './Clusters';
export ClusterDetail from './Clusters/Detail';
Expand Down
1 change: 1 addition & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const routes = {
'/:dash/app/:appId/create-version': Dash.AppAdd,
'/:dash/app/:appId': Dash.AppDetail,
'/:dash/audit/record/:appId': Dash.AuditRecord,
'/:dash/app/audits/:appId': Dash.Audits,

'/:dash/clusters': Dash.Clusters,

Expand Down
Loading

0 comments on commit 8e7de15

Please sign in to comment.