fcad-core-dragon 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +7 -7
- package/.gitlab-ci.yml +124 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +46 -16
- package/CHANGELOG +520 -520
- package/README.md +57 -57
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/junit-report.xml +182 -0
- package/package.json +66 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +3 -2
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +325 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +287 -284
- package/src/components/AppCompInputTextNx.vue +156 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
- package/src/components/AppCompInputTextToFillNx.vue +316 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -2
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2290 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +355 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +377 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +493 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -15
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +27 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompQuizNext.spec.js +114 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +28 -19
- package/vitest.setup.js +28 -0
- package/src/components/AppBaseButton.test.js +0 -21
|
@@ -1,508 +1,508 @@
|
|
|
1
|
-
<!-- Composante qui génère les boutons d'embranchment d'une page.
|
|
2
|
-
|
|
3
|
-
**props**
|
|
4
|
-
*********************************
|
|
5
|
-
customButtons (optionel):
|
|
6
|
-
Tableau d'objets contenant les informations à propos des boutons custom à créer pour chaque embranchement.
|
|
7
|
-
exemple pour une page avec 2 embranchments:
|
|
8
|
-
[
|
|
9
|
-
{ name: 'bouton_A02_P01_E01', brchName: 'P02_E01' },
|
|
10
|
-
{ name: 'bouton_A02_P01_E02', brchName: 'P02_E02' }
|
|
11
|
-
]
|
|
12
|
-
un template custom peut ensuite être donner pour habiller chaque bouton en faisant reférence au nom dynamique du slot
|
|
13
|
-
<template #P02_E01></template>
|
|
14
|
-
*********************************
|
|
15
|
-
card (optionel):
|
|
16
|
-
Objet qui défini le contenu de la composante b-card
|
|
17
|
-
exemple:
|
|
18
|
-
{
|
|
19
|
-
imgFile: '/src/assets/img/imgCarte.jpg', (l'image doit être appelé par un import ex : import imgCarte1 from '@/assets/img/imgCarte.jpg')
|
|
20
|
-
title: 'Titre embranchment',
|
|
21
|
-
text: 'Ceci est un embranchment factice, affiché sous la forme de carte.'
|
|
22
|
-
brchName: 'P02_E01'
|
|
23
|
-
btnTitle: ''
|
|
24
|
-
}
|
|
25
|
-
*********************************
|
|
26
|
-
Si la composante est appelée sans prop, les boutons par défaut seront générés.
|
|
27
|
-
-->
|
|
28
|
-
<template>
|
|
29
|
-
<v-row id="branch-buttons-component">
|
|
30
|
-
<app-base-error-display
|
|
31
|
-
v-if="hasErrors"
|
|
32
|
-
:error-group="'component'"
|
|
33
|
-
:error-title="`ERREUR: COMPOSANT D'EMBRANCHEMENT`"
|
|
34
|
-
:errors-list="hasErrors"
|
|
35
|
-
:error-text="`Vous avez une/des erreur(s) lors de la création des
|
|
36
|
-
${(() => (isCard ? 'cartes' : `boutons`))()} d'embranchments.`"
|
|
37
|
-
/>
|
|
38
|
-
<v-row v-else>
|
|
39
|
-
<v-col
|
|
40
|
-
v-for="branch of branchsStateData"
|
|
41
|
-
:id="`branch_${branch.id}`"
|
|
42
|
-
:key="branch.id"
|
|
43
|
-
class="branch-btn"
|
|
44
|
-
>
|
|
45
|
-
<div
|
|
46
|
-
v-if="isCustomButton"
|
|
47
|
-
:id="getMatchingElement(branch.id).name"
|
|
48
|
-
class="branch-btn-wrapper"
|
|
49
|
-
>
|
|
50
|
-
<slot :name="branch.id">
|
|
51
|
-
<p class="name-btn-branch">
|
|
52
|
-
{{ getMatchingElement(branch.id).name }}
|
|
53
|
-
</p>
|
|
54
|
-
</slot>
|
|
55
|
-
|
|
56
|
-
<app-comp-button-progress
|
|
57
|
-
:set-target="sidebar"
|
|
58
|
-
:percent="branch.progression ? branch.progression : 0"
|
|
59
|
-
:branch-data="branch"
|
|
60
|
-
:btn-title="getMatchingElement(branch.id).btnTitle"
|
|
61
|
-
no-interaction
|
|
62
|
-
></app-comp-button-progress>
|
|
63
|
-
</div>
|
|
64
|
-
<div
|
|
65
|
-
v-else-if="isCard && cards.length"
|
|
66
|
-
class="branch-btn-wrapper branch-btn-card"
|
|
67
|
-
>
|
|
68
|
-
<v-card class="mx-auto" max-width="15rem">
|
|
69
|
-
<v-card-title v-html="branch.title"></v-card-title>
|
|
70
|
-
<app-comp-button-progress
|
|
71
|
-
:set-target="sidebar"
|
|
72
|
-
:percent="branch.progression ? branch.progression : 0"
|
|
73
|
-
:branch-data="branch"
|
|
74
|
-
:btn-title="getMatchingElement(branch.id).btnTitle"
|
|
75
|
-
></app-comp-button-progress>
|
|
76
|
-
<v-img :src="branch.imgFile" :alt="`${branch.imgAlt}`" cover />
|
|
77
|
-
<v-card-text v-html="branch.text"></v-card-text>
|
|
78
|
-
</v-card>
|
|
79
|
-
</div>
|
|
80
|
-
<div v-else class="branch-btn-wrapper">
|
|
81
|
-
<app-comp-button-progress
|
|
82
|
-
:set-target="sidebar"
|
|
83
|
-
:percent="branch.progression ? branch.progression : 0"
|
|
84
|
-
:branch-data="branch"
|
|
85
|
-
:btn-title="branch.text"
|
|
86
|
-
></app-comp-button-progress>
|
|
87
|
-
</div>
|
|
88
|
-
</v-col>
|
|
89
|
-
</v-row>
|
|
90
|
-
</v-row>
|
|
91
|
-
</template>
|
|
92
|
-
<script>
|
|
93
|
-
import { mapState } from 'pinia'
|
|
94
|
-
import { useAppStore } from '../module/stores/appStore'
|
|
95
|
-
import AppCompButtonProgress from './AppCompButtonProgress.vue'
|
|
96
|
-
|
|
97
|
-
export default {
|
|
98
|
-
components: {
|
|
99
|
-
AppCompButtonProgress
|
|
100
|
-
},
|
|
101
|
-
props: {
|
|
102
|
-
consigne: {
|
|
103
|
-
type: Boolean,
|
|
104
|
-
default: false
|
|
105
|
-
},
|
|
106
|
-
sidebar: {
|
|
107
|
-
type: String,
|
|
108
|
-
default: 'branch-viewer'
|
|
109
|
-
},
|
|
110
|
-
btnTitle: {
|
|
111
|
-
type: String,
|
|
112
|
-
default: ' '
|
|
113
|
-
},
|
|
114
|
-
cards: {
|
|
115
|
-
type: Array,
|
|
116
|
-
default: () => null,
|
|
117
|
-
validator(dataArray) {
|
|
118
|
-
let isValid = true
|
|
119
|
-
if (import.meta.env.DEV) {
|
|
120
|
-
let errMsg = false
|
|
121
|
-
|
|
122
|
-
//card must have at least 2 elements
|
|
123
|
-
if (dataArray.length < 1) {
|
|
124
|
-
isValid = false
|
|
125
|
-
errMsg = `\n card require at least 1 element`
|
|
126
|
-
} else {
|
|
127
|
-
for (let el of dataArray) {
|
|
128
|
-
//Each element in card must be of type Object and can not be empty
|
|
129
|
-
if (el.constructor !== Object) {
|
|
130
|
-
isValid = false
|
|
131
|
-
errMsg = `\n Element in card must be of type Object`
|
|
132
|
-
break
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (errMsg) console.error(`🧱 app-comp-branch-buttons \n ${errMsg}`)
|
|
138
|
-
}
|
|
139
|
-
return isValid
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
customButtons: {
|
|
143
|
-
type: Array,
|
|
144
|
-
default: () => null,
|
|
145
|
-
validator: (dataArray) => {
|
|
146
|
-
let isValid = true
|
|
147
|
-
if (import.meta.env.DEV) {
|
|
148
|
-
let errMsg = false
|
|
149
|
-
//card must have at least 1 elements
|
|
150
|
-
if (dataArray.length < 1) {
|
|
151
|
-
isValid = false
|
|
152
|
-
errMsg = `\n customButton require at least 1 element`
|
|
153
|
-
} else {
|
|
154
|
-
for (let el of dataArray) {
|
|
155
|
-
//Each element in card must be of type Object and can not be empty
|
|
156
|
-
if (el.constructor !== Object) {
|
|
157
|
-
isValid = false
|
|
158
|
-
errMsg = `\n Element in customButton must be of type Object`
|
|
159
|
-
break
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
//Start validation of keys if no error detected
|
|
163
|
-
if (isValid) {
|
|
164
|
-
const requiredKeys = ['name', 'brchName']
|
|
165
|
-
for (let el of dataArray) {
|
|
166
|
-
//Validate that require key exist for each element in card
|
|
167
|
-
for (let k of requiredKeys) {
|
|
168
|
-
let index = dataArray.indexOf(el)
|
|
169
|
-
|
|
170
|
-
if (!el[k]) {
|
|
171
|
-
isValid = false
|
|
172
|
-
errMsg = `\n 💥 Missing 👉 ${k} 👈 in object ${
|
|
173
|
-
index + 1
|
|
174
|
-
} in ➡ customButtom \n 🚩Allowed indexes are: 👉 ${requiredKeys}`
|
|
175
|
-
break
|
|
176
|
-
} else {
|
|
177
|
-
if (el[k].constructor !== String) {
|
|
178
|
-
errMsg = `\n 💥 Invalid 👉 ${k} 👈 declaration in ➡ object ${
|
|
179
|
-
index + 1
|
|
180
|
-
} ➡ of customButton. \n 🚩 Must be of type {String}`
|
|
181
|
-
isValid = false
|
|
182
|
-
break
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (!isValid) break
|
|
186
|
-
}
|
|
187
|
-
if (!isValid) break
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (errMsg)
|
|
191
|
-
console.error(`🧱 app-comp-branch-buttons \n ${errMsg}`)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return isValid
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
data() {
|
|
199
|
-
const svgRadius = 23
|
|
200
|
-
return {
|
|
201
|
-
errors: [],
|
|
202
|
-
radius: svgRadius,
|
|
203
|
-
circumference: svgRadius * 2 * Math.PI,
|
|
204
|
-
branchs: null,
|
|
205
|
-
branchsStateData: null,
|
|
206
|
-
idActivity: null,
|
|
207
|
-
isCustomButton: false,
|
|
208
|
-
isCard: false,
|
|
209
|
-
customButtonStyle: null,
|
|
210
|
-
status: {
|
|
211
|
-
NEW: 'new',
|
|
212
|
-
STARTED: 'started',
|
|
213
|
-
COMPLETE: 'complete'
|
|
214
|
-
},
|
|
215
|
-
hasErr: null
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
computed: {
|
|
219
|
-
...mapState(useAppStore, [
|
|
220
|
-
'getAllCompleted',
|
|
221
|
-
'getAllActivities',
|
|
222
|
-
'getConnectionInfo'
|
|
223
|
-
]),
|
|
224
|
-
hasErrors() {
|
|
225
|
-
let error = false
|
|
226
|
-
if (import.meta.env.DEV) {
|
|
227
|
-
if (this.isCard) error = this.validateProp('cards')
|
|
228
|
-
else if (this.isCustomButton) error = this.validateProp('customButtons')
|
|
229
|
-
else error = false
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return error
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
watch: {
|
|
236
|
-
getAllCompleted: {
|
|
237
|
-
deep: true,
|
|
238
|
-
//watch the completions to update the branches progression
|
|
239
|
-
handler() {
|
|
240
|
-
if (
|
|
241
|
-
this.getAllCompleted[this.idActivity] &&
|
|
242
|
-
this.getAllCompleted[this.idActivity].length
|
|
243
|
-
)
|
|
244
|
-
this.getbranchsData()
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
mounted() {
|
|
249
|
-
this.isCustomButton = this.customButtons && this.customButtons.length > 0
|
|
250
|
-
this.isCard = this.cards && Object.keys(this.cards).length != 0
|
|
251
|
-
this.idActivity = this.$router.currentRoute.value.meta.activity_ref
|
|
252
|
-
this.branchs = this.$router.currentRoute.value.meta.children
|
|
253
|
-
this.getbranchsData()
|
|
254
|
-
},
|
|
255
|
-
methods: {
|
|
256
|
-
/**
|
|
257
|
-
* @description Method to get the progression for a branch
|
|
258
|
-
*
|
|
259
|
-
*/
|
|
260
|
-
getbranchsData() {
|
|
261
|
-
this.branchsStateData = []
|
|
262
|
-
this.branchs.forEach((branch, index) => {
|
|
263
|
-
let branchData = {}
|
|
264
|
-
branchData.id = branch._ref
|
|
265
|
-
branchData.route = branch._namedRoute
|
|
266
|
-
let { state = 'new', progression = 0 } = this.getBranchProgression(
|
|
267
|
-
branchData.id
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
branchData.state = state
|
|
271
|
-
branchData.progression = progression
|
|
272
|
-
if (isNaN(branchData.progression)) branchData.progression = 0
|
|
273
|
-
|
|
274
|
-
if (this.isCard) {
|
|
275
|
-
const { title, text, imgFile, imgAlt, btnTitle } =
|
|
276
|
-
this.getMatchingElement(branchData.id)
|
|
277
|
-
|
|
278
|
-
branchData = {
|
|
279
|
-
...branchData,
|
|
280
|
-
text,
|
|
281
|
-
title,
|
|
282
|
-
imgFile,
|
|
283
|
-
imgAlt,
|
|
284
|
-
btnTitle
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
typeof branchData.imgAlt === 'undefined' &&
|
|
289
|
-
branchData.imgFile === 'undefined' &&
|
|
290
|
-
import.meta.env.DEV
|
|
291
|
-
) {
|
|
292
|
-
branchData.imgAlt = ''
|
|
293
|
-
console.warn(
|
|
294
|
-
`Bouton d’embranchement: ALT image sans valeur définie pour ${branchData.id}`
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
this.branchsStateData[index] = branchData
|
|
299
|
-
})
|
|
300
|
-
},
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* @description Method to get the progression for a branch
|
|
304
|
-
* @param {String} idBranch the id of the branch ex: "P01_E01"
|
|
305
|
-
* @return {Number} Return the percentage of completion (from 0 to 100) and the current state of a specific branch
|
|
306
|
-
*/
|
|
307
|
-
getBranchProgression(idBranch) {
|
|
308
|
-
let result = { state: null, progression: null }
|
|
309
|
-
|
|
310
|
-
if (!this.getAllCompleted[this.idActivity]) return result // no progression found for this activity return result
|
|
311
|
-
|
|
312
|
-
const total = this.getBranchPagesCount(idBranch) // get all pages for in this branch
|
|
313
|
-
//get all completed branches pages
|
|
314
|
-
const completedPages = this.getAllCompleted[this.idActivity].filter(
|
|
315
|
-
(page) => {
|
|
316
|
-
return Object.keys(page)[0].search(idBranch) !== -1
|
|
317
|
-
}
|
|
318
|
-
)
|
|
319
|
-
result.progression = Math.floor((completedPages.length / total) * 100)
|
|
320
|
-
|
|
321
|
-
if (isNaN(result.progression)) result.progression = 0
|
|
322
|
-
|
|
323
|
-
if (result.progression === 0) {
|
|
324
|
-
result.state = this.status.NEW
|
|
325
|
-
} else if (result.progression === 100) {
|
|
326
|
-
result.state = this.status.COMPLETE
|
|
327
|
-
} else {
|
|
328
|
-
result.state = this.status.STARTED
|
|
329
|
-
}
|
|
330
|
-
return result
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* @description Method to get the numbers of pages in a branch
|
|
335
|
-
* @param {String} idBranch the id of the branch ex: "A02_E01_P01"
|
|
336
|
-
* @return {Array} /Return the total of pages in a specific branch
|
|
337
|
-
*/
|
|
338
|
-
getBranchPagesCount(idBranch) {
|
|
339
|
-
if (this.$router.currentRoute.value.meta.type !== 'branching') return
|
|
340
|
-
|
|
341
|
-
const allPages = this.getAllActivities()
|
|
342
|
-
.list.get(this.idActivity)
|
|
343
|
-
.get(this.$router.currentRoute.value.meta.id)
|
|
344
|
-
.get(idBranch)
|
|
345
|
-
|
|
346
|
-
return allPages.size ? allPages.size : 1
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* @description Method to validate data in the prop card|customButton.
|
|
351
|
-
* @param {String} propName The name of the property card|customButton
|
|
352
|
-
* @return {String} errMsg false | string message
|
|
353
|
-
*/
|
|
354
|
-
validateProp(propName) {
|
|
355
|
-
let isValid = true
|
|
356
|
-
let errMsg = false
|
|
357
|
-
|
|
358
|
-
switch (propName) {
|
|
359
|
-
case 'cards': {
|
|
360
|
-
// verify that numbers of element is equal to number
|
|
361
|
-
//card must have at least 2 elements
|
|
362
|
-
if (
|
|
363
|
-
this.cards.constructor !== Array ||
|
|
364
|
-
this.cards.length !== this.branchs.length
|
|
365
|
-
) {
|
|
366
|
-
isValid = false
|
|
367
|
-
errMsg = `La propriété cards doit être un tableau contenant ${this.branchs.length} elements`
|
|
368
|
-
} else {
|
|
369
|
-
//Start validation of content of each object in cards
|
|
370
|
-
for (let el of this.cards) {
|
|
371
|
-
//Each element in card must be of type Object and can not be empty
|
|
372
|
-
if (el.constructor !== Object) {
|
|
373
|
-
isValid = false
|
|
374
|
-
let index = this.cards.indexOf(el)
|
|
375
|
-
errMsg = `Le data pour la carte ${index + 1} n'est pas de type Object`
|
|
376
|
-
}
|
|
377
|
-
if (!isValid) break
|
|
378
|
-
}
|
|
379
|
-
//Start validation of required keys
|
|
380
|
-
if (isValid) {
|
|
381
|
-
for (let el of this.cards) {
|
|
382
|
-
let index = this.cards.indexOf(el)
|
|
383
|
-
|
|
384
|
-
// Start validation of existing branch for this element
|
|
385
|
-
let searchEl = this.branchs.find((b) =>
|
|
386
|
-
b._ref.includes(el.brchName)
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
if (!searchEl) {
|
|
390
|
-
errMsg = `Aucune branch correspondante avec le nom ${el.brchName} pour la carte ${
|
|
391
|
-
index + 1
|
|
392
|
-
}. Êtes vous sûre d'avoir indiqué la bonne branch?`
|
|
393
|
-
isValid = false
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (!isValid) break
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
break
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
case 'customButtons': {
|
|
404
|
-
if (
|
|
405
|
-
this.customButtons.constructor !== Array ||
|
|
406
|
-
this.customButtons.length !== this.branchs.length
|
|
407
|
-
) {
|
|
408
|
-
isValid = false
|
|
409
|
-
errMsg = `La propriété customButtons doit être un tableau contenant ${this.branchs.length} elements`
|
|
410
|
-
} else {
|
|
411
|
-
//Start validation of content of each object in customButtons
|
|
412
|
-
for (let el of this.customButtons) {
|
|
413
|
-
//Each element in card must be of type Object and can not be empty
|
|
414
|
-
if (el.constructor !== Object) {
|
|
415
|
-
isValid = false
|
|
416
|
-
let index = this.customButtons.indexOf(el)
|
|
417
|
-
errMsg = `Le data pour le button ${index + 1} n'est pas de type {Object}`
|
|
418
|
-
}
|
|
419
|
-
if (!isValid) break
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
//Start validation of required keys
|
|
423
|
-
if (isValid) {
|
|
424
|
-
const requiredKeys = ['name', 'brchName']
|
|
425
|
-
|
|
426
|
-
for (let el of this.customButtons) {
|
|
427
|
-
//Validate that require key exist for each element for customButton
|
|
428
|
-
let index = this.customButtons.indexOf(el)
|
|
429
|
-
for (let k of requiredKeys) {
|
|
430
|
-
if (!el[k]) {
|
|
431
|
-
isValid = false
|
|
432
|
-
errMsg = `l'Attribut 👉 ${k} 👈 pour la button ${index + 1} n'as pas été défini`
|
|
433
|
-
break
|
|
434
|
-
} else {
|
|
435
|
-
// Expecting String for properties name and brchName
|
|
436
|
-
if (
|
|
437
|
-
['name', 'brchName'].includes(k) &&
|
|
438
|
-
el[k].constructor !== String
|
|
439
|
-
) {
|
|
440
|
-
errMsg = `l'Attribut 👉 ${k} 👈 pour la button ${
|
|
441
|
-
index + 1
|
|
442
|
-
} doit être de type {String}`
|
|
443
|
-
isValid = false
|
|
444
|
-
break
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
if (!isValid) break
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Start validation of existing branch for this element
|
|
451
|
-
let searchEl = this.branchs.find((b) =>
|
|
452
|
-
b._ref.includes(el.brchName)
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
if (!searchEl) {
|
|
456
|
-
errMsg = `Aucune branch correspondante avec le nom ${
|
|
457
|
-
el.brchName
|
|
458
|
-
} pour le bouton ${index + 1}. Êtes vous sûre d'avoir indiqué la bonne branch?`
|
|
459
|
-
isValid = false
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (!isValid) break
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
break
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return errMsg
|
|
471
|
-
},
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* @description methode to retive matching data of branch in the prop card| customButton
|
|
475
|
-
* @param {String} id The id of the branch
|
|
476
|
-
* @return {Object} Matching element from
|
|
477
|
-
*/
|
|
478
|
-
getMatchingElement(id) {
|
|
479
|
-
if (this.isCustomButton) {
|
|
480
|
-
return this.customButtons.find((c) => c.brchName === id)
|
|
481
|
-
}
|
|
482
|
-
if (this.isCard) {
|
|
483
|
-
return this.cards.find((c) => c.brchName === id)
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
</script>
|
|
489
|
-
|
|
490
|
-
<style lang="scss">
|
|
491
|
-
.branch-btn-wrapper {
|
|
492
|
-
position: relative;
|
|
493
|
-
|
|
494
|
-
.branch-btn-custom {
|
|
495
|
-
background-color: transparent;
|
|
496
|
-
border: none;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
.v-card {
|
|
501
|
-
position: relative;
|
|
502
|
-
.button-progress-wrapper {
|
|
503
|
-
position: absolute;
|
|
504
|
-
right: 55px;
|
|
505
|
-
top: 5px;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
</style>
|
|
1
|
+
<!-- Composante qui génère les boutons d'embranchment d'une page.
|
|
2
|
+
|
|
3
|
+
**props**
|
|
4
|
+
*********************************
|
|
5
|
+
customButtons (optionel):
|
|
6
|
+
Tableau d'objets contenant les informations à propos des boutons custom à créer pour chaque embranchement.
|
|
7
|
+
exemple pour une page avec 2 embranchments:
|
|
8
|
+
[
|
|
9
|
+
{ name: 'bouton_A02_P01_E01', brchName: 'P02_E01' },
|
|
10
|
+
{ name: 'bouton_A02_P01_E02', brchName: 'P02_E02' }
|
|
11
|
+
]
|
|
12
|
+
un template custom peut ensuite être donner pour habiller chaque bouton en faisant reférence au nom dynamique du slot
|
|
13
|
+
<template #P02_E01></template>
|
|
14
|
+
*********************************
|
|
15
|
+
card (optionel):
|
|
16
|
+
Objet qui défini le contenu de la composante b-card
|
|
17
|
+
exemple:
|
|
18
|
+
{
|
|
19
|
+
imgFile: '/src/assets/img/imgCarte.jpg', (l'image doit être appelé par un import ex : import imgCarte1 from '@/assets/img/imgCarte.jpg')
|
|
20
|
+
title: 'Titre embranchment',
|
|
21
|
+
text: 'Ceci est un embranchment factice, affiché sous la forme de carte.'
|
|
22
|
+
brchName: 'P02_E01'
|
|
23
|
+
btnTitle: ''
|
|
24
|
+
}
|
|
25
|
+
*********************************
|
|
26
|
+
Si la composante est appelée sans prop, les boutons par défaut seront générés.
|
|
27
|
+
-->
|
|
28
|
+
<template>
|
|
29
|
+
<v-row id="branch-buttons-component">
|
|
30
|
+
<app-base-error-display
|
|
31
|
+
v-if="hasErrors"
|
|
32
|
+
:error-group="'component'"
|
|
33
|
+
:error-title="`ERREUR: COMPOSANT D'EMBRANCHEMENT`"
|
|
34
|
+
:errors-list="hasErrors"
|
|
35
|
+
:error-text="`Vous avez une/des erreur(s) lors de la création des
|
|
36
|
+
${(() => (isCard ? 'cartes' : `boutons`))()} d'embranchments.`"
|
|
37
|
+
/>
|
|
38
|
+
<v-row v-else>
|
|
39
|
+
<v-col
|
|
40
|
+
v-for="branch of branchsStateData"
|
|
41
|
+
:id="`branch_${branch.id}`"
|
|
42
|
+
:key="branch.id"
|
|
43
|
+
class="branch-btn"
|
|
44
|
+
>
|
|
45
|
+
<div
|
|
46
|
+
v-if="isCustomButton"
|
|
47
|
+
:id="getMatchingElement(branch.id).name"
|
|
48
|
+
class="branch-btn-wrapper"
|
|
49
|
+
>
|
|
50
|
+
<slot :name="branch.id">
|
|
51
|
+
<p class="name-btn-branch">
|
|
52
|
+
{{ getMatchingElement(branch.id).name }}
|
|
53
|
+
</p>
|
|
54
|
+
</slot>
|
|
55
|
+
|
|
56
|
+
<app-comp-button-progress
|
|
57
|
+
:set-target="sidebar"
|
|
58
|
+
:percent="branch.progression ? branch.progression : 0"
|
|
59
|
+
:branch-data="branch"
|
|
60
|
+
:btn-title="getMatchingElement(branch.id).btnTitle"
|
|
61
|
+
no-interaction
|
|
62
|
+
></app-comp-button-progress>
|
|
63
|
+
</div>
|
|
64
|
+
<div
|
|
65
|
+
v-else-if="isCard && cards.length"
|
|
66
|
+
class="branch-btn-wrapper branch-btn-card"
|
|
67
|
+
>
|
|
68
|
+
<v-card class="mx-auto" max-width="15rem">
|
|
69
|
+
<v-card-title v-html="branch.title"></v-card-title>
|
|
70
|
+
<app-comp-button-progress
|
|
71
|
+
:set-target="sidebar"
|
|
72
|
+
:percent="branch.progression ? branch.progression : 0"
|
|
73
|
+
:branch-data="branch"
|
|
74
|
+
:btn-title="getMatchingElement(branch.id).btnTitle"
|
|
75
|
+
></app-comp-button-progress>
|
|
76
|
+
<v-img :src="branch.imgFile" :alt="`${branch.imgAlt}`" cover />
|
|
77
|
+
<v-card-text v-html="branch.text"></v-card-text>
|
|
78
|
+
</v-card>
|
|
79
|
+
</div>
|
|
80
|
+
<div v-else class="branch-btn-wrapper">
|
|
81
|
+
<app-comp-button-progress
|
|
82
|
+
:set-target="sidebar"
|
|
83
|
+
:percent="branch.progression ? branch.progression : 0"
|
|
84
|
+
:branch-data="branch"
|
|
85
|
+
:btn-title="branch.text"
|
|
86
|
+
></app-comp-button-progress>
|
|
87
|
+
</div>
|
|
88
|
+
</v-col>
|
|
89
|
+
</v-row>
|
|
90
|
+
</v-row>
|
|
91
|
+
</template>
|
|
92
|
+
<script>
|
|
93
|
+
import { mapState } from 'pinia'
|
|
94
|
+
import { useAppStore } from '../module/stores/appStore'
|
|
95
|
+
import AppCompButtonProgress from './AppCompButtonProgress.vue'
|
|
96
|
+
|
|
97
|
+
export default {
|
|
98
|
+
components: {
|
|
99
|
+
AppCompButtonProgress
|
|
100
|
+
},
|
|
101
|
+
props: {
|
|
102
|
+
consigne: {
|
|
103
|
+
type: Boolean,
|
|
104
|
+
default: false
|
|
105
|
+
},
|
|
106
|
+
sidebar: {
|
|
107
|
+
type: String,
|
|
108
|
+
default: 'branch-viewer'
|
|
109
|
+
},
|
|
110
|
+
btnTitle: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: ' '
|
|
113
|
+
},
|
|
114
|
+
cards: {
|
|
115
|
+
type: Array,
|
|
116
|
+
default: () => null,
|
|
117
|
+
validator(dataArray) {
|
|
118
|
+
let isValid = true
|
|
119
|
+
if (import.meta.env.DEV) {
|
|
120
|
+
let errMsg = false
|
|
121
|
+
|
|
122
|
+
//card must have at least 2 elements
|
|
123
|
+
if (dataArray.length < 1) {
|
|
124
|
+
isValid = false
|
|
125
|
+
errMsg = `\n card require at least 1 element`
|
|
126
|
+
} else {
|
|
127
|
+
for (let el of dataArray) {
|
|
128
|
+
//Each element in card must be of type Object and can not be empty
|
|
129
|
+
if (el.constructor !== Object) {
|
|
130
|
+
isValid = false
|
|
131
|
+
errMsg = `\n Element in card must be of type Object`
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (errMsg) console.error(`🧱 app-comp-branch-buttons \n ${errMsg}`)
|
|
138
|
+
}
|
|
139
|
+
return isValid
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
customButtons: {
|
|
143
|
+
type: Array,
|
|
144
|
+
default: () => null,
|
|
145
|
+
validator: (dataArray) => {
|
|
146
|
+
let isValid = true
|
|
147
|
+
if (import.meta.env.DEV) {
|
|
148
|
+
let errMsg = false
|
|
149
|
+
//card must have at least 1 elements
|
|
150
|
+
if (dataArray.length < 1) {
|
|
151
|
+
isValid = false
|
|
152
|
+
errMsg = `\n customButton require at least 1 element`
|
|
153
|
+
} else {
|
|
154
|
+
for (let el of dataArray) {
|
|
155
|
+
//Each element in card must be of type Object and can not be empty
|
|
156
|
+
if (el.constructor !== Object) {
|
|
157
|
+
isValid = false
|
|
158
|
+
errMsg = `\n Element in customButton must be of type Object`
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//Start validation of keys if no error detected
|
|
163
|
+
if (isValid) {
|
|
164
|
+
const requiredKeys = ['name', 'brchName']
|
|
165
|
+
for (let el of dataArray) {
|
|
166
|
+
//Validate that require key exist for each element in card
|
|
167
|
+
for (let k of requiredKeys) {
|
|
168
|
+
let index = dataArray.indexOf(el)
|
|
169
|
+
|
|
170
|
+
if (!el[k]) {
|
|
171
|
+
isValid = false
|
|
172
|
+
errMsg = `\n 💥 Missing 👉 ${k} 👈 in object ${
|
|
173
|
+
index + 1
|
|
174
|
+
} in ➡ customButtom \n 🚩Allowed indexes are: 👉 ${requiredKeys}`
|
|
175
|
+
break
|
|
176
|
+
} else {
|
|
177
|
+
if (el[k].constructor !== String) {
|
|
178
|
+
errMsg = `\n 💥 Invalid 👉 ${k} 👈 declaration in ➡ object ${
|
|
179
|
+
index + 1
|
|
180
|
+
} ➡ of customButton. \n 🚩 Must be of type {String}`
|
|
181
|
+
isValid = false
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!isValid) break
|
|
186
|
+
}
|
|
187
|
+
if (!isValid) break
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (errMsg)
|
|
191
|
+
console.error(`🧱 app-comp-branch-buttons \n ${errMsg}`)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return isValid
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
data() {
|
|
199
|
+
const svgRadius = 23
|
|
200
|
+
return {
|
|
201
|
+
errors: [],
|
|
202
|
+
radius: svgRadius,
|
|
203
|
+
circumference: svgRadius * 2 * Math.PI,
|
|
204
|
+
branchs: null,
|
|
205
|
+
branchsStateData: null,
|
|
206
|
+
idActivity: null,
|
|
207
|
+
isCustomButton: false,
|
|
208
|
+
isCard: false,
|
|
209
|
+
customButtonStyle: null,
|
|
210
|
+
status: {
|
|
211
|
+
NEW: 'new',
|
|
212
|
+
STARTED: 'started',
|
|
213
|
+
COMPLETE: 'complete'
|
|
214
|
+
},
|
|
215
|
+
hasErr: null
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
computed: {
|
|
219
|
+
...mapState(useAppStore, [
|
|
220
|
+
'getAllCompleted',
|
|
221
|
+
'getAllActivities',
|
|
222
|
+
'getConnectionInfo'
|
|
223
|
+
]),
|
|
224
|
+
hasErrors() {
|
|
225
|
+
let error = false
|
|
226
|
+
if (import.meta.env.DEV) {
|
|
227
|
+
if (this.isCard) error = this.validateProp('cards')
|
|
228
|
+
else if (this.isCustomButton) error = this.validateProp('customButtons')
|
|
229
|
+
else error = false
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return error
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
watch: {
|
|
236
|
+
getAllCompleted: {
|
|
237
|
+
deep: true,
|
|
238
|
+
//watch the completions to update the branches progression
|
|
239
|
+
handler() {
|
|
240
|
+
if (
|
|
241
|
+
this.getAllCompleted[this.idActivity] &&
|
|
242
|
+
this.getAllCompleted[this.idActivity].length
|
|
243
|
+
)
|
|
244
|
+
this.getbranchsData()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
mounted() {
|
|
249
|
+
this.isCustomButton = this.customButtons && this.customButtons.length > 0
|
|
250
|
+
this.isCard = this.cards && Object.keys(this.cards).length != 0
|
|
251
|
+
this.idActivity = this.$router.currentRoute.value.meta.activity_ref
|
|
252
|
+
this.branchs = this.$router.currentRoute.value.meta.children
|
|
253
|
+
this.getbranchsData()
|
|
254
|
+
},
|
|
255
|
+
methods: {
|
|
256
|
+
/**
|
|
257
|
+
* @description Method to get the progression for a branch
|
|
258
|
+
*
|
|
259
|
+
*/
|
|
260
|
+
getbranchsData() {
|
|
261
|
+
this.branchsStateData = []
|
|
262
|
+
this.branchs.forEach((branch, index) => {
|
|
263
|
+
let branchData = {}
|
|
264
|
+
branchData.id = branch._ref
|
|
265
|
+
branchData.route = branch._namedRoute
|
|
266
|
+
let { state = 'new', progression = 0 } = this.getBranchProgression(
|
|
267
|
+
branchData.id
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
branchData.state = state
|
|
271
|
+
branchData.progression = progression
|
|
272
|
+
if (isNaN(branchData.progression)) branchData.progression = 0
|
|
273
|
+
|
|
274
|
+
if (this.isCard) {
|
|
275
|
+
const { title, text, imgFile, imgAlt, btnTitle } =
|
|
276
|
+
this.getMatchingElement(branchData.id)
|
|
277
|
+
|
|
278
|
+
branchData = {
|
|
279
|
+
...branchData,
|
|
280
|
+
text,
|
|
281
|
+
title,
|
|
282
|
+
imgFile,
|
|
283
|
+
imgAlt,
|
|
284
|
+
btnTitle
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
typeof branchData.imgAlt === 'undefined' &&
|
|
289
|
+
branchData.imgFile === 'undefined' &&
|
|
290
|
+
import.meta.env.DEV
|
|
291
|
+
) {
|
|
292
|
+
branchData.imgAlt = ''
|
|
293
|
+
console.warn(
|
|
294
|
+
`Bouton d’embranchement: ALT image sans valeur définie pour ${branchData.id}`
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
this.branchsStateData[index] = branchData
|
|
299
|
+
})
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @description Method to get the progression for a branch
|
|
304
|
+
* @param {String} idBranch the id of the branch ex: "P01_E01"
|
|
305
|
+
* @return {Number} Return the percentage of completion (from 0 to 100) and the current state of a specific branch
|
|
306
|
+
*/
|
|
307
|
+
getBranchProgression(idBranch) {
|
|
308
|
+
let result = { state: null, progression: null }
|
|
309
|
+
|
|
310
|
+
if (!this.getAllCompleted[this.idActivity]) return result // no progression found for this activity return result
|
|
311
|
+
|
|
312
|
+
const total = this.getBranchPagesCount(idBranch) // get all pages for in this branch
|
|
313
|
+
//get all completed branches pages
|
|
314
|
+
const completedPages = this.getAllCompleted[this.idActivity].filter(
|
|
315
|
+
(page) => {
|
|
316
|
+
return Object.keys(page)[0].search(idBranch) !== -1
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
result.progression = Math.floor((completedPages.length / total) * 100)
|
|
320
|
+
|
|
321
|
+
if (isNaN(result.progression)) result.progression = 0
|
|
322
|
+
|
|
323
|
+
if (result.progression === 0) {
|
|
324
|
+
result.state = this.status.NEW
|
|
325
|
+
} else if (result.progression === 100) {
|
|
326
|
+
result.state = this.status.COMPLETE
|
|
327
|
+
} else {
|
|
328
|
+
result.state = this.status.STARTED
|
|
329
|
+
}
|
|
330
|
+
return result
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @description Method to get the numbers of pages in a branch
|
|
335
|
+
* @param {String} idBranch the id of the branch ex: "A02_E01_P01"
|
|
336
|
+
* @return {Array} /Return the total of pages in a specific branch
|
|
337
|
+
*/
|
|
338
|
+
getBranchPagesCount(idBranch) {
|
|
339
|
+
if (this.$router.currentRoute.value.meta.type !== 'branching') return
|
|
340
|
+
|
|
341
|
+
const allPages = this.getAllActivities()
|
|
342
|
+
.list.get(this.idActivity)
|
|
343
|
+
.get(this.$router.currentRoute.value.meta.id)
|
|
344
|
+
.get(idBranch)
|
|
345
|
+
|
|
346
|
+
return allPages.size ? allPages.size : 1
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @description Method to validate data in the prop card|customButton.
|
|
351
|
+
* @param {String} propName The name of the property card|customButton
|
|
352
|
+
* @return {String} errMsg false | string message
|
|
353
|
+
*/
|
|
354
|
+
validateProp(propName) {
|
|
355
|
+
let isValid = true
|
|
356
|
+
let errMsg = false
|
|
357
|
+
|
|
358
|
+
switch (propName) {
|
|
359
|
+
case 'cards': {
|
|
360
|
+
// verify that numbers of element is equal to number
|
|
361
|
+
//card must have at least 2 elements
|
|
362
|
+
if (
|
|
363
|
+
this.cards.constructor !== Array ||
|
|
364
|
+
this.cards.length !== this.branchs.length
|
|
365
|
+
) {
|
|
366
|
+
isValid = false
|
|
367
|
+
errMsg = `La propriété cards doit être un tableau contenant ${this.branchs.length} elements`
|
|
368
|
+
} else {
|
|
369
|
+
//Start validation of content of each object in cards
|
|
370
|
+
for (let el of this.cards) {
|
|
371
|
+
//Each element in card must be of type Object and can not be empty
|
|
372
|
+
if (el.constructor !== Object) {
|
|
373
|
+
isValid = false
|
|
374
|
+
let index = this.cards.indexOf(el)
|
|
375
|
+
errMsg = `Le data pour la carte ${index + 1} n'est pas de type Object`
|
|
376
|
+
}
|
|
377
|
+
if (!isValid) break
|
|
378
|
+
}
|
|
379
|
+
//Start validation of required keys
|
|
380
|
+
if (isValid) {
|
|
381
|
+
for (let el of this.cards) {
|
|
382
|
+
let index = this.cards.indexOf(el)
|
|
383
|
+
|
|
384
|
+
// Start validation of existing branch for this element
|
|
385
|
+
let searchEl = this.branchs.find((b) =>
|
|
386
|
+
b._ref.includes(el.brchName)
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
if (!searchEl) {
|
|
390
|
+
errMsg = `Aucune branch correspondante avec le nom ${el.brchName} pour la carte ${
|
|
391
|
+
index + 1
|
|
392
|
+
}. Êtes vous sûre d'avoir indiqué la bonne branch?`
|
|
393
|
+
isValid = false
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!isValid) break
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
break
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
case 'customButtons': {
|
|
404
|
+
if (
|
|
405
|
+
this.customButtons.constructor !== Array ||
|
|
406
|
+
this.customButtons.length !== this.branchs.length
|
|
407
|
+
) {
|
|
408
|
+
isValid = false
|
|
409
|
+
errMsg = `La propriété customButtons doit être un tableau contenant ${this.branchs.length} elements`
|
|
410
|
+
} else {
|
|
411
|
+
//Start validation of content of each object in customButtons
|
|
412
|
+
for (let el of this.customButtons) {
|
|
413
|
+
//Each element in card must be of type Object and can not be empty
|
|
414
|
+
if (el.constructor !== Object) {
|
|
415
|
+
isValid = false
|
|
416
|
+
let index = this.customButtons.indexOf(el)
|
|
417
|
+
errMsg = `Le data pour le button ${index + 1} n'est pas de type {Object}`
|
|
418
|
+
}
|
|
419
|
+
if (!isValid) break
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
//Start validation of required keys
|
|
423
|
+
if (isValid) {
|
|
424
|
+
const requiredKeys = ['name', 'brchName']
|
|
425
|
+
|
|
426
|
+
for (let el of this.customButtons) {
|
|
427
|
+
//Validate that require key exist for each element for customButton
|
|
428
|
+
let index = this.customButtons.indexOf(el)
|
|
429
|
+
for (let k of requiredKeys) {
|
|
430
|
+
if (!el[k]) {
|
|
431
|
+
isValid = false
|
|
432
|
+
errMsg = `l'Attribut 👉 ${k} 👈 pour la button ${index + 1} n'as pas été défini`
|
|
433
|
+
break
|
|
434
|
+
} else {
|
|
435
|
+
// Expecting String for properties name and brchName
|
|
436
|
+
if (
|
|
437
|
+
['name', 'brchName'].includes(k) &&
|
|
438
|
+
el[k].constructor !== String
|
|
439
|
+
) {
|
|
440
|
+
errMsg = `l'Attribut 👉 ${k} 👈 pour la button ${
|
|
441
|
+
index + 1
|
|
442
|
+
} doit être de type {String}`
|
|
443
|
+
isValid = false
|
|
444
|
+
break
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (!isValid) break
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Start validation of existing branch for this element
|
|
451
|
+
let searchEl = this.branchs.find((b) =>
|
|
452
|
+
b._ref.includes(el.brchName)
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
if (!searchEl) {
|
|
456
|
+
errMsg = `Aucune branch correspondante avec le nom ${
|
|
457
|
+
el.brchName
|
|
458
|
+
} pour le bouton ${index + 1}. Êtes vous sûre d'avoir indiqué la bonne branch?`
|
|
459
|
+
isValid = false
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!isValid) break
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
break
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return errMsg
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @description methode to retive matching data of branch in the prop card| customButton
|
|
475
|
+
* @param {String} id The id of the branch
|
|
476
|
+
* @return {Object} Matching element from
|
|
477
|
+
*/
|
|
478
|
+
getMatchingElement(id) {
|
|
479
|
+
if (this.isCustomButton) {
|
|
480
|
+
return this.customButtons.find((c) => c.brchName === id)
|
|
481
|
+
}
|
|
482
|
+
if (this.isCard) {
|
|
483
|
+
return this.cards.find((c) => c.brchName === id)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
</script>
|
|
489
|
+
|
|
490
|
+
<style lang="scss">
|
|
491
|
+
.branch-btn-wrapper {
|
|
492
|
+
position: relative;
|
|
493
|
+
|
|
494
|
+
.branch-btn-custom {
|
|
495
|
+
background-color: transparent;
|
|
496
|
+
border: none;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.v-card {
|
|
501
|
+
position: relative;
|
|
502
|
+
.button-progress-wrapper {
|
|
503
|
+
position: absolute;
|
|
504
|
+
right: 55px;
|
|
505
|
+
top: 5px;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
</style>
|