Hábitos – Habit & Therapy Tracker
2026Shared habit and therapy progress tracker for a patient and their therapist. Full-stack Next.js 16 app with role-based interfaces, a weekly habit calendar, goal tracking with clinical observations, and a job application tracker with a behavioral accountability gate — under custom JWT auth with Prisma/PostgreSQL.
Hábitos is a full-stack habit and therapy tracking app built for a specific real-world scenario: a therapy patient managing daily medications, sleep schedule, self-care routines, and an active job search — with their therapist needing structured visibility into weekly progress without requiring a separate communication channel. The patient interface is a weekly calendar with fixed daily habits (medications, wake-up time, bedtime) and custom tasks; the therapist interface adds a Planejador view that shows each goal's patient notes alongside a space for clinical observations and planned adjustments for the following week.
Therapy patients typically track progress through verbal check-ins or ad-hoc shared documents with no structure for daily habits, goal history, or week-over-week comparisons. A patient managing medications, sleep patterns, and a job search simultaneously has too many moving parts for informal tracking. The therapist has no passive visibility into the patient's week — they see only what the patient remembers to report in a session.
Build a two-user shared app that: (1) gives the patient a structured weekly calendar with habit accountability; (2) gives the therapist a read-only view of goal progress with space for clinical observations; (3) enforces behavioral accountability through the job application gate; (4) persists session state across page refreshes without a third-party auth library; (5) deploys with zero-downtime schema migrations on Vercel.
- ◈Solo developer — architecture, full-stack, UX, data modeling, deployment
- 01Next.js App Router Server Components as the data-fetching layer — pages query Prisma directly and pass data as props to Client Components. This eliminates client-side loading states for initial renders and keeps auth verification server-side, so an unauthenticated request is redirected at the server before any HTML is sent.
- 02Custom JWT auth with jose (no NextAuth) — sessions are HS256 JWTs in an httpOnly, SameSite=Lax cookie with a 24h TTL. The decision to build auth from scratch was deliberate: the app has exactly two users and a third-party auth library would add unnecessary abstraction and configuration surface.
- 03Prisma migrate deploy runs automatically on every Vercel build — the build script is prisma generate && prisma migrate deploy && next build, ensuring the production schema is always in sync with code without a manual migration step.
- 04GoalProgress upsert pattern — rather than separate patient and therapist records per goal per week, a single GoalProgress row holds both patientNotes and therapistNotes. The /api/progresso endpoint does a findOrCreate by (goalId + weekStart), so both users write to the same record without coordination.
- 05After-midnight time sorting — times 00:00–05:59 get a sort key of h + 24, so a 02:00 bedtime entry correctly appears after 23:59 in the schedule view. Without this, night-owl sleep habits would be grouped in the early-morning block.
- 06Bulk week-level wake/sleep setter — a single PATCH /api/semanas/[scheduleId] uses prisma.task.updateMany to propagate a time to all 7 days at once, avoiding 7 separate API calls from the client.
- 07Job application gate enforced at the data level — the job task cannot be marked complete until 3 applications have been logged for that day. The completion button renders disabled with a counter ('X vagas restantes'). Behavioral accountability is baked into the data model, not just the UI.
- 08dnd-kit installed and Task.sortOrder scaffolded but not yet wired — drag-and-drop libraries are in package.json and the Task model has a sortOrder integer with full PATCH support, deliberately scaffolded for a future task reordering feature without adding complexity to the current release.
- 09iOS Safari font-size fix applied globally — all text inputs have style={{ fontSize: '16px' }} set inline, the only reliable way to prevent iOS Safari's auto-zoom on focus from breaking the mobile calendar layout.
- →Building JWT auth from scratch with jose is not significantly harder than wiring NextAuth for a small, closed-user app — and it produces a much simpler mental model of how sessions actually work, making debugging auth issues straightforward.
- →The GoalProgress upsert pattern (find-or-create by composite key) is clean for co-authoring records across roles, but requires the API to be truly idempotent — calling it twice for the same (goalId, weekStart) must not overwrite the other role's field, which means using update with only the field that changed, not a full object replace.
- →Running Prisma migrate deploy in the Vercel build command is reliable for zero-downtime schema evolution, but requires all migrations to be backwards-compatible — a destructive migration (e.g. dropping a column) that passes locally will block the production deploy with no automatic rollback.
- →Designing for a specific real user — not a persona — produces much more intentional UX. The after-midnight sort, the bulk week setter, and the job application gate all came directly from observing the actual patient's workflow rather than from abstract product thinking.
- →Server Components work best when the data shape matches the render tree closely. Any mismatch — needing to filter or transform data on the client — is better solved by returning more data from the server and filtering in the Client Component, not by adding extra API routes.
Active — in daily use by the patient and therapist it was built for