398 lines
10 KiB
TypeScript
398 lines
10 KiB
TypeScript
|
|
import { ref, computed } from 'vue';
|
||
|
|
import { Api } from '@api';
|
||
|
|
import type {
|
||
|
|
DriverProfile,
|
||
|
|
Vehicle,
|
||
|
|
UserPreferences,
|
||
|
|
DriverPublicProfile,
|
||
|
|
ApiResponse
|
||
|
|
} from '../types';
|
||
|
|
|
||
|
|
// ============================================================
|
||
|
|
// STATE
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
const driverProfile = ref<DriverPublicProfile | null>(null);
|
||
|
|
const myDriverProfile = ref<DriverProfile | null>(null);
|
||
|
|
const myVehicles = ref<Vehicle[]>([]);
|
||
|
|
const myPreferences = ref<UserPreferences | null>(null);
|
||
|
|
|
||
|
|
const loading = ref(false);
|
||
|
|
const error = ref<string | null>(null);
|
||
|
|
|
||
|
|
// ============================================================
|
||
|
|
// COMPOSABLE
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
export function useDriverProfile() {
|
||
|
|
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// COMPUTED
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
|
||
|
|
const isDriver = computed(() => myDriverProfile.value?.isDriver ?? false);
|
||
|
|
const hasVehicles = computed(() => myVehicles.value.length > 0);
|
||
|
|
const defaultVehicle = computed(() =>
|
||
|
|
myVehicles.value.find(v => v.isDefault) || myVehicles.value[0]
|
||
|
|
);
|
||
|
|
const averageRating = computed(() => myDriverProfile.value?.averageRating ?? 0);
|
||
|
|
const totalRides = computed(() =>
|
||
|
|
(myDriverProfile.value?.ridesCompletedAsDriver ?? 0) +
|
||
|
|
(myDriverProfile.value?.ridesCompletedAsPassenger ?? 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// API CALLS
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Ottieni profilo pubblico di un conducente
|
||
|
|
*/
|
||
|
|
const fetchDriverProfile = async (userId: string) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
`/api/trasporti/driver/${userId}`,
|
||
|
|
'GET'
|
||
|
|
) as ApiResponse<DriverPublicProfile>;
|
||
|
|
|
||
|
|
if (response.success && response.data?.data) {
|
||
|
|
driverProfile.value = response.data.data;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nel recupero del profilo';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Aggiorna il mio profilo conducente
|
||
|
|
*/
|
||
|
|
const updateDriverProfile = async (profileData: Partial<DriverProfile>) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
'/api/trasporti/driver/profile',
|
||
|
|
'PUT',
|
||
|
|
{ driverProfile: profileData }
|
||
|
|
) as ApiResponse<{ driverProfile: DriverProfile; preferences: UserPreferences }>;
|
||
|
|
|
||
|
|
if (response.success && response.data?.data) {
|
||
|
|
myDriverProfile.value = response.data?.data.driverProfile;
|
||
|
|
myPreferences.value = response.data?.data.preferences;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nell\'aggiornamento del profilo';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Aggiorna le mie preferenze
|
||
|
|
*/
|
||
|
|
const updatePreferences = async (preferences: Partial<UserPreferences>) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
'/api/trasporti/driver/profile',
|
||
|
|
'PUT',
|
||
|
|
{ preferences }
|
||
|
|
) as ApiResponse<{ driverProfile: DriverProfile; preferences: UserPreferences }>;
|
||
|
|
|
||
|
|
if (response.success && response.data?.data) {
|
||
|
|
myPreferences.value = response.data?.data.preferences;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nell\'aggiornamento delle preferenze';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Aggiungi veicolo
|
||
|
|
*/
|
||
|
|
const addVehicle = async (vehicle: Omit<Vehicle, '_id'>) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
'/api/trasporti/driver/vehicles',
|
||
|
|
'POST',
|
||
|
|
{ vehicle }
|
||
|
|
) as ApiResponse<Vehicle[]>;
|
||
|
|
|
||
|
|
if (response.success && response.data?.data) {
|
||
|
|
myVehicles.value = response.data.data;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nell\'aggiunta del veicolo';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Aggiorna veicolo
|
||
|
|
*/
|
||
|
|
const updateVehicle = async (vehicleId: string, vehicle: Partial<Vehicle>) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
`/api/trasporti/driver/vehicles/${vehicleId}`,
|
||
|
|
'PUT',
|
||
|
|
{ vehicle }
|
||
|
|
) as ApiResponse<Vehicle[]>;
|
||
|
|
|
||
|
|
if (response.success && response.data?.data) {
|
||
|
|
myVehicles.value = response.data.data;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nell\'aggiornamento del veicolo';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Rimuovi veicolo
|
||
|
|
*/
|
||
|
|
const removeVehicle = async (vehicleId: string) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
`/api/trasporti/driver/vehicles/${vehicleId}`,
|
||
|
|
'DELETE'
|
||
|
|
) as ApiResponse<void>;
|
||
|
|
|
||
|
|
if (response.success) {
|
||
|
|
myVehicles.value = myVehicles.value.filter(v => v._id !== vehicleId);
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nella rimozione del veicolo';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Imposta veicolo predefinito
|
||
|
|
*/
|
||
|
|
const setDefaultVehicle = async (vehicleId: string) => {
|
||
|
|
try {
|
||
|
|
loading.value = true;
|
||
|
|
error.value = null;
|
||
|
|
|
||
|
|
const response = await Api.SendReq(
|
||
|
|
`/api/trasporti/driver/vehicles/${vehicleId}/default`,
|
||
|
|
'POST'
|
||
|
|
) as ApiResponse<void>;
|
||
|
|
|
||
|
|
if (response.success) {
|
||
|
|
// Aggiorna localmente
|
||
|
|
myVehicles.value = myVehicles.value.map(v => ({
|
||
|
|
...v,
|
||
|
|
isDefault: v._id === vehicleId
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
return response;
|
||
|
|
} catch (err: any) {
|
||
|
|
error.value = err.data?.message || err.message ||'Errore nell\'impostazione del veicolo predefinito';
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// UTILITIES
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Formatta tipo veicolo
|
||
|
|
*/
|
||
|
|
const formatVehicleType = (type: string): string => {
|
||
|
|
const types: Record<string, string> = {
|
||
|
|
auto: '🚗 Auto',
|
||
|
|
moto: '🏍️ Moto',
|
||
|
|
furgone: '🚐 Furgone',
|
||
|
|
minibus: '🚌 Minibus',
|
||
|
|
altro: '🚙 Altro'
|
||
|
|
};
|
||
|
|
return types[type] || type;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Formatta veicolo completo
|
||
|
|
*/
|
||
|
|
const formatVehicle = (vehicle: Vehicle): string => {
|
||
|
|
const parts = [];
|
||
|
|
if (vehicle.brand) parts.push(vehicle.brand);
|
||
|
|
if (vehicle.model) parts.push(vehicle.model);
|
||
|
|
if (vehicle.color) parts.push(`(${vehicle.color})`);
|
||
|
|
return parts.join(' ') || 'Veicolo';
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Formatta response time
|
||
|
|
*/
|
||
|
|
const formatResponseTime = (time?: string): string => {
|
||
|
|
const times: Record<string, string> = {
|
||
|
|
within_hour: 'Entro un\'ora',
|
||
|
|
within_day: 'Entro un giorno',
|
||
|
|
within_days: 'Entro qualche giorno'
|
||
|
|
};
|
||
|
|
return times[time || 'within_day'] || 'N/D';
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Formatta member since
|
||
|
|
*/
|
||
|
|
const formatMemberSince = (date?: Date | string): string => {
|
||
|
|
if (!date) return 'N/D';
|
||
|
|
const d = new Date(date);
|
||
|
|
return d.toLocaleDateString('it-IT', { month: 'long', year: 'numeric' });
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calcola livello utente
|
||
|
|
*/
|
||
|
|
const calculateLevel = (points: number): { level: number; progress: number; nextLevel: number } => {
|
||
|
|
const levels = [0, 100, 300, 600, 1000, 1500, 2500, 4000, 6000, 10000];
|
||
|
|
let level = 1;
|
||
|
|
|
||
|
|
for (let i = 1; i < levels.length; i++) {
|
||
|
|
if (points >= levels[i]) {
|
||
|
|
level = i + 1;
|
||
|
|
} else {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const currentLevelPoints = levels[level - 1] || 0;
|
||
|
|
const nextLevelPoints = levels[level] || levels[levels.length - 1];
|
||
|
|
const progress = ((points - currentLevelPoints) / (nextLevelPoints - currentLevelPoints)) * 100;
|
||
|
|
|
||
|
|
return {
|
||
|
|
level,
|
||
|
|
progress: Math.min(100, Math.max(0, progress)),
|
||
|
|
nextLevel: nextLevelPoints
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Ottieni badge icon
|
||
|
|
*/
|
||
|
|
const getBadgeIcon = (badgeName: string): string => {
|
||
|
|
const badges: Record<string, string> = {
|
||
|
|
first_ride: '🎉',
|
||
|
|
five_rides: '🚗',
|
||
|
|
ten_rides: '🏆',
|
||
|
|
fifty_rides: '⭐',
|
||
|
|
hundred_rides: '👑',
|
||
|
|
eco_warrior: '🌱',
|
||
|
|
super_driver: '🦸',
|
||
|
|
top_rated: '💯',
|
||
|
|
fast_responder: '⚡',
|
||
|
|
friendly: '😊'
|
||
|
|
};
|
||
|
|
return badges[badgeName] || '🏅';
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Inizializza profilo dal user corrente
|
||
|
|
*/
|
||
|
|
const initFromUser = (user: any) => {
|
||
|
|
if (user?.profile?.driverProfile) {
|
||
|
|
myDriverProfile.value = user.profile.driverProfile;
|
||
|
|
myVehicles.value = user.profile.driverProfile.vehicles || [];
|
||
|
|
}
|
||
|
|
if (user?.profile?.preferences) {
|
||
|
|
myPreferences.value = user.profile.preferences;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Pulisci stato
|
||
|
|
*/
|
||
|
|
const clearState = () => {
|
||
|
|
driverProfile.value = null;
|
||
|
|
myDriverProfile.value = null;
|
||
|
|
myVehicles.value = [];
|
||
|
|
myPreferences.value = null;
|
||
|
|
error.value = null;
|
||
|
|
};
|
||
|
|
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
// RETURN
|
||
|
|
// ------------------------------------------------------------
|
||
|
|
|
||
|
|
return {
|
||
|
|
// State
|
||
|
|
driverProfile,
|
||
|
|
myDriverProfile,
|
||
|
|
myVehicles,
|
||
|
|
myPreferences,
|
||
|
|
loading,
|
||
|
|
error,
|
||
|
|
|
||
|
|
// Computed
|
||
|
|
isDriver,
|
||
|
|
hasVehicles,
|
||
|
|
defaultVehicle,
|
||
|
|
averageRating,
|
||
|
|
totalRides,
|
||
|
|
|
||
|
|
// API Methods
|
||
|
|
fetchDriverProfile,
|
||
|
|
updateDriverProfile,
|
||
|
|
updatePreferences,
|
||
|
|
addVehicle,
|
||
|
|
updateVehicle,
|
||
|
|
removeVehicle,
|
||
|
|
setDefaultVehicle,
|
||
|
|
|
||
|
|
// Utilities
|
||
|
|
formatVehicleType,
|
||
|
|
formatVehicle,
|
||
|
|
formatResponseTime,
|
||
|
|
formatMemberSince,
|
||
|
|
calculateLevel,
|
||
|
|
getBadgeIcon,
|
||
|
|
initFromUser,
|
||
|
|
clearState
|
||
|
|
};
|
||
|
|
}
|