Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add version audit record #567

Merged
merged 2 commits into from
Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

// 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 });
}
}
}

toggleReason(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.toggleReason(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.toggleReason(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