master: change repo
This commit is contained in:
550
src/views/accounts/AccountItem.vue
Normal file
550
src/views/accounts/AccountItem.vue
Normal file
@@ -0,0 +1,550 @@
|
||||
<template>
|
||||
<section class="hero is-fullheight account-item" v-if="account">
|
||||
<account-encrypted v-if="isEncrypted" />
|
||||
<div class="account-item-container">
|
||||
<h2 class="title is-2">
|
||||
{{ account.name }}
|
||||
<router-link v-if="isAdmin" class="tag" :to="{ name: 'account-setting', params: { id } }">
|
||||
<awe-icon icon="cog" />
|
||||
</router-link>
|
||||
</h2>
|
||||
<div class="columns is-centered" v-if="showShare">
|
||||
<div class="column is-one-third">
|
||||
<account-share :account="account" />
|
||||
</div>
|
||||
</div>
|
||||
<article class="message is-warning" v-if="transactionWithoutExchange.length">
|
||||
<div class="message-header">
|
||||
<p>Attention</p>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
Certaines dépenses ne sont pas comptées car en attente
|
||||
de récupérer les taux pratiqués à leur date respectives.
|
||||
<ul>
|
||||
<li v-for="(transaction, k) in transactionWithoutExchange" :key="k">
|
||||
-
|
||||
<router-link
|
||||
:to="{ name: 'transaction', params: { id: transaction._id } }"
|
||||
>{{ transaction.name }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<online-view @online="retrieveExchange">
|
||||
<button
|
||||
@click="retrieveExchange"
|
||||
class="button is-success is-large is-rounded"
|
||||
:class="{ 'is-loading': retrievingExchange }"
|
||||
>Récupérer les devises</button>
|
||||
</online-view>
|
||||
</div>
|
||||
</article>
|
||||
<div class="tabs is-toggle is-fullwidth" v-if="transactions.length">
|
||||
<ul>
|
||||
<li :class="{ 'is-active': activeTab === 'transaction' }">
|
||||
<a href="#" @click.prevent="toggleTab('transaction')">
|
||||
<awe-icon icon="dollar-sign" />
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'is-active': activeTab === 'tag' }" v-if="tagCount > 1">
|
||||
<a href="#" @click.prevent="toggleTab('tag')">
|
||||
<awe-icon icon="tag" />
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'is-active': activeTab === 'balance' }" v-if="isMultiUser">
|
||||
<a href="#" @click.prevent="toggleTab('balance')">
|
||||
<awe-icon icon="balance-scale" />
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'is-active': activeTab === 'refund' }" v-if="isMultiUser">
|
||||
<a href="#" @click.prevent="toggleTab('refund')">
|
||||
<awe-icon icon="exchange-alt" />
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'is-active': activeTab === 'total' }">
|
||||
<a href="#" @click.prevent="toggleTab('total')">
|
||||
<awe-icon icon="equals" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<transition-group :name="transitionName" tag="div" mode="out-in" v-if="transactions.length">
|
||||
<div
|
||||
key="transaction"
|
||||
class="transaction-panel tab-content columns is-centered"
|
||||
v-if="activeTab === 'transaction'"
|
||||
>
|
||||
<div class="column">
|
||||
<account-transaction-list :account="account" :transactions="transactions" />
|
||||
</div>
|
||||
</div>
|
||||
<div key="tag" class="tag-panel tab-content columns is-centered" v-if="activeTab === 'tag'">
|
||||
<div class="column">
|
||||
<tag-list :transactions="transactionWithoutRefund" :account="account" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
key="balance"
|
||||
class="balance-panel tab-content columns is-centered"
|
||||
v-if="activeTab === 'balance'"
|
||||
>
|
||||
<div class="column">
|
||||
<chart-balance :stats="userStats" :currency="account.mainCurrency" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
key="refund"
|
||||
class="refund-panel tab-content columns is-centered"
|
||||
v-if="activeTab === 'refund'"
|
||||
>
|
||||
<div class="column">
|
||||
<div v-if="userRefunds.length" class="columns is-centered is-multiline">
|
||||
<div
|
||||
class="column is-one-fifth"
|
||||
v-for="(refund, k) in userRefunds"
|
||||
:key="`${refund.from.alias}-${refund.to.alias}`"
|
||||
>
|
||||
<refund-transaction :refund="userRefunds[k]" :account="account" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="equilibrium">Le compte est à l'équilibre !</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
key="total"
|
||||
class="total-panel tab-content columns is-center"
|
||||
v-if="activeTab === 'total'"
|
||||
>
|
||||
<div class="column table-container">
|
||||
<p>{{ $tc('account.total', transactions.length, { count: transactions.length }) }}</p>
|
||||
<table class="table is-striped is-bordered">
|
||||
<tbody v-if="isMultiUser">
|
||||
<tr v-for="(user, k) in totalUserCost" :key="k">
|
||||
<th scope="row" v-if="user.alias === userAlias">
|
||||
<em>mes dépenses</em>
|
||||
</th>
|
||||
<th scope="row" v-else>{{ user.alias }}</th>
|
||||
<td class="numeric">{{ user.total | money(account.mainCurrency) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="is-selected">
|
||||
<th scope="row" class="total">total</th>
|
||||
<th class="numeric">{{ totalCost | money(account.mainCurrency) }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
<fab-button
|
||||
:to="{ name: 'transaction-new', params: { id: account._id } }"
|
||||
:button-style="backgroundColor"
|
||||
:margin="true"
|
||||
>
|
||||
<awe-icon icon="money-bill-wave" />
|
||||
<span slot="fulltext">dépense</span>
|
||||
</fab-button>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Watch } from 'vue-property-decorator'
|
||||
import { Action, Getter } from 'vuex-class'
|
||||
import BaseAccount from '@/base-components/BaseAccount'
|
||||
import bus, { SYNC } from '@/utils/bus-event'
|
||||
import notif from '@/utils/notif'
|
||||
import TransactionType from '@/enums/TransactionType'
|
||||
import accountService from '@/services/AccountService'
|
||||
import chartService from '@/services/ChartService'
|
||||
import balanceService from '@/services/BalanceService'
|
||||
import transactionService from '@/services/TransactionService'
|
||||
import exchangeService from '@/services/ExchangeService'
|
||||
import IAccount from '@/models/IAccount'
|
||||
import IBalance from '@/models/IBalance'
|
||||
import ICurrency from '@/models/ICurrency'
|
||||
import IRefund from '@/models/IRefund'
|
||||
import IStat from '@/models/IStat'
|
||||
import ITransaction from '@/models/ITransaction'
|
||||
import IUser from '@/models/IUser'
|
||||
import { primary, main, findDarkValue } from '@/utils'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
'account-share': () => import('@/components/AccountShare.vue'),
|
||||
'account-encrypted': () => import('@/components/AccountEncrypted.vue'),
|
||||
'account-transaction-list': () =>
|
||||
import('@/components/AccountTransactionList.vue'),
|
||||
'tag-list': () => import('@/components/TagList.vue'),
|
||||
'chart-balance': () => import('@/components/ChartBalance.vue'),
|
||||
'online-view': () => import('@/components/OnlineView.vue'),
|
||||
'refund-transaction': () => import('@/components/RefundTransaction.vue'),
|
||||
'fab-button': () => import('@/components/FabButton.vue')
|
||||
}
|
||||
})
|
||||
export default class AccountItem extends BaseAccount {
|
||||
@Action
|
||||
public setTitle!: any
|
||||
public retrieveTransactions: boolean = false
|
||||
public transactions: ITransaction[] = []
|
||||
public activeTab: string = 'transaction'
|
||||
public retrievingExchange: boolean = false
|
||||
public transitionName: string = ''
|
||||
|
||||
public async getData(docIds?: string[]): Promise<void> {
|
||||
if (docIds && !docIds.find((i: string) => i === this.id)) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
if (this.id) {
|
||||
this.account = await accountService.get(this.id)
|
||||
if (!this.account) {
|
||||
notif.alert(`le compte ${this.id} n'existe pas`)
|
||||
this.$router.push({ name: 'home' })
|
||||
return
|
||||
}
|
||||
this.transactions = await transactionService.getAllByAccountId(this.id)
|
||||
this.retrieveTransactions = true
|
||||
if (document && document.documentElement) {
|
||||
if (this.account.color) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary-color',
|
||||
this.account.color
|
||||
)
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary-font-color',
|
||||
findDarkValue(this.account.color) || main
|
||||
)
|
||||
} else {
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary-color',
|
||||
primary
|
||||
)
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary-font-color',
|
||||
main
|
||||
)
|
||||
}
|
||||
}
|
||||
this.setTitle(this.account.name)
|
||||
}
|
||||
} catch (error) {
|
||||
// tslint:disable-next-line
|
||||
console.error({ error })
|
||||
this.$router.push({ name: 'home' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
public toggleTab(tab: string): void {
|
||||
this.activeTab = tab
|
||||
}
|
||||
|
||||
public async retrieveExchange(): Promise<void> {
|
||||
if (!this.transactionWithoutExchange.length) {
|
||||
return
|
||||
}
|
||||
this.retrievingExchange = true
|
||||
try {
|
||||
const transactions: ITransaction[] = [...this.transactionWithoutExchange]
|
||||
for (const transaction of transactions) {
|
||||
transaction.exchange = await exchangeService.get(
|
||||
transaction.mainCurrency.code,
|
||||
transaction.date
|
||||
)
|
||||
}
|
||||
await transactionService.multipleSave(transactions)
|
||||
notif.confirm('Dépenses mises à jour.')
|
||||
} catch (error) {
|
||||
// tslint:disable-next-line
|
||||
console.warn({ error })
|
||||
}
|
||||
this.retrievingExchange = false
|
||||
}
|
||||
|
||||
public totalCostByUserAlias(alias: string): number {
|
||||
if (!this.account) {
|
||||
return 0
|
||||
}
|
||||
const transactions: ITransaction[] = this.transactionWithExchange
|
||||
return transactionService.getTotalCost(
|
||||
transactions,
|
||||
this.account.mainCurrency.code,
|
||||
alias
|
||||
)
|
||||
}
|
||||
|
||||
public get totalUserCost(): Array<{ alias: string; total: number }> {
|
||||
if (!this.account) {
|
||||
return []
|
||||
}
|
||||
return this.account.users
|
||||
.map((user: IUser) => ({
|
||||
alias: user.alias || '',
|
||||
total: this.totalCostByUserAlias(user.alias || '')
|
||||
}))
|
||||
.filter((user) => user.total > 0)
|
||||
}
|
||||
|
||||
public get showShare(): boolean {
|
||||
return !!(
|
||||
this.account &&
|
||||
this.account.users.length > 1 &&
|
||||
this.retrieveTransactions &&
|
||||
!this.transactions.length
|
||||
)
|
||||
}
|
||||
|
||||
public get transactionWithoutRefund(): ITransaction[] {
|
||||
if (!this.transactions) {
|
||||
return []
|
||||
}
|
||||
return this.transactions.filter(
|
||||
(transaction: ITransaction) =>
|
||||
transaction.transactionType === TransactionType.normal
|
||||
)
|
||||
}
|
||||
|
||||
public get tagCount(): number {
|
||||
const tags = new Set(
|
||||
this.transactionWithoutRefund.map(
|
||||
(transaction: ITransaction) => transaction.tag
|
||||
)
|
||||
)
|
||||
return tags.size
|
||||
}
|
||||
|
||||
public get transactionWithExchange(): ITransaction[] {
|
||||
if (!this.transactions) {
|
||||
return []
|
||||
}
|
||||
return this.transactions.filter(
|
||||
(transaction: ITransaction) => !!transaction.exchange
|
||||
)
|
||||
}
|
||||
|
||||
public get transactionWithoutExchange(): ITransaction[] {
|
||||
if (!this.transactions) {
|
||||
return []
|
||||
}
|
||||
return this.transactions.filter(
|
||||
(transaction: ITransaction) => !transaction.exchange
|
||||
)
|
||||
}
|
||||
|
||||
public get userStats(): IStat[] {
|
||||
const totalCost: number = this.totalCost
|
||||
if (!this.account || totalCost === 0) {
|
||||
return []
|
||||
}
|
||||
const account: IAccount = this.account
|
||||
const transactions: ITransaction[] = this.transactionWithExchange
|
||||
const mainCurrency: ICurrency = account.mainCurrency
|
||||
return this.account.users.map((user: IUser) => {
|
||||
const userCost: number = transactionService.getTotalCost(
|
||||
transactions,
|
||||
account.mainCurrency.code,
|
||||
user.alias
|
||||
)
|
||||
const percent: number = Math.round((userCost / totalCost) * 100)
|
||||
return {
|
||||
label: user.alias || '',
|
||||
value: userCost,
|
||||
percent,
|
||||
balance: balanceService.getBalanceByUser(
|
||||
user,
|
||||
mainCurrency,
|
||||
transactions
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public get myTotalCost(): number {
|
||||
if (!this.account) {
|
||||
return 0
|
||||
}
|
||||
const transactions: ITransaction[] = this.transactionWithExchange
|
||||
return transactionService.getTotalCost(
|
||||
transactions,
|
||||
this.account.mainCurrency.code,
|
||||
this.userAlias
|
||||
)
|
||||
}
|
||||
|
||||
public get totalCost(): number {
|
||||
if (!this.account) {
|
||||
return 0
|
||||
}
|
||||
const transactions: ITransaction[] = this.transactionWithExchange
|
||||
return transactionService.getTotalCost(
|
||||
transactions,
|
||||
this.account.mainCurrency.code
|
||||
)
|
||||
}
|
||||
|
||||
public get userBalance(): IBalance[] {
|
||||
if (!this.account || !this.account.users) {
|
||||
return []
|
||||
}
|
||||
const mainCurrency: ICurrency = this.account.mainCurrency
|
||||
const transactions: ITransaction[] = this.transactionWithExchange
|
||||
return this.account.users.map((user: IUser) =>
|
||||
balanceService.getBalanceByUser(user, mainCurrency, transactions)
|
||||
)
|
||||
}
|
||||
|
||||
public get userRefunds(): IRefund[] {
|
||||
if (!this.account) {
|
||||
return []
|
||||
}
|
||||
return balanceService.getRefund(this.userBalance)
|
||||
}
|
||||
|
||||
public get isAdmin(): boolean {
|
||||
if (!this.account || !this.account.admin || !this.user) {
|
||||
return true
|
||||
}
|
||||
return this.account.admin.slugEmail === this.user.slugEmail
|
||||
}
|
||||
|
||||
public get userAlias(): string {
|
||||
if (!this.account || !this.user) {
|
||||
return ''
|
||||
}
|
||||
const mainUser = this.user
|
||||
const user = this.account.users.find(
|
||||
(u: IUser) => u.slugEmail === mainUser.slugEmail
|
||||
)
|
||||
return (user && user.alias) || ''
|
||||
}
|
||||
|
||||
public get isEncrypted(): boolean {
|
||||
return !!(this.account && this.account.name.length > 30)
|
||||
}
|
||||
|
||||
public get backgroundColor(): any | null {
|
||||
if (!this.account || !this.account.color) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
backgroundColor: this.account.color,
|
||||
color: findDarkValue(this.account.color) || 'black'
|
||||
}
|
||||
}
|
||||
|
||||
public get isMultiUser(): boolean {
|
||||
return !!this.account && this.account.users.length > 1
|
||||
}
|
||||
|
||||
@Watch('activeTab')
|
||||
public onActiveTabChange(newTab: string, oldTab: string): void {
|
||||
const tabs: string[] = ['transaction', 'tag', 'balance', 'refund', 'total']
|
||||
this.transitionName =
|
||||
tabs.indexOf(newTab) > tabs.indexOf(oldTab) ? 'slide-left' : 'slide-right'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../styles/variables';
|
||||
.account-item {
|
||||
font-size: 14pt;
|
||||
|
||||
h2.title,
|
||||
a {
|
||||
color: var(--primary-font-color);
|
||||
}
|
||||
|
||||
table tr.is-selected {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--primary-font-color);
|
||||
}
|
||||
|
||||
.tabs.is-toggle {
|
||||
max-width: 400pt;
|
||||
margin: auto;
|
||||
li.is-active {
|
||||
a {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-font-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs.is-toggle {
|
||||
a {
|
||||
height: 35pt;
|
||||
width: 35pt;
|
||||
border-radius: 50%;
|
||||
margin: auto;
|
||||
border-width: 2px;
|
||||
font-size: 14pt;
|
||||
}
|
||||
li:first-child,
|
||||
li:last-child {
|
||||
a {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.hero {
|
||||
&.is-fullheight {
|
||||
min-height: calc(100vh - 52px);
|
||||
}
|
||||
}
|
||||
.equilibrium {
|
||||
font-size: 25pt;
|
||||
color: var(--primary-font-color);
|
||||
}
|
||||
table.table {
|
||||
margin: auto;
|
||||
}
|
||||
th,
|
||||
.pay-by {
|
||||
text-align: center;
|
||||
}
|
||||
.numeric {
|
||||
text-align: right;
|
||||
}
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.total-panel {
|
||||
font-size: 16pt;
|
||||
}
|
||||
}
|
||||
.no-transaction {
|
||||
font-size: 16pt;
|
||||
}
|
||||
.tab-content {
|
||||
position: absolute;
|
||||
width: calc(100% - 30px);
|
||||
margin: auto;
|
||||
transition: opacity 0.5s cubic-bezier(0.55, 0, 0.1, 1),
|
||||
transform 0.5s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
&.columns {
|
||||
&:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.slide-left-enter,
|
||||
.slide-right-leave-active {
|
||||
opacity: 0;
|
||||
transform: translate(15px, 0);
|
||||
}
|
||||
.slide-left-leave-active,
|
||||
.slide-right-enter {
|
||||
opacity: 0;
|
||||
transform: translate(-15px, 0);
|
||||
}
|
||||
.table {
|
||||
th,
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.account-item-container {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user