improve form errors

This commit is contained in:
Simon Vieille 2025-04-27 14:07:11 +02:00
commit 34211970ae
Signed by: deblan
GPG key ID: 579388D585F70417
14 changed files with 115 additions and 37 deletions

View file

@ -11,23 +11,41 @@ import (
"gitnet.fr/deblan/budget/database/manager"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/go-playground/locales/fr"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
fr_translations "github.com/go-playground/validator/v10/translations/fr"
)
type UpdateCallback func(*gorm.DB, interface{}, interface{}) (interface{}, error)
type CreateCallback func(*gorm.DB, interface{}) (interface{}, error)
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Code int `json:"code"`
Message string `json:"message"`
Errors validator.ValidationErrorsTranslations `json:"errors"`
}
type Controller struct {
Config Configuration
Config Configuration
Validator *validator.Validate
Trans ut.Translator
}
func New() *Controller {
c := Controller{}
fr := fr.New()
uni := ut.New(fr, fr)
trans, _ := uni.GetTranslator("fr")
validate := validator.New()
fr_translations.RegisterDefaultTranslations(validate, trans)
c.Validator = validate
c.Trans = trans
return &c
}
@ -155,10 +173,13 @@ func (ctrl *Controller) Create(c echo.Context, body interface{}, createCallback
})
}
if err := c.Validate(body); err != nil {
if err := ctrl.Validator.Struct(body); err != nil {
errs := err.(validator.ValidationErrors)
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: errs.Translate(ctrl.Trans),
})
}
@ -211,10 +232,13 @@ func (ctrl *Controller) Update(c echo.Context, body interface{}, updateCallback
})
}
if err := c.Validate(body); err != nil {
if err := ctrl.Validator.Struct(body); err != nil {
errs := err.(validator.ValidationErrors)
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: errs.Translate(ctrl.Trans),
})
}

View file

@ -5,6 +5,7 @@ import (
"io"
"strconv"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"gitnet.fr/deblan/budget/backend/controller/crud"
"gitnet.fr/deblan/budget/backend/message"
@ -86,7 +87,10 @@ func (ctrl *Controller) Create(c echo.Context) error {
if err != nil {
return c.JSON(400, crud.Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: validator.ValidationErrorsTranslations{
"BankAccount": message.BlankBankAccount,
},
})
}
@ -96,7 +100,10 @@ func (ctrl *Controller) Create(c echo.Context) error {
if count == 0 {
return c.JSON(400, crud.Error{
Code: 400,
Message: "Invalid bank account",
Message: message.InvalidForm,
Errors: validator.ValidationErrorsTranslations{
"BankAccount": message.UnknownBankAccount,
},
})
}
@ -104,7 +111,10 @@ func (ctrl *Controller) Create(c echo.Context) error {
if err != nil {
return c.JSON(400, crud.Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: validator.ValidationErrorsTranslations{
"File": message.BlankFile,
},
})
}
@ -112,7 +122,10 @@ func (ctrl *Controller) Create(c echo.Context) error {
if err != nil {
return c.JSON(400, crud.Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: validator.ValidationErrorsTranslations{
"File": message.UnproccessableFile,
},
})
}
@ -126,7 +139,10 @@ func (ctrl *Controller) Create(c echo.Context) error {
if err != nil {
return c.JSON(400, crud.Error{
Code: 400,
Message: err.Error(),
Message: message.InvalidForm,
Errors: validator.ValidationErrorsTranslations{
"File": message.InvalidFile,
},
})
}

View file

@ -1,8 +1,14 @@
package message
var (
LoginRequired = "Login required"
FileNotFound = "File not found"
NotFound = "Not found"
BadRequest = "Bad request"
LoginRequired = "Login required"
FileNotFound = "File not found"
NotFound = "Not found"
BadRequest = "Bad request"
InvalidForm = "Le formulaire n'est pas valide"
BlankBankAccount = "Le compte bancaire n'est pas renseigné"
UnknownBankAccount = "Le compte bancaire n'existe pas"
BlankFile = "Le fichier n'est pas renseigné"
UnproccessableFile = "Le fichier ne peut être traité"
InvalidFile = "Le fichier n'est pas valide"
)

View file

@ -21,4 +21,15 @@ const request = async (endpoint, options) => {
return fetch(endpoint, createRequestOptions(options)).then((response) => response.json())
}
export { createRequestOptions, request, requestCallback }
const requestErrorBuilder = (response) => {
let value = response.message
const errors = Object.values(response.errors ?? {})
if (errors.length) {
value += `<ul class="px-3 py-0 my-0 pt-1">${errors.map((e) => `<li class="m-0 p-0">${e}</li>`).join('')}</ul>`
}
return value
}
export { createRequestOptions, request, requestCallback, requestErrorBuilder }

View file

@ -56,10 +56,14 @@ const remove = async (id, callback, callbackError) => {
const fixData = (data) => {
if (data.blocked_amount) {
data.blocked_amount = parseFloat(data.blocked_amount)
} else {
data.blocked_amount = 0.0
}
if (data.released_amount) {
data.released_amount = parseFloat(data.released_amount)
} else {
data.released_amount = 0.0
}
return data

View file

@ -32,7 +32,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -51,6 +51,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { create, createForm } from '../../models/bank_account'
import { requestErrorBuilder } from '../../lib/request'
const router = useRouter()
const form = ref(null)
@ -60,7 +61,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}

View file

@ -45,7 +45,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -63,6 +63,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { update, remove, getOne, createForm } from '../../models/bank_account'
import { requestErrorBuilder } from '../../lib/request'
const route = useRoute()
const router = useRouter()
@ -95,7 +96,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}

View file

@ -32,7 +32,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -51,6 +51,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { create, createForm } from '../../models/category'
import { requestErrorBuilder } from '../../lib/request'
const router = useRouter()
const form = ref(null)
@ -60,7 +61,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
router.replace({ name: 'category_edit', params: { id: data.id } })
}

View file

@ -61,7 +61,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol
@ -101,6 +101,7 @@ import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { update, remove, getOne, createForm } from '../../models/category'
import { updateCategories, getOne as getTransactionOne } from '../../models/transaction'
import { requestErrorBuilder } from '../../lib/request'
const route = useRoute()
const router = useRouter()
@ -197,7 +198,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
form.value = null
addTransaction = false

View file

@ -32,7 +32,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -51,6 +51,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { create, createForm } from '../../models/saving_account'
import { requestErrorBuilder } from '../../lib/request'
const router = useRouter()
const form = ref(null)
@ -60,7 +61,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}

View file

@ -45,7 +45,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -63,6 +63,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { update, remove, getOne, createForm } from '../../models/saving_account'
import { requestErrorBuilder } from '../../lib/request'
const route = useRoute()
const router = useRouter()
@ -96,7 +97,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}

