fix: latest lesson

This commit is contained in:
2026-03-31 20:59:53 +02:00
parent 426b9f89d9
commit f65c24ffcd
4 changed files with 739 additions and 0 deletions

207
Les08-Docenttekst.md Normal file
View File

@@ -0,0 +1,207 @@
# Les 8 — Docenttekst
## Van In-Memory naar Supabase
---
## Lesoverzicht
| Gegeven | Details |
|---------|---------|
| **Les** | 8 van 18 |
| **Onderwerp** | Supabase koppelen aan Next.js |
| **Duur** | 3 uur (09:00 12:00) |
| **Voorbereiding** | Werkend QuickPoll project, Supabase project met polls/options tabellen |
| **Benodigdheden** | Laptop, Cursor/VS Code, browser, Supabase account |
| **Lesmateriaal** | Lesopdracht PDF (studenten werken hier zelfstandig doorheen) |
## Leerdoelen
Na deze les kunnen studenten:
1. De Supabase JavaScript client installeren en configureren
2. Environment variables gebruiken voor API keys
3. Data ophalen via Supabase queries (select met relaties, eq, single)
4. Het verschil uitleggen tussen Server Components en Client Components
5. Een formulier bouwen dat data INSERT in Supabase
---
## Aanpak
Studenten krijgen een **Lesopdracht PDF** met alle component-code (volledige UI). Ze hoeven alleen de **Supabase queries** zelf te schrijven (gemarkeerd als TODO-blokken). De docent legt concepten uit met slides, doet een korte demo, en loopt daarna rond.
---
## Lesplanning
### 09:0009:15 | Welkom & Uitleg aanpak (15 min)
📌 Slide 1, 2, 3
**Wat te zeggen:**
- "Vorige week: werkende polling app met in-memory data."
- "Vandaag koppelen we Supabase: onze database-as-a-service."
- "Jullie werken vandaag **zelfstandig** met een PDF. Alle UI-code staat erin. Jullie schrijven de Supabase queries."
- "Ik leg eerst de concepten uit, dan gaan jullie aan de slag."
**Check:**
- Iedereen heeft Supabase account met polls en options tabellen
- Iedereen heeft QuickPoll project lokaal draaiend
- Deel de Lesopdracht PDF uit (digitaal)
---
### 09:1509:45 | Uitleg concepten (30 min)
📌 Slide 4, 5, 6
#### 09:15 | Slide 4: Van Array naar Database
**Zeg:**
"Tot nu toe stond jullie data in een array. Dat werkt, maar is weg zodra je de server herstart. Supabase geeft ons een echte PostgreSQL database."
Toon het verschil:
```
// OUD: in-memory
const polls = [{ question: "...", votes: [0, 0] }]
// NIEUW: Supabase
supabase.from("polls").select("*, options(*)")
```
#### 09:25 | Slide 5: Supabase queries
**Toon de vier belangrijkste operaties:**
1. `.from("polls").select("*, options(*)")` → Haal alles op met relaties
2. `.eq("id", 5).single()` → Filter op 1 record
3. `.insert({ question })` → Nieuw record toevoegen
4. `.rpc("vote_option", { option_id })` → Database functie aanroepen
**Demo:** Open Supabase dashboard, toon Table Editor met polls en options tabel. Laat de relatie zien (foreign key).
#### 09:35 | Slide 6: Server vs Client
**Zeg:**
"Belangrijk patroon: Server Components zijn `async` — die halen data op. Client Components hebben `'use client'` — die zijn interactief (forms, klikken). In de PDF zien jullie dit terug."
---
### 09:4510:15 | Deel 1: Setup + Queries (30 min, zelfstandig)
📌 Slide 5 (blijft staan als referentie)
**Zeg:**
"Open de Lesopdracht PDF. Werk Deel 1, 2 en 3 door. Dat is de setup, queries schrijven, en componenten kopiëren. Na de pauze doen we Deel 4: de /create pagina."
**Studenten doen nu:**
- Deel 1 (PDF): npm install, .env, supabase client, types
- Deel 2 (PDF): lib/data.ts — TODO blokken invullen (getPolls, getPollById, votePoll)
- Deel 3 (PDF): Componenten kopiëren (page.tsx, PollItem, VoteForm, poll/[id])
**Jij loopt rond. Veelvoorkomende issues:**
| Probleem | Oplossing |
|----------|-----------|
| npm install failed | Check internet, node_modules verwijderen en opnieuw |
| Env vars undefined | NEXT_PUBLIC_ prefix? Dev server herstart? |
| getPolls() returns [] | Query syntax checken. Staat er data in Supabase? |
| TypeScript errors | Import vergeten? Types kloppen met database? |
| "RLS policy violation" | RLS uitschakelen of SELECT policy toevoegen |
**Check-in (10:00):**
"Wie heeft de homepage al werkend met Supabase data? Steek je hand op."
→ Als minder dan de helft: kort voordoen op beamer.
→ Als meer dan de helft: doorgaan, help de rest individueel.
---
### 10:1510:30 | PAUZE (15 min)
📌 Slide 7
---
### 10:3010:45 | Uitleg INSERT + /create (15 min)
📌 Slide 8
**Zeg:**
"Nu gaan jullie een /create pagina bouwen. Het formulier staat al in de PDF — jullie schrijven alleen de INSERT logica."
**Toon op beamer:**
```typescript
// 1. Insert poll
const { data: poll } = await supabase
.from("polls")
.insert({ question: "Mijn vraag" })
.select()
.single();
// 2. Insert options met poll.id
await supabase.from("options").insert([
{ poll_id: poll.id, text: "Optie A", votes: 0 },
{ poll_id: poll.id, text: "Optie B", votes: 0 },
]);
```
**Zeg:**
- "Eerst insert je de poll → je krijgt het id terug"
- "Dan insert je de options met dat poll_id"
- "En dan redirect je naar de homepage"
**RLS policy:**
"Voordat het werkt: voeg INSERT policies toe. Staat in Deel 4, Stap 4.1 van de PDF."
---
### 10:4511:30 | Deel 2: /create pagina (45 min, zelfstandig)
**Studenten doen nu:**
- Deel 4 (PDF): RLS policy toevoegen, handleSubmit implementeren
- Testen: poll aanmaken → verschijnt op homepage
**Jij loopt rond. Veelvoorkomende issues:**
| Probleem | Oplossing |
|----------|-----------|
| "RLS policy violation" bij INSERT | SQL policy uitgevoerd in dashboard? |
| poll is undefined na insert | .select().single() vergeten? |
| Opties worden niet opgeslagen | poll.id doorgeven aan options insert? |
| Form refresht de pagina | e.preventDefault() in handleSubmit? |
| Redirect werkt niet | useRouter van "next/navigation"? |
**Check-in (11:15):**
"Wie heeft succesvol een poll aangemaakt? Open Supabase dashboard en toon dat ie erin staat."
→ Toon op beamer als demo.
---
### 11:3011:45 | Vragen & Reflectie (15 min)
**Mogelijke vragen:**
**V: Waarom async/await?**
A: Supabase is over het netwerk. We moeten wachten op antwoord.
**V: Wat is het verschil tussen Server en Client Component?**
A: Server = async, data fetching, geen interactiviteit. Client = 'use client', useState, onClick.
**V: Kan ik realtime updates zien?**
A: Later! Supabase heeft realtime subscriptions.
---
### 11:4512:00 | Huiswerk & Afsluiting (15 min)
📌 Slide 9, 10
**Huiswerk:**
1. /create pagina afmaken (als niet klaar)
2. Validatie: vraag niet leeg, min 2 opties, foutmeldingen
3. Extra: delete functionaliteit, styling
**Slide 10: Afsluiting**
"Volgende les: Supabase Auth. Inloggen, registreren, en bepalen wie wat mag. Tot dan!"
---
## Tips voor docenten
1. **Niet te veel voordoen.** De PDF is self-contained. Studenten leren meer door zelf te doen.
2. **Loop ronde, spot problemen vroeg.** De eerste 10 minuten na "ga aan de slag" zijn cruciaal.
3. **Check-ins doen.** Vraag om handopsteken. Als <50% het heeft: kort voordoen.
4. **Toon Supabase dashboard.** "Zie je? De data staat echt in de database!"
5. **Authenticatie is volgende les.** Zeg het af en toe, zodat ze weten dat RLS nog tijdelijk is.

