Implemented complete design system and foundational infrastructure: **Design System Components:** - Button (all variants: primary, secondary, ghost, danger) - Input & Textarea (with validation and error states) - Card (elevated, outlined, flat variants) - Modal/Dialog (with focus trap and accessibility) - Avatar (with fallback initials) - Badge (all color variants) - Form helpers (FormField, Checkbox, Select) - Link component with Next.js integration - Navigation (Header, Footer with responsive design) **Layouts:** - MainLayout (with Header/Footer for public pages) - AuthLayout (minimal layout for auth flows) - DashboardLayout (with sidebar navigation) **Hooks & Utilities:** - useAuth() - authentication state management - useApi() - API calls with loading/error states - useLocalStorage() - persistent state management - apiClient - Axios instance with token refresh - authStore - Zustand store for auth state **Configuration:** - Tailwind config with design tokens - Dark mode support via CSS variables - Global styles with accessibility focus - WCAG 2.2 AA+ compliant focus indicators All components follow accessibility best practices with proper ARIA labels, keyboard navigation, and screen reader support. Job ID: MTAD-IMPL-2025-11-18-CL
73 lines
1.6 KiB
TypeScript
73 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useCallback } from 'react'
|
|
import { AxiosError, AxiosRequestConfig } from 'axios'
|
|
import { apiClient } from '../api'
|
|
|
|
export interface UseApiOptions {
|
|
onSuccess?: (data: any) => void
|
|
onError?: (error: Error) => void
|
|
}
|
|
|
|
export interface UseApiReturn<T> {
|
|
data: T | null
|
|
error: Error | null
|
|
isLoading: boolean
|
|
execute: (config: AxiosRequestConfig) => Promise<T | null>
|
|
reset: () => void
|
|
}
|
|
|
|
export function useApi<T = any>(options?: UseApiOptions): UseApiReturn<T> {
|
|
const [data, setData] = useState<T | null>(null)
|
|
const [error, setError] = useState<Error | null>(null)
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
const execute = useCallback(
|
|
async (config: AxiosRequestConfig): Promise<T | null> => {
|
|
setIsLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
const response = await apiClient.request<T>(config)
|
|
setData(response.data)
|
|
|
|
if (options?.onSuccess) {
|
|
options.onSuccess(response.data)
|
|
}
|
|
|
|
return response.data
|
|
} catch (err) {
|
|
const error = err as AxiosError
|
|
const errorMessage = error.response?.data
|
|
? JSON.stringify(error.response.data)
|
|
: error.message
|
|
|
|
const finalError = new Error(errorMessage)
|
|
setError(finalError)
|
|
|
|
if (options?.onError) {
|
|
options.onError(finalError)
|
|
}
|
|
|
|
return null
|
|
} finally {
|
|
setIsLoading(false)
|
|
}
|
|
},
|
|
[options]
|
|
)
|
|
|
|
const reset = useCallback(() => {
|
|
setData(null)
|
|
setError(null)
|
|
setIsLoading(false)
|
|
}, [])
|
|
|
|
return {
|
|
data,
|
|
error,
|
|
isLoading,
|
|
execute,
|
|
reset,
|
|
}
|
|
}
|