Menu ▾ ▴

#26 Aumentar boton de historial

open
nobody
None
2026-09-09
2026-09-08
Anonymous
No

Originally created by: gtshadow33
Originally owned by: gtshadow33

Mejorar panel lateral de proyectos, actividades y sesiones

Descripción

Mejorar el panel lateral de la aplicación para que permita gestionar y consultar los proyectos de forma más completa.

Actualmente el panel muestra los proyectos, pero se necesita una estructura más organizada que permita desplegar cada proyecto y consultar las actividades y sesiones asociadas.

Objetivos

  • Añadir y gestionar proyectos desde el panel lateral.
  • Mostrar los proyectos reales almacenados en la aplicación.
  • Permitir desplegar y contraer cada proyecto.
  • Mostrar las actividades asociadas a cada proyecto.
  • Mostrar las sesiones registradas dentro de cada proyecto.
  • Mantener una navegación clara cuando existan muchos proyectos, actividades o sesiones.

Estructura propuesta

Proyectos


+ Nuevo proyecto

▼ Proyecto 1
  ├── Actividades
  │   ├── Actividad 1
  │   └── Actividad 2
  │
  └── Sesiones
      ├── Hoy · 01:32
      └── Ayer · 00:48

▶ Proyecto 2

▶ Proyecto 3

Sesiones

Cada sesión debería mostrar, como mínimo:

  • Actividad realizada.
  • Fecha.
  • Duración.
  • Hora de inicio y finalización, si está disponible.

Interfaz

  • Utilizar elementos desplegables para proyectos.
  • Diferenciar visualmente proyectos, actividades y sesiones.
  • Mantener el panel lateral con scroll cuando el contenido supere el espacio disponible.
  • Mantener el botón Nuevo proyecto en una posición accesible.
  • Evitar que la incorporación de muchos proyectos haga que la interfaz resulte difícil de utilizar.

Criterios de aceptación

  • [ ] Los proyectos mostrados proceden de los datos reales de la aplicación.
  • [ ] Se puede crear un nuevo proyecto desde el panel.
  • [ ] Cada proyecto puede desplegarse y contraerse.
  • [ ] Al desplegar un proyecto aparecen sus actividades.
  • [ ] Al desplegar un proyecto aparecen sus sesiones.
  • [ ] Las sesiones muestran información relevante de tiempo y fecha.
  • [ ] El panel funciona correctamente con varios proyectos.
  • [ ] El panel mantiene el scroll cuando existe mucho contenido.
  • [ ] La interfaz es usable tanto en escritorio como en pantallas pequeñas.
  • [ ] La información mostrada se actualiza cuando se crean nuevas actividades o sesiones.

Prioridad

Media/Alta

Esta mejora afecta directamente a la gestión y consulta del historial de trabajo de la aplicación.

