lancetw/react-isomorphic-bundle

View on GitHub
src/client/admin/components/widget/PermissionsTableWidget.js

Summary

Maintainability
F
4 days
Test Coverage
import React, { Component, PropTypes } from 'react'
import { isEmpty, includes, without } from 'lodash'
import classNames from 'classnames'
import { toDate } from 'shared/utils/date-utils'
import {
  Pagination
} from 'client/admin/components/widget'

let swal
if (process.env.BROWSER) {
  swal = require('sweetalert')
}

export default class PermissionsTableWidget extends Component {

  static propTypes = {
    collect: PropTypes.object.isRequired,
    auth: PropTypes.object.isRequired,
    action: PropTypes.func.isRequired,
    deleteAction: PropTypes.func.isRequired,
    fetchList: PropTypes.func.isRequired,
    submit: PropTypes.func.isRequired,
    changePassword: PropTypes.func.isRequired,
    selected: PropTypes.number
  }

  static defaultProps = {
    selected: 0
  }

  constructor (props) {
    super(props)

    this.state = { checked: [], page: 1 }
  }

  handleChange (id, event) {
    let checked = this.state.checked
    if (event.target.checked) {
      if (!includes(checked, id)) {
        checked.push(id)
        this.setState({ checked: checked })
      }
    } else {
      if (includes(checked, id)) {
        checked = without(checked, id)
      }
      this.setState({ checked: checked })
    }
  }

  handleClick () {
    const checked = this.state.checked

    this.props.action(checked).then(() => {
      this.setState({ checked: [] })
    }).catch(() => {
      this.setState({ checked: [] })
    })
  }

  handleCreateClick () {
    swal({
      title: '新增管理者',
      text: '請輸入 Email',
      type: 'input',
      showCancelButton: true,
      closeOnConfirm: false,
      animation: 'slide-from-top',
      inputPlaceholder: 'Email',
      confirmButtonText: '確認',
      cancelButtonText: '取消',
      confirmButtonColor: '#ff8800'
    }, function (email) {
      if (email === false) {
        return false
      }
      if (email === '') {
        swal.showInputError('輸入有誤')
        return false
      }

      swal({
        title: '新增管理者',
        text: '請輸入密碼',
        type: 'input',
        inputType: 'password',
        showCancelButton: true,
        closeOnConfirm: false,
        animation: 'slide-from-top',
        inputPlaceholder: '密碼(長度應大於 6)',
        confirmButtonText: '確認',
        cancelButtonText: '取消',
        confirmButtonColor: '#ff8800'
      }, function (password) {
        if (password === false) {
          return false
        }
        if (password === '') {
          swal.showInputError('輸入有誤')
          return false
        }

        swal({
          title: '新增管理者',
          text: '名稱(可不填)',
          type: 'input',
          showCancelButton: true,
          closeOnConfirm: false,
          animation: 'slide-from-top',
          inputPlaceholder: '名稱',
          confirmButtonText: '確認',
          cancelButtonText: '取消',
          confirmButtonColor: '#ff8800',
          showLoaderOnConfirm: true
        }, function (name) {
          this.props.submit({ email: email, password: password, name: name })
          .then((res) => {
            const { admin, errors } = res
            if (admin && admin.email === email) {
              swal('完成', email + ' 已新增', 'success')
              this.props.fetchList({ offset: 0, limit: 8, status: 0 })
            } else {
              if (!isEmpty(errors)) {
                const message = errors[0].message
                if (message === 'email must be unique') {
                  swal('新增失敗', email + ' 已存在', 'error')
                } else if (message === 'should be an email') {
                  swal('新增失敗', email + ' 不是個 email', 'error')
                } else if (message === 'length should bigger than 6') {
                  swal('新增失敗', '密碼太短', 'error')
                } else {
                  swal('新增失敗', email + ' 新增時發生錯誤', 'error')
                }
              }
            }
          })
          .catch((err) => {
            swal('失敗', err, 'error')
          })
        }.bind(this))
      }.bind(this))
    }.bind(this))
  }

