Informe François
https://informe-francois.vercel.app
PWA de un solo index.html (sin build) para anotar lo que pasa en el turno y sacar la bitácora del día, de la semana o de cierre: DeepSeek arma el pre-informe desde la app (función api/preinforme.js, con la clave en Vercel) y tú lo revisas y lo mandas. Si prefieres otro chat, el prompt maestro sigue saliendo armado, listo para pegar.
Instalarla en el teléfono
Abre la URL y toca el botón del teléfono con la flecha (arriba a la derecha, o en la pestaña Datos → «Instalar en el teléfono»). La ficha muestra los pasos de tu propio dispositivo:
- Android (Chrome): normalmente ofrece Instalar ahora de una vez; si no, menú ⋮ → Instalar aplicación.
- iPhone (Safari): Compartir → Agregar a inicio. Si estás en Chrome iOS te avisa que abras en Safari y te copia la dirección.
- Computador: te deja copiar o compartir el enlace para abrirlo en el teléfono (y el navegador también puede instalarla ahí).
Queda con ícono propio, a pantalla completa y funciona sin señal (service worker). Las notas viven en ese aparato; para que el teléfono y el computador muestren lo mismo, entra con tu cuenta en los dos (ver Sincronía).
Qué hace
-
Notas — agregas la nota al paso: fecha, hora, área (Sala · Cocina · Caja · Personal · Proveedores · Equipos · Producto · Otro), qué pasó y, opcional, la medida tomada en el momento. Se pueden editar y borrar; se agrupan por día. Ctrl + Enter guarda rápido.
Quién anota — la nota queda firmada por la persona que está usando la app (ver
Varias personas). La firma se pone al crear la nota
y no se toca más: editar una nota ajena —o una vieja sin firma— no te convierte en su autor. Se ve
en la lista y viaja al prompt como — anotó Ángel. La regla 11 le dice a Claude que esa persona es
quien registró la nota, no la responsable del hecho: que la use solo cuando aporte —notas del
mismo tema que se contradicen, o a quién preguntarle en el CIERRE—. Vive en la columna quien de
if_notas; si el proyecto todavía no la tiene, la app sube las notas sin firma y lo avisa en
Datos en vez de caerse.
La medida viaja al prompt en la misma línea, después de → MEDIDA:, y la regla 9 le dice a Claude que eso ya está hecho: lo reporta en pasado dentro del mismo punto, no lo repite como pendiente, y si la medida cierra el tema lo trata como resuelto (si solo lo parcha, lo dice y deja el pendiente de fondo).
Fotos — la nota tiene dos juegos de fotos: las de Foto del hecho, bajo «Qué pasó», respaldan lo que ocurrió; las de Foto de la medida, bajo «Medida tomada en el momento», respaldan lo que se hizo. Cada botón abre la cámara del teléfono y su Galería toma una ya sacada (varias a la vez). Se comprimen a 1600 px / JPEG antes de guardarlas (una foto de teléfono queda en ~100-250 KB), aparecen como miniaturas en el formulario y en la nota, y se abren en el visor: pasar entre fotos, Compartir (mandarla por WhatsApp desde el teléfono), Guardar en el dispositivo o Borrar. Si aprietas «Agregar nota» mientras la foto se está guardando, la nota se guarda sola apenas termina.
Al prompt solo va la marca [con foto] / [con N fotos], pegada a lo que respalda: después del hecho si la foto es del hecho, y después de → MEDIDA: si es de la medida. La imagen no viaja al chat. La regla 10 le dice a Claude que no describa ni suponga lo que muestra, y que como mucho cierre ese punto con «(hay foto)».
Comentarios — cada nota tiene Comentar: lo que se dice después sobre ese tema, firmado y con fecha y hora. Cada quien borra lo suyo y quien administra, todo; borrar la nota se lleva sus comentarios. Van en su propia tabla (if_comentarios, se crea con SQL-comentarios.sql), así dos personas comentando a la vez no se pisan y editar la nota no los toca. No entran a lo que va al grupo: solo el CIERRE los lleva, en la misma línea de su nota, después de → COMENTARIO [fecha] autor: (lineaComentarios); la regla está en el bloque del CIERRE en prompt.js. El Pase los lee junto a su nota, los muestra y sus agentes los analizan. Mientras la tabla no exista, quedan en ese aparato y Datos lo avisa.
Para revisar antes de la reunión — bajo la lista, Mostrar tiene «Semana de la reunión» (lunes a domingo; el mismo lunes de la reunión, la que acaba de terminar: la misma regla de El Pase) y Solo las por tratar deja las notas sin medida, con el número al lado.
-
Generar — eliges qué informe (modo y período) y lo armas:
- DIARIO → resumen del día para el grupo (elige un día).
- SEMANAL → resumen para el grupo con las fechas que elijas (desde/hasta: una semana, quince días o un mes; atajos «esta semana» / «semana pasada»).
- CIERRE → informe para la reunión del lunes, también desde/hasta; aparece el campo opcional Pendientes de la reunión pasada, que se cruza con las notas del período.
Preparar reunión deja el CIERRE listo con la semana de la reunión y rellena los pendientes con los temas del último cierre guardado que no sea de este mismo período (temasDeCierre, solo lo de TEMAS PARA LA REUNIÓN). La app no sabe qué se cerró en la reunión, porque eso queda en El Pase: quien la arma borra lo resuelto. Lo que queda en el cuadro se guarda en ese aparato, para esa semana y esa persona; volver a tocar el botón no lo pisa. No lo ve quien solo ve sus propias notas: su cierre saldría incompleto.
Qué notas entran: todas las del período, con los dos extremos incluidos (periodo() / notasDelPeriodo()).
El período siempre es cerrado: sin «hasta» no arma nada (antes tomaba todo lo posterior a «desde»), y si
«desde» queda después de «hasta» las fechas se dan vuelta solas. Bajo las fechas se ve el período, cuántas
notas son, de qué áreas y quién las anotó, y Ver las notas que entran muestra las líneas exactas que van
a DeepSeek o a Claude. Si el perfil no ve las notas de todos (ver Quién anota), lo dice ahí mismo.
Las notas del período van con el formato acordado, en orden cronológico:
[lun 18-08 · 14:20] (Caja) diferencia de 3.400 en el arqueo [con foto] → MEDIDA: se hizo arqueo ciego de nuevo [con foto].
Pre-informe con DeepSeek (el camino de siempre): Armar pre-informe manda esas líneas a
api/preinforme.js, que le pide el informe a DeepSeek con las mismas reglas del prompt maestro
(prompt.js) y deja el borrador en el cuadro Informe, ya en formato WhatsApp. Antes sincroniza, para
que entren las notas del otro teléfono, y al terminar dice cuántas notas leyó DeepSeek (la función las
cuenta: recibidas). Tope: 200.000 caracteres de notas, más de un mes. Lo que DeepSeek
quiere que revises —una nota ambigua, un área que no calza con lo que dice la nota— sale aparte,
en «Para revisar», y no va en el informe. Si el cuadro ya tenía texto, pregunta antes de
reemplazarlo. Necesita internet y la cuenta de la app conectada en Datos (así nadie de afuera
gasta el saldo de DeepSeek). Si falta la clave en Vercel, la app lo avisa al abrir Generar.
-
Informe (en Generar, bajo el pre-informe) — ahí queda el borrador de DeepSeek o lo que pegues de otro chat: lo corriges y lo mandas. Incluye:
- Vista «cómo llega al grupo»: una burbuja tipo WhatsApp con las negritas y viñetas ya renderizadas, para revisar antes de enviar.
- Revisión de formato: avisa si hay
**doble asterisco**, encabezados con #, viñetas con guion, filas de tabla o un asterisco sin cerrar. Arreglar formato lo pasa todo a formato WhatsApp (*negrita*, • viñeta), y al enviar, si queda algo, pregunta si lo arregla primero. En el CIERRE respeta las filas N° | CATEGORÍA | ….
- Barra de redacción:
*N* pone en negrita lo que marques, *TÍTULO* convierte la línea en encabezado, • le pone viñeta y el último botón agranda el cuadro.
- Borrador automático: lo que estés escribiendo se guarda (
if_borrador_v1) y vuelve si cierras la app.
- Fotos del período: bajo la vista de WhatsApp aparece la tira con las fotos de las notas del período. Tócalas para incluirlas o dejarlas fuera (la lupa las abre grandes) y Enviar fotos las manda con el compartir nativo del teléfono, para elegir el mismo grupo justo después del informe — el enlace
wa.me solo lleva texto, por eso van aparte. Al enviar el informe, un aviso recuerda mandarlas. En el PC, que no comparte archivos, ofrece descargarlas.
- PDF: arma una hoja con el título (modo + período) y el informe ya formateado, con cada foto justo bajo el punto que la menciona (no todas juntas al final). En el CIERRE, los temas para la reunión (
N° | CATEGORÍA | Qué pasó | Acuerdo | Responsable | Evidencia) salen como tabla. Como el informe lo redacta la IA con sus propias palabras, la app empareja cada foto con la línea que más se le parece: palabras en común, el «(hay foto)» de la regla 10 y el título del bloque (SALA, CAJA…). Las del hecho buscan la línea del hecho y las de la medida, la de la medida — si el informe junta las dos cosas en un punto, quedan las dos ahí (primero el hecho); si las separa (por ejemplo el hecho en CAJA y la medida en RESUELTOS), cada una va donde corresponde, y las de la medida llevan «respalda la medida» en el pie. Las que no calzan con ninguna línea caen en «Otras fotos» al final, con su día · hora · área y el texto de la nota — así ninguna se pierde. Se abre el diálogo de imprimir: en el teléfono, «Guardar como PDF». Ese archivo sí se adjunta al grupo o se lleva a la reunión.
-
Informes — los informes guardados con su modo, período y fecha; desde cada uno: WhatsApp, copiar, descargar .txt o borrar.
-
Datos — cuenta y sincronía (ver más abajo), exportar / importar JSON de respaldo (notas + informes y, si marcas la casilla, las fotos), glosario interno (86, 101, arqueo ciego) y borrado (notas, informes o solo las fotos).
El texto del período (lun 17-08, 17-08 al 23-08) se calcula solo, pero es editable a mano.
Dónde se guarda
Todo en el navegador del dispositivo:
if_notas_v1 — las notas.
if_comentarios_v1 — los comentarios sobre las notas.
if_informes_v1 — los informes que pegaste de vuelta.
if_borrador_v1 — el informe que estás redactando (borrador).
if_prefs_v1 — modo claro/oscuro, número de WhatsApp, quién entró en este aparato.
if_perfiles_v1 — las personas que usan la app (con la clave cifrada, nunca en texto).
Las fotos no caben en localStorage, así que van en IndexedDB (base if_fotos_v1): el almacén fotos guarda la imagen comprimida y thumbs la miniatura y el peso; la nota solo lleva los id de sus fotos, en dos listas: fotos (respaldan el hecho) y fotosm (respaldan la medida). Las fotos que quedaron sin nota (la app se cerró antes de guardarla) se limpian solas al abrir.
Sin cuenta, la app funciona igual pero cada aparato tiene lo suyo: para mover las notas a mano usa Exportar JSON e Importar JSON. La importación no duplica (compara por id). El respaldo incluye las fotos en base64 si dejas marcada la casilla — pesa bastante más, así que para mover solo el texto, desmárcala.
Varias personas en la misma cuenta
Una sola cuenta de Supabase para todos —eso es lo que guarda y sincroniza— y adentro, una persona
por perfil, cada una con su propia clave de app. Al abrir, la app pregunta quién eres.
- Quién ve qué: quien administra la app ve todo junto (para armar el informe del turno
completo) y puede apagarlo con «Ver también las notas de los demás»; los demás ven solo lo suyo.
Filtra las notas, el prompt, las fotos del período, los informes guardados y los botones de borrado
—nadie borra lo del otro sin querer—.
- Las claves van cifradas:
sha-256 de una sal aleatoria + la clave, calculado con Web Crypto.
En la tabla queda el hash; la clave en texto no existe en ninguna parte. Sin ella no se entra como
otra persona, ni siquiera compartiendo la cuenta.
- Hasta dónde llega: es la chapa de la puerta de una casa donde viven los dos, no una caja
fuerte. La app corre entera en el teléfono de cada uno y comparten la cuenta de Supabase, así que
alguien con conocimientos técnicos puede saltársela. Frena lo que tiene que frenar: que se anote
con el nombre de otro.
- Si se olvida una clave: se borra esa persona (el basurero en la ficha) y se crea de nuevo. Sus notas no
se tocan: quedan firmadas con su nombre.
- Nombrar administrador: si ya hay quien administre, la casilla solo aparece para esa persona.
Así nadie se hace administrador solo por abrir la app en un teléfono recién instalado.
- Lo anotado antes de que existieran los perfiles se le ofrece al primer perfil, preguntando:
si el primero en crearse fuera el otro teléfono, se llevaría notas que no son suyas.
- Puesta en marcha: el bloque
if_perfiles de supabase.sql. Si no se corre, los
perfiles funcionan igual pero solo en ese aparato, y la app lo avisa en Datos en vez de caerse.
Una app por sede: François PV y Maximarket PV (28-oct-2026)
La misma app sirve a las tres sedes. Cada sede tiene su propia cuenta de Supabase, así que sus
notas, personas, informes y fotos quedan aparte solos (las políticas RLS ya son por cuenta) y el
panel de operaciones lee cada una por separado. La Florida no lleva sede y para ella nada cambió.
| Sede | Enlace para instalarla | Nombre en el teléfono | Título del informe |
|---|
| La Florida | informe-francois.vercel.app | Informe François | Bitácora François |
| François PV (cafetería, pastelería y heladería) | informe-francois.vercel.app/?sede=pv | Informe François PV | Bitácora François PV |
| Maximarket PV (minimarket) | informe-francois.vercel.app/?sede=maximarket | Informe Maximarket PV | Bitácora Maximarket PV |
- La sede la dice la cuenta:
auth.users.raw_app_meta_data.sede (s2 = François PV,
s3 = Maximarket PV; los mismos códigos del Checklist). Se pone por SQL y nadie la puede cambiar
desde el teléfono. api/preinforme.js la lee de la sesión para que DeepSeek sepa de qué local
escribe (prompt.js → SEDES). Hay que ponerla antes de entrar con esa cuenta en un teléfono:
una sesión abierta antes guarda la cuenta sin sede hasta que se renueve (≈1 hora).
- El enlace solo bautiza un aparato en blanco (sin notas, informes ni personas): cambia el nombre,
el ícono instalado (
manifest-pv.json, manifest-maximarket.json) y los textos. Un aparato que ya
tiene lo suyo no cambia de sede por abrir otro enlace.
- Entrada obligatoria en una sede: primero la cuenta de la sede (la app abre Datos), después
nombre y PIN (4 números o más; la primera persona administra y no se puede saltar ese paso).
- Puesta en marcha (una vez): crear las dos cuentas en Authentication → Users → Add user con
Auto Confirm, y correr
SQL-cuentas-sede.sql (pone la sede en cada
una). Ese SQL lleva los correos de las cuentas: queda solo en el computador
(.gitignore), porque este repositorio es público. Después, en cada tablet, abrir su enlace y entrar con su cuenta.
- Nunca se mezclan dos sedes: si un aparato entra con la cuenta de otra sede, no se sube ni se
baja nada y Datos lo explica; si el aparato tenía notas, la sesión se cierra al tiro. Un aparato en
blanco que entra con la cuenta de una sede toma esa sede.
Sincronía entre el teléfono y el computador
Opcional: si entras con tu correo en Datos, las notas, los informes y las fotos suben a
Supabase y los dos aparatos muestran lo mismo. El dispositivo sigue mandando —todo se guarda
primero en local y sin señal se anota igual—; la nube es solo la copia común.
- Proyecto: el mismo de François (
zmpiqkbvmjvfvxyvcdhi), compartido con el Checklist de Turno.
Tablas propias con prefijo if_, así que no se pisan. La llave pública va en el index.html:
lo que protege los datos son las políticas RLS, que solo dejan ver las filas de tu uid.
- Puesta en marcha (una vez): correr
supabase.sql en el SQL Editor y crear el
usuario en Authentication → Users → Add user con Auto Confirm marcado.
- Cómo resuelve los choques: cada nota e informe llevan
act (cuándo se tocaron) y gana la
versión más nueva. Los borrados quedan anotados en if_borrados_v1 y se suben como
borrada = true, para que la nube no los reviva en el otro aparato.
- Fotos: bucket privado
if-fotos, una carpeta por cuenta (<uid>/<idFoto>.jpg). En la tabla, la
columna fotos lleva la lista de siempre cuando todas respaldan el hecho, y {h:[…], m:[…]}
cuando además hay fotos de la medida — así no hubo que tocar el esquema y una versión vieja de la
app sigue leyendo lo suyo. Suben las que
el otro aparato no tiene y bajan las que faltan acá (la miniatura se rehace al bajarlas). Se puede
apagar con la casilla «Sincronizar también las fotos». Las fotos que ya no usa ninguna nota se
borran de la nube.
- Qué movió la última vez: bajo el estado, en Datos, queda escrito el resumen del último
viaje — «subieron 12 notas · no bajó nada · en la nube hay 12 notas» —, porque un «al día» a
secas no distingue entre «no había nada que traer» y «no subió nada de lo mío». Copiar el
detalle copia ese resumen (y la falla, si la hubo) para mandarlo por WhatsApp.
- Sin tope de filas: las notas y los informes se bajan por tramos de 1.000 (
traerTodo). Antes
se pedían de una vez y Supabase entrega como mucho 1.000 filas por consulta (las borradas también
cuentan): pasado ese número, lo nuevo del otro aparato no habría bajado nunca.
- Cuándo sincroniza: al abrir la app, al volver a ella, al recuperar la señal, 2,5 s después de
cualquier cambio y a mano con Sincronizar ahora o el botón de la nube del encabezado.
- ⚠️ No toques la configuración de Auth del proyecto (por ejemplo, apagar los registros):
el Checklist de Turno entra con sesiones anónimas y se quedaría afuera.
Archivos
index.html — toda la app (datos, estilos, lógica). Los íconos son SVG de línea (sprite al inicio del body, función ico()), sin emojis.
prompt.js — el prompt maestro: reglas y los tres modos. Lo usan la página («Copiar prompt») y la función de DeepSeek: si cambias una regla, cámbiala aquí.
api/preinforme.js — función de Vercel que le pide el pre-informe a DeepSeek. Solo atiende a una sesión de Supabase de la app que no sea anónima; responde {informe, observaciones, modelo}. GET dice si la clave está cargada.
sw.js — service worker. Constante CACHE (hoy if-v28): súbela al cambiar assets del núcleo. HTML y JS propios network-first, el resto cache-first. Las llamadas a Supabase y a /api/ no pasan por la caché.
supabase.sql — tablas, políticas y bucket de la sincronía. Se corre una sola vez.
manifest.json (La Florida), manifest-pv.json, manifest-maximarket.json (las otras sedes), icon-192.png, icon-512.png, apple-touch-icon.png, icon-maskable-512.png, icon.svg — PWA instalable.
skill/SKILL.md — copia versionada de la skill bitacora-francois.
vercel.json / netlify.toml — deploy sin build; /sw.js se sirve con Cache-Control: no-cache y la función tiene 300 s (DeepSeek espera hasta 240 s).
servir-local.cjs — servidor para el computador (estático + /api/, como Vercel). No se publica (.vercelignore).
Correr en local
Servidor en la raíz, puerto 5189 (config informe-francois en Desktop\.claude\launch.json):
node servir-local.cjs 5189
Sin DEEPSEEK_API_KEY en el entorno, el pre-informe avisa que falta la clave; el resto de la app funciona igual.
npx serve falla en este equipo (la ruta de Node tiene un espacio): por eso el servidor propio.
⚠️ El navegador puede cachear index.html: al verificar cambios usa recarga forzada o ?v=<timestamp>.
La skill de Claude
El mismo prompt vive también como skill (bitacora-francois), instalada en
~/.claude/skills/bitacora-francois/SKILL.md. La copia versionada de este repo está en
skill/SKILL.md: si la editas aquí, cópiala a ~/.claude/skills/ para que
tome efecto.
Con la skill instalada no hace falta pegar el prompt: basta con pegar las notas (o escribir
/bitacora-francois) y Claude redacta el informe con las mismas reglas y los mismos tres modos.
El botón Copiar prompt de la app sigue siendo útil para pegarlo donde no esté la skill
—claude.ai, otro computador, el teléfono—.
Deploy
Variables de entorno (Vercel → proyecto informe-francois → Settings → Environment Variables):
DEEPSEEK_API_KEY — obligatoria para el pre-informe. La misma clave de DeepSeek que usa el panel de operaciones. Sin ella, la app avisa en Generar y todo lo demás funciona.
DEEPSEEK_MODEL — opcional. Por defecto deepseek-flash; si el modelo no existe para la clave, la función pregunta a DeepSeek cuáles hay y usa otro (primero los «flash»).
Después de agregarla hay que volver a desplegar para que la función la lea.
La URL y la llave pública de Supabase van en el index.html y en la función (es su diseño: lo que
protege los datos son las políticas RLS). Sin cuenta, las notas no salen del dispositivo.