feat(profile): implementación de perfil de usuario, brecha de habilidades y correcciones de UI#28
Merged
Merged
Conversation
Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com>
…sktop Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com>
There was a problem hiding this comment.
Pull request overview
Este PR consolida una feature completa de perfil de usuario (con skill gap) de punta a punta: agrega soporte backend (modelo/migración + endpoints + servicios), amplía el cliente API del frontend para CSRF y métodos PATCH/DELETE, y suma la nueva vista/lógica de perfil junto con mejoras en el landing (Top Skills y carrusel).
Changes:
- Backend: nuevo módulo
/api/profile(perfil, skill-gap, skills, cambio de contraseña) + campointentenUsercon migración/constraints. - Frontend: nueva vista
perfil.html+ lógicaperfil.jsy estilos asociados; cliente API con CSRF +apiPatch/apiDelete. - UI landing: sección “Top skills” y corrección del auto-scroll del carrusel cuando está fuera de viewport.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/views/perfil.html | Nueva vista de perfil con secciones (datos/habilidades/seguridad) y hooks para JS. |
| frontend/views/index.html | Nueva sección “Top skills” para desktop/landing. |
| frontend/assets/js/pages/perfil.js | Lógica de perfil: carga de datos, navegación por secciones, CRUD de skills y cambio de contraseña. |
| frontend/assets/js/pages/index.js | Render de chips Top Skills y aumento de cantidad solicitada. |
| frontend/assets/js/components/carousel.init.js | Ajustes para evitar scroll inesperado y controlar auto-rotate según visibilidad. |
| frontend/assets/js/api/client.js | CSRF dinámico + soporte PATCH/DELETE con cookies. |
| frontend/assets/css/pages/_perfil.css | Estilos específicos del perfil (layout, chips, sticky card, etc.). |
| frontend/assets/css/pages/_index.css | Estilos y layout responsive extra + estilos de la nueva sección Top Skills. |
| frontend/assets/css/main.css | Habilita imports de _grid.css y _perfil.css. |
| frontend/assets/css/layouts/_grid.css | Nuevo helper .grid-2-col con breakpoint 1024px. |
| backend/migrations/versions/050a32090a02_add_intent_to_users.py | Migración: agrega intent + check constraint. |
| backend/app/services/profile_service.py | Servicio de perfil: skill-gap, update de perfil y cambio de contraseña. |
| backend/app/schemas/profile_schema.py | Esquemas Marshmallow para PATCH profile, skills y change-password. |
| backend/app/repositories/user_skill_repository.py | Repositorio para tabla usuario-habilidad (idempotente). |
| backend/app/models/user.py | Modelo User: agrega columna intent + constraint. |
| backend/app/controllers/profile_bp.py | Blueprint /api/profile con endpoints nuevos. |
| backend/app/config.py | Config JWT: habilita blocklist checks para access/refresh. |
| backend/app/init.py | Registra profile_bp y blocklist loader (ya existente) para invalidación por cambio de contraseña. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+6
to
+28
| class UpdateProfileSchema(Schema): | ||
| first_name = fields.String( | ||
| load_default=None, | ||
| validate=validate.Length(min=1, max=50), | ||
| ) | ||
| last_name = fields.String( | ||
| load_default=None, | ||
| validate=validate.Length(min=1, max=50), | ||
| ) | ||
| intent = fields.String( | ||
| load_default=None, | ||
| validate=validate.OneOf( | ||
| ["ESTUDIANTE", "RECLUTADOR"], | ||
| error="El intent debe ser ESTUDIANTE o RECLUTADOR.", | ||
| ), | ||
| allow_none=True, | ||
| ) | ||
|
|
||
| @validates_schema | ||
| def require_at_least_one_field(self, data, **kwargs): | ||
| # Un PATCH sin ningun campo modificado no tiene sentido funcional, lo rechazamos con un mensaje claro. | ||
| if not any(v is not None for v in data.values()): | ||
| raise ValidationError("Se requiere al menos un campo para actualizar el perfil.") |
Comment on lines
+14
to
+16
| user_skill_rows = UserSkillRepository.get_skills_by_user(user_id) | ||
| user_skill_ids = {row.UserSkill.skill_id for row in user_skill_rows} | ||
| user_skills_by_id = {row.UserSkill.skill_id: row.Skill for row in user_skill_rows} |
Comment on lines
+24
to
+28
| for rank_index, snapshot in enumerate(top_snapshots, start=1): | ||
| skill_entry = { | ||
| "skill_id": snapshot.skill_id, | ||
| "name": snapshot.skill.name if snapshot.skill else None, | ||
| "demand_count": snapshot.demand_count, |
Comment on lines
+91
to
+93
| user_id = int(get_jwt_identity()) | ||
| UserSkillRepository.add_skill(user_id, skill_id) | ||
| return success_response(data={"skill_id": skill_id, "name": skill.name}, status_code=201) |
|
|
||
|
|
||
| class UserSkillRepository: | ||
| # Encapsula el acceso a datos de la tabla de relacion usuario-habilidad. Usa metodos idem potentes para que el controlador no tenga que verificar existencia antes de operar. |
Comment on lines
+199
to
+206
| const chip = document.createElement("div"); | ||
| chip.className = "chip"; | ||
| chip.innerHTML = ` | ||
| ${skill.name} | ||
| <button type="button" aria-label="Eliminar ${skill.name}" data-action="remove-skill" data-id="${skill.skill_id}"> | ||
| <i data-lucide="x" style="width:14px; height:14px;"></i> | ||
| </button> | ||
| `; |
Comment on lines
+216
to
+223
| const chip = document.createElement("div"); | ||
| chip.className = "chip"; | ||
| chip.innerHTML = ` | ||
| ${skill.name} | ||
| <button type="button" aria-label="Agregar ${skill.name}" data-action="add-skill" data-id="${skill.skill_id}"> | ||
| <i data-lucide="plus" style="width:14px; height:14px;"></i> | ||
| </button> | ||
| `; |
| // Si esta fuera (por ejemplo el usuario bajo al footer), avanza el indice | ||
| // logico sin mover la pagina, para que al volver vea el slide correcto. | ||
| const rect = carousel.getBoundingClientRect(); | ||
| const inViewport = rect.top >= 0 && rect.bottom <= window.innerHeight; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feature/profile-and-skill-gap
Descripción
Consolidamos feat and fix. En este ciclo, completamos el módulo de perfilado de usuario, expandimos el cliente API y aplicamos correcciones estructurales a la interfaz principal.
Detalles técnicos que integramos:
intental modelo deUser(con su respectiva migración) para definir objetivos del usuario. Construimos los endpoints operativos enprofile_bp.py, respaldados por su capa de servicios, repositorios y esquemas para orquestar el skill gap (brecha de habilidades).client.jsinyectando soporte dinámico para protección CSRF y añadimos los métodos utilitariosapiPatchyapiDelete, habilitando interacciones CRUD completas y seguras.perfil.htmljunto con su lógica modular (perfil.js) y estilos estructurados en un nuevo sistema de grid (_grid.cssy_perfil.css). La interfaz unifica el rastreo de habilidades y la gestión de seguridad de la cuenta.index.htmlpara pantallas de escritorio, mejorando el renderizado de la sección Top Skills.Tipo de cambio
Cómo probar
git checkout develop && git pull origin developcd backend) y activamos el entorno virtual.intenten la tabla de usuarios:flask db upgradeflask run) y el frontend mediante Live Server (puerto 5500).index.html) para comprobar visualmente la adaptación del layout en resolución de escritorio y que el bug de scroll del carrusel ya no se reproduce.Checklist