From e4d2fdfce47e39b09a594c10f442070a70514dc0 Mon Sep 17 00:00:00 2001
From: Sebastian Kippe
Date: Mon, 28 Sep 2020 14:13:17 +0200
Subject: [PATCH] WIP Reimbursements/expenses
---
app/components/add-expense-item/component.js | 59 +++++++++++
app/components/add-expense-item/template.hbs | 64 ++++++++++++
app/components/add-reimbursement/component.js | 35 ++++---
app/components/add-reimbursement/template.hbs | 99 ++++++-------------
.../reimbursement-list/template.hbs | 20 ++--
app/models/expense.js | 43 --------
app/styles/_forms.scss | 7 +-
app/styles/_item-list.scss | 6 +-
app/styles/app.scss | 1 +
app/styles/components/_budget-balances.scss | 2 +-
app/styles/components/_expense-list.scss | 48 +++++++++
.../components/_reimbursement-list.scss | 29 +-----
.../add-expense-item/component-test.js | 26 +++++
13 files changed, 271 insertions(+), 168 deletions(-)
create mode 100644 app/components/add-expense-item/component.js
create mode 100644 app/components/add-expense-item/template.hbs
delete mode 100644 app/models/expense.js
create mode 100644 app/styles/components/_expense-list.scss
create mode 100644 tests/integration/components/add-expense-item/component-test.js
diff --git a/app/components/add-expense-item/component.js b/app/components/add-expense-item/component.js
new file mode 100644
index 0000000..6ac06d4
--- /dev/null
+++ b/app/components/add-expense-item/component.js
@@ -0,0 +1,59 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action } from '@ember/object';
+import moment from 'moment';
+
+export default class AddExpenseItemComponent extends Component {
+
+ // @tracked newExpense = Expense.create();
+ @tracked amount = '0';
+ @tracked currency = 'EUR';
+ @tracked date = moment().startOf('hour').toDate();
+ @tracked title = '';
+ @tracked description = '';
+ @tracked url = '';
+ @tracked tags = '';
+
+ defaultDate = moment().startOf('hour').toDate();
+
+ // TODO fetch/apply exchange rate to (W)BTC
+ currencies = [
+ { code: 'EUR' },
+ { code: 'USD' },
+ { code: 'GBP' }
+ ];
+
+ get submitButtonEnabled () {
+ return true;
+ }
+
+ get submitButtonDisabled () {
+ return !this.submitButtonEnabled;
+ }
+
+ setDefaultValues () {
+ }
+
+ @action
+ submit (e) {
+ e.preventDefault();
+
+ let dateInput = (this.date instanceof Array) ?
+ this.date[0] : this.date;
+ const [ date ] = dateInput.toISOString().split('T');
+
+ // TODO validate form
+ const expense = {
+ amount: parseFloat(this.amount),
+ currency: this.currency,
+ date: date,
+ title: this.title,
+ description: this.description,
+ url: this.url,
+ tags: this.tags.split(',').map(t => t.trim())
+ }
+
+ this.args.addExpenseItem(expense);
+ this.resetProperties();
+ }
+}
diff --git a/app/components/add-expense-item/template.hbs b/app/components/add-expense-item/template.hbs
new file mode 100644
index 0000000..ddc176c
--- /dev/null
+++ b/app/components/add-expense-item/template.hbs
@@ -0,0 +1,64 @@
+
\ No newline at end of file
diff --git a/app/components/add-reimbursement/component.js b/app/components/add-reimbursement/component.js
index 17d9c38..8dc305d 100644
--- a/app/components/add-reimbursement/component.js
+++ b/app/components/add-reimbursement/component.js
@@ -5,40 +5,39 @@ import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { A } from '@ember/array';
import Reimbursement from 'kredits-web/models/reimbursement';
-import Expense from 'kredits-web/models/expense';
export default class AddReimbursementComponent extends Component {
@service kredits;
+ @alias('kredits.contributorsSorted') contributors;
+
@tracked recipientId = null;
@tracked title = '';
@tracked total = '0';
- @tracked expenses = A([]);;
- @tracked newExpense = Expense.create();
+ @tracked expenses = A([]);
+ @tracked expenseFormVisible = true;
- // TODO fetch/apply exchange rate to (W)BTC
- currencies = [
- { code: 'EUR' },
- { code: 'USD' },
- { code: 'GBP' }
- ];
-
- get typeofTotal() {
- return typeof this.total;
- }
-
- get submitButtonEnabled() {
+ get submitButtonEnabled () {
return this.expenses.length > 0;
}
- get submitButtonDisabled() {
+ get submitButtonDisabled () {
return !this.submitButtonEnabled;
}
- @alias('kredits.contributorsSorted') contributors;
+ @action
+ showExpenseForm () {
+ this.expenseFormVisible = true;
+ }
@action
- submit(e) {
+ addExpenseItem (expenseItem) {
+ this.expenses.pushObject(expenseItem);
+ this.expenseFormVisible = false;
+ }
+
+ @action
+ submit (e) {
e.preventDefault();
console.log('submit', e);
// TODO
diff --git a/app/components/add-reimbursement/template.hbs b/app/components/add-reimbursement/template.hbs
index 3dbb710..e42b008 100644
--- a/app/components/add-reimbursement/template.hbs
+++ b/app/components/add-reimbursement/template.hbs
@@ -17,82 +17,41 @@
-
- Expense items
-
- {{#each this.expenses as |expense|}}
-
-
- | {{expense.title}} – {{expense.description}} |
- {{fmt-fiat-currency expense.amount expense.currency}} |
-
-
- TODO: date, url, tags
+ Expense items
+ {{#if this.expenses}}
+
+
+
+
{{else}}
No line items yet.
- {{/each}}
+ {{/if}}
{{input type="submit" value="Submit" disabled=this.submitButtonDisabled
title="Submit/propose this reimbursement"}}
-
- New expense item
-
-
+ {{#if this.expenseFormVisible}}
+ New expense item
+
+ {{/if}}
diff --git a/app/components/reimbursement-list/template.hbs b/app/components/reimbursement-list/template.hbs
index 0bdd008..82d3404 100644
--- a/app/components/reimbursement-list/template.hbs
+++ b/app/components/reimbursement-list/template.hbs
@@ -11,14 +11,22 @@
{{sats-to-btc reimbursement.amount}} WBTC
-
+
{{#each reimbursement.expenses as |expense|}}
-
- | {{expense.title}} – {{expense.description}} |
- {{fmt-fiat-currency expense.amount expense.currency}} |
-
+ -
+
+
+ {{expense.date}}:
+ {{expense.title}}
+
+
{{expense.description}}
+
+
+ {{fmt-fiat-currency expense.amount expense.currency}}
+
+
{{/each}}
-
+
{{/each}}
\ No newline at end of file
diff --git a/app/models/expense.js b/app/models/expense.js
deleted file mode 100644
index cd050af..0000000
--- a/app/models/expense.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import EmberObject, { computed } from '@ember/object';
-import moment from 'moment';
-import { A } from '@ember/array';
-
-export default EmberObject.extend({
- title: null,
- description: null,
- currency: null,
- amount: null,
- date: null,
- url: null,
- tags: null,
-
- init () {
- this._super(...arguments);
- this.setDefaultValues();
- },
-
- iso8601Date: computed('date', 'time', function() {
- return this.time ? `${this.date}T${this.time}` : this.date;
- }),
-
- jsDate: computed('iso8601Date', function() {
- return moment(this.iso8601Date).toDate();
- }),
-
- setDefaultValues () {
- this.set('defaultDate', moment().startOf('hour').toDate());
- this.setProperties({
- title: '',
- description: '',
- currency: 'EUR',
- amount: 0,
- date: this.defaultDate,
- url: null,
- tags: A([])
- });
- },
-
- serialize () {
- return JSON.stringify(this);
- }
-});
diff --git a/app/styles/_forms.scss b/app/styles/_forms.scss
index 443ab72..58e9ec0 100644
--- a/app/styles/_forms.scss
+++ b/app/styles/_forms.scss
@@ -13,18 +13,19 @@ section#signup {
p {
font-size: 1.2rem;
- margin-bottom: 1.5rem;
&.mg-bottom-md {
margin-bottom: 2rem;
}
&.label {
+ margin-bottom: 1.5rem;
font-size: 1rem;
margin-bottom: .5rem;
}
&.actions {
+ margin-bottom: 1.5rem;
padding-top: 1.5rem;
text-align: center;
a {
@@ -38,6 +39,10 @@ section#signup {
display: block;
margin-bottom: 0.5rem;
opacity: 0.7;
+
+ > p {
+ margin-bottom: 1.5rem;
+ }
}
fieldset {
diff --git a/app/styles/_item-list.scss b/app/styles/_item-list.scss
index bdef14c..548bf3c 100644
--- a/app/styles/_item-list.scss
+++ b/app/styles/_item-list.scss
@@ -1,7 +1,7 @@
ul.item-list {
list-style: none;
- li {
+ > li {
padding: 0.8rem 1.2rem;
font-size: 1.2rem;
background-color: $item-background-color;
@@ -19,7 +19,7 @@ ul.item-list {
&.loading {
@include loading-border-top;
- li {
+ > li {
&:first-of-type {
border-top: none;
}
@@ -27,7 +27,7 @@ ul.item-list {
}
&.spaced {
- li {
+ > li {
border-top: 1px solid $item-border-color;
margin-bottom: 2rem;
}
diff --git a/app/styles/app.scss b/app/styles/app.scss
index 14dba8f..ba4617f 100644
--- a/app/styles/app.scss
+++ b/app/styles/app.scss
@@ -96,6 +96,7 @@ section {
@import "components/contribution-list";
@import "components/contributor-list";
@import "components/contributor-profile";
+@import "components/expense-list";
@import "components/external-account-link";
@import "components/loading-spinner";
@import "components/reimbursement-list";
diff --git a/app/styles/components/_budget-balances.scss b/app/styles/components/_budget-balances.scss
index 05226bd..83d2d91 100644
--- a/app/styles/components/_budget-balances.scss
+++ b/app/styles/components/_budget-balances.scss
@@ -25,7 +25,7 @@ section#funds {
text-align: right;
&.amount {
- font-size: 1.6rem;
+ font-size: 1.5rem;
padding-right: 1.2rem;
}
diff --git a/app/styles/components/_expense-list.scss b/app/styles/components/_expense-list.scss
new file mode 100644
index 0000000..b369f5f
--- /dev/null
+++ b/app/styles/components/_expense-list.scss
@@ -0,0 +1,48 @@
+ul.expense-list {
+ grid-column-start: span 2;
+ width: 100%;
+ margin-top: 0.8rem;
+ border-collapse: collapse;
+
+ li {
+ display: grid;
+ grid-template-columns: auto 10rem;
+ grid-row-gap: 0.5rem;
+ padding-top: 1.2rem;
+ border-top: 1px solid $item-border-color;
+ font-size: 1.2rem;
+
+ &:not(:last-child) {
+ padding-bottom: 1.2rem;
+ }
+ }
+
+ .description {
+ grid-row-start: 1;
+ grid-row-end: 3;
+ }
+
+ .amount {
+ justify-self: end;
+ // font-weight: normal;
+ }
+
+ .actions {
+ justify-self: end;
+ }
+
+ h4 {
+ font-size: 1.2rem;
+ font-weight: normal;
+ line-height: 2rem;
+ }
+
+ p {
+ line-height: 2rem;
+
+ &.description {
+ font-size: 1rem;
+ opacity: 0.7;
+ }
+ }
+}
diff --git a/app/styles/components/_reimbursement-list.scss b/app/styles/components/_reimbursement-list.scss
index c5f6458..2423ffd 100644
--- a/app/styles/components/_reimbursement-list.scss
+++ b/app/styles/components/_reimbursement-list.scss
@@ -1,7 +1,7 @@
ul.reimbursement-list {
width: 100%;
- li {
+ > li {
display: grid;
grid-template-columns: auto 10rem;
grid-row-gap: 0.5rem;
@@ -17,36 +17,13 @@ ul.reimbursement-list {
}
.amount {
- font-weight: 500;
+ font-size: 1.5rem;
}
.symbol {
- font-size: 0.8rem;
+ font-size: 1rem;
padding-left: 0.2rem;
}
}
-
- table {
- grid-column-start: span 2;
- width: 100%;
- margin-top: 0.8rem;
- border-collapse: collapse;
-
- tr {
- border-top: 1px solid $item-border-color;
- }
-
- td {
- padding: 0.8rem 0;
-
- &.amount {
- text-align: right;
- }
- }
-
- @include media-max(small) {
- td.date { display: none; }
- }
- }
}
}
diff --git a/tests/integration/components/add-expense-item/component-test.js b/tests/integration/components/add-expense-item/component-test.js
new file mode 100644
index 0000000..5508b95
--- /dev/null
+++ b/tests/integration/components/add-expense-item/component-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | add-expense-item', 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) { ... });
+
+ await render(hbs``);
+
+ assert.equal(this.element.textContent.trim(), '');
+
+ // Template block usage:
+ await render(hbs`
+
+ template block text
+
+ `);
+
+ assert.equal(this.element.textContent.trim(), 'template block text');
+ });
+});