Refactor: Use existing API
This commit is contained in:
373
src/services/emergencyApi.ts
Normal file
373
src/services/emergencyApi.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
import { API_BASE_URL } from './config';
|
||||
|
||||
// Types para el sistema de emergencias
|
||||
export interface Incident {
|
||||
id: string;
|
||||
type: 'theft' | 'assault' | 'accident' | 'medical' | 'other';
|
||||
priority: 'low' | 'medium' | 'high' | 'critical';
|
||||
status: 'pending' | 'assigned' | 'in_progress' | 'resolved' | 'closed';
|
||||
title: string;
|
||||
description: string;
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
};
|
||||
reportedBy: {
|
||||
id: string;
|
||||
name: string;
|
||||
phone?: string;
|
||||
};
|
||||
assignedOfficer?: {
|
||||
id: string;
|
||||
name: string;
|
||||
badge: string;
|
||||
};
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
resolvedAt?: string;
|
||||
}
|
||||
|
||||
export interface EmergencyAlert {
|
||||
id: string;
|
||||
type: 'panic' | 'medical' | 'fire' | 'security' | 'natural_disaster';
|
||||
priority: 'high' | 'critical';
|
||||
status: 'active' | 'resolved';
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
};
|
||||
user: {
|
||||
id: string;
|
||||
name: string;
|
||||
phone?: string;
|
||||
};
|
||||
createdAt: string;
|
||||
deactivatedAt?: string;
|
||||
}
|
||||
|
||||
export interface Officer {
|
||||
id: string;
|
||||
name: string;
|
||||
badge: string;
|
||||
rank: string;
|
||||
status: 'available' | 'busy' | 'off_duty';
|
||||
location?: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
phone: string;
|
||||
assignedIncidents: number;
|
||||
}
|
||||
|
||||
export interface SecurityStats {
|
||||
totalIncidents: number;
|
||||
activeIncidents: number;
|
||||
resolvedToday: number;
|
||||
averageResponseTime: number;
|
||||
activeAlerts: number;
|
||||
availableOfficers: number;
|
||||
incidentsByType: Record<string, number>;
|
||||
incidentsByPriority: Record<string, number>;
|
||||
}
|
||||
|
||||
class EmergencyApiService {
|
||||
private getAuthHeaders() {
|
||||
const token = localStorage.getItem('karibeo-token') || localStorage.getItem('karibeo_token');
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': token ? `Bearer ${token}` : '',
|
||||
};
|
||||
}
|
||||
|
||||
// === INCIDENTES ===
|
||||
|
||||
/**
|
||||
* Reportar un nuevo incidente
|
||||
*/
|
||||
async createIncident(incidentData: {
|
||||
type: string;
|
||||
priority: string;
|
||||
title: string;
|
||||
description: string;
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
};
|
||||
}): Promise<Incident> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents`, {
|
||||
method: 'POST',
|
||||
headers: this.getAuthHeaders(),
|
||||
body: JSON.stringify(incidentData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error creating incident: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener todos los incidentes (solo officers/admin)
|
||||
*/
|
||||
async getAllIncidents(filters?: {
|
||||
status?: string;
|
||||
type?: string;
|
||||
priority?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<{ incidents: Incident[]; total: number; page: number; limit: number }> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.status) params.append('status', filters.status);
|
||||
if (filters?.type) params.append('type', filters.type);
|
||||
if (filters?.priority) params.append('priority', filters.priority);
|
||||
if (filters?.page) params.append('page', filters.page.toString());
|
||||
if (filters?.limit) params.append('limit', filters.limit.toString());
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents?${params}`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching incidents: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener incidentes asignados al oficial actual
|
||||
*/
|
||||
async getMyIncidents(): Promise<Incident[]> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents/my`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching my incidents: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener incidente por ID
|
||||
*/
|
||||
async getIncidentById(id: string): Promise<Incident> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents/${id}`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching incident: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualizar incidente
|
||||
*/
|
||||
async updateIncident(id: string, updateData: Partial<Incident>): Promise<Incident> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: this.getAuthHeaders(),
|
||||
body: JSON.stringify(updateData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error updating incident: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asignar incidente a oficial (solo admin)
|
||||
*/
|
||||
async assignIncident(incidentId: string, officerId: string): Promise<Incident> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/incidents/${incidentId}/assign/${officerId}`, {
|
||||
method: 'PATCH',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error assigning incident: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// === ALERTAS DE EMERGENCIA ===
|
||||
|
||||
/**
|
||||
* Crear alerta de emergencia (Botón de pánico)
|
||||
*/
|
||||
async createEmergencyAlert(alertData: {
|
||||
type: string;
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
};
|
||||
message?: string;
|
||||
}): Promise<EmergencyAlert> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/emergency-alerts`, {
|
||||
method: 'POST',
|
||||
headers: this.getAuthHeaders(),
|
||||
body: JSON.stringify(alertData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error creating emergency alert: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener alertas de emergencia activas (solo officers/admin)
|
||||
*/
|
||||
async getActiveEmergencyAlerts(): Promise<EmergencyAlert[]> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/emergency-alerts`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching emergency alerts: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Desactivar alerta de emergencia
|
||||
*/
|
||||
async deactivateEmergencyAlert(id: string): Promise<EmergencyAlert> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/emergency-alerts/${id}/deactivate`, {
|
||||
method: 'PATCH',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error deactivating emergency alert: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// === OFICIALES/POLITUR ===
|
||||
|
||||
/**
|
||||
* Obtener oficiales disponibles
|
||||
*/
|
||||
async getAvailableOfficers(): Promise<Officer[]> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/officers/available`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching available officers: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualizar estado de servicio del oficial
|
||||
*/
|
||||
async updateOfficerStatus(id: string, status: 'available' | 'busy' | 'off_duty'): Promise<Officer> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/officers/${id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: this.getAuthHeaders(),
|
||||
body: JSON.stringify({ status }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error updating officer status: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// === ESTADÍSTICAS ===
|
||||
|
||||
/**
|
||||
* Obtener estadísticas de seguridad (solo admin)
|
||||
*/
|
||||
async getSecurityStats(): Promise<SecurityStats> {
|
||||
const response = await fetch(`${API_BASE_URL}/security/stats`, {
|
||||
method: 'GET',
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching security stats: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// === GEOLOCALIZACIÓN ===
|
||||
|
||||
/**
|
||||
* Obtener ubicación actual del usuario
|
||||
*/
|
||||
getCurrentLocation(): Promise<{ latitude: number; longitude: number }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!navigator.geolocation) {
|
||||
reject(new Error('La geolocalización no está soportada en este navegador'));
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
resolve({
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
reject(new Error(`Error obteniendo ubicación: ${error.message}`));
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 60000,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activar botón de pánico con ubicación automática
|
||||
*/
|
||||
async activatePanicButton(type: 'panic' | 'medical' | 'security' = 'panic'): Promise<EmergencyAlert> {
|
||||
try {
|
||||
const location = await this.getCurrentLocation();
|
||||
|
||||
return await this.createEmergencyAlert({
|
||||
type,
|
||||
location,
|
||||
message: `Alerta de pánico activada desde la aplicación`,
|
||||
});
|
||||
} catch (error) {
|
||||
// Si no se puede obtener la ubicación, crear alerta sin ubicación específica
|
||||
return await this.createEmergencyAlert({
|
||||
type,
|
||||
location: { latitude: 0, longitude: 0, address: 'Ubicación no disponible' },
|
||||
message: `Alerta de pánico activada - ubicación no disponible`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const emergencyApi = new EmergencyApiService();
|
||||
Reference in New Issue
Block a user