Compare commits

..

19 Commits

Author SHA1 Message Date
raucao 1b78cb4cb4 2.2.1 2024-03-21 12:26:21 +01:00
Râu Cao b32f805cbd Merge pull request #223 from 67P/bugfix/220-reimbursement_sums
Reimbursement form fixes and improvements
2024-03-21 11:38:45 +01:00
raucao 3110fd347e Remove unused variable 2024-03-20 18:31:16 +01:00
raucao 5612ac4e95 Add button type 2024-03-20 18:28:13 +01:00
raucao 69ab4913aa Require connected account for adding reimbursement
Just in the UI, so nothing gets reloaded by accident later
2024-03-20 18:18:10 +01:00
raucao 39e01e4558 Remove commented code 2024-03-20 17:46:26 +01:00
raucao 7afc75dbff Fix wrong BTC sum when adding multiple line items
closes #220
2024-03-20 17:24:37 +01:00
raucao f5f74ae27f Automatically select connected user/contributor as recipient 2024-03-20 17:24:07 +01:00
raucao 55f7baecf6 Update caniuse 2024-03-20 17:21:21 +01:00
raucao bd199fcf26 2.2.0 2024-03-20 15:17:01 +01:00
Râu Cao 0777c213ba Merge pull request #222 from 67P/feature/reimbursement-lists
Improve reimbursement lists
2024-03-20 14:50:33 +01:00
raucao 071922d979 Remove obsolete debug log 2024-03-20 14:35:01 +01:00
raucao fc044df9fd Add correct role to interactive element
Co-authored-by: Garret Alfert <alfert@wevelop.de>
2024-03-20 14:32:37 +01:00
raucao dd2bdd5332 Show dates in collapsed entries of confirmed reimbursements 2024-03-14 19:15:10 +01:00
raucao 0cd05d14bc Don't fail linting for click handlers on arbitrary HTML elements 2024-03-14 17:55:46 +01:00
raucao 3ee140723c Hide line items by default for confirmed reimbursements 2024-03-14 17:49:12 +01:00
raucao 49b3825af4 Show confirmation block for confirmed reimbursements 2024-03-14 17:48:23 +01:00
raucao af309ff682 Hide veto button for confirmed reimbursements 2024-03-14 16:43:57 +01:00
raucao 53f13e4a63 Move reimbursement items to separate component 2024-03-14 16:43:57 +01:00
22 changed files with 258 additions and 148 deletions
@@ -27,6 +27,10 @@ export default class AddReimbursementComponent extends Component {
this.exchangeRates.fetchRates(); this.exchangeRates.fetchRates();
} }
get contributorId () {
return this.recipientId || this.kredits.currentUser?.id;
}
get isValidTotal () { get isValidTotal () {
return isValidAmount(this.total); return isValidAmount(this.total);
} }
@@ -70,7 +74,7 @@ export default class AddReimbursementComponent extends Component {
} }
updateTotalAmountFromFiat() { updateTotalAmountFromFiat() {
let btcAmount = parseFloat(this.total); let btcAmount = 0;
if (this.exchangeRates.btceur > 0 && this.totalEUR > 0) { if (this.exchangeRates.btceur > 0 && this.totalEUR > 0) {
btcAmount += (this.totalEUR / this.exchangeRates.btceur); btcAmount += (this.totalEUR / this.exchangeRates.btceur);
@@ -104,7 +108,7 @@ export default class AddReimbursementComponent extends Component {
@action @action
updateContributor (event) { updateContributor (event) {
this.recipientId = event.target.value; this.recipientId = parseInt(event.target.value);
} }
@action @action
@@ -136,12 +140,12 @@ export default class AddReimbursementComponent extends Component {
if (!this.kredits.currentUser) { window.alert('You need to connect your RSK account first.'); return false } if (!this.kredits.currentUser) { window.alert('You need to connect your RSK account first.'); return false }
if (!this.kredits.currentUserIsCore) { window.alert('Only core contributors can submit reimbursements.'); return false } if (!this.kredits.currentUserIsCore) { window.alert('Only core contributors can submit reimbursements.'); return false }
const contributor = this.contributors.findBy('id', parseInt(this.recipientId)); const contributor = this.contributors.findBy('id', this.contributorId);
const attributes = { const attributes = {
amount: parseInt(parseFloat(this.total) * 100000000), // convert to sats amount: parseInt(parseFloat(this.total) * 100000000), // convert to sats
token: config.tokens['BTC'], token: config.tokens['BTC'],
recipientId: parseInt(this.recipientId), recipientId: this.contributorId,
title: `Expenses covered by ${contributor.name}`, title: `Expenses covered by ${contributor.name}`,
description: this.description, description: this.description,
url: this.url, url: this.url,
@@ -2,8 +2,7 @@
<label> <label>
<p class="label">Contributor:</p> <p class="label">Contributor:</p>
<p> <p>
<select required {{on "change" this.updateContributor}}> <select id="contributor" required {{on "change" this.updateContributor}}>
<option value="" selected disabled hidden></option>
{{#each this.contributors as |contributor|}} {{#each this.contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq this.contributorId contributor.id}}>{{contributor.name}}</option> <option value={{contributor.id}} selected={{eq this.contributorId contributor.id}}>{{contributor.name}}</option>
{{/each}} {{/each}}
@@ -15,6 +14,7 @@
<p class="label">Total amount (BTC):</p> <p class="label">Total amount (BTC):</p>
<p> <p>
<Input @type="text" <Input @type="text"
@name="total-btc"
@placeholder="0.0015" @placeholder="0.0015"
@value={{this.total}} @value={{this.total}}
@required={{true}} @required={{true}}
@@ -50,7 +50,9 @@
<p class="actions"> <p class="actions">
<button {{on "click" this.showExpenseForm}} <button {{on "click" this.showExpenseForm}}
class="green small" type="button">+ Add another item</button> id="add-another-item" class="green small" type="button">
+ Add another item
</button>
</p> </p>
{{else}} {{else}}
<p>No line items yet.</p> <p>No line items yet.</p>
+4 -2
View File
@@ -1,3 +1,5 @@
{{#unless this.isConfirmed}} {{#if this.isConfirmed}}
Confirmed at block <strong>{{@confirmedAtBlock}}</strong> (~ {{this.confirmedInHumanTime}} ago)
{{else}}
Confirming in <strong>{{this.confirmedInBlocks}}</strong> blocks (~ {{this.confirmedInHumanTime}}) Confirming in <strong>{{this.confirmedInBlocks}}</strong> blocks (~ {{this.confirmedInHumanTime}})
{{/unless}} {{/if}}
@@ -1,10 +1,20 @@
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import { action } from '@ember/object'; import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import config from 'kredits-web/config/environment'; import config from 'kredits-web/config/environment';
import fmtDateLocalized from 'kredits-web/helpers/fmt-date-localized';
export default class ReimbursementItemComponent extends Component { export default class ReimbursementItemComponent extends Component {
@service kredits; @service kredits;
@tracked showExpenseDetails = false;
constructor(owner, args) {
super(owner, args);
if (this.isUnconfirmed && !this.isVetoed) {
this.showExpenseDetails = true;
}
}
get ipfsGatewayUrl () { get ipfsGatewayUrl () {
return config.ipfs.gatewayUrl; return config.ipfs.gatewayUrl;
@@ -18,10 +28,37 @@ export default class ReimbursementItemComponent extends Component {
return !this.isConfirmed; return !this.isConfirmed;
} }
get isVetoed () {
return this.args.reimbursement.vetoed;
}
get showVetoButton () { get showVetoButton () {
return this.isUnconfirmed && this.kredits.currentUserIsCore; return this.isUnconfirmed && this.kredits.currentUserIsCore;
} }
get showConfirmedIn () {
return !this.isVetoed &&
(this.showExpenseDetails || this.isUnconfirmed);
}
get expenses () {
return this.args.reimbursement.expenses;
}
get expensesDateRange () {
const dates = this.expenses.map(e => e.date).uniq().sort();
let out = fmtDateLocalized.compute(dates.firstObject)
if (dates.length > 1) {
out += ' - ' + fmtDateLocalized.compute(dates.lastObject)
}
return out;
}
@action
toggleExpenseDetails () {
this.showExpenseDetails = !this.showExpenseDetails;
}
@action @action
veto (id) { veto (id) {
this.kredits.vetoReimbursement(id).then(transaction => { this.kredits.vetoReimbursement(id).then(transaction => {
@@ -1,5 +1,6 @@
<li data-reimbursement-id={{@reimbursement.id}} <li data-reimbursement-id={{@reimbursement.id}}
class="{{item-status @reimbursement}}"> class="{{item-status @reimbursement}}"
role="button" {{on "click" this.toggleExpenseDetails}}>
<p class="meta"> <p class="meta">
<span class="recipient"> <span class="recipient">
<UserAvatar @contributor={{@reimbursement.contributor}} /> <UserAvatar @contributor={{@reimbursement.contributor}} />
@@ -13,11 +14,17 @@
{{sats-to-btc @reimbursement.amount}}</span>&#8239;<span class="symbol">BTC</span> {{sats-to-btc @reimbursement.amount}}</span>&#8239;<span class="symbol">BTC</span>
</p> </p>
{{#if this.showExpenseDetails}}
<ExpenseList @expenses={{@reimbursement.expenses}} /> <ExpenseList @expenses={{@reimbursement.expenses}} />
{{/if}}
<div class="meta"> <div class="meta">
<p class="confirmation-eta"> <p>
{{#if this.showConfirmedIn}}
<ConfirmedIn @confirmedAtBlock={{@reimbursement.confirmedAt}} /> <ConfirmedIn @confirmedAtBlock={{@reimbursement.confirmedAt}} />
{{else}}
{{#unless this.isVetoed}}{{this.expensesDateRange}}{{/unless}}
{{/if}}
</p> </p>
<p class="actions"> <p class="actions">
<a href="{{this.ipfsGatewayUrl}}/{{@reimbursement.ipfsHash}}" <a href="{{this.ipfsGatewayUrl}}/{{@reimbursement.ipfsHash}}"
@@ -1,4 +1,4 @@
<ul class="item-list spaced reimbursement-list {{if @loading 'loading'}}"> <ul class="item-list collapsible spaced reimbursement-list {{if @loading 'loading'}}">
{{#each this.itemsSorted as |item|}} {{#each this.itemsSorted as |item|}}
<ReimbursementItem @reimbursement={{item}} /> <ReimbursementItem @reimbursement={{item}} />
{{/each}} {{/each}}
+16
View File
@@ -1,10 +1,26 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed'; import { alias } from '@ember/object/computed';
import { action } from '@ember/object';
export default class BudgetController extends Controller { export default class BudgetController extends Controller {
@service kredits; @service kredits;
@service router;
@alias('kredits.reimbursementsUnconfirmed') reimbursementsUnconfirmed; @alias('kredits.reimbursementsUnconfirmed') reimbursementsUnconfirmed;
@alias('kredits.reimbursementsConfirmed') reimbursementsConfirmed; @alias('kredits.reimbursementsConfirmed') reimbursementsConfirmed;
@alias('kredits.currentUserIsCore') currentUserIsCore;
@action
addReimbursement () {
if (!this.kredits.currentUser) {
window.alert('You need to connect your RSK account first.');
return false;
}
if (!this.kredits.currentUserIsCore) {
window.alert('Only core contributors can submit reimbursements.');
return false;
}
this.router.transitionTo('reimbursements.new');
}
} }
+1 -1
View File
@@ -1,7 +1,7 @@
import { helper } from '@ember/component/helper'; import { helper } from '@ember/component/helper';
import getLocale from 'kredits-web/utils/get-locale'; import getLocale from 'kredits-web/utils/get-locale';
export default helper(function(dateStr) { export default helper(function fmtDateLocalized(dateStr) {
const date = new Date(dateStr); const date = new Date(dateStr);
const locale = getLocale(); const locale = getLocale();
return new Intl.DateTimeFormat(locale).format(date); return new Intl.DateTimeFormat(locale).format(date);
-3
View File
@@ -30,8 +30,5 @@ export default class BudgetRoute extends Route {
} }
schedule('afterRender', this.kredits.fetchMissingReimbursements, schedule('afterRender', this.kredits.fetchMissingReimbursements,
this.kredits.fetchMissingReimbursements.perform); this.kredits.fetchMissingReimbursements.perform);
schedule('afterRender', this.kredits.syncReimbursementEvents,
this.kredits.syncReimbursementEvents.perform);
} }
} }
-27
View File
@@ -780,32 +780,5 @@ export default Service.extend({
this.contributors this.contributors
.findBy('address', to) .findBy('address', to)
.incrementProperty('balance', value); .incrementProperty('balance', value);
},
syncReimbursementEvents: task(function * () {
yield this.fetchEvents(
'Reimbursement', // contract
'ReimbursementAdded', // event
0,
// this.kredits.currentBlock - (2*60*24*14), // from block
this.currentBlock // to block
);
}).group('syncTaskGroup'),
async fetchEvents(contractName, eventName, fromBlock, toBlock) {
const contract = this.kredits[contractName].contract;
const eventFilter = contract.filters[eventName]();
const filterOptions = { fromBlock, toBlock };
console.debug(contract, eventFilter, filterOptions);
contract.queryFilter(eventFilter)
.then(events => {
events.forEach(event => {
console.debug("Event:", event.args.creator, event.args.key, event.args.value);
});
}).catch(e => {
console.error(e);
});
} }
}); });
+10
View File
@@ -16,6 +16,16 @@ ul.item-list {
} }
} }
&.collapsible {
> li {
border-left: 1px solid transparent;
&:hover {
border-left: 1px solid $blue;
}
}
}
&.loading { &.loading {
@include loading-border-top; @include loading-border-top;
@@ -43,9 +43,6 @@ ul.reimbursement-list {
flex: 1; flex: 1;
padding: 1.6rem 0 1rem 0; padding: 1.6rem 0 1rem 0;
&.confirmation-eta {
}
&.actions { &.actions {
text-align: right; text-align: right;
} }
+10 -18
View File
@@ -1,22 +1,6 @@
<main id="budget"> <main id="budget">
<div id="aside"> <div id="aside">
<!-- <section id="main&#45;nav"> -->
<!-- <header> -->
<!-- <h2>Budget</h2> -->
<!-- </header> -->
<!-- <div class="content"> -->
<!-- <ul> -->
<!-- <li> -->
<!-- <LinkTo @route="budget.expenses">Expenses</LinkTo> -->
<!-- </li> -->
<!-- <li> -->
<!-- <a href="#">Rewards</a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- </div> -->
<!-- </section> -->
<section id="funds"> <section id="funds">
<header> <header>
<h2>Community funds</h2> <h2>Community funds</h2>
@@ -33,7 +17,11 @@
<header class="with-nav"> <header class="with-nav">
<h2>Proposed Reimbursements</h2> <h2>Proposed Reimbursements</h2>
<nav> <nav>
<LinkTo @route="reimbursements.new" @title="Submit a reimbursement" class="button small green">add</LinkTo> <button type="button" class="button small green"
title="Submit a reimbursement"
{{on "click" this.addReimbursement}}>
add
</button>
</nav> </nav>
</header> </header>
<div class="content"> <div class="content">
@@ -47,7 +35,11 @@
<header class="with-nav"> <header class="with-nav">
<h2>Confirmed Reimbursements</h2> <h2>Confirmed Reimbursements</h2>
<nav> <nav>
<LinkTo @route="reimbursements.new" @title="Submit a reimbursement" class="button small green">add</LinkTo> <button type="button" class="button small green"
title="Submit a reimbursement"
{{on "click" this.addReimbursement}}>
add
</button>
</nav> </nav>
</header> </header>
<div class="content"> <div class="content">
+1 -3
View File
@@ -62,9 +62,7 @@ module.exports = function(environment) {
} }
}, },
corsProxy: 'https://cors.5apps.com/?uri=', corsProxy: 'https://cors.5apps.com/?uri='
hideVetoedEntriesAfterBlocks: 5760 // 48 hours
}; };
if (environment === 'development') { if (environment === 'development') {
+13 -6
View File
@@ -1,12 +1,12 @@
{ {
"name": "kredits-web", "name": "kredits-web",
"version": "2.1.1", "version": "2.2.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "kredits-web", "name": "kredits-web",
"version": "2.1.1", "version": "2.2.1",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.19.1",
@@ -6816,7 +6816,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001332", "version": "1.0.30001599",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz",
"integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -6826,9 +6828,12 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite" "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
], ]
"license": "CC-BY-4.0"
}, },
"node_modules/capture-exit": { "node_modules/capture-exit": {
"version": "2.0.0", "version": "2.0.0",
@@ -29050,7 +29055,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001332", "version": "1.0.30001599",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz",
"integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==",
"dev": true "dev": true
}, },
"capture-exit": { "capture-exit": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "kredits-web", "name": "kredits-web",
"version": "2.1.1", "version": "2.2.1",
"private": true, "private": true,
"description": "Contribution dashboard of the Kosmos project", "description": "Contribution dashboard of the Kosmos project",
"repository": "https://github.com/67P/kredits-web", "repository": "https://github.com/67P/kredits-web",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -8,10 +8,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="kredits-web/config/environment" content="%7B%22modulePrefix%22%3A%22kredits-web%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22kredits-web%22%2C%22version%22%3A%222.1.1%2B5c9fbf50%22%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frsk-testnet.kosmos.org%22%2C%22web3ChainId%22%3A31%2C%22web3NetworkName%22%3A%22RSK%20Testnet%22%2C%22githubConnectUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%2Fkredits%2Fsignup%2Fconnect%2Fgithub%22%2C%22githubSignupUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%2Fkredits%2Fsignup%2Fgithub%22%2C%22ipfs%22%3A%7B%22host%22%3A%22ipfs.kosmos.org%22%2C%22port%22%3A%225444%22%2C%22protocol%22%3A%22https%22%2C%22gatewayUrl%22%3A%22https%3A%2F%2Fipfs.kosmos.org%2Fipfs%22%7D%2C%22tokens%22%3A%7B%22BTC%22%3A%220x2260fac5e5542a773aa44fbcfedf7c193bc2c599%22%7D%2C%22communityFundsAPI%22%3A%7B%22balances%22%3A%7B%22onchain%22%3A%7B%22icon%22%3A%22icon-btc.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20chain%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Fonchain_btc_balance%22%7D%2C%22lightning%22%3A%7B%22icon%22%3A%22icon-btc-lightning.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20Lightning%20Network%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Flightning_btc_balance%22%7D%7D%7D%2C%22corsProxy%22%3A%22https%3A%2F%2Fcors.5apps.com%2F%3Furi%3D%22%2C%22exportApplicationGlobal%22%3Afalse%7D" /> <meta name="kredits-web/config/environment" content="%7B%22modulePrefix%22%3A%22kredits-web%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22kredits-web%22%2C%22version%22%3A%222.2.1%2Bb32f805c%22%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frsk-testnet.kosmos.org%22%2C%22web3ChainId%22%3A31%2C%22web3NetworkName%22%3A%22RSK%20Testnet%22%2C%22githubConnectUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%2Fkredits%2Fsignup%2Fconnect%2Fgithub%22%2C%22githubSignupUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%2Fkredits%2Fsignup%2Fgithub%22%2C%22ipfs%22%3A%7B%22host%22%3A%22ipfs.kosmos.org%22%2C%22port%22%3A%225444%22%2C%22protocol%22%3A%22https%22%2C%22gatewayUrl%22%3A%22https%3A%2F%2Fipfs.kosmos.org%2Fipfs%22%7D%2C%22tokens%22%3A%7B%22BTC%22%3A%220x2260fac5e5542a773aa44fbcfedf7c193bc2c599%22%7D%2C%22communityFundsAPI%22%3A%7B%22balances%22%3A%7B%22onchain%22%3A%7B%22icon%22%3A%22icon-btc.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20chain%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Fonchain_btc_balance%22%7D%2C%22lightning%22%3A%7B%22icon%22%3A%22icon-btc-lightning.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20Lightning%20Network%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Flightning_btc_balance%22%7D%7D%7D%2C%22corsProxy%22%3A%22https%3A%2F%2Fcors.5apps.com%2F%3Furi%3D%22%2C%22exportApplicationGlobal%22%3Afalse%7D" />
<link integrity="" rel="stylesheet" href="/assets/vendor-ca96b2e19fc9f356e3dbad90c9f3f323.css"> <link integrity="" rel="stylesheet" href="/assets/vendor-ca96b2e19fc9f356e3dbad90c9f3f323.css">
<link integrity="" rel="stylesheet" href="/assets/kredits-web-950f3a97cf1dfead67fdfdc4f5dc1cfb.css"> <link integrity="" rel="stylesheet" href="/assets/kredits-web-1ce689f6aada92a7d79a1947300931cf.css">
@@ -25,7 +25,7 @@
<script src="/assets/vendor-4d87b3e0995c5bb18e46836a089900a4.js" integrity="sha256-5WmWY05Kd+n98Sw0GwuaWodmDVBZ2yVGGtC/2owVfQE= sha512-tiGPA5zvfhnSC9LPTmpvXJmOu30yhsdPqSiZJMPGmE/ez/xJLMaM29vjs4Cs74cDf6fX4uYSGRIu1KfOyGpJYA==" ></script> <script src="/assets/vendor-4d87b3e0995c5bb18e46836a089900a4.js" integrity="sha256-5WmWY05Kd+n98Sw0GwuaWodmDVBZ2yVGGtC/2owVfQE= sha512-tiGPA5zvfhnSC9LPTmpvXJmOu30yhsdPqSiZJMPGmE/ez/xJLMaM29vjs4Cs74cDf6fX4uYSGRIu1KfOyGpJYA==" ></script>
<script src="/assets/kredits-web-5b714ebc501e0789ddba130f2a3f10e1.js" integrity="sha256-jb10J7E1WRUc8DztQgvbdsKXYRHqXTqjbap4CVh2AaM= sha512-dx+d1T+WrZs0qG1J4puW0hZEzG2Qh+d5iU3XXnkfkF8ya3lMp/L1xdfzh697Ca5x3/Gw7+j7Mb/LSPUfWuNCEQ==" ></script> <script src="/assets/kredits-web-71e8e218f4178df50b78058520257c0d.js" integrity="sha256-pQsThEeIoXS7h22Jg3XPg+5l268k7S63rhGhhHC10o8= sha512-CV9qAyQL3jdXW4+DmTdaGFtGYZ9Lz+f4+8aUnQyK+wOeDkLpUultyhhF2P1+wHS6Hm2vj7n213Ef89YblxDgUA==" ></script>
</body> </body>
@@ -1,13 +1,58 @@
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers'; import { click, fillIn, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars'; import { hbs } from 'ember-cli-htmlbars';
import contributors from '../../../fixtures/contributors';
module('Integration | Component | add-reimbursement', function(hooks) { module('Integration | Component | add-reimbursement', function(hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test('it works', async function(assert) { hooks.beforeEach(function() {
let kredits = this.owner.lookup('service:kredits');
kredits.set('contributors', contributors);
kredits.set('currentUser', contributors.findBy('id', 3));
});
test('Contributor select menu', async function(assert) {
await render(hbs`<AddReimbursement />`); await render(hbs`<AddReimbursement />`);
assert.ok(true);
assert.equal(this.element.querySelectorAll('select#contributor option').length, contributors.length,
'contains correct amount of items');
assert.equal(this.element.querySelector('select#contributor option:checked').value, "3",
'preselects the connected contributor account');
});
test('Adding expense items', async function(assert) {
await render(hbs`<AddReimbursement />`);
assert.dom(this.element).includesText('New expense item');
await fillIn('form input[name="expense-amount"]', '49');
await fillIn('form input[name="expense-title"]', 'Domain kosmos.org (yearly fee)');
await click('form#add-expense-item input[type="submit"]');
assert.equal(this.element.querySelector('input[name="total-eur"]').value, '49',
'updates the total EUR amount');
assert.equal(this.element.querySelector('input[name="total-usd"]').value, '0',
'does not update the total USD amount');
assert.equal(this.element.querySelector('input[name="total-btc"]').value, '0.00534493',
'updates the total BTC amount');
assert.dom(this.element).doesNotIncludeText('New expense item');
await click('button#add-another-item');
await fillIn('form input[name="expense-amount"]', '29');
await fillIn('select[name="expense-currency"]', 'USD');
await fillIn('form input[name="expense-title"]', 'Domain kosmos.social (yearly fee)');
await click('form#add-expense-item input[type="submit"]');
assert.equal(this.element.querySelector('input[name="total-usd"]').value, '29',
'updates the total USD amount');
assert.equal(this.element.querySelector('input[name="total-eur"]').value, '49',
'does not update the total EUR amount');
assert.equal(this.element.querySelector('input[name="total-btc"]').value, '0.00804268',
'updates the total BTC amount');
}); });
}); });