Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Deploy FELG Dashboard

on:
push:
branches: [main]
workflow_dispatch:

env:
PUBLIC_SUPABASE_URL: ${{ secrets.PUBLIC_SUPABASE_URL }}
PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.PUBLIC_SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}

jobs:
validate-sync-deploy:
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

# Step 1: Validate the data file
- name: Validate dashboard.json
run: node scripts/validate-data.js

# Step 2: Sync validated data to Supabase
- name: Sync to Supabase
run: node scripts/sync-to-supabase.js

# Step 3: Build the Astro site
- name: Build Astro
run: npm run build

# Step 4: Upload and deploy to GitHub Pages
- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: dist

- name: Deploy to Pages
id: deployment
uses: actions/deploy-pages@v4
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# build output
dist/

# generated types
.astro/

# dependencies
node_modules/

# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# environment variables
.env
.env.production

# macOS-specific files
.DS_Store

# jetbrains setting folder
.idea/
4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}
11 changes: 11 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}
111 changes: 111 additions & 0 deletions AGENT-PROMPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# 🤖 FELG Dashboard — Agent Build Prompt

Copy this document into a coding agent's context (Claude Code, or equivalent) to execute the MVP build. It is self-contained but assumes the agent also has read access to [`PRJ-FELG.md`](./PRJ-FELG.md) (source of truth, **v1.3.0-r1 / Amendment A3**) and [`IMPLEMENTATION-GUIDE.md`](./IMPLEMENTATION-GUIDE.md) (architecture) in the same repo.

> **Amendment A3:** the frontend is **Astro + Tailwind CSS**, not vanilla HTML/CSS/JS. Brand detail and "all brands" views are real Astro routes, not hash-based fake routes. The backend (Supabase + Realtime, Amendment A2) is unchanged. If you've seen an earlier version of this prompt describing a hand-written `router.js` and raw ES module render functions, that version is stale — this one supersedes it.

---

## Your Mission

Build the MVP (Phase 1) of the FELG Ecosystem Dashboard: a public, open web app — **no signup, ever, for viewing** — dark-themed, built with Astro + Tailwind CSS, displaying four FELG sections — 🎉 Fun, 💰 Earning, 📚 Learning, 🫶 Giving — in that exact order, backed by Supabase for storage and realtime push delivery, deployable to GitHub Pages.

Before writing any code, read `PRJ-FELG.md` in full and `IMPLEMENTATION-GUIDE.md` §1–7. Every requirement below cites an FR/NFR number from the PRD — if you're ever unsure what a requirement means, that number is where the definitive answer lives, not this prompt.

---

## Non-Negotiables (violating any of these is a failed build, not a style choice)

| Rule | Detail | Source |
|:---|:---|:---|
| **Section order is fixed** | Fun → Earning → Learning → Giving, in the DOM, always. Never data-driven. | FR-40, 🔒 IMMUTABLE |
| **No authentication, anywhere** | No login form, no session, no auth-gated route, no admin panel. This is an open web app — nothing gates viewing. | FR-44, 🔒 IMMUTABLE, NFR-09 |
| **Dark mode is the only mode** | No light-mode toggle, no `darkMode` config in Tailwind. One theme. | PRD Out of Scope table |
| **No calculated score field** | Never add a `felg_score`, `contribution_score`, or similar aggregate. | PRD Out of Scope table |
| **Astro + Tailwind, static output** | `output: 'static'` in `astro.config.mjs`. No SSR, no API routes — Supabase is the only backend this product needs. Don't reach for React/Vue/Svelte components; plain Astro components + small client-side TypeScript scripts are sufficient for everything this build requires. | PRD Amendment A3, Implementation Guide §2 |
| **Brand navigation uses real routes, not a hand-written router** | `src/pages/brand/[id].astro` with `getStaticPaths()`. Do not build a hash-based router or a click-handler-driven modal system — plain `<a href>` links plus the browser's native back button satisfy FR-49–52 for free. | FR-67, Implementation Guide §5 |
| **Realtime delivery IS required — this is MVP, not a stretch goal** | The dashboard must push updates to open browsers via Supabase Realtime, with no client polling loop. | FR-65, PRD Amendment A2 |
| **Data ingestion is still manual for MVP** | Do not build Postiz/SendGrid/GitHub API integrations. Content still comes from a human editing `data/dashboard.json` in a PR. | PRD Phasing §, Phase 2 unchanged |
| **The service-role Supabase key must never reach the client** | It exists only as a GitHub Actions secret, used exclusively by `scripts/sync-to-supabase.js`. The browser and the Astro build-time frontmatter both use only the public anon key, safe specifically because RLS restricts it to SELECT-only. | FR-66, 🔒 IMMUTABLE |
| **Live-patch scripts never own page structure** | `src/scripts/live/patch/*.ts` update specific DOM nodes (tagged `data-live="..."`) or rebuild a small, scoped sub-tree. They do not duplicate whole-page layout logic that belongs in the `.astro` components. | Implementation Guide §4.4 |