346
Les08-Lesopdracht.pdf Normal file
View File

@@ -0,0 +1,346 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 5 0 R /F4 6 0 R /F5 8 0 R /F6 19 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
9 0 obj
<<
/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 31 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 32 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 33 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/Contents 34 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/Contents 35 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
18 0 obj
<<
/Contents 36 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
19 0 obj
<<
/BaseFont /ZapfDingbats /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
20 0 obj
<<
/Contents 37 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
21 0 obj
<<
/Contents 38 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 24 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
22 0 obj
<<
/PageMode /UseNone /Pages 24 0 R /Type /Catalog
>>
endobj
23 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260331170259+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260331170259+02'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
24 0 obj
<<
/Count 14 /Kids [ 4 0 R 7 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R
17 0 R 18 0 R 20 0 R 21 0 R ] /Type /Pages
>>
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 952
>>
stream
Gatm:9iKe#&A@sBCi?#<#YVI>WA?]aThlS0Rc;a'(LrB]`3C?'T``'D/(lXP#/c#QbAqd6]C5.9-59^4;Yq=b!5=2,GX?s:#k*:i`.RO\^4jk\Trk6=Lp*2_L%5KaJ\Z]Zj@'<q3esPaTpf%?FYF3j&s"d!U<2]RXN]b$gaC6E$6?td7R&Q0ces.)eZ8JZ-o/;@/:aKO\Bqh0dTM#!i=*Sa0](ZAPtqUA':_TcLPt>;A&m;kcN6lBiR6hF!E%W7?@_#n=;Z:K6"[AU7YG`WCp@7/(!$R[/rMKaUA2Ij&8Z^.Jq-bYo0<dP&5mW\Lk3-:q%S"U],3R3XG7qf80GSU_0)![=[)hY(Ie!&Gna7t[4\6/BVdd-gKTD@#U'I[q(N5W3eug?Xc/`Y.1M)@Z>DUeVI1o%D?0<N#G]&FRR<gA1i-i\<?7@o:_Zp98oR3M#`HP6kAuut^lPcp_k48T>U8%u7lBK;U(#J_C$7+=!D9^kB6Q3[l=bb1>$%\0OcCO%a*dD859?t\P27fU$<a(H#OXb0hbpIg.r)XWA=0dFV[q8lL/=Yl9qCM=\oK@NK/"f5O%UY.ck7Br4B2%7^T1s<D;=1E]%VAQD.jD@Z/8Qb#].kO,$s`/j>M0/\p7/=3n$*:K#>+H;6?ONVJ.+=%1TcoE-.,G:C:Cr)c.K&pCt#*liE7)N5Ecu9=W6(q-<)A#CAh8D!#M=l#]qkI,E#6n7Ea+4FtU8NT\'6/Ak5*@*>R;l+#'=3W8.9SiK1g!t'4T^J>joi1H3C2W_gX\FPIjY%R/X\?^j>oaQ/T0mL!B^q5SWH$oeO)8fuuA2,P&2cpLg1LN?/*-Oe4[h>Qe1W24()Q)9uX!pp`IC;7$ZYq62SYA_qfXL-r045$/P%pBCkud++ISi:hbG]2rZOIF>Dj+1g*UcR'_ZFsukPUOhm[f^=U":[0$TS.eIO$@u~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1267
>>
stream
Gau0CD0+Dj&H9tYf^ojA*6.Og2OW?+d\FKLF%KP+HT/tS,E*7/Cs/(I8__@:^Qj`a&1G7tJf2aAYG>fTT1:CD0_$@`*[Um-4_VPU5V2,)!HAMQHDf-Se\9PSgsWM;0<bKZ/[o;Q^^I@gIPrb'C,mm`[VslN"\)n#+,K5$N_,P6kp8<C"dY`Hh+saD)e1Ua2KY@WB$daE1gqgY9YuU@/=$24IHXCupKD#$JCc,1[]c;;T$7J:ZS4Y[`'j5#f%Y>_<XG.<3[VQFgm'\u@M%a-<XV[N>gbTD9L*fjd36cfDS2Q:ScHe[i*_FuL?!#.k8B"i_E]l=6n+*=8n!\+q_4#HRgU:XO9n*ZT`h`lEPsksA@hn4cX+WT#:d9ghL9iR1L.Gl)1cG/HjA5,oHApCYPsRN)Er<Hb6PlAppH^&Wq/,DUYWDq=-$FOW9A`"-0VX0!rSM5;p[';)O%gcX3QU'G!Y$YFqkV75,X/<`*C1NR>,2XP"V@"S![(S;/X2<9HZlW[)P$R<@Jll)<=K8R+SG>dL;_:_H&t]:-+:9:Jl9JAlM0?(paQHF+%s5csrpgr-[GgG0!M[K@1^c;eiDf^2C/sB#Hg-nbc$?;B7/[(!fAY"Qp-"QGB$S(!,dRh@#J#0pftp&es=@'J=X97-#Enk,e/eDfB]AI!Y?qd(R(O3^2hP#+$E-&'%R[H+Vb"nEFe"m^Q-"h?^ZPJI99V+cajncN`i"Ii[hR2!&H3L4*ot,:4m.ceai\;A>scV)5VR$L47XFJP(T:8ns8;5h^)CFQ80*tL@H,@)I'P[<PB1E9\j(iVr1;$:d3ag^K&V/91:WNlJt/D%8#>?Q-CXG%I!*20re6ZH;GbA&MeL5WIeOSL53<KN8OOr':Bmr+DNB0S[G;3Gpp_fl@iUQW=<Bn1Q?&j1_H)p?4$K8"ESI+Xu2,32j9TX9?hPnEod#ItQm0,ml&aRWRhA_XT)pX>=QQSREE(nn]oYWIEEf6V>W3sF_m?aD6$_fhP_,Pkt0_G!(d\l;4h\@SoV9hbT,Z'VW@$'hN$X"C5!b#IX?=-*GsR4fffU>Bf=,Jb%H0P>Bii\p=aL(*_"nG>-'7C_-Mdq$P<_)@K1;jPU_j/SSAJ]mUb_a5cH!L5`@H)='TgMj;;rr-QPnp[>b*ulcAr%FBlN6.`ESRcrVI@5)snsr/H0KAeA.-Rg1d.HiVL*n\dcXA5"_[7(]*cG?+6HNbPlhXq+<qXju@JRk5fIu`Si]2r*8C])9<jU@M.b+G$g2VTX~>endstream
endobj
27 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1525
>>
stream
Gb!;d?$#!`'Re<2\BP]/CkO*adn\XhdrrL'RjLg98nZ2s(JnRW,0SZF8cF(?CdW^^$"UBd1h8:B=45VZ8&n\MYis',pNR*cgkGH?*4UOe!4]odke;gZ]6dV+]SZ7`o0O%0n6M[VcjQfp2)F1thf45#JRsT<=D40g:r>F7P:oVY*Rb](6oB[DIh/Xp`"YTE:*L]E)F[$W$/rdI3JmT0JT+V`P#r\hpB%p]-D<Zgoh\P60r_";H"a);Y*lLt,<#o_DG`Orii7a[qP/^P";_?o]I)mS:Fh_/G!`/`>r59^iE@p%<Q$+.0<R$>E.X1<$,SRUPmBbDX&<8=OP/"ua17X*Uf90&(qc+M:j3oCffE?<!3cnDYj,!gJHl9>0:2tUDemt>N1QF,`k=t_6,hThN[>kcYN^A":F)Zd6u(pqTqa/a3a^3Q(XcHTe(L%k^JJ3+fanF.R>hJsTW_S)2b"7%Q4R7]U@]&W4McopmY?u:cqj8V9?^e6;qqO2-F<"n9elN_K8&"hZ\7_J-cY`>!CTTmR[jS^f<aB/Qj]P[=Qin:i5"WCLs+^)0`Oo+Z@KN;@t?s+5-3mPh@qROAl'7#au0Cb`oAbrnK_0SDP3B=o*tsS+2^9XcYi-"?Qi=HGTM@'Vs->;kI-sWY\4([L!C./7:\<@/>#SdY2-1Blk$QrUZf+3#ISAT7o9U*Ym;#l!;U/XN9Ja"iCX7=\?__m(&hE'*u36UL-_6H>36\R@aK[)U-f\u/\:UBI5/urs&4FH&T40E`:4WmU*P-<$ToBl-9W-SJV"OJe.B28FQcmk-cCWSE,2R`r>!/[Etgt/H]Cl4+NLm'$t`@/DHNUZDHGT%"6[).!n&,ro%#7X(+tMQ7'b26,,R)*(8iJ3p]*cp7M,jJ!dZGPBr!'K%bP7c;V0MUdm[p6enh6<ZQ4@orG(2;)Pk._@nh.W?d0q^[17Nge@%3L1&2>0Q)bD*<*&'/ZK`seiY:ZUA^F8`fV9kT(lQZj_AFHOhTd;ekLd1c+o4q-GI&A,G![d<f%gO'&^F$2b45*uZ=5;r'%9=)id<QRhk?an&BLXL=ZPoFHH][QQ@dP,$]D8AnU_NDX'Pm$i.$a*1rL1DN9*=a--e=I<)At)A41Q/<L:d;/&->;:GBGU+IVT2U+&[+%1,Np0T`92HAus/a`<_7B#><53T#Tj&c4#78",aAYUEF"On:\)C@#M,nIl]12#AKLaV/k7js+."`Q[L+f4_VA^%7b0*#hMp1X=&C2b;""rl=/B%mdl:hE:$erL=pp;7;2QE&W#%mg7Qsin7[K4_HcMN5NjiS$URN)ORaL+"Fl[69tYje)Bq%`JU,&A0qNOO#>YP.Q<@cHo0,5irJMU038?Tg+`2U?"d5U+DJP/8ajMK\DYTmTtg<Z)#C+Xr_&1bGf"ZUGpGX9XYUQXS5rPI[?$7_%`V44n'WU^5=(VGn;u_L:Ia5MV"LP4DPqb"V5(]M*S[;4S^]JA@53"bn2W.kg:=$Ns'13=W/TF-(QHc3bJQ+%r<K2YL!p~>endstream
endobj
28 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1260
>>
stream
Gatm:D0+E#&H9tYf\oIk@L5d8+d(4;,Yab'4cW`&rDNLNO;:YDl'0Z=i*lEL\N=,o5V3oW_s-9+pN^q#?O1c7Qg"Gl!Ve@#p2:(i%AP?`a)YR0%A-+LD[j#.l.VHhgHL1;YAY$1oqe1+D:u)JGC!Y>]h[1g_=G5'"+8[]3m!MP;'J`e?u$G@ru-i'&S(#7i$1lMQ=;<tKd$o(>[&oY$DY';`*`(^$4&']+WYhqTXn0T&:]=#OsQQ5:F!D!i%'Dd9VI7Fg_C]tclD#9-/o7EL'h+?&0)ji1nA&EErcZg6VK*,P@Y=MhdJHG)C!L]go*Y/ZtdtQCp"JSP8dcYgMa%:gYN*a\=0k(C#95c6.[;7[d?9*/8Jm?L6foI0K/?\59N/nBA@eO^$G<N^m,Hj$Gja<#PXjG02I?37$L&B!(q0FgR<X.1hclU#2=npD$Ero6Ps[Xb-F%(Z%)trO1H)`&li4iVqH7ja)!?R=%NDBp$^cW<Y#rTbBH$rc[k@26^<8fWLr5D/5M.Sm<*q(I!D\$QQt2L8Ti1Bp;WG)nC3*jG@i*@_?I#KL>nN<E=QcOEgAu^QZa?Cg<)q1pR8BO]%d28;0p"5Qf:oaXm,e@D+`h1!gC=?I@fNeh7Fmd/W*1Q)WHts1+kVZ0J1^<6nF&</AYP9(<=G)"fm9l:$d$r^f$SLRH?:.*QC6.-!;s*(?R"PT#u\7:4%9^BsWK?!sC@'o,F.VYdlHc73"Z]Qfc/lQh"m*dR#Y/9XhQpppm!qPLemWfU.aD0Y>cSPbl6eYW.;H;R'_Z/tt%gToM]N;V%G'b7WtP1$:M:2VS7&=f\#Fd8l*6L#XF2cu_>iQnt/UK0n*cH1Xq^Kh9L?qb*G`?J`LmKZ.E\YmW\C<j'!g_3G+gB/DmFd<&]Un4G'eQ-`4eLnS]sNX`J_CZiUAM=$5>4['?5"tL&43dWfpCM:I$a)PnM>$hn9)2;dTW@rI#QAfsG0&r&FDYWq&iLd'sWqY+q6s_X$3%%5"O)^s6ocB%Zcl)C>?HWNQ'?]nb^"@D7(U)_S*Tq5l-N4C!nXDj?Q@Yq3VVIi9f6(0a-.C-0d)s=;L3^2U7WMbq;cuT`IJT89/3i#m<AG'0^Fh0:305q"1-toRT"qf:Es6mj\_p=GX!u%Ff6jeKrU55KjjlZWf=@Sb5Aog-1H&:F+MiA#G1P,@LgYhSs"K"='[bHJMqI#+('3!gX(*A-Ot/PrMW:]fT><V^d!AM8Y+S^k]KA4I*)[+^hZ5kXLW]~>endstream
endobj
29 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 791
>>
stream
Gb!#[gMWKG&:N^l7TM%mQ53Rr*(]BpMU_@=Wg!k`aUcCq(n-.)bMnGAs1O%<<UB@@`$DcV4rDp8B?h")5f(BuY()'<i5PlZ$O(9r(BLpd)n[>Ap9]1+DXZprHJc,8$GB*4;n)5T34u=SY%.0135k8m2>mU=RdTmo9?2cSPZ'.9imbU->j)Y-"5C]a$uWZ1/^B<<k\l_Lf>[!eFKKM<>'o2W-R-p:M/)6!PZ%Rc$Z5*Xp[^'-E<BW)K7Nhfn]ak_TJ[<:A.k[dZ]Km&'>(Z_ms5HCSb8$`Z1X2FZ%XA0%["gH.9N$4":lZH*(RCI<A$8_mn6][6tE>CZl?8m]E&6a(V(VAZ6WK"!K4T#\'S&<+14W+c/2A'#$W.INb@($!9k3YJV!nNTmZi,-s",@Qq]C(_^F)jVpEqQ`WLOG\-peCi<md>:$[m7Pqk`'f*u\g3Q!,SK0-L]<82)Mn3u%.;MZWW#1_A)e'!!FaYK)7?8\%Za$*0/B+%6E9+)B:9JQ@/P4i>Lk6+F`.17:j/s=\`M8bFq.tP)T;+oo9bnZ`1(Q_I\9Z^91=;9"]/fi8_e@0A4(gke#R1U6V&gq,/ZI><='Pl+F#qm1\T9#lT3D-$/F&:<2MPRCn`94dUSluN0jD7Xm0Mb"M?bmT&@A$Zia09m=P5Hse:7dc`[L"b[>dRnNPs&kIMtC9*Cu#>o/542#k#hW.XgIFUO4q#Wp-$sD^2#UkS:#aEH@\k"g0:KONj%jW?\raOBqShQS2fella_I.IGA.R)i])qTDH%,JbBJt6)]/~>endstream
endobj
30 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1301
>>
stream
Gau0CgN):C&:N^l3dt5:#m,sQ?pcF*Fe!L&<oRX]?7\;L;?J[`U`GH1'ZIhb7i):B(TgC=MEt.@qjbApNr]^d(s/s;#Ikd@rS@OlK]*c,QJR5P(\hiLq,T4*ac<?EH=kKRDTC+`?Dg<^d:;!Gd"MLUX-cjWVm/*HMW\Ne"J_0JY`o1UX(3A1od8=gf``ajLB0mtU\_`V`;;gDFW%lEXaTn+\db9Q!P;Nhi<p&Y.ZAl)k86t#\V"r6N6tR;N4-Tbco)T8do8TEo4.k4I)[F*E$Sn2L*D4?id?bkLHC0=*p?Kb7fgg=O7(lab^5bo1dFT>-Z2IKC('%s)#1hIAX!N])Cu!Lb+,EM(n[H,0LVMDbOlFd0m=nF;]NW3pHn!J"8_*H#tE3>hu`]bQ[NF'p0qe0X@BN$L[3j4=7eC%.seDJkZTPt>UeeTC6n_)XL<#oCX?sf+:rET-AM8hcu*qsSI1%<#,2Ei\oBnH+6!RB8b1h-6;0+,*99tAKS24E"=9@%fYCSlF1;h'*-q-i.pe%orniV4>5,^-h.;`iQ2`Y)?GLc\jXS2^7l1tD'#g4u#^IIUMBIjc?"cnf+^]ghD:Ho(5<\JfhX'RJf*$th@i"qs9)sR.>I(XoAEmROhZmOj:IB*b"URtR^g)prIY%jeQh:;6%(7dr[5%7t[&]fOkY[@_'.jQBHU5\D4]B@++-d12^nMQfW#MQ)*o&sKGWnG[Zj,bf^RXGq^D-$N9oA]cTjt=$fPrU&'W*1OekhTNNifZTDbQ*FIHTU2JPG.cn)UeMIU?B<roS#j'<Xo=P\2IB`e)+SkuC9Nc_BV62TNXn`)g*2#Xi"b1`A;U$G=?/GDsl+"Y#I_:oe?pC"q,m_t@tM]lr*1kP/[pCon\sbd?[fG$.<Y23usrB"j&i3bo?(S;"?EA8/Y[87b)Q+>A\LU^f9"eKD3!HZ$sd/&)4WV?CNi=KB-Bi)+TXS0]&^X&h7KH4_+lkRQ0H$ba-*SK(b_`\D=6UIf=`a/H9cP%%Ng4c.e!]>'"Vg<bJ>]BI[2ZTupa;03b:s4;A\idO-=i5o!$rN;kJ;'3C?l4jAg2j(H[hLX@_Q;68k-FJmB#5@t>Wr;:kBQUi;4or%=Od:]04f!M5roCW9ooMmE*<8\SC:2oKW0JRL#O_4HTRB6p=^@OTOu]$*(dL8P+$nif)$KTma6e-%^PoF5.>V%%qD3<(F)o2WhCrAVLYW8/2:UEa*o(f-EUJggm#_t;dBT,Fnm;!RXVjlV=6FDY9phru_ZBXB#YkWBm3o&$5>po[>?DIPW;Hm`ERh/~>endstream
endobj
31 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 675
>>
stream
GatU0?#Q2d'Rf.GSG%e=_I,n_ABQH6>,FpB['"(<]K!_6XFM`Z9<*mL4p+NFUhSF0!cbES?g%:>LVcq"D83Cc(^D`B'\#T+#OR^=JfB(DZj2RSHb)+TIG:Y:3'O%Ml`3!V-D'[/NfQR'j/YWR=D>]tX\5jY6Wl%K*/<(XI;_A5'aB$CU?!.uBX]ItJjd'rD25-56.R6m;-4i`c&Xjj[1jPU6C&!%\:BS\JmVaPKS#I?X&'>3d2OZoL8\XNj9sJ9>Mh>"m,@3tC-U<DjHW+VdM*q1'h]ef>^fc?n`+a+lEA1o$\b;6f=;=_%Ll"(YkM=7f__m1*AJp_K_ei(kUkTe+r>8DQFXlES,C($I]I5*jg*,#hM+7<,"d;oQEmOe`\K_=?Q&I3g;o5TNKoVpaOUQ"8qFiucb"^'mn8/;=A3pS8\ULF]hU9u\MU0-<TU*UeN#U#bm;D.P#jCNH$oa51T%'os&2HP&S2/D1jH#tL[TLg_u-\N)e<]6s+oDk:u*`P9sMiXHbj&EMqr(VZQ+,Oqqfmi;30hLp>l:mFaT[i+L^04rJ"L!\3gi4FW0Br-VC-c^U5&U#AQ@g//WEI9#OUC6dn`JN2<dU(<<^3K'#*q86Nn&/)3^fAN\"jQhU%XT4:'sa5+%/;c`E_IsT&5GUcf5SK5'Gm16b^;ul~>endstream
endobj
32 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1259
>>
stream
Gau0BD/\E'&H9tYR.:PYabDYWCoUN.Enh[P)LC>AC.hPYG[_GIc)oTk<L!O?=hjKI8P4&@LbQ,IB:aN`SnH<A73A+Lcdr.u*aHA<^b,'DJD89Pmfiu&m8Ju\O^3lf?-D?Vlgu$<].U4\a3<gP4hCftmBkTM"jChQ5>XY*Eq6n!hO7&=?tCY.pC/t9,)0M"iF8rdBS;?NA0bOuVd#+Ki%;cl@pj\[cVm[85*U\Ggbf7\a-=\h`_BUU>85CJ];biL[j`k)6a@;$KTV>R'=^4K7O>.<&8I1198!_CfhS-J&P47hY_g7OJQ3V&&0b;9<O%H&Jd-k:#ko$g^'kR%)OB,o,[(H_$]-@1nqt8-EXBj8U#bd)o$Ier6F\B,7a+8P0YEqb`ZBeQ+Qn?I5Z]K9Aa"FqT\FUh!,Co-A0"QbQ*'0(CjMVQ[1b,M?o&9kbNcq=An&iDo=mQ2GTUF78q4J?efLD9m-h+qX5HV[ZS5^f&=)*l\LcuU0\R)a`so0QI??'mYu!W3$^6Vs&kX"Aj":G1TO"Rt\Z+*WJ)7Es>5JbJ>U(^X^5h+&=9l8lk&srla+iV)2(4&ta8F[eLH2`#2!of[\9a>R5,?VA;`_gnQ*]-Ja4tp-cL^XJreeGHa0EbjOR4jYp%<QKkmcRkdEV1W(T:\q@D%:g3hlj$UVq6sNq,X3h5\k>+f'=WED^9@cl7Olib3PrkFqVNY!3>t^P?MjWt_JVYH+R(`7%QCNL"SVYJD!t%cJ0^^C]nXUX;.?NHT3Ga4E[q;F:h3g;$p\3"+Ui&]b*^>l#;[jXM&q\PeML?\f$4E.a&G$cpnXB%H%&Q`4;]i%oQR5IV4T7p*.g;i40[7\XgP1&[V#:I)l\Y<Z[^W(LL]L-o96[&B6B!Qh9\OqUf4g&1kJ\[ha!auf#A<.8ffSA?nOINqJEHpn9<!7D+3.LcaMZcSf&"uJH(7r$Gt_Tr4I,-a;J2^#P\jsV['g9&N2[_/h+'sFNpH>X)efoP-58PU38AfMGY^HU.sO@q?8,t(Rb`toL=(uh&$^D%le+@Hl`A1,<P!QTusdSQ0k<9TR4Wu8SlI.^X@<RF=?%Z4"(XP?.k.;75?K5a@Y8O[>Rdq6#Phk#K!OCA#RqY_&f):eO]'/q..K5asX,GAmHqD8nIB<TU%oj"*G:U2dWjhAa[\G`4q<P2"p1h_Rkl$Z1a)C*L7YMX\TV9f54/pm!/';/.lRE)=pD,rMT^mRgVb.K'g_kT9DrMl@-^'M-]!L.]p,6~>endstream
endobj
33 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1341
>>
stream
Gau0BD0+Gi%0#[%Ja,n!?:L?g,YRR<gY*u[Dsbt"rYF[?ZqWVSoi@5rd`^ae(SpsQ3OY])gGp'J'_)0J!!/b>_&kQ7-OISX+kW\,VuspT:m\[Gr!4[ff!cE#hXhiJLJCC6k>O]75=@MZ9kXbR-u?(rhEjoI<eJ0oWc3Lb7R`sbZnE-:_'NA(3DL)eT%appRGrD8k+!iFG6Gf&(r"FqI)4\mF.@-eS=31kC8KG/Wf2.$X57bLZp-;='oYmtWk%$DB@[9m%?.+drlXefErfE)j[(FZRrXm,;bl7HY$dQPUkg8.&4nuZnsJI:nba`FJOL0OQ+LlKj3.(W.G"hCo8@(J7-]mD;>R!f?'GuJl1pdNSJN9\]=rgS%Dnde%:OTHJ);FkgpTc;GC"fJgFJ"CKjOo[k6;t6:^gUD"+nF[=%70];Jt)R3#sQJ$s"/GI;h/L0+3X7,,;,`Bft"M)u?9Sc\-ms^`/YNMSfP?7$`@WEL\HCp%j"%l?QDY%+"3Vb3tRcZ:-T_.a$21;R4BsR)t>>hsEBAgT0@lN03.E7W-@]%)WO\,+WC\G\cm9Eu)fg0uZWLd1P/P[$>p@=sdB%'jQ2<\C@h)+M%?SI(4dEg?2>fZ:7?fT7G%(/LM"M=h1_g>[#]5=Ou$O:iXLP;*$X4+3T'QC;s`)T>ohVAAY"0khWRn&nMmbRuhAO[j&(MOA2W!"Xn.q0IYiX[SL7YfeA5`&OP'uDR$ZPJ!!OSa,12JoL;qqmeW"\ad=;k$PjPk%oN]B^cmF/F8Ib1Q]3H/>rmfX<kQ*!)jL3)OhW_rlf]7F)p25RhI%AFM2);R$_Cme.e,AgItF_7L*0-$8BfPn\qXS8<s*CQ:iKEjoI#$(-;e,NL<QC%r#"/f*(*\J.tSRi!>r"18'P$29?O<;$n5i[HTGl2nX/1r\r`+Sm&;??C]OKDSo^&&Xteh!<I/c.eOP0o(;<XMM;h@6HR*9>?+%Bem,LB-GA2!KVQhf654H0L3_n#oW,Brrq_Pql4XY>keU?1lG'7Jt48/*f9YI[eRD!`U%Va75HU,0Ff0KtrEI/]Z1nFH(X0rJTZZg&1pn0"-"F8QSe&A>JIe`g"jF#U=2;<"p&'Uh@;/%L?2W,Q:/Z(U3H6Y"<Cs:qZ?tBQ""'03g2d"+.RTB5l,)HE<TJ7S$Ab["AL'FA"$-\(N[\hq,_N;i`2eq6fC-$m#kK$:;o8-%`+u+parf#W^DD7SE]Y3HkYE7!YXJHNS`+bp?,9,\A&^(phRh'4m8J3YPSuS4m;>lNA6OVg3?5Dr.LQ:d`2j`&5&mj]LM5H9+:/-FtR*YSK!0C#F)b_R]qn?'9T_/EHb,<5~>endstream
endobj
34 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 466
>>
stream
GasbU?V;n(&B3Q$;]L1"7FNO,?[WU#^=3C1*u?2;>$'G&hVHZg(Vfj</g=INP_:$DABmm2-bsGQiI"l2(F:DX"GB0'LJU[<SDgb)rYFMRV3/uHkln0X9R,SHDn'C[)eZ$U>[!s-DQbIufj8nT]cqTHlD6nX-lSVem8ct<kIcF)kTVX>M];5]-H'P(=tkqV27,38jttjO0IL(5NNuqjV)_[4Xt81rm"+^E,E,L6m#fBmf:bVH6AP4&&g)1EL4<mcEd;"VSKPt^I"Kt7,K2\"LTEjJ_^<kNrONFKToX$HV,]B68o:_0diVS"<]>HnE]%<b<T?G`%k`?uku[-Y?!?FDZ$+qf;u$_77U"3C](LYM/4+$)*3pj"2oF-p!&dl-!R(f_n\_LBB&^'EQoEe]KjFY:[[-f(9Db:RoTQ`Ae;O*qLX>I!Su(pU2iDE-&'I.;j7@[DI6ZU,%#Y'`0Y$`~>endstream
endobj
35 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1122
>>
stream
Gb"/(>u0HF'Rf.GgmF'ef9T/\V3o4,pFFoS\h7OR!tQ/.5]te[dj\[\s*\P6.MS"6D1RHO`o@'d4nn9t$R3s9"3&$%*l)J/.c1d[*SPp,L2'!N%)0/CG9rKfe'qC(Tf3P4c'jM.Aj]fN?<318%L<i4%8,WOkfF6b[O4c4J?"G4BqX8@Fo[8#KhcoHfnbeWk5[QS?3c41`-h@-\iPB8%C?WlRnN9R`lKjj^rT%`$)^YEW"q2cic9!*(h*u'b.NP?R&TS="tmn(*:YNO*VgM+U$XoE\"^#QT']$sO1Y13%CVQJCXl=k<l<@c%9UR+hb.\n`[/$$C3@^M724VRTNLd*Y\L0a/d&FtMR^Le@;Ip`XB@pMG/@[UiA9G9'eK/Ks&k_$iIWi'hE4,nP"n#$8gbG3OG-I:3:7&u`"'\+gV_:LZ-n[=!RI>cA&WN)Rj`Hu.0PO=QX"L8hX-XJ)kqU^*)F)=:Pd)fc]p+/_Hch`G-Z)Tib4;0(FA,_Uang>aQSmJRUX8bKDjl<^NPLc+;[-J.)V'SC75R-O^BN'3V8E&(R<o!6otW0,&bZ/<"blDM,Z=0o>6WBO)kU7"1s>Rq`GNJCZC"eSJiVo(R+Z(lOqp`Kt-2105mUgM"SU%NZ38LXAMi+SV9;P[Kct)ZP&&"fSm@i&96Y&6)k??`J//=P6a+FG7b=q9DohKB,+8CZY7g=0\@gaZ'7((;A*+<0u$Dq'dQarZN93'*/Af6.`D&POW>i2F=EEs&#57hJt.E+*Y9K,6SYu'C[SU?:kotu,M=oRWYTE?iOd/GP7B(Qjo)G?QRM*JJc5m*#dh;`J\DE.7NWB[c9`7l8cu%2U$C;sVq`^Ka!5OlgJ6n\/=0(N^#F]LI,CI6":CHt(PSqdn,rs('XGsudF*8UG]^*?FK?f(ZI-m,@2B.9.Yn/Hk4g,kVK#k]8%ZVi$QIq:?I=--P$>l-6uA^BbUSueFJd[[e:A-e<.74Gf3?I+!Gg'Yn2/&hp'5sN-_TZ"'usN%(%H1[3V?;c:C^S[U+?R--#W0PMWkUcl!fd#FHfeVXbp-pQ@8^3e$NDSQ\D+=,WgJ7=k>J9-JR3q<l*>;Haf11lPAJXG\Oq^3HOmY#q5`UdhM,Z~>endstream
endobj
36 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 545
>>
stream
GauHJd;GF-'Rf-pNd,;"iF3]RUs15tg@eE]lbm:>9Q6q3YVRuT\n_7/#KG>j;Pe,?1?'-eT6m_hY%.ms1toWE'sL5Kp7?j?8CsM51.?MKGH(2YTtb50!Jus^Ta5@'BG)V'SJ`bo')UhuNq(9tLP4;?,`2GWG`$"4$V/'lpWkZ+8Sj\9LjdbmMc8fY0H3UUO3/RR%:*.[$EoH?F2[qtCCobM2=q4+"d#>8";@S<ru%,djlC(+[7GVo6e6"S,kV%ice\T<[2nNDP^H\me_U.SjnYg6(.qcVm1<;ECRU<c1'q^p?$f`-E\I"^EMFSlXkN>lf#jN7V2u[fOJ_<PqW.=Z;I3eHcR,UM>K$t&8+P)2miQ=*%CE[u*cp&n4g9CaAsL`oXe1N+AL5[c&#EaGOG.h\5B?MD>IS1#XS>R,mnCC+'q/Xm"fTu.htac:qa)3!]GN`LiV[;&_cB+]H_EZ)bmk6")n6:T6PP1b(GiRnPrVg`AB!uHF=e?N>.G,2CK4FhU3WWAO(L!lQ[aeegeH+"%W4smo*\3Kj(&~>endstream
endobj
37 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1622
>>
stream
GauHKD/\/e&H88.EQtDg6PR4;;?s:-;tjkpg*HBB!DQG<f#<q,M(1qS9n3,%PcRED<I#UH#`6eGn*YIG00EBFdUVOVM\P+2%*i0`@+d6G!R!?*Qq^sN48Nu.eN6:00ad<p!oNSY.ia8=7q0@WftQd'i<6"kg0fSoq>mi`,+DWVbq(.W2V?`;qE'7l=,70-cXDQ^`9\qsehDHJr*IgXcaOSODp%hR%Aqlr+E3W<81f)]q%0(0")!TMs$22u?f>IX`.]2N2%$E,E9gPBl.@TTf$eLaSECIB2hZkd:]S*^i3suJ!cCkOj9p4O172PUCP5F!R!<WO5i4ClgDTZ*grT)U%:AhEXMEYEmH7$07U8J`@/iY0hXVA%[R;5NObB5P8HEBR@9Vf0c3f6jP:T00.AD>P4&%HkipQ[-!IPp^dHQUR%;HLu3R8u?>fIJbPR-=u(77\4Fj<Bp!.N+:FSpV6nV0;1'Ma.O*0`!LJ0G\?9[fXY9fEs9o0[9C$ggH^0I;5u<JE+?`Bfh%V9XK]7BBV93>3!QPHa/36<)$N]f_t34l:R-8nSN_9]@Z5<GL?/Od8R4<M@:8'oXa'V`NcHT])<":?+Ye'k:Ch2A@6jbSEXP=N,&].'T^U_#`K]nBo(EpLhlpTFOuYeqIJMJ7g@.Lb,3T&#g7!0ETRBH^hA-]?Q*1H`PBo0B[GFX>lPd[d6F!L5'QuF?j0QGQ2fXOd\W>KG0@qKi?_0JT14>CoXDi@g>;8%gB8m@t_S`\KO*5ghD'-5-:u!,5U1K>CeeJflGmjZ#&i>g9L#5V);Z5fup_,X+r\S6613\T&a(S&V(nu.QL6V"K,M?[$:fsI?krlVB>TXp()GcMkZ[uhp4bl9AdYG4502o3Qj0QI4`_g89n/]\/u?_ODUibCJi^Cg$[&J\i:`T;Z"Z.PffD/P\PBFTjI1_FV<_N$X:3aU!+]+e//X?XqfiTs.4YQq!7&&h>(^)"mLK4JI].cTV.T1AB"8LPnc/Pab0f.&I]p9\+0<!9GbY<75&\f3k+.Ma](-uG]<uCQmYT#X.FA?W&R,G236AYmeq6T2&eka]9u9WK54Sn`/$J5$^b6qT[O>DC%'j5?d^b@/of'37(pgG;0=:u"36$;+U,'p!iD+\KcEEZ4"q"8E$;D\Y+HW4$5-<Fkt4DSEB06c42:&mT\7$L%:;^AGQ$Uhl9nt-N=SH<:MDqgX:`stHXN%t<TRDkRi7$#eNV?QK\Y"Gq?h.CR:r<"`2r[@@TerR<($#K'36K"Ki'Db.-j/n%J^*4C;mKo.pbgdI("-Q`u0>kB,TIDbO;9I@jJ!nZ[SsR["UD^r1rpbdD>2:R\(YaR1S]pQAhdlX;lgtlb:3G-u$9lBGa3%+aeln:7D^SN*E.8Q?.?S9%-ZnC=lAF1q[:IQ`>9_;6$n%cuBaJ90n3Pa%o&;>U``1:G_L=nf:6*jPou3UoO85:WDQRGQt@T55\QsgATf)<--(JoXd:BJl%F>nF6&7c&pu$D)54gpK*qi-8<soZRmjms#+(#>?cC^WNi6F;ThPjFkj$/,GL<q/Y_?%UumQ#81bbmN>$.@/K9i*JYAAkOl&I`hh,l/(n;TL9>0qtLaX7f@`S:B5B-6*'q86>~>endstream
endobj
38 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1632
>>
stream
Gatm;gN)%,&:O:SD#G""j3T&r&Xg,jini*Y'5O*Sc#d>*Q6Ck\%74*Z:B/!8hF,ML.4I,EU1!>Z4bqYnQ6*P\H0+cO"*cnYb:=)c!g>_8![iN?8,+buGs(V3bD8,T64sgKiRm[`-p^(fG">$@5mWJm]7*)^8UUC?H@CI5?73rB_[lrA)8^iPK[J+=?EBNC3.o<S-Kd;H-bQlVp#GV0NOmqrpoOSFnm(8>_<-00<m]4a]Ma#Wd^ijJ(OUCEh@('U!n)q["-6(H$F>,kAW5Hp@,s=E7U._f?-Y:%L-\St>Z.5=)1>nu[V@Kd5Xb\]#*LbE%t[[g.pA0<QMu\&#JQm`VpoYi7^4>[k"NMBJ1Y%Wa6g@fE2+kQ6\_3gZs[\[htTO@6bi!f4-r();ue?J?45fp.S![]^(:sS,i#Aqad>W0SAQpcJ<Ft8681%`V:f?!8aL]lmehr)^R>,Yo_S'dd1iIQ-`&X6OpSst0[#\P_rUH+O/*0R=%fD,dJlFOmQD3l;snNPB8.pnM..;\cZ3nqa;E@Jp-G^kI.4ZZiW>^=?L^J,1b?1glX`h0Bu+-"a(t9Hi9G0?Jgp8@Tn)8cA4ghEc7n'$P`$9Cko/43!rV:-4>*%>;X,.0@>08sCGmu9/M122NZV/:Phon=E#Wc0E4duV68EEf*k,ZOZXdU--+d"UWtc_3@)Za&Nn4,X>akO^+dYO?dH(EdS+FL1VX8n*\7IZ8G[KDnYbK-<ndiWQK*6ZmM8)u+&B:igroHK#Ef%qZM9BpbVrWfu"r&E72nLU/jHh,%nONf$m7#o\6qJ=S+Q+W0'8LH*YOQ#KR/,p$Bs5O2\D(hiA5'i_J^&Q/jX'F>Z8[f(5n8NUA:LY_+_5MldN3GA<Rq.hZrL`L2qTTp!11@2H=^SR+=Zo=e6PcrkqH72Ct#OYZVGO`bWIhc;ldA+K!fW[4&Ds0%MoaG:(e\s`oRE6qcefQ;TJdub-4nE&p#u]JW%+tSNEsZl"GB$UnRLo`iXT")"+4/G5t!KRHT!(?MV4nYP$FbFnem^!YhQ%/-`J+EDV3#-)j%,1`jEj^((ZGW%;HL;DUVnk2AQKcT%Gd?^/6[P/VNb5!:u0<(&3$p:-0VgA6[7K56)%kmI<tPT;'H)u5_rV\]mc8j74[Vh(bO@/C5.4g9?@;;A'd"gVSd%&N)7&Ik?!PNL*eM2Cp!lS%L21;'X`*%hVrpI[!?WE8Ft2.,"i:rf]TB'b"`,?!0IJq";9N[3>[O8TllpAN4Z;G3t0YS=HJ*M]W(qh)c2B0)\s>h>ChB7$kdXZn>%_u$oc=,O+&Q,g,t`BJ]JLAb,;pn>YI<Y\)^YB"eX=-O49*OWeOZ8AesHX6/m.[o9R/@>;hiuFl1YKH1]&pqTci;!0=C-Y,QnZF^'G:Le$`*^P5W1gAYcADup$J,JhEt@7PY8<j[<uDP\f*@qf?Ln!g=%MeZTlA&rm(RafWF0O<qC@"1-Vg2a[M(RfT78nA6<LWIBXVeglqkK?3TYE3gtog%Na]t=#EWe2]2N5LB)C*'PT@22fUd8aE7doo&)*Sk@6<TFo[mS6jn3<>@5AYm=N"tf;3+)OV@`A:"@5Oo?Y_gP]H:5_mCP^^N(Dl-95CH[5r8P?]\qj8$7q!W~>endstream
endobj
xref
0 39
0000000000 65535 f
0000000061 00000 n
0000000143 00000 n
0000000250 00000 n
0000000362 00000 n
0000000567 00000 n
0000000672 00000 n
0000000787 00000 n
0000000992 00000 n
0000001102 00000 n
0000001307 00000 n
0000001513 00000 n
0000001719 00000 n
0000001925 00000 n
0000002131 00000 n
0000002337 00000 n
0000002543 00000 n
0000002749 00000 n
0000002955 00000 n
0000003161 00000 n
0000003245 00000 n
0000003451 00000 n
0000003657 00000 n
0000003727 00000 n
0000004008 00000 n
0000004161 00000 n
0000005204 00000 n
0000006563 00000 n
0000008180 00000 n
0000009532 00000 n
0000010414 00000 n
0000011807 00000 n
0000012573 00000 n
0000013924 00000 n
0000015357 00000 n
0000015914 00000 n
0000017128 00000 n
0000017764 00000 n
0000019478 00000 n
trailer
<<
/ID
[<e1bbb34f5161d1e1554d7a62d828d476><e1bbb34f5161d1e1554d7a62d828d476>]
% ReportLab generated PDF document -- digest (opensource)
/Info 23 0 R
/Root 22 0 R
/Size 39
>>
startxref
21202
%%EOF

