diff --git a/Curriculum-Lesboek.pdf b/Curriculum-Lesboek.pdf new file mode 100644 index 0000000..5585058 Binary files /dev/null and b/Curriculum-Lesboek.pdf differ diff --git a/Curriculum-Lesboek.pptx b/Curriculum-Lesboek.pptx new file mode 100644 index 0000000..05ad8d0 Binary files /dev/null and b/Curriculum-Lesboek.pptx differ diff --git a/Curriculum-Overzicht-Compleet.pdf b/Curriculum-Overzicht-Compleet.pdf new file mode 100644 index 0000000..126d1e3 Binary files /dev/null and b/Curriculum-Overzicht-Compleet.pdf differ diff --git a/Curriculum-Overzicht-Compleet.pptx b/Curriculum-Overzicht-Compleet.pptx new file mode 100644 index 0000000..eb3dfba Binary files /dev/null and b/Curriculum-Overzicht-Compleet.pptx differ diff --git a/Les04-TypeScript-Fundamentals/Les04-Huiswerkopdracht.pdf b/Les04-TypeScript-Fundamentals/Les04-Huiswerkopdracht.pdf deleted file mode 100644 index 78fc827..0000000 --- a/Les04-TypeScript-Fundamentals/Les04-Huiswerkopdracht.pdf +++ /dev/null @@ -1,250 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 7 0 R /F6 9 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 -<< -/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj -6 0 obj -<< -/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/BaseFont /ZapfDingbats /Name /F5 /Subtype /Type1 /Type /Font ->> -endobj -8 0 obj -<< -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font ->> -endobj -10 0 obj -<< -/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -11 0 obj -<< -/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -12 0 obj -<< -/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -13 0 obj -<< -/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -14 0 obj -<< -/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -15 0 obj -<< -/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -16 0 obj -<< -/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -17 0 obj -<< -/PageMode /UseNone /Pages 19 0 R /Type /Catalog ->> -endobj -18 0 obj -<< -/Author (\(anonymous\)) /CreationDate (D:20260303130225+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260303130225+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False ->> -endobj -19 0 obj -<< -/Count 9 /Kids [ 6 0 R 8 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R ] /Type /Pages ->> -endobj -20 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1134 ->> -stream -Gau`R95iQE&AJ$Cbhg!d_ho-S;8+U0`7qF,3(Y@%QbB?K`0"Pn%OF,+cl_/=/1Z1Kh4>A`%#I=]kBh/]%N[dGrgbYB4s]XsD[p#;EXuD1$-aF@oj_X]^Vhs5j;Ne2%rY\EiJTE+*jC<936O1\;[q"a!ITZ7mP\;,nTp^5I+PTO#A3dhAVn!ij4N3kquO4Inlb[:)=$65(R6S2*C,4"RHnrLJ;MC0c&5N25%66p880G"o6GMLN"5tL!s)EPlJ;krgEF(cC7i[%s8&ur\ja>ks2;U^?IjuT`QXB*dX%ZNG7q>F$R_k$3Fo-eH:LrW(\2/qC"rZ6T"rBf(m!VGH8PG5&(l_ZK:#5kG909FE#V%2P,PtsGMI!r>)R%j@OM#E/*.Su[lH/-EO&?D"F0BjTh;%cFJ?:2:Hi4HDs9BQ_h*22f^A-39^K]f^qX<)_3(D\of]]O;6a"P9ud+c3TJ5IAGA&82[j*m>bhgdY=S'T^Hd7$BWrp/Y1qYY@jfu)BG(ic,!CD40YN0KGpQQ6Gr)W1jH3/cA45VeB*K[=SknUFpk_W:R:5Oce=>QZlrYo12Gl/kNfmh>u>+1Ej\_>~>endstream -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1140 ->> -stream -Gau0C>Ap!%'Roe[3.3$:4CPBXZqoA`C8IrbW-"X_74_,K*'=c:-eeLpScMMj-Opnd6Y$=4B$U4dF+o4,(N"ZN%DZN`k8u*K#4W.!5m5,uiI*i#S'c\]$40dm6,5Je"d?H5G4hAqb!CJ8_fL#0Qk(@Y)k`ld]@H%*D16i^97/&_=5=M8S3agE>M6cGGNFH+as;UG/pb'0BIi5GD/LG;I61fjH!6Y`>rT>AO*PkeRKrSDJQO9=gIgJ7%e/#TTMud+Bi*[rRKtG"^jk&rM9/"%-nG5;1u+1r0KF2.UP(!Z;4d-)'OQUEFe8aT#(3I570]7A2GY16&8tBp7]5Nb=Ekf_K7935I@r^#Ult"r"[-I+1n':dR1'Nc?Tq+67XP*J+(lp0=5>@)$aV[4^&lD/<-hdq63TU#H=n,-7R-(>)W5nai(5W#A4l3.%?tT9%afqpUe`3ZUGUL[+'ej%^]Uf@sJ7>I(^,=0'6.c_9,VlDDC4Dit70Wu:@t4CHXF=*9q);h*[)),;u\XL$No0>p9PS%SNEmendstream -endobj -22 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1271 ->> -stream -Gau0C9lK&M&A@sB9Q/X=cj96(Zt]C\2k-.47[l>Yj$#,uD\CNJ+mYo$95nbq&A[u"pZ^5ac/e9p9HR[=Z&SSH-+&966p,/1^1c]Up8?`JKY>XM/0G>JD]u\SM*4'(QSC_a`*C63:Zup2c8F0BWXRN+e2k9VC=KZg:kOIHE?"UUFB^d^0#HJ^pFX`\RX1%LMs*=_18!Kp?mjrICO[7Jf%Ll;RAMR9`Sg,k\8>2[$EPlc0aKh1k8_NHD^rj)"\+niWQsF8Ms+mIo$;\Nfm$T""n/i(k^%Pp7_6;m,c$A3Y.NS4rQ[cTF,MLUFdTZWJ87digeriL!!T_fBZ!9HKNkDlm\Gks*';_H4.?d!'dmj1=J5@BqA*?tY;s6;a##I/d8Z7cXO.r*_%KjOM-5D;*fS/[QJ"bLR&I+@2ZJTbbu@Ita)-i+6U&?ELMTBmA_JQr0;Jl^:l!#-970^m6IEWqAP+N#>TUiufjVhoaJY<'@5"Y`_ep`B_bNYV2>j[k-`Pda)*XHb]_BMV8ES=Q4Na!$&cMg]/<9\r't,kSb]`/E]G'&a]#%3U.]KC99WKquQ5WJm'c8^X>>7.*.b4BWi(o!4G`oQ;9k^H-3Y.kMk8JVj2tBqZbaR&Z^Stibr.`%C$]j4jr$4e)Dsd!Npi%nAa`42okIrI`B(i3GOj[#tQFt`50k=Ca\u8;"W]*[?S%9+#@Bk#O5-h1(l^OVDb=3)9*X,D-SY@mCN,t8c%6Ts1'Ij>di\5Bko$<+S7l0s?unGGBtJU=5G-`Ak+8iGIIJ>oK(sH6FR\NBBMd-p1[@#9jiPr^dd5*YX[$=6Br_IW,UcS"fnBqQ[+W/^nX8K7endstream -endobj -23 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1324 ->> -stream -Gau0DD/\/e&H88.EUtQ#86&N]?CVH,)Iu8=1i%lX)[?'F/("i:R8cbX6+ZHummP9X?_YWrlki8;h2up-h=9p,#mC#HmWZAJ\=C@82ZuYWP8Oqs;jb4MT-n8Yc:Fk$A:Bbr+f'5HB5n^G16_urlb^Rb_bh@;Gt79BbHDPC"CY.dGgW/Fu+tbOphULkON@]k*qj?#dTV,MA4jCK4R!#H6b.o"!0Z_jU`f\JCGFC,ZtN9KcmnOE:1)A6kMM([%jcCgrSMXi_HtQ8GqXf+UVa^&6+Ig;mY*,\bt:!SMH"i02*(J:4BkIPOF+jLNf5MVO0;lVUtptfbGsWJr2*ni_YgC5B6(2;^)KO/L`[9eN+efGa4[6cbBBqB2o/t@L`2J+36m+h$pGh3)TL^U)3"o=i-UgG==+E30#6]KQ-oif`kO;[,Y>I8(PZW@-_2O\JD0Vp>QK["19X>mI%i/BVr!NYfPC869+/2Uk6OpmV0lip"dU51+\@\#Trs8Qi'1CWCGYWg36sn]qsI/hRX9&K:!>1+VhNq@#7uJ9"n!f6!Ss!gi0[@'gc?G;o&5dHlDQjCkKq`-FjUG(-;'f$[QAt8@4=&AL:nu,7,Di.!rY((VI@Mp:1:R^!+WQr=loEXh-rf6*X8=4;Ipto"]VY$&).m2olY8QuG6cA4lh@,RE7lDm%FI7g1e^_j4S!SW_cg^0B!BI%OF"TK0h)Gm_YKq9fZhF9&i%\U.>)PE0MB8q(G5UdB>o:0ViS<\3>js0[-U@r#V0LJ0U5T2%6$?.bgq2s0e.5*?]!D%diGAeboPH2(4kC'A'q46nh8SNCpV1"O>;aIem]BAuqsjiY$d!p:\-?UG!1(YObl~>endstream -endobj -24 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 664 ->> -stream -GauHIbAO(/']%q&mZNSc`$RUAd4D;3e4S7R,W)i9j2)7F'/2:=X8Mo56`U?Q-Asdf"O__nSo\]g;]pB`o1]UClPLsi$IjWO!S"M_M!s:L*^,Kr=6YM6e9;l$SZ.Rj#Z3\nCj,I?Q-_0oK!!+R=6FF>"EeL9eLf+7_#u!t"Ku@GC3m"U$];G<^pegOC*-*JdeNSh"aE8q4#.gdf$>!08on>,giG%49BR;QC)W1kl5'b=9NF$$8?oXQ9*O(j1ooCPH6mr0fa97g8do`L8cE*QKc7'JUF)QPQfb:Co%tn6p=YTM9hsnFl1D$C/AnA+L9o$m3qSM9%=Bd;tbC"8N$KWU!/I:l"/P(mJ9$QfW)[c-B%)&4tG9M]fVKOT6#9h[>qBO9tl&eWFCgD"1=1d3!Y8Jfe8b&I5"jE7iBak4UL&#<^EA/mL3tl]RE/W&lg8]\l>eKrquH`%W4_endstream -endobj -25 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1157 ->> -stream -Gaua>h/Car&A[3#=54Gr0O3@Plsrn>W.RZT$l4qdp6)7Yeuc?8PCePaj1.-df;7`cF_E1$e+htkI[@\$;5;_n8NbI[j)`0f:U8[AF99:f_3`!s8NJhW%d>:80C%Yk]*sZ16ZGsB\ic$q9SGT^:rJ7q5;od"<8Q'9$:Po0?GdNH>!ioKKrQFkWa`6?5)_GDUuFZqRA(X3ZETO(]hI/%F[r=q6s2#qm%dek$gPW]Y66(FnufIS\.p\6$c.1l%="`J"#68S1355jRVrJ)]"-C/lakI+7bipIm\':bU:[Q^/a^f%QeiXMnUH5Jn&+IkHo3C4FaD"-\XcF$WOD?5gnH1f!E8N@2$S!=A'CC@afCOViMXZ8iM_8nj/]?@1q:h92URe5-+q/!X/=1452T&KM_1!c(ZOEGLBblGZeV<[#?gF'HZlC[$:Ei1pf_FA#KP+"Vg5ZTTA38TXmgNgk)oFTRqgEhs(Y$S1eHNm^WS*4=EajZPi@+9&&Npp3B0N'1E&tcL$X61!a<\D3Fa6Xn>9^jr!kW@#KGXn/\)d3]K\i[+QuGKZ\6S'14QgdgnUt9"Pk0X1W+6_m0d:"(Q?]HrO:'7XqpX(%?NucQdOKKN6uD9r6h`IC\3u''uV2_[F0Q8r@d4=A,S('-FE>HY!mnEmXlGm#08`0T,fu311]S4KHIG_$;i1PrU-DP^R8a*(k[[\Q>\"&A`XV(M/?&t4He)GpYNjGI4F4N0p%?)n`QjAH('/p:52t6=p>No2<;t>6b$GTaQ="%/KP:W\T=u>_#@$CI^T_n:$m''#aCHE[9S;2/qj(lU~>endstream -endobj -26 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1307 ->> -stream -Gat%"D/\/e&H;*)EQs9T.l'TD])iD[g8o/g@9]nN7uXOrTkOcj'dc#AXfMQJts*M;N*,:PVI6gZ\1!8aSlopu5-+YA6@`/!spISV=E'0:Am1%@T^KucnX5\Z2#-4bHH>XTg_p!D+@3kS-W'"EcoJL!u'4$G^JfqcV&6&<9!8_f!90DItV-sf]*:Qs.^JP`\q"VM#Zm.t>AVF8Ijk`-"G^3OLX3qcRgL2M3m`M_78N6oM1hJghHBa*D4U'=Z8N2pbIq23cHC.SA=q-ta?X'`\M*3qTNQ=st>=>\hP1n#.iNKcQS2?\Tg8i;:'KIXS[qHq*;/='*g-6_P:6b7g?2,&q*dhONW;:"0=f8Umh**-"r0hlT$4MjmUI_C1(l1)B&rD0[T+k/e4*8?E>\+X;MLjKH<)[Y.0Vj+u$5!uV+Nqh1obCX;1NF4rokXWIMAu+X:MJ;]W&ZY3>-Zd8T/4^tgu&tn7^4Acb8'1d<"&q1U7/TS&enWBC"14+'VfCFLH(,4*5g)mZ#&diiTCjKZmY<.r!_=fNbG":bR+"VO7W$IFr5*hKB[fBXZ.SZ6Bm9JZ=jnX^%Vd+l-khJkiq>HY\XA8G>2-oNM4&mI&qV(j?mEe0JYY8_6=ikmlSmElGL\L9,Hr]%s4PrV/7!+aYSrT"J6*V0_mi'&pt,#?'5GeRZ>"Ao#J-K(TC;fm`MmTjHqdQ50>B3R$8@;08qY6818i0H~>endstream -endobj -27 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1026 ->> -stream -Gau`R>BA7S&:Vs/f\li4LT8qUG%[0C3bb'NC!3L7M&52#NZjN_=im*&ld&aS6^oF_5*3[,-?=:3h/@5(6m9F&L>QGEn32KB'+0si2?kXeJF"6[=mr4XgFlPn\7,luKSUm^\?1N>MT$26A$M+08BOh]2I+ok@@L5rO\pp5_W*%:^hFY#6DMeXT*;t<4U/ZY,ctF(,=G?;S@i,k@&n`@fP\3,Q*`VJH8i1Ij)9?=h7p8YCfBcDATZ,KBnkdTif>3g*Q;:)IRdWNP2N.b_sAQ5ZD;,=cUTTf^%gKd+$Pcsa=>M=MUjM./_DDImbhW:W%XBIb`8C7+)'o4q;;j`9fLJqK:"-<8]6iIVM;JF`#KA\^?9X7qYE%+@(ObWiL1USJ2a(L%b&*LjrXFlHkUEnj`:,:f@?gDVg3orY-k[mg):ZOld1)on#`UIb+eKEe>Y!$fk2IL0ori&&J4Ej&%nuT1rl+KJm0<4jL_nXh+a)*`-0iS;/JfQ_sdU%u_=%#jf(.pif]&Qthf&$"&[Vn40NHQT^kFCm]=[kLWPFReBg3,d0TG0[\e2PGNe;n*:K8)Dl"]6%)CDTpMba7/`Qjl!=]:h!LenpD*THi"8m/WJ=\:BXB,E]]kOE?t&WQ4XJf`e@6oQE:=IW/SV+hET%P&k?oLW9Sl?qdJY4QPU;G?h$71Mp!b@34"1ZXIZMI=*]YS7nE&6KAM*jhl>W~>endstream -endobj -28 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 979 ->> -stream -Gatn$hf$st&BE]*!7[&\\\-6OCh"D@<=ZQpJY#8m;htBAlHlr@<;^Kq_#,ecfh0ODmE=d@L;4e-s,P%.OJ_g3]iN8,k2NX-QsXDP4osJ0ZGOLiuglRQJ=djaU&3nQd(\Rh&7;;:[u`"-k5o8*$3#PUZVlcp28CPMc@5p;FKNQ;Ih8(qo)8qHmnEhH1Y!XZE1EcTgK5MT6?lBU!XX][]R\!S(%^EKD!gA4$L?MWXb^A:b.t4qSTRoeAg"`i^>D=@M[M<_C:Y>BP*&I7iu56d]ET2Lj#oS3YoClML56V!#uq@7q8_cKO!OM"+I4Q$bH.p"?pQO@D7V22+!Td:c0"_O\PiW5o?q1-FXWGl,nB4!(W5/S6Dij>a+rHRSYQ(*/gi5r'/^0lkH?R+KZ[2_M^Xpen1>(nZ_UfAMpOr)Z2AIKl;pTn!h/6]76[l"H:X5]F@f,Lu/+m:42r7:spp0=cL\_h'p"9c=S=[\.)4A5uH#9]>Am&ggP:EY-R;kKe%"\qo:0R's\rg[]WukA^oK'\#h73j5"C!FQ!cLP8L=U4gO/IknXm0;5MK*8:o6hendstream -endobj -xref -0 29 -0000000000 65535 f -0000000061 00000 n -0000000142 00000 n -0000000249 00000 n -0000000361 00000 n -0000000476 00000 n -0000000581 00000 n -0000000786 00000 n -0000000869 00000 n -0000001074 00000 n -0000001151 00000 n -0000001357 00000 n -0000001563 00000 n -0000001769 00000 n -0000001975 00000 n -0000002181 00000 n -0000002387 00000 n -0000002593 00000 n -0000002663 00000 n -0000002944 00000 n -0000003059 00000 n -0000004285 00000 n -0000005517 00000 n -0000006880 00000 n -0000008296 00000 n -0000009051 00000 n -0000010300 00000 n -0000011699 00000 n -0000012817 00000 n -trailer -<< -/ID -[] -% ReportLab generated PDF document -- digest (opensource) - -/Info 18 0 R -/Root 17 0 R -/Size 29 ->> -startxref -13887 -%%EOF diff --git a/Les04-TypeScript-Fundamentals/Les04-Slides.key b/Les04-TypeScript-Fundamentals/Les04-Slides.key new file mode 100755 index 0000000..87b7264 Binary files /dev/null and b/Les04-TypeScript-Fundamentals/Les04-Slides.key differ diff --git a/Les05-NextJS-Basics/Les05-Slides.key b/Les05-NextJS-Basics/Les05-Slides.key new file mode 100755 index 0000000..e601476 Binary files /dev/null and b/Les05-NextJS-Basics/Les05-Slides.key differ diff --git a/Les05-NextJS-Basics/les5-quickpoll-voorbeeld.zip b/Les05-NextJS-Basics/les5-quickpoll-voorbeeld.zip deleted file mode 100644 index 7009fd7..0000000 Binary files a/Les05-NextJS-Basics/les5-quickpoll-voorbeeld.zip and /dev/null differ diff --git a/Les05-NextJS-Basics/quickpoll-starter.zip b/Les05-NextJS-Basics/quickpoll-starter.zip new file mode 100644 index 0000000..33f8e13 Binary files /dev/null and b/Les05-NextJS-Basics/quickpoll-starter.zip differ diff --git a/Les06-NextJS-QuickPoll-Part2/Les06-Slides.key b/Les06-NextJS-QuickPoll-Part2/Les06-Slides.key new file mode 100755 index 0000000..024b692 Binary files /dev/null and b/Les06-NextJS-QuickPoll-Part2/Les06-Slides.key differ diff --git a/Les07-Supabase/Les07-Slides.key b/Les07-Supabase/Les07-Slides.key new file mode 100755 index 0000000..52639a7 Binary files /dev/null and b/Les07-Supabase/Les07-Slides.key differ diff --git a/Les08-Van-InMemory-naar-Supabase/Les08-Slides.key b/Les08-Van-InMemory-naar-Supabase/Les08-Slides.key new file mode 100755 index 0000000..a2fdd31 Binary files /dev/null and b/Les08-Van-InMemory-naar-Supabase/Les08-Slides.key differ diff --git a/Les09-Supabase-Auth/Les09-Huiswerk.md b/Les09-Supabase-Auth/Les09-Huiswerk.md new file mode 100644 index 0000000..b476adf --- /dev/null +++ b/Les09-Supabase-Auth/Les09-Huiswerk.md @@ -0,0 +1,95 @@ +# Les 9 β€” Huiswerk + +## Verdieping: profielpagina + eerste RLS-policy + +Duur: ~2 uur take-home werk. In te leveren voor Les 10. + +--- + +### Doel + +Je QuickPoll-app uit de lesopdracht verder uitbouwen: +- Beschermde `/profile` pagina +- Eerste eigenaars-RLS op `polls` +- Korte reflectie auth vs. autorisatie + +--- + +### Deel 1 β€” Profielpagina (45 min) + +Bouw een `/profile` route die: + +1. Alleen zichtbaar is voor ingelogde gebruikers + - Anoniem? β†’ redirect naar `/login` +2. Toont: + - Email + - User-ID (UUID) + - Aanmaakdatum (`user.created_at`) +3. Bevat een "Wijzig wachtwoord"-knop (mag mock zijn, link naar `#`) + +Tip: gebruik `getUser()` in de server component zelf. Geen `useEffect`-gedoe. + +--- + +### Deel 2 β€” Eigenaars-RLS (45 min) + +Voeg een `user_id`-kolom toe aan je `polls` tabel: + +```sql +alter table polls add column user_id uuid references auth.users(id); +``` + +Pas je `createPoll`-action aan zodat hij de `user_id` van de huidige user mee-stuurt. + +Schrijf RLS-policies: + +```sql +-- Iedereen mag polls lezen +create policy "polls public read" + on polls for select + using (true); + +-- Alleen eigenaar mag updaten +create policy "owner can update poll" + on polls for update + using (auth.uid() = user_id); + +-- Alleen eigenaar mag verwijderen +create policy "owner can delete poll" + on polls for delete + using (auth.uid() = user_id); +``` + +Test: maak een poll aan met account A, log in als B, probeer A's poll te bewerken. Wat gebeurt er? + +--- + +### Deel 3 β€” Reflectie (30 min, ~300 woorden) + +Beantwoord: + +1. Wat is het verschil tussen authenticatie en autorisatie? +2. Waarom is RLS in de database veiliger dan een check in je server action? +3. Wat zou er fout gaan als je RLS uitzette? Geef een concreet scenario. + +--- + +### Op te leveren + +- GitHub-link naar repo met: + - Werkende `/profile` route + - Werkende RLS-policies (zie screenshot in dashboard) + - `REFLECTIE.md` in repo-root met je antwoorden +- Screenshot van Supabase dashboard β†’ Authentication β†’ Policies (laat je policies zien) + +**Inleveren:** Via Teams chat. Deadline: voor Les 10. + +--- + +### Twijfel? + +- Geen idee waar te beginnen? Lees `Les09-Lesstof.md` opnieuw β€” sectie 7 +- Stuck op RLS? Vraag in Teams `#hulp-vragen` +- Test je policies altijd met TWEE accounts. Anders mis je security-gaten. + +Veel succes! diff --git a/Les09-Supabase-Auth/Les09-Huiswerk.pdf b/Les09-Supabase-Auth/Les09-Huiswerk.pdf new file mode 100644 index 0000000..b5b3d38 --- /dev/null +++ b/Les09-Supabase-Auth/Les09-Huiswerk.pdf @@ -0,0 +1,105 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 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 +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/PageMode /UseNone /Pages 10 0 R /Type /Catalog +>> +endobj +9 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Huiswerk) /Trapped /False +>> +endobj +10 0 obj +<< +/Count 2 /Kids [ 6 0 R 7 0 R ] /Type /Pages +>> +endobj +11 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2006 +>> +stream +Gb!;d8TWWE'Ya0?]LabK>/:3K!)*nDBC$mY1Tck^mR8oL>(S#%>SJgaN">mSI39u%Ot9g8h6aFLU57oJptgVCJDu?Lhr>"#bn*Y,112)V;d^)T@/'cJLXoObE/tCI"_(,>q](E1Rm0GVY;?bNN('>aC.\m/71=q`Hepa*5S0Mg)\=Vl%m]Y`97Qd;f[<*g/ooI%1"fmo'W7h`TbS;`=,O#+*a411H-Sq$Ik=H<'AY,pl4GOO3!_ru>VJ]D$/62NZL,Uf6AJfNF3Z]N;TOOJUqa+q>.U?"Q/qTLO"(3,V+45`l&g"7hRk\V2^>]+7U'L)=Ed-V,q`\j[>Htt]?Eds=F$/,<5:_DM`a,^)[J!$4%BOfkg3#Ug]iU@d]2mD!gE9q2QFrpSR%L;GpT9g*tRdhnPa)D-QG7_^NYTe=rRV,'O^4sB^i"PZhU&+@+;IH#e^`Wi'e_8AhgE%`qC%OkE`tr$\=I$laT=YWg.>="OOGO>=i$cP6G5,(Ra$Ph'(J,J;KB3^QS\rAoT-_ZP)OSqQbqN"oRCQ(M]>pSr"==B&@OopaJOt$:a3K!=%keeT+a+3Lae?j+.pgs5.l+6**MYsiN@CDt)PNEg*[bmnHuQYH`b(#p!AqcN^kap%N&5U?R#1RXI[UsacA?uqiHD@RilJY"*2$cQj1@\El]-BuE>+)HB(m5(kqtL4kcDoW3hBcV1W*!NitOWSF.qZkB9@Ep202@;m!c/hg%pAJ2%'LO0b2$+tV`H9kN$:T'R<2JMhsC6GMY/q-LjcWfT&Wm3\5X1&V=o9e\_Du6S'boWtXN;n=RP:\jJOt__8:U".8@^t+**4>NmmSW)^[0h+ZL934UgSZq[Pp))Ahdb"s/,q+=VG>ki1jr<-d5Eg5:hhns6Z(;::ksqrLg7(HFAm&5mA&B+L,%/&Xf0qp1ed;B$YpfW`JBV]*2:\!C1s"nWGJE+XVCGo]Zi#$?a13h#?D=kKh$kOl>\;a=FQj,*!)endstream +endobj +12 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1860 +>> +stream +Gb!;dD/\/u%0#[%_0$ic'_PIMHFZt/+\:4l%1oTon^9&pL9jS.8n?%*8I:grqsEE8[d5u>h0;5o`J,D(6lu/cI)n)KLL.XiN2?SnHLUbO#;2Z.!q%cRHKT8$lhoFE+qW2oJ[rpC6NJ`[m2q^8-Nu_B5q#&0b)o@h%m7h!;P=.XE`-oa#%@/0_h#qFOidd'\0%[-J\9/-+_YYPq*>RpbKoQSf"@(JF6gokqG#,r#sNR5dHZ&%iP+rX2hcnA5$jhsLMVop1s*$+IP2+!P61SI82rV^[s3Ns2FDNk]dr<[Zl?(po`ED?l,``o5Ep/:MrqV%nk"o&<.e<#ns0&QDk&qM7j4,c6`$j`Kf]>iO`diguEn7b%=BucFC`jT#C3hCAni,Ye'5enAcOfL!InP%GHf!l&h"`pu\VHBdMQ)p8Bd&A+%/8/'TeaapKE$giR\a:r53Qo+1NjCPhTo6qA\bsmFjhFZ*d]!l$^?TsXIcj%"-*5#64=+#P(:$bU&HE?S$MQ#Xh(G5V&.N(U;a#k:kt'?(_pmUMoXr<\c01F@ce>LG*i7o#P4XCPr1uFDf)H1QZ*u;6`Jp&?%ARk43()Vdnp4'W/hJiQjSr\R^_BWkGl(jnOc>^:1oE,Y9Or(lpE%g/QFaZQXn("1HRQ!/>55CtWZ_"sTV`2r)q`_bV:o.+@Yai:5g$1fc@9socSGF-T^t'+EKZ4]6cr$$mB`TTb+N_V7VkD/T2m6V$Z2kSS-U6_#"Q;7(bNYBLpo4]=irD_N!1Mqpk&5FY$!//*FgG?QDXg4rSrc/ORiT.19Ydc7b82.$?AO`jFWHjhkpuPPFW@p91,HkUJ%45k:PJ[uqZ_1H<1=o9maO(":\ZG%3S!L#B/b-*1LhusM>>t]ENN/0$ZN/TEn4tL0lPm]QTI>;#+H0D%MMG`W`^5d@`k&ncRIafdUQJ!Og\\u(GZZep9Hi&BN09jT>c$L/GAa>Rquiqrc3Hr94s&G9WJCZ7m\@oLrkH)W7#M872(W_m8I(Wc--_k<.%-)V;!\K&d[:]J7/DUhdn*2!"i^4P[J4E&d\;H0e)/B%Z:CLiB?2!L*93HSgdD)LhPY`fgFd`[hYn^asSErO#+sm#n=BXF^B^,b]\bb7T4uaoD,fHl_21!c-i@%cRCrVAjo-km9ljOqOrPneD*N5K(JYhn,X@u@bb"ElM6rnpZk>/do:hQC''8IVri4ogmd'@9Ck_U<,AE&aWT&E\>;\.$Tl\tnm\fr34k[>r8mS,q<>)rZVrb&4pWStC=B_/1@j-o4@Do%NkM:Yamj$9H`otI/>IJX[!PZqlH[ksFclFTRSCX0emOs(JWl@"9dB,%jZb?g/^_/H\k,1[^eN9[/(kfI\.W2DXuA6gm69BT3I5!4E9apC$T;sE)MHYO8/s('BQ7u*TH-LI!^dt`1Xk61U!:tHrfqRrF1iZTN$g+H>F36#s,@X:^VTt%2q35s#H]C_L-fk==SB[EmfkglVU*s`7b\!0uO5\N#?(K^7N*ZbQa%;]C\i3*>s5-dg:U'T76mkEN26?Scp\"0#EK&-[S5AZ13?87oHI.[E',TQ;A,DJ6,=YB[s1s9`dN26V15[dc@;S[$r!h+.6eV~>endstream +endobj +xref +0 13 +0000000000 65535 f +0000000061 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000446 00000 n +0000000523 00000 n +0000000728 00000 n +0000000933 00000 n +0000001002 00000 n +0000001287 00000 n +0000001353 00000 n +0000003451 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 9 0 R +/Root 8 0 R +/Size 13 +>> +startxref +5403 +%%EOF diff --git a/Les09-Supabase-Auth/Les09-Lesopdracht.md b/Les09-Supabase-Auth/Les09-Lesopdracht.md new file mode 100644 index 0000000..a498282 --- /dev/null +++ b/Les09-Supabase-Auth/Les09-Lesopdracht.md @@ -0,0 +1,76 @@ +# Les 9 β€” Lesopdracht + +## Bouw de auth-flow voor QuickPoll + +Duur: ~60 minuten in-class, samen met klasgenoten. + +--- + +### Doel + +Aan het einde van deze opdracht heeft jouw QuickPoll-fork een werkende signup-, login- en logout-flow met een Navbar die de sessie-status toont. + +--- + +### Vereisten + +1. **Supabase Auth aan** + - Open je Supabase dashboard β†’ Authentication β†’ Providers β†’ Email + - Zet "Disable Email Confirmations" tijdelijk AAN (voor testen) + +2. **`/signup` pagina** + - Form met email + password + - Server action die `supabase.auth.signUp` aanroept + - Bij succes: redirect naar `/` + - Bij fout: toon de error-message + +3. **`/login` pagina** + - Form met email + password + - Server action die `supabase.auth.signInWithPassword` aanroept + - Bij succes: redirect naar `/` + - Bij fout: toon de error-message + +4. **Navbar component** + - Server component + - Vraagt `getUser()` op + - Toont email + "Uitloggen" als ingelogd + - Toont "Inloggen" + "Registreren" als anoniem + +5. **`/signout` server action** + - Roept `supabase.auth.signOut()` aan + - Redirect naar `/` + +6. **Middleware** + - Gebruik de `updateSession`-helper uit de Supabase docs + - Configureer matcher om alle routes te dekken behalve statische assets + +--- + +### Stappenplan + +1. Installeer `@supabase/ssr` (10 min) +2. Maak `lib/supabase-server.ts` (10 min) +3. Maak `/signup` route + action (15 min) +4. Maak `/login` route + action (10 min) +5. Maak Navbar component (10 min) +6. Test de volledige flow (5 min) + +--- + +### Op te leveren + +- GitHub-commit met werkende code +- Screenshot van Navbar in beide states (ingelogd + anoniem) +- 2 zinnen reflectie: wat ging makkelijker dan verwacht, wat lastiger? + +**Inleveren:** Push naar je QuickPoll-repo Γ©n plaats een screenshot in de Teams chat. + +--- + +### Hulp nodig? + +- Stuck op cookies? Vraag een klasgenoot, dan Tim +- Stuck op middleware? `Les09-Live-Coding-Guide.md` heeft de complete code +- Stuck op Supabase config? Loop met Tim mee in een browser-share + +Veel succes! diff --git a/Les09-Supabase-Auth/Les09-Lesopdracht.pdf b/Les09-Supabase-Auth/Les09-Lesopdracht.pdf index f0f2318..e9e87c3 100644 --- a/Les09-Supabase-Auth/Les09-Lesopdracht.pdf +++ b/Les09-Supabase-Auth/Les09-Lesopdracht.pdf @@ -2,7 +2,7 @@ %“Œ‹ž ReportLab Generated PDF document (opensource) 1 0 obj << -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R /F5 14 0 R +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R >> endobj 2 0 obj @@ -17,12 +17,17 @@ endobj endobj 4 0 obj << -/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +/BaseFont /Symbol /Name /F3 /Subtype /Type1 /Type /Font >> endobj 5 0 obj << -/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -30,14 +35,9 @@ endobj /Type /Page >> endobj -6 0 obj -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj 7 0 obj << -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -47,217 +47,59 @@ endobj endobj 8 0 obj << -/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/PageMode /UseNone /Pages 10 0 R /Type /Catalog >> endobj 9 0 obj << -/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Lesopdracht) /Trapped /False >> endobj 10 0 obj << -/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Count 2 /Kids [ 6 0 R 7 0 R ] /Type /Pages >> endobj 11 0 obj << -/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1745 >> +stream +Gb"/'gMYb*&:N/3%.`Ic9T-JY<8Z,@ZOKa@-(K`1**a9_1]T+9qUhN-!bM\m"2_%`387X*'';1;g*8dkiTBtd5&S4F$*0f<0ZstF_\of_eQ?rW@T,M\@D%-,S$s33n4Lr9(:3#+\?7c#;H[2%h'TeVms+S8rMs'CR7L?fV@(\f;[2C*MZ'$"VH:5AJEJ/@'(#,Jih`Ie@2?1$bpOKc)j5L2.=5L.YfD@7]`eHrb@"Wft^,c(.`X9;qsRCRgG]<]=;-fJO.sX^eUY7om.Q^?7nMpjl`dm?t'uVL="(6+bkUWEV&Nld+tWebTd4<=53AMmGl[SEL+RH/gn%:/S(=E]#3U5%g)91[F/DPZ)dC('Rqnif/SID*Cbn,ZMVL'./K!&hlN)Lm""":!3\dOabD;$1B2709p;EfNs?E?)W&Q`G(Sm8'=JFfpOD2\D,K[?(>uI%/9DREpsN)oKg@X?>+T#h^\o,<(%LfL4scC54>M*s$B34_eXB$;N[SJ/:*cq2C*Gl8BTDVnPg^VTNIJQF`R2qu8s8.4ko5`*cRl#e1^crn)a8sBh1gIS8qe3Z'6RN3(`5H]\l/0.&kap:-!%VtB">ua)<=Ddq(>@Qrc_lK6>C6,AinVRK\!KINn^+YQ@iC[:iC]e6d=)/@Q_9f`8$!pjMP^]Y!aVqGa6T@Lp*.ngK4[J@?e.AqCS4BdNIab"jd4SD)bA2h_$WhY9c$t9LLeR;F40Pn2WOL2kk;>:QbH^\\6hmRHU@X\NAtSnr^,^+H4afL.Zl"qpd:n,=>(":ZQmQOl\SBkZ#<"%OjWH*W.LE5Ed(YVB=iGIb)L<>8mP1dR`&pq']?.SO[G"<9]bKGjAq:r:@g9^S2WIZI."@:F1T/>)6=qendstream endobj 12 0 obj << -/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -13 0 obj -<< -/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -14 0 obj -<< -/BaseFont /Symbol /Name /F5 /Subtype /Type1 /Type /Font ->> -endobj -15 0 obj -<< -/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -16 0 obj -<< -/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -17 0 obj -<< -/PageMode /UseNone /Pages 19 0 R /Type /Catalog ->> -endobj -18 0 obj -<< -/Author (\(anonymous\)) /CreationDate (D:20260331161655+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260331161655+02'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False ->> -endobj -19 0 obj -<< -/Count 10 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 15 0 R 16 0 R ] /Type /Pages ->> -endobj -20 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 367 +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1420 >> stream -Gatn`h+kg@(^Api?H%8IB%6fuo1TfT-D3Zi"Vb!G7OJ!uDr5Hj&d[EPD:7mGDu2\33EVZ]>N[Npf&GtdK+$%,AsEO\++'M,`]<[8]68FL--H=*r_=R\_[+I.!I$ZfmDGnMendstream -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1444 ->> -stream -GauHKD/\E'&H88.0g2KN[)@4f%TSc@3HBE+HGDG^C-V3_<1p+[^7'mG!*eI5A1q;A)"L(*ugMYq2Pi;*LM#n!i'>)(Wa]!^6U>jo3U,j`gh$/mH(mO2U$EeP)8&k4jC>^T%GZ985fg[m+a3S^p*!g[G1E\1O,)-glY(Q$Te5A,hBHC$q)2,E`Nu)4FBVJQpa:X5R)cl`Aa:am2kn/#LPfN8,bc+JjZtM^C0rIC=(bD*9A'S$UI_5$#(3d9$_Nuff^d9a^@Q7q]hKWXEn6q_WOV1B$0HN/0W:FJ7AR4E3('qeP,WU2QB&XIijlk8XH,,T@Y+%q^7XR^F-A>Op`s"diJ26c92qTt\QeR,$$6C/@nLa'@*fNI@5H,N%!j2;*JPuDQ[h$n.e#k]W0a2_Cjd%k/iODQ=ICcBkE@B7Ik5'R&'$NDC''8Q<(L+/QF3'Pj'.4']Y>*WP&pbRT>">J'%MNj0qWr[qm:."+.5Y[c\G2D^"MeU-og(SQ5bp:!\7Q+cW^,7N[C&T':Yl+6"%?fAUUYG?\#jb^5](gD>_Yu\qdtU[q8`73\*AhE9c=2S`L*28R-b[JSPu)Pp[k)ELMTXJeKS6WL'M;a2+I&fXQ6EGdbbe8nXLE20M/Zd:^lhVuO"Ur%(F*Nc0?+Y[hIRjN?T2'endstream -endobj -22 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 581 ->> -stream -GasalIr!dm&H1LYi`[0YUJkVpp/8oMP2a;[aKes"%1GPmPh)=@V398DP^N4rm3HNeBB^:,+ME-rlcFc#IU9^b"i-3epO`^B%t"C!,7O>Q,4=5Ih?HOhTe'^tKHVWBUD3Zs(DAA:8f,?Jq-tVUE-aBH?TZ?=FnK^I+n,PC0^.+3r$m4):[D9=6(THAdXcbld]RZuWD^Jd)-`p`H+8>+l\c8BT>4eTd[1KFEA_"?dT2T&*@eDlmg2BnO!tC$*f9Lno5X5m'SX2M4-?^c;bNUZd_Hd=^3t3gomsZQ'+7@d2Su82OpcX.?d'Ed@FH03nG4R$88h^mpGj;8hVFi`^9e4rn48UHZ:[[T!'f*:DB7_lrbE8LTm]&qJR$XGSnP8&CSCR&\OOfNddh52263R@$R9$'[JFa9-J%PFfU>4ddp3U4Q@$C(VY7)n.W)H\WKtED@?\Z1T#oF?=;G'TE;kendstream -endobj -23 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1164 ->> -stream -GatU2qetI`&H4hB`>n.F:?`Z]![LXsP)*)8VRc>ldFAnkFULSDJ'-LnjD4VY2://-HHdM].+--Nt9K@_ITV'@U>gX[=T^H:tm*B(SSiLa-:``a=;'Vj%KZi.Ju:'@+O\$'8'iJdLD2S;\Om"lr@#Kl&q\d-fGNUIOuEs@R&&?A'^tm>Zb&DN3nj&9)V5d#c[sLP2`E$\r&6G'oOMh7q`AmbbX3j+)rFLfCH[Vo22tpSg4EYQa*sDo>f%IccDY8@`mM\*J^5MH9:53.KZ:2>:$;Zo9G<":cI_t$h-9Rs5;A;=O^Q3P?PA`DXhqH]M/Z6(.glO'$AlHtOJmD`>aV=L.od,gY;jchjC`%3VZlZ$)8HtpLt>n`I,L_cO!kW9F2#pC=?>;q%*EH;]]dJ.T\!ObW.4uD6K;I`H!gt*jXV+Cp1,u_;lV5GD_\?-kQnCJS$`0R0j@-r4GB*/NV.>SC?1%i"!eGc%uD=rts``A[0<0EY,L9k2%%qOIargcpQF;'@N?:AS#KnPqt1%bXWA!Y6B4?3!?)(`6;F2Ogk+UW"8J8Sj-@?Z\QJ5IHRt6XO[*TnB(76d[TP`F0@ri[aj1q1sA^A,Ohg6K-lGP4Kc6j0\/3Y[^V5-6SE\sH%tNpkMQatQdt&t^GC*)S>`&E>2VVYla6KP.ji>U"1pBNp5P%__rfUq.!MT3!MR:Qj1a\H8E'[pp'fTg2c(d]62fGTj0@l&AL(7d::$Wh)V[m`"B=h0ZeD"]1PrDPg[DE'P1s'5m)>ODaCA:bq`=2^@6b1YG1bXMDse&hs>8hS!.(QeA!d/~>endstream -endobj -24 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1414 ->> -stream -Gb!SjD/Z1=&H88.0h(Au;\^uE[Zd5PlfI6(8njP;Xt$jILBY\b"r;)Z@],OlHY4-^S:GZ@l0_1\al[/@Sp=pkFr]_ojlg&9!B?R#rQtYD#Lb[mY3!Om$8/4]T+7BnADafkma)ukkigY;\G\VUd:]L=m#Bd$=T,"4s0r068%.rSi]f4V*`$;I"=uND36Q6j`i-#AWGbT9>^tr,#,lJac7UlcF5R+1f7=>EZQ=T6*aQuu5B?coSFO_Xa`?5YbVam$.EW.e8!:^LBkl"i[dl]Hm(7uF#Qpf;"ME>u*BOOo"LUoJYGmug,#1g$i@$S1,#&]p:A;cNg6g5JLB'sFpAlL"rcrRT[4c/'q)H-2uJV(pZmPk#9<;(dRfqdY!3lnP@U-)CIIn<-=;f"3fLfl`KkbX_9@FSDH.@b^o_9>o&lGh9)7Vu@_#+S)=e+i,9N&f$+1SEff'[^Ft)Ce$T<,fkb"bj'4)i1@mk:=f8U6XcQ><#nZ`<);U&WYu_fYTsl*8k\Ko%\&1AHo86tR6C&<\uB19Jai^BK1b*$&IAl]IXsKU7HV;fYCA6U+dXX/rGlqZ9jI9o^">hpe`smib=TXUOG@j2=V!GuKlaGZOq,>ai`pC4"+hq6?Au^2]ff>$`o"`&Kr.@kS&9-Q9[7Yjd6Xp%g;X2PoH(tC,rd*bb3MVkRkU!9k+MUg/l\m'#OfJg_geH.Z7H)Q^tendstream -endobj -25 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1156 ->> -stream -Gb!#Z;/b2I&:XAW3!`XF%$P3^E]0`%$`$FH!a1X3#5U%BJWe:k=,Bm*.X#G!^[F]rYnP5npFou3CeN9++7Hp**YlJH5(WZl$\58]p`BPC`irQ2&:,KNDW#gqpc8]SQKOrc*aXL90V1tN8U,\Z\A8]b$R::f22-9$m(gCZ-Jk8Ll"g&BR06W2W=^]=/;CW,7>^V*fOp*fH2qlUBhKaGPMn`Zi,FIp22qBb#'ruBbX'3H19*$t,j^L5TT#%kC*s.F(MFtc.=>6?H";U3;F7gp)TW[cj1QBSOj"/O5p7Y)ZPpQQ:%e>:>C2c]u5?)*^&9\.O>hrN5'/J%k;h*esTV0<.#CP\^&o>08G3pC8WWff_$i3=!i+.DG'Dd7sI"lc"Bg,[ko$I*ZW_6HVGjg",`;5(.X+LaFKdtBqNS5[4q=Fi8@mB5_:LOsBu[9FnQCg>C]6Kqf9o+'J0AAL!OVVUU!2`[t8cu(+MieS$dt,2]$&-5sLZ'CQ$>d#F\SirVrs><,52u>n^^ZpO`PSY?7$!1>D6CNRp>]H5i\u7%q1.UJ[58FiH;m+SOYZ-6H&i[Y@#1@Ctg$Ui"0FUH9@,ZL?"Nh1Kq0>iFR&ofUQO95rT#i1+tendstream -endobj -26 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1392 ->> -stream -Gatm:D/\E'&H88.0sXba;Oq\&&kG!TdJIb4%BkMQIZS`%p$k;(BpM"Cg[HJ:7tN+\Np#;<#RkQIe)&B_WJbbl>1'+.bO$VD,H0+/C)8Zr8HP#V?gXC2PO_.R0Sm,hI3b)MB$D^riVmWLWFj`;pJ]6BU4N]#P6U7X*,b'BFR[ZA8,5W&H)-*8-9BHp!uF"Vd4k!f/nE;mNL*=7*\jq/2DUTX(YSe8W'\NqTZA+#(Pf,=HhB2aijgW7u_nTubJY;'/uS$lFA,&Y)bL6ArC\RN[Hd+-D^UiMHf'n$SDE/:>@8;JJg8pn#6XLH$7>YM-n,29Ka#\d,FlI:'a-Y8l:`'m"H*C4+h0ZiP\*_WK83cmisHp)GVdi2aTRaXFf0K?@u:"fOf[FS+dV(Z?Ua,-'_$Go'J7<5>HDhsDA:s+!]Vj*9Ni&4DJr7%R3ki5lp,7&u:\WZLqNgU[P6#SdNo(.i#=jDjiGT1>FZ,>UE-&>'0%<5hH`/JlKkU$rpA0='<]L'X1a+tDbJOu[/IRF;pk^Jp`NX@XC/ala-m%sVlK@V3SFP`a9ad#OEh;E8h]&IYRe)?rdYXf:MdC+HD*O#2hSn<8")?K.fgej5RQ7iKTfB-YG*:nTr'+4&&rendstream -endobj -27 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 317 ->> -stream -Garo=;+ne\'SYH9.h4pTo:^$P=<)3B#l:?,@NP9JgG\@IBo6UE8EkU_X`Gn[2fKpqF7+6>3$NkId'F(hQO"Znp_7`V/cl(LKEoWU)*^)j&]>k*$'Nsm\/')NF6mpYe[Dn$3N)E_=^ocd[fu@:J"k`-."\N_6f;QA9RFPl^8u>semLY45e]NjIB.e@GHTB@A;\@IkgA4C@5+O#reXendstream -endobj -28 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 757 ->> -stream -Gatn#h/8]I&;BTO'M"98ftANj+1LNhj),c7A37#u'a_u+Y_d'JU^P[ET>%p1*'(!f\nXN5L=r!dc#O"/6ZMDV2r+IH'W:+TF9D.>\trQ&!=8(@lfjH\9hR8oa)^*a,L*Yj;rie>AiPN\g!EE2BT_=B&E1>Qnhau:=F!j8P[V2,OOcX&:dgJi%aQ0\SP.=bcB5p-W^lL1?;+qclE0Pfu0.at@e[B#d(#?k,UWhQ%pIt,[;U-Q^lM=pb;`Wg`HT""2(g?@rNA*2\(*#Q4qU:+dh+'^j@4=Z3(fZFtCYo$CUUFh&8cR%3`k(KQB,T`gQ+.KkfGX-J/oK]uB9-Q:$b#*n$c9D%,X>\mZD+qT(g>n%)L$%$Z5qC@\Pl=qR>um)]`.sXBV[IdgTtcZ*-;[T4IsTCiBco[JQ%$d~>endstream -endobj -29 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 735 ->> -stream -GatUq?#SFN'Sc)P($Dae<12D7E8YW!g9+>_Yr1i"`\\QR#VJlB`XuKb4hAp\4lUd=(B=sN`:mNgp9S2E!ldAFL`8DCJ,pOto`IY2ZX_E(R4,HU$u"9aKXJVkLJ"!M(5iQVSfi>\.t4bj2-'&W/)\sXY:"RBS'W16DR%u?jpfld(:fUWGmWO&HSe!Yn\q^7,9,tl3)+X)OL9\Ke4q70)F6u'<9T`<"h@07-=G%7#Ee3J]`,+=DuV%2c#X*u8rpoH[*G="=:e/[<1!!]N7T0#!_%t902ZnCE#c_^rq:XA+kn=`7`bp`'FQIbe/;jPO\_'SV>edT"Q#9(NuA\a9RlU7<@)-WBVeW8JW7*Fb2$*i(#&:QI%q0<0*LUg0bp1jm;He,FJp#!-5+,8O';n)Fbc73>5[*DSa`ftPSeN-P5TAN`3]\V*Kendstream +Gb!Sl9iL(A&A@Zc3]HLSQ8SO4?:_@.$WZNp_;XIu=DHp5dYsnn>DN2Hm"NYB9J([q[T0iJ7A+(4^@UQ>'P)aQXE[X?[7T*DtsXbrq8D4k[9$H^-;#]`9\G#A\SjN+en6D$HMRu`n91juTt4udBqf'e'4jh_Y_'`K_`@EaPZq0:YA]DFRLXh%C:d*`Am`^XjGf0Ab=$@s_W+A57??T]nFLJm9g:lZ>0RP0:gg`UfXIT?:p1a`aNSU@!8VRUZiW30Vd18;l?Z8nD@Km-^fMWFj2gC6a$RN7eV*DqXoEm'VlF"+%!J?Z_tNtF=M-!RW(Hs=n=i0NAC!eKbA#]%Fe!(;Rn\.>eUa7)OOOhV(j6Y`fC_]hd(8S"s0tT!j[-,qL$qr"3_K@r*q`%,/,q-_ZVKJIQjhQRpGJ7X/?KXa%n7[$$3KW:#R!Xc?r.sBabhD#V`'H+hP!-jn5c;=T2pAi"^o:4^dnHOM^Hp/\m'W\lC&uF9b[\K'&*7)H;\9&/J%nQ>9QHPsMfK6Z2;TE]fVk.I/TM01hHUDs^oYU$hG4c<=gopu,d?TbR&gR,()e5KpdcK0Ah7.#[rZ?/EDOiBO-;nCAR8ZaL@8V-^/HLfp@T`b<%8K%XVhIgrH')2bKg].Xq8k+s0?mZq,j>[c=Kn`u4?d!q5*m(oM1oBC!rfcRV2fB\J[GUeorE5-=ViK86=*hHj)Eo;!<$Io>PD]!*//4%KT1L1"aGgDh1$HYY;h(dW,NQCiGfn9KEI05[4J&lBYHe5*,-gHCf_sf`ffuRFgmT^aJLX2]e4cN!KtEN>Zt\-NGa>('MJ-(TE$Y!u-8c$SR'tY[]sF>S4O#0eHe?(IF6nB'K#k>!V'7(gnt\YP.H5bsEn5[^6h@T'^NWOi&SeN,EH?OkKhGG+7RmuoJDRQ&i0kpT;D#ugp[rX#Da<#bG)"<)0HZVB*F]5aMIY.#D/BS,rrHGV#Ts~>endstream endobj xref -0 30 +0 13 0000000000 65535 f 0000000061 00000 n -0000000133 00000 n -0000000240 00000 n -0000000352 00000 n -0000000467 00000 n -0000000672 00000 n -0000000777 00000 n -0000000982 00000 n -0000001187 00000 n -0000001392 00000 n -0000001598 00000 n -0000001804 00000 n -0000002010 00000 n -0000002216 00000 n -0000002294 00000 n -0000002500 00000 n -0000002706 00000 n -0000002776 00000 n -0000003057 00000 n -0000003178 00000 n -0000003636 00000 n -0000005172 00000 n -0000005844 00000 n -0000007100 00000 n -0000008606 00000 n -0000009854 00000 n -0000011338 00000 n -0000011746 00000 n -0000012594 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000418 00000 n +0000000523 00000 n +0000000728 00000 n +0000000933 00000 n +0000001002 00000 n +0000001290 00000 n +0000001356 00000 n +0000003193 00000 n trailer << /ID -[] +[<9b100e9ac191c85172926033485b5ccd><9b100e9ac191c85172926033485b5ccd>] % ReportLab generated PDF document -- digest (opensource) -/Info 18 0 R -/Root 17 0 R -/Size 30 +/Info 9 0 R +/Root 8 0 R +/Size 13 >> startxref -13420 +4705 %%EOF diff --git a/Les09-Supabase-Auth/Les09-Lesstof.md b/Les09-Supabase-Auth/Les09-Lesstof.md new file mode 100644 index 0000000..6857c4e --- /dev/null +++ b/Les09-Supabase-Auth/Les09-Lesstof.md @@ -0,0 +1,229 @@ +# Les 9 β€” Supabase Auth: Eerste Login Flow + +## Lesstof + +Eerste echte authenticatie voor QuickPoll. Aan het einde van deze les heeft je app signup, login, logout Γ©n een werkende Navbar die de sessie-status toont. We sluiten af met een eerste blik op Row Level Security (RLS) als opmaat voor Les 10. + +--- + +## 1. Waarom Supabase Auth? + +Je hebt drie opties als je een webapp wil beveiligen: + +1. **Zelf bouwen** β€” password-hashing, sessie-cookies, e-mailverificatie, OAuth-callbacks, password-reset, rate-limiting. Een fulltime baan. +2. **Auth0 / Clerk / Cognito** β€” externe service, prima maar duur en je data ligt elders. +3. **Supabase Auth** β€” zit naast je database, gratis voor onze schaal, werkt out-of-the-box met RLS. + +Voor deze cursus kiezen we Supabase Auth omdat het een directe lijn heeft naar je Postgres-database. Dat maakt RLS (autorisatie op database-niveau) mogelijk. + +### Authenticatie vs. Autorisatie +- **Authenticatie:** wie ben je? (login + wachtwoord, magic link, OAuth) +- **Autorisatie:** wat mag je? (RLS-policies, role checks) + +Vandaag focussen we op authenticatie. Autorisatie (RLS) gaan we in detail behandelen in Les 10. + +--- + +## 2. Supabase Auth in vogelvlucht + +Supabase Auth ondersteunt veel methodes. De drie meest gebruikte: + +| Methode | UX | Geschikt voor | +|---------|----|----| +| E-mail + wachtwoord | Klassiek | Apps met geregistreerde gebruikers | +| Magic link | Klik op link in e-mail | Lage drempel, geen wachtwoord onthouden | +| OAuth (Google/GitHub) | EΓ©n klik | Snelle signup voor consumer apps | + +Vandaag gebruiken we e-mail + wachtwoord. Magic link en OAuth zijn beide twee config-regels extra. + +### De vier kern-functies + +```typescript +// 1. signUp β€” nieuw account +await supabase.auth.signUp({ email, password }); + +// 2. signInWithPassword β€” inloggen +await supabase.auth.signInWithPassword({ email, password }); + +// 3. signOut β€” uitloggen +await supabase.auth.signOut(); + +// 4. getUser β€” wie is ingelogd? +const { data: { user } } = await supabase.auth.getUser(); +``` + +Dat is het. Geen complexer API. De rest doet Supabase voor je: cookies, JWT-refresh, e-mailverificatie. + +--- + +## 3. Sessies en `@supabase/ssr` + +Een sessie is een geldig JWT-token. Supabase bewaart dit in cookies. Het pakket `@supabase/ssr` zorgt dat zowel je server-components als je middleware automatisch toegang hebben tot deze cookie. + +### Server-client setup + +```typescript +// lib/supabase-server.ts +import { createServerClient } from "@supabase/ssr"; +import { cookies } from "next/headers"; + +export async function createSupabaseServerClient() { + const cookieStore = await cookies(); + return createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { return cookieStore.getAll(); }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => + cookieStore.set(name, value, options) + ); + } catch { /* server component, geen mutation */ } + }, + }, + } + ); +} +``` + +Belangrijk: in een server component **mag** je geen cookies muteren. Daarom de `try/catch`. De middleware doet de cookie-refresh. + +--- + +## 4. Server actions voor signUp en signIn + +```typescript +// app/signup/actions.ts +"use server"; +import { createSupabaseServerClient } from "@/lib/supabase-server"; +import { redirect } from "next/navigation"; + +export async function signUpAction(formData: FormData) { + const supabase = await createSupabaseServerClient(); + const { error } = await supabase.auth.signUp({ + email: formData.get("email") as string, + password: formData.get("password") as string, + }); + if (error) return { error: error.message }; + redirect("/"); +} +``` + +Dezelfde structuur voor `signInAction` met `signInWithPassword`. Server actions houden de wachtwoord-flow uit de browser. + +--- + +## 5. Navbar met sessie-status + +De Navbar is een server component. Hij vraagt de huidige user op en toont "Inloggen" of "Uitloggen" op basis daarvan. + +```tsx +// components/Navbar.tsx +import { createSupabaseServerClient } from "@/lib/supabase-server"; +import Link from "next/link"; +import { signOutAction } from "@/app/signout/actions"; + +export async function Navbar() { + const supabase = await createSupabaseServerClient(); + const { data: { user } } = await supabase.auth.getUser(); + + return ( + + ); +} +``` + +--- + +## 6. Middleware voor sessie-refresh + +Zonder middleware verloopt je sessie na 1 uur. Met deze middleware blijft je token automatisch actueel. + +```typescript +// middleware.ts +import { type NextRequest } from "next/server"; +import { updateSession } from "@/lib/supabase-middleware"; + +export async function middleware(request: NextRequest) { + return await updateSession(request); +} + +export const config = { + matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"], +}; +``` + +De `updateSession`-helper (template uit Supabase docs) leest de cookie, verlengt het token en blokkeert optioneel routes voor anonieme bezoekers. + +--- + +## 7. Eerste blik op Row Level Security (RLS) + +Tot nu toe was je `polls`-tabel "open": iedereen kan alles lezen en schrijven. Dat moet beter. RLS = autorisatie op database-niveau, ingebakken in Postgres. + +```sql +-- Zet RLS aan op polls +alter table polls enable row level security; + +-- Iedereen mag alle polls lezen +create policy "polls zijn publiek leesbaar" + on polls for select + using (true); + +-- Alleen ingelogde gebruikers mogen polls maken +create policy "ingelogde gebruikers mogen polls maken" + on polls for insert + with check (auth.uid() is not null); +``` + +Belangrijk: `auth.uid()` is een Postgres-functie die Supabase invult op basis van het JWT van de huidige sessie. Geen JWT? Dan is `auth.uid()` `null`. + +In Les 10 gaan we dit veel verder uitbouwen. + +--- + +## 8. Checklist einde les + +Aan het einde van deze les heb je: + +- `@supabase/ssr` geΓ―nstalleerd en geconfigureerd +- Server-client helper (`lib/supabase-server.ts`) +- `/signup` route met server action +- `/login` route met server action +- `/signout` server action +- Navbar met sessie-status +- Middleware die routes beschermt +- RLS aan voor `polls` (twee policies) + +Als één van deze ontbreekt: pak de Live-Coding-Guide erbij en loop het stappenplan door. + +--- + +## 9. Veelgemaakte fouten + +| Fout | Oplossing | +|------|-----------| +| `auth.uid() is null` ondanks login | Middleware niet geconfigureerd β€” sessie wordt niet ververst | +| "Email not confirmed" | Zet "Disable Email Confirmations" aan in dashboard voor lokaal | +| Navbar updatet niet na login | Vergeten `revalidatePath("/")` of `redirect()` aan eind van action | +| `cookies()` errors in server component | Niet proberen te `set()` in een server component zelf | + +--- + +## 10. Volgende les + +Les 10 gaat veel dieper op RLS in: meerdere policies, `auth.uid()` overal, en het verschil tussen `using` (read) en `with check` (write). We voegen ook eigenaarschap toe: "alleen ik mag mijn polls bewerken". diff --git a/Les09-Supabase-Auth/Les09-Lesstof.pdf b/Les09-Supabase-Auth/Les09-Lesstof.pdf new file mode 100644 index 0000000..cd85346 --- /dev/null +++ b/Les09-Supabase-Auth/Les09-Lesstof.pdf @@ -0,0 +1,175 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 5 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 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 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 +<< +/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +9 0 obj +<< +/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +10 0 obj +<< +/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +11 0 obj +<< +/PageMode /UseNone /Pages 13 0 R /Type /Catalog +>> +endobj +12 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Lesstof) /Trapped /False +>> +endobj +13 0 obj +<< +/Count 6 /Kids [ 4 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R ] /Type /Pages +>> +endobj +14 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2254 +>> +stream +GauHL>BAOW'7JK9fVabY8dkaWqpP#dUoiCY\N+B%itk=>,X`oOnffseofW:Gl=*=cSBMd0V?m9]h6X_+(LZ=`_p%R@:@u;*HMS:"`p%Y9b]eHGI6l1t#QG2P*qIA_aRN`k9/nqgVuKUS*u#EB:71O(;=$bI*;S[9b_b@*-B<4G`$-Z7c+Y2PEh^p?@\90X"^rD->OOQqkLeL4JhTk.f=1Uq>.qi\>OA)\rIHZH@'*;><6obn(.82Zk&g::bP*sfMjfQ=Z;<$GGBG>]Mjj&nO6HFpSr'kre4Ps$kk5,7I]GE[&aG-+9F.DATeAf6eS8]M]Lq@m8;Z^>GRXd;#pF6;$9dN>8`0XFZg\GkXNLit'Ne%7b4(QRg^Wp84'79Ca1(L\9a:P(A"M]b!RkhnA"hpO/>6Ag@u6c,Dq9tC#!61)K6FSM`8+%,\!p(r0%@u3]:IhCoI(o$YSb0T%Z=ib_01jSn4\YV5i_1`c97!UU3P:5*]\-)#4^8fT!Yj#c2&#hHiMOKhN,0NC$p/N0=G-o$_2aPQu*<(*?MEfRF*=Frn+`1q@iQ.F_W&'GafX6To4;1OiKVBTVCaA8Jeu!-8oo!0]%k<6O:mIJp173iZt%."X(49oA\S@8G[U1f]UGT*mu.pHCU9gHLp,JaeCE20p?;\,V+o@9[R(BA?G0.$hsW#-X&XaZdMQ4=S*eD5^):3XQ)n^Tm>*/,7=B2a8<6:Rpu$%aAr%uMDQpLD6ljTBdM&mp,L6'Q7/V^13%l;g-+lI[F2KQm:]p&_m$DJ1PDp#H9pGUoN[;p.`G!qo6UhW5#A'$ec99D`uqaACu%hdV0*I>G6;c4`o+j5?VEJ;D.6WYX@mJm$T.e;$9SDHM"d9`T8O?(7_/[5PeqTSS"-:^'XUo@Ut:Tr/75A-F6FP1?\k/(e:(Q\D(7^e#@U`YCAIo:X-KBi#1StgZiT<_)RL;e^krWB.j58RLiR37CakKs2'c''%[LR'eS3i=<%Ctu%fM7SXH>B+V)-9%(ALg1ji'sANs!lsJED=mpSkprU5\0qJ?`h)_g:\Gnd24:T9Hli"kN95MgdC5`q@lj>I3+crR=q.)Y4&eNXR@/Q&FGW4KCE@Zo!c\P1fhjjf[/InFCf/tPucF)Q.+,)XVN%B@2Ic6V3\o@87Ig]1'?(JRE`>e^!t=K:!tR3Vit[Lm%_(WY;u/3N:["^F(U@)^E>JT*U+A-HP\W8[L9"57.HS-19m$mL1^N,U>,R%`"aso[ON0A3^'['\"o(1iSip[F%a$/%lRH2jS`tJ"OQHff=]t`&"W/3a_)0DkL6-0P=c_'KKM!p:X+(T1JiiGUEO#`)>l#i&?hJ9Mm@0)'`VuTAP:PO9-C:),lOtB6"H7Eol74lUALS?@jVEodpY>eNJOYNU@K&2foB97W\=W.t$YFtrr;HB[mTqHK87YP**ZpFZ3EGic3b6od(O7in^ZXIqM4<6o*!@>0knU=oL*">33l,*qRH2KW+hY)n8Op#t[MG>XXURF@)FaH9'-@0@>^R#3Fi&*F7rr`17=q^[l2C\Kn%3fXMK`C4UK"*7q/b>QJeDkC)qgSV,Z$:AAEO!iJ:/+NiJ2EBS$3i/$lknB48+sH=Hij<4-@BN;a.3;FK;NEY.I4'$6scNIhku34uWXk@=b9n9YXD&!HNH4C_)m@J`8C#j0Ffj>$TG?`endstream +endobj +15 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1624 +>> +stream +GatU2gN)%,&:N_Cm%]`t0EI=ZQqjQ%a/SIUD3A'tqOiU/'L]d38;.j1j%THc$h,LrZB_kqCfoV)ER%l#5f(Epg;!.bi7B]Rr'l5m(sdd0`/!jt]H0/,*PscDr;_X$>VBPO,1$0POZ43]SFD>''SID<&c1Dn8q`!'0O,EK1k\33r1uc3K\R#j%n+cp$iXhQ7lY"g[8-kLUPJ/>I)<[W#]J*6biXQ'd"8^%HEWo9>T/7rN)q_0oBh<+\B#=``eurTiMJ)(]Vo5T=9tgkE2N&&R]f`]!lh]'IVC^mEp@^s'R7.RoLbB[Q-UBsmpr.K$#If$H]6l-=?I9c"KmfJ587KA68eu>R/).mc-r-V@\3s`9Un7j34=a4s/]QHE6O)Q$\GbehhhY/4uNOnW+qI@eGcCDNhfelFiH@in$hmj86CC=>.\j>-,$D<0$h!?i`c3h+,/omGSiaT&!IVnWjCdfKWY4AWEqp*[OXUUTT3)OV[$&-CTh9C?rj5V>,=`N.$_V<@Z>5d`!l2F"'*ZH[bhr]AC/HQe%/_uA9=F+8ZGP2F`gPW-SGt&Dq;XoGQH&sM&[!JDX=P_Q)4m\SRhEI`Z!i!/*iHM5d&`#Q4m3;Dt*MP+]Ja0@DO_cB?tb0^_2C:c5dP5VCTd=:g`uLKkkLZpH&i#=l0c33i1=_`.!=]kRC5+5kEZGHed/5Z9$R$X];XR#FWi49J@R;U=Mu$'\0&qIb77(JY?*EXdVZ5le;iF9$\2N)l65UjuPLT<2.eReeD\'>V+r&YMjV59-6f6>."):@-U:-V5N9&(P^el=rtTameXFj;FJg`#hkqAFB4Hq>>N66WdD]7Vn`fIKGU&-E/]^tPq5fF7f"cGFiPMI%eo/eO.U-R'.tN2%X)&r#*fE--,PS(K("=Z7]sVH(;1)97#n24"^b[b/,9OmgA(T#cVLd+=M*ld@f'0HpGs&cf0Xp5.mn!D.K\eqE/jJ>3`TMuh!)FDJi>[%`?=YQXt$r!+Z?7PXa5]Pq%At`#i9qKUOLP:G&c"Q"F$lmEudo,m>q9\7KAu2j$$Ra'tM%`5]%(nLu7VUo.rPAV'Bl$IMumDZU`BNjW+EENj\IL,OCsc>6I;fgJX?k3U`"5b=sV.RE'6N%\4X2G4VfhIj)Qo+e,:YED`D.Zmkc`A=&/M^h5al<^k@^E_puendstream +endobj +16 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1805 +>> +stream +Gau`SD/\/e&H;*)EQ]^gBIT(<6n(Q[,u&sq$drj2p&Z':(n%h_b*#ChG.YQiIA_9nENL!)[Q'*hMYGo^F!RA6"VX,-q!Tj9c:22H;P4Ff;kTY"(gY%uB^f_$R)9'9JQT'hGmoLAjZfDNK*Z:K3$.Z>\>CCOi4rVj>h=%JTq)`oJ6"?e*`*ur0nmf%\C>\,kF'"P*6->_-ar&[?2H*82\44Z:)+e(=oKT^cCe08%B@C(8SqnRBE?#?pO?Ga]6L1cIicWRgo"?Oo+]3'#roVMF1"nS]N"_2N'>&@3-VS1arM9sE5XBm[7ZT=SsCM.EY_%UX=\t[OMMjO@9$F"]*+=-aS^QbPBm_edp,P7F*\&3-XeonHa"?++\HL=Z#IKa1H0-*)ZCadE`6e8\FghkcrS8pLrZGo5W*$]Tbau.#%sMU-T)Jm,7)g`KEFi"lYaBo>;3DVjU]QnQk1@.==a8,S+bDTbQl*Jb!0IJNCsYl@_J5KX]EA@_7\PBF+=QjN3ffrL&k`PBI)8/50?:,8sEX2:"!R%d2NK5mjq=$"H-im;g8@N/"*EL!o[u5W$m?/9Qe!uUL.a@7Zuu1f0/'%*)):E*V;>j(oX6]N7@%?Qn(0o3pB)?OEJ2[i.4H.a^o)KBoLgC%EPpclEMUim*!R/#rb"R6#)Zs3ndMfnaJ4BF^8J:2QLcsQ9+_Z-PI5saN_\2UPIV$Zg4[$%sRcWVe;oO(D$8T?iTc(#?3\$p0@UaTVja;D]XA@pq%?qiPipd+SQFCD2'!$[#mVunO]o5a1^P3Dp&i&VUEpI/5+i]hA29]mpEu00,Na`:H3aB5uI__;3'WtQ1hC78d-r'\Jm%aWk\10RPpk"br_4#9go%m)X@dqcS;W^'1?l7?G];eu;mc+!/-H@@06n[e-DSVU<5K?Y@a&&@g(b$M!&al`e\/80JM_(.4$#sdsk-,Y+O?>Y:j/$[ZE18hQE49Z[/1c\bMm:j/-S1"HOEVPlr*H\oCHd0q/+D&&"THk.)ZCH`d\=K+3\E3i0Z'hs4bM^]$=u'C_2"(!&4DWOh(8^C&K$k9Z6(^[X;\)^L[[Tp`N#1+Ml*k=#b4PU9?KZQ0NN%CHcIP7hZZ@l[W:]UPX/T.ASEpfoT-BI4MA=;`g,m-fI]K&ZYRB9\[jY(h9@55pK'+:):s&l2^YHB#37a/oZfliE3PTf_(%k(VLBaI-F8l3bNOfTKqVuOA%*qt@GtmHk4gU[.7*pnRSQk^4FDf?Zfn,WoYQ)+RgX/1NguNKijH[lR7TILc`be3QO0V/R>H+fYgrQ:JQh'nIT8OE@t18!ghGC^JoDFYj;O/P1iCp@(Y*UD+=h((Jg^2`gshXI+1oJbgJL#1IHTj:8Lnt)2'A6;iu8hj5EiiHA$+U"Hl^hdB#jFSn[4_-HS="P@r88Msish("#`/K,k7-n6>:aTFZHO?!kS68U2HeCiAE/_5=DSh\Fml@:NW`d>q:6MFl;&o_0m)\b\gM;j9*!2tPKLe"ol_A[7PYE^MsQh1\J9@dn#lc>TaM4r;k36I"P,K$#oaATs-YBY)3.-Ej:T]S7scV"[a,CHQ4C6$"J2M8O3#F[8QM.g>g&"P^pIksPl2IfZOoNt2~>endstream +endobj +17 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1950 +>> +stream +Gb!#\gN)%,&:N/3D"T&d@ZV]u&p.rfBp24Gj#@i:I;%0q:_Y2&+pir+'do%A]aL"_`H=jcVUMMeTa5%McN9#W6W5]/7Eb-R!VkahN0b6W"d]UiN%hb(K,9lL:Es>ZQH'sf2E,L1!ohu2&$k:F"ihplDtt*Wkn3><@%7kTI1@%HGolW$'\lh[2^1>6b:C)MU]Y_/17)JXY1IlD-LtE`7HNT9Y`.!1=NA7)SACa$f;`8)Q:8;n17\/\Jt[MbPJ@^\+j+@NX(B[m:S%qR&%8eeK.$sJ;TM;8'cDZ[fuR-gMSQ:V$FX(K//?A4XGU[4/HlhJ&EF&B6qNdr(r&gU\Qk#6%%+p,A#,RJbq=dn/BBQk4:&60OHNpbpEkYQ6Fc>ZSK+=Mp$l[AX_LD1rr?I_nX8d[cFIf>C/BPUBgGhf9g$97S9MQhesY42$:e\9-H;MZCMhs@e,BbGX&6E!oW'GDj/!36\`tO#Fh)^r!s;cWEl%;ei];6_N/#^q6#KXj6<&D^ImH"G\4o(df%MB9lV,,(KUE+1\0s69Y.qIp/*tkI6H=$QMbGHOrn3+&R??)g%]_'!58aahiBf6AksdamdWOq5("WfKVX$N4%fWcI53P+4VJck8/6%a*4#hl;`mB.>:e"9)k(EXN-`0U2S.CRa?)"M@Kt&a4"SnB@KLM#(+K@kt:8S881D%sNEj3c&AcNW&bn]SW(JZ+J*(9(CFjVpuP+i_kF(uoSi7:rF&RJ+V\Ad&Peh/og$FM'NTg%IlIDV?u@A_?lSib(TEKCWE1mq0/QGRb7Y]&g;`:5jgXT4C1?jgB_jS=(_Lm.?Q(>#c!2MV_5B-ssqa:;kCdfI39/,?79Mk8,'>\.Aa1,d.Mmu&T!^QWYbD!6m&Iu]6;h.'`?o;IF$M%=T8jWH=5"Me^f)8JtW=AO^MbMpJ7>h1]#-juFh:i1oMS)1Ta*J/o3B6pG)a!:H)2aQ0@00?P]V^54bOs#X#Nt"6a."LOnl8j,4@t7^U'Q,AmAV8k2@[9DsI+fQDa%"TDi81r*FLq]U4#cer%+sP_@QP"+:Y-A+p[F6d_nDM_d?;*$nsS]Q5DRH"kgLu(F>qlggQ1W/p2[l+[Hc@K*+X\UpW2*3^8mgU,m@EGp=X,R=o@s?J@DhiG!bo*doC.,%@rr?U"h1,`qCeo8cQ0beK!Ofe=TTi_7JN(u(*fJ>F9%'i+)%*Q0O2N1o?RG?o*n&,MHZ6]oqq/Tfmrt0DP%`Hr=Y!dF9IuFh9I?UQnrDf,=6HTSKL)SSC\bbN#1,H7lCRV`XmXut:(C>LhB:?D-V>YR(^EBY$b[500UH4.2%pA2HgphlP]@e$a!Dgc'VDp;aLr>+id2Haoid/F2,D^mfbbIG(,C7]&UAMd"K>'>OW[;TY>VGjQLD`1'LfAr^OH<*9D@Hlr:+@!c3rjN2=Lp4rEo`Z`1_qE$$i!siGr=/\XNN3~>endstream +endobj +18 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2272 +>> +stream +GauHLgN)%,&:N/3m-%%*<%nVb!2e4Ze"fRa8^k%De!Z5'+:=iN;1o:>dPP=g"aH02S)q"e>I$p.3.&L0f`ccmnCMl6dhTY/'=`>TK25o2#3C1a%X&DYE@%J;LETi'%%i081MNX9?muEoGqMb2j_&t\(HTX9]8\?I]Fks\[&dXb.qo:erpD"YgY"LuFet'I7AV9n29Z3G&>^?_C*Q1G25h^_;mBROVCLMV0!!4;P2,ZT8In65,B:N/oapXV5D_+FqTgK"D#b#!iWtN_4Fu$*A?6*OIOlRogp7B46[aX-`!\`:5Jk#ml(B-i;5Y]5@L(GE7DriK&(JAhD#eHSBc(6&&MHV+b["Wdha.!7kf$A"HWc_CQ3\(e8#4rnB'4=!D^Zh.kp.qm+!_5]8!^JP(n9Q!:?KC>0;E"3"AsuFBi+nQd4.!)#!J*l[(>GaM0T96UWV*Wp=TO#N#8gorQ?f%UF+f#&&(<,\X^p+&[Rl.9Oh#%V)0L(5peYqrJ5Uj=UY"Hhi*;.2BhQDokGk8@joOXgT?Zr/WYU[KA@b/6,J>["X_,&;gLm0O-lhj@q0f05-87[L$1&qVO$2Jk/-mu=\!Qpi#FUJo6UG*C5@XJ;%1`d&C2A+!UYYIQ/lKmB1OO\Rf8clY7@Q?ACmsB/Y1R.-0/3EUrc@)ua3@b[!0t[OAC#!Q.^AIl8`5RCJWmjd;GgPYO&GLeZM!YEUasSVR'8_lms4B9k?V9[O;kBahA1WAgl>s3n@$n6'l5/De4(@P&u\efeb'^Di>&A3,blJQX=C+qKLnDe@Wurjosdki]',OaD,f[$?J8N+(kngfs#m%+]Jo%/dX65k,DN-of#EHmB>aRc_FoSj`"qbHY9f3QM``R2C)mFH>_BHH?HY]nm:%C\N2B=/.q')=`cGJ]@P]8R1oAaK/&Rp@saBoZXdPIEirMR'Oa#m8Cp>a\9.0iqLpm)W>Sd!jl!6MiiQkNtLU=ebI)H%UKXY"=YFpArbADZN\4Ydjl8sL.aqBuc2Y-A\D0Td:&5p/2Rie"EdHobcjWPcgm`aUr0`W'=]'L/.jSZFKt=>]Bcg1tO6Srh*H0<:*$`F^Sn]5tF=O--lNqL".9KDuSte5"SiXSPB1B"?\(e+>sZ3_MToiji-Z^g!9t?GK$,6B*>TZ7As$-,ffVE"'o^09Cob"d6VH="geT4iNVN8J"j.7'E%]!HN8f@[UeAd$"l]p?Vu[kMLm'qH6P3JhHj^/MpZ5sO>CG%q>7:Y8NVE[AX:\SuFrmQZXePJjArOMp"t"`X7i/n4H=glR!d/e'Jn\os/V[Y&q`[V,X&XT#dUihLQ9%:bQ/e(mguFe.jH*hi>j+*O=!H&:

[<%:?*_?/.7j0,k1*b??oJ1nubZ7(J\"J\!nfan8Pfr1%*\*CsC8lLdhMZ8JTm=1PG%p`rQ\0nV/j2Bl3foi)a0Gjou5]>AYTq@g;EM*."%?VHk4o9_U&"k)\AW_)IWD?`l#StWH0rW#t#K#D+T*!^/~>endstream +endobj +19 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 599 +>> +stream +Gas1[?#Q2d'Re<239h0V.L-osTl6F/BU,=R>r=\&[k"Qc*/PT8%67?qp\;K!!Ron78(=Z_"hV7'.1Pr/(f`/Rd=`A#_bd[l=Vj@YT?a%]!n\`?]"%jLW6)Af/lL/+3:so=]>Qc&VQDI9]E:G3FJRO-*,dYsR\<2R;n7$?"%@e>!I/Yj#9,!\#&eU>pP$T?S5=>+o~>endstream +endobj +xref +0 20 +0000000000 65535 f +0000000061 00000 n +0000000112 00000 n +0000000219 00000 n +0000000331 00000 n +0000000536 00000 n +0000000641 00000 n +0000000846 00000 n +0000001051 00000 n +0000001256 00000 n +0000001461 00000 n +0000001667 00000 n +0000001737 00000 n +0000002022 00000 n +0000002113 00000 n +0000004459 00000 n +0000006175 00000 n +0000008072 00000 n +0000010114 00000 n +0000012478 00000 n +trailer +<< +/ID +[<4dac3b99daee1a608629bd626643645b><4dac3b99daee1a608629bd626643645b>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 12 0 R +/Root 11 0 R +/Size 20 +>> +startxref +13168 +%%EOF diff --git a/Les09-Supabase-Auth/Les09-Slides.key b/Les09-Supabase-Auth/Les09-Slides.key new file mode 100755 index 0000000..041d0df Binary files /dev/null and b/Les09-Supabase-Auth/Les09-Slides.key differ diff --git a/Les09-Supabase-Auth/Les09-Slides.pdf b/Les09-Supabase-Auth/Les09-Slides.pdf new file mode 100644 index 0000000..27e1ab1 Binary files /dev/null and b/Les09-Supabase-Auth/Les09-Slides.pdf differ diff --git a/Les10-Supabase-Auth/Les10-Lesopdracht.md b/Les10-Supabase-Auth/Les10-Lesopdracht.md new file mode 100644 index 0000000..ade57a4 --- /dev/null +++ b/Les10-Supabase-Auth/Les10-Lesopdracht.md @@ -0,0 +1,81 @@ +# Les 10 β€” Lesopdracht + +## Volledige auth + RLS-policies werkend krijgen + +Duur: ~60 minuten in-class. + +> Voor de gedetailleerde hands-on stappen: zie de [Les10-Hands-on.pdf](./Les10-Hands-on.pdf) bijlage. + +--- + +### Doel + +Aan het einde van deze opdracht is jouw QuickPoll-fork multi-user veilig: +- Eigen polls aanmaken +- Eigen polls bewerken en verwijderen +- Andermans polls bekijken maar niet wijzigen +- "My Polls" pagina toont alleen jouw polls + +--- + +### Vereisten + +1. **`user_id` kolom op `polls`** + ```sql + alter table polls add column user_id uuid references auth.users(id); + ``` + En in je `createPoll` action: stuur `auth.uid()` mee. + +2. **Vier RLS-policies op `polls`** + - Public select + - Auth users insert (eigen user_id) + - Owner update + - Owner delete + +3. **Twee RLS-policies op `options`** + - Public select + - Owner-van-poll insert + +4. **`/my-polls` pagina** + - Server component + - Toont alleen polls van ingelogde user + - Edit-knop per poll (alleen voor eigenaar) + - Delete-knop met confirm + +5. **`/polls/[id]/edit` route** + - Toont 404 als poll niet bestaat of niet van jou is + - Form om vraag en opties aan te passen + +6. **Middleware-bescherming** + - `/create` β†’ redirect naar `/login` als anoniem + - `/my-polls` β†’ idem + +--- + +### Stappenplan + +1. Migratie + `user_id` aanpassen (10 min) +2. RLS-policies schrijven en testen in Supabase dashboard (15 min) +3. `/my-polls` pagina bouwen (15 min) +4. Edit + delete flows (15 min) +5. Test met 2 accounts (5 min) + +--- + +### Op te leveren + +- GitHub-commit met werkende code +- Screenshot van Supabase dashboard β†’ Policies (4 op polls, 2 op options) +- 5-zinnen-reflectie: wat ging fout in je RLS, hoe ontdekte je dat? + +**Inleveren:** Push naar QuickPoll-repo Γ©n screenshot in Teams chat. + +--- + +### Hulp nodig? + +- RLS doet niks? Check dat RLS daadwerkelijk **aan** staat (alter table ... enable) +- "Permission denied" overal? Je `using` clause is te streng β€” gebruik `using (true)` voor public select +- Test ALTIJD met 2 accounts. Anders zie je geen security-gaten. + +Veel succes! diff --git a/Les10-Supabase-Auth/Les10-Lesopdracht.pdf b/Les10-Supabase-Auth/Les10-Lesopdracht.pdf new file mode 100644 index 0000000..6ee80aa --- /dev/null +++ b/Les10-Supabase-Auth/Les10-Lesopdracht.pdf @@ -0,0 +1,105 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 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 +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +7 0 obj +<< +/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/PageMode /UseNone /Pages 10 0 R /Type /Catalog +>> +endobj +9 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Lesopdracht) /Trapped /False +>> +endobj +10 0 obj +<< +/Count 2 /Kids [ 5 0 R 7 0 R ] /Type /Pages +>> +endobj +11 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1947 +>> +stream +Gb!;dD/\/e&H8h>E>VNfl!T6!U-s-r1:1<:(=_H,8nHXaS?Z(D*dX%aAfR-k'1](/uqe\:cX%9:.;>o%VpJfC17skVB`jo+ZYW$6<8o?)o#9AE=`[V4nE:DDbg[W`u[WD5@@.r.O?ltm@6\"cN<=,0buUHH=d%Kj:#@m:91IgTiN)>as)V8)(C$Dle@2_`Mdi1*'@??Pi;bLEsrOULP\?9Hop\"TLe%B+a38eWi/l43tL%F]s<+,@.+=`p3qa"C"+X0C@"EW,a'n$/23E0aTB\H/ud'5'Dog';28f4!s=Ijq(W.^b\?2I0#0sj.J8Gc.B4L_PEi]1%<3VZH9H9"<9m,L2Mmn2cONgP*T6m_%Rq)ek#rop=m79jE3Cr*CF%J7g+CfFMXVeZ2/O-dhmBgJJd_AG'JehX1=?pR'$rm@4>!?eM9JVEk)O=>g=Xg+35n0NbW?IM*XURS9Mb8mJ69]Ua1;Tk,[4R4n=knK.'%so^BF_Om,9g8h^taC!B=M/5;b_f)3ZX2JX&475%\/1i>j)N:P+cfsE>7^l)Z!>a.H*$hhn],3&m`2gX)**R(K[hGVc>KXQb%]_SYXA:S:k\f.YE;C4%BKeK@`C.XU]QJb;o1m;'W>FBIXFklVVPIJbOM'DY:Ai0[,h+?L"<.eDOg6reU9\KSPB/FAo%:I]dg5P?c,=\/1gO1VZ/i4n%PI%/=9LAn;MD._.AYQXNu]Q=*T5cnEBJpAt4'EUF#%LL:&cPIKWaY\H5).aPF.6:6u?PMEPJG,I<.$L;UuEMjXEVU?FXZ/lKGpYueS/^B03f*!^CqqtN&[^8NDnOq9P*1S0G%#b+M1bF5X4+XFpc'QMid(\a&:EFddglmBuZif7g/.ar20[otB42H$m/:>&+#:m_$r):bi,FJ.:[%=1g$@MgoWC*V7C5b1eMGHU(j3XcrHG$!9QF2Ojb!#)>2kTQM"5Z$bb.*t=Kh7qd-$-9,Ad*J$V8LbE^!(Sq9_tK21F?Rqnp$!ZIiHFotj\8E2g;j-n9=@BgW9q)qIMb(fa.feP]OYFhG,eYobQib`B3@0(g6=dT.9-/d@+A16)*8c9=rV*3coEp:^F]WS@<-8[b7p%\R?Y[m!/D6)de!0b1fr3\gZeWO!N7%3H>/-b?IQ=rq@@j^;l#[l50/pNdQ!CE.@LeDQ-;jR0fendstream +endobj +12 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1670 +>> +stream +Gb!;ehfIL@&:X@\Z-#iijXYq\SWp[.?sqsE?m9b40$SP0'5)aNl5!QTo1#TVam@;lSB#7"TG/2qYq56/lbD6ND[eb@8uW9cc7&6`N"m5km6OVLAMpp">JEBaYomY`8:!ei=SFl7cbKm"1-@)YkP`aS1^cO*sE>l.P5LAf["BBtK#o(lBH>9,<%H'0oYKJN_f*?'7W+Y>>3+@;*8!GDT.\mkjT\/e%sGVsmU@!ppW)2LGMKMV98g6p2p:P.\T"St>1DGeIbEqr(I6sGc_0h"uQIl2.@S$kU7U/Zs-sCQ0UA`bZX\S3=5qM:D4*7e-8l#iZjO(hP"VO2c.M%l9a>9Kpa]!WK5fqIZR_i:;bj'q"Oi4i\'J?'HIRnaBR)=$b(QRRD"IRcXMaK@T&1,LSurC=C1`4g4>WPiQ=i3P5Z[o&%Zk&0NZ28+ZD'T]jK)&nXci1E*N:6319QETm'b3N^&5r-`J?tAf2`M'.JY&]gKD`g6a``5kWL,VH^.^CQOpZf#XS5V^0F7H^J6FU3qL;^'&lR'YHA0OR\lfP2!Pu3We(\J7S&pg%jb/R!?XE0hVS]cmh+LVr="gtgY<4"0U1*^ZdfS]Fn^L=Ifrr!\#hQp"_E!`CMt'OnjYhRfj914qeQ#.3fju^Sg%EI8KoZ^)I/b>D<+bOp@lS1T*h@8EhqZ@N@8S7+1_B2@dN8"gAg*mEAd)0E]W=,2Vk$pTV-9fO_K0,Guc02"Xto8H7YO7@gu:Q/IKecA:3)#_41TEIX4`!9s9Q;QU3q*fg"\Lj$KF&.NIOq,!!Ag)]I=aE&/?YG\^L.'`rNTf.`Kr2!(PnTmF:*JM#bs;:>0DR,?[=gi^m#7*Orj9rg@Ge'GNZ<_?CHBp9cZ:1'piMbTlmN^P1RcQqU1#6%+)s.ElAW^R[0CSA*rXU%_9kld%XVYV^B%EEI:nH"bb:uV07LC'D2LSDg<(0s]]g[ZC:endstream +endobj +xref +0 13 +0000000000 65535 f +0000000061 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000446 00000 n +0000000651 00000 n +0000000728 00000 n +0000000933 00000 n +0000001002 00000 n +0000001290 00000 n +0000001356 00000 n +0000003395 00000 n +trailer +<< +/ID +[<7b1b4fe2c9130351e760fb4d2ff648df><7b1b4fe2c9130351e760fb4d2ff648df>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 9 0 R +/Root 8 0 R +/Size 13 +>> +startxref +5157 +%%EOF diff --git a/Les10-Supabase-Auth/Les10-Lesstof.md b/Les10-Supabase-Auth/Les10-Lesstof.md new file mode 100644 index 0000000..16d63ec --- /dev/null +++ b/Les10-Supabase-Auth/Les10-Lesstof.md @@ -0,0 +1,245 @@ +# Les 10 β€” Supabase Auth & Row Level Security: Verdieping + +## Lesstof + +Vervolg op Les 9. We bouwen de auth-flow uit tot productie-kwaliteit en zetten RLS goed in. Aan het einde is je QuickPoll-app multi-user veilig: elke gebruiker kan alleen zijn eigen polls bewerken en verwijderen, anderen kunnen meelezen en stemmen. + +--- + +## 1. Recap Les 9 + +Aan het einde van Les 9 had je: +- `signUp`, `signInWithPassword`, `signOut` server actions +- Navbar met sessie-status +- Middleware die de sessie ververst +- EΓ©n basale RLS-policy op `polls` + +Vandaag gaan we daar veel verder mee. Vier onderwerpen: de drie auth-methodes in detail, sessies onder de motorkap, RLS in detail, en beschermde routes via twee patronen. + +--- + +## 2. De drie auth-methodes + +### 2.1 E-mail + wachtwoord +Wat we in Les 9 gebruikten. Vereist e-mailverificatie in productie. Code is simpel, UX is klassiek. + +### 2.2 Magic link +Geen wachtwoord, gebruiker krijgt een link in zijn e-mail. + +```typescript +await supabase.auth.signInWithOtp({ + email: "user@example.com", + options: { emailRedirectTo: "https://app.com/auth/callback" } +}); +``` + +Voordeel: gebruiker hoeft geen wachtwoord te onthouden, geen "wachtwoord vergeten"-flow. +Nadeel: gebruiker moet altijd zijn e-mail openen. + +### 2.3 OAuth (Google, GitHub, Discord, etc.) + +```typescript +await supabase.auth.signInWithOAuth({ + provider: "google", + options: { redirectTo: "https://app.com/auth/callback" } +}); +``` + +Voordeel: één klik. Nadeel: gebruiker moet account hebben bij die provider. + +**Wanneer welke?** +- B2B SaaS β†’ e-mail + wachtwoord of magic link +- Consumer app β†’ OAuth (Google/Apple) +- Internal tools β†’ magic link +- High-security β†’ e-mail + wachtwoord + MFA + +--- + +## 3. Hoe een sessie Γ©cht werkt + +Een Supabase-sessie bestaat uit twee tokens: + +| Token | Levensduur | Gebruik | +|-------|-----------|---------| +| Access token (JWT) | 1 uur | Elke API-call | +| Refresh token | 30 dagen | Nieuwe access token aanvragen | + +De flow: +1. Login β†’ server stuurt beide tokens terug, opgeslagen in cookies +2. Elke request stuurt het access token mee +3. Na 1 uur: access token verlopen, refresh token genereert een nieuwe +4. Na 30 dagen of na logout: opnieuw inloggen + +`@supabase/ssr` regelt stap 3 automatisch in de middleware. + +### Wat zit er in een JWT? + +```json +{ + "sub": "abc-uuid-van-gebruiker", + "email": "user@example.com", + "role": "authenticated", + "exp": 1734567890 +} +``` + +`sub` is de user-ID. Postgres leest dit uit voor `auth.uid()` in je RLS-policies. + +--- + +## 4. Server- vs. client-componenten + +In Next.js 16 App Router heb je twee plekken waar je auth gebruikt: + +### Server component (default) +```tsx +import { createSupabaseServerClient } from "@/lib/supabase-server"; + +export default async function Page() { + const supabase = await createSupabaseServerClient(); + const { data: { user } } = await supabase.auth.getUser(); + if (!user) redirect("/login"); + return

Hallo {user.email}
; +} +``` + +### Client component ("use client") +```tsx +"use client"; +import { createBrowserClient } from "@supabase/ssr"; + +const supabase = createBrowserClient(URL, KEY); + +export function LogoutButton() { + return ; +} +``` + +**Vuistregel:** doe alles in server components tenzij je interactie nodig hebt (een knop, een form-event). + +--- + +## 5. Row Level Security in detail + +RLS draait in Postgres zelf. Je server action vraagt data op, en Postgres beslist wat je terugkrijgt β€” niet je code. + +### `using` vs. `with check` + +| Clausule | Gebruikt bij | Wat het doet | +|----------|--------------|--------------| +| `using` | SELECT, UPDATE, DELETE | Welke rijen zie/raak je? | +| `with check` | INSERT, UPDATE | Mag de NIEUWE waarde bestaan? | + +Voorbeeld: voorkomen dat een gebruiker een poll claimt die van iemand anders is: + +```sql +create policy "owner can update" + on polls for update + using (auth.uid() = user_id) + with check (auth.uid() = user_id); +``` + +`using` zegt: je mag de rij bewerken als je de eigenaar bent. +`with check` zegt: na de update moet je nog steeds de eigenaar zijn (anders kun je `user_id` overschrijven). + +### Vier basis-policies voor je `polls` tabel + +```sql +-- 1. Iedereen mag polls lezen +create policy "polls public select" + on polls for select + using (true); + +-- 2. Ingelogde gebruikers mogen polls aanmaken +create policy "auth users can insert" + on polls for insert + with check (auth.uid() is not null and auth.uid() = user_id); + +-- 3. Alleen eigenaar mag updaten +create policy "owner can update" + on polls for update + using (auth.uid() = user_id) + with check (auth.uid() = user_id); + +-- 4. Alleen eigenaar mag verwijderen +create policy "owner can delete" + on polls for delete + using (auth.uid() = user_id); +``` + +--- + +## 6. Beschermde routes: twee patronen + +### Patroon 1 β€” middleware redirect +Voor hele route-groepen (bijv. alle `/app/*`): + +```typescript +// middleware.ts +const { data: { user } } = await supabase.auth.getUser(); +if (!user && request.nextUrl.pathname.startsWith("/create")) { + return NextResponse.redirect(new URL("/login", request.url)); +} +``` + +Snel, gecentraliseerd. Nadeel: je kunt geen page-specifieke UX tonen ("log in om dit te zien" met preview). + +### Patroon 2 β€” server component check +Voor pagina-specifieke logica: + +```tsx +export default async function MyPollsPage() { + const supabase = await createSupabaseServerClient(); + const { data: { user } } = await supabase.auth.getUser(); + if (!user) return ; + + const { data: polls } = await supabase + .from("polls") + .select("*") + .eq("user_id", user.id); + + return ; +} +``` + +Gebruik **patroon 1** voor "deze hele sectie is privΓ©", **patroon 2** voor "deze pagina werkt anders per user". + +--- + +## 7. Belangrijk: dubbele veiligheid + +Best practice = check op twee niveaus: + +1. **Middleware** zegt: anonieme bezoeker mag deze route niet zien +2. **RLS** zegt: zelfs als de bezoeker er komt, ziet/wijzigt hij niks dat niet van hem is + +Waarom? Als ooit één laag faalt (bug, vergeten check, security update), is de andere er nog. Diepte-verdediging. + +--- + +## 8. Veelvoorkomende fouten + +| Fout | Oplossing | +|------|-----------| +| Policy werkt niet | RLS aan? `alter table X enable row level security;` | +| `auth.uid()` is null in policy | Cookies komen niet door β€” middleware check | +| Anoniem account kan ineens INSERTen | `with check` ontbreekt op insert-policy | +| User kan andermans rij stelen via update | `with check` op update vergeten | +| Service-role-key in browser | NOOIT. Gebruik anon-key in browser, service-role alleen op server | + +--- + +## 9. Checklist einde les + +- 4 RLS-policies actief op `polls` (select, insert, update, delete) +- 2 RLS-policies actief op `options` (select-iedereen, insert-eigenaar-van-poll) +- "My Polls" pagina toont alleen polls van ingelogde gebruiker +- Edit + Delete knoppen werken alleen voor eigenaar +- Middleware-bescherming op `/create` en `/my-polls` +- Werkende auth-flow getest met 2 verschillende accounts + +--- + +## 10. Vooruitblik + +In Les 11 verlaten we QuickPoll en starten met de Vercel AI SDK. Je app moet op dat moment auth + RLS op productie-niveau hebben β€” dat is de basis waar je AI-features veilig op kunt bouwen. diff --git a/Les10-Supabase-Auth/Les10-Lesstof.pdf b/Les10-Supabase-Auth/Les10-Lesstof.pdf index f6b5c68..aed9750 100644 --- a/Les10-Supabase-Auth/Les10-Lesstof.pdf +++ b/Les10-Supabase-Auth/Les10-Lesstof.pdf @@ -2,7 +2,7 @@ %“Œ‹ž ReportLab Generated PDF document (opensource) 1 0 obj << -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 8 0 R +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R >> endobj 2 0 obj @@ -22,7 +22,7 @@ endobj endobj 5 0 obj << -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << +/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -32,17 +32,12 @@ endobj endobj 6 0 obj << -/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font >> endobj 7 0 obj << -/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << +/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -52,12 +47,17 @@ endobj endobj 8 0 obj << -/BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page >> endobj 9 0 obj << -/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << +/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -67,7 +67,7 @@ endobj endobj 10 0 obj << -/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << +/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -77,7 +77,7 @@ endobj endobj 11 0 obj << -/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << +/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Trans << @@ -87,167 +87,64 @@ endobj endobj 12 0 obj << -/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/PageMode /UseNone /Pages 14 0 R /Type /Catalog >> endobj 13 0 obj << -/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Lesstof) /Trapped /False >> endobj 14 0 obj << -/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Count 6 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R ] /Type /Pages >> endobj 15 0 obj << -/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1819 >> +stream +GauHK>Bed\&:XAWR)cQQ<(XgGIEQL]S(%"qcupN%U)4%JN(n.,:Aa?=ZAi],&3"I8iBSta1\8lQRt1UZ5?>J<$\6"4nE^kl`aO6&bTe0f0AtsEq0eZpd#JYhJ`<*f"HkBNHaSurBb'D_&?.PZ"e<5Ps7mZ6c).LsB3CP9-^1TUB&K-L&C/S[Kj(H$>H7T-N:10Pe/IO"o`B[UJoFrYKdmFnh:>1^aJho\Yb$H-(r;2dJ2_?L/gFfc],:@=iH547[rB",Y@C0J_2!hc)VAtSYmuH1]B90'VgVss-.M[W^"](]`!oiPXZ"1n>,jI2T#=CmlbmLr[3r?17QY+b6d^krh])CR_k(>V3AUo(8sg1F7I2!%_lZO!?o3l4(H$ah(&#Qc^8)\0TU1F7G&KIL9c;Oh*67?2?MVO=BN6#NF(jY5I8_u/qiF4V.$_[?!t%Bgnp%>/&*82&Ki@h^ZpC#_UE2DPLfB+$R<'J]EWRL*a9nMXt9.AL=I=\i;I?1i=?QEo5HPnBaLuk+F^AY':Y:2i0(,7PRk&]<<4"i;1BGZHV;$>G9UFqT0<`S3AP?.UQc/'XHi<=GE9s_.(#$Wgi%K>fn!g.'U,X*0eT#:QT*Xp0]EL(r"&,6Yn6CTFEb$=T`O-_2$9@!Q8rsfVCJ/Y?nlPBCFl@h6o\!S:k,iL[B1s-\*bV&\,RU>e\4qFiAM*(-#ZrRcoiHiG--EMR"1T7h?osmXs:p%92",n3t/fCRh21T!q$bM/9O)TO/sidj';J'/TlMDGS`hM'<&.N/r#!925Tb>e;;]U92na'NEWB1NfWs%/m[eTM98hEg7"7?FTD"g:%;_e<2.+YnlW:-A/0-hU:N;h!=@,^RU&4p5/92`M/AQT`Be132j=5gr#RluhT=<-bd$=>_SU;q7YQu%4GKl*:4F>8,:DGS4ddR1/TL6te]fj\%F4Ojb0Ik6nGYq"&!]93=7S#J?([sDj'="S;YgZEHkqV'VM:;QuB4cdG*hKaWGrGA;0P0AuG&i)dr+-b)b:M8]HR\_$S-eQ-lY&(dscV,9'U0R,T]?l\_2cs"FFZd(IVH/&_;ji#KQlkaAPc03DoCN^*@>)6JX'`QGZendstream endobj 16 0 obj << -/Contents 31 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1951 >> +stream +Gb"/'bBDVu&Dcq.k'5u51.6X86+VuoBk#+CY4Jk9DnMbl=JA6H$40mQEq8Z>n1DV*\NX^D[3SBh#P5Hm\X3s4'-FUV+3X^k%A82CJMJ(t!p,*Qhu_aDN%YBD%MRV%@_&At0d*'aru`=$1"ZtU4Q]L[9gN%#J\./VY3,l(F[An*WJ'$h"pH1j;n*g'95V^lITUM#LG:X1M=!:ZlMAXf=1dW-Vt(>R/>&jL-"n#mUXeABUpdL90S@9dpRfdjD).D[0M.E5r_TR&-44W-'6%t%YN_j$Hk#u;7f@12_9+Ck10tAaE=56t+0Nonk"EeNeW2d%H`+,Oob>PmDPu-L,Es5RO?QoS:\SgT=fd\d^>Cg4kc:%H(n[2QX7alepN/:EmOa`PLh*8<_/+V49DNtg)%F^UlF+E#`Z%Q=tQbW#W"^+%`LOLS,T;r:?_JsrE?\`Ce?HmBu^HK7.[5Q2P_IBTG#9[Z.(`gL[T#pW$r6q:_,>!JtG0o!FL*>21_(.Cb>_Pb>*Q,?aj!Qho0g!o.6/<3Nk1Oahn'C0#F^KLO&,[goqSDe(^2LGZ^M?3oDk?p52*![4P#GFHPgAFu_cMCS_&6k,)0uMt5:9#`OqJoUb@-ip;jkKJOgb\4b8;!YD:c#G'WD1*'"6j+F,]-s7+koF&(7pP5mI=X"UUOUCPHn27k:5os_G&Q9RjQn65RE5[ub,Gld%XhLt?^ruLU&c$9p#KG)G"n,6+KAbn]/4mjDm(li'hWD]s]FM7J6gK;&!P*Kah(dYn`1n+JT-sVJ4J=]Y?`'3gaaH.3.2MQc8;tSf7/HAWK8m9:VrS&*i*4:]`j2Rm`A!:gBiEil>1Gp#jKX;n*"J)QL>a/36J4W76Z$njfu%1*m-h>(`tEf'aCm&Q?#-NU2`NTT`m!FcSUq:bXQchOoe(!XrEJ#lmGRCA'k4;/(*tl80(%Go?AP,l8uAj8J\j<,gE2WM15#Nn(`nCJgMf88b%-U3U&+(hWoaK_IX:R;^E;'J+(^+p?9q'l$O&Y,sKkVb'2R0Zr!o=LTt(,DG\m7_BMI*%ei^j^oJYH$(JW3SXPRIYb03M,;H+XO#G<8mqiS]@.;GJb`HFb7$'dh.1/cb!3WTgcKgO9kCK*/k?hb'UOPnVqjQ6\VW`.NPH-c1Lj7+6]pNmK0>>+Z%HF75&T_NjDTSYF0qn-Ethegfj]Y4K^c_X,!:UhE?NglSo1Pgoka>O9j`L&!m"Z+`FEH'Xg),M8$e.*)8\f+6gf/i?aNX'h(@/gJhdKd\/7QPUe9u7fIY'E8sP>Hl:(S+a%GH7d^*CICc.=Vr^!fh-oL8]$/%h_mJK*nO&1l)$uq\$t@,KcELH@0).#MX7aEH"=&MfCb7@qXMn@Nmok*uM2EP4%Wt6pfeKg'^P#VX"RJq9pQ++2IcG,om=iAF1(N@C8O&CfcFo;g9PfCcL.uR)=u\.4q7)2Y^fY32c4rSH6'=o/R;Fo1q3)r?G!LpL%]Q0X;u%_0[]T_)Pc]F>+>YH*[/s,:UU<3^endstream endobj 17 0 obj << -/Contents 32 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1735 >> +stream +Gb"/'D0+/c&H;*)Z'S7;+c7S\aZX$^",:h:i#j^p1]:.jo1]Nm$5to.ArWL9>CQ\?9i;#+?'J#=.9-GCmj8pY=Q%L-Vm;cH`lYV6FLnZ!e\X9m'm-o!m68*17I:$T>]:NrpTdsd36ZGK%FpJA`3Zu:64<3CNDUKX2hFll2lT*"a*I,`Et)`aI>*Ed%OT.R?*)i`iWhVLjKs@NUW,3+T)mP'JO!<)n2rZ8)`=;FLCI(hQj?W%ONW9>mKpANR'8IIlm[m8ZlphRgHUi-'HsmA$)::dgs[>ZCEqWR=:=o.+;f,3'SKX-*C+#Y>&Z!(GZ+pa(J%Gu;RZ'T`Z!PRo,e<;naNU*C3_.*J%,QhZ-+PFS]'t*XKTCCY$45m)WjjQ4K`KoJo=;s#1'jZo<1HLor"IB.hfBpa^55)'pZdR>EXrn(oi!?MY30Ci=c<'>%BQ]8&q&(aoE3iQ#&o;oL^F=7M6&I),IL;hjN7/dDTYXsEXcaqu=ih'n8'1&\RikFH\5LsIf?NX%@l:t*nQoeLo%`Z;s/bQD)mh7[Wm!DR4ZGiVR-e=AE;9Ieo?4P>T;m7:aL5]oOSNI-;ln^%2D0m#,1W$P!u9pPT'QaFFSP[laWpTPe_g+`OopE2;9;QL:\#?LKGRWkk1LpSG`)u(2#u\T/g6`L\^F;MSo0BdOi5rM8o*M\e(XEREBWu%'ar%ZmW@=9e$<7Bm?]:On6n*gl-YJ31s3(38lP(gYo>:'\sEK&`VYd&W3.VdG#&<5Z%+BdP$#,"8nS9M?Lo^T4p#jcbN32d&$hSo#7!)lN4O7Yl0Z_H$;Y=(Zd#R=Y^=$9'D7lTD/ECsV#7B]*JkCTIXe`ibCP*r\$WH*n*FVjc\G$qWW8d>mNLSF87r(^MO+15hSh"PN([GD6'L&m1)368)9%?>e4m0/endstream endobj 18 0 obj << -/PageMode /UseNone /Pages 20 0 R /Type /Catalog +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1845 >> +stream +Gau`TgN)%,&:N/3m,Z:6`%V1C!9d1UPDbm@j/0@8absdMMLm-X'WI1Y2>,RF_7Lsb9Uedm=eZ12(Q5W3B,>#Bn\pC3?j6%/!gc,-?lUM;^]Y6'\Uj9]oQkg`>#*Pm[fFU&1+=(!f1HkeFGl]+d-nO=qFb-DdU5a:L!EQ+tN!+bjlj@i=&b(WmU);6CFlZR&o64c0q:-iKn(mZ=&e?&U_f$'.e16j]oXnCNsD39U)VQH4H-cQoqbaFHd-3E=5\3._RQ>Q7$mQP.1EQN-u6X"n*#="1+fO7(ZD@njeii]iTe[&VPBt=V;3D@?fIL1c[_HjOKP5+D,YV8)]&sIMbBJI3/P_"&6:^#5sg14VS+0`&>qI+b-IQ;ZX+5aeY4rHS;=*QqI&(k+.IQ#U%5T8LK(`l%cRJQmqm`eQ+daB%o;VNm45d2rYEkoP4qXpMi6HDJHK4pZ&Dr2IM$Rp\fn%Xgs(HaY@e+,)%no._L6o+td]tfNYb7KQe#U^o2al'D[eMOmer@`ba`1:N5-%JkZ\&s*+&R=1(+>#cip1K!*B_,tU-T-CS5V"+FN&.s/T9_oL\X?3ZumMn=*E71_k9\AH#g%6XD?KP'>d1VgIk1C1=fSNQ=eWlLird*rE26OfoU:dJ**7+ZL3;*.D_`M>!kWqg98>+0fi\\Voi@=oO.sYPqC(o-.5Z%Luog;IVu5/_r+=8P&+Y#Ck,jIXO6(j8Uj@^RN.)2L\"CY5Z.eS#'WXB.6&p*&Ren9.f"[!i:bCPj9r7"B=DN_kO9]+S+I6(4$TjK^6Z?6+ul:cLEe+B%8>_;JbaFIG?D-911`$+AI-hS1*T/RDh7=89Up]?EBfDGU$Lq>W=L$GrUQ]qd;c4%^cP3(1H8Y[3J`fm@UUFT0)nmWu%O"TP>I0=)pK(nO.=0NNYeR"Y,Ki&KoULE-,%If$"STS0Zm>CEF;;(CZ.%"_Fu\\)dI/$U=536$Em9,aG1rI]rg=o_%/OV?j4WruWSm5%3CE3Plu0CD)4Vd37kd`jcVL.:G7(]n$?Yf""#!FbP/%J&Rt4.4[cp@A\]Lush\sG]cdO/rn\UF:bS9Hso3DgC>Iuendstream endobj 19 0 obj << -/Author (\(anonymous\)) /CreationDate (D:20260422143453+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260422143453+02'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2276 >> +stream +Gau`T=``=U&:W67f_V9dB5Eq8Dtf]rD;'*e2"d")5r0OK>"PqH;B\?*E@1_!$h06g99u,TQ=NT1p>5ak5n4C<\G`&D!+&pIa\*+K^uYj3a#pjoKC:I?ms7Y1,4p7jDWJ=4!#E3/%uP?["$^0Z/7K*O'='%Y^l(11U,W3?FCE6X-kJkoH5p$(3+:kKMmsis_**+2"[\;aO82'Qhkb8`Mi;s:$QAD'c2(/YBErI7lF=DHF1<=gE&iRMlX/M_JfjS=QH:Du;n16Z-5-gj+[UGQ$GOC]VYM3i6b'd[b@a;[)H/;FaFk\kA9ruAWUj!3jGb.[9S1%!i)fJWb97FV=oj&0_n[MCpDA+!*0>NYC[G`oSf[]k$(odYR.\*GXBjS_V*-JP%,ks#o7+OqcO&3)U39>\ldir*GhqmAX*,Rj?!$gJ16:4&;l;pj6EF3PdN3%0'Wp27Q?+en=b/q/Q(\H]%$tpUIV)!D3MlGc;5[8kUdj,\e(Ui7p2P"N;!GGh@:0?*pL\,$PS;![@[0l%Q&5FD6R`^;PRU:L?Y#NF_&*j0FK>qMK&5MkWGUZA2MsJ.YE*Q6OL"d\5en:^^N*E$X>)b%LuWWQ"*3de$^49K6?4!NM1R"4)>Y_9!ZuZH2ilR7@n-7C=Jq^=\u[Tq.sq*f8rZ[R;R7rX%Ig\I'UNaDtCUqc6Ku+GFqHj7%Wf_mSP0_JK+Rf&BG:t#>VK5JNWE>&'KR$Em?Q@BrcN#'8nHN4Y/RKRHI#$BNP>?&ZJH[B%^%RGaNlR@9W47;qhinT'[h1Djcn,h[uE!iu`#,%E*X(CA=U-psaK\V+KW%7=MTJJ+^Wa5!C0":[(q%NN=^b^"\.,2,^T=Et)QOrrZRDQ(!:>@M.Jbl-C;bkKT!P\.8t"\-GC67@6n2#kk6*&QD?H81b%KccNf2fSPC9eM%J+/>`OTfanPN8O[2WFhm]\\JoUZkf+4h2u%j2iP>W8L(f3Bq:V`Se;Yhdn,cOVN=0kb4b["P)uRqRZ*L3_sL<0@6Lp+j'P:!BAhM+eOBn,!r.B.'%B4"72$p,)Y!:E9us-dlW\d4,(*7FKfeXk/Pp[Z#0d'*EVM@p%bJMZob\7[ab;eS$29,h(=Z[.A^.<.@_Pdj)%Oi6NJu'#+LK=?>8Fg)R7Mj[8Y*OrXgf>7sO"AS8`q#Z,?EB9LgC"K(\)k#O"=qBDT]:X\Y8Jk#RI!9[jM3@p5==:jZas1FiDBQ2NkjJ)-CYJcP`]q9Gb;$3-KZe3(HrkG3*h;\(W[@m1d+P2\B_P,=%$s10*j;&L/!E+s^.AT;6GW[a6^&(R1D^qnul!A$"]_@.?hZ[COsU&U\#\]/,g1SdqT_:&)jp`o"\&Ggjg'7YZKFW:<4r^71OdP8VS-Nsq#$lGD.h4:T^cT&!AI'4HVTbG^dDET?*m;nM(pbR9V@+uG4S*);DW46ncLdJA5Y8;Tg?f=/hf)hGO$Z'Sg2Zm/FHbC\SDXZ&kUT)u=ibn'8o/&*n8s?[]GHj!]/MX8<*#]Vmo"JggVTea)Doels6-AO5.V;D3XQ;;sU>Gu-qk_B#OIA[3>nQE?50UfRq:U&$U=mW3h,G_(Q)<[D3k+DAdAc*pD4XNQW1Q4N$E>iANjQ=6?-1"JHSXN2rp%m\Fo?3,hhkjAjBW:X"D3UI&/eH.Ne1t#n'8A]"aM%Z>\_kX#&<.UP18TuVO_[ad?APlUbTiM$?akkt\8lotC~>endstream endobj 20 0 obj << -/Count 12 /Kids [ 5 0 R 6 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 ] /Type /Pages ->> -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1754 +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1256 >> stream -Gatm:968fX&AI`dqN@),ZH)RfBMY,r@5drSgG=-u0\@WS<&&,Bh>L&4^)dIm^Wc_tF+Dje3.Ju!E>[L5P=3:O')idY^(6$mTqN*T:tfR``!F=L$,pG:TsA)MiJ_PKTcuTq)E9**#Zq(\L"hpr&iTRe,8$-]+?%d`OCAkBf^\W:B'WfYP5`*mW8Pk@B/i8>D.#qr5@IIJ/_a/?9islq>h40TuF@QcO.1meN;AfBJbL.O.X-m_#&LgbBTK=.=A*%)c%uojue/q(MBqo1+=_8L:?m5VYO\=Zg%^QmU5\8\aJ.;ggGrWQMjH\!i(9==lm-#fmfbgP"!04G!9E13hX%b)IOo)Tm4;^Tfn7p]%c_&nEe/h?)XKee7.e)$K[7guEorPU\QL""8*PYhA*W-^62&E7UJKKmBEGon)b.CP[*tc:@f1/>cn*]DM'#M$*aL6"KPUJg5d]k//hOYijpZrN:C@BGa+=GS!mlMGc6G(Gq@RJC?RZVM9:<7=]iF*oSo"%^b7M*m&k>r2:LT(Z-+5piJ!0=][AsR1g'k.T't,5CE0n_@NL*(3rHLAqD)WrgMKH52h2U4p+hJ3<`J7-B7q&F"5"=@t>9GKD111Vn=-6jbfcM8A*S4KRj8`*3G6#XCMXT"<[E1DM-G*+BiROi'8n3m=nnfFXFETqi-1_5sBQ[96,E)\g<>=oGm77%\7IH7noI5`J/F5&CP!t)N;3J'pm+F>8iFteX#G@EP@&L!;2(PN=2Qo'thgQ\(ponu+j3[5VM.CDE)X^$:QuDH,1Q7+T%?Jl3DL;Ze)6Au-(R^RsA5hDISgN55IHdZbS2%0)UT-2rd\."3^8/Yh:neEs*?`Ak?+boW+OkL5J(9Pjs4rJ>WC+([-$.N%@=dW#rEQ5SK2]&N\^CDI'B*Yn*[JII[:YY;(H:*uoF/^"cnW6ag,^W;>/FQ/U[c$>j5IbLegKfRkrF)7E=A'gnF#ZZ=79QO^qj\+&A2#c`8aZdNR,Gs9-n!bI$T>1mBI%Z2U,XCohEWOEX\ML8FX_Bmn>!V`@2^O?b`ffY]\5b#";29Sq7T4N)@okmh2PoU>Kj-"bh\Y>G!*XL]C]@CJ0[Js"H*HA$n_$,M1%3aVhYd\DCEo\h'VlX'$%S%2rJO]DcL]&EID!*EYcA8!e4@_aJScRr%q>mdJ)/8?0t'n?h[dN#fl?P]?2/a,;ZO?#HL4:*b4pWW?F.%]ts%S8^h#~>endstream -endobj -22 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2925 ->> -stream -Gb"/(>E@Ms(4Ml_EF&]+fo/O$e1JIF0gX5ug9fUHgs/Q!A;ar5/5&o5*i&>mpDd(4>JA+m53sK78/00e--t[/#DW8gr]=Be]H>$%5B>Z``Xfk]-"6]T5)uiG]!?,fpe69N40J&+@/75U!X\\h.`KXr%kPPN$VoV^_K/rn_K%f#H]Tb&G]RF"eUEQ>^+\t)\Dme\IVAsmFL3=!@m4!mU5[18@JGD;KXcS7Q>3q^Y2q_jdYXB-HkXALS(m7uU54_*7IWT+X!E$qWjI-_j$ss$+k@\1;YXp7H\;;6<<-Z^O.si>%,'OT=e(3h3cog@7l>nPBPQZ5i,PQ'9,AV-dnEOVS[>GcG-u!'c$Mq^5J3F5A-?I'8JF3q>/E+QT?uLKY#p9m>:p74EKG_<=c@?m#'Ngka)g1nU)g!Om@`.'FM?9ng6\r'c'Km-CBhlr1Y*5]DDj6)QJgY8IJ$uOV^]jKrXGZ)Y>cEWl$_J6[%c,)f?KdFa[^]n"a$7KN\GptQ*>NP)+Ze15Q$\IRj[NaTfk'6;q+Y0RKHR,=rW:"8^5]Adtd>MO:ra'nSM.?IaA`:a'A3eWH0d%FP]Z:r%*$[=dKAOutc@F\"Ks6g]?K+Jf(Mfk?<&n9!Le,!/>X4Nb3N-I#L,l?A4MnXsRYP;a]nIb\Htfr/9iS)-.`E^=.LVUoPsC^+.PE>tYMuM?K3+ZJU"O'Q*B-*]3/M8T_Pa@J<>*9khTF):Fg^\GP#Np1SlPFg.URNVlXN[ML`KFV^!ci?#jB2TD?!Gcm79lm,#bDa:E+Yf(g07C_*E`DdS1'Rt+nFgY3N[kFoje_K#]-$dRh<,K;Pp\>Q'L@]17.qI^k(Z^i!l7QZeOmk_Sjr2Vg*k&P(Du6MTb0VjXT(rGlKNh)&(6t1JW&=ph00P;#3(EZIoYIo2:jHQ=6L@-+uc>G"^4"+(O;ERGT$Lj!I?^^/Ub7''C]:OogqOh`!G84f]P58p%iZ;I73NpL0L?Q1)7tPoJ"Z[s@],7k@#"Lb!Zh[ejY2rYkL_^4ck#dU%GeAp2LAqhCNZfX%EZ>-pK53p`eQIc@UO-YgDkjLCm_6@UOPhMas*-p^DG$^9Vt._KFl%6]e=@4i42q'.=M"rVSB_P'l3T"R3YOJ.'0\@XTd$po2h`R&$J.('V!2Jfc_%3i8!>`9(TG+S#^>4:8Q$jm^2emfrFIj0X87"n070ZhAsOtGTIN3P4fAWW7p*2ISL@f>nq;-neFC?E\k=2MWr@=`LWJE"FroKClk9]8UqqWsY&(c[8e_-g5=/U))PW>;EA1d`6P58at-C,;-BCod3/H^'>7dO;%E;k&RLUEB6gLl^Wi^+@0=B;#NX+9tP:HF;)Vfe%RmX@P+AGR7cJZ7@`WU8uJ++1uAX20F"ECFNj'f!$_a_?$N3q[8TrlTSbLR`mSZ=L?2X8J:5%9EVn^@;Upe%Wp0pM$Fa314su?Tl'dg71hA&"TGII$!-^%D4V@jqp@l]'\t&l]V"-niQ6g&P[9>-p&Z5b!:lU;B;gQa=,Z^$VL13bJ.AD*)X5i=/G?a>$]Dp$MDV.+B)TR.tj9*Thn.LM=#T_]DLLZ(o5KLZ)mhcnu)F1Y\(Qlhqn.rZ;;pS"C#dM]hNg!dA8k_6e#umdEVn"fAkW_$Te=gkkkcK$KX-.!=1_XJ%&87/[s#r*>OZ/TlH4B=)nQX%h?)D+pI+=%f4Q5e\R#l'U8qaqa'5;TGfs?)R#-L%DO9;a46[U*(RdU?9sL^4e=HW3eg].?e&I/$Hc!6p8Kb/0?17Yh&?'U"OOF:;?*.89VM8K(JMFQ#b,6oc>7qNP7$6@C*R*)Meu:1&?ti[7G@a%#&%bScM_1eWA1An,1n8p_(PXaV0>\L%\]l>kqlV@AHrJ++AZifYhJ;,+[WeS?l>4X7=sX$/ca.W7Z]e#Xt(E'Snm9oofacP8r][?0FP:7;&7s9R;.j"qFN2FYM_F:$BBS_.X'"u_e#M;?c#r.KeIGW3:,lC+f9mPIU6$jT$i=90ud=C?f^b;9]qqc3!fot:\o%KM$6*knH.N:B$rL8eB&#\:"4pt:IMX4!n&WW#:fI"LL3NoqDphjSIPmb,Y8e"AY$qH_*@6-3;!k('))M3MMXN_,T=b&O%hLY<59tb;c[Z<-7*BQ.$2G>)DnH!p&)@n&Z3#Hcq(Zpd"p+MSKYTOZO7]9PZEE+21ZP!W0nKm`T1l:j_/t_*_M'cHRMUendstream -endobj -23 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2894 ->> -stream -Gau`V>Beg[&q9SYR!n1tP-nFt#a!hsmR`nFEDDsu)l8#*$VeGMD2M:aaP)hb^B8uhWLG2Zj5e-5#nf$2$td6L5aVVhJ(4JCjoE/TiMT`k."h%!cnG`6r"a`#TDa0RE0A-bKRaFJ9+F83"\V1\Ve7:_a#")>AJ.BT!Yu(KN!1toBEY_pS*+EMd`P'J`%f.[UI$<@Hl+,l8>)1P4;KZ)!>@:E4e-nYi]I7kj5^k$s.A2p!WafN&s4<&P(k)m5T#grjrbMdJL#k\"aiFJB,R11-@p!r(kajgM'3-lZk5e@"UpKjSR**>?(5QLML:!C$W1XK@@.0Aj;]*BK]og7KE<*di9iThM;u*aLQM1gLfp^[KhFrkPe)ajuC\"K\l)1\?9U2Td`GJTb<%*H]-;7[G`/Ls6[s1Sn<+(e!]]Ta:h11?%)R^HPk*FkJsEA)1sD3`pYIVa@0%\oIFK*8Biuoc_c0pnbPY,#S)Y'\!0n"!`L.YoTTKX-$CfZ-`r1LeFTXNTi\Unq.#S&L,9hq/qPriX0XlQU9VTSJ:JATTIPnuK39;NSGAQI%lL&*HGY3*_+>/;#O[46B'qZd)X*WbI_"6Y(B@`tEg#moj:^qBCnM^@LKVKj,qk.P^/pKiF[`P$RN5_d_SlVf/sdHOb4&CteqRU.[!gS5k,h0i1?j`D(A7&KKbYOZdp$P\N-+2TiC?SGPW(efSSP/QX56I&sj3^=T3+&)G2)]emB,`5H?&lMt+%htQK+U9kp@TSluMT'#;Co?,'BF`"o3LM5,E\Y*`,T.8ObN+f>Y>S;/eP;^BPCLqXCM'&[*0S.B9q-;lMJ3k`q6^(,pcSENm"ef-'A:HuRf+j?L*gG6[4F22BfStOJlsRtk%S/;5C4`?iXTOR6tEUT]GUBS:Z@\`ng`7t%r'K05'3mL2*g=GUhAbO1,gs@k0q;1166)7aSo;UWgP_'5HnA+.739RjhSS$PgT>f^H77f=:`=7lFO-!SpDk=5a0OgIdJ@UrP0b@:Aq>n$le?GsgTi_)N"SlsgoHR3f8oYD$;2'0X\NSdGUGnPji,Y#=kfdd&8U[N]GRn"M*k%U9mS7Dgei7Md5.jkg^t9!idA:s=$&md`lT.`9n#gW=/Bp*_>NmcCnO93o>fS9EF5:@_,YX#Bk*$198+8U>8_VCkX0ec1U\d0]q^^0aB;s1W&q5QGF`j#A?KV$,k!qu+cCtBj6qJ6!V2l[,+1qFTMgGIOj"7G7L=#dJe1f*H2m(CEsDl(A"Rh$/jVfCq]%P[KZ9n*PP08@ID0Q-h`&2i0mM7ul#4#Aq^3^2I&#=4N3&Gc^!PdqjM9'G2mp9.n968B%-DgHLJ4q-G")D2!4+%5barf^%:iA(q2?[To&g2nCX;a0QAF6.P\<&SYoH1.D43V1@A3Ql@7aO1pt)"90oRX)JlE8sC&$<[]3e75jS`QeJ)A;)C7Y`^@!cW=aI6UWr1#-?!0?TqR]>gY780_+hp"=KDs4$$Ml9.0b9l7\FVM[$r+)N*::@VV:n^$FO/Epm7SgH'bc[Qgk^F`kli:>ZA*'j&Rsl"\6t+,FQOj1VF*8i=3Sk;d&WYO)uBnb/?!V6JgYd+aendstream -endobj -24 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2717 ->> -stream -Gau`UBli&f')et?dIEMQA$6$oioMg4a`\k>?*YH0mG06l\MtRB`"A%:acR46o&$oBO8(Hpf%;,NpF:YLbB_h815fH)0Y^V5WIdV3.XH,kIA;fEBTA.:aihu]gICK^IK=%kP4,^EJ(F/,_8To?q_-j4$H3P*>[iSVSA2J70%#AoFFXqhgX6%%c!]hNtU>mR089Wb(\"^"c)+TB[EeQi,#;/V6:+A&@WN%Om%i%5bpf,kfKN#_)+LF^m^[s?A(t!7jAqL0KAn'jVX$KM(5:D4AXmV.a4UQe]V1(/+?cW"KpNI$@W[@Eb#8B637cEk;gu5dTL+#Vt;+GR3?XH1/o29/[Q''n.G\TVui'$Kb.?Ed+Qu,4D\i+l^GWo:@,f<1,A^U/@qM'^=cEQfI2JF%[elfd?+1*32H([l6qgH%D&iPl2#uN(ua)%i7Yu8]`h!oAq$L`Q.Fe4WC2?(b[!'ql]q@4\j5@S(dW1l[<1tS58G%41NT=/Hcr7P'WUALuWFrG[#?\bs,C]"Bn)fi^uFKG%]AA)?\FH7!RMOS4#GO4kJh)*G\No>Hb/>=;!ZS5MCB=]fK67k^#ar/SdNA>XaX(WO3*MKMdus9lgGE(RJdYpfR6+U.VgCDB.=Xo;2#$:"FjYXL1%=:[=qOb1q2!o-nlkNWC0*$!BGi$O+W"Q%]*WTM&i;kKm#gBIh4hN$M`+U-`@$Ds7CZYC;jk??WjKSaj3<^U+9\S?'s\BNJlV#b=_/N(:U`&]m%Y>IU+f\pS:B`lV56_ZCHj0@Q2rDA\k$#)LC-gE$H_29>[%'$d^3m0=t;49k87M2!<3hDu5>E#QkY#2njA(`BmZ'O\o2c;lN4gl1?ijs\CaZP.;>NuA7"XAROKaGJYKX[ppEt7)N6o"lo"EEsR-u7rV*heho\q!:6;ic]P?gt5"$-j<&n"a%:)X1R/RbO8:mFah3if#KT'"/GW[jIfGIIj-)Ql^C#lCbR=HhP^SBC6`?gnKWY\\kls#V8@4qP&Yp*/:'s%-,5e_FEu1)VR2%cL$.(m9-bnG6V+@ch;][fisNoh#1mTRdlap.G\6cSiO:Kiom#)3)AW'B\ZQa"O0aiL+jF$;-QiPc<+`*Te81g$%8d6D%6NMZEc2OcYA;+E2&I6m"HLjg/<$aKM$54KW1>T[C<_PAV"8,ZMe`Nd.K[Xn=bTGA&-j8#O]e_DgF18X\-mr9(N_QU\+#@k0&]cU7]GSZ-'VY\+\6M9S$d(U7SkrGe7qQV_P^eEhjKU%B-6"g\_tBRMRiUu3pF'O3$r'VZGH+<@1nQr"hH(Pqm$I0#i;-`S*RuRkD#am1tRkKeLUA90N:IZBAr"M&Tl"Wc/Ri!Te>m;pXVrr:W"AEJLm2XC6i+LY.-@t<%"AXo#qMVONgZsh)_NCO[OMlk%/If:U[Ka@&WWAgWV#"pM>K&lEs!ckhp:j04)))!/I*!o=]AUH]VKZB,A;-4>!A9E3!AU:B$M*LK/LQE-jWX%2[O:->HauW^6Tk5W,*=dklsYWhph"WG6l1j)h/]Z9I)RN3i$pfKrrgGI-nBCk5*X5$]jj+hk`.9X)fr/ni8O:J2S:5eEVp[eYl`;'WBRThG.C%E@Uq&4]kkqaioUlZ:r/+/r;MrLWc!I3pG[>g&$4rdq(F@ATDmfjsp9E1tTW5;$p!NuS(kTi&tQU"o5u^g)$D58Uf/5O&QcQtK82k?0SoAjXcJkHEgIc/X#?.nYRfOCtqCI-sAjmd"JW6VpR[h%jcg]]RkJIhNDnhik5!:kf3.(\mo5>_"f\Hs2q#=-/#:CTh^A[s-It-)ZO?YLk[fAO?[FsrRabIS!.KgTRk5'"[&F_gZrNSDV]bjj!"(:;A"-,ln~>endstream -endobj -25 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2259 ->> -stream -Gau`UqbQ!:&H;Y-MQ*U(QRFEALL3Sd9*d@g.3[Y@=pYKQHT;KuqeH!N-/sFCm.ti8T-6KqN[2>EG%SIZq.@9$TjZdhec5IZJ6*uJgnR+PN6Zt],%.bg];*3W"2YN!0a&&*>)pRQ38A&IJ93o0;:qQp@?ao&\gCjoB`V_^Z>F>D.cL=$J@MGgD4$@$nB%VC\W+6EY%:^#.4!mle5\"+GmD=!$`KtbG743W^CQ,,2lOYQLgsReJ4YU6\A_hW-Gqt`mbD%M7tYiKNnU:%XE'@iTK0mj+p;@C9;[7R<$=X1>'d3W_Cac7$INMg_h3.Thnkjn/2R;M8Kp.>;s@ZME5ItOgM]$ZZRj=@)8L$5/Yci;O`C@/7[GfTY"[1+(X,/]o-OA41';7k5-d:20@V%hj"U2BSC@'N"'#H28t\)K#^Xq=!9:9G9]9=pI>@ZuWI1?`DlB(F+OmiQ9imqN(l#4?A2:ia.JQbr)hFJ(%<<2ShJOP!,Odj.DVXBa2@*.mMgh"L=nM-Hb]]TJ3Dar-[q**iGh5=r"ER@Oa^T!arup1g(^5V,._RhsK\AD.5CR6[_aEX@l@7F_);".t=7fD$qc6;Vr@CEYZ1iA%kl3,lp/$dY"mM,Au^4,Y@>hp7RKVoW``V'[=H(!I4I%O)mc,\@&eKODb]-Cr%W1Ub);#+TZZedO.IWT>so3--#PC>bigoo(B>C1&l]L=YVLe&!t3[AnTk)RU(lnEDsOK-rDaYLB7lY9H)XpCFF;"1=!%q]/\!Xqenj^it&ncVnX-$V@T=Nr_RZQ5M0NZmD1Ka=oijKg[\8Z.b\a6$NSE<2H(M2s<<,3*HZYl"1Yo^MY+rnF!mUZ98Z=5-6WVoKnQIsTLn%ZaRR&Dd&r]7\QsGQ6oZ86sdk`/]eNO0't(L7ub\X\K;<6Dafl4h(Y*gn">q%.bZd;pk[qRlmkW/bV"7mD\YV6sSHZ,KQ7[%JrT]r[ls2T8(t@&Zc@lFEF+ZCZm_=tnS)@#,K>*S=8r')s$Bl:teYTVklUtqL&40#i,J1UO)#C*R"QPgi^8B&[/8"#A`!fo^ZU;"9T/7"!6/EO0PoZ6g*PtUjt\f8]kbN16rkJT7egVnL]dt'/=o\cqh?.8ZaKQEr^BaC7Pga7bA*Aubm3F=XFWj,0XH;I7)F*!V0k0(pX6Ud8%\=G:fCY@SQ)A(5f??7N3FcI,]h>6UZUWA*AlWl=@48AFCEleSOdGq8Delm\KFN-6Sj=N4ktN%<;L[Q>:rLXN*_1Z^YLCe^Nt#U;mE\1Y;Nf*?g;U7ELXqSrOX32*e:g<%YrU^@_8?NRPQ)7R;r<9S9+Is`/#Sm[sf@>[2o'WIHEl!DjXQM-32gH'P5hCMIlE#!!a`<1KQl1a_aLLc^o%!dQ2A3*l^"6A7IaFYg*]&J'B4&9]XPsM@,mGdOk!HBS]0N2^F/EY3'&%Gk)^hWPX2\h3-%bN5B^_kWAR9m9_+D=l/?OInh)44+<15e7g0C>Tf8EIhZr_5G4:dOL>+97&o!E=LNS,~>endstream -endobj -26 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1767 ->> -stream -Gau`UD/\/e&H;*)EP[aj#ngF;W4)h2QaZ,d(8Q4qed_3^0J[LGP9j;)P86ahqi6gZ43k]$aOnUbBaQ'QiUbhFO"pkD5IC9I-NGfHd!\941?]0uEl+NZ%su"@k8&'..!.E[fCMAe"VSBfM`;EGH0$Rk571ird=06\[1fSf]Q(Et\;/&!XZD%D<<4@31F#'D;,3LV"/V4SAKJ*e4*r76J)u8HO[#^/$b6PW6n[),.hU\-=1nRNRVDb+A$$r)po[JNA!KP?8%M,e-@L%[_bSgt(omQJ4ct/-L&+NO&kt7;RpqrqW\,Y/]$(BQ<^pcA0W_^Bj&_'!J@B[)p33F/Yu[\.8Z.`&h$2r1B2mMr]FhVmg!t7G>dkK8f-*CnVGEm6)!t[hTl^$()N-P0In8=!l!<*d=Te-H0Q\l_ZIa)==cE^OuH+&qtqOO?mkP6l5r=-7l0]Unc&g3GHjpj88&pE[k9=00Hc4q%7?2GQfYWkrRc^'N?)*TPee0$(36aGT(d,+0MUEc=t?6]pl+o6gYND>mFLb&[KGMNSSno,WNNkUDgEQJdscf`Y-Rtu45L#td`h)KNmU%s*>bTSlVb>l@C"-%RgsmW0HAef;j5_tW@@$C`B:Jo4h=pD-U\:D#r=]"@[$m79X.Z.SXN)G'!SC^&imRgR=Pj+fqLZK>erZRJ[JpGo*.E@+>-*g3/S8%;e')SS`1=FqM7;B5'i08(98UCn#_gW`Q8KIQ@kuZacm*/5am`HP@PF:U1=P,,9X4LDC,PtZ\ufNY"4p=sF7T[;XhC6=?=MN#!PaE3#U@[fL>&5'j)"6f(=1r8dn/5%^pgrPI/.7=XSs/+$'iH\FXNT5Oh;`mMZcTqpiTm(VJEl-oJaj'J"pjA:"W,'Ii6I6X%HEFiopC!U@mEL+XIWk!`.i9C-h/-C^7!<~>endstream -endobj -27 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2586 ->> -stream -Gb"/(>E@OI&q8_F0u-)d:6;;@h%cUHg#g^8OJ_9^Bn9`#BUQ<>Q6hl\f4g-?pWel3pmu:[IQ@]?<[,d+I[iJi*$"msc/3UQnDaNFY;gJ;#%QRTo3q,,?4P9[3b+bF`/YAP$c4mqJAkjQ-9gY8=cUET:RK>]&:u08DA7c^\h&da*eBV2IWEGkRQbbD3-OiVnfUUeHClLW'*@>5gC*(&?g:hn\;:G26`(jBf5T];+%.2(YcCO"jNdh4[Z/>WYXi&"*lLL\].0SM`_5^rDtk+eI\EAWg1pJ:jLWRRe7-lRmX33`,<(pR4L)]*IP$b3;@1ic$>gp+.i;k!l\O*6>jiW526?b*\*B#_W2'`&F!H0P_.I[$8A-X,?OSf']FXY!fhVQtfa)N?M_&dWP,b=^\inZSkk[9/W;OI5^;:A`jfI(uQ\,CI@^5.cOtcYK@ApK:Z_!CAPREm/+Yn9e5?.!XCp5Y$n86`t;5I3jpRElgo!+W+m/gEjE*!_l<\g.9G^o\'KTTM$ICoGK$__=e>"L-KV5aeu,q=W:f89mtn3?Kb$FKTB=B8@&(nWfA!GU2@j,iU)<^0e)$6'cg4]E=<*iaWuJVW4.M%[1!O80KUN*Uh9/_RB*H*?j&[/0iu-[DO)0ur6-%D8L.YPk19P?52FQ9&6>CRB2s43Q@rEn#8om;1p$5k8*QYP6l!,b#9Yo5`6mh:%iJ!jUC%K3E>d*.09rA+1_X-BE=E??Ao]LbOI8!M*jWJmR(He&B?)6.6183ZgfVgE2FiRt/?[&2uVn2/0XG8J_;1AD'eT1Uo''Q+Vm/F-gl2$$T(U=V-6`]APK=O+1$j_a%XX12RS<6B$I"nm[8L_*io:BQV')UWGp#OK!W9-bPS^mumT`,bsHlObrHK@rRj$5]LWbLO,q3;F@_A#^G160[%tIHAV`9\^VA,>;!ijPS-/PaG&!qdE_reIWNRPdUFK:(W2KLhEDo4,dO\,=GU,1"[+/_8>!akQ)2E,8\(O^>:GtrN4"5Rhsn*7.0[KS5R&4R9T(pNgPO^FIqq(U4NrjgK>_Ods'4f='oVDQOY%U#+TkEMj?:4mkF9nn)4T'J)=X"CJ[Jok(Cps"T($V*)GjD-B,cqGi,k3DiVB-#`/V=Ep9P5r##PKtJjs*E8jme+BWB>EkI0]'f(FGq]lJ!Qs$+l+mgWdm)aeB*J:3k5"G'G=UUOEe$]V\8bLF!f$`pj$mR8JtDEN+S=m%/jO0'GWU\<2b;4rR>"0%T\P0Yq<gs)1,#_gYa8:VW.j:?qEQ`@U!?h,E@0>39"SM]tZ8DK+_tq'G6pLS;>9&&?r+Q[:3AaU^iRUAR)K@D'h58ejAaoQ"p"e+0[gelC&Sg:l?)L4>5>K:bb=5Xf0Sg+&rGl'/^'bGS0M7D\4%Hk3bm;>o-VAkS]N4?a03M,0(q!]T+B#L"O#]'5d70BYj14]']'0WXtj.T:S'mEo't>>9?'75V#XiXMnTXQUS-*h.%na&EZG-U_U`2!>d1E@*oG;nO*mk:!iAKmlMu^;q6Z*X;Jf:ilj(Vo-,m%ohCK/CcnS-FBk?3oA!]X=:&bH8`haP'@Zj>H3*Pj\E9)R,n$)mj2p&78oGA8CG#qMuf<5N.a/GEH^9Rq$9D9o=n+Y$b3\S5r'"nesaG6N:=olAJu;5r<4bgc%(5:j>mn*QN5=59/o^<>\r8cIRP*V.71D@h=I3s0XQoPI\S8%j]s9t7YL(^;^$V2\JY6FHWN2=f$cd;_@j,J-`jS*62r.YA2:d2cc$8<@ZAFEaSbYEPFa.V0DNYuWeY/..=C3EShl?PqCt5p89HM]`g[3&7/mR"n45T\/::6[')DHTN'#'"](n;=H\3pPiYO5ihIf77Q:r;CUdk:DBl1Us.O6RL/?mROe$-X(:j=n*/JBeT=p#,;7b1)+,(R4gd17_K[5gT/"?Yr4cXG%kiIBfd'-Jid<.n3[a_GQ[*ZXP\AfeUlnA,BD;,eL4o=Z]$Ya9KhtrS>>4endstream -endobj -28 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2150 ->> -stream -GauHM>>s99'RnB33&a`_P)"3\2.+Nr=rqT_O%jWb*:r_i\ZS,p8B5lWD>C;O?ee0['^hSYgKkO/ncN5.Ag+I;'aXp)bQ*&FfBRrec?q_TEB2-k/&LR=[nJnYkLo;Dm3L[h\\#qC5HE.$Q]ete/]tP+)GCI1f,DhGot#-!)QH?+-c>ghfX_WQ`.hYM!qq)#)L!%k;))V<5R!=4a:u#H]dK2/pRT'q1:!YI)^lqLV\0O4B#1^l*jA/?#4BBJ'W2/#%3Y#Z&)p"QQ0H83=dEZ*169$b.*ULrP+kULRk%^S"PpdTG/e&Pjk7np&>inV:LM-(N$T*R9.i27Bd#28,@qM6B$h7Q5Oiq`2F=W(#d/*L"lXXnDT9^t_Ko[A(UT2>\W'crh?dCuZSo>:tY`)C!DV/hWV)lYGK00P=@60mYe<"sjb]Y3%+#*%6O;GE/U]kd11u,i'ljAb_.l^>M1-SGhA-$6&1R9bC_-#4XoS[R]]`G<#d3Da&(T]@"![fAMtRnkktm`8iXB^b*X-K[>f79eW/uoT7M2[hQ=m[hDO,W5;W=$;.W?1D?6!$XQ7h6_!&RWdH3T5L]L;qiW'F`^c1@mGHgis#:iF2,;im1ICI/C8NH8*M^LH/>gKqfR1mmd%H&h/NKJWc,#ZF]SA/>1J#*=dFG@/Pku-Lo.3GDkH8F5@ShmUbd-eTO0bE*$iRaC9fla=;I2G@4[QE"uu5Fo\o_T`tPW$lqen-:L?(W2NKiGnI@gr:7,#R7@dAMJ;?52p27?&[qs+9Q"bVtJ_J<@E-PqAcF`";09=YkXsQc+#5Bp/-JU_d.b>8SrWBaYA*!~>endstream -endobj -29 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2772 ->> -stream -Gau`U=``=e&q9#Ik[_k:Zn$gm":!^1fqr8U(eH&gVipTY7#i[O+@$U%<77u-Sf(eGFB[?i`F&d(h-V8Qa+sf5s/>m?Z2f,6L:u9D-1J@teNj'GBRh(:^_o1XI/uiFO<=[5.8LeMidr6m@QurND(H5;$`o?k\e[*simk"t,Z$n,M+2DX$CE+/&j>77M?Rteq5B-8+ora0KMZ4c*gZ7L:_^dIBceZ2;=@7`mj][/O0.O:RgJYdM.B6r=bFZY9a%pOpi!^WHk<@-"O>u02ir2hl#aM5pH9n#JQi$9e\>@f5M-'%TT98?V<;eDCkaG@@05Z[-WIo'C1b@H^uOTNUX+/l>ucSTDsb4I-ke=Bb%J[m#3B8#g>Xj.HkB3-Hu9#V.mVT*5\>(2GF2ZGGiRb5Oe,g\io10VGDCgupXa3N]aIRUl'QBO.RJV8dd%(,eI-i-UZ)@(#QgdL^inl:`?dS7.S@ND[ORS*\gLWebZ9&^APdhkpikP6SnaW^(e[`/iDq==*dlNBSE`Ge0j0l^E+-V"VNW"^QVIJC`[RdN,XDjlEfJ=#f#V5:R8;H0qcY@gA.`/k]ng9^kZaVW##UU;U:L)B\\u19cWSNj9!>DXMlC%Ie:Wg%@FR^`gDWPJ7CJ*FW/1,TMS4:g3Z->9UJf2)4GjbibHoq5,I6F8LIPAdns%mm'A*Tf-VI;G\B6s:+:*)FIed!,(Hu]Xhj`h_9lr^TSeR^L6mo4u"B%bm7rn>Fh^G<>g>n[a,F0eB^`u(/Y7Fa(1UQl9&V/(b=0Q\>n9aT*1O&]hhPt@U7:&?2/$PVDZ@uh7;U\ghEk/2\Un^7*Gc2`5l45Q.PQf\FXlkh%a#D(jRkn?-Z@Or4+UulY=hu/X2;X0tRV'a*\jS0uj--Y=b)%e0D!o/^"-"(O;rRW\O;UQ,PlWFZ9AsU,5MOY&EU4K_Y`pb^#S717d9?n=KieD\D8p9>.oflYJF1@"rNiT$.Qj0JV0=]#uX]-aCtkRgk;>cZ%8ccJqK>YBPZp8`!TqkP&[5,L?Hh8StR,?j>)ZC/cN)9.'MH82QIIfjP44MPMENU":*anM.f$uo\;2_%1dj1F/[9cS4H,E)Q9l>;&HQ8KlCO,u6Bg9]4i(=*DIuD1t(0^7[.Do>EE&^.g[e)PHn2n9a]1ZPXrPmNo2M"Be^uF;mQ!F0k5PZW:i%%T^Qd48e;H@$HI%jmV-%Q@UHXX[X/%=on)B#[rrNYo>s#do#VpJ(55DAC'(;!9DCYK]KhZ7+p&Pf33c)7BnJi.pV*/ZCYH8f;\EMHG#]5&`8f?K%3J2e,Jaq'C,aIb?oJ4&FqV^`=]7Ah`eDZMe[?A](#GOU*]]^bF6fEm]L^b(mRcc^>te+1^%rhdR1];E(oJm+08qNMDRfQ\jl`Pp>m86l:WbqIFgBG.fkM*6Kq=l(WsCKqa8!%OrRu<#OrK;9Ic@$U.-FRsp3M6(D@,qUg_G$K@&/#j0F*5MIgG(epOg1/2)l]PO"<"JrX/r/DuB]qqB*E]&''2"G<\2UrWRQ"ZA>jZB`RrbS_6qg&8mD80BF[BS"g6.qcaN?M')(&%L.RbW80Q5ZJPfWV0ms+rm9:<@V8U3m\WF`1U$?'8CI!o`26s7ROg!!n7iWAA&U6E0W)]`#u8J%pb,k*oT4GICQWtr4dUjQ\p/XO5f2[i1:;W'eDe(C+o*NP8kp3*R'MmmWWp;SL:h488*pU)K0nTr#m>EtBZ2BkT-?JXG>3;HOQ-ciJrhKG?/J%&bq-Iij6]j.[X:!qDWehel#D5m2o_g@+Jf!ZEF)0kX27Q9#ZEiS*@&:'SDMB0/,miJ-u0;"nKS^u05FA9kSJ'e3LFuRWg:;=Oq>dO$[G(mYFGD-(RBF4jhn.4:rEe;l7DuqgX<1A\\F^D3m1g'$d0R34sC/"B>?ESD#[q=c7`cXQ$cMjfV`*P8BLAcaE+&7h$LEHMM?V`5?BOsuZ[C,hp%Ho=U]#dkKl[F@;eq"e_Pns4<49TjjrX\tIf?rR)n)fdDFfE-jhJE&^Anf5scr\XAC$>I*)7endstream -endobj -30 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2137 ->> -stream -Gb"/(CJWtM'`HlqEJ65I)1fhOm)5=BC;g0/F;OHu1Kk*!'iIt.o&qDLB]n"9F^@d8$Y>W]*R+j)pEoV,n\+WVG0PQs+"R?f&\s/c]_fd#ETga3'1>+c&GH!l^H9B.Y+buR-#I3t5a3S*.j,jU@Vi-59/tri866Gl8/jN9[c9%H;@V5r42e8T4&d]KZ/eU4m^0YMY"`b@bT`@9$3]=N\Y_jL?=@ou"nYa%GK]\mT/oBW4Z[R2;7=^P84G#:C^q%JNan`"lWFMVSSFD3%+6GF2+Ado40!f:JJbX>8aug;#dFE2,1Z"kkK30%[*O2r0nraK>(]O;@uppfGL'Jn8^[4A&5?j&j]@"qB"R:?V[_0e7)9F;Pi't\qhO^h/1GFT5mKW,oYU+.[G]T\n4lkI"W%l2=%Xo7R0oIruMEp+`3>E#B+W;tqQu+[5i8o$M>fu&)gBN.B.$Nem-&KTW20G\De3AEVYq[#&m1[7`V@b]2Hn9*85jKn"5mt%`gq]8c&9m!pGjo*/KGF$2q6HUdC'o&%NH2>R,'[ZgMY0MunFL6Lf$VJ'g9uk%S!-fY-Y_Z'36P(0eADH"1!;!]l:e`FcPEH/bsakQm;:J^6`ji(iZV(NiBY:W?_rtEX?P1E3U`(rF>?N!Eotad5=5bdd&!jm_Snj+)ooXdttfor+-AXksGq0REQG`OCAfm`>(@J._Go7?(C8La]20nI5(L9$s0,`C!d]RT2bPJ:gNN%N^MogUAm$[h'KU\,uRCUH'#rjXd,sKa/$,$oA7lmk;/RfOtal-;FZk72\fYNdJ!8EpM371dj^I65c6T^0>!@lM^X`[0Y$IUDI.3Ec#a$#:XT,O]'k475#(i)_@oQN?Fe7kRDbbX^u7a/bHpp/G^\_dBXhrQ6]E%qjM&M,C7@3s,K"ihMYaS\YK\$UCS=FOTsZol$nTrGXt%H39,am5!M3cXoQ?>5Ne68GNEBSnjM@]HC,@*5UIRfI@\7bI]H`FWU#J#hVNkpc,dN-:+&E=/!ek_@irN3@0U_E,7eK#&_;*5*d1XSTR1d#Gdq.+79>:&ctT;`fkT(-mu@h.U>HA7s!A1EG6LQLmpX*:U]CR[2lt-eCG;dACZ+:q>j6U+idl3(XH:u`gj9?arL>.tQ.FpPr@B-4N#Cpg#70HQ/OXB>E/<'EE2=6_j/.Tk;3+0N,_`X-EM";!A"u:,S'hT4UCVDZup6&5GP>VSLE'dn>g?jg>r2]D11g/DZ7!)1Z]gk=q!f%FUCD-,LF`I%81t^Fgu3X\iqV=g8f"q5`g@G,pLsRX5a<3XQn>_D#J@%oP:%H4(WKI\1(>EE$HWZA^]O0+5=otjVW$\j"('4o)Np%OWAc!-)djZ(NN`i.cZ(ELDl:h@.CES&te1Me:p*oN_\EeW9#b%?o2pp8sgP_KfT.(:CFrRQI,FDJk;-;~>endstream -endobj -31 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2380 ->> -stream -Gb"/(>Ar7S'Roe[3%l6!@V&,gUo4=3d*(";bITQN=('+-/2N;q=ir!M7ec4m-rRG=`,X3?BFCUP;j;MZ4I>LV8dm-IA_h:5!-g\^VnXup*6)X,";*=OJmZNmImt]XM5C>F,aDCB&04?6#cMC2&rAJ=#\RCn9I/T%<5:LkEQhmj%@fR&Oi5_h<,0Ztj@lRa9eT&3Vujs_&o"=2&2%*p=#bMcjA?!"a3c8prqP$"86@%!QkHM8'-J0d4Am.7+?Ak@-A-XKS;d@u[G>@[Pi!\t.#r$b6D0>e8Q*4;XG09]]VU4E8.dGU7cOXl]`$lo6[2n]9Tu#K<<;RCg19@uS8o[gZbn0:RO2rV1PM1XaqWCf_NaF4(gk)RYn]F[06h$:\AeKh1r(Ntd'V>kA,Bn"bKCS/2IQ.e4`sItV)1J:`opiYr8#:6*OP(,0PK4BKri?Kd=).r(*H"3Ukg4b'Hkb=YnRfu6:UB,hU,N1k5qs^7#R)*T.,^*N2=>0de3P.)@>e-oK;Xc%7J]$bH,5Z5l2jVTUiM/SAU-&:l`7Gjn.8ep-n=U%o9_-@>;#tXC$QI2_;4R)P_0q&WF[Y7%u&dL4"!H`OF/Ig1;*3q:0gd"1%232'cTR:Epeh:OhIhLWFKq3-f_$Rnf<0fa*=@3%uK]`>s@6qrah6F&V5G:@8^\6stUq)Z0F6om:;?H[]!0`ftFBa5B-Hq_fGi?6,eYNd[LmB^D$$P8.'Vi*Z!PbWbu@I$H8t0"M&KJ1r\VD.W&AmM]Y7%Drr:'e%&%Ol,VWT(fZV(-!f96X_[,qV%*lNl,K[f`d1G-@5qYa4e*fD@S1jAer!Yl66F-0D:']?dE"5[fT9m]Lhq"['2cq(l_G9qVjjtIf3Jp]>u.EQqmmiUMnZ<3&A_B7A>3>r_J0-R%H(@=H"/AW2$^hLG#[r5]osP&mu$/MR-kAWLM>8kG+CY21m53J'0iG`f+[q1G&hKhB/a!m3KX1B-hk)/U\Cj*R+:NFB!7?'%"@^E;f:M@=Q/]OleI2Fdtj;;%cb-n4()@Pe7O]C0WBJBnG1&Ht1RJ_*3Q5J`mG]V(Q_)?kRWQc"VL=lc']dhkEHETYo-*9I_BuTXLVMR&YGYZ-!68Pm..7@T8a0"*'2!aiOOq7QO=gqI:[B$OcOP6POj;q?!:P=#_nhfQA,%*O<7UoDDLEnb$+A1T%lh$?kME(l9.c_Zn-h$=#b'B@7>G+`O0!!J$;\bt7:8cH#-uNIYc?u&Mask:Bo3VH#eW!uaJ[M(XO;ESDc`egF0f,ZGZ+@hfL'A\5`fffF-tkd):-?ZH1eq;R6TWc=+YXJ-:HLhH5Gd6E?#aA9?9Uqar#A#jOe,#"8=!g-i6,YhgY'A?<=2Ie5f<9!B+^rD:7#1gWhUQO'I1sAltoGQW7`_7c;57=+ND??I-SRh.5LoL.m%,W/3cUcIrbQ9l@5g0n)C-Y65^fVFcA/gPLkoX0CMd6H7bE@snr&r;BePYoag.F=6ZE!98N9h!!lCDsWDE#u9Ug?M;)(Kfc'HCMY#hFdhHp5K']D\fY>4r'=h]-9"-lPXKbcfIr/[?F3O1bLRdO4uZ3*U7q'8[65rqFnTpZ#XQ+N!5kuB$oup-=Ng(56TeA'"lBq0En_mLdVsniXFk>:I+`^qa41NWlpl6c+0oIK[ib&KMG2gI%1f,A*u'cs:'R)>&3*D='G&%9BO;!..J#'R/d_G]PuC)nbGrXigb]d3e"XX9R@+c>Hu;V@-1K'*Y7$FOr/_37_Dd%:OOaQVGh@J\^omhB=VR&fMeM94*6&^j6H2)',`-rPi_TCZc+EGK(nP^`T'qtj2MYd`Ank7VgO0`orW@JS;p,~>endstream -endobj -32 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2151 ->> -stream -Gau0Eh/D%-%"HT1_3Y2U%TP&_o#JH![9^66O=a\#DA^_:a3bO/Nh*MI#'YRq;=\q"-?5/8Z,s@@r^_pBA;5\eIoQI:Y5k`&0_dna178%BapEjf#_pgbh`YEeY%)DnVQ_&`"QGf#\K$c$01'9+\qEpmJT(d$[]k9:@bEZI+d)9[?&uK!^MO7s@5p"l/@Xd)`3g!]-oHkG#R<=tK">\ccL2mfklT#Lq;%S@p1'-hfV/r3+HC#1%Y>=GR21Di#_k5SDW>".P?q[oUPs;O(d&%krF0TH!G^1R3eYnjbG=C&e`X(B;OrIe=IBjPaE&"F\1YHEJ]0"C5a%%cd2NR7'6[]NHZ1[seO1(p),7*4]+c\P!&a9dE'`kVcXaJHQd$rZ9rVN`4CHr!#H.[YeT4'c9Z1+HH:t&;i_b4bf50WYDEl%5$%1,H`cD(!.\5f13n%0pI"KMEd^Z.CoBe'/YbA*OX(lUAfaaCPaKrG!;bj*8m+O(GgGg<_ClhQfH^22Rue\4pCUgkEYp7,s]n2W1>&$A&P#$Ua\Fh5n;XQc_U_-D_4N`nTM3iZUJ$Gh_GZ?f;LKG/m=[S!45(RqrCHc[YTVW-D/?6oJ1,AA6AEC#TZ??re..^j:eA&'7SO]rMA7D8/^&D2O)2aDg%Oc6"_Af5ZI>l2B@(IP3q<292ug$G^,,+"BKc^o-"O:`tV2?=KORD%GGg"p3WRU&>IrGdU3[CXmc,l@^Gbq=8Wm4i;<$K1W8mZQ1oNBs/C@2rI8GL:%T[OQ=%<^h8[S72![6ho$U)A`et"4`(FXGp$`>uMif\XjFjZBg;sZ]m)7`KXN#_d5?B#3b!VV7P/@lNSdOu2q!f[mdHpHMs':`JqgQW;ruo,n1:tH!n3gL,PMi&k_)tgjfVi"?]O#pC6*nn#j-QP*n.qmf=/-oM6=BRW5MhEM^hELk3.f9AqX4&hqX_35cBIG#UPZReqF`AZBKu=oUt";_cS!L;B:L:"YA]2_dij)ZO)D+MC3PWOki'GS(*,+J?;HWoiB6?gOts^Qa'mp+F%X859=@X:q@\fiY/'ceq/U>SCd$5JY4KuAY:93B8(CmrAlX=c.oLNfb'$I"$H[%?=fj+uX!sggA^jtb_pXoMA2T&_p!"p2X!ucbU,A9POYipL=\Z4g+X6Kj),noWd@h[GtW)(Gm>dQR,R9&T>Opa_PP>lH2F+#")LhBN/<%],o-MG*2Be1K6TNFD)D?47qKdDP9HEXjU^o>MJhi[`XmmFc"#(mOc,V^a;Y;^fUX-:8kV.ZJSt'MF>"d+TQ*Q65O"MM=hA8E?>bWk3JVF,>@B*IDN<.AH5cWb7PInigQF_*%EMlIQ`;aX8`?-OlZEOoCV6SNQ]Y-NlWq#;%Ls_o`?cLqGS:(0d1h_CTrYY?TV>^1(,]Q^],JbOG"%.j$%X+I^@:H(r%!#^fE1]Xe%CP>$`#YCrghdu5I,D'$C3o0W$77/Nk]T_BOHSN]7~>endstream +GauHJ?$G$]%"7lR_!.';7>scc;30JcFD94#C#&&$0lad\K(B?[>ek"[gh/g0Y2I32nFqHi:dfa4b`f1G$Xg5PJ&'843SQ]6%)VYmJ-1]r2j-?Yab*EE8*<#N$:3$&$c4S]4Q".+D0RcCgZU'0op_J6"`KUYGkHIHnfS9I[".,-f6\o#_Ark?`8FKpOXle->b@D`&]$(S\g>l=re=5GC/'B]X@^;M;i`YU^?pE>/nWqFVC]gL2u4,bD)b.4YKB`fe%WEo-8"Cbm1XC?@oEf"FFo+?e!O:)%WNNZG:YCdVd.I@D)_IJ@u.lP)BYWPT:2Zr/lk3b]/O0>.#*OA)scIFrf`_Yp^W%+FL#UdrS%caoJh'qW6pSl^1:[Pr45d?!J+s$fe>og3uCcdJSH9'O!\+@kAn%J=,LP_3_*H'0"WRH,,,H^:]THp+X@=;rS^3p6CchgB^$Y>urfTNLZ89'a.TAksWoTO`n0)=tCcfI-XjXa#0igPNPo0DR1.u07Th-uFf@epQT,E##HA_h?Rf5ssUn'p2(MLi$Q%k`mk'EAVgH^p%@J$A(,MpPm&sF4TE',J'k)O4_W7""lPENY]W/omL$.50(aD405/l?2[l%D&3(<3pl@Eo@MK>BbZ%/q%~>endstream endobj xref -0 33 +0 21 0000000000 65535 f 0000000061 00000 n 0000000122 00000 n @@ -255,42 +152,30 @@ xref 0000000341 00000 n 0000000446 00000 n 0000000651 00000 n -0000000856 00000 n -0000001061 00000 n -0000001171 00000 n -0000001376 00000 n -0000001582 00000 n -0000001788 00000 n -0000001994 00000 n -0000002200 00000 n -0000002406 00000 n -0000002612 00000 n -0000002818 00000 n -0000003024 00000 n -0000003094 00000 n -0000003375 00000 n -0000003513 00000 n -0000005359 00000 n -0000008376 00000 n -0000011362 00000 n -0000014171 00000 n -0000016522 00000 n -0000018381 00000 n -0000021059 00000 n -0000023301 00000 n -0000026165 00000 n -0000028394 00000 n -0000030866 00000 n +0000000728 00000 n +0000000933 00000 n +0000001138 00000 n +0000001343 00000 n +0000001549 00000 n +0000001755 00000 n +0000001825 00000 n +0000002110 00000 n +0000002202 00000 n +0000004113 00000 n +0000006156 00000 n +0000007983 00000 n +0000009920 00000 n +0000012288 00000 n trailer << /ID -[<35a2564fd62d2f3a937cd464104f15c9><35a2564fd62d2f3a937cd464104f15c9>] +[] % ReportLab generated PDF document -- digest (opensource) -/Info 19 0 R -/Root 18 0 R -/Size 33 +/Info 13 0 R +/Root 12 0 R +/Size 21 >> startxref -33109 +13636 %%EOF diff --git a/Les10-Supabase-Auth/Les10-Slides.pdf b/Les10-Supabase-Auth/Les10-Slides.pdf new file mode 100644 index 0000000..83e0ab4 Binary files /dev/null and b/Les10-Supabase-Auth/Les10-Slides.pdf differ diff --git a/Les11-AI-SDK/polderfest-demo.zip b/Les11-AI-SDK/polderfest-demo.zip new file mode 100644 index 0000000..7c2a9e4 Binary files /dev/null and b/Les11-AI-SDK/polderfest-demo.zip differ diff --git a/Les13-Cursor-Vercel-Deploy/Les13-Slides.key b/Les13-Cursor-Vercel-Deploy/Les13-Slides.key new file mode 100755 index 0000000..ef4bf8c Binary files /dev/null and b/Les13-Cursor-Vercel-Deploy/Les13-Slides.key differ diff --git a/Les13-Cursor-Vercel-Deploy/Les14-Huiswerk.pdf b/Les13-Cursor-Vercel-Deploy/Les14-Huiswerk.pdf deleted file mode 100644 index 0805762..0000000 --- a/Les13-Cursor-Vercel-Deploy/Les14-Huiswerk.pdf +++ /dev/null @@ -1,194 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 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 -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -6 0 obj -<< -/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -10 0 obj -<< -/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -11 0 obj -<< -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 14 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -12 0 obj -<< -/PageMode /UseNone /Pages 14 0 R /Type /Catalog ->> -endobj -13 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260521105902+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260521105902+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 15 Huiswerk) /Trapped /False ->> -endobj -14 0 obj -<< -/Count 7 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R ] /Type /Pages ->> -endobj -15 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2334 ->> -stream -Gatm<8TWXr'#*s=oOTMp+p88i0S``km&nK)4H:aeYK@Z=KI#7cWK<@8RD\phY5ps4[FioP*2b%o"M4JGo^W"k./ksmR4\Ns:>ke8#)pH1#0\GY9YS;tM>+n44QMVH!"kBOQii2!rWi,O.j50i)"X,GU_/`O&&)RBb#e_cF]$($W"mN_GRR+WdRa4",RZpVm]@9Nj;_1s,RD;4GJ1C[C@bmI>3jPZmQV&r)9d\Q;M@mp$_dVJ9,RX$p%pasY3)`U(KFJN+)S..7131e7rWW85&6V"BiPB+eZC=$eP_\"F-p4MjFu/BORJ*Xpjtuio"V+-=9+uFH#onKRniB?1=+,HfKdgPkgYTfOBY@F_+57F!]T.*O*`%Q>C(`4FDdsPi'U0t%J?3Aqa2fWru=Aj\Hg+WTg?Kdg3tRu,%<8dBoL$e/>p^OUu($PiE9`0K_Ei-Y:T>pRSi"@SSu:8-jHBSn8P!+PEH5,5Q\qNl[ekb#;!l*g#SP"lVc^78(8.9Cr*pH2I-M1N%*$r_$*5?,,J/lLToZ)mO!t';ns(RAr;7d:i8s9cEma'p2,uLbj0"LAoKZKXFcf.mMnHgF],(E=OgEPdPe:$*.._EilfsRP%Aen@o-1$9+GRXE.Ua^^uoUP&!*12)sD-2PgD!JI,4hil@P.q;/$PF+f8A%^*bjX[Yu82'k:WcGFU.PD9G+LT!m0H&Fr[@hQ%nPnkpJuZ;IhZZkLu"(moh[Wi?tRWAJUqMs(bOT]14Fi:;7q"G3$ZoORb2]-ieZcACTCV^IHAZFHWXoQXF10)5_o^Kf5>7$?m_3+YE8Mjr(aO?ni0tk:j;-%#!F(**e0q,>.>FYfdi8V[/:VHQeRGdd_O=CSfV18X.qD]7aojW@g"%RbA)(-T-(EY^erF5jA6AY[Hm.+Yes&7A""6%C#Jg\0DC44^N/XD,,WYK%O$Ir+VXA00i)dQEK)]E"=TVGD[9`^T0Cq7A"U%cO#!&e/X]&@A]32N?2a`"h/aYn()V[\T$D@9O42;#->6%Zjiot.oWoa4M9o#a#.T'b_Jo#I_%8p1!!Os0*bulg0)"`LO;&oD!%Ob)oEag:(/.b6^YUiX`q(5MjQNMJB^?:#&\l+*lGIgIn?1RlEk4k(%g)+hFP92_&+<(4DU920b^/"=QM`=M$^Ikc8G>B6:bX0Ra@Q6[ZI`=c+Dj8q1aYJ3Bk6FOJ?n3DG0t!)n7qHp;k_5#b0MIa>#QTSf_H6+q.b-r&A0OTl/>i`].T&X\DisLUsFbb+-q3es!-FHC9[cZar)_:[!^Q5KB;3WBIOI+eP)8ZUS,Y5l\8EJl^I^>;G2jn9SdX7@])lRS+o4t90fT3.s"Es;i_ksFgYrZ99-jMVMORX>ICM&_G?(]u>hs*@rrD8\WES6eT_[ULp9I&JgtkUWOq8k7h4\:s8Ec@#j!7YM+uoce+UnP2sS&[dJ.ar`/$U!FbDfCe^G"VFBh@Pa3Y=ef9f:]El/P2iEZ&dF"42`Qui8$p+F*1o[*aendstream -endobj -16 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1035 ->> -stream -Gb!#[gN)"%&:N^lp5u.>Z$_i6Q8D0sRC"+ha\6>l&S]L4cb)J"jd6eAtiD#T%%bl4;F$3],'VHMRn`\\dd*/EsW3oh\O+9?H_G\?UgrpUjR>k.TC.JBO-=Yh`?Jb20(M%fF^Kh.W;rf"^ZU(U#2FIMq%_o^ffsb:EujL;du^jmUeZ'CQG7/WVeR>ueNV1L$uGB39D13l-LZh3*OBM&G+V7O:66MC!MtaXEbuE:?O9kh-#Rdgb'cX[P;*KUY#>0De?5i4&lXE$\n^e6\kG[l3CqH%S`gnYrp_7iG5'EK$$s@-Cj9jL+;p*MJDA:Tb$.jMB"7p'*pu==4RUBtLR):]Z^l2@g0'_EdVADXOZdkMUEGB!M&kmrK\SaI@=la)fqHM"-2AoWY!e-[nT$fY4)=PpgW"E9;Rp0%L),&co#'dZji@A8=k6g$la>G_%8NN#Dp08#*2qP"#]mi@JgZa]X-jsE\ZMu^`+_kq#!T%<n=NmXR,MG@9UF@NZi/SreXZnC@&E*~>endstream -endobj -17 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1666 ->> -stream -GauHK=`<%a&:WfGQq&HQhR,k`i`c1DKV9UJ5Q]nN:oq;o'kO6o`794aH%Y^lpK>.]FQ`\51i5DCtCg-Bq+Y=Q=#&9+n*:+0PD94d"?ui=.T%3N-gE^jUbE*O]ACSrUU8+[UeXqo!">0C/Y6*>%VF6lt_u,l",Z\J+DT(g%i)CTe$opMPCDHgQE_M-`Wp/j9J'$OC5Ck6;d_$KIVqB?pg3+fmih]@R(tC@D>B^VuP0paj4r]nQK%>eR*Qn1:U!4=fC0>I^&:D6ZZAN4b'=@o8p$g-s!R:`CI>_e2]42Mk'I2'gb%MA8fKa!&+:dnO0&!qJudLY4ElX*E/aAf?D*5ZVm4Xr"F6]XYOHnsaY8`T1g'SD@XtOIHLiTh7-Bb:&g>.Ge@b^hj,gCBEJ^dA3A`\h+HK>2i+@SRB4^eIY\&W?/40rLV*r.uiL^(,f"F)PZakZ3BD\[@92]h$QLA5?uEI@g7G3*fTg(>hGM%#24.SanOBY[g0fO5gVn64WLj1B,m,Na#fuaR**:qSma#fY"bI?1kZ4eQDQc+)qceWEEBUe?rJGp5D?eRD-;i)Ed'Fkhaml#MD0t>[QK2WdP/uG*YE%Tlq'7Oja'TBV*r`shGYLPLA2UT:Ai$_Xb\'dNhXhA.JaA97@dHO1^PZpr=KTt6"=$DJRJB/N0;0&)edc;#+.__\e>M,1,WhUaQr-U-CqPpe'_:ik35p*J5MpmjU[1KggGOK0dmU#?[H]WpVJ[i`pQ*Ee)FIbKJ0UU+$Yg@s"qocG4\1%5bp5ks/6OD-B85@@t`;km4@5g1l1d\'&)-WLdL`Hl]BTT64b_RM]A;k`apY.EE6+KP/)0c=lfV9CmsG4eRb9'YnSS*NH.NcPPEdpebOE54Y4@Hj((1$7ti_q??JEZkO3NF_-#*LkK+^M'#*#Qp%NWfm)MoEHaU6VG@1%;*LlS:d,'[jn(9EZ4pW3SZeNji=K!AN_ZFUZ?J&Dj>Y_%2VGH.J2tr;QcDns&k3c/PXR&e2fOuAF=T0>\mPXt$*;/.t9%DNfNjIJL9Al.+dq$"KgRBSXjl2E/f!R2U/MM2'NtZ+iLt1KHXM1<^?cW2@kGiEMS37CX/+dPH/Vdpr?FF^kqhE7kA"t8Pr(uYQ.P>R]Hs5o8OY_9d<#"InL4sekHPo=;:q?s_#fW-e!p870%-A)D6N8\!Y\5MZ3\@XK!LC\>Ait&52W_IVCbAW$p8+]:GhTJ(;>!Pf&G.&K5I1r$YtqPCrendstream -endobj -18 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1812 ->> -stream -Gb"/'gN)%,&:N_CD#KGDWJqH38:l:]a0D2h1JZsi8iiNS%ho1=N-UOW88A1=$^(SUMe(L<1MNbQ`h\p9m[hC,pOEror6X_YPQJM^_lL6\."hL)-mp#2B5lY8Xt@LQk&V>p,A\`85QXIU5r#f'E'<1a/>+,%T*V%k(R,1;#[fX3hP$G3(R\EqIO0hl-$]ZV*%AVDLJP82[[3(V;Sk1Xo)*`Y%C+&t:kb&%*4c)t'QCL!3MXP24bAIpQRG,:Fk!jS8VTs^H2e(#>K_UD\&g1&a_DG4a(>1mI@@XG#j_Y0cZN2KCBqPq`1:l1p$[FmnYu?<0SL"Rpn+BA?$3kO8UGS5>=<.2V:2E@R2L<=([bm@+jd=,(8,:*$T$&Wa93FL]k#W8(!$bA!HIoKk,'m+:_1laSIkW&9Y]C.^32r\C>.m#X2_93h'N<4>rI=J)W8F]uCKYCs^.HI9lPXE+)&`hDr3"!",*P0hQUX5Q=4>-_\^XObCj!'hXKYp`UkBTq0PBFg,I7&6T6?gqF!+a8U(`)dV>3*Y!_j(TIO(C'$]XP-GO+a%6s.>%\l"TqV+$L,U*M8t;rsa>$j%dEap[9FJ*hAh!=`rBO/+Qs4_E@fQ6H5rs9=_Xn[[)3cZ$]@E&U!tR#((L>o[-Q@`OXNGThcJ;Wu7t;pf)A&s0jKWH'&VMio('cbj(.I9Fg`;aV[hi4,0X9.tI:O7?aH/~>endstream -endobj -19 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 972 ->> -stream -Gb!Sj9lJ`N&A@Zck"*pDn_.c*l(q##7#_ngM*fZ!(Lmuq@!:e;J`P^4CkqJ0R^PhXJd"JQBAT,'o>VaiHLpDX*WqpUTQS)%]?odE(JI*W$I1_mmg\C`DCBm?SohWR$`r`\0/.*-JKM+hmC##G-Y+tH+=(St[O&G*/e!"2#,-P%i5!4F$)IVK\KAn#kIeh:f;6uKq*EBa56$F7QLNTZGXP%Ul\gAB5PQJ&AQ2GZL\mbUSM;rcJ%<1E:HCo`&%'X@CLJn<'[V!Y&5Fk8SQMB7!Yb?b1X,N&N2ejhc4E%WAZiH9_Xj2bS8^:`'jBNZI%"YjNb282]oLN77Gt?Q\tPkd+:[$*PJ/9?)0FW4DV(\a[Q%8kggsD"k9YAY5'!;jCWIM--.^Y$]80TI]%Q1R\,gRiUrSnL\`@P:TYC+&kF9<&77n^A3/uopC@kIOE0=+=5O?Wcno7uregf:XBQ9DRD0h"`!])[HI+n$6ED&/9LR>atZ/=\$Nf#BADA*c/UB6/U$"D=Gg<"cLKctCO_@GXqY]nQ;=XL6VM@p'PFWIp*9a]-LN/J,/>W#\tQS@$'eBdf8D^e`YjiIIF^904K!Yn-.=IKtWBs4qCMf$ENHPkqLHdHY?K;e-%(GLa]OG0@EZMeC8?LshlGdVkZ;DQpEQDq58BlnqQ5Bm84NTK(-Y^h1G?f98VShHX!4aVW@Hfn\\k6c=WU7%-6m"3rZ5i6R2D$'[ojVk\NogGoM?)n.Y[9c5lg>FO"3LSnQf+6tqK<=H=RcZ`qO]80qr49"YO!HK?[W%S_mBj>s]qK!VG]HK28B&h2$8Er=NPkn6S!V:[/`?5@6[%lYNH0?%XX4BcPr&:WeDbY,Fu[S`+t+M(??R^Un5[Vj[\m[fV`H8&B9@R((#h<_?&&R`EK1n42QA&mB#D_BIc1BRdo5IC=t@XsY+*5/16)>%itEl+N"!G?i@c61>>0"6C6_R)E0`3](XA0W<"gEDTt4:8rIM/YIo6&?/E!/C)MQb&8M\9VLZ+Kf4cJccV7G(d1[h]`P?.%1h(EP0it[=1HCk7OWC%b7Yp#_jR;D,c.&EFMi#[@n\[_8f>YcJcGTaZLCj'uNiiKZ9%d9Z3e!6G8^EIk!3d.57R4pbU+'BcQ@jt%o*An1HKZ0jKMfdf+c0OO?pP.j+`8Z&JfT#"0o[Y;Oo`?,(mGE1qDi*A"k$`$$@9>]ji_8"@dGC:T0pc6T)>8*9Z#%?sBuVdjVH1&YO`6o>mU"PdV+I#0:VV"GYTE&`H0RA3IIXULSRWN/_jVkte_\7rS(_.])Hiq:2'@&@l_e/\22A"nNmC9f.9p09mA;Q$Ugqjm+=KBIF6GG_pYUnZOHY^C[/K(.X%)qKsW9]>9do(Y_$Rtd9"jW)_Ki29DmA0K.9jG_N@NB8W_g:R3`K+HfsV(:T7"t5KH0k=7@gnhG4B[J.T$GaNmM^Yf+k/`Br.N>f!kY,+4.Q8i-W9*iLnh7d,\iB7KThp9%.Cc;21rpNg12*e-/%REUc=_C1Sh:r;#^1H_Hg/*G$F?=N'Co?otVUV(,Sp/6Amd8W@%d_]K]fGuUUNHI+.Xbk(,m-%lq9=#8g.>*81se>'AKc.S)J7G?CO9UE^T)E58a]8k]=kA)(Ua@iU6ITN0.Z\"PRjtqZ]E!o_;gfa)H]?,Ff]5U-0/ed;p`N#]/o6V8;<[V0])8UK4F&lX%u^f[V0nnHp>c;ZW"AO5Llgejn)7*08S_p>q5G+@]N[S&hq]P5aYgf9BnI+&qll-:;d=?/!G@?J#0flJ28m'==7I%sPcE7m]2fZ^j"2nm9Ipp_s(o^LV?q.bcJ[7>k3\8P]Fs6AIYii/55oD91G?mU9_)mEB;(i2)W%Aq%;Tf%LN:Z)4i^,=p[fHtRgr_+&*,jcc-,TC:!3;6R;o`4Adukjai*JSFDkr0jsRuIkEeL>\u,a>VjX#;Jnc::9oeV,cegR">+uO,S46CcA00BbCS<8L_^9XdhH!um30r<,.8?,Bg]LeWghKBfL4*(sI^B?[)G89"0MO.6j8U\HGsX1m8VFVU),J#\'1aWmq9C*@bSuk(gBc5o&cAo)3O+Aegr26?RbC;Be.U#V46,!ZN\JWlrW%;W$lojE'W1`'+jNEbY#EG";(?cQin0OsF6O.0eR9C9Jr["2V.[3&2B'Y@(i"q/JA);.^-5"DX?JS4H"I@"i6fOVAj*$f2+r?"(rMt!+>KH2XfpE2rq0N.Et:h+p6JV$0!FN?MPD4t$r\2!)fh3ZXYcDe?aBMPAMh;P2,S@]R:^n)R>+S`@Tj)Pg8!q=eS.a#2n[F98VjW/cnAtJah[YW70B#fb75@3OJlSiFXS036E-j%^5q>2W$n@X.HeilBbUbr.C7Xq]Jn*UM_W:G.`J%:jms5&VJ`O"P%/m(=S7ZkThH\R%mKR==R1m2%qY;=k.Z:u9Fi#Z@BtKAoQZ!s=J/>YP;EJLOSD[P+X=2/@8T3JN)VJSpO6Cb[0Z)72i,so,%XeS2,3J>F,1QGL%k25k21[*gZ7<^%E2'\f@m8l'9%DA[Ju.L~>endstream -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 831 ->> -stream -GarnTgMWKG&:O:SSN]V!BX2n^9cbMNh[N2H=5mZR@,i/3FG9i]o#sBBINMILT&@$##B_#"(57g-I`!VV8721(;+sis3No4k](oe,V@=C=:&7H^!aFf;5+%3r7GZP$._l5fX#=3_S^You3HEd`6m$QpS1DkJ;9i5+of+V'XToWoEUEA+^=1C(I[HXG@"b(S'V2lW<*%/]"l=A\Y]'Uo`2d0-glB8184f/T\O);$_I*_J>kK<9Ore+H'+."M65#JX+E,UnE66Z.:+VsO5VGf9&\<24Y:AT);rZ&Bp1>Jq$;,3Es.=YC:GY^,2R@]_p,Z>\YTp0P87(Tt*0m7W5"BA`Dgh4('*kh;`0?K\4&/3IJEr\&F79m,:cA$l'E_0^7pB04lhOeW-rLA4nMWooh:KXrmLh6g?(e)2cKQ[*(Agl?C".N>1h:@].Y1WlNBH(W/e%S.7BES]CnA+;Igm^/sTBgU7'Z_/8NQu?u--#u?j@AUSf%^GH$8Z=s`neTPt;.2am2qeb>O.Vije(h%90:5k.JtAq[5T.u!L#G1o%RDbmMAPIRAodi?PSn(ZJPIjRW`n%dT^!aO9c]Cm!pM0rjCAV#K^fFb5N-MX*?RQ.N3\C@[/s]03AW'85[Q5^L48#bnS5DsDVb"&7fNH:Q93[~>endstream -endobj -xref -0 22 -0000000000 65535 f -0000000061 00000 n -0000000112 00000 n -0000000219 00000 n -0000000331 00000 n -0000000436 00000 n -0000000641 00000 n -0000000846 00000 n -0000001051 00000 n -0000001256 00000 n -0000001461 00000 n -0000001667 00000 n -0000001873 00000 n -0000001943 00000 n -0000002236 00000 n -0000002334 00000 n -0000004760 00000 n -0000005887 00000 n -0000007645 00000 n -0000009549 00000 n -0000010612 00000 n -0000012811 00000 n -trailer -<< -/ID -[<5aa6d014f38fa67da8a3214366a4339b><5aa6d014f38fa67da8a3214366a4339b>] -% ReportLab generated PDF document -- digest (opensource) - -/Info 13 0 R -/Root 12 0 R -/Size 22 ->> -startxref -13733 -%%EOF diff --git a/Les13-Cursor-Vercel-Deploy/Les14-Lesopdracht.pdf b/Les13-Cursor-Vercel-Deploy/Les14-Lesopdracht.pdf deleted file mode 100644 index 5d953fb..0000000 --- a/Les13-Cursor-Vercel-Deploy/Les14-Lesopdracht.pdf +++ /dev/null @@ -1,156 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 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 -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -6 0 obj -<< -/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -10 0 obj -<< -/PageMode /UseNone /Pages 12 0 R /Type /Catalog ->> -endobj -11 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260521105902+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260521105902+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 15 Lesopdracht) /Trapped /False ->> -endobj -12 0 obj -<< -/Count 5 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R ] /Type /Pages ->> -endobj -13 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2129 ->> -stream -Gb"/'968TB&AJ$CFAYKW&/[j!D+WjmbI`W@Yfssq/riFnS9Sj[HO/uhrqC:*+aIC>YY.`%;RRlE?[#`1?amt(ifCM!A-qb[$k?W"?lWc+^]G.aln,l;H!`\N"d_L\`)kHL30;3?Io)\^3Jpj8ROhmYaV3A4ik+#(hqI]T2Y1'u0$[AhhlDuD#3c?(Tll9UB7b.i0huVe6+P6/@l5Eb1GUpp*'jKnrnS+H4^d_EJn@K0'bJONXt:ddCkFI7cnK!BY3Qb>QRi2r``U`MZGLERif.8W?8'Or:kqN2Yf*<87Y]NDI"*rck_J#_LJ(@/;VXds3P(.+3f_(\nc.:fI>%6h)V`O`To+TIg!6@APSIeWN:"&^D7;[XTi!3(5ML(k64u&NQno-k0k:m9b7J:Jf1!U999^Sn^n>3-5%6ufN)@lrc[CnR,,I&8?6"'Y4*'HU5\R-K[8c*l#p#.a1SY's^Fq5%I`N<$)\hC@\nQ6r%mfLgK+8[.b_kkNa)M9TFHu(Vg2K>$dSLF.bBSOHr_QEQi/:+[f>Eq@'ks^4[lcUnVr4Un$#j0YEDu?9.t!<31gcZ):\/:``H?To"[?P5a!P\)oaOUYd'jTK*2i[)5"oSQq*O7%\gq;...Kl/jOhk6b6"4NM>h=VesEtZJ5t;C=$"=p>M=JWd)pZW(h2K'a8d/cm03^-*O))6s*06gQRu8e=@+.)N[Bjbi8a5Z$)FqY?bH9V)RC/8nStFg;j%?J/1jSn2T1N+KdGTA.RiR%FlMMsE(r_@OYASTCs*B"i'nX_r=kBRKTU*i5O0A(A8j%`@3ajW5)1YmRVd3%oC(sY!ZCC\>t[HU'TD:'\bKY+.BMP2RR1d4\74tfs'aM>uKaOc`08R.RBi)Ce.rrSM?o$-7KWiCsaSe]GSW-)YUQ)j`ifr8QP=)%8PFo2MAI_9EQA_a(`FQ&mQk&K&*2"#X\gcROtTnpW"Bl@qn>[TaSe?pTeO8bmO5Qh.h$-]F*U[>shNZ">kC=L:(5K`%F_8e^t0IH=++$O),DG)5/Ku)uJp(eV=E]_8j0Q\S=i:*7^%,Wi7P$/uCV3p6dFP9ZkA3\b90s\Hq3AtXWf(p:cDe_dOVoTS]'0AQd?X\Xu1AeSg-*7!fS+dMD>E`Hg\>+!K39eSR210K[D/s!tV^?BUIMg`1^I?:8Se=Drg$:IRa?=Skjkdq.bLb9^:=cP1k>?drbj!#8;!-jP,;.fErmdsmq"Y+'ZM*HZ^+5On*+Beg-ScqR92hDJ8R=>qQ=6JsJGCI)B9Aulc6$;"^T11[^AW\c>M@tsjHfaaOqarI8iu,AUH:0sLFaAZn2i2=7BFteHrZVod\dG^M";YQ\Mk2sa:C&!r"mc<.FIaZ9_;H"Qh[kgVN4Wg]$t\p^GY'l9C9$tDF]L%B@7h=)5gjViNYYVG?TQ8h_L,#G:"sJh6+q2I=%)V@bAU?hrJP4Q)Z5V@:KQ7O^(K.6[C[/^6P@[ud3?+pG[@cohEK<1e"8D3Mb0aLd0l6*,OL40hB&--MJu$gLmc8FoYUQa+P71Z<1K9\pTC4s;)t85+mXIWRA(,NB#08Z*qVA`5ZS!9^=0r0%((G.@-YB9$PeoL3l]+&+3^TMrU7nj2G&"Z5@\rHIDR>UWYthY&d/MiGL7F78nD';uU(L'^o`#7iadC&;e8%'Hqs:N[qWSQ2LhsE;qj2ZPg6(/T%pu^d%"_>j9E8H!K;2A70~>endstream -endobj -14 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1390 ->> -stream -Gb!#Z9lK&M&A@C2b[jX!LhW$H-n.DYAD>pXJ42EO]'[HQ7\Xb=P,OW[-cf_GH\^*`5+:8a7ZJqGRQ%!eO#(<3nRCoJ.0/5.@J,``j07AA'1BY41ON;Oh`Sb%%n-K:O/O.A+L,kbT\ar?"Hpin`sa.1Osh"e,"A:TNe!m1(pX]f%GOgB!=:%],69'!JW/YGH_3jSdBt*JPjq]L5/,*m(DGIlLLG?J"N)Ths&()A0*l,T(r3_5`G],_\qEjkVjh489FcQ\[8-EICcDsaic9=l1g/;m'RnG"SXZY+AU=LE)d&_9?Sd)G"lZ]l+6.k%%BohO0ND;W&c,,eZ;L^q@=?,-%UbIo_+Hp=9EkRon-Gd?QC,%!m#l:Nk$aP^%ra[d_@pA1-%&t@4V:P,3%^,k84!Ihk:=sL9]D%I%*9'pcQ(HE((_pJ%g+dY`na!l"QKhU9p=@B,`JJNcZB5`PHVp";_U4,``RTbl#!te_Q\-.145=!8L](b@\%^[kIarGN'n!?bLlF,u61DQjg>2`/=[^db=CLY)E0bGUe\AS9@<">lskSf_?l/ZpTS]f%]nXUi;Qp/J&E,<2tJn,/HCK.aK/X\iDd'5!dOpFk5h6^c1I5"QaJtkkY`8LX?]JChiTcWtW_#YPPiEbmmS8qf\5>Qh<\4@6<[4!6"V)6qE)Zme3QB7R<$LI\Qmh`YBTd2Aars,C>kOYF"ooh2u3`Lmk*\[r%BsW^YK^)n"nd<%iMgKCSbe:Y6#R\-!%*MORK>0<^(3IFfr"L-7pKgG9$;^&67l)e6\%DMrdjL,\i:AU_r%@gHLGsD5'n*qI2qfC`-<>[\0(A+)k6_Buh^.M6"t%m`P:>24elVj-k+'WMcMfI[sK!2sX4$'o$:@8aYORnno5liW;bu=7R7X1YkC#;I,m1dHKE5EIMRD22/=U)\!-I\h.LG9$2"Z_^7mb0$n"F3T,s4etT^Siendstream -endobj -15 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2009 ->> -stream -Gb"/'=``=U&:WfGf^Y/r=qE;mIV?c]SYM$_b/nbj50sufdH21,ZBJnKhqeMIOGXRj)3<4$=;mB,!daq)Ib5qKMB>n!%hjKN-SU"fJ8ps(!%*PUa`BG3=q9-k/EMFM5\:m?BWDFk3B1&V@U(+1El6-#did+W]f4@W@ko[mh-;ga/1WD]n(H4%^.J3d`?\].p!jA9c!S`j@3?no:1fTI<+'75k8#_u5Jh4_dQ`e7AWasAZ]Cg!h(A3TOo>LQcWr@dnU[*2Xa0\o;HWqDO?69::I;S0AdIap"6;i>@DZm``OkMSNR$BRLK\PGH?)D3f>lmW`.EHN8npHs?k:4U%nR$!lW5$A45`r)E7'7o;:;7@Y'q@2e-KnrtAJ4DF\Of\Em*+_ghBKKRj@V!Zq(+QJ$8B[Se'$u$Xem?Q)UeqQa8cOD,:ibc<3HpVp]j3a\BiFFO/.bN&>lj0se^`bcKQfrB06ScRP?^*qCJ7;([#:TmP'CbYM8s!7H#'"tUXU:2)9J#A0#`GhN.djUceRip+&LBC(pZtTEJo;/3m`_@]5bt#be>nqf5Nn,W$g!,1&C"l^4QL8u34V!oqkb;AqSTW0a`EA`a=O]25du=h[cehBSI#Sti96]Z8p5rllW(S!+$Eo?R9SM"KdE$Zk7][2]r4X1u(1He^3JC;`BFMc:8su)^&1ilZa'aL&E)La`sP=a#So-Q'%fq^kq+(99>pNB&V&sJa3j^J1@RQjEVB3cak&K%GMKM0N3t-DfQ(fslW7!a3csF]-rlXKN[,W\YXtc7mX?X$[r<>c5*R"V%Vfe1WXYHWKlW6ng:"F@.kf[,;\C>Es^683*#kE@C4iR(Kgu@8;:ooi]HhZPZcO%6c`Hh+q4N![Bf'%+@,DkS+g3sfXRk3XU9b_VC_u:q/6U$h75QA=0P@0^_\rdFgaHId[J@<$<9RCgTS]Bqe@GemLs:nW(`k_(/I#LR]bh%Dt68djbP2[qJr^0kdkHm]!fY<9r6lk7+Bt&#TDgWM4jha5A.4@B8d=\!dqRlR4ee]+0($R6SL`&'UpHEr~>endstream -endobj -16 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 627 ->> -stream -GasId9lJN8'YOa`W(/uUuLU]^jPVJrk9<;[Hnih%&/QL3:'fXm'S_%CgM8aH_P2LA2RU_Yn$kHt>fM.8>#"p4,Xcnp)E%JWsbuZU5mZfN@Ot":ZiB$)(Vcc5?EijbS(B(_0:kp@4>=!HEFS=*~>endstream -endobj -17 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2099 ->> -stream -Gb"/'=``=U&:WfGfZ?fJ:1>FNG_,9Ma#WP<9q4aoV]EP!,Xg$,$&4-"35<08?D;7-X?DQ8i^;]+I(P;Bm'd?0-N;Hi1X#i*:#Y1q$C&AK#1OtX9TS0"eG*bijtb8+J6:U6Hj7NuoFI1kOb;[,f4R8NDL-t_2V=N4c@$02kBS-ATG0),DGVIICO4b?_PuR9pWe/E\;[+pRlIS:hk&`ja0g4WEEhg,c/oPQE!1$;l;9.&R+1XGQtESoiT'/7*6MA_iEXM>^8Oq_Es638H#R`0G>M:2V*kH)`0El)Obm=+KBWP5T5.R3I%/fBq6mbiIk3(B%cE[)U,(r+:P(RJ")nHHT5\'t0UK2iH6t#*AU\]D\9s8(h\Mn#+H"bE]3j/*nj8PD6FSsVhlk800aM;ZB,`GZ6!C^7O41o_U+8CQ:AsmRe&+JBnh^@bC^ltsr3o6DndW>%"4]9RhY1@dZ[U^:eV_sF=hc>rq&D(Hl7#p=AtU;"Xk'I0Uqs/,q9BlN]G20:8ttVkMN/UrK'$tR3*c^f/.)];]G-M'#-rdRM)nn3mm=9=:Qoam=:U]n*mM-iXF&XoB9FWF&>V9r-3CTjA>Q)X`>*hiO9l)oAQHlEP[!KrgrHaB.I1;`*Y\c_Mt0b3.PJ3CB0/OgA.XTuZY*K9ZhWRK[ZknfdnW=LMA1qG7Z1(h!ks%GDJ\-<(Gd/ZY,/4HfepGY\DqOp3Jau*AHbX8XXh^68K)$1`ml*?T5Md>ak(O:IaMEmPC"C6Hi5&1^!l$dWffIWBp-N(ZRccq,eoVLf$L\@r@T!X$=KCdnn$@`a6^OuD&9)kA)$=h&78MUB^2_gDAM[<0kB0i!3jL5EUC/'NSq+MZ=N+[2>1dcF.gcXWNHFbbd=,]nig4!$Ou$2S-2&9N[3?c#(l_O#@o!_VPP5,jAX_F]8QfBB9]Wd4/*2%+ZLJEfR8tC@j.*NKO&e8V4o:17^j81J!FF)9,Im(8BEe-[?m!rZ@<.PH9$1uK^8F0G1.XUbk`;)Y.]bDb#I70qg2l>HQe5!O!\VqEL_[u]Ukob$S.AAT+MrZ]Pf%g,>,YBd5,!_281?/:SWI#1eL\9BKda81K1tFK9;p7Zi\q,V?,aTiEX8#k#361&Vpgi$I4B5W0RYo\@:C'O(#Ei).dBsfbcL6Xf;:0XXO%FOC-4Wq=KR)\0^msA%MaY2;WtGrW9&>C,?IOG4eK6E`PGe>Gd[fJaFS;Uj&/%>^U)9LHO8]C96@fVE@u^:/L"Mih%Wd4L%scfCU5&.I%%I7+jAoKkE$WY0oOqoL%qOkZ#CgLUCK$ZnXD)c-8Ke46h.$FAH2I%OQtQ,=j*'Gg8]@-dlH$JOD9endstream -endobj -xref -0 18 -0000000000 65535 f -0000000061 00000 n -0000000112 00000 n -0000000219 00000 n -0000000331 00000 n -0000000436 00000 n -0000000641 00000 n -0000000846 00000 n -0000001051 00000 n -0000001256 00000 n -0000001461 00000 n -0000001531 00000 n -0000001827 00000 n -0000001911 00000 n -0000004132 00000 n -0000005614 00000 n -0000007715 00000 n -0000008433 00000 n -trailer -<< -/ID -[] -% ReportLab generated PDF document -- digest (opensource) - -/Info 11 0 R -/Root 10 0 R -/Size 18 ->> -startxref -10624 -%%EOF diff --git a/Les13-Cursor-Vercel-Deploy/Les14-Lesstof.pdf b/Les13-Cursor-Vercel-Deploy/Les14-Lesstof.pdf deleted file mode 100644 index 86f61bf..0000000 --- a/Les13-Cursor-Vercel-Deploy/Les14-Lesstof.pdf +++ /dev/null @@ -1,271 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 5 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 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 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 -<< -/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -10 0 obj -<< -/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -11 0 obj -<< -/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -12 0 obj -<< -/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -13 0 obj -<< -/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -14 0 obj -<< -/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -15 0 obj -<< -/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -16 0 obj -<< -/PageMode /UseNone /Pages 18 0 R /Type /Catalog ->> -endobj -17 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260521105902+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260521105902+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 15 Lesstof) /Trapped /False ->> -endobj -18 0 obj -<< -/Count 11 /Kids [ 4 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R - 15 0 R ] /Type /Pages ->> -endobj -19 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1912 ->> -stream -Gatm;;0/3d&:WeDlq;!>p7b.\"PfK19A<-eD;)Sg4_1FBZ"t(`j8sMYqt7E2(pG"oij8$W/Wb/MPBO]Z@6jA-b]gDo\/<)`f*iLOi<5N8*#QDFX.f48,$]*5EIlb+[0$maYk8C0WKi&cL^87@^o&7D4U2t!&`4+Hi4.)(Qj9%4aU="-nHAN4#Dg]2iJYDB2A)ISCPTDd"M=!#pj=)g5XZ>-L08uP(sZM(W7n9=&B\8(L#E*Y5(*9O%<'crk0Zl%"MPY>N+N7KEpgd%qL"CfEF-^@r$BrM;9OYeMrue,KAd4*X(C1.3,p[cD>Wd5%a5+SaU9sTc/+-&3":GHm!QZ5h&$d;$<5Z:G_'Yj=`ePk-#h\WE]ghdAI8_R!lCq`G?VU3I%*')!Kg@m(paNk?)H43f*&?A(-l_rZ(RUe40,PQB9>V;N!-nrq:1P?ME,@p/3IO]I%T/LSR\\P->@k_NRM>!2O_!9^Q"e2K_j3k)sf\cB%>2l.iNatW3OrUBOd#[cMfbGD9O:Y@m4[Y3J>!q^foEUR!tBenEMn-&@tstZ$98#XUN2VB:d@bn1[7&]qCq4-?HA>'f&E.$uW4hGR9_;/6<_gq'NF=-JNZOR"H:=YWKN%,iDA:L!IDd.3pAMPF9Q$8Ad-6AZ!JJW/,"&_Eo"W^Yolb0aF-+*4_*k4cJJX;8;]m$O_B0R[C=`g(S4YE-X7uVa\ktboU?Te=beY)fF5*fIT>(j_('80Hi;II1VYU6`C?/mAL*15J9:0D6n*6O-A5)4,Vd3'Sqgn7)n7HSN[O[YD4,H#KXIYMX;Jhj]rM]O/(oSSF%8N!Sj:"YZbTlq45QHrWhTR6Cj1DGV.D!k8fXD4YF]a[T:KaeFITeI9l1>1JHblAbp]A#'o0B0p6%;&pY:dj7"0.R>rR=ZH#i8o^NJ8;$-Ukncj-i6^d=Oq6]1i-5m[HO8DK*4%.Ok^^b'7@D/u>0fYN>7E!t;'JjKU&q&7?Kr;!X!S1AW)_=1S8`=p>;`rCRNYjO"q8,+Ucn?(I&A^:>hV0aK*^)phkW$.s3l972$P:"5<+9rF38E9if.=Mhd8W`4i;?u_aFPEp_8#8ECk&sE)c)3MC:!ad?,r@*[?A\BL_Z=.HnMk,P\>Q)LWm,Sc]R`M^8@H*6O0TB[peWn*t)X'h]b#,U)kBe0@-8+/X7PI(\.a?rC)X^T`T-W\C&QDSEc]T-=]nFc_:<%Y8E`%/#?jg\m5_1:)k'=KFM_Ip';j-PC`5AL1K9Tn&?;Kd4d+^#P;3SA4)WN,p?DfTe)BWMQk!@=_T?ITi6PM[:U5@ruDW1tQd@,SA_0g87m]3^g:G:BHFJ!7c,_,Z%jZ3MQtP3!hRV%leRiM*8ps?'q.G?ul*Og:5kG[umG,8k#f(H]F&L,/ZJ@#ecV4b`;(EuM1eH@S#=._a)BSememMioGG:!YjoTmE;,`D5ak0\"S@"f$Lqq:PPD:n0&cZ[7[Lg.Ct86.Dgi3&VFMh6$/?K4+W_;^I)0~>endstream -endobj -20 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2006 ->> -stream -Gb!;d>BA7Q'RnB336UYU?s:O409TI]B?IXsB"8T]4QQUo;%g@HAS``2l1DaZO`,5\*>jO)9i='1o]_n7*1`!:=SSWPru_Mj5(p`[$C&AG!n8A'$t$ooYkPOqR)c_s!jRGjq[A:!Rm4uk]`_T$GWt^J#`BA4*H2--6MLU!Y2c$ma:q=>oI(:==^3pO"EcldXf]'h`u&^]&D-OOeb-=t_MI@gYEf[;XPO5;1MEmiMeY>lU+UXu(dn>j]?d\=V4O\LQt[SWV]o'UXFLmm*0+>G*p%[7(MXB!C7uiB`[WDY_sd_^:X!,cI)f[67Ifg+^K*?k(\!S(H;_SNa_@,5?lbd3aCtb;Z!'tA$"DX)7pG=*]OrHD_KJ&^K=Js,;Veck;3,^+=cV^)0i?8;#j!,nNk!?(T\#4_%2G*GW-68&H+jY<[S[+_41`>]5L!Y_>eS>EI/+J&)f("_i:`oZ0*ra9(Ts@)&dFkQ?_?A_ql[iC&qn0c*?L&&-]=["!"K(;N/.\gnA3u-QG;dE7<Y1@"Kn?Q\,bY04k"/C&3g!9AW3Cfq3A/eF9L^KTN<]@->%]Ni&1`HI#@s:c0Fj?K3nNu(!bU`QHr1d"@=hF@*(IA@&`6sP-'7>13$no0Ji07;._$Akn?KUWlD[:cX9P"*FCPLh1aT!$Cb.Bbl`\KR(`S\4YMc`YC`aG-ZN5(.ZmP5%^BN*r.>WM3B-X9nf[ld"$SA]Ug/Y[g9?LC9N(nm"Uu-SN73;1EeC#6lN;^=pfKWRfNh2$o5W^H,+7FfhqOXHMWj/4'Y?Gb-&r.UQgtCCG/ekGQ:Lk7W-*_c.DKXP7),1!_/)ZEb5geAB':qlf]s)5q@!Y[JcX.U;#*_g0eE[:<([&XHS0*,6ETO'%F]]-F&<'D1!5arMZgO-WW0)"nQ3gX(IL/uhLF"RQI]/\d[5Jd^!%fiiu1bIpSJ+dP/k93LTR95U!()k.0Rr6br'VOkXPIRu;;)8bsmErEfSdm^^\HIqqS::<'Cm:lmB"ng'h1;?D4[e,@FS$DfpRkuJj`@Vg#p@SsHXeRs_quT?A6;@7;1B)`2<7qiN<8T\-6_/=D5Vk5l)67S"&Kd??gQdgNE!FS+*endstream -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2085 ->> -stream -Gb!#\>Ar7S'RnZ;3%.hO.:SaA,`9am%)&A9ZDL((p7X*87A4QEZK+W`QQjM#h]ZQ.E\;Oa%`+;N&h3B5(S8et0qe@qP4;]-UX.f.0+^FNPo7=E7\fh:k](uYhM3EKDL^8:I5gk2(^]R@f,%W0Rj1ZXD!>Gm\:E0Qa&2]Ii&BT;C`JsgK&Y,V0O8)%pfCuK@P?PY]Lm<.^B)RKm\:UiAZDaor..[N_L,8&,4oT[3Dt8`7j(Y*_n97m2GZqq>o)P@&p7IU];HE&s?E0hooD)'keIE#6@/*.5lJR"1-U+Q>Ndmis?p9dVBL!k`>oMX(Cim2IU1:"b#uA@\iBF5Zm9JrGJ5GOQc5`1OS"i8#9c=VEl?%W@_F9LIL4&D)V"=DX>SCTIT^7#VI2pSl(".;L#'$Uh+]fV,D_.1WGBX3K4SNodN%F\GKnhtlOAYBB3!i"\Y__P;0_N"R`\GC^p?.t),lN-ER$YhI@RY8r"!n3b'2ip2D.+i`mfrWcFS#2L0MXGIrZa-a(/.1DH3$]+7c3@f''B4;WZ%I=Ki[0%G3JWu_GZj!5,/00t\[=PctY4f?U[(!LKV$itW"+"0FL*Yi=$OC%V#c0PjhI$?2Hp3q\kA$;qK?m[W;dTb#BVJ$t?JJkJ)Ba-`l['H])CR)MB/,>]=^37u"qt"MX"d+-NB,^DbKX]E,b?A02eW22Ss:$kit@qMoM:]NFVp0cRo+S#?Ira='S*C9Wg]9$"(Pds:Ipudn1n*dC\2P3>/RDE]:5KWT*RB;(ASRuF]%Pb[8>$p9hqHUm=?c9Ke*'icc/gE)L$N[BA\g[b(o=R%_WQOr&X95n\H?e$KU?6G/g8okeQ7=`g,pG1Wh:]u#5h-GSJif79Y2V8(+WX^TrP7"#T=m=>F7gPKdc>4L5s>OTbb_;%GKQ0.b(hdSQ$"at?::[$^ncddUkE:l_->+kN5Q0KEVaWt[Bf43f8!GJrFLOc'VdTI4,6]%c1%n]R&LEHdj(ejZ'^Jn2Au=@2ZB5'R7^D3N-@7/rVVD3pNO#\c]t%X]d]jKDl%T`K&3CRbEOLM+N]cr0g^43f<6W=03-$&]7;M(Eh)N+Q;g_Ep?.1`ra(cTCr0\h>HWF3`k5u!e98YJbIGk#nNW[$W)G,.3fO_8Ea@7i0gaBJ=rPl6(`Lj3=rPl6ck_SKZ6#CQ0Fo6u(7H<oKo4mWYXn+o5`IfD<`/LYn8%"J_?dW@kpT[a9:CrJ+=VlBmDN^ucS`Z*F.GdsWa=]0(F2k;=ieW!BtBQQXH>SXrIa/4klj14\0mo$L;/-oo]nR4=t?C5_Wq1m2-$+2oSQhE_NLO.@+Ug:`EKffX)kH6[B!T3M=XLU2q(S%K`0A1H#T_f`&M=XTI4+Xp?)ZNH$"pLQ4U>dQll6mm#L+A85lqah*Gj7a7q(Z8VTqOF!X\VI2NdiF;MqgMR0&u?.5,I(3@`a2q2ij#+=(Cm.#qL%KKLNJj#jD5h,$^fAC[hf*lG^W,!6('F,cn^8hLp%n*R6LH`YIU<<*T42SnSnmFb:B[Q?6`k?Ug=,920_poO&O^buupmA3"eGlMq1PR?+H30N+(hpqu>HgbR!QVZ:5[bXZ@f=]3)@[VPqDbFg;>i15`j9-`2=!;mRVn+UO-sm6B>]DZF-T5JUd-u:oq&,3S>$ee?#TB3Cg4"[qHLhkj5\$#2ZMnI7@]u"!r=JLU4pn0.Z#DK=U[b\3p?"emnbm'Uh8s?S>Bu\?^IB+594'oU@ht;JP+:'Fn!Yler!REUef"~>endstream -endobj -22 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 704 ->> -stream -Gas1\fl!<6'Rf^W*6hCpFZd)e?LjD^N>a6<2qjTn+"6Y1/H[G`-rQ=[mq!B(1hcquI(B!AEu.IT\n1W]G/3DHMfT3(OK9Q^f17Oa2IX?<=%6=5K1A[)?@i!P]hkh6J_4r%Q'ZI,Eb@8:PMA1eTIA)hBi,&8:7)0n7TcS)SW&Oa:ZOe&/]]6+fQ&2dTjlG`^k84\pFDL!q49SW3]3$k4mA(+knhU.V3UC0gD`)D;Bk\]Erd*05l$-BTA5Q6up^r*T1_8GYIZLj'RNZ1'T=Q;m(H!&*\CEUsK?++/u,a+JT.7`bHumo[F8gV3'P1_8`l9Hs89o($6tm]>oqGC\qS%[[_%Zg;(feG/S?nKfufQ]hcWp0?^ISZ0n:Y2UB(B?$CX7.gMtL_.Y!jBp1@LEphkpR)$YYPqb"Ma_sCMh%.<\4G*#K=E$4Ca?e]eAuQ2\bY;VRfbH&Uuk/8u9&?!>U"@!Qi7pA"*+olORo24_Le&DOL8FE#%p!mdba'E~>endstream -endobj -23 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1519 ->> -stream -GasIg?$Dc-&:N_C9_5'U8*g*TQM0seBp;)dl_q8OIN5!?K@!f=7Nd=7fD5*1?!tJY[OfP91,F8ipYM?%Y306[u5:BT>%XpZpLdqjf%>bE<8B$B_OTLPl@E:b`2-dof(m$GEToqYTo)f_Tk:]BA3DM/_*sBu4rs#)Nn0/K%kR_A)`F*AqR\X1f+UM_/kehcMUXLk4;ts=nFHrqc,(m)2;CCcN>]Ln&nT9.`S36RDT&+L-1HEGX*UfG[dDahCi-(Ia0SmTaV^-A%bH3N&$?`3pd-TD?nqOo0coPDFfM_&!#gRS'fB'N`ZUYkM?Dgr;)D5-/KHMI:.`H7XD-8,dX,,'qjND@[H9Al%SkZD;TD\;,&3hGS9co##CaF)37&X:jVj!%\)(^[nllU0[JqSaIg@*:7V'3rYPg>hk?h`TjkGDZi^SJrO*SfpMp]^Lsp.=+ON7f8.gg6I':j^BeXJ$;[VK9LU7fuoQqQ&e)_r@rO:Rb@aE2jSle/[cP3QF[/mgJMMmP'\CnO1kJ>h;CF'KU]_PT,@=c9LAr@?07Xik-_/i^++PfEKm.*@7pfs56cX_eH,2)67AkL\dcQ,W;XfqlY\8nfG,p)iob.ip5-06qpSRlGi4c_54R-@#73J;Q9P)0R`TCbY+%WZM]NC5e4YeNdr^MM=P;kK+W(c]9bAWt#,Fr9#!;HN7-sUjh6pk+L8u-cKpBB5fl:eK?B*34jQb!0ise!0RrCBDgSYHEjIW<"6kmY-<[eKfXglh`To96`Z,>a'a;;i7r'fYW>;e&13fFjfecNcG7NPSH5>5$`<3@d2iJV_o$/B4WbJKN2I;u9lYf'Zmf`#3kNqNFPtM,]n(kt/Z6(9)"#b/^qU%2RW;9^Vmfdo[P]^Gfl/W([I;+LbHr,0ANdc>06ZmW'*WX\"5I;DC&~>endstream -endobj -24 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1952 ->> -stream -Gb!;$=`<%Qpg(Yo6\0,h'/QEoMX%DHBt@d,dbAPEr5)"3X]U?Oa[t)k^Q5sS[OI)M[1/%iaR;=7JDunqs'Z9(F:>(n))*#L;d_4t@=/BrO0+HYZ+986*Y:h<-a8.)Nqn3s=W)ISnA#MN#jN)^]AO#cKEq_5Gg?&O%pHgAZ,M7gSGL)+,+-CSh`C3;DVL!$,p)6ck4s`q@E8f^Dl?#YlQ62G*NW\LKBft"1=,]&SAN[s=77g#O*mg^E1Z5!r*=%Pc:aPW[L[!eUU-\NMc*"59\lWm?`#;'+2R^j`fCS3!IWM,M21/o_nT.S0G!YMPkhZjqc_;)9EHImX%eW[8#6f!*-lCpQguXW$[R]Uk\!C.):FLA)@*KJ3/se8agVXc0h%NuJ)@i-Bb^euE!o+T:d(sm-]r**J>mQS.G+;YChm35-m/YND95)_EM?G:IOj2jFNc:.VpC@i<6!+%<=$lji[sXF46!UrUX^0W,g(H/BC0ErLWtZ7nY"P$;Zk7O#WtO;AHK51W?MG)IYjJYChqV5d[*1a"TnHGH"H)["fMrnjamqD9*06mVQGCMA]ESXIrn,CGG\^'fJNiCY#2;Cf_gaBN"_QD5oS=&uq`bM8n3\:iGm"kOK\]&\]Q=$_V1*00MUfMeM$t/(>X?q0]O"sh9""o1>R,/pg4g$lcVQnPu-HTd4k%Q<(mkE5j2P8dP/RM&Z.)?TYTWeL/dLp*ka'dn&>iHl5R^DY^mJe_mgl]jrA-Sk[:ihbb103'76jbr4<#3KrUm.U<(l*9gSKB>4]N=qZ2f-<)//u'Pg.L(E9ANkV-e,!lE(.-\6EB#*';'9jX[':0!=\\`nl2qd0S[9)`p6qeIlARtL#=meCSBC"m8`!W:H,_urG<3-bZ&[-8uXj[5Q/?H1[`e!^O/5iF]@GAMUHa'=oFr"d=!6-g3fO3:L#GcX@^4aEorL!JU$HZoUnt\SBDB;Tsj4?!]%3E1!^^$>J*umbu4q/"SnTd!o!m\C\_2dNV<*8+EZ.d[JGIF5"oGfcjIPG0u0T#U>=(galmnE%endstream -endobj -25 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1559 ->> -stream -Gau`S9lo&I&A@[:m,ZO'R0k9+O9F=%e&H8dC#do^Ep3`O+UJbfW$GrnrUjIc9F'sa#&NK_>K9_J]*Zm$P#SO0G;cso`9md6Y]B5-qYPan,*jE]nAeEGN`Z97PM3Bh51D+J.nQ0EuVB5*<&-1"UF#jUU^6cVFGOXd9R6l9u1:Nr$F5$/(fLH79D+B.@f-l)-L^$]\!9,Of$-Eo-kIDJZ7PZoo'TJB*_Yg&fR$f:Oi_A6*UIkXIqj(6-YliL'kC@93-]c\3oi"+"Du@HR[I>0PJKT11nO2XaAk73c#3%g7i40sT2@BOPa2s4_n]O!H<[Cf+I-Oo;\N=@GSP#^tCe#N[I$K_5h"@^ppX`=nkhO!JG3_;E`WHNA`:S?Jle-rubctl;Jj@V6P%9lo'($ptHUh">O=nd]a;Jdj2e^(rj!u3JI0'T$\,4'.>saa),5nF1K>FmhY>hL?aVOci*WX7eAU<2p;H,!U63fL/QW=>1d6n@*K+Nl)5[*RKmU1kYfmdg6dJMo4a4hap)m5fD'3n0I@Z"qQG+Qi,fN."PgHhh1`OY5c!QU4dKG3nkFgcVgo3TjXNGCj-4uV)29n>us%)98+@Pp`;.XL#kVI2Gn-[bTWk7e>:-EQ%:i_D=%Z?Ap?*cY&h_0k'SBiMU^1C&UYX_.#:;"J#W#XmhR=Xf648'CF6SuDJ*5f0SXb'+"ir5F,Nt`V.1knYB`!3'jb6X=b#:@Z@)$nf=%cG,B)>!lfbo'R+^j*OWo>M(C"&T7K~>endstream -endobj -26 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1871 ->> -stream -Gb"/'8TWWE'Ya0?]L`WR1(f+l$^g0h`b@>,4fRHkm_ousKI#7s_31&3o[)LP#O[a$=`RgB;TU/HO21Kj]Yt:-ITcUgV>tP3nFA^E`aLn1AeQ,\VsnS=rGE58EmJoZ#YB`rJb[*0TU8q$3%f3!mPBsS,LWp7QAeB!9Am>+o9]c:K%Y4'!V'F[8K3IrN[rpiqZUt"8(KS<>h8(1'%<%VEbWthH+4p;uo[naQCO-"B6AP\dpPHlY/K8$E*f0Mp6Al=O,l#^ofl$6.(hk$p$`f::o5.b4o"BmN3d=pSg[H&jN)@G)W)q?2SP"Rk%!2Li*WE,U$g[$V53lmk)bAmVb$8Z(m+U`l'5/c!ljk$UC-.ia&'Al/)qgLT^e0.FNS\jGF*(LT1i&>_(P30?SN.u;3qNc,tMsi_ajU\)bfL+,JdquT!AC,=,$TRuC`d2#tpB%L]p_mm:K)eD[lPSNQiI`Oln"J\<4Sb=:-U'*_1Q3ei5n`''pi:h0Mr!Q5H,FX_khp?f0&L3q%n^CEl>.6^+k':N-`qeY.?C,t^6K$UV(fFUA@JX^)H8FL!diue8'hLaDa0ea5`hYT11[S#G;H^^95MXc)?WI6`*atC?^lkM)Zsoi#]E=3C>#31FC@YX,D1njXJ,%R7!1F2YVgZI";Uls$$3tqdB/*mXrhd@^ppm0n9Z-L6@o(^-1S!Xo7D/ge`?b$RThj`4KaqL4i:Y0J*;'pkPLn2@a.>%F]sKD$bfCV0pp8q3?0KsMg!KDKD+hud2ObhuHnLJ_e-&8od^I&MfDnSY>h'`K?i":YHN^HRk\kK0mW%!4DBfH$@]qep@XW]Z5"1g46QXN>Pp(Oi4=`JjuGM7HN$9mkqF-=FUD\9N>[~>endstream -endobj -27 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1724 ->> -stream -Gatm;>Ar7S'RnZ;35@5`D.Ac4$n-R-o0:n.HoH]$QOF^dfb0Yp!bSXp`Q\5?&KYsA![,4OI`-e*^AU=-no>"&&k4umTVs,.$4r(,V'6'_l(hi&8e&ta*WmTR.[:".=:hM%JDMXskK$hg+:P%BoK2l5'e^ci:-')qrBi8$_0/F2<*p3Z!ZAdi"-YZ.rGi@NMNt?-F\u]=_3?u`)u.LbKP/ocB2t3t7PE\HIAaD\]@"07"LB&74gr!-pd/p4TQpA/YB:""!*h!P/6gc9nLDdnL^#j-d&%g[aEN5.Weg+BM7trbE6h%l-sqWOKT4"SdQY7Ubi=j_.b%jeX5,3jQjie0-g8i$T%h-fVAQX9DC`SF;#h*\BC-Qa5dulKJDG;/0mq1@Qdc%5a!L7h01CU[S<,gAVoD!n`*7R_9\F@MqTG3`G+.=uM;![(^h&H;Xr-na+g/!Me[S9_r'FM$k54HE5A]g6_@Q0R7'HsWFq=V.e@_gEn16;.O]V&MH7TQCLq5RMW<_hJIX%WM/:*M`[*;Z&d,]o+OTg-&4>1`=k&jO&E1Ir].V$[%j!.+G3k9r5)98_-\2M;1hdAK1Z%X,+8[6VFISB?T'?Ac@A8,r>$-_]l>ED"qNsh"E"6%S[o0=,T^;bG^:'gW"Pdei;pcQjY%kr0C1>3^W'YT&da/s222*$=iMX]2$)c'#X(+cq]GsYA$fPe=#((r*q*:Bcn9]G`Q0Ch]QRGU9?]aud2"6dOG,hk[]!k3eYr=>Ae;Q7KJg4ReEQd$f+lTff"GP&R,rSu:!nf_a16'!e=UJ]R5%$g-K6l7;$+tb361BbY2K%KSE><[O>G)`(-'%YHAirB7*nIeO<7DY";p'_r6-T-Os!XSPFH#fNgW2=fuOTZYV:%imtF^@ei:JfC'gsje(2JE%$&:otA=QD`O.rRq3`JtSQYicEKKt=Q*N8*oMfdK\Hk38ER_.)'\,`RglD=d/WZ3?b!g=4lTGQ%jb%1!>oTjRkm0j@*]_S#$V>Mq\h\u\eFd-*SPef?;t=Dq!iUQL%pA'lC+8/Mp^PiD^CM*M\("M:RbjZ.IPE-`Qn"=3W9\qcEdkg.g'kK/.E8=st2B*jO?Q57o]13&VrFLj#]Irmm!MWArKApQ#IM6[f[#uoY?8,Tu'O4-$NGALIPH0@AK0=91)"p0*p4f=Z#I4V[gO`t(\Z6WGKAgS"C%g#8qm6e'5u9rLIud8ikcFd-82",)m!=IZMOM'2Q)H[i@g4emCk`tME/TkR,c$60QhMON)a$E>jng+)"XT3_/D4AE..W_5b*%rAos(-gqIOi8]T=W4E`P;e!ic`Y88^+Y(05,D"*a#Cdr;.j)UW'N)+\ns"`Z4!q\&#B!d,[5(9EUrRN]&'lQHCB*L[`endstream -endobj -28 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1906 ->> -stream -Gb"/'968iG&AIa;CbHeVWF,]/8^O$mN:V#K2GFid8M@eS,UCb]$&+'!8Uc((+@X^3g!2Q@e^Bfr*oF[qliL]_n\a/^ZikV6L&E5#N5K`I9Fq6RGOT50IIRRRGpEG6^8-5%J],;$K6lKV&;25iJq&%uRKkf&4ecgFV[7Zbi:-"Bnn9B!+1jNS1#-ZRf.eL;OQ$ES_1RLA8Z:=W+QNV/^0&s]F%Boj%d25#b@6Z#q]Lm_%&^X$il4ti0SYd!a`:mu!_:\!lcUZ;P?\BVVdc1@BRseZ&nVC!kjJ\-_DO)Cr$Mh1ZfZm(%^(2/V11m6VXr-?@!VG#.CZkqV#3rGB7lhH'qJAk(79DC1+Ue6uXY-h-+a`V_B&&O_-4Ks/Cg@&U%UQ[7?gT\'@YZ@I2InWaO8Q'3*8h.n^VJ;qW=DsWS4t7P'1;K.a7E2FaN$rjbmjH>ZbX,3s@RQEZV:$Xu>/kgU>Rf[e/?K'`k$Ii"XiV2-?h99b:UYjr0[K\Uf)r?=MpC6Riflk%3l#.SilW1Vdof'[[0Rs_C08_Y(mCt^bg#(l4jLTfbe!A-HF;*8'qN9pV(@C3gj#;G5CS)#tERjSis^cIogp4]1W!Rmje>];&WAK?,Fp!Q8Hcl@BQh2A&1@LA2$r,.`#S2L5WrTQPgiB,%rMI;&D`a%#/Cf(e59kT&d"GV'i"9I>-&B'qUN:/f_J!pC&4=8B5_2gs/L)^5r(E@3Q1%e@&/GocZaa*B4^]4/pi)mm3o2LZM,Rj_1"k@!%KAkop?Okb)_#I^!EthFJG)@M_ab'3eoYF2j>p$lL/7fEIHNJE'=3kj2%$!`EOqLqtF+>=Qe0N6#F&]Qgn_D_&g^Pe9')GJ;Ij;m[F,o,3-rh6HQf8N[i1,P:5iRAt.oYWr%Suj`M1!U\QQuk7QTGE0\H#Fe=)MS./EaO^m-D8AD[h`n1WQ;4A)<.PdiDEQSrVZl^n6=gD:3`'YMi/LP4b964&H-MGGZb3KA6%ji'ZH[5KiPlT&jZq?_OuHG3T=V$/HP(eTUO)+NhX#;!-endstream -endobj -29 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2014 ->> -stream -Gb"/(968iG&AII3ChGdB-&j#KI"@&b`_N#AD8pQ9-1/N))2a(rfHd/@P[%SlgUdS;MN[Y:26K]6SVVk5]DLtJ53sa5/Gu4(h^ARd*Pq4c"j14p%FcO2N'>+^UYb_d@35"\(Pp9/u(uptk$oE1=aVhKIM'96doD37CJ7f8nKKV0M0]LL\.sJtJKi[^p.]!u\\^5O%b97Jn^2!/IeQ0T^4atfP,Wh:1:)lqAqJTA2(+TU$-JPP$?$-)iMrO6Y`o;bKeAV4;gEG2ol?,ZNJ=nlbKumr+pDMl2q5.iT>ES3j#\-FPKc^]J:+^#]duCl`c-MW7HMK0T:8!fZ7O(-50Q@#K$I/)j&AK%6bbF67)%:G7a\3JJOHOO1F9nSMfn)&]*:=6fPJZBlXD*N)V1sUY6"G!huCd!^!8Jb:;c92SXUe>'4d-/J=e><5Ag!IW+ecX?J-qo\N&@(BJ'3`V5]8^!k"eM4KJ5;A55bF&ZnUc2uJ#TKZ,sXOZaOfppRa03V!J]^@)XMmM>GV/^G]V9B!59VN$8m1@Nm%n["$`>*ep[3RMhka6NL->[r]RIZ>??dc_H:CRq@"l;;F:6N[r-FpuTi2T"C8r)"09gj(RnfN$!A_-C5/o3[M=K>S6m)/u%Q`:IVa%TsM848p*Gu!X2e3H_/gX1r&(Br%#*$13b0(_X:Io)Hsq[P=3;QIIqhP#bT'H8Z$)8ouJS74:/*088V+lfsgXR]mPs#BB@)RfC,+)66TS0R/4[f/-M%ftK4h(23%Hd+EorPa7/V4/%d5@Y'57_5[LL^jA.!D2*Y]>bLX(]=@MoYhG/RC*1AP^SbM=?c,,';'@-;B_.[-bRL;cGUClVEeU('j,]Xgo91%#ruZoT3='iDU%g8FiD\guI-]kJ;BD\6,5Y/F4R+#n%;Cebn[tBM^IrTG^pPSc8Tc\S_ij'F9gP"=L9ZVn@5sefc,i8._7c<%!Nt?g'38p(N5ICj//7HD%dG(>>A9';(m$..3$->q?'0)dfXeH0SeqbRfSoBM27D%2pu+^7$J'h:IJ@3c;:@FL/pI"aK-SuC_o;$Lbi%HlnEetBD,&-LZ;EO&14Gb2Qrn=7q=Q-(qD5%\i.$=ZXAkqM`];]8#.X70W&U39sp?]ES!$ifeU$P@##bR@C'YZ5H;PBO2tAi@:ZcSd[GqpL>u6L#tIL-d#.7+I#gg1p0h47qNYXTKI='qbf0k!aG8`,Y;@?Xe46.4=7fAh=KH!Qk0X;B!=gggelL:(9N<6/*/-d8`6$q@-8Sr`hmEeg$pnL<9]uU$AWGBRn\&tWNu*2UrI1,fH1cZ#ae5mgi`=*cR0%^7![57XjT~>endstream -endobj -xref -0 30 -0000000000 65535 f -0000000061 00000 n -0000000112 00000 n -0000000219 00000 n -0000000331 00000 n -0000000536 00000 n -0000000641 00000 n -0000000846 00000 n -0000001051 00000 n -0000001256 00000 n -0000001461 00000 n -0000001667 00000 n -0000001873 00000 n -0000002079 00000 n -0000002285 00000 n -0000002491 00000 n -0000002697 00000 n -0000002767 00000 n -0000003059 00000 n -0000003189 00000 n -0000005193 00000 n -0000007291 00000 n -0000009468 00000 n -0000010263 00000 n -0000011874 00000 n -0000013918 00000 n -0000015569 00000 n -0000017532 00000 n -0000019348 00000 n -0000021346 00000 n -trailer -<< -/ID -[] -% ReportLab generated PDF document -- digest (opensource) - -/Info 17 0 R -/Root 16 0 R -/Size 30 ->> -startxref -23452 -%%EOF diff --git a/Les13-Cursor-Vercel-Deploy/Les14-Slides.pptx b/Les13-Cursor-Vercel-Deploy/Les14-Slides.pptx deleted file mode 100644 index ad54b27..0000000 Binary files a/Les13-Cursor-Vercel-Deploy/Les14-Slides.pptx and /dev/null differ diff --git a/Les13-Cursor-Vercel-Deploy/lu41eglzw.tmp b/Les13-Cursor-Vercel-Deploy/lu41eglzw.tmp deleted file mode 100644 index a7c8ac6..0000000 Binary files a/Les13-Cursor-Vercel-Deploy/lu41eglzw.tmp and /dev/null differ diff --git a/Les14-Agents/Les14-Slides.key b/Les14-Agents/Les14-Slides.key index 694a208..21717ce 100755 Binary files a/Les14-Agents/Les14-Slides.key and b/Les14-Agents/Les14-Slides.key differ diff --git a/Les15-RAG-Embeddings/Les15-Slides.key b/Les15-RAG-Embeddings/Les15-Slides.key new file mode 100755 index 0000000..de7d6c8 Binary files /dev/null and b/Les15-RAG-Embeddings/Les15-Slides.key differ diff --git a/Les16-MCP/Les16-Slides.key b/Les16-MCP/Les16-Slides.key new file mode 100755 index 0000000..4982086 Binary files /dev/null and b/Les16-MCP/Les16-Slides.key differ diff --git a/Les17-Production-Polish/Les17-Slides.key b/Les17-Production-Polish/Les17-Slides.key new file mode 100755 index 0000000..39750d6 Binary files /dev/null and b/Les17-Production-Polish/Les17-Slides.key differ diff --git a/Les18-AI-Toolbox-Next/Les18-Slides.key b/Les18-AI-Toolbox-Next/Les18-Slides.key new file mode 100755 index 0000000..77e360e Binary files /dev/null and b/Les18-AI-Toolbox-Next/Les18-Slides.key differ diff --git a/OPLEVERING.md b/OPLEVERING.md new file mode 100644 index 0000000..84cd0dc --- /dev/null +++ b/OPLEVERING.md @@ -0,0 +1,109 @@ +# Oplevering β€” AI Developer Leerlijn + +**Opdrachtnemer:** Tim Rijkse +**Opdrachtgever:** NOVI Hogeschool Utrecht +**Vak:** AI-Assisted Development +**Oplevering:** Juni 2026 +**Contract:** Opdrachtovereenkomst Tim Rijkse β€” Art. 1 taakomschrijving + +--- + +## Doel van dit document + +Dit document mapt de **vier contractdeliverables uit Artikel 1** van de Opdrachtovereenkomst expliciet naar concrete bestanden en mappen in dit repository, zodat NOVI snel kan verifiΓ«ren dat de oplevering compleet is voor facturatie. + +--- + +## 1. Leerlijnbeschrijving incl. cursussen + +**Geleverd in:** +- [`readme.md`](./readme.md) β€” de formele leerlijnbeschrijving met opbouw in vier delen en lessenoverzicht. +- [`Curriculum-Overzicht-Compleet.pdf`](./Curriculum-Overzicht-Compleet.pdf) β€” alle 18 lessen samengevoegd in één visueel deck in NOVI-huisstijl. +- [`Curriculum-Lesboek.pdf`](./Curriculum-Lesboek.pdf) β€” tekstuele versie van het curriculum, leesbaar als kort lesboek. +- [`Samenvattingen/`](./Samenvattingen) β€” per-les samenvattingen (markdown) voor publicatie op Brightspace / interne docs. + +**Korte beschrijving:** +18 lessen Γ— 3 uur fysiek, opgebouwd in **vier delen**: +- **Deel 1 β€” AI Foundations** (Les 1–3) +- **Deel 2 β€” Technical Foundations** (Les 4–10) +- **Deel 3 β€” AI Development** (Les 11–16) +- **Deel 4 β€” Production & Advanced** (Les 17–18) + +--- + +## 2. Content ontwikkeling van lesmateriaal en opdrachten + +**Geleverd in:** elke `LesXX-*/` map onder de repo-root. + +Per les bevat de map het volledige lesmateriaal en de bijbehorende opdrachten: + +| Bestand | Inhoud | +|---------|--------| +| `LesXX-Lesstof.md` | Theorie / leesstof voor de student | +| `LesXX-Lesstof.pdf` | Idem, NOVI-huisstijl, klaar voor uitdeling | +| `LesXX-Lesopdracht.md` / `.pdf` | In-class opdracht | +| `LesXX-Huiswerk.md` / `.pdf` | Take-home opdracht | + +Overzicht van alle lesmaterialen: + +| Les | Deel | Onderwerp | Map | +|-----|------|-----------|-----| +| 1 | 1 | Introductie AI & LLMs | `Les01-Introductie-AI/` | +| 2 | 1 | AI Code Assistants & OpenCode | `Les02-OpenCode/` | +| 3 | 1 | Cursor Setup & Basics | `Les03-Cursor-Basics/` | +| 4 | 2 | TypeScript Fundamentals | `Les04-TypeScript-Fundamentals/` | +| 5 | 2 | Next.js Basics β€” QuickPoll part 1 | `Les05-NextJS-Basics/` | +| 6 | 2 | Next.js QuickPoll part 2 | `Les06-NextJS-QuickPoll-Part2/` | +| 7 | 2 | Supabase introductie | `Les07-Supabase/` | +| 8 | 2 | Van In-Memory naar Supabase | `Les08-Van-InMemory-naar-Supabase/` | +| 9 | 2 | Supabase Auth β€” eerste login flow | `Les09-Supabase-Auth/` | +| 10 | 2 | Supabase Auth & RLS verdieping | `Les10-Supabase-Auth/` | +| 11 | 3 | Vercel AI SDK β€” praat met je data | `Les11-AI-SDK/` | +| 12 | 3 | Tool Calling | `Les12-Tool-Calling/` | +| 13 | 3 | Externe APIs + Cursor + Vercel | `Les13-Cursor-Vercel-Deploy/` | +| 14 | 3 | Agents | `Les14-Agents/` | +| 15 | 3 | RAG + Embeddings | `Les15-RAG-Embeddings/` | +| 16 | 3 | Model Context Protocol | `Les16-MCP/` | +| 17 | 4 | AI Production Polish | `Les17-Production-Polish/` | +| 18 | 4 | Advanced AI Toolbox + wrap-up | `Les18-AI-Toolbox-Next/` | + +--- + +## 3. Lesplannen incl. lesopdrachten en sheets + +**Geleverd in:** elke `LesXX-*/` map. + +Per les het complete lesplan-pakket: + +| Bestand | Inhoud | +|---------|--------| +| `LesXX-Slide-Overzicht.md` | Inhoudelijk overzicht van alle slides + timing | +| `LesXX-Docenttekst.md` | Volledig voorleesbaar lesscript voor de docent (3 uur) | +| `LesXX-Slides.pptx` | Slidedeck in NOVI-huisstijl | +| `LesXX-Slides.pdf` | PDF-export van de slides | +| `LesXX-Lesopdracht.md` / `.pdf` | In-class opdracht | +| `LesXX-Huiswerk.md` / `.pdf` | Take-home opdracht | + +Aanvullende werkbestanden waar relevant (per les): +- Starter-repositories (`poll-app-starter.zip`, `polderfest-demo/`, `lesbestanden/`) +- Live-Coding-Guides (Les 9) +- Hands-on bijlagen (Les 10) +- Tools-demo's en seed-scripts + +--- + +## 4. Eindopdracht + +**Geleverd in:** +- [`Eindopdracht-AI-Developer.md`](./Eindopdracht-AI-Developer.md) β€” volledige eindopdracht-eisen, beoordelingscriteria en deadline-structuur. + +**Korte beschrijving:** +De student bouwt een eigen AI-app die de volledige stack uit de leerlijn integreert: Next.js + Supabase met auth & RLS, Vercel AI SDK met Tool Calling, externe API, deployed op Vercel. Eindopdracht doen studenten thuis (niet in-class behandeld). + +--- + +## Status + +**Compleet en gereed voor facturatie.** Alle vier deliverables uit Artikel 1 zijn opgeleverd. Voor controle: alle 18 `LesXX-*` mappen bevatten elk minimaal `Slide-Overzicht.md`, `Docenttekst.md`, `Lesstof.md`, `Huiswerk.md/.pdf`, `Slides.pptx`. + +**Vragen?** Tim Rijkse β€” trijkse@gmail.com. diff --git a/OPLEVERING.pdf b/OPLEVERING.pdf new file mode 100644 index 0000000..0703a6e --- /dev/null +++ b/OPLEVERING.pdf @@ -0,0 +1,137 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 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 +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +9 0 obj +<< +/PageMode /UseNone /Pages 11 0 R /Type /Catalog +>> +endobj +10 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607122247+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607122247+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Oplevering) /Trapped /False +>> +endobj +11 0 obj +<< +/Count 4 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R ] /Type /Pages +>> +endobj +12 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1955 +>> +stream +Gau`Ta`?-*&A@*SqNR.Ken>gMKA9ufFr\-RfOfNZ"Th.=6nC/1.JDAk#Km)i6&Lh'MFC/A@&CQnS(A%hJDq)TIYTAflNNflA\^8VY(>9EL;c[XD..k%#^Haln].`jWLGQE^A=M3,n7Rh786%m[U)Z@$7E*EoebLXsG_(s:;?m?V_Oi6+4bIbi#_2.89!m*[JYiXlYe?V68V3OB!(I$g-ePAYV]2J.Wg4dm/(Q8Ye*J`mm_`a=lO831b1$5sGN&,N&Zpp)iX)hn@d(pV@S:q`t0G=<>Xmc8Q]-o^UA`:04-7'IaSYJ)2UCrE"r--f(5a0'KA*htq<)Q3rfX]nA1=")aNSRhVm,ecuXO4l;PHen5%WN>LC]J&\mP#`HUX(Cfdn9Z,/c'F/G+c'aQQ@CE@S/FPDuh#78?(cjWHYB=cbq'$4:91PFVZ20gtf78H-;7?4U513QWTri;K#Zf26(#fgYoo[_jbjE/X)9JmJ:Z;__MR+k7-.Z0F#9f;),%Y[9s487O62ik!6fj$n2A9Zeii07Z;QZ)]cB3(aY2$h]ngd'N5U&XdJ/'EVcQ5Vn7;%<&kjs.U2(M=cHQt-_I7C6PTb#H+5?`S(hXEfV8p\;W?XDZWA/*c>rl8K6-,7S6_=#c*55moj6]/J_M<".'=]8_tmaVRLk-:%Un?%]CAEfCedbB,@A$;iRP)QDIYQrZq)JtOr[dRWeam+?\mMB3;*)_3HLN+H'2'psI>$s26KJL"bU!>iiqc=7A#,JuTQ(R:-YBFK+qU*FB.D'Y$_!`usrD3T)1,;nT(ZLeG8&o)c',?;'r?^5-/^3B&=%q8ejpm*S"MgO>HM#njQc7:k@5DOWo@,-PA=%kS9A"?%Q@n`g!)TD\CT8u:ih94_*-'WG>i[B8"R*IX5;8cn6eEQ4q*oQ:P/j[T+\t5bohH`;C6%[$b9J(r]-E=sE.0D,MfG/J8eB.tH*)-Caa&(=oMb9e^[=4jnbiu-fbHd"DP!f0%o(i%sp>ZiL,4D9VYB%$7L?Ht4T%lr?bGs.A`\T#Qe^XO4$9C#u?B%@3#'?[POqQ>k;<]lN/0:IJ4g9`+KhhV,2SWuAh$-l5frVKc=W42RHM[,:nl5A3Eq0o'Ud;8GniOOhBht5p$5cHW0[*,7'fk(oJPkl4+&lmE80Rg4iis.QK(3>#(!HH^iV)c8os:Pr%F;Ziu1W?#f]"5ba+uXBTb:pINrLKtY9'PRLJ73endstream +endobj +13 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1977 +>> +stream +GatU4>C2i!&:O;VR&>Di[N\7u>Mml)QZSG2XhF]0YV3pC2DGCOE740Xh[pK/V\$Dlh#,hc1NSSP\KD5;IadHJEXVA[!@R=b:^2h^T\SP2\:f'aaZgGsH-GG(=AE6c+Tj6:j;4&]8Hu>1Gp*PE"L`4P4pN-*.gWul(u"dLoq<(=qf5$>nib_<3*63(]tjNahQ]2_0Jp7DcO1iLCOUi1(L!DBLG[Nl3^X&#U,oIf[qt@+/hFFbrI"S2!^>`s;(^CFT#+iW.@09pTS4'9.[!VW%(f0tCEJ*?O98C=M4ZBau:McNCb!Sd59"W+Fdi\A^%\![u@2TNddHB%Y9f\kC;kfJ0VVe.5WZV=A1_-*A[,-(`O*g4c;Uls,RPX-e!D,_at-qZd9/[&kp`+=Vmqi;YqK4M[2hBRB_LeC(AU!_5m,qY&>T5k+a&#*4#(m=5['<4'*%;lLsh$DB"3pii3UAFk[]KGun]o<@5(lK#9J`icK8X\bLFbBb`E4%cR"(M+W9%J*=nSf6%7bgMAN%A\E71K!:RYj(-M8tN*5rZpANEGA?5ebSup7"pbh4\t^i4,PE8NUPY3CiR$[-8pP2(%sM+BBo>d"06@g;mnr^H?aq?cJ`1gZ3X,Nat#YI$B@,rib"Ql!%$8;&0[T/*!39EZTDfMensQp;(p)CdV)+a!1eqEM:Z(riPJ`f%QQ:CTGEXjCKOn"Aak0`Ij@58%>RC0dc:l*tMV++D_YI)WGM"O+]`@b?j6VE"gIBCPT3`,ah19q-!L]!GIZd\FsB+!-)bZDqm,M!VUCPQ">HFT+39Dm6+,4?J1,ma*a1e7C+4_4]3Yccd?XALrSulHSO6au9dS4%R'Jll]&:B$\A#al%HS:E;D9mRd\u_gUfhOoKW,ij_^oH4:Z?aukUZ16=ts$]u?B=pr$06P!58@MnQY^;[)?FSMo8G[@:h6<1=j6%]3iGCZo7!HjgN]4Ht]&4c+(E-Z^dZ;-sXeeV?:VI\!T3P@Bim9-%G"3`#Uj,=$r%S<;SfWn;R.Zn42?(m)?6=fXcP7Xp5Cc3N?dC9=^*\A@q"H>30d($Df=^g'WMgAGIq6s4d/<,"LD'a=[-kA^KNq6munDP_n)E4s%4b&Gqr%,d6=XHQHmrqnSD/jZT;PO^iB\[u:6oRshn9MFB3$oZY:N2r5%`XnmPuN:q6'XAAG_oapjJNNf[eP/.518m,N-#`H@Yi\uXF49nXpq)TU`_Q"4j'jpreUG$b6d:'m$m8**QSjVG65H/B$=_FV2'8KACKobYAYcc2lpWYM0E?M73#.f5#qP:SHb''ESmp-%;H]Gdd+SiI\#_L0J;FfdO~>endstream +endobj +14 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2027 +>> +stream +GauHL>?fiO&:N_CbYr:TZtNP8]K%,O)B).cq=hrhk@tNrck.7,lK(L%<4-1ZA^H(8g**[Vl':?rpgU27t%;#u6Y"`N][^WAG`TO8j4D%[B;4-I#N0+7HP,bW&-I]Ne=>6+0n*BZ&-%(_#R#S"`*5^TYMW8C\6QmH5?but!0oQc>:LW_mK/^q=_l.9PPbIDZ)SJm4;G2XU;VJ[DP9?#r1i.Oa&9f=>FMV3T2[TCL)!_=i)HTY#25KN<&eX84OTI^5="fS8<:DcNZb[2A6<.^M9nN,l)$XBiVa)-o&G/RXE=?uMSo&CBYm..s=mM@1pA2X)(f#`U*o;1oMad5YYJ@MU>?.d8/*1\tW\`&uea54*n=H6]X'n##[$$_QaZB;56u'Bjk]in!r?`7r./_0q#B%ksLs<)8M(;[WHC&+.X;Nq?R'm"f?N-B*'u/Wm6=QT=\U0NC*K'QY,3F(1XZeH:N$QH7_fo=(86e;Rn+qJdm^b)G)1.>Ok59nmqNh@8-:\^D,e6-Gku!lL8U+hR,(HGJsP1Foq,@XEl5/gLArnZ8N>UM7C1?cIb(eDrtsHQ*/QC=cGS56JK0Cjn'k9"bW6_r+r-nOagX&OD-p6Dj*]`.rUlVZtdkP_.;b7PGYK(o'46=a!I]Q0MXKrqp.*b;`6a`,$]1E@8&3)C0Uu(jlJ$0Fl!l!K0B-4REKtjkK98/4oE1"6!3oKn>BG):8\fWC!(,Jh5)tLQ7+&KB9I"o^<=cX0cas5&0kW`+b(A^I#ZX)-u:1Z`>t"'&IR;8,01p>XmF,PZ\2@"'lD+`V,^mTO'iEg0(C;O>GC`nJp;R4g.:(?>B+'p$:H]Bc7c9FiWh7J^hq[JS10j?Ui3BRS6;AZNnRQYF67>aq)4RRNiA=R&naKOVZDq(Yd`GN'B0-h-Ae%Ro'C"NCj5'KSN]eP1lU;?+_X'bj.1Y5X$K6RT@o8"#/tDqi.Bq%*nG+6c`59k@#*]i5De3B_hk/RP-MF1He/FGa[JP;ObfbgVgT>L*)I)3(h7PS/%h8?**"rTmC0LD5LeDqH@<,E72#?"R&O4'q]8>Me/`]4KB]?=,cd;i#>c>!H@o$tS5SaQs2:Ip%Jj(TDo/1Im\eaJ)ZX.1MClGoZEnX'EYV)2$86#]Z,!C&h,JCQ'0]VIi4&`i43NM\.!bA+T4EK\aglclN/(4Z%Nk2k>d,"FCUR+"MP(\nboKC;mNB5iXj(dmu%iV)8(n\=A#_g<<2.Gcj'$^aBhrL:tdns>%"^Za.b<%6C#DB(f6`JQ%c]]`_5RTA[cUO_\9Wu5i/;q9ko3p7g^=mkRGe=d)Ls+*%@%*05T]En#NQds"N@Zr,E#a,MB=o%.tEA+:D;jdm3G3Yj+et-H'C=rllFf'2T(3=WprA5$sFESXQWWJ!?8]=%*mZcCE*9ClSijp1g*K!b_Gh(17VBMX87l2m]`QOE`#./RXF",!I,98l'hFXVV2/C@^oIo6N"@X0K@*22&G3'MeW0I'&8D~>endstream +endobj +15 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 389 +>> +stream +Gar?-b>,r/&4Q?hMRtEF9PqhRXEY"#.NqEHl&&1L\S4YW`NAoaS2?lSm#`U;F3iVZF0N1ME#&n%k:iBHDumEKQR(O]nDKJUKS1+1.Ssk_RL)fVp'[Qf=t!*+',cR:L9-N!QBS_EKnqd"\3]W:9G7mkRMdVAQ7HhF5CH.-LfdiDdQ79++8smL?:K6_(N#`f_I<]kS,/tYOr.7i_)7TB0@pRoG,7D;gTIUIFpu+;*q:k9__["EJE?_LQRD]r9`+0nZd^RgqNT&KDPR1UFi3h9e*;mq&0asDH7nK2^N3`gA~>endstream +endobj +xref +0 16 +0000000000 65535 f +0000000061 00000 n +0000000112 00000 n +0000000219 00000 n +0000000331 00000 n +0000000436 00000 n +0000000641 00000 n +0000000846 00000 n +0000001051 00000 n +0000001256 00000 n +0000001325 00000 n +0000001613 00000 n +0000001691 00000 n +0000003738 00000 n +0000005807 00000 n +0000007926 00000 n +trailer +<< +/ID +[<4a10b06cbddd56f6c8c1f1c2d7f6aac1><4a10b06cbddd56f6c8c1f1c2d7f6aac1>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 10 0 R +/Root 9 0 R +/Size 16 +>> +startxref +8406 +%%EOF diff --git a/Samenvattingen/Les01-Samenvatting.md b/Samenvattingen/Les01-Samenvatting.md index 7ad19ae..d07ba24 100644 --- a/Samenvattingen/Les01-Samenvatting.md +++ b/Samenvattingen/Les01-Samenvatting.md @@ -11,8 +11,8 @@ --- -## Hoofdstuk -**Hoofdstuk 1: Fundamentals** (Les 1-3) +## Deel +**Deel 1 β€” AI Foundations** (Les 1-3) ## Beschrijving Kennismaking met AI, LLMs, ChatGPT en de basis van prompt engineering. Praktische workflow van schets op papier naar werkende website met v0.dev. diff --git a/Samenvattingen/Les02-Samenvatting.md b/Samenvattingen/Les02-Samenvatting.md index d197d6a..eba2e82 100644 --- a/Samenvattingen/Les02-Samenvatting.md +++ b/Samenvattingen/Les02-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 1: AI Foundations** (Les 1-4) +## Deel +**Deel 1 β€” AI Foundations** (Les 1-3) ## Beschrijving Introductie tot OpenCode: een open-source AI coding assistant met 60.000+ GitHub stars. Na een korte demo van de terminal versie werken studenten met de **Desktop App** (met ingebouwde terminal). Met OpenCode Zen (gratis, geen API key nodig) leren ze de volledige development workflow: lokaal draaien, uitbreiden met AI, en deployen via git push. diff --git a/Samenvattingen/Les03-Samenvatting.md b/Samenvattingen/Les03-Samenvatting.md index 9893019..3fa4660 100644 --- a/Samenvattingen/Les03-Samenvatting.md +++ b/Samenvattingen/Les03-Samenvatting.md @@ -15,8 +15,8 @@ --- -## Hoofdstuk -**Deel 1: AI Foundations** (Les 1-4) +## Deel +**Deel 1 β€” AI Foundations** (Les 1-3) ## Context Oorspronkelijk zou les 3 gaan over AI Ethics, Privacy & Security. Tim heeft besloten om in plaats daarvan een praktische les over Cursor te geven. Dit sluit beter aan bij wat studenten nodig hebben en was een goede keuze β€” studenten waren enthousiast. diff --git a/Samenvattingen/Les04-Samenvatting.md b/Samenvattingen/Les04-Samenvatting.md index e1b18bc..a598689 100644 --- a/Samenvattingen/Les04-Samenvatting.md +++ b/Samenvattingen/Les04-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-9) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving Introductie tot TypeScript: waarom het waardevol is, hoe je types schrijft, en de basis die nodig is om straks React/Next.js te typen. Deel 1 van 2 TypeScript-lessen. diff --git a/Samenvattingen/Les05-Samenvatting.md b/Samenvattingen/Les05-Samenvatting.md index 7638462..49da6df 100644 --- a/Samenvattingen/Les05-Samenvatting.md +++ b/Samenvattingen/Les05-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-8) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving Introductie Next.js voor React developers. App Router, routing, server/client components, data fetching. Hands-on: QuickPoll app Part 1 (stap 0-3) klassikaal bouwen. diff --git a/Samenvattingen/Les06-Samenvatting.md b/Samenvattingen/Les06-Samenvatting.md index a596d0e..308e564 100644 --- a/Samenvattingen/Les06-Samenvatting.md +++ b/Samenvattingen/Les06-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-8) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving Vervolg Next.js: API Routes, Middleware, Deployment. Hands-on: QuickPoll app Part 2 (stap 4-7) klassikaal afbouwen en deployen. diff --git a/Samenvattingen/Les07-Samenvatting.md b/Samenvattingen/Les07-Samenvatting.md index a68f5ba..f3f9025 100644 --- a/Samenvattingen/Les07-Samenvatting.md +++ b/Samenvattingen/Les07-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-9) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving Eerste kennismaking met Supabase. Studenten maken hun QuickPoll uit Les 6 af, leren wat een relationele database is via de Supabase GUI (geen SQL nodig), en koppelen voor het eerst een echte database aan hun Next.js app. diff --git a/Samenvattingen/Les08-Samenvatting.md b/Samenvattingen/Les08-Samenvatting.md index bb193ae..2688a72 100644 --- a/Samenvattingen/Les08-Samenvatting.md +++ b/Samenvattingen/Les08-Samenvatting.md @@ -2,8 +2,8 @@ --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-9) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving Vervolg op Les 7. Studenten kregen vorige les niet alles af, dus loopt de docent de Supabase-koppeling rustig opnieuw door β€” nu in een **self-paced PDF** met copy-paste blokken en TODO-blokken. Daarna bouwen studenten zelfstandig de **/create pagina** (hun eerste INSERT). diff --git a/Samenvattingen/Les09-Samenvatting.md b/Samenvattingen/Les09-Samenvatting.md index 62a6d73..c5131e7 100644 --- a/Samenvattingen/Les09-Samenvatting.md +++ b/Samenvattingen/Les09-Samenvatting.md @@ -1,96 +1,77 @@ -# Les 9: Cursor Deep Dive (Student Plan) +# Les 9: Supabase Auth β€” Eerste Login Flow + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les09-Supabase-Auth/Les09-Slide-Overzicht.md) +- [Docenttekst](../Les09-Supabase-Auth/Les09-Docenttekst.md) +- [Lesstof](../Les09-Supabase-Auth/Les09-Lesstof.md) +- [Lesopdracht](../Les09-Supabase-Auth/Les09-Lesopdracht.md) +- [Huiswerk](../Les09-Supabase-Auth/Les09-Huiswerk.md) +- [Live Coding Guide](../Les09-Supabase-Auth/Les09-Live-Coding-Guide.md) --- -## Hoofdstuk -**Deel 2: Technical Foundations** (Les 4-9) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving -Studenten hebben Cursor met het Student Plan. Tijd voor een deep dive in de 5 professionele workflows: **Rules & Skills, Plan Mode, Agent Mode, Debug Mode, en Code Review & Testing**. We bouwen een nieuwe app from scratch: **LinkVault** (bookmark manager, in-memory, Next.js 16 + TypeScript + Tailwind). +Eerste kennismaking met Supabase Auth in de QuickPoll-app: `signUp`, `signIn`, `signOut`, een werkende Navbar met sessie-status en een middleware die routes beschermt. We sluiten af met een eerste blik op Row Level Security (RLS). --- -## Lesopbouw (3 uur) +## Te Behandelen -| Tijd | Onderwerp | Vorm | -|------|-----------|------| -| 09:00–09:10 | Welkom + terugblik | Klassikaal | -| 09:10–09:35 | Theorie: Agent Harness, Rules, Plan/Debug/Review Mode | Klassikaal | -| 09:35–09:55 | Setup: nieuw project + 3 `.cursor/rules` bestanden | Samen | -| 09:55–10:15 | Plan Mode: plan LinkVault β†’ review β†’ Build | Klassikaal | -| 10:15–10:30 | Pauze | β€” | -| 10:30–11:05 | Agent Mode: features bouwen | Klassikaal | -| 11:05–11:25 | Debug Mode: deterministische bug vinden en fixen | Klassikaal | -| 11:25–11:45 | Code Review & Testing + semantic commits + git push | Klassikaal | -| 11:45–11:55 | (Optioneel) PR review met Cursor | Klassikaal | -| 11:55–12:00 | Afsluiting + huiswerk | Klassikaal | +### Theorie +- Waarom je auth nooit zelf moet bouwen +- Supabase Auth in vogelvlucht (e-mail, magic link, OAuth) +- Sessie-cookies en `@supabase/ssr` in Next.js 16 +- Middleware-patroon voor beschermde routes +- Eerste introductie van RLS: "wie mag wat zien?" ---- - -## De 5 Kernvaardigheden - -### 1. Rules & Skills -- `.cursor/rules/` directory met `.mdc` bestanden (YAML frontmatter) -- 3 bestanden: `project.mdc` (alwaysApply), `components.mdc` (globs: tsx), `styles.mdc` (globs: css/tsx) -- Houd rules kort, specifiek, en wijs naar voorbeelden - -### 2. Plan Mode -- `Shift+Tab` β†’ agent researcht, stelt vragen, maakt interactief plan -- Plan bewerken in markdown β†’ "Build" klikken -- Start elke feature met een plan - -### 3. Agent Mode -- Agent = uitvoeren (files, terminal, multi-file). Ask = alleen vragen. -- Goede context geven: `@file`, `@folder`, `@codebase`, `@docs` -- Itereren met screenshots en feedback - -### 4. Debug Mode -- Voor complexe bugs: hypotheses β†’ logging β†’ reproduceren β†’ analyseren β†’ fix -- Tim introduceert bug handmatig (deterministic) β†’ studenten debuggen - -### 5. Code Review & Testing -- "Find Issues" voor self-review -- Tests schrijven met Agent Mode -- Semantic commits: achteraf changes opdelen in logische groepen -- (Optioneel) PR review met Cursor +### Live coding +- `signUp` + `signIn` server actions +- Navbar component met `user`-prop +- `signOut` server action + redirect +- `middleware.ts` met `updateSession` +- RLS-policy voor `polls` tabel --- ## Tools -- Cursor (Student Plan) -- Next.js 16 -- TypeScript -- Tailwind CSS +- Supabase (Auth + Postgres + RLS) +- Next.js 16 App Router + Server Actions +- `@supabase/ssr` +- TypeScript + Tailwind CSS --- -## Lesmateriaal -- `Les09-Slide-Overzicht.md` -- `Les09-Docenttekst.md` -- `Les09-Huiswerk.md` +## Lesopdracht (in-class, 60 min) + +Bouw de signup + login + Navbar in je eigen QuickPoll-fork: +- `/signup` en `/login` routes met server actions +- Navbar toont "Inloggen" of "Uitloggen" op basis van sessie +- `middleware.ts` blokkeert `/create` voor anonieme bezoekers +- Werkende redirect na succesvolle login + +**Inleveren:** GitHub repo + screenshot van werkende flow. + +--- + +## Huiswerk (take-home) +- Voeg een `/profile` pagina toe die alleen ingelogd zichtbaar is +- Toon e-mailadres en gebruikers-ID +- Schrijf een korte RLS-policy zodat gebruikers alleen hun eigen polls zien +- Reflectie van 300 woorden: wat is het verschil tussen authenticatie en autorisatie? + +**Inleveren:** Via Teams/Brightspace. --- ## Leerdoelen Na deze les kan de student: -- Uitleggen hoe een coding agent werkt (harness: instructions + tools + model) -- `.cursor/rules` opzetten met meerdere bestanden (alwaysApply, globs) -- Plan Mode gebruiken om een feature te plannen -- Agent Mode inzetten voor multi-file feature development -- Debug Mode gebruiken voor systematisch debuggen -- Code review doen met "Find Issues" en tests schrijven -- Semantic commits maken en een PR aanmaken -- Een app from scratch bouwen met alle 5 technieken - ---- - -## Voorbereiding docent -- [ ] `npx create-next-app@latest` getest -- [ ] 3 `.cursor/rules` bestanden voorbereid -- [ ] Plan Mode demo doorgelopen -- [ ] Bug voor Debug Mode voorbereid (tag.toUpperCase() in filter) -- [ ] GitHub repo klaar voor push demo - ---- - -*Laatste update: april 2026* +- Het verschil tussen authenticatie en autorisatie uitleggen +- Een werkende signup/login/logout-flow bouwen met Supabase Auth +- Een Next.js middleware schrijven die routes beschermt +- Een eerste RLS-policy formuleren en activeren diff --git a/Samenvattingen/Les10-Samenvatting.md b/Samenvattingen/Les10-Samenvatting.md index b8e1979..2f86e36 100644 --- a/Samenvattingen/Les10-Samenvatting.md +++ b/Samenvattingen/Les10-Samenvatting.md @@ -1,116 +1,77 @@ -# Les 10: Styling: Tailwind CSS & shadcn/ui +# Les 10: Supabase Auth & Row Level Security β€” Verdieping + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les10-Supabase-Auth/Les10-Slide-Overzicht.md) +- [Docenttekst](../Les10-Supabase-Auth/Les10-Docenttekst.md) +- [Lesstof](../Les10-Supabase-Auth/Les10-Lesstof.md) +- [Lesopdracht / Hands-on](../Les10-Supabase-Auth/Les10-Lesopdracht.md) +- [Huiswerk](../Les10-Supabase-Auth/Les10-Huiswerk.md) --- -## Hoofdstuk -**Deel 3: Full-Stack Development** (Les 9-12) +## Deel +**Deel 2 β€” Technical Foundations** (Les 4-10) ## Beschrijving -Styling je applicatie met Tailwind CSS en modern components met shadcn/ui. Utility-first approach, component libraries, themeing en dark mode implementatie. +Vervolg op Les 9: we bouwen de complete auth-flow uit met sessie-management, beschermde routes en gedegen RLS-policies. De QuickPoll-app krijgt een professioneel niveau van autorisatie en is daarmee klaar voor de AI-lessen. --- -## Te Behandelen (~45 min) +## Te Behandelen -- Waarom Tailwind? Utility-first CSS approach vs traditioneel CSS -- Tailwind in Next.js (meestal al ingesteld) -- Core utilities: spacing, colors, flexbox, grid, responsive (mobile-first) -- Tailwind components: buttons, cards, forms patterns -- Wat is shadcn/ui? Beautifully designed component library -- shadcn/ui installatie en configuratie -- shadcn/ui components: Button, Card, Input, Dialog, etc. -- Custom Tailwind color themes (tailwind.config.ts) -- Dark mode implementation met Tailwind -- Performance: class optimization, purging unused styles +### Theorie +- Drie auth-methodes in Supabase (e-mail/password, magic link, OAuth) +- Hoe sessies werken (JWT, refresh tokens, cookies) +- Server- vs. client-componenten in Next.js voor auth +- Row Level Security in detail: `using` vs. `with check`, `auth.uid()` +- Beschermde routes via middleware vs. server components + +### Hands-on +- Supabase Auth provider configureren in dashboard +- Login + registratie pagina's met error handling +- Sessie ophalen in server components +- Drie RLS-policies: read-eigen, insert-eigen, update-eigen +- Test scenario: hoe weet je dat RLS Γ©cht werkt? --- ## Tools -- Tailwind CSS -- shadcn/ui -- Cursor -- TypeScript +- Supabase (Auth + RLS) +- Next.js 16 App Router +- `@supabase/ssr` + `@supabase/supabase-js` +- TypeScript + Tailwind CSS --- -## Lesopdracht (2 uur, klassikaal) +## Lesopdracht (in-class, 60 min) -### Styling Je Mini-Project +Uitbreiding van de QuickPoll-app: +- Authenticatie volledig werkend (signup, login, logout, sessie) +- Beschermde `/create` en `/my-polls` routes +- RLS-policies geactiveerd op `polls` en `options` +- "My Polls" pagina toont alleen polls van ingelogde gebruiker -**Groepsdiscussie (15 min):** -Bespreek klassikaal de Full-Stack Mini Project ervaringen uit Les 9 - welke onderdelen werkten goed en waar liepen jullie vast? - -**Deel 1: Tailwind Basics (30 min)** - -1. Open je mini-project uit Les 9 -2. Refactor bestaande components met Tailwind classes: - - Button: `bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded` - - Card: `bg-white rounded-lg shadow-lg p-6` - - Form inputs: `w-full px-3 py-2 border border-gray-300 rounded-md` -3. Voeg spacing, colors, responsive design toe - -**Deel 2: shadcn/ui Setup & Components (45 min)** - -1. Install shadcn/ui: `npx shadcn-ui@latest init` -2. Install components: `npx shadcn-ui@latest add button input card dialog` -3. Replace je custom components met shadcn versions -4. Test styling en interactiviteit - -**Deel 3: Theme & Dark Mode (30 min)** - -1. Customize Tailwind color scheme in `tailwind.config.ts` -2. Voeg dark mode toggle toe (localStorage + CSS class toggle) -3. Zorg dat je hele app responsive is (mobile-first) - -### Deliverable -- Gerestyled mini-project met Tailwind + shadcn/ui -- Dark mode toggle werkend -- Mobile responsive design -- GitHub commit met improvements +**Inleveren:** GitHub repo + screen-recording van werkende auth-flow. --- -## Huiswerk (2 uur) +## Huiswerk (take-home) +- Voeg "Edit poll" toe die alleen werkt voor eigenaar (RLS-check) +- Voeg "Delete poll" toe met confirm-dialog +- Test wat er gebeurt als je de RLS-policy uitzet (security demo) +- Reflectie van 500 woorden: waarom is RLS beter dan auth-checks in code? -### Vervolg Styling & Polish - -**Deel 1: Component Library Uitbreiden (1 uur)** - -Install en integreer meer shadcn/ui components: -- Select, Tabs, Modal/Dialog -- Forms package voor betere form handling -- Toast notifications -- Zorg dat je hele app consistent gelayout is - -**Deel 2: Custom Theme (30 min)** - -Maak je eigen color palette in `tailwind.config.ts`: -- Primary, secondary, accent colors -- Custom spacing, typography -- Test in light en dark mode - -**Deel 3: Accessibility & Polish (30 min)** - -1. Voeg alt text toe aan images -2. Zorg voor proper heading hierarchy -3. Test keyboard navigation -4. Fix UI inconsistencies - -### Deliverable -- Compleet gestylde mini-project -- Alle shadcn/ui components correct geintegreerd -- Custom color theme -- GitHub commits met styling improvements +**Inleveren:** Via Teams/Brightspace. --- ## Leerdoelen - Na deze les kan de student: -- Tailwind utility-first approach begrijpen en toepassen -- shadcn/ui components installeren en gebruiken -- Custom Tailwind themes maken -- Dark mode implementeren -- Responsive design (mobile-first) toepassen -- Professional-looking UI bouwen met componenten -- Het verschil tussen styling approaches begrijpen +- Een complete auth-flow opzetten in Next.js + Supabase +- Sessies veilig beheren via cookies + middleware +- Effectieve RLS-policies schrijven met `auth.uid()` +- Verschil uitleggen tussen auth in code vs. auth in database +- Een poll-app maken die multi-user-veilig is diff --git a/Samenvattingen/Les11-Samenvatting.md b/Samenvattingen/Les11-Samenvatting.md index 45ee7bd..74a3cfd 100644 --- a/Samenvattingen/Les11-Samenvatting.md +++ b/Samenvattingen/Les11-Samenvatting.md @@ -1,233 +1,77 @@ -# Les 11: Van Idee naar Prototype +# Les 11: Vercel AI SDK β€” Praat met je eigen data + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les11-AI-SDK/Les11-Slide-Overzicht.md) +- [Docenttekst](../Les11-AI-SDK/Les11-Docenttekst.md) +- [Lesstof](../Les11-AI-SDK/Les11-Lesstof.md) +- [Lesopdracht](../Les11-AI-SDK/Les11-Lesopdracht.md) +- [Huiswerk](../Les11-AI-SDK/Les11-Huiswerk.md) --- -## Hoofdstuk -**Deel 3: Full-Stack Development** (Les 9-12) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Proces van vaag idee naar werkend prototype. Feature breakdown, component thinking, MVP planning, user stories, en voorbereiding op eindproject. Kiezen van eindproject idea en architectuur opzetten. +Eerste echte AI-les. We bouwen een Next.js-app from scratch, koppelen Supabase, seeden 500 fictieve bands van het "Polderfest 2027" festival en laten studenten met natuurlijke taal vragen stellen aan hun eigen data via de Vercel AI SDK. --- -## Te Behandelen (~45 min) +## Te Behandelen -- Hoe ga je van vaag idee naar concrete features? -- Feature breakdown method en AI-hulp -- Component thinking: UI opdelen in herbruikbare stukken -- MVP (Minimum Viable Product) denken -- User stories schrijven (As a user I want to...) -- Prioritizing features: must-have, nice-to-have, later -- Database schema planning voor je idee -- Project architecture design -- Folder structure planning -- Eindproject idee kiezen (vereenvoudigde versie) +### Theorie +- Waarom een AI SDK i.p.v. de OpenAI API direct +- De vier kern-functies: `generateText`, `streamText`, `generateObject`, `streamObject` +- Provider-agnostic werken (OpenAI ↔ Anthropic ↔ Google ↔ Groq) +- Streaming en de UX-impact +- "Praat met je data": pattern voor LLM + database ---- - -### Van Idee naar Feature Breakdown - -**Stap 1: Beschrijf je idee in 1 zin** -``` -"Ik wil een app waar je recepten kan zoeken op basis van ingrediΓ«nten die je hebt" -``` - -**Stap 2: Werk uit wat gebruikers willen** -``` -User stories: -- Als gebruiker wil ik ingrediΓ«nten kunnen invoeren -- Als gebruiker wil ik recepten suggesties krijgen -- Als gebruiker wil ik recepten kunnen opslaan -``` - -**Stap 3: Break down in features** -``` -Core Features: -- Ingredient input form -- Recipe search/filter -- Save favorites -- View saved recipes - -Nice-to-have: -- Meal planning -- Shopping list generator -- Nutritional info -- Export recipes -``` - -**Stap 4: Prioriteer (MVP)** -- βœ… Input form + recipe search -- βœ… Display results -- βœ… Save/favorites -- ❌ Shopping list (later) -- ❌ Meal planning (later) - ---- - -### Component Thinking - -**Visualiseer je app:** -``` -App -β”œβ”€β”€ Header -β”œβ”€β”€ SearchForm (input + submit) -β”œβ”€β”€ RecipeList -β”‚ └── RecipeCard (repeated) -β”‚ β”œβ”€β”€ Title -β”‚ β”œβ”€β”€ Ingredients -β”‚ β”œβ”€β”€ Favorite button -β”‚ └── View details -└── Footer -``` - -**Questions to ask:** -- Welke componenten worden herhaald? -- Welke componenten zijn child components? -- Welke componenten hebben state? -- Waar gaat data vandaan? - ---- - -### MVP Denken - -**MVP = Minimum Viable Product** - -Het eenvoudigst mogelijke product dat nog steeds waarde levert. - -**❌ Niet MVP:** -- Alle features tegelijk -- Perfect design met animaties -- Geavanceerde filters -- Social features - -**βœ… Wel MVP:** -- Core functionaliteit werkt -- Basis styling (Tailwind) -- Happy path werkt -- Data persists (database) - ---- - -### Database Planning - -**Voor recipe app:** -``` -users table: -- id, email, password_hash, created_at - -recipes table: -- id, title, ingredients, instructions, created_at - -favorites table: -- id, user_id, recipe_id, created_at -``` - -**Relations:** -- users β†’ favorites (one-to-many) -- users β†’ saved_recipes (one-to-many) -- recipes β†’ favorites (one-to-many) +### Live demo's (klassikaal) +1. Next.js scaffold + Supabase koppelen +2. Seed-script met 500 fictieve bands +3. AI SDK installeren + eerste chat-route met `streamText` +4. `useChat` hook + vragen stellen aan de data --- ## Tools -- Cursor -- ChatGPT (voor planning) -- Pen & papier (voor sketches) +- Vercel AI SDK v6 +- Next.js 16 + Server Actions +- Supabase (Postgres + seed-script) +- OpenAI / Anthropic providers +- Polderfest-demo (in repo: `Les11-AI-SDK/polderfest-demo`) --- -## Lesopdracht (2 uur, klassikaal) +## Lesopdracht (in-class, 60 min) -### Kies je Eindproject & Plan Architectuur +Studenten kijken klassikaal mee tijdens de demo's. In-class oefening: +- Maak een eigen API key bij OpenAI of Groq +- Test de demo lokaal: kun je vragen stellen aan de Polderfest-data? +- Wijzig de system prompt: laat de AI antwoorden als een festival-roadie -**Groepsdiscussie (15 min):** -Bespreek klassikaal de styling ervaringen uit Les 10 - welke Tailwind en shadcn/ui patterns werkten goed? - -**Deel 1: Idee Kiezen (30 min)** - -Kies één van deze projecten OF je eigen idee (met goedkeuring docent): -- **AI Recipe Generator** - Input: ingredients, Output: recipe suggestions + cooking tips -- **Smart Budget Buddy** - Track expenses, AI insights on spending patterns -- **Travel Planner AI** - Generate itineraries based on preferences -- **Study Buddy AI** - Quiz generation, note-taking, Q&A helper -- **Jouw eigen idee** - -**Deel 2: Feature Breakdown (30 min)** - -Voor je gekozen project: -1. Schrijf project description (2-3 zinnen) -2. Maak 3-5 user stories -3. Break down in core vs nice-to-have features -4. Selecteer MVP (wat is essentieel?) - -**Deel 3: Architecture Planning (45 min)** - -1. Schets database schema -2. List de main components -3. Plan folder structure -4. Identify donde AI integreert (tool calling? Chat? Completion?) - -**Deel 4: Start Codebase (15 min)** - -1. Maak GitHub repo -2. Create-next-app setup -3. Push initial commit - -### Deliverable -- Project description document -- Feature breakdown met user stories -- Database schema diagram (hand-drawn ok) -- Component tree visualization -- GitHub repo met initial setup +**Inleveren:** Screenshot van werkende lokale demo. --- -## Huiswerk (2 uur) +## Huiswerk (take-home) -### Bouw Basis Architecture +Bouw een eigen "praat met je data" mini-app: +- Eigen dataset (eigen interesse, min. 50 records) +- Werkende chat-UI via AI SDK +- Deploy naar Vercel +- README met setup-stappen -**Deel 1: Folder Structure (30 min)** - -Create proper folder structure: -- src/components/ui/ -- src/components/features/ -- src/lib/ -- src/hooks/ -- src/types/ -- docs/ - -**Deel 2: Type Definitions (30 min)** - -Schrijf TypeScript types voor je project: -- User type -- Main data types -- API response types - -**Deel 3: Component Skeleton (1 uur)** - -Maak skeleton components: -- Main layout -- 3-4 main feature components (without logic) -- Proper props interfaces -- Basic Tailwind styling - -### Deliverable -- GitHub commits met folder structure -- types/ folder met je data types -- Skeleton components -- README.md met project description +**Inleveren:** GitHub URL + Vercel URL via Teams. --- ## Leerdoelen - Na deze les kan de student: -- Een project idee van vaag naar concreet uitwerken -- User stories schrijven -- Feature breakdown maken -- MVP bepalen -- Database schema ontwerpen -- Component architecture plannen -- Project codebase opzetten -- TypeScript types structureren -- Voorbereiding maken op verdere implementatie +- Een Next.js-app koppelen aan Supabase + AI SDK +- Streaming chat implementeren met `streamText` + `useChat` +- Een eigen dataset seeden en die met een LLM bevragen +- Het verschil uitleggen tussen direct OpenAI calls en de AI SDK diff --git a/Samenvattingen/Les12-Samenvatting.md b/Samenvattingen/Les12-Samenvatting.md index 4b0545b..40baee5 100644 --- a/Samenvattingen/Les12-Samenvatting.md +++ b/Samenvattingen/Les12-Samenvatting.md @@ -1,157 +1,77 @@ -# Les 12: Project Setup & AI Config (.cursorrules, claude.md) +# Les 12: Tool Calling β€” Laat AI zelf functies kiezen + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les12-Tool-Calling/Les12-Slide-Overzicht.md) +- [Docenttekst](../Les12-Tool-Calling/Les12-Docenttekst.md) +- [Lesstof](../Les12-Tool-Calling/Les12-Lesstof.md) +- [Lesopdracht](../Les12-Tool-Calling/Les12-Lesopdracht.md) +- [Huiswerk](../Les12-Tool-Calling/Les12-Huiswerk.md) --- -## Hoofdstuk -**Deel 3: Full-Stack Development** (Les 9-12) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Professional project setup en AI configuration voor optimal developer experience. Setup .cursorrules, claude.md, docs/ folder, folder structure optimalisatie en git best practices. +Vervolg op Les 11. We lossen het schaalprobleem op: niet alles in de context proppen, maar de AI laten kiezen welke tool (functie) hij nodig heeft. We bouwen 6 tools voor de Polderfest-app: search, stats, schedule, favorites en meer. --- -## Te Behandelen (~45 min) +## Te Behandelen -- Waarom goede project structuur cruciaal is voor AI tools -- De ideale folder structuur voor Next.js + AI collaboration -- .cursorrules file: syntax, best practices, examples -- claude.md: project documentation voor Claude -- docs/ folder organization: AI-DECISIONS.md, ARCHITECTURE.md, PROMPT-LOG.md -- .env.local vs .env.example configuratie -- .gitignore en .cursorignore voor AI tools -- .git best practices: meaningful commits, proper history -- README setup met project info -- TypeScript configuration optimization +### Theorie +- Wat is Tool Calling? (vergelijking met traditionele API-call) +- Het schaalprobleem: 30.000 tokens per call werkt niet voor 50.000 records +- `tool({ description, inputSchema, execute })` met Zod +- `stopWhen: stepCountIs(5)` β€” multi-step reasoning +- Wanneer tool calling NIET de oplossing is + +### Live demo's +1. Eerste tool: `searchBands` met Zod-schema +2. Multi-step setup + extra tools (stats, schedule) +3. Streaming + tool-results renderen in de UI +4. Debug-view: zien welke tools de AI kiest --- ## Tools -- Cursor -- Git -- GitHub +- Vercel AI SDK v6 (`tool`, `stepCountIs`) +- Zod voor input-validatie +- Supabase queries vanuit tools +- Polderfest-demo (in repo: `Les12-Tool-Calling/polderfest-demo`) --- -## Lesopdracht (2 uur, klassikaal) +## Lesopdracht (in-class, 60 min) -### Setup Je Eindproject Proper +Studenten kijken klassikaal mee. In-class oefening: +- Voeg een eigen tool toe aan de Polderfest-app (bv. `getBandsByGenre`) +- Test of de AI je tool spontaan gebruikt +- Check de debug-view: hoeveel stappen nam de chain? -**Groepsdiscussie (15 min):** -Bespreek klassikaal de architecture planning uit Les 11 - welke design patterns voelen goed voor jullie eindproject? - -**Deel 1: Folder Structure (30 min)** - -Zorg dat je project er zo uitziet: -``` -project/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ app/ -β”‚ β”œβ”€β”€ components/ -β”‚ β”‚ β”œβ”€β”€ ui/ -β”‚ β”‚ β”œβ”€β”€ layout/ -β”‚ β”‚ └── features/ -β”‚ β”œβ”€β”€ lib/ -β”‚ β”œβ”€β”€ hooks/ -β”‚ └── types/ -β”œβ”€β”€ docs/ -β”œβ”€β”€ public/ -β”œβ”€β”€ .cursorrules -β”œβ”€β”€ .env.local -β”œβ”€β”€ .env.example -β”œβ”€β”€ .gitignore -β”œβ”€β”€ README.md -└── claude.md -``` - -**Deel 2: .cursorrules Writing (30 min)** - -Maak comprehensive `.cursorrules`: -1. Project naam + beschrijving -2. Tech stack (Next.js 14, TypeScript, Tailwind, Supabase) -3. File structure conventions -4. Code conventions (naming, styling, error handling) -5. TypeScript strict rules -6. DO's en DON'Ts specifiek voor je project - -Example snippets: -- "Named exports only, no default exports" -- "All components are functional with TypeScript" -- "Use Tailwind classes, no inline styles" - -**Deel 3: Documentation Files (30 min)** - -Maak in docs/ folder: -- `PROJECT-BRIEF.md` - Project overview, features -- `ARCHITECTURE.md` - Component structure, data flow -- `AI-DECISIONS.md` - Start document met AI choices -- `PROMPT-LOG.md` - Template voor prompts die je gebruikt - -**Deel 4: Git Setup (20 min)** - -1. Review `.gitignore` (include `.env.local`) -2. Make initial commit: "Initial project setup" -3. Push naar GitHub -4. Verify: `.env.local` NOT in git history - -### Deliverable -- Complete folder structure -- Comprehensive .cursorrules file -- Documentation files in docs/ -- GitHub repo met clean initial commit -- README.md with project info +**Inleveren:** Screenshot van debug-view met meerdere tool-calls. --- -## Huiswerk (2 uur) +## Huiswerk (take-home) -### Optimize Configuration & Start Building +Bouw vanuit je eigen Les 11-project: +- Minimaal 3 tools met Zod-schemas +- Multi-step setup (max 5 stappen) +- README documenteert elk tool: doel + voorbeeld-prompt +- Reflectie 300 woorden: wanneer kies je tool calling vs. all-in-context? -**Deel 1: tsconfig.json Optimization (30 min)** - -1. Enable strict mode in TypeScript -2. Configure path aliases for cleaner imports: - ```json - "@/*": ["./src/*"] - ``` -3. Set proper include/exclude - -**Deel 2: Tailwind & Component Setup (45 min)** - -1. Customize `tailwind.config.ts` with your colors -2. Setup `globals.css` properly -3. Create 5 base UI components: - - Button.tsx - - Input.tsx - - Card.tsx - - Layout.tsx - - Navigation.tsx - -**Deel 3: Lib Setup (30 min)** - -Create essential lib files: -- `lib/supabase.ts` - Initialize Supabase client -- `lib/utils.ts` - Utility functions -- `lib/constants.ts` - App constants -- `types/database.ts` - Database types - -### Deliverable -- Optimized TypeScript config with path aliases -- Base UI components created -- Lib utilities setup -- Documentation updated with decisions -- GitHub commits with setup progress +**Inleveren:** GitHub URL via Teams. --- ## Leerdoelen - Na deze les kan de student: -- Een professional project structure opzetten -- Een effectieve .cursorrules file schrijven -- claude.md projectdocumentatie maken -- AI-DECISIONS.md beginnen en onderhouden -- Git best practices volgen -- TypeScript strict mode configureren -- Path aliases voor cleaner imports opzetten -- Base components en utilities creΓ«ren -- Voorbereiding maken op Deel 4 (Advanced AI) +- Een Tool definiΓ«ren met `tool()` en Zod-schema +- Multi-step tool calling configureren met `stopWhen` +- Een Supabase-query als tool blootstellen aan de LLM +- Bepalen wanneer tool calling de juiste oplossing is +- Tool-calls debuggen via de AI SDK debug-output diff --git a/Samenvattingen/Les13-Samenvatting.md b/Samenvattingen/Les13-Samenvatting.md index 11a9f20..64d9fd8 100644 --- a/Samenvattingen/Les13-Samenvatting.md +++ b/Samenvattingen/Les13-Samenvatting.md @@ -1,298 +1,80 @@ -# Les 13: Vercel AI SDK, Tool Calling & Agents +# Les 13: Externe APIs + Cursor agents + Vercel deploy + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les13-Cursor-Vercel-Deploy/Les13-Slide-Overzicht.md) +- [Docenttekst](../Les13-Cursor-Vercel-Deploy/Les13-Docenttekst.md) +- [Lesstof](../Les13-Cursor-Vercel-Deploy/Les13-Lesstof.md) +- [Lesopdracht](../Les13-Cursor-Vercel-Deploy/Les13-Lesopdracht.md) +- [Huiswerk](../Les13-Cursor-Vercel-Deploy/Les13-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Vercel AI SDK fundamentals voor het bouwen van AI-powered features. Stream responses, tool calling, Zod schemas, system prompts, agents met autonome actie. Integreer LLM capabilities in je app. +We verlaten `localhost:3000`. We bouwen een kleine PokΓ©dex die de PokΓ©API gebruikt (geen LLM nodig), zetten Cursor's Composer + Background Agent in voor feature branches, en deployen naar Vercel met preview deployments + GitHub Actions CI. --- -## Te Behandelen (~45 min) +## Te Behandelen -- Vercel AI SDK: wat is het en waarom gebruiken? -- Installation en basic setup -- useChat hook voor chat UI state management -- Streaming responses van API -- Tool calling: laat AI externe APIs aanroepen -- Zod schemas voor tool parameters validation -- System prompts schrijven voor AI behavior -- Agent patterns: maxSteps, autonomous execution -- Error handling en edge cases -- Model selection: OpenAI, Claude, Gemini, etc. +### Theorie +- Externe APIs in Next.js (server-side fetch, caching) +- Cursor Composer vs. Background Agent β€” wanneer welke? +- Vercel preview deployments per branch +- GitHub Actions CI (lint + build per PR) +- Environment variables: lokaal vs. Vercel ---- - -### Vercel AI SDK Basics - -**Wat is het?** -- React library van Vercel voor AI integration -- Streaming responses van LLMs -- Server-side tool calling -- Multi-turn conversations -- Gratis, open-source - -**Installation:** -```bash -npm install ai zod openai -``` - ---- - -### useChat Hook - -**Client-side chat state management:** - -```typescript -'use client' -import { useChat } from 'ai/react' - -export function ChatComponent() { - const { messages, input, handleInputChange, handleSubmit } = useChat({ - api: '/api/chat', - }) - - return ( -
- {messages.map((msg) => ( -
- {msg.role}: {msg.content} -
- ))} -
- - -
-
- ) -} -``` - ---- - -### Streaming Responses - -**API Route met streaming:** - -```typescript -import { generateText, streamText } from 'ai' -import { openai } from '@ai-sdk/openai' - -export async function POST(req: Request) { - const { messages } = await req.json() - - const result = await streamText({ - model: openai('gpt-4'), - system: 'You are a helpful assistant', - messages, - }) - - return result.toAIStreamResponse() -} -``` - -**Waarom streaming?** -- Responses verschijnen real-time (beter UX) -- Bespaar tokens vs waiting for full response - ---- - -### Tool Calling - -**Laat AI externe APIs aanroepen:** - -```typescript -import { generateText } from 'ai' -import { openai } from '@ai-sdk/openai' -import { z } from 'zod' - -const tools = { - getWeather: { - description: 'Get weather for a city', - parameters: z.object({ - city: z.string(), - }), - execute: async ({ city }: { city: string }) => { - // Call external API - const response = await fetch(`https://api.weather.com?city=${city}`) - return response.json() - }, - }, -} - -const result = await generateText({ - model: openai('gpt-4'), - tools, - prompt: 'What is the weather in Amsterdam?', -}) -``` - ---- - -### Zod Schemas - -**Type-safe tool parameters:** - -```typescript -import { z } from 'zod' - -const SearchProductsSchema = z.object({ - query: z.string().describe('Search query'), - limit: z.number().optional().describe('Max results'), - sortBy: z.enum(['price', 'rating']).optional(), -}) - -type SearchProductsInput = z.infer -``` - ---- - -### System Prompts - -**Stuur AI behavior:** - -```typescript -const systemPrompt = `You are a helpful recipe assistant. -Your role is to: -1. Suggest recipes based on ingredients -2. Provide cooking instructions -3. Estimate cooking time - -Always be friendly and encouraging.` - -const result = await generateText({ - model: openai('gpt-4'), - system: systemPrompt, - prompt: userMessage, -}) -``` - ---- - -### Agent Patterns - -**Multi-step autonomous execution:** - -```typescript -const result = await generateText({ - model: openai('gpt-4'), - tools: { getWeather, getFlights }, - maxSteps: 3, // Maximum iterations - prompt: 'Plan a trip to Paris next week', -}) -``` - -**Hoe het werkt:** -1. AI bepaalt welke tool nodig is -2. Tool wordt uitgevoerd -3. Result teruggestuurd naar AI -4. AI beslist next stap (repeat tot maxSteps of done) +### Live demo's +1. Nieuwe PokΓ©dex-app β€” PokΓ©API integreren +2. Feature branch via Cursor Composer +3. Background Agent: PR + diff review +4. Vercel deploy + preview URL per PR +5. GH Actions workflow voor lint + typecheck --- ## Tools -- Vercel AI SDK -- Zod -- OpenAI API (of andere LLM provider) -- Cursor +- Next.js 16 +- Cursor (Composer + Background Agent) +- Vercel (productie + previews) +- GitHub Actions +- PokΓ©API (geen key nodig) --- -## Lesopdracht (2 uur, klassikaal) +## Lesopdracht (in-class, 60 min) -### Bouw Chat Interface met Streaming +Studenten kijken mee. In-class oefening: +- Clone de PokΓ©dex-starter +- Maak een feature branch via Cursor Composer ("voeg type-filter toe") +- Push naar GitHub en bekijk je Vercel preview URL +- Merge de PR β€” zie GH Actions runnen -**Groepsdiscussie (15 min):** -Bespreek klassikaal de project setup ervaringen uit Les 12 - hoe goed werken jullie .cursorrules en configuration? - -**Deel 1: Installation & Setup (30 min)** - -```bash -npm install ai zod openai -``` - -Create `app/api/chat/route.ts`: -- Setup Vercel AI SDK -- Configure OpenAI model -- Add system prompt - -**Deel 2: Chat Component (45 min)** - -Build `app/page.tsx`: -1. Use useChat hook -2. Render messages list -3. Input form for user messages -4. Display streaming responses - -**Deel 3: Tool Calling (30 min)** - -Add 2 simple tools: -- getTime: return current time -- getRandomNumber: return random number - -Update API route to handle tools with Zod schemas. - -**Deel 4: Testing (15 min)** - -Test chat locally with different prompts that trigger tools. - -### Deliverable -- Werkende chat interface with streaming -- 2 integrated tools -- GitHub commit with AI chat feature +**Inleveren:** Screenshot van werkende preview URL. --- -## Huiswerk (2 uur) +## Huiswerk (take-home) -### Integreer AI in Eindproject +Neem je AI SDK-project uit Les 11/12: +- Deploy naar Vercel +- Voeg minimaal 1 externe API toe (geen LLM) +- Configureer GH Actions CI (lint + build) +- README beschrijft hoe lokaal + hoe productie -**Deel 1: Project-Specific Tools (1 uur)** - -Add 2-3 tools relevant to your project: -- Recipe Generator: tool to search recipes API -- Budget App: tool to calculate expenses -- Travel Planner: tool to search destinations - -Define with Zod schemas and execute functions. - -**Deel 2: System Prompt Tuning (30 min)** - -Write a custom system prompt for your AI: -- Define personality -- Set constraints -- Add context about your app - -**Deel 3: Integration (30 min)** - -Connect AI chat to your main app: -- Add chat page/component -- Integrate with Supabase auth (if needed) -- Test end-to-end - -### Deliverable -- AI feature integrated in project -- Custom tools defined -- docs/AI-DECISIONS.md updated with choices -- GitHub commits with AI integration +**Inleveren:** GitHub + Vercel URL via Teams. --- ## Leerdoelen - Na deze les kan de student: -- Vercel AI SDK installeren en configureren -- useChat hook gebruiken voor chat UI -- Streaming responses implementeren -- Tool calling setup met Zod schemas -- Externe APIs aanroepen via tools -- System prompts schrijven voor AI behavior -- Agent patterns verstaan (maxSteps) -- AI features in een Next.js app integreren -- Tool parameters valideren met Zod +- Externe APIs aanroepen vanuit een Next.js server route +- Het verschil uitleggen tussen Cursor Composer en Background Agent +- Een feature branch + preview deployment opzetten +- GitHub Actions CI configureren voor een Next.js project +- Environment variables veilig beheren tussen lokaal en Vercel diff --git a/Samenvattingen/Les14-Samenvatting.md b/Samenvattingen/Les14-Samenvatting.md index d8f4a81..5f77d6c 100644 --- a/Samenvattingen/Les14-Samenvatting.md +++ b/Samenvattingen/Les14-Samenvatting.md @@ -1,330 +1,77 @@ -# Les 14: AI Chat Interface & Streaming +# Les 14: Agents β€” LLM in een loop met tools + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les14-Agents/Les14-Slide-Overzicht.md) +- [Docenttekst](../Les14-Agents/Les14-Docenttekst.md) +- [Lesstof](../Les14-Agents/Les14-Lesstof.md) +- [Lesopdracht](../Les14-Agents/Les14-Lesopdracht.md) +- [Huiswerk](../Les14-Agents/Les14-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Bouwen van professionele chat interfaces met streaming responses. Message rendering, markdown support, error handling, loading states, en UX patterns voor AI-powered features. +We gaan een stap verder dan multi-step tool calling. Een agent kan 20-50 stappen autonoom uitvoeren, eigen plannen maken en dynamisch bijsturen. We bouwen een research-agent from scratch met `ToolLoopAgent`, `stopWhen` en `prepareStep`. --- -## Te Behandelen (~45 min) +## Te Behandelen -- Chat UI patterns en best practices -- useChat hook deep dive (state, loading, error) -- Streaming response handling en real-time updates -- Message rendering strategies en optimizations -- Markdown rendering in chat messages -- Error handling en error boundaries -- Loading states en skeleton loaders -- User input validation and sanitization -- Accessibility in chat interfaces (ARIA labels) -- Message persistence (localStorage of database) -- Performance optimization +### Theorie +- Wat is een agent eigenlijk? (LLM + loop + tools + stopconditie) +- Wanneer wel/niet een agent gebruiken +- AI SDK v6 agent-primitives: `ToolLoopAgent`, `stopWhen`, `prepareStep` +- `prepareStep`: dynamisch model + tools per stap wisselen +- Cost & latency van agents β€” wat moet je weten? ---- - -### useChat Hook Deep Dive - -**State management met useChat:** - -```typescript -'use client' -import { useChat } from 'ai/react' - -export function ChatComponent() { - const { - messages, // All messages in conversation - input, // Current input text - handleInputChange, // Update input - handleSubmit, // Send message - isLoading, // Is AI responding? - error, // Any errors? - } = useChat({ - api: '/api/chat', - initialMessages: [], // Optional: pre-load messages - }) - - return ( - <> -
- {messages.map((msg) => ( -
- {msg.role}: {msg.content} -
- ))} -
- {isLoading &&
AI is thinking...
} - {error &&
Error: {error.message}
} -
- - -
- - ) -} -``` - ---- - -### Message Rendering Patterns - -**Basic pattern:** -```typescript -
- {messages.map((msg) => ( -
-
- {msg.content} -
-
- ))} -
-``` - -**With markdown rendering:** -```typescript -import ReactMarkdown from 'react-markdown' - -
- {msg.content} -
-``` - -**With message types:** -```typescript -{messages.map((msg) => ( -
- {msg.content} -
-))} -``` - ---- - -### Error Handling - -**Structured error handling:** - -```typescript -try { - const response = await fetch('/api/chat', { - method: 'POST', - body: JSON.stringify({ messages }), - }) - - if (!response.ok) { - throw new Error(`API error: ${response.status}`) - } - - // Handle streaming... -} catch (error) { - console.error('Chat error:', error) - setError({ - message: 'Failed to send message', - code: error instanceof Error ? error.message : 'unknown' - }) -} -``` - -**Error boundary:** -```typescript -Chat error occurred}> - - -``` - ---- - -### Loading States - -**Skeleton loader:** -```typescript -function MessageSkeleton() { - return ( -
-
-
-
- ) -} - -{isLoading && } -``` - ---- - -### Input Validation - -**Validate before sending:** - -```typescript -function handleSubmit(e: React.FormEvent) { - e.preventDefault() - - // Trim whitespace - const trimmedInput = input.trim() - - // Validate non-empty - if (!trimmedInput) { - setError('Message cannot be empty') - return - } - - // Validate length - if (trimmedInput.length > 1000) { - setError('Message too long (max 1000 chars)') - return - } - - // Send message - handleSubmit(e) -} -``` - ---- - -### Message Persistence - -**Save to localStorage:** - -```typescript -const [messages, setMessages] = useState(() => { - const saved = localStorage.getItem('chat_history') - return saved ? JSON.parse(saved) : [] -}) - -// Save whenever messages change -useEffect(() => { - localStorage.setItem('chat_history', JSON.stringify(messages)) -}, [messages]) -``` - -**Save to database:** -```typescript -const saveMessage = async (message: Message) => { - await fetch('/api/messages', { - method: 'POST', - body: JSON.stringify({ - content: message.content, - role: message.role, - userId: user.id, - }), - }) -} -``` +### Live demo's +1. Setup nieuwe research-agent (los project) +2. Tools: webSearch, fetchPage, summarize, writeReport +3. `stopWhen: stepCountIs(20)` + `hasToolCall("writeReport")` +4. `prepareStep` om Sonnet te gebruiken voor summarize, Haiku voor search +5. Live: vraag de agent een onderwerp te onderzoeken --- ## Tools -- Vercel AI SDK -- React Markdown -- Cursor -- TypeScript +- Vercel AI SDK v6 (`ToolLoopAgent`, `stopWhen`, `prepareStep`) +- Tavily of Exa API voor web search +- Anthropic + OpenAI providers --- -## Lesopdracht (2 uur, klassikaal) +## Lesopdracht (in-class, 60 min) -### Build Professional Chat Interface +Studenten kijken mee. In-class oefening: +- Voeg een eigen tool toe aan de research-agent +- Tweak de stopconditie: laat de agent na 5 minuten stoppen +- Bekijk de trace: welke stappen koos de agent? -**Groepsdiscussie (15 min):** -Bespreek klassikaal de Vercel AI SDK ervaringen uit Les 13 - welke tool calling patterns werkten goed? - -**Deel 1: Chat Component (45 min)** - -Build components/ChatInterface.tsx: -1. Use useChat hook -2. Render messages with proper styling -3. User vs AI message styling -4. Input form with validation -5. Tailwind + shadcn/ui components - -**Deel 2: Markdown & Error Handling (30 min)** - -1. Install react-markdown: `npm install react-markdown` -2. Render AI responses with markdown -3. Add error boundary -4. Show error messages to user -5. Proper loading states - -**Deel 3: UX Improvements (30 min)** - -1. Auto-scroll to latest message -2. Disable input while loading -3. Show message count/token usage -4. Add clear chat history button -5. Save messages to localStorage - -**Deel 4: Testing (15 min)** - -Test chat interface locally with various inputs and error scenarios. - -### Deliverable -- Werkende chat interface component -- Markdown rendering working -- Error handling implemented -- LocalStorage persistence -- GitHub commit with chat UI +**Inleveren:** Screenshot van werkende agent-loop. --- -## Huiswerk (2 uur) +## Huiswerk (take-home) -### Integrate Chat into Your Project +Bouw je eigen agent voor een eigen use-case: +- Minimaal 3 tools +- Slimme stop-conditie +- `prepareStep` voor model-routing +- Reflectie 500 woorden: kan dit ook met gewone tool calling? -**Deel 1: Project Integration (1 uur)** - -1. Add chat component to your app -2. Connect to your API route with tools -3. Style to match your design -4. Test with actual tools/integrations -5. Fix any bugs - -**Deel 2: Enhanced Features (30 min)** - -Add one of these: -- Message copy button -- Regenerate response option -- Clear history confirmation -- Export chat history -- Message timestamps - -**Deel 3: Performance & Polish (30 min)** - -1. Optimize re-renders (useMemo, useCallback) -2. Virtual scrolling for long chats -3. Better accessibility (keyboard nav) -4. Mobile responsive tweaks -5. Update docs/AI-DECISIONS.md - -### Deliverable -- Chat fully integrated in project -- Enhanced features implemented -- Performance optimized -- GitHub commits with improvements +**Inleveren:** GitHub URL via Teams. --- ## Leerdoelen - Na deze les kan de student: -- useChat hook volledig begrijpen en gebruiken -- Professionele chat UI patterns implementeren -- Markdown rendering in chat messages -- Error handling en error boundaries toepassen -- Loading states en skeletons bouwen -- User input valideren en sanitizen -- Message persistence (localStorage/DB) -- Accessibility in chat interfaces verbeteren -- Performance optimizations toepassen -- Complete chat feature in Next.js app integreren +- Het verschil uitleggen tussen tool calling en een agent +- Een agent opzetten met `ToolLoopAgent` en custom tools +- Een doordachte stopconditie schrijven met `stopWhen` +- Modellen dynamisch routen per stap via `prepareStep` +- Inschatten wanneer een agent de juiste oplossing is diff --git a/Samenvattingen/Les15-Samenvatting.md b/Samenvattingen/Les15-Samenvatting.md index bcf9ba7..1d8d6ef 100644 --- a/Samenvattingen/Les15-Samenvatting.md +++ b/Samenvattingen/Les15-Samenvatting.md @@ -1,130 +1,79 @@ -# Les 15: Eindproject Werkdag 1 +# Les 15: RAG + Embeddings β€” Praat met je documenten + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les15-RAG-Embeddings/Les15-Slide-Overzicht.md) +- [Docenttekst](../Les15-RAG-Embeddings/Les15-Docenttekst.md) +- [Lesstof](../Les15-RAG-Embeddings/Les15-Lesstof.md) +- [Lesopdracht](../Les15-RAG-Embeddings/Les15-Lesopdracht.md) +- [Huiswerk](../Les15-RAG-Embeddings/Les15-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Eerste intensieve werkdag aan je eindproject. Focus op core features bouwen, Q&A sessions, individuele hulp, en probleemoplossing. Docent beschikbaar voor guidance en klassikale sparring. +Hoe laat je een LLM antwoorden op basis van documenten die hij nooit heeft gezien? Antwoord: Retrieval-Augmented Generation (RAG). We bouwen een PDF Q&A-app from scratch met embeddings, vector search (pgvector) en context-injectie. --- -## Te Behandelen (~45 min guidance + 120 min work) +## Te Behandelen -- Q&A session: wat loop je tegen aan? -- Cursor tips & tricks review -- Debugging strategies en common patterns -- Performance optimization basics -- Deployment troubleshooting preview -- Hands-on assistance beschikbaar -- Code review feedback -- Feature planning discussion +### Theorie +- Het probleem: LLM kent jouw documenten niet +- Twee oplossingen: alles meesturen vs. RAG +- Wat zijn embeddings? (vectoren, semantische gelijkenis) +- pgvector in Supabase: tabel-setup + index +- De RAG-loop: chunk β†’ embed β†’ store β†’ query β†’ retrieve β†’ answer +- Limieten en valkuilen (chunking, recall vs. precision) ---- - -## Doel van deze Werkdag - -- Core features van je eindproject afmaken -- Architecture validatie (is je setup goed?) -- Database integratie testen -- Auth flows uitwerken -- First AI feature implementeren -- Testing en bug fixes - ---- - -### Checklist voor deze Sessie - -**Code Quality:** -- [ ] TypeScript geen errors -- [ ] No console.logs in production code -- [ ] Proper error handling -- [ ] Component props typed - -**Features:** -- [ ] Core feature 1 werkend -- [ ] Core feature 2 werkend -- [ ] Data persistence (Supabase) -- [ ] Loading states visible -- [ ] Error states handled - -**Setup:** -- [ ] Project structure clean -- [ ] .cursorrules up-to-date -- [ ] Environment variables configured -- [ ] Git history clean +### Live demo's +1. Nieuwe Next.js-app + Supabase met pgvector +2. PDF parsen + chunking strategie +3. Embeddings genereren via OpenAI `text-embedding-3-small` +4. Vector-search route + top-K retrieval +5. Chat-interface met context-injectie --- ## Tools -- Cursor -- Chrome DevTools -- Supabase Dashboard -- GitHub -- Vercel (optional preview) +- Vercel AI SDK v6 +- Supabase + pgvector extension +- OpenAI embeddings API +- pdf-parse / unpdf voor PDF-extractie --- -## Lesopdracht (4 uur, hands-on werk) +## Lesopdracht (in-class, 60 min) -### Intensive Development Session +Studenten kijken klassikaal mee. In-class oefening: +- Upload een eigen PDF in de demo-app +- Stel 5 vragen β€” beoordeel de antwoorden +- Probeer een vraag die NIET in de PDF staat: wat gebeurt er? -**Hou je voortgang bij:** - -**Eerste uur (0-60 min):** -- [ ] Maak prioriteiten list van features -- [ ] Fix top 3 bugs van vorige sessies -- [ ] Test alles lokaal -- [ ] Ask docent for guidance if stuck - -**Twee uur (60-120 min):** -- [ ] Implementeer core feature 1 (CRUD) -- [ ] Implementeer core feature 2 -- [ ] Add loading/error states -- [ ] Styling pass 1 - -**Drie uur (120-180 min):** -- [ ] Implementeer AI feature (tool calling) -- [ ] Test end-to-end flows -- [ ] Fix bugs found -- [ ] Performance check - -**Vier uur (180-240 min):** -- [ ] Polish UI -- [ ] Code cleanup -- [ ] Document decisions -- [ ] Commit and push - -### Deliverable -- Werkende core features -- Updated PROMPT-LOG.md (add entries) -- Updated AI-DECISIONS.md -- GitHub commits with progress -- Screenshot of working features +**Inleveren:** Screenshot van Q&A-sessie + reflectie 1 alinea. --- -## Huiswerk +## Huiswerk (take-home) -Continue building your project for next session. +Bouw een eigen RAG-app: +- Eigen dataset (handleidingen, notities, eigen blog) +- Werkende vector-search met pgvector +- Chat-UI met source-attribution (welke chunks gebruikt?) +- Deploy naar Vercel -**Checklist:** -- [ ] Core features complete -- [ ] All changes pushed to GitHub -- [ ] PROMPT-LOG.md updated with prompts used -- [ ] AI-DECISIONS.md updated with choices made -- [ ] Ready for next workday session +**Inleveren:** GitHub + Vercel URL. --- ## Leerdoelen - Na deze les kan de student: -- Zelfstandig problemen identificeren en fixen -- Cursor effectief gebruiken voor rapid development -- Architectuur decisions valideren en aanpassen -- Debugging strategieΓ«n toepassen -- Features end-to-end implementeren -- Development workflows versnellen -- Documentatie up-to-date houden +- Het RAG-pattern uitleggen aan een collega +- pgvector configureren in Supabase +- PDF-content chunken en embedden +- Een werkende vector-search query schrijven +- Bepalen wanneer RAG vs. alles-meesturen vs. fine-tuning past diff --git a/Samenvattingen/Les16-Samenvatting.md b/Samenvattingen/Les16-Samenvatting.md index 43be8e9..f8e5023 100644 --- a/Samenvattingen/Les16-Samenvatting.md +++ b/Samenvattingen/Les16-Samenvatting.md @@ -1,122 +1,80 @@ -# Les 16: Eindproject Werkdag 2 +# Les 16: Model Context Protocol β€” Bouw je eigen MCP-server + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les16-MCP/Les16-Slide-Overzicht.md) +- [Docenttekst](../Les16-MCP/Les16-Docenttekst.md) +- [Lesstof](../Les16-MCP/Les16-Lesstof.md) +- [Lesopdracht](../Les16-MCP/Les16-Lesopdracht.md) +- [Huiswerk](../Les16-MCP/Les16-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 3 β€” AI Development** (Les 11-16) ## Beschrijving -Verlengde werksessie voor je eindproject. Continue Development Day waar je intensief aan je AI-powered applicatie werkt met begeleiding en feedback van de docent. +MCP (Model Context Protocol) is de open standaard waarmee Anthropic, OpenAI en de community AI-clients koppelen aan tools en data. We bouwen een eigen MCP-server voor Polderfest-data en laden hem in Cursor Γ©n Claude Desktop. --- ## Te Behandelen -### Groepsdiscussie (15 min) -Bespreek klassikaal de deployment ervaringen uit Les 15 - welke problemen kwamen jullie tegen en hoe losten jullie die op? +### Theorie +- Wat is MCP en waarom bestaat het? +- Het probleem: tools zitten nu vast aan één app +- MCP-architectuur: server, client, transport (stdio, HTTP) +- Tools vs. Resources vs. Prompts +- Bestaande MCP-ecosysteem (filesystem, git, Slack, GitHub) +- MCP vs. Tool Calling β€” wanneer welke? -### Doel van deze werkdag - -Deze les is puur praktisch werken: -- Bugs fixen die je bent tegengekomen -- Features afmaken die nog niet klaar zijn -- Code cleanup en optimalisatie -- Performance verbeteren -- Documentatie aanpassen -- Testen in productie - ---- - -### Checklist voor deze sessie - -**Code Quality:** -- [ ] TypeScript geen errors -- [ ] ESLint check passed -- [ ] Geen unused imports/variables -- [ ] Proper error handling overal - -**Features:** -- [ ] Alle core features werken -- [ ] AI feature functionaliteit compleet -- [ ] Edge cases afgehandeld -- [ ] Loading states geimplementeerd -- [ ] Error states geimplementeerd - -**Performance:** -- [ ] Lighthouse score > 80 -- [ ] Images optimized -- [ ] Lazy loading waar nodig -- [ ] Bundle size reasonable - -**Deployment:** -- [ ] Builds succesvol lokaal -- [ ] Alle env vars in Vercel -- [ ] Works in production -- [ ] Supabase redirects configured +### Live demo's +1. MCP SDK installeren + eerste server met 1 tool +2. Server laden in Cursor + in Claude Desktop +3. Eigen Polderfest MCP-server (search, stats, schedule) +4. Resources en Prompts toevoegen +5. Server publiceren op npm --- ## Tools -- Cursor -- Chrome DevTools -- Vercel Dashboard -- Supabase Dashboard -- GitHub +- `@modelcontextprotocol/sdk` (TypeScript) +- Cursor (`.cursor/mcp.json`) +- Claude Desktop (`claude_desktop_config.json`) +- Node.js + TypeScript --- -## Lesopdracht (4 uur) +## Lesopdracht (in-class, 60 min) -### Intensive Development Session +Studenten kijken mee. In-class oefening: +- Clone de Polderfest MCP-starter +- Voeg een eigen tool toe (`getBandByCountry`) +- Laad de server in Cursor of Claude Desktop +- Test of de AI je tool vindt en gebruikt -**Hou je voortgang bij:** - -**Eerste uur:** -- [ ] Maak prioriteiten list -- [ ] Fix top 3 bugs -- [ ] Test alles lokaal - -**Twee uur:** -- [ ] Implementeer ontbrekende features -- [ ] Add missing error handling -- [ ] Improve styling/UX - -**Drie uur:** -- [ ] Code cleanup -- [ ] Performance optimization -- [ ] Redeploy - -**Vier uur:** -- [ ] Final testing in production -- [ ] Screenshot taken -- [ ] Docs updated - -### Deliverable -- Werkende app (lokaal en productie) -- Updated PROMPT-LOG.md -- Updated AI-DECISIONS.md -- Screenshot van werkende app +**Inleveren:** Screenshot van werkende MCP-tool in Cursor/Claude. --- -## Huiswerk +## Huiswerk (take-home) -Continue working on your end project for next session. Make sure to: -- Push all changes to GitHub -- Document what you've done -- List remaining work for next session +Bouw een MCP-server voor je eigen domein: +- Minimaal 3 tools +- 1 resource (bijv. een doc-bestand) +- 1 prompt-template +- README met installatie-instructies voor Cursor -### Deliverable -- All work committed and pushed -- Documentation current -- Task list for Les 17 +**Inleveren:** GitHub URL via Teams. --- ## Leerdoelen Na deze les kan de student: -- Zelfstandig bugs identificeren en fixen -- Code quality verbeteren -- Performance optimisatie toepassen -- Productie deployment testen en valideren -- Documentatie up-to-date houden +- MCP uitleggen aan een collega (waarom + hoe) +- Een eigen MCP-server opzetten met de TypeScript SDK +- Een server laden in Cursor en/of Claude Desktop +- Het verschil tussen Tool, Resource en Prompt benoemen +- Bepalen wanneer een MCP-server vs. inline tool calling past diff --git a/Samenvattingen/Les17-Samenvatting.md b/Samenvattingen/Les17-Samenvatting.md index f808727..ddb61de 100644 --- a/Samenvattingen/Les17-Samenvatting.md +++ b/Samenvattingen/Les17-Samenvatting.md @@ -1,207 +1,77 @@ -# Les 17: Eindproject Polish & Code Review +# Les 17: AI Production Polish β€” Observability, Evals, Security, Cost + +> βœ… **Deze les is volledig uitgewerkt** + +## Lesmateriaal + +- [Slide Overzicht](../Les17-Production-Polish/Les17-Slide-Overzicht.md) +- [Docenttekst](../Les17-Production-Polish/Les17-Docenttekst.md) +- [Lesstof](../Les17-Production-Polish/Les17-Lesstof.md) +- [Lesopdracht](../Les17-Production-Polish/Les17-Lesopdracht.md) +- [Huiswerk](../Les17-Production-Polish/Les17-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 4 β€” Production & Advanced** (Les 17-18) ## Beschrijving -Finale polish fase van je eindproject. Focus op code review, peer feedback, en laatste verbeteringen voor inlevering. Voorbereiding op presentatie. +Van "het werkt op mijn laptop" naar productie-klaar. We behandelen de vier pillaren: observability (Langfuse), evals (LLM-as-judge), security (prompt injection + guardrails) en cost (monitoring + model routing). --- ## Te Behandelen -### Groepsdiscussie (15 min) -Bespreek klassikaal de werkdag voortgang uit Les 16 - wat hebben jullie afgekregen, wat bleek moeilijker dan verwacht? +### Theorie +- De vier pillaren: observability, evals, security, cost +- Waarom logs alleen niet genoeg zijn voor AI +- Evals als unit tests voor LLM-output +- Prompt injection β€” een echte demo +- Cost-monitoring: per user, per route, per model -### Code Review Checklist - -**TypeScript & Code Quality:** -- [ ] No TypeScript errors -- [ ] No `any` types -- [ ] Props properly typed -- [ ] Error handling complete -- [ ] No console.logs in production - -**React Best Practices:** -- [ ] No unnecessary re-renders -- [ ] Keys properly set in lists -- [ ] Hooks rules followed -- [ ] Components split logically -- [ ] Prop drilling minimized - -**Styling & UX:** -- [ ] Responsive design working -- [ ] Mobile friendly -- [ ] Consistent styling -- [ ] Accessible (alt text, labels, etc.) -- [ ] No visual bugs - -**Performance:** -- [ ] Lighthouse > 80 -- [ ] Lazy load images -- [ ] Optimize bundles -- [ ] Fast interactions -- [ ] Minimal flickering - ---- - -### Peer Review Process - -**Hoe peer review doen:** - -1. **Voorbereiding (10 min)** - - Share productie URL of GitHub link - - List main features - - Highlight AI features - -2. **Review (15 min)** - - Reviewer tests alle features - - Takes notes - - Looks at code (if applicable) - -3. **Feedback (10 min)** - - βœ… Wat werkt goed - - ⚠️ What could improve - - ❌ Any bugs found - -4. **Discussion (5 min)** - - Q&A - - Discuss suggestions - - Agree on priorities - ---- - -### Final Checklist for Submission - -**Functionality:** -- [ ] All features work in production -- [ ] Auth flows complete -- [ ] CRUD operations complete -- [ ] AI feature functional -- [ ] No console errors - -**Documentation:** -- [ ] README.md complete -- [ ] PROMPT-LOG.md has 10+ entries -- [ ] AI-DECISIONS.md has 5+ entries -- [ ] .env.example up to date -- [ ] Setup instructions clear - -**Code Quality:** -- [ ] Code is clean and organized -- [ ] Comments where needed -- [ ] Consistent naming -- [ ] No dead code -- [ ] .cursorrules present - -**Performance & UX:** -- [ ] Lighthouse score > 80 -- [ ] Loading states visible -- [ ] Error states handled -- [ ] Mobile responsive -- [ ] Fast load times - -**Deployment:** -- [ ] Deployed on Vercel -- [ ] Working on production URL -- [ ] Supabase configured -- [ ] Environment variables secure -- [ ] No errors in production +### Live demo's +1. Langfuse trace-integratie in Polderfest-chat +2. Eval-suite met LLM-as-judge (Vitest + AI SDK) +3. Prompt injection: aanvallen + guardrails (Lakera-style) +4. Cost-tracking middleware + automatic model routing --- ## Tools -- GitHub -- Vercel -- Chrome DevTools -- Cursor -- Peer reviewers +- Langfuse (self-hosted of cloud) +- Vitest voor evals +- Vercel AI SDK middleware +- Lakera AI / prompt-shield patterns --- -## Lesopdracht (3 uur) +## Lesopdracht (in-class, 30 min) -### Code Review & Polish Session +Studenten kijken klassikaal mee. In-class oefening: +- Activeer Langfuse-tracing in je eigen Polderfest-fork +- Bekijk je eerste trace +- Voeg één eval-test toe en run hem -**Deel 1: Peer Review (1 uur)** - -Work in pairs or small groups: -1. Exchange project URLs/repos -2. Each person reviews another's work -3. Take detailed notes -4. Provide constructive feedback -5. Discuss improvements - -**Deel 2: Final Polish (1.5 uur)** - -Based on feedback: -1. Fix identified bugs -2. Implement suggested improvements -3. Code cleanup -4. Update documentation -5. Final test in production - -**Deel 3: Final Checks (30 min)** - -Go through the submission checklist: -1. Verify all items are done -2. Test everything once more -3. Make final commits -4. Push to GitHub -5. Screenshot for documentation - -### Deliverable -- Peer review feedback received -- All feedback items addressed -- Final production-ready code -- Complete documentation -- Screenshot of final app +**Inleveren:** Screenshot Langfuse-trace + 1 passing eval. --- -## Huiswerk +## Huiswerk (take-home) -**Final submission preparation:** +In je eigen project uit Les 11-15: +- Volledige eval-suite (min. 5 tests) +- Security-laag tegen prompt injection +- Cost-dashboard (Langfuse of eigen middleware) +- `POLISH.md` in repo-root: wat heb je toegevoegd en waarom? -1. **Complete ALL documentation:** - - README with features and setup - - PROMPT-LOG.md with 10+ prompts - - AI-DECISIONS.md with 5+ decisions - - Project state documented - -2. **Final testing:** - - Test all features in production - - Check Lighthouse score - - Verify mobile responsiveness - - Check load times - -3. **Code review:** - - Ask classmates to review code - - Ask docent for feedback - - Fix any issues found - - Final cleanup - -4. **Prepare for submission:** - - Ensure Git history is clean - - All commits have good messages - - GitHub repo is public/accessible - - Production URL is stable - -### Deliverable -- Final, polished application -- All documentation complete -- Code review completed -- Ready for submission +**Inleveren:** GitHub URL via Teams. --- ## Leerdoelen Na deze les kan de student: -- Code review uitvoeren volgens best practices -- Peer feedback ontvangen en implementeren -- Final polish toepassen op projecten -- Production checklist doorlopen -- Professional quality deliverables opleveren -- Zelfstandig werk evalueren en verbeteren +- De vier productie-pillaren benoemen en uitleggen +- Een AI-app instrumenteren met Langfuse-traces +- Een LLM-as-judge eval-suite opzetten met Vitest +- Een prompt injection herkennen en bescherming inbouwen +- Cost-per-user meten en model-routing inrichten diff --git a/Samenvattingen/Les18-Samenvatting.md b/Samenvattingen/Les18-Samenvatting.md index f8981d7..13098aa 100644 --- a/Samenvattingen/Les18-Samenvatting.md +++ b/Samenvattingen/Les18-Samenvatting.md @@ -1,308 +1,90 @@ -# Les 18: Deployment & Production + Eindopdracht +# Les 18: Advanced AI Toolbox β€” Voice, Vision, Image, Local, Edge + +> βœ… **Deze les is volledig uitgewerkt β€” laatste les** + +## Lesmateriaal + +- [Slide Overzicht](../Les18-AI-Toolbox-Next/Les18-Slide-Overzicht.md) +- [Docenttekst](../Les18-AI-Toolbox-Next/Les18-Docenttekst.md) +- [Lesstof](../Les18-AI-Toolbox-Next/Les18-Lesstof.md) +- [Lesopdracht](../Les18-AI-Toolbox-Next/Les18-Lesopdracht.md) +- [Huiswerk](../Les18-AI-Toolbox-Next/Les18-Huiswerk.md) --- -## Hoofdstuk -**Deel 4: Advanced AI & Deployment** (Les 13-18) +## Deel +**Deel 4 β€” Production & Advanced** (Les 17-18) ## Beschrijving -Deploy je eindproject naar productie. Leer environment variables, Vercel deployment, en basis performance optimalisatie. Bespreking van de eindopdracht requirements en afrondende werksessie. +Laatste les. Vijf korte demo's van alle modaliteiten die we nog NIET hebben gezien: voice (Whisper + TTS), vision (GPT-4o), image generation (Flux), local LLMs (Ollama) en edge AI (Vercel AI Gateway). Wrap-up van alle 18 lessen + vooruitblik eindopdracht. --- ## Te Behandelen -### Groepsdiscussie (15 min) -Bespreek klassikaal de AI feature ervaringen uit Les 17 - welke system prompts werkten goed en welke challenges kwamen jullie tegen? +### Theorie +- Het AI-landschap eind 2026 (providers + modi) +- Per feature: welke modaliteit past? +- Open-source vs. cloud β€” privacy, cost, latency +- Resources & community om bij te blijven -### Vercel Deployment Flow - -**Hoe Vercel werkt:** -1. Connect GitHub repository -2. Push naar main β†’ automatische deploy -3. Push naar andere branch β†’ preview URL -4. Alles automatisch (build, SSL, CDN) - -**Waarom Vercel voor Next.js:** -- Gemaakt door dezelfde makers -- Zero-config deployment -- Gratis tier ruim voldoende -- Automatische HTTPS -- Global CDN - ---- - -### Environment Variables in Vercel - -**Lokaal (.env.local) vs Productie (Vercel Dashboard):** - -| Waar | Bestand/Locatie | Voorbeeld | -|------|-----------------|-----------| -| Lokaal | `.env.local` | Development | -| Vercel | Dashboard β†’ Settings β†’ Env Vars | Production | - -**Stap voor stap:** -1. Ga naar je Vercel project -2. Settings β†’ Environment Variables -3. Voeg toe: - - `NEXT_PUBLIC_SUPABASE_URL` - - `NEXT_PUBLIC_SUPABASE_ANON_KEY` - - `OPENAI_API_KEY` -4. Selecteer environment (Production, Preview, Development) -5. Save & Redeploy - -**Let op:** -- `NEXT_PUBLIC_` prefix = zichtbaar in browser -- Zonder prefix = alleen server-side (API routes) - ---- - -### Supabase Productie Setup - -**Redirect URLs configureren:** -1. Ga naar Supabase β†’ Authentication β†’ URL Configuration -2. Voeg toe bij "Redirect URLs": - - `https://jouw-app.vercel.app/**` - - `https://jouw-app.vercel.app` - -**Waarom:** -- Auth redirects werken alleen naar toegestane URLs -- Wildcards (`**`) voor sub-routes - ---- - -### Deployment Checklist - -**Voor deployment:** -- [ ] `npm run build` werkt lokaal -- [ ] Geen TypeScript errors -- [ ] Environment variables gedocumenteerd in `.env.example` -- [ ] `.gitignore` bevat `.env*.local` -- [ ] README up-to-date - -**Na deployment:** -- [ ] App laadt correct -- [ ] Auth werkt (login/logout) -- [ ] Data wordt opgehaald uit Supabase -- [ ] AI features werken (indien van toepassing) -- [ ] Geen console errors - ---- - -### Performance Basics - -**Lighthouse Audit:** -1. Open Chrome DevTools -2. Tab "Lighthouse" -3. Klik "Analyze page load" -4. Aim for score >80 in elke categorie - -**Quick wins:** -- Gebruik Next.js `Image` component (niet ``) -- Lazy load components die niet direct nodig zijn -- Verwijder ongebruikte dependencies - ---- - -### Security Checklist - -**Belangrijk voor productie:** -- [ ] API keys nooit in client-side code -- [ ] Supabase RLS enabled (Row Level Security) -- [ ] Error messages geven geen gevoelige info -- [ ] HTTPS (automatisch via Vercel) - ---- - -## Eindopdracht Overview - -### Wat is de eindopdracht? -- Bouw een AI-powered web applicatie -- Gebruik alle tools die je hebt geleerd -- Vrije keuze in implementatie - -**Drie voorbeeldprojecten:** -1. **AI Recipe Generator** - Recepten op basis van ingrediΓ«nten -2. **Smart Budget Buddy** - Financieel overzicht met AI insights -3. **Travel Planner AI** - Reisplanning met AI suggesties - -**Of:** Eigen idee (met goedkeuring docent) - ---- - -### Technische Vereisten - -**Stack:** -- Next.js 14 met App Router -- TypeScript -- Tailwind CSS -- Supabase (database + auth) -- Vercel AI SDK (chat of completion feature) -- Deployed op Vercel - -**Documentatie vereisten:** -- `docs/PROJECT-BRIEF.md` - Project beschrijving -- `docs/PROMPT-LOG.md` - Minimaal 10 prompts -- `docs/AI-DECISIONS.md` - Minimaal 5 beslissingen -- Complete README met setup instructies - ---- - -### Beoordelingscriteria - -| Criterium | Weging | -|-----------|--------| -| Technische uitvoering | 40% | -| AI integratie | 25% | -| Documentatie (PROMPT-LOG, AI-DECISIONS) | 20% | -| Code kwaliteit & organisatie | 15% | - -**Minimale vereisten:** -- App werkt in productie -- Auth + CRUD werkt -- AI feature werkt -- Documentatie compleet - ---- - -### Veelvoorkomende Problemen - -**AI Chat Werkt Niet:** -1. API key correct in `.env.local`? -2. API key ook in Vercel env vars? -3. Correct model gekozen? -4. API route syntax correct? - -**Auth Werkt Niet in Productie:** -1. Redirect URLs in Supabase toegevoegd? -2. Beide URLs: met en zonder trailing slash? -3. Wildcard (`**`) voor sub-routes? - -**Deployment Faalt:** -1. `npm run build` lokaal succesvol? -2. Alle TypeScript errors gefixed? -3. Alle env vars in Vercel? +### Live demo's +1. **Voice** β€” Whisper transcribe + TTS via ElevenLabs +2. **Vision** β€” GPT-4o foto-analyse (defect detection, OCR, receipts) +3. **Image gen** β€” Flux via Fal.ai (festival-poster prompt) +4. **Local LLM** β€” Ollama + llama4:8b offline +5. **Edge AI** β€” Vercel AI Gateway met automatic fallback --- ## Tools -- Vercel -- GitHub -- Supabase -- Chrome DevTools (Lighthouse) -- Cursor +- OpenAI Whisper + GPT-4o + DALL-E +- ElevenLabs TTS +- Fal.ai (Flux image generation) +- Ollama + llama4 (lokaal) +- Vercel AI Gateway +- Cloudflare Workers AI --- -## Lesopdracht (2 uur) +## Lesopdracht (in-class, 30 min) -### Deploy Je Eindproject +Studenten kijken klassikaal mee tijdens de 5 demo's. In-class oefening: +- Kies één modaliteit die je het interessantst vindt +- Schets in 5 zinnen een feature voor je eindopdracht die deze modaliteit gebruikt -**Deel 1: Pre-Deployment Check (30 min)** - -Run door de checklist: -1. `npm run build` - fix eventuele errors -2. Check TypeScript errors -3. Maak/update `.env.example` -4. Update README met project info - -**Deel 2: Vercel Deployment (30 min)** - -1. Ga naar [vercel.com](https://vercel.com) -2. "Add New Project" -3. Import je GitHub repository -4. Voeg environment variables toe -5. Deploy! - -**Deel 3: Supabase Config (20 min)** - -1. Voeg Vercel URL toe aan Supabase redirect URLs -2. Test auth in productie -3. Verifieer data access - -**Deel 4: Testing & Lighthouse (40 min)** - -1. Test alle features in productie: - - Login/logout - - CRUD operaties - - AI chat (indien aanwezig) -2. Run Lighthouse audit -3. Fix issues tot score >80 -4. Screenshot resultaat - -### Deliverable -- Werkende productie URL -- Lighthouse screenshot (score >80) -- Test rapport: wat werkt, wat niet +**Inleveren:** Schets via Teams chat. --- -## Huiswerk (Eindopdracht Afronden) +## Huiswerk (take-home) -### Rond Je Eindproject Af +Bouw één gekozen modaliteit naar productie: +- Werkend op Vercel +- Cost-monitoring actief (Les 17) +- README + 200-woord reflectie: cost, latency, privacy -**Dit is de laatste les. Gebruik deze tijd om je eindopdracht af te ronden.** +**Inleveren:** GitHub + Vercel URL via Teams + Brightspace. -**Deel 1: Bug Fixes (1 uur)** +--- -1. Test je app grondig in productie -2. Fix gevonden bugs -3. Test edge cases: - - Lege states - - Error states - - Loading states -4. Redeploy - -**Deel 2: Performance Optimization (30 min)** - -1. Run Lighthouse opnieuw -2. Fix top 3 performance issues -3. Document wat je hebt verbeterd - -**Deel 3: Documentatie Afronden (30 min)** - -Update je `README.md`: -- Werkende productie URL -- Features lijst -- Tech stack -- Setup instructies -- Screenshots - -Complete `docs/`: -- PROMPT-LOG.md (minimaal 10 entries) -- AI-DECISIONS.md (minimaal 5 entries) - -**Checklist voor inleveren:** -- [ ] App werkt op productie URL -- [ ] Login/logout werkt -- [ ] CRUD operaties werken -- [ ] AI feature werkt -- [ ] PROMPT-LOG.md heeft 10+ entries -- [ ] AI-DECISIONS.md heeft 5+ entries -- [ ] README is compleet -- [ ] Code is opgeruimd - -**Tips:** -- Test alles als "nieuwe gebruiker" -- Vraag iemand anders om te testen -- Check of alles in productie werkt (niet alleen lokaal) - -### Deliverable -- Complete eindopdracht -- Deployed en werkend -- Alle documentatie compleet -- Ingeleverd voor deadline +## Eindopdracht (volgt na les) +De eindopdracht doen studenten thuis. Eisen blijven gelden uit het curriculum-overzicht: +- Next.js + TypeScript + Tailwind +- Supabase met auth en RLS +- AI SDK met Tool Calling +- Externe API +- Deployed op Vercel +- Pitch (8-12 min) na inleveren --- ## Leerdoelen Na deze les kan de student: -- Een Next.js app deployen naar Vercel -- Environment variables configureren in Vercel -- Supabase redirect URLs instellen voor productie -- Een deployment checklist doorlopen -- Lighthouse gebruiken voor performance audit -- Basis security checks uitvoeren -- De eindopdracht requirements begrijpen -- Een project afronden en inleveren -- Documentatie voltooien volgens de vereisten +- Het AI-landschap eind 2026 schetsen (providers + modaliteiten) +- Per feature de juiste modaliteit kiezen +- Een voice-feature (Whisper + TTS) implementeren +- Een vision-feature met GPT-4o bouwen +- Een lokaal model draaien via Ollama +- Vercel AI Gateway gebruiken voor automatic fallback +- Bepalen welke modaliteit past bij privacy- en cost-eisen diff --git a/readme.md b/readme.md index 9d7bc8a..55f7c84 100644 --- a/readme.md +++ b/readme.md @@ -1,797 +1,103 @@ -# AI Development Curriculum - NOVI Hogeschool +# AI Developer β€” Leerlijnbeschrijving -Een 18-weekse cursus die studenten meeneemt van AI-beginner naar AI-powered developer. - -**Totaal: 30 EC** verdeeld over 4 delen. +**Opleiding:** NOVI Hogeschool Utrecht +**Vak:** AI-Assisted Development (18 lessen Γ— 3 uur fysiek) +**Docent:** Tim Rijkse +**Schooljaar:** 2025–2026 --- -## Versies +## Doel van de leerlijn -| Versie | Status | Beschrijving | -|--------|--------|--------------| -| **v1** | Gegeven | Originele lessen (Les01, Les02 mappen) | -| **[v2](v2/)** | In ontwikkeling | Verbeterde lessen op basis van feedback | +Studenten gaan in 18 lessen van "ik heb wel eens ChatGPT gebruikt" naar "ik kan een productie-klare AI-app bouwen". De leerlijn is opgebouwd in vier delen die in moeilijkheid en abstractie opbouwen. -**Feedback & Reflectie:** [v1-feedback.md](v1-feedback.md) - Bevindingen na het geven van les 1-3 +Aan het einde: +- Kan de student een complete Next.js + Supabase-app from scratch opzetten. +- Beheerst de student de Vercel AI SDK voor chat, tool-calling, agents en RAG. +- Weet de student wat er nodig is om een AI-app naar productie te brengen β€” observability, evals, security, cost. +- Heeft de student feeling met de bredere AI-toolbox: voice, vision, image gen, local LLMs, edge AI. --- -## Lesformat (Nieuw!) +## Opbouw β€” vier delen -**Feedback van studenten:** Het traditionele "1 uur lecture + 2 uur solo werk" werkt niet optimaal. Studenten voelen zich verloren en geven voorkeur aan samenwerken. +### Deel 1 β€” AI Foundations (Les 1–3) +Kennismaking met AI, modellen, prompt engineering en de eerste AI-coding tools (ChatGPT, OpenCode, Cursor). -**Nieuw format (toegepast op alle lessen):** -- **~45 minuten:** Theorie met live demo's (docent codeert en verklaart) -- **15 minuten:** Pauze -- **~120 minuten:** Klassikaal bouwen (Tim codeert voor, studenten volgen mee met regelmatige "nu jullie" momenten waar zij zelf code schrijven) +### Deel 2 β€” Technical Foundations (Les 4–10) +TypeScript fundamentals, Next.js basics, QuickPoll als rode draad, en Supabase + Auth + Row Level Security tot productie-niveau. -Dit format betrekent meer interactie, sneller feedback en meer mogelijkheden voor sparring en samenwerken. +### Deel 3 β€” AI Development (Les 11–16) +Vercel AI SDK, Tool Calling, Cursor + Vercel deploy, Agents, RAG met embeddings, en Model Context Protocol (MCP). + +### Deel 4 β€” Production & Advanced (Les 17–18) +Observability, evals, security, cost (Les 17). Voice, vision, image gen, local LLMs en edge AI (Les 18). --- -## Overzicht +## Lessenoverzicht -> **Let op:** Curriculum v2 - geherstructureerd na teaching experience. Old Les 5 (TypeScript voor React/Next.js) is uitgewerkt in de twee Next.js lessen. Old Les 6-8 zijn samengevat in nieuwe Les 5-6. Old Les 9-16 verschuiven naar Les 7-14. Old Les 17 (1 werkdag) is uitgebreid naar 3 werkdagen (Les 15-17). +| Nr | Deel | Onderwerp | Map | +|----|------|-----------|-----| +| 1 | 1 | Introductie AI & LLMs | `Les01-Introductie-AI/` | +| 2 | 1 | AI Code Assistants & OpenCode | `Les02-OpenCode/` | +| 3 | 1 | Cursor Setup & Basics | `Les03-Cursor-Basics/` | +| 4 | 2 | TypeScript Fundamentals | `Les04-TypeScript-Fundamentals/` | +| 5 | 2 | Next.js Basics (QuickPoll part 1) | `Les05-NextJS-Basics/` | +| 6 | 2 | Next.js QuickPoll vervolg (part 2) | `Les06-NextJS-QuickPoll-Part2/` | +| 7 | 2 | Supabase introductie | `Les07-Supabase/` | +| 8 | 2 | Van In-Memory naar Supabase | `Les08-Van-InMemory-naar-Supabase/` | +| 9 | 2 | Supabase Auth β€” eerste login flow | `Les09-Supabase-Auth/` | +| 10 | 2 | Supabase Auth & RLS verdieping | `Les10-Supabase-Auth/` | +| 11 | 3 | Vercel AI SDK β€” praat met je data | `Les11-AI-SDK/` | +| 12 | 3 | Tool Calling | `Les12-Tool-Calling/` | +| 13 | 3 | Externe APIs + Cursor + Vercel | `Les13-Cursor-Vercel-Deploy/` | +| 14 | 3 | Agents | `Les14-Agents/` | +| 15 | 3 | RAG + Embeddings | `Les15-RAG-Embeddings/` | +| 16 | 3 | Model Context Protocol | `Les16-MCP/` | +| 17 | 4 | AI Production Polish | `Les17-Production-Polish/` | +| 18 | 4 | Advanced AI Toolbox + wrap-up | `Les18-AI-Toolbox-Next/` | -| Les | Onderwerp | Deel | Status | -|-----|-----------|------|--------| -| 01 | [Introductie tot AI en Large Language Models](Samenvattingen/Les01-Samenvatting.md) | 1 | βœ… Gegeven | -| 02 | [AI Code Assistants (OpenCode β†’ Cursor)](Samenvattingen/Les02-Samenvatting.md) | 1 | βœ… Gegeven | -| 03 | [Cursor Setup & Basics](Samenvattingen/Les03-Samenvatting.md) | 1 | βœ… Gegeven | -| 04 | [TypeScript Fundamentals](Samenvattingen/Les04-Samenvatting.md) | 2 | βœ… Gegeven | -| 05 | [Next.js β€” Het React Framework (Part 1)](Samenvattingen/Les05-Samenvatting.md) | 2 | βœ… Gegeven (v1) | -| 06 | [Next.js β€” QuickPoll Vervolg (Part 2)](Samenvattingen/Les06-Samenvatting.md) | 2 | πŸ“‹ Gepland | -| 07 | [Van In-Memory naar Supabase (Introductie)](Samenvattingen/Les07-Samenvatting.md) | 2 | βœ… Gegeven | -| 08 | [Van In-Memory naar Supabase (Self-Paced + /create)](Samenvattingen/Les08-Samenvatting.md) | 2 | πŸ“‹ Gepland | -| 09 | [Cursor Deep Dive (Student Plan)](Samenvattingen/Les09-Samenvatting.md) | 2 | πŸ“‹ Gepland | -| 10 | [Styling: Tailwind CSS & shadcn/ui](Samenvattingen/Les10-Samenvatting.md) | 3 | πŸ“‹ Samenvatting | -| 11 | [Van Idee naar Prototype](Samenvattingen/Les11-Samenvatting.md) | 3 | πŸ“‹ Samenvatting | -| 12 | [Project Setup & AI Config (.cursorrules, claude.md)](Samenvattingen/Les12-Samenvatting.md) | 3 | πŸ“‹ Samenvatting | -| 13 | [Vercel AI SDK, Tool Calling & Agents](Samenvattingen/Les13-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | -| 14 | [AI Chat Interface & Streaming](Samenvattingen/Les14-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | -| 15 | [Eindproject Werkdag 1](Samenvattingen/Les15-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | -| 16 | [Eindproject Werkdag 2](Samenvattingen/Les16-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | -| 17 | [Eindproject Polish & Code Review](Samenvattingen/Les17-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | -| 18 | [Deployment, Presentaties & Evaluatie](Samenvattingen/Les18-Samenvatting.md) | 4 | πŸ“‹ Samenvatting | +Per les zit altijd dezelfde set bestanden in de map: +- `LesXX-Slide-overzicht.md` β€” overzicht van de slides +- `LesXX-Huiswerk.pdf` β€” huiswerk in NOVI-huisstijl +- `LesXX-Docenttekst.md` β€” voorleesbaar lesscript +- `LesXX-Lesstof.md` β€” lesstof / theorie +- `LesXX-Slides.pptx` β€” slidedeck +- (waar van toepassing) `LesXX-Lesopdracht.md/.pdf` en starterprojecten --- -## Tool Progressie +## Eindopdracht -| Deel | Lessen | Tools | Kosten | -|------|--------|-------|--------| -| Deel 1: AI Foundations | 1-3 | ChatGPT, v0.dev, OpenCode, **Cursor** | Gratis (Cursor Student Plan) | -| Deel 2: Technical Foundations | 4-9 | TypeScript, Next.js, Supabase, Supabase Auth | Gratis | -| Deel 3: Full-Stack Development | 9-12 | Next.js, Supabase, Tailwind, **shadcn/ui**, Cursor | Gratis | -| Deel 4: Advanced AI & Deployment | 13-18 | Cursor, Vercel AI SDK, Vercel | Gratis (Cursor Student Plan) | - -**Eindopdracht:** Cursor (Student Plan - gratis Pro voor 1 jaar) +Studenten bouwen zelfstandig een eigen AI-app die voldoet aan alle leerdoelen van de leerlijn. Zie [`Eindopdracht-AI-Developer.md`](./Eindopdracht-AI-Developer.md) voor de volledige eisen, beoordelingscriteria en deadlines. De eindopdracht is **niet** een in-class lesonderdeel; studenten werken er thuis aan. --- -# Deel 1: AI Foundations +## Aanvullende documenten -**3 lessen Β· 5 EC** - -Kennismaking met AI, LLMs en de basis van AI-assisted development met Cursor. +- [`OPLEVERING.md`](./OPLEVERING.md) / [`OPLEVERING.pdf`](./OPLEVERING.pdf) β€” mapping van leerlijn naar de contract-deliverables (Art. 1). +- [`Curriculum-Overzicht-Compleet.pdf`](./Curriculum-Overzicht-Compleet.pdf) β€” alle 18 lessen in één visueel deck in NOVI-huisstijl. +- [`Curriculum-Lesboek.pdf`](./Curriculum-Lesboek.pdf) β€” tekstuele versie van het curriculum, leesbaar als kort lesboek. +- [`Samenvattingen/`](./Samenvattingen) β€” per-les samenvattingen. --- -### Les 1: Introductie tot AI en Large Language Models +## Stack en tooling -**Tools:** ChatGPT, v0.dev, Vercel, GitHub - -**Docent vertelt:** -- Het AI-landschap anno nu (ChatGPT, Claude, Grok, Gemini) -- Wat zijn Large Language Models? Next-token prediction uitgelegd -- Hallucinaties: waarom AI soms dingen verzint -- **AI Modellen & Kosten:** Gratis opties (ChatGPT Free, Claude Free, Groq, Ollama) vs Betaald ($20/maand) vs Premium (Claude Opus) -- **Privacy:** Ollama voor lokaal draaien (data verlaat je computer niet) -- **ChatGPT Projects:** Werkruimtes met eigen instructies en geheugen -- Prompt engineering basics: rol, context, taak, format -- De schets-naar-website workflow - -**Studenten doen:** -- ChatGPT account aanmaken en verkennen -- Custom Instructions instellen + Project aanmaken voor v0.dev -- Complete workflow: schets op papier β†’ foto β†’ ChatGPT Project β†’ v0.dev β†’ deploy naar Vercel - -**Lesopdracht:** Doorloop de complete workflow: teken een hero section op papier, maak foto, genereer prompt met ChatGPT, bouw in v0.dev, deploy naar Vercel. - -**Huiswerk:** Reflectie schrijven (500 woorden), 5 AI use cases bedenken voor eigen projecten, tweede component bouwen via de workflow. - -**Lesmateriaal:** -- [Slide-Overzicht](Les01-Introductie-AI/Les01-Slide-Overzicht.md) -- [Lesplan](Les01-Introductie-AI/Les01-Lesplan.md) -- [Docenttekst](Les01-Introductie-AI/Les01-Docenttekst.md) -- [Keynote Notes](Les01-Introductie-AI/Les01-Docenttekst-Notes.md) -- [Lesopdracht](Les01-Introductie-AI/Les01-Bijlage-A-Lesopdracht.md) -- [Huiswerkopdracht](Les01-Introductie-AI/Les01-Bijlage-B-Huiswerkopdracht.md) - -[β†’ Ga naar Les 1 Samenvatting](Samenvattingen/Les01-Samenvatting.md) +| Onderdeel | Keuze | +|-----------|-------| +| Frontend | Next.js 16 (App Router) + TypeScript + Tailwind CSS + Shadcn | +| Database | Supabase (Postgres + Auth + RLS + pgvector) | +| AI SDK | Vercel AI SDK v6 | +| AI-coding tools | ChatGPT, OpenCode, Cursor (Composer + Background Agent) | +| Hosting | Vercel + GitHub Actions CI | +| Productie-stack (Les 17) | Langfuse, Vitest evals, prompt-shield, model-routing | +| Advanced toolbox (Les 18) | Whisper, GPT-4o vision, Flux via Fal.ai, Ollama, Vercel AI Gateway | --- -### Les 2: AI Code Assistants (OpenCode β†’ Cursor) +## Contact -**Tools:** OpenCode Desktop App, Cursor (Preview), Git, Vercel - -**Docent vertelt:** -- Recap: OpenCode workflow uit Les 1 huiswerk -- Introduceer Cursor: professionele AI editor -- Cursor vs OpenCode: krachten en zwakten -- Installatie en configuratie -- Keyboard shortcuts: `Tab` (autocomplete), `Ctrl+K` (inline edit), `Ctrl+L` (chat) -- De deployment workflow: git push β†’ Vercel auto-deploy - -**Studenten doen:** -- OpenCode Desktop App gebruiken voor continue integratie -- Cursor installeren en eerste keer verkennen -- Bestaand v0 project openen in Cursor -- Met Cursor een nieuwe pagina maken + link op homepage -- Deployen via git push β†’ Vercel - -**Lesopdracht:** Maak een nieuwe pagina in je v0 project. Clone je repo, draai lokaal, maak een pagina (/about, /services, of /contact) met componenten, voeg link op homepage toe, deploy minimaal 2x naar Vercel. - -**Huiswerk:** -- Maak nieuw project vanaf scratch met `create-next-app` -- Bouw 3 componenten met Cursor (navbar, hero, + keuze) -- Push naar GitHub, koppel aan Vercel -- Schrijf reflectie (300 woorden) - -**Lesmateriaal:** -- [Slide-Overzicht](Les02-OpenCode/Les02-Slide-Overzicht.md) -- [Lesplan](Les02-OpenCode/Les02-Lesplan.md) -- [Docenttekst](Les02-OpenCode/Les02-Docenttekst.md) -- [Keynote Notes](Les02-OpenCode/Les02-Docenttekst-Notes.md) -- [Lesopdracht](Les02-OpenCode/Les02-Bijlage-A-Lesopdracht.md) -- [Huiswerkopdracht](Les02-OpenCode/Les02-Bijlage-B-Huiswerkopdracht.md) - -[β†’ Ga naar Les 2 Samenvatting](Samenvattingen/Les02-Samenvatting.md) - ---- - -### Les 3: Cursor Setup & Basics - -**Tools:** Cursor (Student Plan), Git, GitHub, Next.js, TypeScript, Tailwind CSS - -**Docent vertelt:** -- Cursor overview: VS Code fork met AI, tab completion, inline edits, chat, composer -- Free vs Pro vs Student Plan (500 fast requests/maand) -- Request types en kosten: Tab = gratis, Chat/Composer/Inline Edit = 1 request -- Cursor Skills & Docs: framework documentatie toevoegen via Settings β†’ Features β†’ Docs -- .cursorrules genereren via Chat (1 request, goed besteed) -- Keyboard shortcuts: `Tab`, `Ctrl+K`, `Ctrl+L`, `Ctrl+I` - -**Studenten doen:** -- Terminal setup check (node, git, npm, npx) -- Cursor Student Plan activeren -- Nieuw project: git init β†’ npx create-next-app@latest -- Skills/Docs configureren (Next.js, React, Tailwind) -- .cursorrules genereren via Chat -- Hands-on (75 min): Hero component bouwen, styling tweaken, extra feature toevoegen -- Git commit - -**Lesopdracht:** Bouw een nieuw Next.js project met Cursor: scaffold, configureer, bouw minimaal 2 componenten, commit. - -**Huiswerk:** Debug Challenge β€” intentioneel kapot project fixen met Cursor. Drie niveaus: standaard (5 fouten), hard (12 fouten), super hard (18 fouten). - -**Lesmateriaal:** -- [Slide-Overzicht](Les03-Cursor-Basics/Les03-Slide-Overzicht.md) -- [Lesplan](Les03-Cursor-Basics/Les03-Lesplan.md) -- [Docenttekst](Les03-Cursor-Basics/Les03-Docenttekst.md) -- [Keynote Notes](Les03-Cursor-Basics/Les03-Docenttekst-Notes.md) -- [Lesopdracht](Les03-Cursor-Basics/Les03-Bijlage-A-Lesopdracht.md) -- [Huiswerkopdracht](Les03-Cursor-Basics/Les03-Bijlage-B-Huiswerkopdracht.md) - -[β†’ Ga naar Les 3 Samenvatting](Samenvattingen/Les03-Samenvatting.md) - ---- - -### Les 4: TypeScript Fundamentals - -**Tools:** Cursor (Student Plan), TypeScript, Node.js - -**Docent vertelt:** -- Waarom TypeScript? Het probleem met JavaScript runtime errors -- Live demo: JS vs TS in Cursor β€” zelfde code, maar TS vangt fouten vooraf -- Basic types: string, number, boolean, arrays -- Type inference: wanneer TypeScript types zelf raadt -- Interfaces en type aliases -- Union types en literal types -- Functies typen: parameters en return types -- Veelvoorkomende TypeScript errors lezen en oplossen - -**Studenten doen:** -- TypeScript Escaperoom: 8 kamers met type-puzzels van makkelijk naar moeilijk -- Elke kamer leert een concept (basics β†’ inference β†’ interfaces β†’ unions β†’ functies) -- Escape code ontgrendelen door alle type-errors te fixen - -**Lesopdracht:** TypeScript Escaperoom β€” los 8 kamers met TypeScript puzzels op. Gebruik Cursor's autocomplete en error fixing. - -**Huiswerk:** JS β†’ TypeScript Converter β€” zet een werkend JavaScript project (users, products, orders, utils) volledig om naar TypeScript. Tests (al in TS) moeten slagen. - -**Lesmateriaal:** -- [Slide-Overzicht](Les04-TypeScript-Fundamentals/Les04-Slide-Overzicht.md) -- [Lesplan](Les04-TypeScript-Fundamentals/Les04-Lesplan.md) -- [Docenttekst](Les04-TypeScript-Fundamentals/Les04-Docenttekst.md) -- [Lesopdracht](Les04-TypeScript-Fundamentals/Les04-Bijlage-A-Lesopdracht.md) -- [Huiswerkopdracht](Les04-TypeScript-Fundamentals/Les04-Bijlage-B-Huiswerkopdracht.md) - -[β†’ Ga naar Les 4 Samenvatting](Samenvattingen/Les04-Samenvatting.md) - ---- - -# Deel 2: Technical Foundations - -**6 lessen Β· 10 EC** - -Stevige technische basis: TypeScript, Next.js, Supabase database en Cursor deep dive. - -> **Curriculum-shift:** Deel 2 telt nu 6 lessen i.p.v. 5. Tijdens het geven van Les 7 bleek dat studenten meer tijd nodig hadden voor het Supabase-traject. Daarom is Les 8 (Self-paced redo + /create) toegevoegd. Les 9 is een Cursor Deep Dive nu studenten de Student Plan hebben. **Supabase Auth schuift naar Les 10. Les 10-18 schuiven hierdoor één plek op en moeten nog opnieuw gepland worden.** - ---- - -### Les 5: Next.js β€” Het React Framework (Part 1) - -**Tools:** Next.js 15, Cursor, TypeScript, Tailwind CSS - -**Docent vertelt (~45 min, demo-driven):** -- Waarom Next.js? Het probleem met pure React -- Create-next-app en project structuur -- App Router: file-based routing, page.tsx, layout.tsx -- Dynamic routes ([id]) met TypeScript -- Server Components vs Client Components ("use client") -- Data Fetching in Server Components (async components) -- Server Actions introductie -- Route Groups en best practices - -**Samen bouwen (~120 min, klassikaal):** -- QuickPoll app Part 1 (Stap 0-3 uit lesopdracht) -- Tim + klas: create-next-app, types definiΓ«ren, folder structuur -- Klassikaal: layout met navigatie, homepage met polls list -- Klassikaal: API route GET single poll - -**Lesopdracht:** QuickPoll app Part 1 (Stap 0-3 uit lesopdracht) - stap-voor-stap handleiding - -**Huiswerk:** Stap 0-3 zelfstandig afmaken als je het niet af hebt - -**Lesmateriaal:** -- Slide-Overzicht -- Docenttekst -- Les05-Lesopdracht.pdf (stap-voor-stap handleiding) -- les5-quickpoll-starter.zip (scaffolded project) -- les5-quickpoll-voorbeeld.zip (Tim's referentie) - -[β†’ Ga naar Les 5 Samenvatting](Samenvattingen/Les05-Samenvatting.md) - ---- - -### Les 6: Next.js β€” QuickPoll Vervolg (Part 2) - -**Tools:** Next.js 15, Cursor, TypeScript, Tailwind CSS - -**Docent vertelt (~30-40 min):** -- Recap Les 5 + Q&A -- API Route Handlers: GET, POST met NextResponse (dieper) -- Middleware: request intercepten, logging, rate limiting -- Environment Variables (.env.local, NEXT_PUBLIC_) -- Loading, Error, Not-Found special files -- next/image, next/link, Metadata -- Deployment op Vercel -- Cursor/AI workflow (.cursorrules, Cmd+K, Cmd+L) - -**Samen bouwen (~120 min, klassikaal):** -- QuickPoll app Part 2 (Stap 4-7 uit lesopdracht) -- Klassikaal: API POST vote route -- Klassikaal: Poll detail pagina (server component) -- Klassikaal: VoteForm (client component met fetch) -- Klassikaal: Loading, error, not-found states -- Bonus: Create poll pagina -- Deploy naar Vercel - -**Lesopdracht:** QuickPoll app Part 2 (Stap 4-7 uit lesopdracht) - -**Huiswerk:** App afmaken en deployen op Vercel, bonus features toevoegen - -**Lesmateriaal:** -- Slide-Overzicht -- Docenttekst -- Les06-Lesopdracht.pdf (stap-voor-stap handleiding) -- les6-quickpoll-starter.zip (scaffolded project) -- les6-quickpoll-voorbeeld.zip (Tim's referentie) - -[β†’ Ga naar Les 6 Samenvatting](Samenvattingen/Les06-Samenvatting.md) - ---- - -### Les 7: Van In-Memory naar Supabase (Introductie) - -**Tools:** Next.js 15, Supabase (Table Editor, SQL Editor), `@supabase/supabase-js`, Cursor, TypeScript - -**Docent vertelt (~75 min, klassikaal):** -- Recap Les 6: QuickPoll met in-memory data β€” wat mist nog? -- Server Component + Client Component patroon (kernmoment) -- Wat is Supabase? Open-source Firebase alternatief op Postgres -- Tabellen, primary keys, foreign keys, CASCADE -- RLS policies basics -- SQL: SELECT, WHERE, JOIN -- Environment variables in Next.js (`NEXT_PUBLIC_` prefix) -- Supabase JavaScript client (`from`, `select`, `eq`, `single`) - -**Samen bouwen (~120 min, klassikaal):** -- **Deel 1:** Poll afmaken β€” `votePoll()`, POST route, Server/Client split, percentage bars -- **Deel 2:** Supabase no-code β€” project, polls + options tabellen met foreign key, RLS, testdata, SQL Editor -- **Deel 3:** Supabase koppelen β€” `npm install`, `.env.local`, `lib/supabase.ts`, types, `data.ts` herschrijven, components aanpassen, testen - -**Lesopdracht:** Volg klassikaal mee. Aan het eind draait je QuickPoll app met data uit Supabase. - -**Huiswerk:** /create pagina bouwen (formulier β†’ INSERT). *Bleek voor de meeste studenten te zwaar β€” daarom wordt dit in Les 8 herhaald.* - -**Lesmateriaal:** -- [Slide-Overzicht](Les07-Supabase/Les07-Slide-Overzicht.md) -- [Docenttekst](Les07-Supabase/Les07-Docenttekst.md) -- [Live Coding Guide](Les07-Supabase/Les07-Live-Coding-Guide.md) -- [Lesopdracht (PDF)](Les07-Supabase/Les07-Lesopdracht.pdf) -- [Slides (PPTX)](Les07-Supabase/Les07-Slides.pptx) - -[β†’ Ga naar Les 7 Samenvatting](Samenvattingen/Les07-Samenvatting.md) - ---- - -### Les 8: Van In-Memory naar Supabase (Self-Paced + /create) - -**Tools:** Next.js 15, Supabase (SQL Editor, RLS), `@supabase/supabase-js`, Cursor, TypeScript - -**Achtergrond:** Studenten kregen vorige les niet door Deel 3 heen. Deze les loopt de docent de Supabase-koppeling rustig opnieuw door β€” nu via een **self-paced PDF** met copy-paste blokken en TODO-blokken. Daarna bouwen studenten zelfstandig de **/create pagina** (hun eerste INSERT). - -**Aanpak:** Deel 1-3 klassikaal door de PDF. Deel 4 (/create pagina) zelfstandig met docent als coach. - -**Tijdsindeling:** -- 09:00–09:15 Welkom + PDF uitdelen -- 09:15–09:45 **PDF Deel 1** β€” Setup (npm, .env, supabase.ts, types, `vote_option` SQL functie) -- 09:45–10:00 **PDF Deel 2** β€” Queries (`getPolls`, `getPollById`, `votePoll` met TODO's) -- 10:00–10:15 **PDF Deel 3** β€” Componenten (page, PollItem, VoteForm, [id]) -- 10:15–10:30 Pauze -- 10:30–10:45 Uitleg INSERT theorie + RLS policy -- 10:45–11:30 **ZELFSTANDIG: PDF Deel 4** β€” /create pagina -- 11:30–12:00 Vragen + Huiswerk + Vooruitblik Auth - -**Belangrijke leermomenten:** -- Self-paced PDF werkt beter dan live coding voor deze klas -- Next.js 15: `params` is nu een `Promise<{ id: string }>` β†’ `const { id } = await params` -- Supabase RPC: `vote_option` PostgreSQL function aanmaken (anders PGRST202 error) -- RLS INSERT policy nodig vΓ³Γ³r `.insert()` (anders "row-level security violation") - -**Lesopdracht:** PDF met 4 delen β€” grijze blokken zijn copy-paste, gele TODO-blokken vullen studenten zelf in. - -**Huiswerk:** /create pagina afmaken, validatie toevoegen (vraag niet leeg, min 2 opties). Extra: delete functie, SQL queries, realtime, styling. - -**Lesmateriaal:** -- [Slide-Overzicht](Les08-Van-InMemory-naar-Supabase/Les08-Slide-Overzicht.md) -- [Docenttekst](Les08-Van-InMemory-naar-Supabase/Les08-Docenttekst.md) -- [Lesopdracht (PDF)](Les08-Van-InMemory-naar-Supabase/Les08-Lesopdracht.pdf) β€” self-paced handleiding -- [Slides (PPTX)](Les08-Van-InMemory-naar-Supabase/Les08-Slides.pptx) - -[β†’ Ga naar Les 8 Samenvatting](Samenvattingen/Les08-Samenvatting.md) - ---- - -### Les 9: Cursor Deep Dive (Student Plan) - -**Tools:** Cursor (Student Plan), Next.js 16, TypeScript, Tailwind CSS - -**Context:** Studenten hebben Cursor met het Student Plan (500 fast requests/maand). Tijd voor een deep dive in 5 professionele workflows die elke developer nodig heeft bij het werken met coding agents. - -**We bouwen een nieuwe app from scratch: LinkVault** β€” een bookmark manager. In-memory data, geen Supabase. - -**Docent vertelt (~25 min):** -- Hoe werkt een coding agent? Het Harness concept (instructions + tools + model) -- Rules & Skills: `.cursor/rules/` met 3 `.mdc` bestanden (project, components, styles) -- Context management: nieuw feature = nieuwe chat, @file/@codebase/@docs combineren -- Plan Mode: Shift+Tab β†’ plan β†’ review β†’ edit β†’ Build -- Agent Mode vs Ask Mode: wanneer welke? -- Debug Mode: hypotheses β†’ logging β†’ reproduceren β†’ analyseren β†’ fix -- Code Review & Testing: "Find Issues", tests schrijven, semantic commits - -**Samen bouwen (~120 min, klassikaal):** -1. Setup: `npx create-next-app@latest` + 3 `.cursor/rules` bestanden -2. Plan Mode: plan LinkVault β†’ review β†’ Build -3. Agent Mode: features bouwen (edit bookmark, tag filtering) -4. Debug Mode: deterministische bug (handmatig geΓ―ntroduceerd) vinden en fixen -5. Code Review: "Find Issues" + unit tests + semantic commits + git push -6. (Optioneel) PR review met Cursor - -**Lesopdracht:** Bouw LinkVault mee met alle 5 technieken. - -**Huiswerk:** Bouw je eigen app from scratch (recepten, films, notities, of eigen idee) met dezelfde 5 technieken. Reflectie schrijven (200 woorden). Zie `Les09-Huiswerk.md`. - -**Lesmateriaal:** -- [Slide-Overzicht](Les09-Cursor-Deep-Dive/Les09-Slide-Overzicht.md) -- [Docenttekst](Les09-Cursor-Deep-Dive/Les09-Docenttekst.md) -- [Huiswerk](Les09-Cursor-Deep-Dive/Les09-Huiswerk.md) - -[β†’ Ga naar Les 9 Samenvatting](Samenvattingen/Les09-Samenvatting.md) - ---- - -# Deel 3: Full-Stack Development - -**4 lessen Β· 7 EC** *(nog te herplannen β€” Les 10-12 schuiven naar Les 11-13)* - -Integratie van alle geleerde technieken, styling en voorbereiding op eindproject. - -> **Let op:** De onderstaande lessen 9-12 zijn nog op de oude nummering. Na de Les 7/8/9 herschikking schuift dit blok één plek op (Les 10 = Mini Project, Les 11 = Styling, etc.). Bestanden zijn nog niet hernoemd. - ---- - -### Les 9 (oud β†’ wordt Les 10): Full-Stack Mini Project - -**Tools:** Cursor, Supabase, Browser DevTools - -**Docent vertelt:** -- Je hebt nu alle bouwstenen: TypeScript, Next.js, Supabase, Auth -- Vandaag combineren we alles in een werkende mini-app -- Geen nieuwe concepten - alleen integratie en toepassing -- Probleemoplossing in real time - -**Studenten doen:** -- **Groepsdiscussie:** Bespreek auth & CRUD ervaringen uit Les 8 -- Mini-project kiezen: Bookmarks, Todo's, Notes, of Recipe Collection -- Met Cursor alle onderdelen kombinieren: auth, database, API, UI -- Zelfstandig problemen oplossen -- Polish en afronden - -**Lesopdracht:** Bouw een volledige mini-app met: login, CRUD operaties, RLS security. Checkpoints: 30 min setup, 60 min auth, 90 min CRUD, 120 min testing, 150 min polish. - -**Huiswerk:** Verbeterpunten toevoegen, reflectie schrijven (200 woorden), repo opruimen, voorbereiden op styling. - -[β†’ Ga naar Les 9 Samenvatting](Samenvattingen/Les09-Samenvatting.md) - ---- - -### Les 10: Styling: Tailwind CSS & shadcn/ui - -**Tools:** Tailwind CSS, shadcn/ui, Cursor - -**Docent vertelt:** -- Waarom Tailwind? Utility-first CSS approach -- Tailwind setup in Next.js (meestal al aanwezig) -- Basis utilities: flexbox, grid, spacing, colors -- Tailwind components: buttons, cards, forms -- Wat is shadcn/ui? High-quality component library -- shadcn/ui installatie en configuratie -- Theme customization -- Dark mode setup - -**Studenten doen:** -- Tailwind basix oefenen op bestaande components -- shadcn/ui components installeren en gebruiken -- Custom Tailwind themes making -- Bestaande mini-project restylen met Tailwind + shadcn -- Dark mode implementeren - -**Lesopdracht:** -1. Refactor je mini-project UI met Tailwind classes -2. Installeer 5 shadcn/ui components (Button, Card, Input, etc.) -3. Cre je eigen color theme in Tailwind -4. Implementeer dark mode toggle -5. Polish: zorg dat het er professioneel uitziet - -**Huiswerk:** Refactor alle components, add animations met Tailwind, explore shadcn variations, dokumenteer styling decisions. - -[β†’ Ga naar Les 10 Samenvatting](Samenvattingen/Les10-Samenvatting.md) - ---- - -### Les 11: Van Idee naar Prototype - -**Tools:** Cursor, ChatGPT (voor planning) - -**Docent vertelt:** -- Hoe ga je van vaag idee naar concrete features? -- Feature breakdown methode -- Component thinking: UI opdelen in herbruikbare stukken -- MVP (Minimum Viable Product) denken -- User stories schrijven -- EfficiΓ«nt met Cursor samenwerken voor iteratie - -**Studenten doen:** -- **Groepsdiscussie:** Reflectie op styling - waar ben je tevreden mee? -- Kiezen van eindproject idee (of voorgestelde opties) -- Met AI feature breakdown maken -- Component structuur ontwerpen -- Start codebase met folder structure -- Eerste components bouwen - -**Voorbeeld projecten:** -- E-commerce product filter & search -- Recipe planner met shoppinglist -- Expense tracker met analytics -- Blog platform met comments - -**Lesopdracht:** Kies eindproject idee, maak feature breakdown met AI, ontwerp component tree, zet up codebase, bouw eerste 2-3 components. - -**Huiswerk:** Bouw meer components, integreer eerste data, documenteer architecture decisions, voorbereiding Les 12. - -[β†’ Ga naar Les 11 Samenvatting](Samenvattingen/Les11-Samenvatting.md) - ---- - -### Les 12: Project Setup & AI Config (.cursorrules, claude.md) - -**Tools:** Cursor, Git, GitHub - -**Docent vertelt:** -- Waarom goede project structuur cruciaal is voor AI samenwerking -- De ideale folder structuur voor Next.js + AI tools -- `.cursorrules` files: syntax, best practices, examples -- `claude.md`: project documentation voor Claude -- `docs/` folder: AI-DECISIONS.md, ARCHITECTURE.md -- Environment variables: lokaal vs productie setup -- `.gitignore` en `.cursorignore` voor AI tools -- README en documentatie best practices - -**Studenten doen:** -- Eindproject repository aanmaken/opschonen -- Folder structuur optimaliseren -- Uitgebreide `.cursorrules` schrijven -- Project documentatie in claude.md -- docs/ folder vullen -- Eerste commits pushen - -**Lesopdracht:** -1. Review folder structuur van je huidige project -2. Schrijf comprehensive `.cursorrules` (50+ lines) -3. Maak `claude.md` met project overview -4. Vul `docs/AI-DECISIONS.md` in -5. Push naar GitHub met cleane git history - -**Huiswerk:** Polish documentatie, add architecture diagrams, test .cursorrules in Cursor, prepare voor Deel 4. - -[β†’ Ga naar Les 12 Samenvatting](Samenvattingen/Les12-Samenvatting.md) - ---- - -# Deel 4: Advanced AI & Deployment - -**6 lessen Β· 8 EC** - -AI-powered features, streaming, finale afronding en deployment. - ---- - -### Les 13: Vercel AI SDK, Tool Calling & Agents - -**Tools:** Vercel AI SDK, Zod, Externe APIs, Cursor - -**Docent vertelt:** -- Vercel AI SDK basics: useChat, streaming -- Tool Calling: AI laat externe APIs aanroepen -- Agents: AI voert autonome multi-step taken uit -- Zod voor schema validation -- System prompts schrijven -- Error handling en best practices -- Gratis APIs voor projecten - -**Studenten doen:** -- AI SDK + Zod installeren -- Chat interface bouwen met streaming -- Tools definiΓ«ren voor externe API -- Tool calling implementeren -- Simple agent bouwen - -**Lesopdracht:** -1. Installeer Vercel AI SDK en Zod -2. Bouw chat interface met streaming -3. Definieer minimaal 2 tools -4. Implementeer tool calling -5. Test met externe API - -**Huiswerk:** Add more tools, implementeer agents met `maxSteps`, documenteer in AI-DECISIONS.md, integreer in eindproject. - -[β†’ Ga naar Les 13 Samenvatting](Samenvattingen/Les13-Samenvatting.md) - ---- - -### Les 14: AI Chat Interface & Streaming - -**Tools:** Vercel AI SDK, Cursor, Browser APIs - -**Docent vertelt:** -- Streaming responses: hoe werkt het? -- useChat hook: state management -- Chat UI patterns: message history, loading states -- Markdown rendering in chat -- Error states en edge cases -- User input handling en sanitization -- Performance optimalisatie - -**Studenten doen:** -- Chat UI bouwen met useChat -- Streaming responses implementeren -- Message history managen -- Error handling toevoegen -- Styling met Tailwind -- In eindproject integreren - -**Lesopdracht:** -1. Bouw chat interface met Vercel AI SDK -2. Implementeer streaming responses -3. Maak message history persistent (localStorage of DB) -4. Voeg error handling toe -5. Style met Tailwind + shadcn/ui - -**Huiswerk:** Add chat features (clear history, export, favorites), deploy naar Vercel, gather user feedback. - -[β†’ Ga naar Les 14 Samenvatting](Samenvattingen/Les14-Samenvatting.md) - ---- - -### Les 15: Eindproject Werkdag 1 - -**Tools:** Cursor, Vercel, Supabase, alle vorige tools - -**Focus:** Core features bouwen - -**Docent vertelt:** -- Q&A session: wat loop je tegen aan? -- Cursor tips & tricks review -- Debugging strategies -- Performance optimization basics -- Deployment troubleshooting - -**Studenten doen:** -- **Volle dag werken aan eindproject** -- Docent beschikbaar voor individuele hulp en klassikaal sparring -- Core features bouwen -- Testing en bug fixes -- Regelmatig vragen stellen - -**Lesopdracht:** -1. Core features van eindproject afmaken -2. Testing: user flows doorlopen -3. Bug fixes en edge cases -4. Code cleanup -5. Checkpoint: is het werkend? - -**Huiswerk:** Voortgang bijhouden, notities maken voor volgende werkdag, extra features plannen. - -[β†’ Ga naar Les 15 Samenvatting](Samenvattingen/Les15-Samenvatting.md) - ---- - -### Les 16: Eindproject Werkdag 2 - -**Tools:** Cursor, Vercel, Supabase, alle vorige tools - -**Focus:** Features afmaken, testing, bug fixes - -**Docent vertelt:** -- Recap vorige werkdag -- Extra features planning -- Performance optimization -- Deployment preparation - -**Studenten doen:** -- **Volle dag werken aan eindproject** -- Docent beschikbaar voor hulp -- Remaining features afmaken -- Uitgebreide testing -- Korte demo's van voortgang aan de klas -- Performance optimization - -**Lesopdracht:** -1. Alle geplande features afmaken -2. Uitgebreide testing en bug fixes -3. Performance audit -4. Code review voorbereiding -5. Presentatie voorbereiding - -**Huiswerk:** Laatste polishing, documentatie finaliseren, code review met klasgenoot. - -[β†’ Ga naar Les 16 Samenvatting](Samenvattingen/Les16-Samenvatting.md) - ---- - -### Les 17: Eindproject Polish & Code Review - -**Tools:** Cursor, Vercel, GitHub, Supabase - -**Focus:** Code review in paren, UI polish, performance check, presentatie voorbereiding - -**Docent vertelt:** -- Code review best practices -- Performance optimization details -- Presentation tips -- Deployment final checks - -**Studenten doen:** -- Code review in paren (peer feedback) -- UI polish en refinement -- Performance optimization (Lighthouse audit) -- Accessibility audit -- Presentatie voorbereiding -- Final deployment checks - -**Lesopdracht:** -1. Zorg dat iemand anders je code review -2. Voer feedback uit -3. Run Lighthouse en fix issues -4. Accessibility check -5. Deploy final version - -**Huiswerk:** Presentatie oefenen, demo video opnemen (optioneel), laatste tweaks. - -[β†’ Ga naar Les 17 Samenvatting](Samenvattingen/Les17-Samenvatting.md) - ---- - -### Les 18: Deployment, Presentaties & Evaluatie - -**Tools:** Vercel, GitHub, Supabase - -**Deployment Setup (30 min):** -- Vercel deployment flow -- Environment variables setup -- Supabase productie configuration -- Custom domains (optioneel) -- Performance optimization (Lighthouse) -- Security checklist - -**Presentaties & Evaluatie (90 min):** -- Iedereen presenteert eindproject (5 min per student) -- Live demo -- Vragen van docent en klasgenoten -- Feedback ontvangen -- Course evaluatie - -**Docent vertelt:** -- Best practices deployment -- Performance monitoring -- Error tracking en logging -- Continuous deployment setup -- Next steps: career paths, advanced topics - -**Studenten doen:** -- Final deployment naar Vercel -- Performance audit (Lighthouse) -- Security checklist doorlopen -- Presentatie geven (5 min) -- Course feedback geven - -**Lesopdracht:** -1. Deploy eindproject productie -2. Configure environment variables -3. Run Lighthouse audit (score >80) -4. Complete security checklist -5. Present your project - -**Huiswerk:** N.A. - Course afgerond! - -[β†’ Ga naar Les 18 Samenvatting](Samenvattingen/Les18-Samenvatting.md) - ---- - -## Tech Stack - -- **Frontend:** React, Next.js, TypeScript, Tailwind CSS -- **Backend:** Supabase (Postgres database, Auth) -- **Data Fetching:** React Query (TanStack Query) -- **AI Tools:** ChatGPT, Claude Desktop, v0.dev, OpenCode, Cursor -- **AI SDK:** Vercel AI SDK voor AI features in apps -- **Deployment:** Vercel, GitHub - ---- - -## Structuur per Les - -Elke uitgewerkte les bevat: -- `Les[xx]-Slide-Overzicht.md` - Slide deck structuur -- `Les[xx]-Lesplan.md` - Tijdsindeling en activiteiten (nu: ~45 min theorie + 15 min pauze + ~120 min klassikaal bouwen) -- `Les[xx]-Docenttekst.md` - Uitgebreide docenthandleiding -- `Les[xx]-Bijlage-A-Lesopdracht.md` - Lesopdracht (klassikaal en huiswerk) -- `Les[xx]-Bijlage-B-Huiswerkopdracht.md` - Huiswerkopdracht - -**Lesformat verandering (v2):** Meer klassikaal bouwen, minder solo werk. Docent codeert voor, studenten volgen mee met regelmatige "nu jullie" momenten. Betere interactie en sparring. +**Tim Rijkse** β€” docent AI-Assisted Development +NOVI Hogeschool Utrecht diff --git a/v1-feedback.md b/v1-feedback.md deleted file mode 100644 index d6866c6..0000000 --- a/v1-feedback.md +++ /dev/null @@ -1,216 +0,0 @@ -# V1 Feedback & Reflectie - -Dit document bevat de reflectie op de eerste versie van het curriculum, gebaseerd op de ervaringen tijdens het geven van les 1 en les 2 aan de eerste groep studenten (februari 2026). - ---- - -## Les 1: Introductie tot AI en Large Language Models - -### Wat ging goed -- ChatGPT gedeelte verliep soepel -- Iedereen kon meedoen en de uitleg volgen -- Studenten waren enthousiast over de workflow - -### Wat ging minder -- Docent versprak zich regelmatig en ging "van de hak op de tak" -- Mogelijk oorzaken: verkeerde slide-volgorde of zenuwen/onwetendheid bij eerste les - -### Probleempunt: Account setup chaos -Bij het aanmaken van v0.dev account, koppelen met GitHub, en deployen naar Vercel werd het rommelig: -- Studenten gingen door elkaar praten -- Veel verschillende issues tegelijkertijd: - - Sommigen konden GitHub niet koppelen - - Anderen hadden eerst gedeployed en daarna pas gekoppeld - - Ontelbare onduidelijkheden - -### Aanbevolen verbeteringen v2 -1. **Strakker stappenplan:** Iedereen doet elke stap samen, wachten tot iedereen klaar is -2. **Checkpoints toevoegen:** "Steek je hand op als je dit scherm ziet" -3. **Troubleshooting slide:** Meest voorkomende problemen + oplossingen -4. **Volgorde slides herzien:** Logischer opbouwen - ---- - -## Les 2: AI Code Assistants en OpenCode - -### Wat ging goed -- Zodra OpenCode draaide, konden studenten goed meekomen -- Lokaal draaien van website lukte uiteindelijk -- Studenten slaagden erin om `/init` te gebruiken en nieuwe pagina's te maken -- Wireframe β†’ prompt β†’ componenten werkte goed - -### Probleem 1: Te vroeg installeren -Zodra studenten de install command en "Download Desktop App" zagen op de slides, begonnen ze direct te installeren. Dit werd chaotisch terwijl de docent nog aan het uitleggen was. - -**Oplossing v2:** -- Installatie-instructies pas tonen NA de conceptuele uitleg -- Of expliciet vermelden: "WACHT - nog niet installeren, eerst kijken" - -### Probleem 2: Terminal en permissies -Veel studenten hadden problemen met hun terminal: -- **Windows vs Mac:** Verschillende omgevingen, verschillende commands -- **EACCESS errors:** Geen rechten om globaal te installeren -- **Sudo vereist:** Sommigen moesten met sudo installeren - -**Oplossing v2:** -- Voorbereiding document met terminal setup -- Aparte instructies voor Windows en Mac -- Uitleg over permissies en sudo - -### Probleem 3: Git clone en SSH keys -Bij het clonen van hun GitHub repository: -- Veel studenten hadden geen SSH key geconfigureerd -- Sommigen konden wel met HTTPS clonen (maar dat is minder ideaal) - -**Oplossing v2:** -- SSH key setup als voorbereiding VOOR de les -- Of HTTPS als fallback accepteren voor beginners -- Duidelijke instructies in voorbereidingsdocument - -### Probleem 4: Node.js versie -Bij npm install: -- Sommige studenten hadden verkeerde Node versie -- Geen ervaring met versie management - -**Oplossing v2:** -- NVM (Node Version Manager) introduceren -- Specifieke Node versie vereisen (bijv. Node 20 LTS) -- Check command in voorbereiding: `node -v` - -### Probleem 5: OpenCode terminal vs System terminal -Studenten runden commands in de OpenCode terminal terwijl ze het in hun system terminal moesten doen: -- Begrepen niet dat ze eerst `exit` moesten doen bij OpenCode -- Verwarring over welke terminal voor wat - -**Oplossing v2:** -- Duidelijker onderscheid maken tussen terminals -- Visueel verschil tonen (screenshots) -- Expliciete instructie: "Dit doe je in je EIGEN terminal, NIET in OpenCode" - -### Probleem 6: Tokens op bij gratis modellen -De tokens van gratis modellen raakten vrij snel op: -- "Arme studenten" - alleen gratis modellen beschikbaar -- Beperkt aantal prompts mogelijk per les - -**Oplossing v2:** -- Studenten aanmoedigen om prompts efficiΓ«nt te formuleren -- Meerdere gratis modellen tonen (backup opties) -- Eventueel: docent-account met meer tokens voor demo's - ---- - -## Algemene verbeteringen voor v2 - -### 1. Voorbereidingsdocument toevoegen -Een "Les 0" of voorbereidingsdocument dat studenten VOOR de eerste les moeten doorlopen: -- [ ] Node.js installeren (via NVM) -- [ ] Git installeren -- [ ] SSH key genereren en toevoegen aan GitHub -- [ ] Terminal basics (cd, ls, pwd) -- [ ] GitHub account aanmaken - -### 2. Platform-specifieke instructies -- Aparte secties voor Windows en Mac -- Screenshots van beide omgevingen -- Bekende issues per platform - -### 3. "Volg mij" aanpak -- Kleinere stappen -- Wachten tot iedereen klaar is -- Checkpoints met handopsteking of poll - -### 4. Troubleshooting secties -- Veelvoorkomende errors met oplossingen -- QR code naar online troubleshooting guide? - -### 5. Slide volgorde herzien -- Conceptuele uitleg VOOR praktische stappen -- "Nog niet doen" waarschuwingen waar nodig - ---- - -## Les 3: Cursor Basics - -### Context -Oorspronkelijk zou les 3 gaan over privacy en security van AI. Tim heeft besloten om in plaats daarvan een les over Cursor te geven β€” een praktischere les die beter aansluit bij wat studenten nodig hebben. Dit was een goede keuze. - -### Wat ging goed -- Studenten waren enthousiast over Cursor -- De les liep goed: de structuur met stappen (git init β†’ Cursor β†’ npx create-next-app β†’ Skills/Docs β†’ .cursorrules β†’ componenten bouwen) werkte -- Huiswerk uitgedeeld: Debug Challenge in drie niveaus (standaard, hard, super hard) β€” studenten konden zelf hun niveau kiezen -- De "hands-on" aanpak (75 minuten bouwen na de pauze) was effectief - -### Wat zou anders kunnen in v2 -- Les was aangepast t.o.v. het originele curriculum (privacy/security β†’ Cursor). In v2 moet het curriculum dit vanaf het begin reflecteren -- Eventueel privacy/security integreren in een andere les, of als korte module ergens toevoegen - ---- - -## Les 4: TypeScript Fundamentals - -### Context -Les over TypeScript basics met een Escaperoom opdracht. - -### Wat ging goed -- TypeScript Escaperoom was effectief als lesvorm -- Studenten begrepen het concept van type safety -- Cursor hielp studenten bij het oplossen van type errors - -### Wat zou anders kunnen in v2 -- Les 4 was goed maar v2 moet de collaborative format meenemen -- Meer live coding demonstraties - ---- - -## Les 5: Next.js β€” Het React Framework - -### Context -Oorspronkelijk was dit "TypeScript voor React" maar Tim sprong direct naar Next.js. De les behandelde Next.js basics inclusief App Router, Components, Data Fetching, en de start van Server Actions. - -### Wat ging goed -- Next.js content was relevant en studenten waren gemotiveerd -- QuickPoll project was een leuke, concrete opdracht -- Starter zip met scaffolded files was goed ontvangen - -### Wat ging minder -- Theorie was te lang: ~1 uur gepraat maar stopte bij Server Actions (slide 17 van 32), slides 18-32 niet behandeld -- Hands-on opdracht was te moeilijk voor zelfstandig werk: in 1.5 uur kwamen de meeste studenten tot stap 2-3 (van 7 stappen) -- Format "1 uur college + 2 uur solo" werkt niet goed - -### Studentfeedback -- Studenten gaven aan dat ze liever SAMEN willen bouwen -- Meer houvast en meer sparren gewenst -- Klassikaal werken heeft voorkeur boven individueel - -### Aanbevolen verbeteringen v2 -1. Les splitsen over 2 lessen (Les 5 = Part 1, Les 6 = Part 2) -2. Theorie inkorten tot ~45 min (stop bij Server Actions) -3. Hands-on klassikaal doen: Tim codeert voor, studenten volgen mee -4. Overige theorie (API Routes, Middleware, Deployment, Cursor) naar Les 6 -5. Stap 0-3 in Les 5, Stap 4-7 in Les 6 - ---- - -## Conclusie - -De kern van de lessen (AI concepten, prompting, development workflow) werkt goed. De problemen zitten vooral in: - -1. **Voorbereiding:** Studenten komen onvoorbereid qua development setup -2. **Tempo:** Te snel door setup-stappen heen -3. **Platform diversiteit:** Windows/Mac verschillen onderschat -4. **Terminal ervaring:** Veel studenten hebben weinig terminal ervaring -5. **Lesformat na Les 5:** Studentfeedback laat duidelijk zien dat klassikaal/collaborative werken effectiever is dan zelfstandig werken - -### Les 4-5 inzichten -Les 4 (TypeScript Escaperoom) werkte goed en moet in v2 behouden blijven. Les 5 heeft echter grote verbeteringen nodig: -- De combinatie van lange theorieblok + korte hands-on time werkt niet -- Studenten prefereren klassikaal leren en samen bouwen -- Content moet gesplitst worden over meerdere lessen met meer praktische focus -- Het format voor alle toekomstige lessen moet aangepast worden naar collaborative/klassikaal werken - -V2 moet focussen op betere voorbereiding, duidelijkere stapsgewijze instructies met checkpoints, en vooral: het overschakelen naar klassikaal/collaborative lesformat voor alle lessen. - ---- - -*Laatste update: maart 2026* -*Gebaseerd op: Les 1, 2, 3, 4 en 5 gegeven aan eerste groep* diff --git a/v2-klasB/Curriculum-Overzicht-KlasB.md b/v2-klasB/Curriculum-Overzicht-KlasB.md deleted file mode 100644 index 339d227..0000000 --- a/v2-klasB/Curriculum-Overzicht-KlasB.md +++ /dev/null @@ -1,109 +0,0 @@ -# Curriculum Klas B β€” 18 Lessen - -**Versie:** 1.1 (concept) -**Doelgroep:** Studenten met al kennis van terminal/git, JS/TS, React/Next.js en AI tools. -**Focus:** Code kwaliteit Β· Veiligheid Β· Mooie ontwerpen -**Eindopdracht:** ongewijzigd (Next.js 14 + Supabase + Vercel AI SDK + externe API) -**Lesvorm:** Online, 2 uur per les - ---- - -## Lesformat (elke les) - -| Tijd | Onderdeel | -|------|-----------| -| 0:00 – 1:00 | Uitleg / live demo (60 min) | -| 1:00 – 1:15 | Pauze (15 min) | -| 1:15 – 2:00 | Praktijk / hands-on (45 min) | - -Plus: **kleine huiswerkopdracht** elke week (~30–60 min) β€” reinforced wat in de les is behandeld en bereidt voor op volgende les. - ---- - -## Filosofie - -Klas A had veel tijd nodig voor setup, basics en klassikaal volgen. Klas B niet. Daarom: - -- **Geen setup-tijd:** terminal, git, npm, Cursor staan al. Controle in Les 2 in 10 min. -- **Theorie kort, demo-driven:** binnen de 60 min talk zo veel mogelijk live coden. -- **Standaarden vanaf dag 1:** elke les bevat één "professional habit" (types, validatie, a11y, security). -- **Huiswerk is verplicht klein:** geen weekend-projecten β€” wel iedere week een concrete oefening. -- **Eindopdracht parallel** vanaf Les 4 (eigen project naast lessen, niet pas na les 18). - ---- - -## Roadmap β€” 4 Blokken - -### Blok 1 β€” Solid Foundation (Les 1–4) - -| Les | Titel | Kern-leerdoel | Huiswerk | -|-----|-------|---------------|----------| -| 1 | Introductie AI & LLMs | (Reeds gegeven 2026-05-12) | β€” | -| 2 | AI-Dev Workflow Mastery | Cursor + Skills.sh + .cursorrules + MCPs effectief inzetten | Maak een eigen .cursorrules voor je stack | -| 3 | Next.js 14 Architectuur | App Router, Server vs Client Components, folder conventies | Bouw een mini-app skeleton met juiste structuur | -| 4 | TypeScript voor Professionals | Generics, discriminated unions, Zod schemas, type-safe contracts | Refactor 1 functie naar volledig type-safe + Zod | - -### Blok 2 β€” Design & Craft (Les 5–9) - -| Les | Titel | Kern-leerdoel | Huiswerk | -|-----|-------|---------------|----------| -| 5 | Design Systems met Tailwind + Shadcn | Design tokens, theming, dark mode, componenten library | Setup eigen design tokens + 3 componenten | -| 6 | Mooie UI in de Praktijk | Smaak ontwikkelen: typografie, spacing, kleur, hiΓ«rarchie | Herontwerp 1 bestaande pagina, screenshot voor/na | -| 7 | Motion & Micro-interactions | Framer Motion, loading states, transitions, feedback patterns | Voeg 3 micro-interactions toe aan je app | -| 8 | Accessibility & Responsive | ARIA, keyboard nav, contrast, mobile-first | a11y audit op eigen project + 3 fixes | -| 9 | Clean Code & Refactor met AI | Naming, code smells, AI-assisted refactoring | Refactor 1 component van "werkt" naar "mooi" | - -### Blok 3 β€” Data & Security (Les 10–13) - -| Les | Titel | Kern-leerdoel | Huiswerk | -|-----|-------|---------------|----------| -| 10 | Supabase + Database Design | Schema modelleren, relaties, indexes, migraties | ERD + schema voor je eindopdracht | -| 11 | Auth & RLS vanaf de Grond | Auth flow, sessions, JWT, RLS policies (vanaf dag 1!) | RLS policies schrijven + testen met 2 users | -| 12 | Server Actions & Validatie | Zod, sanitization, error handling, never-trust-client | Alle forms in je app valideren met Zod | -| 13 | Web Security in de Praktijk | OWASP Top 10, XSS, CSRF, secrets management, .env hygiene | Security audit eigen project + fix top-3 issues | - -### Blok 4 β€” AI Features & Production (Les 14–18) - -| Les | Titel | Kern-leerdoel | Huiswerk | -|-----|-------|---------------|----------| -| 14 | Vercel AI SDK Basics | Streaming, useChat, generateText, structured outputs | Bouw een streaming chat in eigen project | -| 15 | Tool Calling & Externe APIs | Function calling, API integratie, error states | 1 AI tool koppelen aan een gratis externe API | -| 16 | AI Agents & maxSteps | Multi-step autonome agents, planning, tool orchestration | Agent maken die 2+ tools chained gebruikt | -| 17 | Testing & Deployment | Vitest + Playwright (AI-assisted), Vercel deploy, Lighthouse | Tests schrijven + deployen naar productie | -| 18 | Code Review & Pitch | Peer review, refactor sessie, eindopdracht presentatie | Pitch-deck voorbereiden (5 min) | - ---- - -## Mapping naar eindopdracht-leeruitkomsten - -| Leeruitkomst | Lessen die dit dekken | -|---|---| -| 1. TypeScript & Next.js Development | 3, 4, 5, 9 | -| 2. Database Design & Supabase | 10, 11, 12 | -| 3. AI-Assisted Development met Cursor | 2, 9, 17 | -| 4. Skills.sh voor Consistente Output | 2 (intro), terugkerend | -| 5. Prompt Engineering & Iteratief Werken | 2, 14, 16 | -| 6. AI Agents Bouwen met Vercel AI SDK | 14, 15, 16 | - -Bovenop eindopdracht-eisen (kwaliteit/security/design): 6, 7, 8, 13, 17, 18. - ---- - -## Verschillen met Klas A curriculum - -- Geen losse "Cursor Basics" β€” Klas B start direct op niveau (Les 2 = workflow mastery). -- Geen "TypeScript Fundamentals" als beginnersles β€” vervangen door pro-versie (Les 4). -- Design krijgt **5 lessen** i.p.v. impliciet meeliften. -- Security krijgt eigen volwaardige les (13) bovenop Auth/RLS (11, 12). -- AI Agents krijgt 3 lessen i.p.v. 2 (14–16). -- Testing + Deployment samengevoegd in 1 les (17) β€” past in 2-uur format. -- Eindopdracht-werk parallel vanaf Les 4. - ---- - -## Open vragen voor jou - -1. **Eindopdracht parallel vanaf Les 4:** akkoord, of pas later? -2. **Pair programming online:** breakout rooms in Zoom/Teams of laten studenten zelf afspreken? -3. **Huiswerk inleveren:** GitHub PR, Discord/Slack channel, of niet centraal verzamelen? -4. **Die ene gevorderde leerling** (recruitment-app): aparte uitdaging in huiswerk of normaal meeloopen? diff --git a/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md b/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md deleted file mode 100644 index 48885e8..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md +++ /dev/null @@ -1,612 +0,0 @@ -# Les 2 β€” OpenCode Pro: Rules, Worktrees & Scroll Animations -## Docenttekst + Docenthandleiding (Klas B β€” 2 uur, online) - -**Les:** 2 van 18 -**Duur:** 120 minuten online -**Vorige les:** Les 1 β€” Introductie AI Developer leerlijn - ---- - -## Leerdoelen -- Begrijpen waarom we OpenCode kiezen i.p.v. Cursor (multi-provider, open source, alternatieven) -- OpenCode Desktop kunnen draaien met Plan/Build/@explore/init -- `AGENTS.md` regelbestand schrijven (officieel format) -- `opencode.json` config met permissions -- `opencode-worktree` plugin via `ocx` installeren -- Parallel agents via Sessions sidebar -- Live: Next.js 16 + GSAP + Lenis scroll-site opzetten - ---- - -## Lesflow - -**Geen heen-en-weer switchen.** De les is opgedeeld in: -- **Theorie 1** β€” uninterrupted uitleg (slides 4-6) -- **Live Demo 1** β€” alle features tonen achter elkaar (slide 7) -- **Theorie 2** β€” uninterrupted uitleg (slides 8-12) -- **Live Demo 2** β€” setup + bouw SmoothScroll (slide 13) -- Pauze -- Lesopdracht - ---- - -# DEEL 1 β€” Docenthandleiding (volg dit live tijdens de les) - -## VÓÓR DE LES β€” Setup (30 min) - -### 1. OpenCode Desktop - -- Download van https://opencode.ai/download -- Installeer -- Log in met OpenAI key (uit Brightspace) - -### 2. `ocx` + worktree plugin - -```bash -curl -fsSL https://kdco.dev/ocx/install.sh | sh -ocx add kdco/worktree --from https://registry.kdco.dev -``` - -Sluit en open OpenCode opnieuw zodat plugin geladen wordt. - -### 3. Demo-repo aanmaken - -```bash -cd ~ -npx create-next-app@latest scroll-demo \ - --typescript --tailwind --app --eslint --no-src-dir --turbopack -cd scroll-demo -npm install gsap @gsap/react lenis -git init -git add . -git commit -m "init: next 16 + gsap + lenis" - -# GitHub remote (maak repo eerst via gh CLI of dashboard) -gh repo create scroll-demo --public --source=. --remote=origin --push - -# Vercel CLI + koppelen + eerste deploy -npm i -g vercel -vercel link # koppelt repo aan een Vercel project -vercel # eerste deploy naar productie URL -``` - -**Check:** open Vercel dashboard β†’ project bestaat β†’ productie-URL werkt. Open GitHub β†’ repo staat er β†’ Vercel preview comment in PR werkt. - -### 4. Test je setup - -- Open OpenCode Desktop β†’ File β†’ Open Folder β†’ `~/scroll-demo` -- Type in chat: `Wat is de stack?` β†’ moet Next.js 16 + Tailwind herkennen -- Type `/init` en abort (Esc) β€” alleen testen dat 't werkt -- Maak test-worktree om plugin te valideren: - ``` - Maak een testworktree feature-test - ``` -- Klik **+ New Session** linksboven, open folder van die worktree -- Sluit Session, verwijder worktree: - ```bash - git worktree remove ~/.local/share/opencode/worktree/scroll-demo/feature-test - ``` - -### 5. Browser tabs open - -- https://opencode.ai/download -- https://opencode.ai/docs/rules/ -- https://gsap.com/resources/React/ -- https://github.com/kdcokenny/opencode-worktree - -### 6. Backup plan - -Als plugin live faalt: -```bash -git worktree add ../scroll-demo-hero -b feature-hero -``` -Dan handmatig nieuwe Session β†’ Open Folder. - ---- - -## TIJDENS DE LES - -### Blok 1 β€” Welkom + Terugblik (10 min) - -**Slides 1, 2, 3.** - -- Start meeting -- Slide 2: vraag in chat: "Wie heeft tot nu toe alleen met chat (ChatGPT/Claude) gewerkt?" -- Slide 3: planning langslopen. **Benadruk** dat we eerst theorie geven, dan demo, dan weer theorie, dan demo. Geen heen-en-weer. - -**Geen demo nodig in dit blok.** - ---- - -### Blok 2 β€” THEORIE 1: Waarom OpenCode + Kern features (20 min) - -**Slides 4, 5, 6. Achter elkaar, zonder switchen.** - -#### Slide 4 β€” Waarom OpenCode (en niet Cursor) β€” 6 min - -**Vertel:** -"Iedereen kent Cursor. Sommigen gebruiken het al. Goede tool. Waarom beginnen we dan met OpenCode? - -Drie redenen. - -**EΓ©n β€” alternatieven kennen is professioneel.** Wie alleen Cursor kent, zit vast aan één tool. Een goede dev kent meerdere coding-assistenten en kiest per project. - -**Twee β€” provider-keuze.** Cursor heeft eigen routing β€” jij hebt niet vol overzicht welk model wanneer wordt gebruikt. OpenCode is provider-agnostisch: OpenAI vandaag, Anthropic morgen, lokaal met Ollama als je dat wilt. Jij beslist. - -**Drie β€” open source = geen lock-in.** Voor je eindopdracht en je carriΓ¨re is dit relevant. Tools veranderen, abonnementen veranderen. Open source blijft beschikbaar. - -Belangrijk: dit is **geen Cursor-bashing**. Cursor is uitstekend. We komen er later in de leerlijn op terug. Vandaag de open variant β€” dan kun je daarna zelf kiezen." - -Loop tabel langs. - -#### Slide 5 β€” Wat is OpenCode β€” 7 min - -**Vertel:** -"OpenCode is een AI coding-IDE. 60.000+ GitHub stars. Gemaakt door SST. Werkt met elke provider β€” OpenAI, Anthropic, Google, Groq, of lokaal met Ollama. - -Twee smaken: Desktop en TUI. Wij gebruiken Desktop. Waarom? -- Visuele diff-viewer (klik accept/reject) -- File tree links, Sessions sidebar -- Multiple sessies naast elkaar -- Sneller leren als je begint - -De TUI bestaat ook. Goed voor SSH-servers, scripts, CI/CD. Ik laat 'm 1 minuut zien in de demo β€” daarna 100% Desktop." - -Loop tabel langs. - -#### Slide 6 β€” Plan/Build/@explore + /init β€” 7 min - -**Vertel:** -"Vier dingen die je echt moet kennen. - -**Modes** β€” twee modes, Tab om te wisselen: -- **plan** is read-only. Denkt mee, leest files, wijzigt niks. Goed voor verkenning en brainstorm. -- **build** is default. Schrijft code, voert commands uit. - -**Sub-agents** β€” drie ingebouwde: -- `@explore` is read-only verkenning. Eigen context, spaart tokens. -- `@general` is brede taak. -- `@scout` is gerichte zoek. - -**`/init` commando** β€” eerste actie in elk nieuw project. Scant je repo, stelt vragen, schrijft je `AGENTS.md`. - -**Best practice:** plan eerst, lees mee, geef feedback, dan Tab β†’ build. Dit voorkomt rommel. - -Dat was de theorie. Nu zien jullie het live." - ---- - -### Blok 3 β€” LIVE DEMO 1: Desktop Tour (10 min) - -**Slide 7.** - -**Setup vooraf:** OpenCode Desktop open op `~/scroll-demo`. Systeem-terminal open naast OpenCode voor de TUI-demo. - -#### Stap 1 β€” TUI in 1 minuut (1 min) - -- Switch naar systeem-terminal -- Type: `opencode` -- Toon kort de TUI met chat onderaan -- Type: `wat is de stack van dit project?` β†’ krijg antwoord -- Druk **Tab** β†’ switch naar plan mode β†’ tab terug -- **Ctrl+D** om te sluiten - -**Zeg:** "Dat was de TUI. Werkt prima. Vandaag verder met Desktop." - -#### Stap 2 β€” Desktop UI tour (2 min) - -- Switch naar Desktop -- Wijs aan: - - **File tree** links (klik op `app/page.tsx` om te tonen) - - **Chat panel** onderaan - - **Diff viewer** in midden - - **Sessions sidebar** (toon dat 't er is, leeg op één session na) - - **Model selector** + **mode indicator** bovenin - -**Zeg:** "Alles op één scherm. File tree, chat, diff. Veel makkelijker dan switchen tussen terminals." - -#### Stap 3 β€” Plan mode demo (2 min) - -- Onder in chat, check dat mode-badge op `build` staat -- Druk **Tab** β†’ wisselt naar `plan` -- Type in chat: `analyseer dit project en stel verbeteringen voor` -- Wacht op antwoord -- **Zeg:** "Zie? Concrete voorstellen. Geen file wijzigingen. Veilig om los te laten." - -#### Stap 4 β€” Build mode demo (2 min) - -- Druk **Tab** β†’ terug naar `build` -- Type: `voeg een comment bovenaan app/page.tsx toe met de tekst "scroll demo"` -- Toon diff in viewer β€” klik **Accept** -- Open `app/page.tsx` in file tree β€” comment staat er - -**Zeg:** "Plan is denken, build is doen." - -#### Stap 5 β€” @explore demo (1.5 min) - -- Type in chat: `@explore wat is de folder structuur van dit project?` -- Toon dat het in eigen context werkt -- **Zeg:** "Read-only sub-agent. Spaart tokens, kan niks kapot maken." - -#### Stap 6 β€” /init demo (1.5 min) - -- Type: `/init` -- Beantwoord de vragen die OpenCode stelt (Next.js 16, GSAP, scroll-animaties) -- Wacht tot AGENTS.md gegenereerd is -- Open `AGENTS.md` in file tree β€” toon wat 't gegenereerd heeft -- **Zeg:** "Dit is een goede basis. Volgende blok: hoe we 'm aanvullen met onze regels." - ---- - -### Blok 4 β€” THEORIE 2: AGENTS.md + config + plugin + stack + Vercel (17 min) - -**Slides 8, 9, 10, 11, 12. Achter elkaar, zonder switchen.** - -#### Slide 8 β€” AGENTS.md β€” 3 min - -**Vertel:** -"Belangrijkste concept van vandaag. `AGENTS.md` is **dΓ©** manier om OpenCode jouw regels te geven. Officieel format, automatisch in elke context. - -Twee locaties: -- `./AGENTS.md` in je repo β€” versioned, deelt met team -- `~/.config/opencode/AGENTS.md` β€” globaal, jouw persoonlijke voorkeuren - -Externe regels mag je referencen met `@rules/styling.md` β€” agent laadt 'm lazy. - -Tip: schrijf je AGENTS.md zoals je 'm aan een junior dev zou geven. Concreet, controleerbaar." - -#### Slide 9 β€” opencode.json β€” 3 min - -**Vertel:** -"Naast regels heb je config. Welk model, welke permissies, welke plugins. - -`$schema` geeft autocomplete in je editor. -`model` is `provider/model-id`. - -Belangrijkste is **permissions**. Drie levels: `allow`, `ask`, `deny`. Glob patterns voor bash Γ©n files. - -Voor student-project: `bash.*` op `ask` is veilig β€” je reviewed elke shell command. Productie: alleen specifieke folders editable." - -#### Slide 10 β€” Worktree plugin β€” 3 min - -**Vertel:** -"Plugin van **kdcokenny** β€” community lid, ook achter `ocx` extension manager. Twee tools voor de agent: -- `worktree_create` β€” branch + worktree -- `worktree_delete` β€” commit + cleanup - -Installatie via `ocx`. Restart na install. - -Verschil TUI vs Desktop: -- TUI: plugin spawnt automatisch nieuw terminal venster -- Desktop: plugin maakt de worktree, jij opent 'm in nieuwe Sessions tab - -Klein verschil, zelfde resultaat: parallel agents." - -#### Slide 11 β€” Demo stack β€” 3 min - -**Vertel:** -"De stack waarmee we vandaag bouwen β€” en waarom dit dΓ© combinatie is voor scroll storytelling. - -**Next.js 16** β€” uit oktober 2025. Belangrijkste verandering t.o.v. 15: `cookies()`, `headers()`, `params`, `searchParams` zijn allemaal async. Altijd `await`. Turbopack is default β€” snellere builds. - -**GSAP 3.15** β€” sinds januari 2025 100% gratis, incl. ScrollTrigger en SplitText. HΓ©t animatie-pakket op pro-niveau. Apple product pages, Stripe homepage, OpenAI marketing β€” allemaal GSAP. Award-winning sites op Awwwards en FWA: vrijwel altijd GSAP. - -**Lenis 1.3** β€” door **Darkroom Engineering** (voorheen Studio Freight, een award-winning studio uit Brooklyn die zelf onder andere voor Studio Freight, Activision en Riot werkt). Beste smooth scroll lib. Integreert naadloos met GSAP ScrollTrigger. - -**`@gsap/react`** β€” geeft je de officiele `useGSAP` hook. Drop-in voor useEffect met auto cleanup. - -**Waarom niet Framer Motion?** -Framer Motion is uitstekend β€” voor app UI. Modals, page transitions, micro-interacties. Maar voor scroll storytelling (zoals jij gaat bouwen) wil je timing-precisie en GPU-optimalisaties die GSAP biedt en Framer Motion niet matcht. Pro-studios zoals **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** β€” allemaal GSAP-based. - -Daarom staat in onze AGENTS.md: 'Geen Framer Motion'. Het is geen anti-Framer, het is pro-GSAP voor dit type werk." - -#### Slide 12 β€” Onze AGENTS.md β€” 2 min - -**Vertel:** -"Hier zie je onze concrete regels voor dit project. Let op de structuur: -- Why this stack β€” context voor de agent -- Stack β€” versies expliciet -- Hard rules β€” niet onderhandelbaar -- Patterns β€” hoe we organiseren -- Done β€” wat klaar betekent - -Regels die opvallen: -- 'Geen Framer Motion' β€” voorkomt dat AI default naar 't bekendere -- 'useGSAP, nooit useEffect' β€” werkt met Strict Mode -- 'Lenis sync via gsap.ticker.add' β€” voorkomt jank -- 'await params in Next 16' β€” anders runtime error - -Deze regels heb ik uit ervaring. In de volgende demo zet ik dit in, en zie je dat de AI ze volgt." - -#### Slide 13 β€” Vercel + Preview Deployments β€” 3 min - -**Vertel:** -"Laatste theorie-stuk voor we gaan demoen. Hoe sluiten we onze worktree-workflow goed af? Met **Vercel preview deployments**. - -Het idee: je hoeft niet handmatig te deployen. Elke `git push` triggert Vercel om automatisch een deploy te maken. En het mooie β€” voor Γ©lke branch krijg je een eigen unieke URL. - -- Push naar `main` β†’ productie URL (`jouw-app.vercel.app`) -- Push naar `feature-hero` β†’ preview URL (`jouw-app-git-feature-hero-jij.vercel.app`) -- Push naar `feature-gallery` β†’ andere preview URL - -Stel je voor: je werkt aan een hero-sectie. Je pusht naar je feature-branch. Twee minuten later heb je een live URL die je naar je designer kunt sturen voor feedback. Geen 'het werkt op mijn machine'. Geen screenshots. Live. - -**Voor scroll-storytelling is dit goud** β€” animaties moet je zien om over te oordelen. Preview URLs maken die feedback-loop razend snel." - -**Setup (kort uitleggen β€” komt zo in demo):** -- `npx vercel link` koppelt je repo aan een Vercel project -- Vercel installeert eigen GitHub app -- Vanaf nu: elke push = deploy - -**Combinatie met onze workflow:** -"En hier komt 't mooi samen: 1 worktree = 1 branch = 1 Vercel preview. Drie features parallel? Drie live preview URLs. Verschillende stakeholders kunnen verschillende features tegelijk reviewen." - -**Demo aankondiging:** -"In Live Demo 2 ga ik niet alleen onze SmoothScroll bouwen, maar ook pushen naar de feature-branch en zien dat Vercel automatisch een preview URL maakt." - ---- - -### Blok 5 β€” LIVE DEMO 2: Setup + Worktree + SmoothScroll + Vercel preview (20 min) - -**Slide 13.** - -**Setup vooraf:** OpenCode Desktop nog open op `~/scroll-demo`. Plugin al geΓ―nstalleerd. - -#### Stap 1 β€” AGENTS.md vervangen (3 min) - -- Open `AGENTS.md` in Desktop file tree -- Selecteer alles β†’ vervang met content uit slide 12: - -```markdown -# Project Rules - -## Why this stack -High-end scroll storytelling site (Awwwards/FWA niveau). -GSAP + Lenis = pro standaard β€” Apple, Stripe, OpenAI, Active Theory, -Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor -scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. - -## Stack -- Next.js 16 (App Router, TypeScript, Turbopack) -- TailwindCSS Β· GSAP 3.15+ (incl. ScrollTrigger, SplitText) -- Lenis 1.3+ (`lenis/react`) -- @gsap/react voor de useGSAP hook - -## Hard rules -- Geen Framer Motion, react-spring, AOS -- Animaties altijd in Client Components (`"use client"`) -- Animatie-code in useGSAP(() => {}, { scope: ref }) -- Nooit useEffect voor GSAP code -- Lenis sync: gsap.ticker.add met autoRaf: false -- Next 16: await params, await cookies(), await headers() - -## Patterns -- EΓ©n wrapper in app/layout.tsx -- Tailwind voor layout; GSAP voor transform/opacity - -## Done = -- Geen hydration warnings -- ScrollTrigger.refresh() na dynamische content -``` - -- Cmd+S -- Type in chat: `Lees AGENTS.md. Welke regels ga jij volgen, en waarom kiezen we deze stack?` -- Toon dat het ook de **why** uitlegt β€” niet alleen de regels napapegaait - -#### Stap 2 β€” opencode.json maken (3 min) - -- Right-click in file tree β†’ New File β†’ `opencode.json` -- Plak: - -```json -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-4o-mini", - "permission": { - "bash": { - "*": "ask", - "git *": "allow", - "npm *": "allow", - "rm *": "deny" - } - } -} -``` - -- Cmd+S -- Restart OpenCode (File β†’ Reload Window) -- Type: `rm -rf node_modules` β†’ toon dat 't blokkeert -- Type: `git status` β†’ toon dat 't direct werkt -- **Zeg:** "Geen surprises meer." - -#### Stap 3 β€” Worktree plugin in actie (3 min) - -- Type: `Welke tools heb je beschikbaar?` -- Wijs aan: `worktree_create`, `worktree_delete` in lijst -- Type: `Maak een worktree feature-hero voor een hero-sectie.` -- Wacht tot plugin klaar is -- Klik **+ New Session** linksboven -- **Open Folder** β†’ navigeer naar `~/.local/share/opencode/worktree/scroll-demo/feature-hero/` -- Nieuwe Session opent β€” toon dat 't naast main staat in sidebar - -#### Stap 4 β€” SmoothScroll laten bouwen (5 min) - -- In de **feature-hero** Session, type: - ``` - Bouw een SmoothScroll wrapper component volgens AGENTS.md. - Plaats in components/SmoothScroll.tsx en wrap children in app/layout.tsx. - ``` -- Wacht op diff -- **Wijs aan wat de regels volgen:** - - `"use client"` bovenaan - - `useGSAP` import (niet useEffect) - - `gsap.ticker.add` met `autoRaf: false` - - Geen Framer Motion of andere libs -- Klik **Accept** - -#### Stap 5 β€” Bewijs van regels (1 min) - -**Zeg:** "Zie je? Met goede AGENTS.md krijg je consistente output. Zonder zou de AI waarschijnlijk Framer Motion pakken of useEffect gebruiken. Schrijf je regels op, krijg je betrouwbare resultaten." - -#### Stap 6 β€” Push naar feature-branch + Vercel preview (4 min) - -**Pre-setup check:** repo is al gekoppeld aan Vercel (deed je vΓ³Γ³r de les). Open Vercel dashboard naast OpenCode op je scherm. - -**In de feature-hero Session:** -- Type in chat: `Commit deze wijzigingen en push naar de feature-hero branch.` -- Of typ zelf in ingebouwde terminal: - ```bash - git add . - git commit -m "feat: SmoothScroll wrapper" - git push origin feature-hero - ``` -- Wacht 5-10 seconden - -**Switch naar Vercel dashboard:** -- Refresh β€” zie je nieuwe deploy verschijnen voor `feature-hero` branch -- Status: "Building" β†’ "Ready" (duurt ~30-60 sec) -- Klik op de deploy β†’ toon **preview URL** (iets als `jouw-app-git-feature-hero-jij.vercel.app`) - -**Open de preview URL:** -- Laat zien: ja, het werkt live -- Scroll erdoorheen β€” smooth scroll werkt -- "Deze URL is publiek, je kunt 'm nu doorsturen naar een designer of stakeholder." - -**Vertel:** -"Zie wat hier gebeurde: -- Lokaal: ik bouwde een feature met OpenCode -- Push: één commando -- Vercel: automatische deploy, eigen URL voor deze branch -- Designer/PM: kan reviewen zonder Git of localhost -- Iedere nieuwe push naar deze branch overschrijft deze URL - -En als ik nu een tweede worktree maak voor 'feature-gallery'? Krijgt die zijn eigen preview URL. Drie features parallel = drie live previews. Dat is professionele scroll-storytelling workflow." - -**Optioneel β€” toon git workflow visueel:** -- Open GitHub in browser -- Toon de feature-hero branch op repo -- Toon Vercel comment in PR (als er een PR is) met preview URL - ---- - -### Blok 6 β€” Pauze (15 min) - -**Slide 14.** - -Stuur in chat: "Terug over 15 minuten." Camera mag uit. - ---- - -### Blok 7 β€” Lesopdracht (35 min) - -**Slide 15.** - -#### Briefing (3 min): - -"Eigen worktree maken, sectie kiezen, bouwen. Plan eerst, dan Build." - -Loop checklist op slide 15 langs. - -#### Tijdens 30 min werken: - -- Tim blijft in main meeting-room -- Breakout-rooms op verzoek -- Check in chat: "Wie heeft worktree? Type W." β†’ "Wie ziet animatie? Type A." - -#### Veelvoorkomende problemen: - -| Probleem | Oplossing | -|----------|-----------| -| Plugin niet beschikbaar | `ocx list` om te checken | -| Worktree-tool onbekend | Desktop herstarten na ocx add | -| Sessions sidebar leeg | Klik `+ New Session` | -| `useGSAP is not a function` | `npm install @gsap/react` | -| Hydration mismatch | `"use client"` mist of `useEffect` ipv `useGSAP` | -| Lenis import error | Niet `@studio-freight/lenis`, maar `lenis` | -| ScrollTrigger niets | `gsap.registerPlugin(ScrollTrigger)` mist | -| Async params error Next 16 | `await params` toevoegen | - -#### Check-in (~25 min in): - -"Wie heeft werkende animatie? Type A in chat." Bij <50%: 5 min klassikaal debug. - ---- - -### Blok 8 β€” Huiswerk + Afsluiting - -**Slides 16, 17.** - -- Loop huiswerk eisen langs -- "Volgende keer: **Introductie Cursor** β€” de commerciele tegenhanger. Cursor rules vs AGENTS.md, Composer, wanneer welke tool." -- Vragen + feedback - ---- - -# DEEL 2 β€” Docenttekst (referentie per slide) - -## Slide 4 β€” Waarom OpenCode - -Vertel waarom **niet** Cursor: -- Cursor is goed maar commercieel ($20/mnd), eigen routing -- OpenCode is gratis (alleen API-kosten), open source, jij kiest model -- Voor je carriere: meerdere tools kennen > vasthangen aan één -- We komen later in leerlijn terug op Cursor β€” vandaag de open variant - -## Slide 5 β€” Wat is OpenCode - -Provider-agnostisch, scriptable, multi-instance. Twee smaken: Desktop (vandaag), TUI (kort tonen). - -## Slide 6 β€” Plan/Build/@explore + /init - -Tab voor mode switch. `@explore` voor verkenning. `/init` voor eerste AGENTS.md. - -## Slide 8 β€” AGENTS.md - -Officieel format. Automatisch in context. Concrete regels werken, vage regels niet. - -## Slide 9 β€” opencode.json - -Schema voor autocomplete. Model. Permissions (allow/ask/deny). Plugins. - -## Slide 10 β€” Worktree plugin - -Via ocx installeren. Twee tools voor agent. Desktop: nieuwe Session tab; TUI: auto terminal. - -## Slide 11 β€” Stack - -Next.js 16 (async params, Turbopack). GSAP gratis. Lenis door darkroomengineering. useGSAP hook. - -## Slide 12 β€” Onze AGENTS.md - -Concrete regels die voorkomen dat AI default naar bekende-maar-verkeerde keuzes (Framer Motion, useEffect). - ---- - -## Bronnen voor studenten - -**OpenCode:** -- Docs: https://opencode.ai/docs/ -- Download: https://opencode.ai/download -- Rules: https://opencode.ai/docs/rules/ -- Config: https://opencode.ai/docs/config/ -- GitHub: https://github.com/sst/opencode - -**Plugin:** -- opencode-worktree: https://github.com/kdcokenny/opencode-worktree -- ocx: https://github.com/kdcokenny/ocx - -**Stack:** -- Next.js 16: https://nextjs.org/blog/next-16 -- GSAP: https://gsap.com/docs/v3/ -- GSAP React: https://gsap.com/resources/React/ -- Lenis: https://github.com/darkroomengineering/lenis - ---- - -## Backup-onderwerpen (als tijd over) - -1. **Custom sub-agents** in `~/.config/opencode/` -2. **MCP servers** β€” context7 voor up-to-date library docs -3. **opencode-workspace plugin** (uitgebreidere variant) -4. **Cursor live laten zien** β€” vergelijken -5. **Ollama setup** voor lokale models (privacy) diff --git a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md deleted file mode 100644 index ef35649..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md +++ /dev/null @@ -1,165 +0,0 @@ -# Les 2 β€” Huiswerk -## Bouw je eigen scroll-animatie landing page - -**Vak:** AI-Assisted Development -**Opleiding:** NOVI Hogeschool Utrecht -**Deadline:** Voor de volgende les (Les 3) -**Inleveren:** GitHub repo URL + Vercel deploy URL + `WORKFLOW.md` - ---- - -## Doel - -Bouw **verder in de repo van de les**. Maak van je eerste sectie een complete kleine landing page met 3-4 secties. Deploy + previews lopen al via Vercel. - -Dit is geen design-opdracht β€” focus is op de **workflow** en de **technologie**. Een simpele maar werkende site is genoeg. - -> **Doorbouwen, niet herstarten.** Je werkt verder in dezelfde fork als in de les. -> AGENTS.md, opencode.json, Vercel β€” staat allemaal al. Je voegt secties toe. - ---- - -## Vertrek vanuit je les-werk - -Je begint **niet opnieuw**. Open je laptop, ga naar je `scroll-demo` fork, en bouw verder. - -### Thema kiezen (als nog niet gedaan) -Heb je in de les een type sectie gekozen? Bouw daaromheen je thema. Of kies nu: -- **Mini portfolio** (over jou β€” projects, contact) -- **Restaurant landing** (menu, story, reserveer) -- **Product launch** (features, prijs, signup) -- **Conferentie / event** (speakers, schedule, register) -- **Fitness app marketing** (hero, features, testimonials) - -Eigen idee mag ook β€” even checken met Tim als 't iets totaal anders is. - ---- - -## Eisen (verplicht) - -### Code (bouwt voort op les) -- [ ] **Doorgewerkt in dezelfde fork** als in de les (Next.js 16, TS, Tailwind) -- [ ] `AGENTS.md` aanwezig β€” uitgebreid waar nodig (regels die je tijdens werk leerde) -- [ ] `opencode.json` aanwezig (uit starter, mag aangepast) -- [ ] **Eerste sectie afgemaakt** (uit lesopdracht) + **2-3 extra secties** = totaal **3-4 scroll-secties** -- [ ] **Minimaal 3 verschillende animatie-technieken** (fade-in, parallax, SplitText, horizontaal, stagger…) -- [ ] **Lenis** smooth scroll werkt -- [ ] Werkt zonder console-errors of hydration warnings - -### Workflow -- [ ] **Totaal minstens 2 worktrees gebruikt** (de lesopdracht-worktree telt mee) -- [ ] Elke feature-branch gepushed β†’ Vercel preview URL bekeken -- [ ] Branches gemerged naar `main` -- [ ] Worktrees opgeruimd na merge -- [ ] Code op GitHub gepushed - -### Deployment & previews -- [ ] **Vercel gekoppeld** aan repo (`vercel link` of via dashboard) -- [ ] **Production URL** werkt (deploy van `main`) -- [ ] **Minstens 2 preview URLs** (van twee feature-branches) β€” Vercel maakt deze automatisch bij elke push -- [ ] Geen broken pages / 500's - -### Documentatie -- [ ] **`WORKFLOW.md`** in repo-root met: - - Welke prompts werkten goed? - - Welke regels heb je later toegevoegd aan AGENTS.md (en waarom)? - - Hoe heb je worktrees georganiseerd? - - Wat zou je anders doen volgende keer? - - **Screenshots van minstens 2 preview URLs** (Vercel dashboard view + browser view) - - Screenshot van `git worktree list` tijdens het werk - - Max 400 woorden β€” concreet, geen fluff - ---- - -## Bonus (optioneel, geen extra punten β€” wel meer leren) - -- **Eigen sub-agent** definieren in `~/.config/opencode/` (bv. een "animation-reviewer") -- **MCP server** toevoegen (bv. context7 voor up-to-date docs in agent context) -- **GSAP advanced**: horizontale scroll met pin + scrub, of SplitText met stagger -- **Custom Lenis options** experimenteren (`lerp`, `duration`, etc.) -- **Lighthouse score** > 90 op alle 4 metrics -- **Reduced motion respecteren** (`prefers-reduced-motion` media query) -- **Eigen `ocx` plugin** schrijven (zie kdcokenny/ocx docs) - ---- - -## Tijd-indicatie - -Setup is al klaar (gedaan in de les). Je begint bij sectie 2. - -- Sectie 1 afmaken (als niet klaar in les): 15-30 min -- 2 extra secties bouwen via opencode (2Γ— ~30 min): ~60 min -- Polish + responsiveness: 20 min -- Vercel previews checken + screenshots: 10 min -- `WORKFLOW.md`: 15 min - -**Totaal: ~2 uur.** Loop je vast? Vraag in Brightspace of plan een korte 1-op-1 met Tim. - ---- - -## Tips - -### Workflow tips - -- **Begin met een werkende `SmoothScroll` wrapper** β€” zonder dat doet niks goed. -- **EΓ©n sectie per worktree** β€” niet alles in main mengen. -- **Plan-mode eerst** voor elke nieuwe sectie. Krijg een plan, geef feedback, dan pas Build. -- **AGENTS.md update tijdens werk** β€” loop je tegen iets aan dat de agent steeds verkeerd doet? Voeg een regel toe. - -### Technische tips - -- **`useGSAP` heeft een `scope` prop** β€” gebruik 'm met `useRef` zodat je `gsap.from(".class")` selectors scoped zijn aan je component. -- **`ScrollTrigger.refresh()`** als je dynamisch content laadt β€” anders triggert het niet. -- **`prefers-reduced-motion`** kun je checken met een media query in CSS Γ©n in JS (`window.matchMedia('(prefers-reduced-motion: reduce)')`). -- **Lenis `lerp`** is "hoe smooth" β€” lager = soepeler maar trager. Default `0.1` is goed. -- **Pin-secties** mogen niet inside flex/grid die hun height anders berekenen β€” pas op met layout. - -### Deploy tips - -- Vercel pakt je Next.js automatisch op -- `npm run build` lokaal eerst om te checken of er geen TS errors zijn -- Environment vars (als je ze hebt) in Vercel dashboard zetten - ---- - -## Inleveren - -1. **GitHub repo** β€” push alle code naar `main` -2. **Vercel production URL** in README van je repo -3. **`WORKFLOW.md`** in root met preview URL screenshots -4. **Brightspace inlevering**: - - GitHub URL - - Production URL - - Minstens **2 preview URLs** (van feature-branches) - - Korte note (1-2 zinnen): wat is je trots-punt? - ---- - -## Beoordeling - -| Criterium | Punten | -|-----------|--------| -| 3+ scroll-animaties, verschillend qua techniek | 3 | -| `AGENTS.md` aanwezig + concreet | 1 | -| `opencode.json` met permissions/plugin | 1 | -| Minstens 2 worktrees gebruikt (zichtbaar in git log) | 1 | -| Vercel deploy werkt | 1 | -| `WORKFLOW.md` concreet + screenshot | 2 | -| Geen console errors / hydration warnings | 1 | -| **Totaal** | **10** | - -Voldoende = 6+. Bonus telt mee bij twijfelgevallen. - ---- - -## Veelvoorkomende valkuilen (uit eerdere groepen) - -- **AGENTS.md te vaag** β€” "gebruik clean code" werkt niet. Concrete regels werken. -- **Te veel willen** β€” beter 3 secties goed dan 7 half. Schaal omlaag bij twijfel. -- **Worktrees na merge laten staan** β€” opruimen, anders chaos. -- **`Lenis` van oude package importeren** β€” `@studio-freight/lenis` werkt niet meer. Gebruik `lenis`. -- **`useEffect` voor GSAP code** β€” gebruik `useGSAP` van `@gsap/react`. Anders Strict Mode bugs. -- **Next.js 16 async params vergeten** β€” `cookies()`, `headers()`, `params`, `searchParams` zijn nu async. Altijd `await`. -- **Geen Vercel deploy testen vΓ³Γ³r deadline** β€” minstens 1 dag van tevoren deployen. - -Succes! diff --git a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf deleted file mode 100644 index 4ad1277..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf +++ /dev/null @@ -1,143 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 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 -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -6 0 obj -<< -/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj -7 0 obj -<< -/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -10 0 obj -<< -/PageMode /UseNone /Pages 12 0 R /Type /Catalog ->> -endobj -11 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518125917+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518125917+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 2 Huiswerk) /Trapped /False ->> -endobj -12 0 obj -<< -/Count 4 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R ] /Type /Pages ->> -endobj -13 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1980 ->> -stream -GauHLfll+.&:N_CbY(a&<(7.4moq[tDOib,'^*Fg0Ek%T8:oeETChG2+MgdAAN+>i,FT<#1O!g$c7fr+r1WdrQiOdf_M^2WPehGOe0[(I0Au6IphgZ*1=(,o=MAJQ$qOd*Iul=B'HEIL!=L=NVFQ$[#2?&opB)KSmdiR+5qZREgn6npfCXG0Lef&CGIR9M`+9?_gVe*f?us)CcWB'/`7<7DR*@9&_\H.'lE"/I0ra1n0MUKdj5oqo]U'"'KM*b(pfcf(>aB?HQj5:j:L=l+<0UhrDo>ht1.t#=5(i)?9V3hB"8fMA+B[M?gV4:'0R-qn`P'>oNS.*T&Z$K2^s6T:Bkte$3`+G&`![Omcf/?L%2\abL@$uqQ;*2*-6UNSeMi-I]=u8\HP]#D]:m^uVW1L\[t!NQbG,)[egLurYCAF;C\\aS8(a=>]6e;ken51L/:l^LOTf_0c7XJV$'dk[8r.P)eFYH,S#=6W^c-p.a\Ee85n;^Au6U?*HIP>eS%1pU)F'OFZ^&=q;d20=q\IIaArp37,LMr_46[:QUt8FR>-4E4?N6"ddq`/t-NlVP^8`!Bi!PUAE'PR]*f^AIL"2cTFVbZfJoth;+A\GS"[V+h@eg+e4j1f*'))R&%&#O"_`HeG).[Z+qROfl-KhjBJQ!2Gr&.Tc_@j^Fb1(O3)F!k&[qkb]Mfp,a+#eRZ\c"*`*Ai8G2Ka5D+ks[BD"X?Ar&_j1G_riIHE>It9P#YPXT?0pon)>TXQ'^t$/_=ACA`fQY_@c6B&K.L@;Li3G&.dJbgX8]*JAp"2bcY(\mZbI.,.$E*$PSTA%HgOB>iK;eYf2=.lc0k^+.CMgX=,eccqe=c7&S!Vtl`Q,fG1P=`V"Vb^&A@No<;E1p?k2HO.^rBP5":t!V7PPbM+^j_.SQG!17Q(22a\HHdYk5NE=q=gN_a:3OdgOZF.E<"]'Y9drAr:kB#,ngZq9rQFak(+WMg:,;a4P31LLq@ePbbMMI`\k1b;sh)<^78cJ'c]1[-$VUFX]HG02q55J'#Q%J@BI6/j0[.&e>K?ACXXaQHM?@W+(A'+t\(B*lss<(i+Uarmo[GnQuS:OXfNr,eDh!a8le$eHFDlk!(2k_hH1`fKBa3n&O?n&&%Z/8Ec\7+V\otBt2q!HS0PG+l!AFHZY%0,$LdBFqAj^2#hmWXfk7SIP'$Z)N<5!o)`l%eo+5ln3n]!$6("[%[UX*`BkUD6G71U5'cid-K%Jjc_<0070,No+Q<,gPe1(ZD-fu1-UETc&g];m:e#Y>?uI]pUVZA\D]*I~>endstream -endobj -14 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2005 ->> -stream -Gb"/(968fX&AI`dqQu.o;"U(+Wi`C6j2@p>SgNu]b9/ZOCe6u`"!mm=gS5Q%0F9b$:_dIB*bfr3^Zk"^\n-Fa0<9J]14X")Gk/]QbD+sY/D"iLGRB*T.=>)-UT&j-D$TS&1(+o%EC1*g5EXpG8pZ2FBm_r+m[dP;13<%dZ,=bVWihN@s1u3jVh1XgZp(jF&1;"a68Er>*]rS%["J=klSi%Y*]dNiM(RS9qn?7W[7bt>:0,6$kO:&o7;Ck:M%ld)F`Em5ecLJGTQBrPmZt6(7G'dVdq'#!32jh\k+,:E5'.Ab.&m6CO.\IU-HZH--tT\:uC/_K`F(N.a;-%K(AQqq4$?.Oi?&\Y^D:O4K5u.c;fI+M*-M=@hNeolUtN?#ng2l8%:i]r$KFrJ2/aRLbcu`!)X_#)F2PN#6H/_Qq)7oB_+[,#G`sq"GL;_U$P(b&q)@J)(X3C#X&-3&2\/C#f/Mn8lnhsUhH7N,f,GE>I=W(K`R_\0oI?QN&NeMa&([E8FG2[#O!j5)g*MZ`]h^q0Op9B.JqPGX'*TqOPoNB<(MHcEXcPT4(aYK*8.3uf,O*%`c:mr_Y>G>I+r:k9F*MX]q^NNC7gna['Ei72.fgRS.B>oUGUrOndVM,C\7_@j\0WIZ*uf#&tUA3?cCQ0To68YAoU73#(OQTL2aIRXB'0UeN0[mY;"WO]tH=L(&X&fe6'0r6)$`l$WZa7fE?V!9K"$"\c=bTnf#0&biG9,LT^C3%fQA7KDAo/nOS.6StEWYAp`2:J[)E#O5J(uJuiccDaa]#/Z;-H(j6_45/l<%*_ijeQ$[^/XF4jQ^2UiX0gq&F%Hpo59Nn-?V,,$=B;K]tI&lP9_:bS-rBl1??p(,+GR3ThU(J&qTR;^KAPVLCC6\p/;ss^4"-<8l1*D(\AK0=hfAA5iE[RZL'endstream -endobj -15 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1415 ->> -stream -GauHK>Bf'r%"@A@k`O[R'75*GmmDH-gV#L^Z3edin1rVtL,C#r`[3]oaD\80JZqD4CjUcE*'hq"IF:Zl$P$H[o:@K5h@/B0]aHHJi<3DXq(TI3GJi9E4iTi1q#N0F3*sg[`ErKC1[X98KIi(-m"01#-6aOZiA)Qc!M&eVaGmtX9O@Et?r?,U_?VFC+;Edp^;iRc$/0#X(rk>,SU[AW04nYU*P'ieaV[UZ>u5=;T$JB8L]SDGT`<'om3h\-VeD9^ef"Bg)GF`hN&ZmVr6sTZ>%L?#iE'8nVMbW?jU2pN@JI.Jg9C$m0a0)%n*OJT"V)V)AadunCX$2-WQHN3)g8q,V^k+Xmi7=3a@+t]P`*GDDg:5+=Q?!d-e92BGa#N6)kC%G7X\R,9';#`kf%c8RguNf$=^a.JCFGP_DPo`1hFMT7Bt8Wi4@3J:A\h)>GJ=L,<(LOo[7e`i4?dR"P^?:g3L'KGkX$nkeC%pbe[??JXimKqA<9U;Oj[jNP*%_,c>[-3+Ifj-Ydt)$d99Tin9OP$(tt(NIhnrM%'=]p'N@>-(:C_98uT(2*n#:GfbI;0Ccc8@&^PbWYgtHONJ1ZeVQFc9*1h$lhA0`YeiG`LCZ?,=!5tgbS*ZWmr1#17f?'Ob7)367V$aA!#$>J;L!g=tG45l7sKu"'64#!ebTN+%?pLiiP5Lpb-iLlB].'e`MohO/H0,QQRG(a[Xn7Be,ofNb:s)4J>eN=kN4B2\.1457=7r6mO2R1l-mNpTcT'.+EWrf4'a:,do;Ar1('N6HMZ'Z_G82Ge4B7quP=XB$WS0glqY0kO^ttB!:JDcFn&T2p"'UiSo19/,adbM6/=$a$\Zf/i8h=2e>8D_CO"c6PIZCXs[ep;HNKho'U>M>a8[*nBE4ajjmiTA1R=G~>endstream -endobj -16 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1916 ->> -stream -Gatm;>?BQ=&:Vs/R$Sk-!Fs7G4_N(n[$8!l02'db_MdCZ8&:Khfh%M/qAm\$ie4gM]-TC`4Sn4>c'X$>04M]F!oSG3VlpC<$H"qJ^>m-#$8.'cDZbcL2"C`f]\)li$;TTK5LA(K64c1d65'F*)`_p!K-(aSNft;.Sr4d*J`^qG@R._c68sdME+0H5EVSb1kCf(oC>T?70\JYIfG>Lu&gqLs6Sn$eJtW<]S6DV4^>kA!$=HuOiJ3J$hng=tnM6X`Q,Aq\o5mmH/Ys&L]feA+92S`;%'TGXkA"%:=ZqQ!'S\9"Xq&27-0T.bD;npm&h(D\MfQr4I1?(1AO80A4#h_"9O"0O2MO[XFQ1+/0c*%aH,g7.6DO;I2+Gj1L"`=CSo^b#R--eJ)f!!BDsg>&,u,=CtQ%N-WK8bZWi[lkJGqiQ=,B99+)(HL15efNa_Vk4pT!1O1r/H&=N952W9K!@GLKBjq7qN6>1[\MGh&KEAR+\1Rn^M3?ub/>$GPn'ffPp$6X6JpB!pnrkm]ZW-cBkmJ@Vj!-]'pVtDF!pOO>Qj+]W',BRXg[AhYhQ&ddpAS-r;kCmocZ8[YL>i!MYsiO?p&YJAEc4-Js7Q6Rj,M1;.V6&G3;P[5qp[GVZqlZ_BCZ\_%">0r%UhnWf"'/%oea8[j,JjqAk$^",FfLooP"Zc:lPZcPX21XtL#T!6Z,-(2L30QT9H%9YYYipkeHaIM`p)h^oCn.fhF7TRLg?*PWXWUa;^\m!Mh/=CHi'p`73r#L8U)Qe:PJKl+Y7,c4Bi`Uo2R).,)8"]"N2>$D[Ra*".fgZ8$\Vnl!`gYgb@A%t=K7[R_VmM2P.R$g)>:DMGF)_)X+j[ZgN"C;N)`mWjhAcW+1'4/mLhS&P8-iP$=R$4WRF-5I-o:%\Ck'qStcF&0OrWXe02DXs7C[Fi)!]Mbt%\=Qlh"D%I,)L!k-b*!@8m)\;Xp6?:Wh(?fAA0oef!-NUP3R:0Un,$(U$HgT)+4mb6g>\#qc@Eb1jHaMD<,"<[PBQ*pN(nb8%+j26#hpC$C[;q`K<\1Z81#,(4'8;@-V9>p>N-A%;X\$$oZ;oV`U1mj6$a)8q3C=./')A5P<&:lr_fH4p6\MP*:7*N?Ofl2.-2K#UbqY;9QJODc'?*XZ1o[mdp&#pknd!nto9*J9h3f0:=b4cBB:j!b2@lRI]jnGJR?_T3W]<_QD6OQKT*iU[mV]h_#FV;`RMDZep)1&;Mca;B.F.$$'9<\U-Z9K\MOP1_Md%k,bX^\&2TB%2Yn8Dh-9*-*Fqt]!IqDEWpd44NXO9at*l3Vl0_'GqO[d_ji;]tADe4sT'maB+l<28$'Qhhg\T),pR/SI+rKX3b-7Ac1m(:LE:TN_7>_aEVm3Y$0'\/]K#lAM:Ye4mGK@T;'C5L$q?#UrS*6WrV>!Pd`7bendstream -endobj -xref -0 17 -0000000000 65535 f -0000000061 00000 n -0000000122 00000 n -0000000229 00000 n -0000000341 00000 n -0000000446 00000 n -0000000651 00000 n -0000000728 00000 n -0000000933 00000 n -0000001138 00000 n -0000001343 00000 n -0000001413 00000 n -0000001705 00000 n -0000001783 00000 n -0000003855 00000 n -0000005952 00000 n -0000007459 00000 n -trailer -<< -/ID -[<095815a8cc0df89fff07917977950c37><095815a8cc0df89fff07917977950c37>] -% ReportLab generated PDF document -- digest (opensource) - -/Info 11 0 R -/Root 10 0 R -/Size 17 ->> -startxref -9467 -%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md deleted file mode 100644 index f5ca0a8..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md +++ /dev/null @@ -1,226 +0,0 @@ -# Les 2 β€” Lesopdracht -## Bouw een scroll-animatie sectie in een worktree - -**Vak:** AI-Assisted Development -**Opleiding:** NOVI Hogeschool Utrecht -**Duur:** 28 minuten (in de les) -**Inleveren:** preview URL in chat β€” code is basis voor het huiswerk - ---- - -## Doel - -Bouw één scroll-animatie sectie in een Next.js 16 project, met: -- OpenCode **Desktop** (met onze AGENTS.md geladen) -- Een eigen worktree (via plugin of handmatig) -- Een nieuwe Sessions tab voor die worktree -- GSAP + Lenis -- TailwindCSS voor styling - -Het hoeft niet groot β€” een werkende sectie van 1-2 schermen is genoeg. - -> **Belangrijk:** dit project is **jouw repo** vanaf nu. In het huiswerk -> bouw je in deze zelfde repo verder met meer secties. Niet klaar in de les? -> Geen probleem β€” thuis ga je verder waar je bleef. - ---- - -## Vereisten (al opgezet vΓ³Γ³r de les) - -In je `scroll-demo` repo staat al: -- Next.js 15 starter (App Router, TypeScript, Tailwind) -- `AGENTS.md` met onze regels -- `opencode.json` met permissions -- `npm install`-ed: `gsap`, `@gsap/react`, `lenis` -- `components/SmoothScroll.tsx` wrapper (klassikaal gebouwd) - -Geen scroll-demo repo? Vraag in chat, of clone: `git clone ` - ---- - -## Wat moet er staan? - -- [ ] Eigen **worktree** + branch voor jouw sectie -- [ ] OpenCode draait in die worktree -- [ ] Een **werkende scroll-animatie sectie** (kies één type β€” zie suggesties) -- [ ] Volgt de regels uit `AGENTS.md` (geen Framer Motion, `useGSAP`, scope, etc.) -- [ ] **Commit + push** naar je branch - ---- - -## Stappenplan - -### Stap 0 β€” Starter forken naar jouw GitHub (3 min) - -Dit doe je **eenmalig** aan het begin. Daarna werk je in jouw eigen repo β€” ook thuis voor het huiswerk. - -1. Ga naar de starter repo (link in Brightspace) β†’ klik **Fork** β†’ kies jouw account -2. Clone naar je laptop: - ```bash - gh repo clone /scroll-demo - cd scroll-demo - npm install - ``` -3. Koppel Vercel (eenmalig): - ```bash - vercel link # kiest of maakt Vercel project - vercel --prod # eerste productie deploy - ``` -4. Open in OpenCode Desktop: **File β†’ Open Folder β†’ scroll-demo** - -Klaar. Productie-URL staat live. Vanaf nu = elke push = automatische preview. - -### Stap 1 β€” Worktree aanmaken (3 min) - -**Met opencode-worktree plugin** (in main Session): - -``` -Maak een worktree voor feature-mijn-sectie (kies een naam, bv. feature-hero). -``` - -De agent roept `worktree_create` aan. De worktree-folder wordt gemaakt op `~/.local/share/opencode/worktree/scroll-demo/feature-hero/`. - -**Daarna in Desktop:** -1. Klik **+ New Session** linksboven in de Sessions sidebar -2. Kies **Open Folder** β†’ navigeer naar de worktree-folder -3. Nieuwe Session opent met eigen chat-context β€” main blijft beschikbaar in sidebar - -**Of handmatig** (als plugin niet werkt): - -```bash -git worktree add ../scroll-demo-hero -b feature-hero -``` -Dan in Desktop: **+ New Session β†’ Open Folder β†’ ../scroll-demo-hero**. - -### Stap 2 β€” Sectie kiezen (2 min) - -Kies één van deze: - -| Type | Wat | Moeilijkheid | -|------|-----|--------------| -| **Hero met SplitText reveal** | Tekst die letter-voor-letter verschijnt op scroll | ⭐⭐ | -| **Features grid stagger** | 3-koloms grid die in-fade'd, staggered | ⭐ | -| **Testimonials horizontaal** | Horizontale scroll-sectie met cards (pin) | ⭐⭐⭐ | -| **Gallery met parallax** | Afbeeldingen die parallax-en bij scroll | ⭐⭐ | -| **Stats counter** | Cijfers die optellen wanneer in beeld | ⭐⭐ | - -### Stap 3 β€” Plan-mode (5 min) - -In opencode (start in plan-mode of `Tab` om te wisselen): - -> Plan een [type sectie] component die voldoet aan de regels in AGENTS.md. Toon me het plan: welke files maak je, welke GSAP API gebruik je, hoe sync je met Lenis? - -Lees het plan. Stel vragen / geef feedback. Niet meteen op Tab naar build. - -### Stap 4 β€” Build-mode (15 min) - -`Tab` β†’ build. Geef commando: - -> Bouw de geplande sectie. Plaats hem in components/ en importeer in app/page.tsx zodat ik 'm direct kan testen. - -Laat het bouwen. Lees mee. Stel correcties als iets ingaat tegen AGENTS.md. - -### Stap 5 β€” Testen (5 min) - -In de worktree: - -```bash -npm run dev -``` - -Open `http://localhost:3000`. Scroll. Check: -- Smooth scroll werkt (Lenis) -- Jouw animatie triggered op het juiste moment -- Geen console errors -- Geen hydration mismatch - -Werkt het niet? Vraag opencode om te debuggen. - -### Stap 6 β€” Commit + push (3 min) - -```bash -git add . -git commit -m "feat: [jouw sectie]" -git push origin [jouw branch] -``` - -### Stap 7 β€” Vercel preview ophalen (2 min) - -- Open Vercel dashboard (link in Brightspace) -- Vind de deploy voor `[jouw branch]` β€” status "Building" β†’ "Ready" (~30-60 sec) -- Klik β†’ preview URL openen -- Test scroll-animatie op de live URL -- **Plak de preview URL in chat** zodat anderen 'm kunnen zien - -### Stap 7 β€” (Optioneel) Cleanup - -Klaar met deze worktree? In de main Session: - -> Sluit de feature-X worktree en merge naar main. - -Of handmatig: - -```bash -git worktree remove ~/.local/share/opencode/worktree/scroll-demo/feature-hero -cd ~/scroll-demo -git merge feature-hero -``` - -Sluit ook de feature-Session in Desktop (right-click β†’ Close). - ---- - -## Veelvoorkomende problemen - -| Probleem | Oplossing | -|----------|-----------| -| `useGSAP is not a function` | `npm install @gsap/react` | -| Hydration mismatch warning | `"use client"` mist of `useEffect` voor GSAP code | -| Lenis import error | Niet `@studio-freight/lenis` β€” gebruik `lenis` package | -| ScrollTrigger niks doet | `gsap.registerPlugin(ScrollTrigger)` boven in file | -| Geen smooth scroll | `SmoothScroll` wrapper mist in `app/layout.tsx` | -| Plugin niet beschikbaar | `ocx list` om te checken, evt. `ocx add` opnieuw | -| Worktree-tool niet bekend | Plugin niet correct geΓ―nstalleerd of opencode niet herstart | -| `gsap.context is not a function` | Oude versie β€” `npm i gsap@latest @gsap/react@latest` | -| `params is not iterable` / `Cannot read params` | Next.js 16: gebruik `const { id } = await params` (async!) | - ---- - -## Klaar zijn = je hebt: - -1. Een **eigen fork** op GitHub + Vercel gekoppeld -2. Een werkende **scroll-animatie sectie** in jouw app -3. Gebruik gemaakt van **OpenCode + worktrees** -4. De **regels uit AGENTS.md** gevolgd -5. **Smooth scroll** werkend via Lenis -6. Minstens 1 **preview URL** live op Vercel - -## Aansluiting op het huiswerk - -**Niet helemaal klaar in de les?** Geen stress. Je staat al goed β€” Vercel is gekoppeld, AGENTS.md staat klaar, één sectie werkt (of bijna). In het huiswerk maak je de eerste sectie af Γ©n voeg je 2-3 secties toe naar een complete landing page. - -**Wel klaar?** Stop hier en zet je laptop dicht. Thuis bouw je gewoon door in dezelfde repo. - ---- - -## Voorbeelden van prompts die werken - -**Goed:** -- "Plan een hero sectie met SplitText reveal. 1 lange koptekst, 1 paragraaf. Lees AGENTS.md eerst." -- "Bouw een features grid van 3 cards die fade-in'en met stagger 0.15 wanneer de sectie 80% in beeld is." -- "Voeg een horizontale scroll sectie toe met 5 cards. Gebruik pin: true en scrub: 1." - -**Te vaag (krijg je rommel):** -- "Maak iets met scroll." -- "Voeg animaties toe." -- "Maak het mooi." - -**Tip:** wees specifiek over wat Γ©n volg de patterns uit AGENTS.md. Hoe specifieker, hoe beter. - ---- - -## Reflectievragen (1 min, niet inleveren) - -- Werkt OpenCode anders met goede AGENTS.md dan zonder? -- Voelde plan-mode β†’ build flow natuurlijker? -- Waar zou je worktrees morgen gebruiken op je eigen project? diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf deleted file mode 100644 index ba03c3b..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf +++ /dev/null @@ -1,187 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 7 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 -<< -/BaseFont /Symbol /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj -6 0 obj -<< -/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/BaseFont /ZapfDingbats /Name /F5 /Subtype /Type1 /Type /Font ->> -endobj -8 0 obj -<< -/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -10 0 obj -<< -/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -11 0 obj -<< -/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -12 0 obj -<< -/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -13 0 obj -<< -/PageMode /UseNone /Pages 15 0 R /Type /Catalog ->> -endobj -14 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518125917+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518125917+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 2 Lesopdracht) /Trapped /False ->> -endobj -15 0 obj -<< -/Count 6 /Kids [ 6 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ] /Type /Pages ->> -endobj -16 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2059 ->> -stream -Gb!;d96\iE']/^gg^WEL86&<3ZXF3;V<;qm+e`_XE?-?FCJ2Zj)MVT`=10>8Bp+2Z0S_i^b*MJmIeRn4R.sCVCQnn5)8bn]"j144!GW8eL6qCIBRDZCF.$Z&5V1g"!X9pSe/.1E6\+202dcXiht2`nfjO]ApjV-'_fMD>4ifWW?5OB4kF.u\"7`ANqX1-M(3(3ZeM_DP2cr<%f]OdV!oc,hTpbQa$H!mkECrm3Cgp;_F'tA%n*7g8"h^1#?:6Ru'DPhp4IVNh@a'IgMkcK1Ll7FWiKQRr6tRkaj`;9q_=30,eWFc+T6N8c>*4pf79>1n6Ym>o(7"*"X/nMSpZ.661nH-bJFR4ssiSqDtYW\loZj@$k!l@W44m=6-oe"at?&/p&T[Y0_j%sm,^#p$OUB,%BPQpe5X\@W/N6^J%BU%aCAh2,BPspFaAgDV31k)%bbX.GXkBD>e*\i)lnQXmRd-T3'oH!&*?>/6h*OD#C[`\TUmn/s7*(kY53bB&fciDg3fk'H\DMI1-eD[EV_89YU_fUbQS"+d?ZbSpA:"'J279gRFVik-9TE5I(!JRp[E^0[qPmo/:9]l(GaZ2PkerKJob]0+bl_8a1^m!ENr34X=Pfk=4.X4q4#E$TCrXeZ)I/\6`A.ta,T#*d0ipI#18<1seL9H>74^R7Y24KkCVSRJ%hF%\C&N$Ne=bSshIMHI[oL;B*i4YWLrmcnR?)n%.Fd8SIA^aZ4:LiOe3YO(E+=B?`$'3-3Qp0o[^)GR-feOLi\AHIB>WJA$o;B:S=884#b_J"jo9'IuojRr3%LFdXcZcp>O;A]W_0kENm,?)QSm8`V#O"]8$_(iK8TZF7i&8NjK*NiHR.1f$o=.olr;Mg=+4n`bSa,/"Aj0\k>,SeJ&8daQ*j_2/kBM&FTs>+VfP]%91*eR+/C=(m/T<)>>)A<@'2WZjes)liGD\e_!et-R/7L>_3uF&Bia=eD`*m^9^/sDBq4puePq^IZChch8X,;pqYn"fA."E?nN7NsNLk_p1d94Shg[/hAWnXrQfe#_VVui'"SF7KhcJCo%'1a);=iiH4.nk&UhQ!o^H,%$9FCuIU*,EgnG3u6s@hRlro4J/PruqmH^=_V/?_nMEB?oFX'Qo28[3:-QqOS0#S*mJPlo#&3[W+!E`^Ye83L*9uLB8#)3mD=3'P%c6H^@2iWK+pnnCeui3;AS0D=32BX@?H=Jg*<+endstream -endobj -17 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2366 ->> -stream -Gb"/(>BAOW(4Ol=3&&ge`UoOq8TkVfn(6?l2F3^u;kl;>cZ=+8QJgGIsY,J!?\C)^f12fVpUQ4IrTd?!*M%lhtIo-@EE"NJQdCAE#K3'.,-f5S9?@:ip%8-[m5gRX+XUJff.1$"<`+!@&a^0QaMWT=(tB<=K=AY>kakPVl_0m1m:VgXk2$)^??F]K-`L;<#1fq.$uj>0LeO]//34@c'_51>9Kh#q%Ct(,E#M6:&(VVbB&:7&d2N7DA-/LZKm+AHaU7_R[Mh-L_X:a$@@:p"I4BQ.87`9@U"C)f9`FH4B7VRl>;cFkm(c40slN;fSh540ani9pG:^NsG7eP#:A)e$*.D`9i^LNm>A\7OqU$S^?+[%/g++%m/Ur"0\MCI\re&A&9/OUr]UE1$A:R^0J(j7],J2PJKuk\ptl*)!s;_rrgnA^>HZMA+'r!@rCVF/^m[;YNs+MBgacNYlt\B"ek'8"1S-?)dfG.OaATeJiaj%R@K;Ki5oZ)c^IL8l=t;1hD`B.6g-tp99YU8T-MeJ.JYTug%]_^L;1^$OnhBP4N35$nc+O(LnM%[Vbo.hb3Lo5c])U1An4n+gWHqA$?Znh]WBB7/apm/][@[4s238ojGAFFceg$k4bZ:!L(DAOq,6'(OV?j5nEqh6]fVs"la\gGTBH>H.W#X@[25'OIY/I5CI6+?:f(B?Xh^),(g_2r?m..>N:k2\&3=)kPgC9[4mT5QBqFD[UjlV3L;S5KeRin3Wt\d*i#CGb8BudlUrIY!^'A^p7h2k/ns`6@V$a6pf%$o\TKCrD[1a-1HB9e,/Oa2l7_?()=S`"?sn!/tgq8^H`NLP]#6Wn60^6?-5&qnno^RQas]=4\7V9K*\Gf^r!:VqMVXoqO(LZmmq5uI8blqQI6?1j*>_X`%E>'*PQHSc>$&K.@(J4C62n'ClondQ,Aqdildb,Al;\PT0,=^7'ZCo'0l?n(B_s[AH83(7ZhHsW&u!i5EED"c_E5Xj0e@,p]g$lVtk&)2Wsqb;E?(ErPSo/4B(E`T9;>qHU"B'Gpn"u:"QO2G3S/HC?m?pM9S+?4@:;4MgSYeN9"&'+X&f20hoafug0'S41VoVnD%r'ne`jFM=hmX1R:.pFX;eHYL*BOW.&M8ndN2CO=;[LD2=OYHf&3+2EanGIPn=-HJr8V>&cJFlP^R-fHIe2ae#!"#OAL**$$BfA)5H0+="Lk?q;]Tg=Q$nkl&B38,gf=b87K;J5tuR[qaE5$L`;-Ti3M99gqhXp\]Vk?(`Ijif2(P$2X9EOQU[2bIMfYq^WTj8@35*fc%h^*[E=F*@_G\8oKh%\QV)-3:XBU8[E/NJWgmp`]U`Oe5ufS']VXk:,59:DC5o?DL>c(0[T0_J33$Vp!?62uFciRCd`R/)3t8(hT2[%lmSHW)h>?Ks"!Klj0Wr%BVhnt76(eZX`3Kc6QDf]JiJDCXtR3M6N<2`ou#23L^p=6B="rMeOo,AXrUIictP/pso`=/s@4b)dPU:r@#Yn'CT`?E40#,kh`nPLoelrth&+KjYlV(lLhu].\L7jtul+eVo0#25I0kbR8Ys^faIA@.lfeKE,N~>endstream -endobj -18 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 550 ->> -stream -GasIda`c,u&A7u11Gg=mUdi@P;!^\*'6YN?Z?fOSHrQ(A5dV/f\4BFh#"Z8DU(KlErbQ\pf4Eo"2%IF>j5'Y/%F&"^%K_]=/^msN3CG7E#p%ADm0e6hH=u0Z*\<#P3o7O]klqUiL\U>,ol4CS$%]i8Y/rVU-f/S'Qmdh0"2%/\]bGM9/WGcLZm."(JLPr4>Mls+lSk;,C?W8tR29#P$neL`EiNS0P*7CMS";sRtdrS>('aUj@KiYta#*Z8Et3kmLMI4$b0W-bhZ,X^:he$mJen6W+/X%]Re`hi/RBRG2q0n\5/E9kUeC"0Xa^W]NqIKMBAg4]~>endstream -endobj -19 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1956 ->> -stream -Gb"/(968iG&AIa;m.=a7XW[ZI6-tsYSL1=?:ZB;b/o_@X$&c/]66U'Tm\.D/$W;B)nOo:T12/=S,SC7RKb*&n+9iP;XSIlsJDDYJ?85h-VC0.^&MW$rQW*(*6b)3kn:8$+\cE$K\Fp*fbUL>k6j>b]TVtYN00gpTPR8+D*9[4CLJl%3F,'ed-2!N'U6mF]i8&EaunR6Bb$VNhL=74hp"hr;.<3'+6l`o8I\1[qlmE9[8U(>qrA4aMBeYe;70(j3FmYn^:@Vh8ZUl^["g`u2thj-:X-^4rqC.!@&$UL.5q7+E0qF"*9L)3NYQ'XDb=6qGpt,'?-QWX,\l[r7A7;a.Rom.5n/tMbqk,&6g-`\/5\NA`6&orj$CXsfgJr$+g>"3o/9DI].Q=Ch/8Ica=S-d(':=Np?ui2'>bkAr1L1t`gV..Ic$&W#ao[D'4]u5^46,g_%9X;F99,3C6S)E+nN^6P:lI5Aj>(T?-9O49Q(&PW,>J2FT%7(NdO-@+hNgTPm+n_i7*aYLMbj*JDg+QNaB-I%Z+obOL3D_rZkC'bE2X7W6.X5Q]4jU>7-0bP;,_d(4a'p#SM-V5$*%T-5uc_A#O?&>pGl9bn[R6\p/1U.[Yu=`lNSF\4UtYF/:h"C\V-qW442+j"3q;e$0U2'aTtU1YDtF>cWI:,?Fl4:LClWde_%moIm8=rjdWG7J$D?k6lG\3*Dg)2UYG/rL!R:h,J1>,9]PpF[+/H8r(?9_L>)dCDa)Ygs\^Qf/??4@.`o$(PZL+,A76gqPO*cc:^3Su@"CCEDJ3Z>T3dcek?.K/S:+Uo!!qp"3=^qQZrOrhk/,H9*4C(CeWtX'#p`nH86<9ZI<2If4)k7^ZikQDkVe;3p9a'R:"q+?#t0t$$rI#q8=;E'0qioCJ.=2:"_\?%+2q-1`VF%*49mOcM.a;0@>lI4[Jk13.GhBUM)[$gk-b#:SSkK8?,>hRJW.gL[ACYFiC$br`e+9!oPA0:BiJ#e^Agc^CMH_bGR9RW9I;^YC!+i,),SE"dBZ`./iT&$C]6)W;sRepqja<*<%KghGcR$40qe(IE]3lYH:;_Oc7fi&O)c*d/*l^i..5#2Dh.s_!FDQ"aX<$p(ebL2X*>n4!aT5Sp$\Vs1O34j<+/"FI)q+BJTgIadbOk#&65:-*4-@#@-$2t*G$ITiAL;d;\[!CMV8=o"3kIE,=pWrI2]AoJ#c;tk'5ZTkXZ"'MOWjd$DksU="2LIQnCq`"aqWZsc#V3!-Ma@?_D+#o;\."9Dq0X9'QotpfdX0u9[IT.Ra?9#jQ]]R$;%o@6ERAKN*,LJ*Q&+B7^^NO_h,?Ll<\7)pdg?Nl"\@4^gAa-b6?J\SbK(CXbhmm+~>endstream -endobj -20 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 470 ->> -stream -Gas2E:QO3`&B4,6'^o)6;?r9CIlVDsY.>6\GH9((Q\ZJL(E>?T;Me1VP5ktZC2@%=SG/L3o,&Z(ma(\5pmbjl^gN"H!EQ"X:[g5;?5&Jk>,=Na'58dZ=aKki/<^'NaCI&93+9p3"m),Ab8b'B=j%S?a5S^[O>Xm9"`g](1H5/pN0LTS,&L$IYQ!b3*UI?JNp`Xd@7OGtK9&`tWMq)Xg1o[fN5#>eon[PZd^SgJ?*?!M)F%SOQq;"3s7kimD6(,:-,0/F?e=%l&)Bb:^QICoC0g=oFC\BN?1B;VKg\&hD:oV9nl)fp632LcHB)LKnS1uo:(%fsLsMfF*non3&UcA(*6Unq-TrmWPLG5.!Hg(XIZPpfTCcg:em:ALoU%CJB#gfI\%I:_:*k2@bn?eLFn((3A)$cajo8`bF+PlL^++m5^@sS'^+Pendstream -endobj -21 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1412 ->> -stream -Gat=*gN)%,&:N/3lsqG!Ei+Qtn-2'&DX0:(A8F2*m2*H$'L^=Q!=#@$\6n+s8DS)RmaRCGP/s3*!Dha_YZR0$qRRK8C="JbF)0cKs0&=0OWMLgFC#VuZ1$f)oW@\=l>8ik`(f`9&]"\%K?VDW4Hu=mp^KW5[Gme-%;E4]&HJUJie&j-;.A*CkFX;0W!i#u;I;lp/n0>qE)PP[krcd#q"8k"Ke;?-SN@7CN+PDAC$e)](8m'CsAI7=8&E.dPch\E>!#"Jkln^!6VhY"q)r=/cSrBq8#(r:8W3)I9M:Xg7[V;gA(B7GSg=bG[oo>Z=o@7Hbf4n$spT5U,X+CmhI]oiIk\XiGr"%j>GnW0n)q>n;dG?R$E7scKI#[/]#/1=F?fPAFSH)C_L(A5#L<=7\IF9Yh/8"4uf8mGEMr%^j'pQ:DKi7E>BLI8\V8s1S(j]b]2uE0)h;l46.7A8kp8'8TQtFXa/AVD>$>6fSc=1[*)$]g_)sQ<)Kmo(Um9C;@G;-(`AU-#N8>&:q'dUO1KJP\td#3YaT"+s0\CNg&[$]>u^D\06Q*@Z$je2-ea2s'\_Or=:gj];eG`T9'.Me0N)98.815:RJ%)9DofBY?PShkW?l1RaRIA,Db][FkaosrkGh3"$/l:89!Id-nCO'EP$O>DTd9DN*eJ[IQ\;X>S*>?A>2?Dm;PbJ'^d8je>qa^+lTlh2r7OiW1:dUdT%]-'7Y4C#39ZAVYf&V"'J]XWc;j]#?">/?H_Q*1cn[5-=+Y7Y9^k7;O0*Ont=I5+U8mPZQD?4K(?K:S=!Rpc4e`iuI&K(ER2MAmo~>endstream -endobj -xref -0 22 -0000000000 65535 f -0000000061 00000 n -0000000132 00000 n -0000000239 00000 n -0000000351 00000 n -0000000428 00000 n -0000000533 00000 n -0000000738 00000 n -0000000821 00000 n -0000001026 00000 n -0000001231 00000 n -0000001437 00000 n -0000001643 00000 n -0000001849 00000 n -0000001919 00000 n -0000002214 00000 n -0000002307 00000 n -0000004458 00000 n -0000006916 00000 n -0000007557 00000 n -0000009605 00000 n -0000010166 00000 n -trailer -<< -/ID -[<9ede7c3cd6e0a19e0d40b08da394d3dc><9ede7c3cd6e0a19e0d40b08da394d3dc>] -% ReportLab generated PDF document -- digest (opensource) - -/Info 14 0 R -/Root 13 0 R -/Size 22 ->> -startxref -11670 -%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesstof.md b/v2-klasB/Les02-OpenCode/Les02-Lesstof.md deleted file mode 100644 index 684564c..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Lesstof.md +++ /dev/null @@ -1,706 +0,0 @@ -# Les 2 β€” OpenCode Pro -## Lesstof β€” Rules, Worktrees & Scroll Animations - -**Vak:** AI-Assisted Development -**Opleiding:** NOVI Hogeschool Utrecht -**Onderwerp:** OpenCode Desktop professioneel inzetten met regels, config en parallel worktrees - ---- - -## Inhoudsopgave - -1. [OpenCode in 2026](#1-opencode-in-2026) -2. [TUI vs Desktop β€” welke kies je?](#2-tui-vs-desktop) -3. [Plan/Build/@explore + /init](#3-planbuildexplore--init) -4. [`AGENTS.md` β€” het officiΓ«le regelbestand](#4-agentsmd--het-officiele-regelbestand) -5. [`opencode.json` β€” config & permissies](#5-opencodejson--config--permissies) -6. [Plugins & `opencode-worktree`](#6-plugins--opencode-worktree) -7. [De scroll-animatie stack (Next.js 16)](#7-de-scroll-animatie-stack) -8. [Vercel & Preview Deployments](#8-vercel--preview-deployments) -9. [Project setup van A tot Z](#9-project-setup-van-a-tot-z) -10. [Workflow patterns](#10-workflow-patterns) -11. [Bronnen](#11-bronnen) - ---- - -## 1. OpenCode in 2026 - -OpenCode is een open-source AI coding-IDE. 60.000+ GitHub stars. Gemaakt door SST. Provider-agnostisch β€” werkt met OpenAI, Anthropic, Google, Groq, lokale modellen via Ollama, en meer. - -Vergelijkbaar concept als Cursor of Claude Code, maar: -- **Open source** β€” geen vendor lock-in -- **Provider-keuze** β€” niet vast aan één AI-bedrijf -- **Script-vriendelijk** β€” perfect voor automation, CI/CD, plugins -- **Twee smaken:** Desktop (UI) en TUI (terminal) - -### Desktop App (vandaag onze keuze) - -Native app voor macOS, Windows, Linux. Download via https://opencode.ai/download. - -Layout (globaal): -- **File tree** links β€” bestanden + folders, right-click voor acties -- **Diff viewer** midden β€” visueel groen/rood met klik-accept/reject -- **Chat panel** onder β€” prompts naar de agent -- **Sessions sidebar** links β€” meerdere parallelle sessies (voor worktrees!) -- **Ingebouwde terminal** β€” onderaan, optioneel - -### TUI (terminal) - -```bash -npm i -g opencode-ai@latest -opencode -``` - -Zelfde engine, zelfde commando's, zelfde plugins. Goed voor SSH, scripts, CI/CD. - ---- - -## 2. TUI vs Desktop β€” Welke kies je? - -| Aspect | TUI | Desktop | -|--------|-----|---------| -| Leercurve | Steiler β€” alles via keyboard | Vlakker β€” visueel | -| Diff review | Plain text inline | Visuele diff-viewer | -| Files browsen | Via @-references | File tree zichtbaar | -| Sessies parallel | Tmux / aparte terminals | Sessions sidebar | -| SSH / headless servers | βœ… | ❌ (X-server nodig) | -| Worktree plugin auto-spawn | βœ… (spawnt terminal) | ❌ (handmatige tab) | -| Snel typen / muscle memory | βœ… | Ok, maar trager | -| Scripted workflows | βœ… | Beperkt | - -### Wanneer wel TUI? - -- **SSH-only servers** β€” geen X11 nodig -- **CI/CD pipelines** β€” scripted invocaties -- **Tmux fans** β€” past in je tmux setup -- **Worktree power-users** β€” auto-terminal-spawn van de plugin - -### Onze keuze voor deze les en eindopdracht: Desktop - -Sneller te leren, visueler, beter voor parallel-werken via Sessions sidebar. - ---- - -## 3. Plan/Build/@explore + /init - -### Plan vs Build mode - -OpenCode heeft twee modes β€” Tab om te wisselen. - -**Plan mode** is read-only. Tools: `read`, `grep`, `glob`, `webfetch`. Geen edits, geen shell commands. Goed voor brainstorm / verkenning. - -**Build mode** is default. Alle tools aan. Schrijft code, voert commands uit. - -Best practice: begin **altijd** in plan-mode. Laat het uitleggen wat het van plan is. Geef feedback. Pas dan Tab β†’ build. - -### Sub-agents - -Drie ingebouwde sub-agents: - -| Naam | Wat | Wanneer | -|------|-----|---------| -| `@explore` | Read-only verkenning | "Waar staat X?" "Wat is dit?" | -| `@general` | Brede taak-agent | Default voor de meeste taken | -| `@scout` | Gerichte zoekopdracht | "Vind alle plaatsen waar..." | - -Manuele aanroep: `@explore vind alle authenticatie code`. Primaire agent kan ze ook autonoom inschakelen. - -### `/init` commando - -In de chat: `/init`. OpenCode scant je repo, stelt vragen, en genereert/update je `AGENTS.md`. - -Doe dit **als eerste actie** in elk nieuw project. Daarna kun je het bestand uitbreiden met jouw specifieke regels. - -### Belangrijke keybinds (Desktop Γ©n TUI) - -| Shortcut | Actie | -|----------|-------| -| `Tab` | Wissel plan ↔ build | -| `Enter` | Submit | -| `Esc` | Cancel | -| `Ctrl+X h` | Help (alle bindings) | -| `Ctrl+D` | Exit (TUI) | - -Leader-key voor commando-specifieke bindings: `Ctrl+X`. Customizen via `~/.config/opencode/tui.json` of in Desktop settings. - ---- - -## 4. `AGENTS.md` β€” Het officiΓ«le regelbestand - -`AGENTS.md` is markdown in je project-root die OpenCode automatisch in elke context laadt. Officieel format. Expliciet vergeleken in de docs met Cursor's `.cursorrules`. - -### Twee locaties - -| Pad | Wanneer | -|-----|---------| -| `./AGENTS.md` | Per project (in repo, geversioneerd) | -| `~/.config/opencode/AGENTS.md` | Globaal voor alle projecten | - -### Externe regels referencen - -```markdown -# Stack rules -@rules/styling.md -@rules/security.md -``` - -OpenCode laadt deze lazy via z'n Read-tool β€” pas wanneer relevant. - -### Voorbeeld voor scroll-animatie project - -```markdown -# Project Rules - -## Why this stack -High-end scroll storytelling site (Awwwards/FWA niveau). -GSAP + Lenis = pro standaard β€” Apple, Stripe, OpenAI, Active Theory, -Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor -scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. - -## Stack -- Next.js 16 (App Router, TypeScript, Turbopack) -- TailwindCSS voor styling -- GSAP 3.15+ (incl. ScrollTrigger, SplitText) met `@gsap/react` -- Lenis 1.3+ (`lenis/react`) voor smooth scroll - -## Hard rules -- Geen Framer Motion, react-spring, AOS -- Animaties altijd in Client Components (`"use client"`) -- ScrollTrigger registratie 1x per file -- Animatie-code altijd binnen `useGSAP(() => {}, { scope: ref })` -- Nooit `useEffect` voor GSAP code -- Lenis sync: `gsap.ticker.add` met `autoRaf: false` -- **Async params (Next 16):** altijd `await params` en `await searchParams` - -## Patterns -- EΓ©n `` wrapper in `app/layout.tsx` -- Refs i.p.v. globale selectors; scope animaties -- Tailwind voor layout/typografie; GSAP voor transform/opacity -- Geen `transition-*` Tailwind classes op geanimeerde elementen - -## Done = -- Geen hydration warnings -- Werkt in React Strict Mode -- `ScrollTrigger.refresh()` na dynamische content -``` - -**De `## Why this stack` sectie is cruciaal.** Hiermee snapt de agent *waarom* -we deze keuzes maken β€” niet alleen *wat*. Resultaat: hij stelt op eigen -initiatief geen Framer Motion meer voor, want hij begrijpt dat we voor scroll -storytelling werken. - -### Tips voor goede AGENTS.md - -- **Concreet, niet vaag.** "Gebruik clean code" werkt niet. "useGSAP alleen, geen useEffect" wel. -- **Versie-pinning** voor libraries die snel veranderen. -- **Definitie van klaar** β€” checkpoints die de agent zelf kan toetsen. -- **Anti-patterns expliciet noemen** β€” "Geen Framer Motion" voorkomt veel discussie. - ---- - -## 5. `opencode.json` β€” Config & Permissies - -### Locaties - -| Pad | Scope | -|-----|-------| -| `./opencode.json` | Per project | -| `~/.config/opencode/opencode.json` | Globaal | - -### Basis voorbeeld - -```json -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-4o-mini" -} -``` - -Model formaat: `provider/model-id`. Voorbeelden: -- `openai/gpt-4o-mini` -- `openai/gpt-4o` -- `anthropic/claude-sonnet-4` -- `groq/llama-3.3-70b-versatile` - -### Permissies - -```json -{ - "$schema": "https://opencode.ai/config.json", - "permission": { - "bash": { - "*": "ask", - "git *": "allow", - "rm *": "deny" - }, - "edit": { - "*": "deny", - "app/**/*.tsx": "allow", - "components/**": "allow" - } - } -} -``` - -Drie levels: `allow`, `ask`, `deny`. Glob patterns voor bash commands en file paths. - -### Plugin syntax (npm) - -```json -{ - "plugin": [ - "@anthropic/some-plugin@1.0.0" - ] -} -``` - -OpenCode installeert plugins automatisch via Bun in `~/.cache/opencode/node_modules/` bij het opstarten. - -### MCP servers (optioneel) - -```json -{ - "mcp": { - "context7": { - "type": "local", - "command": "npx", - "args": ["-y", "@upstash/context7-mcp"] - } - } -} -``` - -Voor up-to-date library docs binnen je AI-context. Niet vereist voor deze les. - ---- - -## 6. Plugins & `opencode-worktree` - -### Wat is `opencode-worktree`? - -Plugin van **kdcokenny** (community maker, ook achter `ocx` en `opencode-workspace`). - -**Wat doet het?** -Geeft de agent twee nieuwe tools: -- `worktree_create` β€” maakt nieuwe branch + worktree-folder -- `worktree_delete` β€” commit changes + cleanup - -**Worktree-locatie:** -`~/.local/share/opencode/worktree///` - -### Installatie via `ocx` - -```bash -# Eenmalig: ocx installeren -curl -fsSL https://kdco.dev/ocx/install.sh | sh - -# Plugin toevoegen -ocx add kdco/worktree --from https://registry.kdco.dev -``` - -Restart OpenCode na install. - -### Verschil Desktop vs TUI - -| Actie | TUI | Desktop | -|-------|-----|---------| -| `worktree_create` aanroepen | βœ… | βœ… | -| `worktree_delete` aanroepen | βœ… | βœ… | -| **Auto nieuw terminal venster** | βœ… | ❌ | -| Nieuwe context openen | Auto | Handmatig (Sessions sidebar) | - -In Desktop maakt de plugin de worktree, daarna open je hem zelf in een nieuwe Sessions tab. - -### Workflow in Desktop - -1. In je hoofd-Session: `Maak een worktree voor feature-hero.` -2. Plugin maakt worktree-folder -3. Klik **+ New Session** in sidebar -4. **Open Folder** β†’ navigate naar de worktree-folder -5. Nu heb je 2 sessies parallel in sidebar β€” main + feature-hero -6. Klik tussen sessies om te wisselen - -### Handmatig alternatief (zonder plugin) - -```bash -git worktree add ../mijn-repo-hero -b feature-hero -``` - -Dan handmatig nieuwe Session openen op die folder. - -### Best practices - -- EΓ©n opencode-instance per worktree-directory (gouden regel) -- Niet wisselen tussen sessies van verschillende worktrees binnen één session-context (cwd-bug) -- Branch name = folder name (makkelijker zoeken in `git worktree list`) -- Cleanup na merge β€” niet stapelen - ---- - -## 7. De scroll-animatie stack - -### Next.js 16 (mei 2026) - -Uitgekomen oktober 2025. **Breaking changes ten opzichte van 15:** - -- **Async API's verplicht:** `cookies()`, `headers()`, `params`, `searchParams` zijn nu allemaal async. Altijd `await`: - ```tsx - // app/products/[id]/page.tsx - export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params; // VERPLICHT in 16 - return
{id}
; - } - ``` -- **Turbopack is default** voor `dev` en `build`. Webpack alleen via opt-in. -- **`middleware.ts` β†’ `proxy.ts`** met nieuwe Node.js runtime. -- **`revalidateTag` vereist 2e argument** (cacheLife profile). -- **Node.js 20.9+** vereist (18 is dropped). - -### Waarom GSAP + Lenis? - -Dit is **dΓ©** stack voor scroll storytelling op pro-niveau. Niet "een" optie β€” de standaard. - -**Wie gebruikt dit?** -- **Apple** β€” product pages met scroll-revealed content -- **Stripe** β€” homepage en product pages -- **OpenAI** marketing pages -- **Awwwards / FWA winnaars** β€” vrijwel altijd GSAP -- Pro studios: **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** (makers van Lenis) - -**Waarom niet Framer Motion?** -- Framer Motion is uitstekend voor **app UI** β€” modals, page transitions, micro-interacties -- Maar voor **scroll storytelling** (jouw eindopdracht): GSAP heeft betere timing-precisie en GPU-optimalisaties -- Daarom: Framer Motion voor app-werk, GSAP voor scroll-werk - -**Andere voordelen:** -- **GSAP sinds januari 2025: 100% gratis** onder de "no charge" license β€” incl. ScrollTrigger, SplitText, MorphSVG (vroeger premium) -- **Lenis** integreert naadloos met ScrollTrigger via één RAF sync -- Beide zijn **populair op v0 en Cursor** β€” AI's kennen de patterns goed - -Schrijf dit op in je `AGENTS.md` (sectie `## Why this stack`). Dan kiest je AI agent dezelfde keuzes om dezelfde redenen. - -### Packages - -```bash -npm install gsap @gsap/react lenis -``` - -**Belangrijk:** -- Pakket heet **`lenis`** (niet meer `@studio-freight/lenis`) -- `@gsap/react` geeft je de officiele `useGSAP` hook - -### De `useGSAP` hook - -Drop-in vervanger voor `useEffect`/`useLayoutEffect` voor GSAP code. Doet automatisch cleanup via `gsap.context()`. Werkt veilig met React Strict Mode en SSR. - -```tsx -"use client"; -import { useRef } from "react"; -import gsap from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; -import { useGSAP } from "@gsap/react"; - -gsap.registerPlugin(useGSAP, ScrollTrigger); - -export default function FadeIn() { - const ref = useRef(null); - - useGSAP(() => { - gsap.from(".fade-item", { - opacity: 0, - y: 60, - duration: 1, - stagger: 0.15, - scrollTrigger: { - trigger: ref.current, - start: "top 80%", - }, - }); - }, { scope: ref }); - - return ( -
-

Hello scroll

-

Ik fade in.

-
- ); -} -``` - -### Lenis sync met ScrollTrigger - -Zonder sync lopen GSAP en Lenis hun eigen requestAnimationFrame β€” animaties haperen. Officieel patroon: `autoRaf: false` op Lenis, GSAP's ticker drijft Lenis aan. - -```tsx -// components/SmoothScroll.tsx -"use client"; -import { ReactLenis, type LenisRef } from "lenis/react"; -import { useEffect, useRef } from "react"; -import gsap from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; - -gsap.registerPlugin(ScrollTrigger); - -export default function SmoothScroll({ children }: { children: React.ReactNode }) { - const lenisRef = useRef(null); - - useEffect(() => { - function update(time: number) { - lenisRef.current?.lenis?.raf(time * 1000); - } - gsap.ticker.add(update); - gsap.ticker.lagSmoothing(0); - return () => gsap.ticker.remove(update); - }, []); - - return ( - - {children} - - ); -} -``` - -In `app/layout.tsx`: - -```tsx -import SmoothScroll from "@/components/SmoothScroll"; -import "./globals.css"; - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - - {children} - - - ); -} -``` - ---- - -## 8. Vercel & Preview Deployments - -Vercel maakt **automatisch een unieke URL** voor elke branch die je naar GitHub pusht. Voor scroll-storytelling is dit goud β€” animaties moet je *zien* om over te oordelen. - -### Hoe het werkt - -| Branch | Type deploy | URL pattern | -|--------|-------------|-------------| -| `main` | Production | `jouw-app.vercel.app` | -| `feature-hero` | Preview | `jouw-app-git-feature-hero-jij.vercel.app` | -| `feature-gallery` | Preview | `jouw-app-git-feature-gallery-jij.vercel.app` | -| Pull request | Preview met PR-comment | Unieke URL per PR | - -Iedere nieuwe push naar dezelfde branch overschrijft de bestaande preview deploy. - -### Setup (eenmalig) - -```bash -# 1. Vercel CLI installeren -npm i -g vercel - -# 2. Inloggen -vercel login - -# 3. Koppel project (in repo root) -vercel link - -# 4. Eerste productie deploy -vercel --prod -``` - -Of via dashboard: vercel.com β†’ New Project β†’ Import van GitHub. - -Vercel installeert een eigen GitHub App. Vanaf nu detecteert het automatisch elke push. - -### Combinatie met worktrees - -Je worktree-workflow + Vercel preview deployments past perfect samen: - -``` -1 worktree = 1 branch = 1 preview URL -``` - -Drie features parallel ontwikkelen? -- `feature-hero` worktree β†’ eigen preview URL -- `feature-gallery` worktree β†’ eigen preview URL -- `feature-footer` worktree β†’ eigen preview URL - -Drie verschillende stakeholders kunnen tegelijk reviewen. - -### Pull request flow - -``` -1. Push feature-branch -2. Vercel deploy (preview URL) -3. Open PR op GitHub -4. Vercel bot plakt preview URL in PR comment -5. Reviewer klikt URL, ziet live versie, geeft feedback -6. Merge naar main β†’ automatische productie deploy -``` - -Voor je eindopdracht: gebruik deze flow. Het scheelt enorm in feedback-rondes. - -### Environment variables - -Heeft je app secrets nodig (API keys etc.)? -- Vercel dashboard β†’ Project β†’ Settings β†’ Environment Variables -- Voor scroll-animatie sites meestal niet nodig -- Maar handig om te weten voor latere lessen - -### Wat NIET commiten - -```gitignore -.env*.local -.vercel -``` - -`.vercel` folder bevat link-info β€” staat al standaard in `.gitignore` van Next.js starter. - ---- - -## 9. Project setup van A tot Z - -### Stap 1 β€” Next.js 16 project aanmaken - -```bash -npx create-next-app@latest scroll-site \ - --typescript --tailwind --app --eslint --no-src-dir --turbopack -cd scroll-site -``` - -### Stap 2 β€” Animation dependencies - -```bash -npm install gsap @gsap/react lenis -``` - -### Stap 3 β€” Git init + first commit - -```bash -git init -git add . -git commit -m "init: next 16 + gsap + lenis" -``` - -### Stap 4 β€” `AGENTS.md` toevoegen - -Plak het voorbeeld uit sectie 4 in `AGENTS.md` in root. Pas aan op jouw project. - -### Stap 5 β€” `opencode.json` toevoegen - -```json -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-4o-mini", - "permission": { - "bash": { - "*": "ask", - "git *": "allow", - "npm *": "allow", - "rm *": "deny" - } - } -} -``` - -### Stap 6 β€” OpenCode Desktop starten - -Open OpenCode Desktop β†’ File β†’ Open Folder β†’ `scroll-site`. - -In chat: `/init` om je AGENTS.md te valideren. - -### Stap 7 β€” SmoothScroll wrapper bouwen - -Vraag in chat: -> Bouw een SmoothScroll wrapper component volgens onze AGENTS.md. Plaats hem in `components/SmoothScroll.tsx` en wrap children in `app/layout.tsx`. - -Plan-mode β†’ review β†’ Tab β†’ Build-mode. - -### Stap 8 β€” Eerste worktree - -> Maak een worktree voor feature-hero en bouw daar een hero-sectie met een SplitText-reveal op scroll. - -Plugin maakt worktree β†’ open nieuwe Sessions tab op die folder β†’ agent bouwt. - ---- - -## 10. Workflow patterns - -### Pattern 1 β€” Plan eerst, dan Build - -Begin altijd in **plan-mode**. Laat de agent uitleggen wat het van plan is. Geef feedback. Pas dan Tab β†’ build. - -### Pattern 2 β€” EΓ©n worktree per feature - -Werk niet alles in main. Een feature = een worktree = een branch. Makkelijk parallel + makkelijk reviewen via PR. - -### Pattern 3 β€” `AGENTS.md` evolueert - -Loop je tegen iets aan dat steeds verkeerd gaat? Voeg toe aan AGENTS.md. Het is een levend document. - -### Pattern 4 β€” `@explore` voor verkenning - -Nieuwe codebase of vergeten waar iets staat? `@explore` eerst, dan pas hoofd-agent inzetten. Spaart tokens en geeft jou betere context. - -### Pattern 5 β€” Permissions matchen je risico - -Beginnersproject: `bash.*` op `ask`. Productie-werk: alleen specifieke folders editable. Schrijf permissions naar het werk dat je doet. - -### Pattern 6 β€” Sessions sidebar = jouw parallel werk - -EΓ©n Session = orchestrator (in main). Worktrees met aparte Sessions = workers. Orchestrator stuurt, workers bouwen. Klik tussen tabs. - ---- - -## 11. Bronnen - -### OpenCode officieel -- Hoofdsite: https://opencode.ai -- Download (Desktop): https://opencode.ai/download -- Docs: https://opencode.ai/docs/ -- Rules (`AGENTS.md`): https://opencode.ai/docs/rules/ -- Config: https://opencode.ai/docs/config/ -- Plugins: https://opencode.ai/docs/plugins/ -- Agents (sub-agents): https://opencode.ai/docs/agents/ -- Modes: https://opencode.ai/docs/modes/ -- Commands: https://opencode.ai/docs/commands/ -- Permissions: https://opencode.ai/docs/permissions/ -- GitHub: https://github.com/sst/opencode -- DeepWiki Desktop apps: https://deepwiki.com/sst/opencode/6.7-desktop-applications - -### Plugin ecosystem -- `opencode-worktree`: https://github.com/kdcokenny/opencode-worktree -- `ocx` extension manager: https://github.com/kdcokenny/ocx -- `opencode-workspace` (uitgebreidere variant): https://github.com/kdcokenny/opencode-workspace - -### Next.js 16 -- Blog post: https://nextjs.org/blog/next-16 -- Upgrade guide: https://nextjs.org/docs/app/guides/upgrading/version-16 -- create-next-app CLI: https://nextjs.org/docs/app/api-reference/cli/create-next-app - -### GSAP -- Docs v3: https://gsap.com/docs/v3/ -- React guide (`useGSAP`): https://gsap.com/resources/React/ -- License (sinds 2025 gratis): https://gsap.com/standard-license -- ScrollTrigger: https://gsap.com/docs/v3/Plugins/ScrollTrigger/ -- SplitText: https://gsap.com/docs/v3/Plugins/SplitText/ - -### Lenis -- GitHub: https://github.com/darkroomengineering/lenis -- React README: https://github.com/darkroomengineering/lenis/blob/main/packages/react/README.md -- Homepage: https://lenis.darkroom.engineering/ - -### Vercel -- Preview Deployments: https://vercel.com/docs/deployments/preview-deployments -- Git Integration: https://vercel.com/docs/git -- Vercel CLI docs: https://vercel.com/docs/cli - -### Combinaties / patterns -- GSAP forum: Next 15/16 best practices: https://gsap.com/community/forums/topic/43831-what-are-the-best-practices-for-using-gsap-with-next-15-clientserver-components/ -- Tutorial: Next.js + Lenis + GSAP: https://devdreaming.com/blogs/nextjs-smooth-scrolling-with-lenis-gsap - -### Volgende les preview -Les 3: **Introductie Cursor**. De commerciele tegenhanger van OpenCode. Cursor rules vs AGENTS.md, Composer mode, Tab-completion, @-references. Wanneer kies je welke tool? diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf b/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf deleted file mode 100644 index fe52eec..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf +++ /dev/null @@ -1,340 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document (opensource) -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 5 0 R /F4 8 0 R /F5 14 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 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 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 -<< -/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj -9 0 obj -<< -/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 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 23 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 23 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 23 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 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -14 0 obj -<< -/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font ->> -endobj -15 0 obj -<< -/Contents 32 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -16 0 obj -<< -/Contents 33 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -17 0 obj -<< -/Contents 34 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -18 0 obj -<< -/Contents 35 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -19 0 obj -<< -/Contents 36 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -20 0 obj -<< -/Contents 37 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 23 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -21 0 obj -<< -/PageMode /UseNone /Pages 23 0 R /Type /Catalog ->> -endobj -22 0 obj -<< -/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518125917+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518125917+00'00') /Producer (ReportLab PDF Library - \(opensource\)) - /Subject (\(unspecified\)) /Title (Les 2 Lesstof) /Trapped /False ->> -endobj -23 0 obj -<< -/Count 14 /Kids [ 4 0 R 6 0 R 7 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 15 0 R 16 0 R - 17 0 R 18 0 R 19 0 R 20 0 R ] /Type /Pages ->> -endobj -24 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1862 ->> -stream -Gatmg`7*5S^j9TVig-b68D_96"X@Fi#X"!8qs?u)RsBH"e=0,T_aQsQQ0q4^o;*)4>>%N"G/9Og1^KJtmI!Oi65Dk&NNoFJC7VbV'!M$ho,;9/*SOYkS[680(e0837_\\=G;097)\5`40Vu9Ell7fM<&p^m2\(V6$T';!e]5s&659r/LRgqp<9pX!j06"N(41C`G1L?im!iMh`W?c,VH=)I-TkcKQHH5EgnucXP;h\m\luqgi_?]M&f;"HKn9[<0gnXOW0b;"idR81o?rXV>i^(A"tGpH'ha@RVBZ^nk(5kAT,&b^Y;X/5_HSU!CBI"ggFPFH9fe3U<'ue6)QrI:WQh2lKC2(H4'4IjJX-<P6j976L2>1!5H,0V^L,suOUFk_sue@I^K6L=s9_`ZE43&T1*Ta^V`!6Q'0h>r>Z]qG:o`PpjcL)O]o`o.?/Ni^?#UOU2H=Y0-EB=hR>J.MJpe\d8Hl:I6Lf=eL5:6H)U=S6]n&_uWNXTKHSS@00UkC!dkACiNbm!oL[`tX`#;Qj*]t&)*]-UYJ((VS:J.h?7`SlZ4c:'5b`.Qq0Ui7<.dXL'-cT,c^$aVOt(VNaoPugW3c[`6;XaZE5DiUWfQ:SQJl==5@?_=Q:<\cGrsu%Gs7VI]_'G0t@/Hi]4SZ@0gLlR]!W^]!..Rgs0<,]?YPcl6#dH-$+*&i8l.9CgNNRQ>6AV/2ekgM_lBjbc.91TDJFIZa^ahi]2TK^dE'^C60t[X;Ok[_k8?A5T;jce^juT-2aR7Un>?$Vao`t))iNbF#72]a+cI)<5)M:bu56Mhd:*>/r8l8/clIlWSfs/8O+7@6trOp/-J4Xu_PrO4m7DpHk&o"j8g`Z/^C![3[7j=oP*o3d;u];V.6BAMOgFZm$;XJ\W6K/X0Gjt"TR*m!Kr[.#YI8\A^Am[TcAJEL\mgt[N%c-h,X,T+Ng8Mj?`9l+o(,Ipc=9nBrU>#S>7Lf4tIj2ruf,T+i+$SJG!O_4L5u#adP>q6m/L!2V$Kg7YW_R$Q5)tK-1VX+C2P3['sdn.uaGHFgOp>(#"E:T=1i)2eT'1`l!mt*.j=@L[h,(8-L3kkZ0cq*JRn;!P%i6QWJM)jg9u147)TFTA20i.l[sTVu[IkIOFkYk6VV(:[q.dhktdWendstream -endobj -25 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1814 ->> -stream -Gb!;d>>s99'Roe[3;^(i9nXhMj?>.'D932bg*,k/m-l2=`JDT5C-,k5^OD71fJ-*nd+Mh387Gf3Nm@ImSXV)'peRC:X8r$:$A\(!:^5B/d#`o74bIrWn!cgLb`XX]&o^'+)3?[oIo,:!JLDjbb#/T%:C'`_#WX3$V]6Q!9+m[Y.:N50UHk!p:O!.udjXAKcMW42N8`2.M;gMnEW/ElmaOm$][75>Cd0ji,][H!Y^r1f#\bY6AfElHT9CDe'ASHMnb*%&Ka2$+`_^n2,40)/VEL4Vh[fNjN:G.k+e_KOL*KRi9]l"XGX=Ee:/jHBicAf:^$L9Xch%Qd!uWT@SSO;G?+R`6pO`%kS4Q/#($bnsQSH.Q@i.JM`fr8DumSVFa2i>ZqKG)Z9NEQS?U-o^&rDg%'Lp4^j/jr]Wdq/IWD-#?Y3a@J]tV!T2uk=]NLm7`*e^jHptN!=87pG?iL#OU\,\aeEFDqrl]h:3r2=KMWR%9U7a=*@SU''TjnSLKb`-R\?g`'3UbrrtZcqK-i`<@u@N^^"!JZ)D)CD$_!p;psoWn>Wd123[-(jMk6.64[f;IOe']:rjR1l:7$M*1?Z7ZqH[Di.JGJQ?XtJ[h3;c&6_DO)JY8sXd+EXX[OC&JR=7O+=<;q)MG9OGZ@dX2#?UE(+[>M!BC5"UO7El;LOph3#a:-2n(m*oI@(/U3?,g9'GWOfB;mr`U91;8X&4A-.2T6MS@ZF[4AHrZP>Pe[:L)[/8Ebg>'/+h+Eq'p9/As5[*9@rAaWQQ6bo>?ibkkG>25*]>(IXV$,8$1,3KsiB:LS=+OI'aEG2Z5MTn"A.qB^'VC$SrG?#A_%HBNVr[G0>[,DZ\7(GSpEBFQG`N%!Za%2Jl\T_5Y:rOXA\8uWZndQ>Fo*\G,m+>U")hRb2$Q_/dSf2Yof1DM>AW:b<\+!-n%XDFGG=5@,cARIB*XMbbo?iq,&:2\UstKmh/[S]?7HWlo_c#?!W-m:/nb?;;YS9Rl*Af>bga\!;8r#nboQ9PbGC=;kG$>qui$_j.N`Kg_S+J[I+ZTQ9Y/6,[0t>iXCEh2G*0rUrK%IK@Q1@WHa*=1>]]?-:o/-oC;+>%rXehGji%_k9M2`ZY6[1?21%oBtm0N(n$5!(3=iBAASiAgG[ZcB01,Lim=j>>'=U@-gQIs!Tq;``GkXnooj_Dq;Ep%P,H;\Mj_10Wcq\hkH"a6E2j-09P?nG>$0Qrqr`5E$?-BY>Hu"ALe[:n(>I-KKgS+q72B1WDld/Rn1D0OT~>endstream -endobj -26 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1690 ->> -stream -GauHK>BcPr&:WeDbfcTAp=+sF,UAt)HeE;ce'lL[:%oea(s?pZ+4isY+qOck4>)K&m%:*q=+jJF#LBntV$(3Vi56qJPDR!P).##AR"afIKd[a0kcoQuA9n=5K,=o$-+Q@aOSQTNr>_MjBLe(Kp]*8;[hZuKn[@5@.=/,oASmU[Pce%UBo3(1QUGe.^+JoS>Y\.7;6J@2p9m?A-.US6oX$)Ok+QiT=fj@`]>WK!EG[SiVbrUCD8+S_A8.3dppatgl8>$EY:GJEsP2-sLS`3a(KX7Z6S&A#roJ?H-PSf[8[gfr\.GX9hY\O&9q,cV.col%,r'0!FgF1U]0C\W:T\j6Ykd;jbSE!rr8[&IEZ0T@iMrUV0C\&t@C+e'j2]ZQ;UcCfDbcKU0@F5ac"2`Y`WDSg<$V?)::N,P_TS(q8ijds]jgo`HApK\kK?@Si03oL`h)]UGG\p+Ys5>_Fq(c*6P0r4RhreullR+iEIW>Ab"VaO:Csa>E\E_qS,hqE8^6T!-pt0@WJ"P>`Xe56sWRn1!)tCCn.bb0d1KGSJ=daS?4QQ$NFSM4#mAkDm@tU@iX3o^?b'M[T>^_B5PeduTdk,IHmSQ*LA,G)U<.dO-a24qFKFof?-0*:TL,_m7]15:A/<;9"jcO"l'FOT-dap_p2SB#s^m,K"DfDbE%dlZb_I=$"q&UQ[A(8Irg*5N&m8U#QMX_t^BA*^&,Ah\FE!)^R6g">UZK45VYl'969+g/1gieC#JsO~>endstream -endobj -27 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1726 ->> -stream -Gatm;bBDVu&Dcq.As6^`[:'%<5qneXDA"<[3I+b%]#Y)M+Ghuu$45C&^V0g>+\J&poX'oX'&[0[%WLSS"r';G?df)7C'#3T(bcG`3A<`%9]!Y08an;S>k1ub5XIoWKEFe4pBU@8-m=aA-9P_0BM;AV5U]*d8ec5L(WS(]g;*W#^js@d`_$5./cG0MT-[WZ6Js[7Q]#^2b:4jdYkbVTB`lB(3[mS%Mo>VR/S(j;'(^k_Bs1%t%VJHM#t#Wk;1*T&K+RcGKgH+Q7DL.r4q;XZ^gf^1:np%R3[m?s/GXN;l=JCOR)90+(iu^3WmQTZD+,JF"$^DN$\RK4WsijZ`(R?bCF/n_BBtFmKs9(o)a7`^=.42cPq1-Ph`fKs5`iGOHdTdd'[7J#m!r'!VRR];>NpY02a(FdC?+IPH>Tnd;r*1L+I%AreZ5.QT9CgC\C@Lh5PhNqU?YKSKU&%a"kNeD8G0+#4O'P%;$G?40Qg)0*2e\%*^RmEs@QVXRMZ[dkc5&e%)aJP/+I[-(%hI)gph?S#+5JS?FVhgfIJbdopB1-@m)EVK_D!hUHbH/@0Be,L/&jaBh6qq"c-9hV1M$o"3g%-kQ^&['$AHQJoifajI;3"$!%kI;%iDYG'm&)/`-%n,j0W#TrMsJ@-X^<&fsQVQmA*F_\7)6OU0B*I#(>^0LL*e%*_@B7Nc.N1tA`/-9at[M[lP&L05m"ieAG2fl0,8S"g?(e4^/'D-Y)Pb4G4+01ToSF"ik$C_g"U-%BWi\)D/Ep^Fg8\[8f0jl_ml'2U(=V14QOGiH\5^/)H2]MGG?KF$nLo=ESB%",LAL9:ONt.9r^C/[9IqI7I(j1lU'Tlk9kdilVhY4T:bIOJc/9p@RNtZ>3AC0G1'I"_5`BImTM@e_:Pf8dAHiM38s_N?oDt=d*"fGOqq>e>(cJ'm.Mo@:"[Wq7!rk-Cq#7J#XOZiQ+I$BjLRS=QY8MR`\e@l+g-4g9DV3.kiULTgN?X=+,5e`@O*X&EJYC2$;I*J9F_,PmSREKTa0jV5oF14?4i9"?mDXB/e&W(7]tAgiDK8+%4mDmLs&c9kHiNPendstream -endobj -28 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2485 ->> -stream -Gau`T>BAQ-&q8H9fGRJ_:*RLS#_JjbEi*a6>WQTYA*Gg'IXt']U.pb$8QenOm;MFp]ZQA5SE\6XQ6h,*G-T]up-o6#5Ks+].tF7)E;#"ej"Qr)'1BY&"'j,@^Q/Yeg_2:Oa/;\JJHV#/K46Q-_3ICm4CI)GO8,:HiA9KjJI`ab4HFbaBUD"+0d*'`ShGWhba#p?L>F):F50q1=3BGUI3YA&`!_B;UdHm&Iq49a=ECF$X!7m]Y33Z<"fEG,rUq;hAIDdub:HN#c5B[b]1C#,ma)f+3qC`RW-d$iIL`_)-5QmJEm7Q3o1@If5q)$'-R_8Nft'mVlYA=4%?U3,ok<=M?IiY+WKG/(k\$-UZKQ`b'[sIT=lu+Z.P>[DpU5C(o)c]N'"h:YN*7/[?qf`Ks4b*O08ZbdddnSs-CFX`+8tdG)-+:gWPYa.9T(s)O@sdCd*otpr7J6TcFm67$hFS(&O7>r1PkUR]b8%W)_:i`T#s#[a?"V/AuVpo#\#GOQ(S.`sr?+,6a/&!HGtF(CM2?RO,i.[tFl27cI8d>oOt?%e=H;>9dMd>g`5@7Hg"op4kV&0aDZa[gPF$1tB/S/>dT%],QC\l-3$JL'Ka)@!H52L>YZ?/NVkQ'X:U#4<)0>j)\);R1UjYH+'?31]eh]^Wm*OFri<^15'e+Yl8MhC'JU?lrL(g*jVjIPpDp`8MX.WCiX8,7ZP[1:iD_@f`0j438u!sUGqfq/h%CV%6D8@PZTU(,j8j!?Ze;u?Ze03B1Dd3rl9K,*D'FnT>4HjI]gH'YrM0sWGMhL\K<1/X#l'"'DT5q6FFb+X>Kp'A-7_3Tn*C%l+;2FSEMeE@"n5UZ)r+Wr"tXZ`FcGR79#klq0W3Wr1MU:jr$2'*toH1&P[E%RpK@9$Z'HR'^hdu/(cm_+kNT&b4A#^1%5lsN`Y86([oC7[_OBD%K-&.(L`#L_/-)JoJ(a7_JGL*WA2'*&f'`['jRWg6(ngYk]DKc'\6V$X.QpB60!f&s3p"CB*.V_ZW\popk[HR\LUB9J83AN\B`Zm1<2*'k'@r?1+F9IMq]dQek_PY*HUS#!`+].q+ScL-;L=;djZbLH=5"U&E^^*mq[8XPO[W'W"P0D--bh[\cSKhA0DOSR1&QT)a!0s,'IE5rc+=V3%F8H[Wgm%.GcqLLU<*$UV?+8/o5g;"Ust0&],i[3?iuc=7a=;:'Zuam<)5Mlm_UNea)8d7mP%Rur6k-5_cGQp"A[l4C"Wm2-07e\DpYLB+c>pkN(5E<%J2CYNG0aC,_EjolAq@4@1E`F^$GQ*`eSkrQP\Zhq4/NpL[i#R@>i)5?QEe?GZ[,%](@a!&mj-##?uOL?FaE!f<.LlZ8VT`1cgc4H<2q)P7G+&(I=Hkm@A^o%6m;I.LS,j7%riB7a%Ji@hqpUCNVeFN9Y`PMajXY+fIQQ!&MNF`J7Af*Ydf#$ZeD3EHQlTS7cT_jD]3fL=2Z5,;aLmgb6hHB(9dUDFh`')PC/6cA'2/Y.%72Rs-""2X.]'.d=N5QPT6*ssQ?TMVFM+jH*9&G+("XEea2``endstream -endobj -29 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1620 ->> -stream -Gau`T95iQS&AIa;%#goWK4q[]?2Bj0:"ge.h+#X<(Rh(03M\'eXFm6fTYtb:#OsP**o?I.u1SFMRr]Mh5?7o[VRK;HQ]E0F:K]QG5IMps2KPPJ$![9010aA=!pOI!!D*_m1@idG1e.?C@8BZgsYm$/8`\7Kp#$9+p#/k1iX9<"#dT/Tj(IW>)U*WZ?a8>e*Ib(J7'A[AXC:5bu6OMB^'i9T0PUWA8=hsBj?f1kh5EtK8:9VhrLHh4W97Z9D`4'GnVi2t)6Vn?p;t$%Iaa3Dh?I[(V6%m]p\5Xtk#rQeODCdW0]B=i7_QbGo$()4/lPae:9qa`0D9-OXK;SH^=JGc/dBQ>+e6b8a.aV5#I]90Lf.-l>,^rg_/.o3HkdM6r%/8F1X'>-[hT0l_IO[gro?(*3_hVRZ>^\4BYU9:8`FF^4I.n6Lo;)j+#of?7V7*uBY!E!Tuo0C0?]4/Rq;JmDC5AEf5BURbp@r+A=L'FI_Z-*@DsPF%V-P)j9^!rR_$2jdt'Ee0A8fW9dFgD490^V>V]JkpeGM]jd!Q,>\U7gJWe`I_3pXimNX"Bkt\Y>+MQ#rZe5/'I35bOX324:f<*7I?s-[cSjO+lbugK]Ii[uP/_U`pLeasYtgF$3!p_TKM$"!-aopEUOgQ!_mGRJW]uD5b`/6q/\#40;:$GU+OkAh]!&A^oJ7N'kTJ&+?p!"+#@0Fj_4'?K_$2I%g7X5tYER0ic6,Q_0Pu%3qf&_3Ud>V;3,`L3cG%-"SJB9"3LjA6c)s6;%%3`((2[(H)E<(m)OWCJDT;=i"[_8[fARn&HIpAP3A3;Fc.Qg,rmLmrT'G@aV0SDA[g-rAq45]I@D8GoS7u?\)J22q*T=$"MBlo]_96j_Db4To475#-XBKjQk*o@@cst<+]^KV[gR_3MIpqB"ZMRpHYN$,jf(IFiM/f>M>>4L=f`u-5;'g4U:^ks2Of[K?KAc)[pqnd-'kPc):M7urlc\ZU@1&o.B?D@_;5PVnk.3$dRrjeTae,VMNodpud&p;Z4eYk_ACS1?D\m\j'ZjSk0fS`FI[=%l/n0,"5j$HK.Pr)EjO[,JMo3a$6+NNGqWVnBrq?H8n-4P=ai4FGiB;lo8q[5e0NanpBFmqoJ.*6&5_]q5JC-[6'+[W'(%HqP5>4W'X14%hm,qK#97[cG*%#pXImV7pqZM;:>pf7;1`u,s;WlVB&8UGe,T52ZMNTiXP1+`2RqOn%mmNQ>[^R3a^/2&k[-:j#h/~>endstream -endobj -30 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2009 ->> -stream -GauHK=c_;q&:W67(na.S,W./O'M0m6(296pS#2[u8RE5]'U_/9ZKOib2Z\g8p'u4lII;Zh]p;%qLNCd#'O:R_i\t$@r%`rA$OW-7E"%sE%`?ofI->rt#STmP+,Hh"047$Hci-3kE>%aE&HN7f-s%$-3H.ED;Lnn(qkU##;@VY6%L"&1.hr;`W%XCfg@#Wi(`pqY019c#IjL_miWV_Ld\3&i(RZEMXd!S1HCVLpQTbfod(=Poko=#uM9_ip0en(";]SSR\7A:MZh+1&/=U4]*l2c@X^e\BkTkWd_^cQfZ"J4SF"U!+\_Q9G>%bVNJ$[ph9]nVbWi.nN;R#SH?dhD1,5/e-ciq=Pq./`OE4&kr+LUk*3r,>:F$/BSRVGpm`rbC(ddGH%D7?m_8c1"LM.SQfDs7u;67ZG[G2D[h;0!GpW1FjC3.EB$M0ZLWtWs_`VapFm(7M(9uBp@l9lT@g'roK]cYG@1,W,o.bdnjVt.,b;0mNn_8/P4'tZi-[QZ?;E)D.4k=LtpEGX0ZodmHrjL>1p.DgL1G!bJ'9h"-i>g(JE3[$g!_TJ=ZE4D,Yl=eaXLF^toCmQ&R,)FEJfj.H4:#n-H19c;Ioa=eX*F:miq+rkK?:E@7d&\@jp;&XSWDr&ggG3j)ZYVj*XU]5!CULq-VS1g=%m:1*?b!`%@6al`)$0e0I-u9ekZ6TWF.Fl!GW%Q4b5JF6T2'O7)[=ht\5.c*bE`sHdMVi@m)*V+k4pbo)[pQ++X=ZA+O`WqP[m_f#I-(gT;)Q,DkdAg%A$X]f]@SMQN^dC[DXn;6rGcQfNAdc-+A,>BO]nksSX`?^M.g*_S#W\KLZi@+I':q&Bb#'c*;k]U!"_lU-9NH>&=.Eu^/m4BdOF2<=cDQ(S*+AilO8Lic]:"mO)O<5@%)$X&8b>=)/k7b.^CX_A@kB'@##(glGg6c`77`!N#s=E6/X(tkSR(Q$r_i,ZNr)tI48bb&77LO$`'?&KEE&=(&S-Q@Q8:M,ubJtV=2KN3`rtX[q;U!#m@5old`SdgOC#JsY%e8Mh:4bnRY4r^KL],R'.m2qreko,Wh#o]cf*(h"'Gftm@rmWP$F[iq:Q[nefFd$W.%Rs+6f:nS^RAX-.VJ\AW*<`Go)tr1N(P'fqUbDUP$S^Wp)Q[@o=.RSutuITBapf7gC`ZH4V=*'9(;0FK7T+2L15@br/2:`ok1e_38J1),]NTbL8b@-EBTt)83G^!p&bXdLq_jOIIF%NOV%=endstream -endobj -31 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1975 ->> -stream -Gb!;d>BA7Q'Roe[3%m@hd&<+TPBh^5"h$3`b*$XLqP)\YU;f-.VZD8F*r-R!Bj/Y5h4a)cQ>D!4IZYIDr3?):-3.t]E:f.kj"S.Njq%cp+5lP9qJ$W1ESkbefRhfX#85\O%uQd?K'O"%huNpGI3O%D*5RFBJH1_rI%U42.(F_D0d+2#IGk/3A`3uY+VRT^bQAp2&[c9@h,SkSqo@VC.(AA-3NYou[d=10gk'MJ8j*'=5<^2?M9JC(OqPaJ0$XDo#D:X*`1XJjLaC+`^>7BDpoh4!@d@NsDd5;^p%XKJV`P!)ja[9i]eLkE^NTH5X):tj9f-@$_Ne,.d-!W-A"%-$F!5HkN(NLI?F"+7;l%g_(3G;sSFJ8VuMJ#o:OM_'n/1=_5_1la((/fo$<:XIr=^-9=MlN>fp1^Ao:eM'$ah^WYF)X]7KM:00gpFZt7=Lah,DhA@<`;4roc&?q:mk,M*uZA$_6$C^\M*\4f*A`7S,m]DujY^orp%^ZV=8bkLY@<-P^XFseXjK[*,UQ`*),7(VlTiH@u\i_s*\WW()g-I>!R?WJ'.TqGrZ[)a=oM#\W&nHu.B."QuqBc9SP.K?tNk!e;FYX?0t]o?N;)FMZj*-e0f>(Nn.\J5b?#2a.mFNT\Z>R=CI(G[51+d[1,TV4faOHGn%*eg2($',Pr!"JR>EDkBg/^a0*:8D_h-C3r\IYp*j)-jCCdA6l$/1RAbO)`rDfK4k?l>.kFh##tOLQ@KJ'NVO1]4]BsuMkaRip].V&Jdm'X5Z+FO2.l'esb=H:-9@t`;UIeQr15>$u@@BB5%p>8GR@-*hIoMgW_D/VfO^Ut0S)TH+W<,6B_:gg""qQpZ?WRRhTTWB]_VI)(:DiEQCrUHp!fl_,4*V)e%WJcAg1E%og"YA0kX)d#i)VfYN-sFQ4)5BnfBDuLi`]ch1l0"fh(GB*.E%^lU[8<2Q.C>;0SbSLOBR.51tn(GU#CFf3WU?XDS,:M"g'fK\/XJ$3F)@DnpqQgGFIFFnpOQnIr?"J[g8t4^$i0k`spHe[!7nNlD)2Kp*!r46'uI>1W=jCX0PV2UQE(\b<1ROP9u`]/MX(;mE@ohd9qcl^`[b%?%\(aW5d-d9m5"AnCGu'fn8qS`lG]Opf=:Voam;Mr.CcT#Aa+`iYhC1TS*U4/J7/'DholE$p6+RZF0_ENueO4%?e2G>nNk(SL(gi)FMY:ha_(qoB@a,p!F3uUCo=9=_o+]3!TN3YXlOYBc!GC3Rn)fEObnJGQG+.W:ZVorTg0B[G,r(]qdlBX7=MeA#>NO6Il&CR33l=Rrf\KruEe1&'\(5eZ2.mkNCK/>qK)'\)`TnlHO0endstream -endobj -32 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2229 ->> -stream -Gatm<>B?;m'Ro4HS=*-@D*"^g63r^A:+'mrDDR-=39a(B0IuUt#"4!L-iN^$![4e]lZFkpm".3`:HIZd3e@CO?e0^=-,;0HnFRFt`o67cQllIUhSRIBi+kTfjKXMkKSkKj#Ejc6#F]d35pV:\Geu_nR&F,;:lHff.,_$?a"2s7_o7gi!Jr+^PlRO\,aH$;*tl[U\eZQS1:aOUn0n_u+k!&OR):!a6ODm%jfO8h:_q"^S<^='p\saj]J43XAfG4eps_/^gN;q=o,+'k/>'CghKF2K0D(M^9O6q`qZ5!NE;%6HRb8KDOsCh5:&AjO$QSZBmEEOWoe]t;<=6jlbTF?jUE.GscBp'oL,?nHgq2E/M!Ld%+bbS)i"6hZrE'[$Qr#9^ClBkl,m]DTT8uC89G[CX]O6UOCW[/6.$m?[D22qhnGpR[$iuC?7DSCARiYIGLjC^W5UR6LhKY,WfhZ*9o.8MsI7i2M+nAE9g!o8-^?'oOgZ.`\:+_0b8ip5A7rqFuH?_Vtr;@Z@d8NR54/Mb^7^JJmFZ-9+@LW]S.kG)UJZESMt8:cW@Z3XbJ<")gIotDg(;?n(LK+i%p7NTGMIrR%d\lVDmBn>nhRlL:ftB%b>7$)R;T0EpFLaU,9HfApaKSY9F3snQ2kD2`=E@!N$!@V&SVTTZ`TQF@C)Kf)G=TC:7IC7rqj+@97K84FQKGhA?\@7!u,+bO2$=cS?*qF4^0V-0fJ2OlVZ5c]9]HX\9r5M3,[H-I1`([o81[;7f0n$bK[!CNh=i5'Um>Bj`C=I[M"eoI75@;A2N=[DLQ(T7*atXSR7rYJD[U;EWrF_rc$2G+ENNR0N&t8"f)00M]T54(Xj^7mK,We&JJ4=dPT[nfFNhhjWBo19G>4eX%Z"bPd;hc#Kq,U!mo2kiG8mjp3<3iMblE+=B8Q>-@I73VTr:k1^-6`3c,2>c1#aRfi3O5b6?@J3)Z&5b5Ll@SI'1];!(TrjpSGJ^*$29]e%f7l^IObY3;l/+Ao2`D7/jf#VI$+(e]B7"3&R/J==H4@HX9fGfNmQ_]^-i5=l26uY<;5rmI*MD5BpaI/W/^j%LJ6C,a>0fjPT(9q8Zl*_PAfCr\9)]=$%OojGO`Iaff,5=7)V]RYdi*1p7^4a5G>5lb]m;K#766:&-:<>F(uVq8N$\"77LK,Qr<:T8,6c&_gC%nmFD^9-nkN1MNt)!adQGH:RJs2B_:!WPf8cb!2]20QWC''\7@o^3fk)H\$=dIZZ)`,fBqL\VJVAp@H:H2:%$F1Q-GA!S[lDkA=oqGQgtb_A>i2ZIYMN[Y3f`("4%bBnR!+k\"5)\X\D&-~>endstream -endobj -33 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1044 ->> -stream -Gatm99lo&K%)(t.i0_bG$qX;A[C:>5Cbh2KgPjn#oHHr4UKA:;M/bb/rBc\?@AiO*1U0'*"se`noKYYAVb=DkF:I_O#3Qd(KS8"[KOP:#fC3YnHjuImd\mFRBR(Ff?uaisTNVOG7%3@B`W@\MbSgZ-$>+-k6'@ip_E`gZ3"6!EUUdW:QkbalqN69K[$6eYYt43Ud!phs2Xk1QGN]Q;+&=4LeIrX24WI7s*P!:DM`L^k?J%[I"ggULG)ODG^X`.\utYY@g6jkMhToeZ1NO]llKc:9s.'8V'Ls)hL>BKWZ.:7a>oE:KNGq?4aGD-]HNcWToMFUQKIp5INiuiI\F?^fbiVL.q@21I)k/bXZ,%l[SYB$_Ngggg0;l"NJkDbYO)\;3#eQf6WJ=#41b)G!OkA<1.>-tBjA1t[[N=l),uVI>!h`YCF2Ms^87_-g1bMg6"n2H4VD9F,J`BHQ$r2N7ug9A2Wf6O^ei[?G/c#303rPU;&(3UdVrmm"#o?dmpcBErNuos5rt\5e-XcYg6d"H-D6L"%6%=#5fi.EA[JT!H508b)-*fR*;;enNksc6H8,PZ?C&euuh:SZ^Tae6VT?%`s0[nU-kB_A*s]Z8lfdVLt/7Zb0PQDEcZ9+X>gdN6ZnWj72S:S?QSmo-KqAHk^jqP]2944hIne(mW/,nE<.,0+b36Z2~>endstream -endobj -34 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1740 ->> -stream -Gb!ksD3*1)&H88.1"oZa!pfG^*9SRmW$j;3!\1X+W;)P!qKBb'ECdA?W8bR9S7J0A>VCkS!i0F0M*8*cO(Y%%:Dcq+Z0n2!!5G_qj=pbV@92e=[h^g&DWm8$HE#*`Y`t=Vujs.cC#YQZ&J3;nFbY]!XGO^Y7D;gD8Xd*l5a,bD/fU26C$!,BI:6>8=#OItQ95=P[t4:%5"?,XhNMn0jcj5&>V837bL=&=;WLsNA8G]3MjT1DJV,C*">S!ST@?KPJ8U))9#%!#nVAXGO?52G03!>9o=?jeUE"JhDi1Z\H%,\A\-1sOQYEB*3e0;qn=)s\p[kZQ])Jk%d;sr8LroC?E:.JVZoK\Iob8;O11G7#jBP8FOc=QOa080"1rpi(cEONbem't.cmLk0*5C8E6gr/D(]XL3>a=-JL`*M??;<5';,#m!(.aa#+C#EPG15.I*4/dd*'?]is"*bXnK=hCpJl#std503snsYSHFBm!D<0)C!UTVN4EAqD!;MGO741ES[#TAno1QF:.,SHXAr$e\-OObamc*Ba`/X*2\OCPI[8u8r?o4=,n#2[)-4AQtO3SLM)TYgn70&'/:B#-O/>1Z?Or'OMfZ&/7#2bfm/mS.D^9qK5R0W//K#G[aR%#jHB"I@D[jU7mSq`.`*_N?_^k*NaTcD5$-\&d2]dC]:P,'XlSLLbY8:%'>(5grJ58JGh:g_t$MndDu*GeFX(Y7/M`oLpfUOfX'5$p89IYZCXEhX=mC27$/YEk!_c'g'I9k5u:Vflp]I'M;-%d0(t;#LNBS;EUe)67Ja15TS:U>!X<_as(tGn%F)`N/J&GkD\1K$eS?mi8NiiMSt0j"C3-\N3P)%1,t,*3,.?YP:7mMI^]6Mf%3Lc0ZUa.4A2s,fKHh<5/CiJ$JV$H[a$dUWjsk/p)BrXd;oCSO`iE`^C3eMHVNu8*uK*Jc4?kP9l8Je<+G1t\)BaP[*9#\9pOI/F5[^(cc$i[Ni8#!Q^C0H*Uq-,]@L$WT\c#SE_5p4_7sGmL$o~>endstream -endobj -35 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 414 ->> -stream -Gat$ra_nsL&A@6WhN9!:fT*H57b'ss6D[4e:L$^MO=DM4M>I0nQ<&[1C0P`s^Lc8@0Rn@LdkgKT?ljB.J`He`8Kq5Z*`U1QJC:5<&TLoba;PA!3E[;(#`gcr)cR<&Bu!#(1Lh`PEi#o;K+CN]iM@hLXeT+ui6Sk*,1;**DmOqbbl?V=:qs**,fZ*H8C!U8XP]@h/s!/1:M#sSXf5QOC9SGi@'Q4Rl8ksb\&Y9@Ed;pj]:ui'npt?*C0g@&.7e`2A,_ao+lI.lP_Yk'Fa)&eSPSp9BB(fH+jsgRC-kWq1$No07?7A,a't.4:q%h'n#=Q?&neaBV_F1n\@!&D&"OXNee_C10(ZcWrXZ0\Wm(';SMcG3/nm"s:46#l~>endstream -endobj -36 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1326 ->> -stream -Gatm:gN)%,&:N_Cm%`Ul0EL%Gb]MQ`C+#Eh*Of)/(V`5r8qKJD$33YgIXRP!1UQ163/K5H%"c[YZY.T:a<@>iDh!ZS^tB,k00!nOZN]ic`A"oWo3(QC?m6=gS,eIX)!(g$*]8FTQU)'Pb0jPU*+YEkRDf_1cD8Z1#^S,HGQnplE?&q*=W1m-"t"VYb4Mat,F/[&TjsCaC&[c#*hR9,jXffH&^9Z9cH(V38I]"$R"[ZE&t!Ookj$'`p[HHZd1n0A.er3Z(+To>6=fTJrQ-#A[.@D:T=DqaCc>Jn="q7Afl7[@j?JY.9*jBas-Mq7!<"rs(7j&m[FDSc*f^*q9q4C,8js7I$RL_*0V_g!POK\=@Up1NE=/1#,ck2[HW;1D(5B(6m0VHM`JeH/)"/^\2FT=dd=P$:)cspX:3LmaKC=V?2?SJbNIh-oXK-m#?2iqt3&h8#-q+\r5uH%K!4%*!87Rm0&ZgRb;pD3jrTQbGSQ>R1Y+r'iH(D97#^.Lb>XcVOS%!i0[`S;FEO1m6cW$27;I>@Z9CarFfi]LQ2L>l+`Q+CTTXsQ$ti*O!35Vd^'%VV$,js*PfVc\.1[_V>#M-3:AYBb["JNuYL(0nFlo>;K=?XE=:Q/WB%=7S6j-p"5eq\mI*/l7*-LpS(-,Eg`u@KeIZ"DcL7K\;mIY[RR7?b[$73R4,XbknOYTpT.S/:g,7BK(fjX*1Q/9=;7^@Y:?;-\i&=QV#[MX7\#M&]%/s/j2-0R&9sV3eP6uc43PF$h+KN1VE;e)cG*Djcfo>iJrq?8)G/H3ai8.Ep;SA]Q-i7ma:I<&i(cE083XUi!_EELk7aOQ2"%?rV8_J4&@-M&p73GPU/?'#+LJJ,@i!m,,"*b%=W(PdOjB$FQeKpHWfJP&*^i.jnr'jT+bEEoi,l`GqDC\(`fc+G5N9XTub6^k#n0_hjQ,_V\TqEhi7/)4h[U0.42RP,R2nPQ-Jt,d#JC0ib*-`:kk[N>bbt\_[e0J?Pp:2q,r\-4E@tUgjeO!QBK)Ad9harQ:OTLB/4j#G4'e3[aQ+VHT-"g8oWF%\XYoiTpt%caYLkRk]Tm9pYVkOFn/>(4<;tM:6m6qqMhPJN>gpendstream -endobj -37 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 1681 ->> -stream -Gb!Sm>B?8p&:WeDlq>[#SQ[-:6KIC,kq+]Z2Jq9AQb3kQ4#C&[!@R9Lmn25_j#FNiAB)[`(j+NgDf8e[K`Jm;rQ?(oRka^,.8U6/$.+Jr0Kt58A+,aS(hZDS!sqC-V@6!6g(..]:IE>4:K^:U":5Z3"Hf?FLf=AlF\+n5W"_p3Gn`fdK1&*m"HBtM)s]l.)+2seM)eA8Y@#F"XEZ2Q9Bf9sp4pS*#$liGW.kljBUm!pT#'9]cTdHJURA,cSf:VFpSLjZ]Ra#$&i_ih*Ro4/2_'Tll;EmZ5#]%<$0.X3IV!d6kXQ400oEG8s&dge!<;B)9NnGDfsK*DJf<#3Ji[0LibU&W&dT,)6%ZPTSKk[S'^.j?h/CtKE)k")]A_]('R[A:C:'^);IFL4EhcKPI6F\!3cF^KG/7qk1)FG,7+MtDU-M@pl!ONYT`Mudt`SU8Y[m'ejcs5*U4F'_V[dQId]j/C4e0``!QL*@DYA<[Q46YNO`ldl.>mMMaQD)/=J5uVm\2$RtKUUANoR@fD11]OX?"JK,^7[D!n\:_KhH*6e5aVC?6RE;7dKi7$sb;BT\13BdQ;)aR[E5O3iW9:t$?sQrp\)Ma(G`P2cTkHVBcLt[M2!9cQ,0e?Pc/2l:h-g-A)Z)/ea.[a`r84LoPVi%T/8&R=BDs:kO3@G_&7-kkN35isJH_ZLZdqPtc%XUBO2!Df\)YcThk5EW9%SdE0["Pr%lVDPgOC"1/KR[@jTT4*LngS7tPZND9EI6Obg/AS0A,$/Och9MA3kIqn8rIp_Dkc''2.N>BH+lDRLiS(8>;m=j1HN2af[^_TN4#he@nFW`=qJNbN"84^`gk!o&Bd.a+L2!4'>nV.#]*rnre9&dGW_>S+)UJr&cf11mC?QA/"32WL#\LAKJ3K34a^#Q1:Bai_6E%&;5M[P0kDe7G>Vi2rH7k)XrALaK`UMWn&7h@KJSG/7k=\)[!]1iT60jT(Bu]7(4e-aPWdn]fib;I;OG[jf\AY`a&OoXbjkB-HiPHN;SqsCBk88HQk+K"N'[.H:@4dY'p=5?4V>tdl?Ft%/RHRG7D'(19*.aXi[)V'?oaqZ&"K.&cJcN>TY2.6k8RMo)PeR5d$BX14N2)O>PH8_E,r$j*+>Uj6ZDMdJEEQlL70QeY%W^(U5?IUBrc`WKhXQrq%_P;tgI2`3PaVV63'HAa*g/RI?"/Lh21./f*'Ij@*7-X;:j"ZRqk./N>E*5R&PjC7IRb?KGWd;@!YJ#EUQ("WYcT\@X%9Jt+)ZX-BncGd/;j&_m#i8nqD>@H~>endstream -endobj -xref -0 38 -0000000000 65535 f -0000000061 00000 n -0000000133 00000 n -0000000240 00000 n -0000000352 00000 n -0000000557 00000 n -0000000662 00000 n -0000000867 00000 n -0000001072 00000 n -0000001149 00000 n -0000001354 00000 n -0000001560 00000 n -0000001766 00000 n -0000001972 00000 n -0000002178 00000 n -0000002294 00000 n -0000002500 00000 n -0000002706 00000 n -0000002912 00000 n -0000003118 00000 n -0000003324 00000 n -0000003530 00000 n -0000003600 00000 n -0000003891 00000 n -0000004043 00000 n -0000005997 00000 n -0000007903 00000 n -0000009685 00000 n -0000011503 00000 n -0000014080 00000 n -0000015792 00000 n -0000017893 00000 n -0000019960 00000 n -0000022281 00000 n -0000023417 00000 n -0000025249 00000 n -0000025754 00000 n -0000027172 00000 n -trailer -<< -/ID -[] -% ReportLab generated PDF document -- digest (opensource) - -/Info 22 0 R -/Root 21 0 R -/Size 38 ->> -startxref -28945 -%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md b/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md deleted file mode 100644 index 99ef937..0000000 --- a/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md +++ /dev/null @@ -1,486 +0,0 @@ -# Les 2 β€” OpenCode Pro: Rules, Worktrees & Scroll Animations -## Slide Overzicht (Klas B β€” 2 uur, online) - -**Bronnen-overzicht onderaan dit document.** - -**Lesflow:** Theorie eerst in blokken, dan dedicated **Live Demo** momenten. Geen heen-en-weer geswitch. - ---- - -## Slide 1: Title -### Les 2 β€” OpenCode Pro - -**Visual:** -- Background: CREAM -- "Les 2" in BLUE -- "OpenCode Pro" in BLACK -- Subtitle: "Rules Β· Worktrees Β· Een scroll-animatie site bouwen" - -**Bronnen:** [1] - ---- - -## Slide 2: Terugblik -### Waar staan we? - -**Content:** -- Les 1: kennismaking + leerlijn-opzet -- Iedereen kent ChatGPT en/of Claude (als chat) -- Eindopdracht: AI-app met Next.js + Supabase + Vercel AI SDK - -**Vandaag:** -"Van AI als chat naar AI als coding-IDE. Met regels, config en parallel worktrees." - -**Visual:** Chat-bubble β†’ IDE icoon - ---- - -## Slide 3: Planning -### Vandaag β€” 120 minuten - -| Onderwerp | Duur | -|-----------|------| -| Welkom + Terugblik | 10 min | -| **Theorie 1** β€” Waarom OpenCode + kern features | 20 min | -| **Live Demo 1** β€” Desktop tour | 10 min | -| **Theorie 2** β€” AGENTS.md + config + plugin + stack + Vercel | 17 min | -| **Live Demo 2** β€” Setup + worktree + bouw + Vercel preview | 20 min | -| **Pauze** | 15 min | -| Lesopdracht: bouw scroll-sectie + preview | 28 min | -| Huiswerk + Afsluiting | (eind) | - -**Visual:** Timeline met YELLOW pauze-rij + PINK demo-rijen - ---- - -## Slide 4: Waarom OpenCode (en niet Cursor)? -### Alternatieven kennen is professioneel - -**Iedereen kent Cursor. Wij beginnen bewust met OpenCode. Waarom?** - -| | Cursor | OpenCode | -|---|--------|----------| -| Licentie | Commercieel ($20/mnd) | Open source (gratis) | -| Models | Eigen routing | Direct: OpenAI, Anthropic, Google, Groq, Ollama | -| Lock-in | Hoog | Geen | -| Scriptable | Beperkt | Volledig (CLI + plugins) | -| Lokale AI | ❌ | βœ… (via Ollama) | - -**Belangrijke punten:** -- **Provider-keuze** β€” jij beslist welk model, niet de tool -- **Geen vendor lock-in** β€” past in een pro-toolchain -- **Concept is hetzelfde** β€” leer OpenCode goed, dan begrijp je Cursor + Claude Code ook -- **Onderdeel van leerlijn** β€” later kijken we naar Cursor; nu de open variant - -**Belangrijk:** dit is **geen Cursor-bashing**. Cursor is uitstekend. We kiezen voor breedte: meerdere tools kennen > vasthangen aan één. - -**Bronnen:** [1] [4] - ---- - -## Slide 5: Wat is OpenCode? -### Een AI coding-IDE in twee smaken - -**Content:** -- Open source Β· 60.000+ GitHub stars Β· door SST -- Werkt met elke provider: OpenAI, Anthropic, Google, Groq, Ollama -- Lokaal β€” code blijft op jouw machine - -**Twee smaken:** - -| Desktop (vandaag) | TUI (terminal) | -|-------------------|----------------| -| Visuele diff-viewer | Snel met keyboard | -| File tree + Sessions sidebar | Werkt over SSH | -| Multi-session parallel | Goed voor scripts / CI/CD | -| Sneller leren als je begint | Worktree plugin spawnt auto terminal | - -**Vandaag:** Desktop. We tonen TUI **1 minuut** in Live Demo zodat je weet dat 't bestaat. - -**Voor jouw eindopdracht: Desktop.** - -**Bronnen:** [1] [2] [3] [5] - ---- - -## Slide 6: Plan/Build/@explore + /init -### De vier dingen die je echt moet kennen - -**Twee modes** (Tab om te wisselen): -- **plan** β€” read-only, denkt mee, wijzigt niks -- **build** β€” default, schrijft code, voert commands uit - -**Sub-agents:** -- `@explore` β€” read-only verkenning (spaart tokens) -- `@general` β€” brede taak -- `@scout` β€” gerichte zoekopdracht - -**`/init` commando** β€” eerste actie in elk nieuw project: -- Scant je repo -- Stelt vragen -- Schrijft/update je `AGENTS.md` - -**Best practice:** -> Plan eerst. Lees mee. Geef feedback. Tab β†’ Build. - -**Bronnen:** [6] [7] [8] - ---- - -## Slide 7: LIVE DEMO 1 β€” Desktop Tour -### Wat je nu gaat zien (~10 min) - -**Setup:** OpenCode Desktop open op een leeg Next.js project. - -**Wat ik laat zien:** -1. **TUI 1 minuut** β€” `opencode` in terminal, snelle Tab/exit. "Hetzelfde, maar terminal-only." -2. **Desktop UI tour** β€” file tree, chat panel, diff viewer, Sessions sidebar -3. **Plan mode** β€” vraag `analyseer dit project`, zie dat niks wijzigt -4. **Tab β†’ Build mode** β€” vraag iets simpels, zie diff -5. **`@explore`** β€” `@explore wat zit er in deze repo?` β€” aparte context -6. **`/init`** β€” laat AGENTS.md genereren - -**Visual:** Mockup van Desktop layout met annotaties. PINK badge "LIVE DEMO". - ---- - -## Slide 8: AGENTS.md β€” Projectregels -### De officiΓ«le OpenCode standaard - -**Wat is AGENTS.md?** -Markdown in project-root dat OpenCode automatisch in elke context laadt. Officieel format β€” alternatief voor Cursor's `.cursorrules`. - -**Twee locaties:** -- `./AGENTS.md` β€” per project (in repo) -- `~/.config/opencode/AGENTS.md` β€” globaal voor alle projecten - -**Externe regels referencen:** -``` -@rules/styling.md -@rules/security.md -``` - -**Hoe te maken:** -- Handmatig schrijven, OF -- `/init` in OpenCode β€” scant repo, stelt vragen, genereert - -**Tip:** schrijf concrete regels die je AI kan controleren. "Gebruik clean code" werkt niet. "useGSAP alleen, geen useEffect" wel. - -**Bronnen:** [9] - ---- - -## Slide 9: opencode.json β€” Config -### Model Β· Permissies Β· Plugins - -**Locaties:** -- `./opencode.json` β€” per project -- `~/.config/opencode/opencode.json` β€” globaal - -**Basis:** -```json -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-4o-mini" -} -``` - -**Permissies (uit officiele docs):** -```json -{ - "permission": { - "bash": { "*": "ask", "git *": "allow", "rm *": "deny" }, - "edit": { "*": "deny", "app/**/*.tsx": "allow" } - } -} -``` - -Drie levels: `allow` Β· `ask` Β· `deny`. Glob patterns voor bash Γ©n files. - -**Bronnen:** [10] [11] - ---- - -## Slide 10: opencode-worktree Plugin -### Auto-magisch worktrees aanmaken - -**Wat doet het?** -Plugin van **kdcokenny**. Geeft de agent twee tools: -- `worktree_create` β€” branch + worktree -- `worktree_delete` β€” commit + cleanup - -**Installatie via `ocx`:** -```bash -curl -fsSL https://kdco.dev/ocx/install.sh | sh -ocx add kdco/worktree --from https://registry.kdco.dev -``` - -**Desktop workflow:** -1. In main session: "Maak worktree feature-hero" -2. Plugin maakt worktree-folder -3. Klik **+ New Session** β†’ **Open Folder** β†’ kies worktree -4. Parallel agents in sidebar - -**Bronnen:** [12] [13] - ---- - -## Slide 11: Demo Stack β€” Next.js 16 + GSAP + Lenis -### De stack voor high-end scroll storytelling - -**Stack:** -- **Next.js 16** App Router + TypeScript (Turbopack default) -- **TailwindCSS** -- **GSAP 3.15** + `@gsap/react` β€” sinds 2025 100% gratis (ScrollTrigger, SplitText) -- **Lenis 1.3** (`lenis/react`) β€” door Darkroom Engineering - -**Waarom deze combinatie?** - -Dit is dΓ© stack waarmee award-winning scroll-storytelling sites gebouwd worden. -Niet "een" optie β€” **de** standaard op pro-niveau. - -**Wie gebruikt dit?** -- Apple product pages (GSAP scroll animaties) -- Stripe, OpenAI marketing β€” beide GSAP-based -- Awwwards / FWA winnaars β€” bijna altijd GSAP -- Studios: **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** (makers van Lenis zelf) - -**Waarom niet Framer Motion?** -- Framer Motion is uitstekend voor **app UI** (modals, transities, micro-interacties) -- GSAP heeft betere timing-precisie en GPU-optimalisaties voor **scroll storytelling** -- Voor jouw eindopdracht (een site die "wow" doet bij scrollen): GSAP - -**Next.js 16 detail:** -- `cookies()`, `headers()`, `params`, `searchParams` zijn **async** β€” altijd `await` - -**Bronnen:** [14] [15] [16] [17] [18] [19] - ---- - -## Slide 12: Onze AGENTS.md -### Regels voor het scroll-animatie project - -```markdown -# Project Rules - -## Why this stack -High-end scroll storytelling site (Awwwards/FWA niveau). -GSAP + Lenis = pro standaard β€” Apple, Stripe, OpenAI, Active Theory, -Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor -scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. - -## Stack -- Next.js 16 (App Router, TypeScript, Turbopack) -- TailwindCSS Β· GSAP 3.15+ (incl. ScrollTrigger, SplitText) -- Lenis 1.3+ (`lenis/react`) -- @gsap/react voor de useGSAP hook - -## Hard rules -- Geen Framer Motion, react-spring, AOS -- Animaties altijd in Client Components (`"use client"`) -- useGSAP(() => {}, { scope: ref }) β€” nooit useEffect voor GSAP -- Lenis sync: gsap.ticker.add met autoRaf: false -- Next 16: await params, await cookies(), await headers() - -## Patterns -- EΓ©n wrapper in app/layout.tsx -- Tailwind voor layout; GSAP voor transform/opacity - -## Done = -- Geen hydration warnings -- ScrollTrigger.refresh() na dynamische content -``` - -**De `## Why this stack` sectie** geeft de agent context. Hij snapt nu -*waarom* deze keuzes β€” en stelt voor dezelfde redenen geen Framer Motion voor. - -**Bronnen:** [14] [15] [19] - ---- - -## Slide 13: Vercel + Preview Deployments -### Feature branches = automatische preview URLs - -**Het idee:** -Elke push naar GitHub β†’ Vercel maakt automatisch een **unieke preview URL** voor die branch. Geen handmatige deploys, geen "stuur me een screenshot". - -**Setup (eenmalig per project):** -1. `npx vercel link` β€” koppelt repo aan Vercel project (of via dashboard) -2. Git remote staat β†’ Vercel installeert eigen GitHub App -3. Klaar β€” elke `git push` triggert deploy - -**Hoe het werkt:** -| Branch | Wat krijgt Vercel? | URL | -|--------|-------------------|-----| -| `main` | Production deploy | `jouw-app.vercel.app` | -| `feature-hero` | Preview deploy | `jouw-app-git-feature-hero-jij.vercel.app` | -| `feature-gallery` | Preview deploy | `jouw-app-git-feature-gallery-jij.vercel.app` | - -**Waarom dit goud is voor scroll-storytelling:** -- Stuur preview URL naar designers/stakeholders per feature -- Vergelijk verschillende secties side-by-side -- Geen "het werkt op mijn machine" -- Comment in PR direct met working URL - -**Combinatie met onze worktrees:** -- 1 worktree = 1 branch = 1 Vercel preview -- 3 features parallel = 3 live previews - -**Bronnen:** [20] [21] - ---- - -## Slide 14: LIVE DEMO 2 β€” Setup + Worktree + Vercel preview -### Wat je nu gaat zien (~20 min) - -**Wat ik laat zien:** -1. **AGENTS.md** invullen met onze projectregels -2. **opencode.json** maken met permissions -3. Tonen dat `rm -rf` geblokkeerd wordt -4. **`ocx` + worktree plugin** verifiΓ«ren (was al klaar) -5. Via prompt: **worktree feature-hero** aanmaken -6. **+ New Session** β†’ openen op worktree-folder -7. **SmoothScroll wrapper** laten bouwen β€” agent volgt AGENTS.md -8. **`git push origin feature-hero`** vanuit worktree -9. Open Vercel dashboard β†’ toon **preview URL** verschijnt automatisch -10. Open preview URL β†’ scroll-animatie werkt live - -**Visual:** Mockup Sessions sidebar + Vercel preview URL. PINK badge "LIVE DEMO". - ---- - -## Slide 15: Pauze -### Pauze! - -**Visual:** "Pauze" groot, "15 minuten" - ---- - -## Slide 16: Lesopdracht -### Bouw een scroll-sectie + preview β€” 28 minuten - -**Setup (al klaar in starter):** -- Next.js 16 starter -- AGENTS.md, opencode.json, plugin -- GSAP + Lenis geΓ―nstalleerd -- SmoothScroll wrapper bestaat al - -**Jouw taak:** -0. Fork starter naar **jouw** GitHub + `vercel link` -1. `/init` runnen β€” check AGENTS.md -2. Vraag agent: maak worktree voor jouw feature -3. Open de worktree in nieuwe Sessions tab -4. Plan-mode β†’ review β†’ Tab β†’ Build -5. Test lokaal op `npm run dev` -6. `git push origin ` β€” Vercel deployt automatisch -7. Open preview URL β†’ animatie werkt live β†’ plak in chat - -> **Niet klaar in de les?** Geen probleem β€” je werkt thuis in dezelfde fork -> verder. Het huiswerk bouwt voort op dit punt. - -**Sectie keuze:** hero (SplitText) Β· features-grid (stagger) Β· testimonials (horizontaal) Β· gallery (parallax) - ---- - -## Slide 17: Huiswerk -### Bouw verder in je les-repo - -**Voor volgende week:** -Doorbouwen in dezelfde fork als in de les β€” eerst sectie 1 afmaken (indien nog niet), dan 2-3 extra secties tot een **kleine landing page** met 3-4 scroll secties. - -**Eisen:** -- [ ] Eigen `AGENTS.md` (onze mag als basis) -- [ ] `opencode.json` met permissions -- [ ] **2+ worktrees** gebruikt (Sessions sidebar) -- [ ] **3 verschillende scroll-animaties** -- [ ] Lenis smooth scroll -- [ ] **Vercel gekoppeld** β€” main = productie, branches = previews -- [ ] `WORKFLOW.md` reflectie (max 400 woorden) met **screenshots van preview URLs per feature** - -**Lever in:** GitHub URL + Production URL + minstens **2 preview URLs** (per worktree-branch) + screenshot `git worktree list` - -**Bonus:** Eigen sub-agent Β· MCP server (context7) Β· Lighthouse 90+ - ---- - -## Slide 18: Afsluiting -### Volgende les β€” Introductie Cursor - -**Vandaag gedaan:** -- Waarom OpenCode (vs Cursor) -- Plan/Build/@explore/`/init` -- AGENTS.md + opencode.json -- opencode-worktree plugin via ocx -- Live: Next.js 16 + GSAP + Lenis scroll-site -- Parallel agents via Sessions sidebar -- Vercel preview deployments per branch - -**Volgende keer β€” Cursor:** -- De commerciele tegenhanger van OpenCode -- Cursor rules vs AGENTS.md -- Composer, Tab-completion, @-references -- Wanneer kies je welke tool? - -**Vragen? Feedback?** - ---- - -## Slide Summary - -| # | Title | Type | Bronnen | -|---|-------|------|---------| -| 1 | Title | Opening | [1] | -| 2 | Terugblik | Intro | β€” | -| 3 | Plan | Intro | β€” | -| 4 | Waarom OpenCode | Theorie | [1] [4] | -| 5 | Wat is OpenCode | Theorie | [1] [2] [3] [5] | -| 6 | Plan/Build/@explore | Theorie | [6] [7] [8] | -| 7 | **LIVE DEMO 1** | Demo | β€” | -| 8 | AGENTS.md | Theorie | [9] | -| 9 | opencode.json | Theorie | [10] [11] | -| 10 | Worktree plugin | Theorie | [12] [13] | -| 11 | Demo stack | Theorie | [14]–[18] | -| 12 | Onze AGENTS.md | Theorie | [14] [15] [19] | -| 13 | Vercel preview deployments | Theorie | [20] [21] | -| 14 | **LIVE DEMO 2** | Demo | β€” | -| 15 | Pauze | Break | β€” | -| 16 | Lesopdracht | Praktijk | alle | -| 17 | Huiswerk | Praktijk | alle | -| 18 | Afsluiting | Closing | β€” | - ---- - -## Bronnen - -**OpenCode (officieel):** -- [1] OpenCode hoofdsite β€” https://opencode.ai -- [2] OpenCode Download (Desktop) β€” https://opencode.ai/download -- [3] OpenCode docs β€” https://opencode.ai/docs/ -- [4] OpenCode GitHub (sst) β€” https://github.com/sst/opencode -- [5] DeepWiki: Desktop Applications β€” https://deepwiki.com/sst/opencode/6.7-desktop-applications -- [6] Modes (plan/build) β€” https://opencode.ai/docs/modes/ -- [7] Agents (sub-agents) β€” https://opencode.ai/docs/agents/ -- [8] Commands (slash) β€” https://opencode.ai/docs/commands/ -- [9] Rules (AGENTS.md) β€” https://opencode.ai/docs/rules/ -- [10] Config β€” https://opencode.ai/docs/config/ -- [11] Permissions β€” https://opencode.ai/docs/permissions/ - -**Worktree plugin:** -- [12] opencode-worktree β€” https://github.com/kdcokenny/opencode-worktree -- [13] ocx (extension manager) β€” https://github.com/kdcokenny/ocx - -**Next.js 16:** -- [14] Next.js 16 blog β€” https://nextjs.org/blog/next-16 -- [15] Upgrading to v16 β€” https://nextjs.org/docs/app/guides/upgrading/version-16 - -**GSAP:** -- [16] GSAP docs v3 β€” https://gsap.com/docs/v3/ -- [17] GSAP React (useGSAP) β€” https://gsap.com/resources/React/ -- [18] GSAP License (gratis sinds 2025) β€” https://gsap.com/standard-license - -**Lenis:** -- [19] Lenis GitHub β€” https://github.com/darkroomengineering/lenis - -**Vercel:** -- [20] Vercel Preview Deployments β€” https://vercel.com/docs/deployments/preview-deployments -- [21] Vercel + Git Integration β€” https://vercel.com/docs/git diff --git a/v2-klasB/Les02-OpenCode/Les02-Slides.pdf b/v2-klasB/Les02-OpenCode/Les02-Slides.pdf deleted file mode 100644 index d9f6012..0000000 Binary files a/v2-klasB/Les02-OpenCode/Les02-Slides.pdf and /dev/null differ diff --git a/v2-klasB/Les02-OpenCode/Les02-Slides.pptx b/v2-klasB/Les02-OpenCode/Les02-Slides.pptx deleted file mode 100644 index 2a344c2..0000000 Binary files a/v2-klasB/Les02-OpenCode/Les02-Slides.pptx and /dev/null differ diff --git a/v2/Les01-Introductie-AI/Les01-Bijlage-A-Lesopdracht.md b/v2/Les01-Introductie-AI/Les01-Bijlage-A-Lesopdracht.md deleted file mode 100644 index cb46a39..0000000 --- a/v2/Les01-Introductie-AI/Les01-Bijlage-A-Lesopdracht.md +++ /dev/null @@ -1,257 +0,0 @@ -# Bijlage A: Lesopdracht Les 1 - -## Van Schets naar Website met AI - -**Cursus:** AI Fundamentals -**Les:** 1 - Introductie AI & Large Language Models -**Duur:** ~55 minuten -**Werkvorm:** Individueel - ---- - -## Leerdoelen - -Na deze opdracht kun je: -- Een idee visualiseren door te tekenen -- ChatGPT gebruiken om een schets om te zetten naar een gedetailleerde prompt -- Tech stack, kleuren en stijl specificeren in je prompt -- v0.dev gebruiken om UI-componenten te genereren -- Zelfstandig een project deployen naar Vercel - ---- - -## Wat ga je maken? - -Je gaat een **complete landingspagina** bouwen voor een fictief product of bedrijf. Het bijzondere? **Je begint met een tekening op papier!** - -Je tekening wordt, via ChatGPT en v0.dev, een echte, werkende website die online staat. - -**Vereiste elementen:** -- Minimaal **3 secties** (bijv. hero, features, pricing, testimonials, footer) -- Minimaal **1 carousel** (image slider of testimonial slider) -- Minimaal **1 accordion** (FAQ of uitklapbare features) -- Je bent vrij in wat je tekent en welk "product" je bedenkt! - ---- - -## De Workflow - -``` -1. TEKEN je idee op papier - ↓ -2. Maak een FOTO van je tekening - ↓ -3. Upload naar CHATGPT + beschrijf je wensen - ↓ -4. ChatGPT maakt een gedetailleerde PROMPT - ↓ -5. Plak die prompt in V0.DEV β†’ Website! - ↓ -6. DEPLOY naar Vercel -``` - ---- - -## Stappenplan - -### Stap 1: Tekenen (15 min) - -Pak pen en papier en teken je complete landingspagina. Je hoeft geen kunstenaar te zijn! - -**Teken:** -- Meerdere secties onder elkaar (minimaal 3!) -- Geef aan waar je carousel komt -- Geef aan waar je accordion komt -- Tekst indicaties (schrijf "HEADLINE" of de echte tekst) -- Buttons en afbeelding placeholders - -**Tips:** -- Simpele blokken en lijnen zijn genoeg -- Schrijf bij elementen wat ze zijn -- Denk na over de flow van je pagina - -### Stap 2: Foto maken (2 min) - -Maak een duidelijke foto van je tekening met je telefoon. -- Zorg voor goede belichting -- Houd de camera recht boven de tekening -- Check of alles leesbaar is - -### Stap 3: ChatGPT Project - Schets + Beschrijving (15 min) - -Open je ChatGPT Project "Les 1 - v0.dev Prompt Engineer" en upload je foto. Voeg een gedetailleerde beschrijving toe. - -**Gebruik dit template:** - -``` -Dit is een schets voor een complete landingspagina. - -Beschrijf deze schets als een gedetailleerde prompt voor v0.dev. - -**Context:** -- Type website: [SaaS / Portfolio / Webshop / Blog / etc.] -- Naam/bedrijf: [Verzin een naam, bijv. "TaskFlow"] -- Doelgroep: [Bijv. startups, developers, consumenten] - -**Tech stack:** -- Gebruik React met Tailwind CSS -- Maak het responsive (mobile-first) - -**Kleuren:** -- Primary: [bijv. #2563EB (blauw) of beschrijf: "warm oranje"] -- Secondary: [optioneel] -- Of: [beschrijf het thema: "donker met neon accenten"] - -**Stijl/Look & Feel:** -- [Kies: Modern / Minimalistisch / Speels / Corporate / Retro / etc.] -- [Extra: "Met subtiele animaties" of "Clean en zakelijk"] - -**Let op:** Er moet een carousel in zitten en een accordion. - -Genereer een gedetailleerde Engelse prompt die ik direct kan gebruiken in v0.dev. -``` - -**Voorbeeld ingevuld:** - -``` -Dit is een schets voor een complete landingspagina. - -Beschrijf deze schets als een gedetailleerde prompt voor v0.dev. - -**Context:** -- Type website: SaaS landingspagina -- Naam/bedrijf: TaskFlow -- Doelgroep: Startups en kleine bedrijven - -**Tech stack:** -- Gebruik React met Tailwind CSS -- Maak het responsive (mobile-first) - -**Kleuren:** -- Primary: #2563EB (blauw) -- Secondary: #1E40AF (donkerder blauw) - -**Stijl/Look & Feel:** -- Modern en minimalistisch -- Professioneel maar toegankelijk -- Subtiele hover animaties op buttons - -**Let op:** Er moet een carousel in zitten en een accordion. - -Genereer een gedetailleerde Engelse prompt die ik direct kan gebruiken in v0.dev. -``` - -### Stap 4: Prompt kopiΓ«ren (1 min) - -ChatGPT genereert een gedetailleerde prompt. Kopieer deze volledig. - -**Let op:** De prompt is in het Engels - v0.dev werkt het beste met Engelse prompts. - -### Stap 5: v0.dev - Genereren (20 min) - -1. Ga naar [v0.dev](https://v0.dev) -2. Log in met je Vercel/GitHub account -3. Plak de prompt van ChatGPT -4. Klik op genereren -5. Bekijk het resultaat - -**Itereren voor carousel en accordion:** -Als de carousel of accordion er niet (goed) in zit, gebruik follow-up prompts: -- "Add a testimonial carousel with 3 slides" -- "Add an FAQ accordion section" -- "Make the carousel auto-scroll" -- "Style the accordion with smooth animations" - -**Let op:** Je hebt ~7 gratis generaties per dag. Gebruik ze slim! - -### Stap 6: Deploy naar Vercel (10 min) - -**Dit ga je zelf uitzoeken!** Dit is een belangrijke skill als developer. - -**Hints:** -- Je hebt een GitHub account nodig (maak gratis aan op github.com) -- Zoek naar "Add Integration" of "Deploy" in v0.dev -- v0 kan koppelen met GitHub β†’ Vercel deployt automatisch - -**Resultaat:** -- Je krijgt een live URL: `jouw-project.vercel.app` -- Je code staat nu ook in GitHub - -**Vastgelopen?** -- Vraag eerst een klasgenoot om hulp -- Probeer het 5 minuten zelf voordat je de docent vraagt -- Google is je vriend: "v0.dev deploy to vercel" - ---- - -## Op te leveren - -Aan het einde van de les heb je: - -1. **Je originele tekening:** - - Foto van je papieren schets (met 3+ secties zichtbaar) - -2. **ChatGPT conversatie:** - - Screenshot van je prompt + de gegenereerde output - - Noteer welke specificaties je meegaf (tech stack, kleuren, stijl) - -3. **v0.dev project:** - - Screenshot van je landingspagina met **carousel** zichtbaar - - Screenshot van je landingspagina met **accordion** zichtbaar - - Eventuele iteratie-prompts die je gebruikte - -4. **Live deployment:** - - Vercel URL van je gedeployde landingspagina - ---- - -## Specificaties Checklist - -Zorg dat je in je ChatGPT prompt het volgende hebt gespecificeerd: - -| Element | Voorbeeld | -|---------|-----------| -| βœ… Tech stack | "React met Tailwind CSS" | -| βœ… Kleuren | "#2563EB" of "warm oranje thema" | -| βœ… Stijl | "Modern, minimalistisch" | -| βœ… Type website | "SaaS landingspagina" | -| βœ… Responsive | "Mobile-first design" | -| βœ… Carousel | "Testimonial slider" of "Image carousel" | -| βœ… Accordion | "FAQ sectie" of "Uitklapbare features" | - ---- - -## Beoordeling - -Deze opdracht wordt niet formeel beoordeeld, maar is essentiΓ«le oefening voor de rest van de cursus. - -**Waar let de docent op:** -- Heb je alle stappen doorlopen? -- Heeft je landingspagina minimaal 3 secties? -- Zit er een werkende carousel in? -- Zit er een werkende accordion in? -- Heb je de deployment zelf uitgezocht? -- Kun je uitleggen hoe de workflow werkt? - ---- - -## Hulp nodig? - -- Vraag je buurman/buurvrouw -- Steek je hand op voor de docent -- Check de officiΓ«le documentatie: [v0.dev/docs](https://v0.dev/docs) - ---- - -## Bonus: Extra uitdaging - -Klaar en tijd over? Probeer: - -1. **Extra interactie:** Voeg een contact formulier of newsletter signup toe -2. **Andere stijl:** Genereer dezelfde schets met een compleet andere look & feel (dark mode?) -3. **Animaties:** Voeg scroll-animaties of hover-effecten toe via prompts -4. **Help een klasgenoot:** Uitleggen is de beste manier om te leren! - ---- - -*Succes en veel plezier met je eerste AI-gegenereerde website!* diff --git a/v2/Les01-Introductie-AI/Les01-Bijlage-B-Huiswerkopdracht.md b/v2/Les01-Introductie-AI/Les01-Bijlage-B-Huiswerkopdracht.md deleted file mode 100644 index 691e111..0000000 --- a/v2/Les01-Introductie-AI/Les01-Bijlage-B-Huiswerkopdracht.md +++ /dev/null @@ -1,84 +0,0 @@ -# Bijlage B: Huiswerkopdracht Les 1 - -## Voor volgende week - -**Cursus:** AI Fundamentals -**Les:** 1 - Introductie AI & Large Language Models -**Inleveren:** Via Teams, voor de volgende les - ---- - -## Opdracht 1: Reflectie (500 woorden) - -Schrijf een reflectie over je eerste ervaring met AI-tools. - -### Deelvragen - -Gebruik deze vragen als leidraad: - -1. **Wat vond je verrassend aan werken met AI?** - - Was er iets dat je niet had verwacht? - - Wat viel je op aan de snelheid, kwaliteit of manier van werken? - -2. **Wat ging goed? Wat vond je lastig?** - - Welke stappen in de workflow gingen soepel? - - Waar liep je tegenaan? Hoe heb je dat opgelost? - -3. **Hoe zie je AI jouw werk als developer veranderen?** - - Wat betekent dit voor jouw toekomstige manier van werken? - - Welke taken zou je vaker met AI willen doen? - -### Tips - -- Wees eerlijk en persoonlijk -- Geef concrete voorbeelden uit de les -- Het gaat om jouw ervaring, er zijn geen foute antwoorden -- Schrijf in je eigen woorden (niet door AI laten schrijven!) - ---- - -## Opdracht 2: Breid je v0 website uit - -Voeg minimaal **1 nieuwe sectie** toe aan je landingspagina van de les. - -### Mogelijke secties - -- Pricing table -- Features overzicht -- Contact formulier -- Footer met links -- Team sectie -- Extra testimonials -- Newsletter signup - -### Werkwijze - -Gebruik dezelfde workflow als in de les: - -1. **Teken** de nieuwe sectie op papier -2. **Upload** naar je ChatGPT Project -3. **Genereer** in v0.dev (vergeet niet: tech stack, kleuren, stijl!) -4. **Deploy** de update naar Vercel - -### Op te leveren - -- Screenshot van je nieuwe schets -- Screenshot van het v0 resultaat -- Je updated Vercel URL - ---- - -## Inleveren - -| Wat | Format | -|-----|--------| -| Reflectie | Word, PDF of direct in Teams | -| Screenshots | Afbeeldingen | -| Vercel URL | Link | - -**Waar:** Teams -**Deadline:** Voor de volgende les - ---- - -*Succes!* diff --git a/v2/Les01-Introductie-AI/Les01-Docenttekst-Notes.md b/v2/Les01-Introductie-AI/Les01-Docenttekst-Notes.md deleted file mode 100644 index e270041..0000000 --- a/v2/Les01-Introductie-AI/Les01-Docenttekst-Notes.md +++ /dev/null @@ -1,499 +0,0 @@ -# Les 1 - Keynote Notes - ---- - -## BLOK 1: WELKOM & INTRODUCTIE (15 min) - ---- - -### Slide 1: Welkom -- Wacht tot iedereen zit -- "Welkom bij de eerste les van AI Fundamentals" -- "Neem even de tijd om je laptop klaar te leggen" - ---- - -### Slide 2: Planning -- Loop door de tijdsblokken op de slide -- "We beginnen met theorie over AI en hoe het werkt" -- "Daarna praktisch aan de slag met ChatGPT" -- "Dan prompt engineering: hoe stel je de juiste vraag?" -- **Met nadruk:** "En dan de grote finale: van een tekening op papier naar een werkende website" -- "Jullie gaan vandaag letterlijk een website bouwen die begint als schets" - ---- - -### Slide 3: Voorstellen -- Naam, developer, gebruik dagelijks AI-tools -- "Dit vak verandert zo snel - wat vandaag cutting edge is, kan over 3 maanden achterhaald zijn" -- "2x per week les, bereikbaar via Teams" - ---- - -### Slide 4: Kennismaking (5 min studenten) -- "Vertel kort je naam" -- "Heb je al eens ChatGPT of andere AI-tool gebruikt? Waarvoor?" -- Laat iedereen kort reageren -- "Mooi, de meesten hebben al met AI gewerkt - goed startpunt" - ---- - -### Slide 5: Leerlijn overzicht -- "18 weken, 4 delen" -- **Deel 1 (les 1-4):** AI Foundations - basis, ChatGPT, OpenCode -- **Deel 2 (les 5-9):** Technical Foundations - TypeScript, Next.js, Supabase -- **Deel 3 (les 10-12):** AI Tooling - welke tool wanneer, prototypen, Cursor -- **Deel 4 (les 13-18):** Advanced - AI agents, Vercel AI SDK, eindproject -- "Maar dat is ver weg - vandaag: de basis en de workflow" - ---- - -### Slide 6: Accounts check -- "Drie accounts nodig vandaag" -- **ChatGPT:** "Wie heeft dit? Wie niet? β†’ chat.openai.com, gratis" -- **GitHub:** "Waar developers code opslaan - nodig voor deployen" -- **Vercel:** "Hier komt je website live te staan - vercel.com, koppel met GitHub" -- Geef 2 minuten om aan te maken -- "Iedereen zover? Alle drie? Mooi, dan gaan we de diepte in" - ---- - -## BLOK 2: WAT IS AI? (25 min) - ---- - -### Slide 7: Wat is AI? -- "Wat Γ­s eigenlijk AI?" -- **🎀 INTERACTIE:** "Noem eens voorbeelden van AI die je vandaag al hebt gebruikt?" - - **Spotify/Netflix:** "Analyseert je gedrag, voorspelt wat je leuk vindt" - - **Google Maps:** "Voorspelt verkeer, berekent snelste route" - - **Autocorrect:** "Letterlijk next-word prediction, zoals ChatGPT" - - **Gezichtsherkenning:** "Patroonherkenning in pixels" - - **Als niemand antwoordt:** "Je telefoon vanochtend - getypt? Autocorrect. Route? Google Maps. Muziek? Spotify kiest voor je. Allemaal AI." -- AI = computersystemen die taken uitvoeren waarvoor normaal menselijke intelligentie nodig is -- Patronen herkennen, beslissingen nemen, taal begrijpen en genereren -- **Narrow AI (Weak AI):** goed in één specifieke taak - ChatGPT is hier een voorbeeld -- **General AI (Strong AI):** alles kunnen wat mens kan - bestaat nog niet -- "Wat we vandaag gebruiken is Narrow AI - heel krachtig, maar beperkt tot specifieke taken" - ---- - -### Slide 8: Hoe werken LLMs? -- "Hoe werkt ChatGPT eigenlijk?" -- **🎀 INTERACTIE:** "Wat denken jullie dat er gebeurt als je iets typt?" - - **Kort antwoord:** "Het voorspelt steeds het volgende woord. Meer niet. Maar het doet dat zΓ³ goed dat het lijkt alsof het 'denkt'." -- LLM = Large Language Model -- Getraind op miljarden pagina's tekst - boeken, websites, Wikipedia, code -- **Kernvraag die het geleerd heeft:** "Als ik deze woorden zie, wat is het meest waarschijnlijke volgende woord?" -- ChatGPT, Claude, Gemini = voorspelmachines -- Token voor token voorspellen wat er daarna komt -- "Het lijkt alsof ze begrijpen wat je zegt, maar er zit geen bewustzijn in" -- "Geavanceerde statistische patroonherkenning op ongekende schaal" -- **🎀 INTERACTIE:** "Als LLMs alleen maar voorspellen, hoe kunnen ze dan creatieve ideeΓ«n genereren?" - - **HOW:** Model heeft miljarden teksten gelezen met concepten naast elkaar. 'Koffie' bij 'ochtend', 'strand' bij 'zon'. Vraag je 'cafΓ© op Mars' β†’ combineert patronen van beide. - - **WHY voelt het creatief?** Combinatie is nieuw voor jou, ook al zijn losse onderdelen dat niet. Geen echte intentie/inspiratie, maar mixt kennis op nieuwe manieren. - ---- - -### Slide 9: Tokens uitleg -- Tekst wordt omgezet naar nummers = tokens -- "Ik drink koffie" wordt [1847, 5621, 8934] -- Woorden met vergelijkbare betekenis liggen dicht bij elkaar -- "koffie" en "thee" = dichtbij / "koffie" en "fiets" = ver uit elkaar -- **Voorbeeld:** "Ik drink koffie met..." β†’ wat komt er? β†’ "melk" of "suiker" -- Model berekent kans per woord, kiest (meestal) meest waarschijnlijke -- **🎀 INTERACTIE:** "Als je ChatGPT 10x dezelfde vraag stelt, krijg je dan 10x hetzelfde antwoord?" (handopsteken ja/nee) - - **Antwoord: Nee!** Er zit bewust randomness in = temperatuur parameter - - **Wat is temperatuur?** Model berekent: 'melk' 60%, 'suiker' 35%, 'room' 5%. Lage temp β†’ kiest altijd 'melk'. Hoge temp β†’ gooit dobbelsteen, kiest soms 'suiker' - - **Waarom?** Anders steeds exact hetzelfde = robotachtig. Variatie maakt het menselijker/creatiever -- "Dit verklaart waarom je soms briljante antwoorden krijgt en soms onzin" - ---- - -### Slide 10: Hallucinaties ⚠️ -- **Belangrijk begrip!** -- AI kan dingen verzinnen - compleet uit de lucht gegrepen -- Klinkt heel overtuigend, grammaticaal perfect -- **Voorbeelden:** - - Wetenschappelijk paper dat niet bestaat (titel, auteurs, jaar, samenvatting) - - npm package dat nooit heeft bestaan - - Restaurants met verzonnen namen en adressen -- **Waarom?** Model voorspelt wat WAARSCHIJNLIJK is, controleert niet of het WAAR is -- **Praktisch:** Verifieer altijd! Kopieer nooit blindelings code. Check of libraries bestaan. Test je code. -- **🎀 INTERACTIE:** "Hoe herken je een hallucinatie? Wat zijn rode vlaggen?" - - **TΓ© specifieke details** (cijfers, datums, namen) β†’ WHY: vult gaten met wat 'waarschijnlijk' klinkt - - **Bronnen die niet te vinden zijn** β†’ WHY: genereert tekst die LIJKT op bronvermelding - - **Onbekende npm packages** β†’ "Ik heb zelf meegemaakt dat ChatGPT bleef volhouden dat een package bestond, zelfs 'documentatie' genereerde, terwijl code steeds errors gaf!" - - **Verdacht zelfverzekerd over obscure onderwerpen** β†’ WHY: weet niet wat het niet weet -- "AI is een tool, geen orakel. Behandel het als een enthousiaste junior die soms dingen verzint" -- **Regel: Vertrouw, maar verifieer!** -- **🎀 INTERACTIE:** "Wie heeft dit zelf meegemaakt? Dat ChatGPT iets verzon?" (laat 2-3 studenten ervaring delen) - ---- - -### Slide 11: De Grote Spelers -- **ChatGPT (OpenAI):** bekendste, maakte AI mainstream -- **Claude (Anthropic):** minder bekend, technisch heel sterk, goed voor lange documenten -- **Gemini (Google):** geΓ―ntegreerd in Google ecosysteem -- **Grok (xAI/Elon Musk):** in X, real-time informatie -- "Vandaag focus op ChatGPT - meest toegankelijk, beste gratis tier" -- "Concepten zijn toepasbaar op allemaal" - ---- - -### Slide 12: Gratis AI Opties -- "Moet je betalen voor AI?" -- **🎀 INTERACTIE:** "Wie betaalt er al voor een AI tool? ChatGPT Plus, Claude Pro?" (tel handen, vraag 1-2 waarom) - "En de rest - waarom niet?" (verwacht: gratis werkt prima, te duur, weet niet wat je extra krijgt) -- "Voor deze cursus hoef je niks te betalen!" -- **ChatGPT Free:** GPT-5, afbeeldingen uploaden, documenten, code -- **Claude Free:** Sonnet 3.5, uitstekend voor lange teksten -- **Gemini Free:** handig als je in Google ecosysteem zit -- **Groq:** gratis API met Llama 3.3, ongelooflijk snel -- **Ollama:** draait lokaal op je laptop, data verlaat je computer nooit β†’ privacy! -- "Voor later: als je met klantdata werkt, is Ollama je vriend" -- **Conclusie: gratis is voldoende voor deze cursus!** - ---- - -### Slide 13: Betaald & Premium -- **~$20/maand (ChatGPT Plus / Claude Pro):** - - Meer berichten, sneller, premium features - - Voldoende voor professioneel gebruik -- **Premium tier - Claude Opus:** - - Beste model voor complexe redenering - - ~$75/maand via API bij intensief gebruik - - Voor als je echt vastloopt -- **Advies:** Start gratis β†’ upgrade naar Plus/Pro als je limieten raakt β†’ Opus voor moeilijke problemen - ---- - -### Slide 14: Welke tool voor welke taak? -- "Welke is het beste? Hangt af van wat je wilt" -- **Snelle code snippets:** ChatGPT of Claude, beide goed -- **Lange documenten:** Claude (grotere context window) -- **UI componenten:** v0.dev (straks!) -- **Privacy-gevoelig:** Ollama (lokaal) -- **Supersnel prototypen:** Groq -- **Tip:** Experimenteer met meerdere tools - elke heeft sterke/zwakke punten - ---- - -## BLOK 3: CHATGPT PRAKTIJK (20 min) - ---- - -### Slide 15: Interface rondleiding -- "Open allemaal ChatGPT - chat.openai.com" -- Wacht tot iedereen erin zit -- **Midden:** chatvenster - vragen en antwoorden -- **Links:** chat geschiedenis -- **Boven:** model kiezen -- **Rechtonder (profielfoto):** instellingen -- "Interface is bewust simpel - kracht zit in wat je typt" - ---- - -### Slide 16: Model kiezen -- Kijk bovenaan je scherm -- **GPT-5:** standaard, snel, voor meeste taken genoeg -- **GPT-5 with canvas:** voor documenten bewerken, opent editor naast chat -- **GPT-5.2:** nieuwste en krachtigste (alleen Plus) -- "Voor vandaag is GPT-5 perfect" - ---- - -### Slide 17: Tijdelijke Chat -- **Privacy feature!** -- "Temporary Chat" bij nieuwe chat -- Gesprek wordt niet opgeslagen, niet gebruikt voor training -- **🎀 INTERACTIE:** "In welke situaties zou je tijdelijke chat willen gebruiken?" - - **Klantgegevens/bedrijfsdata** β†’ WHY: Kan gebruikt worden voor training. Jouw data in iemand anders' antwoord = datalek - - **Medische/persoonlijke info** β†’ WHY: AVG/GDPR - mag niet delen met derden (OpenAI = derde partij) - - **Interne bedrijfscode** β†’ WHY: Intellectueel eigendom - geheime algoritme in trainingsdata -- "Tijdelijke chat voorkomt dit - niet opgeslagen, niet voor training" -- "Voor deze cursus maakt niet uit, maar onthoud voor professioneel werk" - ---- - -### Slide 18: Afbeeldingen analyseren -- **Cruciaal voor onze workflow!** -- Klik paperclip β†’ upload afbeelding -- Demo: upload een schets, vraag "Wat zie je? Beschrijf de layout" -- ChatGPT beschrijft precies wat hij ziet -- Dit heet image-to-text of vision -- **Dit is de sleutel:** jullie tekenen schets β†’ foto β†’ upload naar ChatGPT β†’ ChatGPT vertaalt naar beschrijving voor code -- "Je hoeft geen designer te zijn - ChatGPT begrijpt ook ruwe schetsen" - ---- - -### Slide 19: Bestanden uploaden -- PDF, Word, Excel, code bestanden -- Handig voor: documentatie analyseren, code reviewen, data uit spreadsheets -- Limiet: ~50 pagina's per document -- Meerdere bestanden tegelijk mogelijk - ---- - -### Slide 20: Custom Instructions -- Ga naar Settings β†’ Personalisatie -- Permanent instellen wie je bent en hoe ChatGPT moet antwoorden -- Wordt meegenomen in Γ©lke chat -- **Voorbeeld:** "Ik ben frontend developer. React en Tailwind CSS. Junior niveau. Geef praktische code voorbeelden." -- "Stel dit in vΓ³Γ³r je begint met werken - scheelt herhaling" - ---- - -### Slide 21: ChatGPT Projects -- **Nieuwe feature!** -- Klik "New Project" in zijbalk -- Slimme werkruimtes: chats, bestanden, specifieke instructies bij elkaar -- Handig voor: school, werk, hobby projecten apart houden -- Geef naam + instructies die alleen voor dit project gelden -- **Projects hebben eigen geheugen** - anders dan normale chats -- Gratis tier: 5 bestanden per project - ---- - -### Slide 22: Praktijk Oefening (5 min studenten) -- **Twee dingen:** -- **1. Custom Instructions instellen:** - - Settings β†’ Personalisatie - - "Ik ben frontend developer. React en Tailwind CSS." -- **2. Project aanmaken:** - - "New Project" in zijbalk - - Naam: "Les 1 - v0.dev Prompt Engineer" - - Instructie: "Help me gedetailleerde prompts schrijven voor v0.dev. Focus op React + Tailwind CSS." -- Loop rond en help -- "Dit project gebruiken we straks!" -- Check: "Heeft iedereen een project aangemaakt?" - ---- - -## BLOK 4: PROMPT ENGINEERING (15 min) - ---- - -### Slide 23: Wat is Prompt Engineering? -- Prompt = de instructie die je aan AI geeft -- **Cruciaal inzicht:** kwaliteit antwoord hangt af van kwaliteit vraag -- **Garbage in = garbage out** -- Vage vraag β†’ vaag antwoord / Specifiek β†’ specifieke, bruikbare output -- "Dit is een vaardigheid - en je kunt het leren" - ---- - -### Slide 24: Slecht vs Goed -- **🎀 INTERACTIE:** "Hier staat: 'Maak een button'. Wat is hier mis mee?" -- **Slecht:** "Maak een button" - **WHY is dit slecht?** - - Welke taal? HTML, React, Vue, Angular - allemaal anders - - Welke stijl? Rond, vierkant, schaduw? - - Welke kleur? Blauw, rood, gradient? - - Hoe groot? Mobiel vs desktop? - - β†’ AI moet gokken β†’ generieke output β†’ jij moet aanpassen β†’ tijdverlies -- **Goed:** "Maak een React button component met Tailwind CSS. Primary styling met blauwe achtergrond (#2563EB). Include hover state en loading state met spinner." -- Je specificeert: technologie, styling, features -- Resultaat: bruikbare code die je direct kunt gebruiken - ---- - -### Slide 25: Prompt Framework -- **4 elementen:** -- **1. Rol:** "Je bent een senior frontend developer met 10 jaar ervaring" -- **2. Context:** "Je werkt aan een SaaS landingspagina voor projectmanagement tool" -- **3. Taak:** "Maak een hero section met headline, subtitel, call-to-action button" -- **4. Format:** "Gebruik React en Tailwind CSS. Geef alleen code, geen uitleg." -- **🎀 INTERACTIE:** "Welk element vinden jullie het belangrijkst? Rol, context, taak, of format?" (handopsteken) - - **Taak:** Enige verplichte element - zonder weet AI niet wat te doen. Maar alleen taak = generieke output - - **Context:** Maakt vaak grootste verschil. 'Maak button' vs 'Button voor medische app, oudere gebruikers' = heel andere output - - **Rol:** Helpt AI juiste 'stem' vinden. Senior developer β‰  beginner. Copywriter β‰  technisch schrijver - - **Format:** Voorkomt herschrijven. 'Geef alleen code' scheelt tijd -- Combinatie is krachtig. Niet altijd alle vier nodig, maar meer context = beter resultaat - ---- - -### Slide 26: Tech Stack specificeren -- Tech stack = de technologieΓ«n waarmee je bouwt -- **Deze cursus:** React + Next.js + Tailwind CSS -- Zonder specificatie weet AI niet: HTML? React? Vue? Angular? -- Door expliciet te zeggen "React met Tailwind CSS" β†’ direct bruikbare code -- **v0.dev genereert automatisch React + Tailwind** -- **In ChatGPT moet je het wel specificeren!** - ---- - -### Slide 27: Kleuren en Stijl -- **Kleuren:** - - Exact: "#2563EB voor primary blue" - - Beschrijvend: "warm oranje thema met aarde-tinten" -- **Stijl:** modern, minimalistisch, speels, corporate, brutalist, retro... -- **Referenties:** "In de stijl van Stripe.com" of "vergelijkbaar met Apple website" -- **Complete specificatie:** "Blauw kleurenschema #2563EB. Modern en minimalistisch. Clean met veel whitespace. B2B SaaS startup." - ---- - -### Slide 28: Tips samenvatting -- Geef een rol -- Wees specifiek -- Specificeer tech stack -- Beschrijf look & feel -- Geef voorbeelden als je kunt -- Itereer - eerste prompt is zelden perfect -- **Onthoud: garbage in = garbage out** - ---- - -## BLOK 5: LIVE DEMO V0.DEV (20 min) - ---- - -### Slide 29: Intro v0.dev -- Tool van Vercel (bedrijf achter Next.js) -- **Gamechanger voor UI development** -- Beschrijf wat je wilt in tekst OF upload afbeelding -- v0 genereert werkende React + Tailwind code -- Direct preview zien -- Direct deployen naar internet -- "De slimme workflow combineert ChatGPT en v0 - daar zit de magie" - ---- - -### Slide 30: De Workflow -- **5 stappen:** -- 1. **TEKEN** je idee op papier -- 2. **FOTO** maken met telefoon -- 3. **CHATGPT** - upload foto + beschrijf wensen (tech, kleuren, stijl) -- 4. ChatGPT maakt gedetailleerde **PROMPT** -- 5. Plak in **V0.DEV** β†’ werkende website! -- **Waarom zo?** - - v0 heeft beperkte credits (~7/dag), ChatGPT is onbeperkt - - ChatGPT is beter in vertalen ruwe idee β†’ gestructureerde prompt - - v0 is beter in code genereren - - "Gebruik elk tool waar het goed in is" - ---- - -### Slide 31: LIVE DEMO (15 min) - -**Stap 1: Tekenen** -- Pak pen en papier -- Teken simpele hero section -- Links: tekstvak headline, daaronder button -- Rechts: vierkant met X = placeholder afbeelding -- "Het hoeft niet mooi te zijn - blokken en lijnen zijn genoeg" - -**Stap 2: Foto naar ChatGPT** -- Foto maken -- Upload naar ChatGPT Project "Les 1 - v0.dev Prompt Engineer" -- Beschrijving: "Dit is schets voor hero section. Beschrijf als v0.dev prompt. Tech: React + Tailwind. Kleuren: blauw #2563EB. Stijl: modern, minimalistisch." -- Wacht op response -- "Kijk - ChatGPT maakt gedetailleerde Engelse prompt" - -**Stap 3: v0.dev** -- Prompt kopiΓ«ren -- Plakken in v0.dev -- Genereren (~30 sec wachten) -- "En daar is het! Hero section met de kleuren en stijl die ik specificeerde" - -**Stap 4: Itereren** -- Follow-up: "Make the button larger with hover animation" -- Wacht op generatie -- "Kijk - aangepast. DΓ‘t is de kracht: in gesprek gaan met de AI" - -**⚠️ DEPLOYMENT NIET VOORDOEN!** -- "Dit gaan jullie straks zelf uitzoeken" - ---- - -### Slide 32: Deploy uitleg -- "Hoe krijg je dit online? Ik leg uit, maar doe niet voor" -- In v0: "Add Integration" β†’ koppel GitHub β†’ v0 maakt repository β†’ Vercel deployt -- Binnen minuut: live URL -- **"Dit ga ik niet live doen - jullie zoeken dit straks zelf uit"** -- "Het is niet moeilijk, je leert meer door het zelf te doen" -- "Van tekening op papier naar live website - dat is de kracht van deze workflow" - ---- - -## BLOK 6: HANDS-ON OPDRACHT (55 min) - ---- - -### Slide 33: Aan de slag! (5 min uitleg + 45 min werken) - -**Opdracht uitleggen:** -- "Jullie gaan een complete landingspagina bouwen - groter dan mijn demo" -- **Vereisten:** - - Minimaal **3 secties** (hero, features, pricing, testimonials, footer...) - - Minimaal **1 carousel** (image/testimonial slider) - - Minimaal **1 accordion** (FAQ of uitklapbare content) -- Je bent vrij in wat je tekent - bedenk fictief product -- **Deployment: zelf uitzoeken!** - -**Stappen:** -- Teken β†’ Foto β†’ ChatGPT Project β†’ v0.dev β†’ Deploy naar Vercel - -**Check:** "Heeft iedereen papier en pen?" - -"Je hebt 45 minuten. Begin met tekenen! Succes!" - ---- - -**Tijdens het werken - rondlopen, let op:** -- Tekenstap overslaan? β†’ Terug naar papier -- Te weinig secties? β†’ Herinner aan 3+ vereiste -- Carousel/accordion vergeten? β†’ Check hun schets -- Tech stack/kleuren/stijl vergeten in ChatGPT? β†’ Herinner ze -- v0 rate limit? β†’ Wachten of delen met klasgenoot -- Deployment problemen? β†’ Eerst zelf Googlen, dan pas helpen - ---- - -**Tussentijdse check (halverwege ~25 min):** -- "Snelle check: wie heeft 3+ secties getekend?" -- "Wie heeft carousel en accordion in de schets?" -- "Nog 25 minuten - begin met deployen als je v0 resultaat hebt!" -- "Vraag klasgenoot om hulp als je vastloopt" - ---- - -## BLOK 7: AFSLUITING (20 min) - ---- - -### Slide 34: Resultaten delen (8 min) -- "Wie wil zijn resultaat laten zien?" -- Laat 2-3 studenten scherm delen -- "Wat was je oorspronkelijke idee? Laat je schets zien. Wat heeft v0 ervan gemaakt?" -- Geef korte positieve feedback per student - ---- - -### Slide 35: Samenvatting -- "Wat hebben jullie vandaag geleerd?" -- Theorie: hoe LLMs werken, next-token prediction, hallucinaties -- ChatGPT praktisch: interface, afbeeldingen uploaden, Custom Instructions, Projects -- Prompt engineering: specificiteit, tech stack, kleuren, stijl -- **"En het belangrijkste: de workflow toegepast"** -- **Schets β†’ ChatGPT β†’ v0.dev β†’ live website** -- "Jullie zijn developers die AI tools kunnen gebruiken" - ---- - -### Slide 36: Huiswerk -- "Twee opdrachten, inleveren via Teams voor volgende les" - -**1. Reflectie (500 woorden)** -- Drie deelvragen als leidraad: - - Wat vond je verrassend aan werken met AI? - - Wat ging goed? Wat vond je lastig? - - Hoe zie je AI jouw werk als developer veranderen? -- "Dit hoeft geen essay te zijn - gewoon eerlijk nadenken over je ervaring" - -**2. Breid je v0 website uit** -- Voeg minimaal 1 nieuwe sectie toe (pricing, features, contact, footer...) -- Gebruik zelfde workflow: teken β†’ ChatGPT β†’ v0 -- Lever in: screenshots + Vercel URL - -"Vragen? Nee? Dan bedankt voor jullie inzet!" - -**"Jullie hebben een website gebouwd die begon als een tekening. Tot volgende week!"** diff --git a/v2/Les01-Introductie-AI/Les01-Docenttekst.md b/v2/Les01-Introductie-AI/Les01-Docenttekst.md deleted file mode 100644 index 5525465..0000000 --- a/v2/Les01-Introductie-AI/Les01-Docenttekst.md +++ /dev/null @@ -1,990 +0,0 @@ -# Docenttekst Les 1: Introductie AI & Large Language Models - -> **Totale lesduur:** 3 uur (180 minuten) -> **Spreektijd docent:** ~55 minuten -> **Format:** Volledig uitgeschreven script per slide -> **Kernworkflow:** Schets op papier β†’ ChatGPT β†’ v0.dev β†’ Website - ---- - -## BLOK 1: WELKOM & INTRODUCTIE (13 minuten) - -### Slide 1: Welkomstslide - -**[0:00 - 0:30]** _(30 seconden)_ - -_[Wacht tot iedereen zit, maak oogcontact]_ - -"Goedemorgen allemaal, welkom bij de eerste les van AI Fundamentals. Fijn dat jullie er zijn. Neem even de tijd om je laptop open te klappen en je spullen klaar te leggen." - -_[Korte pauze van 20-30 seconden]_ - ---- - -### Slide 2: Planning - -**[0:30 - 2:00]** _(1,5 minuut)_ - -"Voordat we beginnen, laat ik jullie even zien wat we vandaag gaan doen. Zo weten jullie waar je aan toe bent." - -_[Wijs naar de tijdsblokken op de slide]_ - -"We beginnen met theorie over wat AI is en hoe het werkt. Daarna gaan we praktisch aan de slag met ChatGPT - ik laat zien hoe je die tool effectief gebruikt." - -"Dan komt het spannende deel: prompt engineering. Hoe stel je de juiste vraag? Hoe geef je aan welke technologie, kleuren en stijl je wilt?" - -_[Met nadruk]_ - -"En dan de grote finale: ik ga live demonstreren hoe je van een **tekening op papier** naar een **werkende website** gaat. En daarna gaan jullie dat zelf doen." - -"Jullie gaan vandaag letterlijk een website bouwen die begint als een schets op papier. Dat is het doel." - ---- - -### Slide 3: Voorstellen docent - -**[2:00 - 3:30]** _(1,5 minuut)_ - -"Laat ik mezelf even voorstellen. Mijn naam is [NAAM] en ik ben jullie docent voor deze leerlijn. Ik werk zelf als developer en gebruik dagelijks AI-tools in mijn werk." - -"Wat ik heel gaaf vind aan dit vak is dat het zo snel verandert - wat vandaag cutting edge is, kan over drie maanden alweer achterhaald zijn. Dat maakt het ook uitdagend om les te geven, maar vooral heel leuk." - -"Ik geef hier twee keer per week les en daarnaast werk ik aan mijn eigen projecten. Als je vragen hebt buiten de les om, kun je me altijd bereiken via Teams." - ---- - -### Slide 4: Kennismaking - -**[3:30 - 8:30]** _(5 minuten - studenten aan het woord)_ - -"Maar genoeg over mij - ik ben veel nieuwsgieriger naar jullie. Laten we een snelle ronde doen. Vertel kort je naam en geef antwoord op deze vraag..." - -_[Wijs naar de slide]_ - -"...heb je al eens ChatGPT of een andere AI-tool gebruikt? En zo ja, waarvoor?" - -_[Laat iedereen kort reageren]_ - -"Mooi, ik zie dat de meesten van jullie al wel eens met AI hebben gewerkt. Dat is een goed startpunt." - ---- - -### Slide 5: De Leerlijn AI Developer - -**[8:30 - 10:30]** _(2 minuten)_ - -"Even een vooruitblik op wat we de komende 18 weken gaan doen. De cursus is verdeeld over vier delen." - -_[Wijs naar de vier delen op de slide]_ - -"Deel 1, les 1 tot 4, is waar we nu zitten: AI Foundations. Hier leer je de basis - wat is AI, hoe werkt het, prompt engineering, en tools als ChatGPT en OpenCode." - -"Deel 2, les 5 tot 9, gaat over Technical Foundations. Dan leer je TypeScript, Next.js, en databases met Supabase. Een stevige technische basis." - -"Deel 3, les 10 tot 12, focust op AI Tooling. Je leert welke tool je wanneer gebruikt, hoe je snel prototypes bouwt, en je maakt kennis met Cursor." - -"En deel 4, les 13 tot 18, is het eindspel: Advanced AI Features. AI agents, de Vercel AI SDK om AI in je eigen app te bouwen, en deployment. Je eindigt met een volledig werkend eindproject." - -"Maar dat is ver weg. Vandaag focussen we op de basis - en vooral op de workflow van schets naar website." - ---- - -### Slide 6: Accounts check - -**[10:30 - 13:00]** _(2,5 minuten)_ - -_[Iets serieuzer toon]_ - -"OkΓ©, even een praktisch punt. Jullie hebben vandaag drie accounts nodig. Laten we even checken." - -_[Scan de groep, vraag om handopsteken per account]_ - -"Allereerst: wie heeft een ChatGPT account? Wie nog niet? Maak die dan nu even aan op chat.openai.com - het is gratis." - -"Ten tweede: GitHub. Wie heeft dat? GitHub is waar developers hun code opslaan - we hebben het straks nodig om te deployen." - -"En tot slot: Vercel. Dit is het platform waar jullie website straks live komt te staan. Ga naar vercel.com en maak een account aan - het makkelijkst is om direct te koppelen met je GitHub account." - -_[Geef 2 minuten]_ - -"Is iedereen zover? Alle drie de accounts? Mooi, dan gaan we de diepte in." - ---- - -## BLOK 2: WAT IS AI? (15 minuten) - -### Slide 7: Wat is Artificial Intelligence? - -**[13:00 - 16:00]** _(3 minuten)_ - -"OkΓ©, eerste grote vraag: wat Γ­s eigenlijk AI?" - -_[Laat dit even hangen]_ - -_[INTERACTIE: Quick poll]_ - -"Maar eerst - noem eens voorbeelden van AI die je vandaag misschien al hebt gebruikt. Denk aan je telefoon, apps, websites..." - -_[Laat 3-4 studenten antwoorden]_ - -_[Verwachte antwoorden en hoe je erop reageert:]_ -- **Spotify/Netflix aanbevelingen:** "Ja! Die analyseert je luister/kijkgedrag en voorspelt wat je leuk vindt" -- **Google Maps routes:** "Precies - die voorspelt verkeer en berekent de snelste route" -- **Autocorrect/predictive text:** "Goed voorbeeld - dat is letterlijk next-word prediction, zoals ChatGPT" -- **Gezichtsherkenning (telefoon unlock):** "Ja, patroonherkenning in pixels" -- **Siri/Google Assistant:** "Taalverwerking - heel vergelijkbaar met ChatGPT" - -_[Als niemand antwoordt:]_ "Denk aan je telefoon vanochtend. Heb je getypt? Autocorrect. Route gepland? Google Maps. Muziek geluisterd? Spotify kiest nummers voor je. Dat is allemaal AI." - -"Al die voorbeelden zijn AI. Maar wat is het dan precies?" - -"AI, of Artificial Intelligence, is een verzamelnaam. Het zijn computersystemen die taken kunnen uitvoeren waarvoor normaal menselijke intelligentie nodig is." - -"Denk aan: patronen herkennen in data, beslissingen nemen op basis van informatie, taal begrijpen en genereren. Dingen die wij mensen doen met ons brein, maar dan door een computer." - -_[Maak onderscheid]_ - -"Nu is er een belangrijk onderscheid dat je moet kennen. Er is Narrow AI - ook wel Weak AI genoemd - dat is AI die heel goed is in één specifieke taak. ChatGPT is daar een voorbeeld van. Het kan heel goed tekst genereren, maar het kan niet autorijden of schaken - tenzij je het specifiek daarop traint." - -"En dan heb je General AI - of Strong AI - dat zou AI zijn die alles kan wat een mens kan. Die kan leren, redeneren, problemen oplossen in elke context. Dat bestaat nog niet, en niemand weet of het ooit zal bestaan." - -"Wat we vandaag gebruiken is Narrow AI. Heel krachtig, maar beperkt tot specifieke taken." - ---- - -### Slide 8: Hoe werken Large Language Models? - -**[16:00 - 20:00]** _(4 minuten)_ - -"Nu de vraag die iedereen heeft: hoe werkt ChatGPT eigenlijk?" - -_[INTERACTIE: Laat studenten raden]_ - -"Wat denken jullie? Als je een vraag typt in ChatGPT, wat gebeurt er dan 'under the hood'? Heeft iemand een idee?" - -_[Laat 2-3 studenten antwoorden - noteer antwoorden kort, bouw spanning op]_ - -_[Korte samenvatting antwoord:]_ "Het korte antwoord: het voorspelt steeds het volgende woord. Meer niet. Maar het doet dat zΓ³ goed dat het lijkt alsof het 'denkt'." - -"Interessante ideeΓ«n. De werkelijkheid is fascinerend, en als je dit snapt, snap je ook waarom AI soms briljant is en soms complete onzin produceert." - -"Een Large Language Model - een LLM - is getraind op gigantische hoeveelheden tekst. En met gigantisch bedoel ik: miljarden pagina's. Boeken, websites, Wikipedia, wetenschappelijke papers, code repositories. Een groot deel van wat ooit op het internet is gepubliceerd." - -"Het model heeft al deze tekst 'gelezen' en daarvan geleerd. Maar wat heeft het precies geleerd?" - -_[Laat dit even landen]_ - -"Het heeft geleerd: als ik deze woorden zie, wat is dan het meest waarschijnlijke volgende woord?" - -"Ja, echt. ChatGPT, Claude, Gemini - al die tools - in de kern zijn het voorspelmachines. Ze krijgen tekst als input en voorspellen token voor token wat er logischerwijs daarna komt." - -"En ze doen dat zo ongelooflijk goed, gebaseerd op al die miljarden teksten, dat het lijkt alsof ze 'begrijpen' wat je zegt. Maar er zit geen bewustzijn in, geen echte intentie. Het is geavanceerde statistische patroonherkenning op een schaal die we nog nooit eerder hebben gezien." - ---- - -### Slide 9: Tokens & Next-Token Prediction - -**[20:00 - 24:00]** _(4 minuten)_ - -"Laat me precies uitleggen hoe dit werkt. Het begint met tokens." - -_[Wijs naar de slide]_ - -"Stap 1: Als je tekst naar ChatGPT stuurt, wordt die tekst eerst omgezet naar nummers. Elk woord - of soms een deel van een woord - krijgt een nummer. Dit noemen we tokens." - -"Dus 'Ik drink koffie' wordt zoiets als [1847, 5621, 8934]. Het model werkt puur met getallen, niet met tekst." - -_[Korte pauze]_ - -"Stap 2: Nu komt het slimme. Woorden die qua betekenis op elkaar lijken, krijgen nummers die dicht bij elkaar liggen. Denk aan 'koffie' en 'thee' - die liggen dichtbij in deze 'getallenruimte'. Of 'staal' en 'metaal'. Maar 'koffie' en 'fiets' liggen ver uit elkaar." - -"Dit betekent dat het model snapt dat bepaalde woorden bij elkaar horen, puur door naar patronen in miljoenen teksten te kijken." - -_[Wijs naar het voorbeeld]_ - -"Stap 3: Nu de voorspelling. Als ik vraag: 'Ik drink koffie met...' - wat komt er waarschijnlijk?" - -_[Wacht op antwoorden]_ - -"Precies! 'Melk' of 'suiker'. Het model berekent voor elk mogelijk woord de kans dat het volgt. En dan kiest het - meestal het meest waarschijnlijke, maar niet altijd." - -_[INTERACTIE: Testen van begrip]_ - -"Vraag: als je ChatGPT 10 keer precies dezelfde vraag stelt, krijg je dan 10 keer hetzelfde antwoord?" - -_[Laat handopsteken - wie denkt ja, wie denkt nee]_ - -"Het antwoord is: nee! Er zit bewust randomness in - dit heet de 'temperatuur' parameter." - -"**Wat is temperatuur?** Stel, het model berekent: 'melk' heeft 60% kans, 'suiker' heeft 35% kans, 'room' heeft 5% kans. Bij lage temperatuur kiest het bijna altijd 'melk'. Bij hoge temperatuur gooit het vaker een dobbelsteen en kiest het soms 'suiker' of zelfs 'room'." - -"**Waarom bestaat dit?** Anders kreeg je steeds exact hetzelfde antwoord - dat voelt robotachtig. Die variatie maakt het menselijker en creatiever. ChatGPT gebruikt standaard een gemiddelde temperatuur - genoeg variatie om interessant te zijn, maar niet zo hoog dat het onzin wordt." - -"Dit verklaart waarom je soms briljante antwoorden krijgt en soms onzin. Het model gokt slim, maar het blijft gokken." - -_[INTERACTIE: Doordenker]_ - -"Maar wacht even - als LLMs alleen maar voorspellen wat het volgende woord is, hoe kunnen ze dan creatieve ideeΓ«n genereren? Hoe kan het iets 'nieuws' maken?" - -_[Laat 2-3 studenten antwoorden - verwacht: combinaties van bestaande kennis, patronen mixen, toeval/randomness]_ - -"Goede vraag en goed nagedacht! Het antwoord: het combineert patronen op nieuwe manieren." - -"**Hoe werkt dat?** Het model heeft miljarden teksten gelezen waarin concepten naast elkaar voorkomen. 'Koffie' staat vaak bij 'ochtend' en 'energie'. 'Strand' bij 'zon' en 'vakantie'. Als jij vraagt om iets nieuws - bijvoorbeeld 'een cafΓ© op Mars' - dan combineert het patronen van 'cafΓ©' met patronen van 'Mars' en 'ruimte'. Dat levert iets op dat nieuw lijkt." - -"**Waarom voelt het creatief?** Omdat de combinatie nieuw is voor jou, ook al zijn de losse onderdelen dat niet. Het is niet 'echt' creatief zoals een mens - het heeft geen intentie of inspiratie - maar het mixt bestaande kennis op manieren die wij als creatief ervaren." - -"En dit brengt me bij iets heel belangrijks..." - ---- - -### Slide 10: Hallucinaties - -**[23:00 - 28:00]** _(5 minuten)_ - -_[Iets serieuzere toon]_ - -"Hallucinaties. Dit is een term die je moet kennen en onthouden." - -"Wat bedoelen we hiermee? AI kan dingen verzinnen. Compleet uit de lucht gegrepen informatie presenteren alsof het waar is. En het klinkt heel overtuigend, want het is grammaticaal perfect en logisch geformuleerd." - -_[Geef concrete voorbeelden]_ - -"Laat me een paar voorbeelden geven. Vraag ChatGPT naar een wetenschappelijk paper over een specifiek onderwerp. Hij kan je een titel geven, auteursnamen, een publicatiejaar, een samenvatting - van iets dat niet bestaat. Het klinkt volledig legitiem, maar als je het opzoekt: niets te vinden." - -"Of vraag hem naar een JavaScript library voor een specifiek probleem. Hij kan een npm package naam noemen, installatie-instructies geven, code voorbeelden - voor een package dat nooit heeft bestaan." - -"Ik heb ooit ChatGPT gevraagd naar restaurants in een stad waar ik naartoe ging. Drie van de vijf suggesties bestonden niet. Verzonnen namen, verzonnen adressen." - -_[Leg uit waarom dit gebeurt]_ - -"Waarom gebeurt dit? Onthoud: het model voorspelt wat het MEEST WAARSCHIJNLIJK volgende woord is. Het controleert niet of iets waar is. Het genereert tekst die LIJKT op tekst die het eerder heeft gezien. Als je vraagt naar een paper, genereert het tekst die eruitziet als een paper-referentie - maar dat betekent niet dat die referentie bestaat." - -_[Praktische implicaties]_ - -"Wat betekent dit voor jullie als developers? Heel simpel: verifieer altijd. Kopieer nooit blindelings code van ChatGPT zonder te begrijpen wat het doet. Check of libraries bestaan. Test je code. Valideer feiten." - -_[INTERACTIE: Probleemoplossend denken]_ - -"Maar hoe herken je een hallucinatie? Wat zijn rode vlaggen?" - -_[Laat studenten antwoorden]_ - -"Goede ideeΓ«n! Hier zijn de belangrijkste rode vlaggen Γ©n waarom:" - -"**TΓ© specifieke details** - exacte cijfers, datums, namen. Waarom? Het model vult gaten in met wat 'waarschijnlijk' klinkt. Een paper uit '2019 door Dr. Smith et al.' klinkt geloofwaardig, maar is vaak verzonnen." - -"**Bronnen die je niet kunt vinden** - als je het Googelt en niets vindt, bestaat het waarschijnlijk niet. Het model genereert tekst die LIJKT op een bronvermelding." - -"**Onbekende npm packages** - dit is een klassieker. Ik heb zelf meegemaakt dat ChatGPT zo overtuigd was van een package dat hij bleef volhouden dat het bestond, terwijl de code steeds errors gaf. Hij genereerde zelfs 'documentatie' voor een package dat nooit heeft bestaan!" - -"**Verdacht zelfverzekerd over obscure onderwerpen** - het model weet niet wat het niet weet. Het klinkt even zelfverzekerd over iets dat het verzint als over iets dat waar is." - -"AI is een tool, geen orakel. Het is ongelooflijk nuttig, maar je moet het behandelen als een junior collega die heel enthousiast is maar soms dingen verzint. Vertrouw, maar verifieer." - -_[INTERACTIE: Ervaring delen]_ - -"Wie van jullie heeft dit zelf al eens meegemaakt? Dat ChatGPT iets verzon dat niet klopte?" - -_[Laat 2-3 studenten hun ervaring delen - dit maakt het concreet en persoonlijk]_ - ---- - -## BLOK 3: HET AI-LANDSCHAP (10 minuten) - -### Slide 11: De Grote Spelers - -**[28:00 - 31:00]** _(3 minuten)_ - -"OkΓ©, nu je begrijpt hoe LLMs werken, laten we kijken welke er zijn. Het landschap verandert snel, maar dit zijn de grote namen." - -_[Wijs naar de tabel]_ - -"ChatGPT van OpenAI - dat is de bekendste, de meesten van jullie hebben die al gebruikt. Het is de tool die AI mainstream maakte." - -"Claude van Anthropic - minder bekend, maar technisch heel sterk. Vooral goed in lange documenten en genuanceerde gesprekken. Persoonlijk gebruik ik Claude vaak voor complexere taken." - -"Gemini van Google - geΓ―ntegreerd in Google's ecosysteem. Handig als je al veel met Google Workspace werkt." - -"En Grok van xAI, het bedrijf van Elon Musk. GeΓ―ntegreerd in X, met toegang tot real-time informatie." - -"Vandaag focussen we op ChatGPT omdat die het meest toegankelijk is en de beste gratis tier heeft. Maar de concepten die je leert zijn toepasbaar op allemaal." - ---- - -### Slide 12: Gratis AI Opties - -**[31:00 - 35:00]** _(4 minuten)_ - -"Laten we het hebben over kosten. Want dat is een belangrijke vraag: moet je betalen voor AI?" - -_[INTERACTIE: Peiling]_ - -"Even peilen: wie betaalt er al voor een AI tool? ChatGPT Plus, Claude Pro, Copilot? Steek je hand op." - -_[Tel handen, vraag 1-2 mensen waarom ze betalen]_ - -"Interessant! En de rest - waarom niet? Te duur? Gratis is goed genoeg?" - -_[Korte reacties - verwacht: gratis werkt prima, te duur, weet niet wat je extra krijgt]_ - -_[Wijs naar de tabel]_ - -"Het korte antwoord: voor deze cursus hoef je niks te betalen! Er zijn zoveel gratis opties dat je voor deze hele cursus geen cent hoeft uit te geven." - -"ChatGPT Free draait op GPT-5 - dat is al een heel krachtig model. Je kunt afbeeldingen uploaden, documenten analyseren, code genereren." - -"Claude Free van Anthropic heeft Sonnet 3.5 - persoonlijk vind ik die uitstekend voor lange teksten en genuanceerde uitleg." - -"Gemini van Google is handig als je al in het Google ecosysteem zit." - -_[Nu met nadruk]_ - -"Maar er zijn ook twee opties die jullie misschien niet kennen. Groq - G-R-O-Q - biedt een gratis API met Llama 3.3, een open source model. Het is ongelooflijk snel - sneller dan alle anderen - en helemaal gratis." - -"En dan Ollama. Dit is anders. Met Ollama draai je AI modellen lokaal op je eigen laptop. Dat betekent: je data verlaat je computer nooit. Perfect voor als je werkt met gevoelige informatie." - -_[Privacy punt]_ - -"Dit is belangrijk voor later in de cursus: als je met klantdata werkt, of gevoelige bedrijfsinformatie, wil je misschien niet dat dat naar OpenAI of Anthropic gaat. Dan is Ollama je vriend." - -"Voor nu: ChatGPT Free is perfect voor wat we gaan doen." - ---- - -### Slide 13: Betaalde & Premium Modellen - -**[35:00 - 38:00]** _(3 minuten)_ - -"OkΓ©, maar wat als je tΓ³ch wilt upgraden? Wat krijg je dan?" - -_[Wijs naar de betaalde sectie]_ - -"Voor ongeveer $20 per maand krijg je bij ChatGPT Plus of Claude Pro meer berichten, snellere response, en toegang tot premium features. Voor de meeste mensen is dit voldoende als ze professioneel met AI werken." - -_[Nu over premium]_ - -"En dan is er de premium tier. Hier wil ik vooral Claude Opus noemen." - -_[Met nadruk]_ - -"Opus is het beste model dat er is voor complexe redenering. Als je een echt moeilijk probleem hebt - iets waar je al drie uur op zit te staren - dan gooi je het naar Opus en krijg je vaak een doorbraak." - -"Maar het is ook duur. Via de API kost het ongeveer $75 per maand als je het intensief gebruikt. Het is voor professionals die het echt nodig hebben." - -_[Samenvatten]_ - -"Mijn advies: start gratis. Upgrade naar Plus of Pro als je tegen limieten aanloopt. En Opus? Dat bewaar je voor die momenten dat je echt vastloopt." - -"Voor deze cursus: gratis is ruim voldoende." - ---- - -### Slide 14: Welke tool voor welke taak? - -**[38:00 - 42:00]** _(4 minuten)_ - -"Welke tool is het beste? Die vraag krijg ik vaak. En het eerlijke antwoord: het hangt af van wat je wilt doen." - -_[Loop door de tabel]_ - -"Voor snelle code snippets - een functie schrijven, een bug fixen - zijn ChatGPT en Claude beide uitstekend. Ik wissel zelf regelmatig." - -"Voor lange documenten analyseren of schrijven is Claude beter. Die heeft een groter 'geheugen' - technisch: een grotere context window. Je kunt een heel boek uploaden en vragen stellen." - -"Voor UI componenten - en dat is wat we straks gaan doen - gebruiken we v0.dev. Dat is een speciale tool van Vercel die geoptimaliseerd is voor het genereren van React componenten met Tailwind CSS." - -"Voor privacy-gevoelige zaken gebruik je Ollama - lokaal, geen data die je computer verlaat." - -"En als je supersnel wilt prototypen, is Groq je vriend. Die API is zo snel dat het bijna instant voelt." - -_[Praktische tip]_ - -"Mijn tip: experimenteer met meerdere tools. Elke tool heeft sterke en zwakke punten. De beste developers weten wanneer ze welke tool moeten gebruiken." - ---- - -## BLOK 4: CHATGPT IN DE PRAKTIJK (22 minuten) - -### Slide 15: ChatGPT Interface - -**[42:00 - 45:00]** _(3 minuten)_ - -_[Energieke toon]_ - -"OkΓ©, genoeg theorie. Open allemaal ChatGPT op je laptop. Ga naar chat.openai.com en log in." - -_[Wacht tot iedereen de pagina open heeft - 1 minuut]_ - -"Iedereen erin? Mooi. Laat me jullie rondleiden door de interface." - -_[Open ChatGPT op het projectiescherm]_ - -"In het midden heb je het chatvenster - daar typ je je vragen en zie je de antwoorden. Links zie je je chat geschiedenis - handig om terug te gaan naar eerdere gesprekken. Bovenaan kun je het model kiezen - daar komen we zo op. En rechtsonder in je profielfoto vind je instellingen." - -"De interface is bewust simpel gehouden. De kracht zit in wat je typt, niet in complexe menu's." - ---- - -### Slide 16: Model Kiezen - -**[45:00 - 47:00]** _(2 minuten)_ - -"Kijk even bovenaan je scherm. Daar staat welk model je gebruikt." - -_[Wijs op je eigen scherm]_ - -"Je hebt een paar opties. GPT-5 is de standaard - dat is snel en voor de meeste taken meer dan genoeg. Dit is wat je waarschijnlijk ziet." - -"GPT-5 with canvas is speciaal voor wanneer je documenten wilt bewerken - het opent een soort editor naast de chat." - -"En als je Plus hebt, zie je ook GPT-5.2 - het nieuwste en krachtigste model." - -"Voor vandaag is GPT-5 perfect. Laat het daarop staan." - ---- - -### Slide 17: Tijdelijke Chat - -**[47:00 - 49:00]** _(2 minuten)_ - -"Nu iets belangrijks over privacy: tijdelijke chat." - -_[Demonstreer waar dit staat]_ - -"Als je een nieuwe chat start, zie je een optie voor 'Temporary Chat'. Als je dit aanzet, wordt je gesprek niet opgeslagen in je geschiedenis en niet gebruikt om het model te trainen." - -"Wanneer gebruik je dit?" - -_[INTERACTIE: Toepassen]_ - -"In welke situaties zou je tijdelijke chat willen gebruiken? Noem eens voorbeelden." - -_[Laat 2-3 studenten antwoorden]_ - -"Goede voorbeelden! Laat me uitleggen waarom dit belangrijk is:" - -"**Klantgegevens/bedrijfsdata** - waarom? Alles wat je naar ChatGPT stuurt kan gebruikt worden om het model te trainen. Stel je voor dat jouw klantgegevens straks in iemand anders' antwoord opduiken. Dat is een datalek." - -"**Medische/persoonlijke info** - waarom? Privacy-wetgeving zoals AVG/GDPR. Je mag dit niet zomaar delen met derden - en OpenAI is een derde partij." - -"**Interne bedrijfscode** - waarom? Intellectueel eigendom. Je wilt niet dat je geheime algoritme in de trainingsdata van een AI-model belandt." - -"Tijdelijke chat voorkomt dit allemaal - je gesprek wordt niet opgeslagen en niet gebruikt voor training." - -"Voor deze cursus maakt het niet uit, maar onthoud dit voor je professionele werk." - ---- - -### Slide 18: Afbeeldingen Analyseren - -**[49:00 - 54:00]** _(5 minuten)_ - -_[Dit is een belangrijke slide voor de workflow]_ - -"Nu een feature die cruciaal is voor wat we straks gaan doen: ChatGPT kan afbeeldingen lezen en begrijpen." - -_[Demonstreer live]_ - -"Kijk, ik klik hier op het paperclip icoontje... en ik kan een afbeelding uploaden." - -_[Upload een simpele tekening of screenshot]_ - -"Ik upload hier een schets die ik eerder heb gemaakt... en ik vraag: 'Wat zie je op deze afbeelding? Beschrijf de layout.'" - -_[Wacht op response]_ - -"Kijk wat er gebeurt. ChatGPT beschrijft precies wat hij ziet. De positie van elementen, de structuur, zelfs details die ik misschien over het hoofd zou zien." - -"Dit heet image-to-text, of vision. Het model 'ziet' de afbeelding en kan erover praten." - -_[Met nadruk]_ - -"Dit is de sleutel tot onze workflow vandaag. Jullie gaan een schets tekenen op papier, een foto maken, en die uploaden naar ChatGPT. En ChatGPT gaat die schets 'vertalen' naar een gedetailleerde beschrijving die we kunnen gebruiken voor code generatie." - -"Het mooie is: je hoeft geen designer te zijn. Je hoeft geen perfecte tekening te maken. ChatGPT begrijpt ook ruwe schetsen." - ---- - -### Slide 19: Bestanden Uploaden - -**[54:00 - 56:00]** _(2 minuten)_ - -"Hetzelfde geldt voor documenten. Je kunt een PDF uploaden, een Word document, een Excel bestand, een code bestand." - -_[Demonstreer kort met een document]_ - -"Dit is handig voor: documentatie analyseren, code reviewen, data uit spreadsheets halen." - -"De limiet is ongeveer 50 pagina's per document, en je kunt meerdere bestanden tegelijk uploaden." - ---- - -### Slide 20: Custom Instructions - -**[56:00 - 59:00]** _(3 minuten)_ - -"Dan iets heel nuttigs dat weinig mensen kennen: Custom Instructions." - -_[Ga naar instellingen β†’ Personalisatie]_ - -"Hier kun je permanent instellen wie je bent en hoe ChatGPT moet antwoorden. Dit wordt automatisch meegenomen in Γ©lke chat." - -"Bijvoorbeeld: 'Ik ben een frontend developer. Ik werk met React en Tailwind CSS. Ik heb een junior niveau. Geef praktische code voorbeelden.'" - -"Vanaf nu weet ChatGPT in elke nieuwe chat al deze context. Je hoeft niet elke keer opnieuw te vertellen welke technologie je gebruikt." - -_[Praktische tip]_ - -"Mijn tip: stel dit in vΓ³Γ³r je begint met werken. Het scheelt enorm veel herhaling en je antwoorden worden relevanter." - ---- - -### Slide 21: ChatGPT Projects - -**[59:00 - 62:00]** _(3 minuten)_ - -"Nu iets heel handig: Projects. Dit is een relatief nieuwe feature die jullie werk veel makkelijker maakt." - -_[Demonstreer Projects in de zijbalk]_ - -"Zie je hier links 'New Project'? Projects zijn slimme werkruimtes. Je kunt alles wat bij een taak hoort bij elkaar houden: chats, bestanden, en specifieke instructies." - -"Waarom is dit handig? Stel je werkt aan meerdere projecten - een voor school, een voor werk, een voor een hobby. Met Projects hoef je niet steeds opnieuw uit te leggen waar je mee bezig bent." - -_[Klik op New Project]_ - -"Je geeft het project een naam, en je kunt instructies toevoegen die alleen voor dit project gelden. Plus: je kunt bestanden uploaden die ChatGPT kan gebruiken als context." - -"En hier is het mooie: Projects hebben hun eigen geheugen. Chats binnen een project onthouden wat je eerder hebt besproken. Dat is anders dan normale chats, die elke keer met een schone lei beginnen." - -_[Met nadruk]_ - -"Op de gratis tier kun je 5 bestanden per project uploaden. Dat is ruim voldoende voor wat we vandaag doen." - ---- - -### Slide 22: Praktijk Oefening - -**[62:00 - 67:00]** _(5 minuten - studenten werken)_ - -"OkΓ©, nu gaan jullie je ChatGPT instellen. Dit is belangrijk, want we gaan dit straks gebruiken voor de v0.dev demo." - -_[Wijs naar de instructies op de slide]_ - -"Twee dingen wil ik dat je doet:" - -"Een: ga naar Settings, dan Personalisatie, en vul je Custom Instructions in. Schrijf iets als: 'Ik ben een frontend developer. Ik werk met React en Tailwind CSS.' Dit zorgt dat ChatGPT altijd weet welke technologie je gebruikt." - -"Twee: maak je eerste Project. Klik op 'New Project' in de zijbalk. Noem het 'Les 1 - v0.dev Prompt Engineer'. En voeg als instructie toe: 'Help me gedetailleerde prompts schrijven voor v0.dev. Focus op React en Tailwind CSS componenten.'" - -_[Geef 5 minuten - loop rond en help waar nodig]_ - -"Dit project gaan we straks gebruiken! Als we schetsen uploaden, doet ChatGPT precies wat we nodig hebben voor v0.dev." - -_[Check of iedereen klaar is]_ - -"Heeft iedereen een project aangemaakt? Mooi, dan gaan we verder met prompt engineering." - ---- - -## BLOK 5: PROMPT ENGINEERING (12 minuten) - -### Slide 23: Wat is Prompt Engineering? - -**[64:00 - 66:00]** _(2 minuten)_ - -"Nu komen we bij een cruciaal onderdeel: prompt engineering." - -"Een prompt is simpelweg de instructie die je aan AI geeft. De vraag die je stelt, de opdracht die je geeft." - -"En hier is het belangrijke inzicht dat alles verandert: de kwaliteit van je antwoord hangt direct af van de kwaliteit van je vraag." - -"Garbage in, garbage out. Als je een vage vraag stelt, krijg je een vaag antwoord. Als je specifiek bent, krijg je specifieke, bruikbare output." - -"Dit is een vaardigheid - en net als elke vaardigheid kun je het leren." - ---- - -### Slide 24: Slechte vs Goede Prompt - -**[66:00 - 69:00]** _(3 minuten)_ - -"Laat me het verschil laten zien." - -_[Wijs naar de slechte prompt]_ - -_[INTERACTIE: Analyseren]_ - -"Hier staat een prompt: 'Maak een button'. Wat is hier mis mee? Wat ontbreekt er?" - -_[Laat studenten antwoorden - verwacht: geen kleur, geen taal, geen stijl, geen grootte]_ - -"Precies! Dit is wat de meeste mensen doen. Drie woorden." - -"**Waarom is dit slecht?** De AI weet niet:" -- "Welke programmeertaal? HTML, React, Vue, Angular - allemaal anders" -- "Welke stijl? Rond, vierkant, met schaduw?" -- "Welke kleur? Blauw, rood, gradient?" -- "Hoe groot? Klein voor mobiel, groot voor desktop?" - -"De AI moet alles gokken. En gokken leidt tot generieke output die je toch moet aanpassen. Je verliest tijd." - -_[Wijs naar de goede prompt]_ - -"En dit is beter: 'Maak een React button component met Tailwind CSS. Primary styling met een blauwe achtergrond (#2563EB). Include hover state en een loading state met een spinner.'" - -"Zie je het verschil? Je specificeert de technologie, de styling, de features. De AI weet precies wat je wilt." - -_[Demonstreer live als tijd het toelaat]_ - -"Het resultaat? Bruikbare code die je direct in je project kunt zetten." - ---- - -### Slide 25: Prompt Framework - -**[69:00 - 71:00]** _(2 minuten)_ - -"Er is een framework dat helpt. Vier elementen die je kunt gebruiken." - -_[Wijs naar elk element]_ - -"EΓ©n: Rol. Vertel de AI wie hij is. 'Je bent een senior frontend developer met 10 jaar ervaring.'" - -"Twee: Context. Geef achtergrondinformatie. 'Je werkt aan een SaaS landingspagina voor een projectmanagement tool.'" - -"Drie: Taak. Wat moet er gebeuren. 'Maak een hero section met een headline, subtitel, en call-to-action button.'" - -"Vier: Format. Hoe moet de output eruit zien. 'Gebruik React en Tailwind CSS. Geef alleen de code, geen uitleg.'" - -_[INTERACTIE: Discussie]_ - -"Welk van deze vier elementen vinden jullie het belangrijkst? Rol, context, taak, of format?" - -_[Laat handopsteken per optie, vraag 1-2 mensen om hun keuze te verdedigen]_ - -"Interessante keuzes! Laat me uitleggen hoe elk element bijdraagt:" - -"**Taak** is het enige verplichte element - zonder taak weet de AI niet wat hij moet doen. Maar alleen een taak geeft vaak generieke output." - -"**Context** maakt vaak het grootste verschil. 'Maak een button' vs 'Maak een button voor een medische app waar oudere gebruikers op moeten klikken' - heel andere output." - -"**Rol** helpt de AI de juiste 'stem' te vinden. Een senior developer geeft andere code dan een beginner. Een copywriter schrijft anders dan een technisch schrijver." - -"**Format** voorkomt dat je output moet herschrijven. 'Geef alleen code' scheelt je tijd." - -"De combinatie is krachtig. Je hoeft niet altijd alle vier te gebruiken, maar hoe meer context, hoe beter het resultaat." - ---- - -### Slide 26: Tech Stack Specificeren - -**[71:00 - 73:00]** _(2 minuten)_ - -"Nu iets dat jullie moeten begrijpen voor deze cursus: tech stack specificeren." - -"Wat is een tech stack? De technologieΓ«n waarmee je bouwt. Voor deze cursus gebruiken we React met Next.js en Tailwind CSS." - -"Waarom is dit belangrijk om te specificeren? Als je tegen AI zegt 'maak een button', weet hij niet of je HTML wilt, React, Vue, Angular, Svelte... Er zijn tientallen opties." - -"Door expliciet te zeggen 'React met Tailwind CSS' krijg je code die je direct kunt gebruiken." - -_[Met nadruk]_ - -"En goed nieuws: v0.dev, de tool die we straks gebruiken, genereert automatisch React met Tailwind. Maar in ChatGPT moet je het wel specificeren!" - ---- - -### Slide 27: Kleuren en Look & Feel - -**[73:00 - 75:00]** _(2 minuten)_ - -"Het volgende dat je moet specificeren: kleuren en stijl." - -_[Loop door de opties]_ - -"Kleuren kun je meegeven op twee manieren. Exact met hex codes: '#2563EB voor primary blue'. Of beschrijvend: 'warm oranje thema met aarde-tinten'." - -"Stijl beschrijf je met woorden: modern, minimalistisch, speels, corporate, brutalist, retro..." - -"En je kunt referenties gebruiken. 'In de stijl van Stripe.com' of 'vergelijkbaar met de Apple website'. Dat vertelt de AI heel veel in weinig woorden." - -"Een complete specificatie: 'Blauw kleurenschema met primary #2563EB. Modern en minimalistisch. Clean met veel whitespace. Geschikt voor een B2B SaaS startup.'" - ---- - -### Slide 28: Prompting Tips - -**[75:00 - 76:00]** _(1 minuut)_ - -"Even de tips op een rij voor we verder gaan:" - -"Geef een rol. Wees specifiek. Specificeer je tech stack. Beschrijf de look & feel. Geef voorbeelden als je kunt. En itereer - je eerste prompt is zelden perfect." - -"Onthoud: garbage in, garbage out. Investeer in je prompt." - ---- - -## BLOK 6: LIVE DEMO V0.DEV (25 minuten) - -### Slide 29: Introductie v0.dev - -**[76:00 - 79:00]** _(3 minuten)_ - -_[Energieke toon]_ - -"Nu wordt het echt leuk. We gaan kijken naar v0.dev." - -"Dit is een tool van Vercel - het bedrijf achter Next.js, een populair React framework. En het is een gamechanger voor UI development." - -_[Open v0.dev op je scherm]_ - -"Wat doet v0? Je beschrijft wat je wilt in tekst, of je uploadt een afbeelding, en v0 genereert werkende React code met Tailwind CSS. Je ziet direct een preview. En je kunt het direct deployen naar het internet." - -"Maar de slimme workflow die ik jullie vandaag leer combineert ChatGPT en v0. En daar zit de magie." - ---- - -### Slide 30: De Slimme Workflow - -**[79:00 - 82:00]** _(3 minuten)_ - -_[Wijs naar het diagram]_ - -"Dit is de workflow. Vijf stappen. Simpel maar krachtig." - -"Stap één: je TEKENT je idee op papier. Gewoon pen en papier, zoals vroeger." - -"Stap twee: je maakt een FOTO van je tekening met je telefoon." - -"Stap drie: je uploadt die foto naar CHATGPT. En je beschrijft je wensen - welke technologie, welke kleuren, welke stijl." - -"Stap vier: ChatGPT maakt een gedetailleerde Engelse PROMPT voor je. Een perfecte beschrijving van wat je wilt." - -"Stap vijf: je plakt die prompt in V0.DEV en je krijgt een werkende website!" - -_[Leg uit waarom]_ - -"Waarom doen we het zo? Twee redenen. EΓ©n: v0 heeft beperkte gratis credits - ongeveer zeven generaties per dag. ChatGPT is onbeperkt. Door eerst in ChatGPT je prompt te optimaliseren, verspil je geen v0 credits aan slechte prompts." - -"Twee: ChatGPT is beter in het 'vertalen' van je ruwe idee naar een gestructureerde prompt. V0 is beter in het genereren van code. Je gebruikt elk tool waar het goed in is." - ---- - -### Slide 31: Live Demo - -**[82:00 - 97:00]** _(15 minuten)_ - -_[Pak pen en papier - dit is live!]_ - -"OkΓ©, ik ga dit nu live doen. Kijk mee." - -**Stap 1: Tekenen** - -_[Teken terwijl je praat]_ - -"Ik teken een simpele hero section. Links tekstvak voor headline, daaronder button. Rechts een vierkant met X - placeholder voor afbeelding." - -_[Toon je tekening]_ - -"Het hoeft niet mooi te zijn. Blokken en lijnen zijn genoeg. Het gaat om de layout." - -**Stap 2: Foto naar ChatGPT** - -_[Maak foto en upload naar ChatGPT Project]_ - -"Foto maken... uploaden naar mijn ChatGPT Project... en nu mijn beschrijving:" - -"Dit is een schets voor een hero section. Beschrijf als v0.dev prompt. Tech stack: React + Tailwind. Kleuren: blauw thema #2563EB. Stijl: modern, minimalistisch." - -_[Wacht op response]_ - -"Kijk - ChatGPT maakt een gedetailleerde Engelse prompt. Precies wat v0 nodig heeft." - -**Stap 3: v0.dev** - -_[Kopieer prompt naar v0.dev]_ - -"Prompt kopiΓ«ren... plakken in v0... genereren..." - -_[Resultaat verschijnt]_ - -"En daar is het! Hero section met de kleuren en stijl die ik specificeerde. De code is clean React met Tailwind." - -**Stap 4: Itereren** - -_[Typ een follow-up]_ - -"Nu itereren: 'Make the button larger with hover animation.'" - -_[Wacht op generatie]_ - -"Kijk - aangepast. DΓ‘t is de kracht: in gesprek gaan met de AI." - -**Belangrijk:** Deployment laat ik NIET zien - dat gaan jullie straks zelf uitzoeken! - ---- - -### Slide 32: Hoe deploy je naar Vercel? - -**[97:00 - 99:00]** _(2 minuten)_ - -"Laatste vraag: hoe krijg je dit online? Dat ga ik jullie nu uitleggen, maar niet voordoen." - -_[Wijs naar de interface]_ - -"In v0 zie je een optie om te deployen. Je klikt op 'Add Integration', koppelt je GitHub account, en v0 maakt automatisch een repository aan. Dan deployt Vercel je code - binnen een minuut heb je een live URL." - -_[Met nadruk]_ - -"Maar dit ga ik niet live doen. Waarom niet? Omdat jullie dit straks zelf gaan uitzoeken. Dat is onderdeel van de opdracht. Het is niet moeilijk, en je leert meer door het zelf te doen." - -"Van een tekening op papier naar een live website. Dat is de kracht van deze workflow. En straks gaan jullie dat bewijzen." - ---- - -## BLOK 7: HANDS-ON OPDRACHT (53 minuten) - -### Slide 33: Aan de slag! - -**[99:00 - 150:00]** _(51 minuten - 5 min uitleg + 46 min werken)_ - -_[Pak papier en pennen - deel uit als nodig]_ - -"Nu zijn jullie aan de beurt. Jullie gaan een complete landingspagina bouwen - groter dan mijn demo." - -_[Wijs naar de vereisten]_ - -"Vereisten: minimaal drie secties, één carousel, één accordion. Je bent vrij in wat je tekent - bedenk een fictief product." - -"Stappen: teken, foto, ChatGPT Project, v0.dev, deploy naar Vercel. Die laatste stap zoek je zelf uit." - -"Je hebt 45 minuten. Begin met tekenen! Succes!" - -_[Studenten werken - loop actief rond]_ - -**Let op deze problemen:** -- Tekenstap overslaan? β†’ Terug naar papier -- Te weinig secties? β†’ Herinner aan 3+ vereiste -- Carousel/accordion vergeten? β†’ Check schets -- v0 rate limit? β†’ Wachten of delen met klasgenoot -- Deployment? β†’ Eerst Googlen, dan pas helpen - -**Tussentijdse check [125:00]:** - -"Snelle check: wie heeft 3+ secties? Carousel en accordion in de schets? Nog 25 minuten - begin met deployen als je v0 resultaat hebt." - -_[Studenten werken door tot 150:00]_ - ---- - -## BLOK 8: AFSLUITING (15 minuten) - -### Slide 34: Resultaten Delen - -**[150:00 - 158:00]** _(8 minuten)_ - -"OkΓ©, tijd om af te ronden. Wie wil zijn resultaat laten zien?" - -_[Laat 2-3 studenten hun scherm delen en kort presenteren]_ - -"Wat was je oorspronkelijke idee? Laat je schets zien. En wat heeft v0 ervan gemaakt?" - -_[Geef korte positieve feedback per student]_ - ---- - -### Slide 35: Samenvatting - -**[158:00 - 162:00]** _(4 minuten)_ - -"Laten we even terugkijken naar wat jullie vandaag hebben geleerd." - -"We begonnen met de theorie: hoe LLMs werken, next-token prediction, en waarom hallucinaties bestaan." - -"We hebben ChatGPT praktisch verkend: de interface, afbeeldingen uploaden, Custom Instructions." - -"We hebben prompt engineering gehad: het belang van specificiteit, tech stack, kleuren en stijl meegeven." - -_[Met nadruk]_ - -"En het belangrijkste: jullie hebben de workflow geleerd en toegepast. Schets op papier, foto naar ChatGPT, prompt naar v0, en live op het internet." - -"Jullie zijn developers die AI tools kunnen gebruiken. Dat is wat deze eerste les jullie heeft gegeven." - ---- - -### Slide 36: Huiswerk - -**[162:00 - 167:00]** _(5 minuten)_ - -"Voor volgende week heb je twee opdrachten. Inleveren via Teams." - -_[Wijs naar de slide]_ - -"EΓ©n: een reflectie van 500 woorden. Ik geef je drie deelvragen om je op weg te helpen." - -"Wat vond je verrassend aan werken met AI? Wat ging goed en wat vond je lastig? En hoe zie je AI jouw werk als developer veranderen?" - -"Dit hoeft geen essay te zijn - gewoon eerlijk nadenken over je ervaring vandaag." - -_[Wijs naar opdracht 2]_ - -"Twee: breid je v0 website uit. Voeg minimaal één nieuwe sectie toe. Dat kan pricing zijn, features, een contact formulier, een footer - kies zelf." - -"Gebruik dezelfde workflow: teken eerst, dan ChatGPT, dan v0. Lever screenshots in Γ©n je Vercel URL." - -"Deadline: voor de volgende les." - -"Vragen? Nee? Dan bedankt voor jullie inzet. Jullie hebben een website gebouwd die begon als een tekening. Tot volgende week!" - -_[Einde les]_ - ---- - -## APPENDIX: Timing Overzicht - -| Blok | Onderwerp | Start | Eind | Duur | -| ---- | ---------------------- | ----- | ---- | ------ | -| 1 | Welkom & Introductie | 0:00 | 0:13 | 13 min | -| 2 | Wat is AI? | 0:13 | 0:28 | 15 min | -| 3 | AI Landscape | 0:28 | 0:38 | 10 min | -| 4 | ChatGPT in de Praktijk | 0:38 | 1:00 | 22 min | -| 5 | Prompt Engineering | 1:00 | 1:12 | 12 min | -| 6 | Live Demo v0.dev | 1:12 | 1:37 | 25 min | -| 7 | Hands-on Opdracht | 1:37 | 2:30 | 53 min | -| 8 | Afsluiting | 2:30 | 3:00 | 30 min | - -**Spreektijd docent (Blok 1-6):** ~55 minuten -**Hands-on + Afsluiting:** ~83 minuten -**Totaal:** 180 minuten (3 uur) - ---- - -## Tips voor de docent - -1. **Papier en pennen** - Neem extra mee. Dit is cruciaal voor de workflow. - -2. **Benadruk dat tekenen NIET mooi hoeft** - Studenten zijn vaak onzeker. Blokken en lijnen zijn genoeg. - -3. **De ChatGPT prompt is de sleutel** - Loop rond en check of studenten volledige specificaties meegeven. - -4. **Heb backup klaar** - Screenshots van je demo voor als v0 traag is. - -5. **Tempo aanpassen** - Als de groep snel is, kun je meer tijd voor hands-on geven. Als ze trager zijn, kort de demo in. - -6. **Actief rondlopen** - Tijdens de opdracht, help proactief. - ---- - -## Checklist voor prompts - -Laat studenten dit checken in hun ChatGPT prompt: - -| Element | Voorbeeld | -| ---------- | -------------------------- | -| Tech stack | "React met Tailwind CSS" | -| Kleuren | "#2563EB" of "warm oranje" | -| Stijl | "Modern, minimalistisch" | -| Doel | "SaaS landingspagina" | diff --git a/v2/Les01-Introductie-AI/Les01-Lesplan.md b/v2/Les01-Introductie-AI/Les01-Lesplan.md deleted file mode 100644 index 23d684f..0000000 --- a/v2/Les01-Introductie-AI/Les01-Lesplan.md +++ /dev/null @@ -1,272 +0,0 @@ -# Lesplan Les 1: Introductie AI & Large Language Models - -> **Titel cursus:** AI Fundamentals -> **Leeruitkomst/prestatie-indicatoren** waaraan gewerkt wordt: - -- De student schrijft effectieve prompts voor tekst-, beeld- en codegeneratie met tools zoals ChatGPT, DALL-E en Midjourney. -- De student past AI op ethische wijze toe en houdt rekening met de valkuilen, waaronder auteursrecht, bias en transparantie. -- De student reflecteert kritisch op het gebruik van AI binnen het creatieve of ontwikkelproces. - ---- - -| **Les 1: Introductie AI & Large Language Models (3 uur)** | | -|---|---| -| **Benodigdheden** | Laptop met internetverbinding, projectiescherm, **papier en pen voor elke student**, toegang tot v0.dev, ChatGPT account, GitHub account, Vercel account | -| **Bijzonderheden** | Dit lesplan is voor een **fysieke** les. Focus op "wow-factor" en enthousiasmeren. Kernworkflow: schets op papier β†’ ChatGPT β†’ v0.dev β†’ website. | - ---- - -## Lesverloop - -| **Tijd (minuten)** | **Onderdeel** | **Werkvorm + toelichting + clue** | -|---|---|---| -| | | | -| **0-13** | **Welkom & Introductie** | **Welkom en kennismaking** | -| | | *Werkvorm: klassikaal gesprek (5 min)* | -| | | Stel jezelf voor als docent. Vraag kort wie de studenten zijn en wat hun achtergrond is met AI. Peiling: "Wie heeft ChatGPT al eens gebruikt?" | -| | | | -| | | **Lesindeling en planning** | -| | | Communiceer duidelijk de structuur van de les: | -| | | - Theorie over AI en LLMs (~45 min) | -| | | - Praktijk: ChatGPT leren gebruiken (~25 min) | -| | | - Prompt Engineering basics (~15 min) | -| | | - Live demo: van schets naar website (~20 min) | -| | | - Hands-on opdracht: bouw een complete landingspagina (~55 min) | -| | | | -| | | **Leerdoelen van vandaag** | -| | | 1. Begrijpen wat AI en LLMs zijn | -| | | 2. ChatGPT praktisch leren gebruiken | -| | | 3. Prompt engineering: tech stack, kleuren, stijl specificeren | -| | | 4. **Van schets op papier naar werkende website** | -| | | | -| | | **Accounts check** | -| | | Check of iedereen een ChatGPT, GitHub en Vercel account heeft. Laat studenten die dit nog moeten doen dit nu aanmaken. Tip: Vercel koppelen met GitHub is het makkelijkst. | -| | | | -| **13-33** | **Wat is AI?** | *Inventariseren voorkennis (5 min)* | -| | | *Werkvorm: brainstorm* | -| | | | -| | | Stel de vraag: "Wat is AI volgens jullie?" Laat studenten vrijuit antwoorden. Noteer kernwoorden op het bord/scherm. | -| | | | -| | | *Instructievorm: interactief doceren (15 min)* | -| | | | -| | | **Definitie AI** | -| | | Leg uit dat AI een verzamelnaam is voor systemen die taken uitvoeren waarvoor normaal menselijke intelligentie nodig is. Maak onderscheid tussen: | -| | | - Narrow AI (specifieke taken) | -| | | - General AI (theoretisch, nog niet bereikt) | -| | | | -| | | **Large Language Models (LLMs)** | -| | | Kernuitleg: | -| | | - Getraind op enorme hoeveelheden tekst | -| | | - Voorspellen het volgende woord (next-token prediction) | -| | | - Geen echte "kennis" of "begrip", wel patroonherkenning | -| | | | -| | | *Demonstratie: Next-token prediction (5 min)* | -| | | Open ChatGPT en typ langzaam een zin. Bijvoorbeeld: "De kat zat op de..." | -| | | | -| | | **Hallucinaties** | -| | | Leg uit wat hallucinaties zijn en waarom ze voorkomen. Benadruk het belang van verificatie. | -| | | | -| | | **Clue: LLMs zijn geen databases met feiten, maar statistische modellen die patronen herkennen.** | -| | | | -| **33-45** | **AI Landscape** | *Instructievorm: vergelijkende demonstratie (12 min)* | -| | | | -| | | **De grote spelers** | -| | | Loop door de belangrijkste AI-tools: ChatGPT, Claude, Gemini, Grok. | -| | | | -| | | **Gratis vs Betaald** | -| | | Bespreek kort de verschillen. Conclusie: gratis is voldoende voor deze cursus. | -| | | | -| | | **Welke tool voor welke taak?** | -| | | Korte klassikale discussie. | -| | | | -| | | **Clue: Er is geen "beste" AI-tool - de juiste keuze hangt af van je specifieke use case.** | -| | | | -| **45-70** | **ChatGPT in de Praktijk** | *Werkvorm: interactieve demonstratie met meekijken (25 min)* | -| | | *Doel:* Studenten leren ChatGPT effectief gebruiken | -| | | | -| | | **Interface rondleiding (5 min)** | -| | | Open ChatGPT op het projectiescherm. Laat studenten meekijken en hun eigen ChatGPT openen. | -| | | | -| | | **Model kiezen (3 min)** | -| | | Demonstreer de model selector: GPT-5, GPT-5 with canvas, GPT-5.2. | -| | | | -| | | **Tijdelijke Chat (3 min)** | -| | | Demonstreer waar dit staat en wanneer je het gebruikt. | -| | | | -| | | **Afbeeldingen analyseren (5 min)** | -| | | Live demonstratie: upload een schets en laat ChatGPT beschrijven wat hij ziet. **Dit is de basis voor de workflow straks!** | -| | | | -| | | **Bestanden uploaden (3 min)** | -| | | Demonstreer het uploaden van een PDF of document. | -| | | | -| | | **Custom Instructions (3 min)** | -| | | Laat zien waar dit staat en geef voorbeelden. | -| | | | -| | | **ChatGPT Projects (3 min)** | -| | | Demonstreer Projects: werkruimtes met eigen instructies, bestanden en geheugen. | -| | | | -| | | **Praktijk: Instellen ChatGPT (5 min)** | -| | | Studenten stellen Custom Instructions in + maken Project "Les 1 - v0.dev Prompt Engineer". | -| | | | -| | | **Dit project gebruiken we straks voor de v0.dev demo!** | -| | | | -| **70-85** | **Prompt Engineering Basics** | *Instructievorm: demonstratie met oefening (15 min)* | -| | | | -| | | **Wat is een prompt?** | -| | | Een prompt is de instructie die je aan een AI geeft. | -| | | | -| | | **Slechte vs Goede Prompt** | -| | | Toon het verschil tussen vage en specifieke prompts. | -| | | | -| | | **Prompt Framework** | -| | | Introduceer: Rol, Context, Taak, Format. | -| | | | -| | | **Tech Stack Specificeren** | -| | | Leg uit wat een tech stack is en waarom je dit moet meegeven: | -| | | - React - JavaScript framework voor UI | -| | | - Tailwind CSS - Utility-first CSS framework | -| | | - "v0.dev genereert automatisch React + Tailwind" | -| | | | -| | | **Kleuren en Look & Feel** | -| | | Leg uit hoe je visuele specificaties meegeeft: | -| | | - Kleuren: hex codes (#2563EB) of beschrijvend ("warm oranje") | -| | | - Stijl: modern, minimalistisch, speels, corporate, etc. | -| | | - Sfeer en referenties: "In de stijl van Stripe.com" | -| | | | -| | | **Clue: Specificeer altijd je tech stack, kleuren en stijl voor consistente resultaten.** | -| | | | -| **85-105** | **Live Demo: v0.dev** | *Instructievorm: live demonstratie (20 min)* | -| | | *Doel:* "Wow-moment" creΓ«ren met de schets-naar-website workflow | -| | | | -| | | **Introductie v0.dev + Workflow (5 min)** | -| | | Leg uit wat v0.dev is en de 5-stappen workflow: | -| | | 1. TEKEN op papier | -| | | 2. FOTO maken | -| | | 3. Upload naar CHATGPT + beschrijf wensen | -| | | 4. ChatGPT maakt PROMPT | -| | | 5. Plak in V0.DEV β†’ Website! | -| | | | -| | | **Live Demo (15 min)** | -| | | Voer de hele workflow uit in één vloeiende demo: | -| | | - Teken simpele hero section op papier | -| | | - Foto maken en uploaden naar ChatGPT Project | -| | | - Prompt genereren met tech stack, kleuren, stijl | -| | | - KopiΓ«ren naar v0.dev en genereren | -| | | - Itereren met follow-up prompts | -| | | | -| | | **BELANGRIJK: Deployment NIET live doen!** | -| | | Leg kort uit hoe het werkt (Add Integration β†’ GitHub β†’ Deploy), maar studenten zoeken dit straks zelf uit. | -| | | | -| | | **Clue: De schets is je startpunt, ChatGPT is je vertaler, v0.dev is je bouwer.** | -| | | | -| **105-160** | **Hands-on Opdracht** | *Werkvorm: individueel werken (55 min)* | -| | | *Doel:* Zelfstandig de workflow doorlopen | -| | | | -| | | **Opdracht introductie (5 min)** | -| | | Deel de opdracht uit (zie Bijlage: Lesopdracht). Studenten bouwen een **complete landingspagina**: | -| | | - Minimaal **3 secties** (hero, features, pricing, etc.) | -| | | - Minimaal **1 carousel** (image/testimonial slider) | -| | | - Minimaal **1 accordion** (FAQ of uitklapbare content) | -| | | - **Deployment:** Zelf uitzoeken! | -| | | | -| | | **Benodigdheden check** | -| | | Zorg dat iedereen papier en pen heeft! | -| | | | -| | | **Zelfstandig werken (45 min)** | -| | | Loop rond en help studenten waar nodig. Let op: | -| | | - Hebben ze 3+ secties getekend? | -| | | - Carousel en accordion in de schets? | -| | | - Volledigheid van ChatGPT prompt (tech stack, kleuren, stijl) | -| | | - Deployment: laat ze eerst zelf zoeken, help pas daarna | -| | | | -| | | **Tussentijdse check (halverwege)** | -| | | "Wie heeft 3+ secties? Carousel en accordion gepland?" | -| | | "Begin met deployen als je v0 resultaat hebt!" | -| | | | -| | | **Clue: Het doel is de workflow leren, niet perfectie!** | -| | | | -| **160-180** | **Afsluiting** | *Werkvorm: klassikaal gesprek (20 min)* | -| | | | -| | | **Resultaten delen (8 min)** | -| | | Vraag 2-3 studenten om te delen: | -| | | - Laat je schets en v0 resultaat zien | -| | | - Wat ging goed? Wat was lastig? | -| | | | -| | | **Samenvatting (4 min)** | -| | | - LLMs en next-token prediction | -| | | - Prompt engineering: tech stack, kleuren, stijl | -| | | - **De workflow: Schets β†’ ChatGPT β†’ v0.dev β†’ Website** | -| | | | -| | | **Huiswerk (5 min)** | -| | | 1. **Reflectie (500 woorden)** met deelvragen: | -| | | - Wat vond je verrassend aan werken met AI? | -| | | - Wat ging goed? Wat vond je lastig? | -| | | - Hoe zie je AI jouw werk als developer veranderen? | -| | | 2. **Breid je v0 website uit** met minimaal 1 nieuwe sectie | -| | | - Inleveren via Teams, voor volgende les | -| | | | -| | | **Afsluiting** | -| | | Bedank de studenten. Tot volgende week! | - ---- - -## Timing Overzicht - -| Blok | Onderwerp | Start | Eind | Duur | -|------|-----------|-------|------|------| -| 1 | Welkom & Introductie | 0:00 | 0:15 | 15 min | -| 2 | Wat is AI? | 0:15 | 0:40 | 25 min | -| 3 | AI Landscape | 0:40 | 0:50 | 10 min | -| 4 | ChatGPT in de Praktijk | 0:50 | 1:10 | 20 min | -| 5 | Prompt Engineering | 1:10 | 1:25 | 15 min | -| 6 | Live Demo v0.dev | 1:25 | 1:45 | 20 min | -| 7 | Hands-on Opdracht | 1:45 | 2:40 | 55 min | -| 8 | Afsluiting | 2:40 | 3:00 | 20 min | - -**Totaal: 180 minuten (3 uur)** - ---- - -## Notities voor de docent - -### Voorbereiding -- **BELANGRIJK:** Zorg voor papier en pennen voor alle studenten -- Test v0.dev vooraf - soms zijn er rate limits -- Zorg voor backup-schermafbeeldingen mocht live demo falen -- Maak zelf een schets klaar voor de demo -- Zorg dat je eigen accounts werken (ChatGPT, Claude, v0.dev, GitHub) - -### De Schets-Workflow - Key Points -- Benadruk dat tekenen NIET mooi hoeft te zijn -- Simpele blokken en lijnen zijn voldoende -- Het gaat om communiceren van layout en structuur -- ChatGPT is de "vertaler" - die maakt de technische prompt - -### Prompt Specificaties - Wat moeten studenten meegeven? -1. **Tech stack:** React met Tailwind CSS (v0 default) -2. **Kleuren:** Hex codes of beschrijving -3. **Stijl:** Modern/minimalistisch/speels/corporate etc. -4. **Responsive:** Mobile-first design -5. **Doel:** Type website (SaaS, portfolio, webshop) - -### Mogelijke vragen/discussiepunten -- "Kan ik ook mijn eigen schets direct uploaden naar v0?" β†’ Ja, maar ChatGPT maakt de prompt gedetailleerder -- "Wat als mijn tekening lelijk is?" β†’ Maakt niet uit, het gaat om de layout -- "Welke kleuren moet ik kiezen?" β†’ Kies wat je wilt, of gebruik Tailwind default colors - -### Differentiatie -- **Snelle studenten:** Laat ze een tweede component maken of experimenteren met andere stijlen -- **Studenten die moeite hebben:** Help met de ChatGPT prompt, geef ze het template - -### Assessment indicatoren -Observeer tijdens de les: -- Maken studenten daadwerkelijk een schets? -- Geven ze volledige specificaties mee aan ChatGPT? -- Kunnen ze de workflow zelfstandig doorlopen? -- Begrijpen ze waarom je tech stack/kleuren/stijl specificeert? - ---- - -## Bijlagen -1. Bijlage A: Lesopdracht - Van Schets naar Website met AI -2. Bijlage B: Huiswerkopdracht - Reflectie en Website Uitbreiding diff --git a/v2/Les01-Introductie-AI/Les01-Slide-Overzicht.md b/v2/Les01-Introductie-AI/Les01-Slide-Overzicht.md deleted file mode 100644 index 200cdfb..0000000 --- a/v2/Les01-Introductie-AI/Les01-Slide-Overzicht.md +++ /dev/null @@ -1,831 +0,0 @@ -# Les 1: Introductie tot AI en Large Language Models - -## Slide Overzicht (3 uur les) - V2 - -**V2 Verbeteringen:** -- Account setup met checkpoints (slides 6-8) -- Deployment demo samen met class (slide 34) -- Troubleshooting notities toegevoegd - -**Spreektijd docent:** ~55 minuten -**Account setup (samen):** ~10 minuten -**Demo v0.dev + deployment:** ~20 minuten -**Hands-on opdracht:** ~50 minuten -**Afsluiting + buffer:** ~25 minuten - ---- - -## BLOK 1: WELKOM & INTRODUCTIE (13 minuten) - -### Slide 1: Titelslide -*(30 seconden)* - -**Titel:** Introductie tot AI en Large Language Models -**Subtitel:** AI Development - Les 1 -**Docent:** Tim Rijkse -**Datum:** [Datum invullen] - ---- - -### Slide 2: Planning -*(1,5 minuut)* - -**Titel:** Wat gaan we vandaag doen? - -**Inhoud:** - -| Tijd | Onderwerp | -|------|-----------| -| 09:00 - 09:15 | Welkom & voorstelrondje | -| 09:15 - 09:40 | Wat is AI? LLMs, hallucinaties, tools | -| 09:40 - 10:00 | ChatGPT in de praktijk | -| 10:00 - 10:15 | Prompt Engineering | -| 10:15 - 10:35 | Live demo: Van schets naar website | -| 10:35 - 11:30 | Jullie aan de slag! | -| 11:30 - 12:00 | Resultaten delen & huiswerk | - -**Einddoel:** Van een tekening op papier naar een live website! - ---- - -### Slide 3: Voorstellen docent -*(1,5 minuut)* - -**Titel:** Even voorstellen - -**Inhoud:** -- Naam: Tim Rijkse -- Developer + AI docent bij NOVI -- Dagelijks bezig met AI-assisted development -- Bereikbaar via Teams - ---- - -### Slide 4: Kennismaking -*(5 minuten - studenten aan het woord)* - -**Titel:** Wie zijn jullie? - -**Inhoud:** -- Naam -- Achtergrond (werk/studie) -- Heb je al eens ChatGPT of andere AI-tools gebruikt? -- Zo ja, waarvoor? - ---- - -### Slide 5: De Leerlijn AI Developer -*(2 minuten)* - -**Titel:** 18 weken, 4 delen - -**Inhoud:** - -| Deel | Lessen | Focus | -|------|--------|-------| -| 1: AI Foundations | 1-4 | AI basics, tools, ethics, prompting | -| 2: Technical Foundations | 5-9 | TypeScript, Next.js, databases, Supabase | -| 3: AI Tooling & Prototyping | 10-12 | Tool selectie, prototyping, Cursor | -| 4: Advanced AI Features | 13-18 | Agents, MCP, AI SDK, deployment | - -**Eindresultaat:** Complete full-stack applicatie met AI - ---- - -### Slide 6: Accounts Check - ChatGPT -*(3 minuten)* - -**Titel:** Stap 1: ChatGPT Account - -**Inhoud:** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 1: CHATGPT ACCOUNT β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Ga naar chat.openai.com β”‚ -β”‚ 2. Klik "Sign up" β”‚ -β”‚ 3. Kies: Email of Google/Apple login β”‚ -β”‚ 4. Bevestig je email β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je ziet het ChatGPT chatvenster β”‚ -β”‚ Steek je hand op als je dit hebt! β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**⚠️ WACHT** - We gaan pas door als iedereen klaar is! - ---- - -### Slide 7: Accounts Check - GitHub -*(3 minuten)* - -**Titel:** Stap 2: GitHub Account - -**Inhoud:** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 2: GITHUB ACCOUNT β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Ga naar github.com β”‚ -β”‚ 2. Klik "Sign up" β”‚ -β”‚ 3. Kies username (dit wordt je URL!) β”‚ -β”‚ 4. Bevestig je email β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je ziet je GitHub dashboard β”‚ -β”‚ Steek je hand op als je dit hebt! β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**⚠️ WACHT** - We gaan pas door als iedereen klaar is! - ---- - -### Slide 8: Accounts Check - Vercel -*(4 minuten)* - -**Titel:** Stap 3: Vercel Account (koppel met GitHub!) - -**Inhoud:** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 3: VERCEL ACCOUNT β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Ga naar vercel.com β”‚ -β”‚ 2. Klik "Sign Up" β”‚ -β”‚ 3. BELANGRIJK: Kies "Continue with GitHub" β”‚ -β”‚ (niet email!) β”‚ -β”‚ 4. Geef Vercel toegang tot je GitHub β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je ziet Vercel dashboard β”‚ -β”‚ EN je GitHub username staat rechtsboven β”‚ -β”‚ Steek je hand op als je dit hebt! β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**⚠️ Veel voorkomende fout:** Eerst deployen, dan pas GitHub koppelen. Doe het NU! - ---- - -## BLOK 2: WAT IS AI? (15 minuten) - -### Slide 9: Wat is Artificial Intelligence? -*(3 minuten)* - -**Titel:** AI - Meer dan een buzzword - -**Inhoud:** - -**Definitie:** Computersystemen die taken uitvoeren die normaal menselijke intelligentie vereisen - -**Typen AI:** -- **Narrow AI** (wat we nu hebben): specifieke taken -- **General AI** (toekomst): breed menselijk denken - -**Voorbeelden:** -- Aanbevelingen (Netflix, Spotify) -- Spraakassistenten (Siri, Alexa) -- Code generatie (ChatGPT, Copilot) - ---- - -### Slide 10: Hoe werken Large Language Models? -*(4 minuten)* - -**Titel:** De magie achter ChatGPT - -**Inhoud:** - -1. **Training:** Model leest miljarden teksten -2. **Patroonherkenning:** Leert statistische verbanden -3. **Voorspelling:** Voorspelt het volgende woord -4. **Output:** Genereert tekst token voor token - -**Belangrijk inzicht:** -- LLMs "begrijpen" niet echt - ze voorspellen -- Geen bewustzijn, geen intentie -- Geavanceerde patroonherkenning op enorme schaal - ---- - -### Slide 11: Tokens & Next-Token Prediction -*(4 minuten)* - -**Titel:** Hoe "denkt" een LLM? - -**Inhoud:** - -**Stap 1: Tekst β†’ Tokens (nummers)** -``` -"Ik drink koffie" β†’ [1847, 5621, 8934] -``` -Elk woord (of woorddeel) krijgt een nummer. - -**Stap 2: Betekenis in getallen** -Woorden met vergelijkbare betekenis liggen dicht bij elkaar: -- "koffie" en "thee" β†’ dichtbij -- "staal" en "metaal" β†’ dichtbij -- "koffie" en "fiets" β†’ ver uit elkaar - -**Stap 3: Voorspel het volgende token** -``` -"Ik drink koffie met ___" -``` -Het model berekent kansen: -- "melk" β†’ 45% -- "suiker" β†’ 30% -- "een" β†’ 15% - -β†’ Kiest waarschijnlijk "melk" (maar niet altijd!) - -**Dit verklaart:** -- Waarom het soms briljant is (patronen herkend) -- Waarom het soms onzin produceert (verkeerde voorspelling) -- Waarom dezelfde vraag verschillende antwoorden geeft (kans, niet zekerheid) - ---- - -### Slide 12: Hallucinaties -*(5 minuten)* - -**Titel:** Waarom AI soms "liegt" - -**Inhoud:** - -**Wat zijn hallucinaties?** -- AI verzint informatie die niet bestaat -- Klinkt heel overtuigend -- Grammaticaal perfect, maar feitelijk onjuist - -**Voorbeelden:** -- Fake wetenschappelijke papers met echte-klinkende titels -- Niet-bestaande npm packages met installatie-instructies -- Verzonnen restaurants met adressen - -**Waarom gebeurt dit?** -Het model voorspelt wat LIJKT op correcte output, niet wat WAAR is. - -**Regel:** Verifieer altijd! Vertrouw, maar check. - ---- - -## BLOK 3: HET AI-LANDSCHAP (10 minuten) - -### Slide 13: De Grote Spelers -*(3 minuten)* - -**Titel:** Welke AI tools zijn er? - -**Inhoud:** - -| Tool | Bedrijf | Sterk in | -|------|---------|----------| -| **ChatGPT** | OpenAI | Algemeen, code, creatief | -| **Claude** | Anthropic | Lange teksten, nuance | -| **Gemini** | Google | Google integratie | -| **Grok** | xAI | Actueel nieuws (X/Twitter) | - -**Vandaag:** Focus op ChatGPT - meest toegankelijk. - ---- - -### Slide 14: Gratis AI Opties -*(4 minuten)* - -**Titel:** Gratis modellen - meer dan genoeg! - -**Inhoud:** - -| Provider | Model | Sterk in | Privacy | -|----------|-------|----------|---------| -| **ChatGPT Free** | GPT-5 | Algemeen, code, images | Cloud | -| **Claude Free** | Sonnet 3.5 | Lange teksten, nuance | Cloud | -| **Gemini Free** | Gemini Pro | Google integratie | Cloud | -| **Groq** | Llama 3.3 | Supersnel, gratis API | Cloud | -| **Ollama** | Diverse (lokaal) | Privacy, offline | **Lokaal** | - -**Conclusie:** Voor deze cursus is gratis ruim voldoende! - -**Privacy tip:** Ollama draait lokaal β†’ data verlaat je computer niet. - ---- - -### Slide 15: Betaalde & Premium Modellen -*(3 minuten)* - -**Titel:** Wanneer upgraden? - -**Inhoud:** - -**Betaald (~$20/maand):** - -| Provider | Voordelen | -|----------|-----------| -| ChatGPT Plus | Meer berichten, sneller, GPT-5.2, DALL-E | -| Claude Pro | Meer berichten, prioriteit, langere context | - -**Premium Tier:** - -| Model | Kosten | Wanneer? | -|-------|--------|----------| -| **Claude Opus** | ~$75/maand API | Beste redenering, complexe taken | -| GPT-5.2 Turbo | Via Plus | Lange context, nieuwste features | - -**Advies:** Start gratis. Upgrade alleen als je tegen limieten aanloopt. - ---- - -### Slide 16: Welke tool voor welke taak? -*(4 minuten)* - -**Titel:** Snelle vergelijking - -**Inhoud:** - -| Taak | Beste keuze | -|------|-------------| -| Snelle code snippets | ChatGPT of Claude | -| Lange documenten | Claude (200K context) | -| Actueel nieuws | Grok of Perplexity | -| UI componenten | v0.dev (straks!) | -| Privacy-gevoelig | Ollama (lokaal) | -| Supersnel prototypen | Groq API | - -**Tip:** Professionele developers gebruiken meerdere tools! - ---- - -## BLOK 4: CHATGPT IN DE PRAKTIJK (22 minuten) - -### Slide 17: ChatGPT Interface -*(3 minuten)* - -**Titel:** Laten we ChatGPT openen - -**Inhoud:** - -*[Live demonstratie - open chat.openai.com]* - -**Wat je ziet:** -- Chatvenster (midden) -- Chat geschiedenis (links) -- Model kiezen (bovenaan) -- Instellingen (rechtsonder) - -**Actie:** Open nu ChatGPT op je laptop! - ---- - -### Slide 18: Model Kiezen -*(2 minuten)* - -**Titel:** Welk model gebruik je? - -**Inhoud:** - -**In ChatGPT kun je kiezen:** -- **GPT-5** - Standaard, snel, gratis -- **GPT-5 with canvas** - Voor documenten bewerken -- **GPT-5.2** - Nieuwste en krachtigste (betaald) - -**Tip:** GPT-5 is voor de meeste taken prima! - ---- - -### Slide 19: Tijdelijke Chat -*(2 minuten)* - -**Titel:** Privacy: Tijdelijke Chat - -**Inhoud:** - -**Wat is tijdelijke chat?** -- Chat wordt NIET opgeslagen -- Wordt NIET gebruikt om model te trainen - -**Wanneer gebruiken?** -- Gevoelige informatie -- Werk-gerelateerde data -- Client gegevens - ---- - -### Slide 20: Afbeeldingen Analyseren -*(5 minuten)* - -**Titel:** Image-to-Text - Cruciaal voor onze workflow! - -**Inhoud:** - -**Wat kun je doen?** -- Foto uploaden en vragen stellen -- Screenshot laten analyseren -- **Schets laten interpreteren** ← Dit gebruiken we straks! - -**Hoe werkt het?** -- Klik op πŸ“Ž (paperclip) -- Upload afbeelding -- Stel je vraag - -*[Live demonstratie met een schets]* - ---- - -### Slide 21: Bestanden Uploaden -*(2 minuten)* - -**Titel:** PDF's en documenten - -**Inhoud:** - -**ChatGPT kan:** -- PDF's lezen en samenvatten -- Word documenten analyseren -- Code bestanden reviewen -- Spreadsheets interpreteren - -**Limiet:** ~50 pagina's per document - ---- - -### Slide 22: Custom Instructions -*(3 minuten)* - -**Titel:** ChatGPT Personaliseren - -**Inhoud:** - -**Wat zijn Custom Instructions?** -- Permanente instructies die ChatGPT onthoudt -- Zit in Instellingen β†’ Personalisatie - -**Handige voorbeelden:** -- "Ik ben een frontend developer, gebruik React en Tailwind" -- "Antwoord in het Nederlands tenzij ik Engels vraag" -- "Geef code voorbeelden met TypeScript" - ---- - -### Slide 23: ChatGPT Projects -*(3 minuten)* - -**Titel:** Alles op één plek: Projects - -**Inhoud:** - -**Wat zijn Projects?** -- Slimme werkruimtes voor langlopende taken -- Eigen instructies per project -- Bestanden uploaden (PDF, Word, afbeeldingen) -- Project-specifiek geheugen - -**Waarom handig?** -- Context blijft behouden tussen chats -- Geen herhaling van instructies nodig -- Perfect voor: cursussen, klanten, projecten - -**Gratis tier:** 5 bestanden per project -**Betaald:** 25-40 bestanden per project - ---- - -### Slide 24: Praktijk Oefening -*(5 minuten - studenten werken)* - -**Titel:** Stel je ChatGPT in! - -**Inhoud:** - -**Opdracht (5 minuten):** - -1. **Custom Instructions instellen:** - - Ga naar Instellingen β†’ Personalisatie - - Vul in: "Ik ben een frontend developer. Ik werk met React en Tailwind CSS." - -2. **Maak je eerste Project:** - - Klik op "New Project" (linkerzijbalk) - - Naam: "Les 1 - v0.dev Prompt Engineer" - - Instructie: "Help me gedetailleerde prompts schrijven voor v0.dev. Focus op React + Tailwind CSS componenten." - -**Dit project gebruiken we straks voor de v0.dev demo!** - ---- - -## BLOK 5: PROMPT ENGINEERING (12 minuten) - -### Slide 25: Wat is Prompt Engineering? -*(2 minuten)* - -**Titel:** De kunst van het vragen stellen - -**Inhoud:** - -**Een prompt is:** De instructie die je aan AI geeft - -**Waarom belangrijk?** -- Goede prompt = bruikbaar resultaat -- Slechte prompt = frustratie - -**Garbage in = garbage out!** - ---- - -### Slide 26: Slechte vs Goede Prompt -*(3 minuten)* - -**Titel:** Het verschil zien - -**Slechte prompt:** -> "Maak een button" - -**Goede prompt:** -> "Maak een React button component met Tailwind CSS. Primary styling met blauwe achtergrond (#2563EB). Include hover state en loading state met spinner." - -**Waarom beter?** -- Specificeert technologie -- Beschrijft features -- Geeft visuele details - ---- - -### Slide 27: Prompt Framework -*(2 minuten)* - -**Titel:** 4 elementen van een goede prompt - -**Inhoud:** - -1. **Rol:** "Je bent een senior frontend developer..." -2. **Context:** "...die werkt aan een SaaS landingspagina..." -3. **Taak:** "...maak een hero section met..." -4. **Format:** "...gebruik React en Tailwind" - ---- - -### Slide 28: Tech Stack Specificeren -*(2 minuten)* - -**Titel:** Vertel de AI welke technologie - -**Inhoud:** - -**Voor deze cursus:** -- **React** - JavaScript framework voor UI -- **Tailwind CSS** - Utility-first CSS -- **Next.js** - React framework - -**In je prompt:** -> "Gebruik React met Tailwind CSS" - -**Tip:** v0.dev genereert automatisch React + Tailwind! - ---- - -### Slide 29: Kleuren en Look & Feel -*(2 minuten)* - -**Titel:** Visuele specificaties - -**Inhoud:** - -**Wat specificeren?** -- **Kleuren:** "#2563EB" of "blauw thema" -- **Stijl:** "Modern, minimalistisch" -- **Referenties:** "In de stijl van Stripe.com" - -**Voorbeeld:** -> "Blauw kleurenschema met primary #2563EB. Modern en minimalistisch. B2B SaaS stijl." - ---- - -### Slide 30: Prompting Tips -*(1 minuut)* - -**Titel:** Samenvatting - -**Inhoud:** - -1. Geef een rol -2. Wees specifiek -3. Specificeer tech stack -4. Beschrijf look & feel -5. Itereer - eerste poging is zelden perfect - ---- - -## BLOK 6: LIVE DEMO V0.DEV (25 minuten) - -### Slide 31: Introductie v0.dev -*(3 minuten)* - -**Titel:** Van schets naar website - -**Inhoud:** - -**v0.dev door Vercel:** -- AI-powered UI generator -- Begrijpt tekst EN afbeeldingen -- Genereert React + Tailwind code -- Direct deploybaar - -**De workflow:** -Tekening β†’ Foto β†’ ChatGPT β†’ Prompt β†’ v0.dev β†’ Website! - ---- - -### Slide 32: De Slimme Workflow -*(3 minuten)* - -**Titel:** 5 stappen - -**Inhoud:** - -``` -1. TEKEN je idee op papier - ↓ -2. Maak een FOTO - ↓ -3. Upload naar CHATGPT + beschrijf wensen - (tech stack, kleuren, stijl) - ↓ -4. ChatGPT maakt gedetailleerde PROMPT - ↓ -5. Plak in V0.DEV β†’ Website! -``` - -**Waarom ChatGPT eerst?** -- v0 heeft beperkte credits (~7/dag) -- ChatGPT optimaliseert je prompt - ---- - -### Slide 33: Live Demo - v0.dev -*(10 minuten)* - -**Titel:** Van schets naar website - live! - -**Inhoud:** - -*[Docent demonstreert de workflow live]* - -**De stappen:** -- ✏️ **Tekenen:** Simpele hero section op papier (blokken en lijnen) -- πŸ“Έ **Foto:** Upload naar ChatGPT Project -- πŸ’¬ **Prompt:** "Beschrijf als v0.dev prompt, React + Tailwind, blauw thema" -- ⚑ **v0.dev:** Prompt plakken β†’ genereren (~30 sec) -- πŸ”„ **Itereren:** "Make button larger", "Add gradient" - ---- - -### Slide 34: Live Demo - Deployment (SAMEN!) -*(8 minuten)* - -**Titel:** Samen deployen naar Vercel - -**Inhoud:** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 1: GITHUB KOPPELEN IN V0 β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Klik rechtsboven op "..." menu β”‚ -β”‚ 2. Klik "Add to codebase" β”‚ -β”‚ 3. Kies "Connect to GitHub" β”‚ -β”‚ 4. Autoriseer v0 voor je GitHub β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je ziet "Create repository" knop β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 2: REPOSITORY AANMAKEN β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Kies een naam (bijv. "mijn-landingspagina") β”‚ -β”‚ 2. Klik "Create repository" β”‚ -β”‚ 3. Wacht tot code gepusht is (~10 sec) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je ziet "Deploy" knop β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 3: DEPLOYEN NAAR VERCEL β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Klik "Deploy" β”‚ -β”‚ 2. Vercel opent β†’ Klik "Import" β”‚ -β”‚ 3. Wacht op build (~1 minuut) β”‚ -β”‚ 4. Kopieer je URL: jouw-project.vercel.app β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… CHECKPOINT: Je website is live! β”‚ -β”‚ Open de URL in een nieuw tabblad β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**⚠️ TROUBLESHOOTING:** -- "GitHub niet gekoppeld" β†’ Ga naar Vercel settings, koppel GitHub -- "Repository exists" β†’ Kies andere naam -- "Build failed" β†’ Vraag docent om hulp - ---- - -## BLOK 7: HANDS-ON OPDRACHT (50 minuten) - -### Slide 35: Aan de slag! -*(50 minuten - 5 min uitleg + 45 min werken)* - -**Titel:** Bouw een complete landingspagina! - -**Inhoud:** - -**Vereisten:** -- Minimaal **3 secties** (hero, features, pricing, testimonials, footer...) -- Minimaal **1 carousel** + **1 accordion** -- Vrije keuze in onderwerp/product - -**Stappen:** Teken β†’ Foto β†’ ChatGPT Project β†’ v0.dev β†’ Deploy (zelf!) - -**Checklist:** -- [ ] 3+ secties getekend -- [ ] Carousel & accordion gepland -- [ ] Tech stack + kleuren gespecificeerd -- [ ] Gedeployed naar Vercel - -**Tip:** Begin simpel, itereer! **Hulp nodig?** Steek je hand op! - ---- - -## BLOK 8: AFSLUITING (30 minuten) - -### Slide 36: Resultaten Delen -*(8 minuten)* - -**Titel:** Wie wil laten zien? - -**Inhoud:** - -- Laat je schets zien -- Toon het v0 resultaat -- Wat ging goed? Wat was lastig? - ---- - -### Slide 37: Samenvatting -*(4 minuten)* - -**Titel:** Wat hebben we geleerd? - -**Inhoud:** - -βœ… Hoe LLMs werken (next-token prediction) -βœ… AI-landschap: ChatGPT, Claude, Gemini -βœ… ChatGPT praktisch gebruiken -βœ… Prompt engineering: tech stack, kleuren, stijl -βœ… **De workflow: Schets β†’ ChatGPT β†’ v0.dev β†’ Website!** - ---- - -### Slide 38: Huiswerk -*(5 minuten)* - -**Titel:** Voor volgende week - -**Inhoud:** - -**1. Reflectie (500 woorden)** -- Wat vond je verrassend aan werken met AI? -- Wat ging goed? Wat vond je lastig? -- Hoe zie je AI jouw werk als developer veranderen? - -**2. Breid je v0 website uit** -- Voeg minimaal 1 nieuwe sectie toe (pricing, features, contact, footer...) -- Gebruik weer de workflow: teken β†’ ChatGPT β†’ v0 -- Lever screenshots + Vercel URL in - -**Inleveren:** Via Teams, voor volgende les - -**Vragen?** Tot volgende week! πŸš€ - ---- - -## Timing Samenvatting (V2) - -| Blok | Onderwerp | Duur | Slides | -|------|-----------|------|--------| -| 1 | Welkom & Introductie + **Account Setup** | 18 min | 1-8 | -| 2 | Wat is AI? | 15 min | 9-12 | -| 3 | AI Landscape | 10 min | 13-16 | -| 4 | ChatGPT Praktijk | 22 min | 17-24 | -| 5 | Prompt Engineering | 12 min | 25-30 | -| 6 | Demo v0.dev + **Deployment (samen!)** | 20 min | 31-34 | -| 7 | Hands-on Opdracht | 50 min | 35 | -| 8 | Afsluiting | 30 min | 36-38 | - -**V2 Veranderingen:** -- Account setup met checkpoints (+5 min in Blok 1) -- Deployment demo samen met class (niet "zelf uitzoeken") - -**Spreektijd (Blok 1-5):** ~60 minuten -**Demo (Blok 6):** ~20 minuten -**Hands-on + Afsluiting:** ~80 minuten -**Totaal:** 180 minuten (3 uur) -**Totaal slides:** 38 diff --git a/v2/Les02-OpenCode/Les02-Bijlage-A-Lesopdracht.md b/v2/Les02-OpenCode/Les02-Bijlage-A-Lesopdracht.md deleted file mode 100644 index c233e43..0000000 --- a/v2/Les02-OpenCode/Les02-Bijlage-A-Lesopdracht.md +++ /dev/null @@ -1,213 +0,0 @@ -# Les 2 - Lesopdracht: Website uitbreiden met OpenCode - ---- - -## Doel - -Maak een nieuwe pagina in je v0 project met de OpenCode Desktop App. Je leert: -- Werken met de OpenCode Desktop App -- Plan mode en Build mode gebruiken -- Wijzigingen deployen via git push - ---- - -## Tijd - -**85 minuten** (09:50-10:15 + 10:30-11:30) - -β˜• Pauze: 10:15-10:30 - ---- - -## Stap 1: Project Lokaal Draaien (15 min) - -### Clone je repository - -**Optie A - Terminal:** -```bash -git clone https://github.com/jouw-username/jouw-project.git -cd jouw-project -``` - -**Optie B - GitHub Desktop:** -1. File β†’ Clone Repository -2. Selecteer je v0 project -3. Clone - -### Installeer dependencies -```bash -npm install -``` - -### Start development server -```bash -npm run dev -``` - -Open http://localhost:3000 - je ziet je website! - -**Checkpoint:** βœ… Website draait lokaal - ---- - -## Stap 2: OpenCode Desktop App (10 min) - -### Open de Desktop App -1. Start OpenCode Desktop App -2. Open je projectfolder (File β†’ Open Folder) -3. Selecteer je v0 project - -### Selecteer gratis AI model -1. Druk `Ctrl+X M` (eerst Ctrl+X, dan M) -2. Kies "OpenCode Zen" -3. Selecteer "Kimi K2.5 Free" - -### Start je project (in de ingebouwde terminal) -```bash -npm install -npm run dev -``` - -**Checkpoint:** βœ… Desktop App draait + localhost:3000 werkt - ---- - -## Stap 3: Uitbreiden met OpenCode (45 min) - -### Fase 1: Analyseer (Plan Mode) - -Check dat je in Plan mode zit (πŸ“‹ icoon). - -``` -Analyseer dit project. Hoe werkt de routing? -Hoe maak ik een nieuwe pagina in Next.js? -``` - -### Fase 2: Maak een nieuwe pagina (Build Mode) - -Druk `Tab` om naar Build mode (πŸ”¨) te gaan. - -**πŸ’‘ BELANGRIJK: EΓ©n prompt per taak!** - -Splits je werk op in kleine stappen. Niet alles in één prompt! - -**Opdracht: Maak een nieuwe pagina met componenten** - -#### Stap 1: Kies je pagina -Kies één van deze opties: -- **About** - Over ons pagina -- **Services** - Diensten pagina -- **Contact** - Contact pagina - -#### Stap 2: Maak de pagina structuur -**Prompt 1:** -``` -Maak een nieuwe pagina /about (of /services of /contact) met een basis layout. -``` - -#### Stap 3: Voeg componenten toe -**Prompt 2:** -``` -Voeg een hero sectie toe aan de /about pagina met titel en beschrijving. -Gebruik Tailwind CSS voor styling. -``` - -**Prompt 3:** -``` -Voeg een content sectie toe onder de hero (bijv. team cards, diensten lijst, of contact info). -``` - -#### Stap 4: Voeg navigatie toe -**Prompt 4:** -``` -@app/page.tsx -Voeg een link toe naar de /about pagina. -De link moet zichtbaar zijn in de hero of header. -Gebruik Next.js Link component. -``` - -**Checkpoint:** βœ… Nieuwe pagina gemaakt + link op homepage - ---- - -## Stap 4: Deploy naar Vercel (10 min) - -### Commit je wijzigingen - -**Terminal:** -```bash -git add . -git commit -m "Add testimonials section" -git push -``` - -**Of GitHub Desktop:** -1. Zie changed files links -2. Schrijf commit message -3. Commit to main -4. Push origin - -### Check Vercel -1. Open je Vercel dashboard -2. Zie "Building..." status -3. Na ~1 min: bekijk je live site! - -**Checkpoint:** βœ… Wijzigingen live op Vercel - ---- - -## Stap 5: Herhaal! (10 min) - -Doe minimaal nog één wijziging en push opnieuw. - -Het doel is dat je de volledige workflow meerdere keren doorloopt: -1. Wijzig met OpenCode -2. Test lokaal -3. Commit & push -4. Bekijk live - ---- - -## Tips - -### @ Mentions gebruiken -``` -@src/components/Hero.tsx -Maak de heading groter en gebruik een gradient -``` - -### Plan mode voor vragen -``` -πŸ“‹ Hoe kan ik animaties toevoegen aan mijn cards? -``` - -### Build mode voor actie -``` -πŸ”¨ Voeg fade-in animatie toe aan de feature cards -``` - -### Stuck? -1. Wees specifieker in je prompt -2. Verwijs naar het exacte bestand met @ -3. Bekijk de error in terminal -4. Vraag een klasgenoot of de docent - ---- - -## Inleveren - -Aan het einde van de les: -- [ ] Website draait lokaal -- [ ] Nieuwe pagina aangemaakt (bijv. /about) -- [ ] Link op homepage naar nieuwe pagina -- [ ] Minimaal 2x gepusht naar Vercel -- [ ] Wijzigingen zijn live - ---- - -## Bonus (als je klaar bent) - -- [ ] Voeg een tweede pagina toe -- [ ] Maak een navbar component met links naar alle pagina's -- [ ] Voeg dark mode toggle toe -- [ ] Implementeer smooth scroll animaties diff --git a/v2/Les02-OpenCode/Les02-Bijlage-B-Huiswerkopdracht.md b/v2/Les02-OpenCode/Les02-Bijlage-B-Huiswerkopdracht.md deleted file mode 100644 index 99e9c9a..0000000 --- a/v2/Les02-OpenCode/Les02-Bijlage-B-Huiswerkopdracht.md +++ /dev/null @@ -1,163 +0,0 @@ -# Les 2 - Huiswerkopdracht - -**Deadline:** Voor les 3 - ---- - -## Opdracht 1: Nieuw Project vanaf Scratch (2 uur) - -In de les heb je een bestaand project uitgebreid. Nu ga je een **nieuw project** maken met OpenCode. - -### Stap 1: Maak een nieuw React project - -```bash -npx create-next-app@latest mijn-nieuwe-project -cd mijn-nieuwe-project -npm run dev -``` - -Kies bij de prompts: -- TypeScript: Yes -- Tailwind CSS: Yes -- App Router: Yes - -### Stap 2: Configureer OpenCode Desktop App - -1. Open de Desktop App -2. Open je projectfolder -3. Selecteer OpenCode Zen β†’ Kimi K2.5 Free (gratis) - -### Stap 3: Bouw minimaal 3 componenten - -Gebruik Plan mode om te ontwerpen, Build mode om te bouwen. - -**⚠️ BELANGRIJK: EΓ©n prompt per taak!** - -Maak niet één grote prompt voor alles. Verdeel je werk: -- Prompt 1: "Maak een Header component met logo en navigatie" -- Prompt 2: "Voeg een mobile hamburger menu toe aan de Header" -- Prompt 3: "Maak een Hero section met headline en buttons" -- etc. - -**Houd je prompts bij!** Je moet ze inleveren (zie Opdracht 2). - -**Verplichte componenten:** - -#### 1. Header/Navbar -- Logo -- Navigatie links -- CTA button -- Mobile hamburger menu (werkend!) - -#### 2. Hero Section -- Headline -- Subtitle -- 1-2 buttons -- Achtergrond (gradient of afbeelding) - -#### 3. Kies één: - -**Optie A: Feature Cards** -- Minimaal 3 cards -- Icon, titel, beschrijving per card -- Responsive grid - -**Optie B: Testimonials** -- Minimaal 2 testimonials -- Quote, naam, foto placeholder -- Mooie styling - -**Optie C: Pricing Table** -- 3 pricing tiers -- Features per tier -- Highlight recommended - -### Stap 4: Push naar GitHub - -**Anders dan bij v0!** Bij v0 was Vercel automatisch gekoppeld. Nu moet je dit handmatig doen. - -#### 4a. Maak GitHub repository - -1. Ga naar github.com -2. Klik "+" β†’ "New repository" -3. Naam: bijvoorbeeld `mijn-nieuwe-project` -4. **Public** aanvinken -5. Klik "Create repository" - -#### 4b. Push je code - -GitHub toont instructies. Voer uit in je projectmap: - -```bash -git remote add origin https://github.com/JOUW-USERNAME/mijn-nieuwe-project.git -git branch -M main -git push -u origin main -``` - -**Of met GitHub Desktop:** -1. File β†’ Add Local Repository -2. Selecteer je projectmap -3. Publish repository - ---- - -### Stap 5: Koppel aan Vercel - -1. Ga naar vercel.com (log in met GitHub) -2. Klik "Add New..." β†’ "Project" -3. "Import Git Repository" -4. Selecteer je nieuwe repo -5. Framework: Next.js (automatisch gedetecteerd) -6. Klik "Deploy" - -**Na ~1 minuut:** Je website is live! - -**Tip:** Elke volgende `git push` wordt automatisch gedeployed. - ---- - -## Opdracht 2: Houd je Prompts Bij! - -**Belangrijk:** Houd tijdens het bouwen een lijst bij van alle prompts die je gebruikt. - -**Voorbeeld format:** -``` -1. [Plan] "Analyseer dit project en beschrijf de folder structuur" -2. [Build] "Maak een Header component met logo links en navigatie rechts" -3. [Build] "Voeg een hamburger menu toe voor mobile" -4. [Build] "Maak een Hero section met grote headline en gradient achtergrond" -5. [Build] "Voeg twee buttons toe aan de Hero: primair en secundair" -6. [Build] "Maak een Features sectie met 3 cards in een grid" -... -``` - -**Waarom?** -- In Les 3 gaan we iedereen langs -- We bespreken hoeveel prompts je hebt gebruikt -- En of je het werk goed hebt opgesplitst (één prompt per taak) - -**Dit hoef je niet in te leveren**, maar zorg dat je het kunt laten zien in de les! - ---- - -## Inleveren via Teams (voor Les 3) - -Post in Teams: -1. **Link naar Vercel** (je live website) -2. **Link naar GitHub** (je repository) - -Dat is alles! In Les 3 gaan we kort iedereen langs: -- Bekijken wat je hebt gemaakt -- Bespreken hoeveel prompts je hebt gebruikt -- Delen wat goed werkte en wat niet - -**πŸ’‘ Tip:** Houd je prompt geschiedenis bij (zie Deel A hierboven) - we bespreken dit in de les! - ---- - -## Hulp nodig? - -- Check de OpenCode documentatie: opencode.ai/docs -- Bekijk de les slides nogmaals -- Stel vragen in Teams -- Stel vragen in Teams diff --git a/v2/Les02-OpenCode/Les02-Docenttekst-Notes.md b/v2/Les02-OpenCode/Les02-Docenttekst-Notes.md deleted file mode 100644 index 0245af6..0000000 --- a/v2/Les02-OpenCode/Les02-Docenttekst-Notes.md +++ /dev/null @@ -1,235 +0,0 @@ -# Les 2 - Keynote Notes - -**Spreektijd:** ~50 min | **Hands-on:** ~85 min | **Pauze:** 15 min (10:15-10:30) - ---- - -## BLOK 1: WELKOM & TERUGBLIK (10 min) - ---- - -### Slide 1: Welkom -- "Welkom terug bij les 2!" -- "Vandaag: jullie projecten uitbreiden" - ---- - -### Slide 2: Planning -- 09:00-09:20: Intro OpenCode -- 09:20-09:50: Setup + Demo's -- 09:50-10:15: Hands-on start -- 10:15-10:30: PAUZE -- 10:30-11:30: Hands-on vervolg -- 11:30-12:00: Resultaten + Afsluiting - ---- - -### Slide 3: Terugblik Les 1 -- **Jullie hebben:** website op Vercel, code in GitHub -- **Maar:** veel copy/paste, geen lokale dev -- "Vandaag gaan we dat oplossen" - ---- - -### Slide 4: Vandaag -- Bestaande repos lokaal draaien -- Uitbreiden met AI -- Deployen via git push - ---- - -## BLOK 2: INTRO OPENCODE (10 min) - ---- - -### Slide 5: Wat is OpenCode? -- Open source, 60.000+ stars -- Terminal Γ©n **Desktop App** (met ingebouwde terminal!) -- Kies je AI: OpenCode Zen, Groq, Ollama, **OpenAI** - ---- - -### Slide 6: Installatie -- **Terminal:** `npm i -g opencode-ai@latest` -- **Desktop App:** opencode.ai/download -- **Gratis AI Model:** `Ctrl+X M` β†’ OpenCode Zen β†’ **Kimi K2.5 Free** -- "Wij gebruiken vandaag de Desktop App" - ---- - -### Slide 7: Build vs Plan Mode -- **Plan (πŸ“‹):** leest, analyseert, verandert NIETS -- **Build (πŸ”¨):** schrijft code, maakt bestanden -- Wisselen: `Tab` -- **πŸ’‘ EΓ©n prompt per taak!** - ---- - -### Slide 8: Shortcuts -- `Ctrl+P` = command menu -- `Tab` = wissel mode -- `@` = zoek bestanden (context!) -- `@explore` = codebase verkennen (read-only) - ---- - -### Slide 9: @explore - Codebase Verkennen -- **Read-only** subagent -- Zoekt bestanden, vindt keywords -- Maakt **geen wijzigingen** -- Voorbeeld: `@explore how does routing work?` - ---- - -### Slide 10: Deployment Workflow -- Wijzig code β†’ git add β†’ commit β†’ push -- Vercel pakt automatisch op -- Jullie repos zijn al gekoppeld! - ---- - -### Slide 11: Optioneel - OpenAI API Key -- **Wil je GPT-4?** platform.openai.com β†’ API Keys -- In OpenCode: `Ctrl+X M` β†’ OpenAI β†’ Plak key -- **Let op:** betaald (~$0.01/prompt) -- **Tip:** Start met Kimi K2.5 Free! - ---- - -## BLOK 3: SETUP & DEMO (20 min) - ---- - -### Slide 12: Live Demo Overzicht (blijft op scherm) - -**DEMO 1: Terminal (5 min)** -- `opencode` β†’ toon Ctrl+P, Tab, @, Ctrl+X M -- "Krachtig, maar Desktop App is vriendelijker..." - -**DEMO 2: Desktop App Setup (5 min)** -- Open App β†’ Open Folder β†’ v0 project -- Ctrl+X M β†’ OpenCode Zen β†’ Kimi K2.5 Free -- Terminal: `npm install && npm run dev` - -**DEMO 3: Plan Mode (5 min)** -- πŸ“‹ "Analyseer dit project. Hoe werkt de routing?" -- AI leest alleen, verandert niets - -**DEMO 4: Build Mode (5 min)** -- Tab β†’ πŸ”¨ -- "Maak nieuwe pagina /about met hero" -- Review β†’ Accept β†’ Refresh browser - ---- - -## BLOK 4: GIT & VERCEL (10 min) - ---- - -### Slide 13: Demo - Git Push & Vercel -- **DEMO:** -```bash -git add . -git commit -m "Add about page" -git push -``` -- Of: GitHub Desktop -- β†’ Open Vercel dashboard β†’ "Building..." β†’ Live! - ---- - -### Slide 14: Preview Deployments -- **Elke branch = eigen preview URL** -- main β†’ productie | feature/x β†’ preview URL -- Test zonder productie te breken -- Deel preview link met team/klant -- **DEMO:** nieuwe branch pushen β†’ toon preview in Vercel - ---- - -### Slide 15: Nieuw project naar Vercel -- **Voor huiswerk!** v0 was automatisch gekoppeld -- Vercel β†’ Add New Project β†’ Import Git Repository -- Selecteer repo β†’ Deploy - ---- - -## BLOK 5: HANDS-ON (85 min + 15 min pauze) - ---- - -### Slide 16: Opdracht (blijft op scherm) - -``` -STAP 1: PROJECT KLAARZETTEN - git clone β†’ cd β†’ npm install β†’ npm run dev - -STAP 2: OPENCODE DESKTOP APP - Open App β†’ Open Folder β†’ Ctrl+X M β†’ Zen β†’ Kimi K2.5 Free - -STAP 3: BOUW MET OPENCODE - β–‘ Nieuwe pagina (/about, /services, /contact) - β–‘ Voeg 2 componenten toe - β–‘ Voeg link op homepage - πŸ’‘ EΓ©n prompt per taak! - -STAP 4: DEPLOY - git add . β†’ git commit β†’ git push -``` - -**Doel:** minimaal 2x deployen | β˜• Pauze: 10:15-10:30 - ---- - -### Hands-on deel 1 (09:55-10:15) -- Help bij opstartproblemen -- Draait npm run dev? -- Kimi K2.5 Free geselecteerd? - ---- - -### β˜• PAUZE (10:15-10:30) - ---- - -### Hands-on deel 2 (10:30-11:30) -**Checks:** -- 10:45 - "Wie draait lokaal?" -- 11:15 - "Wie heeft 2x gepusht?" - ---- - -## BLOK 6: AFSLUITING (30 min) - ---- - -### Slide 17: Resultaten -- 2-3 studenten tonen werk -- "Wat toegevoegd? Hoeveel keer gepusht?" - ---- - -### Slide 18: Samenvatting -- βœ… OpenCode Terminal + Desktop App -- βœ… Gratis: Kimi K2.5 Free -- βœ… Optioneel: OpenAI met API key -- βœ… Plan vs Build mode -- βœ… @explore voor codebase verkennen -- βœ… Git push β†’ auto deploy - ---- - -### Slide 19: Huiswerk -- **Nieuw project vanaf scratch** -- create-next-app β†’ 3 componenten -- Push naar GitHub -- **Vercel: Import Project** (zie slide 15!) -- **Inleveren:** Vercel + GitHub link in Teams -- **Les 3:** Iedereen langs, prompts bespreken - ---- - -### Slide 20: Volgende les -- OpenCode IDE Integratie -- VSCode plugin -- "Tot dan!" diff --git a/v2/Les02-OpenCode/Les02-Docenttekst.md b/v2/Les02-OpenCode/Les02-Docenttekst.md deleted file mode 100644 index 99074d6..0000000 --- a/v2/Les02-OpenCode/Les02-Docenttekst.md +++ /dev/null @@ -1,406 +0,0 @@ -# Docenttekst Les 2: OpenCode - AI Code Assistants - -> **Totale lesduur:** 3 uur (180 minuten) -> **Spreektijd docent:** ~50 minuten -> **Hands-on:** ~85 minuten -> **Pauze:** 15 minuten (10:15-10:30) - ---- - -## BLOK 1: WELKOM & TERUGBLIK (10 minuten) - -### Slide 1: Welkomstslide - -**[09:00 - 09:05]** - -"Welkom terug bij les 2! Vorige week hebben jullie allemaal een website gebouwd en online gezet. Vandaag gaan we een stap verder." - ---- - -### Slide 2: Planning - -**[bij slide 1]** - -"De planning: tot 10 voor 10 doe ik uitleg en demo's. Dan gaan jullie aan de slag met jullie eigen projecten. Kwart over 10 hebben we een korte pauze, daarna werken jullie door tot half 12." - ---- - -### Slide 3: Terugblik Les 1 - -**[09:05 - 09:08]** - -"Jullie hebben nu: een werkende website op Vercel, code in GitHub, en ervaring met v0.dev." - -_[Wijs naar de problemen]_ - -"Maar... veel copy/paste. Geen lokale development. Beperkte controle over je code. Vandaag gaan we dat oplossen." - ---- - -### Slide 4: Vandaag - De volgende stap - -**[09:08 - 09:10]** - -"Vandaag werken we met jullie bestaande repos. We gaan ze lokaal draaien, uitbreiden met AI, en weer deployen naar Vercel. De volledige development workflow." - ---- - -## BLOK 2: INTRODUCTIE OPENCODE (10 minuten) - -### Slide 5: Wat is OpenCode? - -**[09:10 - 09:12]** - -"OpenCode - open source met meer dan 60.000 GitHub stars. Het is een AI coding assistant die direct in je project werkt." - -"Er is een terminal versie Γ©n een Desktop App. Die Desktop App is super handig - vriendelijke interface en een ingebouwde terminal. Die gaan we vandaag gebruiken." - -"Het mooie: je kiest je eigen AI. Er zijn gratis modellen beschikbaar, dus je hebt geen API key nodig." - ---- - -### Slide 6: Installatie - -**[09:12 - 09:14]** - -"Hoe installeer je het? Voor terminal: npm i -g opencode-ai@latest." - -"Maar wij gebruiken de Desktop App. Download van opencode.ai/download. Installeer het, en je bent klaar." - -"Het AI model stel je in met Ctrl+X M. Kies OpenCode Zen, dan Kimi K2.5 Free. Gratis!" - ---- - -### Slide 7: Build vs Plan Mode - -**[09:14 - 09:15]** - -"Belangrijk concept: twee modes." - -"**Plan mode** - het klembord icoon. AI kan alleen lezen en analyseren. Perfect om je project te begrijpen." - -"**Build mode** - de hamer. AI kan code schrijven en bestanden aanmaken." - -"Wisselen doe je met Tab." - -_[Belangrijk punt - benadrukken!]_ - -"En dit is cruciaal: **één prompt per taak**. Vraag niet 'maak een complete website met alles erop en eraan'. Splits het op: eerst de header, dan de hero, dan de volgende sectie. Zo houd je controle en kun je elk onderdeel goed reviewen." - ---- - -### Slide 8: Shortcuts - -**[09:15 - 09:16]** - -"Vier shortcuts om te onthouden:" - -"Ctrl+P voor het command menu. Tab om te wisselen tussen modes. @ om bestanden te zoeken - super handig voor context. @explore om je codebase te verkennen zonder wijzigingen." - ---- - -### Slide 9: @explore - Codebase Verkennen - -**[09:16 - 09:17]** - -"@explore is een speciale feature. Het is een read-only subagent - hij leest alleen, verandert niets." - -"Super handig voor nieuwe codebases. Typ @explore en dan je vraag. Bijvoorbeeld: '@explore waar is de authentication code?' of '@explore hoe werkt de routing?'" - -"Het verschil met Plan mode: @explore zoekt actief door je hele project. Plan mode analyseert alleen wat je hem geeft." - ---- - -### Slide 10: De Deployment Workflow - -**[09:17 - 09:18]** - -"Dit is de workflow die jullie vandaag gaan gebruiken:" - -"Wijzig code met OpenCode. Git add, commit, push. En dan... Vercel pakt het automatisch op. Jullie project is al gekoppeld aan GitHub, dus elke push wordt automatisch live." - ---- - -### Slide 11: Optioneel - OpenAI API Key - -**[09:18 - 09:20]** - -"Even dit: als je GPT-4 wil gebruiken in plaats van de gratis modellen, kan dat ook." - -"Je maakt een account op platform.openai.com, gaat naar API Keys, maakt een key aan, en plakt die in OpenCode via Ctrl+X M." - -"Let op: OpenAI is betaald, ongeveer een cent per prompt. Voor nu beginnen we met Kimi K2.5 Free - dat is gratis en werkt prima!" - ---- - -## BLOK 3: SETUP & DEMO (20 minuten) - -### Slide 12: Live Demo Overzicht - -**[09:20 - 09:40]** - -_[Deze slide blijft op het scherm terwijl je alle demo's geeft]_ - ---- - -#### DEMO 1: Terminal Versie (5 min) - -_[Open terminal - LIVE DEMO]_ - -"Laat me eerst de terminal versie laten zien. Installatie: npm i -g opencode-ai@latest, dan opencode." - -_[Start opencode in terminal]_ - -"Dit is de TUI - Terminal User Interface. Kijk naar de shortcuts:" - -- "Ctrl+P voor het command menu" -- "Tab om te wisselen tussen Plan en Build mode" -- "@ om bestanden te zoeken" -- "Ctrl+X M om je AI model te kiezen" - -"Krachtig! Maar het kan overweldigend zijn voor beginners. Daarom..." - ---- - -#### DEMO 2: Desktop App Setup (5 min) - -"...is er ook de Desktop App!" - -_[Open Desktop App]_ - -"Zelfde features, maar veel vriendelijker. En kijk - een ingebouwde terminal! Dit gaan we vandaag gebruiken." - -_[Laat studenten Desktop App downloaden van opencode.ai/download]_ - -"Ik open de Desktop App en selecteer mijn projectfolder - dat is mijn v0 repo." - -_[Open project folder]_ - -"Nu het AI model: Ctrl+X M, kies OpenCode Zen, dan Kimi K2.5 Free." - -_[Laat studenten ook configureren - help waar nodig]_ - -"In de ingebouwde terminal run ik npm install en npm run dev." - -_[Toon localhost:3000 in browser]_ - ---- - -#### DEMO 3: Plan Mode (5 min) - -_[LIVE DEMO in Desktop App]_ - -"Ik zit in Plan mode - zie het klembord icoon." - -"Ik vraag: 'Analyseer dit project. Hoe werkt de routing? Hoe maak ik een nieuwe pagina?'" - -_[Wacht op response]_ - -"Kijk - het leest alles, geeft een analyse, maar verandert niets. Perfect om eerst te begrijpen." - ---- - -#### DEMO 4: Build Mode (5 min) - -_[LIVE DEMO in Desktop App]_ - -"Tab om naar Build mode te gaan. Nu vraag ik:" - -"Maak een nieuwe pagina /about met een hero sectie en een team sectie. Voeg ook een link toe op de homepage naar /about." - -_[Wacht op generatie]_ - -"Het maakt de pagina aan, de componenten, past de homepage aan. Kijk in de browser - refresh - daar is de link! En als ik klik... de nieuwe pagina!" - ---- - -## BLOK 4: GIT & VERCEL WORKFLOW (10 minuten) - -### Slide 13: Demo - Git Push & Vercel - -**[09:40 - 09:45]** - -_[LIVE DEMO in Desktop App terminal]_ - -"Nu de belangrijke stap: deployen. Ik gebruik gewoon git." - -```bash -git add . -git commit -m "Add about page" -git push -``` - -"Of als je GitHub Desktop prefereert: changed files links, commit message, commit, push." - -_[Open Vercel dashboard]_ - -"Zie je? Building. Vercel heeft de push opgepikt. Na ongeveer een minuut... Live! De wijzigingen staan online." - -"Dit is de workflow: code lokaal, push, automatisch live." - ---- - -### Slide 14: Preview Deployments - -**[09:45 - 09:47]** - -"En nu iets super handig: Preview Deployments." - -_[Toon Vercel dashboard]_ - -"Elke branch die je pusht krijgt automatisch z'n eigen preview URL. Kijk:" - -"Je main branch gaat naar je productie URL. Maar als je een feature branch pusht - bijvoorbeeld feature/about - krijg je een aparte preview URL." - -_[LIVE DEMO - optioneel]_ - -```bash -git checkout -b feature/test-page -# Maak kleine wijziging -git add . && git commit -m "Test preview" -git push -u origin feature/test-page -``` - -"En kijk in Vercel... daar is je preview deployment!" - -"Waarom is dit handig? Je kunt wijzigingen testen zonder je productie site te breken. En je kunt de preview link delen met je team of klant voor feedback." - ---- - -### Slide 15: Nieuw project naar Vercel - -**[09:47 - 09:50]** - -"Dit werkt omdat jullie v0 project al aan Vercel gekoppeld is. Maar voor het huiswerk maken jullie een **nieuw** project. Dan moet je Vercel handmatig koppelen." - -_[Toon Vercel dashboard]_ - -"Stappen: Add New Project, Import Git Repository, selecteer je repo, Deploy. Dat is het." - -"Dit hebben jullie nodig voor het huiswerk - een nieuw project vanaf scratch deployen." - ---- - -## BLOK 5: HANDS-ON OPDRACHT (85 minuten + 15 min pauze) - -### Slide 16: Aan de slag! - -**[09:50 - 09:55]** _(5 minuten uitleg)_ - -"Jullie gaan nu een nieuwe pagina toevoegen aan jullie v0 website." - -"Stap 1: Clone je repo of pull latest. Stap 2: npm install, npm run dev. Stap 3: Maak een nieuwe pagina met OpenCode - about, services, of contact. Voeg componenten toe en een link op de homepage. Stap 4: Push naar GitHub, bekijk live op Vercel." - -"Doel: minimaal 2 keer deployen. Je leert hoe routing werkt in Next.js Γ©n hoe je navigatie toevoegt." - -"Kwart over 10 hebben we een korte pauze van 15 minuten. Begin!" - ---- - -### Hands-on Deel 1 - -**[09:55 - 10:15]** _(20 minuten werken)_ - -_[Loop actief rond, help bij opstartproblemen]_ - -**Let op:** -- Draait npm run dev? -- Lukt git clone/pull? -- Kimi K2.5 Free geselecteerd? - ---- - -### β˜• PAUZE - -**[10:15 - 10:30]** _(15 minuten)_ - -"OkΓ©, pauze! Om half 11 gaan we verder." - ---- - -### Hands-on Deel 2 - -**[10:30 - 11:30]** _(60 minuten werken)_ - -_[Loop actief rond]_ - -**Let op:** -- Zit student in juiste mode? -- Gebruikt @ mentions? -- Lukt git push? - -**Check [10:45]:** -"Wie heeft de website lokaal draaien? Wie heeft al gepusht naar Vercel?" - -**Check [11:15]:** -"Hoeveel mensen hebben al 2x gepusht? Wie zit vast?" - ---- - -## BLOK 6: AFSLUITING (30 minuten) - -### Slide 17: Resultaten delen - -**[11:30 - 11:40]** - -"Stop met coderen. Wie wil laten zien wat ze hebben gebouwd?" - -_[2-3 studenten tonen hun werk]_ - -"Wat heb je toegevoegd? Hoeveel keer gepusht? Wat ging goed, wat was lastig?" - ---- - -### Slide 18: Samenvatting - -**[11:40 - 11:50]** - -"Wat hebben we gedaan vandaag?" - -- OpenCode: terminal versie Γ©n Desktop App -- Gratis model: Kimi K2.5 Free -- Optioneel: OpenAI met API key -- Plan vs Build mode -- @explore voor codebase verkennen -- De volledige workflow: lokaal ontwikkelen, pushen, automatisch live - -_[Ruimte voor vragen]_ - ---- - -### Slide 19: Huiswerk - -**[11:50 - 11:55]** - -"Thuis: een nieuw project vanaf scratch. Niet je v0 project uitbreiden, maar helemaal nieuw beginnen." - -"Stappen: create-next-app, bouw 3 componenten, push naar GitHub, en dan - belangrijk - Vercel Import Project. Zoals ik eerder liet zien." - -"Inleveren via Teams: gewoon je Vercel link en GitHub link. Volgende les gaan we iedereen even langs en bespreken we je prompts. Hoeveel prompts heb je gebruikt? Hoe heb je je werk opgesplitst? Dus houd je prompts bij!" - ---- - -### Slide 20: Afsluiting - -**[11:55 - 12:00]** - -"Volgende week: OpenCode in je IDE. Dan gaan we de VSCode plugin gebruiken voor nog sneller werken." - -"Vragen? Anders tot volgende week!" - ---- - -## Timing Overzicht - -| Blok | Tijd | Duur | -|------|------|------| -| 1. Welkom & Terugblik | 09:00-09:10 | 10 min | -| 2. Intro OpenCode | 09:10-09:20 | 10 min | -| 3. Setup & Demo | 09:20-09:40 | 20 min | -| 4. Git & Vercel | 09:40-09:50 | 10 min | -| 5. Hands-on (deel 1) | 09:50-10:15 | 25 min | -| β˜• PAUZE | 10:15-10:30 | 15 min | -| 5. Hands-on (deel 2) | 10:30-11:30 | 60 min | -| 6. Afsluiting | 11:30-12:00 | 30 min | - -**Spreektijd:** ~50 min | **Hands-on:** ~85 min | **Pauze:** 15 min diff --git a/v2/Les02-OpenCode/Les02-Lesplan.md b/v2/Les02-OpenCode/Les02-Lesplan.md deleted file mode 100644 index e34f581..0000000 --- a/v2/Les02-OpenCode/Les02-Lesplan.md +++ /dev/null @@ -1,179 +0,0 @@ -# Lesplan Les 2: OpenCode - AI Code Assistants - ---- - -## Lesgegevens - -| Item | Details | -|------|---------| -| **Les** | 2 van 18 | -| **Onderwerp** | OpenCode - AI Code Assistants | -| **Deel** | 1: AI Foundations (Les 1-4) | -| **Duur** | 3 uur (180 minuten) | -| **Spreektijd** | ~50 minuten | -| **Hands-on** | ~85 minuten | -| **Pauze** | 15 minuten (10:15-10:30) | - ---- - -## Leerdoelen - -Na deze les kan de student: - -1. Uitleggen wat OpenCode is en het verschil tussen Terminal en Desktop App -2. De OpenCode Desktop App installeren en configureren met een gratis model (Kimi K2.5 Free) -3. Werken met Plan mode en Build mode -4. @explore gebruiken om een codebase te verkennen (read-only) -5. Een bestaand GitHub project openen en lokaal draaien (in de ingebouwde terminal) -6. Wijzigingen maken met OpenCode en deployen via git push -7. De @ file mention gebruiken voor context - ---- - -## Voorbereiding docent - -### Technische setup -- [ ] OpenCode Terminal geΓ―nstalleerd (`npm i -g opencode-ai@latest`) -- [ ] OpenCode Desktop App geΓ―nstalleerd (opencode.ai/download) -- [ ] Demo project: een v0 repo gekloond en werkend -- [ ] Git geconfigureerd - -### Student voorkennis (uit Les 1) -- Hebben een werkende v0 website op Vercel -- Hebben de code in een GitHub repository -- Weten hoe GitHub Desktop werkt - ---- - -## Lesverloop - -### Blok 1: Welkom & Terugblik (10 minuten) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 09:00-09:05 | Welkom, planning | 1-2 | -| 09:05-09:08 | Terugblik Les 1: v0 workflow + problemen | 3 | -| 09:08-09:10 | Vandaag: OpenCode + verder bouwen | 4 | - -**Kernboodschap:** "Jullie hebben werkende websites. Vandaag gaan we die uitbreiden met AI direct in je editor." - ---- - -### Blok 2: Introductie OpenCode (10 minuten) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 09:10-09:12 | Wat is OpenCode? | 5 | -| 09:12-09:14 | Installatie (Terminal + Desktop App) | 6 | -| 09:14-09:15 | Build vs Plan mode | 7 | -| 09:15-09:17 | Shortcuts + @explore | 8-9 | -| 09:17-09:19 | Vercel deployment workflow | 10 | -| 09:19-09:20 | Optioneel: OpenAI API key | 11 | - -**Belangrijk:** We gebruiken Kimi K2.5 Free - gratis, geen API key nodig! - ---- - -### Blok 3: Setup & Demo (20 minuten) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 09:20-09:40 | **DEMO's:** Terminal, Desktop App, Plan mode, Build mode | 12 | - -**Slide 12 blijft op scherm tijdens alle demo's:** -- DEMO 1: Terminal versie (5 min) - shortcuts tonen -- DEMO 2: Desktop App setup (5 min) - project laden + Kimi K2.5 Free -- DEMO 3: Plan mode (5 min) - project analyseren -- DEMO 4: Build mode (5 min) - nieuwe pagina maken - ---- - -### Blok 4: Git & Vercel Workflow (10 minuten) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 09:40-09:45 | **DEMO:** Git push + Vercel auto-deploy | 13 | -| 09:45-09:47 | **DEMO:** Preview Deployments (branches) | 14 | -| 09:47-09:50 | Nieuw project naar Vercel (voor huiswerk) | 15 | - -**Kernboodschap:** "Push naar GitHub = automatisch live op Vercel. Branches krijgen preview URLs!" - ---- - -### Blok 5: Hands-on Opdracht (85 minuten + 15 min pauze) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 09:50-09:55 | Opdracht uitleggen | 16 | -| 09:55-10:15 | Studenten werken (20 min) | - | -| 10:15-10:30 | β˜• **PAUZE** | - | -| 10:30-11:30 | Studenten werken (60 min) | - | - -**Opdracht:** Maak een nieuwe pagina in je v0 project - -1. Clone je v0 repo (of gebruik GitHub Desktop) -2. Run lokaal met `npm install` + `npm run dev` -3. Met OpenCode: - - Maak nieuwe pagina (/about, /services, of /contact) - - Voeg minimaal 2 componenten toe aan de pagina - - Voeg link op homepage toe naar de nieuwe pagina -4. Push naar GitHub β†’ bekijk live op Vercel - -**Tijdens het werken:** -- Loop actief rond -- Help bij git/npm problemen -- Check: werkt lokale server? -- Aanmoedigen: meerdere keren pushen en deployen - -**Tussentijdse check (10:45):** -- "Wie heeft lokaal draaien?" -- "Wie heeft een nieuwe pagina aangemaakt?" - -**Check (11:15):** -- "Wie heeft de link op de homepage?" -- "Wie heeft 2x gepusht?" - ---- - -### Blok 6: Afsluiting (30 minuten) - -| Tijd | Activiteit | Slides | -|------|------------|--------| -| 11:30-11:40 | Resultaten delen (2-3 studenten) | 17 | -| 11:40-11:50 | Samenvatting + Q&A | 18 | -| 11:50-11:55 | Huiswerk uitleggen | 19 | -| 11:55-12:00 | Afsluiting | 20 | - ---- - -## Materialen - -- **Slide-Overzicht:** Les02-Slide-Overzicht.md -- **Lesopdracht:** Les02-Bijlage-A-Lesopdracht.md -- **Huiswerkopdracht:** Les02-Bijlage-B-Huiswerkopdracht.md - ---- - -## Veelvoorkomende problemen - -| Probleem | Oplossing | -|----------|-----------| -| OpenCode installeert niet | Check Node.js β‰₯18 | -| `npm install` faalt | Delete `node_modules`, probeer opnieuw | -| `npm run dev` werkt niet | Check of je in de juiste map staat | -| Git push rejected | `git pull` eerst, dan push | -| Vercel deployt niet | Check GitHub repo permissions | -| Kimi K2.5 traag | Normaal, gratis model is iets langzamer | - ---- - -## Alternatieve AI providers - -| Provider | Kosten | Setup | -|----------|--------|-------| -| **Kimi K2.5 Free** | Gratis | Geen key nodig (via OpenCode Zen) | -| **OpenAI (GPT-4)** | Betaald (~$0.01/prompt) | API key via platform.openai.com | -| **Groq** | Gratis tier | API key via console.groq.com | -| **Ollama** | Gratis (lokaal) | Installatie + model download | - -**Aanbeveling:** Start met Kimi K2.5 Free. OpenAI voor wie extra kwaliteit wil (kost geld). diff --git a/v2/Les02-OpenCode/Les02-Slide-Overzicht.md b/v2/Les02-OpenCode/Les02-Slide-Overzicht.md deleted file mode 100644 index b727300..0000000 --- a/v2/Les02-OpenCode/Les02-Slide-Overzicht.md +++ /dev/null @@ -1,418 +0,0 @@ -# Les 2: OpenCode - AI Code Assistants (V2) - -> **Deel 1: AI Foundations** (Les 1-4) -> **Totale slides:** 21 -> **Lesduur:** 3 uur (180 minuten) -> **Spreektijd:** ~50 min | **Hands-on:** ~85 min | **Pauze:** 15 min - -**V2 Verbeteringen:** -- Voorbereiding check toegevoegd (slide 3) -- "WACHT - nog niet installeren" waarschuwingen -- Duidelijk onderscheid System Terminal vs OpenCode Terminal -- Gratis model backup opties toegevoegd -- Platform-specifieke instructies (Mac/Windows) - -**⚠️ VEREIST:** Studenten moeten [Les02-Voorbereiding.md](Les02-Voorbereiding.md) doorlopen VOOR deze les! - ---- - -## Slide-structuur per blok - -| Blok | Onderwerp | Slides | Tijd | -|------|-----------|--------|------| -| 1 | Welkom & Terugblik + **Voorbereiding Check** | 1-5 | 12 min | -| 2 | Introductie OpenCode | 6-12 | 10 min | -| 3 | Setup & Demo | 13 | 20 min | -| 4 | Git & Vercel Workflow | 14-16 | 10 min | -| 5 | Hands-on Opdracht | 17 | 85 min | -| - | **β˜• PAUZE** | - | 15 min | -| 6 | Afsluiting | 18-21 | 28 min | - ---- - -## BLOK 1: WELKOM & TERUGBLIK (10 minuten) - -### Slide 1: Welkomstslide -**Titel:** Les 2: OpenCode - AI Code Assistants - ---- - -### Slide 2: Planning vandaag - -| Tijd | Onderwerp | -|------|-----------| -| 09:00-09:12 | Terugblik + **Voorbereiding Check** | -| 09:12-09:22 | Intro OpenCode (KIJK ALLEEN, nog niet installeren!) | -| 09:22-09:42 | Setup + Demo's (NU installeren) | -| 09:42-09:52 | Git & Vercel Workflow | -| 09:52-10:15 | Hands-on: Start | -| 10:15-10:30 | β˜• **PAUZE** | -| 10:30-11:30 | Hands-on: Vervolg | -| 11:30-12:00 | Resultaten + Afsluiting | - ---- - -### Slide 3: Voorbereiding Check ⚠️ - -**Titel:** Heb je de voorbereiding gedaan? - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ CHECK JE SETUP - Voer uit in je EIGEN terminal: β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ node -v β†’ Moet v20.x.x tonen β”‚ -β”‚ git --version β†’ Moet 2.x.x tonen β”‚ -β”‚ ssh -T git@github.com β†’ "Hi [username]!" β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ βœ… Alles werkt? Top! β”‚ -β”‚ ❌ Iets mist? Vraag de docent NU om hulp β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**⚠️ Niet voorbereid?** Dan kun je helaas niet volledig meedoen. - ---- - -### Slide 4: Terugblik Les 1 - Wat hebben we? - -**Jullie hebben:** -- βœ… Een werkende website op Vercel -- βœ… Code in een GitHub repository -- βœ… Ervaring met v0.dev - -**Maar:** -- Veel copy/paste naar v0 -- Geen lokale development -- Beperkte controle - ---- - -### Slide 5: Vandaag - De volgende stap - -**OpenCode:** AI direct in je development workflow - -- βœ… Werk met je bestaande repository -- βœ… Draai lokaal, zie wijzigingen direct -- βœ… Push naar GitHub β†’ automatisch live op Vercel - ---- - -## BLOK 2: INTRODUCTIE OPENCODE (10 minuten) - -### Slide 6: Wat is OpenCode? - -**⚠️ KIJK ALLEEN - NOG NIET INSTALLEREN!** - -- 🌟 Open source (60.000+ GitHub stars) -- πŸ”Œ Kies je AI: OpenCode Zen, Groq, Ollama, **OpenAI** -- πŸ–₯️ Terminal Γ©n **Desktop App** (met ingebouwde terminal!) -- πŸ”’ Privacy-first - -**Vandaag:** Desktop App + gratis model (Kimi K2.5 Free) - ---- - -### Slide 7: Installatie (WACHT - straks samen!) - -**⚠️ NOG NIET INSTALLEREN - Dit doen we straks SAMEN in de demo!** - -**Terminal versie:** -```bash -npm i -g opencode-ai@latest -``` - -**Desktop App:** -- Download: **opencode.ai/download** -- Installeer voor jouw OS (Mac/Windows) - -**Gratis AI Model:** -- `Ctrl+X M` β†’ OpenCode Zen β†’ **Kimi K2.5 Free** - ---- - -### Slide 8: Build vs Plan Mode - -| Mode | Icoon | Wat het doet | -|------|-------|--------------| -| **Plan** | πŸ“‹ | Analyseert, verandert NIETS | -| **Build** | πŸ”¨ | Schrijft code, maakt bestanden | - -**Wisselen:** `Tab` - -**πŸ’‘ Tip: EΓ©n prompt per taak!** -- ❌ "Maak een complete website met header, hero en footer" -- βœ… "Maak een Header component" β†’ "Voeg hero toe" β†’ "Maak footer" - ---- - -### Slide 9: Keyboard Shortcuts - -| Shortcut | Actie | -|----------|-------| -| `Ctrl+P` | Command menu | -| `Tab` | Wissel Build/Plan | -| `@` | Zoek bestanden | -| `@explore` | Codebase verkennen (read-only) | - ---- - -### Slide 10: @explore - Codebase Verkennen - -**Wat is @explore?** -- Een **read-only** subagent voor het verkennen van je codebase -- Zoekt bestanden, vindt keywords, analyseert structuur -- Maakt **geen wijzigingen** aan je code - -**Gebruik:** -``` -@explore find all authentication code -@explore how does the routing work? -@explore where are the API endpoints? -``` - -**Wanneer gebruiken?** -- Nieuwe codebase leren kennen -- Code zoeken voordat je wijzigt -- Project structuur begrijpen - ---- - -### Slide 11: De Deployment Workflow - -``` -1. Wijzig code met OpenCode -2. git add . -3. git commit -m "Add about page" -4. git push -5. ✨ Vercel deployt automatisch! -``` - -Jullie Vercel project is al gekoppeld aan GitHub. - ---- - -### Slide 12: Optioneel - OpenAI API Key - -**Wil je GPT-4 gebruiken?** - -1. Ga naar **platform.openai.com** -2. Maak account aan (of log in) -3. API Keys β†’ Create new secret key -4. Kopieer de key (begint met `sk-...`) -5. In OpenCode: `Ctrl+X M` β†’ OpenAI β†’ Plak key - -**Let op:** OpenAI is betaald (~$0.01 per prompt) - -**Tip:** Start met Kimi K2.5 Free, upgrade later als je wilt! - ---- - -## BLOK 3: SETUP & DEMO (20 minuten) - -### Slide 13: Live Demo Overzicht (blijft op scherm) - -**⚠️ BELANGRIJK: Twee soorten terminals!** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ πŸ–₯️ SYSTEM TERMINAL (je eigen terminal) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ β†’ Mac: Terminal app β”‚ -β”‚ β†’ Windows: PowerShell / Git Bash β”‚ -β”‚ β†’ Voor: git clone, npm install, opencode starten β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ πŸ€– OPENCODE TERMINAL (binnen OpenCode) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ β†’ Verschijnt als je 'opencode' runt β”‚ -β”‚ β†’ Heeft AI chat + ingebouwde terminal β”‚ -β”‚ β†’ Voor: AI prompts, code generatie β”‚ -β”‚ β†’ Type 'exit' om terug te gaan naar system terminalβ”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**DEMO STAPPEN:** - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ DEMO 1: INSTALLATIE (5 min) - in SYSTEM terminal β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ Mac: npm i -g opencode-ai@latest β”‚ -β”‚ Windows: npm i -g opencode-ai@latest β”‚ -β”‚ (EACCESS error? β†’ sudo npm i -g opencode-ai@latest)β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ DEMO 2: DESKTOP APP SETUP (5 min) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Open Desktop App β”‚ -β”‚ 2. File β†’ Open Folder β†’ v0 project β”‚ -β”‚ 3. Ctrl+X M β†’ OpenCode Zen β†’ Kimi K2.5 Free β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ πŸ’‘ Tokens op? Probeer: MiniMax M2.1 Free β”‚ -β”‚ of Big Pickle Free β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ DEMO 3: PLAN MODE (5 min) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ πŸ“‹ "Analyseer dit project. Hoe werkt de routing?" β”‚ -β”‚ β†’ AI leest alleen, verandert niets β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ DEMO 4: BUILD MODE (5 min) β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ Tab β†’ πŸ”¨ β”‚ -β”‚ "Maak nieuwe pagina /about met hero" β”‚ -β”‚ β†’ Review code β†’ Accept β†’ Refresh browser β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**Gratis Model Backup Opties:** -| Model | Snelheid | Tokens | -|-------|----------|--------| -| Kimi K2.5 Free | ⭐⭐⭐ | Beperkt | -| MiniMax M2.1 Free | ⭐⭐ | Beperkt | -| Big Pickle Free | ⭐⭐ | Beperkt | - ---- - -## BLOK 4: GIT & VERCEL WORKFLOW (10 minuten) - -### Slide 14: Demo - Git Push & Vercel - -**Live demo (in Desktop App terminal):** -```bash -git add . -git commit -m "Add about page" -git push -``` - -**β†’ Open Vercel dashboard:** -1. Zie: "Building..." -2. Na ~1 minuut: live op je URL! - ---- - -### Slide 15: Preview Deployments - -**Branches = Preview URLs** - -``` -main branch β†’ productie-site.vercel.app -feature/about β†’ productie-site-git-feature-about.vercel.app -``` - -**Waarom handig?** -- βœ… Test wijzigingen zonder productie te breken -- βœ… Deel preview link met team/klant -- βœ… Automatisch bij elke branch push - -**Demo:** -```bash -git checkout -b feature/test-page -# Maak wijziging... -git add . && git commit -m "Test preview" -git push -u origin feature/test-page -``` -β†’ Check Vercel: nieuwe preview deployment! - ---- - -### Slide 16: Nieuw project naar Vercel (voor huiswerk) - -**Stappen voor een nieuw project:** - -1. Maak GitHub repository (github.com β†’ New) -2. Push je code naar GitHub -3. Vercel: "Add New Project" -4. "Import Git Repository" -5. Selecteer je repo β†’ Deploy - -**Dit heb je nodig voor het huiswerk!** - ---- - -## BLOK 5: HANDS-ON OPDRACHT (85 minuten) - -### Slide 17: Aan de slag! (blijft op scherm) - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ STAP 1: PROJECT KLAARZETTEN β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ git clone https://github.com/[user]/[repo].git β”‚ -β”‚ cd [repo] β”‚ -β”‚ npm install β”‚ -β”‚ npm run dev β”‚ -β”‚ β†’ Open http://localhost:3000 β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ STAP 2: OPENCODE DESKTOP APP β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ 1. Open Desktop App β”‚ -β”‚ 2. File β†’ Open Folder β†’ je project β”‚ -β”‚ 3. Ctrl+X M β†’ OpenCode Zen β†’ Kimi K2.5 Free β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ STAP 3: BOUW MET OPENCODE β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ β–‘ Maak nieuwe pagina (/about, /services, /contact) β”‚ -β”‚ β–‘ Voeg 2 componenten toe β”‚ -β”‚ β–‘ Voeg link op homepage β”‚ -β”‚ πŸ’‘ EΓ©n prompt per taak! β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ STAP 4: DEPLOY β”‚ -β”‚ ───────────────────────────────────────────────── β”‚ -β”‚ git add . β”‚ -β”‚ git commit -m "Add about page" β”‚ -β”‚ git push β”‚ -β”‚ β†’ Check Vercel dashboard β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**Doel:** Minimaal 2x deployen | β˜• **Pauze:** 10:15-10:30 - ---- - -## BLOK 6: AFSLUITING (30 minuten) - -### Slide 18: Resultaten delen - -- Wie wil laten zien? -- Wat heb je toegevoegd? -- Hoeveel keer gepusht? - ---- - -### Slide 19: Samenvatting - -- βœ… OpenCode: open-source AI assistant -- βœ… Gratis model: Kimi K2.5 Free -- βœ… Optioneel: OpenAI met API key -- βœ… Plan vs Build mode -- βœ… @explore voor codebase verkennen -- βœ… Git push β†’ Vercel auto-deploy - ---- - -### Slide 20: Huiswerk - -**Opdracht: Nieuw project vanaf scratch** - -1. `npx create-next-app@latest` -2. Bouw 3 componenten met OpenCode -3. Push naar GitHub -4. Vercel: Import Project β†’ Deploy - -**Inleveren via Teams:** -- Link naar Vercel -- Link naar GitHub - -**In Les 3:** We gaan iedereen langs en bespreken je prompts! - -**Stappen voor Vercel: zie slide 16!** - ---- - -### Slide 21: Volgende les - -**Les 3:** OpenCode IDE Integratie - -- VSCode plugin -- Inline code generatie -- Advanced prompting - -Tot volgende week! diff --git a/v2/Les02-OpenCode/Les02-Voorbereiding.md b/v2/Les02-OpenCode/Les02-Voorbereiding.md deleted file mode 100644 index 6448e42..0000000 --- a/v2/Les02-OpenCode/Les02-Voorbereiding.md +++ /dev/null @@ -1,256 +0,0 @@ -# Les 2: Voorbereiding (Verplicht!) - -**⚠️ BELANGRIJK:** Voltooi deze stappen VOOR Les 2 begint. Zonder deze setup kun je niet meedoen! - -**Geschatte tijd:** 20-30 minuten - ---- - -## Checklist - -Aan het einde van deze voorbereiding heb je: -- [ ] Node.js geΓ―nstalleerd via NVM -- [ ] Git geΓ―nstalleerd -- [ ] SSH key gegenereerd en toegevoegd aan GitHub -- [ ] Terminal basics begrepen - ---- - -## 1. Node.js installeren via NVM - -**Waarom NVM?** NVM (Node Version Manager) laat je makkelijk wisselen tussen Node versies. Veel projecten vereisen specifieke versies. - -### Mac / Linux - -Open Terminal en voer uit: - -```bash -# 1. Installeer NVM -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash - -# 2. Sluit terminal en open opnieuw (of run dit): -source ~/.zshrc # voor zsh (standaard op Mac) -# OF -source ~/.bashrc # voor bash - -# 3. Installeer Node 20 (LTS) -nvm install 20 - -# 4. Gebruik Node 20 als standaard -nvm use 20 -nvm alias default 20 - -# 5. Controleer installatie -node -v # Moet v20.x.x tonen -npm -v # Moet 10.x.x tonen -``` - -### Windows - -**Optie A: NVM voor Windows (aanbevolen)** - -1. Download installer: https://github.com/coreybutler/nvm-windows/releases -2. Download `nvm-setup.exe` -3. Installeer (volg wizard) -4. Open **nieuwe** PowerShell of Command Prompt -5. Voer uit: - -```powershell -nvm install 20 -nvm use 20 -node -v # Moet v20.x.x tonen -``` - -**Optie B: Direct Node.js installeren** - -1. Ga naar https://nodejs.org -2. Download LTS versie (20.x) -3. Installeer (volg wizard) -4. Herstart terminal -5. Controleer: `node -v` - ---- - -## 2. Git installeren - -### Mac - -Git is vaak al geΓ―nstalleerd. Check: - -```bash -git --version -``` - -Als niet geΓ―nstalleerd: -```bash -# Via Homebrew (aanbevolen) -brew install git - -# Of download van https://git-scm.com/download/mac -``` - -### Windows - -1. Download van https://git-scm.com/download/windows -2. Installeer (gebruik standaard opties) -3. **Belangrijk:** Kies "Git Bash" als terminal optie -4. Herstart terminal -5. Controleer: `git --version` - -### Linux (Ubuntu/Debian) - -```bash -sudo apt update -sudo apt install git -git --version -``` - ---- - -## 3. SSH Key voor GitHub - -**Waarom SSH?** Met SSH hoef je niet steeds je wachtwoord in te voeren bij git push/pull. - -### Stap 1: Genereer SSH key - -**Mac / Linux / Git Bash (Windows):** - -```bash -# Genereer key (vervang met je eigen email!) -ssh-keygen -t ed25519 -C "jouw-email@example.com" - -# Druk Enter voor standaard locatie -# Kies wachtwoord of laat leeg (Enter) -``` - -### Stap 2: Kopieer je public key - -**Mac:** -```bash -cat ~/.ssh/id_ed25519.pub | pbcopy -``` - -**Linux:** -```bash -cat ~/.ssh/id_ed25519.pub -# Selecteer en kopieer handmatig -``` - -**Windows (Git Bash):** -```bash -cat ~/.ssh/id_ed25519.pub | clip -``` - -### Stap 3: Voeg toe aan GitHub - -1. Ga naar https://github.com/settings/keys -2. Klik "New SSH key" -3. Titel: "Mijn Laptop" (of iets herkenbaars) -4. Plak je key -5. Klik "Add SSH key" - -### Stap 4: Test de verbinding - -```bash -ssh -T git@github.com -``` - -Je ziet: `Hi username! You've successfully authenticated...` - -**⚠️ Eerste keer:** Je krijgt een vraag over fingerprint. Type `yes`. - ---- - -## 4. Terminal Basics - -Je hebt een terminal nodig om met OpenCode te werken. Hier de basics: - -### Wat is een terminal? - -- **Mac:** Terminal app (in Applications β†’ Utilities) -- **Windows:** PowerShell, Command Prompt, of **Git Bash** (aanbevolen) -- **Linux:** Terminal - -### Basis commando's - -| Commando | Wat het doet | Voorbeeld | -|----------|--------------|-----------| -| `pwd` | Toon huidige map | `pwd` β†’ `/Users/tim/projects` | -| `ls` | Toon bestanden in map | `ls` β†’ toont bestanden | -| `cd` | Ga naar map | `cd projects` | -| `cd ..` | Ga map omhoog | `cd ..` | -| `mkdir` | Maak nieuwe map | `mkdir mijn-project` | - -### Oefening - -Open je terminal en probeer: - -```bash -# 1. Waar ben ik? -pwd - -# 2. Wat staat hier? -ls - -# 3. Ga naar Documents (of andere map) -cd Documents - -# 4. Waar ben ik nu? -pwd - -# 5. Ga terug -cd .. -``` - ---- - -## 5. Controleer alles - -Voer deze commando's uit en controleer de output: - -```bash -# Node.js versie (moet 20.x.x zijn) -node -v - -# npm versie (moet 10.x.x zijn) -npm -v - -# Git versie -git --version - -# SSH verbinding met GitHub -ssh -T git@github.com -``` - -**Alles werkt?** βœ… Je bent klaar voor Les 2! - ---- - -## Troubleshooting - -### "command not found: nvm" -- Sluit terminal en open opnieuw -- Mac: voeg toe aan `~/.zshrc`: `export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"` - -### "Permission denied" bij npm install -- Gebruik `sudo npm install -g ...` (Mac/Linux) -- Of fix npm permissies: https://docs.npmjs.com/resolving-eacces-permissions-errors - -### "Permission denied (publickey)" bij GitHub -- Je SSH key is niet goed toegevoegd -- Controleer: `ls ~/.ssh/` - je moet `id_ed25519` en `id_ed25519.pub` zien -- Voeg opnieuw toe aan GitHub - -### Windows: Git Bash vs PowerShell -- **Git Bash:** Unix-achtige commando's (`ls`, `cat`, etc.) -- **PowerShell:** Windows commando's -- Tip: Gebruik Git Bash voor consistentie met Mac/Linux - ---- - -## Hulp nodig? - -- Stuur bericht via Teams -- Kom 15 minuten eerder naar Les 2 voor hulp - -**Tot Les 2!** πŸš€ diff --git a/v2/Les03-Cursor-Basics/00-START-HERE.md b/v2/Les03-Cursor-Basics/00-START-HERE.md deleted file mode 100644 index f203a59..0000000 --- a/v2/Les03-Cursor-Basics/00-START-HERE.md +++ /dev/null @@ -1,260 +0,0 @@ -# DevDash Debug Challenge Series - START HERE - -Welcome to the **DevDash Debug Challenge Series** - a comprehensive three-tier debugging curriculum for Next.js developers. - -## Quick Links - -### Debug Challenge Tiers - -1. **Easy Tier** (5 errors - 30-45 min) - - File: `les3-debug-challenge.zip` - - Answer Key: `Les03-Debug-Challenge-ANTWOORDEN.md` - - Topics: Basic syntax, typos, simple logic - - **Start here if:** You're new to debugging or Next.js - -2. **Hard Tier** (12 errors - 60-90 min) - - File: `les3-debug-challenge-hard.zip` - - Answer Key: `Les03-Debug-Challenge-Hard-ANTWOORDEN.md` - - Topics: Next.js patterns, React hooks, TypeScript - - **Start here if:** You're comfortable with React basics - -3. **Super Hard Tier** (16-18 errors - 2-3 hours) ← YOU ARE HERE - - File: `les3-debug-challenge-super-hard.zip` - - Answer Key: `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` - - Topics: Advanced patterns, circular deps, complex interactions - - **Start here if:** You're ready to master advanced concepts - -## What is DevDash? - -DevDash is a fully-featured developer portfolio and dashboard application built with: -- **Next.js 14** (App Router) -- **React 18** (with hooks) -- **TypeScript 5** -- **Tailwind CSS 3** - -The application includes: -- Homepage with Hero section, Features grid, Testimonials carousel -- Blog system with dynamic routes -- Dashboard with analytics and user profile -- API routes for data -- Middleware for authentication -- Theme context for dark/light mode - -## Getting Started (5 minutes) - -### Option 1: Start the Super Hard Challenge - -```bash -# 1. Extract the project -unzip les3-debug-challenge-super-hard.zip -cd les3-debug-challenge-super-hard - -# 2. Install dependencies -npm install - -# 3. Run the development server -npm run dev - -# 4. Open browser to http://localhost:3000 -# (You'll see errors - that's the point!) - -# 5. Read the errors and start debugging -# Open Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md for help -``` - -### Option 2: Read First, Debug Later - -1. Start with `DEBUG-CHALLENGES-OVERVIEW.md` for context -2. Read `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` to understand all errors -3. Then extract and debug the project yourself - -## What Will You Learn? - -By fixing all 16-18 errors, you'll master: - -### Advanced React Concepts -- Advanced hook patterns (useState, useEffect, useContext) -- Closure patterns and stale closures -- Key prop best practices -- Memory leak prevention -- Context API provider patterns - -### Next.js 14 Deep Dive -- Client vs Server components -- Middleware patterns -- Dynamic routes with metadata -- API routes and type safety -- App Router structure - -### TypeScript Mastery -- Type safety in React -- Optional chaining -- API contracts -- Type narrowing - -### Advanced Debugging Skills -- Circular dependency detection -- Performance issue identification -- Memory leak detection -- State management debugging - -## The 6 Error Categories - -| Category | Count | Examples | -|----------|-------|----------| -| **Blocking Errors** | 3 | Missing deps, wrong imports, circular refs | -| **Next.js Specific** | 4 | Client/Server components, metadata, middleware | -| **Logic Errors** | 3 | Filter bugs, stale closures, key issues | -| **TypeScript Errors** | 2 | Type mismatches, optional chaining | -| **React Patterns** | 3 | Cleanup, context, prop drilling | -| **Styling Errors** | 2-3 | Custom CSS, inline styles | - -## How to Use the Answer Key - -The answer key (`Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`) provides: - -1. **Error Location** - Exact file and line -2. **Issue Description** - What's wrong and why -3. **Symptom** - What breaks or misbehaves -4. **Multiple Solutions** - Usually 2-3 ways to fix -5. **Severity Level** - CRITICAL, HIGH, MEDIUM, LOW -6. **Code Examples** - Before/after comparisons - -**Pro Tip:** Try to fix each error without looking at the answer first. Use the answer key to verify your fix or for hints. - -## Debugging Strategy - -**Recommended approach:** - -1. Start the dev server: `npm run dev` -2. Read the console errors -3. Fix blocking errors first (they prevent the app from running) -4. Then tackle Next.js errors (build-time failures) -5. Next, logic errors (functionality breaks) -6. Then TypeScript errors (feature-specific) -7. Finally, React patterns and styling - -## Testing Your Fixes - -After each fix, verify: - -```bash -# Check for build errors -npm run build - -# Check for runtime errors -npm run dev - -# Run in your browser -# Click around and check if features work -``` - -## Validation Checklist - -Your fixes are complete when: -- [ ] `npm install` succeeds -- [ ] `npm run build` succeeds (no errors) -- [ ] `npm run dev` starts without errors -- [ ] Homepage loads with all sections -- [ ] Blog shows 2 published posts -- [ ] Blog post detail pages work -- [ ] Dashboard page loads -- [ ] Stats load from API -- [ ] No console errors or warnings -- [ ] No TypeScript errors - -## Need Help? - -### If you're stuck on an error: -1. Read the error message carefully -2. Check `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` for that error number -3. Try the "Symptom" section to understand what's broken -4. Look at the code example to see the fix - -### If something doesn't make sense: -- Refer to the "Key Concepts Covered" section in the answer key -- Research the specific Next.js/React feature being tested -- Try different solutions to understand the principle - -### If you need a broader context: -- Read `DEBUG-CHALLENGES-OVERVIEW.md` for the big picture -- Review the "Learning Outcomes" section - -## File Structure - -``` -les3-debug-challenge-super-hard/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ app/ # Next.js pages and API routes -β”‚ β”œβ”€β”€ components/ # React components (many with errors) -β”‚ β”œβ”€β”€ context/ # Theme context -β”‚ β”œβ”€β”€ lib/ # Utilities and types -β”‚ └── data/ # Static blog data -β”œβ”€β”€ package.json # Dependencies (some missing!) -β”œβ”€β”€ tsconfig.json # TypeScript config -β”œβ”€β”€ tailwind.config.ts # Tailwind setup -β”œβ”€β”€ middleware.ts # Auth simulation (has error) -└── README.md # Project overview -``` - -## Time Estimate - -- **Easy errors (1-3):** 20-30 min -- **Next.js errors (4-7):** 30-40 min -- **Logic errors (8-10):** 25-35 min -- **TypeScript errors (11-12):** 20-30 min -- **React patterns (13-15):** 30-45 min -- **Styling (16-18):** 20-30 min -- **Testing:** 15-20 min - -**Total:** 2-3 hours for the complete challenge - -## Progression Path - -After this challenge, you'll be ready for: -- Building production Next.js applications -- Advanced React patterns -- Complex state management -- Performance optimization -- Team code reviews - -## Questions About Specific Errors? - -Each error is thoroughly explained in `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`. The document includes: -- Exact error location -- Root cause explanation -- Visual code examples -- Multiple solution options -- Why the error matters -- Best practices to avoid it - -## Next Steps - -1. **Now:** Extract the zip and run `npm install` -2. **Then:** Start the dev server with `npm run dev` -3. **When stuck:** Check the answer key -4. **After fixing all:** Review your solutions and make sure you understand each error -5. **Finally:** Try building a similar project from scratch using these patterns - ---- - -## Document Reference - -| Document | Purpose | -|----------|---------| -| **00-START-HERE.md** | This file - quick start guide | -| **DEBUG-CHALLENGES-OVERVIEW.md** | Big picture overview of all 3 tiers | -| **Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md** | Complete answer key with explanations | -| **les3-debug-challenge-super-hard.zip** | The actual buggy project | - ---- - -**Good luck debugging! You've got this.** - -Remember: The goal isn't just to fix errors, but to understand *why* they happened and how to prevent them in the future. - ---- - -Created: February 2026 -Level: Super Hard (16-18 errors) -Time: 2-3 hours diff --git a/v2/Les03-Cursor-Basics/DEBUG-CHALLENGES-OVERVIEW.md b/v2/Les03-Cursor-Basics/DEBUG-CHALLENGES-OVERVIEW.md deleted file mode 100644 index 21e7b5a..0000000 --- a/v2/Les03-Cursor-Basics/DEBUG-CHALLENGES-OVERVIEW.md +++ /dev/null @@ -1,222 +0,0 @@ -# DevDash Debug Challenge Series - Complete Overview - -This folder contains a complete series of 3 debug challenges of increasing difficulty, all featuring the "DevDash" developer portfolio and dashboard application. - -## Challenge Tiers - -### 1. Easy Tier (5 Errors) -- **File:** `les3-debug-challenge.zip` -- **Answer Key:** `Les03-Debug-Challenge-ANTWOORDEN.md` -- **Target:** Beginners, first-time debuggers -- **Topics:** Basic syntax errors, typos, simple logic mistakes - -### 2. Hard Tier (12 Errors) -- **File:** `les3-debug-challenge-hard.zip` -- **Answer Key:** `Les03-Debug-Challenge-Hard-ANTWOORDEN.md` -- **Target:** Intermediate developers -- **Topics:** Next.js patterns, React hooks, TypeScript basics, state management - -### 3. Super Hard Tier (16-18 Errors) -- **File:** `les3-debug-challenge-super-hard.zip` -- **Answer Key:** `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` -- **Target:** Advanced developers, mastery level -- **Topics:** Circular dependencies, advanced React patterns, middleware, complex type issues - -## Challenge Structure - -All challenges are based on the same application concept: **DevDash** - a developer portfolio and dashboard application built with Next.js 14, React 18, TypeScript, and Tailwind CSS. - -### Features Included -- **Homepage:** Hero section, features showcase, testimonials carousel -- **Blog:** Dynamic routes with MDX-style content -- **Dashboard:** Analytics, stats grid, user profile, charts placeholder -- **API Routes:** `/api/stats`, `/api/posts` -- **Middleware:** Authentication simulation -- **Context:** Theme management (dark/light mode) - -### Tech Stack -- Next.js 14 (App Router) -- React 18 -- TypeScript 5 -- Tailwind CSS 3 -- PostCSS - -## Error Categories by Tier - -### Easy (5 errors) -1. Wrong variable name -2. Missing closing tag -3. Typo in function name -4. Array method confusion -5. Logic error in condition - -### Hard (12 errors) -- 3 Blocking errors (missing deps, wrong imports) -- 3 Next.js specific errors (layout, metadata, routing) -- 2 Logic errors -- 2 TypeScript errors -- 2 React pattern errors - -### Super Hard (16-18 errors) -- 3 Blocking errors (circular dependencies, missing deps) -- 4 Next.js specific errors (client/server components, middleware) -- 3 Logic errors (filters, closures, keys) -- 2 TypeScript errors (type mismatches, optional chaining) -- 3 React pattern errors (cleanup, context, props) -- 2-3 Styling errors (CSS and inline styles) - -## Getting Started - -Each zip file contains a complete Next.js project. To work on a challenge: - -```bash -# Extract the zip -unzip les3-debug-challenge-super-hard.zip - -# Navigate to project -cd les3-debug-challenge-super-hard - -# Install dependencies -npm install - -# Run development server -npm run dev - -# Open browser to http://localhost:3000 -``` - -## Using Answer Keys - -Each answer key document provides: -- Exact location of each error -- Code snippet showing the problem -- Explanation of why it's an error -- The symptom (what breaks) -- One or more solutions -- Difficulty level (CRITICAL, HIGH, MEDIUM, LOW) - -## Progression Path - -**Suggested learning path:** - -1. Start with **Easy Tier** to get familiar with the codebase and debugging process -2. Move to **Hard Tier** to tackle more complex Next.js and React patterns -3. Finish with **Super Hard Tier** for mastery of advanced debugging concepts - -## Timeline Recommendations - -| Tier | Recommended Time | Notes | -|------|-----------------|-------| -| Easy | 30-45 minutes | Quick confidence builder | -| Hard | 60-90 minutes | Requires research and testing | -| Super Hard | 120-180 minutes | Deep analysis and multiple fixes needed | - -## Learning Outcomes - -After completing all three tiers, students will have mastered: - -### Easy Tier -- Basic syntax debugging -- Reading error messages -- Variable naming conventions -- Simple conditional logic - -### Hard Tier -- Next.js App Router concepts -- React hooks and their rules -- TypeScript type safety basics -- Component composition -- Data flow and props - -### Super Hard Tier -- Circular dependencies and module loading -- Advanced React patterns (closures, cleanup, context) -- Client vs Server components -- Middleware patterns -- Type-safe API contracts -- Performance patterns (keys, memoization) -- Styling best practices with Tailwind - -## Key Concepts Covered - -### JavaScript/TypeScript -- Module imports and circular dependencies -- Type safety and optional chaining -- Closures and their pitfalls -- Array methods and filtering -- Object destructuring - -### React -- Hooks (useState, useEffect, useContext) -- Rules of hooks and dependencies -- Client vs Server components -- Context API and providers -- Key props and list rendering -- Memory leaks and cleanup - -### Next.js -- App Router structure -- API routes -- Middleware -- Metadata and SEO -- Dynamic routes -- Image optimization - -### CSS/Styling -- Tailwind CSS utilities -- Inline styles vs. classes -- CSS custom properties -- Responsive design - -## Files Included - -Each zip contains: -``` -les3-debug-challenge-super-hard/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ app/ # Next.js pages and layouts -β”‚ β”œβ”€β”€ components/ # React components -β”‚ β”œβ”€β”€ context/ # React Context -β”‚ β”œβ”€β”€ lib/ # Utilities and types -β”‚ └── data/ # Static data -β”œβ”€β”€ package.json -β”œβ”€β”€ tsconfig.json -β”œβ”€β”€ tailwind.config.ts -β”œβ”€β”€ next.config.js -β”œβ”€β”€ postcss.config.js -β”œβ”€β”€ middleware.ts -└── README.md -``` - -## Grading Rubric - -Students can self-assess: -- All 5/12/16-18 errors identified and documented -- Code builds successfully (`npm run build`) -- No runtime errors or warnings -- Application functionality works as intended -- Code follows best practices (Tailwind usage, React patterns, etc.) -- Understanding demonstrated in commit messages or documentation - -## Support for Instructors - -Each answer key provides: -- Clear explanation of each error -- Multiple solution options where applicable -- Severity levels to help prioritize feedback -- Testing checklist to verify all fixes -- Common student mistakes and misconceptions - -## File Sizes - -| File | Size | Components | -|------|------|-----------| -| les3-debug-challenge.zip | 12 KB | 10 files | -| les3-debug-challenge-hard.zip | 19 KB | 32 files | -| les3-debug-challenge-super-hard.zip | 26 KB | 38 files | - ---- - -**Created:** February 2026 -**Updated:** February 25, 2026 -**Version:** 3.0 (Complete Series) diff --git a/v2/Les03-Cursor-Basics/Les03-Bijlage-A-Lesopdracht.md b/v2/Les03-Cursor-Basics/Les03-Bijlage-A-Lesopdracht.md deleted file mode 100644 index 1a41fa7..0000000 --- a/v2/Les03-Cursor-Basics/Les03-Bijlage-A-Lesopdracht.md +++ /dev/null @@ -1,493 +0,0 @@ -# Les 3: Cursor Basics - Lesopdracht (In-Class Assignment) - -## Doelstelling - -In deze les leer je hoe je **Cursor** effectief gebruikt om snel professionele componenten te bouwen. Je maakt een **NIEUW** Next.js project met Cursor Composer, genereert AI-gegenereerde cursorrules, en oefent met Tab Completion, Chat, Inline Edit en Composer. - -**Eindresultaat:** Een werkende Next.js applicatie met Hero component en Feature Cards, volledig gebouwd met Cursor. - ---- - -## Timing - -**Totaal: 85 minuten** -- Setup & Git: 10 min -- Skills & cursorrules: 10 min -- Hero Component (Composer): 20 min -- Feature Cards (Composer): 20 min -- Inline Edit styling: 15 min -- Git commit: 5 min -- Buffer: 5 min - ---- - -## Voorbereiding - -### Benodigde software -- **Cursor** (gedownload en geΓ―nstalleerd van cursor.com) -- **Node.js** 18+ en npm -- **Git** (voor versiecontrole) -- **Terminal/Command Prompt** - ---- - -## Stap 1: Project aanmaken met Git (10 minuten) - -### Stap 1.1: Nieuwe folder en Git init - -Open Terminal/Command Prompt op je computer (niet in Cursor nog): - -```bash -mkdir les3-cursor-project -cd les3-cursor-project -git init -``` - -### Stap 1.2: Open in Cursor - -```bash -cursor . -``` - -Dit opent je lege project in Cursor. - -**Checkpoint 1:** Project is open in Cursor met lege folder. - -### Stap 1.3: Create Next.js app in Cursor Terminal - -Open de Terminal in Cursor met **Ctrl+`** (backtick): - -```bash -npx create-next-app@latest . --typescript --tailwind --app -``` - -Beantwoord alle vragen met **Yes (y)**: -- TypeScript? β†’ **Yes** -- ESLint? β†’ **Yes** -- Tailwind CSS? β†’ **Yes** -- `src/` directory? β†’ **No** -- App Router? β†’ **Yes** -- Default import alias? β†’ **Yes** - -Dit duurt ~2-3 minuten. Wacht tot het klaar is. - -**Checkpoint 2:** Next.js project is aangemaakt. Je ziet `app/`, `components/`, `package.json`, etc. in de file tree. - ---- - -## Stap 2: Cursor Skills & Docs (10 minuten) - -### Stap 2.1: Open Settings - -Klik op het **βš™οΈ Settings icoon** linksonder in Cursor. - -### Stap 2.2: Activeer Docs - -Ga naar **Features** β†’ **Docs** - -Voeg deze drie documenten toe (zoek en click Add): -1. **Next.js** (officiΓ«le docs) -2. **React** (officiΓ«le docs) -3. **Tailwind CSS** (officiΓ«le docs) - -Dit helpt Cursor veel beter code te genereren omdat het toegang heeft tot de juiste documentatie. - -### Stap 2.3: Controleer Tab Completion - -Ga naar **Features** β†’ **Completion** -- Zorg dat **Tab Completion** is ingeschakeld (dit is gratis!) - -**Checkpoint 3:** Alle drie docs zijn zichtbaar in je Docs sidebar rechts. Tab Completion is enabled. - ---- - -## Stap 3: Generate cursorrules met Chat (1 request) - -### Stap 3.1: Open Cursor Chat - -Druk **Ctrl+L** (Windows/Linux) of **Cmd+L** (Mac). - -Een chat panel opent aan de zijkant. - -### Stap 3.2: Prompt voor .cursorrules - -Typ deze prompt en druk Enter: - -``` -Je bent een expert Next.js developer. Genereer een .cursorrules bestand -voor een Next.js project met TypeScript, Tailwind CSS, en App Router. - -Het bestand moet guidelines bevatten voor: -- Functional components en React hooks -- TypeScript best practices -- Tailwind CSS utilities (nooit inline styles) -- Bestandsnaamgeving (PascalCase voor components, camelCase voor utils) -- Folder structuur (/app, /components, /lib) -- Comments in het Nederlands -- Keine `any` types - -Output het volledige .cursorrules bestand klaar om te copy-pasten. -``` - -Wacht op Cursor's antwoord. - -### Stap 3.3: Copy de output - -Cursor geeft je het complete .cursorrules bestand. - -Selecteer alle code (Ctrl+A in de chat output) en kopieer het. - -### Stap 3.4: Maak .cursorrules bestand - -1. Rechtsklik op de root folder in de left sidebar -2. Klik "New File" -3. Noem het `.cursorrules` (met punt aan het begin!) -4. Plak de content - -Save het bestand (Ctrl+S). - -**Checkpoint 4:** `.cursorrules` bestand bestaat in je project root met goede guidelines. - -**Request Budget: 1 van ~7 used** - ---- - -## Stap 4: Hero Component met Composer (20 minuten) - -### Stap 4.1: Maak component file - -1. Rechtsklik op de `app` folder -2. "New File" -3. Noem het `app/components/Hero.tsx` - -(Dit maakt automatisch de `components` subfolder) - -### Stap 4.2: Open Composer - -Met het Hero.tsx file open, druk **Ctrl+Shift+I** (Windows/Linux) of **Cmd+Shift+I** (Mac). - -De Composer panel opent. - -### Stap 4.3: Geef instructie aan Composer - -In Composer, typ: - -``` -Maak een Hero component (TypeScript, React functional component) met: -- Grote h1 heading: "Welkom bij Cursor" -- Subheading met beschrijvende tekst: "Bouw websites snel met AI" -- Een CTA button: "Aan de slag" (link naar #features) -- Responsive design: Tailwind CSS (md: breakpoint) -- Flexbox voor verticale centrering -- Dark mode compatible (dark: classes) -- Gradient background (blauw naar paars) -- Geen external dependencies - -Geef alleen de component code, geen imports of exports statements voor buiten. -``` - -Cursor genereert het component. - -### Stap 4.4: Accept of refine - -Review de getoonde code. Klik **Accept All** (groene knop) om het in je bestand in te voegen. - -**Checkpoint 5:** Hero.tsx file bevat een werkend Hero component met juiste structuur. - -**Request Budget: 2 van ~7 used** - ---- - -## Stap 5: Feature Cards Component met Composer (20 minuten) - -### Stap 5.1: Maak component file - -1. Rechtsklik op `app` folder -2. "New File" -3. Noem het `app/components/FeatureCards.tsx` - -### Stap 5.2: Open Composer - -Druk **Ctrl+Shift+I** in dit nieuwe bestand. - -### Stap 5.3: Composer prompt - -``` -Maak een FeatureCards component (TypeScript, React) dat: -- Een grid van 3 feature cards toont -- Responsive: 1 kolom op mobile, 3 kolommen op desktop (Tailwind grid) -- Elke card heeft: - * Een icon uit lucide-react (Package, Zap, Layers) - * Een h3 title - * Een description text -- Hover effect: transform scale-105 en shadow toename -- Cards hebben border en padding (Tailwind) -- Dark mode ready -- lucide-react is geΓ―mporteerd bovenaan - -Geef alleen de component code. -``` - -Composer genereert het component. - -### Stap 5.4: Accept - -Klik **Accept All**. - -### Stap 5.5: Install lucide-react - -In je Cursor Terminal (Ctrl+`): - -```bash -npm install lucide-react -``` - -Wacht tot npm klaar is. - -**Checkpoint 6:** FeatureCards.tsx werkt, lucide-react is geΓ―nstalleerd. - -**Request Budget: 3 van ~7 used** - ---- - -## Stap 6: Integreer in Homepage (5 minuten) - -### Stap 6.1: Open `app/page.tsx` - -Dit is je homepage. - -### Stap 6.2: Update de content - -Vervang de bestaande content (of voeg toe aan het bestand) met: - -```typescript -import Hero from './components/Hero'; -import FeatureCards from './components/FeatureCards'; - -export default function Home() { - return ( -
- -
-
-

Features

- -
-
-
- ); -} -``` - -Save (Ctrl+S). - -### Stap 6.3: Test in browser - -In Cursor Terminal: - -```bash -npm run dev -``` - -Open `http://localhost:3000` in je browser. - -Je ziet nu: Hero β†’ Features section met 3 cards. - -**Checkpoint 7:** Website draait lokaal en ziet er netjes uit. - ---- - -## Stap 7: Styling Refinement met Inline Edit (15 minuten) - -Nu gebruiken we **Inline Edit** (Ctrl+K) voor kleine styling tweaks. - -### Stap 7.1: Hero button styling - -1. Open `app/components/Hero.tsx` -2. Klik ergens in de button JSX -3. Druk **Ctrl+K** -4. Type: - -``` -Voeg een hover effect toe aan de button: -- Scale slightly bigger on hover -- Change background color smoothly -- Add smooth transition -``` - -5. Review de suggestie, klik Accept - -### Stap 7.2: Card shadows verbeteren - -1. Open `app/components/FeatureCards.tsx` -2. Selecteer een card div -3. Druk **Ctrl+K** -4. Type: - -``` -Voeg een mooier shadow effect toe aan de cards: -- Larger shadow on hover -- Smooth transition -- Better depth perception -``` - -5. Accept - -### Stap 7.3: Typography verbeteren - -1. Open `app/page.tsx` -2. Druk **Ctrl+K** -3. Type: - -``` -Maak de "Features" heading groter en mooier: -- Groter font size -- Betere kleur gradient -``` - -5. Accept - -Refresh je browser (F5) om de changes te zien. - -**Checkpoint 8:** Styling ziet er gepolijst uit, hover effects werken. - -**Request Budget: 4-5 van ~7 used** - ---- - -## Stap 8: Git Commit (5 minuten) - -### Stap 8.1: Open Source Control - -Druk **Ctrl+Shift+G** in Cursor. - -Source Control panel opent links. - -### Stap 8.2: Stage alle bestanden - -Klik op het **+** icoon bij "Changes" of druk **Ctrl+Shift+A**. - -Alle bestanden worden staged (groen). - -### Stap 8.3: Write commit message - -In het message veld, typ: - -``` -feat: Build Hero and FeatureCards components with Cursor Composer -``` - -### Stap 8.4: Commit - -Druk **Ctrl+Enter** of klik de βœ“ button. - -Je commit is gemaakt! - -**Checkpoint 9:** Git commit is succesvol (geen errors). - ---- - -## Keyboard Shortcuts Reference - -| Actie | Windows | Mac | -|-------|---------|-----| -| **Cursor Chat** | Ctrl+L | Cmd+L | -| **Composer** | Ctrl+Shift+I | Cmd+Shift+I | -| **Inline Edit (quick fix)** | Ctrl+K | Cmd+K | -| **Terminal** | Ctrl+` | Cmd+` | -| **Command Palette** | Ctrl+Shift+P | Cmd+Shift+P | -| **Source Control** | Ctrl+Shift+G | Cmd+Shift+G | -| **Save** | Ctrl+S | Cmd+S | -| **Tab Completion** | Tab | Tab | - ---- - -## Request Budget Tracking - -Je hebt **~7 requests** beschikbaar in deze les: - -| Fase | Type | Requests | -|------|------|----------| -| cursorrules generatie | Chat | 1 | -| Hero Component | Composer | 1 | -| FeatureCards Component | Composer | 1 | -| Button styling tweak | Inline Edit | 0.5 | -| Card styling tweak | Inline Edit | 0.5 | -| Typography tweak | Inline Edit | 0.5 | -| Buffer/extra | - | 2.5 | - -**πŸ’‘ Pro tip:** Gebruik **Tab Completion** (gratis!) voor: -- Imports toevoegen -- Props schrijven -- Repetitieve code (zelfde classNames herhalen) - ---- - -## Troubleshooting - -### Probleem: "Module not found: lucide-react" -**Oplossing:** -```bash -npm install lucide-react -npm run dev -``` -(Herstart je dev server na npm install) - -### Probleem: Composer stelt rare dingen voor -**Oplossing:** Wees specifieker in je prompt. Verwijs naar bestaande code: -- "Voeg lucide-react icons toe" (voldoende duidelijk) -- "Zorg dat het met TypeScript werkt" (specifieer) - -### Probleem: Inline Edit accepteert niet -**Oplossing:** Klik de groene βœ“ knop of druk Ctrl+Enter - -### Probleem: `npm run dev` geeft error -**Stappen:** -1. Controleer dat je in de juiste folder bent: `pwd` -2. Controleer node versie: `node --version` (moet 18+ zijn) -3. Verwijder en herinstalleer dependencies: - ```bash - rm -rf node_modules package-lock.json - npm install - npm run dev - ``` - -### Probleem: Port 3000 al in gebruik -```bash -npm run dev -- -p 3001 -# Openen: http://localhost:3001 -``` - -### Probleem: Git werkt niet -**Controleer:** -- Ben je in de juiste folder? `pwd` moet je project folder zijn -- Bestaat `.git` folder? `ls -la .git` -- Zijn je bestanden getracked? `git status` - ---- - -## Checklist - Vinkje alles af voor je klaar bent - -- [ ] Git init en create-next-app uitgevoerd -- [ ] Cursor Skills (Next.js, React, Tailwind) geactiveerd -- [ ] `.cursorrules` bestand gegenereerd en in root geplaatst -- [ ] `app/components/Hero.tsx` aangemaakt met Composer -- [ ] `app/components/FeatureCards.tsx` aangemaakt met Composer -- [ ] lucide-react geΓ―nstalleerd via npm -- [ ] Componenten geΓ―mporteerd in `app/page.tsx` -- [ ] `npm run dev` draait zonder errors -- [ ] Website ziet er goed uit op http://localhost:3000 -- [ ] Styling verfijnd met Inline Edit (minimaal 2 aanpassingen) -- [ ] Git commit gemaakt met duidelijke message -- [ ] Alle bestanden zijn saved (geen rode stippen in sidebar) - ---- - -## Volgende stappen - -**Voor volgende les:** -- Zorg dat `npm run dev` nog werkt -- Download het huiswerk zip bestand van Teams (les3-debug-challenge.zip) -- Voorkom dat je project files verwijdert/opslaanslecht - -**Veel succes! πŸš€** diff --git a/v2/Les03-Cursor-Basics/Les03-Bijlage-B-Huiswerkopdracht.md b/v2/Les03-Cursor-Basics/Les03-Bijlage-B-Huiswerkopdracht.md deleted file mode 100644 index d325a14..0000000 --- a/v2/Les03-Cursor-Basics/Les03-Bijlage-B-Huiswerkopdracht.md +++ /dev/null @@ -1,375 +0,0 @@ -# Les 3: Cursor Basics - Huiswerkopdracht (Homework - DEBUG CHALLENGE) - -**Deadline:** VΓ³Γ³r Les 4 - -**Type:** Debug Challenge - Je krijgt een kapot Next.js project en moet het repareren met Cursor. - ---- - -## Introductie - -Dit huiswerk is anders dan gewoon code schrijven. Je krijgt een **intentioneel kapot** Next.js project dat je met Cursor moet debuggen. - -Je leert: -- Errors opsporen met Cursor -- De Terminal gebruiken voor error messages -- Inline styles naar Tailwind CSS converteren -- Missing imports en dependencies vinden -- ESLint warnings beheren - -**Geschatte tijd:** 1.5-2 uur - ---- - -## Stap 0: Download het Zip-bestand - -Download `les3-debug-challenge.zip` van Teams. - -Unzip het bestand: -```bash -unzip les3-debug-challenge.zip -cd les3-debug-challenge -``` - -Open het in Cursor: -```bash -cursor . -``` - ---- - -## Wat je tegenkomt - -Het project `les3-debug-challenge` bevat deze INTENTIONELE ERRORS: - -### Error Type 1: Missing Dependencies - -**Wat:** `lucide-react` wordt gebruikt in `components/HeroIcon.tsx`, maar staat niet in `package.json`. - -**Hoe te vinden:** -- Open Terminal in Cursor (Ctrl+`) -- Run: `npm run dev` -- Je ziet: `Error: Module not found: lucide-react` - -**Hoe te fixen:** -```bash -npm install lucide-react -``` - -### Error Type 2: Syntax Errors (typos, missing closing tags) - -**Wat:** Een component heeft een vergeten closing JSX tag of import typo. - -**Bijv. in `app/page.tsx`:** -```tsx - -``` - -Of in imports: -```tsx -import { Button } from '@/componenst/Button' // typo: componenst -``` - -**Hoe te vinden:** -- Terminal errors: `SyntaxError` of `Cannot find module` -- Cursor geeft rode underlines in editor -- Gebruik Cursor Chat (Ctrl+L) om de error uit te leggen - -**Hoe te fixen:** -1. Kijk naar de regel number in error -2. Open dat bestand -3. Controleer imports en JSX syntax -4. Fix typos -5. Save en test: `npm run dev` - -### Error Type 3: Inline Styles (moeten naar Tailwind) - -**Wat:** Een component gebruikt `style={{...}}` ipv Tailwind classes. - -**Bijv. in `components/Card.tsx`:** -```tsx -
- Content -
-``` - -**Hoe te fixen:** Gebruik Cursor **Inline Edit (Ctrl+K)** - -1. Open het bestand -2. Selecteer de `style={{...}}` block -3. Druk **Ctrl+K** -4. Type: - -``` -Converteer deze inline styles naar Tailwind CSS classes. -Verwijder de style prop en voeg className toe. -``` - -5. Cursor converteerd het naar: - -```tsx -
- Content -
-``` - -6. Accept en verify - -**Tabel: Inline Styles β†’ Tailwind Mapping** -| Inline Style | Tailwind Class | -|--------------|----------------| -| `padding: '20px'` | `p-5` (4px Γ— 5) | -| `backgroundColor: '#f0f0f0'` | `bg-gray-100` | -| `borderRadius: '8px'` | `rounded-lg` | -| `border: '1px solid #ccc'` | `border border-gray-300` | -| `marginBottom: '16px'` | `mb-4` | -| `display: 'flex'` | `flex` | -| `justifyContent: 'center'` | `justify-center` | - -### Error Type 4: Missing Imports - -**Wat:** Een component wordt gebruikt maar niet geΓ―mporteerd. - -**Bijv. in `app/page.tsx`:** -```tsx -export default function Home() { - return // FeatureCards is niet geΓ―mporteerd! -} -``` - -**Hoe te vinden:** -- Cursor geeft rode underline: "Cannot find name 'FeatureCards'" -- Terminal error bij `npm run dev` - -**Hoe te fixen:** -- Voeg import bovenaan toe: - -```tsx -import FeatureCards from '@/components/FeatureCards'; -``` - -Of gebruik Cursor's **Quick Fix** (Ctrl+. op de fout). - ---- - -## Debug-aanpak stap-voor-stap - -### Stap 1: Installeer eerst alles - -```bash -npm install -``` - -### Stap 2: Start dev server - -```bash -npm run dev -``` - -Lees ALLE errors van boven naar beneden. - -### Stap 3: Fix errors één voor één - -**Beginnen bij:** -1. **npm install errors** (missing packages) -2. **Syntax errors** (ESLint, typos) -3. **Runtime errors** (module not found, missing imports) -4. **Styling issues** (inline styles) - -### Stap 4: Use Cursor Chat voor moeilijke errors - -Druk **Ctrl+L** en beschrijf de error: - -``` -Ik krijg deze error: -Error: Cannot find module 'lucide-react' - -Hoe fix ik dit? -``` - -Cursor helpt je. - -### Stap 5: Verify in browser - -Na elke fix: -1. Zorg dat `npm run dev` draait zonder errors -2. Open http://localhost:3000 -3. Check visueel dat alles werkt -4. Geen rode errors in console (F12 β†’ Console tab) - ---- - -## Checklist van errors om op te lossen - -Zorg dat je ALLE deze punten fixt: - -- [ ] **npm install errors** - Alle missing dependencies geΓ―nstalleerd -- [ ] **Syntax errors** - Alle closing tags toegevoegd, imports gecorrigeerd -- [ ] **Missing imports** - Alle componenten die gebruikt worden zijn geΓ―mporteerd -- [ ] **Inline styles** - Minimaal 3 inline styles β†’ Tailwind conversions -- [ ] **ESLint warnings** - Geen rode/oranje squiggly lines meer -- [ ] **Browser console** - Geen rood errors (F12) -- [ ] **Visual check** - Website ziet er correct uit - ---- - -## Tips per error-type - -### Voor Missing Dependencies - -```bash -# Check welke packages ontbreken -npm install - -# Of installeer handmatig -npm install [package-name] -``` - -### Voor Syntax Errors - -- **Controleer imports:** `import X from '@/components/X'` (check spelling) -- **Controleer JSX closing:** Elke `
` moet `
` hebben -- **Controleer quotes:** Tailwind classes moeten in dubbele quotes: `className="..."` - -### Voor Inline Styles β†’ Tailwind - -Gebruik altijd Inline Edit (Ctrl+K): -1. Select de `style={{...}}` code -2. Ctrl+K -3. Type: "Convert to Tailwind classes" -4. Accept - -Tailwind Cheat Sheet: -- Padding: `p-2`, `p-4`, `p-8` (2, 4, 8 Γ— 4px) -- Margin: `m-2`, `mb-4`, `mt-2` -- Background: `bg-white`, `bg-gray-100`, `bg-blue-500` -- Border: `border`, `border-2`, `border-gray-300` -- Radius: `rounded`, `rounded-lg`, `rounded-full` -- Display: `flex`, `grid`, `block` -- Colors: `text-white`, `bg-red-500`, `border-blue-300` - -### Voor Missing Imports - -Cursor's Quick Fix (Ctrl+.) werkt vaak: -1. Klik op de rode error -2. Druk Ctrl+. -3. "Add import" - -Of manual: -```tsx -import ComponentNaam from '@/components/ComponentNaam'; -``` - ---- - -## Submitting Your Work - -### Voor Les 4: - -Maak een **GitHub commit** en **push** het: - -```bash -git add . -git commit -m "fix: debug all errors and convert styles to Tailwind" -git push -``` - -Post in Teams: -1. **GitHub link** naar je debugged project -2. **Screenshot** van je werkende website (http://localhost:3000) -3. **Korte beschrijving:** Welke errors heb je gerepareerd? - ``` - - Fixed missing lucide-react dependency - - Fixed typo in import statement (componenst β†’ components) - - Converted 5 inline styles to Tailwind classes - - Added missing Hero import to page.tsx - - Verified no ESLint errors - ``` - -### Checklist voor inlevering - -- [ ] Alle npm/syntax/import errors zijn fixed -- [ ] `npm run dev` draait zonder errors -- [ ] http://localhost:3000 toont de website correct -- [ ] Browser console (F12) toont geen errors -- [ ] Minimaal 3 inline styles zijn converted naar Tailwind -- [ ] Git commit is gemaakt -- [ ] GitHub link gepost in Teams - ---- - -## Bonus: Maak je eigen component (optioneel) - -Na alle errors te hebben opgelost, voeg je **een eigen component** toe: - -IdeeΓ«n: -- Een "Newsletter Signup" sectie -- Een "Call to Action" button bar -- Een "Stats" sectiie met cijfers -- Een "Testimonials" carousel -- Een "FAQ" sectie met accordions - -**Hoe:** -1. Open Composer (Ctrl+Shift+I) -2. Prompt: - -``` -Maak een [ComponentNaam] component met: -- [beschrijving] -- Tailwind CSS styling -- Responsive design -- TypeScript types - -Voeg het toe aan /components en importeer het in /app/page.tsx -``` - -3. Accept en test - -**Bonus punten!** - ---- - -## Troubleshooting Debug Errors - -### Error: "SyntaxError: Unexpected token" -**Oorzaak:** Ontbrekende haakje, quote, of JSX closing tag -**Fix:** Kijk naar de regel in de editor, controleer haakjes - -### Error: "Cannot find module 'X'" -**Oorzaak:** Package niet geΓ―nstalleerd OF typo in import -**Fix:** -- Check spelling in import statement -- Of: `npm install X` - -### Error: "Cannot find name 'Component'" -**Oorzaak:** Component niet geΓ―mporteerd -**Fix:** Voeg import toe boven het bestand - -### Error in Browser Console (F12) -**Oplossing:** Zeg dit tegen Cursor Chat: -``` -Ik zie deze error in de browser console: -[paste de error] - -Wat betekent het en hoe fix ik het? -``` - ---- - -## Veel Succes! - -Je gaat leren: -- βœ… Debuggen met Cursor -- βœ… Terminal errors lezen -- βœ… Inline styles naar Tailwind converteren -- βœ… Imports en dependencies beheren -- βœ… Website testen in browser - -Dit zijn essentiΓ«le vaardigheden als je echt met code werkt! - -**Deadline:** VΓ³Γ³r Les 4 πŸš€ diff --git a/v2/Les03-Cursor-Basics/Les03-Debug-Challenge-ANTWOORDEN.md b/v2/Les03-Cursor-Basics/Les03-Debug-Challenge-ANTWOORDEN.md deleted file mode 100644 index 0e3be1d..0000000 --- a/v2/Les03-Cursor-Basics/Les03-Debug-Challenge-ANTWOORDEN.md +++ /dev/null @@ -1,175 +0,0 @@ -# Debug Challenge - Antwoordenblad (ALLEEN VOOR TIM) - -**⚠️ NIET DELEN MET STUDENTEN** - ---- - -## Overzicht Fouten - -Het project bevat **4 typen fouten**, verspreid over **8 bestanden**. - ---- - -## FOUT 1: Missing Dependency β€” `lucide-react` - -**Bestanden:** `components/Hero.tsx`, `components/FeatureCards.tsx` -**Symptoom:** `npm install` werkt, maar `npm run dev` geeft: -``` -Module not found: Can't resolve 'lucide-react' -``` - -**Oorzaak:** `lucide-react` wordt geΓ―mporteerd in Hero.tsx en FeatureCards.tsx, maar staat NIET in package.json. - -**Fix:** -```bash -npm install lucide-react -``` - -**Leerdoel:** Studenten leren dat je imports moet matchen met dependencies in package.json. - ---- - -## FOUT 2: Typo in Import Path - -**Bestand:** `app/page.tsx` (regel 1) -**Symptoom:** -``` -Module not found: Can't resolve '@/componenst/Hero' -``` - -**Oorzaak:** Typo: `@/componenst/Hero` i.p.v. `@/components/Hero` - -**Fix:** -```tsx -// FOUT: -import Hero from "@/componenst/Hero"; - -// CORRECT: -import Hero from "@/components/Hero"; -``` - -**Leerdoel:** Lezen van error messages, herkennen van typos in import paden. - ---- - -## FOUT 3: Missing Import β€” `TestimonialSection` - -**Bestand:** `app/page.tsx` (regel 9) -**Symptoom:** -``` -ReferenceError: TestimonialSection is not defined -``` - -**Oorzaak:** `` wordt gebruikt in page.tsx maar is niet geΓ―mporteerd. - -**Fix:** Voeg import toe bovenaan page.tsx: -```tsx -import TestimonialSection from "@/components/TestimonialSection"; -``` - -**Leerdoel:** Begrijpen dat elk component geΓ―mporteerd moet worden. - ---- - -## FOUT 4: Missing Imports in layout.tsx β€” `Navbar` en `Footer` - -**Bestand:** `app/layout.tsx` (regels 17-18) -**Symptoom:** -``` -ReferenceError: Navbar is not defined -ReferenceError: Footer is not defined -``` - -**Oorzaak:** `` en `