[KnackSteem] - User list for moderative actions by luschn

View this thread on steempeak.com
· @luschn · (edited)
$138.84
[KnackSteem] - User list for moderative actions
This PR is about a new component for moderators and supervisors. Supervisors can add/remove the moderator role to a user/contributor, moderators can ban/unban users.

## Repository & Pull Request
https://github.com/knacksteem/knacksteem.org
https://github.com/knacksteem/knacksteem.org/pull/12

## About knacksteem.org
"Do you have any talent? If yes! then KnackSteem is for you."
"Rewards people with talents, it can be any talent, anything you know how to do best is highly welcome on the platform."

## Technology Stack
* JavaScript ES6+ with Babel
* React + Redux
* Ant Design

## Changes / New Features
* New menu entry in the sidebar for the list of users (contributors, moderators, supervisors).
* User list component with backend search.
* Single entries have tags for their user roles, if moderator or supervisor. No tag for simple contributors.
* There is a special "banned" tag for banned users, with a PopOver showing the ban details.
* Depending on the user role, several buttons for moderative actions show up (see screenshots).
* Banning users requires a reason and an end date, both data can be entered in a modal (see screenshots).

## Screenshots

### Tags/Buttons
![userlist_tags.png](https://cdn.steemitimages.com/DQmRQ9CPyRqwWb6ML17RyAKEwc3LiCDgXJ4faa1z6xCMx1Y/userlist_tags.png)

### Ban Modal
![userlist_ban_modal.png](https://cdn.steemitimages.com/DQmXG2hvYzv3DqzXiBcJfbLVPk3uQMQEmKuUbo8wnf26MZR/userlist_ban_modal.png)

### Ban Popover
![userlist_ban_popover.png](https://cdn.steemitimages.com/DQmYNrNv8DXgf15jrMR29JsKef4sCWP422AECRzSqWbhruU/userlist_ban_popover.png)

## Details

Now let´s go through the most important or interesting parts of the PR. I decided to use one single Redux action for the moderative operations, because there is not much difference and redundant code is uncool. I believe it is easy to read, `banReason` and `bannedUntil` are only used for the ban/unban operation, of course. It takes the action as string to specify a dynamic API endpoint. Of course you could just use the endpoint directly as action string, but I want to keep API endpoint information in Redux actions:

```
export const moderateUser = (username, action, banReason, bannedUntil) => {
  return async (dispatch) => {
    const modEndpoints = {
      addSupervisor: '/moderation/add/supervisor',
      removeSupervisor: '/moderation/remove/supervisor',
      addModerator: '/moderation/add/moderator',
      removeModerator: '/moderation/remove/moderator',
      ban: '/moderation/ban',
      unban: '/moderation/unban'
    };
    try {
      await apiPost(modEndpoints[action], {
        access_token: Cookies.get('accessToken'),
        username: username,
        banReason: banReason,
        bannedUntil: bannedUntil,
      });

      dispatch(getUserList());
    } catch (error) {
      console.log(error);
    }
  };
};
```
(actions/stats.js)

The mod buttons may be used elsewhere, so I decided to create a separate component for those. **Depending on the user role, different mod buttons may appear**. Checking for a username directly is the only option to check for a "Master User" as of now, there is no separate role for it right now in the backend:

```
render() {
  const {showBanModal, banReason, showModalError} = this.state;
  const {user, item} = this.props;

  return (
    <div className="mod-buttons">
      {(user.username === 'knowledges' && item.roles.indexOf('supervisor') === -1) && <Button type="primary" size="small" onClick={this.onMakeSupervisorClick}>Make Supervisor</Button>}
      {(user.username === 'knowledges' && item.roles.indexOf('supervisor') !== -1) && <Button type="primary" size="small" onClick={this.onRemoveSupervisorClick}>Remove Supervisor</Button>}
      {(user.isSupervisor && item.roles.indexOf('moderator') === -1) && <Button type="primary" size="small" onClick={this.onMakeModClick}>Make Mod</Button>}
      {(user.isSupervisor && item.roles.indexOf('moderator') !== -1) && <Button type="primary" size="small" onClick={this.onRemoveModClick}>Remove Mod</Button>}
      {(user.isModerator && !item.isBanned) && <Button type="primary" size="small" onClick={this.onBanClick}>Ban</Button>}
      {(user.isModerator && item.isBanned) && <Button type="primary" size="small" onClick={this.onUnbanClick}>Unban</Button>}
      <Modal
        title="Ban User"
        visible={showBanModal}
        onOk={this.onBanOkClick}
        onCancel={this.onBanCancelClick}
      >
        <div className="mod-modal-input"><Input placeholder="Reason" value={banReason} onChange={this.handleBanReasonInput} /></div>
        <div className="mod-modal-input"><DatePicker onChange={this.handleBannedUntilInput} /></div>
        {showModalError && <div className="mod-modal-error">Reason and End Date have to be entered!</div>}
      </Modal>
    </div>
  );
}
```
(components/Common/ModButtons.js)

The Modal for banning users includes an Input and a DatePicker, both have to be entered in order to ban a user. There is just one error message for both, because it´s only for two fields anyway.

The Users container includes a stateless component for the title of the list item. This is basically to keep the main render function smaller and it contains a PopOver component showing details for a user ban. It shows the ban reason, the end date of the ban and the user who created the ban:

```
const Title = ({username, roles, isBanned, bannedBy, bannedReason, bannedUntil}) => {
  const bannedPopover = (
    <div>
      <p>Reason: {bannedReason}</p>
      <p>Banned Until: {timestampToDate(bannedUntil)}</p>
      <p>Banned By: {bannedBy}</p>
    </div>
  );
  return (
    <div>
      <a href={`https://www.steemit.com/@${username}`}>{username}</a>
      <div className="mod-tags">
        {roles.filter(role => role !== 'contributor').map((role) => {
          return (
            <Tag key={`${username}-${role}`} color={(role === 'supervisor') ? 'magenta' : 'blue'}>{role}</Tag>
          );
        })}
        {isBanned && <Popover content={bannedPopover} title="Ban Details"><Tag color="red">banned</Tag></Popover>}
      </div>
    </div>
  );
};
```
(containers/Users/index.js)

The main list is a simple Ant Design List component and includes the already mentioned `Title` and `ModButtons`  components in its item renderer:

```
<List
  dataSource={users}
  renderItem={item => {
    return (
      <List.Item>
        <List.Item.Meta
          avatar={<Avatar src={`https://steemitimages.com/u/${item.username}/avatar`} />}
          title={<Title username={item.username} roles={item.roles} isBanned={item.isBanned} bannedBy={item.bannedBy} bannedReason={item.bannedReasons} bannedUntil={item.bannedUntil} />}
          description={`Contributions: ${item.contributions || 0}`}
        />
        <ModButtons item={item} />
      </List.Item>
    );
  }}
/>
```
(containers/Users/index.js)

As always, I am happy to get feedback for improvement, so don´t be shy :)

## Roadmap
* Bugfixing and UI improvements.
* Error handling with Toast messages or Modals.
...

## How to contribute?
Talk to @knowledges (or me) :)

<hr>

My GitHub Account: https://github.com/ateufel
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 114 others
properties (23)
post_id57,327,958
authorluschn
permlinkknacksteem-user-list-for-moderative-actions
categoryutopian-io
json_metadata{"format":"markdown","image":["https:\/\/cdn.steemitimages.com\/DQmRQ9CPyRqwWb6ML17RyAKEwc3LiCDgXJ4faa1z6xCMx1Y\/userlist_tags.png"],"app":"steemit\/0.1","links":["https:\/\/github.com\/knacksteem\/knacksteem.org","https:\/\/github.com\/knacksteem\/knacksteem.org\/pull\/12","https:\/\/github.com\/ateufel"],"tags":["utopian-io","development","knacksteem","steemdev","steemapps"],"users":["knowledges"]}
created2018-07-22 06:12:03
last_update2018-07-22 06:17:54
depth0
children4
net_rshares64,567,315,605,308
last_payout2018-07-29 06:12:03
cashout_time1969-12-31 23:59:59
total_payout_value105.117 SBD
curator_payout_value33.726 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length7,131
author_reputation19,349,362,753,909
root_title"[KnackSteem] - User list for moderative actions"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
author_curate_reward""
vote details (178)
@steemitboard ·
Congratulations @luschn! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

[![](https://steemitimages.com/70x80/http://steemitboard.com/notifications/comments.png)](http://steemitboard.com/@luschn) Award for the number of comments

<sub>_Click on the badge to view your Board of Honor._</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>


To support your work, I also upvoted your post!


**Do not miss the last post from @steemitboard:**
[SteemitBoard World Cup Contest - The results, the winners and the prizes](https://steemit.com/steemitboard/@steemitboard/steemitboard-world-cup-contest-the-results-and-prizes)

> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
properties (22)
post_id57,339,132
authorsteemitboard
permlinksteemitboard-notify-luschn-20180722t090956000z
categoryutopian-io
json_metadata{"image":["https:\/\/steemitboard.com\/img\/notify.png"]}
created2018-07-22 09:09:54
last_update2018-07-22 09:09:54
depth1
children0
net_rshares0
last_payout2018-07-29 09:09:54
cashout_time1969-12-31 23:59:59
total_payout_value0.000 SBD
curator_payout_value0.000 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length947
author_reputation38,705,954,145,809
root_title"[KnackSteem] - User list for moderative actions"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
@codingdefined ·
$0.11
Thank you for your contribution. Is there any specific reason why https://github.com/knacksteem/knacksteem.org/pull/12/commits/78f47b1bfd827297fa580afb64d3a956705e8f61#diff-ddf42f71ba795f6c1c20a6f37c330cdbR93 is not db driven?


Your contribution has been evaluated according to [Utopian policies and guidelines](https://join.utopian.io/guidelines), as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, [click here](https://review.utopian.io/result/3/2221211).

---- 
Need help? Write a ticket on https://support.utopian.io/. 
Chat with us on [Discord](https://discord.gg/uTyJkNm). 
[[utopian-moderator]](https://join.utopian.io/)
👍  , ,
properties (23)
post_id57,460,573
authorcodingdefined
permlinkre-luschn-knacksteem-user-list-for-moderative-actions-20180723t132319093z
categoryutopian-io
json_metadata{"app":"steemit\/0.1","links":["https:\/\/github.com\/knacksteem\/knacksteem.org\/pull\/12\/commits\/78f47b1bfd827297fa580afb64d3a956705e8f61#diff-ddf42f71ba795f6c1c20a6f37c330cdbR93","https:\/\/join.utopian.io\/guidelines","https:\/\/review.utopian.io\/result\/3\/2221211","https:\/\/support.utopian.io\/","https:\/\/discord.gg\/uTyJkNm","https:\/\/join.utopian.io\/"],"tags":["utopian-io"]}
created2018-07-23 13:23:18
last_update2018-07-23 13:23:18
depth1
children1
net_rshares51,496,108,213
last_payout2018-07-30 13:23:18
cashout_time1969-12-31 23:59:59
total_payout_value0.090 SBD
curator_payout_value0.018 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length718
author_reputation71,157,752,447,147
root_title"[KnackSteem] - User list for moderative actions"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
author_curate_reward""
vote details (3)
@luschn · (edited)
$0.18
Hi! Thanx for the review, I have explained that in my article, if that´s what you mean: "Checking for a username directly is the only option to check for a "Master User" as of now, there is no separate role for it right now in the backend". It would be best to create a separate role in the DB for it, of course. I have informed the backend dev about it already, he needs to implement it before i can use it in the Frontend :)
👍  , , ,
properties (23)
post_id57,462,406
authorluschn
permlinkre-codingdefined-re-luschn-knacksteem-user-list-for-moderative-actions-20180723t134055166z
categoryutopian-io
json_metadata{"app":"steemit\/0.1","tags":["utopian-io"]}
created2018-07-23 13:43:06
last_update2018-07-23 13:45:51
depth2
children0
net_rshares86,350,407,059
last_payout2018-07-30 13:43:06
cashout_time1969-12-31 23:59:59
total_payout_value0.140 SBD
curator_payout_value0.041 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length426
author_reputation19,349,362,753,909
root_title"[KnackSteem] - User list for moderative actions"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
author_curate_reward""
vote details (4)
@utopian-io ·
$0.03
Hey @luschn
**Thanks for contributing on Utopian**.
We’re already looking forward to your next contribution!

**Want to chat? Join us on Discord https://discord.gg/h52nFrV.**

<a href='https://v2.steemconnect.com/sign/account-witness-vote?witness=utopian-io&approve=1'>Vote for Utopian Witness!</a>
👍  ,
properties (23)
post_id57,616,794
authorutopian-io
permlinkre-knacksteem-user-list-for-moderative-actions-20180725t003010z
categoryutopian-io
json_metadata{"app":"beem\/0.19.42"}
created2018-07-25 00:30:12
last_update2018-07-25 00:30:12
depth1
children0
net_rshares14,530,351,324
last_payout2018-08-01 00:30:12
cashout_time1969-12-31 23:59:59
total_payout_value0.023 SBD
curator_payout_value0.005 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length298
author_reputation152,913,012,544,965
root_title"[KnackSteem] - User list for moderative actions"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
author_curate_reward""
vote details (2)