# Les 11 — Lesopdracht
## Bouw je eigen AI + Supabase app

**Vak:** AI-Assisted Development
**Opleiding:** NOVI Hogeschool Utrecht
**Wanneer:** Thuis, vóór volgende les
**Inleveren:** GitHub URL + screenshot van werkende chat

---

## Doel

Bouw een complete versie van wat Tim live demonstreerde — **met je eigen thema**. Tim bouwde Polderfest 2027. Jij bouwt iets anders.

Je oefent end-to-end:
- Next.js project from scratch
- Nieuwe Supabase aanmaken + koppelen
- Eigen seed-script schrijven (mag AI bij helpen)
- Chat-route + chat-UI met `streamText` + `useChat`
- 3 vragen kunnen stellen die alleen via jouw data beantwoord kunnen worden

> **Belangrijk:** dit is **niet** dezelfde Polderfest-demo namaken. Je kiest een eigen thema. Anders leer je vooral kopiëren.

---

## Vereisten

### Jouw thema moet:
- [ ] **Volledig fictief zijn** — geen Spotify, geen restaurants in Amsterdam, geen Wikipedia-data
- [ ] Minstens 5 velden hebben (waarvan 1 categorisch, 1 numeriek, 1 tekstrijk)
- [ ] Minstens **100 records** bevatten
- [ ] Vragen oproepen die je écht niet aan ChatGPT kunt stellen zonder jouw data

### Verboden thema's (te bekend voor LLMs):
- Restaurants in een echte stad
- Films / muziek / boeken die echt bestaan
- Sport-statistieken uit de echte wereld
- Klimaat / financiële data uit publieke bronnen

### Geschikte thema-richtingen (kies of bedenk eigen):
- Fictief restaurant-aggregator in een verzonnen stad (bv. "Polderstad")
- Galactische bestuurders archief (sci-fi)
- Verzonnen scriptie-archief van NOVI (1000 nep-titels)
- Fictieve museumcollectie met verzonnen kunstenaars
- Fictief NPO-programma overzicht voor 2030
- Verzonnen mysteries / cryptid-sightings database NL
- D&D campagne-NPCs voor een fictieve wereld
- Fictieve nederlands ondernemers ecosysteem

---

## Stappenplan

### Stap 1 — Thema kiezen + schema ontwerpen (30 min)

- Bedenk thema
- Schrijf op papier (of in Notion): welke velden? Welke vragen wil je kunnen stellen?
- Vertaal naar Supabase SQL schema (zie `schema.sql` van Polderfest als voorbeeld)

### Stap 2 — Next.js project + Supabase (15 min)

```bash
npx create-next-app@latest mijn-thema \
  --typescript --tailwind --app --eslint --no-src-dir --turbopack
cd mijn-thema
npm i @supabase/supabase-js ai @ai-sdk/openai zod
npm i tsx dotenv --save-dev
```

- Maak nieuwe Supabase project aan
- Run je schema in SQL Editor
- Vul `.env.local` met URL + keys + OpenAI key

### Stap 3 — Seed script (45 min)

Open `seed-polderfest.ts` (zie bijlage) als referentie. Je hebt twee opties:

**Optie A — Met de hand:**
- Kopieer de structuur
- Vervang Polderfest-bouwstenen (genres, stages, cities…) door jouw thema-bouwstenen
- Pas de `generateBand` functie aan naar `generateItem` voor jouw thema

**Optie B — Met AI hulp (aanbevolen):**
- Open OpenCode / Cursor met `seed-polderfest.ts` als context
- Vraag: "Pas dit seed-script aan voor [mijn thema]. Schema is [paste schema]. Genereer 100+ records."
- Review wat AI maakt — vragen om aanpassingen waar nodig

Run je seed:
```bash
npx tsx scripts/seed-mijn-thema.ts
```

Verifieer in Supabase Table Editor: 100+ records.

### Stap 4 — Chat route (20 min)

`app/api/chat/route.ts` — gebruik Polderfest-versie als template:

```typescript
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);

export async function POST(req: Request) {
  const { messages } = await req.json();
  const { data: items } = await supabase.from("items").select("*");

  const context = items!.map((i) =>
    `- ${i.name} (${i.category}, ${i.rating})`
  ).join("\n");

  const system = `Je bent een assistent voor [thema-naam].
Hier is alle data:

${context}

Beantwoord vragen op basis van bovenstaande data. Verzin niets.
Antwoord in het Nederlands.`;

  const result = streamText({
    model: openai("gpt-4o-mini"),
    system,
    messages,
  });

  return result.toDataStreamResponse();
}
```

### Stap 5 — Chat UI (15 min)

`app/chat/page.tsx` — kopieer Polderfest UI, pas titel + placeholder aan.

### Stap 6 — Testen + 3 vragen (15 min)

Browse naar `/chat`. Stel deze 3 vragen aan jouw AI:

1. Een **filter-vraag** ("Welke X hebben Y?")
2. Een **aggregatie-vraag** ("Hoeveel X zijn er in totaal?" / "Wie heeft de hoogste Z?")
3. Een **samenvatting-vraag** ("Vat de Z-categorie samen in 3 zinnen")

Screenshots van werkende antwoorden bewaren — die heb je nodig.

---

## Inleveren (vóór volgende les)

1. **GitHub repo** met je code
2. **3 screenshots** van werkende chat-antwoorden in je README
3. **Eén alinea** onder de screenshots: waarom kan een gewone LLM deze vragen niet beantwoorden zonder jouw data?

---

## Veelvoorkomende problemen

| Probleem | Oplossing |
|----------|-----------|
| `OPENAI_API_KEY is not defined` | Dev server herstarten na `.env.local` aanpassen |
| Supabase insert: `permission denied` | Gebruik `SUPABASE_SERVICE_ROLE_KEY` in seed-script (geen anon) |
| AI verzint dingen | System prompt verstevigen: "Verzin niets. Gebruik alleen onze data." |
| AI antwoordt in Engels | Voeg toe aan prompt: "Antwoord in het Nederlands." |
| Chat hangt / streamt niet | API endpoint moet `result.toDataStreamResponse()` returnen |
| `tsx command not found` | `npm i tsx --save-dev`, run met `npx tsx ...` |

---

## Tijd-indicatie

| Stap | Tijd |
|------|------|
| Thema + schema bedenken | 30 min |
| Project + Supabase setup | 15 min |
| Seed script (met AI hulp) | 45 min |
| Chat route + UI | 35 min |
| Testen + screenshots | 15 min |
| **Totaal** | **~2,5 uur** |

Loop je vast? Vraag in Brightspace of plan korte 1-op-1 met Tim.

---

## Tips

- **Begin klein.** 100 records is genoeg. 500 als je extra wil.
- **AI laten helpen bij seed.** Schaal je productiviteit 10×.
- **System prompt is je hefboom.** Goede prompt = goede antwoorden. Slechte prompt = AI verzint.
- **Test met simpele vraag eerst** ("Hoeveel records zijn er?"). Dan opbouwen.
- **Token cost in de gaten houden** — onze hele les kostte <2 cent. Jouw test ook.

Succes!
