Skip to content

Commit

Permalink
Added PhotoCapture Tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
dlymonkai committed Jul 25, 2024
1 parent 1c6ede4 commit 6e82ba1
Show file tree
Hide file tree
Showing 31 changed files with 847 additions and 71 deletions.
58 changes: 58 additions & 0 deletions \
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { BackdropDialog, Button } from '@monkvision/common-ui-web';
import { PhotoCaptureHUDTutorial, PhotoCaptureHUDTutorialProps } from '../../../src';
import { expectPropsOnChildMock } from '@monkvision/test-utils';
import { TutorialViews } from '../../../src/PhotoCapture/hooks';

const BACKDROP_TEST_ID = 'backdrop';
const translationPrefix = 'photo.hud.tutorial';

function createProps(): PhotoCaptureHUDTutorialProps {
return {
tutorialView: TutorialViews.WELCOME,
onTutorialNext: jest.fn(),
allowSkipTutorial: false,
enableSightGuidelines: true,
enableSightTutorial: true,
sightGuidelines: [],
sightId: 'test-sight-id',
};
}

describe('PhotoCaptureHUDTutorial component', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should not be shown if tutorialView is null', () => {
const props = createProps();
render(<PhotoCaptureHUDTutorial {...props} tutorialView={null} />);

expect(screen.queryByTestId(BACKDROP_TEST_ID)).toBeNull();
});

it('should be shown if tutorialView is set', () => {
const props = createProps();
render(<PhotoCaptureHUDTutorial {...props} />);

expect(screen.queryByTestId(BACKDROP_TEST_ID)).toBeNull();
});

it('handles the next button click correctly', () => {
const props = createProps();
render(<PhotoCaptureHUDTutorial {...props} />);

const buttonProps = (Button as unknown as jest.Mock).mock.calls[1][0];
buttonProps.onClick();
expect(props.onTutorialNext).toHaveBeenCalledWith(TutorialViews.GUIDELINE);
});

