28 Commits
Author SHA1 Message Date
westfarn a867a57750 Show campaign QR codes for email as well as postcard (#14)
Deploy Beta / unit-tests (push) Successful in 21s
Deploy Beta / docker (push) Successful in 29s
Deploy Beta / deploy-beta (push) Successful in 6m33s
## Summary
Closes #13.
- Draw the tracked-link QR for email campaigns as well as postcard (copy / download PNG).
- Keep the QR hidden for SMS.
- Fix `.utm-qr { display: flex }` overriding the HTML `hidden` attribute so SMS does not leak an empty canvas.

## Test plan
- [ ] Open campaign composer on Email — QR renders for the short `piha.li` URL
- [ ] Copy QR image / Download PNG work
- [ ] Switch to SMS — QR box is gone
- [ ] Switch to Postcard — QR still renders; postcard-designer hint still shows
- [ ] Email campaign report page also shows a filled QR

Reviewed-on: #14
2026-09-01 11:51:57 -07:00
westfarn bc7c47a69c Mint short links on piha.lc / beta.piha.li (#12)
Deploy Beta / unit-tests (push) Successful in 21s
Deploy Beta / docker (push) Successful in 24s
Deploy Beta / deploy-beta (push) Successful in 2m4s
## Summary
- Point `SHORTENER_BASE_URL` at `https://piha.lc` (prod) and `https://beta.piha.li` (beta).
- Stop documenting `shortener.aimloperations.com` as the mint host.

Closes #11.

Depends on url_shortening_service serving `/api/` on the short host.

## Test plan
- [ ] Set `SHORTENER_BASE_URL=https://beta.piha.li` and matching token in beta env
- [ ] Compose a campaign SMS and confirm mint hits beta.piha.li (not shortener-beta)
- [ ] Body contains `https://beta.piha.li/<code>`

Reviewed-on: #12
2026-08-30 17:27:51 -07:00
westfarn 2b3f10e784 Mint campaign UTM links through the piha.li shortener (#10)
Deploy Beta / unit-tests (push) Successful in 21s
Deploy Beta / docker (push) Successful in 26s
Deploy Beta / deploy-beta (push) Successful in 5m20s
## Summary
Closes #9.

- Campaign composer auto-inserts a tracked homepage link (`utm_source=monica`, `utm_medium` = channel, `utm_campaign` = slug of the name) for email, SMS, and postcard QR — no manual UTM paste.
- When `SHORTENER_BASE_URL` + `SHORTENER_API_TOKEN` are set, the app mints that long HTTPS URL via `POST /api/links/` on the shortener **API host** and puts the returned `piha.li` / `beta.piha.li` short URL in SMS, email hrefs, and QR codes. Empty env (local) falls back to the long UTM URL.
- Live composer resolves shorts through a portal JSON endpoint (login + CSRF). Browser never calls the shortener.

## Secrets (control node, not git)

**monica_site** (`~/Documents/secrets/monica_site/`):
```
# prod
SHORTENER_BASE_URL=https://shortener.aimloperations.com
SHORTENER_API_TOKEN=monica:<secret>

# beta
SHORTENER_BASE_URL=https://shortener-beta.aimloperations.com
SHORTENER_API_TOKEN=monica:<beta-secret>
```

**url_shortening_service** (same secret, named token):
```
SHORTENER_API_TOKENS=monica:<secret>
SHORT_ALLOWED_HOSTS=mkdrealtor.com,aimloperations.com
```
Prod public short host: `piha.li`. Beta: `beta.piha.li`. Generate with `python -c "import secrets; print(secrets.token_urlsafe(32))"`.

Template port: westfarn/web_django_template#3

## Test plan
- [ ] `cd site && uv run python manage.py test messaging.tests.CampaignUtmLinkTests`
- [ ] Composer: type a campaign name — email gets an HTML link, SMS gets a URL, postcard shows a QR
- [ ] With shortener env set: SMS/QR show `piha.li` (or `beta.piha.li`); without it, long UTM URL still works
- [ ] Copy/download QR into postcard designer
- [ ] Secret files have `SHORTENER_*` on both caller and operator sides before beta/prod deploy

Reviewed-on: #10
2026-08-30 13:24:40 -07:00
westfarn dc0f3b2908 Improve public Lighthouse scores (#7) (#8)
Deploy Beta / unit-tests (push) Successful in 16s
Deploy Beta / docker (push) Successful in 22s
Deploy Beta / deploy-beta (push) Successful in 5m20s
## Summary
Closes #7.

Raise public marketing page Lighthouse scores (Performance / Accessibility / Best Practices / SEO) on `/`, `/about/`, and `/contact/`.

### Baseline (preview, mobile)
| Page | Perf | A11y | BP | SEO |
|------|------|------|----|-----|
| `/` | 70 | 80 | 82 | 100 |
| `/about/` | 71 | 81 | 82 | 100 |
| `/contact/` | 74 | 90 | 82 | 100 |

### Changes
- **Accessibility:** contrast overrides, nav `aria-label`s, footer/contact heading order (no skipped levels).
- **Best practices:** replace theme `unload` listeners with `pagehide` (also helps bfcache); `WHITENOISE_MAX_AGE` for hashed static.
- **Performance:** WebP heroes/headshot/breadcrumbs + preload; Google Fonts `display=swap` + fewer weights; async icon `fonts.css`; bundled/minified vendor CSS; lazy second swiper slide; remove above-fold WOW delay on home hero.
- **SEO:** already 100; kept structured data / meta.

### Local verify (dev server — no gzip, so Perf is not representative)
A11y / Best Practices / SEO all **100** on the three pages after fixes.

Perf will look better on beta/preview (WhiteNoise compression + CDN/proxy) than on `runserver`. Remaining Perf headroom is mostly the theme’s large `core.min.js` / unused CSS and infra HTTP/2.

## Test plan
- [ ] Deploy/preview branch and re-run Lighthouse on `/`, `/about/`, `/contact/`
- [ ] Spot-check home hero, about headshot, contact form + mobile nav toggles
- [ ] Confirm icons still appear after async `fonts.css` load
- [ ] Confirm swiper slide 2 background appears when navigating slides

Reviewed-on: #8
2026-08-27 10:49:10 -07:00
westfarn e8611ef926 Social composer: send now and rename messy account names (#6)
Deploy Beta / unit-tests (push) Successful in 16s
Deploy Beta / docker (push) Successful in 20s
Deploy Beta / deploy-beta (push) Successful in 3m11s
## Summary
Closes #5.
- Composer can **Send now** without a schedule time (Schedule and Save draft still available)
- Account names wrap and show the platform id so a bad OAuth label is still readable
- Social accounts page can rename the display name; re-auth keeps a custom name instead of overwriting it with a placeholder
- Worker polls `dispatch_due` every 15s so scheduled campaigns and social posts fire without a manual command; local `docker compose up` now starts the worker

## Test plan
- [ ] Open Social → Compose: confirm **Send now**, **Schedule**, and **Save draft** buttons (no combined Save / publish)
- [ ] Send now with a connected account and caption → post queues immediately, no datetime required
- [ ] Schedule with a datetime → post is scheduled; Schedule with a blank time → validation error
- [ ] Composer account list shows full name plus external id, wrapping if the name is long
- [ ] Social accounts → rename a messy label → new name shows in composer without reconnecting
- [ ] Re-authorize an account that was renamed → custom name is kept
- [ ] Rebuild and start compose (`docker compose up --build`) so the **worker** service is running
- [ ] Schedule a social post ~2 minutes ahead → after due time, worker logs `Enqueued N due item(s).` and the post leaves Scheduled (published or failed)
- [ ] `docker compose logs -f worker` shows `Polling due scheduled work every 15s.`

Reviewed-on: #6
2026-08-24 09:47:58 -07:00
westfarn 71de919c34 Update public About bio, address, and headshot (#2)
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m57s
## Summary
Closes #1.

- Replace About page bio with the western-suburbs / Exit Realty Redefined copy and Monica's headshot
- Add `CONTACT_ADDRESS` (`1245 Butterfield Rd. #100, Wheaton, IL 60189`) and show it on Contact, nav, footer, and structured data
- Align Home hero + service-area defaults with Chicago's western suburbs

## Test plan
- [ ] `/about/` shows new bio and headshot
- [ ] `/contact/` map block shows Butterfield Rd address
- [ ] Header / footer on Home show the address
- [ ] Set `CONTACT_ADDRESS` in deploy `.env` if not relying on defaults
- [ ] Confirm OG image for About uses the new headshot

Reviewed-on: #2
2026-08-20 03:35:33 -07:00
westfarn 46abaa6917 Add public PageView tracking for portal analytics (#4)
Deploy Beta / unit-tests (push) Successful in 12s
Deploy Beta / docker (push) Successful in 22s
Deploy Beta / deploy-beta (push) Successful in 5m40s
## Summary
Closes #3.

- Add `PageView` model + migration for successful public marketing GET hits
- Wire `PublicPageViewMiddleware` (skips portal/admin/accounts/api/static/health/etc.; fails soft on DB errors)
- Portal analytics report: last-30-day views + top pages table
- Admin registration and tests

## Test plan
- [ ] Apply migration `analytics.0002_pageview`
- [ ] Hit `/` and `/about/` → rows in `PageView` / admin
- [ ] Hit `/healthz/`, `/portal/`, `/admin/` → no new pageviews
- [ ] Open portal Analytics → views count + Top pages look right
- [ ] `uv run python site/manage.py test analytics.tests`

Reviewed-on: #4
2026-08-20 03:34:56 -07:00
westfarn 30ce58e610 Add Meta Facebook Login OAuth and social media attach/publish.
Deploy Beta / unit-tests (push) Successful in 14s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m32s
Connect Facebook and Instagram via shared portal Meta App credentials (Facebook Login for Business), and let compose attach DB-stored images/videos with live preview and platform publish.
2026-08-11 14:45:23 -05:00
westfarn c8dacd0d94 Reject invalid SOCIAL_TOKEN_ENCRYPTION_KEY instead of 500ing.
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 18s
Deploy Beta / deploy-beta (push) Successful in 1m39s
Placeholder Fernet values like replace-with-fernet-key now raise a clear SocialCryptoError in the portal, and prod env examples no longer ship a fake key.
2026-08-11 07:59:36 -05:00
westfarn 44d300271a Add LinkedIn OAuth connect with portal-saved app credentials.
Deploy Beta / unit-tests (push) Successful in 12s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m41s
Replace manual token paste with Authorization Code flow; store Client ID/secret in the DB from the social accounts UI and surface a copyable callback URL for LinkedIn Auth setup.
2026-08-11 07:52:10 -05:00
westfarn 2643ba7a96 Require a complete PCM return address before placing orders.
Deploy Beta / unit-tests (push) Successful in 14s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m40s
Empty PCM_RETURN_ADDRESS was sending SITE_NAME as firstName with blank street fields, which PCM rejects with 400; validate early and wire PCM env through compose.
2026-08-11 05:52:39 -05:00
westfarn 7fb5317308 Fix PCM postcard orders to use POST /order/postcard.
Deploy Beta / unit-tests (push) Successful in 12s
Deploy Beta / docker (push) Successful in 18s
Deploy Beta / deploy-beta (push) Successful in 1m38s
Plain POST /order 404s at PCM; product-specific path is required for sandbox and production sends.
2026-08-10 14:47:10 -05:00
westfarn dde5b96c62 Fix SMTP2GO SMS send payload for current API schema.
Deploy Beta / unit-tests (push) Successful in 12s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m14s
Use destination/content with E.164 numbers and surface provider error bodies so 400s are actionable.
2026-08-10 12:56:18 -05:00
westfarn 4becb90d66 Include file and line number in application log formats.
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m37s
Makes Grafana and container logs easier to trace back to the emitting source line.
2026-08-10 12:43:58 -05:00
westfarn ee28dcabab Unify SMTP2GO email and SMS into one webhook URL.
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 18s
Deploy Beta / deploy-beta (push) Successful in 1m38s
Classify payloads on /webhooks/smtp2go/ so one SMTP2GO webhook covers both channels under the 10-webhook limit; keep /email/ and /sms/ as aliases and harden SMS event matching.
2026-08-10 11:06:37 -05:00
westfarn f7f3174e1c Track email opens and clicks on recipient status.
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m39s
SMTP2GO open/click webhooks now advance Message to opened/clicked, and the campaign engagement chart uses live stats instead of placeholder bars.
2026-08-10 10:24:26 -05:00
westfarn 8680c082fe Improve campaign personalization and contact duplicate handling.
Deploy Beta / unit-tests (push) Successful in 13s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 1m44s
Add merge tags for email/SMS, paginated removable recipients, PCM event panels for postcard campaigns, and a portal modal when phone/address matches an existing contact.
2026-08-10 09:47:04 -05:00
westfarn 58258f2875 Instrument webhooks, fix compose layout, and ship social connect.
Deploy Beta / unit-tests (push) Successful in 10s
Deploy Beta / docker (push) Successful in 15s
Deploy Beta / deploy-beta (push) Successful in 1m40s
Add Grafana-friendly webhook request logging, Quill overflow fix, New Contact flow, in-place postcard campaign compose, and functional social account connect + composer.
2026-08-10 05:15:49 -05:00
westfarn b3a6ee0cd0 Improve outreach compose, contact merge, and email assets.
Deploy Beta / unit-tests (push) Successful in 10s
Deploy Beta / docker (push) Successful in 14s
Deploy Beta / deploy-beta (push) Successful in 1m40s
Add a Quill email editor with DB-backed image storage, selectable PCM designs, postcard defaults for addressed contacts, and merge-by-phone/address on the contact form.
2026-08-09 10:42:22 -05:00
westfarn d3db6eed76 Document compose-safe DEFAULT_FROM_EMAIL quoting.
Deploy Beta / docker (push) Successful in 14s
Deploy Beta / unit-tests (push) Successful in 8s
Deploy Beta / deploy-beta (push) Successful in 1m39s
Docker Compose .env rejects "Name" <addr>; prefer a bare address or one pair of quotes around the whole From value.
2026-08-09 07:23:47 -05:00
westfarn 5b53ef376d Show SITE_NAME as the email From display name.
Deploy Beta / unit-tests (push) Successful in 9s
Deploy Beta / docker (push) Successful in 14s
Deploy Beta / deploy-beta (push) Failing after 1m20s
Bare DEFAULT_FROM_EMAIL addresses were rendering as "noreply" in inboxes; wrap them as "Monica Dhillon" <address>.
2026-08-09 06:54:20 -05:00
westfarn aed84b6e11 Fix email send crash when worker lacks staticfiles manifest.
Deploy Beta / unit-tests (push) Failing after 25s
Deploy Beta / docker (push) Skipped
Deploy Beta / deploy-beta (push) Skipped
Fall back to an unhashed logo URL if ManifestStaticFilesStorage misses, and run collectstatic in the worker entrypoint.
2026-08-09 06:46:07 -05:00
westfarn d830f07757 Send branded HTML emails and harden SMTP2GO webhooks.
Deploy Beta / docker (push) Successful in 15s
Deploy Beta / deploy-beta (push) Successful in 1m36s
Deploy Beta / unit-tests (push) Successful in 9s
Fix campaign stuck on sending under async queue, and stop UUID ValidationError when SMTP2GO tests send "Headers Unavailable".
2026-08-09 06:37:20 -05:00
westfarn 7496af72d0 Fix PCM auth: login for API token and multi webhook secrets.
Deploy Beta / unit-tests (push) Successful in 8s
Deploy Beta / docker (push) Successful in 14s
Deploy Beta / deploy-beta (push) Successful in 1m34s
PCM DirectMail v3 needs POST /auth/login (apiKey+apiSecret) before design/order calls; accept each subscription's copy-only signature secret via PCM_WEBHOOK_SECRETS.
2026-08-09 06:02:32 -05:00
westfarn 1ef6624a8b Add Terms of Service and tighten mobile public polish.
Deploy Beta / unit-tests (push) Successful in 9s
Deploy Beta / docker (push) Successful in 13s
Deploy Beta / deploy-beta (push) Successful in 1m10s
Route Cookie Preferences to /terms/, link analytics banner there, teal mobile nav, and give the contact form more side padding on small screens.
2026-08-08 09:12:11 -05:00
westfarn 7f3bac7da3 Improve public SEO and align chrome with EXIT Realty teal.
Deploy Beta / unit-tests (push) Successful in 8s
Deploy Beta / docker (push) Successful in 13s
Deploy Beta / deploy-beta (push) Successful in 1m0s
Add meta/canonical/OG, robots/sitemap, and contact-form admin email; restyle consent, icons, and scroll-top away from theme blue.
2026-08-08 08:50:59 -05:00
westfarn 40f2555ee9 Fix collectstatic crash from missing CSS-referenced images.
Deploy Beta / unit-tests (push) Successful in 9s
Deploy Beta / docker (push) Successful in 14s
Deploy Beta / deploy-beta (push) Successful in 47s
Whitenoise Manifest storage failed post-processing style.css when bg-pattern-boxed.png and related assets were absent, so containers never reached gunicorn.
2026-08-08 07:55:14 -05:00
westfarn 1f7d78de64 Add Django site, Docker packaging, and beta/prod Gitea deploys.
Deploy Beta / unit-tests (push) Successful in 9s
Deploy Beta / docker (push) Successful in 17s
Deploy Beta / deploy-beta (push) Successful in 2m31s
Unignore site/ (was blocked by mkdocs /site rule), add compose/Docker/uv tooling, and split deploys so push to main goes to beta while prod stays manual.
2026-08-08 07:32:55 -05:00