# Habits — a tracker with a real database (★★)

Build a habit tracker that works on my phone and my laptop and keeps them in step, with no login. I add habits (read 20 minutes, practise guitar, drink water), tick them each day, and see my streaks and a month grid. Everything works offline first, saved in the browser. A small Cloudflare Worker with a D1 database keeps a copy. To sync another device, I type a short sync code instead of making an account. I can export everything as a CSV file.

## Done looks like

- Add, rename, reorder and archive habits. Each habit has a name and a colour.
- Today's view: one big tick button per habit. A tap toggles it.
- A month grid per habit shows done days as filled squares. Arrows move between months.
- Current streak and best streak per habit. A streak counts back from today, or from yesterday if today is not ticked yet.
- The app works with Wi-Fi off. Ticks made offline sync when the connection comes back.
- A sync code like `K7PQ-M2XD-9R` appears in Settings. Typing it on a second device brings over every habit and tick within a few seconds. After that, changes on either device show up on the other after a reload.
- A status line says "Saved on this device" or "Synced 1 minute ago" or "Offline, will sync later".
- "Export CSV" downloads `habits.csv` with columns `habit,date,done`.
- Live at the address `npx wrangler deploy` prints. No console errors.

## Constraints

- One Cloudflare Worker with static assets: it serves the front end and the `/api/` routes. Use wrangler. Free plan only.
- Front end: plain HTML, CSS and JavaScript, no frameworks, no build step for the front end.
- Offline first: every change is written to localStorage first and the screen updates at once. Sync runs on load, two seconds after the last change, and on the `online` event.
- Sync code: 10 characters from an alphabet with no lookalikes (no 0, O, 1, I or L), made with `crypto.getRandomValues`, shown in groups. Treat it like a password: anyone with it can see and change the data. Say so in Settings.
- D1 tables: `habits(code, id, name, colour, sort, archived, updated_at)` and `checks(code, habit_id, day, done, updated_at)` with primary key `(code, habit_id, day)`. Days are local dates as `YYYY-MM-DD` text.
- Merge rule: last write wins per row by `updated_at`. Never delete rows: un-ticking sets `done = 0`, deleting a habit sets `archived = 1`, so the change syncs.
- API: `GET /api/sync/:code?since=<ms>` returns rows changed since then. `POST /api/sync/:code` takes changed rows and keeps each one only if it is newer than what is stored. The Worker checks the code format, caps a request at 500 rows and habit names at 60 characters, and uses prepared statements (never builds SQL from strings).
- No accounts, no email, no tracking, no cookies.
- Phone works: big tap targets, readable on a 360 px screen.

## Milestones

1. **Local tracker.** Add habits, tick today, all in localStorage. No server yet.
   Check: add three habits, tick two, reload: everything is still there, and it works with Wi-Fi switched off.
2. **Month grid and streaks.** The month grid and a `streaks(days)` function with a small test page. Test cases: ticks on the last 5 days gives current 5; a gap yesterday with today ticked gives current 1; today not ticked but the 3 days before ticked gives current 3.
   Check: the test page shows every case passing and the grid matches the ticks you made.
> Milestones 1 and 2 are a complete tracker for one device. Use it for a few days before you build the sync. You will know better what you want from it.
3. **A Worker on your computer.** Set up the Worker project with wrangler, serving the existing files from static assets, plus `GET /api/health` returning `ok`. Run it locally.
   Check: the app opens from the local wrangler address and `/api/health` shows `ok`.
4. **D1 and the sync API.** Create the D1 database and the two tables with a migration file. Write the two sync routes with the merge rule.
   Check: a small script posts two rows, then gets them back with `since=0`; posting an older copy of a row does not overwrite the newer one.
5. **Sync codes.** Make a code on first sync and show it in Settings. An "Enter a code" box on another device pulls everything for that code and switches to it.
   Check: in a second browser, type the code: all habits and ticks appear.
6. **Offline first sync.** Queue changes made offline, send them when back online, show the status line.
   Check: turn Wi-Fi off on the phone, tick a habit, turn it back on: within a few seconds the status says "Synced" and the laptop shows the tick after a reload.
7. **Export and go live.** "Export CSV" with a `Blob` download. Deploy with `npx wrangler deploy`.
   Check: the live address works on your phone over mobile data, and the CSV opens in a spreadsheet with one row per tick.

## Stretch

- Install it to the phone home screen with a web app manifest and a service worker, so it opens offline from an icon.
- Weekly goals ("3 times a week") with their own kind of streak.
- A year view heatmap, one small square per day.
- Sync codes as four short words instead of characters, from a word list built into the app.
- A "rotate code" button that copies the data to a new code and deletes the old one.

## Leave out

- Accounts, email, passwords, social login.
- Push notifications from a server.
- Charts libraries, frameworks, analytics.

Start by reading WRITING.md and the guide chapter on data if you need storage.
