diff --git a/frontend/components/metrics/MetricsPage/index.tsx b/frontend/components/metrics/MetricsPage/index.tsx index 62e7b2f5..df65d404 100644 --- a/frontend/components/metrics/MetricsPage/index.tsx +++ b/frontend/components/metrics/MetricsPage/index.tsx @@ -4,6 +4,7 @@ import { MetricsPageIntro } from '@/components/metrics/MetricsPageIntro'; import { MetricsPageLines } from '@/components/metrics/MetricsPageLines'; import { MetricsPagePassengers } from '@/components/metrics/MetricsPagePassengers'; import { MetricsPageRecords } from '@/components/metrics/MetricsPageRecords'; +import { MetricsPageService } from '@/components/metrics/MetricsPageService'; /* * */ @@ -14,6 +15,7 @@ export function MetricsPage() { + ); } diff --git a/frontend/components/metrics/MetricsPageService/index.tsx b/frontend/components/metrics/MetricsPageService/index.tsx new file mode 100644 index 00000000..9c2181c4 --- /dev/null +++ b/frontend/components/metrics/MetricsPageService/index.tsx @@ -0,0 +1,161 @@ +'use client'; + +/* * */ + +import { LiveIcon } from '@/components/common/LiveIcon'; +import { Section } from '@/components/layout/Section'; +import { Surface } from '@/components/layout/Surface'; +import { Routes } from '@/utils/routes'; +import { ServiceMetrics } from '@carrismetropolitana/api-types/metrics'; +import { useTranslations } from 'next-intl'; +import { useMemo } from 'react'; +import useSWR from 'swr'; + +import styles from './styles.module.css'; + +/* * */ + +export function MetricsPageService() { + // + + // + // A. Define Variables + + const t = useTranslations('lines.LinesDetailMetricsService'); + const { data: serviceMetricsData, isLoading: serviceMetricsLoading } = useSWR(`${Routes.API}/metrics/service/all`); + + // + // B. Transform data + + const totalForAllLines = useMemo(() => { + if (!serviceMetricsData) return; + let totalScheduledTrips = 0; + let totalPassedTrips = 0; + serviceMetricsData.forEach((item) => { + totalScheduledTrips += item.total_trip_count; + totalPassedTrips += item.pass_trip_count; + }); + return { + pass: totalPassedTrips, + percentage: totalPassedTrips / totalScheduledTrips * 100, + total: totalScheduledTrips, + }; + }, [serviceMetricsData]); + + // const passTripCount = useMemo(() => { + // if (!linesDetailContext.data.service_metrics) return null; + // return linesDetailContext.data.service_metrics.reduce((acc, curr) => acc + Number(curr.pass_trip_count), 0); + // }, [linesDetailContext.data.service_metrics]); + + // const last15DaysService = useMemo(() => { + // if (!linesDetailContext.data.service_metrics) return []; + // linesDetailContext.data.service_metrics.sort((a, b) => Number(b.operational_date) - Number(a.operational_date)); + // return linesDetailContext.data.service_metrics.slice(0, 15); + // }, [linesDetailContext.data.service_metrics]); + + // const service15dayAverage = useMemo(() => { + // if (!last15DaysService) return 0; + // const { passTripCount, totalTripCount } = last15DaysService.reduce((acc, curr) => ({ + // passTripCount: acc.passTripCount + Number(curr.pass_trip_count), + // totalTripCount: acc.totalTripCount + Number(curr.total_trip_count), + // }), { passTripCount: 0, totalTripCount: 0 }); + // return ((passTripCount / totalTripCount) * 100).toFixed(2); + // }, [last15DaysService]); + + // const service15DayDistribution = useMemo(() => { + // if (!last15DaysService) return null; + // return last15DaysService + // .sort((a, b) => { + // return a.operational_date?.localeCompare(b.operational_date); + // }) + // .map(service => ({ + // ...service, + // fail_trip_count: service.total_trip_count - service.pass_trip_count, + // operational_date: DateTime.fromFormat(service.operational_date, 'yyyyMMdd').toFormat('ccc, dd LLL yyyy', { locale: 'pt-PT' }), + // pass_trip_percentage: (service.pass_trip_percentage * 100).toFixed(2), + // })); + // }, [last15DaysService]); + + // + // C. Render components + + // if (!linesDetailContext.data.line || !totalTripCount || !passTripCount || !service15DayDistribution) { + // return null; + // } + + if (serviceMetricsLoading || !totalForAllLines) { + return <>; + } + + return ( + +
+ +
+
+

{t('big_number', { value: totalForAllLines?.percentage })}

+ +
+

{t('title', { pass_trip_count: totalForAllLines?.pass, total_trip_count: totalForAllLines?.total })}

+

{t('description')}

+
+ + {/*
+ +
*/} + +
+

{t('footnote')}

+
+ +
+
+ ); + + // +}; diff --git a/frontend/components/metrics/MetricsPageService/styles.module.css b/frontend/components/metrics/MetricsPageService/styles.module.css new file mode 100644 index 00000000..9b149ad9 --- /dev/null +++ b/frontend/components/metrics/MetricsPageService/styles.module.css @@ -0,0 +1,53 @@ +/* * */ +/* WRAPPERS */ + +.infoWrapper { + display: flex; + flex-direction: column; + gap: var(--size-spacing-5); +} + +.chartWrapper { + width: 100%; + padding-top: var(--size-spacing-15); +} + +/* * */ +/* CONTENT */ + +.bigNumberWrapper { + display: flex; + flex-direction: row; + gap: var(--size-spacing-10); + align-items: flex-start; +} + +.bigNumber { + font-size: 60px; + font-weight: var(--font-weight-bold); + line-height: 1; +} + +.liveIcon { + margin-top: var(--size-spacing-5); +} + +.title { + font-size: 18px; + font-weight: var(--font-weight-bold); + color: var(--color-system-text-100) +} + +.description { + font-size: 12px; + font-weight: var(--font-weight-medium); + line-height: 1.6; + color: var(--color-system-text-200); +} + +.footnote { + font-size: 12px; + font-weight: var(--font-weight-medium); + line-height: 1.6; + color: var(--color-system-text-300); +} \ No newline at end of file