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%; 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 { .box {
@ -155,6 +179,34 @@
.label { .label {
text-align: left; 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 { to {
transform: rotate(0deg); } 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; $widescreen-enabled: false;
$fullhd-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 { .columns {
@include from(576px) { @include from(576px) {
&.is-small { &.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> <template>
<div class="ceremony"> <div class="ceremony">
<div class="title is-size-1 is-spaced"> <h1 class="title is-size-1 is-spaced">Lorem <span>Ipsum Dolor</span></h1>
Hello, <span>@{{ user.handle }}</span> <p class="p is-size-5">
</div> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
<div class="subtitle">Lorem ipsum dolor sit amet, consectetur?</div> labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
<p class="p"> laboris nisi ut aliquip ex ea commodo consequat.
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> </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"> <div class="buttons is-centered">
<b-button <b-button type="is-primary" outlined tag="router-link" to="/make-contribution">
v-if="!isContributeBtnDisabled"
@click="makeContribution"
type="is-primary"
outlined
>
Make the contribution Make the contribution
</b-button> </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>
<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> </div>
</template> </template>
<script> <script>
/* eslint-disable no-console */ import Link from '@/components/icons/Link'
import Cloak from '@/components/Cloak'
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
export default { export default {
components: { components: {
Cloak Link
}, },
data() { data() {
return { return {
isContributeBtnDisabled: false, contributions: [
status: { {
type: '', id: 1,
msg: '' account: '@VitalikButerin',
}, name: 'Vitalik Buterin',
user: { name: '', handle: 'Anonymous', company: '' } 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: { computed: {
isLoggedIn() { filteredContributions() {
return !!this.user.name && this.user.name !== 'Anonymous' return this.contributions.filter((contribution) => {
} return contribution.name.toLowerCase().includes(this.contributionSearch.toLowerCase())
}, })
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')
} }
} }
} }

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>