add table layout

-change route to make contribution
This commit is contained in:
Danil Kovtonyuk 2020-02-02 03:17:32 +10:00
parent 0de5c68d56
commit 213a7879fd
9 changed files with 647 additions and 136 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="16"><path fill-rule="evenodd" d="M8.713 1.691L2.404 8l6.309 6.309a.992.992 0 1 1-1.404 1.404L.353 8.758C.332 8.74.306 8.733.287 8.713A.984.984 0 0 1-.002 8a.984.984 0 0 1 .289-.713c.019-.02.045-.027.066-.044L7.309.287a.992.992 0 1 1 1.404 1.404z"/></svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="16"><path fill-rule="evenodd" d="M8.713 8.713c-.019.02-.045.027-.066.045l-6.956 6.955a.992.992 0 1 1-1.404-1.404L6.596 8 .287 1.691A.992.992 0 1 1 1.691.287l6.956 6.956c.021.017.047.024.066.044A.984.984 0 0 1 9.002 8a.984.984 0 0 1-.289.713z"/></svg>

After

Width:  |  Height:  |  Size: 308 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><path fill="#eee" d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z" /></svg>

After

Width:  |  Height:  |  Size: 629 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#94FEBF" fill-rule="evenodd" d="M15.678 15.678a1.045 1.045 0 0 1-1.478 0l-3.06-3.06A6.941 6.941 0 0 1 7 14a7 7 0 1 1 7-7 6.941 6.941 0 0 1-1.382 4.14l3.06 3.06c.408.408.408 1.07 0 1.478zM7 2a5 5 0 1 0 .001 10.001A5 5 0 0 0 7 2z"/>
</svg>

After

Width:  |  Height:  |  Size: 318 B

View File

