refactor : merge Inventory_frontend submodule into frontend/ directory
Merges the full git history of Inventory_frontend into the monorepo under frontend/. Removes the submodule in favor of a unified repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
141
frontend/app/composables/useApi.ts
Normal file
141
frontend/app/composables/useApi.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { useToast } from './useToast'
|
||||
import { humanizeError, extractApiErrorMessage } from '~/shared/utils/errorMessages'
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean
|
||||
data?: T
|
||||
error?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
interface ApiCallOptions extends RequestInit {
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
export function useApi() {
|
||||
const { showError } = useToast()
|
||||
const { public: publicConfig } = useRuntimeConfig()
|
||||
const API_BASE_URL = (publicConfig.apiBaseUrl as string) || 'http://localhost:3000'
|
||||
const parsedApiTimeout = Number(publicConfig.apiTimeout ?? 30000)
|
||||
const API_TIMEOUT = Number.isNaN(parsedApiTimeout) ? 30000 : parsedApiTimeout
|
||||
|
||||
const apiCall = async <T = any>(endpoint: string, options: ApiCallOptions = {}): Promise<ApiResponse<T>> => {
|
||||
const url = `${API_BASE_URL}${endpoint}`
|
||||
const isFormData = options.body instanceof FormData
|
||||
const defaultOptions: ApiCallOptions = {
|
||||
credentials: 'include',
|
||||
headers: isFormData ? {} : { 'Content-Type': 'application/json' },
|
||||
}
|
||||
|
||||
// Ajouter un timeout à la requête
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT)
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: {
|
||||
...defaultOptions.headers,
|
||||
...options.headers,
|
||||
},
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (response.ok) {
|
||||
let data: T | null = null
|
||||
if (response.status !== 204) {
|
||||
const contentType = response.headers.get('content-type') || ''
|
||||
if (contentType.includes('application/json') || contentType.includes('application/ld+json') || contentType.includes('+json')) {
|
||||
const text = await response.text()
|
||||
data = text ? JSON.parse(text) : null
|
||||
} else {
|
||||
const text = await response.text()
|
||||
data = (text || null) as T | null
|
||||
}
|
||||
}
|
||||
return { success: true, data: data as T }
|
||||
} else {
|
||||
const contentType = response.headers.get('content-type') || ''
|
||||
let errorData: Record<string, unknown> = {}
|
||||
if (contentType.includes('json')) {
|
||||
errorData = await response.json().catch(() => ({}))
|
||||
} else {
|
||||
const text = await response.text().catch(() => '')
|
||||
errorData = text ? { message: text } : {}
|
||||
}
|
||||
const rawMessage = response.status === 403
|
||||
? 'Permissions insuffisantes pour cette action.'
|
||||
: extractApiErrorMessage(errorData) || `Erreur ${response.status}: ${response.statusText}`
|
||||
const errorMessage = humanizeError(rawMessage)
|
||||
showError(errorMessage)
|
||||
return { success: false, error: errorMessage, status: response.status }
|
||||
}
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId)
|
||||
const err = error as Error & { name?: string }
|
||||
const errorMessage = err.name === 'AbortError'
|
||||
? 'La requête a pris trop de temps. Veuillez réessayer.'
|
||||
: 'Impossible de contacter le serveur. Vérifiez votre connexion.'
|
||||
showError(errorMessage)
|
||||
return { success: false, error: errorMessage }
|
||||
}
|
||||
}
|
||||
|
||||
const get = async <T = any>(endpoint: string): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, { method: 'GET' })
|
||||
}
|
||||
|
||||
const post = async <T = any>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/ld+json',
|
||||
},
|
||||
body: data !== undefined ? JSON.stringify(data) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const patch = async <T = any>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/merge-patch+json',
|
||||
},
|
||||
body: data !== undefined ? JSON.stringify(data) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const put = async <T = any>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/ld+json',
|
||||
},
|
||||
body: data !== undefined ? JSON.stringify(data) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const postFormData = async <T = any>(endpoint: string, formData: FormData): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
}
|
||||
|
||||
const del = async <T = any>(endpoint: string): Promise<ApiResponse<T>> => {
|
||||
return apiCall<T>(endpoint, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
return {
|
||||
apiCall,
|
||||
get,
|
||||
post,
|
||||
postFormData,
|
||||
patch,
|
||||
put,
|
||||
delete: del,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user