feat : ajout d'une gestion d'erreur au global côté front avec la lib toaster et I18n pour centraliser les messages d'erreur
This commit is contained in:
18
frontend/assets/css/toast.css
Normal file
18
frontend/assets/css/toast.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.iziToast {
|
||||
font-size: 16px;
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
.iziToast > .iziToast-body {
|
||||
padding: 18px 24px;
|
||||
}
|
||||
|
||||
.iziToast > .iziToast-body .iziToast-title {
|
||||
font-size: 18px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.iziToast > .iziToast-body .iziToast-message {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
@@ -16,12 +16,25 @@ export type ApiFetchOptions<ResponseType extends 'json' | 'blob'> =
|
||||
FetchOptions<ResponseType> & {
|
||||
toast?: boolean
|
||||
toastTitle?: string
|
||||
toastErrorMessage?: string
|
||||
toastSuccessMessage?: string
|
||||
toastErrorKey?: string
|
||||
toastSuccessKey?: string
|
||||
}
|
||||
|
||||
export const useApi = (): ApiClient => {
|
||||
const config = useRuntimeConfig()
|
||||
const baseURL = config.public.apiBase ?? '/api'
|
||||
const toast = useToast()
|
||||
const nuxtApp = useNuxtApp()
|
||||
const i18n = nuxtApp.$i18n as
|
||||
| {
|
||||
t: (key: string) => string
|
||||
te?: (key: string) => boolean
|
||||
}
|
||||
| undefined
|
||||
const t = (key: string) => (i18n?.t ? String(i18n.t(key)) : key)
|
||||
const te = (key: string) => (i18n?.te ? i18n.te(key) : false)
|
||||
|
||||
const extractErrorMessage = (error: unknown, responseData?: unknown): string => {
|
||||
const data = responseData ?? (error as FetchError)?.data
|
||||
@@ -46,17 +59,55 @@ export const useApi = (): ApiClient => {
|
||||
return (error as FetchError)?.message ?? 'Erreur inconnue.'
|
||||
}
|
||||
|
||||
const methodErrorKeys: Record<string, string> = {
|
||||
GET: 'errors.http.get',
|
||||
POST: 'errors.http.post',
|
||||
PUT: 'errors.http.put',
|
||||
PATCH: 'errors.http.patch',
|
||||
DELETE: 'errors.http.delete'
|
||||
}
|
||||
|
||||
const client = $fetch.create({
|
||||
baseURL,
|
||||
retry: 0,
|
||||
onResponse({ options }) {
|
||||
const apiOptions = options as ApiFetchOptions<'json'>
|
||||
if (apiOptions?.toast === false) {
|
||||
return
|
||||
}
|
||||
|
||||
const successKey = apiOptions?.toastSuccessKey
|
||||
const successMessage =
|
||||
apiOptions?.toastSuccessMessage ||
|
||||
(successKey ? (te(successKey) ? t(successKey) : successKey) : '')
|
||||
|
||||
if (successMessage) {
|
||||
toast.success({
|
||||
title: 'Succès',
|
||||
message: successMessage
|
||||
})
|
||||
}
|
||||
},
|
||||
onResponseError({ response, error, options }) {
|
||||
const apiOptions = options as ApiFetchOptions<'json'>
|
||||
if (apiOptions?.toast === false) {
|
||||
return
|
||||
}
|
||||
|
||||
const method =
|
||||
typeof options?.method === 'string' ? options.method.toUpperCase() : 'GET'
|
||||
const defaultKey = methodErrorKeys[method]
|
||||
const defaultMessage =
|
||||
defaultKey && te(defaultKey) ? t(defaultKey) : ''
|
||||
const errorKey = apiOptions?.toastErrorKey
|
||||
const errorMessage =
|
||||
errorKey ? (te(errorKey) ? t(errorKey) : errorKey) : ''
|
||||
const extractedMessage = extractErrorMessage(error, response?._data)
|
||||
const message =
|
||||
extractErrorMessage(error, response?._data) ||
|
||||
apiOptions?.toastErrorMessage ||
|
||||
errorMessage ||
|
||||
defaultMessage ||
|
||||
extractedMessage ||
|
||||
'Une erreur est survenue.'
|
||||
|
||||
toast.error({
|
||||
|
||||
23
frontend/i18n/locales/fr.json
Normal file
23
frontend/i18n/locales/fr.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"errors": {
|
||||
"http": {
|
||||
"get": "Impossible de récupérer les données.",
|
||||
"post": "Impossible de créer la ressource.",
|
||||
"put": "Impossible de mettre à jour la ressource.",
|
||||
"patch": "Impossible de mettre à jour la ressource.",
|
||||
"delete": "Impossible de supprimer la ressource."
|
||||
},
|
||||
"reception": {
|
||||
"list": "Impossible de récupérer la liste des réceptions.",
|
||||
"fetch": "Impossible de récupérer la réception.",
|
||||
"create": "Impossible de créer la réception.",
|
||||
"update": "Impossible de mettre à jour la réception.",
|
||||
"weigh": "Impossible de récupérer la pesée."
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"reception": {
|
||||
"update": "Réception mise à jour avec succès."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,34 @@ export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
ssr: false,
|
||||
modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt', 'nuxt-toast'],
|
||||
modules: [
|
||||
'@nuxtjs/tailwindcss',
|
||||
'@pinia/nuxt',
|
||||
'nuxt-toast',
|
||||
'@nuxtjs/i18n'
|
||||
],
|
||||
css: ['~/assets/css/toast.css'],
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
apiBase: process.env.NUXT_PUBLIC_API_BASE
|
||||
}
|
||||
},
|
||||
toast: {
|
||||
settings: {
|
||||
timeout: 0,
|
||||
closeOnClick: true,
|
||||
progressBar: false
|
||||
}
|
||||
},
|
||||
i18n: {
|
||||
strategy: 'no_prefix',
|
||||
defaultLocale: 'fr',
|
||||
langDir: 'locales',
|
||||
locales: [
|
||||
{ code: 'fr', file: 'fr.json', name: 'Français' }
|
||||
]
|
||||
},
|
||||
typescript: {
|
||||
strict: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
2438
frontend/package-lock.json
generated
2438
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
"build:dist": "nuxt generate && rm -rf dist && cp -R .output/public dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/i18n": "^10.2.1",
|
||||
"@pinia/nuxt": "^0.11.3",
|
||||
"izitoast": "^1.4.0",
|
||||
"nuxt": "^4.2.2",
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
import { useApi } from '~/composables/useApi'
|
||||
import type { ReceptionData } from '~/services/dto/reception-data'
|
||||
import type { WeightData } from '~/services/dto/weight-data'
|
||||
import {useApi} from '~/composables/useApi'
|
||||
import type {ReceptionData} from '~/services/dto/reception-data'
|
||||
import type {WeightData} from '~/services/dto/weight-data'
|
||||
|
||||
export async function getReceptionList() {
|
||||
const api = useApi()
|
||||
return api.get<ReceptionData>(`receptions`)
|
||||
return api.get<ReceptionData>(`receptions`, {}, {
|
||||
toastErrorKey: 'errors.reception.list'
|
||||
})
|
||||
}
|
||||
|
||||
export async function getReception(id: number) {
|
||||
const api = useApi()
|
||||
return api.get<ReceptionData>(`receptions/${id}`)
|
||||
return api.get<ReceptionData>(`receptions/${id}`, {}, {
|
||||
toastErrorKey: 'errors.reception.fetch'
|
||||
})
|
||||
}
|
||||
|
||||
export async function createReception(payload: Partial<ReceptionData> = {}) {
|
||||
const api = useApi()
|
||||
return api.post<ReceptionData>('receptions', payload)
|
||||
return api.post<ReceptionData>('receptions', payload, {
|
||||
toastErrorKey: 'errors.reception.create'
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateReception(id: number, payload: Partial<ReceptionData>) {
|
||||
const api = useApi()
|
||||
return api.patch<ReceptionData>(`receptions/${id}`, payload)
|
||||
return api.patch<ReceptionData>(`receptions/${id}`, payload, {
|
||||
toastErrorKey: 'errors.reception.update',
|
||||
toastSuccessKey: 'success.reception.update'
|
||||
})
|
||||
}
|
||||
|
||||
export async function getWeight(): Promise<WeightData> {
|
||||
const api = useApi()
|
||||
return api.get<WeightData>('receptions/weigh')
|
||||
return api.get<WeightData>('receptions/weigh', {}, {
|
||||
toastErrorKey: 'errors.reception.weigh'
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user