Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed patch in vehicleTypeSelection #798

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import { useMonkAppState } from '@monkvision/common';
import { Page } from '../pages';

export function VehicleTypeSelectionPage() {
const { config, vehicleType, setVehicleType } = useMonkAppState();
const { config, vehicleType, authToken, inspectionId, setVehicleType } = useMonkAppState();
const { i18n } = useTranslation();

if (vehicleType || !config.allowVehicleTypeSelection) {
return <Navigate to={Page.PHOTO_CAPTURE} replace />;
}

return <VehicleTypeSelection onSelectVehicleType={setVehicleType} lang={i18n.language} />;
return (
<VehicleTypeSelection
onSelectVehicleType={setVehicleType}
lang={i18n.language}
inspectionId={inspectionId ?? ''}
authToken={authToken ?? ''}
apiDomain={config.apiDomain}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import { useMonkAppState } from '@monkvision/common';
import { Page } from '../pages';

export function VehicleTypeSelectionPage() {
const { config, vehicleType, setVehicleType } = useMonkAppState();
const { config, vehicleType, authToken, inspectionId, setVehicleType } = useMonkAppState();
const { i18n } = useTranslation();

if (vehicleType || !config.allowVehicleTypeSelection) {
return <Navigate to={Page.PHOTO_CAPTURE} replace />;
}

return <VehicleTypeSelection onSelectVehicleType={setVehicleType} lang={i18n.language} />;
return (
<VehicleTypeSelection
onSelectVehicleType={setVehicleType}
lang={i18n.language}
inspectionId={inspectionId ?? ''}
authToken={authToken ?? ''}
apiDomain={config.apiDomain}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import { useMonkAppState } from '@monkvision/common';
import { Page } from '../pages';

export function VehicleTypeSelectionPage() {
const { config, vehicleType, setVehicleType } = useMonkAppState();
const { config, vehicleType, authToken, inspectionId, setVehicleType } = useMonkAppState();
const { i18n } = useTranslation();

if (vehicleType || !config.allowVehicleTypeSelection) {
return <Navigate to={Page.PHOTO_CAPTURE} replace />;
}

return <VehicleTypeSelection onSelectVehicleType={setVehicleType} lang={i18n.language} />;
return (
<VehicleTypeSelection
onSelectVehicleType={setVehicleType}
lang={i18n.language}
inspectionId={inspectionId ?? ''}
authToken={authToken ?? ''}
apiDomain={config.apiDomain}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import { useMonkAppState } from '@monkvision/common';
import { Page } from '../pages';

export function VehicleTypeSelectionPage() {
const { config, vehicleType, setVehicleType } = useMonkAppState();
const { config, vehicleType, authToken, inspectionId, setVehicleType } = useMonkAppState();
const { i18n } = useTranslation();

if (vehicleType || !config.allowVehicleTypeSelection) {
return <Navigate to={Page.PHOTO_CAPTURE} replace />;
}

return <VehicleTypeSelection onSelectVehicleType={setVehicleType} lang={i18n.language} />;
return (
<VehicleTypeSelection
onSelectVehicleType={setVehicleType}
lang={i18n.language}
inspectionId={inspectionId ?? ''}
authToken={authToken ?? ''}
apiDomain={config.apiDomain}
/>
);
}
4 changes: 3 additions & 1 deletion packages/common-ui-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,6 @@ function VehicleSelectionPage() {
| availableVehicleTypes | VehicleType[] | A list of available vehicle type to choose from. The order of the list will be modified to always follow the same order. | | `[SUV, CUV, SEDAN, HATCHBACK, VAN, MINIVAN, PICKUP]` |
| onSelectVehicleType | (type: VehicleType) => void | Callback called when the user has selected a vehicle type. | | The center-most vehicle type in the list. |
| lang | string | The language to use by the component. | | `en` |
| patchInspection | boolean | Boolean indicating if the patch vehicle inspection feature is enabled. | | `true` |
| inspectionId | string | The ID of the inspection. | | |
| apiDomain | string | The domain of the Monk API. | | |
| authToken | string | The authentication token used to communicate with the API. | | |
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,39 @@ import {
i18nWrap,
useI18nSync,
useLoadingState,
useMonkAppState,
useMonkTheme,
useResponsiveStyle,
} from '@monkvision/common';
import { VehicleType } from '@monkvision/types';
import { AllOrNone, VehicleType } from '@monkvision/types';
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
import { decodeMonkJwt, useMonkApi } from '@monkvision/network';
import { decodeMonkJwt, MonkApiConfig, useMonkApi } from '@monkvision/network';
import { useMonitoring } from '@monkvision/monitoring';
import { useAnalytics } from '@monkvision/analytics';
import { styles } from './VehicleTypeSelection.styles';
import { i18nVehicleTypeSelection } from './i18n';
import { Button } from '../Button';
import { getInitialSelectedVehicleType, getVehicleTypes } from './utils';
import {
getInitialSelectedVehicleType,
getVehicleTypeFromInspection,
getVehicleTypes,
} from './utils';
import { VehicleTypeSelectionCard } from './VehicleTypeSelectionCard';
import { Spinner } from '../Spinner';

/**
* Props used to check if a vehicle type is already defined before displaying vehicle type selection.
*/
export interface MonkApiProps extends MonkApiConfig {
/**
* The ID of the inspection.
*/
inspectionId: string;
}

/**
* Props accepted by the VehicleTypeSelection component.
*/
export interface VehicleTypeSelectionProps {
export type VehicleTypeSelectionProps = {
/**
* The initially selected vehicle type.
*
Expand All @@ -46,16 +59,7 @@ export interface VehicleTypeSelectionProps {
* @default en
*/
lang?: string;
/**
* Boolean indicating if the patch vehicle inspection feature is enabled. If true, it will trigger 2 actions:
* - At the start, it will check if a vehicle type is defined. If so, it will immediately
* call the 'onSelectVehicleType' callback.
* - When the 'confirm button' is pressed by the user, it will make a request to the API to PATCH the vehicle type of the inspection.
*
* @default true
*/
patchInspection?: boolean;
}
} & AllOrNone<MonkApiProps>;

function scrollToSelectedVehicleType(
ref: RefObject<HTMLDivElement>,
Expand All @@ -73,126 +77,105 @@ function scrollToSelectedVehicleType(
/**
* A single page component that allows the user to select a vehicle type.
*/
export const VehicleTypeSelection = i18nWrap(
({
availableVehicleTypes,
selectedVehicleType,
onSelectVehicleType,
lang,
patchInspection = true,
}: VehicleTypeSelectionProps) => {
useI18nSync(lang);
const { config, authToken, inspectionId } = useMonkAppState();
const { updateInspectionVehicle, getInspection } = useMonkApi({
authToken: authToken ?? '',
apiDomain: config.apiDomain,
});
const loading = useLoadingState(true);
const { handleError, setTags, setUserId } = useMonitoring();
const analytics = useAnalytics();
const [initialScroll, setInitialScroll] = useState(true);
const vehicleTypes = useMemo(
() => getVehicleTypes(availableVehicleTypes),
[availableVehicleTypes],
);
const [selected, setSelected] = useState(
getInitialSelectedVehicleType(vehicleTypes, selectedVehicleType),
);
const { t } = useTranslation();
const { rootStyles } = useMonkTheme();
const sliderRef = useRef<HTMLDivElement>(null);
const { responsive } = useResponsiveStyle();
export const VehicleTypeSelection = i18nWrap((props: VehicleTypeSelectionProps) => {
useI18nSync(props.lang);
const { getInspection } = useMonkApi({
authToken: props.authToken ?? '',
apiDomain: props.apiDomain ?? '',
});
const loading = useLoadingState(true);
const { handleError, setTags, setUserId } = useMonitoring();
const analytics = useAnalytics();
const [initialScroll, setInitialScroll] = useState(true);
const vehicleTypes = useMemo(
() => getVehicleTypes(props.availableVehicleTypes),
[props.availableVehicleTypes],
);
const [selected, setSelected] = useState(
getInitialSelectedVehicleType(vehicleTypes, props.selectedVehicleType),
);
const { t } = useTranslation();
const { rootStyles } = useMonkTheme();
const sliderRef = useRef<HTMLDivElement>(null);
const { responsive } = useResponsiveStyle();

const onValidate = (type: VehicleType) => {
if (patchInspection && inspectionId) {
updateInspectionVehicle({ inspectionId, vehicle: { type } });
}
onSelectVehicleType?.(type);
};
useEffect(() => {
if (props.inspectionId) {
setTags({ inspectionId: props.inspectionId });
analytics.setUserId(props.inspectionId);
}
const userId = props.authToken ? decodeMonkJwt(props.authToken) : undefined;
if (userId?.sub) {
setUserId(userId.sub);
analytics.setUserProperties({ authToken: userId.sub });
}
}, [props.inspectionId, props.authToken, analytics, setTags, setUserId]);

useEffect(() => {
if (inspectionId) {
setTags({ inspectionId });
analytics.setUserId(inspectionId);
useEffect(() => {
const fetchInspection = async () => {
if (!props.inspectionId) {
loading.onSuccess();
return;
}
const userId = authToken ? decodeMonkJwt(authToken) : undefined;
if (userId?.sub) {
setUserId(userId.sub);
analytics.setUserProperties({ authToken: userId.sub });
loading.start();
const fetchedInspection = await getInspection({
id: props.inspectionId,
});
const vehicleType = getVehicleTypeFromInspection(fetchedInspection);
if (vehicleType && props.availableVehicleTypes?.includes(vehicleType)) {
props.onSelectVehicleType?.(vehicleType);
}
}, [inspectionId, authToken, analytics, setTags, setUserId]);

useEffect(() => {
const fetchInspection = async () => {
if (patchInspection && inspectionId) {
const fetchedInspection = await getInspection({
id: inspectionId,
});
loading.onSuccess();
};

const vehicle = fetchedInspection.entities.vehicles.find(
(v) => v.inspectionId === inspectionId,
);
const vehicleTypeFoundInInspection = Object.values(VehicleType).find(
(vehicleType) => vehicleType === vehicle?.type,
);
if (vehicleTypeFoundInInspection) {
onSelectVehicleType?.(vehicleTypeFoundInInspection);
}
}
loading.onSuccess();
};
fetchInspection().catch(handleError);
}, [props.inspectionId]);

loading.start();
fetchInspection().catch(handleError);
}, [patchInspection, inspectionId]);

useEffect(() => {
const index = vehicleTypes.indexOf(selected);
if (index >= 0 && !loading.isLoading) {
scrollToSelectedVehicleType(sliderRef, index, !initialScroll);
setInitialScroll(false);
}
}, [vehicleTypes, selected, loading]);
useEffect(() => {
const index = vehicleTypes.indexOf(selected);
if (index >= 0 && !loading.isLoading) {
scrollToSelectedVehicleType(sliderRef, index, !initialScroll);
setInitialScroll(false);
}
}, [vehicleTypes, selected, loading]);

const loadingContainer = loading.isLoading ? styles['loadingContainer'] : {};
const loadingContainer = loading.isLoading ? styles['loadingContainer'] : {};

return (
<div
style={{
...rootStyles,
...styles['container'],
...loadingContainer,
...responsive(styles['containerSmall']),
}}
>
{loading.isLoading && <Spinner size={80} />}
{!loading.isLoading && !loading.error && (
<>
<div style={styles['title']}>{t('header.title')}</div>
<Button style={styles['button']} onClick={() => onValidate(selected)}>
{t('header.confirm')}
</Button>
<div
style={{
...styles['sliderContainer'],
...responsive(styles['sliderContainerSmall']),
}}
>
<div style={styles['slider']} ref={sliderRef}>
{vehicleTypes.map((v) => (
<VehicleTypeSelectionCard
key={v}
vehicleType={v}
isSelected={selected === v}
onClick={() => setSelected(v)}
/>
))}
</div>
return (
<div
style={{
...rootStyles,
...styles['container'],
...loadingContainer,
...responsive(styles['containerSmall']),
}}
>
{loading.isLoading && <Spinner size={80} />}
{!loading.isLoading && !loading.error && (
<>
<div style={styles['title']}>{t('header.title')}</div>
<Button style={styles['button']} onClick={() => props.onSelectVehicleType?.(selected)}>
{t('header.confirm')}
</Button>
<div
style={{
...styles['sliderContainer'],
...responsive(styles['sliderContainerSmall']),
}}
>
<div style={styles['slider']} ref={sliderRef}>
{vehicleTypes.map((v) => (
<VehicleTypeSelectionCard
key={v}
vehicleType={v}
isSelected={selected === v}
onClick={() => setSelected(v)}
/>
))}
</div>
</>
)}
</div>
);
},
i18nVehicleTypeSelection,
);
</div>
</>
)}
</div>
);
}, i18nVehicleTypeSelection);
Loading