diff --git a/public/index.html b/public/index.html index 1a21375..64e713c 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - NeTEx validation reports + Line Statistics diff --git a/src/lineStatistics/apiHooks/lineStatistics.response.types.ts b/src/lineStatistics/apiHooks/lineStatistics.response.types.ts index 3e8f8b8..c4605c5 100644 --- a/src/lineStatistics/apiHooks/lineStatistics.response.types.ts +++ b/src/lineStatistics/apiHooks/lineStatistics.response.types.ts @@ -1,4 +1,9 @@ -import { LineStatistics, PublicLine, Validity } from '../lineStatistics.types'; +import { + LineStatistics, + LineType, + PublicLine, + Validity, +} from '../lineStatistics.types'; export type FetchError = { status: number; @@ -44,6 +49,7 @@ export interface ExportedDayTypeStatisticsResponse { export interface ExportedLine { lineName: string; + lineType: LineType; operatingPeriodTo: string; operatingPeriodFrom: string; exportedDayTypesStatistics: ExportedDayTypeStatisticsResponse[]; diff --git a/src/lineStatistics/apiHooks/useExportedLineStatisticsForAllProviders.ts b/src/lineStatistics/apiHooks/useExportedLineStatisticsForAllProviders.ts index 2af9ff6..e30b88f 100644 --- a/src/lineStatistics/apiHooks/useExportedLineStatisticsForAllProviders.ts +++ b/src/lineStatistics/apiHooks/useExportedLineStatisticsForAllProviders.ts @@ -35,6 +35,7 @@ export const useExportedLineStatisticsForAllProviders = ( operatingPeriodFrom lines { lineName + lineType operatingPeriodTo operatingPeriodFrom exportedDayTypesStatistics { @@ -57,7 +58,7 @@ export const useExportedLineStatisticsForAllProviders = ( const client = new GraphQLClient(endpoint, { headers: { Authorization: `Bearer ${accessToken}`, - 'Et-Client-Name': 'entur-ninsar' + 'Et-Client-Name': 'entur-ninsar', }, }); const response = await client.request<{ diff --git a/src/lineStatistics/apiHooks/useExportedLineStatisticsForProvider.ts b/src/lineStatistics/apiHooks/useExportedLineStatisticsForProvider.ts index 3e9f940..587beba 100644 --- a/src/lineStatistics/apiHooks/useExportedLineStatisticsForProvider.ts +++ b/src/lineStatistics/apiHooks/useExportedLineStatisticsForProvider.ts @@ -19,19 +19,20 @@ export const useExportedLineStatisticsForProvider = (provider?: Provider) => { const getLineForProviderQuery = ` query GetExportedLineStatistics($providerCode: ID!) { lineStatistics(providerCode: $providerCode) { - startDate, + startDate publicLines { providerCode - publicCode, - operatingPeriodTo, - operatingPeriodFrom, + publicCode + operatingPeriodTo + operatingPeriodFrom lines { - lineName, - operatingPeriodTo, - operatingPeriodFrom, + lineName + lineType + operatingPeriodTo + operatingPeriodFrom exportedDayTypesStatistics { - dayTypeNetexId, - operatingPeriodTo, + dayTypeNetexId + operatingPeriodTo operatingPeriodFrom serviceJourneyName } @@ -50,7 +51,7 @@ export const useExportedLineStatisticsForProvider = (provider?: Provider) => { const client = new GraphQLClient(endpoint, { headers: { Authorization: `Bearer ${accessToken}`, - 'Et-Client-Name': 'entur-ninsar' + 'Et-Client-Name': 'entur-ninsar', }, }); const response = await client.request<{ @@ -58,7 +59,7 @@ export const useExportedLineStatisticsForProvider = (provider?: Provider) => { }>(getLineForProviderQuery, { providerCode: provider.code }); setExportedLineStatistics( - calculateExportedLineStatistics(response.lineStatistics) + calculateExportedLineStatistics(response.lineStatistics), ); setExportedLineStatisticsError(undefined); } diff --git a/src/lineStatistics/components/linesValidity/linesFilters/lineTypeChips.tsx b/src/lineStatistics/components/linesValidity/linesFilters/lineTypeChips.tsx new file mode 100644 index 0000000..69bdfc3 --- /dev/null +++ b/src/lineStatistics/components/linesValidity/linesFilters/lineTypeChips.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { ChoiceChip, ChoiceChipGroup } from '@entur/chip'; +import { useLocale } from '../../../../appContext'; +import { lineTypeLabel, titleText } from '../../../lineStatistics.constants'; +import { useRandomId } from '@entur/utils'; +import { LineType } from '../../../lineStatistics.types'; + +interface Props { + selectedLineType: LineType; + setSelectedLineType: (selectedValidity: LineType) => void; +} + +export const LineTypeChips = ({ + selectedLineType, + setSelectedLineType, +}: Props) => { + const randomId = useRandomId('lineType'); + const locale = useLocale(); + + return ( +
+ + setSelectedLineType(LineType[e.target.value as keyof typeof LineType]) + } + value={`${selectedLineType}`} + > + + {lineTypeLabel(locale)[LineType.ALL]} + + + {lineTypeLabel(locale)[LineType.FIXED]} + + + {lineTypeLabel(locale)[LineType.FLEXIBLE]} + + +
+ ); +}; diff --git a/src/lineStatistics/components/linesValidity/linesValidityList.tsx b/src/lineStatistics/components/linesValidity/linesValidityList.tsx index ca98d32..99e1a3a 100644 --- a/src/lineStatistics/components/linesValidity/linesValidityList.tsx +++ b/src/lineStatistics/components/linesValidity/linesValidityList.tsx @@ -1,6 +1,11 @@ import React, { useEffect, useState } from 'react'; import style from './linesValidity.module.scss'; -import { LineStatistics, PeriodValidity, Validity } from '../../lineStatistics.types'; +import { + LineStatistics, + LineType, + PeriodValidity, + Validity, +} from '../../lineStatistics.types'; import { sortLines } from './linesFilters/sortingUtilities'; import { LinesValidityHeader } from './linesValidityHeader'; import { @@ -8,35 +13,41 @@ import { validityCategoryLabel, } from '../../lineStatistics.constants'; import { useLocale } from '../../../appContext'; -import { Timeline } from "../timeline/timeline"; -import { ExpandableTimeline } from "../expandableTimeline/expandableTimeline"; -import { useRandomId } from "@entur/utils"; -import { Heading3 } from "@entur/typography"; -import { SortingChips } from "./linesFilters/sortingChips"; -import { ValidityChips } from "./linesFilters/validityChips"; -import { BannerAlertBox } from "@entur/alert"; -import { ValidNumberOfDaysText } from "./validNumberOfDaysText"; +import { Timeline } from '../timeline/timeline'; +import { ExpandableTimeline } from '../expandableTimeline/expandableTimeline'; +import { useRandomId } from '@entur/utils'; +import { Heading3 } from '@entur/typography'; +import { SortingChips } from './linesFilters/sortingChips'; +import { ValidityChips } from './linesFilters/validityChips'; +import { BannerAlertBox } from '@entur/alert'; +import { ValidNumberOfDaysText } from './validNumberOfDaysText'; interface Props { defaultSelectedValidity: Validity; lineStatistics: LineStatistics; listTitle: string; + selectedLineType?: LineType; } export const LinesValidityList = ({ defaultSelectedValidity, lineStatistics, - listTitle + listTitle, + selectedLineType = LineType.ALL, }: Props) => { const locale = useLocale(); const randomId = useRandomId('eds-expandable'); - const [expandedLinesState, setExpandedLinesState] = useState>(new Map()); + const [expandedLinesState, setExpandedLinesState] = useState< + Map + >(new Map()); const [sorting, setSorting] = useState(1); const [sortedLineNumbers, setSortedLineNumbers] = useState(); - const [selectedValidity, setSelectedValidity] = useState(defaultSelectedValidity); + const [selectedValidity, setSelectedValidity] = useState( + defaultSelectedValidity, + ); const toggleLineOpen = (lineNumber: string) => { const expandedLinesStateCopy = new Map(expandedLinesState); @@ -50,28 +61,19 @@ export const LinesValidityList = ({ useEffect(() => { setSelectedValidity(defaultSelectedValidity); - }, [defaultSelectedValidity]) + }, [defaultSelectedValidity]); useEffect(() => { lineStatistics && - setSortedLineNumbers( - sortLines(sorting, lineStatistics, selectedValidity), - ); + setSortedLineNumbers( + sortLines(sorting, lineStatistics, selectedValidity), + ); }, [lineStatistics, selectedValidity, sorting]); - const DayTypesValidity = ({ - index, - lineNumber, - }: { - index: number; - lineNumber: string; - }) => ( + const DayTypesValidity = ({ lineNumber }: { lineNumber: string }) => ( <> {lineStatistics.linesMap[lineNumber].lines.map((l, i) => ( - + ))} ); @@ -80,51 +82,70 @@ export const LinesValidityList = ({ <> {listTitle}
- + {!sortedLineNumbers || sortedLineNumbers.length === 0 ? ( {selectedValidity === Validity.ALL ? infoText(locale).noLinesFoundInfo - : infoText(locale).foundNot(validityCategoryLabel(locale)[selectedValidity])} + : infoText(locale).foundNot( + validityCategoryLabel(locale)[selectedValidity], + )} ) : ( <> - {sortedLineNumbers.length > 1 && - - } - {sortedLineNumbers.map((lineNumber, index) => ( - <> - toggleLineOpen(lineNumber)} - effectivePeriodsForLineNumber={lineStatistics.linesMap[lineNumber].effectivePeriods as PeriodValidity[]} - lineNumber={lineNumber} - lineNames={lineStatistics.linesMap[lineNumber].lineNames.join(', ')} - key={`LineItem${randomId}${index}`} - numberOfDaysHeader={ - 1 && ( + + )} + {sortedLineNumbers + .filter( + (lineNumber) => + selectedLineType === LineType.ALL || + !lineStatistics.linesMap[lineNumber].lineType || + lineStatistics.linesMap[lineNumber].lineType === + selectedLineType, + ) + .map((lineNumber, index) => ( + <> + toggleLineOpen(lineNumber)} + effectivePeriodsForLineNumber={ + lineStatistics.linesMap[lineNumber] + .effectivePeriods as PeriodValidity[] + } + lineNumber={lineNumber} + lineNames={lineStatistics.linesMap[ + lineNumber + ].lineNames.join(', ')} + key={`LineItem${randomId}${index}`} + numberOfDaysHeader={ + + } + linesValidityListHeader={ + + } + > + - } - linesValidityListHeader={ - - } - > - - - - ))} + + + ))} )}
diff --git a/src/lineStatistics/exportedLineStatisticsForAllProviders.tsx b/src/lineStatistics/exportedLineStatisticsForAllProviders.tsx index bb157cd..768ed03 100644 --- a/src/lineStatistics/exportedLineStatisticsForAllProviders.tsx +++ b/src/lineStatistics/exportedLineStatisticsForAllProviders.tsx @@ -1,20 +1,31 @@ -import React from 'react'; -import { Provider, Validity } from './lineStatistics.types'; +import React, { useState } from 'react'; +import { LineType, Provider, Validity } from './lineStatistics.types'; import style from './lineStatistics.module.scss'; import { useLocale } from '../appContext'; -import { LineStatisticsPerProviderId } from "./apiHooks/lineStatistics.response.types"; -import { Card } from "./components/card/card"; -import { titleText } from "./lineStatistics.constants"; -import { LinesValidityList } from "./components/linesValidity/linesValidityList"; +import { LineStatisticsPerProviderId } from './apiHooks/lineStatistics.response.types'; +import { Card } from './components/card/card'; +import { titleText } from './lineStatistics.constants'; +import { LinesValidityList } from './components/linesValidity/linesValidityList'; +import { LineTypeChips } from './components/linesValidity/linesFilters/lineTypeChips'; interface Props { onClose: () => void; allProviders: Provider[] | undefined; exportedLineStatistics: LineStatisticsPerProviderId | undefined; + defaultSelectedLineType: LineType; } -export const ExportedLineStatisticsForAllProviders = ({ onClose, allProviders, exportedLineStatistics }: Props) => { +export const ExportedLineStatisticsForAllProviders = ({ + onClose, + allProviders, + exportedLineStatistics, + defaultSelectedLineType, +}: Props) => { const locale = useLocale(); + const [selectedLineType, setSelectedLineType] = useState( + defaultSelectedLineType, + ); + return (
<> + {allProviders && - exportedLineStatistics && ( - allProviders - .filter( - (provider) => - Object.keys(exportedLineStatistics).some( - (key) => key === String(provider.id), - ), - ) - .map((provider, index) => ( - - ) - ) - )} + exportedLineStatistics && + allProviders + .filter((provider) => + Object.keys(exportedLineStatistics).some( + (key) => key === String(provider.id), + ), + ) + .map((provider, index) => ( + + ))}
diff --git a/src/lineStatistics/lineStatistics.constants.ts b/src/lineStatistics/lineStatistics.constants.ts index 7fdcd68..b70969a 100644 --- a/src/lineStatistics/lineStatistics.constants.ts +++ b/src/lineStatistics/lineStatistics.constants.ts @@ -1,4 +1,14 @@ -import { Locale, Validity } from './lineStatistics.types'; +import { LineType, Locale, Validity } from './lineStatistics.types'; + +export const lineTypeLabel = (locale?: Locale) => ({ + [LineType.ALL]: textForLocale('Alle', 'All', locale), + [LineType.FIXED]: textForLocale('Faste linjer', 'Fixed lines', locale), + [LineType.FLEXIBLE]: textForLocale( + 'Flexible linjer', + 'Flexible lines', + locale, + ), +}); export const validityCategoryLabel = (locale?: Locale) => ({ [Validity.INVALID]: textForLocale('Utgåtte linjer', 'Expired lines', locale), @@ -52,7 +62,8 @@ export const infoText = (locale?: Locale) => ({ export const titleText = (locale?: Locale) => ({ sortLines: textForLocale('Sorter linjer', 'Sort lines', locale), - selectLines: textForLocale('Velg linjer ', 'Select lines', locale), + selectLines: textForLocale('Velg linjer', 'Select lines', locale), + selectLineTypes: textForLocale('Velg linjetype', 'Select line type', locale), lineStatisticsFromNplan: textForLocale( 'Linjestatus fra NPlan', 'Line statistics from Nplan', diff --git a/src/lineStatistics/lineStatistics.types.ts b/src/lineStatistics/lineStatistics.types.ts index 25a6f56..ebe4de1 100644 --- a/src/lineStatistics/lineStatistics.types.ts +++ b/src/lineStatistics/lineStatistics.types.ts @@ -46,6 +46,7 @@ export interface PublicLine { lineNumber: string; lineNames: string[]; effectivePeriods: Period[] | PeriodValidity[]; + lineType: LineType; lines: Line[]; } @@ -57,3 +58,9 @@ export enum Locale { NO = 'NO', EN = 'EN', } + +export enum LineType { + ALL = 'ALL', + FIXED = 'FIXED', + FLEXIBLE = 'FLEXIBLE', +} diff --git a/src/lineStatistics/lineStatisticsCalculator/exportedLineStatisticsCalculator.ts b/src/lineStatistics/lineStatisticsCalculator/exportedLineStatisticsCalculator.ts index 90e8763..dcc5f94 100644 --- a/src/lineStatistics/lineStatisticsCalculator/exportedLineStatisticsCalculator.ts +++ b/src/lineStatistics/lineStatisticsCalculator/exportedLineStatisticsCalculator.ts @@ -1,10 +1,10 @@ import moment, { Moment } from 'moment'; import { + Line, LineNumbers, LinesMap, LineStatistics, PeriodValidity, - Timetable, Validity, } from '../lineStatistics.types'; import { @@ -21,7 +21,6 @@ import { ExportedLineStatisticsResponse } from '../apiHooks/lineStatistics.respo export const calculateExportedLineStatistics = ( exportedLineStatisticsResponse: ExportedLineStatisticsResponse, ): LineStatistics => { - const startDateLine: Moment = moment( exportedLineStatisticsResponse.startDate, 'YYYY-MM-DD', @@ -86,47 +85,41 @@ export const calculateExportedLineStatistics = ( const daysValid: number = getDaysRange(startDateLine, publicLineValidPeriod) || 0; - - const timetables: Timetable[] = publicLine.lines.flatMap( - (line, lineIndex) => { - return line.exportedDayTypesStatistics.map( - (dayType, dayTypeIndex) => ({ - id: dayTypeIndex, - objectId: dayType.serviceJourneyName - ? `${dayType.dayTypeNetexId} (${dayType.serviceJourneyName})` - : dayType.dayTypeNetexId, - periods: [ - { - to: dayType.operatingPeriodTo, - from: dayType.operatingPeriodFrom, - timelineStartPosition: findTimeLineStartPositionForTimeTable( - dayType.operatingPeriodFrom, - startDateLine, - 180, - ), - timelineEndPosition: findTimeLineEndPositionForTimeTable( - dayType.operatingPeriodTo, - endDateLine, - 180, - ), - }, - ], - }), - ); - }, - ); + const lines: Line[] = publicLine.lines.flatMap((line) => ({ + timetables: line.exportedDayTypesStatistics.map( + (dayType, dayTypeIndex) => ({ + id: dayTypeIndex, + objectId: dayType.serviceJourneyName + ? `${dayType.dayTypeNetexId} (${dayType.serviceJourneyName})` + : dayType.dayTypeNetexId, + periods: [ + { + to: dayType.operatingPeriodTo, + from: dayType.operatingPeriodFrom, + timelineStartPosition: findTimeLineStartPositionForTimeTable( + dayType.operatingPeriodFrom, + startDateLine, + 180, + ), + timelineEndPosition: findTimeLineEndPositionForTimeTable( + dayType.operatingPeriodTo, + endDateLine, + 180, + ), + }, + ], + }), + ), + })); return { [publicLine.publicCode]: { lineNumber: publicLine.publicCode, lineNames: publicLine.lines.map((line) => line.lineName), effectivePeriods: [effectivePeriodFormatted], - lines: [ - { - timetables, - }, - ], + lines: lines, daysValid: daysValid, + lineType: publicLine.lines[0].lineType, }, }; }) diff --git a/src/lineStatistics/lineStatisticsForAllProviders.tsx b/src/lineStatistics/lineStatisticsForAllProviders.tsx index 126fc6e..a44458d 100644 --- a/src/lineStatistics/lineStatisticsForAllProviders.tsx +++ b/src/lineStatistics/lineStatisticsForAllProviders.tsx @@ -1,20 +1,18 @@ import React, { useState } from 'react'; import { useAllProviders } from './apiHooks/useAllProviders'; import { useLineStatisticsForAllProviders } from './apiHooks/useLineStatisticsForAllProviders'; -import { Locale, Provider, Validity } from './lineStatistics.types'; +import { LineType, Locale, Provider, Validity } from './lineStatistics.types'; import { LinesValidity } from './components/linesValidity/linesValidity'; import { PieStatisticsForAllProviders } from './pieStatisticsForAllProviders'; import { useExportedLineStatisticsForAllProviders } from './apiHooks/useExportedLineStatisticsForAllProviders'; -import { - IncompleteLineStatisticsError -} from './components/incompleteLineStatisticsError/incompleteLineStatisticsError'; +import { IncompleteLineStatisticsError } from './components/incompleteLineStatisticsError/incompleteLineStatisticsError'; import { LoadingLineStatistics } from './components/loadingLineStatistics'; import { Card } from './components/card/card'; import style from './lineStatistics.module.scss'; import { useLocale } from '../appContext'; -import { FloatingButton } from "@entur/button"; -import { ExportedLineStatisticsForAllProviders } from "./exportedLineStatisticsForAllProviders"; -import { titleText } from "./lineStatistics.constants"; +import { FloatingButton } from '@entur/button'; +import { ExportedLineStatisticsForAllProviders } from './exportedLineStatisticsForAllProviders'; +import { titleText } from './lineStatistics.constants'; export const LineStatisticsForAllProviders = () => { const locale = useLocale(); @@ -29,7 +27,8 @@ export const LineStatisticsForAllProviders = () => { const [defaultSelectedValidity, setDefaultSelectedValidity] = useState(Validity.ALL); const [selectedProvider, setSelectedProvider] = useState(); - const [showAllExportedLineStatistics, setShowAllExportedLineStatistics] = useState(false); + const [showAllExportedLineStatistics, setShowAllExportedLineStatistics] = + useState(false); const handlePieOnClick = ( selectedValidityCategory: Validity, @@ -48,16 +47,19 @@ export const LineStatisticsForAllProviders = () => { (!allProviders && !allProvidersError) || (!lineStatisticsForAllProviders && !lineStatisticsForAllProvidersError) || (!exportedLineStatisticsForAllProviders && - !exportedLineStatisticsForAllProvidersError); + !exportedLineStatisticsForAllProvidersError); return (
- {showAllExportedLineStatistics ? + {showAllExportedLineStatistics ? ( setShowAllExportedLineStatistics(false)} exportedLineStatistics={exportedLineStatisticsForAllProviders} allProviders={allProviders} - /> : <> + defaultSelectedLineType={LineType.ALL} + /> + ) : ( + <> {selectedProvider ? ( setSelectedProvider(undefined)} @@ -92,34 +94,38 @@ export const LineStatisticsForAllProviders = () => { />
{allProviders && - (lineStatisticsForAllProviders || - exportedLineStatisticsForAllProviders) && ( - <> - {exportedLineStatisticsForAllProviders && - setShowAllExportedLineStatistics(true)} - style={{ margin: "20px" }} - > - {titleText(locale).showAllLinesFromNplan} - - } - - - )} + (lineStatisticsForAllProviders || + exportedLineStatisticsForAllProviders) && ( + <> + {exportedLineStatisticsForAllProviders && ( + setShowAllExportedLineStatistics(true)} + style={{ margin: '20px' }} + > + {titleText(locale).showAllLinesFromNplan} + + )} + + + )}
)} - } + )}
); };