Especialización en Diseño y Gerencia de Producto | U Javeriana - Información Programas Académicos

The following has evaluated to null or missing:
==> yearMatch [in template "20102#20129#9999248" at line 509, column 29]
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${yearMatch[1]} [in template "20102#20129#9999248" at line 509, column 27]
----
1<style>
2 /*Quitar controles a numeros Importante */
3 input[type='number']::-webkit-inner-combobox,
4 input[type='number']::-webkit-outer-combobox {
5 -webkit-appearance: none;
6 margin: 0;
7 }
8 /* Ocultar los controles de incremento y decremento cuando el campo está enfocado */
9 input[type='number']:focus::-webkit-inner-spin-button,
10 input[type='number']:focus::-webkit-outer-spin-button {
11 display: none;
12 }
13
14 /* Ocultar los controles de incremento y decremento cuando el mouse está encima */
15 input[type='number']:hover::-webkit-inner-spin-button,
16 input[type='number']:hover::-webkit-outer-spin-button {
17 display: none;
18 }
19
20 .p-md-5 {
21 padding: 1.5rem !important;
22 }
23
24 .form-group {
25 margin-bottom: 0.6rem;
26 }
27
28 .form-control {
29 background-clip: border-box;
30 border-color: #ced4da;
31 border-bottom-width: 0.0625rem;
32 border-left-width: 0.0625rem;
33 border-right-width: 0.0625rem;
34 border-top-width: 0.0625rem;
35 height: 2.2rem;
36 min-width: 0;
37 }
38
39 .error_text {
40 color: red;
41 font-size: smaller;
42 }
43
44 .formulario_programa {
45 padding: 20px;
46 }
47
48 .formulario-jave {
49 z-index: 5 !important;
50 }
51
52 .contenedor-titulo-form h3 {
53 color: #004c8f;
54 font-weight: 600;
55 text-align: center;
56 }
57
58 .contenedor-titulo-form p {
59 text-align: center;
60 }
61
62 #boton_enviar1,
63 #boton_enviar2 {
64 color: #004c8f !important;
65 }
66
67 @media only screen and (max-width: 600px) {
68 .contenedor-titulo-form h3 {
69 font-size: 1.3rem;
70 }
71 }
72</style>
73
74<script src="https://www.javeriana.edu.co/recursosdb/1372208/10609114/vueprod.js"></script>
75<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
76<div id="app">
77 <div class="formulario_programa">
78 <div class="contenedor-titulo-form">
79 <h3>Conoce más del programa</h3>
80 <p>Déjanos tus datos y te contactaremos para brindarte toda la información.</p>
81 </div>
82 <form
83 action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8"
84 id="form_SF"
85 ref="form_SF"
86 v-on:submit.prevent="validateForm"
87 method="post"
88 class="d-flex flex-column justify-content-around px-4 py-5 p-md-5"
89 novalidate
90 aria-label="Formulario de contacto"
91 class="needs-validation">
92 <input type="hidden" name="oid" value="00Df4000003l8Bf" />
93 <input type="hidden" name="retURL" id="retURL" :value="data.urlPrograma + '?Subsource=' + data.programa" />
94 <!-- <input type="hidden" name="debug" value="1">
95 <input type="hidden" name="debugEmail" value="@javeriana.edu.co"> -->
96
97 <!-- Información del evento OCULTO-->
98 <input v-if="data.nevento" type="hidden" id="00NJw000006f1BF" name="00NJw000006f1BF" :value="data.nevento" />
99 <input v-if="fevento" type="hidden" id="00NJw000006f1BE" name="00NJw000006f1BE" :value="fevento" />
100
101 <!--Nombres y apellidos-->
102 <div class="form-row">
103 <div class="form-group col-6">
104 <input
105 placeholder="*Nombre(s)"
106 id="first_name"
107 v-model="data.first_name"
108 class="form-control border-top-0 border-left-0 border-right-0 rounded-0"
109 maxlength="40"
110 v-on:change="deleteInvalid('first_name')"
111 name="first_name"
112 type="text"
113 :class="{ error: errors.first_name }"
114 required="" />
115 <div class="error_text" v-show="errors.first_name">*Campo obligatorio</div>
116 </div>
117
118 <div class="form-group col-6">
119 <input
120 placeholder="*Apellidos"
121 id="last_name"
122 v-model="data.last_name"
123 v-on:change="deleteInvalid('last_name')"
124 class="form-control border-top-0 border-left-0 border-right-0 rounded-1 needs-validation"
125 maxlength="80"
126 name="last_name"
127 type="text"
128 required=""
129 :class="{ error: errors.last_name }" />
130 <div class="error_text" v-show="errors.last_name">*Campo obligatorio</div>
131 </div>
132 </div>
133
134 <!--Documentos de identidad-->
135 <div class="form-row">
136 <div class="form-group col-5">
137 <select
138 id="00N5G00000WmhsT"
139 v-model="data.tipo_doc"
140 v-on:change="deleteInvalid('tipo_doc')"
141 name="00N5G00000WmhsT"
142 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
143 required=""
144 :class="{ error: errors.tipo_doc }">
145 <option value="">*Tipo de documento</option>
146 <option value="CC">Cédula Ciudadanía</option>
147 <option value="CE">Cédula Extranjería</option>
148 <option value="PA">Pasaporte</option>
149 <option value="TI">Tarjeta Identidad</option>
150 <!-- Agregar opciones aquí -->
151 </select>
152 <div class="error_text" v-show="errors.tipo_doc">*Campo obligatorio</div>
153 </div>
154
155 <div class="form-group col-7">
156 <input
157 placeholder="*Número de documento"
158 v-on:change="deleteInvalid('numero_doc')"
159 v-model="data.numero_doc"
160 id="00N5G00000WmhsR"
161 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
162 maxlength="255"
163 name="00N5G00000WmhsR"
164 type="text"
165 required
166 :class="{ error: errors.numero_doc }" />
167 <div class="error_text" v-show="errors.numero_doc">*Campo obligatorio</div>
168 </div>
169 </div>
170 <!--Email y celular-->
171 <div class="form-row">
172 <div class="form-group col-12">
173 <input
174 placeholder="*Email"
175 id="email"
176 v-model="data.email"
177 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
178 maxlength="80"
179 name="email"
180 type="email"
181 required
182 :class="{ error: errors.email }" />
183 <div class="error_text" v-show="errors.email">Ingrese un correo válido</div>
184 </div>
185 </div>
186
187 <!--prefijo y celular-->
188 <div class="form-row">
189 <div class="form-group col-3">
190 <div class="select-wrapper" style="width: 100%">
191 <select
192 id="00NJw000002mzb7"
193 name="00NJw000002mzb7"
194 class="prefijoSelect form-control border-top-0 border-left-0 border-right-0 rounded-1"
195 v-model="data.prefijoCel"
196 v-on:change="deleteInvalid('prefijoCel')"
197 required
198 style="font-size: medium; padding: 0px 25px 0px 5px">
199 <option value="">(+) Indicativo</option>
200 <option
201 v-for="pais in prefijoCel"
202 :value="pais.phoneCode"
203 v-bind:style="{ backgroundColor: pais.phoneName === 'Colombia' ? 'aliceBlue' : '', color: pais.phoneName === 'Colombia' ? '#000000' : '', fontWeight: pais.phoneName === 'Colombia' ? '900' : '' }">
204 (+{{ pais.phoneCode }}) {{ pais.phoneName }}
205 </option>
206 </select>
207 <div class="error_text" v-show="errors.prefijoCel">*Seleccione</div>
208 </div>
209 </div>
210 <div class="form-group col-9">
211 <input
212 placeholder="*Teléfono celular"
213 id="mobile"
214 v-model="data.mobile"
215 v-on:change="deleteInvalid('mobile')"
216 class="form-control col-12 border-top-0 border-left-0 border-right-0 rounded-1"
217 name="mobile"
218 type="text"
219 minlength="8"
220 maxlength="15"
221 required
222 :class="{ error: errors.mobile }" />
223 <div class="error_text" v-show="errors.mobile">*Campo obligatorio (entre 8 y 10 caracteres)</div>
224 </div>
225 </div>
226
227 <!--Lugar de residencia-->
228 <div class="form-row">
229 <!-- Pais -->
230 <div class="form-group col-12">
231 <select
232 v-model="data.pais"
233 v-on:change="deleteInvalid('pais')"
234 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
235 id="00N5G00000WmhvJ"
236 name="00N5G00000WmhvJ"
237 required
238 :class="{ error: errors.pais }">
239 <option value="">*País de residencia</option>
240 <option v-for="(item, index) in ubicaciones" v-bind:value="index" v-bind:style="(index=='COL') ? 'font-weight: 700;' : '' ">
241 {{ item.nombre }}
242 </option>
243 </select>
244 <div class="error_text" v-show="errors.pais">*Campo obligatorio</div>
245 </div>
246
247 <!-- Departamento -->
248 <div class="form-group col-md-6 col-sm-12" id="departamentoContainer" v-if="data.pais=='COL'">
249 <select
250 v-model="data.departamento"
251 v-on:change="deleteInvalid('departamento')"
252 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
253 id="00N5G00000WmhvX"
254 name="00N5G00000WmhvX"
255 :class="{ error: errors.departamento }">
256 <option value="">*Selecciona el departamento</option>
257 <option v-for="(item, index) in ubicaciones[data.pais].departamentos" v-bind:value="item.codigo">{{ item.nombre }}</option>
258 </select>
259 <div class="error_text" v-show="errors.departamento">*Campo obligatorio</div>
260 </div>
261
262 <!-- Ciudad -->
263 <div class="form-group col-md-6 col-sm-12" id="ciudadContainer" v-if="data.pais=='COL' && data.departamento!=''">
264 <select
265 v-model="data.ciudad"
266 v-on:change="deleteInvalid('ciudad')"
267 class="form-control border-top-0 border-left-0 border-right-0 rounded-1"
268 id="00N5G00000WmhvO"
269 name="00N5G00000WmhvO"
270 v-bind:required="data.pais=='COL'"
271 :class="{ error: errors.ciudad }">
272 <option value="">*Selecciona la Ciudad</option>
273 <option v-for="(item, index) in getCiudades(data.departamento)" v-bind:value="item.codigo">{{ item.nombre }}</option>
274 </select>
275 <div class="error_text" v-show="errors.ciudad">*Campo obligatorio</div>
276 </div>
277 </div>
278
279 <!--Periodo de Ingreso-->
280 <div class="form-row">
281 <div class="form-group col-12">
282 <select
283 class="form-control validates-as-required border-top-0 border-left-0 border-right-0 rounded-1"
284 id="00N5G00000WmhvI"
285 v-model="data.periodo_esperado"
286 v-on:change="deleteInvalid('periodo_esperado')"
287 :class="{ error: errors.periodo_esperado }"
288 maxlength="20"
289 name="00N5G00000WmhvI"
290 required="">
291 <option value="">*Periodo de Ingreso</option>
292 <option v-for="(periodo, periodoLabel) in periodoSel" v-bind:value="periodo" v-bind:key="periodo">
293 {{ periodoLabel }}
294 </option>
295 </select>
296 <div class="error_text" v-show="errors.periodo_esperado">*Campo obligatorio</div>
297 </div>
298 </div>
299
300 <!--Programa-->
301 <div class="form-row col-12" style="display: none">
302 <div class="col-12">
303 <span class="wpcf7-form-control-wrap">
304 <input
305 id="00N5G00000WmhvV"
306 v-model="data.programa"
307 class="form-control validates-as-required rounded-1 text-dark mb-3"
308 name="00N5G00000WmhvV"
309 :class="{ error: errors.programa }" />
310 <div class="error_text" v-show="errors.programa">Selecciona</div>
311 </span>
312 </div>
313 </div>
314
315 <!--origen IMPORTANTE debe estar oculto-->
316 <div style="display: none">
317 <div class="form-row">
318 Origen de la Solicitud:<select
319 id="00NJw000001J3Hl"
320 name="00NJw000001J3Hl"
321 v-model="data.origen_sol"
322 title="Origen de la Solicitud">
323 <option selected value="Web to Lead">Web to Lead</option>
324 </select>
325
326 Fuente autorización datos personales:<select
327 id="00N5G00000WmhvT"
328 name="00N5G00000WmhvT"
329 v-model="data.fuenteAutoriza"
330 title="Fuente autorización datos personales">
331 <option value="Landing Programas">Landing Programas</option>
332 </select>
333 <label for="lead_source">Origen del candidato</label>
334 <select id="lead_source" v-model="data.lead" name="lead_source">
335 <option value="Landing Pages">Landing Pages</option>
336 </select>
337 <label for="company">Compañía</label
338 ><input id="company" v-model="data.company" maxlength="40" name="company" size="20" type="text" value="NA" />
339 </div>
340
341 <!--UTMS-->
342 <div class="form-row">
343 Fuente:<input id="00N5G00000WmhvW" v-model="data.fuente" name="00N5G00000WmhvW" title="Fuente" type="text" /> Subfuente:<input
344 id="00N5G00000WmhvZ"
345 v-model="data.subfuente"
346 name="00N5G00000WmhvZ"
347 title="Subfuente"
348 type="text" />
349 Medio
350 <input id="00NJw000001J3g8" v-model="data.medio" name="00NJw000001J3g8" title="Medio" type="text" />
351 Campaña:<input id="00N5G00000Wmi8x" v-model="data.campana" maxlength="255" name="00N5G00000Wmi8x" type="text" />
352 </div>
353 </div>
354
355 <!--Legales y botón de inscripción-->
356 <div class="form-row col-12 mb-3">
357 <div class="col-12">
358 <span class="d-block text-azul"
359 >¿Autoriza usted el tratamiento de sus datos personales de acuerdo con la
360 <a
361 href="https://www.javeriana.edu.co/recursosdb/20125/12161151/politica-de-proteccion-de-datos-personales-universidad-javeriana.pdf"
362 class="text-azul fw-medium text-decoration-underline"
363 target="_blank"
364 >autorización de privacidad</a
365 >?</span
366 >
367 <div class="form-check form-check-inline">
368 <input
369 v-on:change="validateAutoriza()"
370 class="form-check-input"
371 type="radio"
372 v-model="data.autoriza"
373 id="00N5G00000WmhvF"
374 name="00N5G00000WmhvF"
375 value="1" />
376 <label class="form-check-label text-azul" for="00N5G00000WmhvF">Sí</label><br />
377 </div>
378 <div class="form-check form-check-inline">
379 <input
380 v-on:change="validateAutoriza()"
381 class="form-check-input"
382 type="radio"
383 v-model="data.autoriza"
384 name="00N5G00000WmhvF"
385 id="00N5G00000WmhvF"
386 value="0" />
387 <label class="form-check-label text-azul" for="autorizacion-2">No</label>
388 </div>
389 </div>
390 <div class="col-md-12" id="autorizacionErrorMessage" v-show="errors.autoriza">
391 <p style="color: brown; font-size: 0.8rem">
392 La Pontificia Universidad Javeriana requiere de su autorización para el tratamiento de sus datos personales para continuar con
393 el presente proceso, sin la autorización legalmente no podemos darle continuidad al mismo
394 </p>
395 </div>
396 </div>
397 <!--Botón-->
398 <div class="form-row col-12 mb-3">
399 <div class="col-12">
400 <button
401 id="boton_enviar1"
402 class="btn btn-warning w-50 py-2 text-azul fw-bold shadow my-2 border-0 mx-auto d-block"
403 type="button"
404 style="background-color: #f6bd30; font-size: 1rem; white-space: unset; border-radius: 8rem; color: #ffffff; font-weight: 700"
405 disabled="disabled">
406 Cargando...
407 </button>
408 <button
409 id="boton_enviar2"
410 class="btn btn-warning w-50 py-2 text-azul fw-bold shadow my-2 border-0 mx-auto d-none"
411 type="submit"
412 v-bind:disabled="formInValid || isSubmitting"
413 style="
414 padding: 0.5rem 1rem;
415 border-radius: 1rem;
416 background-color: #f6bd30;
417 font-size: 1rem;
418 white-space: normal;
419 border-radius: 8rem;
420 color: #ffffff;
421 font-weight: 700;
422 ">
423 Enviar
424 </button>
425 </div>
426 </div>
427 </form>
428 </div>
429</div>
430
431<script>
432 console.log('🎬 [SCRIPT] Script del formulario cargado')
433
434 Liferay.on('allPortletsReady', function () {
435 console.log('🚀 [LIFERAY] allPortletsReady event triggered')
436
437 let boton1 = document.getElementById('boton_enviar1')
438 let boton2 = document.getElementById('boton_enviar2')
439
440 console.log(' - boton1 (Cargando...):', !!boton1)
441 console.log(' - boton2 (Enviar):', !!boton2)
442
443 if (boton1 && boton2) {
444 boton1.classList.remove('d-block')
445 boton1.classList.add('d-none')
446
447 boton2.classList.remove('d-none')
448 boton2.classList.add('d-block')
449
450 console.log('✅ [LIFERAY] Botones actualizados correctamente')
451 console.log(' - boton1 disabled:', boton1.disabled)
452 console.log(' - boton2 disabled:', boton2.disabled)
453 } else {
454 console.error('❌ [LIFERAY] No se encontraron los botones')
455 }
456 })
457
458 //Captura del codigo SAE
459 //let codPrograma = "DSIND";
460 console.log('🔍 [SCRIPT] Verificando codPrograma:', typeof codPrograma !== 'undefined' ? codPrograma : 'NO DEFINIDO')
461
462
463 const config = {
464 eventDate: "", // <- ajusta la fecha por defecto aquí. Formato: DD/MM/AAAA, HH:MM a./p. m.
465 }
466
467 const PERIODOS_SOURCE = 'https://www.javeriana.edu.co/recursosdb/1372208/10609114/periodos.json'
468 const PROGRAM_CLOSURE_DATE_SOURCE = Object.freeze([
469 'https://www.javeriana.edu.co/JaveMovil/ValoresMatricula-1/rs/psujsfvaportals/filter?nivelEstudio=carrera',
470 'https://www.javeriana.edu.co/JaveMovil/ValoresMatricula-1/rs/psujsfvaportals/filter?nivelEstudio=Maestr%C3%ADa',
471 'https://www.javeriana.edu.co/JaveMovil/ValoresMatricula-1/rs/psujsfvaportals/filter?nivelEstudio=especializaci%C3%B3n',
472 'https://www.javeriana.edu.co/JaveMovil/ValoresMatricula-1/rs/psujsfvaportals/filter?nivelEstudio=Doctorado',
473 'https://www.javeriana.edu.co/JaveMovil/ValoresMatricula-1/rs/psujsfvaportals/filter?nivelEstudio=Licenciatura'
474 ])
475 const COLOMBIA_MIDNIGHT_UTC_HOUR = 5
476
477 function fetchJson(url, fallbackValue, options = {}) {
478 return axios
479 .get(url)
480 .then(response => response.data)
481 .catch(error => {
482 if (options.onError) options.onError(error)
483 return fallbackValue
484 })
485 }
486
487 function normalizeAcademicCycleText(value) {
488 return String(value || '')
489 .normalize('NFD')
490 .replace(/[\u0300-\u036f]/g, '')
491 .replace(/[.]/g, '')
492 .trim()
493 .toLowerCase()
494 }
495
496 function resolveAcademicCycleSuffix(normalizedDescription) {
497 if (normalizedDescription.includes('intersemestral')) return '20'
498 if (normalizedDescription.includes('primer') || /\b1(?:er|ro)?\b/.test(normalizedDescription)) return '10'
499 if (normalizedDescription.includes('segundo') || /\b2(?:do)?\b/.test(normalizedDescription)) return '30'
500 return ''
501 }
502
503 function resolveAcademicCycleKeyFromText(cycleDescription) {
504 const normalizedDescription = normalizeAcademicCycleText(cycleDescription)
505 const yearMatch = normalizedDescription.match(/\b(20\d{2})\b/)
506 if (!yearMatch) return ''
507
508 const cycleSuffix = resolveAcademicCycleSuffix(normalizedDescription)
509 return cycleSuffix ? `${yearMatch[1]}-${cycleSuffix}` : ''
510 }
511
512 function resolveAcademicCycleKeyFromPeriodCode(periodCode) {
513 const codeMatch = String(periodCode || '').match(/(\d{2})(10|20|30)$/)
514 return codeMatch ? `20${codeMatch[1]}-${codeMatch[2]}` : ''
515 }
516
517 function resolveAcademicCycleKeyFromPeriodEntry(periodEntry = []) {
518 const [periodLabel, periodCode] = periodEntry
519 return (
520 resolveAcademicCycleKeyFromPeriodCode(periodCode) ||
521 resolveAcademicCycleKeyFromText(periodLabel)
522 )
523 }
524
525 function parseTime12Format(timeStr, meridianHint = '') {
526 const [h, m] = timeStr.split(':')
527 let hour = Number.parseInt(h, 10) || 0
528 const minute = m ? Number.parseInt(m, 10) : 0
529 const meridian = /p/i.test(meridianHint || timeStr) ? 'pm' : 'am'
530
531 if (meridian === 'pm' && hour < 12) hour += 12
532 if (meridian === 'am' && hour === 12) hour = 0
533
534 return { hour, minute }
535 }
536
537 function extractTime(dateStr) {
538 let s = dateStr
539 let hasTime = false
540 let hour = 0
541 let minute = 0
542
543 const time12 = s.match(/(\d{1,2}(?::\d{2})?)\s*(a\.?m\.?|p\.?m\.?)/i)
544 if (time12) {
545 ;({ hour, minute } = parseTime12Format(time12[1], time12[2]))
546 hasTime = true
547 s = s.replace(time12[0], ' ')
548 return { s, hasTime, hour, minute }
549 }
550
551 const time24 = s.match(/(\d{1,2}):(\d{2})/)
552 const numericDate = s.match(/^\s*\d{1,2}[/-]\d{1,2}[/-]\d{2,4}/)
553 if (time24 && !numericDate) {
554 hour = Number.parseInt(time24[1], 10) || 0
555 minute = Number.parseInt(time24[2], 10) || 0
556 hasTime = true
557 s = s.replace(time24[0], ' ')
558 }
559
560 return { s, hasTime, hour, minute }
561 }
562
563 function parseNumericDate(dateStr, hour, minute, hasTime) {
564 const match = dateStr.match(/(\d{1,2})[/-](\d{1,2})[/-](\d{2,4})/)
565 if (!match) return null
566
567 const date = new Date(
568 Number.parseInt(match[3], 10),
569 Number.parseInt(match[2], 10) - 1,
570 Number.parseInt(match[1], 10),
571 hour,
572 minute
573 )
574 date.hasTime = hasTime
575 return date
576 }
577
578 function parseWordBasedDate(dateStr, hour, minute, hasTime, months) {
579 const parts = dateStr
580 .toLowerCase()
581 .replaceAll(',', ' ')
582 .replaceAll(/\s+/g, ' ')
583 .trim()
584 .split(' ')
585
586 let day = null
587 let month = null
588 let year = null
589
590 for (const part of parts) {
591 if (!day && /^\d{1,2}$/.test(part)) day = Number.parseInt(part, 10)
592 else if (month === null && Object.hasOwn(months, part)) month = months[part]
593 else if (!year && /^\d{4}$/.test(part)) year = Number.parseInt(part, 10)
594 }
595
596 if (!day || month === null || !year) return null
597
598 const date = new Date(year, month, day, hour, minute)
599 date.hasTime = hasTime
600 return date
601 }
602
603 function parseSpanishEventDate(dateStr) {
604 if (!dateStr || typeof dateStr !== 'string') return null
605
606 const months = {
607 enero: 0,
608 febrero: 1,
609 marzo: 2,
610 abril: 3,
611 mayo: 4,
612 junio: 5,
613 julio: 6,
614 agosto: 7,
615 septiembre: 8,
616 octubre: 9,
617 noviembre: 10,
618 diciembre: 11
619 }
620
621 const { s, hasTime, hour, minute } = extractTime(dateStr.trim())
622 return (
623 parseNumericDate(s, hour, minute, hasTime) ||
624 parseWordBasedDate(s, hour, minute, hasTime, months)
625 )
626 }
627
628 function parseClosureDateToBogotaCutoff(closureDateText) {
629 const closureDate = parseSpanishEventDate(
630 String(closureDateText || '').replace(/[.]/g, '')
631 )
632 if (!closureDate) return null
633
634 return new Date(
635 Date.UTC(
636 closureDate.getFullYear(),
637 closureDate.getMonth(),
638 closureDate.getDate() + 1,
639 COLOMBIA_MIDNIGHT_UTC_HOUR
640 )
641 )
642 }
643
644 function normalizeProgramCode(programCode) {
645 return String(programCode || '').trim().toUpperCase()
646 }
647
648 function findClosureProgram(programClosureData = [], programCode = '') {
649 const requestedProgramCode = normalizeProgramCode(programCode)
650 if (!requestedProgramCode) return null
651
652 const closurePrograms = Array.isArray(programClosureData)
653 ? programClosureData
654 : [programClosureData]
655
656 return closurePrograms.find(
657 closureProgram =>
658 normalizeProgramCode(closureProgram?.codPrograma) === requestedProgramCode
659 )
660 }
661
662 function hasClosedPeriodStatus(closureDateText, currentDate) {
663 const statusText = String(closureDateText || '').trim()
664 if (!statusText) return false
665
666 const cutoffDate = parseClosureDateToBogotaCutoff(statusText)
667 return cutoffDate ? currentDate >= cutoffDate : true
668 }
669
670 function closedCycleKeysForProgram(closureProgram, now) {
671 const currentDate = now instanceof Date ? now : new Date(now)
672 if (Number.isNaN(currentDate.getTime())) return new Set()
673
674 return new Set(
675 (closureProgram?.datosFechaCierreInscripcion || [])
676 .filter(closureDate =>
677 hasClosedPeriodStatus(closureDate?.fFinCierreLetra, currentDate)
678 )
679 .map(closureDate =>
680 resolveAcademicCycleKeyFromText(closureDate?.descCiclo)
681 )
682 .filter(Boolean)
683 )
684 }
685
686 function filterClosedPeriodEntries({
687 periodEntries = [],
688 programCode = '',
689 programClosureData = [],
690 now = new Date()
691 } = {}) {
692 const entries = Array.isArray(periodEntries) ? [...periodEntries] : []
693 const closureProgram = findClosureProgram(programClosureData, programCode)
694 if (!closureProgram) return entries
695
696 const closedCycleKeys = closedCycleKeysForProgram(closureProgram, now)
697 if (!closedCycleKeys.size) return entries
698
699 return entries.filter(
700 periodEntry =>
701 !closedCycleKeys.has(resolveAcademicCycleKeyFromPeriodEntry(periodEntry))
702 )
703 }
704
705 function getStandardPeriodEntries(level, periodEntries) {
706 const entries = Array.isArray(periodEntries) ? [...periodEntries] : []
707 return entries.filter(([, code]) => !String(code).endsWith('20'))
708 }
709
710 function getVisiblePeriodEntries({
711 level,
712 periodEntries,
713 programCode = '',
714 programClosureData = [],
715 now = new Date()
716 } = {}) {
717 const entries = Array.isArray(periodEntries) ? [...periodEntries] : []
718 const openEntries = filterClosedPeriodEntries({
719 periodEntries: entries,
720 programCode,
721 programClosureData,
722 now
723 })
724
725 return getStandardPeriodEntries(level, openEntries)
726 }
727
728 function resolveNivelPeriodo(nivelAcad, grado) {
729 if (nivelAcad && nivelAcad.toLowerCase().includes('eclesiástico')) return 'ECLE'
730
731 const gradoMap = {
732 Pregrado: 'PREG',
733 Posgrado: 'GRAD',
734 Eclesiástico: 'ECLE',
735 Técnico: 'ETDH'
736 }
737
738 if (grado && gradoMap[grado]) return gradoMap[grado]
739
740 if (nivelAcad) {
741 const nivel = nivelAcad.toLowerCase()
742 if (nivel.includes('especialización') || nivel.includes('maestría') || nivel.includes('doctorado')) return 'GRAD'
743 if (nivel.includes('técnico')) return 'ETDH'
744 }
745
746 return 'PREG'
747 }
748
749 const { createApp } = Vue
750
751 const app = createApp({
752 data() {
753 return {
754 programas: [],
755 prefijoCel: [],
756 periodos: {},
757 programClosureDates: [],
758 nivelAcadPrograma: 'PREG',
759 ubicaciones: [],
760 formInValid: true,
761 isSubmitting: false,
762 data: {
763 urlPrograma: '',
764 first_name: '',
765 last_name: '',
766 tipo_doc: '',
767 numero_doc: '',
768 email: '',
769 prefijoCel: '',
770 mobile: '',
771 pais: '',
772 departamento: '',
773 ciudad: '',
774 programa: codPrograma,
775 periodo_esperado: '',
776 origen_sol: 'Web to Lead',
777 fuenteAutoriza: 'Landing Programas',
778 lead: 'Landing Pages',
779 company: 'NA',
780 fuente: 'Javeriana',
781 subfuente: 'Organico',
782 medio: 'Landing',
783 campana: 'Mercadeo',
784 autoriza: '',
785 nevento: ''
786 },
787 errors: {
788 urlPrograma: false,
789 first_name: false,
790 last_name: false,
791 tipo_doc: false,
792 numero_doc: false,
793 email: false,
794 prefijoCel: false,
795 mobile: false,
796 pais: false,
797 departamento: false,
798 ciudad: false,
799 programa: false,
800 periodo_esperado: false,
801 origen_sol: false,
802 fuenteAutoriza: false,
803 lead: false,
804 company: false,
805 fuente: false,
806 subfuente: false,
807 medio: false,
808 campana: false,
809 autoriza: false
810 }
811 }
812 },
813 watch: {
814 'data.departamento': function (newVal, oldVal) {
815 this.data.ciudad = ''
816 this.errors.ciudad = false
817 },
818 'data.first_name': function (newText, oldText) {
819 this.data.first_name = this.limpiarTexto(newText)
820 },
821 'data.last_name': function (newText, oldText) {
822 this.data.last_name = this.limpiarTexto(newText)
823 },
824 'data.mobile': function (newMobile, oldMobile) {
825 this.data.mobile = this.limpiarNumeros(newMobile)
826 },
827 'data.email': function (newEmail, oldEmail) {
828 const regex =
829 /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
830 if (regex.test(newEmail) === false) {
831 this.errors.email = true
832 } else {
833 this.errors.email = false
834 }
835 }
836 },
837 mounted() {
838 console.log('🚀 [FORM DEBUG] Vue mounted - Iniciando formulario')
839 console.log(' - codPrograma:', typeof codPrograma !== 'undefined' ? codPrograma : 'NO DEFINIDO')
840
841 // Recien carga el sitio se cargan los datos
842 this.limpiarDatos()
843 this.getUbicaciones()
844 this.getProgramas(codPrograma)
845 this.getPeriodos()
846 this.getProgramClosureDates()
847 this.getParametersGet()
848 this.getPrefijoCel()
849
850 console.log('✅ [FORM DEBUG] Mounted completado')
851 },
852 methods: {
853 /**
854 * Funcion para limpiar la variable de los datos
855 **/
856 limpiarDatos() {
857 this.data = {
858 urlPrograma: '',
859 first_name: '',
860 last_name: '',
861 tipo_doc: '',
862 numero_doc: '',
863 email: '',
864 mobile: '',
865 prefijoCel: '',
866 pais: '',
867 departamento: '',
868 ciudad: '',
869 programa: codPrograma,
870 periodo_esperado: '',
871 origen_sol: 'Web to Lead',
872 fuenteAutoriza: 'Landing Programas',
873 lead: 'Landing Pages',
874 company: 'NA',
875 fuente: 'Javeriana',
876 subfuente: 'Organico',
877 medio: 'Landing',
878 campana: 'Mercadeo',
879 autoriza: ''
880 }
881 },
882 /**
883 * Funcion para obtener los valores de los parametros que vienen por GET
884 **/
885 getParametersGet() {
886 let urlParams = new URLSearchParams(window.location.search)
887 this.data.fuente = urlParams.has('utm_source') ? urlParams.get('utm_source') : 'Javeriana'
888 this.data.subfuente = urlParams.has('utm_subsource') ? urlParams.get('utm_subsource') : 'Organico'
889 this.data.medio = urlParams.has('utm_medium') ? urlParams.get('utm_medium') : 'Landing'
890 this.data.campana = urlParams.has('utm_campaign') ? urlParams.get('utm_campaign') : 'Mercadeo'
891 this.data.nevento = urlParams.has('nevento') ? urlParams.get('nevento') : this.data.nevento
892 },
893 /**
894 * Funcion para cargar los programas
895 **/
896 getProgramas(codPrograma) {
897 console.log('🔍 [PROGRAMAS] Buscando información del programa')
898 console.log(' - Código buscado:', codPrograma)
899
900 //return axios.get('https://raw.githubusercontent.com/gesalas/feria/main/programas.json')
901 return axios
902 .get('https://www.javeriana.edu.co/recursosdb/1372208/10609114/programas.json')
903 .then(response => {
904 console.log('✅ [PROGRAMAS] JSON de programas obtenido')
905 const data = response.data
906 let programaEncontrado = false
907
908 for (let facultadKey in data) {
909 const facultad = data[facultadKey]
910 for (let programaKey in facultad) {
911 const programas = facultad[programaKey].Programas
912 for (let i = 0; i < programas.length; i++) {
913 if (programas[i].Codigo === codPrograma) {
914 this.data.urlPrograma = facultad[programaKey].url
915 programaEncontrado = true
916 console.log('✅ [PROGRAMAS] Programa encontrado!')
917 console.log(' - Facultad:', facultadKey)
918 console.log(' - Programa:', programaKey)
919 console.log(' - URL:', this.data.urlPrograma)
920 return
921 }
922 }
923 }
924 }
925
926 if (!programaEncontrado) {
927 console.warn('⚠️ [PROGRAMAS] Programa NO encontrado con código:', codPrograma)
928 console.log(' - urlPrograma quedará vacío')
929 }
930 })
931 .catch(error => {
932 console.error('❌ [PROGRAMAS] Error al obtener los programas:', error)
933 })
934 },
935 //
936 /**
937 * Funcion para cargar los periodos
938 **/
939 getPeriodos() {
940 fetchJson(PERIODOS_SOURCE, {}, {
941 onError: error => console.error('Error al cargar periodos:', error)
942 }).then(periodos => {
943 this.periodos = periodos
944 })
945 },
946 /**
947 * Funcion para cargar fechas de cierre por programa
948 **/
949 getProgramClosureDates() {
950 Promise.all(
951 PROGRAM_CLOSURE_DATE_SOURCE.map(closureDateUrl =>
952 fetchJson(closureDateUrl, [], {
953 onError: error =>
954 console.error('Error al cargar fechas de cierre de periodos:', error)
955 })
956 )
957 ).then(programClosureDateGroups => {
958 this.programClosureDates = programClosureDateGroups.flatMap(
959 programClosureDateGroup =>
960 Array.isArray(programClosureDateGroup) ? programClosureDateGroup : []
961 )
962 })
963 },
964 /**
965 * Funcion para cargar las ubicaciones
966 **/
967 getUbicaciones() {
968 axios
969 .get('https://www.javeriana.edu.co/recursosdb/1372208/10609114/ubicaciones.json')
970 //axios.get('https://raw.githubusercontent.com/gesalas/feria/main/ubicaciones.json')
971 .then(response => {
972 this.ubicaciones = response.data
973 })
974 .catch(function (error) {
975 console.log(error)
976 })
977 },
978
979 /**
980 * Funcion para cargar los celulares
981 **/
982 getPrefijoCel() {
983 axios
984 .get('https://www.javeriana.edu.co/recursosdb/1372208/10609114/codigos_pais.json')
985 //axios.get('https://raw.githubusercontent.com/gesalas/Piloto_registro_de_llamadas/main/Json/codigos_pais.json')
986 .then(response => {
987 // Filtrar los datos para obtener solo los códigos de país y sus prefijos
988 this.prefijoCel = response.data.map(pais => {
989 return {
990 iso2: pais.iso2,
991 phoneCode: pais.phoneCode,
992 phoneName: pais.nameES
993 }
994 })
995 })
996 },
997
998 /**
999 * Funcion para obtener las ciudades del departamento seleccionado
1000 **/
1001 getCiudades(departamento) {
1002 let departamentoSel = this.ubicaciones['COL'].departamentos.filter(res => {
1003 return res.codigo == departamento
1004 })
1005 return departamentoSel[0].ciudades
1006 },
1007 /**
1008 * Funcion para validar si esta el campo de aceptar terminos
1009 **/
1010 validateAutoriza() {
1011 console.log('📋 [AUTORIZA] validateAutoriza llamado')
1012 console.log(' - Valor autoriza:', this.data.autoriza)
1013
1014 if (this.data.autoriza == '1') {
1015 this.errors.autoriza = false
1016 this.formInValid = false
1017 console.log('✅ [AUTORIZA] Usuario autorizó - formInValid=false')
1018 } else if (this.data.autoriza == '0') {
1019 this.errors.autoriza = true
1020 this.formInValid = true
1021 console.log('❌ [AUTORIZA] Usuario NO autorizó - formInValid=true')
1022 }
1023
1024 console.log(' - Estado final: formInValid=', this.formInValid)
1025 },
1026 /**
1027 * Funcion para limpiar textos
1028 **/
1029 limpiarTexto(texto) {
1030 let textolimpio = texto.replace(/[^a-zA-Záéíóúñ ]/g, '')
1031 return textolimpio
1032 },
1033 /**
1034 * Funcion para limpiar numeros
1035 **/
1036 limpiarNumeros(numero) {
1037 let textolimpio = numero.replace(/[^0-9 ]/g, '')
1038 return textolimpio
1039 },
1040 /**
1041 * Funcion para limpiar json
1042 **/
1043 limpiarJson(texto) {
1044 return texto
1045 .replaceAll('|', '')
1046 .replace(/(\r\n|\n|\r)/gm, '')
1047 .replace(/['"]+/g, "'")
1048 .trim()
1049 },
1050 /**
1051 * Funcion para limpiar alfanumericos
1052 **/
1053 limpiarAlfanumerico(text) {
1054 let textolimpio = text.replace(/[^a-zA-Z0-9- ]/g, '')
1055 return textolimpio
1056 },
1057 /**
1058 * Funcion para validar que los campos tengan datos
1059 **/
1060 validateInputs() {
1061 console.log('🔍 [VALIDATION] Iniciando validateInputs')
1062 let valid = true
1063 let keys = Object.keys(this.data)
1064 let erroresEncontrados = []
1065
1066 keys.forEach(element => {
1067 this.errors[element] = false
1068 switch (element) {
1069 case 'first_name':
1070 case 'last_name':
1071 case 'tipo_doc':
1072 case 'numero_doc':
1073 case 'urlPrograma':
1074 case 'pais':
1075 case 'periodo_esperado':
1076 case 'prefijoCel':
1077 case 'programa':
1078 if (this.data[element] == '' || this.data[element] == null) {
1079 valid = false
1080 this.errors[element] = true
1081 erroresEncontrados.push({
1082 campo: element,
1083 valor: this.data[element],
1084 razon: 'Campo vacío o null'
1085 })
1086 }
1087 break
1088 case 'departamento':
1089 case 'ciudad':
1090 if ((this.data[element] == '' || this.data[element] == null) && this.data.pais == 'COL') {
1091 valid = false
1092 this.errors[element] = true
1093 erroresEncontrados.push({
1094 campo: element,
1095 valor: this.data[element],
1096 razon: 'Campo vacío (Colombia requiere depto/ciudad)'
1097 })
1098 }
1099 break
1100 case 'email':
1101 const regex =
1102 /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
1103 if (this.data[element] == '' || this.data[element] == null) {
1104 valid = false
1105 this.errors[element] = true
1106 erroresEncontrados.push({
1107 campo: element,
1108 valor: this.data[element],
1109 razon: 'Email vacío'
1110 })
1111 } else if (regex.test(this.data[element]) === false) {
1112 valid = false
1113 this.errors[element] = true
1114 erroresEncontrados.push({
1115 campo: element,
1116 valor: this.data[element],
1117 razon: 'Email inválido (formato)'
1118 })
1119 }
1120 break
1121 case 'mobile':
1122 if (
1123 this.data[element] == '' ||
1124 this.data[element] == null ||
1125 this.data[element].length < 8 ||
1126 this.data[element].length > 10
1127 ) {
1128 valid = false
1129 this.errors[element] = true
1130 erroresEncontrados.push({
1131 campo: element,
1132 valor: this.data[element],
1133 razon:
1134 'Celular inválido (longitud: ' + (this.data[element] && this.data[element].length ? this.data[element].length : 0) + ')'
1135 })
1136 }
1137 break
1138 default:
1139 break
1140 }
1141 })
1142
1143 if (erroresEncontrados.length > 0) {
1144 console.log('❌ [VALIDATION] Se encontraron errores:')
1145 console.table(erroresEncontrados)
1146 } else {
1147 console.log('✅ [VALIDATION] Todos los campos válidos')
1148 }
1149
1150 console.log(' - Resultado final: ' + (valid ? 'VÁLIDO' : 'INVÁLIDO'))
1151
1152 return valid
1153 },
1154 /**
1155 * Funcion para enviar el formulario
1156 **/
1157 validateForm: function (e) {
1158 console.log('=================================================')
1159 console.log('📝 [FORM DEBUG] validateForm LLAMADO')
1160 console.log('=================================================')
1161 console.log(' - Evento:', e)
1162 console.log(' - isSubmitting:', this.isSubmitting)
1163 console.log(' - formInValid:', this.formInValid)
1164
1165 console.log('📋 [FORM DEBUG] Datos completos antes de validar:')
1166 console.table(this.data)
1167
1168 console.log('🔍 [FORM DEBUG] Llamando validateInputs()...')
1169 const validationResult = this.validateInputs()
1170 console.log(' - Resultado validación:', validationResult)
1171
1172 if (!validationResult) {
1173 console.log('❌ [FORM DEBUG] VALIDACIÓN FALLIDA - Formulario NO se enviará')
1174 console.log(' - Errores encontrados:')
1175 console.table(this.errors)
1176 e.preventDefault()
1177 return false
1178 } else {
1179 console.log('✅ [FORM DEBUG] VALIDACIÓN EXITOSA')
1180 console.log(' - Estado de errores:')
1181 console.table(this.errors)
1182
1183 console.log('📤 [FORM DEBUG] Preparando envío...')
1184 console.log(' - Referencia al formulario:', !!this.$refs.form_SF)
1185 console.log(' - Action del form:', this.$refs.form_SF ? this.$refs.form_SF.action : 'undefined')
1186 console.log(' - Method del form:', this.$refs.form_SF ? this.$refs.form_SF.method : 'undefined')
1187
1188 // Capturar FormData antes de enviar
1189 const formData = new FormData(this.$refs.form_SF)
1190 console.log('📦 [FORM DEBUG] Datos que se enviarán:')
1191 const dataToSend = {}
1192 for (let [key, value] of formData.entries()) {
1193 dataToSend[key] = value
1194 }
1195 console.table(dataToSend)
1196
1197 this.isSubmitting = true
1198 console.log('🚀 [FORM DEBUG] ENVIANDO FORMULARIO...')
1199 console.log('=================================================')
1200
1201 this.$refs.form_SF.submit()
1202 }
1203 },
1204 /**
1205 * Funcion para limpiar en caso que sea invalido
1206 **/
1207 deleteInvalid(campo) {
1208 this.errors[campo] = false
1209 },
1210
1211 // Sanitiza la fecha del evento en el formato correcto con non-breaking space
1212 sanitizeFecha(valueFevento) {
1213 if (!valueFevento || typeof valueFevento !== "string")
1214 return valueFevento;
1215 // a) Detectar formato ISO (YYYY-MM-DD) y convertir a DD/MM/YYYY
1216 valueFevento = valueFevento.replace(
1217 /(\d{4})-(\d{2})-(\d{2})/,
1218 "$3/$2/$1"
1219 );
1220
1221 // b) Saneamiento y Normalización de formato de entrada - PRE-LIMPIEZA
1222 valueFevento = valueFevento
1223 .replace(/[-]/g, "/") // Normalizar separadores: '-' a '/'
1224 .replace(/:(\d{2}):\d{2}(\s|$)/, ":$1$2") // Eliminar segundos si existen
1225 .replace(/\s{2,}/g, " ") // Espacios múltiples a uno solo
1226 .replace(/(\d{4})\s+/, "$1, ") // Asegura coma después del año
1227 .replace(/\s*,\s*/, ", ") // Coma con espacio estandarizado
1228 .trim();
1229
1230 // c) Añade ":00" si faltan (ej. "10 Pm" -> "10:00 Pm")
1231 // También captura casos pegados al meridiano (ej. "10Pm" -> "10:00 Pm")
1232 valueFevento = valueFevento.replace(
1233 /(^|[,\s])(\d{1,2})(?!:\d{2})\s*([AaPp]\s*\.?\s*[Mm]\s*\.?\b)/g,
1234 "$1$2:00 $3"
1235 );
1236
1237 // d) Formateo específico de AM/PM (acepta variantes con/sin puntos/espacios)
1238 // Ej.: PM, P.M, P.m, p.m., p m, etc. => p. m.
1239 valueFevento = valueFevento.replace(
1240 /\b[Pp]\s*\.?\s*[Mm]\s*\.?\b/g,
1241 "p.\u00A0m."
1242 );
1243 // Ej.: AM, A.M, A.m, a.m., a m, etc. => a. m.
1244 valueFevento = valueFevento.replace(
1245 /\b[Aa]\s*\.?\s*[Mm]\s*\.?\b/g,
1246 "a.\u00A0m."
1247 );
1248
1249 // e) Asegurar formato estricto en espacios:
1250 // - exactamente ", " tras la fecha
1251 // - exactamente un espacio antes del meridiano normalizado
1252 valueFevento = valueFevento
1253 .replace(/, \s*/g, ", ")
1254 .replace(/(\d{1,2}:\d{2})\s*(a\.\u00A0m\.|p\.\u00A0m\.)/i, "$1 $2")
1255 .replace(/(a\.\u00A0m\.|p\.\u00A0m\.)\.+$/i, "$1")
1256 .trim();
1257
1258 return valueFevento;
1259 },
1260 },
1261 computed: {
1262 periodoSel() {
1263 const nivel = this.nivelAcadPrograma || 'PREG'
1264 const periodosNivel = this.periodos?.[nivel] || {}
1265 const entries = Object.entries(periodosNivel)
1266
1267 if (entries.length === 0) return {}
1268
1269 const visibleEntries = getVisiblePeriodEntries({
1270 level: nivel,
1271 periodEntries: entries,
1272 programCode: this.data.programa,
1273 programClosureData: this.programClosureDates
1274 })
1275
1276 return Object.fromEntries(visibleEntries)
1277 },
1278 // Campo de fecha de evento saneado
1279 fevento() {
1280 let finalFevento = "";
1281
1282 let urlParams = new URLSearchParams(window.location.search);
1283 const utmFevento = urlParams.has("fevento")
1284 ? urlParams.get("fevento")
1285 : "";
1286
1287 // 1. PRIORIDAD ALTA: fevento por configuración (config.eventDate)
1288 if (config && config.eventDate && typeof config.eventDate === "string") {
1289 finalFevento = config.eventDate;
1290 }
1291
1292 // 2. PRIORIDAD MÁXIMA: UTM (fevento)
1293 // Si existe, sobrescribe (reemplaza) el valor establecido por la configuración.
1294 if (utmFevento && typeof utmFevento === "string") {
1295 finalFevento = utmFevento;
1296 }
1297
1298 // 3. Si no hay ni config ni UTM → se deja vacío
1299 if (!finalFevento) {
1300 return "";
1301 }
1302
1303 // Sanitizar el valor seleccionado
1304 return this.sanitizeFecha(finalFevento);
1305 },
1306 },
1307 })
1308
1309 const vueApp = app.mount('#app')
1310
1311 /**
1312 * Maneja el evento de carga de datos del programa
1313 */
1314 function manejarCargaDatos(event) {
1315 console.log('📡 [EVENT] dataLoad recibido')
1316 console.log(' - event.detail:', event.detail)
1317
1318 try {
1319 const dataPrograma = event.detail
1320
1321 if (!dataPrograma || !dataPrograma.grado) {
1322 console.error('❌ [EVENT] Datos del programa no válidos o sin información de grado')
1323 console.log(' - dataPrograma:', dataPrograma)
1324 return
1325 }
1326
1327 const tipoPrograma = dataPrograma.grado
1328 console.log('✅ [EVENT] Tipo de programa detectado:', tipoPrograma)
1329
1330 if (vueApp) {
1331 vueApp.nivelAcadPrograma = resolveNivelPeriodo(dataPrograma.nivelAcad, dataPrograma.grado)
1332 vueApp.data.periodo_esperado = ''
1333 }
1334 } catch (error) {
1335 console.error('❌ [EVENT] Error al procesar datos del programa:', error)
1336 console.error(error.stack)
1337 }
1338 }
1339
1340 // Registrar listener para el evento dataLoad
1341 document.addEventListener('dataLoad', manejarCargaDatos)
1342
1343 console.log('✅ [LISTENER] Listener para actualización de nivel académico registrado correctamente')
1344 console.log('=================================================')
1345 console.log('🎯 [SCRIPT] FORMULARIO COMPLETAMENTE INICIALIZADO')
1346 console.log('=================================================')
1347</script>
Especialización en Gerencia de Proyectos de Diseño
Desarrolla proyectos viables en donde confluyen el desarrollo de productos y servicios y la gerencia organizacional.
Inscríbete al Programa

¿Qué aprenderás en la Especialización en Gerencia de Proyectos de Diseño?
Enfoques del
plan de estudio:
- Metodologías de diseño Design Factory.
- Gerencia de productos y servicios.
- Gerencia de la innovación y la creatividad.
- Gestión de Diseño.
- Procesos de prototipado.
Propuesta Académica


Posgrado

Cargando...


Cargando... / Semestre
Cargando datos...
Proceso de Inscripción
Paso 1:
Crea una cuenta en nuestra plataforma de inscripción - Acceso.
Paso 2:
Diligencia el formulario de inscripción y paga el recibo de los derechos de admisión.
Paso 3
Ingresa nuevamente a tu cuenta, agenda y completa las actividades del proceso de admisión.
Requisitos de admisión al programa
-Proyección de ideas de forma escrita y verbal.
-Manejo del inglés (leer y escribir).
Beneficios económicos
*Beneficio egresado javeriano: Los egresados javerianos reciben el 10% de descuento en la matrícula de su programa de posgrado de interés.
Programa de financiación
*Opciones de corto y mediano plazo directamente con la Universidad, tasas de interés preferenciales.
*A mediano y largo plazo con entidades aliadas que ofrecen bajas tasas de interés.
*Descuento para estudiantes egresados de pregrado de la Universidad Javeriana.
*Conoce, también, nuestros descuentos y convenios con entidades públicas y privadas.
¡Te acompañamos a hacer tus sueños
realidad!
Beca Liderazgo Público Javeriano - Convocatoria cerrada
Te informamos que la convocatoria de la Beca Liderazgo Público Javeriano ya finalizó. Te invitamos a estar atento a próximas convocatorias.
Descubre los detalles
¿Por qué estudiar la Especialización en Gerencia de Proyectos de Diseño?
-
Desarrollo personalFormular proyectos de diseño debe ser un acto de confianza, que involucra la capacidad creativa, el análisis del entorno, el entendimiento, la transformación de ideas y saber a dónde se quiere llegar.
-
SostenibilidadEste aspecto es un vector de competitividad en la función de la gerencia de proyectos de diseño, con el cual se busca valorar el impacto que se genera en el entorno.
-
Gerencia de proyectosEl gerente de proyectos de diseño es el vínculo que mantiene íntegro un proyecto definido, garantizando el cumplimiento de objetivos, manejo de presupuesto y la alineación de tiempos y resultados; siempre respetando el concepto que genera valor desde diversas visiones.
-
WorkshopsParticipación en actividades académicas y prácticas que integren conocimientos, a través de la infraestructura institucional exitosa como el Centro Javeriano de Emprendimiento y el Design Factory.
-
Servicio bibliotecario expertoLos estudiantes de nuestros posgrados cuentan con la Biblioteca General Alfonso Borrero Cabal, S.J., que tiene uno de los repositorios más importantes de América Latina.
-
InfraestructuraLa Javeriana cuenta con una infraestructura única, con la más alta tecnología y con espacios pensados para la investigación.
-
Consejería integral
Comprometidos con tu bienestar, brindamos consejería integral que fusiona apoyo académico y desarrollo personal, guiándote con estrategias personalizadas.
-
En la Javeriana
Tu desarrollo integral es clave. Explora actividades en deporte, cultura, salud e identidad, y enriquece tu vida universitaria.


Acreditaciones Especialización en Gerencia de Proyectos de Diseño

Red de innovación presente en los 5 continentes.
Descubre programas relacionados
Preguntas frecuentes Especialización en Gerencia de Proyectos de Diseño
La especialización está dirigida a profesionales de distintas disciplinas con título de pregrado emitido por universidades colombianas o extranjeras, interesados en adquirir conocimientos y habilidades en las áreas de diseño de productos y servicios, la gerencia organizacional, y la formulación de proyectos productivos. El candidato al programa es un profesional orientado hacia procesos de emprendimiento y promoción empresarial, con capacidad de análisis, síntesis y disposición para el trabajo en equipo, creativo e innovador, motivado para asumir retos tecnológicos, sociales y económicos.
Los egresados de la Especialización en Gerencia de Proyectos de Diseño estarán en la capacidad de:
• Comprender de manera integral y sistémica, las variables que se deben considerar desde aspectos económicos, tecnológicos, ambientales, socioculturales, entre otros, al gerenciar un proyecto de diseño.
• Utilizar metodologías y herramientas de planificación de proyectos, que puedan ser aplicadas en procesos de diseño y desarrollo de productos y servicios de distinto tipo.
• Emplear diferentes herramientas para la solución creativa de problemas, así como para integrar diferentes actores a través del trabajo interdisciplinario y colaborativo, en el desarrollo y gerencia de proyectos de diseño.
• Comprender dinámicas y tendencias en el mercado, a partir de las cuales pueda tomar decisiones gerenciales para el desarrollo de proyectos de diseño.
• Integrar elementos de sostenibilidad y de cuidado con los aspectos ambientales en la toma de decisiones gerenciales en procesos de diseño.
• Transformar plausiblemente la realidad con criterios éticos, asumiendo las implicaciones sociales, políticas, éticas e institucionales de las decisiones tomadas en proyectos de diseño sobre la vida, la naturaleza, las instituciones y las personas.
• Actuar con tolerancia y respeto frente a la diferencia, la dignidad humana y la libertad con base en su alta sensibilidad social, humanística y ambiental.
• Comprender de manera integral y sistémica, las variables que se deben considerar desde aspectos económicos, tecnológicos, ambientales, socioculturales, entre otros, al gerenciar un proyecto de diseño.
• Utilizar metodologías y herramientas de planificación de proyectos, que puedan ser aplicadas en procesos de diseño y desarrollo de productos y servicios de distinto tipo.
• Emplear diferentes herramientas para la solución creativa de problemas, así como para integrar diferentes actores a través del trabajo interdisciplinario y colaborativo, en el desarrollo y gerencia de proyectos de diseño.
• Comprender dinámicas y tendencias en el mercado, a partir de las cuales pueda tomar decisiones gerenciales para el desarrollo de proyectos de diseño.
• Integrar elementos de sostenibilidad y de cuidado con los aspectos ambientales en la toma de decisiones gerenciales en procesos de diseño.
• Transformar plausiblemente la realidad con criterios éticos, asumiendo las implicaciones sociales, políticas, éticas e institucionales de las decisiones tomadas en proyectos de diseño sobre la vida, la naturaleza, las instituciones y las personas.
• Actuar con tolerancia y respeto frente a la diferencia, la dignidad humana y la libertad con base en su alta sensibilidad social, humanística y ambiental.
El especialista en Gerencia de Proyectos de Diseño, estará en capacidad
de desempeñarse como:
- Diseñador y /o Gerente de proyectos y productos en pequeñas, medianas o grandes empresas.
- Emprendedor independiente orientado a procesos de creación y desarrollo de productos y servicios.
- Consultor y/o asesor en procesos y formulación de proyectos de diseño y gerencia de producto y servicios.
- Consultor y /o asesor en procesos de emprendimiento para el mejoramiento de las empresas, para la creación de empresas y para el emprendimiento de carácter social.
Buscamos la formación integral de las personas con el apoyo de la Vicerrectoría del Medio Universitario a través de sus 5 centros:
- Centro de Fomento de la Identidad y Construcción de la Comunidad.
- Centro Pastoral San Francisco Javier.
- Centro de Asesoría Psicológica y Salud.
- Centro de Gestión Cultural.
- Centro Javeriano de Formación Deportiva.
La Javeriana cuenta con más de 400 convenios internacionales de colaboración y cooperación académica y hace parte de redes de intercambio que les permiten a los estudiantes acceder a más de 300 instituciones en 42 países.
En la Javeriana tenemos planes de financiación flexibles porque queremos adaptarnos a tus necesidades. Tenemos créditos a corto, mediano y largo plazo. Contamos con descuentos a través de convenios con diferentes organizaciones, entre otros.
El proceso de admisión de la Universidad se fundamenta en tres grupos de criterios: Actitud, Habilidades y/o Conocimiento. Para el programa se evaluará a través de lectura de un documento escrito que aborde la experiencia profesional del aspirante y sus motivaciones para cursar el programa, el agendamiento de una entrevista con la dirección del programa, y la revisión de su historial académico según su reporte de notas del pregrado cursado, para esto se tendrán en cuenta los siguientes criterios:
ACTITUD 30%
(Motivación por el programa y visualización de oportunidades)
CONOCIMIENTO 40%
(Conocimiento contexto local y rendimiento académico)
HABILIDAD 30%
(Claridad en las ideas, claridad de ideas verbal y lectura en inglés)
Si ya has realizado el pago de la matrícula, para facilitar el proceso de formalización como estudiante internacional o nacional con estudios en el exterior, debes cargar los siguientes documentos en el autoservicio de inscripción:
- Diploma o acta de grado de pregrado apostillado por el Ministerio de Relaciones Exteriores o autenticado por el Cónsul de Colombia en el país de origen.
- Pasaporte, visa y Cédula de Extranjería para no nacionales colombianos.
- Seguro médico con cobertura global.
Ten en cuenta estas recomendaciones.