- Profile avatar button fixed top-left on all pages (initials fallback with
hashed color when no photo set)
- Profile modal: change display name, upload photo, switch theme
- PATCH /api/users/:id updates name in D1; syncs back to currentUser
- POST /api/users/:id/avatar uploads image to R2 as avatars/{userId}.ext,
stores URL in D1 users.avatar_url (new column via ALTER TABLE)
- Author avatars shown on proposal cards, modal view, and comments
- serveMedia fixed to use R2 httpMetadata contentType and full key path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move canonical, robots, JSON-LD, sitemap link inside <head> (were incorrectly
placed after </head>, causing Google to ignore them)
- Title changed to "The Radical Party | Direct Democracy for Australia" so the
party name shows up in search results
- Description now leads with "The Radical Party" for brand recognition
- Keywords updated to include "The Radical Party", "radical party australia", CIR
- JSON-LD updated to dual WebSite + Organization schema with richer data
- Added /robots.txt endpoint (was 404) pointing crawlers to sitemap
- Added /sitemap.xml endpoint (was 404) with homepage + top 50 proposals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
worker.js:
- Store join submissions in new 'members' D1 table (in addition to email)
- Add GET /api/admin/members — full member list, password gated
- Add GET /api/admin/stats — member counts, by-state breakdown, recent
signups, top petitions, site stats; all password gated ('dictator')
admin.html:
- Password gate → full dashboard
- AEC progress bar (X/2000 with % and members-to-go)
- Stat cards: total members, this week, site users, proposals, votes
- State breakdown with mini bars
- Recent signups (last 10, NEW badge for this week)
- Top 10 petitions by signature count
- Full searchable/filterable/sortable member table with NEW badges
- CSV export (respects active search + state filter)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getComments was using createResponse default cacheDuration=86400 (24 hours).
Cloudflare was serving a stale cached response, so comments appeared empty
or outdated on every load. Set cacheDuration=0 (no-store) so each request
hits the DB fresh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SVG: clean two-zone layout — dark header with RADICAL wordmark + PETITION
badge, full-width text area with left accent bar and large quote mark,
footer band with signature count, author, date and CTA. Proper word-wrap
centred in the card. Looks like a real share card now.
Remove flag button and handleFlag from proposal cards entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /api/join worker endpoint that emails membership details to party contact
- Register /api/login route (was missing — emoji login was broken)
- Add full AEC-compliant member registration form in index.html (#join section)
- Update copyright year to 2026 in index.html and login.html
- Fix dictator/target image URLs: radical.omar-c29.workers.dev → theradicalparty.com
- Add onerror handlers to all login page images for graceful degradation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
safeFetch already returns parsed JSON and throws on non-2xx — callers were
treating the result as a Response object and checking .ok / .json(), which
made votes, comment votes, and comment posts silently fail every time.
Also: fetchProposals was missing cache:'no-store' so newly posted proposals
were invisible until the 24hr browser cache expired; getProposals was sending
24hr Cache-Control so Cloudflare CDN served stale data; getShareableImageUrl
had a double slash (/api//petition-svg); wrangler.toml had an invalid [pages]
stanza that caused a warning on every deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>