287 lines
6.9 KiB
TypeScript
287 lines
6.9 KiB
TypeScript
// RideWidget.ts
|
|
import { defineComponent, ref, computed, onMounted, watch } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { Api } from '@api';
|
|
import type { Ride, ContribType } from '../../types/trasporti.types';
|
|
|
|
interface WidgetStats {
|
|
offers: number;
|
|
requests: number;
|
|
matches: number;
|
|
}
|
|
|
|
interface WidgetData {
|
|
stats: WidgetStats;
|
|
recentRides: Ride[];
|
|
myActiveRides: Ride[];
|
|
pendingRequests: number;
|
|
unreadMessages: number;
|
|
}
|
|
|
|
export default defineComponent({
|
|
name: 'RideWidget',
|
|
|
|
props: {
|
|
// Se vuoi che il widget parta espanso
|
|
defaultExpanded: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// Numero massimo di ride da mostrare
|
|
maxRides: {
|
|
type: Number,
|
|
default: 3
|
|
},
|
|
// Auto-refresh interval in ms (0 = disabled)
|
|
refreshInterval: {
|
|
type: Number,
|
|
default: 60000 // 1 minuto
|
|
}
|
|
},
|
|
|
|
emits: ['loaded', 'error'],
|
|
|
|
setup(props, { emit }) {
|
|
const router = useRouter();
|
|
|
|
// State
|
|
const isExpanded = ref(props.defaultExpanded);
|
|
const loading = ref(false);
|
|
const stats = ref<WidgetStats>({
|
|
offers: 0,
|
|
requests: 0,
|
|
matches: 0
|
|
});
|
|
const recentRides = ref<Ride[]>([]);
|
|
const myActiveRides = ref<Ride[]>([]);
|
|
const pendingRequests = ref(0);
|
|
const unreadMessages = ref(0);
|
|
|
|
// Computed
|
|
const totalCount = computed(() => stats.value.offers + stats.value.requests);
|
|
|
|
// Methods
|
|
const toggleExpand = () => {
|
|
isExpanded.value = !isExpanded.value;
|
|
if (isExpanded.value && recentRides.value.length === 0) {
|
|
loadWidgetData();
|
|
}
|
|
};
|
|
|
|
const loadWidgetData = async () => {
|
|
loading.value = true;
|
|
|
|
try {
|
|
const response = await Api.SendReqWithData('/api/trasporti/widget/data', 'GET', {});
|
|
|
|
if (response.success) {
|
|
const data: WidgetData = response.data;
|
|
|
|
stats.value = data.stats || { offers: 0, requests: 0, matches: 0 };
|
|
recentRides.value = data.recentRides || [];
|
|
myActiveRides.value = data.myActiveRides || [];
|
|
pendingRequests.value = data.pendingRequests || 0;
|
|
unreadMessages.value = data.unreadMessages || 0;
|
|
|
|
emit('loaded', data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Errore caricamento widget trasporti:', error);
|
|
emit('error', error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const loadStats = async () => {
|
|
try {
|
|
const response = await Api.SendReqWithData('/api/trasporti/stats/summary', 'GET');
|
|
|
|
if (response.success) {
|
|
stats.value = response.data;
|
|
}
|
|
} catch (error) {
|
|
console.error('Errore caricamento stats:', error);
|
|
}
|
|
};
|
|
|
|
const formatDate = (date: string | Date): string => {
|
|
const d = new Date(date);
|
|
const now = new Date();
|
|
const diff = d.getTime() - now.getTime();
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
|
|
if (days === 0) {
|
|
return `Oggi, ${d.toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' })}`;
|
|
} else if (days === 1) {
|
|
return `Domani, ${d.toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' })}`;
|
|
} else if (days > 1 && days <= 6) {
|
|
return d.toLocaleDateString('it-IT', {
|
|
weekday: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
} else {
|
|
return d.toLocaleDateString('it-IT', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
};
|
|
|
|
const getContribIcon = (contribution: any): string => {
|
|
if (!contribution?.types?.length) return '💶';
|
|
|
|
const iconMap: Record<string, string> = {
|
|
'Dono': '🎁',
|
|
'Offerta Libera': '💸',
|
|
'Baratto': '🤝',
|
|
'Scambio Lavoro': '💪',
|
|
'Monete Alternative': '🪙',
|
|
'RIS': '🍚',
|
|
'Euro': '💶',
|
|
'Bitcoin': '₿',
|
|
'Banca del Tempo': '⏳'
|
|
};
|
|
|
|
const firstType = contribution.types[0];
|
|
return iconMap[firstType?.label] || '💶';
|
|
};
|
|
|
|
const getContribLabel = (contribution: any): string => {
|
|
if (!contribution?.types?.length) return 'Gratuito';
|
|
|
|
const labels = contribution.types.map((t: ContribType) => t.label);
|
|
return labels.join(', ');
|
|
};
|
|
|
|
const getStatusColor = (status: string): string => {
|
|
const colors: Record<string, string> = {
|
|
'active': 'positive',
|
|
'pending': 'warning',
|
|
'completed': 'info',
|
|
'cancelled': 'negative'
|
|
};
|
|
return colors[status] || 'grey';
|
|
};
|
|
|
|
const getStatusLabel = (status: string): string => {
|
|
const labels: Record<string, string> = {
|
|
'active': 'Attivo',
|
|
'pending': 'In attesa',
|
|
'completed': 'Completato',
|
|
'cancelled': 'Annullato'
|
|
};
|
|
return labels[status] || status;
|
|
};
|
|
|
|
// Navigation
|
|
const goToCreate = (type: 'offer' | 'request') => {
|
|
router.push({
|
|
path: '/trasporti/crea',
|
|
query: { type }
|
|
});
|
|
};
|
|
|
|
const goToList = () => {
|
|
router.push('/trasporti');
|
|
};
|
|
|
|
const goToRide = (rideId: string) => {
|
|
router.push(`/trasporti/ride/${rideId}`);
|
|
};
|
|
|
|
const goToMyRides = () => {
|
|
router.push('/trasporti/rides/my');
|
|
};
|
|
|
|
const goToSearch = () => {
|
|
router.push('/trasporti/cerca');
|
|
};
|
|
|
|
const goToMap = () => {
|
|
router.push('/trasporti/mappa');
|
|
};
|
|
|
|
const goToHistory = () => {
|
|
router.push('/trasporti/storico');
|
|
};
|
|
|
|
const goToChat = () => {
|
|
router.push('/trasporti/chat');
|
|
};
|
|
|
|
// Auto-refresh
|
|
let refreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
const startAutoRefresh = () => {
|
|
if (props.refreshInterval > 0) {
|
|
refreshTimer = setInterval(() => {
|
|
if (isExpanded.value) {
|
|
loadWidgetData();
|
|
} else {
|
|
loadStats();
|
|
}
|
|
}, props.refreshInterval);
|
|
}
|
|
};
|
|
|
|
const stopAutoRefresh = () => {
|
|
if (refreshTimer) {
|
|
clearInterval(refreshTimer);
|
|
refreshTimer = null;
|
|
}
|
|
};
|
|
|
|
// Lifecycle
|
|
onMounted(() => {
|
|
loadStats();
|
|
if (props.defaultExpanded) {
|
|
loadWidgetData();
|
|
}
|
|
startAutoRefresh();
|
|
});
|
|
|
|
// Cleanup
|
|
watch(() => props.refreshInterval, () => {
|
|
stopAutoRefresh();
|
|
startAutoRefresh();
|
|
});
|
|
|
|
return {
|
|
// State
|
|
isExpanded,
|
|
loading,
|
|
stats,
|
|
recentRides,
|
|
myActiveRides,
|
|
pendingRequests,
|
|
unreadMessages,
|
|
|
|
// Computed
|
|
totalCount,
|
|
|
|
// Methods
|
|
toggleExpand,
|
|
formatDate,
|
|
getContribIcon,
|
|
getContribLabel,
|
|
getStatusColor,
|
|
getStatusLabel,
|
|
|
|
// Navigation
|
|
goToCreate,
|
|
goToList,
|
|
goToRide,
|
|
goToMyRides,
|
|
goToSearch,
|
|
goToMap,
|
|
goToHistory,
|
|
goToChat
|
|
};
|
|
}
|
|
});
|