Skip to content

Commit

Permalink
Add feedback when card is missing
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashbrnrd committed May 7, 2024
1 parent 1af68fc commit 81633ef
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 139 deletions.
2 changes: 1 addition & 1 deletion frontend/cypress/e2e/shoulder-bolt-rifle-securing.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Shoulder Bolt Rifle Securing', () => {
cy.getByDataTestid('next-step').should('not.have.attr', 'disabled')
cy.getByDataTestid('next-step').click()
cy.url().should('contain', '/guide-identification/resultat-final')
cy.getByDataTestid('arm-category').should('contain', 'Catégorie B')
cy.getByDataTestid('arm-category').should('contain', 'Catégorie C')
cy.getByDataTestid('return-to-home-end').click()
cy.url().should('contain', '/accueil')
})
Expand Down
Binary file added frontend/src/assets/missing_card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare module 'vue' {
DsfrTag: typeof import('@gouvminint/vue-dsfr')['DsfrTag']
FooterMES: typeof import('./components/FooterMES.vue')['default']
HeaderMain: typeof import('./components/HeaderMain.vue')['default']
MissingCardAlert: typeof import('./components/MissingCardAlert.vue')['default']
OnboardingSwiper: typeof import('./components/OnboardingSwiper.vue')['default']
PopupContact: typeof import('./components/PopupContact.vue')['default']
PopupVideo: typeof import('./components/PopupVideo.vue')['default']
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/MissingCardAlert.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div class="fr-alert fr-alert--warning">
<h3 class="fr-alert__title">
Absence de carte
</h3>
<p>
Pour obtenir un <strong>résultat plus précis</strong>, recommencez la prise de photo en ajoutant
<strong>une carte à côté de l’arme</strong>.
</p>
</div>
</template>
220 changes: 95 additions & 125 deletions frontend/src/components/ResultPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { ref, computed, watchEffect } from 'vue'
import axios from 'axios'
import SnackbarAlert from '@/components/SnackbarAlert.vue'
import { resultTree, ALARM_GUNS_TYPOLOGIES, DISCLAIMERS } from '@/utils/firearms-utils/index'
import { resultTree, ALARM_GUNS_TYPOLOGIES, MEASURED_GUNS_TYPOLOGIES, DISCLAIMERS } from '@/utils/firearms-utils/index'
import { isUserUsingCrosscall } from '@/utils/isUserUsingCrosscall'
import { useSnackbarStore } from '@/stores/snackbar'
import { useStepsStore } from '@/stores/steps'
Expand Down Expand Up @@ -52,6 +52,7 @@ const typology = computed(() => resultStore.typology)
const isDummy = computed(() => stepsStore.isDummy)
const isDummyTypology = computed(() => resultTree[typology.value]?.isDummyTypology === true)
const isCardDetected = computed(() => resultStore.gunLength !== null && resultStore.gunBarrelLength !== null)
const isUp = ref(false)
const isDown = ref(false)
Expand Down Expand Up @@ -119,131 +120,111 @@ function sendFeedback (isCorrect: boolean) {
<div
class="result-image"
:style="{backgroundImage:`url(${img})`}"
/>
></div>
<div class="fr-tile fr-enlarge-link mb-3">
<h2 class="fr-tile__title px-2">
<div v-if="confidenceLevel === 'low'">
<div class="fr-tile__body">
<DsfrTag
class="fr-tag--sm error-tag"
label="Indice de fiabilité insuffisant"
/>
<h2 class="fr-tile__title px-2" />
<div v-if="confidenceLevel === 'low'">
<div class="fr-tile__body">
<DsfrTag
class="fr-tag--sm error-tag"
label="Indice de fiabilité insuffisant"
/>
</div>
<p class="category fr-callout__title mt-3">
<img
class="px-2"
src="@/assets/guide-identification/icones/gun.jpg"
alt=""
>
Catégorie non déterminée
</p>
<p class="text-sm font-normal m-4 text-left text-current">
Nous n'avons pas suffisamment d'éléments pour fournir une réponse fiable. Nous vous conseillons de faire
appel à un expert.
</p>
<ContactExpert v-if="isUserUsingCrosscall()" />
</div>
<div v-else>
<div class="fr-tile__body">
<div v-if="confidenceLevel === 'high'">
<DsfrTag class="fr-tag--sm success-tag">
Indice de fiabilité : {{ Math.floor(confidence * 100) }}%
</DsfrTag>
</div>
<p class="category fr-callout__title mt-3">
<img
class="px-2"
src="@/assets/guide-identification/icones/gun.jpg"
alt=""
<div v-else>
<DsfrTag class="fr-tag--sm warning-tag">
Indice de fiabilité : {{ Math.floor(confidence * 100) }}%
</DsfrTag>
<p class="warning-text">
Nous vous conseillons de faire appel à un expert pour confirmer cette réponse.
</p>
<ContactExpert v-if="isUserUsingCrosscall()" />
</div>
<div class="fr-alert fr-alert--info mt-3">
<h3
v-if="isDummy"
class="fr-alert__title"
>
Catégorie non déterminée
</p>
<p class="text-sm font-normal m-4 text-left text-current">
Nous n'avons pas suffisamment d'éléments pour fournir une réponse fiable. Nous vous conseillons de faire appel à un expert.
</p>
<ContactExpert v-if="isUserUsingCrosscall()" />
</div>
<div v-else>
<div class="fr-tile__body">
<div v-if="confidenceLevel === 'high'">
<DsfrTag
class="fr-tag--sm success-tag"
>
Indice de fiabilité : {{ Math.floor(confidence * 100) }}%
</DsfrTag>
</div>
<div v-else>
<DsfrTag
class="fr-tag--sm warning-tag"
>
Indice de fiabilité : {{ Math.floor(confidence * 100) }}%
</DsfrTag>
<p class="warning-text">
Nous vous conseillons de faire appel à un expert pour confirmer cette réponse.
</p>
<ContactExpert v-if="isUserUsingCrosscall()" />
</div>
<div v-if="route.name !== 'IdentificationTypologyResult'|| isDummyTypology !== true">
<p
Arme factice de type {{ label }}
</h3>
<h3
v-else-if="stepsStore.selectedAlarmGun"
class="fr-alert__title"
>
Arme d'alarme de type {{ label }}
</h3>
<h3
v-else
class="fr-alert__title"
>
{{ label }}
</h3>
<template v-if="confidenceLevel !== 'low' && (route.name !== 'IdentificationTypologyResult' || isDummyTypology !== true)">
<h3
class="fr-alert__title"
data-testid="arm-category"
class="category fr-callout__title mt-3"
>
<img
class="px-2"
src="@/assets/guide-identification/icones/gun.jpg"
alt=""
>
<span>Catégorie {{ category }}
<p
v-if="disclaimer"
class="disclaimer"
><span
class="fr-icon-warning-fill text-blue"
aria-hidden="true"
/><span v-html="disclaimer" /></p>
</span>
</p>
<div
class="callout-mention"
>
<p v-for="mention in getMentionsFromCategories(category)">
{{ mention }}
</p>
</div>
</div>
<div>
<p class="mt-2 text-left text-base fr-callout__text">
<span class="font-normal">Typologie : </span>
<span v-if="isDummy">Arme factice de type </span>
<span v-else-if="stepsStore.selectedAlarmGun">Arme d'alarme de type </span>
<span class="typo">
{{ label }}
</span>
Catégorie {{ category }}
</h3>
<p v-for="mention in getMentionsFromCategories(category)">
{{ mention }}
</p>
</div>
</template>
</div>
</div>
</h2>
</div>
<div
v-if="route.name === 'IdentificationTypologyResult' && confidenceLevel !== 'low' && resultTree[typology]?.isDummyTypology"
class="fr-tile fr-enlarge-link p-4"
>
<div class="fr-tile__body pt-0">
<h3 class="fr-tile__title" />
<div class="flex">
<img
class="h-24"
src="@/assets/guide-identification/icones/warning.jpg"
alt="alt"
<div
v-if="disclaimer && confidenceLevel !== 'low' && (route.name !== 'IdentificationTypologyResult' || isDummyTypology !== true)"
class="fr-alert fr-alert--warning"
>
<p class="text-sm text-justify">
Basegun a identifié votre arme mais a besoin <span class="font-bold">d’informations complémentaires</span> pour vous donner sa catégorie légale.
</p>
</div>
</div>
</div>
<div v-else>
<template v-if="confidenceLevel !== 'low'">
<div class="fr-tile fr-enlarge-link fr-tile__body pt-0">
<h3 class="fr-tile__title" />
<div class="block">
<div class="flex">
<span><span
class="fr-icon-warning-fill text-blue mx-1"
aria-hidden="true"
/>Attention<span
class="fr-icon-warning-fill text-blue mx-1"
aria-hidden="true"
/></span>
</div>
<p class="text-sm text-justify">
Le résultat donné par Basegun n’emporte qu’une simple <span class="font-bold">valeur de renseignement</span>.
Pour faire référence dans une procédure, il <span class="font-bold">doit impérativement et réglementairement être validé</span>
<p v-html="disclaimer" />
</div>
<MissingCardAlert v-if="MEASURED_GUNS_TYPOLOGIES.includes(typology) && isCardDetected === false" />
<div
v-if="confidenceLevel !== 'low' && (route.name !== 'IdentificationTypologyResult' || isDummyTypology !== true)"
class="fr-callout mt-3"
>
<p class="fr-callout__text">
Le résultat donné par Basegun n’emporte qu’une simple <span
class="font-bold"
>valeur de
renseignement</span>.
Pour faire référence dans une procédure, il <span class="font-bold">doit impérativement et
règlementairement être validé</span>
par le biais d'un examen scientifique ou technique prévu par le code de procédure pénale.
</p>
</div>
<div
v-if="confidenceLevel !== 'low' && route.name === 'IdentificationTypologyResult' && isDummyTypology"
class="fr-alert fr-alert--warning"
>
<p>
Basegun a identifié votre arme mais a besoin <span
class="font-bold"
>d’informations complémentaires</span>
pour vous donner sa catégorie légale.
</p>
</div>
</div>
</template>
</div>
</div>
</div>
<div
Expand Down Expand Up @@ -340,13 +321,6 @@ function sendFeedback (isCorrect: boolean) {
font-weight: initial;
}
.callout-mention {
font-weight: normal;
margin-top: 10px;
font-style: italic;
line-height: 1.3rem;
}
.feedback {
display: flex;
align-items: center;
Expand Down Expand Up @@ -416,8 +390,4 @@ function sendFeedback (isCorrect: boolean) {
color: black;
}
.text-blue {
color: var(--blue-france-sun-113-625);
}
</style>
7 changes: 7 additions & 0 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createWebHistory, createRouter, type RouteRecordRaw, type RouteLocation

import { clearLocalStorage } from '@/utils/storage-utils.js'

import MissingCardPage from '@/views/MissingCardPage.vue'

const HomePage = () => import('@/views/HomePage.vue')
const StartPage = () => import('@/views/StartPage.vue')
const InstructionsPage = () => import('@/views/InstructionsPage.vue')
Expand Down Expand Up @@ -37,6 +39,11 @@ const routes: RouteRecordRaw[] = [
wholeLogo: true,
},
},
{
path: '/carte-manquante',
name: 'MissingCard',
component: MissingCardPage,
},
{
path: '/accueil',
name: 'StartPage',
Expand Down
17 changes: 10 additions & 7 deletions frontend/src/utils/firearms-utils/get-next-route-after-result.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { resultTree } from '@/utils/firearms-utils/index'
import { resultTree, MEASURED_GUNS_TYPOLOGIES } from '@/utils/firearms-utils/index'

export const getNextRouteAfterResult = ({ securingTutorial, confidenceLevel, typology, gunLength, gunBarrelLength }) => {
const noCardDetected = computed(() => gunLength === undefined || gunBarrelLength === undefined)
const isCardDetected = gunLength !== null && gunBarrelLength !== null
const isMeasuredGun = MEASURED_GUNS_TYPOLOGIES.includes(typology)

const isAbleToWatchTutorial = securingTutorial === true && confidenceLevel !== 'low' && noCardDetected
const isAbleToWatchTutorial = securingTutorial === true && confidenceLevel !== 'low'
if (!isAbleToWatchTutorial) {
return { name: 'IdentificationTypologyResult' }
console.log(isAbleToWatchTutorial)
console.log(isCardDetected)
if (isCardDetected === false && isMeasuredGun === true) { return { name: 'MissingCard' } } else { return { name: 'IdentificationTypologyResult' } }
}

const hasNoSecuringOptions = !resultTree[typology].isSecuringOptions && noCardDetected
const hasNoSecuringOptions = !resultTree[typology].isSecuringOptions && !isCardDetected
if (hasNoSecuringOptions) {
return { name: 'SecuringAchievement' }
}

const hasMoreThanOneOptions = resultTree[typology]?.options_step_1 && noCardDetected
const hasMoreThanOneOptions = resultTree[typology]?.options_step_1 && !isCardDetected
if (hasMoreThanOneOptions) {
return { name: 'SecuringSelectOption', params: { step: 1 } }
}

const hasSecuringOptions = resultTree[typology]?.options && noCardDetected
const hasSecuringOptions = resultTree[typology]?.options && !isCardDetected
return {
name: hasSecuringOptions ? 'SecuringSelectOption' : 'SecuringTutorialContent',
...(hasSecuringOptions ? { params: { step: 1 } } : {}),
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/utils/firearms-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,17 @@ export const identificationRoutePathsWithArmeAlarme = [
] as const

export const ALARM_GUNS_TYPOLOGIES = ['pistolet_semi_auto_moderne', 'revolver']
export const MEASURED_GUNS_TYPOLOGIES = [
'epaule_a_levier_sous_garde',
'epaule_a_pompe',
'epaule_a_un_coup_par_canon',
'epaule_a_verrou',
'epaule_semi_auto_style_chasse',
]
export const DISCLAIMERS = {
epaule_a_levier_verrou: 'Si la <strong>capacité est supérieure à 11 munitions</strong>, ou si le <strong>canon est lisse</strong> : <strong>Catégorie B</strong>. Si le <strong>canon est rayé</strong> : <strong>Catégorie C</strong>.',
epaule_a_levier_verrou: '<ul><li>Catégorie B : si la capacité > 11 munitions OU si le canon est lisse</li><li>Catégorie C : si la capacité < 11 munitions ET le canon est rayé</li></ul>',
semi_auto_style_chasse: 'Si la <strong>capacité est supérieure à 3 munitions</strong>, ou si le <strong>canon est lisse</strong> : <strong>Catégorie B</strong>. Si le <strong>canon est rayé</strong> : <strong>Catégorie C</strong>.',
epaule_a_pompe: 'Attention : Si la <strong>capacité maximale (chambre comprise) est supérieure à 5</strong>, ou si <strong>la crosse n’est pas fixe</strong>, ou si le <strong>canon est lisse</strong> : <strong>Catégorie B</strong>.',
epaule_semi_auto_style_militaire: 'Attention : Si à l’origine l’arme était à répétition automatique puis a été transformée en arme semi automatique, alors elle est de catégorie A. Si l’arme possède <strong>une crosse rétractable / pliable</strong> et qu’en configuration la plus courte elle mesure <strong>moins de 60 cm</strong> : <strong>Catégorie A</strong>.',
alarm_guns: 'Les armes d’alarmes sont susceptibles d’être modifiées pour tirer des munitions létales. Pour des raisons de sécurité, faites si possible expertiser l’arme.',
alarm_guns: 'Les <strong>armes d’alarmes</strong> sont susceptibles d’être <strong>modifiées pour tirer des munitions létales</strong>. Pour des raisons de sécurité, <strong>faites si possible expertiser l’arme.</strong>',
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ const alarmGunsOptions = alarmGuns.options.filter(gun => gun.typology === result
type="radio"
name="armeAlarme"
@input="stepsStore.selectedAlarmGun = ''"
>
<label
>
<label
class="fr-label"
for="radio-rich-2"
data-testid="aucune-correspondance"
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/views/InstructionsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,15 @@ function onFileSelected (event: InputEvent & { target: InputEvent['target'] & {
class="lg:absolute lg:inset-x-0 fr-col-lg-6 fr-col-sm-9 mx-auto"
>
<div class="fr-alert fr-alert--info mt-5 mx-5">
<h3 class="fr-alert__title mb-8">Pour un résultat optimal :</h3>
<h3 class="fr-alert__title mb-8">
Pour un résultat optimal :
</h3>
<p>1 - Présenter le <span class="font-bold">canon vers la droite</span>.</p>
<p>2 - Ne photographier qu'<span class="font-bold">une seule</span> arme.</p>
<p>3 - Placer l'arme <span class="font-bold">en entier</span> et <span class="font-bold">au centre de la photo</span>.</p>
<p class="mt-3">Pour les armes d'épaule :</p>
<p class="mt-3">
Pour les armes d'épaule :
</p>
<p>4 - Placer <span class="font-bold">une carte à côté de l'arme</span> pour permettre de la mesurer.</p>
</div>

Expand Down
Loading

0 comments on commit 81633ef

Please sign in to comment.