fcad-core-dragon 2.1.1 → 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.
Files changed (160) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +124 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +46 -16
  6. package/CHANGELOG +520 -520
  7. package/README.md +57 -57
  8. package/documentation/.vitepress/config.js +114 -114
  9. package/documentation/api-examples.md +49 -49
  10. package/documentation/composants/app-base-button.md +58 -58
  11. package/documentation/composants/app-base-error-display.md +59 -59
  12. package/documentation/composants/app-base-popover.md +68 -68
  13. package/documentation/composants/app-comp-audio.md +75 -75
  14. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  15. package/documentation/composants/app-comp-button-progress.md +53 -53
  16. package/documentation/composants/app-comp-carousel.md +53 -53
  17. package/documentation/composants/app-comp-container.md +53 -53
  18. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  19. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  20. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  21. package/documentation/composants/app-comp-input-text-next.md +35 -35
  22. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  23. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  24. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  25. package/documentation/composants/app-comp-jauge.md +31 -31
  26. package/documentation/composants/app-comp-menu-item.md +55 -55
  27. package/documentation/composants/app-comp-menu.md +29 -29
  28. package/documentation/composants/app-comp-navigation.md +41 -41
  29. package/documentation/composants/app-comp-note-call.md +53 -53
  30. package/documentation/composants/app-comp-note-credit.md +53 -53
  31. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  32. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  33. package/documentation/composants/app-comp-quiz-next.md +235 -235
  34. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  35. package/documentation/composants/app-comp-svg-next.md +53 -53
  36. package/documentation/composants/app-comp-table-of-content.md +50 -50
  37. package/documentation/composants/app-comp-video-player.md +82 -82
  38. package/documentation/composants.md +46 -46
  39. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  40. package/documentation/composants_critiques/app-base-module.md +43 -43
  41. package/documentation/composants_critiques/app-base-page.md +48 -48
  42. package/documentation/composants_critiques/app-base.md +311 -311
  43. package/documentation/composants_critiques/main.md +15 -15
  44. package/documentation/demarrage.md +50 -50
  45. package/documentation/deploiement.md +57 -57
  46. package/documentation/index.md +33 -33
  47. package/documentation/markdown-examples.md +85 -85
  48. package/documentation/public/vite.svg +14 -14
  49. package/documentation/public/vuejs.svg +1 -1
  50. package/documentation/public/vuetify.svg +5 -5
  51. package/eslint.config.js +60 -60
  52. package/junit-report.xml +182 -0
  53. package/package.json +66 -59
  54. package/playwright/index.html +12 -0
  55. package/playwright/index.js +21 -0
  56. package/playwright-ct.config.js +95 -0
  57. package/src/$locales/en.json +157 -157
  58. package/src/$locales/fr.json +120 -120
  59. package/src/assets/data/onboardingMessages.json +47 -47
  60. package/src/components/AppBase.vue +1171 -1169
  61. package/src/components/AppBaseButton.vue +90 -95
  62. package/src/components/AppBaseErrorDisplay.vue +438 -438
  63. package/src/components/AppBaseFlipCard.vue +84 -84
  64. package/src/components/AppBaseModule.vue +1639 -1634
  65. package/src/components/AppBasePage.vue +867 -866
  66. package/src/components/AppBasePopover.vue +41 -41
  67. package/src/components/AppBaseSkeleton.vue +66 -66
  68. package/src/components/AppCompAudio.vue +261 -256
  69. package/src/components/AppCompBranchButtons.vue +508 -508
  70. package/src/components/AppCompButtonProgress.vue +137 -132
  71. package/src/components/AppCompCarousel.vue +342 -336
  72. package/src/components/AppCompContainer.vue +29 -29
  73. package/src/components/AppCompInputCheckBoxNx.vue +325 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +287 -284
  76. package/src/components/AppCompInputTextNx.vue +156 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +316 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -1
  82. package/src/components/AppCompMenuItem.vue +246 -240
  83. package/src/components/AppCompNavigation.vue +977 -972
  84. package/src/components/AppCompNoteCall.vue +167 -161
  85. package/src/components/AppCompNoteCredit.vue +496 -491
  86. package/src/components/AppCompPlayBarNext.vue +2290 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +355 -350
  90. package/src/components/AppCompSVGNext.vue +346 -346
  91. package/src/components/AppCompSettingsMenu.vue +177 -172
  92. package/src/components/AppCompTableOfContent.vue +433 -427
  93. package/src/components/AppCompVideoPlayer.vue +377 -377
  94. package/src/components/AppCompViewDisplay.vue +6 -6
  95. package/src/components/BaseModule.vue +55 -55
  96. package/src/composables/useIdleDetector.js +56 -56
  97. package/src/composables/useQuiz.js +89 -89
  98. package/src/composables/useTimer.js +172 -172
  99. package/src/directives/nvdaFix.js +53 -53
  100. package/src/externalComps/ModuleView.vue +22 -22
  101. package/src/externalComps/SummaryView.vue +91 -91
  102. package/src/main.js +493 -476
  103. package/src/module/stores/appStore.js +960 -947
  104. package/src/module/xapi/ADL.js +520 -520
  105. package/src/module/xapi/Crypto/Hasher.js +241 -241
  106. package/src/module/xapi/Crypto/WordArray.js +278 -278
  107. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  108. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  109. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  110. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  111. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  112. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  113. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  114. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  115. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  116. package/src/module/xapi/Crypto/index.js +53 -53
  117. package/src/module/xapi/Statement/activity.js +47 -47
  118. package/src/module/xapi/Statement/agent.js +55 -55
  119. package/src/module/xapi/Statement/group.js +26 -26
  120. package/src/module/xapi/Statement/index.js +259 -259
  121. package/src/module/xapi/Statement/statement.js +253 -253
  122. package/src/module/xapi/Statement/statementRef.js +23 -23
  123. package/src/module/xapi/Statement/substatement.js +22 -22
  124. package/src/module/xapi/Statement/verb.js +36 -36
  125. package/src/module/xapi/activitytypes.js +17 -17
  126. package/src/module/xapi/launch.js +157 -157
  127. package/src/module/xapi/utils.js +167 -167
  128. package/src/module/xapi/verbs.js +294 -294
  129. package/src/module/xapi/wrapper.js +1895 -1895
  130. package/src/module/xapi/xapiStatement.js +444 -444
  131. package/src/plugins/analytics.js +34 -34
  132. package/src/plugins/bus.js +12 -8
  133. package/src/plugins/gsap.js +17 -17
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +27 -26
  136. package/src/plugins/idb.js +227 -227
  137. package/src/plugins/save.js +37 -37
  138. package/src/plugins/scorm.js +287 -287
  139. package/src/plugins/xapi.js +11 -11
  140. package/src/public/index.html +33 -33
  141. package/src/router/index.js +57 -57
  142. package/src/router/routes.js +312 -312
  143. package/src/shared/generalfuncs.js +344 -344
  144. package/src/shared/validators.js +1018 -1018
  145. package/tests/component/AppBaseButton.spec.js +53 -0
  146. package/tests/component/pinia.spec.js +24 -0
  147. package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
  148. package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
  149. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  150. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  152. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  153. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  154. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  155. package/tests/unit/AppCompQuizNext.spec.js +114 -0
  156. package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
  157. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  158. package/vitest.config.js +28 -19
  159. package/vitest.setup.js +28 -0
  160. package/src/components/AppBaseButton.test.js +0 -21
