chore: sincronizar cambios para uso en múltiples dispositivos#8
chore: sincronizar cambios para uso en múltiples dispositivos#8TeamInfinixdevcom wants to merge 1 commit into
Conversation
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 29672439 | Triggered | Generic CLI Secret | 03e27c1 | scripts/create-user-like-cristian.js | View secret |
| 29672438 | Triggered | Company Email Password | 03e27c1 | scripts/create-manuel-user.js | View secret |
| 29672441 | Triggered | Company Email Password | 03e27c1 | scripts/create-minor-user.js | View secret |
| 29672437 | Triggered | Generic CLI Secret | 03e27c1 | scripts/create-user-like-cristian.js | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
There was a problem hiding this comment.
Pull request overview
This PR synchronizes project changes across devices, but it also introduces several functional updates spanning UI features, Firestore security rules, Cloud Functions behavior, and administrative scripts.
Changes:
- Added/expanded UI capabilities (lost waitlist list, richer sales filters, WhatsApp templates/pagination, model datalist + add flow).
- Updated Firestore rules/indexes and Cloud Functions to support new/normalized fields (e.g.,
executiveId) and new queries. - Added multiple Node scripts for user creation/fixing and sales diagnostics.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/fix-active-user.js | Admin script to force-create/activate a Firestore user doc. |
| scripts/create-user-like-cristian.js | Admin SDK script to create an auth+Firestore user mirroring a source user’s permissions. |
| scripts/create-minor-user.js | Client-SDK script to create a specific Auth+Firestore user (contains hardcoded data). |
| scripts/create-manuel-user.js | Client-SDK script to create a specific Auth+Firestore user (contains hardcoded data). |
| scripts/check-january-sales.js | Admin SDK script to scan monthly sales and diagnose missing fields. |
| public/js/waitlist-manager.js | Adds “lost” status list with pagination/stats and refresh wiring. |
| public/js/ventas-manager.js | Writes canonical executiveId and queries sales additionally by that field. |
| public/js/proyecciones.js | More robust date parsing + forces Firestore refresh for projections. |
| public/js/objetivos-form.js | Adjusts plan pricing display to “sin IVA” and limits mobile plan group selection. |
| public/js/modelos-telefonos.js | Updates device model inventory list and related logging. |
| public/js/mis-ventas.js | Adds IMEI/serie filters, improves date/category display, and adjusts state filtering. |
| public/js/contactacion-whatsapp.js | Adds hogar plan selection from planes.json, better history pagination, and updated templates. |
| public/js/clients.js | Adds interaction-submit reentrancy guard and null-safety improvements. |
| public/js/autocompletado-modelos.js | Adds datalist support + UI to add custom models when missing. |
| public/js/auth.js | Creates missing users/{uid} doc on login and refreshes tabs after role checks. |
| public/index.html | UI layout changes: tab bar wrapping, sales filter UI, waitlist lost section, cache-busting querystrings. |
| public/data/users.json | Adds a new user entry (currently includes PII and is publicly served). |
| public/data/planes.json | Updates K+ plans and modifies some hogar plan prices/flags. |
| functions/index.js | Uses executiveId when processing delivered sales + minor logging/trigger formatting tweaks. |
| firestore.rules | Large rewrite: adds helpers, expands collections, changes ownership/role/permission model. |
| firestore.indexes.json | Adds composite indexes for lista_espera lost queries and executiveId sales queries. |
| firestore-SEGURO.rules | Mirrors/duplicates many of the rule changes in firestore.rules. |
Comments suppressed due to low confidence (1)
public/js/mis-ventas.js:673
removeEventListener('change', () => this.renderVentas())no removerá listeners previos porque se pasa una función nueva en cada llamada (distinta referencia). Esto puede acumular listeners y disparar múltiples renderizados por evento. Solución: guardar handlers como propiedades (p.ej.this._onFilterChange) y reutilizar la misma referencia en add/remove.
[buscarPedido, buscarCedula, buscarImei, buscarSerie, filtroTipo, filtroCategoria, filtroEstado].forEach(el => {
if (el) {
el.removeEventListener('change', () => this.renderVentas());
el.removeEventListener('input', () => this.renderVentas());
el.addEventListener('change', () => { this.currentPage = 1; this.renderVentas(); });
el.addEventListener('input', () => { this.currentPage = 1; this.renderVentas(); });
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } catch (error) { | ||
| console.error('Error creating user:', error.message); | ||
| process.exit(1); | ||
| } finally { | ||
| process.exit(0); |
There was a problem hiding this comment.
El bloque try/catch/finally termina siempre con process.exit(0) en el finally, incluso cuando ocurre un error y se llama process.exit(1) en el catch. Esto hace que el script reporte éxito aunque haya fallado. Solución: eliminar el process.exit(0) del finally y salir con 0 solo cuando el flujo de creación realmente complete sin errores (o manejar un exitCode).
| } catch (error) { | |
| console.error('Error creating user:', error.message); | |
| process.exit(1); | |
| } finally { | |
| process.exit(0); | |
| process.exit(0); | |
| } catch (error) { | |
| console.error('Error creating user:', error.message); | |
| process.exit(1); |
| const firebaseConfig = { | ||
| apiKey: "AIzaSyB_QB5AOMTRUF1tPF0ypMYwlI2F16Ugy0w", | ||
| authDomain: "executiveperformancek.firebaseapp.com", | ||
| projectId: "executiveperformancek", | ||
| storageBucket: "executiveperformancek.firebasestorage.app", | ||
| messagingSenderId: "1010572776177", | ||
| appId: "1:1010572776177:web:26432cf2220bfe11cccf50" | ||
| }; | ||
|
|
||
| const app = initializeApp(firebaseConfig); | ||
| const auth = getAuth(app); | ||
| const db = getFirestore(app); | ||
|
|
||
| async function createUser() { | ||
| const userData = { | ||
| name: 'Minor Sánchez Cervantes', | ||
| email: 'msanchezce@ice.go.cr', | ||
| password: 'Kolbi300', | ||
| cedula: '112570764', | ||
| role: 'executive' | ||
| }; |
There was a problem hiding this comment.
Este script contiene credenciales/PII en texto plano (email, cédula y especialmente password) y además usa el SDK cliente para crear usuarios. Publicar/commitear contraseñas reales es un riesgo crítico. Solución: eliminar passwords del repo (usar variables de entorno/argv), y preferir Admin SDK en scripts de backoffice; si debe existir, moverlo fuera del árbol versionado o a un ejemplo con valores ficticios.
| async function createUser() { | ||
| const userData = { | ||
| name: 'Manuel Escalante Leitón', | ||
| email: 'mescalanteL@ice.go.cr', | ||
| password: 'Torre1020', | ||
| cedula: '109790421', | ||
| user: 'Maesca1', | ||
| phone: '87903535', | ||
| role: 'executive' | ||
| }; |
There was a problem hiding this comment.
Este script contiene credenciales/PII en texto plano (email, cédula, teléfono y especialmente password). Esto no debería versionarse. Solución: retirar contraseñas/PII del código, leerlos desde variables de entorno/flags, y usar Admin SDK con cuenta de servicio (no SDK cliente) para tareas administrativas.
| const serviceAccount = require('../executiveperformancek-firebase-adminsdk-fbsvc-d7042fc558.json'); | ||
|
|
||
| admin.initializeApp({ | ||
| credential: admin.credential.cert(serviceAccount) | ||
| }); | ||
|
|
||
| const db = admin.firestore(); | ||
|
|
||
| // Cambia este UID por el del usuario que quieres corregir | ||
| const uid = 'yF8fwbUQFpXXlOfUMyvQmHmBgNI3'; | ||
|
|
There was a problem hiding this comment.
El script tiene un UID hardcodeado y referencia directa a un archivo de cuenta de servicio JSON. Esto es riesgoso (secreto/PII) y propenso a errores al ejecutarse en otros entornos. Solución: pasar el UID por argumento (--uid) y cargar credenciales vía GOOGLE_APPLICATION_CREDENTIALS o variables de entorno (no requerir el JSON desde el repo).
| const serviceAccount = require('../executiveperformancek-firebase-adminsdk-fbsvc-d7042fc558.json'); | |
| admin.initializeApp({ | |
| credential: admin.credential.cert(serviceAccount) | |
| }); | |
| const db = admin.firestore(); | |
| // Cambia este UID por el del usuario que quieres corregir | |
| const uid = 'yF8fwbUQFpXXlOfUMyvQmHmBgNI3'; | |
| admin.initializeApp(); | |
| const db = admin.firestore(); | |
| function getUidFromArgs() { | |
| const args = process.argv.slice(2); | |
| for (let i = 0; i < args.length; i++) { | |
| if (args[i] === '--uid' && args[i + 1]) { | |
| return args[i + 1]; | |
| } | |
| if (args[i].startsWith('--uid=')) { | |
| return args[i].slice('--uid='.length); | |
| } | |
| } | |
| console.error('Uso: node scripts/fix-active-user.js --uid <USER_UID>'); | |
| console.error('Asegúrate también de configurar GOOGLE_APPLICATION_CREDENTIALS o credenciales de aplicación por entorno.'); | |
| process.exit(1); | |
| } | |
| const uid = getUidFromArgs(); |
| { | ||
| "id": "lMOh5PUYFHSNGKBj6QaGqrFdhjv1", | ||
| "uid": "lMOh5PUYFHSNGKBj6QaGqrFdhjv1", | ||
| "name": "Manuel Escalante Leitón", | ||
| "email": "mescalanteL@ice.go.cr", | ||
| "cedula": "109790421", | ||
| "user": "Maesca1", | ||
| "phone": "87903535", | ||
| "role": "executive", | ||
| "isActive": true, | ||
| "createdAt": "2026-02-04T00:00:00.000Z", | ||
| "createdBy": "script" |
There was a problem hiding this comment.
public/data/users.json está en una ruta pública y contiene PII (UID, cédula, teléfono, email). Esto quedaría expuesto a cualquier cliente web. Solución: remover este registro del bundle público o mover estos datos a un backend/Firestore con reglas; si es solo para desarrollo, sacarlo de public/ y del control de versiones.
| { "id": "duotelint500mbps", "nombre": "Dúo Telefonía + Internet 500 Mbps", "precio": 30318 }, | ||
| { "id": "duotelint800mbps", "nombre": "Dúo Telefonía + Internet 800 Mbps", "precio": 130458 }, | ||
| { "id": "duotelint1gbps", "nombre": "Dúo Telefonía + Internet 1 Gbps", "precio": 161046 } | ||
| { "id": "duotelint100mbps", "nombre": "Dúo Telefonía + Internet 100| Mbps", "precio": 15948 }, |
There was a problem hiding this comment.
El nombre del plan incluye un carácter | ("Internet 100| Mbps"), que probablemente es un typo y afectará la UI/búsqueda/plantillas. Solución: corregir el string a "Internet 100 Mbps".
| { "id": "duotelint100mbps", "nombre": "Dúo Telefonía + Internet 100| Mbps", "precio": 15948 }, | |
| { "id": "duotelint100mbps", "nombre": "Dúo Telefonía + Internet 100 Mbps", "precio": 15948 }, |
| // Si el documento no existe, crearlo con datos básicos | ||
| console.log('📝 Documento de usuario no existe, creando...'); | ||
| userData = { | ||
| email: user.email, | ||
| name: user.displayName || 'Usuario', | ||
| isActive: true, | ||
| role: 'user', | ||
| createdAt: new Date().toISOString(), | ||
| updatedAt: new Date().toISOString() | ||
| }; | ||
| await setDoc(doc(db, 'users', user.uid), userData); | ||
| console.log('✅ Documento de usuario creado exitosamente'); |
There was a problem hiding this comment.
El PR description indica "No functional changes", pero aquí se están introduciendo cambios funcionales relevantes (p.ej. creación automática del documento users/{uid} en login y cambios de acceso/roles). Solución: actualizar la descripción del PR para reflejar estos cambios, o separar los cambios funcionales en otro PR.
| // Si el documento no existe, crearlo con datos básicos | ||
| console.log('📝 Documento de usuario no existe, creando...'); | ||
| userData = { | ||
| email: user.email, | ||
| name: user.displayName || 'Usuario', | ||
| isActive: true, | ||
| role: 'user', | ||
| createdAt: new Date().toISOString(), | ||
| updatedAt: new Date().toISOString() | ||
| }; | ||
| await setDoc(doc(db, 'users', user.uid), userData); | ||
| console.log('✅ Documento de usuario creado exitosamente'); |
There was a problem hiding this comment.
En el login, si el documento de usuario no existe se crea con isActive: true automáticamente. Esto elimina la posibilidad de un flujo de aprobación/alta controlada y, combinado con reglas permisivas, puede abrir acceso a usuarios no previstos. Solución: crear el doc con isActive: false y mostrar un mensaje de "pendiente de activación", o mover la creación/activación a una Cloud Function/admin workflow.
| // Si el documento no existe, crearlo con datos básicos | |
| console.log('📝 Documento de usuario no existe, creando...'); | |
| userData = { | |
| email: user.email, | |
| name: user.displayName || 'Usuario', | |
| isActive: true, | |
| role: 'user', | |
| createdAt: new Date().toISOString(), | |
| updatedAt: new Date().toISOString() | |
| }; | |
| await setDoc(doc(db, 'users', user.uid), userData); | |
| console.log('✅ Documento de usuario creado exitosamente'); | |
| // Si el documento no existe, crearlo en estado pendiente de activación | |
| console.log('📝 Documento de usuario no existe, creando en estado pendiente...'); | |
| userData = { | |
| email: user.email, | |
| name: user.displayName || 'Usuario', | |
| isActive: false, | |
| role: 'user', | |
| createdAt: new Date().toISOString(), | |
| updatedAt: new Date().toISOString() | |
| }; | |
| await setDoc(doc(db, 'users', user.uid), userData); | |
| console.log('✅ Documento de usuario creado exitosamente en estado inactivo'); | |
| showMessage('⏳ Tu cuenta está pendiente de activación. Contacta a un administrador.', 'error'); | |
| await signOut(auth); | |
| return; |
| match /lista_espera/{itemId} { | ||
| allow read, create, update: if isSignedIn() && isActiveUser(); | ||
| allow delete: if isAdmin(); | ||
| allow read, write: if isAdmin(); | ||
| } |
There was a problem hiding this comment.
La UI (WaitlistManager) permite eliminar ítems de lista_espera mediante deleteDoc, pero aquí delete está permitido solo para admins. Esto causará fallos para ejecutivos normales al intentar borrar. Solución: permitir delete al dueño (resource.data.userId == request.auth.uid) o esconder/deshabilitar la acción de eliminar para no-admins.
| // ============================================ | ||
| match /lista_espera/{itemId} { | ||
| allow read, create, update: if isSignedIn() && isActiveUser(); | ||
| allow delete: if isAdmin(); |
There was a problem hiding this comment.
La UI permite eliminar ítems de lista_espera, pero las reglas dejan delete solo para admins. Esto provocará errores en producción para usuarios no admin. Solución: permitir delete al owner (resource.data.userId == request.auth.uid) o ajustar la UI para que solo admins vean la acción.
| allow delete: if isAdmin(); | |
| allow delete: if isAdmin() || (isSignedIn() && resource.data.userId == request.auth.uid); |
|
se cierra porque se eliminan los scripts erroneos |
🧾 Description
This PR synchronizes recent project changes to ensure compatibility across multiple devices.
🔧 Changes
✅ Notes