From 34211970aee533bce1b04bd7617d74c0d2a0a4cb Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 27 Apr 2025 14:07:11 +0200 Subject: [PATCH] improve form errors --- backend/controller/crud/crud.go | 38 +++++++++++++++---- backend/controller/transaction/controller.go | 26 ++++++++++--- backend/message/message.go | 14 +++++-- frontend/js/lib/request.js | 13 ++++++- frontend/js/models/saving_account.js | 4 ++ frontend/js/views/bank_account/CreateView.vue | 5 ++- frontend/js/views/bank_account/EditView.vue | 5 ++- frontend/js/views/category/CreateView.vue | 5 ++- frontend/js/views/category/EditView.vue | 5 ++- .../js/views/saving_account/CreateView.vue | 5 ++- frontend/js/views/saving_account/EditView.vue | 5 ++- frontend/js/views/transaction/ListView.vue | 8 ++-- frontend/js/views/user/CreateView.vue | 14 +++++-- frontend/js/views/user/EditView.vue | 5 ++- 14 files changed, 115 insertions(+), 37 deletions(-) diff --git a/backend/controller/crud/crud.go b/backend/controller/crud/crud.go index c85a9a5..ccbd99f 100644 --- a/backend/controller/crud/crud.go +++ b/backend/controller/crud/crud.go @@ -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), }) } diff --git a/backend/controller/transaction/controller.go b/backend/controller/transaction/controller.go index 93afafd..e1dfac2 100644 --- a/backend/controller/transaction/controller.go +++ b/backend/controller/transaction/controller.go @@ -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, + }, }) } diff --git a/backend/message/message.go b/backend/message/message.go index be386e1..600e379 100644 --- a/backend/message/message.go +++ b/backend/message/message.go @@ -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" ) diff --git a/frontend/js/lib/request.js b/frontend/js/lib/request.js index aa509d5..e524447 100644 --- a/frontend/js/lib/request.js +++ b/frontend/js/lib/request.js @@ -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 += `` + } + + return value +} + +export { createRequestOptions, request, requestCallback, requestErrorBuilder } diff --git a/frontend/js/models/saving_account.js b/frontend/js/models/saving_account.js index 8ff3c9e..e3a2e46 100644 --- a/frontend/js/models/saving_account.js +++ b/frontend/js/models/saving_account.js @@ -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 diff --git a/frontend/js/views/bank_account/CreateView.vue b/frontend/js/views/bank_account/CreateView.vue index ed17cfd..a7b67b0 100644 --- a/frontend/js/views/bank_account/CreateView.vue +++ b/frontend/js/views/bank_account/CreateView.vue @@ -32,7 +32,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
@@ -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() } diff --git a/frontend/js/views/bank_account/EditView.vue b/frontend/js/views/bank_account/EditView.vue index ef24794..1a1c60f 100644 --- a/frontend/js/views/bank_account/EditView.vue +++ b/frontend/js/views/bank_account/EditView.vue @@ -45,7 +45,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
@@ -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() } diff --git a/frontend/js/views/category/CreateView.vue b/frontend/js/views/category/CreateView.vue index f3b19f3..a6b57ac 100644 --- a/frontend/js/views/category/CreateView.vue +++ b/frontend/js/views/category/CreateView.vue @@ -32,7 +32,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
@@ -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 } }) } diff --git a/frontend/js/views/category/EditView.vue b/frontend/js/views/category/EditView.vue index bfec4e2..5bc3f9b 100644 --- a/frontend/js/views/category/EditView.vue +++ b/frontend/js/views/category/EditView.vue @@ -61,7 +61,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
{ form.value.data, (data) => { if (data.code === 400) { - form.value.error = data.message + form.value.error = requestErrorBuilder(data) } else { form.value = null addTransaction = false diff --git a/frontend/js/views/saving_account/CreateView.vue b/frontend/js/views/saving_account/CreateView.vue index 1997349..482aad0 100644 --- a/frontend/js/views/saving_account/CreateView.vue +++ b/frontend/js/views/saving_account/CreateView.vue @@ -32,7 +32,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
@@ -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() } diff --git a/frontend/js/views/saving_account/EditView.vue b/frontend/js/views/saving_account/EditView.vue index 0f04075..9b726d5 100644 --- a/frontend/js/views/saving_account/EditView.vue +++ b/frontend/js/views/saving_account/EditView.vue @@ -45,7 +45,7 @@ variant="warning" :model-value="true" > - {{ form.error }} +
@@ -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() } diff --git a/frontend/js/views/transaction/ListView.vue b/frontend/js/views/transaction/ListView.vue index 28d58c3..5c5d71e 100644 --- a/frontend/js/views/transaction/ListView.vue +++ b/frontend/js/views/transaction/ListView.vue @@ -54,8 +54,9 @@ {{ form.error }} + > +
+ @@ -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 diff --git a/frontend/js/views/user/CreateView.vue b/frontend/js/views/user/CreateView.vue index f6e266c..cf6f801 100644 --- a/frontend/js/views/user/CreateView.vue +++ b/frontend/js/views/user/CreateView.vue @@ -26,6 +26,14 @@ class="p-3" @submit="submit" > + +
+
@@ -36,13 +44,13 @@