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

367 lines
8.2 KiB
Vue

<template>
<BContainer
fluid
class="p-0"
>
<Header title="Comptes épargnes">
<template #menu>
<BButtonToolbar key-nav>
<BButton
variant="primary"
@click="doAdd"
>Ajouter</BButton
>
</BButtonToolbar>
</template>
</Header>
<Pager
v-model:page="page"
v-model:pages="pages"
v-model:limit="limit"
@update="refresh()"
/>
<div class="crud-list">
<BTableSimple
v-if="data !== null"
caption-top
responsive
>
<BThead>
<BTr>
<BTh
v-for="field in fields"
:key="field.key"
:width="field.width"
class="cursor"
:class="field.thClasses"
valign="top"
@click="doSort(field.key)"
>
<SortButton
:current-order="order"
:current-sort="sort"
:order="field.orderKey ?? field.key"
:label="field.label ?? field.renderLabel(data.rows)"
/>
</BTh>
</BTr>
</BThead>
<BTbody>
<BTr
v-for="(row, key) in data.rows"
:key="key"
>
<BTd
v-for="field in fields"
:key="field.key"
class="cursor"
:class="field.tdClasses"
@click="doEdit(row)"
>
<span v-if="field.key">
<span
v-if="field.render"
v-html="field.render(row)"
></span>
<span
v-else
v-text="row[field.key]"
></span>
</span>
<span
v-else
v-html="field.render(row)"
></span>
</BTd>
</BTr>
</BTbody>
</BTableSimple>
</div>
<BModal
v-if="form !== null"
v-model="formShow"
:title="form?.label"
footer-class="justify-content-between"
@ok="doSave"
>
<BAlert
:model-value="form.error !== null"
variant="danger"
v-text="form.error"
></BAlert>
<BForm @submit="doSave">
<BFormGroup
v-for="(field, key) in form.fields"
:id="'form-label-' + key"
:key="key"
class="mb-2"
:label="field.label"
:label-for="'form-label-' + key"
:description="field.description"
>
<BFormInput
v-if="(field.widget ?? 'text') === 'text'"
:id="'form-input-' + key"
v-model="form.data[field.key]"
:type="field.type"
:required="field.required"
/>
</BFormGroup>
</BForm>
<template #footer>
<div>
<BButton
v-if="form.data.id"
variant="danger"
@click="doDelete"
>Supprimer</BButton
>
</div>
<div>
<BButton
variant="secondary"
class="me-2"
@click="formShow = false"
>Annuler</BButton
>
<BButton
variant="primary"
@click="doSave"
>OK</BButton
>
</div>
</template>
</BModal>
</BContainer>
</template>
<script setup>
import {
BTbody,
BThead,
BTr,
BTd,
BTh,
BContainer,
BTableSimple,
BModal,
BButton,
BForm,
BFormGroup,
BFormInput,
BAlert,
BButtonToolbar,
} from 'bootstrap-vue-next'
import SortButton from './../components/SortButton.vue'
import Header from './../components/crud/Header.vue'
import Pager from './../components/crud/Pager.vue'
import {ref, onMounted, watch} from 'vue'
import {getStorage, saveStorage} from '../lib/storage'
import {renderDate, renderEuro, renderLabelWithSum} from '../lib/renderers'
const endpoint = `/api/saving_account`
const order = ref(getStorage(`${endpoint}:order`))
const sort = ref(getStorage(`${endpoint}:sort`))
const page = ref(getStorage(`${endpoint}:page`))
const limit = ref(getStorage(`${endpoint}:limit`))
const data = ref(null)
const pages = ref(null)
const form = ref(null)
const formShow = ref(false)
watch(order, (v) => saveStorage(`${endpoint}:order`, v))
watch(sort, (v) => saveStorage(`${endpoint}:sort`, v))
watch(page, (v) => saveStorage(`${endpoint}:page`, v))
watch(limit, (v) => saveStorage(`${endpoint}:limit`, v))
const refresh = () => {
fetch(
`${endpoint}?${new URLSearchParams({
order: order.value,
sort: sort.value,
page: page.value,
limit: limit.value,
})}`,
)
.then((response) => {
return response.json()
})
.then(function (value) {
data.value = value
order.value = value.order
sort.value = value.sort
page.value = value.page
pages.value = value.total_pages
limit.value = value.limit
})
}
const doEdit = (item) => {
const data = {...item}
form.value = {
action: `${endpoint}/${item.id}`,
method: 'POST',
data: data,
label: data.label,
error: null,
fields: [
{
label: 'Libellé',
type: 'text',
required: true,
key: 'label',
},
{
label: 'Montant bloqué',
type: 'number',
key: 'blocked_amount',
},
{
label: 'Montant débloqué',
type: 'number',
key: 'released_amount',
},
],
}
formShow.value = true
}
const doAdd = () => {
const data = {label: null, released_amount: 0, blocked_amount: 0}
form.value = {
action: `${endpoint}`,
method: 'POST',
data: data,
label: 'Nouveau',
error: null,
fields: [
{
label: 'Libellé',
type: 'text',
required: true,
key: 'label',
},
{
label: 'Montant bloqué',
type: 'number',
key: 'blocked_amount',
},
{
label: 'Montant débloqué',
type: 'number',
key: 'released_amount',
},
],
}
formShow.value = true
}
const doDelete = () => {
if (!confirm('Je confirme la suppression')) {
return
}
fetch(`${endpoint}/${form.value.data.id}`, {
method: 'DELETE',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
}).then(() => {
formShow.value = false
refresh()
})
}
const doSave = (e) => {
e.preventDefault()
const url = form.value.data.id
? `${endpoint}/${form.value.data.id}`
: endpoint
form.value.data.released_amount = parseFloat(form.value.data.released_amount)
form.value.data.blocked_amount = parseFloat(form.value.data.blocked_amount)
fetch(url, {
method: form.value.method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(form.value.data),
})
.then((response) => {
return response.json()
})
.then((data) => {
if (data.code === 400) {
form.value.error = data.message
} else {
form.value = null
formShow.value = false
refresh()
}
})
.catch((err) => {
form.value.error = `Une erreur s'est produite : ${err}`
})
}
const doSort = (key) => {
let nextSort = 'asc'
if (order.value === key) {
nextSort = sort.value === 'asc' ? 'desc' : 'asc'
}
order.value = key
sort.value = nextSort
page.value = 1
refresh()
}
const fields = [
{
key: 'label',
label: 'Libellé',
},
{
key: 'blocked_amount',
renderLabel: (rows) => renderLabelWithSum('Bloqué', rows, 'blocked_amount'),
render: (item) => renderEuro(item.blocked_amount),
width: '170px',
thClasses: ['text-end'],
tdClasses: ['text-end'],
},
{
key: 'released_amount',
renderLabel: (rows) =>
renderLabelWithSum('Débloqué', rows, 'released_amount'),
render: (item) => renderEuro(item.released_amount),
width: '170px',
thClasses: ['text-end'],
tdClasses: ['text-end'],
},
{
key: 'updated_at',
label: 'Mise à jour',
render: (item) => renderDate(item.updated_at),
width: '170px',
thClasses: ['text-end'],
tdClasses: ['text-end'],
},
]
onMounted(() => {
refresh()
})
</script>