diff --git a/apps/demo-app/package.json b/apps/demo-app/package.json
index 74dbabafd..1a11da701 100644
--- a/apps/demo-app/package.json
+++ b/apps/demo-app/package.json
@@ -24,11 +24,13 @@
},
"dependencies": {
"@auth0/auth0-react": "^2.2.4",
+ "@monkvision/analytics": "4.0.0",
"@monkvision/common": "4.0.0",
"@monkvision/common-ui-web": "4.0.0",
"@monkvision/inspection-capture-web": "4.0.0",
"@monkvision/monitoring": "4.0.0",
"@monkvision/network": "4.0.0",
+ "@monkvision/posthog": "4.0.0",
"@monkvision/sentry": "4.0.0",
"@monkvision/sights": "4.0.0",
"@monkvision/types": "4.0.0",
diff --git a/apps/demo-app/src/index.tsx b/apps/demo-app/src/index.tsx
index 80681b31d..b29dfeb03 100644
--- a/apps/demo-app/src/index.tsx
+++ b/apps/demo-app/src/index.tsx
@@ -1,28 +1,31 @@
-import React from 'react';
import ReactDOM from 'react-dom';
import { MonitoringProvider } from '@monkvision/monitoring';
+import { AnalyticsProvider } from '@monkvision/analytics';
import { Auth0Provider } from '@auth0/auth0-react';
import { getEnvOrThrow, MonkThemeProvider } from '@monkvision/common';
import { sentryMonitoringAdapter } from './sentry';
+import { posthogAnalyticsAdapter } from './posthog';
import { AppRouter } from './components';
import './index.css';
import './i18n';
ReactDOM.render(
-
-
-
-
-
+
+
+
+
+
+
+
,
document.getElementById('root'),
);
diff --git a/apps/demo-app/src/pages/CreateInspectionPage/CreateInspectionPage.tsx b/apps/demo-app/src/pages/CreateInspectionPage/CreateInspectionPage.tsx
index befea8dd7..ed9dc0723 100644
--- a/apps/demo-app/src/pages/CreateInspectionPage/CreateInspectionPage.tsx
+++ b/apps/demo-app/src/pages/CreateInspectionPage/CreateInspectionPage.tsx
@@ -6,6 +6,7 @@ import { useMonkApi } from '@monkvision/network';
import { getEnvOrThrow, useLoadingState, useMonkAppParams } from '@monkvision/common';
import { useMonitoring } from '@monkvision/monitoring';
import { TaskName } from '@monkvision/types';
+import { useAnalytics } from '@monkvision/analytics';
import { Page } from '../pages';
import styles from './CreateInspectionPage.module.css';
@@ -18,6 +19,7 @@ export function CreateInspectionPage() {
authToken: authToken ?? '',
apiDomain: getEnvOrThrow('REACT_APP_API_DOMAIN'),
});
+ const { setUserId } = useAnalytics();
const handleCreateInspection = () => {
loading.start();
@@ -25,6 +27,7 @@ export function CreateInspectionPage() {
.then((res) => {
loading.onSuccess();
setInspectionId(res.id);
+ setUserId(res.id);
})
.catch((err) => {
loading.onError(err);
diff --git a/apps/demo-app/src/posthog.ts b/apps/demo-app/src/posthog.ts
new file mode 100644
index 000000000..850480b64
--- /dev/null
+++ b/apps/demo-app/src/posthog.ts
@@ -0,0 +1,9 @@
+import { PosthogAnalyticsAdapter } from '@monkvision/posthog';
+
+export const posthogAnalyticsAdapter = new PosthogAnalyticsAdapter({
+ token: 'phc_azbiXVxUvUjxUAVLb5zrrlzNCFpf0RSClkiJ9RxTDGU',
+ api_host: 'https://eu.posthog.com',
+ environnement: 'development',
+ projectName: 'test',
+ release: '1.0.0',
+});
diff --git a/documentation/docs/packages/analytics.md b/documentation/docs/packages/analytics.md
new file mode 100644
index 000000000..37f254b14
--- /dev/null
+++ b/documentation/docs/packages/analytics.md
@@ -0,0 +1,20 @@
+---
+sidebar_position: 1
+---
+
+# analytics
+Monk analytics abstraction package.
+
+![npm latest package](https://img.shields.io/npm/v/@monkvision/analytics/latest.svg)
+
+## Overview
+This package provides an abstraction layer for the analytics features in the MonkJs ecosystem. If you plan on using any
+of these features, you can use this package to properly set up the analytics inside your application.
+
+In the MonkJs SDK, we use this "abstraction" package as a way of offering the possibility to developers to choose their
+own Analytics tools and platform if they don't want to use Posthog (the platform used internally by Monk).
+
+## Complete Documentation
+As every other package in the SDK, please refer to
+[its README file](https://github.com/monkvision/monkjs/blob/main/packages/analytics/README.md) for a complete
+documentation on how this package works.
diff --git a/documentation/docs/packages/camera-web.md b/documentation/docs/packages/camera-web.md
index 879132c72..ef7e5cefe 100644
--- a/documentation/docs/packages/camera-web.md
+++ b/documentation/docs/packages/camera-web.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 1
+sidebar_position: 2
---
# camera-web
diff --git a/documentation/docs/packages/common-ui-web.md b/documentation/docs/packages/common-ui-web.md
index b39e33bc2..2c3dd9459 100644
--- a/documentation/docs/packages/common-ui-web.md
+++ b/documentation/docs/packages/common-ui-web.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 3
+sidebar_position: 4
---
# common-ui-web
diff --git a/documentation/docs/packages/common.md b/documentation/docs/packages/common.md
index 278aa2671..c3c1512ff 100644
--- a/documentation/docs/packages/common.md
+++ b/documentation/docs/packages/common.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 2
+sidebar_position: 3
---
# common
diff --git a/documentation/docs/packages/inspection-capture-web.md b/documentation/docs/packages/inspection-capture-web.md
index 7b24830d2..c96b1e5e5 100644
--- a/documentation/docs/packages/inspection-capture-web.md
+++ b/documentation/docs/packages/inspection-capture-web.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 5
---
# inspection-capture-web
diff --git a/documentation/docs/packages/inspection-report-web.md b/documentation/docs/packages/inspection-report-web.md
index 097012b0d..94d765c07 100644
--- a/documentation/docs/packages/inspection-report-web.md
+++ b/documentation/docs/packages/inspection-report-web.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 5
+sidebar_position: 6
---
# inspection-report-web
diff --git a/documentation/docs/packages/monitoring.md b/documentation/docs/packages/monitoring.md
index 74476ee70..5e19eb3d1 100644
--- a/documentation/docs/packages/monitoring.md
+++ b/documentation/docs/packages/monitoring.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 6
+sidebar_position: 7
---
# monitoring
diff --git a/documentation/docs/packages/network.md b/documentation/docs/packages/network.md
index cd7f3d3ec..ae8958eef 100644
--- a/documentation/docs/packages/network.md
+++ b/documentation/docs/packages/network.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 7
+sidebar_position: 8
---
# network
diff --git a/documentation/docs/packages/posthog.md b/documentation/docs/packages/posthog.md
new file mode 100644
index 000000000..e57e62bc1
--- /dev/null
+++ b/documentation/docs/packages/posthog.md
@@ -0,0 +1,20 @@
+---
+sidebar_position: 9
+---
+
+# posthog
+Posthog implementation used to analyze MonkJs SDK and applications.
+
+![npm latest package](https://img.shields.io/npm/v/@monkvision/posthog/latest.svg)
+
+## Overview
+This package provides an implementation of the analytics abstraction layers exposed by the
+[analytics package](docs/packages/analytics.md) using the [Posthog](https://posthog.com/) analytics platform. Posthog is
+the platform used internally by Monk to analyze the user's behaviour in our applications. Unless you already have your own analytics
+platform, and you really don't want to add Posthog as a dependency in your app, we highly recommend installing this
+analytics adapter in your app.
+
+## Complete Documentation
+As every other package in the SDK, please refer to
+[its README file](https://github.com/monkvision/monkjs/blob/main/packages/posthog/README.md) for a complete
+documentation on how this package works.
diff --git a/documentation/docs/packages/sentry.md b/documentation/docs/packages/sentry.md
index 22107467b..c6aa9cfef 100644
--- a/documentation/docs/packages/sentry.md
+++ b/documentation/docs/packages/sentry.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 8
+sidebar_position: 10
---
# sentry
diff --git a/documentation/docs/packages/sights.md b/documentation/docs/packages/sights.md
index 974ba262c..81e516e29 100644
--- a/documentation/docs/packages/sights.md
+++ b/documentation/docs/packages/sights.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 9
+sidebar_position: 11
---
# sights
diff --git a/documentation/docs/packages/types.md b/documentation/docs/packages/types.md
index b0f367d6a..aeddc81b0 100644
--- a/documentation/docs/packages/types.md
+++ b/documentation/docs/packages/types.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 10
+sidebar_position: 12
---
# types
diff --git a/packages/inspection-capture-web/package.json b/packages/inspection-capture-web/package.json
index 2245bd52a..3b13797bb 100644
--- a/packages/inspection-capture-web/package.json
+++ b/packages/inspection-capture-web/package.json
@@ -25,6 +25,7 @@
"lint:fix": "yarn run prettier:fix && yarn run eslint:fix"
},
"dependencies": {
+ "@monkvision/analytics": "4.0.0",
"@monkvision/camera-web": "4.0.0",
"@monkvision/common": "4.0.0",
"@monkvision/common-ui-web": "4.0.0",
diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx
index b0bf02f36..71103586e 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx
+++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCapture.tsx
@@ -3,6 +3,7 @@ import { Camera, CameraHUDProps, CameraProps, CompressionOptions } from '@monkvi
import { ComplianceOptions, DeviceOrientation, Sight, TaskName } from '@monkvision/types';
import { useI18nSync, useLoadingState, useWindowDimensions } from '@monkvision/common';
import { MonkApiConfig } from '@monkvision/network';
+import { useAnalytics } from '@monkvision/analytics';
import { useMonitoring } from '@monkvision/monitoring';
import {
Icon,
@@ -13,6 +14,7 @@ import {
import { useTranslation } from 'react-i18next';
import {
useAddDamageMode,
+ useComplianceAnalytics,
usePhotoCaptureSightState,
usePictureTaken,
useStartTasksOnComplete,
@@ -117,8 +119,10 @@ export function PhotoCapture({
const { handleError } = useMonitoring();
const [currentScreen, setCurrentScreen] = useState(PhotoCaptureScreen.CAMERA);
const dimensions = useWindowDimensions();
+ const analytics = useAnalytics();
const loading = useLoadingState();
const addDamageHandle = useAddDamageMode();
+ useComplianceAnalytics({ inspectionId, sights });
const startTasks = useStartTasksOnComplete({
inspectionId,
apiConfig,
@@ -152,7 +156,13 @@ export function PhotoCapture({
uploadQueue,
tasksBySight,
});
- const handleOpenGallery = () => setCurrentScreen(PhotoCaptureScreen.GALLERY);
+ const handleOpenGallery = () => {
+ setCurrentScreen(PhotoCaptureScreen.GALLERY);
+ analytics.trackEvent('Capture Closed', {
+ inspectionId,
+ category: 'capture_closed',
+ });
+ };
const handleGalleryBack = () => setCurrentScreen(PhotoCaptureScreen.CAMERA);
const handleNavigateToCapture = (options: NavigateToCaptureOptions) => {
if (options.reason === NavigateToCaptureReason.ADD_DAMAGE) {
@@ -171,6 +181,11 @@ export function PhotoCapture({
const handleGalleryValidate = () => {
startTasks()
.then(() => {
+ analytics.trackEvent('Capture Completed', {
+ inspectionId,
+ category: 'capture_completed',
+ });
+ analytics.setUserProperties({ captureCompleted: true });
onComplete?.();
})
.catch((err) => {
diff --git a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx
index 006539c96..4593fc0aa 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx
+++ b/packages/inspection-capture-web/src/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUD.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { BackdropDialog } from '@monkvision/common-ui-web';
import { CameraHUDProps } from '@monkvision/camera-web';
import { LoadingState } from '@monkvision/common';
+import { useAnalytics } from '@monkvision/analytics';
import { PhotoCaptureHUDButtons } from './PhotoCaptureHUDButtons';
import { usePhotoCaptureHUDStyle } from './hooks';
import { PhotoCaptureMode } from '../hooks';
@@ -101,9 +102,14 @@ export function PhotoCaptureHUD({
const { t } = useTranslation();
const [showCloseModal, setShowCloseModal] = useState(false);
const style = usePhotoCaptureHUDStyle();
+ const { trackEvent } = useAnalytics();
const handleCloseConfirm = () => {
setShowCloseModal(false);
+ trackEvent('Capture Closed', {
+ inspectionId,
+ category: 'capture_closed',
+ });
onClose?.();
};
diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/index.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/index.ts
index 0fc6cf00d..341133db2 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/hooks/index.ts
+++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/index.ts
@@ -3,3 +3,4 @@ export * from './useStartTasksOnComplete';
export * from './useAddDamageMode';
export * from './useUploadQueue';
export * from './usePictureTaken';
+export * from './useComplianceAnalytics';
diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/useAddDamageMode.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/useAddDamageMode.ts
index 5662fd1b8..1b417ca67 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/hooks/useAddDamageMode.ts
+++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/useAddDamageMode.ts
@@ -1,3 +1,4 @@
+import { useAnalytics } from '@monkvision/analytics';
import { useCallback, useMemo, useState } from 'react';
/**
@@ -45,20 +46,31 @@ export interface AddDamageHandle {
*/
export function useAddDamageMode(): AddDamageHandle {
const [mode, setMode] = useState(PhotoCaptureMode.SIGHT);
+ const { trackEvent } = useAnalytics();
- const handleAddDamage = useCallback(() => setMode(PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT), []);
+ const handleAddDamage = useCallback(() => {
+ setMode(PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT);
+ trackEvent('AddDamage Selected', {
+ mode: PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT,
+ category: 'addDamage_selected',
+ });
+ }, []);
- const updatePhotoCaptureModeAfterPictureTaken = useCallback(
- () =>
- setMode((currentMode) =>
- currentMode === PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT
- ? PhotoCaptureMode.ADD_DAMAGE_2ND_SHOT
- : PhotoCaptureMode.SIGHT,
- ),
- [],
- );
+ const updatePhotoCaptureModeAfterPictureTaken = useCallback(() => {
+ setMode((currentMode) =>
+ currentMode === PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT
+ ? PhotoCaptureMode.ADD_DAMAGE_2ND_SHOT
+ : PhotoCaptureMode.SIGHT,
+ );
+ }, []);
- const handleCancelAddDamage = useCallback(() => setMode(PhotoCaptureMode.SIGHT), []);
+ const handleCancelAddDamage = useCallback(() => {
+ trackEvent('AddDamage Canceled', {
+ mode,
+ category: 'addDamage_canceled',
+ });
+ setMode(PhotoCaptureMode.SIGHT);
+ }, []);
return useMemo(
() => ({
diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/useComplianceAnalytics.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/useComplianceAnalytics.ts
new file mode 100644
index 000000000..a9dda62a7
--- /dev/null
+++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/useComplianceAnalytics.ts
@@ -0,0 +1,60 @@
+import { useAnalytics } from '@monkvision/analytics';
+import { useMonkState } from '@monkvision/common';
+import { ComplianceIssue, Image, ImageStatus, ImageType, Sight } from '@monkvision/types';
+import { useEffect, useState } from 'react';
+
+interface ImageEventTracking extends Image {
+ isAlreadySent: boolean;
+}
+
+/**
+ * Parameters of the useComplianceAnalytics hook.
+ */
+export interface ComplianceAnalyticsParams {
+ /**
+ * The inspection ID.
+ */
+ inspectionId: string;
+ /**
+ * The list of sights passed to the PhotoCapture component.
+ */
+ sights: Sight[];
+}
+
+/**
+ * Custom hook used for the compliance analytics by sending an event for 'non_compliant' image.
+ */
+export function useComplianceAnalytics({ inspectionId, sights }: ComplianceAnalyticsParams) {
+ const [imagesEventTracking, setImagesEventTracking] = useState([]);
+ const { state } = useMonkState();
+ const { trackEvent } = useAnalytics();
+
+ useEffect(() => {
+ const newImagesEventTracking = state.images
+ .filter(
+ (image) => image.inspectionId === inspectionId && image.type === ImageType.BEAUTY_SHOT,
+ )
+ .map((image) => {
+ const imageEventTracking = imagesEventTracking.find((i) => i.id === image.id);
+ if (imageEventTracking?.isAlreadySent) {
+ return imageEventTracking;
+ }
+ if (image.status === ImageStatus.NOT_COMPLIANT && image.complianceIssues) {
+ trackEvent('Compliance Issue', {
+ ...Object.fromEntries(
+ Object.values(ComplianceIssue).map((issue) => [
+ issue,
+ image.complianceIssues?.includes(issue),
+ ]),
+ ),
+ sightId: image.additionalData?.sight_id,
+ sightLabel: sights.find((sight) => sight.id === image.additionalData?.sight_id)?.label,
+ });
+ return { ...image, isAlreadySent: true };
+ }
+ return { ...image, isAlreadySent: false };
+ });
+
+ setImagesEventTracking(newImagesEventTracking);
+ }, [state]);
+}
diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts
index 429bee5ad..5b4c31c6c 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts
+++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts
@@ -9,6 +9,7 @@ import { useMonitoring } from '@monkvision/monitoring';
import { LoadingState, useAsyncEffect } from '@monkvision/common';
import { ComplianceOptions, Image, Sight, TaskName, MonkPicture } from '@monkvision/types';
import { sights } from '@monkvision/sights';
+import { useAnalytics } from '@monkvision/analytics';
import { PhotoCaptureErrorName } from '../errors';
/**
@@ -176,10 +177,28 @@ export function usePhotoCaptureSightState({
const [sightsTaken, setSightsTaken] = useState([]);
const { getInspection } = useMonkApi(apiConfig);
const { handleError } = useMonitoring();
+ const analytics = useAnalytics();
+
+ const selectSight = (s: Sight) => {
+ const sightsNotTaken = captureSights.filter((sight) => !sightsTaken.includes(sight)) ?? [];
+ const nextSight = sightsNotTaken.at(sightsNotTaken.indexOf(s) + 1) ?? null;
+
+ setSelectedSight(s);
+ analytics.trackEvent(`Sight Selected`, {
+ inspectionId,
+ sightId: s.id,
+ sightLabel: s.label,
+ nextSightId: nextSight?.id,
+ nextSightLabel: nextSight?.label,
+ category: 'sight_selected',
+ });
+ };
useAsyncEffect(
() => {
loading.start();
+ analytics.setUserProperties({ inspectionId, captureCompleted: false });
+ analytics.setEventsProperties({ inspectionId });
return getInspection({
id: inspectionId,
compliance: { enableCompliance, complianceIssues },
@@ -191,7 +210,22 @@ export function usePhotoCaptureSightState({
try {
const alreadyTakenSights = getSightsTaken(inspectionId, response);
setSightsTaken(alreadyTakenSights);
- setSelectedSight(captureSights.filter((s) => !alreadyTakenSights.includes(s))[0]);
+ const updatedSelectedSight = captureSights.filter(
+ (s) => !alreadyTakenSights.includes(s),
+ )[0];
+ setSelectedSight(updatedSelectedSight);
+ analytics.setUserProperties({
+ alreadyTakenSights: alreadyTakenSights.length,
+ totalSights: captureSights.length,
+ sightSelected: updatedSelectedSight.label,
+ });
+ analytics.setEventsProperties({ totalSights: captureSights.length });
+ analytics.trackEvent('Capture Started', {
+ newInspection: !!alreadyTakenSights,
+ alreadyTakenSights: alreadyTakenSights.length,
+ totalSights: captureSights.length,
+ category: 'capture_started',
+ });
setLastPictureTaken(getLastPictureTaken(inspectionId, response));
assertInspectionIsValid(inspectionId, response, captureSights, tasksBySight);
loading.onSuccess();
@@ -212,9 +246,26 @@ export function usePhotoCaptureSightState({
}, []);
const takeSelectedSight = useCallback(() => {
- const updatedSightsTaken = [...sightsTaken, selectedSight];
+ const isRetake = sightsTaken.includes(selectedSight);
+ const updatedSightsTaken = isRetake ? sightsTaken : [...sightsTaken, selectedSight];
setSightsTaken(updatedSightsTaken);
const nextSight = captureSights.filter((s) => !updatedSightsTaken.includes(s))[0];
+ analytics.trackEvent(`Sight Captured`, {
+ inspectionId,
+ order: captureSights.indexOf(selectedSight) + 1,
+ alreadyTakenSights: updatedSightsTaken.length,
+ totalSights: captureSights.length,
+ sightId: selectedSight.id,
+ sightLabel: selectedSight.label,
+ nextSightId: nextSight?.id ?? null,
+ nextSightLabel: nextSight?.label ?? null,
+ retake: isRetake,
+ category: 'sight_captured',
+ });
+ analytics.setUserProperties({
+ alreadyTakenSights: updatedSightsTaken.length,
+ sightSelected: nextSight?.label ?? null,
+ });
if (nextSight) {
setSelectedSight(nextSight);
} else {
@@ -226,7 +277,6 @@ export function usePhotoCaptureSightState({
(id: string) => {
const sightToRetake = captureSights.find((sight) => sight.id === id);
if (sightToRetake) {
- setSightsTaken((value) => value.filter((sight) => sight.id !== id));
setSelectedSight(sightToRetake);
}
},
@@ -237,7 +287,7 @@ export function usePhotoCaptureSightState({
() => ({
selectedSight,
sightsTaken,
- selectSight: setSelectedSight,
+ selectSight,
takeSelectedSight,
lastPictureTaken,
setLastPictureTaken,
diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts
index fdafc1208..38ecb8df1 100644
--- a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts
+++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts
@@ -1,6 +1,7 @@
import { MonkPicture, TaskName } from '@monkvision/types';
import { Queue } from '@monkvision/common';
import { useCallback } from 'react';
+import { useAnalytics } from '@monkvision/analytics';
import { PictureUpload } from './useUploadQueue';
import { AddDamageHandle, PhotoCaptureMode } from './useAddDamageMode';
import { PhotoCaptureSightState } from './usePhotoCaptureSightState';
@@ -42,6 +43,7 @@ export function usePictureTaken({
uploadQueue,
tasksBySight,
}: UseTakePictureParams): HandleTakePictureFunction {
+ const { trackEvent } = useAnalytics();
return useCallback(
(picture: MonkPicture) => {
sightState.setLastPictureTaken(picture);
@@ -57,6 +59,11 @@ export function usePictureTaken({
uploadQueue.push(upload);
if (addDamageHandle.mode === PhotoCaptureMode.SIGHT) {
sightState.takeSelectedSight();
+ } else {
+ trackEvent('Damage Captured', {
+ mode: addDamageHandle.mode,
+ category: 'damage_captured',
+ });
}
addDamageHandle.updatePhotoCaptureModeAfterPictureTaken();
},
diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx
index 6c7253abc..599d42a9f 100644
--- a/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx
+++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCapture.test.tsx
@@ -28,6 +28,7 @@ jest.mock('../../src/PhotoCapture/hooks', () => ({
length: 3,
})),
useStartTasksOnComplete: jest.fn(() => jest.fn()),
+ useComplianceAnalytics: jest.fn(),
}));
import { act, render, waitFor } from '@testing-library/react';
diff --git a/packages/inspection-capture-web/test/PhotoCapture/hooks/useComplianceAnalytics.test.ts b/packages/inspection-capture-web/test/PhotoCapture/hooks/useComplianceAnalytics.test.ts
new file mode 100644
index 000000000..3f5d40de2
--- /dev/null
+++ b/packages/inspection-capture-web/test/PhotoCapture/hooks/useComplianceAnalytics.test.ts
@@ -0,0 +1,63 @@
+import { renderHook } from '@testing-library/react-hooks';
+import { sights } from '@monkvision/sights';
+import { createEmptyMonkState, useMonkState } from '@monkvision/common';
+import { ComplianceIssue, Image, ImageStatus, ImageType, Inspection } from '@monkvision/types';
+import { useComplianceAnalytics, ComplianceAnalyticsParams } from '../../../src/PhotoCapture/hooks';
+import { useAnalytics } from '@monkvision/analytics';
+
+function createParams(): ComplianceAnalyticsParams {
+ return {
+ inspectionId: 'test-inspection-id',
+ sights: [
+ sights['test-sight-1'],
+ sights['test-sight-2'],
+ sights['test-sight-3'],
+ sights['test-sight-4'],
+ ],
+ };
+}
+
+describe('useComplianceAnalytics hook', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should only track non compliant image', () => {
+ const initialProps = createParams();
+ const state = createEmptyMonkState();
+ state.inspections.push({
+ id: initialProps.inspectionId,
+ images: ['image-1', 'image-2', 'image-3'],
+ } as unknown as Inspection);
+ state.images.push(
+ {
+ id: 'image-1',
+ inspectionId: initialProps.inspectionId,
+ type: ImageType.BEAUTY_SHOT,
+ additionalData: { sight_id: 'sight1' },
+ status: ImageStatus.SUCCESS,
+ } as unknown as Image,
+ {
+ id: 'image-2',
+ inspectionId: initialProps.inspectionId,
+ type: ImageType.BEAUTY_SHOT,
+ additionalData: { sight_id: 'sight2' },
+ status: ImageStatus.SUCCESS,
+ } as unknown as Image,
+ {
+ id: 'image-3',
+ inspectionId: initialProps.inspectionId,
+ type: ImageType.BEAUTY_SHOT,
+ additionalData: { sight_id: 'sight3' },
+ status: ImageStatus.NOT_COMPLIANT,
+ complianceIssues: [ComplianceIssue.NO_VEHICLE],
+ } as unknown as Image,
+ );
+ (useMonkState as jest.Mock).mockImplementation(() => ({ state }));
+ const { unmount } = renderHook(useComplianceAnalytics, { initialProps });
+ const { trackEvent } = (useAnalytics as jest.Mock).mock.results[0].value;
+ expect(trackEvent).toBeCalledTimes(1);
+
+ unmount();
+ });
+});
diff --git a/packages/inspection-capture-web/test/PhotoCapture/hooks/usePhotoCaptureSightState.test.ts b/packages/inspection-capture-web/test/PhotoCapture/hooks/usePhotoCaptureSightState.test.ts
index 215753839..be3ff9389 100644
--- a/packages/inspection-capture-web/test/PhotoCapture/hooks/usePhotoCaptureSightState.test.ts
+++ b/packages/inspection-capture-web/test/PhotoCapture/hooks/usePhotoCaptureSightState.test.ts
@@ -229,14 +229,13 @@ describe('usePhotoCaptureSightState hook', () => {
unmount();
});
- it('should remove the sight from the taken sights and select it when retaking a sight', () => {
+ it('should change the sightSelected when retaking a sight', () => {
const initialProps = createParams();
const { result, unmount } = renderHook(usePhotoCaptureSightState, { initialProps });
act(() => result.current.takeSelectedSight());
act(() => result.current.takeSelectedSight());
act(() => result.current.retakeSight('test-sight-1'));
- expect(result.current.sightsTaken).not.toContain(sights['test-sight-1']);
expect(result.current.selectedSight).toEqual(sights['test-sight-1']);
unmount();