🎨 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:
2026-02-20 15:18:06 +00:00
parent 82d388f555
commit 474c6d2470
27 changed files with 2328 additions and 2 deletions

106
src/api/index.ts Normal file
View 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)