367 lines
8.2 KiB
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>
|