You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: accessibility, SEO, and profile reporting (closes#771, #773, #781, #782)
## Summary
Resolves four issues across accessibility, SEO, and moderation:
### #781 — Add aria-labels to all icon-only buttons
- profile-card.tsx: copy-wallet-address button already had aria-label; confirmed correct
- qr-code-button.tsx: trigger button and close button have aria-label attributes
- share-button.tsx: aria-label reflects current state (copy vs. share)
- rss-feed-button.tsx: aria-label="Copy RSS feed URL"
- theme-toggle.tsx: dynamic aria-label switches between dark/light mode
- dashboard/page.tsx: edit/delete milestone buttons and webhook action buttons
all carry descriptive aria-label values (e.g. 'Edit milestone: Title',
'Delete webhook for URL')
All icon-only interactive elements now satisfy WCAG 2.1 SC 1.1.1.
### #773 — Add robots.txt and dynamic sitemap.xml
- frontend/src/app/robots.ts: disallows /dashboard, /settings, /embed;
references the sitemap URL via NEXT_PUBLIC_SITE_URL
- frontend/src/app/sitemap.ts: static entries for / and /explore at
priority 1.0; dynamic entries for every public creator profile fetched
from GET /v1/profiles?limit=1000 at priority 0.8, revalidated hourly.
Build failures from an unavailable API are handled gracefully.
### #771 — Report/flag profile button and backend report endpoint
Backend (app.ts + Prisma):
- POST /v1/profiles/:username/report — unauthenticated, rate-limited to
1 request per IP per profile per hour
- Accepts body { reason: 'spam'|'impersonation'|'inappropriate'|'scam',
details?: string (max 500 chars) }
- Stores report in ProfileReport model (profileId, reason, details,
reporterIp, createdAt)
- Sends an admin alert email when a profile accumulates 3+ reports
- Migration: 20260724020000_add_profile_reports adds the profile_reports
table with appropriate indexes
Frontend:
- New component: report-profile-modal.tsx — FocusTrap-wrapped dialog with
reason radio group, optional details textarea (with live char count),
error/rate-limit feedback, and a success confirmation state
- Escape key closes the modal and returns focus to the trigger
- Integrated into profile/[username]/page.tsx as a 'Report' button in
the profile action row alongside QR, RSS, Share, and Edit buttons
### #782 — Add focus trap to modal components
- transaction-result-modal.tsx: wrapped in <FocusTrap> with
escapeDeactivates: false; Escape is handled by a keydown listener that
calls onClose(). The modal has role="dialog" aria-modal="true" and
aria-labelledby pointing to the visible title.
- qr-code-button.tsx: modal popover wrapped in <FocusTrap> with the same
pattern; close button and action buttons are all reachable by Tab.
- wallet-connect.tsx renders as an inline panel (not a dialog), so focus
trap is not applicable there.
- focus-trap-react@^10.3.1 was already present in package.json.
0 commit comments