-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
dongrui
committed
Dec 6, 2018
1 parent
3c14060
commit 8e7de15
Showing
6 changed files
with
364 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}> {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)}: { | ||
(_.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')} {version.name} | ||
</div> | ||
{version.isShowAudits && this.renderAudits(version.version_id)} | ||
</div> | ||
))} | ||
</div> | ||
</Layout> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.