Day 06 of 07
Build the results history and shareable grade links
Goal: Today you save sample/test grades to Supabase, build a history page for users to revisit past results, and add a public share URL for each grade. Public links must expose only public-safe result data — never private user or account data.
What to do
Create the grades table in Supabase
Add a database table to permanently store every grade result.
- In Supabase → Table Editor, click Create a new table.
- Name the table: grades
- Keep Row Level Security enabled. Do not expose user data while prototyping; define and test policies for the signed-in user before writing application code.
- Add these columns:
- — id: uuid, default gen_random_uuid(), primary key
- — user_id: text, NOT NULL
- — headline: text, NOT NULL
- — subheadline: text, NOT NULL
- — cta: text, NOT NULL
- — bullets: text, NOT NULL
- — result: text, NOT NULL (stores the full Claude response)
- — overall_score: int4, nullable (you will parse this from the result)
- — share_slug: text, nullable, UNIQUE constraint (check "Is Unique" checkbox)
- — created_at: timestamptz, default now(), NOT NULL
- Click Save.
- The share_slug column is how you will create a public URL for each grade — e.g. /grade/abc123. The public response must contain only public-safe result fields.
- The UNIQUE constraint on share_slug prevents two grades from getting the same share URL.
Save each grade to the database
Update the API route to insert a new row into the grades table after every successful grade.
- Open Claude Code and paste: "Update the /api/grade route to: (1) After a successful Claude API response, extract the overall score by searching the result text for 'OVERALL SCORE: ' followed by a number — use: const scoreMatch = result.match(/OVERALL SCORE: (\d+)\/10/); const overallScore = scoreMatch ? parseInt(scoreMatch[1]) : null. (2) Generate a 6-character share slug: const shareSlug = Math.random().toString(36).substring(2, 8). (3) Insert a new row into the Supabase 'grades' table using the service_role client with: user_id (from the authenticated user), headline, subheadline, cta, bullets (all from the request body), result (the full Claude response text), overall_score, share_slug, created_at: new Date().toISOString(). (4) Return the existing grade result plus shareSlug in the response JSON: { result, gradesUsed, isPro, shareSlug }."
- After Claude Code updates the route, test a grade on localhost:3000.
- Go to Supabase → Table Editor → grades → Browse.
- You should see a new row with all the fields filled in.
- Check the share_slug column has a 6-character random string like "k4x8mq".
- Check overall_score has a number (or null if the regex did not match).
- If the row appears but overall_score is null, the regex is not matching. Paste an example result text into Claude Code and ask it to fix the regex.
Build the grade history page
Create a page where users can see all their past grades.
- Open Claude Code and paste: "Create a server component page at app/history/page.tsx that: (1) Gets the authenticated user using the Supabase server client. (2) If no user, redirects to /sign-in. (3) Queries the grades table for all rows where user_id = user.id, ordered by created_at descending. (4) Renders each grade as a card showing: the headline field (truncated to 60 characters with ... if longer), the overall_score as a coloured badge (green bg for 7+, amber bg for 5–6, red bg for below 5, grey if null), and the created_at date formatted as 'Jun 26, 2026'. (5) Each card is a link to /grade/[share_slug]. (6) If no grades yet, shows: 'No grades yet. Use the grader to see your history here.' with a link back to /. (7) Add a page title 'Your grade history' and a subtitle 'Every landing page you have graded.'"
- After Claude Code creates the page, add a link to it in your app.
- In page.tsx (the main grading page), add a "History" link in the top nav next to the Sign Out button.
- Test by visiting localhost:3000/history — you should see the grades you have already run.
- Click one of the grade cards — it should try to navigate to /grade/[share_slug] (which does not exist yet — you will build that next).
Build the public share page
Create a publicly accessible page that shows only public-safe result data without requiring sign-in. Do not return emails, auth IDs, payment state, private rows, or admin data.
- Open Claude Code and paste: "Create a dynamic page at app/grade/[slug]/page.tsx that: (1) Gets the slug from params. (2) Queries only the public-safe result fields for a matching share_slug. (3) Does not return email addresses, auth IDs, payment state, private rows, or admin data. (4) Verifies the Supabase policy and share-link access separately from signed-in history access. (5) If no public-safe row is found, calls notFound(). (6) Displays only the approved result fields. (7) At the bottom, shows a CTA box with: 'Grade your own landing page — free for your first 3 grades.' and a green button linking to /. (8) This page must NOT require authentication."
- Use sample/test data while building and testing the public page. Do not paste a real user row into a public URL just to make the demo work.
- Test the public response and rendered HTML for accidental email addresses, auth IDs, payment state, private fields, or admin data before sharing the link.
- Test by copying a share_slug from your Supabase grades table and visiting: localhost:3000/grade/[that slug].
- Open the URL in a private browser window (not signed in) to confirm it works publicly.
- The CTA at the bottom is the most important part — every person who sees a shared grade and clicks through is a potential subscriber.
- Treat public share links as intentionally public. Do not index or share a result until its fields and access policy have been reviewed.
Add the share button to grade results
After every grade, show a share section with a copy link button and a pre-written tweet.
- Open Claude Code and paste: "After the grade result cards are displayed in page.tsx, add a share section with: (1) A subtitle 'Share your result' in small grey text. (2) A 'Copy link' button that copies https://[YOUR DOMAIN]/grade/[shareSlug] to the clipboard and shows 'Copied!' for 2 seconds. Use navigator.clipboard.writeText(url). (3) A 'Post on X' button that opens in a new tab: https://twitter.com/intent/tweet?text= followed by URL-encoded text: 'I graded my landing page with Copy Grader — scored [SCORE]/10. Top fix: [TOP PRIORITY FIX]. Try it free: [URL]'. Parse the top priority fix from the result text by finding 'Top priority fix:' and taking everything after it until the end of the line. (4) Style the share section with a light grey background, rounded corners, and a subtle border."
- Replace [YOUR DOMAIN] with your actual Vercel domain.
- Test the copy button — note that clipboard access requires HTTPS, so test on the live Vercel URL rather than localhost.
- Test the tweet button — it should open a Twitter compose window with the text pre-filled including the real score and real top fix.
- The pre-written tweet with a specific score and insight is far more shareable than a generic "check this out" message.
Deploy and test the full sharing flow
Get history and sharing live and verify the viral loop works end-to-end.
- Commit all changes: "Day 6: history and sharing complete". Push to GitHub.
- Wait for Vercel to deploy, then test the full sharing flow on your live URL:
- — Grade a page.
- — Click "Copy link" — paste in a new browser tab to confirm the URL works.
- — Open that URL in a private browser window (not signed in) — the result should be visible publicly.
- — Confirm the CTA at the bottom links to your home page.
- — Click "Post on X" — check the pre-filled tweet text includes the real score and insight.
- Visit /history — confirm past grades appear with score badges and dates.
- Share the updated product link with the same person from Day 2 and Day 3, or post a Day 6 update in your community.
Expected result
Sample/test grades are saved. Users can browse their full grade history with coloured score badges. Approved result fields have public share URLs, while private user, payment, auth, and admin data stays out of the public response. Supabase RLS and share-link access were verified separately.
Key takeaway
- A public result link is a deliberate data-sharing boundary. Share only public-safe result data, test with sample data, and verify RLS and share-link access separately before inviting anyone else to view it.