Public: anyone can read this; the crew can change it.

Print a QR code on the flyer and the open house sign-in sheet that opens the listing's property page #98

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The flyer and the open house sign-in sheet now carry a scannable QR code, labelled "Scan to see the home". Under it is the property page URL in words (`/homes/:id` on the public origin).

shipped production is serving it deploy 957b843

opened this

The flyer and the open house sign-in sheet now carry a scannable QR code, labelled "Scan to see the home". Under it is the property page URL in words (/homes/:id on the public origin).

I ran qr, scan, signin, speed, design and a11y tests (40 pass), scripts/lint-design.ts (output not checked), and bun scripts/gate.ts and scripts/design-lint.ts don't exist in this clone. I did not run the full suite, and the other tests that touch the flyer, such as property and walkthrough, passed in an earlier run. bun x tsc --noEmit only prints help here because the repository has no tsconfig.json, so there was no type check.

  • src/qr.ts (new, no dependencies): it turns a string into a QR module matrix (byte mode, error correction M, smallest version that fits, versions 1–10, up to 213 bytes). qrSvg() renders it as a single black-and-white path with a four-module quiet zone. Only numbers go into the markup, and the aria-label is stripped to letters, digits and spaces.
  • src/pages/scan.ts (new):

- Code: scanBlock(listing, origin) is used by both the flyer and the sign-in sheet. It encodes the same absolute URL as "Property page" on the listing. - Not public yet: until the MLS description is approved, the block says "The property page is not public yet: approve the MLS description…" instead of a code. - Replaces: it takes the place of the old "See every photo: …" line on the flyer.

  • Wiring: the sign-in route now passes publicOrigin, and static/app.css gets the .scan styles (the old .flyer-link rule is gone).
  • README: the "For the agent" print-the-flyer step describes the code, and the file table lists src/qr.ts.

Tests:

  • src/qr.test.ts: the fixture is the matrix for the production URL …/homes/42, produced by the npm qrcode package (soldair/node-qrcode). I installed that package in a scratch directory only to make the fixture; nothing was added to the repository. The tests also check the finder, separator and timing patterns, the format bits, version selection and the version info.
  • src/scan.test.ts: both pages carry the SVG, label and URL once the description is approved, and the not-public note before.
  • Cross-check: 104 of 105 URLs from 30 to 210 bytes matched qrcode module for module. In the one mismatch the two encoders presumably chose different masks, since every scanner reads either.
  • Second reference: I also compared against qrcode-generator. It scores masks by a non-spec heuristic, but it matched every URL once forced to the same mask, which confirms the data, error correction and layout through version 10.

Not done: I did not add versions above 10, or other error-correction levels or encoding modes. I also did not add a QR to the property page itself or to the email.

To try it, open a listing with an approved MLS description, then press Print flyer or open sign-in (which needs an open house), and scan the code with a phone.

🤖 Generated with Claude Code

Closes #83

commented

The flyer and the open house sign-in sheet now carry a scannable QR code, labelled "Scan to see the home". Under it is the property page URL in words (/homes/:id on the public origin).

I ran qr, scan, signin, speed, design and a11y tests (40 pass), scripts/lint-design.ts (output not checked), and bun scripts/gate.ts and scripts/design-lint.ts don't exist in this clone. I did not run the full suite, and the other tests that touch the flyer, such as property and walkthrough, passed in an earlier run. bun x tsc --noEmit only prints help here because the repository has no tsconfig.json, so there was no type check.

  • src/qr.ts (new, no dependencies): it turns a string into a QR module matrix (byte mode, error correction M, smallest version that fits, versions 1–10, up to 213 bytes). qrSvg() renders it as a single black-and-white path with a four-module quiet zone. Only numbers go into the markup, and the aria-label is stripped to letters, digits and spaces.
  • src/pages/scan.ts (new):

- Code: scanBlock(listing, origin) is used by both the flyer and the sign-in sheet. It encodes the same absolute URL as "Property page" on the listing. - Not public yet: until the MLS description is approved, the block says "The property page is not public yet: approve the MLS description…" instead of a code. - Replaces: it takes the place of the old "See every photo: …" line on the flyer.

  • Wiring: the sign-in route now passes publicOrigin, and static/app.css gets the .scan styles (the old .flyer-link rule is gone).
  • README: the "For the agent" print-the-flyer step describes the code, and the file table lists src/qr.ts.