@@ -1,427 +1,433 @@
1
- <!--
2
- ----------------- MUST ADD ERROR GESTION ------------------
3
- ----------------- MUST ADD label terminer ------------------
4
- ----------------- MUST ADD css ------------------
5
-
6
- @ Description: This component is used to display the sub menu to show the anchor of a activity.
7
- @ What it does: The component show the data that was enter in menu.json and create de link to the anchor from those information. This componant must be use with app-comp-menu-Item and app-comp-menu.
8
- -->
9
-
10
- <template>
11
- <div v-if="error" id="sidebar-submenu" :class="{ isOpen: isOpened }">
12
- <focus-trap :active="isOpened" :prevent-scroll="true">
13
- <div ref="target">
14
- <!-- <div class="submenu-header"> -->
15
- <app-base-button
16
- id="close-toc"
17
- class="btn-ghost"
18
- :title="$t('button.closePopUp')"
19
- @click="onCloseWidget('toc')"
20
- >
21
- <svg aria-hidden="true" focusable="false">
22
- <use href="#close-square-icon" />
23
- </svg>
24
- </app-base-button>
25
- <!-- </div> -->
26
-
27
- <!-- <div class="submenu-content"> -->
28
- <p class="t-act" v-html="`${title}`"></p>
29
- <div class="box-prog-act">
30
- <p>
31
- {{ $t('text.activity_progress') }}
32
- <span>{{ actProgress }}</span>
33
- </p>
34
- </div>
35
- <div class="box-anchor">
36
- <p class="t-toc">{{ $t('text.toc') }}</p>
37
-
38
- <RouterLink
39
- v-for="(anchor, index) of anchors"
40
- :key="anchor.title"
41
- class="toc-item"
42
- :done="sectionProgress(index)"
43
- :to="{ name: anchor.path }"
44
- @click="closeAndNextPage(anchor.path)"
45
- >
46
- <div class="box-text-anchor">
47
- <div class="text-anchor">
48
- <p class="anchor-t" v-html="anchor.title"></p>
49
- <p class="state">
50
- <span class="box-text">
51
- <svg aria-hidden="true" focusable="false">
52
- <use href="#check-toc" />
53
- </svg>
54
- {{ $t('text.complete') }}
55
- </span>
56
- </p>
57
- </div>
58
- <svg aria-hidden="true" focusable="false">
59
- <use href="#chevronD-icon" />
60
- </svg>
61
- </div>
62
- </RouterLink>
63
- </div>
64
- <!-- </div> -->
65
- </div>
66
- </focus-trap>
67
- </div>
68
- <div v-else id="sidebar-submenu" :class="{ isOpen: isOpened }">
69
- <p>
70
- Cette activité n'existe pas dans menu setting, svp l'ajouter à votre menu
71
- setting
72
- </p>
73
- </div>
74
- </template>
75
-
76
- <script>
77
- import { mapState, mapActions } from 'pinia'
78
- import { useAppStore } from '../module/stores/appStore'
79
- export default {
80
- data() {
81
- return {
82
- title: '',
83
- anchors: [],
84
- activity: '',
85
- isOpened: false,
86
- menuInfo: null,
87
- actProgress: null,
88
- error: true
89
- }
90
- },
91
- computed: {
92
- ...mapState(useAppStore, [
93
- 'getMenuSettings',
94
- 'getAnchorsForActivity',
95
- 'getBifChoice',
96
- 'getAllCompleted',
97
- 'getAllActivitiesState'
98
- ])
99
- },
100
- beforeUnmount() {
101
- this.$bus.$off('toggle-widget', this.onToggleWidget)
102
- this.$bus.$off('close-widget', this.onCloseWidget)
103
- this.$bus.$off('info-activity', this.onInfoActivity)
104
- },
105
- mounted() {
106
- this.$bus.$on('toggle-widget', this.onToggleWidget)
107
- this.$bus.$on('close-widget', this.onCloseWidget)
108
- this.$bus.$on('info-activity', this.onInfoActivity)
109
- },
110
- methods: {
111
- ...mapActions(useAppStore, ['updateWidgetOpen']),
112
- onInfoActivity(data) {
113
- //should be conditioned to avoid event firing multiple time on call
114
-
115
- if (data) {
116
- this.reset()
117
- this.activity = data
118
-
119
- if (!this.getMenuSettings[data]) {
120
- return (this.error = false)
121
- } else {
122
- this.getInfoSubMenu(data)
123
- this.getProgressActivity(data)
124
- }
125
- }
126
- },
127
- onCloseWidget(data) {
128
- this.isOpened = false
129
- this.updateWidgetOpen(false)
130
- this.reset()
131
- },
132
- onToggleWidget(data) {
133
- if (data.type !== 'toc') return (this.isOpened = false)
134
- this.isOpened = !this.isOpened
135
- this.updateWidgetOpen(this.isOpened)
136
- },
137
- /* @Description:Get and set the section to output
138
- * @params: activity {Object}: the activity which sections need to be output
139
- */
140
- getInfoSubMenu(activity) {
141
- //get the anchors for this activity from the store
142
- let menuInfo = this.getMenuSettings
143
-
144
- let nb = null
145
-
146
- //Get title are if no title construct acitivty
147
- if (menuInfo[activity] && menuInfo[activity].title)
148
- this.title = menuInfo[activity].title
149
-
150
- if (activity.charAt(1) == '0' || activity.charAt(1) == 0)
151
- nb = activity.substr(2)
152
- else nb = activity.substr(1)
153
- //Format title in the Progress info
154
- switch (nb) {
155
- case '0':
156
- this.title = menuInfo[activity].subTitle
157
- ? `Introduction : ${menuInfo[activity].subTitle}`
158
- : `Introduction`
159
- break
160
-
161
- case '99':
162
- this.title = menuInfo[activity].subTitle
163
- ? `Conclusion : ${menuInfo[activity].subTitle}`
164
- : `Conclusion`
165
- break
166
-
167
- default:
168
- this.title = menuInfo[activity].subTitle
169
- ? `${this.$t('text.activity')} ${nb} : ${menuInfo[activity].subTitle}`
170
- : `${this.$t('text.activity')} ${nb}`
171
- }
172
-
173
- //create anchors title and path
174
- if (menuInfo[activity] && menuInfo[activity].anchors) {
175
- menuInfo[activity].anchors.forEach((element) => {
176
- //
177
- let _path = null
178
- let b = null
179
- if (nb == 0 || nb == '0') {
180
- b = `introduction`
181
- } else if (nb == 99 || nb == '99') {
182
- b = `conclusion`
183
- } else {
184
- b = `activite_${nb}`
185
- }
186
-
187
- if (element.page == 'page_1') {
188
- _path = `${b}`
189
- } else {
190
- _path = `${b}.${element.page}`
191
- }
192
-
193
- let info = {
194
- title: `${element.anchorName}`,
195
- path: _path
196
- }
197
- this.anchors.push(info)
198
- })
199
- }
200
- },
201
- disable(data) {
202
- let page = data.pageRef
203
- let act = this.getAct(data.path)
204
- let choosen = this.getBifChoice
205
-
206
- // disable if bif
207
- if (data.bif && choosen) {
208
- if (choosen.act == act && choosen.page == page) {
209
- return false
210
- } else {
211
- return true
212
- }
213
- } else {
214
- return false
215
- }
216
- },
217
- getAct(data) {
218
- //return the id need it
219
- if (data) {
220
- if (data.substring(data.indexOf('_') + 1).length == 1) {
221
- return `A0${data.substring(data.indexOf('_') + 1)}`
222
- } else {
223
- return `A${data.substring(data.indexOf('_') + 1)}`
224
- }
225
- }
226
- },
227
- /**
228
- * @description Gives gives length of completed page for an activity
229
- * @param {String} idActivity
230
- * @returns {Number}
231
- */
232
- getProgressActivity(idActivity) {
233
- /// give all the page that are complete to the gauge
234
- if (idActivity) {
235
- let completed = []
236
- if (this.getAllCompleted[idActivity]) {
237
- completed = this.getAllCompleted[idActivity] //get all completed page for the activity
238
- }
239
-
240
- const allActivitiesState = JSON.parse(
241
- JSON.stringify(this.getAllActivitiesState)
242
- )
243
- let size = allActivitiesState[idActivity]
244
- ? allActivitiesState[idActivity].size
245
- : 0
246
-
247
- // return pourcent of activity completion
248
- this.actProgress = `${Math.round((completed.length * 100) / size)} %`
249
- }
250
- },
251
- close() {
252
- this.isOpened = !this.isOpened
253
- this.updateWidgetOpen(this.isOpened)
254
- },
255
- closeAndNextPage(NextPage) {
256
- this.isOpened = !this.isOpened
257
- this.updateWidgetOpen(this.isOpened)
258
- this.$router.push({ name: NextPage })
259
- },
260
- reset() {
261
- this.anchors = []
262
- this.activity = ''
263
- },
264
- sectionProgress(index) {
265
- let menuInfo = this.getMenuSettings
266
- let nextIndex = index + 1
267
- let nextSec
268
- let curSec = menuInfo[this.activity].anchors[index].pageRef
269
-
270
- let nb
271
- let tabPageSec = []
272
- let tabcomplete = []
273
-
274
- if (nextIndex < menuInfo[this.activity].anchors.length) {
275
- nextSec = menuInfo[this.activity].anchors[nextIndex].pageRef
276
- }
277
-
278
- if (nextSec != undefined) {
279
- nb = parseInt(nextSec.substring(1))
280
-
281
- while (nb > parseInt(curSec.substring(1))) {
282
- nb--
283
- if (nb < 10) tabPageSec.push(`P0${nb}`)
284
- else tabPageSec.push(`P${nb}`)
285
- }
286
- } else {
287
- if (menuInfo[this.activity].anchors.length == 1) {
288
- let numPage = this.getAllActivitiesState[this.activity].size
289
-
290
- for (let g = 1; g == numPage; g++) {
291
- tabPageSec.push(`P${g}`)
292
- }
293
- if (
294
- typeof this.getAllCompleted[this.activity] !== 'undefined' &&
295
- this.getAllCompleted[this.activity].length !==
296
- this.getAllActivitiesState[this.activity].size
297
- )
298
- return false
299
- }
300
- tabPageSec.push(curSec)
301
- }
302
-
303
- let completeActPage = this.getAllCompleted[this.activity]
304
-
305
- if (completeActPage) {
306
- completeActPage.find((value) => {
307
- let w = Object.keys(value)
308
- tabcomplete.push(w[0])
309
- })
310
-
311
- const isSubset = (array1, array2) =>
312
- array2.every((element) => array1.includes(element))
313
-
314
- return isSubset(tabcomplete, tabPageSec)
315
- }
316
- }
317
- }
318
- }
319
- </script>
320
- <style lang="scss">
321
- #sidebar-submenu {
322
- display: none;
323
- position: absolute;
324
- left: 75px;
325
- top: 0;
326
- max-height: 592px;
327
- width: 476px;
328
- transition: left 0.5s ease-in-out;
329
-
330
- &.isOpen {
331
- display: flex;
332
- flex-direction: column;
333
- }
334
-
335
- #close-toc {
336
- position: absolute;
337
- right: 24px;
338
- top: 24px;
339
- margin-bottom: 0;
340
- padding: 12px;
341
- width: 48px;
342
- height: 48px;
343
- display: flex;
344
- justify-content: center;
345
-
346
- &:focus {
347
- box-shadow: inherit !important;
348
- }
349
- }
350
-
351
- .box-prog-act {
352
- width: 319px;
353
- display: flex;
354
- flex-direction: row;
355
- margin-bottom: 16px;
356
-
357
- p {
358
- position: relative;
359
- width: 100%;
360
-
361
- span {
362
- position: absolute;
363
- right: 0;
364
- top: 0;
365
- }
366
- }
367
- }
368
-
369
- .box-anchor {
370
- max-height: 366px;
371
- overflow-y: auto;
372
- padding: 0 4px;
373
-
374
- .t-toc {
375
- margin-bottom: 16px;
376
- }
377
-
378
- .toc-item {
379
- display: block;
380
- width: 95%;
381
- margin-bottom: 8px;
382
-
383
- .state {
384
- display: none;
385
-
386
- .box-text {
387
- display: flex;
388
- justify-content: flex-start;
389
- align-items: center;
390
- margin-top: 7px;
391
-
392
- svg {
393
- margin-right: 10px;
394
- }
395
- }
396
- }
397
- &[done='true'] {
398
- .state {
399
- display: flex;
400
- align-items: center;
401
- svg {
402
- margin-right: 10px;
403
- }
404
- }
405
- }
406
-
407
- .box-text-anchor {
408
- display: flex;
409
- flex-direction: row;
410
- align-items: center;
411
- justify-content: space-between;
412
- }
413
- }
414
- }
415
- }
416
-
417
- .app-nav {
418
- &.show,
419
- &:hover {
420
- .ctn-w {
421
- #sidebar-submenu {
422
- left: 120px;
423
- }
424
- }
425
- }
426
- }
427
- </style>
1
+ <!--
2
+ ----------------- MUST ADD ERROR GESTION ------------------
3
+ ----------------- MUST ADD label terminer ------------------
4
+ ----------------- MUST ADD css ------------------
5
+
6
+ @ Description: This component is used to display the sub menu to show the anchor of a activity.
7
+ @ What it does: The component show the data that was enter in menu.json and create de link to the anchor from those information. This componant must be use with app-comp-menu-Item and app-comp-menu.
8
+ -->
9
+
10
+ <template>
11
+ <div v-if="error" id="sidebar-submenu" :class="{ isOpen: isOpened }">
12
+ <focus-trap :active="isOpened" :prevent-scroll="true">
13
+ <div ref="target">
14
+ <!-- <div class="submenu-header"> -->
15
+ <app-base-button
16
+ id="close-toc"
17
+ class="btn-ghost"
18
+ :title="$t('button.closePopUp')"
19
+ @click="onCloseWidget('toc')"
20
+ >
21
+ <svg aria-hidden="true" focusable="false">
22
+ <use href="#close-square-icon" />
23
+ </svg>
24
+ </app-base-button>
25
+ <!-- </div> -->
26
+
27
+ <!-- <div class="submenu-content"> -->
28
+ <p class="t-act" v-html="`${title}`"></p>
29
+ <div class="box-prog-act">
30
+ <p>
31
+ {{ $t('text.activity_progress') }}
32
+ <span>{{ actProgress }}</span>
33
+ </p>
34
+ </div>
35
+ <div class="box-anchor">
36
+ <p class="t-toc">{{ $t('text.toc') }}</p>
37
+
38
+ <RouterLink
39
+ v-for="(anchor, index) of anchors"
40
+ :key="anchor.title"
41
+ class="toc-item"
42
+ :done="sectionProgress(index)"
43
+ :to="{ name: anchor.path }"
44
+ @click="closeAndNextPage(anchor.path)"
45
+ >
46
+ <div class="box-text-anchor">
47
+ <div class="text-anchor">
48
+ <p class="anchor-t" v-html="anchor.title"></p>
49
+ <p class="state">
50
+ <span class="box-text">
51
+ <svg aria-hidden="true" focusable="false">
52
+ <use href="#check-toc" />
53
+ </svg>
54
+ {{ $t('text.complete') }}
55
+ </span>
56
+ </p>
57
+ </div>
58
+ <svg aria-hidden="true" focusable="false">
59
+ <use href="#chevronD-icon" />
60
+ </svg>
61
+ </div>
62
+ </RouterLink>
63
+ </div>
64
+ <!-- </div> -->
65
+ </div>
66
+ </focus-trap>
67
+ </div>
68
+ <div v-else id="sidebar-submenu" :class="{ isOpen: isOpened }">
69
+ <p>
70
+ Cette activité n'existe pas dans menu setting, svp l'ajouter à votre menu
71
+ setting
72
+ </p>
73
+ </div>
74
+ </template>
75
+
76
+ <script>
77
+ import { mapState, mapActions } from 'pinia'
78
+ import { useAppStore } from '../module/stores/appStore'
79
+ import { useI18n } from 'vue-i18n'
80
+
81
+ export default {
82
+ setup() {
83
+ const { t } = useI18n()
84
+ return { t }
85
+ },
86
+ data() {
87
+ return {
88
+ title: '',
89
+ anchors: [],
90
+ activity: '',
91
+ isOpened: false,
92
+ menuInfo: null,
93
+ actProgress: null,
94
+ error: true
95
+ }
96
+ },
97
+ computed: {
98
+ ...mapState(useAppStore, [
99
+ 'getMenuSettings',
100
+ 'getAnchorsForActivity',
101
+ 'getBifChoice',
102
+ 'getAllCompleted',
103
+ 'getAllActivitiesState'
104
+ ])
105
+ },
106
+ beforeUnmount() {
107
+ this.$bus.$off('toggle-widget', this.onToggleWidget)
108
+ this.$bus.$off('close-widget', this.onCloseWidget)
109
+ this.$bus.$off('info-activity', this.onInfoActivity)
110
+ },
111
+ mounted() {
112
+ this.$bus.$on('toggle-widget', this.onToggleWidget)
113
+ this.$bus.$on('close-widget', this.onCloseWidget)
114
+ this.$bus.$on('info-activity', this.onInfoActivity)
115
+ },
116
+ methods: {
117
+ ...mapActions(useAppStore, ['updateWidgetOpen']),
118
+ onInfoActivity(data) {
119
+ //should be conditioned to avoid event firing multiple time on call
120
+
121
+ if (data) {
122
+ this.reset()
123
+ this.activity = data
124
+
125
+ if (!this.getMenuSettings[data]) {
126
+ return (this.error = false)
127
+ } else {
128
+ this.getInfoSubMenu(data)
129
+ this.getProgressActivity(data)
130
+ }
131
+ }
132
+ },
133
+ onCloseWidget(data) {
134
+ this.isOpened = false
135
+ this.updateWidgetOpen(false)
136
+ this.reset()
137
+ },
138
+ onToggleWidget(data) {
139
+ if (data.type !== 'toc') return (this.isOpened = false)
140
+ this.isOpened = !this.isOpened
141
+ this.updateWidgetOpen(this.isOpened)
142
+ },
143
+ /* @Description:Get and set the section to output
144
+ * @params: activity {Object}: the activity which sections need to be output
145
+ */
146
+ getInfoSubMenu(activity) {
147
+ //get the anchors for this activity from the store
148
+ let menuInfo = this.getMenuSettings
149
+
150
+ let nb = null
151
+
152
+ //Get title are if no title construct acitivty
153
+ if (menuInfo[activity] && menuInfo[activity].title)
154
+ this.title = menuInfo[activity].title
155
+
156
+ if (activity.charAt(1) == '0' || activity.charAt(1) == 0)
157
+ nb = activity.substr(2)
158
+ else nb = activity.substr(1)
159
+ //Format title in the Progress info
160
+ switch (nb) {
161
+ case '0':
162
+ this.title = menuInfo[activity].subTitle
163
+ ? `Introduction : ${menuInfo[activity].subTitle}`
164
+ : `Introduction`
165
+ break
166
+
167
+ case '99':
168
+ this.title = menuInfo[activity].subTitle
169
+ ? `Conclusion : ${menuInfo[activity].subTitle}`
170
+ : `Conclusion`
171
+ break
172
+
173
+ default:
174
+ this.title = menuInfo[activity].subTitle
175
+ ? `${this.$t('text.activity')} ${nb} : ${menuInfo[activity].subTitle}`
176
+ : `${this.$t('text.activity')} ${nb}`
177
+ }
178
+
179
+ //create anchors title and path
180
+ if (menuInfo[activity] && menuInfo[activity].anchors) {
181
+ menuInfo[activity].anchors.forEach((element) => {
182
+ //
183
+ let _path = null
184
+ let b = null
185
+ if (nb == 0 || nb == '0') {
186
+ b = `introduction`
187
+ } else if (nb == 99 || nb == '99') {
188
+ b = `conclusion`
189
+ } else {
190
+ b = `activite_${nb}`
191
+ }
192
+
193
+ if (element.page == 'page_1') {
194
+ _path = `${b}`
195
+ } else {
196
+ _path = `${b}.${element.page}`
197
+ }
198
+
199
+ let info = {
200
+ title: `${element.anchorName}`,
201
+ path: _path
202
+ }
203
+ this.anchors.push(info)
204
+ })
205
+ }
206
+ },
207
+ disable(data) {
208
+ let page = data.pageRef
209
+ let act = this.getAct(data.path)
210
+ let choosen = this.getBifChoice
211
+
212
+ // disable if bif
213
+ if (data.bif && choosen) {
214
+ if (choosen.act == act && choosen.page == page) {
215
+ return false
216
+ } else {
217
+ return true
218
+ }
219
+ } else {
220
+ return false
221
+ }
222
+ },
223
+ getAct(data) {
224
+ //return the id need it
225
+ if (data) {
226
+ if (data.substring(data.indexOf('_') + 1).length == 1) {
227
+ return `A0${data.substring(data.indexOf('_') + 1)}`
228
+ } else {
229
+ return `A${data.substring(data.indexOf('_') + 1)}`
230
+ }
231
+ }
232
+ },
233
+ /**
234
+ * @description Gives gives length of completed page for an activity
235
+ * @param {String} idActivity
236
+ * @returns {Number}
237
+ */
238
+ getProgressActivity(idActivity) {
239
+ /// give all the page that are complete to the gauge
240
+ if (idActivity) {
241
+ let completed = []
242
+ if (this.getAllCompleted[idActivity]) {
243
+ completed = this.getAllCompleted[idActivity] //get all completed page for the activity
244
+ }
245
+
246
+ const allActivitiesState = JSON.parse(
247
+ JSON.stringify(this.getAllActivitiesState)
248
+ )
249
+ let size = allActivitiesState[idActivity]
250
+ ? allActivitiesState[idActivity].size
251
+ : 0
252
+
253
+ // return pourcent of activity completion
254
+ this.actProgress = `${Math.round((completed.length * 100) / size)} %`
255
+ }
256
+ },
257
+ close() {
258
+ this.isOpened = !this.isOpened
259
+ this.updateWidgetOpen(this.isOpened)
260
+ },
261
+ closeAndNextPage(NextPage) {
262
+ this.isOpened = !this.isOpened
263
+ this.updateWidgetOpen(this.isOpened)
264
+ this.$router.push({ name: NextPage })
265
+ },
266
+ reset() {
267
+ this.anchors = []
268
+ this.activity = ''
269
+ },
270
+ sectionProgress(index) {
271
+ let menuInfo = this.getMenuSettings
272
+ let nextIndex = index + 1
273
+ let nextSec
274
+ let curSec = menuInfo[this.activity].anchors[index].pageRef
275
+
276
+ let nb
277
+ let tabPageSec = []
278
+ let tabcomplete = []
279
+
280
+ if (nextIndex < menuInfo[this.activity].anchors.length) {
281
+ nextSec = menuInfo[this.activity].anchors[nextIndex].pageRef
282
+ }
283
+
284
+ if (nextSec != undefined) {
285
+ nb = parseInt(nextSec.substring(1))
286
+
287
+ while (nb > parseInt(curSec.substring(1))) {
288
+ nb--
289
+ if (nb < 10) tabPageSec.push(`P0${nb}`)
290
+ else tabPageSec.push(`P${nb}`)
291
+ }
292
+ } else {
293
+ if (menuInfo[this.activity].anchors.length == 1) {
294
+ let numPage = this.getAllActivitiesState[this.activity].size
295
+
296
+ for (let g = 1; g == numPage; g++) {
297
+ tabPageSec.push(`P${g}`)
298
+ }
299
+ if (
300
+ typeof this.getAllCompleted[this.activity] !== 'undefined' &&
301
+ this.getAllCompleted[this.activity].length !==
302
+ this.getAllActivitiesState[this.activity].size
303
+ )
304
+ return false
305
+ }
306
+ tabPageSec.push(curSec)
307
+ }
308
+
309
+ let completeActPage = this.getAllCompleted[this.activity]
310
+
311
+ if (completeActPage) {
312
+ completeActPage.find((value) => {
313
+ let w = Object.keys(value)
314
+ tabcomplete.push(w[0])
315
+ })
316
+
317
+ const isSubset = (array1, array2) =>
318
+ array2.every((element) => array1.includes(element))
319
+
320
+ return isSubset(tabcomplete, tabPageSec)
321
+ }
322
+ }
323
+ }
324
+ }
325
+ </script>
326
+ <style lang="scss">
327
+ #sidebar-submenu {
328
+ display: none;
329
+ position: absolute;
330
+ left: 75px;
331
+ top: 0;
332
+ max-height: 592px;
333
+ width: 476px;
334
+ transition: left 0.5s ease-in-out;
335
+
336
+ &.isOpen {
337
+ display: flex;
338
+ flex-direction: column;
339
+ }
340
+
341
+ #close-toc {
342
+ position: absolute;
343
+ right: 24px;
344
+ top: 24px;
345
+ margin-bottom: 0;
346
+ padding: 12px;
347
+ width: 48px;
348
+ height: 48px;
349
+ display: flex;
350
+ justify-content: center;
351
+
352
+ &:focus {
353
+ box-shadow: inherit !important;
354
+ }
355
+ }
356
+
357
+ .box-prog-act {
358
+ width: 319px;
359
+ display: flex;
360
+ flex-direction: row;
361
+ margin-bottom: 16px;
362
+
363
+ p {
364
+ position: relative;
365
+ width: 100%;
366
+
367
+ span {
368
+ position: absolute;
369
+ right: 0;
370
+ top: 0;
371
+ }
372
+ }
373
+ }
374
+
375
+ .box-anchor {
376
+ max-height: 366px;
377
+ overflow-y: auto;
378
+ padding: 0 4px;
379
+
380
+ .t-toc {
381
+ margin-bottom: 16px;
382
+ }
383
+
384
+ .toc-item {
385
+ display: block;
386
+ width: 95%;
387
+ margin-bottom: 8px;
388
+
389
+ .state {
390
+ display: none;
391
+
392
+ .box-text {
393
+ display: flex;
394
+ justify-content: flex-start;
395
+ align-items: center;
396
+ margin-top: 7px;
397
+
398
+ svg {
399
+ margin-right: 10px;
400
+ }
401
+ }
402
+ }
403
+ &[done='true'] {
404
+ .state {
405
+ display: flex;
406
+ align-items: center;
407
+ svg {
408
+ margin-right: 10px;
409
+ }
410
+ }
411
+ }
412
+
413
+ .box-text-anchor {
414
+ display: flex;
415
+ flex-direction: row;
416
+ align-items: center;
417
+ justify-content: space-between;
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ .app-nav {
424
+ &.show,
425
+ &:hover {
426
+ .ctn-w {
427
+ #sidebar-submenu {
428
+ left: 120px;
429
+ }
430
+ }
431
+ }
432
+ }
433
+ </style>