feat: added kudos wall (#24)
* feat: leaderboard for admins * feat: added kudos wall
This commit is contained in:
parent
445e7f8471
commit
7233fb934b
|
@ -10,14 +10,17 @@ const Header = ({ isUserAdmin }) => (
|
|||
<img alt="Logo" src={logo} className="mr-3" />
|
||||
Status Meritocracy
|
||||
</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||
{isUserAdmin && (
|
||||
<Navbar.Collapse id="basic-navbar-nav">
|
||||
<Nav className="mr-auto">
|
||||
<Nav.Link href="#/">Home</Nav.Link>
|
||||
<Nav.Link href="#/admin">Admin</Nav.Link>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
<React.Fragment>
|
||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||
<Navbar.Collapse id="basic-navbar-nav">
|
||||
<Nav className="mr-auto">
|
||||
<Nav.Link href="#/">Home</Nav.Link>
|
||||
<Nav.Link href="#/admin">Admin</Nav.Link>
|
||||
<Nav.Link href="#/wall">The Wall</Nav.Link>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Navbar>
|
||||
);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*global web3*/
|
||||
import React, { Fragment } from 'react';
|
||||
import { Tabs, Tab } from 'react-bootstrap';
|
||||
import { Tabs, Tab, Container } from 'react-bootstrap';
|
||||
import Meritocracy from 'Embark/contracts/Meritocracy';
|
||||
import { getFormattedContributorList, getCurrentContributorData } from '../services/Meritocracy';
|
||||
import { getFormattedContributorList, getCurrentContributorData, getAllPraises } from '../services/Meritocracy';
|
||||
import './home.scss';
|
||||
import Step1 from './Step1';
|
||||
import Step2 from './Step2';
|
||||
|
@ -10,7 +10,7 @@ import Loading from './Loading';
|
|||
import Complete from './Complete';
|
||||
import Error from './Error';
|
||||
import Withdrawal from './Withdrawal';
|
||||
import {sortByAlpha} from '../utils';
|
||||
import {sortByAlpha, sortByAttribute} from '../utils';
|
||||
/*
|
||||
TODO:
|
||||
- list praise for contributor
|
||||
|
@ -33,7 +33,8 @@ class Home extends React.Component {
|
|||
praise: '',
|
||||
step: 'HOME',
|
||||
checkbox: false,
|
||||
tab: 'reward'
|
||||
tab: 'reward',
|
||||
praises: []
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -53,6 +54,11 @@ class Home extends React.Component {
|
|||
const currentContributor = await getCurrentContributorData();
|
||||
|
||||
this.setState({ busy: false, currentContributor, contributorList: contributorList.sort(sortByAlpha('label'))});
|
||||
|
||||
getAllPraises().then(praises => {
|
||||
this.setState({praises: praises.sort(sortByAttribute('time'))});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
this.setState({ errorMsg: error.message || error });
|
||||
}
|
||||
|
@ -188,6 +194,7 @@ class Home extends React.Component {
|
|||
award,
|
||||
currentContributor,
|
||||
praise,
|
||||
praises,
|
||||
errorMsg,
|
||||
step,
|
||||
checkbox,
|
||||
|
@ -230,7 +237,11 @@ class Home extends React.Component {
|
|||
|
||||
{step === 'COMPLETE' && <Complete onClick={this.moveStep('HOME')} />}
|
||||
</Tab>
|
||||
|
||||
<Tab eventKey="wall" title="Wall">
|
||||
<Container className="pt-4">
|
||||
{praises.map((item, i) => <Praise key={i} individual={false} contributorList={contributorList} item={item} />)}
|
||||
</Container>
|
||||
</Tab>
|
||||
<Tab eventKey="withdraw" title="Withdraw" className="withdraw-panel">
|
||||
{step === 'HOME' && (
|
||||
<Withdrawal
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* global web3 */
|
||||
import React, {Fragment} from 'react';
|
||||
import moment from 'moment';
|
||||
import Address from './Address';
|
||||
import { Row, Col } from 'react-bootstrap';
|
||||
|
||||
const Praise = ({contributorList, item, individual}) => {
|
||||
const name = contributorList.find(x => x.value === item.author);
|
||||
const date = moment.unix(item.time).fromNow();
|
||||
return (
|
||||
<Row>
|
||||
<Col className="mb-4 text-muted">
|
||||
{!item.praise && (
|
||||
<Fragment>
|
||||
{(name && name.label) || <Address value={item.author} compact={true} />} {individual ? "has sent you" : "sent"}{' '}
|
||||
{web3.utils.fromWei(item.amount, 'ether')} SNT {!individual && <span>to {item.destination}</span>}, <small>{date}</small>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{item.praise && (
|
||||
<Fragment>
|
||||
{(name && name.label) || <Address value={item.author} compact={true} />}{!individual && <span> to {item.destination}</span>}, <small>{date}</small>
|
||||
<div className="chatBubble p-3">
|
||||
"{item.praise}"
|
||||
<small className="float-right">{web3.utils.fromWei(item.amount, 'ether')} SNT</small>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default Praise;
|
|
@ -1,9 +1,7 @@
|
|||
/* global web3 */
|
||||
import React, { Fragment } from 'react';
|
||||
import { Row, Col, Button, Container } from 'react-bootstrap';
|
||||
import moment from 'moment';
|
||||
import { Button, Container } from 'react-bootstrap';
|
||||
import info from '../../images/red-info.svg';
|
||||
import Address from './Address';
|
||||
import Praise from './Praise';
|
||||
|
||||
import './withdrawal.scss';
|
||||
|
||||
|
@ -19,32 +17,7 @@ const Withdrawal = ({ totalReceived, allocation, onClick, contributorList, prais
|
|||
|
||||
<Container>
|
||||
{praises &&
|
||||
praises.map((item, i) => {
|
||||
const name = contributorList.find(x => x.value === item.author);
|
||||
const date = moment.unix(item.time).fromNow();
|
||||
return (
|
||||
<Row key={i}>
|
||||
<Col className="mb-4 text-muted">
|
||||
{!item.praise && (
|
||||
<Fragment>
|
||||
{(name && name.label) || <Address value={item.author} compact={true} />} has sent you{' '}
|
||||
{web3.utils.fromWei(item.amount, 'ether')} SNT <small>{date}</small>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{item.praise && (
|
||||
<Fragment>
|
||||
{(name && name.label) || item.author}, <small>{date}</small>
|
||||
<div className="chatBubble p-3">
|
||||
"{item.praise}"
|
||||
<small className="float-right">{web3.utils.fromWei(item.amount, 'ether')} SNT</small>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
praises.map((item, i) => <Praise key={i} individual={true} contributorList={contributorList} item={item} />)}
|
||||
</Container>
|
||||
|
||||
<p className="text-center">
|
||||
|
@ -57,7 +30,7 @@ const Withdrawal = ({ totalReceived, allocation, onClick, contributorList, prais
|
|||
</Button>
|
||||
</p>
|
||||
|
||||
{parseInt(allocation, 10) > 0 && (
|
||||
{totalReceived !== '0' && parseInt(allocation, 10) > 0 && (
|
||||
<div className="text-muted text-left border rounded p-2 mb-2 learn-more">
|
||||
<img src={info} alt="" />
|
||||
<p className="m-0 p-0">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.header {
|
||||
.navbar-brand {
|
||||
font-size: 22px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
border: none;
|
||||
color: $dark;
|
||||
border-bottom: 2px solid $dark;
|
||||
font-size: 17px;
|
||||
width: 48%;
|
||||
font-size: 15px;
|
||||
width: 32%;
|
||||
text-align: center;
|
||||
|
||||
&.active {
|
||||
|
@ -17,7 +17,7 @@
|
|||
}
|
||||
|
||||
&+.nav-link {
|
||||
margin-left: 4%;
|
||||
margin-left: 2%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,43 @@ export async function getContributorData(_address) {
|
|||
return currentContributor;
|
||||
}
|
||||
|
||||
export function getAllPraises() {
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
let praisesPromises = [];
|
||||
let praiseNumPromises = [];
|
||||
for(let i = 0; i < contributorList.length; i++){
|
||||
praiseNumPromises.push(Meritocracy.methods.getStatusLength(contributorList[i].value).call());
|
||||
}
|
||||
|
||||
Promise.all(praiseNumPromises).then(praiseNums => {
|
||||
for(let i = 0; i < contributorList.length; i++){
|
||||
let currPraises = [];
|
||||
for(let j = 0; j < praiseNums[i]; j++){
|
||||
currPraises.push(Meritocracy.methods.getStatus(contributorList[i].value, j).call());
|
||||
}
|
||||
praisesPromises.push(currPraises);
|
||||
}
|
||||
|
||||
const allPromises = Promise.all(
|
||||
praisesPromises.map(function(innerPromiseArray) {
|
||||
return Promise.all(innerPromiseArray);
|
||||
})
|
||||
);
|
||||
|
||||
allPromises.then(praises => {
|
||||
for(let i = 0; i < praises.length; i++){
|
||||
praises[i] = praises[i].map(x => {
|
||||
x.destination = contributorList[i].label;
|
||||
return x;
|
||||
});
|
||||
}
|
||||
resolve(praises.flat());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export async function getContributor(_address) {
|
||||
const contributor = await Meritocracy.methods.contributors(_address).call();
|
||||
|
|
|
@ -12,6 +12,12 @@ export const sortByAttribute = field => (a, b) => {
|
|||
return 0;
|
||||
};
|
||||
|
||||
export const sortByAttributeDesc = field => (a, b) => {
|
||||
if (a[field] < b[field]) return -1;
|
||||
if (a[field] > b[field]) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const sortNullableArray = field => (a, b) => {
|
||||
const a_field = a[field] || [];
|
||||
const b_field = b[field] || [];
|
||||
|
|
Loading…
Reference in New Issue