@ -78,6 +78,30 @@
border-radius: 100%;
}
}
&.is-icon {
padding:0;
width: 20px;
height: 20px;
background-color: transparent;
border: none;
svg {
height: 100%;
width: 100%;
}
&:hover {
path:not(.no-hover) {
fill: #87feb7;
transition: fill .15s ease-out;
}
}
&:focus:not(:active) {
box-shadow: none;
}
}
}
.box {
@ -155,6 +179,34 @@
.label {
text-align: left;
}
&.is-horizontal {
display: flex;
align-items: center;
@include mobile {
justify-content: center;
}
.field-label.is-normal {
padding-top: 0;
margin-right: 1.5rem;
}
}
}
.currently {
font-size: .85rem;
span {
color: $primary;
font-size: 2.5rem;
font-weight: $weight-bold;
}
&:not(:last-child) {
margin-bottom: $block-spacing;
}
}
}
@ -197,3 +249,249 @@
to {
transform: rotate(0deg); }
}
.b-table {
.table {
a {
color: $primary;
&:hover {
text-decoration: underline;
}
}
th {
font-weight: $weight-normal;
}
td {
border-color: rgba($primary, .5)
}
td, th {
vertical-align: middle;
}
tbody {
tr {
&:last-child {
td {
border-bottom-width: 1px;
}
}
&:not(.is-selected) {
&:nth-child(odd) {
background-color: $table-striped-row-even-background-color;
}
&:hover {
background-color: $table-row-hover-background-color;
&:nth-child(odd) {
background-color: $table-striped-row-even-hover-background-color;
}
}
}
&:not(.is-selected).is-empty {
background-color: transparent;
&:hover {
background-color: transparent;
}
}
}
}
}
.dropdown.is-expanded {
min-width: 75px;
}
}
.icon {
background-position: center;
background-repeat: no-repeat;
background-size: contain;
&-emoticon-sad {
background-image: url('../img/icons/emoticon-sad.svg');
}
&.icon-48px {
height: 48px;
width: 48px;
}
}
.table-search {
.icon {
mask-image: url("../img/icons/search.svg");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 16px 16px;
background-color: $input-placeholder-color;
}
.input {
&:hover {
~ .icon {
background-color: $input-hover-color;
}
}
&:focus {
~ .icon {
background-color: $primary;
}
}
}
}
.dropdown.is-mobile-modal.is-expanded {
.dropdown-trigger {
.control {
.input {
&::after {
border: 1px solid #393939;
border-radius: 1px;
border-right: 0;
border-top: 0;
content: " ";
display: block;
margin-top: -0.5em;
pointer-events: none;
position: absolute;
top: 50%;
transform: rotate(-45deg);
transform-origin: center;
height: .625em;
width: .625em;
right: 1.125em;
transition: border-color .15s ease-in-out;
}
&:hover {
&::after {
border-color: $white;
}
}
&:focus {
&::after {
border-color: $primary;
}
}
}
&.is-loading {
.input {
&::after {
opacity: 0;
visibility: hidden;
}
}
&::after {
height: 1.14rem;
width: 1.14rem;
right: .86rem;
top: .86rem;
border-radius: 1.14rem;
}
}
}
}
.dropdown-menu {
@include touch {
max-width: 460px;
}
> .dropdown-content {
overflow: hidden;
margin: 1px;
> .dropdown-item {
font-size: 1rem;
padding: 0.675rem 1.25rem;
transition: color .15s ease-in-out, background-color .15s ease-in-out;
}
}
}
&.is-active {
.dropdown-trigger {
.control {
.input {
border-color: $primary;
&::after {
border-color: $primary;
}
}
}
}
}
&.is-top-right {
.dropdown-menu {
padding-top: 0px;
padding-bottom: 4px;
}
}
}
.pagination {
&-previous, &-next, &-link {
background-color: rgba($primary, .104);
font-weight: $weight-bold;
&:hover {
background-color: $primary;
}
&[disabled] {
opacity: .5;
&:hover {
background-color: rgba($primary, .104);
}
}
}
&-previous, &-next {
.icon {
width: 9px;
height: 16px;
background-color: $primary
}
&:hover {
.icon {
background-color: $primary-invert;
}
}
&[disabled] {
&:hover {
.icon {
background-color: $primary;
}
}
}
}
&-previous {
.icon {
mask-image: url("../img/icons/arrow-left.svg");
}
}
&-next {
.icon {
mask-image: url("../img/icons/arrow-right.svg");
}
}
}

View File

