add cruds
This commit is contained in:
parent
c24e4ae5a8
commit
a1dfbad0ad
13 changed files with 565 additions and 186 deletions
|
|
@ -36,6 +36,7 @@ func main() {
|
|||
conf := config.Get()
|
||||
conf.Load(*ini)
|
||||
manager.Get().Db.AutoMigrate(&model.User{})
|
||||
manager.Get().Db.AutoMigrate(&model.BankAccount{})
|
||||
|
||||
e := echo.New()
|
||||
e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))
|
||||
|
|
|
|||
20
database/model/bank_account.go
Normal file
20
database/model/bank_account.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type BankAccount struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Label string `gorm:"unique;not null" json:"label"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func NewBankAccount(label string) *BankAccount {
|
||||
account := BankAccount{
|
||||
Label: label,
|
||||
}
|
||||
|
||||
return &account
|
||||
}
|
||||
|
|
@ -11,9 +11,9 @@ import (
|
|||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Username string `gorm:"unique" json:"username"`
|
||||
Password string `json:"-"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Username string `gorm:"unique;not null" json:"username"`
|
||||
Password string `gorm:"not null" json:"-"`
|
||||
DisplayName string `gorm:"not null" json:"display_name"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import { BNavItem } from 'bootstrap-vue-next'
|
|||
<RouterLink to="/users" custom v-slot="{ href, route, navigate, isActive, isExactActive }">
|
||||
<BNavItem :href="href" :active="isActive">{{ route.name }}</BNavItem>
|
||||
</RouterLink>
|
||||
<RouterLink to="/bank_accounts" custom v-slot="{ href, route, navigate, isActive, isExactActive }">
|
||||
<BNavItem :href="href" :active="isActive">{{ route.name }}</BNavItem>
|
||||
</RouterLink>
|
||||
</ul>
|
||||
</div>
|
||||
<RouterView id="body" />
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
<span>
|
||||
{{ props.label }}
|
||||
|
||||
<i v-if="isAsc()" class="ms-1 fa-solid fa-sort-up"></i>
|
||||
<i v-if="isDesc()" class="ms-1 fa-solid fa-sort-down"></i>
|
||||
<span v-if="isAsc() || isDesc()" class="text-black-50">
|
||||
<i v-if="isAsc()" class="ms-1 fa-solid fa-sort-up"></i>
|
||||
<i v-if="isDesc()" class="ms-1 fa-solid fa-sort-down"></i>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ const router = createRouter({
|
|||
name: 'Utilisateurs',
|
||||
component: () => import('../views/UsersView.vue')
|
||||
},
|
||||
{
|
||||
path: '/bank_accounts',
|
||||
name: 'Comptes bancaires',
|
||||
component: () => import('../views/BankAccountsView.vue')
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
|
|
|||
226
frontend/js/views/BankAccountsView.vue
Normal file
226
frontend/js/views/BankAccountsView.vue
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<template>
|
||||
<BContainer fluid class="p-0">
|
||||
<div class="d-flex justify-content-between p-3 mb-3">
|
||||
<h3>Comptes bancaires</h3>
|
||||
<BButtonToolbar key-nav>
|
||||
<BButton @click="doAdd">Ajouter</BButton>
|
||||
</BButtonToolbar>
|
||||
</div>
|
||||
<BTableSimple caption-top responsive v-if="data !== null">
|
||||
<BThead>
|
||||
<BTr>
|
||||
<BTh v-for="field in fields" :width="field.width" class="cursor" :class="field.classes" @click="doSort(field.key)">
|
||||
<SortButton :currentOrder="order" :currentSort="sort" :order="field.key" :label="field.label" />
|
||||
</BTh>
|
||||
</Btr>
|
||||
</BThead>
|
||||
<BTbody>
|
||||
<BTr v-for="row in data.rows">
|
||||
<BTd v-for="field in fields" @click="doEdit(row)" class="cursor">
|
||||
{{ row[field.key] }}
|
||||
</BTd>
|
||||
</Btr>
|
||||
</BTbody>
|
||||
</BTableSimple>
|
||||
<BModal v-if="form !== null" v-model="formShow" :title="form?.label" @ok="doSave" footer-class="justify-content-between">
|
||||
<BAlert :model-value="form.error !== null" variant="danger" v-text="form.error"></BAlert>
|
||||
<BForm @submit="doSave">
|
||||
<BFormGroup
|
||||
class="mb-2"
|
||||
v-for="(field, key) in form.fields"
|
||||
:id="'form-label-' + key"
|
||||
: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 v-slot: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 {ref, onMounted, reactive} from 'vue'
|
||||
|
||||
const data = ref(null)
|
||||
const order = ref(null)
|
||||
const sort = ref(null)
|
||||
const page = ref(1)
|
||||
const pages = ref(null)
|
||||
const limit = ref(null)
|
||||
const form = ref(null)
|
||||
const formShow = ref(false)
|
||||
const endpoint = `/api/bank_account`
|
||||
|
||||
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',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
formShow.value = true
|
||||
}
|
||||
|
||||
const doAdd = () => {
|
||||
const data = {label: null}
|
||||
|
||||
form.value = {
|
||||
action: `${endpoint}`,
|
||||
method: 'POST',
|
||||
data: data,
|
||||
label: 'Nouveau',
|
||||
error: null,
|
||||
fields: [
|
||||
{
|
||||
label: 'Libellé',
|
||||
type: 'text',
|
||||
required: true,
|
||||
key: 'label',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
formShow.value = true
|
||||
}
|
||||
|
||||
const doDelete = (e) => {
|
||||
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
|
||||
|
||||
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: 'id',
|
||||
label: 'ID',
|
||||
width: '70px',
|
||||
},
|
||||
{
|
||||
key: 'label',
|
||||
label: 'Libellé',
|
||||
},
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
refresh()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,168 +1,5 @@
|
|||
<template>
|
||||
<BContainer fluid>
|
||||
<BTable
|
||||
v-model:sort-by="sortBy"
|
||||
:sort-internal="true"
|
||||
:items="itemsTyped"
|
||||
:fields="fieldsTyped"
|
||||
:current-page="currentPage"
|
||||
:per-page="perPage"
|
||||
:filter="filter"
|
||||
:responsive="false"
|
||||
:filterable="filterOn"
|
||||
:small="true"
|
||||
:multisort="true"
|
||||
@filtered="onFiltered"
|
||||
>
|
||||
<template #cell(name)="row">
|
||||
{{ (row.value).first }}
|
||||
{{ (row.value).last }}
|
||||
</template>
|
||||
<template #cell(actions)="row">
|
||||
<BButton size="sm" class="mr-1" @click="info(row.item, row.index)"> Info modal </BButton>
|
||||
<BButton size="sm" @click="row.toggleDetails">
|
||||
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
|
||||
</BButton>
|
||||
</template>
|
||||
<template #row-details="row">
|
||||
<BCard>
|
||||
<ul>
|
||||
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value }}</li>
|
||||
<BButton size="sm" @click="row.toggleDetails"> Toggle Details </BButton>
|
||||
</ul>
|
||||
</BCard>
|
||||
</template>
|
||||
</BTable>
|
||||
<BModal
|
||||
:id="infoModal.id"
|
||||
v-model="infoModal.open"
|
||||
:title="infoModal.title"
|
||||
:ok-only="true"
|
||||
@hide="resetInfoModal"
|
||||
>
|
||||
<pre>{{ infoModal.content }}</pre>
|
||||
</BModal>
|
||||
</BContainer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
BButton,
|
||||
BFormSelect,
|
||||
BInputGroup,
|
||||
BFormCheckbox,
|
||||
BFormGroup,
|
||||
BCol,
|
||||
BFormInput,
|
||||
BInputGroupText,
|
||||
BFormCHeckbox,
|
||||
BPagination,
|
||||
BRow,
|
||||
BModal,
|
||||
BContainer,
|
||||
BTable,
|
||||
BTableSortBy
|
||||
} from 'bootstrap-vue-next'
|
||||
import {computed, reactive, ref} from 'vue'
|
||||
|
||||
const itemsTyped = [
|
||||
{isActive: true, age: 40, name: {first: 'Dickerson', last: 'Macdonald'}},
|
||||
{isActive: false, age: 21, name: {first: 'Larsen', last: 'Shaw'}},
|
||||
{
|
||||
isActive: false,
|
||||
age: 9,
|
||||
name: {first: 'Mini', last: 'Navarro'},
|
||||
_rowVariant: 'success',
|
||||
},
|
||||
{isActive: false, age: 89, name: {first: 'Geneva', last: 'Wilson'}},
|
||||
{isActive: true, age: 38, name: {first: 'Jami', last: 'Carney'}},
|
||||
{isActive: false, age: 27, name: {first: 'Essie', last: 'Dunlap'}},
|
||||
{isActive: true, age: 40, name: {first: 'Thor', last: 'Macdonald'}},
|
||||
{
|
||||
isActive: true,
|
||||
age: 87,
|
||||
name: {first: 'Larsen', last: 'Shaw'},
|
||||
_cellVariants: {age: 'danger', isActive: 'warning'},
|
||||
},
|
||||
{isActive: false, age: 26, name: {first: 'Mitzi', last: 'Navarro'}},
|
||||
{isActive: false, age: 22, name: {first: 'Genevieve', last: 'Wilson'}},
|
||||
{isActive: true, age: 38, name: {first: 'John', last: 'Carney'}},
|
||||
{isActive: false, age: 29, name: {first: 'Dick', last: 'Dunlap'}},
|
||||
]
|
||||
|
||||
const fieldsTyped = [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Person full name',
|
||||
sortable: true,
|
||||
sortDirection: 'desc',
|
||||
},
|
||||
{
|
||||
key: 'sortableName',
|
||||
label: 'Person sortable name',
|
||||
sortable: true,
|
||||
sortDirection: 'desc',
|
||||
formatter: (_value, _key, item) =>
|
||||
item ? `${item.name.last}, ${item.name.first}` : 'Something went wrong',
|
||||
sortByFormatted: true,
|
||||
filterByFormatted: true,
|
||||
},
|
||||
{key: 'age', label: 'Person age', sortable: true, class: 'text-center'},
|
||||
{
|
||||
key: 'isActive',
|
||||
label: 'Is Active',
|
||||
formatter: (value) => (value ? 'Yes' : 'No'),
|
||||
sortable: true,
|
||||
sortByFormatted: true,
|
||||
filterByFormatted: true,
|
||||
},
|
||||
{key: 'actions', label: 'Actions'},
|
||||
]
|
||||
|
||||
const pageOptions = [
|
||||
{value: 5, text: '5'},
|
||||
{value: 10, text: '10'},
|
||||
{value: 15, text: '15'},
|
||||
{value: 100, text: 'Show a lot'},
|
||||
]
|
||||
|
||||
const totalRows = ref(itemsTyped.length)
|
||||
const currentPage = ref(1)
|
||||
const perPage = ref(5)
|
||||
const sortBy = ref([])
|
||||
const sortDirection = ref('asc')
|
||||
const filter = ref('')
|
||||
const filterOn = ref([])
|
||||
const infoModal = reactive({
|
||||
open: false,
|
||||
id: 'info-modal',
|
||||
title: '',
|
||||
content: '',
|
||||
})
|
||||
|
||||
// Create an options list from our fields
|
||||
const sortOptions = computed(() =>
|
||||
fieldsTyped.filter((f) => f.sortable).map((f) => ({text: f.label, value: f.key}))
|
||||
)
|
||||
|
||||
function info(item, index) {
|
||||
infoModal.title = `Row index: ${index}`
|
||||
infoModal.content = JSON.stringify(item, null, 2)
|
||||
infoModal.open = true
|
||||
}
|
||||
|
||||
function resetInfoModal() {
|
||||
infoModal.title = ''
|
||||
infoModal.content = ''
|
||||
}
|
||||
|
||||
function onFiltered(filteredItems) {
|
||||
// Trigger pagination to update the number of buttons/pages due to filtering
|
||||
totalRows.value = filteredItems.length
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
function onAddSort() {
|
||||
sortBy.value.push({key: '', order: 'asc'})
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<BContainer fluid class="p-0">
|
||||
<div class="d-flex justify-content-between p-3 mb-3">
|
||||
<h3>Utilisateurs</h3>
|
||||
<BButtonToolbar key-nav>
|
||||
<BButton @click="doAdd">Ajouter</BButton>
|
||||
</BButtonToolbar>
|
||||
</div>
|
||||
<BTableSimple caption-top responsive v-if="data !== null">
|
||||
<BThead>
|
||||
<BTr>
|
||||
|
|
@ -28,13 +34,23 @@
|
|||
:description="field.description"
|
||||
>
|
||||
<BFormInput
|
||||
v-if="(field.widget ?? 'text') === 'text'"
|
||||
:id="'form-input-' + key"
|
||||
v-model="form.data[field.key]"
|
||||
:type="form.type"
|
||||
:required="form.required"
|
||||
:type="field.type"
|
||||
:required="field.required"
|
||||
/>
|
||||
</BFormGroup>
|
||||
</BForm>
|
||||
<template v-slot: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>
|
||||
|
|
@ -46,7 +62,6 @@ import {
|
|||
BTr,
|
||||
BTd,
|
||||
BTh,
|
||||
BRow,
|
||||
BContainer,
|
||||
BTableSimple,
|
||||
BModal,
|
||||
|
|
@ -55,6 +70,7 @@ import {
|
|||
BFormGroup,
|
||||
BFormInput,
|
||||
BAlert,
|
||||
BButtonToolbar,
|
||||
} from 'bootstrap-vue-next'
|
||||
|
||||
import SortButton from './../components/SortButton.vue'
|
||||
|
|
@ -63,15 +79,21 @@ import {ref, onMounted, reactive} from 'vue'
|
|||
const data = ref(null)
|
||||
const order = ref(null)
|
||||
const sort = ref(null)
|
||||
const page = ref(null)
|
||||
const page = ref(1)
|
||||
const pages = ref(null)
|
||||
const limit = ref(null)
|
||||
const form = ref(null)
|
||||
const formShow = ref(false)
|
||||
const endpoint = `/api/user`
|
||||
|
||||
const refresh = (query) => {
|
||||
fetch(`/api/user?${new URLSearchParams(query)}`)
|
||||
.then(function(response) {
|
||||
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) {
|
||||
|
|
@ -85,11 +107,13 @@ const refresh = (query) => {
|
|||
}
|
||||
|
||||
const doEdit = (item) => {
|
||||
const data = {...item}
|
||||
|
||||
form.value = {
|
||||
action: `api/user/?${item.id}`,
|
||||
action: `${endpoint}/${item.id}`,
|
||||
method: 'POST',
|
||||
data: item,
|
||||
label: item.display_name,
|
||||
data: data,
|
||||
label: data.display_name,
|
||||
error: null,
|
||||
fields: [
|
||||
{
|
||||
|
|
@ -116,8 +140,82 @@ const doEdit = (item) => {
|
|||
formShow.value = true
|
||||
}
|
||||
|
||||
const doAdd = () => {
|
||||
const data = {display_name: null, username: null, password: null}
|
||||
|
||||
form.value = {
|
||||
action: `${endpoint}`,
|
||||
method: 'POST',
|
||||
data: data,
|
||||
label: 'Nouveau',
|
||||
error: null,
|
||||
fields: [
|
||||
{
|
||||
label: 'Nom',
|
||||
type: 'text',
|
||||
required: true,
|
||||
key: 'display_name',
|
||||
},
|
||||
{
|
||||
label: 'Nom d\'utilisateur',
|
||||
type: 'text',
|
||||
required: true,
|
||||
key: 'username',
|
||||
},
|
||||
{
|
||||
label: 'Mot de passe',
|
||||
type: 'password',
|
||||
required: false,
|
||||
key: 'password',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
formShow.value = true
|
||||
}
|
||||
|
||||
const doDelete = (e) => {
|
||||
fetch(`${endpoint}/${form.value.data.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
formShow.value = false
|
||||
refresh()
|
||||
})
|
||||
}
|
||||
|
||||
const doSave = (e) => {
|
||||
console.log(form.value)
|
||||
e.preventDefault()
|
||||
|
||||
const url = form.value.data.id ? `${endpoint}/${form.value.data.id}` : endpoint
|
||||
|
||||
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) => {
|
||||
|
|
@ -127,14 +225,11 @@ const doSort = (key) => {
|
|||
nextSort = (sort.value === 'asc' ? 'desc' : 'asc')
|
||||
}
|
||||
|
||||
refresh({
|
||||
order: key,
|
||||
sort: nextSort,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
})
|
||||
order.value = key
|
||||
sort.value = nextSort
|
||||
page.value = 1
|
||||
|
||||
sort.value = key
|
||||
refresh()
|
||||
}
|
||||
|
||||
const fields = [
|
||||
|
|
|
|||
102
web/controller/bank_account/controller.go
Normal file
102
web/controller/bank_account/controller.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package bank_account
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/budget/database/model"
|
||||
"gitnet.fr/deblan/budget/web/controller/crud"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
crud *crud.Controller
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Config() crud.Configuration {
|
||||
return crud.Configuration{
|
||||
Model: model.BankAccount{},
|
||||
Models: []model.BankAccount{},
|
||||
ValidOrders: []string{"id", "display_name", "username"},
|
||||
ValidLimits: []int{20, 50, 100},
|
||||
DefaultLimit: 20,
|
||||
CreateModel: func() interface{} {
|
||||
return new(model.BankAccount)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func New(e *echo.Echo) *Controller {
|
||||
c := Controller{
|
||||
crud: crud.New(),
|
||||
}
|
||||
|
||||
e.GET("/api/bank_account", c.List)
|
||||
e.POST("/api/bank_account", c.Create)
|
||||
e.GET("/api/bank_account/:id", c.Show)
|
||||
e.POST("/api/bank_account/:id", c.Update)
|
||||
e.DELETE("/api/bank_account/:id", c.Delete)
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (ctrl *Controller) List(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).List(c)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Show(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Show(c)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Delete(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Delete(c)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Create(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
type body struct {
|
||||
Label string `json:"label" form:"label" validate:"required"`
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Create(c, new(body), func(db *gorm.DB, v interface{}) (interface{}, error) {
|
||||
value := v.(*body)
|
||||
item := model.NewBankAccount(value.Label)
|
||||
|
||||
db.Model(ctrl.crud.Config.Model).Create(&item)
|
||||
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Update(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
type body struct {
|
||||
Label string `json:"label" form:"label" validate:"required"`
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Update(c, new(body), func(db *gorm.DB, a, b interface{}) (interface{}, error) {
|
||||
item := a.(*model.BankAccount)
|
||||
update := b.(*body)
|
||||
item.Label = update.Label
|
||||
|
||||
db.Model(ctrl.crud.Config.Model).Where("id = ?", item.ID).Save(&item)
|
||||
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
)
|
||||
|
||||
type UpdateCallback func(*gorm.DB, interface{}, interface{}) (interface{}, error)
|
||||
type CreateCallback func(*gorm.DB, interface{}) (interface{}, error)
|
||||
|
||||
type Error struct {
|
||||
Code int `json:"code"`
|
||||
|
|
@ -96,6 +97,59 @@ func (ctrl *Controller) Show(c echo.Context) error {
|
|||
return c.JSON(200, item)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Delete(c echo.Context) error {
|
||||
db := manager.Get().Db
|
||||
value, err := strconv.Atoi(c.Param("id"))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
db.Model(ctrl.Config.Model).Where("id = ?", value).Count(&count)
|
||||
|
||||
if count == 0 {
|
||||
return c.JSON(404, Error{
|
||||
Code: 404,
|
||||
Message: "Not found",
|
||||
})
|
||||
}
|
||||
|
||||
item := ctrl.Config.CreateModel()
|
||||
db.Model(ctrl.Config.Model).Where("id = ?", value).Delete(&item)
|
||||
|
||||
return c.JSON(200, nil)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Create(c echo.Context, body interface{}, createCallback CreateCallback) error {
|
||||
db := manager.Get().Db
|
||||
|
||||
if err := c.Bind(body); err != nil {
|
||||
return c.JSON(400, Error{
|
||||
Code: 400,
|
||||
Message: "Bad request",
|
||||
})
|
||||
}
|
||||
|
||||
if err := c.Validate(body); err != nil {
|
||||
return c.JSON(400, Error{
|
||||
Code: 400,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
result, err := createCallback(db, body)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(400, Error{
|
||||
Code: 400,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(200, result)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Update(c echo.Context, body interface{}, updateCallback UpdateCallback) error {
|
||||
db := manager.Get().Db
|
||||
value, err := strconv.Atoi(c.Param("id"))
|
||||
|
|
|
|||
|
|
@ -30,8 +30,10 @@ func New(e *echo.Echo) *Controller {
|
|||
}
|
||||
|
||||
e.GET("/api/user", c.List)
|
||||
e.POST("/api/user", c.Create)
|
||||
e.GET("/api/user/:id", c.Show)
|
||||
e.POST("/api/user/:id", c.Update)
|
||||
e.DELETE("/api/user/:id", c.Delete)
|
||||
|
||||
return &c
|
||||
}
|
||||
|
|
@ -52,12 +54,42 @@ func (ctrl *Controller) Show(c echo.Context) error {
|
|||
return ctrl.crud.With(ctrl.Config()).Show(c)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Delete(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Delete(c)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Create(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
type body struct {
|
||||
Username string `json:"username" form:"username" validate:"required"`
|
||||
DisplayName string `json:"display_name" form:"display_name" validate:"required"`
|
||||
Password string `json:"password" form:"password" validate:"required"`
|
||||
}
|
||||
|
||||
return ctrl.crud.With(ctrl.Config()).Create(c, new(body), func(db *gorm.DB, v interface{}) (interface{}, error) {
|
||||
value := v.(*body)
|
||||
item := model.NewUser(value.Username, value.Password, value.DisplayName)
|
||||
|
||||
db.Model(ctrl.crud.Config.Model).Save(&item)
|
||||
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Update(c echo.Context) error {
|
||||
if nil == model.LoadSessionUser(c) {
|
||||
return c.Redirect(302, "/login")
|
||||
}
|
||||
|
||||
type body struct {
|
||||
Username string `json:"username" form:"username" validate:"required"`
|
||||
DisplayName string `json:"display_name" form:"display_name" validate:"required"`
|
||||
Password string `json:"password" form:"password"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/budget/web/controller/app"
|
||||
"gitnet.fr/deblan/budget/web/controller/auth"
|
||||
"gitnet.fr/deblan/budget/web/controller/bank_account"
|
||||
"gitnet.fr/deblan/budget/web/controller/user"
|
||||
)
|
||||
|
||||
|
|
@ -11,4 +12,5 @@ func RegisterControllers(e *echo.Echo) {
|
|||
auth.New(e)
|
||||
app.New(e)
|
||||
user.New(e)
|
||||
bank_account.New(e)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue