Registra gastos con monto, foto de la boleta y quién aprueba.
Dos pantallas: la tuya, donde registras lo que gastaste y ves en qué va, y la de quien aprueba, que es la misma app filtrada por su correo.
Corre en sube, que le pone la base de datos, el dominio, el TLS y el inicio de sesión. La app no trae código de autenticación.
Aquí no hay escritura anónima como en el registro de visitas: todo el mundo tiene sesión. Lo interesante es otra cosa, y es quién puede aprobar.
La plataforma sabe quién eres. No sabe quién es tu jefatura, y no debería — eso cambia por empresa, por proyecto y a veces por monto. Así que la app no inventa roles: quien gasta escribe el correo de quien firma, y la cola de aprobación es literalmente eso.
// app/aprobaciones/page.tsx — la regla de acceso es el filtro
where(and(eq(expenses.approverEmail, user.email), eq(expenses.status, 'pending')))Es cómo funcionaba el formulario en papel, y tiene una propiedad que un flag de
is_admin no tiene: no existe un estado donde alguien vea gastos que nadie le
mandó. La acción que aprueba vuelve a comprobar lo mismo, así que mandar el id
de un gasto ajeno a mano no calza con ninguna fila.
Lo que sí falta, y vale decirlo: nada impide que escribas tu propio correo como aprobador. Si eso importa en tu empresa, es una línea en la acción — pero es una regla tuya, no de la plataforma.
Los montos se guardan como enteros de pesos:
amount: integer('amount').notNull()El peso chileno no tiene centavos, así que el peso ya es la unidad mínima y la plata nunca pasa por un redondeo. Una app en otra moneda debería guardar centavos igual — un entero de la unidad más chica — y no un número flotante.
Las fotos van al almacenamiento de la app y se muestran con un link firmado que expira:
await storage.put(`boletas/${id}`, bytes, file.type)
const url = await storage.temporaryUrl(key)La app no tiene credenciales del almacenamiento. Pide el link a la plataforma, que decide a qué archivos puede llegar esta app: una llave que nombre el archivo de otra app no se firma, diga lo que diga este código.
npm install
npm run buildConstruye sin base de datos. Para verlo con datos hay que desplegarlo:
npm i -g @sube.sh/cli # o usa npx
sube new "Control de gastos"
sube deployapp/page.tsx mis gastos y el formulario
app/aprobaciones/page.tsx la cola de quien aprueba
src/db/schema.ts una tabla: expenses
middleware.ts y next.config.ts los pone la plataforma. AGENTS.md explica
las reglas que un asistente de IA debe respetar al modificar esta app.
MIT.