245 lines
7.6 KiB
Vue
245 lines
7.6 KiB
Vue
<template>
|
|
<div class="card bg-base-100 border border-base-300 shadow">
|
|
<div class="card-body">
|
|
<div class="mb-4 flex items-center justify-between flex-wrap gap-3">
|
|
<h3 class="text-lg font-semibold text-base-content">{{ t('orderCalendar.header.title') }}</h3>
|
|
<div class="flex items-center gap-4">
|
|
<!-- Filters -->
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
id="show-loading"
|
|
v-model="showLoading"
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary checkbox-sm"
|
|
>
|
|
<span class="text-sm text-base-content/80">{{ t('orderCalendar.filters.loading') }}</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
id="show-unloading"
|
|
v-model="showUnloading"
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary checkbox-sm"
|
|
>
|
|
<span class="text-sm text-base-content/80">{{ t('orderCalendar.filters.unloading') }}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Simplified calendar view -->
|
|
<div class="bg-base-200 rounded-box p-4">
|
|
<div class="grid grid-cols-7 gap-2 mb-4">
|
|
<div v-for="month in months" :key="month.name" class="text-center">
|
|
<h4 class="font-medium text-sm text-base-content/80">{{ month.name }}</h4>
|
|
<div class="mt-2 space-y-1">
|
|
<div
|
|
v-for="week in month.weeks"
|
|
:key="week.start"
|
|
class="text-xs"
|
|
>
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-base-content/60">{{ formatWeek(week.start) }}</span>
|
|
<div class="flex space-x-1">
|
|
<div
|
|
v-for="event in week.events"
|
|
:key="event.id"
|
|
class="w-2 h-2 rounded-full"
|
|
:style="{ backgroundColor: event.color }"
|
|
:title="event.title"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Event summary -->
|
|
<div class="border-t border-base-300 pt-4">
|
|
<h4 class="font-medium text-sm text-base-content/80 mb-3">{{ t('orderCalendar.stats.title') }}</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div v-for="period in periodStats" :key="period.name" class="card bg-base-100 border border-base-300">
|
|
<div class="card-body p-3 gap-2">
|
|
<h5 class="font-medium text-sm text-base-content">{{ period.name }}</h5>
|
|
<p class="text-xs text-base-content/70">{{ period.description }}</p>
|
|
<div class="space-y-1 text-xs">
|
|
<div class="flex justify-between">
|
|
<span>{{ t('orderCalendar.stats.trips') }}</span>
|
|
<span class="font-medium">{{ period.tripCount }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span>{{ t('orderCalendar.stats.companies') }}</span>
|
|
<span class="font-medium">{{ period.companyCount }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span>{{ t('orderCalendar.stats.weight') }}</span>
|
|
<span class="font-medium">{{ period.totalWeight }}{{ t('orderCalendar.labels.weight_unit') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const props = defineProps({
|
|
stages: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
|
|
const showLoading = ref(true)
|
|
const showUnloading = ref(true)
|
|
const { t } = useI18n()
|
|
|
|
const allTrips = computed(() => {
|
|
const trips = []
|
|
props.stages.forEach(stage => {
|
|
if (stage.trips?.length) {
|
|
stage.trips.forEach(trip => {
|
|
trips.push({
|
|
...trip,
|
|
stageName: stage.name,
|
|
stageType: stage.stageType
|
|
})
|
|
})
|
|
}
|
|
})
|
|
return trips
|
|
})
|
|
|
|
const calendarEvents = computed(() => {
|
|
const events = []
|
|
|
|
allTrips.value.forEach(trip => {
|
|
const companyColor = getCompanyColor(trip.company?.name)
|
|
|
|
if (showLoading.value && trip.plannedLoadingDate) {
|
|
events.push({
|
|
id: `loading-${trip.uuid}`,
|
|
title: `${trip.name} - ${t('orderCalendar.labels.loading')}`,
|
|
date: new Date(trip.plannedLoadingDate),
|
|
color: companyColor,
|
|
type: 'loading',
|
|
trip: trip
|
|
})
|
|
}
|
|
|
|
if (showUnloading.value && trip.plannedUnloadingDate) {
|
|
events.push({
|
|
id: `unloading-${trip.uuid}`,
|
|
title: `${trip.name} - ${t('orderCalendar.labels.unloading')}`,
|
|
date: new Date(trip.plannedUnloadingDate),
|
|
color: companyColor,
|
|
type: 'unloading',
|
|
trip: trip
|
|
})
|
|
}
|
|
})
|
|
|
|
return events
|
|
})
|
|
|
|
const months = computed(() => {
|
|
const monthsData = []
|
|
const startDate = new Date('2024-08-01')
|
|
const endDate = new Date('2024-10-31')
|
|
|
|
const monthNames = t('orderCalendar.months').split('|')
|
|
|
|
monthNames.forEach((name, index) => {
|
|
const monthStart = new Date(2024, 7 + index, 1) // August = 7
|
|
const monthEnd = new Date(2024, 8 + index, 0)
|
|
|
|
const weeks = []
|
|
let weekStart = new Date(monthStart)
|
|
|
|
while (weekStart <= monthEnd) {
|
|
const weekEnd = new Date(weekStart)
|
|
weekEnd.setDate(weekEnd.getDate() + 6)
|
|
|
|
const weekEvents = calendarEvents.value.filter(event => {
|
|
const eventDate = event.date
|
|
return eventDate >= weekStart && eventDate <= weekEnd
|
|
})
|
|
|
|
weeks.push({
|
|
start: new Date(weekStart),
|
|
events: weekEvents.slice(0, 5) // Show max 5 events
|
|
})
|
|
|
|
weekStart.setDate(weekStart.getDate() + 7)
|
|
}
|
|
|
|
monthsData.push({
|
|
name,
|
|
weeks
|
|
})
|
|
})
|
|
|
|
return monthsData
|
|
})
|
|
|
|
const periodStats = computed(() => {
|
|
const stats = []
|
|
|
|
// Stats per stage
|
|
props.stages.forEach(stage => {
|
|
if (stage.trips?.length) {
|
|
const companies = new Set(stage.trips.map(trip => trip.company?.name).filter(Boolean))
|
|
const totalWeight = stage.trips.reduce((sum, trip) => sum + (trip.plannedWeight || 0), 0)
|
|
|
|
stats.push({
|
|
name: stage.name,
|
|
description: stage.stageType === 'transport' ? getTransportTypeText(stage.transportType) : t('orderCalendar.labels.service'),
|
|
tripCount: stage.trips.length,
|
|
companyCount: companies.size,
|
|
totalWeight: totalWeight
|
|
})
|
|
} else {
|
|
stats.push({
|
|
name: stage.name,
|
|
description: t('orderCalendar.labels.service_stage'),
|
|
tripCount: 0,
|
|
companyCount: stage.selectedCompany ? 1 : 0,
|
|
totalWeight: 0
|
|
})
|
|
}
|
|
})
|
|
|
|
return stats
|
|
})
|
|
|
|
const getCompanyColor = (companyName) => {
|
|
const colors = {
|
|
'Kenya Coffee Logistics Ltd': '#ef4444',
|
|
'Kenya Highland Transport': '#f97316',
|
|
'Mombasa Express Cargo': '#eab308',
|
|
'East Africa Shipping Lines': '#3b82f6',
|
|
'TruckCo': '#10b981',
|
|
'AutoBase #1': '#8b5cf6',
|
|
'RailTrans': '#ec4899'
|
|
}
|
|
return colors[companyName] || '#6b7280'
|
|
}
|
|
|
|
const getTransportTypeText = (transportType) => {
|
|
const texts = {
|
|
auto: t('orderCalendar.transport.auto'),
|
|
rail: t('orderCalendar.transport.rail'),
|
|
sea: t('orderCalendar.transport.sea'),
|
|
air: t('orderCalendar.transport.air')
|
|
}
|
|
return texts[transportType] || transportType
|
|
}
|
|
|
|
const formatWeek = (date) => {
|
|
return `${date.getDate()}-${date.getDate() + 6}`
|
|
}
|
|
</script>
|