Skip to content

feat(profile): implementación de perfil de usuario, brecha de habilidades y correcciones de UI#28

Merged
Ochoa-Stack merged 4 commits into
developfrom
feature/profile-and-skill-gap
Jul 2, 2026
Merged

feat(profile): implementación de perfil de usuario, brecha de habilidades y correcciones de UI#28
Ochoa-Stack merged 4 commits into
developfrom
feature/profile-and-skill-gap

Conversation

@Ochoa-Stack

Copy link
Copy Markdown
Owner

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:

  • Backend (Perfil e Intents): Añadimos el campo intent al modelo de User (con su respectiva migración) para definir objetivos del usuario. Construimos los endpoints operativos en profile_bp.py, respaldados por su capa de servicios, repositorios y esquemas para orquestar el skill gap (brecha de habilidades).
  • Cliente API (Frontend): Actualizamos client.js inyectando soporte dinámico para protección CSRF y añadimos los métodos utilitarios apiPatch y apiDelete, habilitando interacciones CRUD completas y seguras.
  • Interfaz de Perfil: Construimos la vista perfil.html junto con su lógica modular (perfil.js) y estilos estructurados en un nuevo sistema de grid (_grid.css y _perfil.css). La interfaz unifica el rastreo de habilidades y la gestión de seguridad de la cuenta.
  • Correcciones en Landing (Index): Resolvimos un bug crítico de desplazamiento (scroll) en el script del carrusel. Expandimos y adaptamos el layout de index.html para pantallas de escritorio, mejorando el renderizado de la sección Top Skills.

Tipo de cambio

  • feat
  • fix
  • refactor
  • chore
  • docs
  • test

Cómo probar

  1. Tras el merge, actualizamos la rama base localmente: git checkout develop && git pull origin develop
  2. Navegamos a la raíz del backend (cd backend) y activamos el entorno virtual.
  3. Aplicamos la migración pendiente para inyectar la columna intent en la tabla de usuarios:
    flask db upgrade
  4. Levantamos el backend (flask run) y el frontend mediante Live Server (puerto 5500).
  5. Iniciamos sesión en la aplicación web, navegamos a la vista de "Perfil" y validamos el funcionamiento del panel de seguridad y la sección de habilidades.
  6. Volvemos al inicio (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

  • Sigue las convenciones de commits del proyecto
  • Las pruebas existentes pasan sin errores
  • Se agregaron pruebas para el nuevo comportamiento (si aplica)
  • No se incluyen archivos .env ni credenciales

Ochoa-Stack and others added 4 commits July 2, 2026 15:24
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>
@Ochoa-Stack Ochoa-Stack requested a review from Copilot July 2, 2026 21:30
@Ochoa-Stack Ochoa-Stack merged commit 0fbfc99 into develop Jul 2, 2026
1 check passed
@Ochoa-Stack Ochoa-Stack deleted the feature/profile-and-skill-gap branch July 2, 2026 21:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) + campo intent en User con migración/constraints.
  • Frontend: nueva vista perfil.html + lógica perfil.js y 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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants