milibrodeobra
El ingeniero residente carga el día una vez, parado en la obra, con fotos. El mensaje de WhatsApp sale idéntico al que escribía a mano, los seguidores abren la página por link sin instalar nada y el libro de obra se escribe solo.
El libro de obra de papel lo lee quien lo tiene en la mano. Este se escribe desde el teléfono, en la obra, y lo abre gente que nunca instaló nada.
El reporte diario se carga una vez. De ese mismo estado salen el WhatsApp que el grupo ya sabe leer, la página que se comparte por link y el libro de obra. En uso real en obras desde junio de 2026.
El día de un ingeniero residente, de la obra al libro
Pulsa los pasos o deja que avance. Cada paso ilumina la pieza de la que habla.
Lo que recibe el grupo. El formato es el que ya leían: solo cambia quién lo teclea.
Un frontend, un estado y una base que también es la de desarrollo
No hay API separada ni cron. Las páginas leen con la clave anónima bajo RLS; las mutaciones son server actions que validan con Zod y escriben con el service role.
actividades como motor del avance, eventos para la auditoría de uso, reportes_pago con estado. El libro de obra no tiene tabla: se construye leyendo los reportes.Un día de obra, de las 7 de la mañana al libro
Un caso inventado con números pequeños, para ver qué pasa en cada tramo antes de hablar de los retos.
**Cemento 20 sacos a material pendiente, prioridad alta. Guardar valida el reporte y lo escribe como estado del día.Cinco veces en que la solución obvia estaba mal
Cada reto sigue el mismo patrón: lo que cualquiera haría primero, por qué no sirve, lo que se hizo y el número que lo demuestra. El detalle técnico queda plegado.
El filtro de cada decisión fue el mismo: ¿esto le ahorra tecleo al ingeniero parado en la obra, o le cambia algo a la gente que ya lee el reporte? Lo primero entra. Lo segundo se descarta aunque sea "mejor".
La base de desarrollo es la de producción, y de ahí salen las reglas
Un ingeniero cargando reportes reales mientras el producto se construye. Migraciones aditivas, fixtures con prefijo test-* que se crean y se borran por script, una lista de obras protegidas que aborta la limpieza si se la nombra, y backup JSON antes de mutar datos.
Profundizar
Catorce tablas en total, con el snapshot en una sola. Un flag como silenciado en un consumible viaja en el clon del día siguiente porque está en el JSON, no porque una FK lo garantice. Es un comportamiento deseado que hay que conocer.
revalidatePath sobre lo que cambió.Profundizar
Los reportes públicos son force-dynamic: renovar una suscripción o silenciar una alerta se ve en la siguiente recarga sin redeploy. No hay cron ni scheduler; los días de ejecución, las alertas de stock y el libro se calculan al leer.
Profundizar
Google Identity Services entrega un id_token que se pasa a signInWithIdToken. Si el script de Google no carga, cae solo al camino por redirección. La regla de quién puede entrar vive aparte del transporte OAuth (lib/altaGoogle.ts, con la puerta de datos inyectada) para poder probarla sin Google: si ya hay perfil con ese email sin enlazar, se enlaza solo; si apunta a otro usuario de Auth, no se pisa.
Gotcha: locale en renderButton no traduce nada; lo que traduce el botón es ?hl=es en la URL del script. Y el botón falla en silencio si el origen no está autorizado, así que se comprueba que Google haya dibujado algo antes de reemplazar el respaldo.
Profundizar
La cuenta nueva arranca con 30 días de prueba sin tarjeta. En /suscripcion el usuario ve cuántos días le quedan, a dónde pagar y un formulario para avisar que pagó: referencia, banco, fecha y captura obligatoria. El admin revisa en /admin y confirma o descarta con motivo, que el usuario lee en su historial.
Confirmar suma un mes sobre el vencimiento vigente, no sobre hoy: quien paga antes de tiempo no pierde los días que le quedaban. proximoVencimiento corrige además el caso 31 de enero más un mes, que JavaScript manda a marzo.