fcad-core-dragon 2.4.1-test.1 → 2.5.0-test.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/.gitlab-ci.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
image: node:
|
|
1
|
+
image: node:24.16.0
|
|
2
2
|
|
|
3
3
|
stages:
|
|
4
4
|
- install
|
|
@@ -79,7 +79,6 @@ run_components_test:
|
|
|
79
79
|
#Deploy to npm
|
|
80
80
|
publish_to_npm:
|
|
81
81
|
stage: deploy
|
|
82
|
-
#image: node:22
|
|
83
82
|
script:
|
|
84
83
|
- echo " //registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
|
85
84
|
- npm ci
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [2.5.0-test.2](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/compare/v2.5.0-test.1...v2.5.0-test.2) (2026-06-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **cicd:** setup trusted publishing to replace tokens ([f494991](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/commit/f494991e8374e697b0857ffdfe20bbc735adf13d))
|
|
7
|
+
* **cicd:** setup trusted publishing to replace tokens + update node image in CICD ([588ee4e](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/commit/588ee4e96d5f72c1bb3a5c2e6e17528d6422ae9a))
|
|
8
|
+
|
|
9
|
+
# [2.5.0-test.1](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/compare/v2.4.1-test.1...v2.5.0-test.1) (2026-06-08)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **components:** Ajout nouveau composant categorieCheckBoxNx ([f8c64ce](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/commit/f8c64ce9e001dd3e93d450b12715978db3dc04dc))
|
|
15
|
+
|
|
1
16
|
## [2.4.1-test.1](https://git.crosemont.qc.ca/fcad/core/fcad-core-2/compare/v2.4.0...v2.4.1-test.1) (2026-06-08)
|
|
2
17
|
|
|
3
18
|
|
package/package.json
CHANGED
|
@@ -224,6 +224,8 @@ export default {
|
|
|
224
224
|
// this.updateTracker('appBase', 'loading')
|
|
225
225
|
this.initializeApp(this.appConfig)
|
|
226
226
|
}
|
|
227
|
+
// set the language of the app
|
|
228
|
+
this.setLocale(this.getAppConfigs.lang)
|
|
227
229
|
this.setDocumentTitle()
|
|
228
230
|
window.versionFCAD = this.$helper.getFcadVersionString()
|
|
229
231
|
//check if this is running in a mobile environment and register state in the store
|
|
@@ -264,8 +266,6 @@ export default {
|
|
|
264
266
|
},
|
|
265
267
|
|
|
266
268
|
mounted() {
|
|
267
|
-
// set the language of the app
|
|
268
|
-
this.setLocale(this.getAppConfigs.lang)
|
|
269
269
|
window.addEventListener('keydown', this.handleF5KeyPressed)
|
|
270
270
|
window.addEventListener('beforeunload', this.handleBeforeUnload)
|
|
271
271
|
},
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
<!-- About this Component--
|
|
2
|
+
* Renders a checkboxe input type for the Quiz component
|
|
3
|
+
* Related Quiz to question: REPONSE_MULTIPLE
|
|
4
|
+
* Receives the a data object defined by user
|
|
5
|
+
* Used by AppCompQuiz
|
|
6
|
+
-->
|
|
7
|
+
<template>
|
|
8
|
+
<div v-if="inputData.length > 0" :id="id" class="input-box">
|
|
9
|
+
<v-row
|
|
10
|
+
v-for="(categorie,i) in inputData"
|
|
11
|
+
:key="`cat_${categorie.id}`"
|
|
12
|
+
class="box-categorie"
|
|
13
|
+
>
|
|
14
|
+
<v-col>
|
|
15
|
+
<div v-html="categorie.categorie"></div>
|
|
16
|
+
</v-col>
|
|
17
|
+
<v-col>
|
|
18
|
+
<fieldset :aria-label="fieldsetLabel">
|
|
19
|
+
<div class="box-checkbox">
|
|
20
|
+
<label
|
|
21
|
+
v-for="(choixReponse, index) in categorie.values"
|
|
22
|
+
:key="`lbl_chx_${id}-${choixReponse.id}`"
|
|
23
|
+
:for="`boxchx_cat_${i}_${index}`"
|
|
24
|
+
class="checkbox-label"
|
|
25
|
+
:class="[
|
|
26
|
+
{
|
|
27
|
+
answerSlct: isSelected(i,choixReponse)
|
|
28
|
+
},
|
|
29
|
+
`${retro?.[i]?.[index] ?? ''}`
|
|
30
|
+
]"
|
|
31
|
+
>
|
|
32
|
+
<input
|
|
33
|
+
:id="`boxchx_cat_${i}_${index}`"
|
|
34
|
+
:key="`boxchx_cat_${i}_${choixReponse}`"
|
|
35
|
+
:checked="isSelected(i,choixReponse)"
|
|
36
|
+
type="checkbox"
|
|
37
|
+
:name="`btn-checkbox-cat-${i}-${index}`"
|
|
38
|
+
:aria-labelledby="`span_${index}`"
|
|
39
|
+
:aria-describedby="`${index}-msg-erreur`"
|
|
40
|
+
@change="onSelectUpdate($event,i,choixReponse)"
|
|
41
|
+
/>
|
|
42
|
+
<span
|
|
43
|
+
:id="`span_${index}`"
|
|
44
|
+
class="checkbox-contenu"
|
|
45
|
+
v-html="choixReponse"
|
|
46
|
+
></span>
|
|
47
|
+
<span
|
|
48
|
+
:id="`${index}-msg-erreur`"
|
|
49
|
+
:key="`msg_chx_${index}`"
|
|
50
|
+
class="sr-only"
|
|
51
|
+
>
|
|
52
|
+
{{ messageAccessibility[index] }}
|
|
53
|
+
</span>
|
|
54
|
+
</label>
|
|
55
|
+
</div>
|
|
56
|
+
</fieldset>
|
|
57
|
+
</v-col>
|
|
58
|
+
|
|
59
|
+
</v-row>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
<script>
|
|
63
|
+
import { useQuiz } from '../composables/useQuiz'
|
|
64
|
+
import { validateObjType } from '../shared/validators'
|
|
65
|
+
import { useI18n } from 'vue-i18n'
|
|
66
|
+
|
|
67
|
+
export default {
|
|
68
|
+
name: 'AppCompInputCheckBoxNx',
|
|
69
|
+
/* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
|
|
70
|
+
props: {
|
|
71
|
+
inputData: {
|
|
72
|
+
type: Array,
|
|
73
|
+
default: () => []
|
|
74
|
+
},
|
|
75
|
+
modelValue: {
|
|
76
|
+
type: Array,
|
|
77
|
+
default: () => []
|
|
78
|
+
},
|
|
79
|
+
solution: {
|
|
80
|
+
type: Array,
|
|
81
|
+
default: () => []
|
|
82
|
+
},
|
|
83
|
+
id: {
|
|
84
|
+
type: String,
|
|
85
|
+
default: ''
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
emits: ['update:modelValue', 'enable-submit'],
|
|
89
|
+
setup() {
|
|
90
|
+
const { t } = useI18n()
|
|
91
|
+
const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
|
|
92
|
+
return { retroType, addRetroStyle, resetRetroStyle, t }
|
|
93
|
+
},
|
|
94
|
+
data() {
|
|
95
|
+
return {
|
|
96
|
+
result: null,
|
|
97
|
+
retro: [],
|
|
98
|
+
messageAccessibility: []
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
computed: {
|
|
102
|
+
fieldsetLabel() {
|
|
103
|
+
return `${this.$t('quizState.checkboxFieldset')} ${this.$t('quizState.options')}`
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
watch: {
|
|
107
|
+
modelValue: {
|
|
108
|
+
handler(newVal) {
|
|
109
|
+
this.$emit('enable-submit', newVal.length && newVal.every((e) => e.length))
|
|
110
|
+
},
|
|
111
|
+
deep: true,
|
|
112
|
+
immediate: true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
created() {
|
|
116
|
+
if (import.meta.env.DEV) {
|
|
117
|
+
let errors = this.validateInputData()
|
|
118
|
+
|
|
119
|
+
if (errors && errors.errorList.length)
|
|
120
|
+
return this.$bus.$emit('input-error', { e: this.id, errors })
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
mounted() {
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
methods: {
|
|
127
|
+
/**
|
|
128
|
+
* @description validate the raw data received by the component to render is view
|
|
129
|
+
* @returns {Object} errors - errorList: to display in view and errorConsole, to be displayed in console
|
|
130
|
+
*/
|
|
131
|
+
validateInputData() {
|
|
132
|
+
let errors = null //array for errors dectected
|
|
133
|
+
let stringType = ['id','categorie']
|
|
134
|
+
let arrayType = ['values']
|
|
135
|
+
if (!this.inputData.length) return errors
|
|
136
|
+
for (let i = 0; i < this.inputData.length; i++) {
|
|
137
|
+
errors = validateObjType(
|
|
138
|
+
this.inputData[i],
|
|
139
|
+
{ stringType, arrayType },
|
|
140
|
+
null,
|
|
141
|
+
`choix_reponse #${i + 1}`
|
|
142
|
+
)
|
|
143
|
+
const { errorList, errorInConsole } = errors
|
|
144
|
+
|
|
145
|
+
if (!errorList || !errorInConsole) return null
|
|
146
|
+
return errors
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return errors
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @description inicate wether the element is checked or not.
|
|
154
|
+
* Element is selected if in the modelValue
|
|
155
|
+
* @param value value of tje element to be checked
|
|
156
|
+
*/
|
|
157
|
+
isSelected(parent,value) {
|
|
158
|
+
|
|
159
|
+
let existing
|
|
160
|
+
if(this.modelValue.length) existing = this.modelValue[parent].includes(value)
|
|
161
|
+
else existing = false
|
|
162
|
+
|
|
163
|
+
return existing
|
|
164
|
+
},
|
|
165
|
+
/**
|
|
166
|
+
* @description Method to force update of this.inputsValue.
|
|
167
|
+
* this force the old value and new value of the input to be tracked by VUE.
|
|
168
|
+
* @param newValue, the new value of the input
|
|
169
|
+
* @param index index of the selected element
|
|
170
|
+
*/
|
|
171
|
+
onSelectUpdate(event, parent, value) {
|
|
172
|
+
const isChecked = event.target.checked
|
|
173
|
+
this.resetRetroStyle([this.retro, this.messageAccessibility])
|
|
174
|
+
|
|
175
|
+
let ValueModel
|
|
176
|
+
|
|
177
|
+
if(!this.modelValue.length) ValueModel = Array.from({length:this.inputData.length},()=>[])
|
|
178
|
+
else ValueModel = this.modelValue
|
|
179
|
+
|
|
180
|
+
let UpdatedValue = [...ValueModel]
|
|
181
|
+
|
|
182
|
+
if (isChecked) {
|
|
183
|
+
const existing = UpdatedValue[parent].includes(value)
|
|
184
|
+
if (!existing) UpdatedValue[parent].push(value)
|
|
185
|
+
} else UpdatedValue[parent] = UpdatedValue[parent].filter((v) => v !== value)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
this.$emit('update:modelValue', UpdatedValue)
|
|
189
|
+
|
|
190
|
+
},
|
|
191
|
+
/**
|
|
192
|
+
* @description Component validation method to validate the user input against the solution
|
|
193
|
+
* can handle logic of Css style to apply (correct/wrong Answer) and return the result
|
|
194
|
+
* @return {Object} result
|
|
195
|
+
*/
|
|
196
|
+
validateAnswer() {
|
|
197
|
+
// Validate the user answer
|
|
198
|
+
//Created a custom validated object for inputData
|
|
199
|
+
let mappedResults = Array.from({length:this.inputData.length},()=>[])
|
|
200
|
+
if (this.solution != null) {
|
|
201
|
+
this.inputData.forEach((e,index)=>{
|
|
202
|
+
mappedResults[index] = e.values.map(
|
|
203
|
+
(a) =>
|
|
204
|
+
(a = {
|
|
205
|
+
...a,
|
|
206
|
+
correct: this.solution[index].includes(a)
|
|
207
|
+
})
|
|
208
|
+
)
|
|
209
|
+
})
|
|
210
|
+
} else {
|
|
211
|
+
this.inputData.forEach((e,index)=>{
|
|
212
|
+
mappedResults[index] = e.values.map(
|
|
213
|
+
(a) =>
|
|
214
|
+
(a = {
|
|
215
|
+
...a,
|
|
216
|
+
correct: this.solution[index].includes(a)
|
|
217
|
+
})
|
|
218
|
+
)
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let classRetro = Array.from({length:this.inputData.length},()=>[])
|
|
223
|
+
let mesA11y = Array.from({length:this.inputData.length},()=>[])
|
|
224
|
+
|
|
225
|
+
this.inputData.forEach((e,index)=>{
|
|
226
|
+
const { classRetro: cr, mesA11y: ma } =
|
|
227
|
+
this.addRetroStyle(
|
|
228
|
+
this.solution[index],
|
|
229
|
+
mappedResults[index],
|
|
230
|
+
this.inputData[index].values.length
|
|
231
|
+
)
|
|
232
|
+
classRetro[index] = cr
|
|
233
|
+
mesA11y[index] = ma
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
this.retro = classRetro
|
|
237
|
+
this.messageAccessibility = mesA11y
|
|
238
|
+
|
|
239
|
+
//filer mapped to return a customized object of the user selection
|
|
240
|
+
this.result =
|
|
241
|
+
this.modelValue.map((el,i) => {
|
|
242
|
+
return el.map((e,index)=>{
|
|
243
|
+
return{
|
|
244
|
+
id:index,
|
|
245
|
+
selected: e,
|
|
246
|
+
correct: this.solution[i].includes(e)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
let retroResult = Array.from({length:this.inputData.length},()=>[])
|
|
251
|
+
|
|
252
|
+
this.inputData.forEach((e,index)=>{
|
|
253
|
+
retroResult[index] = this.retroType(this.solution[index], this.result[index])
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const retro = retroResult.every(e => e === 'retro_positive') ? 'retro_positive' : 'retro_negative'
|
|
257
|
+
|
|
258
|
+
let cAns
|
|
259
|
+
if (this.solution != null) cAns = this.computeResult(this.result)
|
|
260
|
+
else cAns = false
|
|
261
|
+
|
|
262
|
+
//Object to return validation to parent
|
|
263
|
+
return {
|
|
264
|
+
userAnswer: this.result,
|
|
265
|
+
correctAnswer: cAns,
|
|
266
|
+
retroType: retro
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
/**
|
|
270
|
+
* @description - compute the correct elements in user response
|
|
271
|
+
* compare with the solution and return wether user succeeded or failed
|
|
272
|
+
* @param {Object} result - user responses
|
|
273
|
+
* @retuns boolean
|
|
274
|
+
*/
|
|
275
|
+
computeResult(result) {
|
|
276
|
+
let res = result.map((e) => { return e.filter((a) => (a.correct)) }).flat()
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
res.length == this.solution.flat() &&
|
|
280
|
+
this.solution.flat().length == result.flat().length
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
</script>
|
|
286
|
+
<style lang="scss">
|
|
287
|
+
fieldset {
|
|
288
|
+
border: inherit;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.custom-control-input:focus ~ .custom-control-label::before {
|
|
292
|
+
border: inherit;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.custom-control-input:focus ~ .custom-control-label::before {
|
|
296
|
+
box-shadow: inherit;
|
|
297
|
+
-webkit-box-shadow: inherit;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fieldset {
|
|
301
|
+
border: inherit;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.custom-checkbox {
|
|
305
|
+
width: 100%;
|
|
306
|
+
height: 100%;
|
|
307
|
+
padding-left: 0;
|
|
308
|
+
|
|
309
|
+
input {
|
|
310
|
+
width: 100%;
|
|
311
|
+
height: 100%;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
label {
|
|
315
|
+
width: 100%;
|
|
316
|
+
height: 100%;
|
|
317
|
+
|
|
318
|
+
&:after,
|
|
319
|
+
&:before {
|
|
320
|
+
border: inherit;
|
|
321
|
+
top: 0px;
|
|
322
|
+
left: 0px;
|
|
323
|
+
height: 0;
|
|
324
|
+
width: 0;
|
|
325
|
+
background-color: inherit;
|
|
326
|
+
border: inherit;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
</style>
|