
CommentFlow — Figma Comment Manager
Full-stack comment management platform with React admin panel, Figma OAuth broker, newsletter engine, and support ticketing
Live from Figma API on every fetch
Data freshness
3 (user_settings, feedback, newsletter_subscribers)
Backend tables
Email/password, Google OAuth, Figma OAuth, PAT
Auth methods
Zero — brokered via Cloudflare Function
Client secret exposure
JWT email-gated RLS + security definer RPC
Admin access control
Project Overview
CommentFlow is a full-stack web platform that turns Figma file comments into a structured workflow for designers, developers, and QA teams. Instead of hunting through Figma's native sidebar, users get a dedicated dashboard with rich filtering, status tracking, inline replies, and deep links back to the exact canvas node — all pulling live from the Figma REST API on demand. The frontend is a zero-build vanilla JS app served as a static site on Cloudflare Pages. There is no comment database — every fetch is fresh from Figma, keeping data always accurate. Supabase handles user auth and a single settings table per user. A Cloudflare Function acts as a secure OAuth broker so the Figma client secret never touches the browser. A separate React + Vite admin panel (deployed at /dashboard) gives the operator full visibility into users, support requests, and newsletter subscribers. Feedback items cycle through an open → in progress → resolved workflow. The admin dashboard surfaces live stats across all four data sources simultaneously. The platform also includes a newsletter subscription engine built into the landing page, with subscriber management in the admin panel. A planned sending layer (Resend + Cloudflare Worker cron) will power templated email campaigns, marketing blasts, and automated welcome emails — with per-send delivery tracking and Slack/push notification integrations.
Key Challenges
Mapping comments to Figma pages
The Figma comments API returns a node_id per comment but no page name. With large files containing dozens of pages and hundreds of nodes, there was no way to tell which page a comment lived on without additional work.
Keeping the OAuth client secret out of the browser
Figma's OAuth token exchange requires a client secret, which can never be embedded in a static frontend served to the public.
Stateless filter system that survives re-renders
The filter sidebar needed multi-select per category, live search within each section, and combined AND/OR logic — all while preserving focused input state across full re-renders triggered by every filter change.
Admin panel over Supabase auth.users without service role key
The admin panel needed to list all registered users, but Supabase's auth.users table is not exposed via the public API by default and reading it requires either the service role key or a custom RPC.
Solutions Implemented
File structure fetch to build a node→page map
On every fetch, the app calls GET /v1/files/{key}?depth=4 to traverse the document tree and build a nodePageMap keyed by node ID. Comments are annotated with page names at render time.
- Fetched in parallel with the comments request to avoid sequential latency
- Traverses all document children recursively to map every node.id to its parent page
- Canvas-level comments with no node_id gracefully return null
Cloudflare Function as OAuth broker
A lightweight Cloudflare Function at /figma-token receives the OAuth code from the browser and performs the token exchange server-side, returning only the access token to the client.
- Client secret lives exclusively in Cloudflare environment variables
- Function validates state parameter before exchanging the code
- Returns only the access token — the secret is never exposed to the client
Stateful activeFilters object with selective re-render
An activeFilters object holds current selections for status, pages, authors, and labels. A sidebarState object tracks search strings and sort modes separately, restoring input focus by ID after each render.
- Pages, authors, and labels use Set() for O(1) toggle and membership checks
- Filters combine as OR within a section, AND across sections
- Search input focus is restored after every re-render by element ID targeting
Security-definer RPC for admin user listing
A get_all_users() Postgres function runs with security definer privileges and checks the caller's JWT email against the hardcoded admin address before returning rows from auth.users.
- No service role key needed in the frontend or admin panel
- RLS policies on feedback and newsletter_subscribers restrict all reads to the admin email
- Admin panel uses the same publishable Supabase key as the main app
Technology Stack
frontend
backend
database
devops
Development Process
Auth & API Foundation
2 weeks- Figma OAuth flow with Cloudflare Function broker
- PAT fallback authentication with token verification
- Supabase email/password + Google OAuth integration
- Basic comment fetch and raw table render
Filter System & Page Mapping
2 weeks- File structure fetch and nodePageMap builder
- Filter sidebar: status radio, pages/authors/labels multi-select
- OR logic within sections, AND logic across sections
- Search and A–Z / count sort controls per filter section
Interactions & Persistence
2 weeks- Inline reply posting via Figma comments API
- Local resolve layer merged with API resolved_at
- Saved files management synced to Supabase user_settings
- Deep link generation to Figma canvas nodes
Admin Panel
2 weeks- React + Vite admin SPA deployed at /dashboard
- Admin guard using JWT email check
- Users page via get_all_users() security definer RPC
- Feedback page with status cycling and expandable message rows
- Newsletter page with subscriber list and active toggle
- Dashboard overview with live stats across all data sources
Growth & Support Infrastructure
1 week- Newsletter subscription form on landing page
- Support modal with feature request, bug, and help tabs
- Speed-dial FAB replacing full-width support bar
- Tawk.to live chat with show/hide lifecycle hooks
- PostHog analytics integration
- Blog with 6 SEO posts, sitemap, robots.txt