🎯 MapView v2.0 - Global Deployment Ready
✨ MAJOR FEATURES: • Auto-zoom intelligence với smart bounds fitting • Enhanced 3D GPS markers với pulsing effects • Professional route display với 6-layer rendering • Status-based parking icons với availability indicators • Production-ready build optimizations 🗺️ AUTO-ZOOM FEATURES: • Smart bounds fitting cho GPS + selected parking • Adaptive padding (50px) cho visual balance • Max zoom control (level 16) để tránh quá gần • Dynamic centering khi không có selection 🎨 ENHANCED VISUALS: • 3D GPS marker với multi-layer pulse effects • Advanced parking icons với status colors • Selection highlighting với animation • Dimming system cho non-selected items 🛣️ ROUTE SYSTEM: • OpenRouteService API integration • Multi-layer route rendering (glow, shadow, main, animated) • Real-time distance & duration calculation • Visual route info trong popup 📱 PRODUCTION READY: • SSR safe với dynamic imports • Build errors resolved • Global deployment via Vercel • Optimized performance 🌍 DEPLOYMENT: • Vercel: https://whatever-ctk2auuxr-phong12hexdockworks-projects.vercel.app • Bundle size: 22.8 kB optimized • Global CDN distribution • HTTPS enabled 💾 VERSION CONTROL: • MapView-v2.0.tsx backup created • MAPVIEW_VERSIONS.md documentation • Full version history tracking
This commit is contained in:
118
frontend/src/services/api.ts
Normal file
118
frontend/src/services/api.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import {
|
||||
FindNearbyParkingRequest,
|
||||
FindNearbyParkingResponse,
|
||||
ParkingLot,
|
||||
UpdateAvailabilityRequest,
|
||||
RouteRequest,
|
||||
RouteResponse
|
||||
} from '@/types';
|
||||
|
||||
class APIClient {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
this.client = axios.create({
|
||||
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api',
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor
|
||||
this.client.interceptors.request.use(
|
||||
(config) => {
|
||||
// Add auth token if available
|
||||
const token = typeof window !== 'undefined' ? localStorage.getItem('auth_token') : null;
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
// Response interceptor
|
||||
this.client.interceptors.response.use(
|
||||
(response: AxiosResponse) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
// Handle unauthorized
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('auth_token');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Parking endpoints
|
||||
async findNearbyParking(request: FindNearbyParkingRequest): Promise<FindNearbyParkingResponse> {
|
||||
const response = await this.client.post('/parking/nearby', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getAllParkingLots(): Promise<ParkingLot[]> {
|
||||
const response = await this.client.get('/parking');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getParkingLotById(id: number): Promise<ParkingLot> {
|
||||
const response = await this.client.get(`/parking/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async updateParkingAvailability(id: number, data: UpdateAvailabilityRequest): Promise<ParkingLot> {
|
||||
const response = await this.client.put(`/parking/${id}/availability`, data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getPopularParkingLots(limit?: number): Promise<ParkingLot[]> {
|
||||
const response = await this.client.get('/parking/popular', {
|
||||
params: { limit }
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Routing endpoints
|
||||
async calculateRoute(request: RouteRequest): Promise<RouteResponse> {
|
||||
const response = await this.client.post('/routing/calculate', request);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getRoutingServiceStatus(): Promise<{ status: string; version?: string }> {
|
||||
const response = await this.client.get('/routing/status');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Health endpoint
|
||||
async getHealth(): Promise<{ status: string; timestamp: string }> {
|
||||
const response = await this.client.get('/health');
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const apiClient = new APIClient();
|
||||
|
||||
// Export individual service functions for convenience
|
||||
export const parkingService = {
|
||||
findNearby: (request: FindNearbyParkingRequest) => apiClient.findNearbyParking(request),
|
||||
getAll: () => apiClient.getAllParkingLots(),
|
||||
getById: (id: number) => apiClient.getParkingLotById(id),
|
||||
updateAvailability: (id: number, data: UpdateAvailabilityRequest) =>
|
||||
apiClient.updateParkingAvailability(id, data),
|
||||
getPopular: (limit?: number) => apiClient.getPopularParkingLots(limit),
|
||||
};
|
||||
|
||||
export const routingService = {
|
||||
calculateRoute: (request: RouteRequest) => apiClient.calculateRoute(request),
|
||||
getStatus: () => apiClient.getRoutingServiceStatus(),
|
||||
};
|
||||
|
||||
export const healthService = {
|
||||
getHealth: () => apiClient.getHealth(),
|
||||
};
|
||||
Reference in New Issue
Block a user