From eb6f7e7a1c65af28119d48257ab4f80d04e107e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= Date: Wed, 28 Jan 2026 19:36:00 +0100 Subject: [PATCH] feat: UI overhaul --- assets/favicon.ico | Bin 110218 -> 11885 bytes assets/images/icon.png | Bin 22434 -> 38794 bytes assets/images/icon.svg | 203 ------------- assets/manifest.json | 4 +- assets/styles/input_range.css | 57 ++-- assets/styles/select_arrow.css | 7 + src/components/app.rs | 5 +- src/components/bottom_panel.rs | 72 +---- src/components/button_primary.rs | 29 ++ src/components/button_secondary.rs | 29 ++ src/components/category_calendar_task_list.rs | 5 +- src/components/category_input.rs | 119 +++----- src/components/category_today_task_list.rs | 32 +- src/components/create_button.rs | 31 ++ src/components/error_boundary_message.rs | 1 + src/components/form_open_button.rs | 39 --- src/components/input.rs | 53 ++++ src/components/input_label.rs | 21 ++ src/components/mod.rs | 9 +- src/components/navigation.rs | 35 ++- src/components/navigation_item.rs | 18 +- src/components/project_form.rs | 116 +++++--- src/components/project_list.rs | 18 +- src/components/project_select.rs | 4 +- src/components/reoccurrence_input.rs | 86 ------ src/components/reoccurrence_interval_input.rs | 43 +++ src/components/select_button.rs | 28 ++ src/components/subtasks_form.rs | 224 +++++++------- src/components/task_form.rs | 280 ++++++++---------- src/components/task_list.rs | 57 ++-- src/components/task_list_item.rs | 6 +- src/layouts/main.rs | 50 ---- src/layouts/mod.rs | 4 +- src/layouts/navigation.rs | 38 +++ src/layouts/suspense.rs | 28 ++ src/route/mod.rs | 49 +-- src/views/category_page.rs | 3 +- src/views/mod.rs | 2 + src/views/project_form_page.rs | 12 + src/views/task_form_page.rs | 12 + tailwind.css | 4 + 41 files changed, 839 insertions(+), 994 deletions(-) delete mode 100644 assets/images/icon.svg create mode 100644 assets/styles/select_arrow.css create mode 100644 src/components/button_primary.rs create mode 100644 src/components/button_secondary.rs create mode 100644 src/components/create_button.rs delete mode 100644 src/components/form_open_button.rs create mode 100644 src/components/input.rs create mode 100644 src/components/input_label.rs delete mode 100644 src/components/reoccurrence_input.rs create mode 100644 src/components/reoccurrence_interval_input.rs create mode 100644 src/components/select_button.rs delete mode 100644 src/layouts/main.rs create mode 100644 src/layouts/navigation.rs create mode 100644 src/layouts/suspense.rs create mode 100644 src/views/project_form_page.rs create mode 100644 src/views/task_form_page.rs diff --git a/assets/favicon.ico b/assets/favicon.ico index 070d1dae2042471cfdd81aea7665dea21ecbfa78..adc9155379f199ae0c8724827b8fb944b6dd82bf 100644 GIT binary patch literal 11885 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 literal 110218 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 F{|AnE;-CNk diff --git a/assets/images/icon.png b/assets/images/icon.png index 1c33866a44a4d17d522c4266f3fe4d76e29158e4..ba339b610d092fb616633ffcf028991afee044e9 100644 GIT binary patch literal 38794 zcmYIvbzD?Y^Y`7Q7LZUx5RgU?5D=7xT|y}(lx_h5MQNmjT^dwEDQOTCB&0-|T|z=Y zx*O^4+PLrfJkRg*{`gUVi(x&F?ug}Tjj!urM(U)@=9vcXkq~?X+8|GQ!SsOTlox@m0 zbyJ_&mwahRe8KJP&$2rXlvN;IBrq`k84d4X-cHbrf`T_AL+<%cY$HYZQPP+dejUy1 z1U7RZ9bjB`Y0{^CgtLxVIKB^gColQZr3o3AS=_OZ6kCIo$%$VHg)BugcuL5IB487e zrH%Xht(z`{*Np*1g^H6V`>hdl;6+zlhBBEo9sL5GR$zcqOxWoxy>jP??`|w|uS2bp zc9c*#cSsnp`n+o0p$i#@oC*{2O%Fy{lwlK;qc))5+f%n$4@4~HPd2oSFAMM)Z@$344?EJu|z^TQ2J{_3MwH<$2_CI{t5!n(2fG39PPJk&cl>T#r6y5Tb zewBXWe@kK}|2HU~sf9@oiXl>kFc0yE#{Kbe`6SU#sSk`=KYy8ZCqNmt9&X@4zINT1 zRS(RHzf^7x@gv<)BI6ADM~LmxE{y_|&V85)xE-hrlhIwf?30jd2srLD zD0dF*&Gz$%b?6BKRm(2hbFBYRxSt6h!rmP7dlYA)rrEgiMbWq^>vPbEq@;iHi3*Hs z=@X_2w7eQ3jD7m%a5HwLQ<{nnGiZ#i`e(;bHjV+F`E;4lX;iK*z`V&HN?#Frx}xqu zLi-qE{hBCGT%z>ii=Ytw{|Er{<=*Z6;6qBQ`M*gAH`Ng+g#j66izq7>>xy*|8Lct| zwagI;&auLfcBfCy7?feYB5{19fwtK&W9_90qZjh*$WNa#J?M!jk;IiZJX|wT zmMcKn_bj3l7|LLpAdULKr)3?YtEn|eQ9S1B7a~KM5JKsdU;l;}P#y_VpvwQT^nc*u zLE@JKh;{#4XgT;-FvwB>_<^J33f+=vJS5$!f+GG+sf(`V@LQKM0;d0H_&souhxmU3 z`VWYJ2etf`D95$PP)YybW>%hNq8c9qj^{+dWng~ul;cFs|Cu7Oby0x$=lpk-d)!tz zzqRPq{}QB4|Hn%olKoHOC4q4j9gOz>c*VSL+$Y^q{lBRR9^%yhR>h=e_(Ojd{+FO~ zkEPH5e^?n$;kUN_sgcxp0)iIOL`%i}B@ecGw%Fn7>-5qH!bwEYX<3#?kGO%AS^<+( z{<0HCV30onM^i`j3XW0Kvl;#i zkN$x4=NjeO{{#({fA>cLGLrzQm?dft<|}KqMu$-Z{&BrrF+dM&;)xoLqZxdPL`4e% z@9{)saj|}>G)MJ@|BtVvT0||-^B+ND;-W+UH!7PKVvdRy_}_|*ihXmG!!2OzOpS=} zWe}|_siM|@H5K|YG1?cJnM~SQ{Lew`{9l#{q742kz(F28P)YbFz>SKaLKOe4|B;hh zo~Q+Ms3Bq}L@R`D#n3E4#VAlj@nw`pss3;Da$hHzu@=O7>mRNOq0DsZGD{e${69>P zJ`Y3y58$7+dF;N>1GeBl!0d;i=BUK0K>L4KHrj8Sqehhf#}eDk;12mjf*>wo7~(T*&G$2^1l`Mr_tuX3P5Der3PaJh(AlP9AcRKM}V3UumkHC<{RB; z-9pC{W2|9ZQzTjG~zF45vTtyvapA z-(4rdDlJ8eR?L$Y%u_BD|6Et@lyuW}hgc$y^J8Bd;`SC=(Sa}Kp5;I}TdXQUaF#PI zj=tjFXI+kq5noyHl}jB__wxB4&>``5j=fRo(bvzhGbuf2oW(DJeCYPteyi_q6uUQr!;Wh};c4t! zfXmLQ;j{yO_KPsveS|zp2EPPE{Ne)aL52#q&-ZA9#<%m+D z1Ry{4fUlNkI>qn??s-w2ulsHn^7q$|&Nnvw=8qTsIYZ^+84Lf}tn(VGAo{yMZ1>$0 zTamsJUti}QYGl*VYPWNK)_WM|#8&3n>>J8NeQtCTHv1-fli8;}PK0Gg2avg+HIna+BL(%8Jkg zxMozyP;oR+E|$N?Wo>E$_u-DyC#(5_TUI_f;DLFcF1bh_C$0p?W~eBP6&SA%=vez zLm?1b)IImsT9m4n4{-)qqKa5|8TUFOoOY`l#8+Awuae?=M%bZhbPz7qr`izf6F65a z!^}oVTmipf6I?rjB^M1F#TRL9G6Lv05jP)iFwFEKa#^jKz{Q|KXX5;2U7GNKddDDw zI!+GNk6C4PQnZvqA|PceLsfp1^9pfnB)pt|g1rG>QUivsoS{E_vRSPE%ZH93{>-|H zM;oMSnK3)xwPZOw`Et*t5rg#ow(|yOel=7ne%#3HbhyN6uNpJAj3Y$~8Duv3+5VCM zAhEchO}SELpZoJtyNqYsl@jQ{@Cc0WG6Jcn^$|wN<-@ptAWun3#0rY2 znAth)xQJG!%%kzq)7~H=P5Tx#pa}@V_sjjL{EeI^FMRmO(+A{nlUevPa%lU{|)!D*| z+WKslW77}O+%IT}iuvoP2*C1F>8zs)cfwV|iHQOfj$et5B^;>njF7?T5N?qcbd)mo z1wdRbZ_+l6atezu-nfopldxHBcx9mJNO^giy77i3qypZF3(=KK@gtwVh)7;}HZxZ`_we6#_54q}fYZERRn}39O+E2fX5u&KhcALajSYh~T!>fSjT8M$~Kv@$FQq52x^iYv!Aao>(tyW{)Bg0O49!RR9tosv#>D1BH-N1Z(e zTp_n*Qclr8JVUW@Q(r)qYVb$P$w<}>5G+#a&92H1k0aqqI%pn=Ee@zT0avg+xNQ#c zLnMA66^>VwKw~xIarCPlc+VdqjHnP!eUI!8pb!?V(1pIvCVj7L470c~FtFcC zy^5CDpuwC{bOnFpnOA^TGnB9hXobT%7Z7BqqfFxtQ4>7RtI5M{rUzHBu9#lBQ@F6A zG0ykFyR5rym;amvzLDzR{atOvsnMST;(lrT!ek)_CP*CRdD$T5Qv)f~}kyLOqL+aGn#`=XFH z`Gf$iwvl&Y5m4juf@v!+jYq<_g;>}rdqG+~?HHt?+S4m`kBJjO>n_X^^Ij1j0fRrN;p>?A)`m|X^E zl1Fj-W{iY_^>DA(>UV?fqEvlV`-QRPCE$>p!p%~4e9>eCvi~$x>nMVmFFG&#jq*TS z5dGEfJiL1{CD*dK5q7_SW zQ(K>Qe{n|BmMy^Cpl+p{y8Q59MzT*2tvM|4Qma;4ch4fE#aDK4uz43Pwt@tWQ^2$9 zy`kynKDo0%!llQQoY+XGpPKROVt-b`;3AQd@h#!rL-A-VLfXN8K47`#(e97{c~u0a&UN5q_6hx}R)cTUwbnI($nKPLkg(W;Cge-N z&#}`nH}Yvd2K;S=6PiWaPjvxl64Vx%hpnHQY5VCfp;rr7sX3=`kf{{QuxZ8Iszc+> zMEWL$%~AY`RjGa%g_0-~X}WQp3UQefIg$R?Hsh$wyNcV|+-4pAi7neuLGiumUbjgj z`Db7$8jAARAq!B{zt73B?AP8Gu^(u1I3A>^Bfp1p4qD}EtSF@oX3muDHsDgA$<4jv zDNwmbC4HNz;W-OT?=q(xe+ngr4XBV(U5KqCc$fZ&pToLH=mBv@5t1d1?~nP*{h3TV zqa4usT$b8`5@`wck|K}poESEtiz7^NRbE*&cvf$8n*X2SLGOY1`P9?>acshqFGi|v zd&)I0{K-F7)?wp4^jU(FiB(rKWsG_3P|qbD4~e7XLk0u(Vk7#Mtop24(lcjerTdOR zRH0R)*GdH3Xi#Hs?1#%g-{#ka5QVfq&kDPQ96sjx17ckr!_k5i*iLaRt0h) z^@qRSW))(xq75ho+6B+F%wu*4oVe;!>XfBZ_uGw6t4=Q`qGW|RsZeOPP;gH_G5Aeh zPRQMtoD>PcJ*b)^_7^I`sp4qtC-?@gPdkl(_wP&pGFNkI(CH6B8vVZ0c)RE5dqG$Lqs6(w-zM)b zXcR1|BiG^d@WUFHm@+)!1OKhN|h-?Ip7 zdAo<$dVK!dzcldd-5rn+|KV1M_=`^lP&?u&3#S~9{kfHT659R*tcpDbN5%?a7}m1b z>F!zYuQ9b!l)lY%9gxjHbJ7Kl1V8{_c(gLa+$bZh`k`5rJnOXhSf%LrSe-qzECLEN z!)1-gtbJtTT!Vb*vSF7hZtijp$%ma}y|gN%tPp$dE?qV#1^g}%s3W2ASdas!5+$TG z5&^Dy6csQ*DQSQ1FLmp|k@8;a0f-+zZw$T+7*=@HtKYeIjc|-EJ?z@J4asNZO;tk=opcUAqQvBQj zw-o=XM&$fmodb%y>GW9IyxnJ3^3XlfOB5H4YCeF&n~3?#XXhajM%9*-0zcg7xu!yi z_yq^Mk${l81p!>$9XaUuac@{JYs)POy#_I`-Bzh-{hftSx9EN0goYWGw4HS4J*6|D z)Pps&+%5XC<>qJG`|A4wYEo#=#!*{jL=H9e*$89z+NGOZM>!GrNY?k@j}Yb$4qYoj zERZC|s{g#d(F(DvCz{_($(-XQZ|cQS7i8# z{`pmcd(Vr7-QQRlsE7RF*qglf-~iU;A~1Fa`D_sGKf!w?mD2gvuVr}6z9yh7RDFh} z>lqe;=)&5BP_|bKDd#W81(J7Rr^@Z=i%1W8qKtq{K%Zp(CkIkuekgt7{TAUa0 z6T%1#%9LH)lVI8Qk3J{nbKJs#S{H{h#NTL9YIP%Ger%j@HuSJK02WYl!55E|blHw~ z#>^Qu3Ta<0+t1fknpYuNq}j6!8ZuJpWn^9+^z5|9CiA0qWv^Na@UGq1`z_R#53Wf4 z%}PIaebyW8O_spe2+8v&R-Aw00;II}IeD&e)Zv7rwa}-;pI2_=%tRx~@5 zTS!8XUNN`yz@JbSb}|2j%bgOQ5A}60js@osm_J5El36-F-~shQ99yRhG*9K96sWz9 zuig7EVfeBg|b!F2YwPw>d0I<=pv^8AZoR{h~0%$%h= zS^A0{;&KgE`d;hdv=C&LbE+?HEY~HIIHLN!TuVoDNr;jXccgUq zKv$s)cX^OeALwk4Qo#SkdD>mHZh(KQ{T`&uIhNJm3Oud6P-#s~srzJm6CAI<*j0Sg z*lpE)&f5egq~d9))BJT#BUhh}BqNUkzk8t{Q4jFTX9>KzAD`(seOhKQ|8bQV!mEsI^KvS`g6f^JL9DdYGW+E`# zyd+S{rNnoa44iA~V779yOi!7ts@1YtZ8GkllR_Kgxa#4OJGZD!agF?bq}iRmvA{=} z4$JZoC~6w;A=Sn&b2X)@BouCqveHM`G;4()7{6y~pXzGyFC@k>)^IAoSMvE!(=` zCu-shHU#9u+n@&{uKpwCvHI+wy0Fz^i{Ps@I(`|hmQq4UBVm6te>SST0jKTEKQ`KUKQD+~WH$<(?II!kZ!8dH$V667E2 zV5X8^Y|q7vQ0pe>TD#74@hT~2LK%SrUc16Mb-9b2N)B0EGYRU_O2#v6hhe!y)f(rP zOJGK1J%=Xh=J}oatRImhP+OnSuc@3q`MK#u-ux@1ulzo+irsy>RW>2`+zZ2jK71P0 zgG65*4UQd8a-6T(=4#bGCYyHNDUZ0s1%%Dc#A zc#){qAH>!T*RPbpCYc(GPIyJx(bu+Kz}IJi`@bVM!Hdz~!-#Lrd#f5b)eIH1#L#AH z%@Z7lP7p!Lj?gU!?vZX4wq_aQ)fa>3(WAy-ugj3>9#8G&H{y0UFmJ9aJ=#8P@xFhx z{jooc^S$T(&*55VvWf&^4yEMC-*Dfi15sRsKF?s}vy;DLdd+9Er^ZGRFd-iN+C3|S zJkRB@Z8|I`z~AKFAS~*mUmc&gcpoto!AFm=(%^Q-H)HU{u}k(3mnY8;oD_G;>vvz2 zTFJG8%xNEU(=O7LoiW9N8|lEl$b$F*RCAk^b2->-i<4Q7ygkqCF4=`=$vHsG-|?1^ zn&<_fLXZef)B3PEK^*3nfNefdtF92Wn#y>1{Ejb>8`l^XE{$fJU5*t-ZqN1A1s-_v z7VEE_3cZd=_l?Dzd-B3n5>er#Kc|$$v@f?!VuyB*FN#Bz=X+=p07LRPMxq7T`KZ8m z5;rWUionKd!U7pe%36B-&yJB9foqmyrm|oYeg2F)2|i75J$ItW9%U%LkgWdU>#Sp6 zEZgq#?$XEo*yqm_XTe5O!d%19dvGBMt@Pr&XpciH30@pngX3?0 z?C~xP&ZWcmCVAd~1(~`*|4l5AnXBN5#E~ZO+p@C64_bCoMqYkx_v=O&YCeJ=^XN#N zi;fsvg!!~j(!F$m!Sel&6-rlUCs;=cQ-9)f~AYVY=6KL&@c}$rR-wgAc|# z?V4ZtLfz5Q9;KhqfeSsmTC9a6`g`EXM9S2ie2R||L=`7p$;UPlk7~WGz8%Mg!>!K5 zFkSsG8Ow3^&x{sV(1h#9_{l!xYqQ^dnI~=GY(hFOxdeP6mJ7qz8BF~Ol}8J&h)sXz z2b?ZDU7-nf<`+E5W-M=M^|<^!;(v}zPw^pr}1Z(zBI1r_@IHWf&A zjPHv+o>B{J+S>2KReCx|m0moYdGnnWY#a?x#&&v49B0y5X{}%L`MY7*P(a?_t@1be zfT&RN+C;V+M=)mOD_*Ag6>SGHYd`CIxlAZwVg{;J+fs`0UE0Ncmu)@j2@ z@K7|J;O%1UYToR-W(6^%B?p_k~d>~2?X|3G$hn7i} zKPb0XoUum5`2l%%`v)fO?+_2sr*prbqWf!*#m8y_)~tC+a7lxjGp@aXY5th~+nINM zm9CVtpOhr+PBHnU1>CP+*-k@#aW)?9a{U(K5E4L2>ROSqj!t1@nq;W2w+m51Z>Ny-* zHma>XEynqmuuNI)Q;uA-B0Ofr`nNKO9crQuGYd7X?!0Tf)4-3(bdll*hDa{g)u%BZ z4!MkGWtKv&pjC{WL5wttE)(+?K=BpGf94kG=!sUXjZsplimugk;p!|rjE7GL*Y2b-UZ0F3}C(T6AZ_>Y)6 zLecOy8RyX|n|L*|!c&*AbElu6sx5(GyXJv9M}N4|Dd+pugXS}Z6evr8*hCQdp%^Sq5hY|) z6aJOOX-u=na|=?rKXDcxr5XLOW7`3{nYyd!gMws%v(}7k_G*6zI#y2BGa8h8hKd0?>b5`{()6R1yA?PQJZEwS#P9p02 zb0G*el==qA?q2mFi}%YL!W~hk(4`qd;y+E4|157GxC)aX(PdwwBC7Q|QLUPu_zp|Z zkqs)f@GIp6x)$0N8r{Od^X9{TDNjKD)3R8zGcn&e`$HE8gG}tCa~F+sS6^;+tnyrK z0T4R%F)L+B@v!e%vhiUv^&!S3v5>jS%VqU;fD9)N?_a(!u^6EL)b;f1;qA@o>Z4Vm ziXQ*+a<(u={D| zZ7VPznFDprvyN3NMYiF~Z@pn+A-v?-v@*lVN*o&wY*V z!=!}8R%=-v#zj3Lz0Vo(K&g|aY(n?@)c32qz^LS?^i>oZ7e3HD$qG;`gKk7Wf<@T| ziuM(suVhexQd6B5bcE*`7l&>GU5SD~_3S|eKfn&*lGKx!`kV9Vq)^`eepT43+vBaa zQ^6Ktb&1!$*I2E`3d-M9sP~!SwG2vv&VM}pM76XMxZoWVJUMLye6FJ7%KDOQw#w!p zb2Q@g1#y+kOz4uN{LN$f;sYt9u>LGz-&TgfVt_U%a*?&cvO45j4Pk+@Gr$;_f!=;| zeBixqf1|w+B_*13)Ot}(=+{NCF{{Q9FR0XD(?Ml~56njqrBw@kh7|;cF>uN6rU>%e zF(@yw{L;H#XNqNJNv_smxsUP<;|X_Xh8~1Y$z>(%&Go>WYBk99McdU~x%`Kpe07xH zaGc5m8nTGLvsp~IS2-5cIL`4y>*G~-8TP}|-oG-6vBpnQv^R(QDn9RUGYM>~SJe}&`t95>T;WlYRaz4?dCx^EwjpqHF&*P#wj;jPIsTvw zyBU^-wiF^I2n5^2%}Rpl(NJ9>=xG89i?%If8|R&cb98%0S2;g!M$T>Yg=BLK2RG@F zf3^{GwCJ;iDP4t{!5s?e^LFJ7j0fkQUGKxEJ58Q5Qd=S!e{m;KXyZ5_PC#q$LA-e# z=}PO8uUrw8Sj9@|?DYu}MSrqvm;t1ROj@J>V7os4qRO4QS4ygu2KTzXBKQ1Bhe z-y5jAi{#NjR<>`?d8`NrFqxlzb?ZxhrKJcvz9>lTUJmw{cnlO87G%3Ws>!~F(}+Pi zHTL-$`FPx}YGCEVjJ!o`k5=3Nsp7bOQ2_K%W6vwuOx!2FT9U9CCQ>jo(k&?P9+cVg z_zKO}$KFGIMG#Lj^U3WA)8$Nv`~Vgc>^O;Y`yYQ2Pak?l)Zbt#%;n471WR+rY0wNg zf8aRY>l{Cdt?%c#(ZZ3&Zl0|UEzYBtk<~$;MsiB-q@;JNe$Rg@${N0;c(Y$cGATNGM=Pa*2!SK-Jq*|2zmKqCteqE8)v4B zWd2U~s!H{iV+)l@=M8nMY72|9jnmcIVZ*>pe`}6I^&f$pGopAFX8&fS_Z@mN#2|oU zm{9twHU5Ji7@^1&69U39t7cAt7buTTGU-hD>D&k+kRTb}NU*VKxCeBZeIbHKj?}6P z-9%qHCQPpDgd+7cY=%81e%|{;H8Q-NX?k&=en&66FE~4*KkL_KjZTfc!@f~eb#~y6 z~){!`I_+t`{ z0OC!Q1Ug_;^%sfT?+=XGP*+~-5XI}yw|3L2O|h}FU~7y87>PBi)ZL9$1UWo^xX37_ z%~fT-SO}kbCN%O|ey--+uE|%3qouz5$iYC8|0v(U4AP&a=7JF1>aJaNz8Za`P@C5{ z+j#nX>oDxeCfx@=($w^KD+<7-omsK~3Jaa4f{v8KN<^xGaz&p5ke zTrXHA-*Gm;H?%7WdmSR1jmFoPj;~HEj#h} zx8ytBB|ESLR-kM&`aldydYdj95eQZ#_JBrAsb^!)I^Z|nE`}|t#uJ=__p+ikL0`G| zCEzBzgK6;G-E!P;Fwq#~IdB?fr0KKcjWmLPcnfX0v!qb2Y@vZfzRSB~61 z8*|%2Uz#B}8$jZ;k=|il?U$z*chE=s`+he2MjGEQqE_k3ER7yPLm=p`TNpiV$`KW@Dwdwbwp>gKz z6C2ApLwiA?*Ho`Z)eNaEWtkB+vZT#j3;@YgXPhU-k6`}@Y+@UxZ+e*{w zNly_VQ%8_~8$-`!ZIV{^!`7_F{}E!1BdYeDXBazMYWa?)_L zxk7#usM1atVdqeTcz${n68ba#&!qb9iW=YFIbfQ3bi?SI-=uuLlXeIjMt2i^0>csn zb#<2!BS|3tEQN5Eqw#twipg~K%Jpw6%^D`r6=#~CC+7J1;d0RE(~-k7$+P9!#9OE| zdy!46uQ;NJrO07yUwh&bseIxW1IWtrn$fV~5supgZeyHYw$7Dz9LXKea!DU<(jz2| z292l#ZB6mm)83Zdx97Mpb-Xt$6H06aeA0*dra(gCLP>YO-$4nYyMRnB5*I6EBqiq6 zkDfK1JYwL_Ox~U;P0Y799}%Jb78@77<>AjF+TT)}+5iBmW=(zqR%JUqqp?H%0s z)K;c}TbDXeAvF};R>hI&G|*k;J--6tPrBBGWF2l&) z)VYZZ7r5h^*v0h$T4vTw0=^}DlOv@`fMiQ{pKdvzvVW7K9O;JFDM}p5n_`U#m|vyN zdJ)DJbYye;%;HI9mMJcJ-sl}N-4tgaY1CmQQg$@+vx3oItEh!*cH?2mCDmqyA_eHI zMdmXuQ4BR+GYtGD05vFq<*^4T4cc{vOSX;n+~*q{(tHf&>@M2Q{e7l$v!voO)m|`o zO&~JLY!} z9V8QtrC5lSN4@B~8J%Z>ulkjhuywx~Y4nGh;5NdGef|bs)O}KD_%JJJ=rA%%Z{s?B z>072*3FO~lr!C}AdjBx}S3Uv4(n@D4P#xtLmPa^PJ1w0J`R7fUVr zdw~%pF4pF-6ozM}LVVH0+HT1EX#!X6l*k!>*yn-Z{}eX-YtHhL6FS@A84&qc)|6WK2giGKek{e zPSmb$Vt6{>(_8G7L_kM_bMk05JU}P|!*6IK9IqfzA`>h2xu9*X#mYjJYjT)xN}f3NAY8BQc|<_ zk-->L1|&$a9imzN)GCd@QW>#0v3C5+VT_+VTV1Ywag>orklB6~^GMSMG@3lhH|IH- z3;RX4GK0VKr&Jk~tiig_?~10;1`hSNMO;H7O?ECvvU488-fzYsbV=ic*EBsAIgC~* zKG`qMd!X}AqTn5-EvYQ&e|l=IY;zCIHKB(wg3vM}>pskEkit^65YHi`{Yi%%h9rav z1G&sftm^wGhd5k{W#CnV>qSB~TO7H>YBcm4^4C(-4RAC-{o}nGbJPXhzjkV|hpKy0 zV;_H~1)LR528MRlF-|`x+@Z z_kAo*g&XgzE-qlRDVfzx8AQRkLJ?^rCg8>9;du9|lx97b3#JZ@(1(__$5EifF!OnT z%>lHxHQo7f;cQInf;%xJhQpU}F619B34LVYngErPPcRjF{|Joti@B8L&od$jJ*dGRx#*VQtkHiQX;Ti|_{v znLllQc02EoY`Q;KndL_K%bgVZRt=a1D8aK`_fcNTKQH>?=$`xX%@5B>(cTEgBtleP z*JlF7w~NcyCmxPYJEpRxt!6|YC)pvcU$L{(X9xlgLnd_McD`65+wzfMr!`Wa&-H>(>IJkF8U&+_U;L~(Z;e=M*kKSpZ}ld+n#__{)f9x_iSKO?)Ujg1D7sE}puu){fnVsmx~D)0 z6fjy!vIPyU5^}!jJUplm?nbi>_XgO!LN27L!j+Ts{KJ%;5sWt#+S@SCg&7lfpL{!2 z8_Iq-1zR;cxb@2%C{`#|D!#v|0Q}4~ocnm`>`Hi8yaKs@Z^8&b0U>TvYtB*hYuvjo z+1BrhCQ*1N|Hm5Ybu?Ze-7TPKM@wrb*&f>=jPyD$s^@SEWF!>A5N92#-_LUte&D)P z)=qP&Y9Uwb1@E0$8IiTQ51buN7nE||o%|i2j`AOY6gA>k2eIk_uB{bm@dBnA<1#nA#?D1w;Yg@Sp8^7;7G@;R zdyrSeaZk^Y-x*O#okx8e&VhUqxGsjMIjM63CojrgSw!;F;6d|)+61k@0fU_DM%79; znG*K6edZr9-T!pCU(YuC;px?_+vB#D7JduxLrYXkTqf~DD**9cB#GcPa9(>ty{}xF;k$oXy%?GQ zCDcRXOOCR24nOCC`cF`BUV*W)&=AlTjcCu1jRHIJ=Mu!@UX#DU4t7}{uA2%$)zLZ7 zevh*D9H7BJDtB`nIdlrY2^u&Z;gEw)EKfR~@*8-buX3NkDaMfLi&C$ioikKWKHg<$ zCsTVW+J(CLiu()M!5R~A5TgJ|?1A;$VHSHkOM8P>pi5sF%BrnneOD7vaIj)FRHY(I z7qUkDc&#=^8}ENuk+y}rhrhSO6c6&A_!v3gJ@2a$>3AwqIe9QPs0agf2DYt2DFdVS z9E!7EUSq9;g-@ddo#cV`S#Wky`7`N{d`gjv*LAfg}nE&_rqyQ6Iu%;$HUSZ7-OvMqeB!|?gX5nUQ>jLk(`4t!;S>RV0J zA4*X9HmK9QU3v$(y*2iJhcC};N$y&zcGRBe%NY(UWs?1FAsXlvK8*0Sj=u8G7Hhxs zO4ZPk63;K$$>{}^|AUHc>B?-K0EpWM5!R|=*(Am=EJK*=pXZc~nXBjPf!?2(f0FMIF97@=id2E7Crp*#p-2UoAqfeFitV$~ z#1y{CY}{tA3HY!F3M8RTL8+W}q~#eyg@TRq+d9&Uyve-fC@M{W+vBy8Hs)qiz41Ha zcphYW6I-WgmGfZ+r~@TWb=G-;jy0>X*7lvHSi+l-P8FMvxFGj^cUE&Vl>m`O0r zJ+}+lyE$(7&1&OhjHeyvpdl%F8(CptlZbjkreC-Ctcs%O+)A_))0C&`s^^9AXlfRZ z|NFoLvM5g*@fAUBxhi8vkg@I1+maFelTlkM5uOXVpk_p|=e8n?F99sCr_W2NzPDem zi=9bg-N8BRCSJ@W945gN03VX}96qP}-C0|Zstzh6v0DX>P03M;*}QzgY5FFY$uX&I zlWi=&CRKwow;eT!0l1;RakaXSP~9Mrmrr!bI|ALl+f0j_T0?s0S*Akb;$Cmtc}yX? z+P_yjz}`h#+QQG{>|Rt= z)V;S=sm)fgLH8~6NiKQzHP1?XCSAXWJM*JT`eDCpbLVvi-b!P0V8VEcko0SX98{$) zyyOhlubpJkych!?-NuznEMk?3PJawmuF}nEh_5jIfJ#@j9#!;EpfBJV%ui;s&GYyF5uU$SV8Dv zOO`XTBKB?uNlx}xO@ukOn`tRc21!4fC+14vcYe+!cFourv)hzxn)guaFC~E!wk?J6 z5b@pT1%iT*fKVcvsAEt7(&ie|?J@P}+^X0`ux=AO{2K!RlH`SOse*1!nU7=8sDZvc!JFvN7wvflR>gYLH2DIO0DF zyB>N0Caztax-OMRZxr#Dkg)8oA^BYeOpn|fRG(pw#BMjJ_^9jO8{+-M8> z^Rmvxqms37d!?VSBsBbBo|67P#;_0&?jozHPD&D{Dt=Tvz}-WJnf$dfzp*;=UXYVZ zy~v;Lgmdfu7%?{R5n}<21!z3DQ)9F`GX^GyNdhhHkDu~u`GeI`pAEZ)l=(0Po5&3^ z+^diKps(a72Ex{!8g0;70Fw?{0FqjqONEMeM<{Hy6rv(V2Nn8DZNEA(I$d*;VwFyz zA1534FQa%Wda=x7^bSQ7lf#czH1$q9c%%bO0&^F30#*~c8lG$fbA`IGN+brybwV;E%5>%>@r|8i4H z3UroDrLlfSr>us7@M(xHbd0L-QeZ?I-hHnCA2~V))7s|L!d3ub8ko{lhw};)A(B8r#YyHsQ@Ff z-cS;U-`5o+xwHt@8Uf_oxCp}%WG_Yk9_BF@*eSeSQ1e%M->EqMA%fvIr0?!!U4;+& z&_Z)uT~;)!E;wK$@;?mA49o5LT=IvjrcK}|iJ5od7T3A+o)iE_m)uw{ zbhdF1wIKCJKxeVr5p&0bU>L@hKp?Bs-mF1)|5$SQ-feaB2JrOUh&l@ur}BB9v~4HZ zn1p1PQhGlLy+mSHu39Eu1%KIG=QqFptmMz<#U`8CKTjf;JICLZ@jrDbEckUwMO2lt zc}R+g{)w-fKxxxn?^-6_>Trta%>XR$F|1SFRkhk0eB2ysxc z_M{6!7Z3)(Rp|5eDfyQZLnGED*UCMsi|ua9y=2$AxFpYb=RtTK(^H+6*(Li6{||x0 zT0zaaIR|^q6nqd}PhA!i3Dqsw8-pNit-URgcwvGiT#kF2qaQC-H=lrT)e(o&L=(n`$` z(y4+2(jXxMihzVLgS3d0v{_E~$awNFn! z?i^+7mLh08j@!9_aIuf;w8JG?jNvfvaw45%o+iKB7-2{VwWBy~-{3M}ek?Eo<~ag2 z-@|Y0XZnr*^!77k`1z#={h5#F%~?t$;^(=fZ+E=Ud9sqsv9XRuIvZvy_|vR(rL$4< z-;fo<4bwH+8#)ZIEN$`CImMlR_<|F-JT!@OY%eDdWhJDsO8fhdIw)>_x`b-CN(&cc zWB@2Q4`R2_C|Q}~GofX*k-aI87(L9)J7VKjT^))J3d^xR3C^1?ia+i$oq5VaX48DU zKEj42!Oa>SK#Q4DwvUfa4HKlzND+d_ClhM6jP(dBmiUoLNKSs+0<)8`mh;l!DM+4<%vq zhkBE3MB~oH*KXeBSIBdbG@o3dAO+55I((RKIEX{0 zo-{y#3!~$4>n(49um`W_>h6778Ql_~X`#TW9IgH#tdwc1iO!~-U8zs+S*E)LCgsyn zMZL}Hm4H#U@yrUzDWemCs(`is7F)*s%BMb!!)>z2lkQF_?u&TGkStwvc*5%F+lkIj zOE_oIk&bWN>mvzArQID_OzF11(-OE$*`g~gPNx*D+$6%GnJc`MJB|*c3|Son(s-fk z^y=duB`T?3`6W}@t%^S05ueEEzW=@#Jq4cZQiOTLY2p#N``3zLt68dU@n?9By9#1# zU6BG0CF~*Fr!GYhx+>nbCkTrN3b{SzD&|d0`Nf|_p$5{!#Tt@Gk95`%K5$i&xi6`2 zc0)}b(Eapwm5#FBf7V04r(JUQ8#!rJN^Ij2_j8!Kd|26a!>=CW9OHBS#1Tq~+v}Ne zn>R&{t=Ay$clGl?Ym-AN_oj)O_wH|wyXXBYi`?HH&|REJ=(6+?$au4~Qaq^}e(*kE zL^qT$htDuPob}Cu4f_jT#|4oxVQlCf0BcFv2^7s|@JWSl=e$Kz37mxh;RzBtS@0jP zAmIznx^)9S_y$&`Ni74TcOi$kBWe*y@W)huQxMX*1bxoy@Mct`=6Zxum1`ye5ZxU6!1ysP`n0x(EqByG6v@(9DWsMNUu`f>s{F|vk z>_EbO!pbV8?-9)~OPx4>+wYVRW!Ufz@SZwh0KfNX37O@=d42JvtYz{GN(HAx-LNFL zwR3HgYPc; zI`Y&l6T-#7Z887kH)=2wGR(!zzyW}fnec6O7(+5ZHpOL1-!$Z28Y*L(o@> z0HqMe8DcOhfsE|6s`8TSbjp@#>mo-Cqd*IZuQ6q}bV;aeFp5`Pxaf`^jKFo!Ij~_F zSq@LAWz;;|m>e6Bm^p!-S_#N8{Yr0c14n8w54>Ng%;6oFS23_v9(Mz!*` z7ndOI#?NM-Onm$sr#0e*bewj#(Gy<;jW}uZ0~B@6X8nbNcN#NfZnY z*-*3|@_9jBBh~2HoXM8sG1`FOxX#VnBow*gAZ=$&ZTIWkYy4@HCxil+Qzy6pN>Gn2ZC9=8yS;I`SMr;`buD8{QC3$T%usUi zdVZ~98~x2z`#0x6#ql7!DQ~98VV|2f2+V@R-?X!rGC_*nZ*B$#7LjvdMk<+*LtyuW za2sE6>cw1Q$Fq=0NpI08@@L(<4(r^rK=>BZk^c^g2Bk%d@fd&FrHh$N{_;j8+3Dc& zu6o9WDI2%YxmpDmh6ZwG^;k1izs|rLcdI7)k=&EC=2C_%u|XYCY@qZU?P?O!}K29%%)+M`E|_K;36K`1tu4#^{9q zALNPG8kh$;Bx7N*wD8@5c9W?3rMq0)D$M;`B+o7to}fbHE+e?n$vlf{NA+0;K`MmI z1Vo|#g~Fj?zrXBJmah5b<~j+Vn1}u2(5Alvsb@}f>R3joA1C+9#b8j?sHXq^(+_^7 zJ?}}CLM59C){ZR4_$JF0$fqA?oH@LZBM7UPN`J{QYnPO7tjillbwAp1!Eslsav8C) z4*2j?Zz*;1D_x9A*kwp`|G}@CCX{O>am6yk$w8%qnt1i&qv_WS>c^Cqk1OW^c8m5e zkjWiI=y)V~GUVbiW)C&^UDax@WaXN9<1MurG`+t0&GppCDR2fv?@-fq66qr#g}jiG zhaMYaakP*iQw!FqtgGVTHfmr%8BJfm+&jFW)aYGHOpBDDz&yuuc;FxS z-M?)>HbvH`s{eVW2S4pw6?tGk{+SdKl(?h6MKq~!d2;ta(*q5Ei$bg)O;^$<{L@bS zn`GF(f(So^mzdq#y&}AC#18d64&0+_gWq^agPgzIewh)@`J3ic@W*AH3d-@mBWnGD zec!2Zo6CgS>j3#WF{+rxuz47UQV#feKEHMC&4sZUhaeU7kXjzLLC@eWDq;%D8d7B#qLLAGmYCtupEQq+}=tv6z%0({n0>>l^(U{eV!2ir;qw$WgG{!LS%Xr zK6vSnP0^vUP@ho6X)cX^q?`o-j=SHvG-;9O%YUoba}N3WXRQ|wF;hKJ>is-#AYp`q z3M^=L#xklO)0GTVUp_`F{SG9eASnZ?_e`N{0yBd_(T#KW4pO&%=ZP)|CTJne-2v?) zqP?u1;`DnRZKmqS4B&1vEio&xD6wXPIVt=mBMXX?hv<1j^uUYp5^qYRYpvPOd_LB{ zuyc)Nc!LC{zXBf)tpx9tF>=( z;zw()G|#V6EAMCZWU-8UNu*Lhc!OSvK+MNCghe3E2X^H@Nsx_z4k@x1umR8Y_?y&l zyFUY+oQ(_or7T%7X0)x8zM|m4KwB~p`2LN(?`v@^e(OViNEWcc~#Cu&v*kx4u5UpOk?A5Y{4O3A_dH zU>D=n7zVe>-hSG+L9Wdn`|fPuZ<}6#>1#;_bwA_&(eIbIrR8?j(}}OgoFghTok3Kc zPmFdc&_1>LtFG6bwDl%1<>H_n8;)?A8Iof0Pty`C9O(Fs8 zT30K+fC5=ZNzY7T^=h@SvpG11^^@GET+mqxdI|bo&VIXE9am%Lp0E?`wM$qrK2gz% z9}4bsO2!8HY1*7ycBVOBPiOVr=bD{v@$g>2#;=X@gU4)TfZiKqpP(heGH}{U5(bDJWi6WmK`84`+8PU={j%_6iWug z?A(%$dwYa#xrCWxQXr^=q_?A{#-46uPL)4hqEIm1Y&W9p^4kMO)c~cOY{kT{KUx$7o6G!7Le;yhHUrN_!`_%Ni14lR{kYDn z8I;Iw&(`U7_71SWTdEiHJzQZekv-@rFx{m(a%U(1>Yh1^Aj7l9a*~Rbz6o>r4+nSs zz=mdZLTq;uR|o&LYOesXx@w%50r7$B5W?#zLZBBDbY&)>nF>!EWq!-KVm zI-2z-oP#8o&3|JLlLKA^IA>7lbf*pvq784;fYa=BpXpZF4!%4n)}PH0#={4_oTT|D zv0l&e?-MP2Y_eJW_?vFhY2PX+9@}g}xx6jfK8Oi_OkPr3_Z=Q37hRja%1Hyn@M>ZI zTP4+xxv$~Du&dnl;Ynw#y)uupk1^e+ru7238&$DSo!Bg)?(_f3=PeJ$+_w>fAHWcr za-4OXef;V;*Er8*bF8wnv@8L=q^G<={WGW7hP2IsbBqrd_p$X5)fr!mVu#&DZg1^-JF<-DfG~shlv9=UVk0PhZ1HQuP zh^;;aErz6YN~Dg0&K8iLQhThp-Z5pdh4rqA`gPHxdGR_=iQ;z1Ws;81YP0@%6F@7a z=(N#ht#H8Jw8?XYZu9klK~VfB$|(g^uCpT!(RLl_Mi}{&aK@M#0_BKuM?FIY1vo3% zoN#{!DI1!{3{Jq16(_;A9$*!L2ZQ0z&=X!#m!Epip31k@NG}y1f1 zfsx+Oyu9xi(C8}*=1%TT;a+Ux;xg+@fNyb5YE-0kFN1XcJ?^wE{k;3&&S5Minu+$9 zB@I&)ywA(_#L}}_#tz@e%SQK9=QSUGgL)&W*6haRCZe)D@pAI8kFzh;UAMg-hu#=O z=L-VX>wDQgOK#Z5hsY7)|G*Kl)impv_NfRkS@G7adpcr;X2SaZd+1-^C7nC`LTA(z zJ3W#%QY@GRL#d<>z{YtOdlOkO(BzI|R0Pgi;6Y@^Nt5@TOmn$IUV^Atvg38+f3F3e zcFMFfK4FM{jfWwL=C{qho6}J86J;X-Gf{-sWoGvPuDe?0@*i2rhHS z5lGThc25Ex?f!wsgj7kMf&IR#ZPxYwO`B%K4hy1OY2BvP$dQYKvqI&N7ny_N%MwM; zphj{QqmxuuS9bHNwqjS71(!ZuZES8ZsIweFG6k3m)F$#2+zP zzD99npOZVzcO{O?gMo)b_2Mf@z{Z6@5RK?#;ib?( zdH#XN5stQvJ4lP(9Wc~~x~i1D`k;$dFzbh|U80TQozvPF3_>$+OI+lhSbZDVfe5TD{PJ4><)2-*t^~S7w&dF#ObL?luu5cvbRAB zWE`F&A3Nw+p0`ho<93RNc@rLnhKmWL;_XZA1}@2c_Ts*?N3=n(OD5^0XoX8ZenxEG ztAb_hfU|G&PyEu%jt%yS5eObcEmI4EWd@XKa`X} zfqq)j74j8Y73LMr6@C?w6{%JIT&;%9DS*RCc!4SaPyLTBIdzj%pT6q?xNju@}ARQY1`FWTdR>IxTVBvkS7{GNMk{$H~*4&xjvo2Cn*@0t9;xcV%_ja49;S!OA5gJ zusN-t+m8C&)3SgFlDlGzO#MCBA1ii9;{RtheZJ+uyzN)!(1Z!(^_l;6zyBk$TY(?< zfbk|AuuzIFNnu%S^ivei_MMi$sseZ+44k}LxhlP@Zxq_}NN?$aPbkFY6RM=Is%qqU z(8{55O}u17dP6LYEyL*ij^_JiGwk1gcOykjvijk2=^}XMcc5Y8*)upb8gnaGha){SkXz zC~0fvO{P|f0E1wQ=@%a*G3LZnM$M#lc80235-h(WEmn#gwY*2UUD$i;8>;8vIX0N8 zuy9TF(cpXt4&P5#8oh1MempWx#o%^3m3*LeNpihuY?K#}3&gLlaH)P zdH=)5=}aNL25+kSCEEq;F%Ro>+iCqFS`D{?Zpns?8i0zWx%WN1F-9739v?*(%D+`I zP}IL?eWu{+!19g4&SbOwZgR<#iP84MxEAi*NPx}VaAFh5ZFKo#Oq(PmKjEB;`yUQ+^S??MuGbBmToDsm`?kx^= znm5Yf-{)Fhm4^JhJ}S#7qr{bAadu?J_MsOYD#2zj!cy;q6YzA{q!16u4=4 zG5fIQ_w(L|ZuC|?pzz05XoSh67p8&6NIVN%T$ZU(!nb*a)8Xo=t3eN&Q~w`Eq02Ww zA&R6=>yRzlOW-(u7k8GLL~vN#JuG|c`v&g*O=Q=Y#Ja{<%wKWpnKK9kCH`^B`|qO? zKJ%I3Kccj*X4v*v1WlEVT&o|kS*Rc*5t4a!%zg`+Aq=zjBO~$;IV7!De%9Gi$#4;M z0nTNID87M=0?HfAM`>7LW5Y^MKu7DM2~8mZ%p3S^<|=|d8K5pr_ycXVoCrmTs;ar; z8-}AxrqCrbTIXP-%=Oau!A;KZY;t?{$9s=LgiR|xFt06z)nN@cZy?h+VSJMiZF?Jt z!z&Y5f|ywr%U%TVR3`OtPp0sC^KlS zP%HL?G4`>m*?BL3F+i@Gy_W$DR+LdBP)I4Z~RL{^>dZ}30p^-`Qyb1i_zsV1uet_QKZ zUr~ey-KV6!6PS_t?a7==+vCx#)@GwQ+kDws0YmLprSh2h)Jn#Q%z&V> zkj4bzWrf#DST$?jyEpG|A|~3Mv&t!=W#7ZbqyvN3eLCoS;X>y@cvrcrU{9y+-{}K3 z>mD8P{h|GbCId!)`Z*A$H|Jf;fp~D$o&xtufG-v&>*uyd4RJe4b&F6t($;Sp;Pd`% z72UI2&MAV6s0*TXR#{S(6v(c0GvVPig^$;ez3_+TnDwiV6NJ7qE~tCcE5jkTpoc^W z!2*^9Is*EP$bh9tQ`I@@rD^mb5nFTc`;Fa6hh9wor~5)TAk}N zD?>|Yc!Jb3w`uFJD1yx?8en3_yUqh1C_*Bb?kEJ)(?a(nO4eCVo^LW!Mkwi+8R@8Y ze&D0VxZ+RP!E2nPwmvxl&coK=O12z7qiQVu$P9TsEBcU>6?L6@E6^ONcSw|THfIyD|o`8|tqjB_^-8k@tN5LiY5Xe@3$jUY) zdI?upB$)Mg4>(DjxV|U)G~T(KL|Mf7!BEeQjp77Y^X9n)m6qh$_0xW^OpYXk%2SLy zWA=tkN+oQ)n*1Y~dd~YBh>!V=-b_V*{@c7XJjLk#oVgL-Q1fS=ah^7n?Kr*ZMifyH zttjN28r*!0jNUmh^SvMY2LiqY8kKpd+`ni@loGS(X#dRi;qwui5a?8OX&1pS1o<2) zFH^|Jz0xhEHeBh*GsWf>h4^*We#Bjke-8S}r2%pA;9rvCh$;fh%*k~xL+1pYE!$Ec z-QVCp0v%(J_b1T_4^+BjzQlc&)9F-gOD(#_f8Sqrm$|I0DBOdgbjdWO?dtBrQ@#p8 z>@i~gu;ClR)qyYS7q6&QseoQD3TmeSZwC$=TsQj|&?z~|4%f0#z#8^w zBgs%0I2QrN4yfvDIF{=_j7A=rf8hKWtSx)CD_E*1owo~aLODMpnXX06*EADRoBS|U zp9wt;X!7s22ZT&2VJ=99o%2z%p)tnH7_vf!B**x|b|Id?QF0`l!bp&p6C;fsO-#HV z!n)3}iE|8^_wYT9<*79G_Eje}MiHb}SOn13y~>l>@(jE`#6pmum5_foVXoG+6m zVX!^qVTKG;Yakc5K8N?$AX*0`A35_8fd+!g14%=^Axn^R2*Tsi+8FRVphW4);w)jI&l*`A}CbOq0Ln;F0xgd zjpS(>v?kqA0Gbt47eI2>97De_KqQfFS~jP&Z4!IwxA%*FEZ!p*7EEEwvRESTZoB&I zozm8o$&hSmC7xxX1@Eh$6|XJ?-{hvbvEsSa&wr21gpJ2o?TaDZ&VJMK)MutTgcxY{ z5QQ9Fo{LIZpHlPnUFaUwjl>(Uqy4CE6)TPM>D8h5p zE|vBNU&X)-4=$t| zepBVA36XS-r16;g#bSyKC?grf{k`v^YEx|sv7r9Ei@o=j_@lPuvBLkEJ9h|Ju-rHlC9=){`sm5C8XekhIcTZLOq9{47I`O z`beV)^1{&M>y6~nl9+x+4IT1Sm2jZWYit(i+Pz1Q9J$_WWE#M5Vf%P&Um;g&qSSBo zH5;J#XGjGXVe{0wE^!1=81ti_fG_~1_;e4u;R!!1omNJ6%|U9D@~?)I>)s|8B=t9L z(Fug!t;Er1^;eCW@Y;Pfs_9+SilbB8x^_Ck4ca$LAqdX>V{yrM)I^{}C=Q`Bi-X$u zU3C14s;!$NH$Oq`w7vRDvf*>JtGls^z@}}d2+phT#ve)ki?V|k@iR6LfM`6+_ab2# zXMh`YW1=B*-C`_!2KD4;tB@^>9?d#Zh&GGX4RyWsB?U4qOePsV{@vo#&!dl#XC343 z!NG)Cpx-l}b#-cSSm<9jDJClOtbIEC`Yv&NYPt?Z#J?X66hUuKzk{a5N{(|azcjHw zQ#UX8Oe@;k(kubHGrlF;DBNS!bz_?e*?`*vHc35mcYdBDgfE9gx=iNRrcKl!hDDhC z@a2uPP(|nJn`STyEPY0?g)8E*0NeUvAEQh!sZw=%g?;;Wya+U0IDrr3>)L*kUqQ48 zEzF~(R|;tdUo;+@(-cy03D6PtXmS&2i4fE0i~GORzW+sx=%S}+nl0$pAg=4hsaO^Q zLHVD1X>=PF{?|=|5RQ2eg(Pg3yRuuNN1z!UM~m3yF5)|!miwC z!fk~%_3O>?-VT4r(_Qh0CeH`{^c*Wg6ASbpyBf(va+0lq3gxL3AKv`hzgQ2G%J$5h z!6XI4X_z^bHKfMlABXUtd|w3GCxmyaE$3Js7?2jD3sVt~@Q6W8XnhJ75b^6=viH<$ z{CRBt?tE?8)cN=|1Gbc9tono3X$AzPo0kJNUH$vl807wU-{*;+L~~O38_H~0Iqpb_ z51fk3IM-^Q{a|T2BI+=lkL>r5rBvkj-u7&c1FRA9Q-!Vpnq4-fcbd?nVGR`P#~tx? zW?SOZR=)^$P7|zyUDQ0zf{MA@xVundKvl9g_O0~2EB3qfBFS55NASa!%jQ)9`+@}9 z%+u9&h{i54YyDAE=m6pZ&Vk$`>i@!{(!LWDj;!D?MkbfWJ%UC-W$Sa0=uZWfbx5F` z&*pK8OqaUbt{K8m;AWXFp^5T6z)!H1x&TZrRh~N1cBt0j1xeh~)HLUsHinQA$yNDQ)OMb`^52jd+k;q{v)%t1g|r(;azb8BBkY6D1m>G7YK?glZXCE_e1%%dj~9Mb zHB^JO_H^gO%kA4fRFCf}-0uAdv-=_Nnz@aR#FU2zx)lMI09q}u{yJS z!qXj_?0xLx-U)fJ(hb*H!uh48Vn-FxZ^0IGlDe3;&+@T~4X3%EW>MbCewHLI}gzf6DR|*&^u$q4)#b2}}fRecj zyMNe#&$?(SsI1}s?_0i6Sg?W-a}_>uPazm}iqHz;L>!K#;CN*Tp+~miZ$5_5UZ+k5 zXqdozE1DAEO+JyYA<+`sZ5C@G<#|iAsOtC6G26j3uX_pHXWt)MY#d5|dIc%*dZ)b# zUuUh4yZ+K+WFl4%*-wL@or8oE51%u&5gCSu5bOjH5WUvf4!K(}CdN;Gx2e`;nXxHD z`b)S+M;cJ13J^gS!f~##HNF_%@i}Bfe+guq1E>wqdTu*g5GVe5)&hUTb*fgvlNsKK z-eQ|ns))MTwR@TiTJ9iYC5gr2AvjyytLILX~|*? zh8fvV)v4Zo!e&Yyt!vkAMcmm(Nk-q#aAPGceh_!Y;u3Pc)(+2p-SF-1?VRO=-1j^h zLHrFvO-Hu&Ch^Y?(7DvX!w1mp+1Ou;LmFhR#%)tizi^XWO=S~U|yH1GjBj z9-ipC#N@pPgmJW>dVn-W!q=b16dS~1703cIU#3}gRG<%X%4qgDEX}$L1Ua(I{w^GP z+mQ&_e_VJoQvL~9cCbP5hCW_MFLGY;TsA^ZyMf5ckRG4#NepiE_-sWZmXha z=$h;?cR@@fD9R=|rs2HkBWl%%0M&J25pf@tJN_m74BBBi&YxE^ZVB(*J~wMAwq%Uu zV*O2GydW)ESRm%}(eCK)=UWGDOpXx;kJ0j((|mSWyLnZ`kof|kG@P*e?z=yk37dkHaMjgvy-JELIVD(6IV8en@=xN{3pa@ELAbraTf(F%5pwaKX0!KjPw1@y@s zaXjnquAxC=pU!qGPQWu(pNnYzsnhX%@Vi$FX--&oGC=RG{XzKW5WB>e6|WZFkf@hj z#9y>?77{o?ZZZw~@7G6HB$0yuRNlfrJj`cPKfU0SaegQOQFdA{KRh*IzS-oi4D}BB z9Ahdp_db>)RBS%pu;`d99H^>!)JmC?me1r4cs|PaYV8sgjSJ5q!W~bEl1!MAKXJA! zazXABw{3+wmR7z&Ys+r3MLJ$l7f-troGXW;Be*VyeW9v98&bbB&%<;cnMrqGq0Lg#I{}gj@?*t{JnRLpTg0$KB*r>^Is*s2i|~2#@fi1VSF25 z%lbv34HPNS8k6=lqxvHermHG^^rGaE{Um9Ka2vNZe86Dl(;gAeMZ*gv>D4)og!kLo zLSX%u_iJTu0$|bFpN?^Ui~ZZEEOPsW;X?P#6!bSW!ehd|?sT~3oxsF` zs1`#LqO!Q)dp;yeH)L?CfstkD85%YZQRl#6wQ5 z@FQ*qV3MOVcU$tkSr?dU=R?*6PrN>9-lxP*lNLw`&}sSYh#WB~OR+5Q{#}p~fQ+N1 zBtT2saji|6cg)kGkN{}Y8cW4%KW^x{Px=pM^=bW_#^FhSYNFRng0U^=&rGv`dU2qsTjyVJ{XU3bk4qO}=k+LqH9$`ZmAXZuZmTT@bZwa#_y`L0#l{?krgoq7RmkGqy|k zd$u|pgG_byt?n`eb=OCtrDb-c?_u2g8TPUl4TWX#RP?;V8? z>Rg8=oB=|-a8OFEhhY*}aDJR$@|2(CU6PS}5i z7EAjly%5ke=GS;#iz;(j@@-nHf4fU<=t}7|i5UEnw!nbzn72Ln0t7otI9bckO` zv-XYnm)sQFB5ml0A1Zbh+a2!g8Rk|3n{*eFoSTGrkgKMxX?@Hmj*xkFho(PY;X0^G zg-Vx0-U=EvqaS+(6qyI4oK)Q-Pq?Gzo1%BeF4r)&;(Ht44aMh%DVW*p8H{|NGW?&y zbCYH(@RGOKHm9jP6o_wqRfuGQ2*b_yNuK<6dUTsS{X7BoHo{FGD%|$$?br(mHD+Ar zk%zcDV#onEH+UT)f(wh_jsSF)&O}a+-hG{qa6xAO?r}+(`kI!T&44ia7i&Qn83H~v z;-m(=yvuBA3o5Uh$93TyyJX+&&fq+rok)HC_Wg&8 zaT{$?4vRUVrs5g(t7%wgCr}y^bO-w8#MajMO4Qecy4XDP_?XHNGXI+0h*s^GZ2oiW zeDUD>LElmUk@%TPbE28W>=ZXF+Urbz3#->e02A@@V5GoX#m4%c9y74u6xoTizrzzr zboOj~gRWkGA_r9&T|So)7zvTLuA%+r|NPpj>IzATo`}gTj1km*D<50@xo(#M7lsvw ze2q;BBlT1=*+8M+FDs0q0PE-@mN6qy`I(oSDdB`nXS-|{#eb5=xWY}~COAOh9n`rA zigu1=Eq~EA_js>>*-iV8ki4vOBl**X-thr5QKB3oyUffE+uYXv1dfGvmUe?SnEB!$ zo>}`i)TCd1y$Ls`-!n0i@n%&0b<6d|=d||1PRH%DVEH>Xpwi*TtvX+~#;vn@;@3gZ%TeyvC z;at$iorRL3W-ud}WZGk_C1#7|*%Usv47_9RUJQ@09O19we!$ICTKsl0HWw-jP@~HpO`ug3w&N*UpMrzgooIs5?R?n=$2coxfDQ%lU1+yx^~7y;dzR#H zP#VYka?Y_@VhY%mMdF+2Ph&6>5)vUrtLOx>Hog7#5bYTgRWnu$lD;&=1wW=P8ySNsJ+zRC+}xdG#&mOigA7+In%5 zR7`W^=(nF$euEE|6e-mx!v+U5p_qpXirnd=EbtzBjLQPQFx-@!M;ok5Nq3$sd??Uq zt3!%qRabwx-pC9jx1l)lj#nZ+XJjdII3-_#ABWk^?4G0^Q$#bXZJvlS}|_G5-#q>JNBCuR3Ze(@y) z#bZ+5yd2Ts`J}yIGOS22x%HA2=-2g(M&a&9pm3EL@xQ?OfW@n}O82lEaIQ@VE0Yal za$xQE2{V-~ZEd`Rhs#8du9eDQAeCmv?8xyObf;!pj+(4IQN2-0|2S%G!!AR zJE9)?#rNd=5x)zY(pE~Sm@YFvo}|sT^Uz3do9$_YY0IO3pWdJ&rAOQ{4%2S5IF>D! z_iow%V1?OBGxcAZm2dt8Iqq%^{S;&+4^3jE^M>MvJY1QYe=$}R{xY}wyfZ&lDi^Rp zU(b2or@<}Qtuclyy9Fb3S0#udkGZz#{}2bzJK*jbE^9C&0*^kuGZrKK?p&N8&9q6h zBvg*=T90h+iumI1gC9HrZPQnDieZAeA^vtCD}aQUTGsAHzX1TEYO>l8#^1de9l<~U zQjY^`*V1fYMXZ?ncK%$M6d>W67>w=bWSY#}xij$6xw{3|sc(DRP9>KRqFm2$3Z)^4 zt`b09pOAiJoC@}8=98aLqy>~6#;QZc6U7`K{YRph#)kPuv zRCY^`PwxozajL_D#V9qV?%&jKwVR}D10!t{DM&daiNrA-EI!@D*LnOYh+Fr?X@9~L zEirQAq!6qjgpg|R;x_HWvTeu7oi|)fkCzd@C>Fw3P9OpxVEF=QsRcA7tf%F6B3mg* zw?RMY-bovo{X38Iwb{Kb2C+1QkSsQOo{D`_PRJ(On}#@t?zSCKd27afm{CnTk0rH> z{SH_QJL}=#h@|ywoU!7uVNIsvMkzi<7?S(eYrjVNTf>Ch#qH@O#P%VJHZFwWPq_t9 zxg2i+Ec1eH>-oPVw6JG$0hPOBkO6pwp*l}9%)-Xawe>3NPthCHF<~Ru%jhB?>bA;o z7fUUF??nCh(4s3&8i6G85vcQO&B=N7$>JZ97P#8{^7fz{o}yYW8tJ|Fe&)>W?McPL8(eZ>qpe=xdal$rvNfruO;oo|q4ct8<9CaRU)kISG)=^*0WYZ1%y z{LZ7|;m;r+X>BL_RTjc&-(93RfTs8b!tXq$;o@(|gz4#!>?SZiFK-x#XWXZ|EOVe4 zt8oE$?MGzGQ6L2appBrHaTyirAz|2ZAe z;d&|4LKUy}_-4mo7`ZRuw$1{jVP=8%74HU}#WNGKUTHyshhQ>L8MWK4J!W*C#Yo7f zIj8?Kei1=xqzn zg5y=&H+JhycI&YhTu0}NE41U^SO_=s6=aV2}Yhs1w)ue8s)8B+dWGr#O4 zz70$DHzZ#KqC;ra0eP5qVYxdF{IM%T@8Cv#=;g3(-DU zShw4D7qgA1CkqjVBx^#`iNI*C@-UmRzonoW~^GF5}#eUfV+ zO{nd{j-SWw(Y`0t3?V>E87^>!E%WqRQ6g{>_6vo^#A%+t#=Lp2W2K|hJ2gzSL|I1H znnfR~64`C!smTiTCO5-$MXpXSa1NVxRvz9ESL^pq4rg@G{b+Ih-xCR5N#Cj18(7;{ zLhZf|j2|)r5&T-*o4YR4FN6e-#)jB5AZCnR2ntFx^~*A*f{Q7;GxvUkAU;l?0=@48 zZf-e~Kc+F^!+A)#N2IyzJUyhXoM9mADFqf^0DFwO>;Gu2_8Y)BA0NDYd*jQ?wjWtL zDykwdP?Z`7I`o+AB9*%a!$G6jW%?Q>OozaY?2q(BSc?Kkx~0kehob?;B^TFjX+Tk6 zVAasijutw2U9Pbix=GO)7er?M;$@9XJmrVHf^kIZh1(1;-G$#x;zf)pN^0s{*5G0A zy_KYKCnu2$N{6$!cw><))Vl5FtGS>Dzsi)CM42GV`YIRWItJEMmN&I2u~z&VU<18z z!f)$yqLB80>!p9gBXyX>+oiF((xe*g6*}!iz~@yS<7)F?Zb4k-fMkFmI1!9w?_qdg zGS6U-sDt^t^We55!}sN>9etwT%BhSMTgQd~P2UH}@gI^QEcj2GLm^{BZrMyfi%@B1 zug_e3`)x^ZZz~796qlpE$OW*2zn2`70xeRC%y-ZihGm{lJl2hv*(H)UEJ%TGp*6aL zWeZ1o*X9a-?BWMvT2Jt4>oX_JvF z<*Sp^(liAYsjP1pZQ1{NvAbjcfxXYVHWQ;9Ur2#7{2Nl!Con+>5meAlLvL-AWk7aed%-9do=4svV~Xk3+{RZq<5~swb$*1+%@-pAHQ#ub|1(C$S6s%k-69Eo`&>b<^~;l3{@)AN=c|QrKlW+m2+tkP zTpa$z!sys>u0ghakI_|3m(9vJA<{wk@9whXaF${JF4yFiR|4rs^ z#SeG=j#g2BL+s|7|UgD-zI zIe}$ce=xosOn#{@bE1^3hMQ0nCI6{vj1LGb+rLvDbbwC3mCSKO)Ne*Ud0g$4c1*`v zw?)&m(j&g{;k4ecdf7LvrNx8Nux>e0ap;F^DqvlKE7xbjnLu*LW;*=QphNG^eWBlA z_%+4;Qv~cJ&lpr1LVl&GZa?G-GL!J$bR?*ZyhN^CiHg2pmt5qxd?*|j)Dn*eKqiWI68Rua&=cxjToQo!N6D40TZ2GE z%OZfWdo<4oJ!cB4$n0Z$KEb|+i+iTv^g|N4k)y5bvIgMF{~+#Ap`yOXR}H(4=JYtN zd{_&uUC%Q=KjEwVA^1dpgXX7X*3)YaZrm@)uIqyp4h60{r6d@~EU zk9gTPcX!n9*PG&?)z%kk+RGo$d2&^$D<)NV;UBQ^5aJvB7p0bv1ul0WhSp-eua8}q zr~eIXvdS1SMK0BM?Lu@`ANBW$F02~3;J3)8TXJ-Nkt!9Cl;$#nP?JEA63ujq;>FgEyKLcH!PDMtd^98>=`6WRpDq`R+r(*#GQ6uDn?zBXWbpcZ zJ==lra0`|xJbMqT6kc#Y#c*=tx?fy)hTO2<>9x;C9w?KCZeqCK@MqV3bAncsrMPMq zvo)HpUYBs6f3|)9hA6#5pep7uy+Z?3wF3o+nJ)M31*4Q1Csqp@d$kwH$w$PcNbn2? zcn+r20Ul<86EzSh5*FO*laL;UzwtQjUi5{~L8q10vh@!{%>*TT{QByBL$F7{(lm>s zM@z!{R9_~ZAzE6|o&G%QBA5^;%4iCLrw0dLOy_dwd$*lKph4 zT?P|?opKi0pZp!4>?tnJZs_UBj9%5Xr%C;>R+leXv|)w^t3e>~TZ+04IaBazTw~(2 z=RFyl?6~+iAL#~$$A#wT2f`B;*Ysr_J1k={*9NI~2lkx^*gaS%wx(GxS`{NznFWtB z3O-W5W(HD~34yi27w8KvCKm`&J}@ov>9|KjmJ+G^u{(bU_3#||l?WBE8e>5w=?T_e ztkBKZg27B8S=wWgdn9j|Llm%p?9 z%~-AAgb31JHa70qgKW3P%3n3bmTZ{OP2UWami~vc!stCUz%1* z{Ijp*lGU2Z<{w45{l11T@xcy{Jrd39o{F1Y;SN2W^?tWfXBB$yd-4LW*yX}5fTY#^ zxfE8f?FJ)zbSV`$PwM(FqP`IYXGcHTiCrXuqRlWFe&J~OHlOwT7|MM&1J$h4{hTu$ zL*)ZlfvV)E#s0KUm6f+vm2cSN^|sorRd#V z>({K)0j`%9t$PI5Fpaa=;2g~uAGuUv-rJP}UV=ts8Psc{uqiIm+wC6&--F+xmxR;8J97h-`O9SwHs2yr0%9|D$zx zD*!^HS?CZh>nzmcI9GFZ!%ZGT7sY}+68xpzkRIBp6RzdJh<2cid`EQb$2BSnP*}Na z32%9MwTm4MqC3p$2of0PGY7`uJi>n8l*M`TK1oO9GE>yqr+9js$4<{m3rYQlod)u0 ze)w$j)ypZZ+5o4!O*dES{l*Xz7TW|x>1qnlWwk9w-sDOk&67I+V)VFTRi$`^S zK*GXr(K~J23HOl4+!+Z0hZy-<`Flqq-kb5!nCnrb%IPh(9zAEp8$GmGy>E`p=VXW8(%XM(Jlq1_7>xm;36t+3v%bl729`Cej zwwpHAQ!DZYjuW^nVf&9HLFaWi%4_Dp4TMt2${?kuRU;FOolIh#~eVZ>QQs4jiWMBT$ zv77jUrOfEIZZspT;m&NZZy%Bb@F9_OHpH}TU*big*E)sJN&h(cJ2zdZ`g;)njc8Ef zn?HS&Xv06BUTTBs6}c&IGrA>0s`i56-HUF1RTcib`euy$uWpP4=}SFxy>dTjz%9G( zq^n3*h+TO@y1A?Lx#h-myCSjKFU~nMsaGFl>GGBnNkQkk=I$#{iEJ9x3Pw1eA|t|S zpD}#<3%@crvsKQ-_ysCK(VCEBrnr?9>p;x!{);dR)s{mCu{&)tQWGb&6S@bZEm4xj z893)Fwqk9|oeQ!TeO+;iPIZ;Lf6o`F)*tUi zpJb;-8eJX-=ra~0dz;HM`nQg!)>+?4#NO)SawVw2YvFplkf2~C$30&?lq2WVE*%)e zFDDX3NaZ$by zDj>pcAfps+D%*6#QnqPA8g9!NO?A!}6ade|SN8k}$-t>XQ@$1|79{@X(v;UKZ*cPo zrsc{jCkg^fKPnXj?ROY+4oo9kaY8!D44Tw5GB%xaz@ySVK2p54){3GagM#~aGEMuG|x z*6CByS=FVtzHrhseSNrchH5|3uM4J2W}*OJ4j1 z_4LV=&~xpXX7rDKr$-u)$Cha)+FEbFGW*P@V$|$5#s7*Tz1%Zgb8>zD=Q@1)0$UhLoB_CCkK zNj|aT4!Up;Pl1L29Zf>blY@=HTf~2#BZ@*VVZ1Y#0@4yKLJx{SlbB+KiExH9Pb`(@ zsIY|n=uj#Fa?Jr%#hKwA8dXUY`nzdx=628jn4bQ}bd|)y7akQMM3ipIb1Aq2+VKMP zznhr3i2rfTYa{>Zp1yh*P}%>-WfkDV!p|%It83T%t^dcR|1Z~44*^i6%hFMQo0L1x z>Y)ue3PCPcgMUrNWNx3SuUf;$)$|@S;yKWq!owxN6IW;(<1|jAZv_5oJRA+cw?Qn& zAKZ52+`X*`D^%TV#tKu}Z`>q#G4+Y?vWHQQ^Zpj<=_D-npHa|*#75w}5s+~oEZ{N^ z8$eA+9EhHdsX)QzYy{dA(q;a&^V7J-686iV|9Mw5rnZC~H-=Fn3B2JGyxcbDE&W6V z5(|X$>?|JpHXn2O1=J=%0L>hRlseZmQ-~1pbaSwYC1+MmN{;d~Ih*lCTqe&{Ze4=$ z)ZdAnQiWKeJT7go)IOT@iI1=-jqN;#9-W>9hT2O)_DG)HXOtbe=SyBd#sC%^csRsr znvY=_!%}#$VA>W6#M7wC|7tX;Z&Bwmtt6pM>tvBZ)K!x!|FI6RZVzL>Y(dgDtrPf& zapK&quz}@Bt0>q|0D3mO%lTrXz z`1%N_BMQ3qn4ozgRiX1ey1X$I57iLBfF4^Fh$RCq2-jL#k+rUgyOn}NjA$3l1Dts0sZiAqCuT-`DbOq3iqO(~hI8rMZey_;&_|OO` zZlVp09FD|XLm7ZUDBKmQk)44}t*g|rgSt|t#G*7DfH0k{>TFP&DIb=w7if|h6)-Zd zJeCD{(c^rP@(XA*DT-uHWl2_v7i>NXqD7ARrTJkW(fSu4p|w$B2mKr58F4J<^TWy literal 22434 zcmXtgWmr_*_x70?x&@@9yFoxeas(7mx)Di5knWD*Ar%P)k&p)I66qRR>FyXpQo3{A zgTMFxfotYEvt#X5d&PZtuc4+$h)075005z~l7bchK*6t200#^FIQAO51V3<{lnh+~ zfT;cM57O?HT-{#kEd=A>ojIKU; zc*qQXb6xuwtMy+WWn||EzTV_5Z;KBkSp>09qoaa`VZd+S2N3WYAG0yPCr0C?A^#%q7p#T+GZKQ-LqmtE#lLRM*{E zM2G-LE-WZWx*y|f1+joIuHiG;0`Wzqkr5jrr?4LsBq>1Sa{UDUY)#8dMm?tlJq&@B zz;C>@y}6krkZXV&aUU3fX%yD;GiMzzvLyo-KP=z7r*Y_?oq9;ucx;)#g~ zk0O4~I6>fJTw!4$Tbh->3Qi+2JbKF~B0?x~1GZ|K8!tOsU9ziON=+q+g?N}K8yN6J zYPU)8xi=XCvv8YI0vslt#`!|=De8fsz#^ard z2nb5V#>VEllm~72+>ZiDJAOiPZ z;B6teXT&p0Vn#>T`E&Ie$3>ZpdMdhyKqKx0*?XGL%t^#?u@I9@GTrDWOvPnTm-@Q{ zTfb_;LiL`U7~TFd!U-1yfJ9-dUFrljM_9*np)C`0mdD-t>-$uTY!eP zwlLc#WC`M$v9>Thg40%s3ymj^)-r9`d^BE_|M?5Vto1+4PeG8gUc@z}Q;`XCmfg4j zo5SF5-1&Nsof(ty&K#o&Ohivlat&~phvM^BP{064Btwzi+gmma+Q!Nfma0Ian4^iI z6n_ism#xmEoPxs3OIPM4KW3w9%7Jr$F@&VTU|bP6pacGigSn&%k4eTJT&L! zGi-!mB}Kf1+VVQEP*6|7%q-ON8gcch#HeOh-&m>ejU5otxn{zS5dg_}<<6&|@P^qL zCzfHp-u+0vkViT6BR=rrD?g#9=b1kYnfGt|B>44!vy-;Hy?xEr^DgaYcjSA&fb6LQ zZ}Uq-LA?o&pIY*+^|?Cjf1oodWWqa+`SGhk@ufV%56kXWK|y371j#$wb)>8N2s~l0 z*TVn|v&-YnRygA!3~o|(M`I7xvgfe%{509p=I{=OMUyr zTbtl5xzCn&2PhOEm%FBhH*dX+h~VU*!|L#@b||@N0Dm@Lr2)y?+72O!LG-6bM2vA^ ztiJy8Wt;&#ywFEHKn_P!_t6;-qV@qo-CbHiA^wCGeqH;+W%n$Umd+XXAQ_<1C_Uf42lVRR3C2xG%R-gW%(VI4=y%cas&CP! zkc{u&-^wvBNn=G^+^G<=1RYcP)8}W*^XV)Z?ts^gXp1iJ_TSRhs~u{S_bE61QZlL` zj1(!RTpX^Y_1_Mr$#Zt4vJ&DaWRYv?3JTQn z2?hz*zLu7jD(-BGqwK)g*qDkk>nb-u_U~9CX(4cGv$DeC;%}w(3Fa>`K$TikRb$;J zN4)#+0zB07FTW8@+!T?z`TQ6|j6h$SwB)4oTnkhyMMnW-DYGzSy<&5ek}EJFYUIvl zR%-0gu(?&xM1O>mBtF0)kP`Pj{fp?4h{pt&LX=pY#1Qy8<8TFqoG|glAAT*QayYv8 zIq`vtsX0f;A&qVsNOaC;l4XDJfl+)@^9O&RuyAdGku4zkl#-p9d1TNX^p>}l^`;^_ zATMkdrnP*~07`uupX}bfO)maiFI#{Aoh`R!>z6RNySsbc`ui$W6R4UBijtg`7VrH1 z{a@Fc2-@2McV6X4%2{CQ`h7&ig57^4&9akYV>$M2QudIHyu33-K=1qZNkl|MAM@aI zRwv(j6Fxp|0MxHZ$ntJ{3n;ozOF8ijg=LF}4LU7qJutYcYJahUTq$;J+z2HzsObdr za$YaCwzjHs51(pDV3xaWYMC-Kp#jlAP_CaoJb#CNIKB2!1VDaNe^b10{*Shp8k{efzkG2m!|oV4IDZKe;wI#xZ24!7II3Hp=7=2mce>Kme%6%O ztj&uY{8fR2gDsi#{jGt#ynMGo^FX2^U?;i%sQdyHd~PTS=h1~6`r%rcaKb&rW7joZ z!-O+DV9@{{)Zwgq56Ih6#1c5LqTgX4e>9@8?x@$SzsUXRK-WvDOob7A*G;5j&9MILAG zZ+(3|<+H1^&omH{wg;0tI3VLV4JX9nE{(Ep@GA^XLJ{J!NtAaSIACKNC@D<#ytPIz zJpC?CG<6?BG8!9 zdS}}?bhBMn&yLm=rJ#bor~fHer+N|2or9LAr0CpzuhPcW_Uz*-af}xR(s?-@^X|x> zO8wr4T(2sj_amMG6ubr`)Kp4;v126@dhT}5oyi{7=$xst^;JK^Ruq8>3X%l=&pO<` zE{=`{o+#a-aPokT?k+9DdmzulDs+cmdMLj1bmGtHi;)db|dkHD`7-9?O` z#y*#3T2)lLvQm2lMRwlt_L;n1?gTAM zGua@)y@x(vgbM*Wmp|ChU~CA6mL}ERwqtOq1=ES4p&{q2MX603IHRB!=06)&x$pE1 z*`Risl9vdLrM(3WLMV}BIj8A5L>s8I8sx9AM+X3E-1v7Qi2s1U^ks5G2b}9G6jKSOAx^q0RQ4`iML>SC{1naG31iCh?bGG!el`$WOhs(=EXd z^d$+O@dBamxIl47Xq9-K{@u!TyiMo$KN5NvtNpJ3@`s@*p z9z6|+>~(|&31wa}lrh~Xetp|LjAqKMVQvD0 zB#=9_qbrMURRVh7D@pH|Ou3gxANYpw&SGHIJC4{SI+_%Btopp+O1nBaF+l`Xi~^=S z%((vXNhqGcJFUj+yd^prx}Q+M32IElNc&O8;gTr>E}-=R)8-$Y$0SGLf$ppTPS6F% zZ96Esch7-k!I@DF6{Mv2G+L0(?ZR7G_Z~#fv%tP~QKw zqroSJt!c!K^gvF>tEzvZmB0z2K`84c5=IFgRyv1ZOse-TNO(v`5H3}kUGYuHJeb&SsrIAbHG&(+FCv`6O1VNX4Niy3 zuCRir7jC%xFdE#=lquvMVZZ_2bcUEv>f>^}6I#^<|B;uWSFefgX&R0<(=>Ahb zoYiyG;6Xe3vL!X6r2fY!_rMp<0ZfWMaS2X>&?1;UboLZ7`+(2RmOOuvqr6BNsVnIv z_XhB-XTj*v{rV&)C1b_>*#c1b;ZoChEM}`MK*8_QbeOo(4*&KQ!+#eL416dKIfmI) z*ZKz&jz5^Be=w2WM+;t#msmk1zI?@tOy!wR%x;KUKS8rrzDOc;{py*-vs5S#ybUx) zBE38P^ZB>B5pyn6$P0NK z3M@v21dr>{a=r|g9}0ioQ0_?va*~Ygk$Ks~h31JG=Fjpl_WNwV?~?xnQ^Epz@S@o& z@^dJyRR6Yq{y|TU(N8^s4KQKIu{}}fk;Azkj>0%jZ<+jL4;j~HEzGN}-Q3{^**;!H zkv3<&a={06t|Y?GV*Sm0FH%<)1C?6%g6TP?5m3=4Q=GriRd;{>m#~@T?L!QbK4L}* zrjJVARMqLKvR@>8G1F#P_s@Os2kon=usC1iGSQQRA-^j^+{pc*!b&G8^7^yPH|^oj zO7>Gmo1x_{Uvzv1(xv6Ad-jFsnsH-43Ep+n8Xups$YsDQQ)`(x^ONPC&ax zyn6Xb@)7p%d-H{|^h_yYFOSO528OEg%%G*{pOft1#HPD6He^F#?d$Op>`S<&?pR)Z zd`<`+j59GV#w-Bt&K3V`yBbta^9KTsuSpo~Pd*sfIY48-g|L?BP@Jy7bW030EZ(jV zuVQP6OEP3Ki{9E*gG!r9Y)AmjVOj$DIn#Kc(_`R@cX^=x#oj<~A{*QQ;}FW3m=x*6 zg+0DOC@_ZWhh}u^Ch&z zhLCGmq!0!S;4;TWUH>+&^>DyHRGm#nyFVsl;VkAw?5vmbN16B%?~kHgod&h%G$O{# zfQ+9h`g7qi1CFgf=OE*`tZX{ua7|05>&G(Vxf)ng_4=rkGYNx+R2HRp69OP!R5Lx2 zcu@LdWS%>+Lp$|Pr3QJgqS{mPX92LBs_qHH+7o(g`}sniOuJS9{;tALj@aSl)W^1r zr1qPo5t0w<6m7Ws(T2}cC-{SAcWVxYXw-N#w;lo-&p}O|t3{CvaEl%N=)#@iEaZph z@D|sT_b{usa64@?(8+kmYJGqpwB&$VHy`ZM)Y%WQuTgj$kX5yTCrjU?42a6QIB)8& zg7)V3P|f2Mtb-%*Py#??_YlS}Q;|PC<$G!068brr*oo+71aCyqRl_z9E2!h=O#oud8jfaZu99Aw``s84%Xq7@dzW%k79t zK0m(14l=s6Udep)tuSj9F)_)aT*K6a>&o`>;>ne{s$LU?hio)xumY#QhiAneFgg(pYw8PcL+Oz?u5EH;UbM6MF0b$ zevP}%><4Y>z21xMO;3t!GrVZ*+;dNDf<7=Mo0?)Qf0u`xXrh=QoUSQg#Oq-9HV}jv z2)4JkQIpo0r*$DRjWAo#u~7{Se{d*M>0vG+r4azA0;t z=W>n@<4e^{x4k%lKA_?KE5(Vj{Kc_Iiu~yE{;4qAK>o3l$9-6<|KLpRANmKSNn-07 z$%4R;GA13d=_?S7}L3pUJR0MA_R^cLtQ+sNoJVNaw1QD%G z`W!X!C5khq<4$WitY0r53W+cMt?hLmRSRkN>L;V!!C-6MA+ zlQtFi(Eq=VhQULpPCQCX>Sl#icnZ?JMsjMj?{(+`5`ZhJ{HE>z7m4!!oG!2>fAM4F zV{>=ljz`c{8c0ZRC`4E|*#jzrTJEGTeYt7z`){8#ov0_k(9|9t1_Ny?1cCqn;Unz* zFO{b(0%mWI&ektAn}1?d%N`}StV0GJs+}fTM8$lRD|&vDGSnkG|MW_UCwX#aW>eN4 zs4P14C_KRn(&FyCq=#bkxQovjcl^ zYpLf8KQpxlnX*+G(%n-$f42QK_v}O9O&#d-7sag@4hfqM&z|}vdOf4I3tSUHFF;Uf zd3M*!5Ajo*q?C)uaiM$CgPkW|>wn=El)kO@Dz0(%Z)p%OukH`AaulY*qYc}czB zdB*DG5Ov|Zvn$QIpSgEg>#Rdz0sIm&PY-uYtsI=9yXBp2>k#h!xVYrF{v>E`&T1uT zhM@LipM_)6-V(lkU80uF={vV=lOW>2!SJ*I&`nEBcXqOwY8qqYhV!vDyRAWVD2Vbw zvuPtuEU(3A{SUFF^?5=T=k6LupYmW!rg)7z)*Gur&tzV+?BekzZ&FM0X?(-hQB63X zeThHvmsdU;3B*2ics2B&D|fx_*H1r}x1b8WEQP&FGnaG}4ws*TTO5B1tttCqc##^^ z7^6OYV84g7-MT>*sn zM6F{JWPC9hwS^??0WjO@m*-Pw{$81C$i8hknvzR@XiVc9Cei7>VT7;+sU$JpUlcur zItIenu1j&cv^Xe^Gdncs0=n)Wot-2JhKoouXzdQd>wjUlsd z&k%4F(qDJWyQYl#8e`-&IO#vNU_J4Wb|13X@-UiI*lON7gbEOf2VJE}0LfD(Vb2S) z1o(8D#fry;@4h#_eqejI|HNhl-4*7-nJPAM$-`|oqtDoW+bp=*&jhU9#4%$tOVbzs z`@&v!Ud9XW$heOV#zjeVRxcdEQ>NSV$`+PBso;J~2LeGi|{H-O&7tyH<91>Mmdb8)K8oKPXU9X*| z{QSlix6$ElQfU=JeP9ISXEDqKimpF>MgDiA+_>e(srntLe&Bq#s$~D;%(#~+oEb9`ahIK@c1cU+@`-X6n0BEAy0c!)wPrhL@t37$|PH zJ&BgT?d7r7(eJNzWdh}P(YN}Op^xF>j1@kW)S01bpxEVk`L{byj$VG8;!#{am-YuQ z5OUqEmg$&nNX^0us#MX=;jjG4@*jH&B3cUZny%nnZ^u+IRf}GF{DzcA=!}sF>wwx} zB`}xu^Kp*bZ90k&n|yc02Sz{>%@=3H(DdtyVBhqm?y#=O;6bDFV`Cz$pX zC+BzN8`kP>UG>>D63XiFXz;;#^MYs+x#&8s#{7DcgzP=0sd4PPE!=&lueqA4|K6+J z^{)xg$=AG~52>9E+Hh&!v zOLKA+_8sPft)uVavfdw88`6M56KN#|d+N_$nm(BYI)-VP2Tye|3wy;2zzjz~wxYtJ zp;_NFo=$X!{CrIPxLFdlc>DXQL!ww`as&hkpB!|2=E*JQUHhL96FHu+ z>_}DpZsQ9aI~$(blj&_AnWn`QBph_Zs^}c@Xz3!Qn%Pm;_LR0*YF)qh#3(&qGt(u& zFnM%N4n3bNBI}Y`OAOoJ5&G3RDj~?HzjFUc-Y3b&*tfLlTS96Z!|oIT=jGY;1XF zDwX`w5bHNs%krKDB2`7Dt6onZlZw3>Adcgr0tf`TtVkSF%y-*lO*_KpxHQKtX*rhe z!lht&xPPo}Wxjr;?N4=SEE)4^>6_oiDWM20$@vUJ-Ez86 zd&!yj)Ob$)hu63eziWG1%pp1$q6;k3l;{7va}M}x*d*K|ws|_MBmaj5P~HdK`^8tH zOKe?zIT0DKLL)F&vgm_2T2t;zci0`X1TU7MDeR)YrX6Ac@9p+k@z5T+vrA8AK1N7o z-Xw@Yx)>a--W=zNvGEZC*T#Q-DOgskez)0uOcHLqeEt;;(-CYGFTL42t4OYqX z1&80atN!fG>_E@T)`ny)!+p~$${iX^wqKRjA+iApa9m%A5^Zm9f79L~?&FB~E!Rb~ znZ?GvmbvxxAE6vU0_cHO_$zld$6Bw7@ZW6LBwJw+xbR-`G?7u+z6|%psB2aI9=bcQ zs^Tc)8Ros*RxpT7zfnyu0A`^w=X%l54RAaQnDyf4c@*%oysI93_LI4|{v(SAJ^A?w zH_?r@$K{{b6dq#RWC3LcOH8s^^>`9AFvK(d^o92b``y!|$J)&0cK$>KV35Po8cEcn zxKO0DlY6#b-y}wq^DJb2}XLII`bDABAmIr;W&Y^SFJz>zJK%w zUOf`Gil2RR;kV+D2TP>V^p~SzICCdB#I{-4H^=v|Z=9v&4vZ+gIDcy!V?qdkmtYl# zgV%gl`p2-cCQfGe?>{l<^5L0WMLvak@iEBU)ld37-&yE_H#;S43m(J59El4!rdsUp zw*W3Y$F((ip1^rOnNP7i<4FG?L)CEhZ&&DoYc#fMDfJJKjTZRNuQcd~t$Rx~@$O?S zg`5~Toi%Xq(TaLLz|4xb*~Z%SejbC6DloA4&FiWtEsP0HS=ULNugLo2V|P6|KUfr) zHF|Nv{h$d}SVZM=A*2S&?*cMn`B(>%t#_NzZKOC~H!JqgH%0btr!Fd=Hgslu5!h*? zD?bB=Ep~A;XacYC__(*_$E+Oii0)&VU5%N#H_-22ad>l*MRho)yZxM^%wWXhjd7-d zvq`f%R*v<2a2RL%sGRdErZX_6v=Xek>7$dl|9T}F4-u;uddK)jBfrbHew^0x3FNt} z41VR)O^5OuFE58mR5R(G$LV)eunr`efea$Su|1qxT$C*cApcK`#mx7-tnxIwP^tgW zy|-H9M=gfHU-$isbV+J5Xh75R7{F=R;=R!xwT}ZNm!VtrzM;-YWW?`wG~1IYnUnBA z(1OZ#m&XIyX>4w><3-Uw*w`Y-F`bs=h!=0Hp=x1o`uiKe(r)JMQObh^@yh4^B)|2x zVi3IUdZBgtXnQ*qJK}*ab7(JmANbGq5S6k|H*!N(*;j7p%J= z7M8}4s@H>a%dkBbEtl%jM7XUOiu6;&=RY2f`ii1bi(j#n4~AmH z;%*ZfE4^f2^EV&{NJQBCoB{X`bE|uk-nhGc!Yyg+Iy{LMdRwjMKOFhi8G~`&i(NIl ze6W3$RyD0N38S|f6>}Lsf$~O@Js3+b=JZaQ0w>Tw^-A<*2?g`yn2Q4t4~xKKY8GcO zR9}9o&cVdLmT#cq`e_*xDoVe~0!Ms`y?2Ll zM`&|UN5*OVsww)xb?ycwlw%6!A?GdvaTsIsJZ@Vm^)vK>$9qI1ra^*@Ylqs;96z=@ zB7-NzL~-0W7qabc-H1gSv}X1dQ`zr74#s)BHsE-{B4ZFe({6e47BV@&S& zEhD+&{~(OUV=c3W7B;2RF(pUq`E<9~2trA(+T{QG(O&E!;CCH;pzkf9^dL=h^iA?6 z3s7gH2!J(y5BgOc1n$(ey4o2@`RkTY{!ZI3>mZK_Ven*?USU5B_yHFD0Syqdj=58D zk)Afdgp5OePqmqC`-{hatV{{YIpXpac{u4^c(hZwgfYI*`!h2ryl{8wT2XDC1Z=nL zD9v`rF^cv5OednD4@l}hE(%OvE(!#^+^tRt(CP!!9goIKHlm7fr7ti=>8G_LzE;Ov zp9DvTmwj^w#y_0am)qf!B_uHBx4zY7abJ*ne}A0*wnDkg>8i(;0??n{#)tIre(ecX zy2Gdif5=~gFlOBfPEX`dDhugyXF1Q+XWhem@`%F>45tqNU>_i-F;c$yv+s%*OnZb zDES%kGe4s1V3FsH%kfqX`Ro_*^*I?XZ;I>Ig#pnO$2z!B?Ukl<4UXLosr9Cg?}@Qy zhdZzdX6;zHOSN01T^+auguv{n<*j+4_7Q_eJqdCxoi-IT)14?A)0QlQoo%bgKG{ke zgXMFsk;5fGu^J!~e&OdEX-ev~QIz}J5XYseJ~x%IRI$Eo50ghu zf8kFFBvrAw1vkQ!`-Wi^$uFK1mf-o7?th^@4)B zQ`e(;6;F3SF{p5&On`y}Z!5iia?JiTw0;vrW0BFR_QgBgm4S{m**@ox&+L6`=_Vz{ z(3=l?CTYLi0j_Q!)}k%|^q>&dan>&F*O9+hIJ$*F7xcnY`0bIpELRXLd_3S3FN+qU z^au+<-b$($*q4}c~O*mS*cH=dv!UDfG~Et5H`aA#GxcWghy+Dfz>rxyT+79DJ}Ff4foX{PK59tkUNPNEP#!=HJ$WY zH1J0Lh(XjuHKpH_flk`SG@S(~xI3a|%XB1QpFV>%=dghTzjG_E?HBBBeKe&nyMViH z3D;UpAAJ{T!VEC<76pzFMVtQ?bruc0`N=~M!8z}f@l3v>m=;7sZt7L8*p2)EdGx?u z*y?Cf+p274u-SA+Ua1lagMYXnAO`8^=h3j==Cz&U-bzSrNM*1t#)>|9m0w@&^)ZX7@m+5kuAnygE^s_ZM;H%DR$ zbGld4XVY)fB~_76D6$@+xNKI#Pls$#on!*b2&-VuT?RR~3&2)=-J8~uw)3O#~r5^Xq z0(3TeIii@5w05@~dKu@Hd6&)|)5yQOZx91dD%E*+GqwP;1stSc7;ppn-EUj_^Ajbf zC;upLr=|y_##sPRJ!QddE-j8^tF9wv1_Q7a0j+-`BnC>CQ=IW#`{*E4T_hO{qUcd= zY0RPF_D4VgsBIQ&>X3YUnm?i7Ddq$*HiG*D->_2@_}{b}6Z~(0aqwGoMAMTheN^0s zeujUG(5Ik(>5>=R{Jm3yzFjpe6b^Dqh4*upG<;2m3OZ(f#PFC2aVS>>$w<}ypIP(y zW7%7JLJJ=}{Z$sg@6z}XBZnacy+OVXebO>Rb6QjAe`9wih5ja)>~!k%jTfAO8%u@UjeR?%o62dq_oAgqPb!Pe6Ja(F&zG*W^dI*Aeh!Vqp|N9qG( zLRDpf`D9e66@)g(1j1yW`?9*PKhDWirFTq|-It9M52b=?h4;&PSKw7;HdKU`aM7J> zs|A0jubhN*T#bo_KaPuU_Un0w@ljS3U6Ev*DeAUot9f~77<{+dRUbV>>A29J_v==i zri38IN07h@ZMyn8Ku{WtrT+P8NQQ{O(JqGgy)Z-t=&KvDXx}q;N;-*F|9F%O3+#C} zG*xmQko@q&S$?3Pr=8YX3>1)OLdm8V!Cwu(&RLOmruC|$PFwDVbF3%^S{^YF7na>Z z%+{Zb-+zM*I{B&bC4GO6`9uN_V(ymvPPcAxf&XP!*7U}dm2rg^($nrmXTS5u&)E|5 zCr_KTSmOA%1in>%3#OAVi;=d(e%?2%dFR;;!l1_5JU;`kVHMZpVK0M4+OA2H#C%?i z#-9irW9WgH|(F$ck5!&4rR7!ony_ui*^}k^wQ#*=|b8`%y zjT}USy0eJE($D4nH}|5Gh?vYKlSOlx5dE_5cKG7*&cMbl_Nj<4kn@5fU2U}Y3$j}D zWE4=2L&6(o-XZ47b(-PIkH(qc)t)>La6wHg0sCSu?KUtZPnn^HU+O1aQ2hD3<;&xb zUXLw`X4GSR({8T}-Xh7ckO22M(Hqc3PtOb?i_rh#(0aXg7`)*UD0eB{!aI&ty`w|g z4F}lv1i4EFgVT)HV4@05U&s9n@;H%%>ZzV;BO*@>0@Zr~5S6uSkH-6)CHzEB+f)~jLf##ass-e7Fhm~S5 zmVE18^5ibhYgrCO08X1)Scq&(i@U*PFB0<6D)!bI#!pVo0i{aS@IDjlp~=F4xLvT@ z7Eehf_4P9}i5v_pMvJnj62(++);Bv#>=ZY<(!G+KMo7bW`i(b6CP#=1&NVCK0Lphx zomnYh-eGsU>A6X&f3Y7phiNj?p)zj+MQf`0^fPoN3hK`}Bq1^U<`@ zMz*{8`M;?Easw+LKs-kV0Fd{2UX8O^_vU~BQ`x}ydSS~YHp0qw`hyNi-8jgY4)XJK z`mSMOcE(5w+e+D!9Eioa95Zd&=n1)-p*BSPs#)b`ecdbt-VTWd@{vFW0;4?hF<1{> z{hb-tU{h!QK9ZtHHfU32{cgepTcq?KUo;G9V{*gOp5DkTKpSyaxbPt=BnAMdKhIBC z?$wVzygQ%g_+uXev~0yEeRLt14gP563fZl#8wLDJZk$#?Qbwk=B7GCx#b}{AH#yNn zApf?;RgTK7zh44F6NJS}H|U;y?feKH%n8~ER2vrv=~4c0%zBx8Ns^iqz`Bx*|H|)x zQYv2E%HNeL{pRXT$jz48b*nB^U~kPgcSez4ZxM`je@BUuZk4h8ppbo!SK{!I<^cd? zT$z7L#4R(mm#iuSg~4JHHPV( z2#I2A_7TG;En9)?i!PFr|5 zSVInbqo*GYfy0VC)@g1cP`h0Ye`0XxPn(aVQusUpc{-mOOEZu1U73B=$2pltpXHXQCrS>uE%_k# z&;zMxw{~#wcN1(|f&dwKmRL|4yL1{cKtLEAaB+i+<0^cAINtzzKJy5-&@(uE~qbbk|#bL2cY4ksA@!L?W7O5R(K_tbYJbIt&X=uQvo7BoA@GT5@Nyj zg6Po0h+9!o{=WB|`bqvYYjfT+k%PRh?C*xmew>bj;jJK3}NsNspOpu7u33-xdwAcgw;n+G~ zoj>MQvdQ>mn#gA>to*5e@rpNQ)7?+Drn7Bkr}1*~K3F7IVZgMWohYpjS@GL4#_db# z0_ouev3w3b;U@BEU_U$D`=m@53y89prfs`qcV!?QTa-a8P&7?I#YOjbwvWAexrYrx zuaPxnk?nbgW&R+HP*w7ObNX*eOL5pu%c|{d!yOiptEBJImu7MxLUCpJ#h$v^%cL#t zXg0QemVq1F7zo^za0k+8z26MLQiC_%O<0*C%pHx^mi*VZ#ojy@p<(bV3inJcm~4~< zif7C+9f-?r-MaxZgy}d1%Wl$@xlM^<@Uh4PEaXrnG@);zj6ds>fp~j6s6&pdJiAcO z>rj0(yBQ#e45DYay-coC$5T0GAF69Ga;NTgwnO zNb=SqTe0jx;08I^h52FQN$>H~wz_|k(`{#`3*Mb^*DnHHqs$3EKOSq7|Lt?iuL7fv ztc1YUDbDpCz8x3)QiCOlI<%$u(N#5aqU3U(Lj3RL3*%Q+pP3NH#|uYNe&=%1G3s1F zj&rv1?N!ZgcmzOLfj#ch?|&ZYWuTF4{pofMqtjGi^J3@4(Me-l)?fElIb!sw`en&E zg9qB?!M104YGT-4Vo-pl(Z}|iHU6LPN^BJ^;DbEWvrvZ?rl2pb)KDd2D_v#3D+zyw zH{&B*PP4(u6pDErp8ar;6P_(E?>c(kxWwVxGoXHI$vmiCc}Dr7P7&a$HVf+jYge}Z zUwcXde-2Og-v7yN0SJz5b#YEmHOiZvwVE7@Y}Ux5$}`lL!_flKb=r?f;Wj#I3Fp5# zjsqMzN6TJ<<nF41Z+T&UTO6yy^PSxhKSYW!2UVPln17 z#I-A37z`{bE4w{k=pC*MpaK0?h4IM1n}H`O%|_lsoKycF3KBKnO3wh|!`H|O?w}wC z3mA4SQSfF{cdooQxTrar_l*%9M@GE!!uapha8TcC8qSw>OYaS4o$tim0g&c~pGiF)utyF!b*8$k)6N;oDEcoq5&YdX% zr*(O|+oTW(V{nIb&|WEDJHmEtmK%ht>AytPz3r)b&a25)+(Su^!0 zbtbTXDlFAOh77}cYa>xtq*s5wplH0~cqBO^Kwo(f9_X|oO9o<+^7V;AHMB=!M#`Jj zNhH7APC!6htdm@_Kx9BFTNwR}P~97AXS24C?~I?vIRT)v#`QZEB2IWh?cA@2kG?R% z9{JdcN5d9O^pu8B9nbG77uR}II6J60DV|jIfl=&+SW#T_3ITE8#UUfb04g3JEJ#{g zYDNoMH3v=wwL?v>Lt*j>s~V)@(Ad^XV|*iGZ0FlmetQNsAMTMa8;%SL106Uaf++fe z$vt*wT7dl4Y>`;XeH( zHl~dyZZy^#ai%be%Xx2Y=xC3Qe%v<@#k+F5Ub`Ux)==KO=PO}a#eCH#>XwJRt%ghm z;0H+w?-Gb7FGBPNPMqF;>EMwBk(G~WV&8v{e?E8nkdncxDDNhrnZtFlQgrtmKP9jl zga|Qjr?e+(3nC8llN!8_s|Xll84vqSfS3nu`(cFBcuv6(-J zD|fBA0EE!)JGLx;K-)1}RM<4n8?vr?ov%HPN=&m_Q$qYP0I_IX*N@-swKn7#f>@hr z5ZN?Pnf$lyDZNZ!ZlrSO+w^DKr+9wn4Kl~c-Y+v)LCwj$MnsmC*>$Xb1M!Niou@{L zwT%SL`ReXmuTL$SLLKLA&5c-s5z61yV{0#@`Sk`Kq3P?S`=Jb%rNIemYO0nGun@eu zq-v-KJZIDmnz3X*H3Obe`-Rz&yK$Fd&z%*AHqbi{&1KfpNPPL>hw7>!EZG%1n3xfp zofLBWvp7~-PpTF44-vO~-u3uOgG$7IpPVN z2U8O@6Zy&y#plo^1^H1xcfRlGKQ-CLgSP7gV*h+!C0gF*q4`89D2#E=ETbZoOZ-_E2`!T$bU7!3{j|B5Bh+gn)4%Y}12>`_BFkktm5SP)J9Sy!E078;3 zV6~Q=0Sf+a!+^k22C}bT+72y|iGMYHuo2klHT$z1bRyOHrR)tV+zIDHnDnGWJKl-nP5me24GenEaxr&*eR6rgsp?LzcoK<&v9l z%+iMW)&+Sfy$wPF!0p>-Cuq&L`H1&nyHpz(_VEG&!XDko;h7(bclbJ~=7F}`cB$6% zo(^Wz2pc`W=~|YHi^sQ1u-RUFOSG`1*nJ0d?IEr`-sK8U_EXi#NzSR0AftmI=9RbIZn0P}#o zOdA)EM>pn!lZNMijfv{7K=4Jq1(2crBc!97@+a!^ySKY4EFtiNIq0g&heH0R1p_8Q zkBl%)ifhWvvB4J6mxshGKlsch6yAf^FmH_VH^WUP4oTs~d+4K|p^n$TgHq~jY2ML- zAmIoQ=1s^@=nz^!sRe=+G{rd>x&mM;;5{-mWoEu&3;M&inE~m@CH`^UC>71n(Pk~Kp5bZ^FU7ZxJ;wDGMb`;6T z`yD~{Zj*d!0mshvXSqE?dC_#+tzM>`(eh`hjI3YnV?t(Xp&j4wh+WwUF#!n^G$ZIz zT*__au^i0cTp-#%o0R8ocYiNYmiU`B`4Ff#B%Y5ujBNG(h!P0Q?xG0tFM(pe3~HZ; z?#x5!q~TZlvh(tpg?XmP6IRkv@k8rrC&0-L_XUoEynJ5Db-qFX#;+K_`%@;GzvmOD z00#F55cSOl1v`8X?TLQk`mOPUuTxB2zgwjOtjE9@K&IuGks{%=0E`2w7oTnZ#~kuO z+(Bi{sKFTJD!cMLkVd+pwsw`LFp{y7pq3(S`YV1q8~84WWB3%b^%|JO zjQs?@gmE4pni9AqKL`+8&`3uELy6xs7lPFV9OOEz)?8MU-r;~8GkZ)c!RC5=474<{5w+?E1k>?uK63wU;H~{dC7<@z1eFpWJ$6_mSh`C*0O{wHCdB= z8S5J+$!jMeJ0X;jZNj7?yJTxDEhb}|>5XCRepi3|o@f4dNc9+rq3<24QpL95}4Y+G= z0^m|Cq z(g=VbXAWYLu@nDQ7h0ykVn!tZ8ti*3*6(*C{4785rY5`Is|b>{ZdfIhd`>DBd#mw3 zik7;&O2E?bzaN6;brmg~ljDuf=z}AWFSiV9NsF||fkWrAC6dv5&gXj`-F^yu8;mlN zXND}hxCz@C`1_O@J`&5on>*+4x*lVC#h?m%*5W~f2SHllQ{4}Sr|%CsNE#=&L89dh zP1tkj=J~!aKKh#IsRmh+>F||<#}1n4C;Eu*4AJ4;U7W)HfST5Jx(^)iW$|lr1^-1i$Q zyXSD>HKUb3%k>;(SMZH+(TjqmG@k$vVL*910QVNVz)WyGiH_{;0x;JI9g#baV8H%* z=9fA_;=1#C&WqI80EwXa)kMJc^Qm!BpuLH84`WShIPa}h$!hIR;SP0eCw!eC+BRQ} z;Wx@e#Ss+G@YNi!$AnbLB_MgdVei>?UzU^zkgWsr=rb_bv2{kUi_TgK$5O~$Z>^UB zNX9dR0K)N$Id3tSsqZ=$EiL<4fNRk2X**T6ohq@$#lPm;B*QJ5t&EchSpZ zI(jhOzAivuYze)eFQ_W>-wc}F%$vs+mt|Vm@E~(5m$B%2keKe$H>21fQbeE z?hgPq=I`%oQ)q4x(KP<0;IxFI2`?M`DlV4%s4B*iHhQ9~Tnvyv9kf+JwgYdX$M-5z z2Kg~Ss_g$B*=JM>|MuGdvwxpH3nn78MU0bEg(OA_1>8^Uws`w<>E#~U%oU1?pb<@c z2#=-=l3u@EaKYgltSD?<{%VGU+C6-9lOsct71nrHk18Q;>5ib*0vB5zYu zPF|I}_M<;+=+{(K=YiPN@H$5Gknk(x>#x?2@B}zRobm-OkCl!aKC_e3RXe#q&dCW| z(H~iCyf}58E?(-{f00VOe<96%YlDlU(<$^@*Jaht+qVYl{PN5l>6>WCRXucd`SC z)z@q6?Qc$FdH1w}oLzdm>x^$o1TKyK_4=-4qfEy0NR(cG1Fvkqgs0Z_uy-a>z~+~V z`yk@ci=Fp60ajC;-S)S~&x{D38|r@4P@#JA+>rI5;2qZwxrTgsB`rIhoL(1D5=qt5 zCoF?dQ6;X`h`R5emi^AUS%!7dCSHPHASrZX7@J~jkYMU7oUtkLFO4Wxb=i#1E+z~l zF1RM66k=mDnYD{>rW>>_oxJ3{`g4tApfS1!Pyn9S{ojDYsC z&s13I%tqioBPgP6dj#zL%|vJ(Q~6^#Zn5#wK_kP+%na>u*-H^I76?*8XZeTSUa8W$ zR148IHiWWiu8xX-qy^xEDQf=9W+gbz27?t|(6(&YF(rQ+C&dye;|=ZBY1%%Z`#gOgg{M!G-uAR z^LaaS9a8Ku)?vD%p^w7AMDO(Zz38df`H;y}WGyX}N$afAXMmt8e|q79f2&I6&g`Wd z5JC}izTFTg@*(H-58FW!65Uav9v8E8kBUmOjJ6HPJ3lY66TW@>W`Rnn8>646%+m-0>IT zq|p|=hnRo^t(QhR01pbjHx^1*2iE^pt~J!4gd64Ctv6m zw1E(~J{xgL-ij)49Q#d4)V2s&?PET>_AA@V5=%|3|AJ+$_*u`w!jdu+bRwW|H+c`u z7Zkj?=P+7790oSwefZ|OrAH4-J97p-ABgLn%|Ncn8yR^j2t$x+#`OW|ThBj`w zhNHdwTqe8f?|~-$4kskfvzl>dkklr9Y+_N*LDh_ovu$6852ATtqmFkLJCMSAxZ=)jP8Tjdw=^@`!?2bGpUm^pIKWOUXe znG+&(p+la{+%VJg1inTjKJ``njPq~;5G$s|93P)SrS_FW6QyXjZze)QEAdHsQh6JnefLY)>|{dxy|%Wzi>=sSkq{L&~HT?Ge1+2UvR)AzkJ z>X;zH-kn2hBl+<%5MbsGXxd2sjI#K(GSLpYRb?JF;i;^CJ`i*|9)D05UK+7Kz9Ag* zSjj5;eC_QJHax zHN-1PL{QvO{qZ*>;pj%|WsFo{YJkT_AbUIF;HQa1+l|r95fI*X(*$ut!~c>MK|nA0Or|_ z^#PYNFQgSDh1mfIuc-;iAaNSEAp-dzLP;PJwHLy|yk-^;zaYR7eezo?Pg3ikscG=* zvRg-kvkiQfBQ5fzKBMzu3VRIVr+=%Bbk-Uc5vh38-PMKjIHyQ^g{=$~UT|A(fx<_~ zRa3{@yMV(WWH}0$pqHY_MhDxj3Wo(FS3F(b@<1s2<~n+$1?f7)1_ndI<_^hG$jZWMHrS0#bH1uw=`45L;3uqCH}3q5Nt*p3WT7X*P<# z3f67Fm}Ov99_*vTICy#jNCkqZDlMHKRUL{l9_{(l;9QD++F_Vx;gJe^zChTMvDsM+ z4*ylwLvK+Aib)6_ne~;*T07)YGbHkjX+UI4B#AnVo`;9bo}+R=kO`&OsC))@3aC$e z@kjmcJL)Hm^tL0nxfthYL5j{DCKs{_l|AEg1RHm}#-_Qk$%%HS*EelUP>mB3Jtm;A zj@LIZn`y<+sh>tXmVGSvVo*XtHnorD`-IXD6A|nMERZ?B+>%R`|4Kx3-d6{iWrp)r zI2@f3eZYYdISwWgc#Y9!xvN66cwp)X5|Yf#4GMDH#!?UH>2<6}-PNf?scjo(W;RZ> zlZ6O2g57}3^6Kig2I^4MN$NPXJ}azs^c`bPJZa4CtE%urF9w*s!7H%UEeK=_bm4xx+jcuWC?W|JPNzm`1`205om z8*dib{jAd|9uZL+h`0)S^5C^seMd`c*dAT{FdttB2czLj{O2~^M%sRfo6KJ z=V??bm5w(k%%=7HD7^}qdl}4bMD_|Jv;57laNG%V$kThiJJ}eEHPDh<%AmHT$udAs zWCi@pgtKTPi6?uaR4-n<$gTECM+av3X?eWnCXp!DHsn~Cxfg>>mq&8Jz2J@OLW=RH`7hpkQ=61&>{S-~ z<47nrF!Tf%yPZ)OSHz=hO z8TA=oKA+-}3bSj_NT+1KS-jWke<@?SeK)>Yr}7a-jYG+XB(?OFiDD2EeMcGx0va2Xp smcc#k@)8?+`yU!dk(O~mKY35<7AqzSmHnm-#106hcTKlW$N8WC0Zo*37ytkO diff --git a/assets/images/icon.svg b/assets/images/icon.svg deleted file mode 100644 index 30bb299..0000000 --- a/assets/images/icon.svg +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/manifest.json b/assets/manifest.json index ee671f6..a0062c6 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -3,8 +3,8 @@ "short_name": "Todo Baggins", "start_url": "/", "display": "standalone", - "background_color": "#27272a", - "theme_color": "#27272a", + "background_color": "#101828", + "theme_color": "#b89a2e", "icons": [ { "src": "/assets/images/icon.png", diff --git a/assets/styles/input_range.css b/assets/styles/input_range.css index 555e054..b48083c 100644 --- a/assets/styles/input_range.css +++ b/assets/styles/input_range.css @@ -8,56 +8,37 @@ input[type="range"] { background: transparent; } -input[type="range"]::-moz-range-thumb { +input[type="range"]::-moz-range-thumb, +input[type="range"]::-webkit-slider-thumb { width: 1.25rem; height: 1.25rem; - background: rgba(228 228 231); + background: var(--color-gray-400); + filter: drop-shadow(0 var(--spacing) 0 var(--color-gray-500)); border: 0; border-radius: 0.5rem; + cursor: pointer; } -input[type="range"]::-moz-range-progress { - background: #525259; +input[type="range"]::-webkit-slider-thumb { + position: relative; + top: -9px; +} + +input[type="range"]::-moz-range-track, +input[type="range"]::-webkit-slider-runnable-track { + background: var(--color-gray-800-muted); height: 0.5rem; + filter: drop-shadow( + 0 calc(0px - var(--spacing)) 0 var(--color-gray-900-muted) + ); border-radius: 0.25rem; } input[type="range"]::-moz-range-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-moz-range-progress { - background: #2d2d31; - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-moz-range-track { - background: rgba(113 113 122 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"]::-webkit-slider-thumb { - width: 1.25rem; - height: 1.25rem; - background: rgba(228 228 231); - border: 0; - border-radius: 0.5rem; - position: relative; - top: -0.4rem; + transform: translateY(3px); } input[type="range"]::-webkit-slider-runnable-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-webkit-slider-runnable-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; + position: relative; + top: 3px; } diff --git a/assets/styles/select_arrow.css b/assets/styles/select_arrow.css new file mode 100644 index 0000000..585e826 --- /dev/null +++ b/assets/styles/select_arrow.css @@ -0,0 +1,7 @@ +select { + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3E%3Cpath fill='%239ca3af' d='M300.3 440.8C312.9 451 331.4 450.3 343.1 438.6L471.1 310.6C480.3 301.4 483 287.7 478 275.7C473 263.7 461.4 256 448.5 256L192.5 256C179.6 256 167.9 263.8 162.9 275.8C157.9 287.8 160.7 301.5 169.9 310.6L297.9 438.6L300.3 440.8z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: 2rem; + background-position: right 0.5rem center; +} diff --git a/src/components/app.rs b/src/components/app.rs index 553ee51..6e4204c 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -1,4 +1,5 @@ use crate::internationalization::get_language_identifier; + use crate::route::Route; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; @@ -15,6 +16,7 @@ static FONTS_DIRECTORY: Asset = asset!( const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css"); const INPUT_NUMBER_ARROWS_CSS: Asset = asset!("/assets/styles/input_number_arrows.css"); const INPUT_RANGE_CSS: Asset = asset!("/assets/styles/input_range.css"); +const SELECT_ARROW_CSS: Asset = asset!("/assets/styles/select_arrow.css"); const MANIFEST: Asset = asset!("/assets/manifest.json"); #[component] @@ -36,10 +38,11 @@ pub(crate) fn App() -> Element { document::Stylesheet { href: TAILWIND_CSS } document::Stylesheet { href: INPUT_NUMBER_ARROWS_CSS } document::Stylesheet { href: INPUT_RANGE_CSS } + document::Stylesheet { href: SELECT_ARROW_CSS } document::Link { rel: "manifest", href: MANIFEST, crossorigin: "use-credentials" } div { - class: "min-h-screen pt-4 pb-36 flex flex-col text-zinc-200 bg-zinc-800", + class: "min-h-screen py-4 flex flex-col text-gray-300 bg-gray-900", Router:: {} } } diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs index 7738415..f230871 100644 --- a/src/components/bottom_panel.rs +++ b/src/components/bottom_panel.rs @@ -1,78 +1,22 @@ -use crate::components::error_boundary_message::ErrorBoundaryMessage; use crate::components::navigation::Navigation; -use crate::components::project_form::ProjectForm; -use crate::components::task_form::TaskForm; -use crate::models::project::Project; -use crate::models::task::Task; -use crate::route::Route; use dioxus::prelude::*; #[component] -pub(crate) fn BottomPanel(display_form: Signal) -> Element { - // A signal for delaying the application of styles. - #[allow(clippy::redundant_closure)] - let mut expanded = use_signal(|| display_form()); +pub(crate) fn BottomPanel() -> Element { let navigation_expanded = use_signal(|| false); - let current_route = use_route(); - - let mut project_being_edited = use_context::>>(); - let mut task_being_edited = use_context::>>(); - - use_effect(use_reactive(&display_form, move |display_form| { - if display_form() { - expanded.set(true); - } else { - spawn(async move { - // Necessary for a smooth – not instant – height transition. - #[cfg(not(feature = "server"))] - async_std::task::sleep(std::time::Duration::from_millis(500)).await; - /* The check is necessary for the situation when the user expands the panel while - it is being closed. */ - if !display_form() { - expanded.set(false); - } - }); - } - })); rsx! { div { class: format!( - "flex flex-col pointer-events-auto bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}", - match (display_form(), current_route, navigation_expanded()) { - (false, _, false) => "h-[64px]", - (false, _, true) => "h-[130px]", - (true, Route::ProjectsPage, _) => "h-[130px]", - (true, _, _) => "h-[506px]", + "flex flex-col pointer-events-auto bg-gray-800 transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}", + if navigation_expanded() { + "h-[130px]" + } else { + "h-[66px]" } ), - if expanded() { - ErrorBoundaryMessage { - match current_route { - Route::ProjectsPage => rsx! { - ProjectForm { - project: project_being_edited(), - on_successful_submit: move |_| { - display_form.set(false); - project_being_edited.set(None); - } - } - }, - _ => rsx! { - TaskForm { - task: task_being_edited(), - on_successful_submit: move |_| { - display_form.set(false); - task_being_edited.set(None); - } - } - } - } - } - } else { - Navigation { - expanded: navigation_expanded, - } + Navigation { + is_expanded: navigation_expanded, } } } diff --git a/src/components/button_primary.rs b/src/components/button_primary.rs new file mode 100644 index 0000000..7fe01c2 --- /dev/null +++ b/src/components/button_primary.rs @@ -0,0 +1,29 @@ +use dioxus::prelude::*; + +#[component] +pub(crate) fn ButtonPrimary( + class: Option, + children: Element, + #[props(extends = GlobalAttributes, extends = button)] attributes: Vec, + // TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved. + onclick: Option>>, +) -> Element { + rsx! { + button { + class: format!( + "cursor-pointer pb-[6px] hover:pb-[7px] active:pb-[2px] mt-[1px] hover:mt-0 active:mt-[5px] hover:*:drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] active:*:drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] transition-all duration-150 {}", + class.unwrap_or("".to_owned()) + ), + onclick: move |event| { + if let Some(onclick) = onclick { + onclick.call(event); + } + }, + ..attributes, + div { + class: "py-3.5 px-4 flex flex-row justify-center items-center bg-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] text-amber-700-muted rounded-xl transition-all duration-150", + {children} + } + } + } +} diff --git a/src/components/button_secondary.rs b/src/components/button_secondary.rs new file mode 100644 index 0000000..4be48a0 --- /dev/null +++ b/src/components/button_secondary.rs @@ -0,0 +1,29 @@ +use dioxus::prelude::*; + +#[component] +pub(crate) fn ButtonSecondary( + class: Option, + children: Element, + #[props(extends = GlobalAttributes, extends = button)] attributes: Vec, + // TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved. + onclick: Option>>, +) -> Element { + rsx! { + button { + class: format!( + "cursor-pointer pb-[6px] hover:pb-[7px] active:pb-[2px] mt-[1px] hover:mt-0 active:mt-[5px] hover:*:drop-shadow-[0_7px_0_var(--color-gray-800)] active:*:drop-shadow-[0_2px_0_var(--color-gray-800)] transition-all duration-150 {}", + class.unwrap_or("".to_owned()) + ), + onclick: move |event| { + if let Some(onclick) = onclick { + onclick.call(event); + } + }, + ..attributes, + div { + class: "py-3.5 px-4 flex flex-row justify-center items-center bg-gray-600 drop-shadow-[0_6px_0_var(--color-gray-800)] rounded-xl transition-all duration-150", + {children} + } + } + } +} diff --git a/src/components/category_calendar_task_list.rs b/src/components/category_calendar_task_list.rs index 5a61335..16df019 100644 --- a/src/components/category_calendar_task_list.rs +++ b/src/components/category_calendar_task_list.rs @@ -28,11 +28,12 @@ pub(crate) fn CategoryCalendarTaskList() -> Element { div { class: "flex flex-col gap-4", div { - class: "px-7 flex flex-row items-center gap-2 font-bold", + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", div { class: "pt-1", { - date_current.format_localized(t!( + date_current.format_localized( + t!( if date_current.year() == Local::now().year() { "date-weekday-format" } else { diff --git a/src/components/category_input.rs b/src/components/category_input.rs index b86f805..9420271 100644 --- a/src/components/category_input.rs +++ b/src/components/category_input.rs @@ -1,10 +1,11 @@ +use crate::components::select_button::SelectButton; use crate::models::category::Category; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use dioxus_free_icons::Icon; +use dioxus_free_icons::icons::fa_regular_icons::FaLightbulb; use dioxus_free_icons::icons::fa_solid_icons::{ - FaCalendarDays, FaForward, FaHourglassHalf, FaInbox, FaQuestion, FaWater, + FaCalendarDays, FaHourglassHalf, FaInbox, FaSignsPost, FaWater, }; #[component] @@ -14,105 +15,51 @@ pub(crate) fn CategoryInput( ) -> Element { rsx! { div { - class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::SomedayMaybe { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + class: format!("grid grid-cols-3 gap-3 {}", class.unwrap_or("")), + SelectButton { + icon: FaLightbulb, + is_selected: matches!(selected_category(), Category::SomedayMaybe), + on_select: move |_| { selected_category.set(Category::SomedayMaybe); - }, - Icon { - icon: FaQuestion, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::LongTerm { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaWater, + is_selected: matches!(selected_category(), Category::LongTerm), + on_select: move |_| { selected_category.set(Category::LongTerm); - }, - Icon { - icon: FaWater, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if let Category::WaitingFor(_) = selected_category() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaHourglassHalf, + is_selected: matches!(selected_category(), Category::WaitingFor(_)), + on_select: move |_| { selected_category.set(Category::WaitingFor(String::new())); - }, - Icon { - icon: FaHourglassHalf, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::NextSteps { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaSignsPost, + is_selected: matches!(selected_category(), Category::NextSteps), + on_select: move |_| { selected_category.set(Category::NextSteps); - }, - Icon { - icon: FaForward, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if let Category::Calendar { .. } = selected_category() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaCalendarDays, + is_selected: matches!(selected_category(), Category::Calendar { .. }), + on_select: move |_| { selected_category.set(Category::Calendar { date: chrono::Local::now().date_naive(), reoccurrence: None, time: None, }); - }, - Icon { - icon: FaCalendarDays, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::Inbox { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaInbox, + is_selected: matches!(selected_category(), Category::Inbox), + on_select: move |_| { selected_category.set(Category::Inbox); - }, - Icon { - icon: FaInbox, - height: 16, - width: 16 } } } diff --git a/src/components/category_today_task_list.rs b/src/components/category_today_task_list.rs index 0cb6e0e..772847c 100644 --- a/src/components/category_today_task_list.rs +++ b/src/components/category_today_task_list.rs @@ -46,29 +46,31 @@ pub(crate) fn CategoryTodayTaskList() -> Element { rsx! { div { class: "pt-4 flex flex-col gap-8", - div { - class: "flex flex-col gap-4", + if !long_term_tasks.is_empty() { div { - class: "px-7 flex flex-row items-center gap-2 font-bold", - Icon { - class: "mx-1", - icon: FaWater - } + class: "flex flex-col gap-4", div { - {t!("long-term")._upper_first()} + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", + Icon { + class: "mx-1.5", + icon: FaWater + } + div { + {t!("long-term")._upper_first()} + } + } + TaskList { + tasks: long_term_tasks } - } - TaskList { - tasks: long_term_tasks } } if !overdue_tasks.is_empty() { div { class: "flex flex-col gap-4", div { - class: "px-7 flex flex-row items-center gap-2 font-bold", + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", Icon { - class: "mx-1", + class: "mx-1.25", height: 22, width: 22, icon: FaCalendarXmark @@ -86,9 +88,9 @@ pub(crate) fn CategoryTodayTaskList() -> Element { div { class: "flex flex-col gap-4", div { - class: "px-7 flex flex-row items-center gap-2 font-bold", + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", Icon { - class: "mx-1", + class: "mx-1.25", height: 22, width: 22, icon: FaCalendarCheck diff --git a/src/components/create_button.rs b/src/components/create_button.rs new file mode 100644 index 0000000..a67576d --- /dev/null +++ b/src/components/create_button.rs @@ -0,0 +1,31 @@ +use crate::components::project_form::PROJECT_BEING_EDITED; +use crate::components::{button_primary::ButtonPrimary, task_form::TASK_BEING_EDITED}; +use crate::route::Route; +use dioxus::prelude::*; +use dioxus_free_icons::{Icon, icons::fa_solid_icons::FaGavel}; + +#[component] +pub(crate) fn CreateButton() -> Element { + let navigator = use_navigator(); + let current_route = use_route(); + rsx! { + ButtonPrimary { + class: "pointer-events-auto m-4 self-end *:rounded-full! *:p-4", + onclick: move |_| { + *TASK_BEING_EDITED.write() = None; + *PROJECT_BEING_EDITED.write() = None; + navigator.push( + match current_route { + Route::ProjectsPage => Route::ProjectFormPage, + _ => Route::TaskFormPage, + } + ); + }, + Icon { + icon: FaGavel, + height: 24, + width: 24 + } + } + } +} diff --git a/src/components/error_boundary_message.rs b/src/components/error_boundary_message.rs index 4beb4d4..6beb3ac 100644 --- a/src/components/error_boundary_message.rs +++ b/src/components/error_boundary_message.rs @@ -14,6 +14,7 @@ pub(crate) fn ErrorBoundaryMessage(children: Element, class: Option) -> class: "grow flex flex-col justify-center items-center", div { Icon { + class: "text-gray-500", icon: FaTriangleExclamation, height: 32, width: 32 diff --git a/src/components/form_open_button.rs b/src/components/form_open_button.rs deleted file mode 100644 index 253e1c4..0000000 --- a/src/components/form_open_button.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::models::project::Project; -use crate::models::task::Task; -use dioxus::prelude::*; -use dioxus_free_icons::{ - Icon, - icons::fa_solid_icons::{FaPlus, FaXmark}, -}; - -#[component] -pub(crate) fn FormOpenButton(opened: Signal) -> Element { - let mut project_being_edited = use_context::>>(); - let mut task_being_edited = use_context::>>(); - - rsx! { - button { - class: "pointer-events-auto m-4 py-4 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200 cursor-pointer", - onclick: move |_| { - if opened() { - project_being_edited.set(None); - task_being_edited.set(None); - } - opened.set(!opened()); - }, - if opened() { - Icon { - icon: FaXmark, - height: 24, - width: 24 - } - } else { - Icon { - icon: FaPlus, - height: 24, - width: 24 - } - } - } - } -} diff --git a/src/components/input.rs b/src/components/input.rs new file mode 100644 index 0000000..91aad72 --- /dev/null +++ b/src/components/input.rs @@ -0,0 +1,53 @@ +use dioxus::prelude::*; + +#[component] +pub(crate) fn Input( + class: Option, + name: String, + r#type: String, + id: Option, + #[props(extends = GlobalAttributes, extends = input)] attributes: Vec, + // TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/5271 gets resolved. + autofocus: Option, + // TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved. + oninput: Option>>, + onchange: Option>>, +) -> Element { + rsx! { + input { + class: format!( + /* `w-full` is required for the Chromium renderer to allow the input to shrink + properly. */ + "pt-3 pb-2.25 w-full {} bg-gray-800-muted enabled:hover:bg-gray-800 enabled:focus:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl outline-0 {} transition-all duration-150 {}", + match r#type.as_str() { + "date" => "ps-3.25 pe-3", + _ => "px-4" + }, + match r#type.as_str() { + "text" | "number" => "", + _ => "enabled:cursor-pointer" + }, + class.unwrap_or("".to_owned()) + ), + name: name.clone(), + r#type, + id: id.unwrap_or(format!("input_{}", name)), + oninput: move |event| { + if let Some(oninput) = oninput { + oninput.call(event); + } + }, + onchange: move |event| { + if let Some(onchange) = oninput { + onchange.call(event); + } + }, + onmounted: move |element| async move { + if let Some(true) = autofocus { + let _ = element.set_focus(true).await; + } + }, + ..attributes + } + } +} diff --git a/src/components/input_label.rs b/src/components/input_label.rs new file mode 100644 index 0000000..b1b859d --- /dev/null +++ b/src/components/input_label.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; +use dioxus_free_icons::{Icon, IconShape}; + +#[component] +pub(crate) fn InputLabel( + icon: I, + r#for: Option, +) -> Element { + rsx! { + label { + r#for, + class: "mt-0.5 min-w-7 flex flex-row justify-center items-center", + Icon { + class: "text-gray-600", + icon, + height: 16, + width: 16 + } + } + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 81d8ae0..4ca3745 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,16 +1,21 @@ pub(crate) mod app; pub(crate) mod bottom_panel; +pub(crate) mod button_primary; +pub(crate) mod button_secondary; pub(crate) mod category_calendar_task_list; pub(crate) mod category_input; pub(crate) mod category_today_task_list; +pub(crate) mod create_button; pub(crate) mod error_boundary_message; -pub(crate) mod form_open_button; +pub(crate) mod input; +pub(crate) mod input_label; pub(crate) mod navigation; pub(crate) mod navigation_item; pub(crate) mod project_form; pub(crate) mod project_list; pub(crate) mod project_select; -pub(crate) mod reoccurrence_input; +pub(crate) mod reoccurrence_interval_input; +pub(crate) mod select_button; pub(crate) mod sticky_bottom; pub(crate) mod subtasks_form; pub(crate) mod task_form; diff --git a/src/components/navigation.rs b/src/components/navigation.rs index 4c01216..0ced100 100644 --- a/src/components/navigation.rs +++ b/src/components/navigation.rs @@ -2,32 +2,37 @@ use crate::components::navigation_item::NavigationItem; use crate::route::Route; use dioxus::prelude::*; use dioxus_free_icons::Icon; +use dioxus_free_icons::icons::fa_regular_icons::FaLightbulb; use dioxus_free_icons::icons::fa_solid_icons::{ - FaBars, FaCalendarDay, FaCalendarDays, FaCheck, FaForward, FaHourglassHalf, FaInbox, FaList, - FaQuestion, FaTrashCan, + FaBars, FaCalendarDay, FaCalendarDays, FaHourglassHalf, FaInbox, FaList, FaSignsPost, + FaTrashCan, FaVolcano, }; #[component] -pub(crate) fn Navigation(expanded: Signal) -> Element { +pub(crate) fn Navigation(is_expanded: Signal) -> Element { rsx! { div { class: "grid grid-cols-5 justify-stretch", button { class: format!( - "py-5 flex flex-row justify-center items-center {} cursor-pointer", - if expanded() { "text-zinc-200" } - else { "text-zinc-500" } + "py-2 flex flex-row justify-center items-center cursor-pointer", ), - onclick: move |_| expanded.set(!expanded()), - Icon { - icon: FaBars, - height: 24, - width: 24 + onclick: move |_| is_expanded.set(!is_expanded()), + div { + class: format!("pt-2.5 px-4 {} transition-all duration-150", + if is_expanded() { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" } + else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" } + ), + Icon { + icon: FaBars, + height: 24, + width: 24 + } } }, NavigationItem { route: Route::CategoryNextStepsPage, - icon: FaForward + icon: FaSignsPost }, NavigationItem { route: Route::CategoryCalendarPage, @@ -41,7 +46,7 @@ pub(crate) fn Navigation(expanded: Signal) -> Element { route: Route::CategoryInboxPage, icon: FaInbox }, - {if expanded() { + {if is_expanded() { rsx! { NavigationItem { route: Route::ProjectsPage, @@ -53,11 +58,11 @@ pub(crate) fn Navigation(expanded: Signal) -> Element { }, NavigationItem { route: Route::CategoryDonePage, - icon: FaCheck + icon: FaVolcano }, NavigationItem { route: Route::CategorySomedayMaybePage, - icon: FaQuestion + icon: FaLightbulb }, NavigationItem { route: Route::CategoryWaitingForPage, diff --git a/src/components/navigation_item.rs b/src/components/navigation_item.rs index 39c139e..720c535 100644 --- a/src/components/navigation_item.rs +++ b/src/components/navigation_item.rs @@ -13,14 +13,18 @@ pub(crate) fn NavigationItem( Link { to: route.clone(), class: format!( - "py-5 flex flex-row justify-center items-center {}", - if current_route == route { "text-zinc-200" } - else { "text-zinc-500" } + "py-2.5 flex flex-row justify-center items-center hover:*:bg-gray-900 active:*:text-gray-400", ), - Icon { - icon, - height: 24, - width: 24 + div { + class: format!("pt-2.5 px-4 {} transition-all duration-150", + if current_route == route { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" } + else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" } + ), + Icon { + icon, + height: 24, + width: 24 + } } } } diff --git a/src/components/project_form.rs b/src/components/project_form.rs index 07a2206..5ac7857 100644 --- a/src/components/project_form.rs +++ b/src/components/project_form.rs @@ -1,82 +1,110 @@ +use crate::components::button_primary::ButtonPrimary; +use crate::components::button_secondary::ButtonSecondary; +use crate::components::input::Input; +use crate::components::input_label::InputLabel; use crate::models::project::Project; use crate::server::projects::{create_project, delete_project, edit_project}; use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_solid_icons::{FaFloppyDisk, FaPenClip, FaTrashCan}; +use dioxus_free_icons::icons::fa_solid_icons::{FaFeatherPointed, FaStamp, FaTrashCan, FaXmark}; + +pub(crate) static PROJECT_BEING_EDITED: GlobalSignal> = Signal::global(|| None); #[component] -pub(crate) fn ProjectForm( - project: Option, - on_successful_submit: EventHandler<()>, -) -> Element { +pub(crate) fn ProjectForm() -> Element { + let navigator = use_navigator(); + let project = PROJECT_BEING_EDITED(); let project_for_submit = project.clone(); rsx! { form { + class: "px-4 flex flex-col gap-4", onsubmit: move |event| { event.prevent_default(); let project = project_for_submit.clone(); async move { let new_project = event.parsed_values().unwrap(); - if let Some(project) = project { - let _ = edit_project(project.id, new_project).await; + let result = if let Some(project) = project { + edit_project(project.id, new_project).await } else { - let _ = create_project(new_project).await; + create_project(new_project).await + }; + if result.is_ok() { + navigator.go_back(); } - on_successful_submit.call(()); } }, - class: "p-4 flex flex-col gap-4", + id: "form_project", div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_title", - class: "flex flex-row justify-center items-center min-w-6", - Icon { - class: "text-zinc-400/50", - icon: FaPenClip, - height: 16, - width: 16 - } + InputLabel { + icon: FaFeatherPointed, + r#for: "input_title" } - input { + Input { + class: "grow", name: "title", required: true, - initial_value: project.as_ref().map(|project| project.title.to_owned()), r#type: "text", - class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", - id: "input_title" + initial_value: project.as_ref().map(|project| project.title.to_owned()), } } - div { - class: "flex flex-row justify-between mt-auto", - button { - r#type: "button", - class: "py-3 px-4 bg-zinc-300/50 rounded-lg cursor-pointer", - onclick: move |_| { + } + div { + class: "px-4 grid grid-cols-3 gap-3 mt-auto", + ButtonSecondary { + r#type: "button", + class: "grow", + onclick: { + let project = project.clone(); + move |_| { let project = project.clone(); async move { if let Some(project) = project { - let _ = delete_project(project.id).await; + let result = delete_project(project.id).await; + if result.is_ok() { + navigator.go_back(); + } + } else { + navigator.go_back(); + } + } + } + }, + Icon { + icon: FaTrashCan, + height: 16, + width: 16 + } + } + if project.is_some() { + div { + class: "grow flex flex-col items-stretch", + GoBackButton { + ButtonSecondary { + /* TODO: Replace w-full` with proper flexbox styling once + https://github.com/DioxusLabs/dioxus/issues/5269 is solved. */ + class: "w-full", + r#type: "button", + Icon { + icon: FaXmark, + height: 16, + width: 16 } - on_successful_submit.call(()); } - }, - Icon { - icon: FaTrashCan, - height: 16, - width: 16 } } - button { - r#type: "submit", - class: "py-3 px-4 bg-zinc-300/50 rounded-lg cursor-pointer", - Icon { - icon: FaFloppyDisk, - height: 16, - width: 16 - } + } else { + div {} + } + ButtonPrimary { + form: "form_project", + r#type: "submit", + Icon { + icon: FaStamp, + height: 16, + width: 16 } } } diff --git a/src/components/project_list.rs b/src/components/project_list.rs index 28b5ae1..aa8001f 100644 --- a/src/components/project_list.rs +++ b/src/components/project_list.rs @@ -1,24 +1,22 @@ -use crate::{hooks::use_projects, models::project::Project}; +use crate::route::Route; +use crate::{components::project_form::PROJECT_BEING_EDITED, hooks::use_projects}; use dioxus::prelude::*; #[component] pub(crate) fn ProjectList() -> Element { + let navigator = use_navigator(); let projects = use_projects()?; - let mut project_being_edited = use_context::>>(); - rsx! { div { class: "flex flex-col", for project in projects { div { + class: "px-7 py-4 hover:bg-gray-800 font-medium text-pretty wrap-anywhere select-none transition-all duration-150 cursor-pointer", key: "{project.id}", - class: format!( - "px-7 py-4 select-none {} text-pretty wrap-anywhere", - if project_being_edited().is_some_and(|p| p.id == project.id) { - "bg-zinc-700" - } else { "" } - ), - onclick: move |_| project_being_edited.set(Some(project.clone())), + onclick: move |_| { + *PROJECT_BEING_EDITED.write() = Some(project.clone()); + navigator.push(Route::ProjectFormPage); + }, {project.title.clone()} } } diff --git a/src/components/project_select.rs b/src/components/project_select.rs index fc3021e..2dd8060 100644 --- a/src/components/project_select.rs +++ b/src/components/project_select.rs @@ -10,8 +10,8 @@ pub(crate) fn ProjectSelect(initial_selected_id: Option) -> Element { rsx! { select { name: "project_id", - class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer", - id: "input_project", + class: "px-4 pt-3 pb-2.25 bg-gray-800-muted enabled:hover:bg-gray-800 enabled:active:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer transition-all duration-150", + id: "input_project_id", option { value: 0, {t!("none")} diff --git a/src/components/reoccurrence_input.rs b/src/components/reoccurrence_input.rs deleted file mode 100644 index 9f84b86..0000000 --- a/src/components/reoccurrence_input.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::models::category::ReoccurrenceInterval; -use dioxus::core_macro::rsx; -use dioxus::dioxus_core::Element; -use dioxus::prelude::*; -use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_solid_icons::{FaBan, FaEarthEurope, FaMoon, FaSun}; - -#[component] -pub(crate) fn ReoccurrenceIntervalInput( - reoccurrence_interval: Signal>, - class_buttons: Option<&'static str>, -) -> Element { - rsx! { - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if reoccurrence_interval().is_none() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(None); - }, - Icon { - icon: FaBan, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Day) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) - }, - Icon { - icon: FaSun, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Month) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)) - }, - Icon { - icon: FaMoon, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Year) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)) - }, - Icon { - icon: FaEarthEurope, - height: 16, - width: 16 - } - } - } -} diff --git a/src/components/reoccurrence_interval_input.rs b/src/components/reoccurrence_interval_input.rs new file mode 100644 index 0000000..03a6e27 --- /dev/null +++ b/src/components/reoccurrence_interval_input.rs @@ -0,0 +1,43 @@ +use crate::components::select_button::SelectButton; +use crate::models::category::ReoccurrenceInterval; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use dioxus_free_icons::icons::fa_solid_icons::{FaBan, FaEarthEurope, FaMoon, FaSun}; + +#[component] +pub(crate) fn ReoccurrenceIntervalInput( + reoccurrence_interval: Signal>, + class_buttons: Option<&'static str>, +) -> Element { + rsx! { + SelectButton { + icon: FaBan, + is_selected: reoccurrence_interval().is_none(), + on_select: move |_| { + reoccurrence_interval.set(None); + } + } + SelectButton { + icon: FaSun, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Day)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) + } + } + SelectButton { + icon: FaMoon, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Month)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)); + } + } + SelectButton { + icon: FaEarthEurope, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Year)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)); + } + } + } +} diff --git a/src/components/select_button.rs b/src/components/select_button.rs new file mode 100644 index 0000000..50eba12 --- /dev/null +++ b/src/components/select_button.rs @@ -0,0 +1,28 @@ +use dioxus::prelude::*; +use dioxus_free_icons::{Icon, IconShape}; + +#[component] +pub(crate) fn SelectButton( + icon: I, + is_selected: bool, + on_select: Callback, +) -> Element { + rsx! { + button { + r#type: "button", + class: format!( + "pt-4.5 flex flex-row justify-center items-center {} rounded-xl transition-all duration-150", + if is_selected { "pb-3.75 bg-gray-900 drop-shadow-[0_0_0_var(--color-gray-900-muted)]" } + else { "pb-2.75 mt-1 bg-gray-800-muted hover:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] text-gray-400 cursor-pointer" } + ), + onclick: move |_| { + on_select.call(()); + }, + Icon { + icon, + height: 16, + width: 16 + } + }, + } +} diff --git a/src/components/subtasks_form.rs b/src/components/subtasks_form.rs index fb4948d..1c7f34b 100644 --- a/src/components/subtasks_form.rs +++ b/src/components/subtasks_form.rs @@ -1,3 +1,6 @@ +use crate::components::button_secondary::ButtonSecondary; +use crate::components::input::Input; +use crate::components::input_label::InputLabel; use crate::hooks::use_subtasks_of_task; use crate::models::subtask::NewSubtask; use crate::models::task::Task; @@ -6,146 +9,131 @@ use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_regular_icons::FaSquare; -use dioxus_free_icons::icons::fa_solid_icons::{FaListCheck, FaPlus, FaSquareCheck, FaTrashCan}; +use dioxus_free_icons::icons::fa_solid_icons::{FaGavel, FaListCheck, FaTrashCan}; #[component] pub(crate) fn SubtasksForm(task: Task) -> Element { let subtasks = use_subtasks_of_task(task.id)?; let mut new_title = use_signal(String::new); rsx! { - form { - class: "flex flex-row items-center gap-3", - onsubmit: move |event| { - event.prevent_default(); - let task = task.clone(); - async move { - let new_subtask = NewSubtask { - task_id: task.id, - title: event.get("title").first().cloned().and_then(|value| match value { - FormValue::Text(value) => Some(value), - FormValue::File(_) => None - }).unwrap(), - is_completed: false - }; - let _ = create_subtask(new_subtask).await; - new_title.set(String::new()); - } - }, - label { - r#for: "input_new_title", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaListCheck, - height: 16, - width: 16 - } - } - div { - class: "grow grid grid-cols-6 gap-2", - input { - name: "title", - required: true, - value: new_title, - r#type: "text", - class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg", - id: "input_new_title", - onchange: move |event| new_title.set(event.value()) - } - button { - r#type: "submit", - class: "py-2 col-span-1 flex flex-row justify-center items-center bg-zinc-800/50 rounded-lg cursor-pointer", - Icon { - icon: FaPlus, - height: 16, - width: 16 - } - } - } - } - for subtask in subtasks { - div { - key: "{subtask.id}", + div { + class: "flex flex-col gap-3", + form { class: "flex flex-row items-center gap-3", - button { - class: "min-w-6 flex flex-row justify-center items-center text-zinc-400/50 cursor-pointer", - onclick: { - let subtask = subtask.clone(); - move |_| { - let subtask = subtask.clone(); - async move { - let new_subtask = NewSubtask { - task_id: subtask.task_id, - title: subtask.title.clone(), - is_completed: !subtask.is_completed - }; - let _ = edit_subtask( - subtask.id, - new_subtask - ).await; - } - } - }, - if subtask.is_completed { - Icon { - icon: FaSquareCheck, - height: 24, - width: 24 - } - } else { - Icon { - icon: FaSquare, - height: 24, - width: 24 - } + onsubmit: move |event| { + event.prevent_default(); + let task = task.clone(); + async move { + let new_subtask = NewSubtask { + task_id: task.id, + title: event.get("new_title").first().cloned().and_then(|value| match value { + FormValue::Text(value) => Some(value), + FormValue::File(_) => None + }).unwrap(), + is_completed: false + }; + let _ = create_subtask(new_subtask).await; + new_title.set(String::new()); } + }, + InputLabel { + icon: FaListCheck, + r#for: "input_new_title" } div { - class: "grow grid grid-cols-6 gap-2", - input { + class: "grow flex flex-row items-end gap-3", + Input { + class: "grow", + name: "new_title", r#type: "text", - class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg", - id: "input_title_{subtask.id}", - initial_value: subtask.title.clone(), - onchange: { - let subtask = subtask.clone(); - move |event: Event| { - let subtask = subtask.clone(); - async move { - let new_subtask = NewSubtask { - task_id: subtask.task_id, - title: event.value(), - is_completed: subtask.is_completed - }; - if new_subtask.title.is_empty() { - let _ = delete_subtask(subtask.id).await; - } else { - let _ = edit_subtask( - subtask.id, - new_subtask - ).await; - } - } - } + required: true, + value: new_title, + onchange: move |event: Event| new_title.set(event.value()) + } + ButtonSecondary { + r#type: "submit", + Icon { + icon: FaGavel, + height: 16, + width: 16 } } + } + } + for subtask in subtasks { + div { + key: "{subtask.id}", + class: "flex flex-row items-center gap-3", button { - r#type: "button", - class: "py-2 flex flex-row justify-center items-center col-span-1 bg-zinc-800/50 rounded-lg cursor-pointer", + class: "mt-1.5 hover:mt-1 hover:pb-0.5 min-w-7 cursor-pointer transition-all duration-150", onclick: { let subtask = subtask.clone(); move |_| { let subtask = subtask.clone(); async move { - let _ = delete_subtask(subtask.id).await; + let new_subtask = NewSubtask { + task_id: subtask.task_id, + title: subtask.title.clone(), + is_completed: !subtask.is_completed + }; + let _ = edit_subtask( + subtask.id, + new_subtask + ).await; } } }, - Icon { - icon: FaTrashCan, - height: 16, - width: 16 + div { + class: format!("grow h-7 w-7 mb-[4px] drop-shadow-[0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800)] rounded-full {}", + if subtask.is_completed {"bg-gray-600"} else {"border-3 border-gray-600"} + ) + } + } + div { + class: "grow flex flex-row items-end gap-3", + Input { + class: "grow", + name: "title_edit_{subtask.id}", + r#type: "text", + initial_value: subtask.title.clone(), + onchange: { + let subtask = subtask.clone(); + move |event: Event| { + let subtask = subtask.clone(); + async move { + let new_subtask = NewSubtask { + task_id: subtask.task_id, + title: event.value(), + is_completed: subtask.is_completed + }; + if new_subtask.title.is_empty() { + let _ = delete_subtask(subtask.id).await; + } else { + let _ = edit_subtask( + subtask.id, + new_subtask + ).await; + } + } + } + } + } + ButtonSecondary { + r#type: "button", + onclick: { + let subtask = subtask.clone(); + move |_| { + let subtask = subtask.clone(); + async move { + let _ = delete_subtask(subtask.id).await; + } + } + }, + Icon { + icon: FaTrashCan, + height: 16, + width: 16 + } } } } diff --git a/src/components/task_form.rs b/src/components/task_form.rs index 7d85bd3..5c6eb3e 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,11 +1,14 @@ +use crate::components::button_primary::ButtonPrimary; +use crate::components::button_secondary::ButtonSecondary; use crate::components::category_input::CategoryInput; +use crate::components::input::Input; +use crate::components::input_label::InputLabel; use crate::components::project_select::ProjectSelect; -use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; +use crate::components::reoccurrence_interval_input::ReoccurrenceIntervalInput; use crate::components::subtasks_form::SubtasksForm; use crate::models::category::{CalendarTime, Category, Reoccurrence}; use crate::models::task::NewTask; use crate::models::task::Task; -use crate::route::Route; use crate::server::tasks::{create_task, delete_task, edit_task}; use chrono::Duration; use dioxus::core_macro::{component, rsx}; @@ -13,8 +16,8 @@ use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; use dioxus_free_icons::icons::fa_solid_icons::{ - FaBell, FaBomb, FaClock, FaFloppyDisk, FaHourglassEnd, FaLayerGroup, FaList, FaPenClip, - FaRepeat, FaTrashCan, + FaBell, FaBomb, FaClock, FaFeatherPointed, FaHourglassEnd, FaList, FaRepeat, FaScroll, FaStamp, + FaTrashCan, FaXmark, }; use dioxus_i18n::t; use serde::{Deserialize, Serialize}; @@ -51,24 +54,19 @@ struct InputData { project_id: Option, } +pub(crate) static TASK_BEING_EDITED: GlobalSignal> = Signal::global(|| None); +pub(crate) static LATEST_VISITED_CATEGORY: GlobalSignal = + Signal::global(|| Category::Inbox); + #[component] -pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<()>) -> Element { - let route = use_route::(); +pub(crate) fn TaskForm() -> Element { + let navigator = use_navigator(); + let task = TASK_BEING_EDITED(); let selected_category = use_signal(|| { if let Some(task) = &task { task.category.clone() } else { - match route { - Route::CategorySomedayMaybePage => Category::SomedayMaybe, - Route::CategoryWaitingForPage => Category::WaitingFor(String::new()), - Route::CategoryNextStepsPage => Category::NextSteps, - Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar { - date: chrono::Local::now().date_naive(), - reoccurrence: None, - time: None, - }, - _ => Category::Inbox, - } + LATEST_VISITED_CATEGORY() } }); let category_calendar_reoccurrence_interval = use_signal(|| { @@ -108,9 +106,9 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() rsx! { div { - class: "p-4 flex flex-col gap-4", + class: "grow px-4 flex flex-col gap-6.5", form { - class: "flex flex-col gap-4", + class: "flex flex-col gap-8", id: "form_task", onsubmit: move |event| { event.prevent_default(); @@ -152,56 +150,45 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() project_id: input_data.project_id .and_then(|deadline| deadline.parse().ok()).filter(|&id| id > 0), }; - if let Some(task) = task { - let _ = edit_task(task.id, new_task).await; + let result = if let Some(task) = task { + edit_task(task.id, new_task).await } else { - let _ = create_task(new_task).await; + create_task(new_task).await + }; + if result.is_ok() { + navigator.go_back(); } - on_successful_submit.call(()); } }, div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "input_title", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaPenClip, - height: 16, - width: 16 - } + icon: FaFeatherPointed }, - input { + Input { + class: "grow", name: "title", required: true, initial_value: task.as_ref().map(|task| task.title.clone()), r#type: "text", - class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", - id: "input_title" - }, + autofocus: task.is_none() + } }, div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_project", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaList, - height: 16, - width: 16 - } + InputLabel { + r#for: "input_project_id", + icon: FaList }, SuspenseBoundary { fallback: |_| { rsx ! { select { - class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer", + class: "px-4 pt-3 pb-2.25 bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer", option { - value: 0, - {t!("none")} - }, + value: 0 + } } } }, @@ -212,98 +199,69 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() }, div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_deadline", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaBomb, - height: 16, - width: 16 - } + InputLabel { + icon: FaBomb, + r#for: "input_deadline" }, - input { + Input { name: "deadline", initial_value: task.as_ref().and_then(|task| task.deadline) .map(|deadline| deadline.format("%Y-%m-%d").to_string()), r#type: "date", - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow basis-0 cursor-pointer", - id: "input_deadline" + class: "grow basis-0" } }, div { class: "flex flex-row items-center gap-3", - label { - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaLayerGroup, - height: 16, - width: 16 - } + InputLabel { + icon: FaScroll }, CategoryInput { - selected_category: selected_category, - class: "grow" + class: "grow", + selected_category: selected_category } } match selected_category() { Category::WaitingFor(waiting_for) => rsx! { div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_waiting_for", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaHourglassEnd, - height: 16, - width: 16 - } + InputLabel { + icon: FaHourglassEnd, + r#for: "input_category_waiting_for", }, - input { + Input { + class: "grow", name: "category_waiting_for", required: true, initial_value: waiting_for, r#type: "text", - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", - id: "input_category_waiting_for" - }, + } } }, Category::Calendar { date, reoccurrence, time } => rsx! { div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_category_calendar_date", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaClock, - height: 16, - width: 16 - } + InputLabel { + icon: FaClock, + r#for: "input_category_calendar_date" }, div { - class: "grow flex flex-row gap-2", - input { - r#type: "date", + class: "grow grid grid-cols-7 gap-3", + Input { + class: "grow col-span-4", name: "category_calendar_date", + r#type: "date", required: true, initial_value: date.format("%Y-%m-%d").to_string(), - class: - "py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer", - id: "input_category_calendar_date" }, - input { - r#type: "time", + Input { + class: "grow col-span-3", name: "category_calendar_time", + r#type: "time", initial_value: time.map(|calendar_time| calendar_time.time.format("%H:%M").to_string() ), - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer", - id: "input_category_calendar_time", - oninput: move |event| { + oninput: move |event: Event| { category_calendar_has_time.set(!event.value().is_empty()); } } @@ -311,60 +269,46 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() }, div { class: "flex flex-row items-center gap-3", - label { - r#for: "category_calendar_reoccurrence_length", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaRepeat, - height: 16, - width: 16 - } + InputLabel { + icon: FaRepeat, + r#for: "category_calendar_reoccurrence_length" }, div { - class: "grow grid grid-cols-6 gap-2", + class: "grow grid grid-cols-5 items-end gap-3", ReoccurrenceIntervalInput { reoccurrence_interval: category_calendar_reoccurrence_interval }, - input { + Input { + class: "text-right", r#type: "number", inputmode: "numeric", name: "category_calendar_reoccurrence_length", disabled: category_calendar_reoccurrence_interval().is_none(), required: true, - min: 1, + min: "1", initial_value: category_calendar_reoccurrence_interval().map_or( String::new(), |_| reoccurrence.map_or(1, |reoccurrence| reoccurrence.length).to_string() - ), - class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right", - id: "category_calendar_reoccurrence_length" + ) } } }, if category_calendar_has_time() { div { class: "flex flex-row items-center gap-3", - label { - r#for: "category_calendar_reminder_offset_index", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaBell, - height: 16, - width: 16 - } + InputLabel { + r#for: "input_category_calendar_reminder_offset_index", + icon: FaBell }, input { - r#type: "range", + class: "grow", name: "category_calendar_reminder_offset_index", + r#type: "range", min: 0, max: REMINDER_OFFSETS.len() as i64 - 1, initial_value: category_calendar_reminder_offset_index() .to_string(), - class: "grow input-range-reverse cursor-pointer", - id: "category_calendar_has_reminder", oninput: move |event| { category_calendar_reminder_offset_index.set( event.value().parse().unwrap() @@ -372,8 +316,8 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() } }, label { - r#for: "category_calendar_reminder_offset_index", class: "pr-3 min-w-16 text-right", + r#for: "category_calendar_reminder_offset_index", {REMINDER_OFFSETS[category_calendar_reminder_offset_index()] .map( |offset| if offset.num_hours() < 1 { @@ -399,17 +343,20 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() } } } - div { - class: "flex flex-row justify-between mt-auto", - button { - r#type: "button", - class: "py-3 px-4 bg-zinc-300/50 rounded-lg cursor-pointer", - onclick: move |_| { + } + div { + class: "px-4 grid grid-cols-3 gap-3 mt-auto", + ButtonSecondary { + r#type: "button", + class: "grow", + onclick: { + let task = task.clone(); + move |_| { let task = task.clone(); async move { if let Some(task) = task { - if let Category::Trash = task.category { - let _ = delete_task(task.id).await; + let result = if let Category::Trash = task.category { + delete_task(task.id).await } else { let new_task = NewTask { title: task.title.to_owned(), @@ -417,27 +364,50 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() category: Category::Trash, project_id: task.project_id }; - let _ = edit_task(task.id, new_task).await; + edit_task(task.id, new_task).await.map(|_| ()) + }; + if result.is_ok() { + navigator.go_back(); } + } else { + navigator.go_back(); + } + } + } + }, + Icon { + icon: FaTrashCan, + height: 16, + width: 16 + } + } + if task.is_some() { + div { + class: "grow flex flex-col items-stretch", + GoBackButton { + ButtonSecondary { + /* TODO: Replace w-full` with proper flexbox styling once + https://github.com/DioxusLabs/dioxus/issues/5269 is solved. */ + class: "w-full", + r#type: "button", + Icon { + icon: FaXmark, + height: 16, + width: 16 } - on_successful_submit.call(()); } - }, - Icon { - icon: FaTrashCan, - height: 16, - width: 16 } } - button { - form: "form_task", - r#type: "submit", - class: "py-3 px-4 flex flex-row justify-center items-center bg-zinc-300/50 rounded-lg cursor-pointer", - Icon { - icon: FaFloppyDisk, - height: 16, - width: 16 - } + } else { + div {} + } + ButtonPrimary { + form: "form_task", + r#type: "submit", + Icon { + icon: FaStamp, + height: 16, + width: 16 } } } diff --git a/src/components/task_list.rs b/src/components/task_list.rs index 98a4931..1d1bc8d 100644 --- a/src/components/task_list.rs +++ b/src/components/task_list.rs @@ -1,17 +1,16 @@ +use crate::components::task_form::TASK_BEING_EDITED; use crate::components::task_list_item::TaskListItem; use crate::models::category::Category; -use crate::models::task::{Task, TaskWithSubtasks}; +use crate::models::task::TaskWithSubtasks; +use crate::route::Route; use crate::server::tasks::complete_task; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_regular_icons::FaSquare; -use dioxus_free_icons::icons::fa_solid_icons::FaSquareCheck; #[component] pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str>) -> Element { - let mut task_being_edited = use_context::>>(); + let navigator = use_navigator(); rsx! { div { class: format!("flex flex-col {}", class.unwrap_or("")), @@ -19,28 +18,32 @@ pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str> div { key: "{task.task.id}", class: format!( - "px-7 pt-4.25 {} flex flex-row items-start gap-4 select-none {}", + "px-7 pt-3.75 {} flex flex-row items-start gap-4 hover:bg-gray-800 cursor-pointer select-none transition-all duration-150", if task.task.deadline.is_some() || !task.subtasks.is_empty() { "pb-0.25" } else if let Category::Calendar { time, .. } = &task.task.category { if time.is_some() { "pb-0.25" } else { - "pb-4.25" + "pb-3.75" } } else { - "pb-4.25" + "pb-3.75" }, - if task_being_edited().is_some_and(|t| t.id == task.task.id) { - "bg-zinc-700" - } else { "" } ), onclick: { let task = task.clone(); - move |_| task_being_edited.set(Some(task.task.clone())) + move |_| { + *TASK_BEING_EDITED.write() = Some(task.task.clone()); + navigator.push(Route::TaskFormPage); + } }, button { - class: "text-zinc-500", + class: format!( + "mt-0.5 hover:mt-0 hover:pb-0.5 transition-all duration-150 cursor-pointer {}", + if let Category::Done = task.task.category { "pointer-events-none" } + else { "" } + ), onclick: { move |event: Event| { // To prevent editing the task. @@ -50,23 +53,21 @@ pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str> } } }, - if let Category::Done = task.task.category { - Icon { - icon: FaSquareCheck, - height: 30, - width: 30 - } - } else { - Icon { - class: "cursor-pointer", - icon: FaSquare, - height: 30, - width: 30 - } + div { + class: format!("h-8 w-8 rounded-full {}", + if let Category::Done = task.task.category { + "mt-[3px] mb-[2px] bg-amber-300-muted" + } else { + "mb-[5px] border-3 border-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)]" + } + ) } }, - TaskListItem { - task: task.clone() + div { + class: "mt-1.5", + TaskListItem { + task: task.clone() + } } } } diff --git a/src/components/task_list_item.rs b/src/components/task_list_item.rs index 26b3b47..dfd205b 100644 --- a/src/components/task_list_item.rs +++ b/src/components/task_list_item.rs @@ -25,7 +25,7 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { class: "flex flex-row gap-4", if let Some(deadline) = task.task.deadline { div { - class: "flex flex-row items-center gap-1 text-sm text-zinc-400", + class: "flex flex-row items-center gap-1 text-sm text-gray-500", Icon { icon: FaBomb, height: 14, @@ -76,7 +76,7 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { if let Category::Calendar { time, .. } = task.task.category { if let Some(calendar_time) = time { div { - class: "flex flex-row items-center gap-1 text-sm text-zinc-400", + class: "flex flex-row items-center gap-1 text-sm text-gray-500", Icon { icon: FaClock, height: 14, @@ -91,7 +91,7 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { } if !task.subtasks.is_empty() { div { - class: "flex flex-row items-center gap-1 text-sm text-zinc-400", + class: "flex flex-row items-center gap-1 text-sm text-gray-500", Icon { icon: FaListCheck, height: 14, diff --git a/src/layouts/main.rs b/src/layouts/main.rs deleted file mode 100644 index f0800ad..0000000 --- a/src/layouts/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::form_open_button::FormOpenButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::models::project::Project; -use crate::models::task::Task; -use crate::route::Route; -use dioxus::core_macro::rsx; -use dioxus::dioxus_core::Element; -use dioxus::prelude::*; -use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_solid_icons::FaCog; - -#[component] -pub(crate) fn Main() -> Element { - let mut display_form = use_signal(|| false); - let project_being_edited = - use_context_provider::>>(|| Signal::new(None)); - let task_being_edited = use_context_provider::>>(|| Signal::new(None)); - - use_effect(move || { - display_form.set(project_being_edited().is_some() || task_being_edited().is_some()); - }); - - rsx! { - SuspenseBoundary { - fallback: |_| { - rsx! { - div { - class: "grow flex flex-col justify-center items-center", - Icon { - class: "animate-[spin_2000ms_linear_infinite]", - icon: FaCog, - height: 32, - width: 32 - } - } - } - }, - Outlet:: {} - } - StickyBottom { - FormOpenButton { - opened: display_form, - } - BottomPanel { - display_form: display_form, - } - } - } -} diff --git a/src/layouts/mod.rs b/src/layouts/mod.rs index 052501d..896ab04 100644 --- a/src/layouts/mod.rs +++ b/src/layouts/mod.rs @@ -1,2 +1,2 @@ -mod main; -pub(crate) use main::Main; +pub(crate) mod navigation; +pub(crate) mod suspense; diff --git a/src/layouts/navigation.rs b/src/layouts/navigation.rs new file mode 100644 index 0000000..9dc2b5f --- /dev/null +++ b/src/layouts/navigation.rs @@ -0,0 +1,38 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::create_button::CreateButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::LATEST_VISITED_CATEGORY; +use crate::models::category::Category; +use crate::route::Route; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; + +#[component] +pub(crate) fn Navigation() -> Element { + let current_route = use_route(); + use_effect(use_reactive(¤t_route, move |current_route| { + *LATEST_VISITED_CATEGORY.write() = match current_route { + Route::CategorySomedayMaybePage => Category::SomedayMaybe, + Route::CategoryWaitingForPage => Category::WaitingFor(String::new()), + Route::CategoryNextStepsPage => Category::NextSteps, + Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar { + date: chrono::Local::now().date_naive(), + reoccurrence: None, + time: None, + }, + _ => Category::Inbox, + }; + })); + + rsx! { + div { + class: "grow flex flex-col pb-36", + Outlet:: {} + } + StickyBottom { + CreateButton {}, + BottomPanel {} + } + } +} diff --git a/src/layouts/suspense.rs b/src/layouts/suspense.rs new file mode 100644 index 0000000..1e7f0e8 --- /dev/null +++ b/src/layouts/suspense.rs @@ -0,0 +1,28 @@ +use crate::route::Route; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use dioxus_free_icons::Icon; +use dioxus_free_icons::icons::fa_solid_icons::FaCog; + +#[component] +pub(crate) fn Suspense() -> Element { + rsx! { + SuspenseBoundary { + fallback: |_| { + rsx! { + div { + class: "grow flex flex-col justify-center items-center", + Icon { + class: "text-gray-500 animate-[spin_3000ms_linear_infinite]", + icon: FaCog, + height: 32, + width: 32 + } + } + } + }, + Outlet:: {} + } + } +} diff --git a/src/route/mod.rs b/src/route/mod.rs index a25b856..c573cb4 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -8,7 +8,9 @@ use crate::views::category_today_page::CategoryTodayPage; use crate::views::category_trash_page::CategoryTrashPage; use crate::views::category_waiting_for_page::CategoryWaitingForPage; use crate::views::not_found_page::NotFoundPage; +use crate::views::project_form_page::ProjectFormPage; use crate::views::projects_page::ProjectsPage; +use crate::views::task_form_page::TaskFormPage; use dioxus::prelude::*; // All variants have the same postfix because they have to match the component names. @@ -16,26 +18,33 @@ use dioxus::prelude::*; #[derive(Clone, Routable, Debug, PartialEq)] #[rustfmt::skip] pub(crate) enum Route { - #[layout(layouts::Main)] - #[redirect("/", || Route::CategoryTodayPage {})] - #[route("/today")] - CategoryTodayPage, - #[route("/inbox")] - CategoryInboxPage, - #[route("/someday-maybe")] - CategorySomedayMaybePage, - #[route("/waiting-for")] - CategoryWaitingForPage, - #[route("/next-steps")] - CategoryNextStepsPage, - #[route("/calendar")] - CategoryCalendarPage, - #[route("/done")] - CategoryDonePage, - #[route("/trash")] - CategoryTrashPage, - #[route("/projects")] - ProjectsPage, + #[layout(layouts::navigation::Navigation)] + #[layout(layouts::suspense::Suspense)] + #[route("/today")] + CategoryTodayPage, + #[route("/inbox")] + CategoryInboxPage, + #[route("/someday-maybe")] + CategorySomedayMaybePage, + #[route("/waiting-for")] + CategoryWaitingForPage, + #[route("/next-steps")] + CategoryNextStepsPage, + #[route("/calendar")] + CategoryCalendarPage, + #[route("/done")] + CategoryDonePage, + #[route("/trash")] + CategoryTrashPage, + #[route("/projects")] + ProjectsPage, + #[end_layout] + #[end_layout] + #[layout(layouts::suspense::Suspense)] + #[route("/task")] + TaskFormPage, + #[route("/project")] + ProjectFormPage, #[end_layout] #[redirect("/", || Route::CategoryTodayPage)] #[route("/:..route")] diff --git a/src/views/category_page.rs b/src/views/category_page.rs index 64fb8e3..b069551 100644 --- a/src/views/category_page.rs +++ b/src/views/category_page.rs @@ -11,8 +11,7 @@ pub(crate) fn CategoryPage(category: Category) -> Element { rsx! { TaskList { - tasks: tasks.clone(), - class: "pb-36" + tasks: tasks.clone() } } } diff --git a/src/views/mod.rs b/src/views/mod.rs index 892573d..e19c82b 100644 --- a/src/views/mod.rs +++ b/src/views/mod.rs @@ -8,4 +8,6 @@ pub(crate) mod category_today_page; pub(crate) mod category_trash_page; pub(crate) mod category_waiting_for_page; pub(crate) mod not_found_page; +pub(crate) mod project_form_page; pub(crate) mod projects_page; +pub(crate) mod task_form_page; diff --git a/src/views/project_form_page.rs b/src/views/project_form_page.rs new file mode 100644 index 0000000..7528841 --- /dev/null +++ b/src/views/project_form_page.rs @@ -0,0 +1,12 @@ +use crate::components::{error_boundary_message::ErrorBoundaryMessage, project_form::ProjectForm}; +use dioxus::prelude::*; + +#[component] +pub(crate) fn ProjectFormPage() -> Element { + rsx! { + ErrorBoundaryMessage { + class: "grow py-4 flex flex-col gap-12", + ProjectForm {} + } + } +} diff --git a/src/views/task_form_page.rs b/src/views/task_form_page.rs new file mode 100644 index 0000000..c184611 --- /dev/null +++ b/src/views/task_form_page.rs @@ -0,0 +1,12 @@ +use crate::components::{error_boundary_message::ErrorBoundaryMessage, task_form::TaskForm}; +use dioxus::prelude::*; + +#[component] +pub(crate) fn TaskFormPage() -> Element { + rsx! { + ErrorBoundaryMessage { + class: "grow py-4 flex flex-col gap-12", + TaskForm {} + } + } +} diff --git a/tailwind.css b/tailwind.css index 664bee6..225f3b1 100644 --- a/tailwind.css +++ b/tailwind.css @@ -20,4 +20,8 @@ /* stylelint-disable-next-line */ @theme { --font-sans: "Inter", "sans"; + --color-amber-300-muted: #b89a2e; + --color-amber-700-muted: #80390b; + --color-gray-800-muted: #141d2d; + --color-gray-900-muted: #0b111f; }