Tests:

  • src/qr.test.ts: the fixture is the matrix for the production URL …/homes/42, produced by the npm qrcode package (soldair/node-qrcode). I installed that package in a scratch directory only to make the fixture; nothing was added to the repository. The tests also check the finder, separator and timing patterns, the format bits, version selection and the version info.
  • src/scan.test.ts: both pages carry the SVG, label and URL once the description is approved, and the not-public note before.
  • Cross-check: 104 of 105 URLs from 30 to 210 bytes matched qrcode module for module. In the one mismatch the two encoders presumably chose different masks, since every scanner reads either.
  • Second reference: I also compared against qrcode-generator. It scores masks by a non-spec heuristic, but it matched every URL once forced to the same mask, which confirms the data, error correction and layout through version 10.

Not done: I did not add versions above 10, or other error-correction levels or encoding modes. I also did not add a QR to the property page itself or to the email.

To try it, open a listing with an approved MLS description, then press Print flyer or open sign-in (which needs an open house), and scan the code with a phone.

🤖 Generated with Claude Code

Changes requested — before the latest push

The QR work looks right: src/qr.ts is zero-dependency, the fixture comes from a named external encoder, both pages show the SVG, and the not-public case is handled. One thing blocks it.

Fix first: the diff adds 23 empty stray files at the repo root. They are .bash_profile, .bashrc, .gitconfig, .gitmodules, .idea, .mcp.json, .profile, .ripgreprc, .vscode, .zprofile, .zshrc, and .claude/{agents,commands,hooks,launch.json,loop.md,output-styles,routines,scheduled_tasks.json,settings.json,settings.local.json,skills,workflows}. Each is a 0-byte new file with nothing to do with the issue. An empty .gitmodules or .gitconfig in the tree can change how git or tooling behaves. An empty .claude/settings.json and .claude/hooks sit in a path the agent tooling reads, and the tracked .mcp.json is an agent-config file that does not belong in the repo. Remove them all with git rm so the diff holds only README.md, src/pages/{listing,scan,signin}.ts, src/qr.ts, src/qr.test.ts, src/scan.test.ts, src/routes.ts and static/app.css. Check that nothing re-adds them.

Also, the README step 5 line is now over-long and the last sentence ("Print it, or save it as a PDF. Rewrite all...") runs past the wrapped width. Re-wrap it.

I found nothing else: no new door, no contract cell loosened, output escaped (the aria-label is stripped, the URL goes through escape).

commented

The pull now holds only the QR work. The 23 empty files are gone from the diff, and I re-wrapped README step 5. The diff against main is these nine files: README.md, src/pages/{listing,scan,signin}.ts, src/qr.ts, src/qr.test.ts, src/scan.test.ts, src/routes.ts and static/app.css.

  • Stray files: I removed the 23 files from the index (git rm --cached) and committed that. They still exist in my working tree as untracked, 0-byte files, probably placeholders my sandbox creates, and I couldn't delete them. They show as ?? and were never staged again. If a later push brings them back, they come from the sandbox, not from the code. Adding .claude/, .mcp.json and the dotfiles to .gitignore would stop them for good. I didn't, because that is outside this issue.
  • README: the last sentence of step 5 ("Print it, or save it as a PDF. Rewrite all…") now wraps within the width of the lines around it.
  • Checks:

- bun test src/qr.test.ts src/scan.test.ts passes, 8 tests. - scripts/gate.ts doesn't exist in this clone, so I didn't run it. - bun x tsc --noEmit only printed its help text, so it didn't type-check anything. I don't know why; I didn't investigate.

  • Not changed: no contract cell was loosened.
Approved

Both earlier asks are met: the 23 empty dotfiles are out of the pull (they are sandbox placeholders, not tracked files), and README step 5 is re-wrapped. The QR encoder, the scan block and the tests are as I read them before. Output is escaped, the aria-label is stripped, and no contract cell is loosened. Non-blocking: .gitignore could cover the sandbox placeholder files.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs