Skip to content

Commit 6c4b032

Browse files
committed
Added Sight Tutorial feature
1 parent 2094b17 commit 6c4b032

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2871
-356
lines changed

apps/demo-app/src/local-config.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"apiDomain": "api.preview.monk.ai/v1",
1616
"thumbnailDomain": "europe-west1-monk-preview-321715.cloudfunctions.net/image_resize",
17-
"enableSightTutorial": false,
17+
"enableSightTutorial": true,
1818
"enableTutorial": "first_time_only",
1919
"startTasksOnComplete": true,
2020
"showCloseButton": false,
@@ -207,6 +207,18 @@
207207
]
208208
}
209209
],
210+
"sightTutorial": [
211+
{
212+
"en": "Kneel or bend low in front of the car, aligned with the grille. Centre the car in the frame, ensuring the front grille, headlights, and registration plate are fully visible. Include the top of the car and the bottom of the bumper in the shot.",
213+
"fr": "Agenouillez-vous ou penchez-vous devant la voiture, aligné avec la calandre. Centrez la voiture dans le cadre, en vous assurant que la calandre avant, les phares et la plaque d'immatriculation soient bien visibles. Veillez à inclure le toit de la voiture et le bas du pare-chocs dans la photo.",
214+
"nl": "Kniel of buk laag voor de auto, uitgelijnd met het rooster. Centreer de auto in het frame, zorg ervoor dat het voorrooster, de koplampen en het kenteken volledig zichtbaar zijn. Zorg ervoor dat de bovenkant van de auto en de onderkant van de bumper in de foto zichtbaar zijn.",
215+
"de": "Knien oder beugen Sie sich vor dem Auto, ausgerichtet mit dem Kühlergrill. Zentrieren Sie das Auto im Bild, sodass der vordere Kühlergrill, die Scheinwerfer und das Nummernschild vollständig sichtbar sind. Achten Sie darauf, dass das Dach des Autos und der untere Teil der Stoßstange im Bild zu sehen sind.",
216+
"imageReferenceBySightId": {
217+
"jgc21-QIvfeg0X": null,
218+
"haccord-8YjMcu0D": "https://storage.googleapis.com/monk-sight-images/jgc21-QIvfeg0X.jpeg"
219+
}
220+
}
221+
],
210222
"requiredApiPermissions": [
211223
"monk_core_api:compliances",
212224
"monk_core_api:damage_detection",

documentation/src/utils/schemas.ts

+9
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ export const SightGuidelineSchema = z.object({
105105
nl: z.string(),
106106
});
107107

108+
export const SightTutorialSchema = z.object({
109+
imageReferenceBySightId: z.record(z.string().nullable()),
110+
en: z.string(),
111+
fr: z.string(),
112+
de: z.string(),
113+
nl: z.string(),
114+
});
115+
108116
export const AccentColorVariantsSchema = z.object({
109117
xdark: z.string(),
110118
dark: z.string(),
@@ -315,6 +323,7 @@ export const LiveConfigSchema = z
315323
enableTutorial: z.nativeEnum(PhotoCaptureTutorialOption).optional(),
316324
allowSkipTutorial: z.boolean().optional(),
317325
enableSightTutorial: z.boolean().optional(),
326+
sightTutorial: z.array(SightGuidelineSchema).optional(),
318327
defaultVehicleType: z.nativeEnum(VehicleType),
319328
allowManualLogin: z.boolean(),
320329
allowVehicleTypeSelection: z.boolean(),

packages/common-ui-web/README.md

+54
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,60 @@ function App() {
283283

284284
---
285285

286+
## IconAroundVehicle
287+
### Description
288+
Component used to display an icon representing a vehicle and a point of view (POV) indicator.
289+
290+
### Example
291+
```tsx
292+
import { useState } from 'react';
293+
import { IconAroundVehicle } from '@monkvision/common-ui-web';
294+
295+
function MyComponent() {
296+
297+
return <IconAroundVehicle positionAroundVehicle={90}/>;
298+
}
299+
```
300+
301+
### Props
302+
| Prop | Type | Description | Required | Default Value |
303+
|-----------------------|----------|-----------------------------------------------------------------------------------------------------------|----------|---------------|
304+
| size | number | The size (width and height, in pixels) of the icon.. | | `50` |
305+
| positionAroundVehicle | number | Pov position angle to the north (0: POV in the front of the car / 180: POV is behind the back of the car) | | `0` |
306+
| orientationAngle | number | POV orientation angle: (0: POV is facing north / 180: POV is facing south). | | |
307+
| showCircle | boolean | Boolean indicating if the circle should be visible. | | `true` |
308+
| isPovClose | boolean | Boolean indicating if the POV should be closer to the center. | | `true` |
309+
310+
---
311+
312+
## IconVerticalPosition
313+
### Description
314+
Component used to display an icon representing a vehicle and a point of view (POV) indicator.
315+
316+
### Example
317+
318+
```tsx
319+
import {useState} from 'react';
320+
import {IconVerticalPosition} from '@monkvision/common-ui-web';
321+
import {CameraHeight} from "@monkvision/types";
322+
323+
function MyComponent() {
324+
325+
return <IconVerticalPosition position={CameraHeight.MID}/>;
326+
}
327+
```
328+
329+
### Props
330+
| Prop | Type | Description | Required | Default Value |
331+
|-----------------------|----------|-----------------------------------------------------------------------------------------------------------|----------|---------------|
332+
| size | number | The size (width and height, in pixels) of the icon.. | | `50` |
333+
| positionAroundVehicle | number | Pov position angle to the north (0: POV in the front of the car / 180: POV is behind the back of the car) | | `0` |
334+
| orientationAngle | number | POV orientation angle: (0: POV is facing north / 180: POV is facing south). | | |
335+
| showCircle | boolean | Boolean indicating if the circle should be visible. | | `true` |
336+
| isPovClose | boolean | Boolean indicating if the POV should be closer to the center. | | `true` |
337+
338+
---
339+
286340
## ImageDetailedView
287341
### Description
288342
This component is used to display the preview of an inspection image, as well as additional data such as its label etc.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Styles } from '@monkvision/types';
2+
import { useResponsiveStyle } from '@monkvision/common';
3+
4+
export const styles: Styles = {
5+
container: {
6+
padding: '10px',
7+
display: 'flex',
8+
alignItems: 'center',
9+
justifyContent: 'center',
10+
},
11+
car: { position: 'absolute' },
12+
pov: {
13+
position: 'absolute',
14+
},
15+
};
16+
17+
export interface IconAroundVehicleParams {
18+
size: number;
19+
positionAroundVehicle: number;
20+
orientationAngle?: number;
21+
isPovClose?: boolean;
22+
}
23+
24+
function getPointOnCircle(angleDegrees: number, radius: number): { x: number; y: number } {
25+
const shiftedAngleDegrees = angleDegrees - 90;
26+
const angleRadians = shiftedAngleDegrees * (Math.PI / 180);
27+
28+
const x = Math.round(radius * Math.cos(angleRadians));
29+
const y = Math.round(radius * Math.sin(angleRadians)) * -1;
30+
31+
return { x, y };
32+
}
33+
34+
export function useIconAroundVehicleStyle({
35+
size,
36+
positionAroundVehicle,
37+
orientationAngle,
38+
isPovClose,
39+
}: IconAroundVehicleParams) {
40+
const { responsive } = useResponsiveStyle();
41+
42+
const baseRadius = size / 2;
43+
const closeUpRadius = baseRadius * 0.3;
44+
45+
const position = getPointOnCircle(positionAroundVehicle, baseRadius);
46+
const orientation =
47+
orientationAngle !== undefined ? getPointOnCircle(orientationAngle, size / 2) : null;
48+
const x = orientation ? position.x - orientation.x : 0;
49+
const y = orientation ? position.y - orientation.y : 0;
50+
51+
const angle = orientationAngle ?? positionAroundVehicle;
52+
53+
const closeUpOffset = isPovClose
54+
? getPointOnCircle(positionAroundVehicle, closeUpRadius)
55+
: { x: 0, y: 0 };
56+
57+
return {
58+
container: {
59+
...styles['container'],
60+
...responsive(styles['containerSmall']),
61+
width: size,
62+
height: size,
63+
},
64+
pov: {
65+
...styles['pov'],
66+
transform: ` translate(${x - closeUpOffset.x}px, ${
67+
y - closeUpOffset.y
68+
}px) rotate(${-angle}deg)`,
69+
},
70+
};
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { DynamicSVG } from '../DynamicSVG';
2+
import { assets } from './assets';
3+
import { styles, useIconAroundVehicleStyle } from './IconAroundVehicle.styles';
4+
import { IconAroundVehicleProps } from './IconAroundVehicle.types';
5+
6+
/**
7+
* Component used to display an icon representing a vehicle and a point of view (POV) indicator.
8+
*/
9+
export function IconAroundVehicle({
10+
size = 50,
11+
positionAroundVehicle = 0,
12+
orientationAngle,
13+
showCircle = true,
14+
isPovClose = false,
15+
...passThroughProps
16+
}: IconAroundVehicleProps) {
17+
const style = useIconAroundVehicleStyle({
18+
size,
19+
positionAroundVehicle,
20+
orientationAngle,
21+
isPovClose,
22+
});
23+
const { CAR_SVG, CIRCLE_SVG, POV_SVG } = assets;
24+
const carSize = size * 0.6;
25+
const povSize = size * 1.1;
26+
27+
return (
28+
<div style={style.container} {...passThroughProps}>
29+
{showCircle && (
30+
<DynamicSVG svg={CIRCLE_SVG} width={size} height={size} style={styles['car']} />
31+
)}
32+
<DynamicSVG svg={CAR_SVG} width={carSize} height={carSize} style={styles['car']} />
33+
<DynamicSVG svg={POV_SVG} width={povSize} height={povSize} style={style.pov} />
34+
</div>
35+
);
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Props accepted by the IconAroundVehicle component.
3+
*/
4+
export interface IconAroundVehicleProps {
5+
/**
6+
* The size (width and height, in pixels) of the icon.
7+
*
8+
* @default 50
9+
*/
10+
size?: number;
11+
/**
12+
* Pov position angle to the north:
13+
* - 0: POV in the front of the car
14+
* - 180: POV is behind the back of the car
15+
*
16+
* @default 0
17+
*/
18+
positionAroundVehicle?: number;
19+
/**
20+
* POV orientation angle:
21+
* - 0: POV is facing north
22+
* - 180: POV is facing south.
23+
*/
24+
orientationAngle?: number;
25+
/**
26+
* Boolean indicating if the circle should be visible.
27+
*
28+
* @default true
29+
*/
30+
showCircle?: boolean;
31+
/**
32+
* Boolean indicating if the POV should be closer to the center.
33+
*
34+
* @default false
35+
*/
36+
isPovClose?: boolean;
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const assets = {
2+
CAR_SVG:
3+
'<svg fill="none" viewBox="0 0 44 87" xmlns="http://www.w3.org/2000/svg"><path fill="#F2F2F7" fill-rule="evenodd" d="M42.343 74.861c0 2.638-1.251 5.12-3.371 6.69a27.75 27.75 0 0 1-33.027 0 8.325 8.325 0 0 1-3.371-6.69V73.53c-.916 0-1.658-3.296-1.658-7.362 0-4.066.742-7.363 1.657-7.363V29.363C1.658 29.362.916 26.066.916 22c0-4.066.742-7.363 1.657-7.363V11.23a8.324 8.324 0 0 1 5.435-7.806 41.62 41.62 0 0 1 28.9 0 8.324 8.324 0 0 1 5.434 7.806v3.409h.001C43.258 14.637 44 17.933 44 22c0 4.066-.742 7.362-1.657 7.362V58.805c.915 0 1.657 3.297 1.657 7.363 0 4.066-.742 7.362-1.657 7.362v1.331ZM37.12 56.153a3.077 3.077 0 0 1-1.828 4.466l-2.59.748a36.927 36.927 0 0 1-20.492 0l-2.59-.748a3.077 3.077 0 0 1-1.828-4.466l4.722-8.392.98.181a49.239 49.239 0 0 0 17.925 0l.98-.181 4.721 8.392Zm-3.244-40.967c2.303.483 3.235 3.268 1.684 5.039l-2.042 2.334a3.077 3.077 0 0 1-2.798 1.012l-.547-.087a49.238 49.238 0 0 0-15.433 0l-.547.087a3.077 3.077 0 0 1-2.798-1.012l-2.043-2.334c-1.55-1.771-.62-4.556 1.685-5.039l3.849-.806a36.927 36.927 0 0 1 15.14 0l3.85.806Z" clip-rule="evenodd"/></svg>',
4+
POV_SVG:
5+
'<svg fill="none" viewBox="0 0 164 164" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#test_svg__a)"><path fill="url(#test_svg__b)" d="M14.257 68.215c-3.948-5.83-2.212-13.776 3.807-17.427l19.243-11.674c27.466-16.663 61.92-16.663 89.386 0l19.243 11.674c6.019 3.651 7.755 11.598 3.807 17.427L93 152H71L14.257 68.215Z"/><circle cx="82" cy="152" r="11" fill="#FFC000" stroke="#fff" stroke-width="2"/></g><defs><linearGradient id="test_svg__b" x1="85.071" x2="85.071" y1="70.834" y2="151.615" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC000" stop-opacity="0"/><stop offset="1" stop-color="#FFC000"/></linearGradient><clipPath id="test_svg__a"><path fill="#fff" d="M0 0h164v164H0z"/></clipPath></defs></svg>',
6+
CIRCLE_SVG:
7+
'<svg fill="none" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg"><circle cx="72" cy="72" r="70" fill="#1C1C1E" fill-opacity=".75" stroke="#F2F2F7" stroke-dasharray="4 20" stroke-linecap="round" stroke-width="4"/></svg>',
8+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { IconAroundVehicle } from './IconAroundVehicle';
2+
export { type IconAroundVehicleProps } from './IconAroundVehicle.types';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { CameraHeight } from '@monkvision/types';
2+
import { DynamicSVG } from '../DynamicSVG';
3+
import {
4+
IconVerticalPositionProps,
5+
IconVerticalPositionVariant,
6+
} from './IconVerticalPosition.types';
7+
import { assets } from './assets';
8+
9+
function getSvg(position: CameraHeight, variant: IconVerticalPositionVariant): string {
10+
const { MONK_MID_SVG, HIGH_SVG, MONK_LOW_SVG, LOW_SVG, MID_SVG, MONK_HIGH_SVG } = assets;
11+
const svgMap = {
12+
[CameraHeight.HIGH]: {
13+
[IconVerticalPositionVariant.PRIMARY]: HIGH_SVG,
14+
[IconVerticalPositionVariant.SECONDARY]: MONK_HIGH_SVG,
15+
},
16+
[CameraHeight.MID]: {
17+
[IconVerticalPositionVariant.PRIMARY]: MID_SVG,
18+
[IconVerticalPositionVariant.SECONDARY]: MONK_MID_SVG,
19+
},
20+
[CameraHeight.LOW]: {
21+
[IconVerticalPositionVariant.PRIMARY]: LOW_SVG,
22+
[IconVerticalPositionVariant.SECONDARY]: MONK_LOW_SVG,
23+
},
24+
};
25+
26+
return svgMap[position]?.[variant] || svgMap[CameraHeight.MID][variant];
27+
}
28+
29+
/**
30+
* Component used to display an icon representing a vehicle and a point of view (POV) indicator.
31+
*/
32+
export function IconVerticalPosition({
33+
size = 50,
34+
position = CameraHeight.MID,
35+
variant = IconVerticalPositionVariant.PRIMARY,
36+
}: IconVerticalPositionProps) {
37+
return <DynamicSVG svg={getSvg(position, variant)} width={size} height={size} />;
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { CameraHeight } from '@monkvision/types';
2+
3+
export enum IconVerticalPositionVariant {
4+
PRIMARY = 'primary',
5+
SECONDARY = 'secondary',
6+
}
7+
8+
/**
9+
* Props accepted by the IconVerticalPosition component.
10+
*/
11+
export interface IconVerticalPositionProps {
12+
/**
13+
* The size (width and height, in pixels) of the icon.
14+
*
15+
* @default 50
16+
*/
17+
size?: number;
18+
/**
19+
* The height position of the icon.
20+
*
21+
* @default CameraHeight.MID
22+
*/
23+
position?: CameraHeight;
24+
/**
25+
* The height position of the icon.
26+
*
27+
* @default primary
28+
*/
29+
variant?: IconVerticalPositionVariant;
30+
}

0 commit comments

Comments
 (0)