Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,33 @@ Mapbox does **not** work with SSR. Importing `mapbox-gl` in a code path that run
**Required pattern:** load the map UI only on the client with `next/dynamic` and **`{ ssr: false }`**. Never add a Server Component that imports `mapbox-gl` or a module that imports it at top level without this guard.

**In this repo:** `components/HomePage.tsx` wraps `MapCanvas` in `dynamic(() => import("@/components/map/MapCanvas"), { ssr: false, ... })`. Keep new Mapbox code behind that boundary (or another `dynamic(..., { ssr: false })`).

## Cursor Cloud specific instructions

### Services overview

**pgtruth** is a Next.js 16 (App Router) crowdsourced PG-review app with a Mapbox map. The backend is Supabase (PostgreSQL + service role key). There is no Docker setup or local database — all persistence goes through the Supabase REST API.

### Running the app

- `npm run dev` — starts the dev server on port 3000 (uses `--webpack` flag).
- `npm run build` — production build; also validates TypeScript.
- `npm run lint` — ESLint 9 with `eslint-config-next`. Pre-existing warnings/errors exist in the repo; 4 errors and 13 warnings as of initial setup — do not treat those as regressions.
- `npm test` — Vitest, runs 10 test files (53 tests) in `lib/**/*.test.ts`. Pure unit tests, no external services needed.

### Required secrets (set via Cursor Secrets)

| Secret | Required for |
|---|---|
| `NEXT_PUBLIC_SUPABASE_URL` | All data loading / API routes |
| `SUPABASE_SERVICE_ROLE_KEY` | Server-side writes (review submission) |
| `NEXT_PUBLIC_MAPBOX_TOKEN` | Map rendering |

Without real Supabase + Mapbox tokens the app starts but shows fallback UI (gray map, "Can't load reviews" banner). Lint and tests work without any secrets.

### Gotchas

- The lockfile is `package-lock.json` — use **npm**, not pnpm/yarn.
- `next dev` uses `--webpack` (not Turbopack); the build script does too.
- The `middleware.ts` file triggers a deprecation warning ("use proxy instead") — this is expected with Next.js 16 and can be ignored.
- `.env.local` is gitignored; create it from `env.example` and fill in real keys.
5 changes: 5 additions & 0 deletions lib/place-rent-label.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ describe("formatMapPinRentLine", () => {
const s = formatMapPinRentLine([row(8000), row(28000)]);
expect(s).toContain("–");
});

it("rev count reflects total reviews, not just those with rent data", () => {
const s = formatMapPinRentLine([row(10000), row(0), row(15000)]);
expect(s).toContain("3 rev");
});
});
3 changes: 1 addition & 2 deletions lib/place-rent-label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ export function formatMapPinRentLine(reps: ReportRow[]): string {
const med = Math.round(medianSorted(sorted));
const min = sorted[0]!;
const max = sorted[sorted.length - 1]!;
const n = rents.length;

const medPart = `Med ${ruShort(med)}/mo · ${n} rev`;
const medPart = `Med ${ruShort(med)}/mo · ${reps.length} rev`;
const spread = max - min;
const wide =
spread > Math.max(4000, med * 0.12) && min !== max;
Expand Down