Skip to content

Commit

Permalink
feat: show icon for situation or notice (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
adriansberg authored Nov 10, 2023
1 parent 6ca8716 commit 92c3b2a
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/modules/situations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { SituationOrNoticeIcon } from './situation-or-notice-icon';
export * from './types';
export * from './utils';
30 changes: 30 additions & 0 deletions src/modules/situations/situation-or-notice-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { getIconForMostCriticalSituationOrNotice } from './utils';
import { NoticeFragment, SituationFragment } from './types';
import { ColorIcon } from '@atb/components/icon';

type SituationOrNoticeIconProps = {
accessibilityLabel?: string;
notices?: NoticeFragment[];
cancellation?: boolean;
} & ({ situations: SituationFragment[] } | { situation: SituationFragment });

export const SituationOrNoticeIcon = ({
accessibilityLabel,
notices,
cancellation,
...props
}: SituationOrNoticeIconProps) => {
const situations =
'situation' in props ? [props.situation] : props.situations;

// TODO: It might be needed to check if the transport is flexible and shows a yellow icon (warning)
const icon = getIconForMostCriticalSituationOrNotice(
situations,
notices,
cancellation,
);
if (!icon) return null;

return <ColorIcon icon={icon} alt={accessibilityLabel} />;
};
9 changes: 9 additions & 0 deletions src/modules/situations/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type NoticeFragment = {
id: string;
text: string | null;
};

export type SituationFragment = {
situationNumber: string | null;
reportType: 'general' | 'incident' | null;
};
49 changes: 49 additions & 0 deletions src/modules/situations/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { NoticeFragment, SituationFragment } from './types';
import { Statuses } from '@atb-as/theme';
import { ColorIcons } from '@atb/components/icon';
export const getMessageTypeForSituation = (situation: SituationFragment) =>
situation.reportType === 'incident' ? 'warning' : 'info';

export const getMsgTypeForMostCriticalSituationOrNotice = (
situations: SituationFragment[],
notices?: NoticeFragment[],
cancellation: boolean = false,
): Statuses | undefined => {
if (cancellation) return 'error';
if (!situations.length) {
return notices?.length ? 'info' : undefined;
}
return situations
.map(getMessageTypeForSituation)
.reduce(
(mostCritical, msgType) =>
msgType === 'warning' ? 'warning' : mostCritical,
'info',
);
};

export const getIconForMostCriticalSituationOrNotice = (
situations: SituationFragment[],
notices?: NoticeFragment[],
cancellation: boolean = false,
) => {
const msgType = getMsgTypeForMostCriticalSituationOrNotice(
situations,
notices,
cancellation,
);
return msgType && messageTypeToIcon(msgType);
};

const messageTypeToIcon = (messageType: Statuses): ColorIcons => {
switch (messageType) {
case 'warning':
return 'status/Warning';
case 'error':
return 'status/Error';
case 'valid':
return 'status/Check';
default:
return 'status/Info';
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import style from './trip-pattern-header.module.css';
import { Typo } from '@atb/components/typography';
import { useTranslation, PageText } from '@atb/translations';
import { secondsToDuration } from '@atb/utils/date';
import { flatMap } from 'lodash';
import { getNoticesForLeg } from '@atb/page-modules/assistant/trip/trip-pattern/trip-pattern-header/utils';
import { RailReplacementBusMessage } from './rail-replacement-bus';
import { SituationOrNoticeIcon } from '@atb/modules/situations';

type TripPatternHeaderProps = {
tripPattern: TripPattern;
Expand All @@ -28,6 +32,16 @@ export function TripPatternHeader({ tripPattern }: TripPatternHeaderProps) {
<Typo.span textType="body__secondary" className={style.header__duration}>
{duration}
</Typo.span>

<RailReplacementBusMessage tripPattern={tripPattern} />

<SituationOrNoticeIcon
situations={flatMap(tripPattern.legs, (leg) => leg.situations)}
notices={flatMap(tripPattern.legs, getNoticesForLeg)}
accessibilityLabel={t(
PageText.Assistant.trip.tripPattern.travelFrom(startMode, startPlace),
)}
/>
</header>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { TripPattern } from '../../../server/journey-planner/validators';
import { useTranslation, PageText } from '@atb/translations';
import { ColorIcon } from '@atb/components/icon';

export const RailReplacementBusMessage = ({
tripPattern,
}: {
tripPattern: TripPattern;
}) => {
const { t } = useTranslation();

if (
!tripPattern.legs.some(
(leg) => leg.transportSubmode === 'railReplacementBus',
)
) {
return null;
}

return (
<ColorIcon
icon="status/Warning"
alt={t(
PageText.Assistant.trip.tripPattern.tripIncludesRailReplacementBus,
)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
.header {
display: flex;
justify-content: space-between;
gap: var(--spacings-small);
padding: var(--spacings-small) var(--spacings-medium);
}

.header__duration {
color: var(--text-colors-secondary);
margin-left: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NoticeFragment } from '@atb/modules/situations/types';
import { onlyUniquesBasedOnField } from '@atb/utils/only-uniques';
import { Leg } from '../../../server/journey-planner/validators';

/**
* Filter notices by removing duplicates (by id), removing those without text,
* and also sorting them since the order from Entur may change on each request.
*/
export const filterNotices = (
notices: NoticeFragment[],
): Required<NoticeFragment>[] =>
notices
.filter((n): n is Required<NoticeFragment> => !!n.text)
.filter(onlyUniquesBasedOnField('id'))
.sort((s1, s2) => s1.id.localeCompare(s2.id));

export const getNoticesForLeg = (leg: Leg) =>
filterNotices([
...(leg.line?.notices || []),
...(leg.serviceJourney?.notices || []),
...(leg.serviceJourney?.journeyPattern?.notices || []),
...(leg.fromEstimatedCall?.notices || []),
]);
10 changes: 10 additions & 0 deletions src/translations/pages/assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ export const Assistant = {
}
},
details: _('Detaljer', 'Details', 'Detaljar'),
hasSituationsTip: _(
'Denne reisen har driftsmeldinger. Se detaljer for mer info',
'There are service messages affecting your journey. See details for more info ',
'Denne reisa har driftsmeldingar. Sjå detaljar for meir informasjon.',
),
tripIncludesRailReplacementBus: _(
'Reisen inkluderer buss for tog.',
'This trip includes rail replacement bus.',
'Reisa inkluderer buss for tog.',
),
},
fetchMore: _(
'Last inn flere reiseforslag',
Expand Down
7 changes: 7 additions & 0 deletions src/utils/only-uniques.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const onlyUniques = <T>(value: T, index: number, self: T[]) =>
self.indexOf(value) === index;

export const onlyUniquesBasedOnField =
<T>(field: keyof T) =>
(element: T, index: number, array: T[]) =>
array.findIndex((el) => el[field] === element[field]) === index;

0 comments on commit 92c3b2a

Please sign in to comment.