Merge pull request #12 from jrainville/feat/a-bit-of-style

Add a bit of style
This commit is contained in:
Richard Ramos 2019-04-10 23:24:26 -04:00 committed by GitHub
commit 20b4ff0351
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1517 additions and 54 deletions

93
app/css/bootstrap-overrides.scss vendored Normal file
View File

@ -0,0 +1,93 @@
@import "variable-overrides";
@import "../../node_modules/bootstrap/scss/bootstrap";
#root {
width: 100%;
height: 100%;
position: absolute;
}
.btn-primary {
background-color: $btn-bg-color;
border-color: $btn-bg-color;
color: $primary;
}
p {
margin-bottom: 24px;
font-size: 15px;
}
h2, .h2 {
font-size: 22px;
font-weight: bold;
}
h3, .h3 {
font-size: 17px;
font-weight: bold;
}
$input-height: 52px;
$input-padding-custom: 17px;
.form-control {
font-size: 15px;
background-color: $secondary;
border-color: $secondary;
padding: 0 $input-padding-custom;
height: $input-height;
border-radius: 8px;
&.prepend {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.white-input {
.form-control, .input-group-append, .input-group-text {
background-color: white !important;
}
.input-group-text {
border: 1px solid $secondary !important;
}
}
.input-group {
.input-group-append {
height: $input-height;
.input-group-text {
width: 60px;
text-align: center;
display: inline-block;
padding: 0;
line-height: $input-height;
border: 0;
border-left: 1px solid white;
background-color: $secondary;
}
}
}
$input-icon-height: 20px;
.input-icon {
position: absolute;
right: 15px;
height: $input-icon-height;
bottom: ($input-height / 2) - ($input-icon-height / 2);
}
.border {
border-width: 2px !important;
}
.rounded {
border-radius: 8px !important;
}
.btn {
padding: 11px 36px;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,184 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100;
src: url("Inter-Thin.woff2") format("woff2"),
url("Inter-Thin.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 100;
src: url("Inter-ThinItalic.woff2") format("woff2"),
url("Inter-ThinItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200;
src: url("Inter-ExtraLight.woff2") format("woff2"),
url("Inter-ExtraLight.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 200;
src: url("Inter-ExtraLightItalic.woff2") format("woff2"),
url("Inter-ExtraLightItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url("Inter-Light.woff2") format("woff2"),
url("Inter-Light.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 300;
src: url("Inter-LightItalic.woff2") format("woff2"),
url("Inter-LightItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url("Inter-Regular.woff2") format("woff2"),
url("Inter-Regular.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 400;
src: url("Inter-Italic.woff2") format("woff2"),
url("Inter-Italic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
src: url("Inter-Medium.woff2") format("woff2"),
url("Inter-Medium.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 500;
src: url("Inter-MediumItalic.woff2") format("woff2"),
url("Inter-MediumItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
src: url("Inter-SemiBold.woff2") format("woff2"),
url("Inter-SemiBold.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 600;
src: url("Inter-SemiBoldItalic.woff2") format("woff2"),
url("Inter-SemiBoldItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url("Inter-Bold.woff2") format("woff2"),
url("Inter-Bold.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 700;
src: url("Inter-BoldItalic.woff2") format("woff2"),
url("Inter-BoldItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
src: url("Inter-ExtraBold.woff2") format("woff2"),
url("Inter-ExtraBold.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 800;
src: url("Inter-ExtraBoldItalic.woff2") format("woff2"),
url("Inter-ExtraBoldItalic.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 900;
src: url("Inter-Black.woff2") format("woff2"),
url("Inter-Black.woff") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 900;
src: url("Inter-BlackItalic.woff2") format("woff2"),
url("Inter-BlackItalic.woff") format("woff");
}
/* -------------------------------------------------------
Variable font.
Usage:
html { font-family: 'Inter', sans-serif; }
@supports (font-variation-settings: normal) {
html { font-family: 'Inter var', sans-serif; }
}
*/
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-style: normal;
font-named-instance: 'Regular';
src: url("Inter-upright.var.woff2") format("woff2 supports variations(gvar)"),
url("Inter-upright.var.woff2") format("woff2-variations"),
url("Inter-upright.var.woff2") format("woff2");
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-style: italic;
font-named-instance: 'Italic';
src: url("Inter-italic.var.woff2") format("woff2 supports variations(gvar)"),
url("Inter-italic.var.woff2") format("woff2-variations"),
url("Inter-italic.var.woff2") format("woff2");
}
/* --------------------------------------------------------------------------
[EXPERIMENTAL] Multi-axis, single variable font.
Slant axis is not yet widely supported (as of February 2019) and thus this
multi-axis single variable font is opt-in rather than the default.
When using this, you will probably need to set font-variation-settings
explicitly, e.g.
* { font-variation-settings: "slnt" 0deg }
.italic { font-variation-settings: "slnt" 10deg }
*/
@font-face {
font-family: 'Inter var experimental';
font-weight: 100 900;
font-style: oblique 0deg 10deg;
src: url("Inter.var.woff2") format("woff2-variations"),
url("Inter.var.woff2") format("woff2");
}

3
app/css/index.scss Normal file
View File

@ -0,0 +1,3 @@
body {
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

File diff suppressed because it is too large Load Diff

BIN
app/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@ -5,6 +5,7 @@ import ThemeProvider from 'react-bootstrap/ThemeProvider';
import EmbarkJS from 'Embark/EmbarkJS';
import {isAdmin} from './services/Meritocracy';
import Header from './components/Header';
import Home from './components/Home';
import Admin from './components/Admin';
@ -16,7 +17,8 @@ class App extends React.Component {
state = {
error: null,
loading: true
loading: true,
isUserAdmin: false
};
componentDidMount() {
@ -32,12 +34,15 @@ class App extends React.Component {
if (EmbarkJS.environment === 'livenet' && netId !== MAINNET) {
return this.setState({error: 'Please connect to Mainnet'});
}
this.setState({loading: false})
const isUserAdmin = await isAdmin(web3.eth.defaultAccount);
this.setState({loading: false, isUserAdmin})
});
}
render() {
const {error, loading} = this.state;
const {error, loading, isUserAdmin} = this.state;
if (error) {
return (<div>
@ -54,10 +59,10 @@ class App extends React.Component {
return (<HashRouter>
<ThemeProvider prefixes={{ btn: 'my-btn' }}>
<Header/>
<Header isUserAdmin={isUserAdmin}/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/admin" component={Admin}/>
{isUserAdmin && <Route exact path="/admin" component={Admin}/>}
<Redirect to="/404"/>
</Switch>

View File

@ -107,8 +107,8 @@ class Admin extends React.Component {
<h3>Contributor List</h3>
<ListGroup>
{contributorList.map((contributor, idx) => (
<ListGroup.Item key={contributor.value} action>
{contributor.label}: {contributor.value}
<ListGroup.Item key={contributor.value} action className="contributor-item">
<span className="font-weight-bold">{contributor.label}:</span> {contributor.value}
<div className="contributor-controls float-right">
<OverlayTrigger placement="top"

View File

@ -1,15 +1,18 @@
import React from 'react'
import {Navbar, Nav} from 'react-bootstrap';
const Header = () => (<Navbar bg="light" expand="lg">
<Navbar.Brand href="/#/">Status Meritocracy</Navbar.Brand>
import './header.scss'
import logo from '../../images/logo.png';
const Header = ({isUserAdmin}) => (<Navbar expand="lg" className="header border-bottom mb-3">
<Navbar.Brand href="/#/"><img alt="Logo" src={logo} className="mr-3"/>Status Meritocracy</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav"/>
<Navbar.Collapse id="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>
</Navbar.Collapse>}
</Navbar>
);

View File

@ -1,6 +1,6 @@
/*global web3*/
import React from 'react';
import {Row, Col, Alert, Button, Container, Form} from 'react-bootstrap';
import React, {Fragment} from 'react';
import {Row, Col, Alert, Button, Container, Form, Tabs, Tab} from 'react-bootstrap';
import NumericInput from 'react-numeric-input';
import Select from 'react-select';
@ -8,6 +8,8 @@ import Meritocracy from 'Embark/contracts/Meritocracy';
import {getFormattedContributorList, getCurrentContributorData} from '../services/Meritocracy';
import './home.scss';
/*
TODO:
- list praise for contributor
@ -158,15 +160,18 @@ class Home extends React.Component {
const maxAllocation = selectedContributors.length ? currentContributor.allocation / selectedContributors.length : 0;
return (<div>
return (<Fragment>
{errorMsg && <Alert variant="danger">{errorMsg}</Alert>}
{busy && <p>Working...</p>}
{currentContributor.name && <h2>Hello, {currentContributor.name} !</h2>}
<span>Your Total Received Kudos: { currentContributor.totalReceived || 0} SNT</span> <br/>
<span>Your Total Forfeited Kudos: { currentContributor.totalForfeited || 0} SNT</span> <br/>
<Tabs defaultActiveKey="reward" className="home-tabs mb-3">
<Tab eventKey="reward" title="Reward" className="reward-panel">
<div className="text-center p-4">
<p className="text-muted">Reward Status contributors for all the times they impressed you.</p>
<p className="allocation mb-0">{currentContributor.allocation} <span className="text-muted">SNT</span></p>
<p className="text-muted">Available</p>
</div>
<h4>Award Kudos</h4>
<Select
isMulti
value={selectedContributors}
@ -174,34 +179,49 @@ class Home extends React.Component {
options={contributorList}
placeholder="Choose Contributor(s)..."
isDisabled={busy}
className="mb-2"
/>
<p>Your Allocatable Kudos: { currentContributor.allocation } SNT</p>
{selectedContributors.length === 0 && <Alert variant="warning">
{selectedContributors.length === 0 && <Alert variant="secondary">
Please select one or more contributors
</Alert>}
<NumericInput mobile step={5} min={0} max={maxAllocation} onChange={this.handleAwardChange} value={award} disabled={busy}/>
<NumericInput mobile step={5} min={0} max={maxAllocation} onChange={this.handleAwardChange} value={award}
disabled={busy} className="form-control mb-2"/>
<Form>
<Form.Control disabled={busy} placeholder="Enter your praise..." onChange={this.handlePraiseChange}
value={praise}/>
</Form>
<span> Total Awarding: {award * selectedContributors.length} SNT </span> <br/>
<Button disabled={busy} variant="outline-primary" onClick={this.awardTokens}>Award</Button>
<p className="text-center"> Total Awarding: {award * selectedContributors.length} SNT </p>
<p className="text-center"><Button disabled={busy} variant="outline-primary" onClick={this.awardTokens}>Award</Button></p>
</Tab>
<Tab eventKey="withdraw" title="Withdraw">
<p>Your Total Received Kudos: {currentContributor.totalReceived || 0} SNT</p>
<p>Your Total Forfeited Kudos: {currentContributor.totalForfeited || 0} SNT</p>
<h4>Your Kudos History</h4>
<span>Your Received Kudos: <b>{ currentContributor.received } SNT</b> <Button variant="outline-primary" onClick={this.withdrawTokens} disabled={busy}>Withdraw</Button></span> <br/>
<p>Your Received Kudos: <b>{currentContributor.received} SNT</b></p>
<p className="text-center">
<Button variant="outline-primary" onClick={this.withdrawTokens} disabled={busy}>
Withdraw
</Button>
</p>
<Container>
<Row>
{currentContributor.praises && currentContributor.praises.map((item, i) => {
const name = options.find(x => x.value === item.author);
return <Col key={i}>{(name && name.label) || item.author} has sent you {web3.utils.fromWei(item.amount, "ether")} SNT {item.praise && "\"" + item.praise + "\""}</Col>;
return <Col key={i}>{(name && name.label) || item.author} has sent
you {web3.utils.fromWei(item.amount, "ether")} SNT {item.praise && "\"" + item.praise + "\""}</Col>;
})}
</Row>
</Container>
</div>);
</Tab>
</Tabs>
</Fragment>);
}
}

View File

@ -1,5 +1,10 @@
.contributor-item {
overflow: hidden;
.contributor-controls {
.icon {
cursor: pointer;
}
}
}

View File

@ -0,0 +1,6 @@
.header {
.navbar-brand {
font-size: 22px;
font-weight: bold;
}
}

View File

@ -0,0 +1,33 @@
@import "../../css/variable-overrides";
.home-tabs.nav-tabs {
border: none;
.nav-link {
border: none;
color: $dark;
border-bottom: 2px solid $dark;
font-size: 17px;
width: 48%;
text-align: center;
&.active {
color: $primary;
border-color: $primary;
}
&+.nav-link {
margin-left: 4%;
}
}
}
.reward-panel {
.allocation {
font-size: 32px;
}
.react-numeric-input {
}
}

View File

@ -3,7 +3,9 @@ import ReactDOM from 'react-dom';
import App from './App';
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../css/fonts/Inter/inter.css';
import '../css/bootstrap-overrides.scss';
import '../css/index.scss';
ReactDOM.render(
<App/>,

View File

@ -153,3 +153,17 @@ export function saveContributorList(list) {
});
}
export function isAdmin(address) {
return new Promise(async (resolve, reject) => {
try {
const result = await Meritocracy.methods.admins(address).call();
resolve(result);
} catch (e) {
const message = 'Could not get status of user';
console.error(message);
console.error(e);
reject(message);
}
});
}