Discussion

  • Anonymous

    Anonymous - 2026-09-08
     
  • Anonymous

    Anonymous - 2026-09-08

    Originally posted by: gtshadow33

    Documentación del desarrollo de sidebar.js

    1. Introducción

    El archivo sidebar.js gestiona el menú lateral de la aplicación Hazlo App. Su principal responsabilidad es:

    • Mostrar la lista de proyectos almacenados en la base de datos.
    • Permitir la selección de un proyecto activo.
    • Desplegar, bajo demanda, las actividades de cada proyecto.
    • Actualizarse automáticamente ante cambios (creación de nuevos proyectos, etc.).

    A lo largo del desarrollo, el código ha pasado por varias iteraciones, desde un prototipo con datos ficticios hasta una integración completa con la base de datos a través de la API IPC de Electron.


    2. Evolución del código

    Etapa Descripción
    1. Prototipo con datos mock El archivo contenía un array fijo de proyectos (projects = [{ id: "p1", name: "Proyecto A" }, ...]). Funcionalidades: abrir/cerrar, renderizar lista, seleccionar proyecto, crear proyecto (con prompt) y editar (placeholder). No había conexión con backend.
    2. Primer intento de conexión con backend Se sustituyeron los datos mock por llamadas fetch('/api/proyectos'), asumiendo un servidor HTTP. Esto no funcionó porque la aplicación es una app de escritorio Electron y utiliza comunicación IPC, no HTTP.
    3. Adaptación a la API IPC de Electron Se reemplazaron las peticiones fetch por el uso de window.api.proyectos.listar() e window.api.proyectos.crear(), expuestos desde el preload. Se eliminaron los datos mock y se comenzó a trabajar con datos reales.
    4. Simplificación del código Se eliminaron todas las funciones relacionadas con la creación y edición de proyectos, dejando únicamente listado y selección, para centrarse en la vista.
    5. Implementación de actividades desplegables (acordeón) Se añadió la posibilidad de hacer clic en un proyecto para desplegar una lista de sus actividades. Se utilizaron iconos SVG en lugar de emojis para una mejor integración visual.
    6. Recarga fresca de actividades Se eliminó la caché de actividades; cada vez que se expande un proyecto, se consulta la base de datos para obtener las actividades más recientes.
    7. Refresco automático de la lista de proyectos Se añadieron escuchadores de eventos (project:create y project:created) para recargar la lista de proyectos cuando se crea uno nuevo. Además, al abrir el sidebar se recargan los proyectos para reflejar cambios externos.

    3. Estructura actual del código

    3.1. Referencias DOM

    const sidebar = document.getElementById("sidebar");
    const overlay = document.getElementById("sidebar-overlay");
    const openBtn = document.getElementById("sidebar-open");
    const closeBtn = document.getElementById("sidebar-close");
    const projectListEl = document.getElementById("project-list");
    

    3.2. Estado global

    • projects – Array de objetos proyecto obtenidos de la base de datos.
    • activeProjectId – ID del proyecto seleccionado (resaltado en la interfaz).
    • expandedProjects – Set con los IDs de proyectos que se encuentran expandidos (para mantener el estado al recargar la lista).

    3.3. Funciones principales

    openSidebar() / closeSidebar()

    Controlan la apertura y cierre del menú lateral. openSidebar() recarga los proyectos tras un breve retardo (150ms) para asegurar datos frescos.

    cargarProyectos()

    • Llama a window.api.proyectos.listar() para obtener los proyectos.
    • Asigna el primer proyecto como activo si no hay ninguno seleccionado.
    • Invoca renderProjects() para actualizar la interfaz.

    fetchActivities(projectId)

    • Llama a window.api.actividades.listar(projectId) para obtener las actividades de un proyecto.
    • Siempre consulta la base de datos (sin caché) para garantizar datos actualizados.

    renderProjects()

    • Vacía el contenedor de la lista.
    • Si no hay proyectos, muestra un mensaje.
    • Por cada proyecto, crea:
    • Un contenedor li.
    • Una cabecera div con el nombre y un icono SVG (▶ o ▼) que indica si está expandido.
    • Una lista ul para las actividades, con transición de altura y opacidad.
    • Asigna un evento click a la cabecera que:
    • Selecciona el proyecto (llama a selectProject).
    • Alterna el estado de expansión.
    • Si se expande, carga y muestra las actividades.
    • Actualiza el aspecto de ese proyecto con renderSingleProject.

    renderActivities(projectId, actividades)

    • Busca el contenedor de actividades correspondiente.
    • Muestra "Sin actividades" si la lista está vacía.
    • Por cada actividad, crea un li con su nombre y un evento click que emite activity:select.

    renderSingleProject(projectId)

    • Actualiza solo el proyecto indicado (icono, visibilidad de la lista y clase de activo) sin renderizar toda la lista.
    • Es usado en el toggle de expansión para mejorar el rendimiento.

    selectProject(id)

    • Cambia el proyecto activo.
    • Actualiza las clases CSS de todas las cabeceras para reflejar el cambio.
    • Emite el evento project:select con el proyecto seleccionado.

    3.4. Escucha de eventos externos

    document.addEventListener('project:create', cargarProyectos);
    document.addEventListener('project:created', cargarProyectos);
    

    Esto permite que otros módulos (por ejemplo, el formulario de creación de proyectos) notifiquen al sidebar para que se refresque.

    3.5. Inicialización

    Se ejecuta cargarProyectos() cuando el DOM está listo.

    3.6. Exportaciones

    Se exportan las funciones y variables necesarias para que otros módulos puedan interactuar con el sidebar:

    • openSidebar, closeSidebar
    • selectProject
    • projects, cargarProyectos, renderProjects

    4. Interacción con otros módulos

    • Eventos emitidos:
    • project:select – cuando se selecciona un proyecto.
    • activity:select – cuando se hace clic en una actividad.

    • Eventos escuchados:

    • project:create – para recargar proyectos.
    • project:created – para recargar proyectos.

    5. Tecnologías utilizadas

    • Electron IPC – Comunicación entre proceso principal y renderizador a través de window.api.
    • JavaScript ES Modules – Uso de export e import.
    • Tailwind CSS – Clases utilitarias para estilos.
    • DOM API – Manipulación dinámica del DOM.

    6. Posibles mejoras futuras

    • Persistencia del estado de expansión – Guardar en localStorage los proyectos expandidos para recordarlo entre reinicios.
    • Indicador de carga – Mostrar un spinner mientras se cargan actividades.
    • Edición y eliminación – Añadir acciones para modificar o borrar proyectos y actividades.
    • Arrastrar y soltar – Reordenar proyectos y actividades.
    • Integración con el temporizador – Al hacer clic en una actividad, establecerla como la actividad activa en el temporizador.

    7. Conclusión

    El código de sidebar.js ha evolucionado desde un simple prototipo con datos estáticos hasta un componente totalmente funcional que obtiene datos reales de la base de datos a través de Electron IPC. Ofrece una experiencia fluida con actualizaciones en tiempo real, acordeón para actividades y una clara separación de responsabilidades. Su diseño orientado a eventos facilita la comunicación con el resto de la aplicación, permitiendo una arquitectura desacoplada y mantenible.

     
  • Anonymous

    Anonymous - 2026-09-09

    Originally posted by: gtshadow33

    Integración de actividades y proyectos con el contador

    Descripción

    Se ha implementado la comunicación entre el sidebar de proyectos/actividades y el contador de sesiones de la aplicación.

    El objetivo es que, al seleccionar una actividad desde el sidebar, el contador reciba tanto:

    • La actividad seleccionada.
    • El proyecto al que pertenece.

    De esta forma, el campo del contador se rellena automáticamente con el formato:

    Actividad @ Proyecto
    

    Por ejemplo:

    come @ cenarr
    

    Flujo de funcionamiento

    El flujo implementado es:

    Sidebar
       │
       │ Usuario pulsa una actividad
       ▼
    renderActivities()
       │
       │ CustomEvent("activity:select")
       │
       │ detail:
       │ ├── actividad
       │ └── project
       ▼
    counter.js
       │
       │ Recibe el evento
       ▼
    activityInput
       │
       │ "actividad @ proyecto"
       ▼
    Contador
    

    1. Sidebar

    Paso 1: pasar el proyecto a renderActivities

    La función de renderizado de actividades ahora recibe también el proyecto:

    function renderActivities(projectId, actividades, project) {
        // ...
    }
    

    Anteriormente solo recibía:

    function renderActivities(projectId, actividades) {
        // ...
    }
    

    Esto permite que cada actividad conozca el proyecto al que pertenece.


    Paso 2: pasar project desde renderProjects

    Cuando se cargan las actividades de un proyecto, se pasa también el objeto del proyecto:

    renderActivities(
        project.id_proyecto,
        actividades,
        project
    );
    

    Esto se realiza tanto cuando el usuario expande un proyecto como cuando se cargan inicialmente los proyectos que ya estaban expandidos.


    2. Selección de una actividad

    Cuando el usuario hace clic sobre una actividad:

    item.addEventListener("click", (e) => {
        e.stopPropagation();
    
        console.log("Actividad seleccionada:", actividad);
        console.log("Proyecto seleccionado:", project);
    
        document.dispatchEvent(
            new CustomEvent("activity:select", {
                detail: {
                    actividad,
                    project
                }
            })
        );
    });
    

    Se genera un evento personalizado llamado:

    activity:select
    

    3. Datos enviados mediante el evento

    El evento contiene un objeto detail con dos propiedades:

    detail: {
        actividad,
        project
    }
    

    actividad

    Contiene la información de la actividad seleccionada.

    Ejemplo:

    {
        id_actividad: 12,
        id_proyecto: 4,
        nombre: "come",
        estado: "Pendiente"
    }
    

    project

    Contiene la información del proyecto.

    Ejemplo:

    {
        id_proyecto: 4,
        nombre: "cenarr",
        precio_hora: 10,
        id_moneda: 1
    }
    

    Se decidió utilizar el nombre:

    project
    

    para representar el proyecto.


    4. Recepción en counter.js

    El contador escucha el evento:

    document.addEventListener("activity:select", (e) => {
        const { actividad, project } = e.detail;
    
        console.log("Actividad recibida:", actividad);
        console.log("Proyecto recibido:", project);
    
        if (!actividad || !project) return;
    
        activityInput.value = `${actividad.nombre} @ ${project.nombre}`;
    
        hideSuggestions();
        activityInput.focus();
    });
    

    Se extraen los dos objetos:

    const { actividad, project } = e.detail;
    

    Después se comprueba que ambos existan:

    if (!actividad || !project) return;
    

    5. Rellenado automático del contador

    Una vez recibidos los datos, el input del contador se rellena automáticamente:

    activityInput.value = `${actividad.nombre} @ ${project.nombre}`;
    

    Por ejemplo:

    actividad.nombre = "come"
    project.nombre = "cenarr"
    

    Resultado:

    come @ cenarr
    

    Esto permite utilizar directamente la lógica existente del contador.


    6. Compatibilidad con splitActivityDetails()

    El contador ya dispone de una función para separar actividad y proyecto:

    function splitActivityDetails(activityDetails) {
        let activity;
        let project;
    
        if (activityDetails.trim() === "") {
            activity = DEFAULT_ACTIVITY.nombre;
            project = DEFAULT_PROJECT.nombre;
    
            return { activity, project };
        }
    
        const lst = activityDetails.split("@");
    
        if (lst.length == 1) {
            activity = lst[0].trim();
            project = DEFAULT_PROJECT.nombre;
        }
    
        if (lst.length > 1) {
            activity = lst[0].trim();
            project = lst[1].trim();
        }
    
        return { activity, project };
    }
    

    Por tanto, el formato:

    come @ cenarr
    

    se transforma en:

    {
        activity: "come",
        project: "cenarr"
    }
    

    El .trim() elimina los espacios alrededor del separador @.


    7. Orden de declaración en counter.js

    También se corrigió el orden de inicialización de los elementos DOM.

    Los elementos utilizados por el listener deben declararse antes de utilizarse:

    const activityInput = document.getElementById("activity-details");
    const suggestionBox = document.getElementById("suggestion-box");
    

    Después se registra el listener:

    document.addEventListener("activity:select", (e) => {
        // ...
    });
    

    Esto mantiene el código correctamente organizado y evita problemas con las variables declaradas mediante const.


    8. Resultado final

    Ahora el usuario puede:

    1. Abrir el sidebar.
    2. Expandir un proyecto.
    3. Seleccionar una actividad.
    4. El sidebar obtiene:

    5. La actividad.

    6. El proyecto.
    7. Se envía un CustomEvent.
    8. counter.js recibe ambos objetos.
    9. El input se rellena automáticamente.

    Ejemplo:

    Proyecto:
        cenarr
    
    Actividad:
        come
    

    Al seleccionar come:

    ┌─────────────────────────────┐
    │ come @ cenarr               │
    └─────────────────────────────┘
    

    El contador puede utilizar posteriormente ese valor para obtener los IDs correspondientes y comenzar la sesión.


    9. Arquitectura utilizada

    La comunicación se realiza mediante un evento personalizado del DOM:

    sidebar.js
        │
        │ dispatchEvent()
        ▼
    activity:select
        │
        │ detail
        ├── actividad
        └── project
        │
        ▼
    counter.js
        │
        │ activityInput.value
        ▼
    "Actividad @ Proyecto"
    

    Esta solución permite mantener separados los módulos:

    • sidebar.js se encarga de proyectos y actividades.
    • counter.js se encarga del contador.
    • El CustomEvent sirve como mecanismo de comunicación entre ambos.

    Archivos modificados

    js/sidebar.js

    Responsabilidades añadidas/modificadas:

    • Pasar project a renderActivities().
    • Conservar el proyecto asociado a cada actividad.
    • Emitir activity:select.
    • Enviar actividad y project mediante detail.

    js/counter.js

    Responsabilidades añadidas/modificadas:

    • Escuchar activity:select.
    • Recibir actividad y project.
    • Rellenar automáticamente el input.
    • Mantener compatibilidad con splitActivityDetails().

    Resultado

    La selección desde el sidebar queda integrada con el contador sin necesidad de que el usuario escriba manualmente la actividad y el proyecto.

    Formato utilizado:

    Actividad @ Proyecto
    

    Evento utilizado:

    activity:select
    

    Datos enviados:

    {
        actividad,
        project
    }
    
     

Log in to post a comment.