From a76527d839d2042bf4874905275b27bc66f9aba3 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 26 Jan 2025 00:07:56 -0500 Subject: [PATCH] implement final login flow --- app/account/login/code/page.tsx | 119 ++++++++++++++++++++++++++++++++ app/account/login/page.tsx | 107 +++++++++++++++++++--------- bun.lockb | Bin 203184 -> 203900 bytes components/ui/alert.tsx | 59 ++++++++++++++++ components/ui/card.tsx | 76 ++++++++++++++++++++ components/ui/input.tsx | 22 ++++++ package.json | 10 +-- 7 files changed, 357 insertions(+), 36 deletions(-) create mode 100644 app/account/login/code/page.tsx create mode 100644 components/ui/alert.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/input.tsx diff --git a/app/account/login/code/page.tsx b/app/account/login/code/page.tsx new file mode 100644 index 0000000..05f075e --- /dev/null +++ b/app/account/login/code/page.tsx @@ -0,0 +1,119 @@ +"use client" + +import { useState, useEffect } from "react" +import { useRouter } from "next/navigation" +import { Key } from "lucide-react" +import Cookies from "js-cookie" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" + +export default function Login() { + const [magicCode, setMagicCode] = useState("") + const [errorMessage, setErrorMessage] = useState("") + const [isLoading, setIsLoading] = useState(false) + const router = useRouter() + + useEffect(() => { + const checkStatus = async () => { + setErrorMessage("") + setIsLoading(true) + + try { + const response = await fetch('http://localhost:3001/auth/validateStageTwo', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email: Cookies.get("email"), stageTwoKey: Cookies.get("stageTwoKey") }), + }) + + const data = await response.json() + + if (!response.ok || !data.success) { + router.push("/account/login") + } + } catch (error) { + console.error("There was a problem with checking the status of your request:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } + } + + checkStatus() + }, [magicCode, router]) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsLoading(true) + setErrorMessage("") + + try { + const response = await fetch('http://localhost:3001/auth/validateMagicCode', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email: Cookies.get("email"), stageTwoKey: Cookies.get("stageTwoKey"), magicCode }), + }) + + const data = await response.json() + + if (response.ok && data.success) { + Cookies.set("key", data.key) + Cookies.remove("email") + Cookies.remove("stageTwoKey") + router.push("/account/dashboard") + } else { + setErrorMessage(data.error || "An unknown error occurred.") + } + } catch (error) { + console.error("There was a problem with checking the magic code:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } + } + + return ( +
+ + + Enter Your Magic Code + Check your email for the code we sent you. + + +
+
+ setMagicCode(e.target.value)} + required + /> +
+ + {errorMessage && ( + + {errorMessage} + + )} +
+
+
+
+ ) +} + diff --git a/app/account/login/page.tsx b/app/account/login/page.tsx index b6e9e65..d366aa3 100644 --- a/app/account/login/page.tsx +++ b/app/account/login/page.tsx @@ -2,49 +2,92 @@ import Link from "next/link" import { useState } from "react" -import { TextField, Button, Flex, Text, Card } from "@radix-ui/themes" +import { useRouter } from "next/navigation" import { Mail } from "lucide-react" +import Cookies from "js-cookie" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" export default function Login() { const [email, setEmail] = useState("") + const [errorMessage, setErrorMessage] = useState("") + const [isLoading, setIsLoading] = useState(false) + const router = useRouter() - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + setIsLoading(true) + setErrorMessage("") + + try { + const response = await fetch('http://localhost:3001/auth/login', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email }), + }) + + const data = await response.json() + + if (response.ok && data.success) { + Cookies.set("stageTwoKey", data.stageTwoKey) + Cookies.set("email", email) + router.push("/account/login/code") + } else { + setErrorMessage(data.error || "An unknown error occurred.") + } + } catch (error) { + console.error("There was a problem with requesting a magic code:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } } return ( - - -
- - Log in to your account - - - ) => setEmail(e.target.value)} - required - > - - - - - - - I don't have an account - - -
+ {errorMessage && ( + + {errorMessage} + + )} +
+ + I don't have an account + +
+ +
-
+ ) } diff --git a/bun.lockb b/bun.lockb index cd18a27be1b0471ae70d01755b92f375ad1b63ca..aaa1ec432960fd2fb5ff5774a2c7a4508afe0db0 100755 GIT binary patch delta 39346 zcmeHwd0ZA%`~J+xqYR3>0sa&e0u77?sH#rmiyf2K4;E6GdQoj zR^`T)DhopbT~geu=e+P`!0JyzI`95`W7Mx-jSG15@rGYr>*N{rY~#H@p7ySw@bl@S z0e<;z`&3g=lqu;W%+&YzuB9jisgRW+V>LzbfLse%6>?;5ZhG!WMG1h;d@rB^9dc?~ zRwgU(1YaGxo6Mh+ojVa_l=Uu(;tu`;#Sy|tYknF%=DKm0|$7E)Y zP{zYr*82(gd?qe}pk-S@P@jP>_GrX}w2|q#it-zD^0BCmhRlQH@mFO2m5|i4(kF~b zZ>=bHgZ-;mp722B^i*f5XG2G_;F`N)TvVIr=TRx^z2ETH{M186d(9*NwHlW36UU~H zWB-;*{dq{*`aWEYWP#Zubbc}#s0)2GB)uO7Pq6a=Qf`H;2EAGxt9(w{#IeEmrB9tQ zHe=*il+Vab&r63KjPfX#Uyy~`%uZ#D%g9N~&Fzf?)EgldSWzA%jZTz&4Nt3K7f2d- z6O#E4K+<_@y)3x`Iy)az&kCy$NE(`kAJ7UV#}(t_SnMnL*P_ku*v3vM>G}X2uBgxFUFZC^tQ8a{2^C8JV4#4WZ<>v<7xB zBt7Zd%F0&>z2bnMh36=Lkn%7j$9H%r@?mW#*l(mcJn|=F0x{cbsFLzUNIHIGW^Nvm z%Hsh0Hxm*gThKY&>gRWm>{oV#m48CosEoYeNf|%1Q50BQFch)@h6@&&5(BXx6)-@LA_GTmmy}mKT8?$WaaR02bjpt*+4DCcnLo3W z<(VQ#HoOIr16i$u)sZ(kTLW_ok^}V|Bt7YZi?cRz)Xe2VR%*Nl1EV45SyNkCdmShu)U*ImmnuWSEZx9%T&d60R_p#pC`kjY1MQ^D3C_$OnKluvWM*V#OjO#XTF2L;0vdSGc-qxNpFYT9pE7p3dc@=n zwi->(&6$vn$_tUV7V=&MPovXDW=~+Rrh}(Z^@dn{hEYTJ^N$;9MF-Bh!J{+N#-!al znI2HwhFOF8yOE-M;F&)TLkF2(&4G6A!(THjC#m3iteCDQuvvm*%56A?_x{yb6ECc3`w|eyQL~Ht2o@B*l{alM5lbr*fA?C6t z1mk2kwv*)jCs;%A5hOiffaeMS5Of+=07-ocWIl=1DOQgP5Fj*!Cxx1j6JY?4t5dD9 zya}F`kATjhsSKS4Pq^QTsMRo(6~|1ohHMWc%YTdxk;ndRLi(8Wya_Of{MKnoXLC%} zA!7{?t7U-@NY1MHsE~$u&#)SL1d@77xQ%1E2+|F5wh>sxFTW7gQmY4+5mNz@-T4ir zxJ>>8$yOTS%tQSZn9t*Tp|d;F(On*YMf!0XB=b#y70g#hnmt_VJ*0KvkXRk_3w)5E ze=0%JfLpS_IY{>Z8%Q>=8Q?WpXL6DMShOYa1oQ!y5j)%c z->&K~4P8VCKN)JaBS1E;#hD8%8# zfrEJyqFtfT7R&N?p~V+#H>Fn5>_@Co*x0F0)2#|M<6wg*eY4b1ReH2528<1BMvW#> zY6qjRiBp|z6g6?`J8{Hv&!War`fX^msghYmB9;>x;9x0z3A8rWv5+YBfT1^YYE`Nj z-I~R#qm9C5PW?#?A}w?nDe&!KLl1DO&5cBSPcsSwocdPGf_6BL-up#qdR4>7&F!kFh5tG|O3EknzJQMzkQMTvw4zoJ|5(5z!A zjiU4ipwUq-bb$Jfkr?dMJZl+qgX8tb5X^neO6ZO6pmjAXVX=73UsmE`q)d%=l?t2~ z6r~m!MIlbz2P+2awZ?KNGEbgh|*hQ6~J!R8eQ!X4`Xiqczq8@W~pGLw2D&wjiOMeJ`(E^Iaf0p zn_*TL52&6AjYD729PC%6W)1yWXdF2gqXzuftDd6tH*4mMdLA0P?rP4jpQHvyz%4DY z96@W34ipBDejOSc)r}g6 zkr*sr)G!&~&MDB?fH{QvCTQKvay9&;Fnx<6o$8B*9_7?;)VGenLG_~?o&DfLV_t(; zRTxD$x*K{3%Be;Q+WTE<4%2as8`v?4a8kz_MbS?E1#n@=U(0mESI}e(qbD`7)rz%` zsr`+j7^k|@(Azj2&PIwdz;JI9>sV5pI)hX{lk3n}QTmvv#l@-PNTrxuOcO=vX{P2N zmBf_#nUUDm={VO+QN|ea0%INh18}8krrts-%}mt`#E#xfO)X9xMrw%3MKs5+Ax!BD znKC_q7;~J0Hqc0J8|&zZ^}4T_5=iwnQzww>Wu{_U*r`IKdYIgo#i=I2iqhTW#uuly z7pHVMyfcsLLy_W$V!~p*ddetl@6^^me$lU15&tLh2qf^^%!N4vy6$ zotEWh@Ab!^(QJy~;nyobKXw78QD20VHs*9TysWi4=4hbVmqGu6Ol!kK-Dsm@sF}hL>T8)Qw(3`C zmUkAVMComMTAHkW`$1Le~kL4?~pXv)nVwzL`3LKULnuZ*W zhFCeQO~epr7*pBz4bWO!8vOn(G@M*GnjNEEhuVi2H%ZVq8LZpdmP{F;@E+Md?mVC4ID0Ka^(Y!&yzQGr~SkA-WJVMac0uIQq>DS^Xnu?Vwqw zil!qiFIt<%nb0^E@DYsP0*&1>mvH?&G^-rPB5{3fkIE@ZB3NxMRDx(PB zXAM2e>2SFZWexYNSjQZshMK8gkiv2Zu1zN6m#G4z+L>GH9hrt(R=lHb7FRgtGxlW1>-;qolL1y843BonmYXwu3{sX8xmkJID9UltQRAI@ zz42B7OqkZuM&Wo~YZi@nI_gbgYxC;I>cf$WH(TQ{u7t+r2yMf8pFrcFVtK$Ox8h_w z=fUhKJpmf0o660QE}&VC<*_5stfh%{`c1L>!^xZu%{;s7>x+2~#?R0gGFDhL$+OQt zEHe!n&H%w}U6HU{vj}wUsTg;2k)MpzKz7IR2~yYxVtY~le)=C5!un%Kahh2pdmLJ^ zagH9-5J~3g=rg3+m?t6k2TJDX11*}XvExys%p8tgNOd(!xldtJiLkE0gwa^*Ho~iMUr87vdQfEG^;z%qu((Ce=t)!nRu)8YoS4#vLxSX}7ovz_Y8M&fLzR&#+d zw?Vw#cL9vHx`5NlW@tUkE;tri1CKRyI#z6GTv0K((IFv^Su6cfXfZg3E1vODdhJEU z=Mdx^2+bO9E*Kl3F&wS)>`6nP>r{6diF2Krw_(hk8?W~>tZ_DXPmTr9q78R!wm(9u zwQ1H>q<9v@Mg!FcKVe5FFTlnd*!1f=z;T!nzeA#3p^Sh6yRlyywAebAnS-Y;F!Xs& zed7|_LI#tIu&yu>712@n>8=Plo&pz#I;t6q+DEBB8;SFsy5CZJ;JMezgT|R|ozGr{ zCa2S!Xcs7OD3*i%%j{U^uJ;*exTFY=)vqyCY+~;xt@x^CuA)ysW5v)gj-Nn_W?nBPF?U^P)lQ>uSWGy^Fv8}0PYQ#1QLoX zFwE2}$qd7#PDwotU;`t8O2EAUKP4r-nFTOE&gG`ehUDju675&Sfl20JOR|9}Qm15v z_XDgDCouC3&RavXkOY_PzOG6N;`pQK(=lK&atlsN-% zfw&;$Z;<>@Mgl>|%mzXrX+SHfhekRL+w zL&*w@r2H6?6(5xPAxPHq1tdR|On!+k*82@4J@x}hDOq3s&mhSCf-hQl0g?r-K)OL{ zsFe*>f}~yplAdS;$sV+V?V&7fMof5q@DrE4rW6lU%oO8iRvI8g`@#X zAZtOcgJgx9CI22|HRuN+y&z9QatMBfWO)tUF%5;J0kt4$urIrL7g>dCQy?-lLWb^; zw5+!*K#97QzL4}#Kgs`@WWN5g+(4QCPbJz{n1Kxqk_9MP(O@a>k#dMUPRZj#Az5y? zqb1w3B*! zNPZ}p?1V4&tQ#crB}>^0(#1ITl!v*=8w8ED4Tj{0lFg(+n(H?tSFy>E_+PmnUo`&# zDt{qa?{r!2K}hN|W%*eyh$Na4?N_{yb>sbxS{A;9q z9uog6FXD^&H%b0=NOs^&7eo>ZZUe!FcS7QSWf#6!(Qe6qLJ}WJ8n9m;|5VD)r92GD z`i@EY10?=ee!&+TJTB!)NY;Obk;KG#Nc^u{lKN#SuSt0ml09%i?X0LGBq+rVk_J^~ zt5TA$CXd&W(nIF+gk(qSL-LbvC(4tosG-bASsQvJB>6UyFHh2-Sb3b19gK%0@079~ zBn-({IwQdkCC_N7knG`LNCw?lNLFy4l;a@zp=5>GQm16T36Pw;Ga=d2Igt2YnJ0Av zlI5N-CB}a-kK#kg152cCOCIBHgy3-xnGS}ep)CP^N=kZ&D+WL1NtTNMD)hovc^7t| zO`a9C1=uquz)wj@gW3U$`ui?@C_h@of3; z^&R&V`0zT9$$zi!{=L4l_V)i?-~D@iSKf6UR}1o7KL0$}&cZk!m^E~jf@ zbv3~MjaB**w?TDs>Ymy^Y@kbW-D>YY^K(Y)wI9TtKCF9u(r(Sll}{fWdVNjX7Z-H> z>hUiNzn}Y16KB+CC;E9C!M}JKXMRpn*BCLs3^0!W;%=cV01n1Zrp^n&PY5yz_S)V+7s@^kP}IGNOj$b0mi_S?uPrxB-}kt zJvqQ|J>_m}hqlSkPYp1(K+8Inq`qdn1#RqUcfLT~AWKHwv$#e>c!SXg?Y;H_$(5D{ds=*7FHyOK+loHbb?K>Hy)Y zLfsDag3wjyTcC?96~smH7KyPM2wx4vZz4kj;aLI1J`$IOR|OEeNldQ*;;JYjk>>&; z*agIOG1Ub`fCI!a5;sMl1H@qx^Bf>Twh~p$y zR081;CrB)H1(ECuqLNtV3ZknH;wA}QBkh(~4y+?G+(CF&1+kBWhw!QjVmFED zRY7=(A`*GkKm=C<;Vq_C0})Ui#4!@SBCtA$!zAWa2jM4j}cw6GVi_@C4!M z1!5nGDBx1YbmemK*)epo?5{V+w55yG` z8~i{diK`^mHUKfC0f=O=t^tUF{vh1_LG%=<{vcc%g4j+XMd%GdY$1`=5JVsG7KyQq zK=?KS(NAPF0^!*h#6A)OgjZvH?Itn3F^E)AL?W*Vh~Oq528*dpKm;@eag4+e5!e*O zVG{G2f*2-_keJg9L|iivX`-+hi0A+iXGn|`F##ZslUNY|B3+yyu{01wav+E?Vp$-F zuFXN*B#|K!n}fJQVncHf_lc_{)&_wX5(FYktP27$umuSB79g@kY6}pq!63Gi7%%i- z5L-xO1%t>HZ;==q0>U>0#3Yds0>ZN;h$>I7VW+2n+>rn8dtL5c%Q=i8-x7#I*+TpeSq&B03Dj84?ePm@p8>NvsG1@vt~S zVre*t64$Xb{^;EE0M&h%F?tqCptqEfQm6K={UhSS&JPKzOzRv5$lhUTr|^CNaGYh-IRP zL|!b2;8+mL#nf030c}AXBe6mRwgqvR#Jsj3R*54d=EQ-Biv#hrD2xLU9S`CRi8Uf7 z9>j4HE8;;sCr*%9>I9MO1o47c<^<8T9f+GG)``S+Ag++u&<@0Uah1f{_8^9|2eCn{ zYY$>z2N3QZK)ftcJAiQQ2x2>lO+xPoVhf3^jv!tWZ;=?A0Kzu`#2X?b0fc8K5c^1M z7G9k|>?Se26NtA&5sAFcAc8xC*e0fS1`*H!@vMPL^Yhe^!q0^(h9gv6Y#AmX}$ z*eMFTf{0E8afZbEA|?^UaS|&MLF^JING$CJBDougkHoTWAiDCjag#)mNaSqB=^g&S z>Y?rxIGw{E-9ZfL4q~5J*B!*bWDxGjAoh#YWOaagKx|4@d-hY6Th(r1tIC`I_Q&PD z?r}xOM%Bn&u<}Cv8bdm->C^PX1BopYQqFxbzq=ARb=SlF&z?W<*?`Ek_YA6h{>U6J z_eWMNY4Vd88hGC%@&3}Eb^NVqkO=6Zc2%#5@jcYn9K0V=RiyM(J@9Cli*Emw>`~!A zUTwL#*GTmzRp9%y=-yl1p}E!)Rr{z*^d)u78-fL^H*e^p8k+i;Xx|@|E%L`bkIkqo zTVrh^GgWnURKbk}lf153^~qXudaL2$p5bb^+JEz+;pz*TW92=#QOe?%RnwSz<-a2^ zadZ20)uVDI65Z%eP0#_R{7@m4tRQq7kngN+&aOh$8gBk5Dg@wtq2fS=DK zM^C+svaFa-r?6}dU_8M5pG%GplRO0Q!>3m0QQj?k810)M{_{&L%zJc?Sd?-^atJnM zw&cE&g?TsYF@P0+E%WitRw2Or$0X;8^jyjD@fiHCrXj zBmkX&&OjF+5$MKe6Bt1afF?jgfYH+ws1Nu743b6wZ@@(YQ9yHm-Dj})1MD_~gwN14 z02%_#fTlnI&=?2;0)ZBQ58w-Q;FHAK*CfBCrnF2)qoe2VMeR0X6`Pyfwgc z!1KV0|Oiw|DotjDlh=Ripam&FrQED4%q|f0`Q(|43Gd|eN}ifHw3`5$Nb9yWh5{P zNC!p(V}P+h2Jjo;hZ*1wcmSTj4RAVSEslQ;BwT=cz)c*e3fTy#4|oH$f#1Q`guDiM z8E_!o0H_Guf_@utL%JGJ8K?vJ0CfQcs1B%r23!So9jJh`FQ1jG1h@hXfu=wcpfOMd z;1iE$kohd|9`FvZ2lxos1?&cjfDeI>fxW;sUFU1AqnK`0NuOn4Ja;LwYE{2f3C20^p-Ud_=(us0VlhK7cRK5$FW8L)q$( zF#ylLQ9v}{4&ZT!ib_uxM7qfMRQ1j;0QVrkhX*o&EFc{i4U7T0(hi_EkOH&^mIBLw zCxKib2gn8{0uz9HflPqU>P-UptY2+_4ybJ6E@a$i{@+&fa4jcv+0>jZkKC#vs zHiiKa0H1PM1pEm8C*Wt`IB)_u37iAY1HS?nf$P8x;3jYzP*GL`DgZ7(CBO}+!lzr@ zk*Ee#2WkK{fm%RqpbpRv;A1pf0X|akKIB)xQQ&JJ5k~g``UA^>$-sj^0WclN0(ee2 z3h?Cj6?8s|*A8XR^68dyz*E37paYNqbVH_oz)avFU>5K&@CYy);E9H3L!SMPLLZAV ze3Yp(&;=0S<^xXvnb5}p*}y~q4;7gYDougR&%@VL;2C7({S7=zXP(n{K{i8g`vQY; zEE(7W>;wNY@Djks*Z9;5PsKAN=0QFNJPIrV3W2#&=P~l0yxWLR&KXq<*r)mj2SBH5 zc-G|Rf}0M`zB52&0I{Q-26#Gnc5~e4>c|Q;Xz(fEB*4?db7IqBwN~2mNHUDLyav1qya;RnUINyOokvv<^+j?_q(eZ_Qx$ovNTV)p-KQxE03!y@bgZMW^Et*qj8pj5kN zn`V{nJkD}#?iKO$S85{%sh7-}-D#oi>{m^yhTRx_Y!(#TkE-6CK7nc<+JiLRZ@aoY zyXZSMWc$hViP?fLyJDNpqU;W+WE4$a76WR@X$-5{nx68|1Km;c8V3V z8y^51(D%$nyh`@$J)z{*-?*JoK2Xj@cX+}T+<2dw73?Aq)$IknB{YBm;8o|DV#P&tma4Y3E; zZo!UdD-_HTX7A`^+diu+O}+qS+gEZ7>>425{fUJs|gd++6wr+=O}o+(;&%EH|UI0B%~jQN^w{-`sI> z_sK0Sce1yT(Hpo4+yH!l>%cYOGVnWa4xm97A%6od0lxwlfb#%(>XcW2p1@UryV=_m zklf8y;1-(OVeVop0^G@R=gKWFx5KoOX>Om%6WkuBq7r*Q&aE*9O)i{XB3P?JLhAaWBvdV8v;WGl3M~Szry24v-%RJOexptOkYy zLx6jLRlrK107wM}11o^1fPTP8;1QrNKqCa?61rgkuo$=xcml`-W&!sDg8&0~2zVS= z1S|mN1M>hH{+NjSS@p<&7|HR#7@#*$(&qlqX+s}??X$^Y0PUnK*^fG~jkYttZ7a?&WCP=XEP(#V0GNlz z*=MHNFnz{!Kh9+Kj-DmKbXmy;IEuD6=wvoNLGt#fvtstt9>rXwSr`w0#e{hsofuic;Vlyi8=Jr=dKKtKXMLt@D zT3!>UPOHfg@5&6*JY%nA%7? z?@<(ERxsW%H7V`F7b{I0T7^Wxxj4e2-L4d6I?k=>TV3rSUOS_Hsjd_?k~A-GPBdDT z=aX^Z`#D2@HLVH_3FEcfC8Ri8(x zNEp-_KY)n#$e^ATgCTrwAYWDFYn8o!*&>hW5wt4;&S|B%X=33y)ic6=&xcP~&FCfv z8m~Z(h!AuR+aCOk!#?%A`u-blyNq}eN5ZW(V#Tp@7@4)A#(9VrM5ps=GM>sXD5Rb1 z#dVnLZNJRpxx&?rn)i4nUeo4STNl)DZ~N^ZZY#e3YF4Em!;rbPHRu;b@~^0PhFJb946tAN5qELN zrajdo@LbZ+C@WvA*v}01TR@t(?0VzM;M(+m6k3IaokdCy%}ebseChwgBI+V+Fh?Uf z!hRRXzADLw2EB1EP*d4D1JZsg$m~jiE0fo}`hwYu(2y9`cZL;R6O}Hh%gY(T85|gl z;CZqAk~xB3UP6QSi^{*jO#5Xf{f3{ueQjad?`D6|CbkK}i41bUz3tazEU&ZqWW9&q zz7P3Yg@m!%tzzD9sPBET^EbHXkf`=MN-Y+DphF2P90SU54pf#mmU2RjVWR zTvihr^{H$A!`sDgIywKr^qi-@{P;6Ai5Y>BY$QUjps#ttzk}xGW53yC<%^G>s$1vN zhmkMZ3hNr4qTmXqg#B)lo0;k>ISmuOLJm1JJ3P&+gMus9rj31X;1kbj>N+qUaLfU* z4{dm#gUb0fD)s&MR#sc>(A2NMU_mapDb%ZQOg%4Lnn4Y9yKt&`=h`lsS`iGFuQW(b z=^icLd&X8#VO{qU)FMP;EM+mFRl7nQ)h4~4afQ~ z{H`HGxvr5d(E(T!vXBo?ESOq|c$(Teu?@oKy07Jm?{Dqf7!dBh6jmVM8D-V$i}Tl1 z&q({_E>kbOarmh7?HR~nMsK)czvg9l;>gMk3;S_Q7;)%E8tEX$2w^=t-f2~$SvG4|hq*+7r4=4*(4s7#<_WsEFF2zmQ?}+(wX5CHt zXDwXr9O)~Lv2pvQGDogo{?P5rj}PC;FiX_934g5+K@^)sr<-b6#6h%F9k#yn+-tkK zZg?FJ=)rB_Tq?fD5$qNU2EXDuFg;^drg_A4%0;mOHH4Hsqg7X{Ho}i8C}O`DXW@Si zf8(|AgC}@IT)L^Y(JD3-t!|-%_KRb_+gaz6CKzh38q z<*jyInd#aDIa=ei5Y0VT4L8&qC3aTRf*K9MR1ZeIe+(S^$ai8&1sG};3v&Gp`As_( zR?zBs+b`{@zwGPp^0w7EVfNp&!^-ZKhv>m1#LIZZcfOjZkNrlMK1&unH72dFm%N)=r(NcsC69bQ`_R6e4gtq7 z)KG+i$liczeL;78fe4vvSgcDMGD?Hs=d( z2yea~hi8l~m9mEBBI4=ci7e=TAcTX7srTG!Kk@F_*H@z?HBoc+xepvCy(8{ zdB;-wRX-JveDl*O=689n`Py z9BB|E-mU}(+3y|tb=g<%ef;9c3s&Xq&_FT14m@MOpvYzZw#^@V9PPu=u*Ptq$f^zd zUlPMYA$AMD5Qv||cvsC!Q`?BgT;YudZOjK;3i^-!S$#O`>J`fX#HZqHBX%H%I#fK( zT4sn<%^+5ZMb#m8h?bQheh}?-h|;scHP(EFqMX@aA2F+}$0-z~+ng2a#p>O&xY;G- zbSNG!%OAGA(!zggS{XCsMEa+-NK>rJiVe2os#I-_?e6?>@vx7$I>nAUh)cV}a_-2G z*UE;i6|bsggI3QeW+3dfKON5havaMzOSUM@D;}5s(uZX#FQZBi%Wy2^(MGo4?(Wf& z#e#~*$97Sv+Ww32k*l!1#uYn4+FYKq|3)h=9Yh1HN?O@ysMbOBs&!}0FSS(LCj3RH zSj&`*7`61WQF{9SQ`g9lm20RSlXisIk!yF!o*=T#@*MRy2DjWuDISD73yXdF{u{ox z4J#QpcJa8s4!1WuTDxAahX;1_bqwXz8-EDF4(DUh(i02lU1pB-gKYJCu@`wYc^<9ZcUIq$mED;TcDMg&8<}ENJuS@pkMl1+E;KR(*HBx;nR>9jNcejf z3$1gwXzPtY{!aAs*1{s}H}gepue5oaPtT9cZBZDnHtcu!#XZt<_ok)Y2bxFB>kv8g z_J-+}xPY4Et_xSPDL&X9T6v^ayr!1fm;7N-l_$~iLh^6KkG*o+4*egix!VHsC-dfS z7-<_7-C+AB}-ypxOHKdt<4u6owa)Na&X?(80wyS~W{O?g8R|JPI6 ze%+&Y^*&c-y}D+OwJpPpR_w9(f(HN z|LMZ^Hv+$G0GB=2*n2iR1WH|P*w>o>Wz?4S|9=r~|CdX*z18|d9F*#FarFOhOPmY~ zpSzz}%8tz6+4$eh^zs77?nv=ulINzq6f|Pzdz=U-|^!hvq$2dzh@sYJYMsRu-_|v>8nO{o?iE()69X!@aRV$ z>%Lh0Q0?tQ=Ul(V5#IboM+b!Cg+7A0tP`Gfg-@v{@94-2n$5ZgpD#fRd-cId(>v7Dl} z2<;4US?q_VRqQKHw$oZ`-TR9A?X}jp6`ax@M~8{~+M8Caf+i1o`q(cOk6se|g#Y_B zanET+s}S75!^5s-aZm3?_}RA_9G&{^S;te~oCz@d7|I*m)~!i(wTR^%O8af${Zsb# zNUQ!juNUQwz9js~Y6q>m)-F}N(*YNl2gR2iv}AAlh2dF&^+&w;#E71n$_E?ZRaETk zi0fDTUExcgX!pUPEdxDKV}x~A*z{CTqgnS@H&N}Eho`;gzpKkquiua{92wDxJaH5* z#ll5H9w_)PL1g&-XQD8PQFU^XAPb>d2 zVww@W6V00e7m9!jMik(WqY(_twMrw!txgyaYq+#pBSrVl7^haFL_ueSnS8By!Rad_ zjt#5j8gBN&{FP!a9wXt?7N@E-@TxqsNoyRTtI)ssVqa%8AOoOKcz3}}nKRm&DT77N zE^wp$_VBUiUO(46d}1I<#z!>^fOJ&hxt;@xhVuh#uOF+2%R!{hK(<+ItyJ_L_Q5lR4pDpmYdxuN6=Y4mpt@WYUzER?PJc?7STIPz7 z?zp9O_r@%@%HA~olio;AN#nfjSJ&sh-EaLytr~8FV2YXRgys3a=^uaV(V1cTfpO+z z?*%KxiDU%uR^f}MR?2z2#D0dM)MG6^9dfM4s=7WmZsTFUij`oETp7COh}AtXc!Nay z@d%6Y;vGg9wfDv+pS{NB;@-Tws3u%wx(ydw2dE+mHDu89~BpRVme+zhNj3cQK;k4 z)O2xh8pM22BVF_G4O8qFl_%`F5#!uDey%K!6W@6;uNNAyUsa#IY}WB->+kqpX21eG zQS3wp&40p@LAaGQHik%>;r(n|A?@a&dp`D&}Ke>R%1I!#) za?{1T{bAG;Q3KB^;n(5?qaYTDMZC@Yv{(<}ZNGAUXpisuoJa|1Zx#;a&Df8`&LPNm zNQ4eYKKq63)m&EoFs!vtfy{?B;Gzibjj_Hd(t5+{I=SM3-uOMeey(UWTJsbedu#Qy zz+5qZ3i@KdE>iQS2Tf1Nwbsen zhaT(r&hvZE$fnW1Y2v~?@OFe!h%9axj3XW*q-MeBpYB*^hy?@Ci)Tc32E;4k!7&iq zgxVMJW54yjQGVyZi>Dj#DHl99#ot1|5k31N-#M|I**_M(gCTB-6(A!kO_cY2f6bdc zaGC#RvlMnHtrQ;|!4ozGIqe=;zau}ks(HkWt?-HBbYBcr59Fwc9G{(>a`?x3J$cWa z(T5g?3%`EY|4cy!oS6$AeD?jCU%AX{D>H=SoFI!jYEIm;dU7X3DatKAfgbwH_Nhzj@BWWv)L*o5q{BrALVGIVWTbvNEfn=#;AY z%6P^GJ3_IX;-DvY%SaG|>Yy+Iux} z^1OdCZKES@;`5(NkD?3f#Djxi)K;+^;!om!kJyh4XjxPrdS|Z5IJIf?EoHD4W~*Z5 zVE9~)uGpHbc{pI3waD1C>`MvNYCx9qzIz|7RZzTr56+P0tn{+o<2^&1AEDJ@d{_%x z>7}wl3?4}+xjYpwJ8BxXpO4J2Cd}@HclU2z5Z^-%Am+a8yw6=0Ff=Ev#=ZF=L*dom z#G?DO+WzMGf$zBBGz(ekA9%8TLtLn{5=0# zNA`VlCu7Te5i$&?q?bjPVc7Q0MvYv7R__^8{mfPuZl<{Nfr}T3mxmRHiJUDCm}ifk zH3V{I*si}bmOKgAV`v}M8qE}+3^#cmMp-(`K5DO5rOqR^*Y}DQX$T6r-m2y_s4Z@% zX$~vm%}}?N$-Ay7k$<_Oh_)lKl*n^|6%ZaS5sDQHp4bIqmYXeEHkO+`Tn48-N3F%a zLO8;xvB&MAn<(rb`8GI&5d6J^wY@gi=nG;6x3lIB zsV+~Ujs@0baF9oiaZjJAXXOeALrcd`sZcBv{j#giil?&?{eNnu3{$pT?!M35@s#UG zvvx0^*vGAfcypxx2R~i9pN|)F8-(DL7ZF;g$3@lgI1gWdKWbyizISYwD^tFU-D>7E zA5S+gGm=&F2QE(2b>m^S_16L7*YR3?`^wz9R`8jGCVBnYw&~i?>Kzt7ZB}IN>KqHi z@CkV2@h&?#Rpm5ufTb^MM32o{;9R8RbUd zd~tzQlp;^l4sOK;kj;|oOl z)h9EI5t zX@%lH30pj5(&qs!ta1Eu>z6G(yX}W3Q=Z(0UpFK9Q*dh~z> z8JSZu#*OMS_|v9mgElqeU!ZZ9CT@jw$b2QKW-5M`Ecpjf{e=H?&CkCR#)0(Fe$$>E zu)cv;FX((i|3Qy_Rd3f0|K=W%HeGYpo72mpXGKSRtmaj{@T{|xPk)?USjTL!l*P6uPYi5@ z?&p(R`G9H&iZVHQn339t?`n#Y*9p8lcwJ3V+`t!tR|FrPnVFn9Tu~}PX1S-Jzz&|1 zls1+M?n15%`6@WePZ*z>g*wU-XGL*^yb*Xg@Nr4w#;4|vP?WQ>U`BH0__33c74I^N z;)4R!z**1+PDLq6nd2vqQsMi|S|p3QDOrN%z%!)SY^3%`JCD71Q{vl;m;j z-%Kel0H>`R;9?~6j2@x$gV8`u$O+)|el2){ov$SM8t_Vxe*bTSiNtv0E$e>&fu|P#b!D)1Ji5Eg=3Z$*LXyeW{0f2Z$p^2vQ0eP?%G1E(1o$w|Yrkj~1PkUS!Abmm&5sV&6U zHXY`Zbb%Zr%_yz>zrwg^2CEizvW>I3)SrShGSvgz50#!ekto3i4)v)6lIbR~7tKS#L2o z2ktubQh$HQ9KaaKAB8?o4#XTN^r?uIUVI9l?*$B=7>IqIyZbm)_bRrGOMdGw<*jHLs0Ly6UA;|~H{5K4XmAv;m znTmvzlO*p5UJLcAgL{DQmmXR!JCZ7SPjENnM}g-uQC|YCl7B24d{gq(l6R0i2%Mhq zlH3KHEf%++8-a`J?L)J)|i;9L|-FOms;&GO5T&tT33$Mnfd(UV-6ZH z%=C%L899pIK(k(Q=7fyoWJL)>xoVgVdCwz*-AEcfK7;*ij0~E&caVwK)IIDzx#tI) z@q+y~08Nid8f~+ZgQ_of?pnmJ{l4QUl!7F(_yBM=RAr2!)CZ3mZ7wZ0!CCJ2 zR8#K&$ShwQ+?zA9j4W^!1=y4MP{ciKMDnnSXhX@Aife;Yk#!;|3)v0w)$ZmJF=v8l zKvcSE;GisXiP#R#_&tze;-kh-fX}d|kIx9m#ZH)VO=f(8iseu+3o<>?3ggX+Ga%Ej z0C39nrCesR*`ol23=OFYyehaa^sxM^+2&Ag08Yaz<(NaW9r-mO`%HoV8Dt64!XKxY zV>WrJsUQbEVuLu`Wh9SE&h~*pR6Gj#9Fo4^Rajq^KZ*JrnkeX_A^WD84Fyj(<*&h6 zFBWcRM?-S;78PpehCwaGy~t+BybjL(ya3L%bTK%aIWohP2ctjC&&IY@9lSC6!~DU} z!{!@-vtE6e!E*1RJk55M@-5`kyffhFes11=B5Bm9vU!p>gmsl6!<|MC zaFxa5DlsF-F025)#kEp-+m<@jv+erx-f`+ly};X{cF_H6JJd)$72m7${Mrut1x-;x zpvy_W8W?W(aZ;47kev0+wZqjkJ=MpduG91J{gqzeMmH_`ol9qLd$72k*Sd|!t>x(rqm)KID4_5!46{gQW-`i-7i*I}<$)@)s4 zd-f5K+8AZiA+3cJZPir2(^Km?>{VS91zv`IXx}~z60I?9UkfR=Ncvt%WsIKG)${8+ z?87k3)TrrK>x8STb$>sH{U=~-Q`3X|!c~7g-_N0r(+iNZ9%E{HW^?^;`;U-Vw=C=0 zn`17sWgBbRr$K6I<^_hU+jRei4(*rn`sjwSYJ#5M&|#l}IHPGc{c7C^-M^7Tt)i#m zdzhZz$YEcD@Nb1&xX345yW*-h_K#J=^i+R`nyTmH`#HV9-(mj&u@Qk{nx5V;-0qD@ zOA8?RhTAeAb=K4BMQQI;($D+H+O7kL(9?XP)LwdO6Nh?O&&PKiy`YK1*0+kHG}Slx zMA=_Pigux8|8VFLeGRTsS=$YJk{nMKUS2*HY&uth^@?*)m2jvz{paFK#J=0_oM(46%kxGubw zqVzQsa~3Ut#ICy-bLxPU;0U;-CMIohybaS+Lmc+iz}TpZ;i_Yh%t%Y05aHrgWI=aB zV4F6hzpIhO2vCf<@Es(!Y1e}g9d&TQK?#P#nL{8^zcG0BWso`>^@4oEF=GqD9O`1- zKipwITieWmduoT<0({^>eN&w%b-G@F+}9unqMoX!WA2@jlFe{i`8rlKB9PQ>dO?K4 z{uHoal&@xZVGkr3y$FY!kfN+SHAXLpbf~j*|CSD0gL?R(M-ONjWt&!%`U0sw2IgO1 zQFUpG+jFjrH zC_|0ZBSop*NDVeHuf~cpkSY6QrVI}t!fc;GO4QS$qil_va0W0n9jTs1>IhQZjZ~ec zR%!xLT@CC%MX3q_iqgfv5{ptV6{W5q)q%P8_HaE%6jK%J)MI*nYlnTS#4t(fhpUS2 z-^O7NX=Y9wBQDhpJs;S+z}lg%V#G$J=B6e{LnHLmwhr}aJ-@BPegrtj1o1R1!X?Oz zI|O^naCwpU^)w~Su7ROu{Hn%kyA%>532}&7e-?o~ z+LTZ)0Ua1pByEH=tVnXjq>&2M6i?J8jG9h&!Zrg!W zJ0o=)sX<1nKTIE_rv*gW&mu+3VGmZ~NCzW;wdNt!g(>@2NO9LPO4u91IbBT&_D_Vw z^}wvX4U(z9T5BV^8b#U0AcYt}>M&BUv}?38Y8WQlrXtnbXyhXQ z@i&p;R@xhF~DC>?dDMD==t3o+TQm1`EIdx zXDqF(2v4;Mx2HkkNTLahda=5c)z#?!O{UDUIOTNI{rfmqokxcw(78MDORtQ*F?sN_T`VRm9g-w6p}%K?IP0}?%8 zY@}*yJwMT5n}&c(;3TzIMz=9#urs9lhikif>7)C{+8e+#Y#2>73Ac}f#ObN%n;RMj zqX7=v31HDi1l8V;r4Bi*cwhw*jSws&63#2Ahnhf4x^BA zA#qtSOKyY2dQST0`4KL%AR0i>hxIqRZtTw5oc?-azgYVR0L}7fRr|faK6+@ZJ#>KO zS1x;*kT4H%Y}$bo3mfaM{j!wI)vp;g2zD2HGs;p zf0V=S7*--;?OBkpEXsZ2b4c`|c}#3J+#Ct>keLjLAz-ZE_EnI~dK`)0q-1(8Y=pJy zVr17X<5weN4y!Bl)X_LCLFS5LxW?Xnq-7V5 zmiE<Z%uvb*No*|1^iXQcq2D*fyiVI6W;b$`(2XyP%QMk;2IY*iT3ydXWkk3%?kL z>9n!>=(Jed`v7oMK~}XiS;*cWDY^tRbU-*}ZNYelb~R0JJR#OzZ=9kGFs;IRy9bhS zBC}T?Z+Q{ps&>`=(;f6wy2HMAf?2~D7hHoDASX@t&v4lGX0gFdwWI8=6HOnX!66YY z5V#(x`sR9uz){8Q#mRO{ak&ZM_N$OM+fWeKXzeCho()3Y6OiQkiBUZWNp=Wxx$0!L z%e^@fDHG>tEP=$BF&C!KAoVSh8f9B6J!2*dl38yv^zDJv)mZ84<#69dk8D$sf?ILI zIDiy3Y40d|

wJVZLB|mqIGC&~^)wd2+~`3f+cvZzEM)LV1+a51YCz0x6?}EdwcI z)3&{hl)2?QPqS=eJjX(k`(t1Pvv4T!j?%uFrZ=7vYimE90CiqOD$W>-xLnh#Sl;ZT?I;GysSAuaPpkYHRQqI!9NjBlP^~4qHCtzQ$qwE>bOx zM!D=oK5h*q17#tkE=C@ulaL0P5&}0|_s?^v3-#1IhyA-*MLGf_T%NGTf@3!flHA9k z{uM~Q4HfA_!|hdOTRqtfj}3&x_%&lnhs4>B`Q9g7J33n*Ju}u;YYqd1ml)%aYGv9o zBfKW(2ct~`P@ymjJCnR(1pwH$ypE1hl9*UF^Ew*(?4WU+>ZGnt^`(UI@9h@Ec zklN@0c~Pp9o;urs-;MHTJG9+T=^N_A+SP?H-Ru^2m=Tb02%8n{f zCLj$I0OF@4cZQ5>NaJ%q&U(QhCs3GC>i(R1FvaT{1>qv&Q?zOwqoN`lG}hFGK~*8%Xb5@3q2(7186R>Qw7?Ya;Z*z-h#c#N@gZmVB_QHDh#zv|n0Cg;;(e?{NzQ_pA%>+Z z&GO3M;(Gg~KKlHnm2+#N_ExFkf8o?~Kvd3# z{xe}_#DiBw#$a$N912cDMoBqU@-g6y>~wG{$^xh29PskskArhqSAe^MuLGyPd~nY7 z*TGr;EiO?Nk@x_dA95<(D>IR5L9pu(*(<6ZDdkPm}6ISZypnVg1n{5sBjwq2Ielw-HycoUrUb~qz~*yG&*@W1jtzNqLUaN-9EN=^d~$^0Xde=hl# z;8b{0^3&k>U-=$iZ16|P&w*3_1<8M=Yw*8vO#;74UMTr3aP~lhb}DiP2c)=w(;z!r zm7KV%%&#JO4Oy-xI6LG8&X2`&twc#qMRjCBa$etrfD@09`29Eyij?`}>}fPO@fgWt z!D&c4aC~x&1lPBI0NBI+;0(Hv;8c(*`B-p%$f+<*%H%9J0i2UJ51b7=0gnHbc~V{s z&U#BE*O|%qV}cn^8vsGG$Luxn8X%rXnt*6v0EnNGoSwPgl^n|vXFaYYG?d3LeheOA zB_yY!mLL|40r69k)1ZH^=>EN;Gp_6yB>!H~@e1(YE4qKL=>EN;`}c~@+_4|w!iWz! zlmA}P8SXHy@aT_{S8~ju%oEDLS9Jeg(J>zGe}%`R=f78U|6bAk|NM#$Kamz)(QS!* zEh}8N{S>bk{^+U?{wZF4QP2OWpT7PlSKakoJf7q3f3BaNc+OSd1!_5~Lk^bC43Q#H+jXZg^&hc zi&x**^RJ~J0$O4aXbE zujn77L%PRr=-+SX-*54Fc78vkeUJif#H*j`IXBS18|WXT&vpMo^sf;8D~wl<>c=4+ zgA{W!9)B`1_a^#x6a9m9T#x)6{resL`#qk2jsfW$q=Z}X>M4EsE%fge`UmM-z0)7) z-yi7TAMyCZikpxMAq~DAub$QOZ=-*=(Z4(K>JNJVJLumX^bgWcy8SNtcNhJ;8?P4V zJ0NYp>$=oijaM%$O;!6XO;KH!9)fg9c&KVW;jXH#Vwwuzve-{xAAtZ3z*Ui>0m#+> zoFedx@OJ{x$O*s#Cjh^S;{=Woh;asRL(FvsFvl6dB?31^qzyoX4ZtcJfLo$~z&Qd5 zWdPh3%gX>*RtCUr0NPzebS|s*6>(*OY$}T)s<>GeMG6TFb^+id@?8L|cLCsP2VfKZ z?En((0Co{5E9~U}xRe8sRt|t&>>#k6fOmNSb1DJ2M8HEt zRt6AJ8NjN_06awjfpY{BssQj7%c}rbRt3Oq0zRTsRRD2S0c@%Yz*pQPP)J~KH30QQ zel-B=s{wGW4!}?JuMQxwI)GgS8VY+204_BEq}2f6FLn^vPQbe+fF>ffCV-Th01god z5FTy-+}!|7a|6&!>?g2~K!7`dAd%w^Aln_lDFVU5-vdA+4*&~10ECL;1db7isRbZh z%&i4rPAveJ2tiOThv?)DAkG`WCI)tE zag#tHfx)!_v=#Za0j#eLz|{vpd(qzqK%x(TT?9G`dmR8SbpWK*0nkb8Ah4Z)w=aNr zk?IQ|#TURK0tv#SE&%ts0H)Oi&`sxZv7egG~J=r1B00ElP+U{wPE14RLWa|99^0vIfoHw3V(A%NQi zhKf#&0K_!{u&EJ%Byp2KA%VgE0EUZve*o+K0k}2>kSzK)29Veoz%Bx#guMv>mnHzx zngB=@I|yti;N29!7?Ii(KuS{phX|wzj{pGf0RW~202nX!6WB)}AP_*h$O#0H9SGnQ zflT4w3_zo102VX@Fi{*QaEw4qa{!aY+~xr0GzV~rK#qtE0uT`dU{w%+siJ_uIRXhS z08A6hTL4(r0>EtoxuR1rfVf})n}Pw%5H|@F5*Qo;;4zUO0$_az0M}3evqb+;0EwXh zb`h8@>|p?0!T_X&0hlXx5ZF$@I~>3~ks1yl1&?8hLj)EGj|c$n5dfw|09YvY6WB)} zAQHf0krN3ZI}*St0=n>T37}C+01H|ISSpSaI7T2Q3V;xEqX5i_0&t1IauFE~AR-#T zs%QW!MFD|x1QKEZtP;y(04$3EaGSt$qEjq@xL5$2VgalXHwhFH80-M>qR4juSnmMf z+6utSqJJv@iLC(aBCt-_TLW-u4Ir&GfPAroz;*)OZ2)W(scitHv;lC4z$W3*7Jz$O z0Mpt6cunjlu#Z4MI{=$SPCEeE?Esu2utoT{2hgZJfCcRVY!$}|93v3Z0l+pfw*!DV z9ROS+utP+41Q5{?z^aY_c8UT5=LjUk0oWy$#{pOt2jDh=-J(+`0CAlFZ0ZDHkGM&o zkig*10Nxk*odK-x48Rpjf%>87&$*h&U1Ar3y~5rFfJ+wuX!(?C*-dlX}s+kvN^CI*Vt!sqNG=;!HPni_IH9#8(u1 zx~p#bX>Vj@s4nVz+jezVcROj9tBUEp)o1L}@T)xTtmVz#=G{ltHO#Kf{ZKXA7dH>K zq3U?esQdK*)y4KppagGe+fI*E&pENl5Svp^2@9jF-TKD>;3;`g~%!MEMU8bvY!e zA5vT4M#P=F=7$$x??F8;xOjh`A4_I)4S5ZZ&-=0r#XyPiDG1i%eFPtgeJC;B->56G zk0fT?6C#H8jYlU~nKzuQhXVG=g1jH-1>$GF#CUVo+dveBk6W-VZ+EVh7<)*JPc87C zD~&oRG2ZB1D~&k>%(#`!`#$p}$mcd#nI32)Z_ON$7@u+&4&vuiiSgdgCe)>3KI_4{ zRY2oGEdRO0_-Muq5I=kdgdWBHr(ESR15%VPWkKHmd0b*&NsRY`W=ZUrtjv2uPlBlU zge>QV^lT8zpOlz8(sLxnhfVOmlFhE;bMXt&^{|>zhWG9#If;)BtF^s(6~miV9PUOS ze^6skQ&50VkEm`QAxMUT!a)(BNKh0gS_B-TW55T*UUTf()7qdqAU{xjPykE{zUHy z;uAz?!M_Ln0QwQM9<&bh3TP83AG87VDrh5!0ksD7BIqU1bD)<&FMw8o_!QbW7@P*; zQx>B@kMQY$BqaE}Lmr6FLGTHcnV?KiIw%E{3Yr83lR*i&}`6L&^!>M`za7_tNMcKg6e_lgBpMs z>HeSqP@qsh!zl8pgik@Afj$R)0Xho$3Umx~67)5wJ*spBbpmw-^#Jt-^#Kin{ry0F zKm$N{=9Pb0!DplSoU+m$#QTxqpf(`h+iU`A%J~kGZ4yZn;9;hy;9LNWB0c9_O-UYn_+6Vd& z^gie#&|c67ppQZOK|4VI0qq98&BgLPB=&%|gLZ*-g0_OTflh-?g7|H}H; zj1L86gXe$-Bh9Cl54H^R)4jKVU26dnvpzfem;U1*?or1e^N}<@ zigOWk3A6$vK&?P+KyfJ33zSDyGmw}GdJOb9Xcmb30XHpfQpX{WL?u4M(+<=gv=rEU z&=SyS$YViipbQY7rr{Gh6G4+elR?j;9Dmj46Jz^8?}Pl%*`7A61pSd24|)r902$mh zH-Pxu7JuC1j`^shdEifi=7JW2W`pKPnR&$B`3NOGDHphj)%9x#@e>fYdL98TA&+yd zJO~kC%)QsPwf{;T?o?$BN9}90DT7f6toF+1jO0TCFB#(+n^nwH$a;~`Jh)o8$laD>y7S361Mtf@%v%y zGwoqh%-s`(Sj@cVZoaX$ir%s<6>w4XAQHftq&n-71@t8hwfbvhv|NH($lPN zc{N|0Ii`WR$YJ-|5j@u2lq99o0IkTHE^XEseX z#Tpb#deiJ5=Y?pBaS_{3suilR5Piw8H~J;g;iu61xx1vR*-S|nGdRs=eUBnZt<=Ve zVHrx#86Gpdv^vJuM$E*ZuuV0bC=5JXGsexx6y z`Tk?!XH~Vrz-q;s0>!~oWC+K?vbDHt{zm~@9w<3nmL?W09*-Whn^i0W?q_>RV@ldj zRexSyI0ml9a3H%ljsj>@=~1_gy1$mJck@q$VdV>A{-2!=mc$B^g`VI6e=w}cOx(0q7^b6=J z=nCjEh&W~P8=!8WLeO2%9gu={crw=bR~tNE^JvT?GSBNg!}Gk(<20>gnrC?8B%HU6 zBLmO)ZXh17X*|#3Jj?S;Zym4cAsS_Qf*#=6zZr-fY?6zFKZreR1l|}F0BQ;f1O7CQI+N&@;UzK&vM{~E z#;gX3C4=bd@gO!h3N#Ls1{w>Zhf+W+!+d&yX*NtRGEF_~96e1rmjV+dIUC@(THc`B z*?79dtvH}!_S72J45SUE;M6w_^eAX5XbOl%aQL!8G(MB#-vf!sph=*KpezuTP$@Zy zZl*%3N5m*k2bIpZ0+L4Nf!I?HBfU(oGG^GOIqVpJtCtKy_LRNk7?$*amB+@|YnEd^ z8=(hEHd<0S7)ZC$X$$Jmp*hR7N)mnO*|E0<;{o48(#g!?MgHUK(RQ^Vl$htYkjj|0syTY3bqc zq0QW)f=>gmDwR&Zh&=Ypa;2qYsjNd@GM}zq3)%?E2Q3Dz2bK2DBb@)1(hVp;FO*iq zUUO=d#94M1hz-00Vk57EI)QeA;y`bMsE=zf7kg_}-2t4&YzNKd_-_GmiQNYN7HBKz zO_?s)D3#kmB{6z}y`@1k=3@{I*$<*2dqL$vG-Mq(jra)sB@m6seGgxIK(yvv&~6Zo zvAn{@I4qWIHAt^;oEc!xfVliJI0dLG>a*8XKp#Mk1%Ds>Ly%=WjYirW|B}jABZJE% z8(0ZqBaCuRL94NSNOP^{OrRo`Wdw?TC`e_=QT;V@{~1$^Q2 z{G(#ZCAFPfdCaoK%?2!Jur(&qMIF+jxsmHBE?-hT)P}Q}f~n~1qq z^%K6=(Ndxqe;t)ih`E%KMbZtmdYNFV{aRdQPF+#{SM=I?-9_VOaeovJtbPq@!_8g? z2nP#T@449BbmfVtKJJ~ReZlak$ov%zt`X~gg?-k0Fb-8n_+r4;%l?`Q^8!N=7kx#g z-=N5PLB_lhPj7zb#Qtwtpt(`tjA;9tx)Qf-zhyb=g&FNCy}mAM!I395bviY{y+=j0 z8)~p;QYB;R=8c&DR^QaGSO08yg0ZL=-Sm*X6$@{uwug-Blj5}-@WUFhA4=5?;`R+V zYNv=V1UOS1B3mGG$ySKvg=!P~*(%13w>%>=SZyxUo5+k5o?xDB5aSH5S(OKk9s9WY ztl_30j&OqLh6171J3Ka2`0~!(wnsyZ0*2MrTRgf?AN$VNo434z961EF#HyPxYK^!6 z4cc$jMVa5#cJ*4;G;TB%y~N|n2h%1z`_;kEsSyDUN0bw3zr(blVsJan!^?VU$LbAB zF4U~?*({WcFeAUvO}z8F>Kh@3Se`oI6)>PpYsG<6RUERcEMig~lDKJ8SAC z6yVwuC(=#>uL zccvQ!g9BTHC{x5xS#Zj2wTl`e_TE-Usc(s9ci@ZNzUDHgt-bQ`$dv6(G?n8OhRL=c zIThgC4jb;~du?u0NmF5eAZ9CO&oor6_OmPyykz*4J*%Fdrs0W%{5z_z_Peh*cn2m~ zFELr2*z!y5{o%Ep)gY<7zPQWAt+$>my0ZRYhe-$P6&JAHi*o$djgQJ*Iyzlshm+=ZH&J0Q+ki8tp19!7@DHjy)63a*f$=FK6sirOPgygwM%|W$7=4L zg?{FOa(YjVJC7er`Q8~m3l8SeWMBH6<`HVWJY~jVtzoanv}wip=KEEYy!gp#=N`X& zUAZ`?{nAUCho|*EmI-TPx6hn<;kn}c@xoWdqCHDQfq7c*bIITN$oUs%j+|6n?hP>> z1wySCz+6r|wX|aAt!~8yzHeY0-}3gpaOcRtAAfiWE|trwA}TpSxAjh$ZqALbNsxC0yGMta41kxkj^L#t0MX!Dw!p;+>q z#kWv|2MDVnme|KxEbcgKEj+C^-F&dJ`GW=I(ui0 z0<^<8!5V3nE$4@D#{z;kWdf`7{b@iaJyP|X7%Jbot%C#3((S@A{_=woNqhqU3LD{ z?c2p>C5eSB_oz4%gss_nsm|lig*~xc<MREH71k><+a{M62E2i;Eg;vls!s>X<+R4fIjhE>o z)8Zm4|E_tZ43Tr>?`n~z$a!cPY#Dlww)$4?pO*nEXKSpeQyqb6by&_68Q@y!xHSV7 z&(||#ysXz>HG8uCC*ge-JZ=s?7Kw)q@jo2fQqD6iuqHO=RUKc=!(4Ul@j*SSrw{f) zDb=M!>2DdJe^+-=HHKtScv&;x9&P_%w9M7Eq!*0UMVfp+PydBRHn$eBH4qY|L*t#+ zVm@$dZNFzpwoLdhabvDyGJNnPf5~;FHfPNv8oq^P03CDzenRJ)y`41N3qAuLC7#AtKXFgcnzQuaeKNl|QC9*~>t{bKSjOkHagx3v9_Y96q>JY?Sd z1sg5f?iuU9P|upYB;M~N zn*06r?OFEzo*9lnEU}3Ebw`)(rCbjF6tS{RGjKg0d?fgX9$M&hqg+;7hS>pU7H^;epXaj zt}u=Ke;!7E=@4_TO&Shut8tC;Kh5V-Uij09$?L*?v_*(7!-36iaz?RxWOV7Cf?F?F~ zF|P;z_*Z&}IMq0O{7IbF!vlkHPfR)0UA!8LpZ%;?-rjiq z>5fhVC-MGDSRihGHCM`tGqE_&*A}L^n*bKuu5 z>s`Ca^Q%QIIk06I6ydjj-bq=9f^|@E-@#AL*Xlkbu(;~p9%8x!m(9Heeiv_IKZk<6 zBhg;?cGIehZylP=b9hhVUnkBxGv{R1=B^*RKy6515PlR^9u;@duDVEgw890(vm&7t ztXPZURcTb0F?}9sJ9Cy{h4E9J^%~^?vwf#TZv72E5W+s(^$k-#>?xjYg`X(TqHcB6 zy?&xzjWzja9YzT>hKD43iX$k2KRjL4Q41Dct+iVCClA6~qurWfOl!EudL{JA<@NuQ zps&NPw74^9+{q3T&$ZS@d0H=>_Sm=Berne18&LrFka^vdD16&M(QeU)EJe&E%M`oX zK&|yc>~S|E3lm27^hB)?JPgpnxZ{8V>Pup1Jbq=}(n~aGtF_Q>_7VfY@vlBCXp7tm zVqIIq#*>gdt+#S-*`K?l&8S!Wpp3u4qJI@cCEVT&eQ<$t_~94#iu3yK{@M2IH%ETOM?8*TrBM=QE%NAV9~AcUw|vEJIgZ*^PM98{ zBhBgIBIYr(j@aD^Ma@S%Jgs+a*QnTO!0?o>S{f?D`5Se#aP5rZokZ)-Fa`5|P-kPx zP3){Ccv-LJt{vPV>D-j1bx}>;%s7}LzUi!W(XOV7rtz@Zdg=BjHR4*obba(Wl;oXX z*lfn2T1GtA6;XEo3Cn`D#U{im`ql&G)B)m5yw)Ui%4qYBVQ+*AA2)fSJj+!M4A z>meEQ@dK|1_w(NNJRfYa8I?u$^%mafaFJ-38~hM|nvRqmssE6UNC#PV8Ox@0>|gbR zFMbKz)lC~}pEk~T2t99>@awJxhd$sLp^*DN{Ue`nFduUXd+=i})~luCULN=AF`qJJ z5kjHZBCwj>950$@VkpastCO^v&OmC5N)qT}@(y%w+~DR4*#MlgJzXps zfqwXktEm|BPtrx@G1ytGmwJD^es-l}wvMM{1$6UVy6Dmiv&(u7_=(5%xYzvTD%U{H zLoA?GMQ$(HP+!a#02UyUa=@a+A%LFN%fv5@_`cw`Ia9kBwTxA1uuyxWT$bpO0)Jwi zo*~kP0eDh$BmTTd?Ts}7e;_srX1y_#)2XPS3q+nhzqU4GzWNAmV@CE+~~Meo&QRunS^;s(AMFx3}daaKx^$OAsa2oI-U>irn&Zs!XelijmgTD&vcUKzG>#e z2+EP}AKdPE)|%Zo|Dc6C=Q?L%)+nTu;7Oie2N?1c6HO&y0Qm`{A!k7Ehy~~j~O4br& zs+6Pfkj<;e5F?7L=FEOu21zYeAj-J3mhhr!TS}F*K=yjj80^4*mEYzaaWpjc+r|J-M%m$ku`%?7vGNIH53X|M}Y#@;q@|(Kk`TMyt-!< zlZ(1DE==SOZ`QYZAX}9yMRBQntbtF0(lDp3c~i2IhczG67sR17EWwlI8vXq8a$!C@ z=3kbEVOP1gTB+5v#LCMP&T5=+O0{Pld>#Pfx&o)gn$DPi<}g?zX&fhuwk#QFI8_mbJ?Dl#!DA(Va>3s&;5_~A1kF8c^;71c zj85rTIQewc+eY7wXV+73w&BC_VO^#)34HD{ew5+wDuM9I8&8Rf>3=%ITE^cq{VY)S zsE0U}j;ZhvldaK5GsgbM(>D&M_w|pV&BN>5V)MdqTbS<$-(R1?N0Ry67FJDp(GYqK zCA`s6Tod%qm(P$OM#`a_ew_MErW9DGsF(e%W)7(88O_1fs02mYnH@{=(~b}kjI zCSy3|uP14fH6PD}3FhGUJ~^dP6R!wttDis{cSWR<$(Mx>EcpRIa_P2{kBv@XKQuvQ`F#Wc-Koz&c+2+@H6Iv zp2kXDUF^=*Z06-v^)f;HwW8F5RdPXha|*L&tn`REb;Q#-*aYR_%=5vQE%MsIT&N$i z?#Vv_F|Lc{--39kPl((p=*0rDdxQx!2m}?(JsL^|4$~%}D+0F^${s3#V(r^)IY2_agiF=#S3#Sb>LB zrsKEhNc=!>v)7i=41Xd-1=0!TRr5Pwqkj%=BO3g w-Gm>H2tBA~SGsvg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +

+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..cabfbfc --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..69b64fb --- /dev/null +++ b/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/package.json b/package.json index 7cf42eb..20980e3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@radix-ui/themes": "^3.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.474.0", "next": "15.1.6", "react": "^19.0.0", @@ -22,14 +23,15 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "postcss": "^8", - "tailwindcss": "^3.4.1", "eslint": "^9", "eslint-config-next": "15.1.6", - "@eslint/eslintrc": "^3" + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" } }