---

## What You're Building

Follow the repository structure in `IMPLEMENTATION-GUIDE.md` §3 exactly. In short:

```
astro.config.mjs, tailwind.config.mjs, package.json
src/lib/supabase.ts (public client — used at build time AND client-side)
src/layouts/Layout.astro, src/components/*.astro
src/pages/index.astro, src/pages/brands.astro, src/pages/brand/[id].astro
src/scripts/live/client.ts, src/scripts/live/patch/*.ts
data/dashboard.json (human-edited source — not fetched at runtime)
supabase/schema.sql
scripts/{validate-data,sync-to-supabase}.js
.github/workflows/deploy.yml (validate → sync → astro build → deploy)
```

The data shape must match PRD Appendix B **exactly** — same keys, same nesting, same enum values (`status` ∈ `live | in_progress | not_started | paused`). Do not invent fields. This shape lives as a single JSONB column on one Supabase row (`id = 'current'`) — see Implementation Guide §4.3 for the exact schema and query pattern.

**Critical architectural point:** the browser and the Astro build process both read from Supabase, never from `data/dashboard.json` directly. That file is the human-editable source; CI copies its contents into Supabase on merge, and every subsequent read — at build time or in the browser — goes through Supabase.

---

## Build Order

Work through these in sequence. Steps 2–5 prove the entire data pipeline (file → CI → Supabase → build-time render → browser realtime) end-to-end before any route/page work gets built on top of it.

1. **Scaffold.** `npm create astro@latest`, `astro add tailwind`, configure `astro.config.mjs` (`output: 'static'`). Build `Layout.astro`, `Header.astro`, `Footer.astro`, and an `index.astro` with hardcoded sample data for all four sections, in FELG order, per FR-36–41. Get this looking right before touching Supabase.
2. **Provision Supabase.** Apply `supabase/schema.sql`, enable Realtime replication, seed one row with sample data. Confirm in the Supabase dashboard that RLS is on and anon has SELECT-only access.
3. **Wire the build-time read.** Add `src/lib/supabase.ts`; update `index.astro`'s frontmatter to fetch from Supabase instead of using hardcoded data. Run `astro build` and confirm real data appears in the output HTML.
4. **Wire the realtime subscription.** Build `src/scripts/live/client.ts` and one patch module (start with `patch/social.ts`). Manually edit the row in the Supabase dashboard UI and confirm an already-open browser tab updates with zero reload. **Do not proceed past this step until you've actually watched this happen.**
5. **Wire the CI pipeline.** Build `scripts/validate-data.js` and `scripts/sync-to-supabase.js`, then `.github/workflows/deploy.yml` (validate → sync → `astro build` → deploy). Test for real: edit `data/dashboard.json`, open a PR, merge it, and confirm the change appears live in an open tab within seconds **and** survives a fresh page reload.
6. **Brand detail pages.** Build `src/pages/brand/[id].astro` with `getStaticPaths()` sourced from the same Supabase read used in step 3. Style per PRD §6.2. Add the ESC-key listener (`history.back()` on Escape) for FR-51. Do not build a router — `BrandCard.astro` just links to the page with a plain `<a href>`.
7. **All Brands route.** Build `src/pages/brands.astro`, grouped by status per FR-54–57, each card linking to its `brand/[id]` page.
8. **Remaining live-patch coverage.** `patch/earning.ts`, `patch/learning.ts`, `patch/giving.ts`. Add relative "last updated"/"last synced" timestamps (FR-14, FR-39) and the manual refresh button as an explicit fallback (FR-45).
9. **Responsive + accessibility pass.** Tailwind breakpoints (`sm:`, `lg:`) for the brand grid. Full keyboard operability. Contrast-check the pillar colors against `bg`.
10. **Security verification.** From the browser console, using only the public anon key already present in the deployed bundle, attempt an `insert` or `update` against the Supabase table. It must fail. This is a launch blocker — do not skip it.
11. **Performance check.** Run Lighthouse against the built site. If LCP exceeds 3s or TTI exceeds 2s, investigate before moving on — Astro's default output should make this easier to hit than a hand-rolled site, not harder, so a miss here is worth digging into.