  handleChangePasswordClick (id, event) {
    swal({
      title: '修改密碼',
      text: '請輸入新密碼',
      type: 'input',
      inputType: 'password',
      showCancelButton: true,
      closeOnConfirm: false,
      animation: 'slide-from-top',
      inputPlaceholder: '密碼(長度應大於 6)',
      confirmButtonText: '確認',
      cancelButtonText: '取消',
      confirmButtonColor: '#ff8800',
      showLoaderOnConfirm: true
    }, function (password) {
      if (password === false) {
        return false
      }
      if (password === '') {
        swal.showInputError('輸入有誤')
        return false
      }

      this.props.changePassword({ id: id, password: password })
      .then((res) => {
        const { admin } = res
        if (admin && admin.email) {
          swal('完成', admin.email + ' 已更新密碼', 'success')
        } else {
          swal('新增失敗', '失敗,請重試', 'error')
        }
      }).catch((err) => {
        swal('失敗', err, 'error')
      })
    }.bind(this))
  }

  handleDeleteClick () {
    const checked = this.state.checked

    this.props.deleteAction(checked).then(() => {
      this.setState({ checked: [] })
    }).catch(() => {
      this.setState({ checked: [] })
    })
  }

  isChecked (id) {
    return includes(this.state.checked, id)
  }

  renderActionBtn () {
    const ActionBtnClasses = classNames(
      'ui',
      {'yellow': !this.props.selected },
      {'green': this.props.selected },
      'button',
      {'loading': !this.props.collect.done },
      {'disabled': isEmpty(this.state.checked) }
    )
    const btnLabel = (!this.props.selected) ? '停權' : '復權'
    return (
      <div
        onClick={::this.handleClick}
        className={ActionBtnClasses}>
        { btnLabel }
      </div>
    )
  }

  renderUserActionBtn () {
    const ActionBtnClasses = classNames(
      'ui',
      {'blue': !this.props.selected },
      {'red': this.props.selected },
      'button',
      {'loading': false },
      {'disabled': false }
    )
    const btnLabel = (!this.props.selected) ? '新增' : '封鎖'
    return (
      <div
        onClick={
          (!this.props.selected)
            ? ::this.handleCreateClick
            : ::this.handleDeleteClick}
        className={ActionBtnClasses}>
        { btnLabel }
      </div>
    )
  }

  render () {
    const { items } = this.props.collect
    const handleChange = ::this.handleChange
    const isChecked = ::this.isChecked
    const handleChangePasswordClick = ::this.handleChangePasswordClick

    const TableClasses = classNames(
      'ui',
      'definition',
      'compact',
      'unstackable',
      'striped',
      'table'
    )

    return (
      <table className={TableClasses}>
        <thead className="full-width">
          <tr>
            <th></th>
            <th className="table email">電子郵件信箱</th>
            <th className="table name">名稱</th>
            <th className="table date">加入日期</th>
          </tr>
        </thead>
        <tbody>
        {!isEmpty(items) && items.map(function(item, i) {
          const checked = isChecked(item.id)
          return (
          <tr key={item.id}>
            <td className="collapsing">
              <div className="ui checkbox">
                <input
                  type="checkbox"
                  defaultChecked="false"
                  checked={checked}
                  onChange={handleChange.bind(this, item.id)} />
                <label></label>
              </div>
            </td>
            <td className="table email">
              { (checked) && (
                <div
                  onClick={handleChangePasswordClick.bind(this, item.id)}
                  className="ui orange small header">
                  { item.email }
                </div>)
              }
              { (!checked) && (
                <div
                  onClick={handleChangePasswordClick.bind(this, item.id)}
                  className="ui orange small header">
                  { item.email }
                </div>)
              }
            </td>
            <td className="table name">{ item.name }</td>
            <td className="table date">{ toDate(item.created_at, true) }</td>
          </tr>
          )
        })}
        </tbody>
        <tfoot className="full-width">
          <tr>
            <th colSpan="5">
              {::this.renderActionBtn()}
              {::this.renderUserActionBtn()}
              <Pagination {...this.props} />
            </th>
          </tr>
        </tfoot>
      </table>
    )
  }
}