Bienvenido a este capítulo donde estaremos desarrollando una herramienta de "Gestión de Proyectos" aplicando los conceptos de "lógica básica" y "comunicación básica" que hemos visto en este repositorio. Todas las herramientras usadas en este caso ya han sido explicadas en los capítulos correspondientes. De todas maneras, estaré dejando los enlaces según vea conveniente. Sin más dilación...
Empecemos...
...definiendo ¿qué es una herramienta de gestión de proyectos?. Y básicamente es una aplicación que te ayuda a organizar, planificar, ejecutar, etc., diferentes tareas y recursos que forman parte de un proyecto.
Y ¿para qué sirve una herramienta de este tipo? Pues, dependiendo de la complejidad de la misma, puede ayudarnos más o menos a: aumentar la productividad, mejorar la organización, facilitar la colaboración (si se crea para equipos), etc. Ahora hablemos...
...sobre el proyecto.
En mi caso, quería crear una herramienta de "gestión de proyectos" que me sirviera para trabajar en mi PC de manera personal, que no se conectase a internet y con un aspecto bastante simple y sobrio. Esto, porque he probado muchas herramientas y no me terminan de convencer. Así que pensé que se podía crear una.
A continuación, deberás escoger tu IA de preferencia. Yo trabajo con la IA de Google AI Studio, por razones que ya expliqué aquí. Pero la lógica y comunicación se aplica a cualquier IA de tu preferencia.
Lógica
Para definir la lógica, empezaremos definiendo las características principales que queremos que tenga la herramienta. Preguntas como: ¿qué quiero que haga? ¿cuando haga clic aquí qué va a pasar? ¿cómo va a interactuar tal o cual cosa? ¿cómo se va a ver? ¿qué atributos quiero ver yo en esta herramienta? entre otras más, nos ayudarán a definir la lógica.
Recordemos que la lógica la he dividido en dos partes: por un lado está la parte de Fondo (estructura), que nos ayuda definiendo las funciones, y por otra parte está la Forma (estética), que nos ayuda definiendo la parte visual.
Con esto como punto de partida, definí que habían tres actividades clave que quería que la herramienta me permitiera hacer: agregar un proyecto, agregar tareas a cada proyecto y guardar mi progreso por si quedara algún pendiente para el día siguiente. Te dejo un esquema de mi lógica en base al "Fondo (estructura)":
Como verás, agregué un punto que dice "Lenguaje HTML". Esto debido a que, como lo iba a mostrar en mi web y esta es "Wordpress", solo me deja incluir un código HTML, como te lo mostré en la guía de Google Sites. Pero, si tú vas a generarlo de cero y no te interesa mostrarlo, te recomiendo que lo hagas generando códigos separados para que tengas más control.
A partir de definir las tres actividades principales, empezamos a completar las funcionalidades de cada una como mostró la imagen de arriba. Aquí te dejo un desglose de funcionalidades y componenetes que tuve en cuenta:
Funcionalidades y Componentes
Funcionalidades básicas:
Crear proyectos
Añadir tareas
Visualizar el progreso del proyecto
Guardado del progreso en local
Desglosando la herramienta en componentes:
Lista de proyectos
Formulario para crear proyectos
Vista de proyecto
Formulario para añadir tareas
Lista de tareas
Con la estructura clara pasaremos a definir la lógica visual. En mi caso, como quería que tuviera un aspecto simple y sobrio, me decanté por un estilo minimalista. Un aspecto importante que te permite definir la lógica de forma (estética) es el formato.
El formato tiene mucho que ver porque es el que va a dar forma a tu herramienta. Usualmente manejo dos tipos: contenedores y tablas. En este caso, como es una herramienta que divide varios "proyectos", me conviene más verlo en forma de contenedores y no de tablas.
Por lo demás, es definir de manera básica el aspecto en atributos como los colores, forma de botones, opciones de menú y uno muy importante es la "adaptabilidad". Básicamente la "adaptabilidad" hace referencia a que nuestra herramienta se pueda ver bien en PC si acortamos o agrandamos la página, y en móviles.
Nuestra lógica de "Forma" quedaría de la siguiente manera:
Simple, ¿verdad?. Yo sé que puede parecer complejo al inicio tener que hacer todo esto, pero te aseguro que es la manera en cómo te puedes ahorrar bastantes pasos y dolores de cabeza después. Ahora, ¿qué toca?.
Definir la comunicación...
...que no es otra cosa que hacer el prompt. Pero como ya hemos visto, de esta depende que la IA te entienda, te entienda a medias o no te entienda. Yo he propuesto, para este ejercicio, definir una jerarquía lineal haciendo una descripción de la herramienta y su comportamiento viéndolo de arriba hacia abajo.
Recuerda que la idea detrás de estructurar un buen prompt es evitar la máxima cantidad de errores y llegar al resultado de la manera más rápida.
Entonces, el promtp, en mi caso, quedaría de la siguiente manera:
Prompt 1: Basado en la lógica propuesta y con una jerarquía en la comunicación
Quiero crear una herramienta de gestión de proyecto personal que funcione en local. El lenguaje a usar será HTML, este puede contener CSS o JS, pero siempre dentro del HTML. (Comenzamos indicando el lenguaje, ya que es la base sobre la que se sentará la herramienta y resaltamos que considere un solo archivo porque lo usaremos para compartir.)
Lo primero que debe tener es un contenedor para agregar un nuevo proyecto y este debe tener los atributos "Nombre del proyecto" y "Descripción del proyecto", ambos son para ser rellenados. Luego, deben contar con dos campos, uno para fecha de inicio y otro para fecha de fin, ambos deben poder desplegar un pequeño calendario para elegir el día, mes y año resultando en formato 00/00/0000. Al final de estos puntos debe contener un botón que diga "Agregar Proyecto" para que este se agregue a la cola de proyectos. (Previamente, según el esquema de lógica, vimos que cada parte podía pertenecer a un contenedor, por lo que decide tratarse cada punto de forma independiente. Vamos detallando los atributos de cada elemento que queremos. Siempre detalles de funcionamiento, no de estilo.)
Lo segundo es crear un nuevo contenedor debajo del primero que permita "Agregar Tarea". Este debe tener los atributos "Nombre de la tarea" y "Descripción de la tarea", ambos son para ser rellenados. Debajo debe haber un menú desplegable que diga "Seleccionar un proyecto" y debe poder permitir elegir entre los proyectos que se han agregado en cola. (Continuamos detallando las acciones que queremos que se realice en cada parte de nuestra herramienta. Tendremos las mismas consideraciones para el resto del prompt.)
Tercero, crearás un tercer contenedor donde estarán los proyectos creados con el atributo de "Descripción" y las "Fecha de inicio" y "Fecha de fin", y cada uno contará con una barra de progreso y un botón que permita "Eliminar Proyecto".
Asimismo, las tareas que se agreguen deberán estar dentro de cada proyecto elegido y deberán verse a través de un menú desplegable. Dentro de ese menú, deberá haber un botón que diga "Finalizar Tarea" y cuando se marque, la barra de progreso del proyecto avanzará.
Por último, al final de todo habrán dos botones, uno que permita "limpiar todos los proyectos" que están en el HTML y otro que permita "Guardar el progreso". Recuerda que debe poderse guardar el progreso en el mismo archivo.
Desarrolla un estilo minimalista, con escala de grises, las cajas con bordes redondeados y las flechas de los menús desplegables que sean modernos. Además, los botones podrían tener color "1a1a1a" y texto blanco. (En este punto se agrega brevemente consideraciones de estilo. El estilo minimalista es un buen punto de partida para que no se vea tosco. Después podrás hacer mejoras.)
Ahora usaré un "Codepen" donde he pegado el código "HTML" que me generó Google AI Studio. Recuerda que para verlo y probarlo puedes dar clic donde dice "Run Pen". Además, en la herramienta "Codepen" encontrarás un botón que dice "HTML" donde podrás clicar para ver el código que nos ha generado:
Como podrás observar, la herramienta está completamente funcional y era lo que buscábamos, que en pocos pasos, con menos pruebas y errores, lleguemos al resultado que queríamos. Aun así, nos faltaría agregar la lógica de "Forma (estética)" que habíamos definido anteriormente.
Te dejo abajo el promtp con las indicaciones que le hice a la IA:
Prompt 2: resaltando mejoras estéticas
Hagamos unos cambios de estilo. Primero, me gustaría que los títulos "Proyecto", "Agregar Proyecto" y "Agregar Tarea" estén centrados. (Nuevamente, empezamos realizando los cambios desde arriba y en orden, como definimos previamente al hacer nuestra jerarquía de comunicación).
Seguidamente, los proyectos que se agregan en el contenedor de abajo deben estar enmarcados por un borde grisáseo y deben tener una sombra bastante tenue, con un espacio considerable entre cada proyecto, para que así no se mezclen o se interrumpan.
Después, haz que las tareas aparezcan arriba del botón "Eliminar Proyecto" y debajo de la barra de progreso y enmarcalas en un contenedor con líneas grises sutiles, manteniendo un espaciado para que no se vea pegado al botón de "Eliminar Proyecto" y tampoco a la barra de progreso.
En cuanto a la barra de progreso esta debe contener dos colores, azul "#0070ff" cuando no ha llegado al 100% y verde "#24d83d" cuando está en el 100%. Y en cuanto al resto de colores trabájalos en escala de grises y aplica un color "1a1a1a" para todos los botones. (Los códigos de colores mostrados aquí son valores hexadecimales. Si quieres saber el código de un color en particula, te recomiendo que entres aquí.)
Asimismo, el botón de "Finalizar Tarea" hazlo un poco más pequeño para que guarde relación con el texto de la tarea.
Por último, agrega una descripción al final de todo que diga "Diseñado por Tomas Dulanto" en un color gris claro y letra pequeña. Además, hazlo responsive. Ahora escribe todo el código en su totalidad.
Ahora podrás ver la herramienta terminada en el siguiente "Codepen":
Con esto ya podríamos dar por terminado el proyecto. Como verás, lo más complicado ha sido definir la lógica y la comunicación. Pero como has podido observar, esto nos ayudó a reducir el número de intentos y errores notablemente.
De todas formas, como ya te había comentado, te recomiendo trabajar el código por separado. Esto te dará más control y flexibilidad al momento de querer hacer cambios o escalar tu idea inicial.
Al final de tejo el código para que lo copies y pegues en la herramienta que uses para prototipar. Antes de terminar quiero que tengas en cuenta una cosa...
...y es que este contenido es educativo y no se permite la reproducción con fines lucrativos. Todo el material aquí expuesto se ha creado para ser compartido con las personas que se interesen en conocer y profundizar sobre el manejo de IA Generativa.
¡IA nos vemos pronto!
<!DOCTYPEhtml><htmllang="es"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"> <!-- Para responsive --><title>Gestor de Proyectos Personal</title><style>body {font-family:sans-serif;background-color:#f4f4f4;color:#333; }.container {background-color:#fff;border-radius:8px;padding:20px;margin-bottom:20px;box-shadow:0 2px 5pxrgba(0, 0, 0, 0.1); }h2 {text-align:center; }input,select,textarea {width:100%;padding:10px;margin-bottom:10px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box; }button {background-color:#1a1a1a;color:#fff;padding:8px 12px;border:none;border-radius:4px;cursor:pointer;font-size:0.9em; }.project {border:1px solid #ddd;box-shadow:0 2px 4pxrgba(0,0,0,0.1);margin-bottom:20px;padding:15px;border-radius:8px; }.progress-bar {background-color:#ddd;border-radius:4px;height:20px;margin-bottom:10px; }.progress {height:100%;border-radius:4px;width:0%; }.task-list {border:1px solid #ddd;margin-bottom:10px;padding:10px;list-style:none; }.task-listli {margin-bottom:5px; }.select-wrapper {position:relative;display:inline-block;width:100%; }.select-wrapperselect {-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' viewBox='0 0 12 6'%3E%3Cpath fill='%23666' d='M0.379,0.909l5.243,5.241L11.621,0.909L12,1.288L6,7.288L-5.34359375e-16,1.288L0.379,0.909z'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:30px; }detailssummary {cursor:pointer;margin-bottom:10px; }.footer { /* Estilos para el pie de página */text-align:center;font-size:0.8em;color:#bbb;margin-top:20px; }/* Media query para responsive (ajusta a tus necesidades) */@media (max-width:768px) {.container {padding:15px; /* reduce padding en pantallas pequeñas */ } }</style></head><body> <divclass="container"> <h2>Agregar Proyecto</h2> <inputtype="text"id="projectName"placeholder="Nombre del proyecto"required> <textareaid="projectDescription"placeholder="Descripción del proyecto"required></textarea> <inputtype="date"id="startDate"required> <inputtype="date"id="endDate"required> <buttononclick="addProject()">Agregar Proyecto</button> </div> <divclass="container"> <h2>Agregar Tarea</h2> <inputtype="text"id="taskName"placeholder="Nombre de la tarea"required> <textareaid="taskDescription"placeholder="Descripción de la tarea"required></textarea> <divclass="select-wrapper"> <selectid="projectSelection"disabled></select></div> <buttononclick="addTask()">Agregar Tarea</button> </div><divid="projectsContainer"class="container"> <h2>Proyectos</h2></div><divclass="container"> <buttononclick="clearProjects()">Limpiar Proyectos</button> <buttononclick="saveProjects()">Guardar Progreso</button></div> <divclass="footer"> Diseñado por Tomas Dulanto</div><script>let projects = [];functionaddProject() {constprojectName=document.getElementById('projectName').value.trim();constprojectDescription=document.getElementById('projectDescription').value.trim();conststartDate=document.getElementById('startDate').value;constendDate=document.getElementById('endDate').value;if (projectName ===""|| projectDescription ===""|| startDate ===""|| endDate ==="") {alert("Por favor, complete todos los campos del proyecto.");return; }constnewProject= { name: projectName, description: projectDescription, startDate: startDate, endDate: endDate, tasks: [], completedTasks:0 };projects.push(newProject);renderProjects();updateProjectSelection();document.getElementById('projectName').value ='';document.getElementById('projectDescription').value ='';document.getElementById('startDate').value ='';document.getElementById('endDate').value ='';}functionaddTask() {consttaskName=document.getElementById('taskName').value.trim();consttaskDescription=document.getElementById('taskDescription').value.trim();constselectedProjectIndex=document.getElementById('projectSelection').value;if (taskName ===""|| taskDescription ===""|| selectedProjectIndex ===null ) {alert("Por favor, complete todos los campos de la tarea y seleccione un proyecto.");return; }constnewTask= { name: taskName, description: taskDescription, status:"Sin empezar" }; projects[selectedProjectIndex].tasks.push(newTask);renderProjects();document.getElementById("taskName").value="";document.getElementById("taskDescription").value ="";updateTaskCounter(selectedProjectIndex);}functionupdateTaskCounter(projectIndex) {let completedCount =0;for (consttaskof projects[projectIndex].tasks){if(task.status ==="Finalizado"){ completedCount++; } } projects[projectIndex].completedTasks = completedCount;updateProgressBar(projectIndex);}functioncalculateProgress(projectIndex) {if (!projects[projectIndex] ||!projects[projectIndex].tasks ) return0;consttotalTasks= projects[projectIndex].tasks.length;if(totalTasks ===0 ) return0;returnMath.round((projects[projectIndex].completedTasks / totalTasks) *100);}functionupdateProgressBar(projectIndex) {constprogressBar=document.querySelector(`.project:nth-child(${projectIndex +1}) .progress`);if (progressBar) {progressBar.style.width =`${calculateProgress(projectIndex)}%`; }}functionrenderProjects() {constprojectsContainer=document.getElementById('projectsContainer');projectsContainer.innerHTML ='<h2>Proyectos</h2>';projects.forEach((project, index) => {constprojectDiv=document.createElement('div');projectDiv.className ='project';let progress =calculateProgress(index);let progressBarColor = progress ===0?"#ff1f00": progress ===100?"#24d83d":"#0070ff";projectDiv.innerHTML =`<h3>${project.name}</h3> <p>${project.description}</p> <p>Inicio: ${project.startDate}, Fin: ${project.endDate}</p> <div class="progress-bar"> <div class="progress" style="width: ${progress}%; background-color: ${progressBarColor};"></div> </div> <details> <summary>Tareas</summary> <ul class="task-list">${project.tasks.map(task =>` <li>${task.name} — ${task.status !=="Finalizado"?`<button onclick="completeTask(${index}, '${task.name}')">Finalizar Tarea</button>`:''} </li>`).join('')} </ul> </details> <button onclick="deleteProject(${index})">Eliminar Proyecto</button>`;projectsContainer.appendChild(projectDiv); });}functionupdateProjectSelection() {constprojectSelection=document.getElementById("projectSelection");projectSelection.innerHTML ="";projectSelection.disabled =projects.length===0;projects.forEach((project, index) => {constoption=document.createElement("option");option.value = index;option.text =project.name;projectSelection.appendChild(option); });}functionclearProjects() {if(confirm("Esta seguro que quiere eliminar todos los proyectos?")){ projects = [];renderProjects();updateProjectSelection(); }}functiondeleteProject(index) {if (confirm("¿Estás seguro de que deseas eliminar este proyecto?")) {projects.splice(index,1);renderProjects();updateProjectSelection(); }}functioncompleteTask(projectIndex, taskName) {constproject= projects[projectIndex];consttaskIndex=project.tasks.findIndex((task) =>task.name === taskName);if (taskIndex !==-1&&project.tasks[taskIndex].status !=="Finalizado") {project.tasks[taskIndex].status ="Finalizado";project.completedTasks++;updateProgressBar(projectIndex);renderProjects(); }}functionsaveProjects() {localStorage.setItem('projects',JSON.stringify(projects));}constsavedProjects=localStorage.getItem("projects");if (savedProjects) {try { projects =JSON.parse(savedProjects); } catch (error) {console.error("Error al analizar los datos guardados:", error); projects = []; }renderProjects();updateProjectSelection();projects.forEach((project, index) =>updateProgressBar(index));}</script></body></html>