Stop after step 11. Postiz/SendGrid/GitHub API integration, historical trend charts, and an admin data-entry UI are Phase 2/3 and out of scope for this pass — see Implementation Guide §9 if asked to scope that work later.

---

## Definition of Done

Before reporting this build complete, verify — don't assume — every item:

- [ ] `astro build` completes with zero errors and `dist/` contains one static page per brand in `all_brands`, each with a distinct `<title>` and OG tags (FR-67)
- [ ] Opening the deployed site renders all four sections in Fun→Earning→Learning→Giving order with real data — not hardcoded — sourced from Supabase at build time
- [ ] Every FR-01 through FR-67 in the PRD has a corresponding, working piece of UI — cross-check against the FR table in Implementation Guide §5 module-by-module
- [ ] **You have personally watched a merged PR to `data/dashboard.json` update an already-open browser tab within seconds, with no manual refresh, and confirmed a subsequent fresh page load also reflects it.** Describe what you observed in your report.
- [ ] Attempting a write against Supabase from the browser console, using the public anon key, fails (RLS confirmed)
- [ ] The service-role key does not appear anywhere in `dist/`, `src/`, or any browser network request — grep for it before you report done
- [ ] `scripts/validate-data.js` runs clean against the shipped `dashboard.json` and fails loudly on a deliberately broken copy (test this)
- [ ] No auth code, no signup flow, no light-mode code, no score field, no React/Vue/Svelte dependency, no hand-written router exists anywhere in the diff
- [ ] Clicking a brand card navigates to `/brand/<id>/`; the browser back button returns to the prior scroll position on `/` with zero custom code involved
- [ ] ESC key closes the brand detail page (FR-51)
- [ ] Keyboard-only pass: Tab reaches every interactive element
- [ ] Lighthouse numbers meet NFR-01/NFR-02 targets — paste the actual numbers into your final report, not "should be fine"
- [ ] `.github/workflows/deploy.yml` successfully validates, syncs to Supabase, runs `astro build`, and deploys on a test push

If any box can't be checked, say so explicitly in your summary rather than reporting the build as done — an unchecked box you didn't mention is worse than one you flagged.

---

## When to Stop and Ask

Don't guess on these — surface them instead of picking silently:

- Whether `/brands` should be a dedicated route or an inline expansion on `/` (PRD §7.1 leaves this open; Implementation Guide §10 leans toward a route but defers to @PAT)
- Whether `sync-to-supabase.js` should run automatically on every merge or require a manual trigger — a trust/safety tradeoff for the human team to set
- Anything in `data/dashboard.json` that seems to need a field not present in PRD Appendix B
- Final hosting target if GitHub Pages direct isn't confirmed by the time you reach the deploy step — this affects `astro.config.mjs`'s `site`/`base` values
- Any FR that seems to conflict with another FR — flag the numbers, don't resolve it by picking one
- If you don't have a Supabase project to provision against — don't fabricate credentials or stub around it silently; ask for a project to be created first

---

*Operating brief for PRJ-FELG.md v1.3.0-r1 (Amendment A3) / IMPLEMENTATION-GUIDE.md. Read both in full before starting step 1.*
Loading