diff --git a/src/app/Wallet.php b/src/app/Wallet.php --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -24,7 +24,7 @@ public $timestamps = false; protected $attributes = [ - 'balance' => 0.00, + 'balance' => 0, 'currency' => 'CHF' ]; @@ -37,7 +37,7 @@ ]; protected $casts = [ - 'balance' => 'float', + 'balance' => 'integer', ]; protected $guarded = ['balance']; diff --git a/src/resources/js/app.js b/src/resources/js/app.js --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -220,7 +220,7 @@ } }, price(price) { - return (price/100).toLocaleString('de-CH', { style: 'currency', currency: 'CHF' }) + return (price/100).toLocaleString('de-DE', { style: 'currency', currency: 'CHF' }) } } }) diff --git a/src/resources/js/routes.js b/src/resources/js/routes.js --- a/src/resources/js/routes.js +++ b/src/resources/js/routes.js @@ -15,6 +15,7 @@ import UserListComponent from '../vue/User/List' import UserProfileComponent from '../vue/User/Profile' import UserProfileDeleteComponent from '../vue/User/ProfileDelete' +import WalletComponent from '../vue/Wallet' import store from './store' @@ -86,6 +87,12 @@ meta: { requiresAuth: true } }, { + path: '/wallet', + name: 'wallet', + component: WalletComponent, + meta: { requiresAuth: true } + }, + { name: '404', path: '*', component: Error404Component diff --git a/src/resources/sass/app.scss b/src/resources/sass/app.scss --- a/src/resources/sass/app.scss +++ b/src/resources/sass/app.scss @@ -157,6 +157,12 @@ pointer-events: none; opacity: 0.6; } + + .badge { + position: absolute; + top: .5rem; + right: .5rem; + } } svg { diff --git a/src/resources/vue/Dashboard.vue b/src/resources/vue/Dashboard.vue --- a/src/resources/vue/Dashboard.vue +++ b/src/resources/vue/Dashboard.vue @@ -24,8 +24,9 @@ User accounts - + Wallet + {{ $root.price(balance) }} @@ -36,7 +37,8 @@ data() { return { statusProcess: [], - request: null + request: null, + balance: 0 } }, mounted() { @@ -46,12 +48,14 @@ if (authInfo) { this.parseStatusInfo(authInfo.statusInfo) + this.getBalance(authInfo) } else { this.$root.startLoading() axios.get('/api/auth/info') .then(response => { this.$store.state.authInfo = response.data this.parseStatusInfo(response.data.statusInfo) + this.getBalance(response.data) this.$root.stopLoading() }) .catch(this.$root.errorHandler) @@ -82,6 +86,13 @@ }) }, 10000); } + }, + getBalance(authInfo) { + this.balance = 0; + // TODO: currencies, multi-wallets, accounts + authInfo.wallets.forEach(wallet => { + this.balance += wallet.balance + }) } } } diff --git a/src/resources/vue/Wallet.vue b/src/resources/vue/Wallet.vue new file mode 100644 --- /dev/null +++ b/src/resources/vue/Wallet.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/tests/Browser/Pages/Wallet.php b/src/tests/Browser/Pages/Wallet.php new file mode 100644 --- /dev/null +++ b/src/tests/Browser/Pages/Wallet.php @@ -0,0 +1,44 @@ +assertPathIs($this->url()) + ->waitUntilMissing('@app .app-loader') + ->assertSeeIn('#wallet .card-title', 'Account balance'); + } + + /** + * Get the element shortcuts for the page. + * + * @return array + */ + public function elements(): array + { + return [ + '@app' => '#app', + ]; + } +} diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php --- a/src/tests/Browser/UsersTest.php +++ b/src/tests/Browser/UsersTest.php @@ -242,7 +242,7 @@ $browser->assertElementsCount('tbody tr', 4) // groupware SKU ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Groupware Features') - ->assertSeeIn('tbody tr:nth-child(1) td.price', 'CHF 5.55/month') + ->assertSeeIn('tbody tr:nth-child(1) td.price', '5,55 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertEnabled('tbody tr:nth-child(1) td.selection input') ->assertTip( @@ -251,7 +251,7 @@ ) // Mailbox SKU ->assertSeeIn('tbody tr:nth-child(2) td.name', 'User Mailbox') - ->assertSeeIn('tbody tr:nth-child(2) td.price', 'CHF 4.44/month') + ->assertSeeIn('tbody tr:nth-child(2) td.price', '4,44 CHF/month') ->assertChecked('tbody tr:nth-child(2) td.selection input') ->assertDisabled('tbody tr:nth-child(2) td.selection input') ->assertTip( @@ -260,7 +260,7 @@ ) // Storage SKU ->assertSeeIn('tbody tr:nth-child(3) td.name', 'Storage Quota') - ->assertSeeIn('tr:nth-child(3) td.price', 'CHF 0.00/month') + ->assertSeeIn('tr:nth-child(3) td.price', '0,00 CHF/month') ->assertChecked('tbody tr:nth-child(3) td.selection input') ->assertDisabled('tbody tr:nth-child(3) td.selection input') ->assertTip( @@ -270,10 +270,10 @@ ->with(new QuotaInput('tbody tr:nth-child(3) .range-input'), function ($browser) { $browser->assertQuotaValue(2)->setQuotaValue(3); }) - ->assertSeeIn('tr:nth-child(3) td.price', 'CHF 0.25/month') + ->assertSeeIn('tr:nth-child(3) td.price', '0,25 CHF/month') // Test SKU ->assertSeeIn('tbody tr:nth-child(4) td.name', 'Test SKU') - ->assertSeeIn('tbody tr:nth-child(4) td.price', 'CHF 6.66/month') + ->assertSeeIn('tbody tr:nth-child(4) td.price', '6,66 CHF/month') ->assertNotChecked('tbody tr:nth-child(4) td.selection input') ->assertEnabled('tbody tr:nth-child(4) td.selection input') ->assertTip( diff --git a/src/tests/Browser/WalletTest.php b/src/tests/Browser/WalletTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Browser/WalletTest.php @@ -0,0 +1,76 @@ +getTestUser('john@kolab.org'); + Wallet::where('user_id', $john->id)->update(['balance' => -1234]); + } + + /** + * {@inheritDoc} + */ + public function tearDown(): void + { + $john = $this->getTestUser('john@kolab.org'); + Wallet::where('user_id', $john->id)->update(['balance' => 0]); + + parent::tearDown(); + } + + /** + * Test wallet page (unauthenticated) + */ + public function testWalletUnauth(): void + { + // Test that the page requires authentication + $this->browse(function (Browser $browser) { + $browser->visit('/wallet')->on(new Home()); + }); + } + + /** + * Test wallet "box" on Dashboard + */ + public function testDashboard(): void + { + // Test that the page requires authentication + $this->browse(function (Browser $browser) { + $browser->visit(new Home()) + ->submitLogon('john@kolab.org', 'simple123', true) + ->on(new Dashboard()) + ->assertSeeIn('@links .link-wallet .name', 'Wallet') + ->assertSeeIn('@links .link-wallet .badge', '-12,34 CHF'); + }); + } + + /** + * Test wallet page + * + * @depends testDashboard + */ + public function testWallet(): void + { + $this->browse(function (Browser $browser) { + $browser->click('@links .link-wallet') + ->on(new WalletPage()) + ->assertSeeIn('#wallet .card-title', 'Account balance') + ->assertSeeIn('#wallet .card-text', 'Current account balance is -12,34 CHF'); + }); + } +}