Saltar al contenido
Development

Construir la vista de detalle de proyecto para Greenhouse

Por Victor Da Luz
taurirustsveltedev-loggreenhouse

Greenhouse es mi gestor de proyectos creativos, el que impone enfriamientos y rotación en vez de dejarme acumular ideas a medio terminar para siempre. El panel ya listaba los proyectos activos en tres zonas: listo hoy, lista de trabajo, enfriándose. Pero no se podía entrar a un proyecto todavía. No había forma de ver su cuenta regresiva de enfriamiento, su historial de toques, ni moverlo realmente hacia adelante en el proceso.

El plan

Había dos cosas que decidir antes de poder escribir código, y ninguna era obvia solo mirando el código.

Primero: ¿avanzar un proyecto a la siguiente etapa cuenta como un “toque”? Tocar es lo único que inicia el enfriamiento de 7 días en esta app, y la función existente move_project_to_stage deliberadamente no registra uno. Tuve que elegir un lado. Elegí que sí, que avanzar implica un toque, bajo la teoría de que mover un proyecto hacia adelante ya es evidencia de una sesión de trabajo completada.

Segundo: ¿qué pasa en la última etapa? El esquema ya tenía un estado Released y un timestamp released_at sin usar. Nada los llenaba. Agregué una acción de “Publicar” en la etapa final que por fin los conecta.

El desvío

A mitad de camino, me detuve y me pregunté si los 7 días de enfriamiento eran siquiera el número correcto. ¿Debería ser más corto? ¿Debería existir el enfriamiento en primer lugar, o la app simplemente debería exigir el orden de toques y confiar en que yo toque las cosas con honestidad?

Pensarlo a fondo sacó a la luz algo que yo tenía enredado: rotación (el proyecto tocado menos recientemente sube al tope) y enfriamiento (un piso mínimo de tiempo después de un toque) no son ideas que compiten entre sí. La rotación ya es como se ordena la lista de trabajo. El enfriamiento es solo un piso atornillado encima. Así que la pregunta real no era “enfriamiento vs. rotación”, era “qué tan alto debería ser el piso”. Y la rotación pura sin piso se cae justo donde más me perjudicaría: con solo uno o dos proyectos activos, no hay nada hacia dónde rotar, así que la “brecha” entre toques es simplemente la velocidad a la que yo hago clic de un lado a otro.

Ese replanteamiento hizo fácil la decisión real: bajar el valor por defecto de 7 días a algo más corto, mantener intacta la idea de un ritmo forzado, y dejar la pregunta de “qué tal si en vez de eso fuera un código de honor” como una investigación aparte, en vez de dejar que secuestrara la funcionalidad que en realidad estaba construyendo.

Construyéndolo

El lado de Rust necesitaba dos funciones nuevas en el motor: advance_stage (tocar, y después mover la carpeta a la siguiente etapa en la lista ordenada de etapas de la configuración) y release_project (cambiar el estado a Released, y grabar el timestamp). Las dos combinan piezas ya existentes en vez de reinventarlas. Del lado de Svelte, el nombre de la tarjeta de un proyecto se convirtió en un botón real, que abre una nueva vista de detalle con el nombre de la etapa, la cuenta regresiva de enfriamiento, el historial de toques, y los controles de avanzar/publicar.

La parte que en algún momento quiero documentar como corresponde: verificarlo. Esta app tiene un arnés de WebDriver que controla el binario compilado real mediante clics reales y llamadas IPC reales, no simulaciones. El problema es que una sesión de WebDriver en vivo no puede adelantar el reloj real, y tanto la maduración de ideas como el enfriamiento son temporizadores de varios días. Así que no hay forma de ir de “capturar una idea” a “hacer clic a través de todo el proceso” dentro de una sola corrida de prueba.

La solución fue sembrar la base de datos SQLite directamente con sqlite3 antes de lanzar la app, insertando un proyecto activo ya en la etapa que la prueba necesitara. Eso no es hacer trampa; cada comando sigue corriendo de verdad una vez que la app arranca, solo salté la parte en la que, de otra forma, estaría esperando siete días a que corriera un temporizador.

Dónde quedó

Las dos funciones del motor tienen pruebas unitarias, el componente de Svelte tiene pruebas con mockIPC y una verificación automática de accesibilidad, y el script de WebDriver hace clic a través de avanzar la etapa de un proyecto y publicar uno en la etapa final contra un binario real. Hay capturas de pantalla como evidencia, y todas las verificaciones habituales (clippy, fmt, type-check, build) están limpias.

Pero la mejor parte de la sesión no fue el código. Fue notar que estaba a punto de cambiar una mecánica central de la app por una digresión, dar un paso atrás, y darme cuenta de que el problema real tenía una solución mucho más pequeña esperando justo al lado.

Lecturas relacionadas