it('should render with the correct props when tutorialView is null', () => {
const props = createProps();
const { unmount } = render(<PhotoCaptureHUDTutorial {...props} />);

expect(Button).toBeCalled();

unmount();
});
});
5 changes: 5 additions & 0 deletions apps/demo-app/src/local-config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"id": "demo-app-dev",
"description": "Config for the dev Demo App.",
"allowSkipRetake": true,
"enableAddDamage": true,
"enableSightGuidelines": true,
Expand All @@ -11,6 +13,9 @@
},
"apiDomain": "api.preview.monk.ai/v1",
"thumbnailDomain": "europe-west1-monk-preview-321715.cloudfunctions.net/image_resize",
"enableTutorial": "enabled",
"allowSkipTutorial": true,
"enableSightTutorial": false,
"startTasksOnComplete": true,
"showCloseButton": false,
"enforceOrientation": "landscape",
Expand Down
3 changes: 3 additions & 0 deletions apps/drive-app/src/local-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
},
"apiDomain": "api.preview.monk.ai/v1",
"thumbnailDomain": "europe-west1-monk-preview-321715.cloudfunctions.net/image_resize",
"enableTutorial": "first_time_only",
"allowSkipTutorial": false,
"enableSightTutorial": false,
"startTasksOnComplete": true,
"showCloseButton": false,
"enforceOrientation": "landscape",
Expand Down
3 changes: 3 additions & 0 deletions apps/lux-demo-app/src/local-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
},
"apiDomain": "api.preview.monk.ai/v1",
"thumbnailDomain": "europe-west1-monk-preview-321715.cloudfunctions.net/image_resize",
"enableTutorial": "enabled",
"allowSkipTutorial": true,
"enableSightTutorial": false,
"startTasksOnComplete": true,
"showCloseButton": false,
"enforceOrientation": "landscape",
Expand Down
3 changes: 3 additions & 0 deletions apps/renault-demo-app/src/local-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
},
"apiDomain": "api.preview.monk.ai/v1",
"thumbnailDomain": "europe-west1-monk-preview-321715.cloudfunctions.net/image_resize",
"enableTutorial": "enabled",
"allowSkipTutorial": true,
"enableSightTutorial": false,
"startTasksOnComplete": true,
"showCloseButton": false,
"enforceOrientation": "landscape",
Expand Down
4 changes: 4 additions & 0 deletions documentation/src/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CompressionFormat,
DeviceOrientation,
MonkApiPermission,
PhotoCaptureTutorialOption,
SteeringWheelPosition,
TaskName,
VehicleType,
Expand Down Expand Up @@ -230,6 +231,9 @@ export const LiveConfigSchema = z
enableAddDamage: z.boolean().optional(),
enableSightGuidelines: z.boolean().optional(),
sightGuidelines: z.array(SightGuidelineSchema).optional(),
enableTutorial: z.nativeEnum(PhotoCaptureTutorialOption).optional(),
allowSkipTutorial: z.boolean().optional(),
enableSightTutorial: z.boolean().optional(),
defaultVehicleType: z.nativeEnum(VehicleType),
allowManualLogin: z.boolean(),
allowVehicleTypeSelection: z.boolean(),
Expand Down
65 changes: 35 additions & 30 deletions packages/inspection-capture-web/README.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ComplianceOptions,
CompressionOptions,
DeviceOrientation,
PhotoCaptureTutorialOption,
Sight,
} from '@monkvision/types';
import { useState } from 'react';
Expand All @@ -35,6 +36,7 @@ import {
useComplianceAnalytics,
usePhotoCaptureImages,
usePhotoCaptureSightState,
usePhotoCaptureTutorial,
usePictureTaken,
useStartTasksOnComplete,
useTracking,
Expand All @@ -61,6 +63,9 @@ export interface PhotoCaptureProps
| 'sightGuidelines'
| 'enableSightGuidelines'
| 'thumbnailDomain'
| 'enableTutorial'
| 'allowSkipTutorial'
| 'enableSightTutorial'
>,
Partial<CompressionOptions>,
Partial<ComplianceOptions> {
Expand Down Expand Up @@ -127,6 +132,9 @@ export function PhotoCapture({
allowSkipRetake = false,
enableAddDamage = true,
sightGuidelines,
enableTutorial = PhotoCaptureTutorialOption.FIRST_TIME_ONLY,
allowSkipTutorial = true,
enableSightTutorial = true,
enableSightGuidelines = true,
useAdaptiveImageQuality = true,
lang,
Expand Down Expand Up @@ -181,6 +189,9 @@ export function PhotoCapture({
complianceOptions,
setIsInitialInspectionFetched,
});
const { tutorialView, handleTutorialView } = usePhotoCaptureTutorial({
enableTutorial,
});
const {
isBadConnectionWarningDialogDisplayed,
closeBadConnectionWarningDialog,
Expand Down Expand Up @@ -259,6 +270,10 @@ export function PhotoCapture({
enableAddDamage,
sightGuidelines,
enableSightGuidelines,
tutorialView,
onTutorialNext: handleTutorialView,
allowSkipTutorial,
enableSightTutorial,
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ import { LoadingState } from '@monkvision/common';
import { useAnalytics } from '@monkvision/analytics';
import { PhotoCaptureHUDButtons } from './PhotoCaptureHUDButtons';
import { usePhotoCaptureHUDStyle } from './hooks';
import { PhotoCaptureMode } from '../hooks';
import { PhotoCaptureMode, TutorialViews } from '../hooks';
import { PhotoCaptureHUDOverlay } from './PhotoCaptureHUDOverlay';
import { PhotoCaptureHUDElements } from './PhotoCaptureHUDElements';
import { PhotoCaptureHUDTutorial } from './PhotoCaptureHUDTutorial';

/**
* Props of the PhotoCaptureHUD component.
*/
export interface PhotoCaptureHUDProps
extends CameraHUDProps,
Pick<CaptureAppConfig, 'enableSightGuidelines' | 'sightGuidelines' | 'enableAddDamage'> {
Pick<
CaptureAppConfig,
| 'enableSightGuidelines'
| 'sightGuidelines'
| 'enableAddDamage'
| 'showCloseButton'
| 'allowSkipTutorial'
| 'enableSightTutorial'
> {
/**
* The inspection ID.
*/
Expand Down Expand Up @@ -45,6 +54,14 @@ export interface PhotoCaptureHUDProps
* Global loading state of the PhotoCapture component.
*/
loading: LoadingState;
/**
* The current tutorial view in PhotoCapture component.
*/
tutorialView: TutorialViews | null;
/**
* Callback called when the user clicks on "Next" button.
*/
onTutorialNext: (view: TutorialViews | null) => void;
/**
* Callback called when the user manually select a new sight.
*/
Expand Down Expand Up @@ -74,12 +91,6 @@ export interface PhotoCaptureHUDProps
* displayed.
*/
onClose?: () => void;
/**
* Boolean indicating if the close button should be displayed in the HUD on top of the Camera preview.
*
* @default false
*/
showCloseButton?: boolean;
/**
* The current images taken by the user (ignoring retaken pictures etc.).
*/
Expand Down Expand Up @@ -113,6 +124,10 @@ export function PhotoCaptureHUD({
enableAddDamage,
sightGuidelines,
enableSightGuidelines,
tutorialView,
allowSkipTutorial,
onTutorialNext,
enableSightTutorial,
}: PhotoCaptureHUDProps) {
const { t } = useTranslation();
const [showCloseModal, setShowCloseModal] = useState(false);
Expand Down Expand Up @@ -154,6 +169,8 @@ export function PhotoCaptureHUD({
enableAddDamage={enableAddDamage}
sightGuidelines={sightGuidelines}
enableSightGuidelines={enableSightGuidelines}
tutorialView={tutorialView}
onTutorialNext={onTutorialNext}
/>
</div>
<PhotoCaptureHUDButtons
Expand Down Expand Up @@ -185,6 +202,15 @@ export function PhotoCaptureHUD({
onCancel={() => setShowCloseModal(false)}
onConfirm={handleCloseConfirm}
/>
<PhotoCaptureHUDTutorial
tutorialView={handle.error || loading.error ? null : tutorialView}
onTutorialNext={onTutorialNext}
enableSightGuidelines={enableSightGuidelines}
allowSkipTutorial={allowSkipTutorial}
enableSightTutorial={enableSightTutorial}
sightId={selectedSight.id}
sightGuidelines={sightGuidelines}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CaptureAppConfig, Image, PixelDimensions, Sight } from '@monkvision/types';
import { PhotoCaptureMode } from '../../hooks';
import { PhotoCaptureMode, TutorialViews } from '../../hooks';
import { PhotoCaptureHUDElementsSight } from '../PhotoCaptureHUDElementsSight';
import { PhotoCaptureHUDElementsAddDamage1stShot } from '../PhotoCaptureHUDElementsAddDamage1stShot';
import { PhotoCaptureHUDElementsAddDamage2ndShot } from '../PhotoCaptureHUDElementsAddDamage2ndShot';
Expand All @@ -25,6 +25,14 @@ export interface PhotoCaptureHUDElementsProps
* The current mode of the component.
*/
mode: PhotoCaptureMode;
/**
* The current tutorial view in PhotoCapture component.
*/
tutorialView: TutorialViews | null;
/**
* Callback called when the user clicks on "Next" button.
*/
onTutorialNext: (view: TutorialViews | null) => void;
/**
* Callback called when the user presses the Add Damage button.
*/
Expand Down Expand Up @@ -80,6 +88,7 @@ export function PhotoCaptureHUDElements(params: PhotoCaptureHUDElementsProps) {
enableAddDamage={params.enableAddDamage}
sightGuidelines={params.sightGuidelines}
enableSightGuidelines={params.enableSightGuidelines}
tutorialView={params.tutorialView}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const styles: Styles = {
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
zIndex: 9,
},
elementsContainerPortrait: {
__media: { portrait: true },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { styles } from './PhotoCaptureHUDElementsSight.styles';
import { AddDamageButton } from './AddDamageButton';
import { PhotoCaptureHUDElementsSightProps, usePhotoCaptureHUDSightPreviewStyle } from './hooks';
import { PhotoCaptureHUDCounter } from '../PhotoCaptureHUDCounter';
import { PhotoCaptureMode } from '../../hooks';
import { PhotoCaptureMode, TutorialViews } from '../../hooks';
import { SightGuideline } from './SightGuideline';

/**
Expand All @@ -23,38 +23,43 @@ export function PhotoCaptureHUDElementsSight({
enableAddDamage,
sightGuidelines,
enableSightGuidelines,
tutorialView,
}: PhotoCaptureHUDElementsSightProps) {
const style = usePhotoCaptureHUDSightPreviewStyle({ previewDimensions });

const showSight = previewDimensions && (!tutorialView || tutorialView === TutorialViews.SIGHT);

return (
<div style={styles['container']}>
{previewDimensions && <SightOverlay style={style.overlay} sight={selectedSight} />}
<div style={style.elementsContainer}>
<div style={style.top}>
<SightGuideline
sightId={selectedSight.id}
sightGuidelines={sightGuidelines}
enableSightGuidelines={enableSightGuidelines}
enableAddDamage={enableAddDamage}
/>
<AddDamageButton onAddDamage={onAddDamage} enableAddDamage={enableAddDamage} />
</div>
<div style={style.bottom}>
<PhotoCaptureHUDCounter
mode={PhotoCaptureMode.SIGHT}
totalSights={sights.length}
sightsTaken={sightsTaken.length}
/>
<SightSlider
sights={sights}
selectedSight={selectedSight}
sightsTaken={sightsTaken}
onSelectedSight={onSelectedSight}
onRetakeSight={onRetakeSight}
images={images}
/>
{showSight && <SightOverlay style={style.overlay} sight={selectedSight} />}
{!tutorialView && (
<div style={style.elementsContainer}>
<div style={style.top}>
<SightGuideline
sightId={selectedSight.id}
sightGuidelines={sightGuidelines}
enableSightGuidelines={enableSightGuidelines}
enableAddDamage={enableAddDamage}
/>
<AddDamageButton onAddDamage={onAddDamage} enableAddDamage={enableAddDamage} />
</div>
<div style={style.bottom}>
<PhotoCaptureHUDCounter
mode={PhotoCaptureMode.SIGHT}
totalSights={sights.length}
sightsTaken={sightsTaken.length}
/>
<SightSlider
sights={sights}
selectedSight={selectedSight}
sightsTaken={sightsTaken}
onSelectedSight={onSelectedSight}
onRetakeSight={onRetakeSight}
images={images}
/>
</div>
</div>
</div>
)}
</div>
);
}
Loading

0 comments on commit 6e82ba1

Please sign in to comment.