Add quick filters #134

Merged
raucao merged 3 commits from feature/quick_filter into master 2019-07-08 13:52:07 +00:00
9 changed files with 193 additions and 43 deletions
+46 -2
View File
@@ -1,9 +1,53 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { sort } from '@ember/object/computed';
import { isPresent } from '@ember/utils';
import { inject as service } from '@ember/service';
export default Component.extend({
tagName: 'ul',
classNames: ['contribution-list'],
tagName: 'div',
classNames: ['contributions'],
showQuickFilter: false,
hideSmallContributions: false,
contributorId: null,
contributionKind: null,
kredits: service(),
contributorsSorting: Object.freeze(['name:asc']),
contributors: sort('kredits.contributors', 'contributorsSorting'),
contributorsActive: computed('contributors.[]', 'contributions', function() {
let activeIds = this.contributions.mapBy('contributorId')
.map(id => id.toString())
.uniq();
return this.contributors.filter(c => {
return activeIds.includes(c.id.toString());
});
}),
contributionKinds: computed('contributions.[]', function() {
return this.contributions.mapBy('kind').uniq();
}),
contributionsFiltered: computed('contributions.[]', 'hideSmallContributions', 'contributorId', 'contributionKind', function() {
return this.contributions.filter(c => {
let included = true;
if (this.hideSmallContributions &&
c.amount <= 500) { included = false; }
if (isPresent(this.contributorId) &&
c.contributorId.toString() !== this.contributorId.toString()) { included = false; }
if (isPresent(this.contributionKind) &&
c.kind !== this.contributionKind) { included = false; }
return included;
});
}),
actions: {
+56 -23
View File
@@ -1,25 +1,58 @@
{{#each contributions as |contribution|}}
<li data-contribution-id={{contribution.id}} class="{{contribution-status contribution}} {{if contribution.vetoed "vetoed"}}">
<p class="meta">
<span class="recipient">{{user-avatar contributor=contribution.contributor}}</span>
<span class="category {{contribution.kind}}">({{contribution.kind}})</span>
<span class="title">
{{#if contribution.url}}
<a href={{contribution.url}} target="_blank" rel="noopener">{{contribution.description}}</a>
{{else}}
{{contribution.description}}
{{/if}}
</span>
{{#if showQuickFilter}}
<div class="quick-filter">
<p>
<label class="filter-contributor">
Contributor:
<select onchange={{action (mut contributorId) value="target.value"}}>
<option value="" selected>all</option>
{{#each contributorsActive as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.name}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-kind">
Kind:
<select onchange={{action (mut contributionKind) value="target.value"}}>
<option value="" selected>all</option>
{{#each contributionKinds as |kind|}}
<option value={{kind}} selected={{eq contributionKind kind}}>{{capitalize-string kind}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-size">
{{input type="checkbox" checked=hideSmallContributions}}
Hide small contributions
</label>
</p>
<p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p>
{{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}}
<p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button>
</p>
</div>
{{/if}}
<ul class="contribution-list">
{{#each contributionsFiltered as |contribution|}}
<li data-contribution-id={{contribution.id}} class="{{contribution-status contribution}} {{if contribution.vetoed "vetoed"}}">
<p class="meta">
<span class="recipient">{{user-avatar contributor=contribution.contributor}}</span>
<span class="category {{contribution.kind}}">({{contribution.kind}})</span>
<span class="title">
{{#if contribution.url}}
<a href={{contribution.url}} target="_blank" rel="noopener">{{contribution.description}}</a>
{{else}}
{{contribution.description}}
{{/if}}
</span>
</p>
<p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p>
{{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}}
<p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button>
</p>
{{/unless}}
{{/unless}}
{{/unless}}
</li>
{{/each}}
</li>
{{/each}}
</ul>
+11
View File
@@ -25,6 +25,9 @@ export default Controller.extend({
showUnconfirmedKredits: true,
hideUnconfirmedKredits: not('showUnconfirmedKredits'),
showQuickFilterUnconfirmed: false,
showQuickFilterConfirmed: false,
actions: {
vetoContribution (contributionId) {
@@ -37,6 +40,14 @@ export default Controller.extend({
this.kredits.vote(proposalId).then(transaction => {
console.debug('[controllers:index] Vote submitted to Ethereum blockhain: '+transaction.hash);
});
},
toggleQuickFilterUnconfirmed () {
this.toggleProperty('showQuickFilterUnconfirmed');
},
toggleQuickFilterConfirmed () {
this.toggleProperty('showQuickFilterConfirmed');
}
}
+7
View File
@@ -0,0 +1,7 @@
import Helper from '@ember/component/helper';
export default Helper.extend({
compute([string]) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
});
+14
View File
@@ -11,10 +11,18 @@ button, input[type=submit], .button {
cursor: pointer;
letter-spacing: 0.1em;
&+button, &+input[type=submit], &+.button {
margin-left: 0.5rem;
}
&:hover {
background-color: rgba(22, 21, 40, 0.8);
}
&:active, &.active {
border-color: $primary-color;
}
&.small {
font-size: 0.8rem;
padding: 0.2rem 0.8rem;
@@ -28,6 +36,9 @@ button, input[type=submit], .button {
&:hover {
background-color: rgba(40, 21, 21, 0.8);
}
&:active, &.active {
border-color: $red;
}
}
&.green {
@@ -38,5 +49,8 @@ button, input[type=submit], .button {
&:hover {
background-color: rgba(21, 40, 21, 0.8);
}
&:active, &.active {
border-color: $green;
}
}
}
@@ -6,6 +6,24 @@ main section {
}
}
.quick-filter {
font-size: 1.2rem;
margin-bottom: 2rem;
p {
font-size: inherit;
padding: 0.2rem 0 0;
}
label {
font-size: inherit;
&+ label {
margin-left: 3.6rem;
}
}
}
ul.contribution-list {
clear: both;
width: 100%;
+9 -3
View File
@@ -42,6 +42,7 @@
<h2>Latest Contributions</h2>
{{#if kredits.hasAccounts}}
<nav>
<button {{action "toggleQuickFilterUnconfirmed"}} class="small {{if showQuickFilterUnconfirmed "active"}}">filter</button>
{{link-to "add" "contributions.new" title="Submit a contribution" class="button small green"}}
</nav>
{{/if}}
@@ -50,18 +51,23 @@
{{!-- TODO: We need a better naming for kredits.hasAccounts --}}
{{contribution-list contributions=contributionsUnconfirmedSorted
vetoContribution=(action "vetoContribution")
contractInteractionEnabled=kredits.hasAccounts}}
contractInteractionEnabled=kredits.hasAccounts
showQuickFilter=showQuickFilterUnconfirmed}}
</div>
</section>
{{/if}}
<section id="contributions-confirmed">
<header>
<header class="with-nav">
<h2>Confirmed Contributions</h2>
<nav>
<button {{action "toggleQuickFilterConfirmed"}} class="small {{if showQuickFilterConfirmed "active"}}">filter</button>
</nav>
</header>
<div class="content">
{{contribution-list contributions=contributionsConfirmedSorted
vetoContribution=(action "vetoContribution")}}
vetoContribution=(action "vetoContribution")
showQuickFilter=showQuickFilterConfirmed}}
</div>
</section>
</div>
+9 -9
View File
@@ -3,15 +3,15 @@ import Model from 'kredits-web/models/contribution';
const items = [];
const data = [
{ id: 1, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500 },
{ id: 2, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 5000 },
{ id: 3, contributorId: 2, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500 },
{ id: 4, contributorId: 2, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500 },
{ id: 5, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 5000 },
{ id: 6, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: true, amount: 500 },
{ id: 7, contributorId: 3, confirmedAtBlock: 2000, claimed: false, vetoed: false, amount: 5000 },
{ id: 8, contributorId: 1, confirmedAtBlock: 2000, claimed: false, vetoed: false, amount: 1500 },
{ id: 9, contributorId: 3, confirmedAtBlock: 2000, claimed: false, vetoed: true, amount: 1500 },
{ id: 1, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500, kind: 'dev' },
{ id: 2, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 5000, kind: 'ops' },
{ id: 3, contributorId: 2, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500, kind: 'ops' },
{ id: 4, contributorId: 2, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 1500, kind: 'docs' },
{ id: 5, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: false, amount: 5000, kind: 'design' },
{ id: 6, contributorId: 1, confirmedAtBlock: 1000, claimed: false, vetoed: true, amount: 500, kind: 'dev' },
{ id: 7, contributorId: 3, confirmedAtBlock: 2000, claimed: false, vetoed: false, amount: 5000, kind: 'dev' },
{ id: 8, contributorId: 1, confirmedAtBlock: 2000, claimed: false, vetoed: false, amount: 1500, kind: 'community' },
{ id: 9, contributorId: 3, confirmedAtBlock: 2000, claimed: false, vetoed: true, amount: 1500, kind: 'docs' },
];
data.forEach(attrs => items.push(Model.create(attrs)));
@@ -1,17 +1,34 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { click, fillIn, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import contributors from '../../../fixtures/contributors';
import contributions from '../../../fixtures/contributions';
module('Integration | Component | contribution-list', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
test('it renders all contributions', async function(assert) {
this.set('fixtures', contributions);
await render(hbs`{{contribution-list contributions=fixtures}}`);
await render(hbs`{{contribution-list}}`);
assert.equal(this.element.querySelectorAll('li').length, 9);
});
assert.equal(this.element.textContent.trim(), '');
test('it renders filtered contributions', async function(assert) {
let service = this.owner.lookup('service:kredits');
service.set('contributors', contributors);
this.set('fixtures', contributions);
await render(hbs`{{contribution-list contributions=fixtures showQuickFilter=true}}`);
await fillIn('.filter-contributor select', '1');
assert.equal(this.element.querySelectorAll('li').length, 5, 'select contributor');
await click('.filter-contribution-size input');
assert.equal(this.element.querySelectorAll('li').length, 4, 'hide small contributions');
await fillIn('.filter-contribution-kind select', 'dev');
assert.equal(this.element.querySelectorAll('li').length, 1, 'select kind');
});
});