186
Les08-Slide-Overzicht.md Normal file
View File

@@ -0,0 +1,186 @@
# Les 8 — Slide-overzicht
## Van In-Memory naar Supabase (10 slides)
---
### Slide 1: Titelslide
**Titel:** Les 8 — Van In-Memory naar Supabase
**Ondertitel:** Koppelen van Supabase aan Next.js
**Visual:** Supabase + Next.js logo's, BLUE achtergrond
---
### Slide 2: Terugblik vorige les
**Titel:** Terugblik — Waar waren we?
**Bullets:**
- Stemmen werkt lokaal (in-memory data)
- QuickPoll app heeft 2 pages: / en /poll/[id]
- VoteForm component ziet stemmen onmiddellijk
- Nu: alles naar een echte database
**Code snippet:**
```javascript
// OUD
const polls = [
{ question: "...", options: [...], votes: [...] }
];
```
---
### Slide 3: Planning vandaag
**Titel:** Planning — Les 8 (3 uur)
**Timeline:**
- 09:00-09:15 | Welkom & Uitleg aanpak (15 min)
- 09:15-09:45 | **Uitleg concepten** (30 min)
- 09:45-10:15 | **Zelfstandig: Setup + Queries** (30 min)
- 10:15-10:30 | Pauze (15 min)
- 10:30-10:45 | **Uitleg INSERT queries** (15 min)
- 10:45-11:30 | **Zelfstandig: /create pagina** (45 min)
- 11:30-11:45 | Vragen & Reflectie (15 min)
- 11:45-12:00 | Huiswerk & Afsluiting (15 min)
**Extra tekst:** "Jullie werken met de Lesopdracht PDF. Alle UI staat erin — jullie schrijven de queries!"
---
### Slide 4: Van Array naar Database
**Titel:** Van In-Memory Array naar Supabase
**Links:** In-memory (OUD)
```javascript
const polls = [
{ question: "Favoriete taal?",
options: ["JS", "Python"],
votes: [10, 5]
}
];
```
**Rechts:** Supabase Database (NIEUW)
```
polls tabel
├─ id (1)
├─ question ("Favoriete taal?")
└─ options[] (relatie)
options tabel
├─ id (1)
├─ poll_id (1)
├─ text ("JS")
├─ votes (10)
```
---
### Slide 5: Supabase Queries
**Titel:** Supabase Queries — Vier operaties
**Vier blokken:**
1. **SELECT alles** (met relaties)
```typescript
supabase.from("polls")
.select("*, options(*)")
```
2. **SELECT één** (filter + single)
```typescript
supabase.from("polls")
.select("*, options(*)")
.eq("id", 5).single()
```
3. **INSERT** (nieuw record)
```typescript
supabase.from("polls")
.insert({ question: "..." })
.select().single()
```
4. **RPC** (database functie)
```typescript
supabase.rpc("vote_option",
{ option_id: 42 })
```
---
### Slide 6: Server vs Client: Wie doet wat?
**Titel:** Server vs Client: Wie doet wat?
**Twee kolommen:**
**SERVER Component:**
- `export default async function HomePage() { ... }`
- `const polls = await getPolls()`
- Data fetching
- Direct naar database
- TypeScript compile-time
**CLIENT Component:**
- `'use client'`
- `const [voted, setVoted] = useState(...)`
- Interactief: klikken, typen, formulieren
- useEffect, event handlers
- Browser runtime
**Zeg:** "Server haalt data, Client maakt het interactief."
---
### Slide 7: Pauze
**Titel:** Pauze
**Tekst:** Setup + queries klaar? Na de pauze: /create pagina bouwen!
---
### Slide 8: Zelf Doen — /create pagina
**Titel:** Zelf Doen — /create pagina
**Ondertitel:** Het formulier staat in de PDF. Jij schrijft de INSERT logica!
**INSERT voorbeeld:**
```typescript
// 1. Insert poll → krijg id terug
const { data: poll } = await supabase
.from("polls")
.insert({ question })
.select().single();
// 2. Insert options met poll.id
await supabase.from("options").insert([
{ poll_id: poll.id, text: "...", votes: 0 }
]);
```
**Stappen:**
1. RLS INSERT policy toevoegen (Stap 4.1 in PDF)
2. handleSubmit invullen (TODO blok in PDF)
3. Testen: poll aanmaken → homepage checken
---
### Slide 9: Huiswerk
**Titel:** Huiswerk
**Verplicht:**
- /create pagina afmaken (als niet klaar)
- Validatie toevoegen (vraag niet leeg, min 2 opties)
**Extra:**
- Delete functionaliteit
- Styling verbeteren
---
### Slide 10: Afsluiting
**Titel:** Tot volgende week!
**Tekst:**
- "Volgende les: Supabase Auth"
- "Inloggen, registreren"
- "Bepalen wie wat mag doen"

BIN
Les08-Slides.pptx Normal file

Binary file not shown.