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

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>