{
    "id": "bhaskar/feedback-board-with-voting",
    "kind": "plan",
    "title": "Feedback Board with Voting and a Public Roadmap",
    "summary": "Let users post ideas, upvote once each and comment, while you set statuses that feed a public roadmap. Next.js + Supabase with RLS.",
    "author": "Admin",
    "stars": 0,
    "url": "https://ownlyplans.com/bhaskar/feedback-board-with-voting/",
    "stack": [
        "Next.js",
        "shadcn/ui",
        "Supabase",
        "Tailwind"
    ],
    "tools": [
        "claude-code",
        "cursor"
    ],
    "difficulty": "intermediate",
    "license": "cc-by-4.0",
    "version": "1.0",
    "overview": "The tool every product team ends up paying for: a place where users suggest ideas, vote on what matters and see what you are working on. This plan builds your own, with the important rules enforced in the database: one vote per person, only admins change status, and nobody can fake a vote count.\r\n\r\nReady-made SQL is included, so the security model is right from the first step.\r\n\r\nBefore you start: Node.js 20+, a free Supabase project and Claude Code or Cursor. About 2.5 hours.",
    "steps": [
        {
            "title": "Start from the official starter",
            "body": "Create a free Supabase project first. Then run:\r\n\r\nnpx create-next-app -e with-supabase feedback-board\r\ncd feedback-board\r\n\r\nCopy .env.example to .env.local with your project URL and anon key, then npm run dev. Save CLAUDE.md in the root.",
            "prompt": "Read CLAUDE.md and the starter README. In 5 bullets, explain where the Supabase clients live and how the session is refreshed. Do not change files yet.",
            "done": "You can sign up and sign in at localhost:3000.",
            "files": [
                "CLAUDE.md",
                "Next.js + Supabase starter"
            ]
        },
        {
            "title": "Create the database",
            "body": "Paste supabase-schema.sql into Supabase SQL Editor and run it. Sign up once in the app, then run the commented line at the bottom with your email to make yourself admin.",
            "prompt": "Save the schema as supabase/schema.sql. Generate TypeScript types into lib/database.types.ts and use them in the Supabase clients. Then explain in plain words what each RLS policy allows, table by table.",
            "done": "Four tables and the idea_scores view exist with RLS on, and npm run build passes.",
            "files": [
                "supabase-schema.sql",
                "Supabase Row Level Security guide"
            ]
        },
        {
            "title": "Board: list and sort ideas",
            "body": "The home page is the board. Read from idea_scores so vote counts always come from the database.",
            "prompt": "Build / as the board: tabs Top (most votes), New (newest) and a status filter. Each card shows title, first line of body, status badge, vote count and comment count. Server-render the list from idea_scores. Add simple pagination (20 per page).",
            "done": "Seeded ideas appear sorted correctly in Top and New.",
            "files": []
        },
        {
            "title": "Post an idea",
            "body": "Signed-in users can post. New ideas always start as Under review (the database policy enforces it).",
            "prompt": "Add a \"Suggest an idea\" dialog (shadcn/ui) for signed-in users: title (5 to 120 chars) and details. Save with a Server Action, then go to the new idea page. Signed-out users see \"Sign in to post\" instead. Before saving, search existing titles and show up to 3 similar ideas with a \"Vote on this instead\" link.",
            "done": "Posting works, and typing a title like an existing one shows the similar ideas.",
            "files": []
        },
        {
            "title": "Voting with optimistic UI",
            "body": "One vote per user is guaranteed by the primary key. The UI just has to feel instant and recover if the server says no.",
            "prompt": "Add a vote button on cards and the idea page: toggles insert/delete on votes for the current user. Update the count optimistically, roll back on error, and show \"Sign in to vote\" for signed-out users. Make sure a double click cannot create two requests.",
            "done": "Voting feels instant; trying to insert a second vote for the same idea via the API fails.",
            "files": []
        },
        {
            "title": "Idea page and comments",
            "body": "Each idea gets its own page with the full text, votes and a simple comment thread.",
            "prompt": "Build /ideas/[id]: title, status, author, date, full body, vote button and comments (oldest first). Signed-in users can comment and delete their own comments; admins can delete any. Show relative times like \"3 days ago\".",
            "done": "You can comment, and a second account cannot delete your comment.",
            "files": []
        },
        {
            "title": "Admin: statuses and the public roadmap",
            "body": "Admins move ideas through statuses. The roadmap shows that progress to everyone.",
            "prompt": "For admins only, add a status select on the idea page (Server Action, check is_admin on the server too). Build /roadmap: three columns Planned, In progress and Done, each listing ideas with vote counts, newest status changes first.",
            "done": "Changing an idea to Planned moves it onto the roadmap; a non-admin cannot change status even with a direct API call.",
            "files": [
                "Supabase Row Level Security guide"
            ]
        },
        {
            "title": "Security check and deploy",
            "body": "Prove the rules with two normal accounts and one admin, then deploy to Vercel.",
            "prompt": "Write a test checklist and walk me through it: user A cannot vote twice, cannot set a status, cannot delete B's idea or comment, cannot make themselves admin by updating profiles; signed-out users can read but not write. Then list the env vars for Vercel and the Supabase Auth URL settings for production.",
            "done": "Every check passes and the board runs on your Vercel URL.",
            "files": [
                "Supabase Row Level Security guide"
            ]
        }
    ],
    "files": [
        {
            "type": "markdown",
            "name": "CLAUDE.md",
            "note": "Project root. Cursor users: save as AGENTS.md.",
            "file": "claude",
            "url": "https://ownlyplans.com/bhaskar/feedback-board-with-voting/claude/"
        },
        {
            "type": "markdown",
            "name": "supabase-schema.sql",
            "note": "Tables, vote view, triggers and RLS policies. Save as supabase/schema.sql.",
            "file": "supabase-schema",
            "url": "https://ownlyplans.com/bhaskar/feedback-board-with-voting/supabase-schema/"
        },
        {
            "type": "git",
            "name": "Next.js + Supabase starter",
            "note": "Official starter with cookie-based auth.",
            "url": "https://github.com/vercel/next.js/tree/canary/examples/with-supabase"
        },
        {
            "type": "link",
            "name": "Supabase Row Level Security guide",
            "note": "Read once: it is what keeps votes honest.",
            "url": "https://supabase.com/docs/guides/database/postgres/row-level-security"
        }
    ],
    "install": "",
    "how_to_use": "Call get_step with step 1, do it, check \"done when\", then continue."
}