Merge pull request #12 from jrainville/feat/a-bit-of-style
Add a bit of style
This commit is contained in:
commit
20b4ff0351
|
@ -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.
|
@ -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");
|
||||
}
|
|
@ -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
Binary file not shown.
After Width: | Height: | Size: 718 B |
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
||||
|
|
|
@ -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>);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
.contributor-item {
|
||||
overflow: hidden;
|
||||
|
||||
.contributor-controls {
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
.header {
|
||||
.navbar-brand {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
|
@ -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/>,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue