@ecored-sena/base-kit 0.0.1

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 (67) hide show
  1. package/README.md +1 -0
  2. package/package.json +49 -0
  3. package/plugin/components/AccordionRED.vue +78 -0
  4. package/plugin/components/AcordionA.vue +78 -0
  5. package/plugin/components/Audio.vue +20 -0
  6. package/plugin/components/BannerHero.vue +104 -0
  7. package/plugin/components/CarouselCard.vue +70 -0
  8. package/plugin/components/CarouselRED.vue +65 -0
  9. package/plugin/components/Complementario.vue +81 -0
  10. package/plugin/components/Creditos.vue +101 -0
  11. package/plugin/components/Dialogo.vue +30 -0
  12. package/plugin/components/DialogoActividad.vue +274 -0
  13. package/plugin/components/DialogoBurbuja.vue +46 -0
  14. package/plugin/components/DialogoChat.vue +88 -0
  15. package/plugin/components/Footer.vue +42 -0
  16. package/plugin/components/GlobalComponents.js +39 -0
  17. package/plugin/components/Glosario.vue +100 -0
  18. package/plugin/components/ImagenInfografica.vue +100 -0
  19. package/plugin/components/ImagenInfograficaB.vue +68 -0
  20. package/plugin/components/Inicio.vue +128 -0
  21. package/plugin/components/LineaTiempoA.vue +30 -0
  22. package/plugin/components/LineaTiempoB.vue +36 -0
  23. package/plugin/components/LineaTiempoC.vue +95 -0
  24. package/plugin/components/LineaTiempoD.vue +51 -0
  25. package/plugin/components/LineaTiempoE.vue +25 -0
  26. package/plugin/components/ModalA.vue +22 -0
  27. package/plugin/components/PasosA.vue +59 -0
  28. package/plugin/components/PasosB.vue +90 -0
  29. package/plugin/components/Referencias.vue +45 -0
  30. package/plugin/components/ScrollHorizontal.vue +105 -0
  31. package/plugin/components/Separador.vue +12 -0
  32. package/plugin/components/SlyderA.vue +65 -0
  33. package/plugin/components/SlyderB.vue +48 -0
  34. package/plugin/components/SlyderC.vue +53 -0
  35. package/plugin/components/SlyderD.vue +50 -0
  36. package/plugin/components/SlyderE.vue +58 -0
  37. package/plugin/components/SlyderF.vue +141 -0
  38. package/plugin/components/TabsA.vue +44 -0
  39. package/plugin/components/TabsB.vue +40 -0
  40. package/plugin/components/TabsC.vue +39 -0
  41. package/plugin/components/TarjetaAudio.vue +65 -0
  42. package/plugin/components/TimeLineRED.vue +51 -0
  43. package/plugin/components/actividad/ActividadController.vue +34 -0
  44. package/plugin/components/actividad/actividadCompletar/ActividadCompletar.vue +283 -0
  45. package/plugin/components/actividad/actividadCompletar/ActividadCompletarFooter.vue +63 -0
  46. package/plugin/components/actividad/actividadCompletar/ActividadParrafo.vue +218 -0
  47. package/plugin/components/actividad/actividadCompletar/ActividadResultados.vue +215 -0
  48. package/plugin/components/actividad/actividadCuestionario/Actividad.vue +222 -0
  49. package/plugin/components/actividad/actividadCuestionario/ActividadBarraAvance.vue +116 -0
  50. package/plugin/components/actividad/actividadCuestionario/ActividadPregunta.vue +146 -0
  51. package/plugin/components/actividad/actividadCuestionario/ActividadResultados.vue +215 -0
  52. package/plugin/components/audioMixins.js +73 -0
  53. package/plugin/components/componentSlotMixins.js +87 -0
  54. package/plugin/components/dialogoMixins.js +28 -0
  55. package/plugin/components/globalMixins.js +11 -0
  56. package/plugin/components/plantilla/Accesibilidad.vue +101 -0
  57. package/plugin/components/plantilla/AsideMenu.vue +242 -0
  58. package/plugin/components/plantilla/BannerInterno.vue +66 -0
  59. package/plugin/components/plantilla/BannerPrincipal.vue +104 -0
  60. package/plugin/components/plantilla/BarraAvance.vue +379 -0
  61. package/plugin/components/plantilla/Curso.vue +12 -0
  62. package/plugin/components/plantilla/Footer.vue +42 -0
  63. package/plugin/components/plantilla/Header.vue +152 -0
  64. package/plugin/components/plantilla/ScrollHorizontal.vue +105 -0
  65. package/plugin/components/plantillaMixins.js +34 -0
  66. package/plugin/components/slyderMixins.js +18 -0
  67. package/plugin/plugin.js +49 -0
@@ -0,0 +1,65 @@
1
+ <template lang="pug">
2
+ .tarjeta-audio
3
+ .row.align-items-center
4
+ .col
5
+ .tarjeta-audio__texto.h5.fst-italic.mb-0(v-html="texto")
6
+ .tarjeta-audio__input.mt-2(v-if="!noBarra")
7
+ input(
8
+ v-model="sliderVal",
9
+ type="range",
10
+ max="100",
11
+ :style="{'background-size': `${sliderVal}% 100%`}",
12
+ @input="onSliderMove"
13
+ )
14
+ .tarjeta-audio__tiempo.text-end.pt-1(v-if="tiempo")
15
+ span {{currentTimeDisplay}}
16
+ span {{audioDurationDisplay}}
17
+ .col-auto.ps-0
18
+ .audio.position-relative(@mouseover.once="$emit('audio-hover')")
19
+ slot
20
+ .spinner-border.spinner-border-sm(v-if="!audioCanPlay" role="status")
21
+ span.visually-hidden Loading..
22
+ button.audio__btn(v-else-if="state ==='pause'" @click="play")
23
+ img(src="@/assets/template/audio.svg")
24
+ button.audio__btn(v-else @click="pause")
25
+ img(src="@/assets/template/pause.svg")
26
+
27
+ </template>
28
+
29
+ <script>
30
+ import audioMixins from './audioMixins'
31
+ export default {
32
+ name: 'TarjetaAudio',
33
+ mixins: [audioMixins],
34
+ props: {
35
+ texto: {
36
+ type: String,
37
+ required: true,
38
+ },
39
+ noBarra: {
40
+ type: Boolean,
41
+ default: false,
42
+ },
43
+ tiempo: {
44
+ type: Boolean,
45
+ default: false,
46
+ },
47
+ },
48
+ data: () => ({
49
+ sliderVal: 0,
50
+ }),
51
+ watch: {
52
+ currentTime() {
53
+ this.sliderVal = (this.currentTime / this.audioDuration) * 100
54
+ },
55
+ },
56
+ methods: {
57
+ onSliderMove() {
58
+ this.audioElement.currentTime =
59
+ (this.sliderVal / 100) * this.audioDuration
60
+ },
61
+ },
62
+ }
63
+ </script>
64
+
65
+ <style lang="sass"></style>
@@ -0,0 +1,51 @@
1
+ <template lang="pug">
2
+ .linea-tiempo-d
3
+ .linea-tiempo-d__item.row(
4
+ v-for="(item, index) in elements"
5
+ :key="'linea-tiempo-d-key-'+item.id"
6
+ :class="{'linea-tiempo-d__item--selected' : selected === item.id}"
7
+ )
8
+ .col-auto.linea-tiempo-d__item__col-number
9
+ .linea-tiempo-d__item__number.tarjeta.tarjeta--gris(
10
+ :class="[,{'px-3' : item.numero.length > 1}]"
11
+ @click="selected = item.id"
12
+ @mouseover="mostrarIndicador = mostrarIndicador && index >= 1 ? false : mostrarIndicador"
13
+ )
14
+ .indicador__container(v-if="mostrarIndicador && index === 1")
15
+ .indicador--click
16
+ span(v-html="item.numero")
17
+ .linea-tiempo-d__item__dots(
18
+ v-if="index < elements.length -1"
19
+ )
20
+
21
+ .col(:class="{'mb-4' : index < elements.length -1}")
22
+ .linea-tiempo-d__item__content.tarjeta.tarjeta--gris
23
+ .linea-tiempo-d__item__content__title.px-3(
24
+ @click="selected = item.id"
25
+ @mouseover="mostrarIndicador = mostrarIndicador && index >= 1 ? false : mostrarIndicador"
26
+ )
27
+ span(v-html="item.titulo")
28
+ .linea-tiempo-d__item__content__slot(
29
+ :style="{ height: rendered && selected === item.id ? getActiveHeight(item.id) : 0 } "
30
+ )
31
+ .p-3.pt-0(:ref="item.id" v-child="item.elm" )
32
+
33
+ .hidden-slot
34
+ slot
35
+ </template>
36
+
37
+ <script>
38
+ import componentSlotMixins from './componentSlotMixins'
39
+ export default {
40
+ name: 'TimeLineRED',
41
+ mixins: [componentSlotMixins],
42
+ data: () => ({
43
+ mostrarIndicador: true,
44
+ }),
45
+ computed: {},
46
+ watch: {},
47
+ methods: {},
48
+ }
49
+ </script>
50
+
51
+ <style lang="sass"></style>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div class="actividad-controller">
3
+ <!-- parrafo → actividad Completar frases -->
4
+ <ActividadCompletar v-if="parrafo && !cuestionario" :parrafo="parrafo" />
5
+ <!-- cuestionario → actividad Cuestionario -->
6
+ <ActividadCuestionario
7
+ v-else-if="cuestionario"
8
+ :cuestionario="cuestionario"
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import ActividadCompletar from './actividadCompletar/ActividadCompletar.vue'
15
+ import ActividadCuestionario from './actividadCuestionario/Actividad.vue'
16
+
17
+ export default {
18
+ name: 'ActividadController',
19
+ components: {
20
+ ActividadCompletar,
21
+ ActividadCuestionario,
22
+ },
23
+ props: {
24
+ parrafo: {
25
+ type: Object,
26
+ default: null,
27
+ },
28
+ cuestionario: {
29
+ type: Object,
30
+ default: null,
31
+ },
32
+ },
33
+ }
34
+ </script>
@@ -0,0 +1,283 @@
1
+ <template>
2
+ <div class="tarjeta--blanca">
3
+ <div class="row align-items-center mb-4">
4
+ <div class="col-auto">
5
+ <img src="@/assets/componentes/icon-actividad.svg" alt="" />
6
+ </div>
7
+ <div class="col ">
8
+ <h2 class="titulo-tercero mb-0">
9
+ {{ parrafo.titulo }}
10
+ </h2>
11
+ <p class="mb-0" v-html="parrafo.introduccion"></p>
12
+ </div>
13
+ </div>
14
+ <div class="tarjeta tarjeta--lightest-gray px-4 pb-4 pt-4 px-md-5">
15
+ <ActividadResultados
16
+ v-if="mostrarResultados"
17
+ :respuestas="resultadosVerificacion"
18
+ :mensaje-aprobado="parrafo.mensaje_final_aprobado"
19
+ :mensaje-reprobado="parrafo.mensaje_final_reprobado"
20
+ :titulo-aprobado="parrafo.titulo_aprobado"
21
+ :titulo-reprobado="parrafo.titulo_reprobado"
22
+ :porcentaje-aprobadas="porcentajeAprobacion"
23
+ :total-preguntas-base="parrafo.textos.length"
24
+ :preguntas-count="parrafo.textos.length"
25
+ @reiniciar="reiniciarActividad"
26
+ />
27
+ <ActividadParrafo
28
+ v-else
29
+ :instruccion="parrafo.instruccion"
30
+ :imagen="parrafo.imagen"
31
+ :textos="parrafo.textos"
32
+ @respuesta-cambiada="actualizarRespuesta"
33
+ />
34
+ </div>
35
+ <ActividadCompletarFooter
36
+ v-if="!mostrarResultados"
37
+ class="mx-4 mx-md-5"
38
+ :verificando="verificando"
39
+ :contador="contadorResultados"
40
+ @continuar="verificarRespuestas"
41
+ />
42
+ </div>
43
+ </template>
44
+
45
+ <script>
46
+ import ActividadParrafo from './ActividadParrafo'
47
+ import ActividadCompletarFooter from './ActividadCompletarFooter'
48
+ import ActividadResultados from './ActividadResultados'
49
+ import endGameSuccessSound from '@/assets/actividad/audio/end-game-success.mp3'
50
+ import endGameFailSound from '@/assets/actividad/audio/end-game-fail.mp3'
51
+
52
+ export default {
53
+ name: 'ActividadCompletar',
54
+ components: {
55
+ ActividadParrafo,
56
+ ActividadCompletarFooter,
57
+ ActividadResultados,
58
+ },
59
+ props: {
60
+ parrafo: {
61
+ type: Object,
62
+ required: true,
63
+ },
64
+ },
65
+ data() {
66
+ return {
67
+ respuestasUsuario: {},
68
+ mostrarResultados: false,
69
+ resultadosVerificacion: [],
70
+ porcentajeAprobacion: 0,
71
+ verificando: false,
72
+ contadorResultados: 0,
73
+ intervaloContador: null,
74
+ }
75
+ },
76
+ mounted() {
77
+ this.inicializarRespuestas()
78
+ },
79
+ beforeDestroy() {
80
+ // Limpiar intervalo si existe
81
+ if (this.intervaloContador) {
82
+ clearInterval(this.intervaloContador)
83
+ }
84
+ },
85
+ methods: {
86
+ inicializarRespuestas() {
87
+ if (this.parrafo.textos.length > 0 && this.parrafo?.barajarPreguntas) {
88
+ this.parrafo.textos.sort(() => Math.random() - 0.5)
89
+ }
90
+ this.parrafo.textos.forEach(item => {
91
+ this.$set(this.respuestasUsuario, item.id, '')
92
+ })
93
+ },
94
+
95
+ actualizarRespuesta(data) {
96
+ this.respuestasUsuario = { ...data.todasLasRespuestas }
97
+ },
98
+
99
+ // Nueva función para normalizar texto removiendo tildes y caracteres especiales
100
+ normalizarTexto(texto) {
101
+ return texto
102
+ .toLowerCase()
103
+ .trim()
104
+ .normalize('NFD')
105
+ .replace(/[\u0300-\u036f]/g, '') // Remover diacríticos (tildes, acentos)
106
+ .replace(/[^a-z0-9]/g, '') // Remover caracteres especiales, solo alfanuméricos
107
+ },
108
+
109
+ // REPRODUCIR SONIDOS
110
+ reproducirSonido(audioSrc) {
111
+ const audio = new Audio(audioSrc)
112
+ audio.play()
113
+ },
114
+
115
+ verificarRespuestas() {
116
+ if (this.verificando) return // Evitar múltiples clics
117
+
118
+ const resultados = []
119
+ let correctas = 0
120
+
121
+ this.parrafo.textos.forEach(item => {
122
+ const respuestaUsuario = this.respuestasUsuario[item.id] || ''
123
+ const respuestaCorrecta = item.respuesta
124
+
125
+ // Normalizar ambas respuestas para comparación sin tildes
126
+ const respuestaUsuarioNormalizada = this.normalizarTexto(
127
+ respuestaUsuario,
128
+ )
129
+ const respuestaCorrectaNormalizada = this.normalizarTexto(
130
+ respuestaCorrecta,
131
+ )
132
+
133
+ const esCorrecta =
134
+ respuestaUsuarioNormalizada === respuestaCorrectaNormalizada
135
+
136
+ if (esCorrecta) {
137
+ correctas++
138
+ }
139
+
140
+ // Aplicar feedback visual con icono
141
+ this.aplicarFeedbackConIcono(item.id, esCorrecta)
142
+
143
+ resultados.push({
144
+ id: item.id,
145
+ pregunta: item.texto,
146
+ respuestaUsuario: respuestaUsuario,
147
+ respuestaCorrecta: respuestaCorrecta,
148
+ esCorrecta: esCorrecta,
149
+ })
150
+ })
151
+
152
+ this.resultadosVerificacion = resultados
153
+ this.porcentajeAprobacion = (correctas / this.parrafo.textos.length) * 100
154
+
155
+ // Iniciar contador de 10 segundos
156
+ this.iniciarContadorResultados()
157
+ },
158
+
159
+ aplicarFeedbackConIcono(itemId, esCorrecta) {
160
+ const input = this.$el.querySelector(`input[data-item-id="${itemId}"]`)
161
+ if (input) {
162
+ // Remover clases anteriores
163
+ input.classList.remove('input-correcto', 'input-incorrecto')
164
+
165
+ // Agregar clase según resultado
166
+ input.classList.add(esCorrecta ? 'input-correcto' : 'input-incorrecto')
167
+
168
+ // Deshabilitar input para evitar cambios
169
+ input.disabled = true
170
+
171
+ // Crear y agregar icono
172
+ this.agregarIconoFeedback(input, esCorrecta)
173
+ }
174
+ },
175
+
176
+ agregarIconoFeedback(input, esCorrecta) {
177
+ // Verificar si ya existe un icono para evitar duplicados
178
+ const iconoExistente = input.parentNode.querySelector('.feedback-icon')
179
+ if (iconoExistente) {
180
+ iconoExistente.remove()
181
+ }
182
+
183
+ // Crear elemento del icono
184
+ const icono = document.createElement('span')
185
+ icono.className = `feedback-icon ${
186
+ esCorrecta ? 'feedback-icon--correcto' : 'feedback-icon--incorrecto'
187
+ }`
188
+
189
+ // Insertar el icono después del input
190
+ input.parentNode.insertBefore(icono, input.nextSibling)
191
+ },
192
+
193
+ iniciarContadorResultados() {
194
+ this.verificando = true
195
+ this.contadorResultados = 1 //
196
+
197
+ this.intervaloContador = setInterval(() => {
198
+ this.contadorResultados--
199
+
200
+ if (this.contadorResultados <= 0) {
201
+ clearInterval(this.intervaloContador)
202
+ this.mostrarResultados = true
203
+ this.verificando = false
204
+
205
+ // REPRODUCIR SONIDO SEGUN RESULTADO
206
+ //-------
207
+ if (this.porcentajeAprobacion >= 70) {
208
+ this.reproducirSonido(endGameSuccessSound)
209
+ } else {
210
+ this.reproducirSonido(endGameFailSound)
211
+ }
212
+ }
213
+ }, 1000)
214
+ },
215
+
216
+ reiniciarActividad() {
217
+ // Limpiar intervalo si existe
218
+ if (this.intervaloContador) {
219
+ clearInterval(this.intervaloContador)
220
+ }
221
+
222
+ this.mostrarResultados = false
223
+ this.verificando = false
224
+ this.contadorResultados = 0
225
+ this.inicializarRespuestas()
226
+ this.resultadosVerificacion = []
227
+ this.porcentajeAprobacion = 0
228
+
229
+ // Limpiar los inputs en el DOM
230
+ this.$nextTick(() => {
231
+ const inputs = this.$el.querySelectorAll('.input-inline')
232
+ inputs.forEach(input => {
233
+ input.value = ''
234
+ input.disabled = false
235
+ input.classList.remove('input-correcto', 'input-incorrecto')
236
+ })
237
+
238
+ // Remover todos los iconos de feedback
239
+ const iconos = this.$el.querySelectorAll('.feedback-icon')
240
+ iconos.forEach(icono => {
241
+ icono.remove()
242
+ })
243
+ })
244
+ },
245
+ },
246
+ }
247
+ </script>
248
+
249
+ <style lang="sass" scoped>
250
+ .boton--disabled
251
+ opacity: 0.5
252
+ pointer-events: none
253
+
254
+ .tarjeta--lightest-gray
255
+ border: 3px solid #dce4eb
256
+
257
+ // Estilos para los iconos de feedback
258
+ ::v-deep .feedback-icon
259
+ display: inline-block
260
+ width: 20px
261
+ height: 20px
262
+ margin-right: 8px
263
+ background-size: contain
264
+ background-repeat: no-repeat
265
+ background-position: center
266
+ vertical-align: middle
267
+ animation: fadeInScale 0.3s ease-out
268
+
269
+ &--correcto
270
+ background-image: url('~@/assets/actividad/correcto.svg')
271
+
272
+ &--incorrecto
273
+ background-image: url('~@/assets/actividad/incorrecto.svg')
274
+
275
+ // Animación para la aparición del icono
276
+ @keyframes fadeInScale
277
+ 0%
278
+ opacity: 0
279
+ transform: scale(0.5)
280
+ 100%
281
+ opacity: 1
282
+ transform: scale(1)
283
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div class="tarjeta-avance">
3
+ <div class="row align-items-end justify-content-end">
4
+ <div class="col-auto">
5
+ <button
6
+ class="boton btn-lg boton--b py-3 px-4"
7
+ :disabled="verificando"
8
+ @click="$emit('continuar')"
9
+ >
10
+ <span v-if="!verificando">Verificar respuestas</span>
11
+ <span v-else>Mostrando resultados en {{ contador }}s</span>
12
+ <i v-if="!verificando" class="fas fa-arrow-right"></i>
13
+ <i v-else class="fas fa-clock"></i>
14
+ </button>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ export default {
22
+ name: 'ActividadCompletarFooter',
23
+ props: {
24
+ verificando: {
25
+ type: Boolean,
26
+ default: false,
27
+ },
28
+ contador: {
29
+ type: Number,
30
+ default: 0,
31
+ },
32
+ },
33
+ }
34
+ </script>
35
+
36
+ <style lang="sass" scoped>
37
+ .tarjeta-avance
38
+ background-color: #dce4ebff
39
+ border-bottom-right-radius: 10px
40
+ border-bottom-left-radius: 10px
41
+ overflow: hidden
42
+ @media (max-width: 560px)
43
+ padding-left: .85rem !important
44
+
45
+ .boton
46
+ border-radius: 0
47
+
48
+ .boton--disabled
49
+ opacity: 0.5
50
+ pointer-events: none
51
+
52
+ &__barra-container
53
+ background-color: #b4cce3ff
54
+ border-radius: 8px
55
+ height: 16px
56
+ overflow: hidden
57
+ .tarjeta-avance__barra
58
+ background-color: #2c70abff
59
+ height: 100%
60
+ border-radius: 8px
61
+ width: 0%
62
+ transition: width 0.5s
63
+ </style>