diff --git a/frontend/composable/useApi.ts b/frontend/composable/useApi.ts deleted file mode 100644 index 0d5b79a..0000000 --- a/frontend/composable/useApi.ts +++ /dev/null @@ -1,183 +0,0 @@ -import type { FetchOptions } from 'ofetch' -import { $fetch, FetchError } from 'ofetch' -import { useAuthStore } from '~/stores/auth' - -export type AnyObject = Record - -export type ApiClient = { - get(url: string, query?: AnyObject, options?: ApiFetchOptions<'json'>): Promise - getBlob(url: string, query?: AnyObject, options?: ApiFetchOptions<'blob'>): Promise - post(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise - put(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise - patch(url: string, body?: AnyObject, options?: ApiFetchOptions<'json'>): Promise - delete(url: string, query?: AnyObject, options?: ApiFetchOptions<'json'>): Promise -} - -export type ApiFetchOptions = - FetchOptions & { - 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 auth = useAuthStore() - const nuxtApp = useNuxtApp() - let isHandlingUnauthorized = false - 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 - - if (typeof data === 'string') { - return data - } - - if (data && typeof data === 'object') { - const record = data as Record - return ( - (record['hydra:description'] as string) || - (record.detail as string) || - (record.message as string) || - (record.error as string) || - (record.title as string) || - (record['hydra:title'] as string) || - '' - ) - } - - return (error as FetchError)?.message ?? 'Erreur inconnue.' - } - - const methodErrorKeys: Record = { - 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, - credentials: 'include', - onResponse({ options, response }) { - const apiOptions = options as ApiFetchOptions<'json'> - if (apiOptions?.toast === false) { - return - } - - if (response?.status && response.status >= 400) { - return - } - - const successKey = apiOptions?.toastSuccessKey - const successMessage = - apiOptions?.toastSuccessMessage || - (successKey ? (te(successKey) ? t(successKey) : successKey) : '') - - if (successMessage) { - toast.success({ - title: 'Succès', - message: successMessage - }) - } - }, - async onResponseError({ response, error, options }) { - if (response?.status === 401) { - const requestUrl = typeof options?.url === 'string' ? options.url : '' - if (!requestUrl.includes('login_check') && !requestUrl.includes('logout')) { - if (!isHandlingUnauthorized) { - isHandlingUnauthorized = true - auth.clearSession() - const route = useRoute() - if (route.path !== '/login') { - await navigateTo('/login') - } - isHandlingUnauthorized = false - } - } - return - } - - 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 = - apiOptions?.toastErrorMessage || - errorMessage || - defaultMessage || - extractedMessage || - 'Une erreur est survenue.' - - toast.error({ - title: apiOptions?.toastTitle ?? 'Erreur', - message - }) - } - }) - - const request = ( - method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', - url: string, - options: ApiFetchOptions<'json'> = {} - ) => { - const needsJsonBody = method === 'POST' || method === 'PUT' - const needsMergePatch = method === 'PATCH' - - const headers = new Headers(options.headers as HeadersInit | undefined) - - if (needsMergePatch && !headers.has('Content-Type')) { - headers.set('Content-Type', 'application/merge-patch+json') - } else if (needsJsonBody && !headers.has('Content-Type')) { - headers.set('Content-Type', 'application/json') - } - - return client(url, { ...options, method, headers }) - } - - return { - get(url: string, query: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { - return request('GET', url, { ...options, query }) - }, - getBlob(url: string, query: AnyObject = {}, options: ApiFetchOptions<'blob'> = {}) { - return client(url, { ...options, method: 'GET', query, responseType: 'blob' }) - }, - post(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { - return request('POST', url, { ...options, body }) - }, - put(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { - return request('PUT', url, { ...options, body }) - }, - patch(url: string, body: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { - return request('PATCH', url, { ...options, body }) - }, - delete(url: string, query: AnyObject = {}, options: ApiFetchOptions<'json'> = {}) { - return request('DELETE', url, { ...options, query }) - } - } -} diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index 4942fcb..22845f6 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -11,12 +11,52 @@ "login": "Identifiants invalides.", "logout": "Impossible de se déconnecter.", "session": "Session expirée" + }, + "employee": { + "create": "Impossible de créer l'employé.", + "update": "Impossible de mettre à jour l'employé.", + "delete": "Impossible de supprimer l'employé." + }, + "site": { + "create": "Impossible de créer le site.", + "update": "Impossible de mettre à jour le site.", + "delete": "Impossible de supprimer le site." + }, + "absenceType": { + "create": "Impossible de créer le type d'absence.", + "update": "Impossible de mettre à jour le type d'absence.", + "delete": "Impossible de supprimer le type d'absence." + }, + "absence": { + "create": "Impossible de créer l'absence.", + "update": "Impossible de mettre à jour l'absence.", + "delete": "Impossible de supprimer l'absence." } }, "success": { "auth": { "login": "Connexion réussie.", "logout": "Déconnexion réussie." + }, + "employee": { + "create": "Employé créé.", + "update": "Employé mis à jour.", + "delete": "Employé supprimé." + }, + "site": { + "create": "Site créé.", + "update": "Site mis à jour.", + "delete": "Site supprimé." + }, + "absenceType": { + "create": "Type d'absence créé.", + "update": "Type d'absence mis à jour.", + "delete": "Type d'absence supprimé." + }, + "absence": { + "create": "Absence créée.", + "update": "Absence mise à jour.", + "delete": "Absence supprimée." } } } diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 7075ed1..9dcb4ea 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -1,7 +1,7 @@