🎨 Frontend komplett implementiert
Views: - Login/Registrierung - Dashboard mit Stats - Aufträge (Liste + Detail) - Mitarbeiterverwaltung - Verfügbarkeitskalender - Stundenzettel - Einstellungen - Module (Dev-Panel) Features: - Vue 3 + Composition API - TailwindCSS mit Dark Mode - Pinia State Management - JWT Auth mit Refresh - Responsive Design - Rollen-basierte Navigation
This commit is contained in:
106
src/api/index.ts
Normal file
106
src/api/index.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
const API_URL = import.meta.env.VITE_API_URL || '/api'
|
||||
|
||||
interface ApiResponse<T = unknown> {
|
||||
data: T
|
||||
status: number
|
||||
}
|
||||
|
||||
class ApiClient {
|
||||
private baseUrl: string
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl
|
||||
}
|
||||
|
||||
private getToken(): string | null {
|
||||
return localStorage.getItem('accessToken')
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
method: string,
|
||||
endpoint: string,
|
||||
data?: unknown
|
||||
): Promise<ApiResponse<T>> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
const token = this.getToken()
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
const config: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
}
|
||||
|
||||
if (data) {
|
||||
config.body = JSON.stringify(data)
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.baseUrl}${endpoint}`, config)
|
||||
|
||||
// Handle 401 - try to refresh token
|
||||
if (response.status === 401) {
|
||||
const refreshed = await this.tryRefreshToken()
|
||||
if (refreshed) {
|
||||
// Retry request with new token
|
||||
headers['Authorization'] = `Bearer ${this.getToken()}`
|
||||
const retryResponse = await fetch(`${this.baseUrl}${endpoint}`, {
|
||||
...config,
|
||||
headers,
|
||||
})
|
||||
const retryData = await retryResponse.json()
|
||||
return { data: retryData, status: retryResponse.status }
|
||||
}
|
||||
}
|
||||
|
||||
const responseData = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(responseData.error || 'Request failed')
|
||||
}
|
||||
|
||||
return { data: responseData, status: response.status }
|
||||
}
|
||||
|
||||
private async tryRefreshToken(): Promise<boolean> {
|
||||
const refreshToken = localStorage.getItem('refreshToken')
|
||||
if (!refreshToken) return false
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/auth/refresh`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ refreshToken }),
|
||||
})
|
||||
|
||||
if (!response.ok) return false
|
||||
|
||||
const data = await response.json()
|
||||
localStorage.setItem('accessToken', data.accessToken)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
get<T>(endpoint: string): Promise<ApiResponse<T>> {
|
||||
return this.request<T>('GET', endpoint)
|
||||
}
|
||||
|
||||
post<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
|
||||
return this.request<T>('POST', endpoint, data)
|
||||
}
|
||||
|
||||
put<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
|
||||
return this.request<T>('PUT', endpoint, data)
|
||||
}
|
||||
|
||||
delete<T>(endpoint: string): Promise<ApiResponse<T>> {
|
||||
return this.request<T>('DELETE', endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
export const api = new ApiClient(API_URL)
|
||||
Reference in New Issue
Block a user