From 9af8a3fbb29bbb4e40761fd238d2ca61ce145cbd Mon Sep 17 00:00:00 2001 From: David Ly Date: Wed, 3 Jul 2024 18:38:51 +0200 Subject: [PATCH] Added sight guidelines --- apps/demo-app/src/local-config.json | 19 +++++- .../src/__mocks__/@monkvision/common.tsx | 1 + documentation/src/utils/schemas.ts | 10 +++ .../src/components/Button/Button.tsx | 14 +++-- .../src/components/Button/hooks.ts | 19 ++++-- .../common/src/hooks/useObjectTranslation.ts | 6 +- packages/common/src/i18n/utils.tsx | 15 ++++- packages/inspection-capture-web/README.md | 2 + .../src/PhotoCapture/PhotoCapture.tsx | 6 ++ .../PhotoCaptureHUD/PhotoCaptureHUD.tsx | 16 ++--- .../PhotoCaptureHUDButtons.styles.ts | 1 - .../PhotoCaptureHUDElements.tsx | 13 ++-- .../AddDamageButton/AddDamageButton.tsx | 27 ++++---- .../PhotoCaptureHUDElementsSight.styles.ts | 7 +-- .../PhotoCaptureHUDElementsSight.tsx | 11 +++- .../SightGuideline/SightGuideline.styles.ts | 26 ++++++++ .../SightGuideline/SightGuideline.tsx | 59 ++++++++++++++++++ .../SightGuideline/index.ts | 1 + .../PhotoCaptureHUDElementsSight/hooks.ts | 18 +++--- .../test/PhotoCapture/PhotoCapture.test.tsx | 12 ++++ .../AddDamageButton.test.tsx | 27 ++++---- .../PhotoCaptureHUDElementsSight.test.tsx | 4 -- .../SightGuideline.test.tsx | 62 +++++++++++++++++++ packages/types/src/config.ts | 12 +++- packages/types/src/sights.ts | 26 ++++++++ 25 files changed, 334 insertions(+), 80 deletions(-) create mode 100644 packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.styles.ts create mode 100644 packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.tsx create mode 100644 packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/index.ts create mode 100644 packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline.test.tsx diff --git a/apps/demo-app/src/local-config.json b/apps/demo-app/src/local-config.json index 37f48dcab..6115966e3 100644 --- a/apps/demo-app/src/local-config.json +++ b/apps/demo-app/src/local-config.json @@ -1,6 +1,7 @@ { "allowSkipRetake": true, - "enableAddDamage": true, + "enableAddDamage": false, + "enableSightGuideline": true, "allowVehicleTypeSelection": true, "allowManualLogin": true, "fetchFromSearchParams": true, @@ -183,6 +184,22 @@ "ff150-7nvlys8r" ] }, + "sightGuidelines": [ + { + "en": "Stand centred in front of the vehicle", + "fr": "Placez-vous centré devant le véhicule", + "de": "Stellen Sie sich zentriert vor das Fahrzeug", + "nl": "Plaats jezelf in het midden voor het voertuig", + "sightIds": ["jgc21-QIvfeg0X", "ff150-zXbg0l3z"] + }, + { + "en": "Stand centred in front of the vehicle, aligned with the front grille. Center the vehicle in the frame, ensuring the entire hood, windshield and side mirrors are visible. Include the top of the vehicle in the shot.", + "fr": "Placez-vous au centré devant le véhicule, aligné avec la calandre avant. Centrez le véhicule dans le cadre, en veillant à ce que l'ensemble du capot, du pare-brise et des rétroviseurs soient visibles. Incluez le haut du véhicule dans la prise de vue.", + "de": "Stellen Sie sich zentriert vor das Fahrzeug, ausgerichtet an der Frontstoßstange. Zentrieren Sie das Fahrzeug im Rahmen, achten Sie darauf, dass die gesamte Motorhaube, die Windschutzscheibe und die Seitenspiegel sichtbar sind. Nehmen Sie die Oberseite des Fahrzeugs in den Schuss auf.", + "nl": "Plaats jezelf in het midden voor het voertuig, uitgelijnd met de voorgril. Centreren Sie het voertuig in de beeldframe, zorg ervoor dat de hele motorkap, de voorruit en de zijspiegels zichtbaar zijn. Neem de bovenkant van het voertuig op in de opname.", + "sightIds": ["jgc21-KyUUVU2P", "ffocus18-XlfgjQb9 ", "ff150-3he9UOwy"] + } + ], "requiredApiPermissions": [ "monk_core_api:compliances", "monk_core_api:damage_detection", diff --git a/configs/test-utils/src/__mocks__/@monkvision/common.tsx b/configs/test-utils/src/__mocks__/@monkvision/common.tsx index 11ad3eb7a..6bdcc168b 100644 --- a/configs/test-utils/src/__mocks__/@monkvision/common.tsx +++ b/configs/test-utils/src/__mocks__/@monkvision/common.tsx @@ -119,4 +119,5 @@ export = { useSearchParams: jest.fn(() => ({ get: jest.fn(() => null) })), useObjectMemo: jest.fn((obj) => obj), usePreventExit: jest.fn(), + getLanguage: jest.fn(), }; diff --git a/documentation/src/utils/schemas.ts b/documentation/src/utils/schemas.ts index c8a4e6748..5feb5f058 100644 --- a/documentation/src/utils/schemas.ts +++ b/documentation/src/utils/schemas.ts @@ -92,6 +92,14 @@ export const ComplianceOptionsSchema = z.object({ .refine(validateSightIds, getInvalidSightIdsMessage), }); +export const SightGuidelineSchema = z.object({ + sightIds: z.array(z.string()), + en: z.string(), + fr: z.string(), + de: z.string(), + nl: z.string(), +}); + export const AccentColorVariantsSchema = z.object({ xdark: z.string(), dark: z.string(), @@ -220,6 +228,8 @@ export const LiveConfigSchema = z useAdaptiveImageQuality: z.boolean().optional(), allowSkipRetake: z.boolean().optional(), enableAddDamage: z.boolean().optional(), + enableSightGuideline: z.boolean().optional(), + sightGuidelines: z.array(SightGuidelineSchema), defaultVehicleType: z.nativeEnum(VehicleType), allowManualLogin: z.boolean(), allowVehicleTypeSelection: z.boolean(), diff --git a/packages/common-ui-web/src/components/Button/Button.tsx b/packages/common-ui-web/src/components/Button/Button.tsx index 1eec10ff8..91ac8a3fa 100644 --- a/packages/common-ui-web/src/components/Button/Button.tsx +++ b/packages/common-ui-web/src/components/Button/Button.tsx @@ -72,12 +72,14 @@ export const Button = forwardRef ( <> {icon && ( - +
+ +
)} {children} diff --git a/packages/common-ui-web/src/components/Button/hooks.ts b/packages/common-ui-web/src/components/Button/hooks.ts index 7c3f34381..b00f2451a 100644 --- a/packages/common-ui-web/src/components/Button/hooks.ts +++ b/packages/common-ui-web/src/components/Button/hooks.ts @@ -156,6 +156,12 @@ export function useButtonStyle(params: MonkButtonStyleParams): MonkButtonStyle { const contentSize = params.size === 'normal' ? BUTTON_CONTENT_SIZE_NORMAL : BUTTON_CONTENT_SIZE_SMALL; + const iconStyle = { + ...styles['icon'], + ...(params.size === 'small' ? styles['iconSmall'] : {}), + ...(!params.hasChildren ? styles['iconOnly'] : {}), + }; + return { style: { ...styles['button'], @@ -172,14 +178,17 @@ export function useButtonStyle(params: MonkButtonStyleParams): MonkButtonStyle { backgroundColor, }, iconStyle: { - style: { - ...styles['icon'], - ...(params.size === 'small' ? styles['iconSmall'] : {}), - ...(!params.hasChildren ? styles['iconOnly'] : {}), - }, + style: iconStyle, color: foregroundColor, size: contentSize, }, + iconContainerStyle: { + style: { + width: contentSize, + height: contentSize, + ...iconStyle, + }, + }, spinnerStyle: { style: { top: `calc(50% - ${contentSize / 2}px)`, diff --git a/packages/common/src/hooks/useObjectTranslation.ts b/packages/common/src/hooks/useObjectTranslation.ts index 4b50a050e..d4af16429 100644 --- a/packages/common/src/hooks/useObjectTranslation.ts +++ b/packages/common/src/hooks/useObjectTranslation.ts @@ -1,6 +1,7 @@ -import { MonkLanguage, TranslationObject } from '@monkvision/types'; +import { TranslationObject } from '@monkvision/types'; import { useTranslation } from 'react-i18next'; import { useCallback } from 'react'; +import { getLanguage } from '../i18n'; /** * The result of the useObjectTranslation. It contains a function which takes a LabelTranslation object and return the @@ -20,8 +21,7 @@ export function useObjectTranslation(): UseObjectTranslationResult { const { i18n } = useTranslation(); const tObj = useCallback( (obj: TranslationObject) => { - const lang = i18n.language.slice(0, 2) as MonkLanguage; - return obj[lang] ?? 'translation-not-found'; + return obj[getLanguage(i18n.language)] ?? 'translation-not-found'; }, [i18n.language], ); diff --git a/packages/common/src/i18n/utils.tsx b/packages/common/src/i18n/utils.tsx index 233f8def0..67d10cf0d 100644 --- a/packages/common/src/i18n/utils.tsx +++ b/packages/common/src/i18n/utils.tsx @@ -9,7 +9,7 @@ import { useEffect, } from 'react'; import { I18nextProvider, initReactI18next, useTranslation } from 'react-i18next'; -import { monkLanguages } from '@monkvision/types'; +import { MonkLanguage, monkLanguages } from '@monkvision/types'; /** * This custom hook automatically updates the current i18n instance's language with the given language if is it not null @@ -88,3 +88,16 @@ export function i18nWrap( )); } + +/** + * This function retrieves the language prefix from a given language string. + * If the prefix is not found in the list of supported languages (monkLanguages in Types package), defaults to 'en'. + * + * @param language - The full language string (e.g., 'en-US', 'fr-CA'). + * @returns The language prefix if it is supported; otherwise, 'en'. + * + */ +export function getLanguage(language: string): MonkLanguage { + const languagePrefix = language.slice(0, 2) as MonkLanguage; + return monkLanguages.includes(languagePrefix) ? languagePrefix : 'en'; +} diff --git a/packages/inspection-capture-web/README.md b/packages/inspection-capture-web/README.md index 585ce2ac0..3cd110e52 100644 --- a/packages/inspection-capture-web/README.md +++ b/packages/inspection-capture-web/README.md @@ -88,3 +88,5 @@ export function MonkPhotoCapturePage({ authToken }) { | customComplianceThresholds | `CustomComplianceThresholds` | Custom thresholds that can be used to modify the strictness of the compliance for certain compliance issues. | | | | customComplianceThresholdsPerSight | `Record` | A map associating Sight IDs to custom compliance thresholds. | | | | validateButtonLabel | `string` | Custom label for validate button in gallery view. | | | +| enableSightGuideline | `boolean` | Boolean indicating whether the sight guideline feature is enabled. If disabled, the guideline text will be hidden. | | `true` | +| sightGuidelines | `sightGuideline[]` | A collection of sight guidelines in different language with a list of sightIds associate to it. | | | diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx index 2c96b58a3..db872d540 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx @@ -57,6 +57,8 @@ export interface PhotoCaptureProps | 'enforceOrientation' | 'allowSkipRetake' | 'enableAddDamage' + | 'sightGuidelines' + | 'enableSightGuidelines' >, Partial, Partial { @@ -122,6 +124,8 @@ export function PhotoCapture({ useLiveCompliance = false, allowSkipRetake = false, enableAddDamage = true, + sightGuidelines, + enableSightGuidelines = true, useAdaptiveImageQuality = true, lang, enforceOrientation, @@ -249,6 +253,8 @@ export function PhotoCapture({ showCloseButton, images, enableAddDamage, + sightGuidelines, + enableSightGuidelines, }; return ( diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx index d659ca8ec..20ab2e12f 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx @@ -1,5 +1,5 @@ import { useMemo, useState } from 'react'; -import { Image, ImageStatus, Sight } from '@monkvision/types'; +import { CaptureAppConfig, Image, ImageStatus, Sight } from '@monkvision/types'; import { useTranslation } from 'react-i18next'; import { BackdropDialog } from '@monkvision/common-ui-web'; import { CameraHUDProps } from '@monkvision/camera-web'; @@ -14,7 +14,9 @@ import { PhotoCaptureHUDElements } from './PhotoCaptureHUDElements'; /** * Props of the PhotoCaptureHUD component. */ -export interface PhotoCaptureHUDProps extends CameraHUDProps { +export interface PhotoCaptureHUDProps + extends CameraHUDProps, + Pick { /** * The inspection ID. */ @@ -82,12 +84,6 @@ export interface PhotoCaptureHUDProps extends CameraHUDProps { * The current images taken by the user (ignoring retaken pictures etc.). */ images: Image[]; - /** - * Boolean indicating if `Add Damage` feature should be enabled or not. If disabled, the `Add Damage` button will be hidden. - * - * @default true - */ - enableAddDamage?: boolean; } /** @@ -115,6 +111,8 @@ export function PhotoCaptureHUD({ cameraPreview, images, enableAddDamage, + sightGuidelines, + enableSightGuidelines, }: PhotoCaptureHUDProps) { const { t } = useTranslation(); const [showCloseModal, setShowCloseModal] = useState(false); @@ -154,6 +152,8 @@ export function PhotoCaptureHUD({ previewDimensions={handle.previewDimensions} images={images} enableAddDamage={enableAddDamage} + sightGuidelines={sightGuidelines} + enableSightGuidelines={enableSightGuidelines} /> { /** * The currently selected sight in the PhotoCapture component : the sight that the user needs to capture. */ @@ -56,12 +57,6 @@ export interface PhotoCaptureHUDElementsProps { * The current images taken by the user (ignoring retaken pictures etc.). */ images: Image[]; - /** - * Boolean indicating if `Add Damage` feature should be enabled or not. If disabled, the `Add Damage` button will be hidden. - * - * @default true - */ - enableAddDamage?: boolean; } /** @@ -83,6 +78,8 @@ export function PhotoCaptureHUDElements(params: PhotoCaptureHUDElementsProps) { previewDimensions={params.previewDimensions} images={params.images} enableAddDamage={params.enableAddDamage} + sightGuidelines={params.sightGuidelines} + enableSightGuidelines={params.enableSightGuidelines} /> ); } diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton/AddDamageButton.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton/AddDamageButton.tsx index 4cd29d2b7..44e5dbf65 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton/AddDamageButton.tsx +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton/AddDamageButton.tsx @@ -1,5 +1,4 @@ import { Button } from '@monkvision/common-ui-web'; -import { CSSProperties } from 'react'; import { useTranslation } from 'react-i18next'; import { usePhotoCaptureHUDButtonBackground } from '../../hooks'; @@ -19,10 +18,6 @@ export interface AddDamageButtonProps { enableAddDamage?: boolean; } -function getButtonStyle(enableAddDamage?: boolean): CSSProperties { - return { visibility: enableAddDamage ? 'visible' : 'hidden' }; -} - /** * Custom button displayed in the PhotoCapture Camera HUD that allows user to enter add damage mode. */ @@ -31,15 +26,17 @@ export function AddDamageButton({ onAddDamage, enableAddDamage }: AddDamageButto const primaryColor = usePhotoCaptureHUDButtonBackground(); return ( - + <> + {enableAddDamage && ( + + )} + ); } diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.styles.ts b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.styles.ts index d9ad27c15..b4981f61d 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.styles.ts +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.styles.ts @@ -24,9 +24,9 @@ export const styles: Styles = { top: { display: 'flex', alignSelf: 'stretch', - flexDirection: 'row', alignItems: 'start', - justifyContent: 'end', + justifyContent: 'space-between', + gap: 10, margin: 10, }, bottom: { @@ -36,7 +36,4 @@ export const styles: Styles = { alignItems: 'center', zIndex: 9, }, - overlay: { - zIndex: 9, - }, }; diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.tsx index 8c5d886d9..7c38f0ccd 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.tsx +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.tsx @@ -5,6 +5,7 @@ import { AddDamageButton } from './AddDamageButton'; import { PhotoCaptureHUDElementsSightProps, usePhotoCaptureHUDSightPreviewStyle } from './hooks'; import { PhotoCaptureHUDCounter } from '../PhotoCaptureHUDCounter'; import { PhotoCaptureMode } from '../../hooks'; +import { SightGuideline } from './SightGuideline'; /** * Component implementing an HUD displayed on top of the Camera preview during the PhotoCapture process when the current @@ -20,14 +21,22 @@ export function PhotoCaptureHUDElementsSight({ previewDimensions, images, enableAddDamage, + sightGuidelines, + enableSightGuidelines, }: PhotoCaptureHUDElementsSightProps) { const style = usePhotoCaptureHUDSightPreviewStyle({ previewDimensions }); return (
- {previewDimensions && } + {previewDimensions && }
+
diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.styles.ts b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.styles.ts new file mode 100644 index 000000000..b89e33474 --- /dev/null +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.styles.ts @@ -0,0 +1,26 @@ +import { Styles } from '@monkvision/types'; +import { PHOTO_CAPTURE_HUD_BUTTONS_BAR_WIDTH } from '../../PhotoCaptureHUDButtons/PhotoCaptureHUDButtons.styles'; + +export const styles: Styles = { + container: { + display: 'flex', + justifyContent: 'start', + }, + containerWide: { + display: 'flex', + position: 'fixed', + left: '50%', + transform: 'translate(-50%, 0)', + width: `calc(98% - (${PHOTO_CAPTURE_HUD_BUTTONS_BAR_WIDTH * 4}px))`, + justifyContent: 'center', + }, + button: { + textAlign: 'start', + borderRadius: '12px', + fontSize: 14, + flexDirection: 'row-reverse', + paddingRight: 0, + alignItems: 'start', + gap: 10, + }, +}; diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.tsx new file mode 100644 index 000000000..04e8719f0 --- /dev/null +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/SightGuideline.tsx @@ -0,0 +1,59 @@ +import { useEffect, useMemo, useState } from 'react'; +import { Button } from '@monkvision/common-ui-web'; +import { CaptureAppConfig } from '@monkvision/types'; +import { useTranslation } from 'react-i18next'; +import { getLanguage } from '@monkvision/common'; +import { usePhotoCaptureHUDButtonBackground } from '../../hooks'; +import { styles } from './SightGuideline.styles'; + +/** + * Props of the SightGuidelineButton component. + */ +export interface SightGuidelineProps + extends Pick { + /** + * The id of the sight. + */ + sightId: string; +} + +/** + * Custom button displaying the sight guideline. + */ +export function SightGuideline({ + sightId, + sightGuidelines, + enableSightGuidelines, + enableAddDamage, +}: SightGuidelineProps) { + const [showGuideline, setShowGuideline] = useState(true); + const primaryColor = usePhotoCaptureHUDButtonBackground(); + const { i18n } = useTranslation(); + + const style = useMemo( + () => (enableAddDamage ? styles['container'] : styles['containerWide']), + [enableAddDamage, styles], + ); + + const guideline = useMemo(() => { + const guidelineFound = sightGuidelines?.find((value) => value.sightIds.includes(sightId)); + return guidelineFound ? guidelineFound[getLanguage(i18n.language)] : undefined; + }, [sightId, sightGuidelines]); + + useEffect(() => setShowGuideline(true), [sightId]); + + return ( +
+ {enableSightGuidelines && showGuideline && guideline && ( + + )} +
+ ); +} diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/index.ts b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/index.ts new file mode 100644 index 000000000..5f131bd9e --- /dev/null +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline/index.ts @@ -0,0 +1 @@ +export * from './SightGuideline'; diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/hooks.ts b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/hooks.ts index cdffa3647..6ddb364ec 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/hooks.ts +++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/hooks.ts @@ -1,11 +1,13 @@ -import { Image, PixelDimensions, Sight } from '@monkvision/types'; +import { CaptureAppConfig, Image, PixelDimensions, Sight } from '@monkvision/types'; import { useResponsiveStyle } from '@monkvision/common'; +import { CSSProperties } from 'react'; import { styles } from './PhotoCaptureHUDElementsSight.styles'; /** * Props of the PhotoCaptureHUDElementsSight component. */ -export interface PhotoCaptureHUDElementsSightProps { +export interface PhotoCaptureHUDElementsSightProps + extends Pick { /** * The list of sights provided to the PhotoCapture component. */ @@ -38,12 +40,6 @@ export interface PhotoCaptureHUDElementsSightProps { * The current images taken by the user (ignoring retaken pictures etc.). */ images: Image[]; - /** - * Boolean indicating whether the Add Damage feature is disabled. If disabled, the `Add Damage` button will be hidden. - * - * @default true - */ - enableAddDamage?: boolean; } export function usePhotoCaptureHUDSightPreviewStyle({ @@ -64,5 +60,11 @@ export function usePhotoCaptureHUDSightPreviewStyle({ width: previewDimensions?.width, height: previewDimensions?.height, }, + guidelineBtn: styles['guidelineBtn'], + addDamageBtn: styles['addDamageBtn'], }; } + +export function getVisilityStyle(enable?: boolean): CSSProperties { + return { visibility: enable ? 'visible' : 'hidden' }; +} diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx index 0c9e9a0d0..206850803 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx +++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx @@ -96,6 +96,16 @@ function createProps(): PhotoCaptureProps { allowSkipRetake: true, enableAddDamage: true, maxUploadDurationWarning: 456, + enableSightGuidelines: true, + sightGuidelines: [ + { + en: 'en-test', + fr: 'fr-test', + de: 'de-test', + nl: 'nl-test', + sightIds: ['sightId-test-1', 'sightId-test-2'], + }, + ], }; } @@ -305,6 +315,8 @@ describe('PhotoCapture component', () => { onOpenGallery: expect.any(Function), images, enableAddDamage: props.enableAddDamage, + enableSightGuidelines: props.enableSightGuidelines, + sightGuidelines: props.sightGuidelines, }, }); diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton.test.tsx index 005eb5682..07c9ed665 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton.test.tsx +++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/AddDamageButton.test.tsx @@ -5,9 +5,21 @@ import { Button } from '@monkvision/common-ui-web'; import { AddDamageButton } from '../../../../src'; describe('AddDamageButton component', () => { + it('should not render when enableAddDamage is false', () => { + const onAddDamage = jest.fn(); + const { unmount } = render( + , + ); + expect(Button).not.toHaveBeenCalled(); + + unmount(); + }); + it('should pass the onAddDamage callback to the onClick event of the Button', () => { const onAddDamage = jest.fn(); - const { unmount } = render(); + const { unmount } = render( + , + ); expectPropsOnChildMock(Button, { onClick: onAddDamage }); @@ -18,22 +30,11 @@ describe('AddDamageButton component', () => { const label = 'test-label-ok'; const tMock = jest.fn(() => label); (useTranslation as jest.Mock).mockImplementationOnce(() => ({ t: tMock })); - const { unmount } = render(); + const { unmount } = render(); expect(tMock).toHaveBeenCalledWith('photo.hud.sight.addDamageBtn'); expectPropsOnChildMock(Button, { children: label }); unmount(); }); - - it('should be disabled and not visible when enableAddDamage is false', () => { - const onAddDamage = jest.fn(); - const { unmount } = render( - , - ); - - expectPropsOnChildMock(Button, { style: { visibility: 'hidden' }, disabled: true }); - - unmount(); - }); }); diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.test.tsx index 1dc7e438f..a8adbd4be 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.test.tsx +++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/PhotoCaptureHUDElementsSight.test.tsx @@ -57,10 +57,6 @@ describe('PhotoCaptureHUDElementsSight component', () => { expectPropsOnChildMock(SightOverlay, { sight: props.selectedSight, - style: expect.objectContaining({ - width: props.previewDimensions?.width, - height: props.previewDimensions?.height, - }), }); unmount(); diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline.test.tsx new file mode 100644 index 000000000..aadbc94bd --- /dev/null +++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline.test.tsx @@ -0,0 +1,62 @@ +import { render } from '@testing-library/react'; +import { useTranslation } from 'react-i18next'; +import { expectPropsOnChildMock } from '@monkvision/test-utils'; +import { Button } from '@monkvision/common-ui-web'; +import { SightGuideline } from '../../../../src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDElementsSight/SightGuideline'; +import { getLanguage } from '@monkvision/common'; + +function createProps() { + return { + sightId: 'sightId-test-1', + guidelines: [ + { + en: 'en-test', + fr: 'fr-test', + de: 'de-test', + nl: 'nl-test', + sightIds: ['sightId-test-1', 'sightId-test-2'], + information: 'info-test', + }, + ], + enableAddDamage: true, + enableSightGuidelines: true, + }; +} + +describe('SightGuideline component', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should be display when enableSightGuideline is false', () => { + const props = createProps(); + (getLanguage as jest.Mock).mockImplementationOnce(() => 'en'); + (useTranslation as jest.Mock).mockImplementationOnce(() => ({ i18n: { language: 'en' } })); + const { unmount } = render( + , + ); + expect(Button).not.toHaveBeenCalled(); + + unmount(); + }); + + it('should display a Button with the proper label', () => { + const props = createProps(); + (getLanguage as jest.Mock).mockImplementationOnce(() => 'en'); + const { unmount } = render( + , + ); + + expectPropsOnChildMock(Button, { children: props.guidelines[0].en }); + + unmount(); + }); +}); diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 9ba70fb1f..dcf6c690f 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -1,5 +1,5 @@ import { CameraResolution, CompressionOptions } from './camera'; -import { SteeringWheelPosition, VehicleType } from './sights'; +import { SightGuideline, SteeringWheelPosition, VehicleType } from './sights'; import { MonkPalette } from './theme'; import { ComplianceOptions, TaskName } from './state'; import { DeviceOrientation } from './utils'; @@ -93,6 +93,16 @@ export type CaptureAppConfig = CameraConfig & * @default true */ enableAddDamage?: boolean; + /** + * A collection of sight guidelines in different language with a list of sightIds associate to it. + */ + sightGuidelines?: SightGuideline[]; + /** + * Boolean indicating whether the sight guideline feature is enabled. If disabled, the guideline text will be hidden. + * + * @default true + */ + enableSightGuidelines?: boolean; /** * The default vehicle type used if no vehicle type is defined. */ diff --git a/packages/types/src/sights.ts b/packages/types/src/sights.ts index e3a2a1794..5981fbc59 100644 --- a/packages/types/src/sights.ts +++ b/packages/types/src/sights.ts @@ -114,6 +114,32 @@ export enum VehicleModel { VWTROC = 'vwtroc', } +/** + * Interface describing the sight guideline. + */ +export interface SightGuideline { + /** + * The list of sight IDs associated with this guideline. + */ + sightIds: string[]; + /** + * The guideline text in English. + */ + en: string; + /** + * The guideline text in French. + */ + fr: string; + /** + * The guideline text in German. + */ + de: string; + /** + * The guideline text in Dutch. + */ + nl: string; +} + /** * Details of a sight with its overlay as an SVG string. */