budget-go/frontend/js/views/DashboardView.vue

238 lines
6.3 KiB
Vue

<template>
<div class="p-3">
<div
v-if="
!isLoading &&
data !== null &&
accounts.length > 0 &&
categories.length > 0
"
>
<div class="d-flex">
<Filters
v-model:account="account"
v-model:date-from="dateFrom"
v-model:date-to="dateTo"
:accounts="accounts"
@update="refresh"
/>
<div class="ms-auto">
<BButton @click="toggleMode()" variant="none">
<i class="fa-solid fa-gear"></i>
</BButton>
</div>
</div>
<Draggable v-model="config" class="row" handle=".handle" @change="refresh()">
<transition-group>
<div
v-for="(item, key) in config"
:key="key"
class="component mb-4 col-12"
:class="componentClasses(item)"
>
<div
v-if="mode === 'edit'"
class="mb-3 d-flex justify-content-beetween"
>
<BButton variant="none" size="sm" class="handle">
<i class="fa-solid fa-up-down-left-right"></i>
</BButton>
<BButtonGroup size="sm" class="d-sm-none d-md-block">
<BButton
v-for="i in [
{size: 3, label: '1'},
{size: 6, label: '2'},
{size: 9, label: '3'},
{size: 12, label: '4'},
]"
:variant="i.size == config[key].size ? 'primary' : 'secondary'"
@click="config[key].size = i.size"
>{{ i.label }}</BButton>
</BButtonGroup>
</div>
<Capital
v-if="item.component === 'Capital'"
:data="data"
:date-from="dateFrom"
:date-to="dateTo"
/>
<SavingAccounts
v-if="item.component === 'SavingAccounts'"
:data="savingAccounts"
/>
<Distribution
v-if="item.component === 'Distribution'"
:data="data"
:categories="categories"
:date-from="dateFrom"
:date-to="dateTo"
/>
<DiffCreditDebit
v-if="item.component === 'DiffCreditDebit'"
:data="data"
:date-from="dateFrom"
:date-to="dateTo"
/>
<MonthThresholds
v-if="item.component === 'MonthThresholds'"
:data="monthThresholdsData()"
:date-from="dateFrom"
:date-to="dateTo"
/>
</div>
</transition-group>
</Draggable>
</div>
<div v-else>Chargement...</div>
</div>
</template>
<script setup>
import {ref, reactive, onMounted, watch} from 'vue'
import {compute as monthThresholds} from '../chart/monthThreshold'
import {getStorage, saveStorage} from '../lib/storage'
import {BButtonGroup, BButton} from 'bootstrap-vue-next'
import Filters from './../components/dashboard/Filters.vue'
import Capital from './../components/dashboard/Capital.vue'
import SavingAccounts from './../components/dashboard/SavingAccounts.vue'
import Distribution from './../components/dashboard/Distribution.vue'
import MonthThresholds from './../components/dashboard/MonthThresholds.vue'
import DiffCreditDebit from './../components/dashboard/DiffCreditDebit.vue'
import { VueDraggableNext as Draggable } from 'vue-draggable-next'
const data = ref(null)
const isLoading = ref(true)
const accounts = ref([])
const categories = ref([])
const savingAccounts = ref([])
const mode = ref('view')
const account = ref(getStorage(`dashboard:account`))
const dateFrom = ref(getStorage(`dashboard:dateFrom`))
const dateTo = ref(getStorage(`dashboard:dateTo`))
const config = reactive(
getStorage(`dashboard:config`, [
{component: 'Capital', size: 8},
{component: 'SavingAccounts', size: 4},
{component: 'Distribution', size: 12},
{component: 'DiffCreditDebit', size: 12},
{component: 'MonthThresholds', size: 12},
]),
)
let winWidth = 0
const _monthThresholdsData = ref(null)
const monthThresholdsData = () => {
if (_monthThresholdsData.value !== null) {
return _monthThresholdsData.value
}
_monthThresholdsData.value = monthThresholds(
data.value,
categories.value,
dateFrom.value,
dateTo.value,
)
return _monthThresholdsData.value
}
const toggleMode = () => {
mode.value = mode.value === 'edit' ? 'view' : 'edit'
}
const componentClasses = (item) => {
const data = [`col-md-${item.size}`]
if (mode.value === 'edit') {
data.push('editable')
}
return data
}
watch(dateFrom, (v) => saveStorage(`dashboard:dateFrom`, v))
watch(dateTo, (v) => saveStorage(`dashboard:dateTo`, v))
watch(account, (v) => saveStorage(`dashboard:account`, v))
watch(config, (v) => {
saveStorage(`dashboard:config`, v)
refresh()
})
window.addEventListener('resize', () => {
if (Math.abs(window.innerWidth - winWidth) < 20) {
return
}
winWidth = window.innerWidth
isLoading.value = true
window.setTimeout(() => {
isLoading.value = false
}, 500)
})
const refresh = () => {
let query = {
order: 'date',
sort: 'asc',
limit: 0,
}
isLoading.value = true
_monthThresholdsData.value = null
if (account.value) {
query['bank_account_id__eq'] = account.value
}
fetch(`/api/transaction?${new URLSearchParams(query)}`)
.then((response) => response.json())
.then((value) => {
data.value = value.rows.filter(
(row) => !row.category || row.category.ignore_transactions === false,
)
isLoading.value = false
})
}
onMounted(() => {
winWidth = window.innerWidth
refresh()
fetch('/api/category?order=label&sort=asc&ignore_transactions__eq=0')
.then((response) => response.json())
.then((data) => {
categories.value = data.rows
categories.value.push({id: -1, label: 'Sans catégorie', color: '#cccccc'})
})
fetch('/api/bank_account')
.then((response) => response.json())
.then((data) => {
accounts.value = data.rows
})
fetch('/api/saving_account')
.then((response) => response.json())
.then((data) => {
savingAccounts.value = data.rows
})
})
</script>
<style scoped>
.config {
width: 200px;
max-width: 100%;
}
.handle {
cursor: grab;
}
</style>