130 lines
4.3 KiB
Vue
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>
|