Files
webapp/app/components/TripBadge.vue
2026-01-07 09:10:35 +07:00

130 lines
4.3 KiB
Vue

<template>
<div
class="group cursor-pointer transition-all duration-200"
:class="getTripStatusClass(trip)"
@click="$emit('click', trip)"
>
<!-- Main info -->
<div class="flex items-center space-x-2">
<span class="font-medium text-xs">{{ trip.name }}</span>
<span class="text-xs opacity-75">{{ trip.plannedWeight }}t</span>
</div>
<!-- Time -->
<div class="text-xs opacity-75 mt-1">
{{ formatTime(trip.plannedLoadingDate) }} - {{ formatTime(trip.plannedUnloadingDate) }}
</div>
<!-- Status -->
<div class="flex items-center mt-1">
<div class="w-full bg-black bg-opacity-20 rounded-full h-1">
<div
class="bg-white rounded-full h-1 transition-all duration-300"
:style="{ width: `${getTripProgress(trip)}%` }"
></div>
</div>
</div>
<!-- Tooltip on hover -->
<div class="hidden group-hover:block absolute z-10 bg-base-200 text-base-content p-3 rounded-lg shadow-lg min-w-64 -translate-y-full -translate-x-1/2 left-1/2 border border-base-300 pointer-events-none group-hover:pointer-events-auto">
<div class="font-semibold">{{ trip.name }}</div>
<div class="text-sm mt-1">{{ trip.company?.name }}</div>
<div class="mt-2 space-y-1 text-xs">
<div>Planned weight: {{ trip.plannedWeight }}t</div>
<div v-if="trip.weightAtLoading">Weight at loading: {{ trip.weightAtLoading }}t</div>
<div v-if="trip.weightAtUnloading">Weight at unloading: {{ trip.weightAtUnloading }}t</div>
</div>
<div class="mt-2 space-y-1 text-xs">
<div v-if="trip.plannedLoadingDate">
📅 Planned loading: {{ formatDateTime(trip.plannedLoadingDate) }}
</div>
<div v-if="trip.actualLoadingDate">
Actual loading: {{ formatDateTime(trip.actualLoadingDate) }}
</div>
<div v-if="trip.plannedUnloadingDate">
📅 Planned unloading: {{ formatDateTime(trip.plannedUnloadingDate) }}
</div>
<div v-if="trip.actualUnloadingDate">
Actual unloading: {{ formatDateTime(trip.actualUnloadingDate) }}
</div>
</div>
<!-- Tooltip arrow -->
<div class="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-base-300"></div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
trip: {
type: Object,
required: true
}
})
defineEmits(['click'])
const getTripStatusClass = (trip) => {
const baseClass = "relative inline-flex px-3 py-2 rounded-lg text-sm min-w-20 text-center text-base-100"
// Determine status by dates
const now = new Date()
const plannedStart = trip.plannedLoadingDate ? new Date(trip.plannedLoadingDate) : null
const actualEnd = trip.actualUnloadingDate ? new Date(trip.actualUnloadingDate) : null
const plannedEnd = trip.plannedUnloadingDate ? new Date(trip.plannedUnloadingDate) : null
if (actualEnd) {
// Completed
return `${baseClass} bg-success hover:bg-success/80`
} else if (plannedStart && now >= plannedStart) {
// In progress
return `${baseClass} bg-primary hover:bg-primary/80`
} else {
// Planned
return `${baseClass} bg-base-300 text-base-content hover:bg-base-400`
}
}
const getTripProgress = (trip) => {
const plannedStart = trip.plannedLoadingDate ? new Date(trip.plannedLoadingDate) : null
const plannedEnd = trip.plannedUnloadingDate ? new Date(trip.plannedUnloadingDate) : null
const actualEnd = trip.actualUnloadingDate ? new Date(trip.actualUnloadingDate) : null
if (!plannedStart || !plannedEnd) return 0
if (actualEnd) return 100
const now = new Date()
if (now < plannedStart) return 0
if (now > plannedEnd) return 90 // Overdue but not completed
const total = plannedEnd - plannedStart
const current = now - plannedStart
return Math.min((current / total) * 100, 90)
}
const formatTime = (dateStr) => {
if (!dateStr) return '--:--'
const date = new Date(dateStr)
return date.toLocaleTimeString('ru-RU', {
hour: '2-digit',
minute: '2-digit'
})
}
const formatDateTime = (dateStr) => {
if (!dateStr) return 'Not specified'
const date = new Date(dateStr)
return date.toLocaleString('ru-RU', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit'
})
}
</script>