import { Injectable } from '@nestjs/common'; import { BOOKING_LIST_TABS, mapStatusCountsToTabs, } from '../bookings/booking-list-tabs.config'; import type { OverviewRangeQuery } from './dto/overview-query.dto'; import type { OverviewResponseDto } from './dto/overview-response.dto'; import type { OverviewBillingTabDto, OverviewBookingsTabDto, OverviewCustomersTabDto, OverviewOperationsTabDto, OverviewStaffTabDto, } from './dto/overview-tab-response.dto'; import { OVERVIEW_RANGE_DAYS } from './overview.constants'; import { OverviewRepository } from './overview.repository'; @Injectable() export class OverviewService { constructor(private readonly overviewRepository: OverviewRepository) {} private mapStatusCounts(statusCounts: Record) { const pipelineTabs = mapStatusCountsToTabs(statusCounts); const bookingsByPipeline = BOOKING_LIST_TABS.filter( (tab) => tab.key !== 'all', ).map((tab) => ({ stage: tab.key, count: pipelineTabs[tab.key], })); const bookingsByStatus = Object.entries(statusCounts) .map(([status, count]) => ({ status, count })) .sort((a, b) => b.count - a.count); return { bookingsByPipeline, bookingsByStatus }; } async getDashboard(range: OverviewRangeQuery = '30d'): Promise { const days = OVERVIEW_RANGE_DAYS[range]; const [ bookingKpis, operationsKpis, customerKpis, billingKpis, staffKpis, bookingTrend, statusCounts, paymentTrend, recentBookings, ] = await Promise.all([ this.overviewRepository.getBookingKpis(), this.overviewRepository.getOperationsKpis(), this.overviewRepository.getCustomerKpis(), this.overviewRepository.getBillingKpis(), this.overviewRepository.getStaffKpis(), this.overviewRepository.getBookingTrend(days), this.overviewRepository.getStatusCounts(), this.overviewRepository.getPaymentTrend(days), this.overviewRepository.getRecentBookings(8), ]); const { bookingsByPipeline, bookingsByStatus } = this.mapStatusCounts(statusCounts); return { kpis: { bookings: bookingKpis, operations: operationsKpis, customers: customerKpis, billing: billingKpis, staff: staffKpis, }, bookingTrend, bookingsByStatus, bookingsByPipeline, paymentTrend, recentBookings: recentBookings.map((row) => ({ ...row, createdAt: row.createdAt.toISOString(), })), generatedAt: new Date().toISOString(), }; } async getBookingsTab(range: OverviewRangeQuery = '30d'): Promise { const days = OVERVIEW_RANGE_DAYS[range]; const [ kpis, bookingTrend, statusCounts, bookingsByFreightType, bookingsByCurrency, recentBookings, ] = await Promise.all([ this.overviewRepository.getBookingKpis(), this.overviewRepository.getBookingTrend(days), this.overviewRepository.getStatusCounts(), this.overviewRepository.getBookingsByFreightType(), this.overviewRepository.getBookingsByCurrency(), this.overviewRepository.getRecentBookings(8), ]); const { bookingsByPipeline, bookingsByStatus } = this.mapStatusCounts(statusCounts); return { kpis, bookingTrend, bookingsByStatus, bookingsByPipeline, bookingsByFreightType, bookingsByCurrency, recentBookings: recentBookings.map((row) => ({ ...row, createdAt: row.createdAt.toISOString(), })), generatedAt: new Date().toISOString(), }; } async getBillingTab(range: OverviewRangeQuery = '30d'): Promise { const days = OVERVIEW_RANGE_DAYS[range]; const [kpis, paymentTrend, paymentsByStatus, paymentsByMethod, revenueByCurrency] = await Promise.all([ this.overviewRepository.getBillingKpis(), this.overviewRepository.getPaymentTrend(days), this.overviewRepository.getPaymentsByStatus(), this.overviewRepository.getPaymentsByMethod(), this.overviewRepository.getRevenueByCurrency(), ]); return { kpis, paymentTrend, paymentsByStatus, paymentsByMethod, revenueByCurrency, generatedAt: new Date().toISOString(), }; } async getOperationsTab(): Promise { const [ kpis, trainStatusBreakdown, wagonStatusBreakdown, containerStatusBreakdown, cargoStatusBreakdown, ] = await Promise.all([ this.overviewRepository.getOperationsKpis(), this.overviewRepository.getTrainStatusBreakdown(), this.overviewRepository.getWagonStatusBreakdown(), this.overviewRepository.getContainerStatusBreakdown(), this.overviewRepository.getCargoStatusBreakdown(), ]); return { kpis, trainStatusBreakdown, wagonStatusBreakdown, containerStatusBreakdown, cargoStatusBreakdown, generatedAt: new Date().toISOString(), }; } async getCustomersTab(range: OverviewRangeQuery = '30d'): Promise { const days = OVERVIEW_RANGE_DAYS[range]; const [kpis, customerGrowthTrend, customersByType, topCustomersByBookings] = await Promise.all([ this.overviewRepository.getCustomerKpis(), this.overviewRepository.getCustomerGrowthTrend(days), this.overviewRepository.getCustomersByType(), this.overviewRepository.getTopCustomersByBookings(8), ]); return { kpis, customerGrowthTrend, customersByType, topCustomersByBookings, generatedAt: new Date().toISOString(), }; } async getStaffTab(range: OverviewRangeQuery = '30d'): Promise { const days = OVERVIEW_RANGE_DAYS[range]; const [kpis, usersByStatus, employeeGrowthTrend, activeUsersBreakdown] = await Promise.all([ this.overviewRepository.getStaffKpis(), this.overviewRepository.getUsersByStatus(), this.overviewRepository.getEmployeeGrowthTrend(days), this.overviewRepository.getActiveUsersBreakdown(), ]); return { kpis, usersByStatus, employeeGrowthTrend, activeUsersBreakdown, generatedAt: new Date().toISOString(), }; } }