@ -129,6 +129,36 @@ $dropdown-background-color: $modal-background-background-color;
$widescreen-enabled: false;
$fullhd-enabled: false;
$table-background-color: transparent;
$table-color: $white;
$table-head-cell-color: $white;
$table-head-cell-border-width: 0 0 4px;
$table-cell-border: 1px solid $primary;
$table-striped-row-even-background-color: rgba($primary, .104);
$table-row-hover-background-color: rgba($primary, .154);
$table-striped-row-even-hover-background-color: rgba($primary, .154);
$table-cell-padding: 1.5em 0.75em;
$pagination-color: $primary;
$pagination-border-color: $primary;
$pagination-hover-color: $primary-invert;
$pagination-hover-border-color: $primary;
$pagination-focus-color: $pagination-hover-color;
$pagination-focus-border-color: $pagination-hover-border-color;
$pagination-active-color: $pagination-hover-color;
$pagination-active-border-color: $pagination-hover-border-color;
$pagination-disabled-color: $primary;
$pagination-disabled-background-color: rgba($primary, .104);
$pagination-disabled-border-color: $primary;
$pagination-current-color: $primary-invert;
$pagination-current-background-color: $primary;
$pagination-current-border-color: $primary;
.columns {
@include from(576px) {
&.is-small {

View File

@ -0,0 +1,9 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<path
fill="#94FEBF"
fill-rule="evenodd"
d="M17 18H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 2 0v3h14v-3a1 1 0 0 1 2 0v4a1 1 0 0 1-1 1zm-7.134-6.332a1.08 1.08 0 0 1-.78.315C9.056 11.985 9.03 12 9 12c-.03 0-.056-.015-.086-.017a1.08 1.08 0 0 1-.78-.315L4.298 7.833a1.086 1.086 0 0 1 1.534-1.535L8 8.466V1a1 1 0 0 1 2 0v7.466l2.167-2.168a1.087 1.087 0 0 1 1.535 0 1.087 1.087 0 0 1 0 1.535l-3.836 3.835z"
/>
</svg>
</template>

View File

@ -1,159 +1,167 @@
<template>
<div class="ceremony">
<div class="title is-size-1 is-spaced">
Hello, <span>@{{ user.handle }}</span>
</div>
<div class="subtitle">Lorem ipsum dolor sit amet, consectetur?</div>
<p class="p">
If you dont trust binaries, we encorage you to follow this <a href="">instruction</a> to
contribute by compiling from source code. It is very easy!
<h1 class="title is-size-1 is-spaced">Lorem <span>Ipsum Dolor</span></h1>
<p class="p is-size-5">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat.
</p>
<div class="columns is-centered">
<div class="column is-one-third">
<div class="box">
<div class="title is-5">Lorem ipsum</div>
<Cloak />
</div>
</div>
<div class="column is-one-third">
<div :class="{ 'is-hovered': isLoggedIn }" class="box">
<div class="title is-5">Lorem ipsum</div>
<div v-if="isLoggedIn" class="fields">
<b-field label="Name">
<b-input v-model="user.name"></b-input>
</b-field>
<b-field label="Company">
<b-input v-model="user.company"></b-input>
</b-field>
</div>
<div v-else class="buttons">
<b-button @click="logIn" type="is-primary" outlined expanded>
Sign In
</b-button>
</div>
</div>
</div>
</div>
<div v-show="status.msg !== ''" class="status">
<div :class="status.type" class="status-message">{{ status.msg }}</div>
<div v-show="status.type === ''" class="status-spinner"></div>
</div>
<div class="buttons is-centered">
<b-button
v-if="!isContributeBtnDisabled"
@click="makeContribution"
type="is-primary"
outlined
>
<b-button type="is-primary" outlined tag="router-link" to="/make-contribution">
Make the contribution
</b-button>
<b-button
v-if="isContributeBtnDisabled && status.type === 'is-success'"
type="is-primary"
tag="a"
href="https://twitter.com/intent/tweet?text=Hello%20world"
target="_blank"
outlined
>
Tweet about your contribution
</b-button>
</div>
<div class="currently">Currently there are <span>8999</span> contributions</div>
<b-table
:data="filteredContributions"
:hoverable="true"
:mobile-cards="false"
:per-page="rowsPerPage"
paginated
pagination-position="both"
>
<template slot-scope="props">
<b-table-column field="id" label="#" width="40" numeric>
{{ props.row.id }}
</b-table-column>
<b-table-column label="Account">
<a :href="`#${props.row.account}`" target="_blank">{{ props.row.account }}</a>
</b-table-column>
<b-table-column field="name" label="Name">
{{ props.row.name }}
</b-table-column>
<b-table-column field="company" label="Company">
{{ props.row.company }}
</b-table-column>
<b-table-column label="Attestation">
<a :href="props.row.attestation" target="_blank">{{ props.row.account }}</a>
</b-table-column>
<b-table-column>
<a :href="props.row.contribution" class="button is-icon">
<Link />
</a>
</b-table-column>
</template>
<template slot="empty">
<section class="section">
<div class="content has-text-centered">
<p>
<span class="icon icon-emoticon-sad icon-48px"></span>
</p>
<p>Nothing here.</p>
</div>
</section>
</template>
<template slot="top-left">
<b-field class="table-search">
<b-input
v-model="contributionSearch"
placeholder="Search..."
type="search"
icon="magnify"
></b-input>
</b-field>
</template>
<template slot="bottom-left">
<b-field horizontal label="Show">
<b-dropdown v-model="rowsPerPage" expanded aria-role="list" position="is-top-right">
<div slot="trigger" class="control">
<div class="input">
<span>{{ rowsPerPage }}</span>
</div>
</div>
<b-dropdown-item
v-for="(rows, index) in [10, 15, 20, 50]"
:key="index"
:value="rows"
aria-role="listitem"
>
{{ rows }}
</b-dropdown-item>
</b-dropdown>
</b-field>
</template>
</b-table>
</div>
</template>
<script>
/* eslint-disable no-console */
import Cloak from '@/components/Cloak'
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
import Link from '@/components/icons/Link'
export default {
components: {
Cloak
Link
},
data() {
return {
isContributeBtnDisabled: false,
status: {
type: '',
msg: ''
},
user: { name: '', handle: 'Anonymous', company: '' }
contributions: [
{
id: 1,
account: '@VitalikButerin',
name: 'Vitalik Buterin',
company: 'Ethereum',
attestation: 'https://twitter.com/VitalikButerin/status/1220158987456237568',
contribution: '#'
},
{
id: 2,
account: '@Chims1974',
name: 'Rickey Kline',
company: 'Big Wheel',
attestation: '#',
contribution: '#'
},
{
id: 3,
account: '@Diguest',
name: 'Ryan Obrien',
company: 'Brilliant Home',
attestation: '#',
contribution: '#'
},
{
id: 4,
account: '@Mathervenrat',
name: 'William Hartwig',
company: 'New World',
attestation: '#',
contribution: '#'
},
{
id: 5,
account: '@Hichercy',
name: 'Wayne Biggins',
company: 'Balanced Fortune',
attestation: '#',
contribution: '#'
},
{
id: 6,
account: '',
name: 'Anonymous',
company: '',
attestation: '',
contribution: '#'
}
],
rowsPerPage: 10,
contributionSearch: ''
}
},
computed: {
isLoggedIn() {
return !!this.user.name && this.user.name !== 'Anonymous'
}
},
async mounted() {
try {
const data = await this.$axios.$get('/api/user_data')
console.log('data', data)
if (data.name !== 'Anonymous') {
this.user.handle = data.handle
this.user.name = data.name
}
} catch (e) {
console.error('user_data fail', e)
}
},
methods: {
async makeContribution({ retry = 0 } = {}) {
try {
this.isContributeBtnDisabled = true
this.status.msg = 'Downloading last contribution'
this.status.type = ''
let data = await fetch('api/challenge')
data = new Uint8Array(await data.arrayBuffer())
this.status.msg = 'Generating random contribution'
await timeout(100) // allow UI to update before freezing in wasm
console.log('Source params', data)
const contribute = await this.$contribute()
const result = contribute(data)
console.log('Updated params', result)
this.status.msg = 'Uploading and verifying your contribution'
console.log('this.user.name', this.user)
const formData = new FormData()
formData.append('response', new Blob([result], { type: 'application/octet-stream' }))
formData.append('name', this.user.name)
formData.append('company', this.user.company)
const resp = await fetch('api/response', {
method: 'POST',
body: formData
})
if (resp.ok) {
this.status.msg = 'Your contribution is verified and recorded. THX BYE.'
this.status.type = 'is-success'
} else if (resp.status === 422) {
if (retry < 3) {
console.log(`Looks like someone else uploaded contribution ahead of us, retrying`)
await this.makeContribution({ retry: retry++ })
} else {
this.status.msg = `Failed to upload your contribution after ${retry} attempts`
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
} else {
this.status.msg = 'Error uploading your contribution'
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
} catch (e) {
console.error(e.message)
this.status.msg = e.message
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
},
logIn() {
window.location.replace('/api/connect')
filteredContributions() {
return this.contributions.filter((contribution) => {
return contribution.name.toLowerCase().includes(this.contributionSearch.toLowerCase())
})
}
}
}

160
pages/make-contribution.vue Normal file
View File

@ -0,0 +1,160 @@
<template>
<div class="ceremony">
<h1 class="title is-size-1 is-spaced">
Hello, <span>@{{ user.handle }}</span>
</h1>
<h2 class="subtitle">Lorem ipsum dolor sit amet, consectetur?</h2>
<p class="p">
If you dont trust binaries, we encorage you to follow this <a href="">instruction</a> to
contribute by compiling from source code. It is very easy!
</p>
<div class="columns is-centered">
<div class="column is-one-third">
<div class="box">
<div class="title is-5">Lorem ipsum</div>
<Cloak />
</div>
</div>
<div class="column is-one-third">
<div :class="{ 'is-hovered': isLoggedIn }" class="box">
<div class="title is-5">Lorem ipsum</div>
<div v-if="isLoggedIn" class="fields">
<b-field label="Name">
<b-input v-model="user.name"></b-input>
</b-field>
<b-field label="Company">
<b-input v-model="user.company"></b-input>
</b-field>
</div>
<div v-else class="buttons">
<b-button @click="logIn" type="is-primary" outlined expanded>
Sign In
</b-button>
</div>
</div>
</div>
</div>
<div v-show="status.msg !== ''" class="status">
<div :class="status.type" class="status-message">{{ status.msg }}</div>
<div v-show="status.type === ''" class="status-spinner"></div>
</div>
<div class="buttons is-centered">
<b-button
v-if="!isContributeBtnDisabled"
@click="makeContribution"
type="is-primary"
outlined
>
Make the contribution
</b-button>
<b-button
v-if="isContributeBtnDisabled && status.type === 'is-success'"
type="is-primary"
tag="a"
href="https://twitter.com/intent/tweet?text=Hello%20world"
target="_blank"
outlined
>
Tweet about your contribution
</b-button>
</div>
</div>
</template>
<script>
/* eslint-disable no-console */
import Cloak from '@/components/Cloak'
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
export default {
components: {
Cloak
},
data() {
return {
isContributeBtnDisabled: false,
status: {
type: '',
msg: ''
},
user: { name: '', handle: 'Anonymous', company: '' }
}
},
computed: {
isLoggedIn() {
return !!this.user.name && this.user.name !== 'Anonymous'
}
},
async mounted() {
try {
const data = await this.$axios.$get('/api/user_data')
console.log('data', data)
if (data.name !== 'Anonymous') {
this.user.handle = data.handle
this.user.name = data.name
}
} catch (e) {
console.error('user_data fail', e)
}
},
methods: {
async makeContribution({ retry = 0 } = {}) {
try {
this.isContributeBtnDisabled = true
this.status.msg = 'Downloading last contribution'
this.status.type = ''
let data = await fetch('api/challenge')
data = new Uint8Array(await data.arrayBuffer())
this.status.msg = 'Generating random contribution'
await timeout(100) // allow UI to update before freezing in wasm
console.log('Source params', data)
const contribute = await this.$contribute()
const result = contribute(data)
console.log('Updated params', result)
this.status.msg = 'Uploading and verifying your contribution'
console.log('this.user.name', this.user)
const formData = new FormData()
formData.append('response', new Blob([result], { type: 'application/octet-stream' }))
formData.append('name', this.user.name)
formData.append('company', this.user.company)
const resp = await fetch('api/response', {
method: 'POST',
body: formData
})
if (resp.ok) {
this.status.msg = 'Your contribution is verified and recorded. THX BYE.'
this.status.type = 'is-success'
} else if (resp.status === 422) {
if (retry < 3) {
console.log(`Looks like someone else uploaded contribution ahead of us, retrying`)
await this.makeContribution({ retry: retry++ })
} else {
this.status.msg = `Failed to upload your contribution after ${retry} attempts`
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
} else {
this.status.msg = 'Error uploading your contribution'
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
} catch (e) {
console.error(e.message)
this.status.msg = e.message
this.status.type = 'is-danger'
this.isContributeBtnDisabled = false
}
},
logIn() {
window.location.replace('/api/connect')
}
}
}
</script>