View file

@ -54,8 +54,9 @@
<BAlert
:model-value="form.error !== null"
variant="danger"
>{{ form.error }}</BAlert
>
>
<div v-html="form.error"></div>
</BAlert>
<BForm @submit="doSave">
<FormView :form="form" />
</BForm>
@ -107,6 +108,7 @@ import { useRouter, RouterView } from 'vue-router'
import { getList, getListFields, getListFilters, createForm, importFile } from '../../models/transaction'
import { getList as getAccountList } from '../../models/bank_account'
import { getList as getCategoryList } from '../../models/category'
import { requestErrorBuilder } from '../../lib/request'
const endpoint = `/api/transaction`
const order = ref(getStorage(`${endpoint}:order`))
@ -183,7 +185,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
form.value = null
formShow.value = false

View file

@ -26,6 +26,14 @@
class="p-3"
@submit="submit"
>
<BAlert
v-if="form?.error"
dismissible
variant="warning"
:model-value="true"
>
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
<FormView :form="form" />
@ -36,13 +44,13 @@
</template>
<script setup>
import { BContainer, BButton, BForm, BRow, BCol } from 'bootstrap-vue-next'
import { BContainer, BButton, BForm, BRow, BCol, BAlert } from 'bootstrap-vue-next'
import CrudHeader from '../../components/crud/CrudHeader.vue'
import FormView from '../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { create, createForm } from '../../models/user'
import { requestErrorBuilder } from '../../lib/request'
const router = useRouter()
const parentRouteName = 'users'
@ -53,7 +61,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}

View file

@ -45,7 +45,7 @@
variant="warning"
:model-value="true"
>
{{ form.error }}
<div v-html="form.error"></div>
</BAlert>
<BRow>
<BCol>
@ -63,6 +63,7 @@ import FormView from './../../components/crud/FormView.vue'
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { update, remove, getOne, createForm } from '../../models/user'
import { requestErrorBuilder } from '../../lib/request'
const route = useRoute()
const router = useRouter()
@ -96,7 +97,7 @@ const doSave = () => {
form.value.data,
(data) => {
if (data.code === 400) {
form.value.error = data.message
form.value.error = requestErrorBuilder(data)
} else {
doBack()
}