From 3bafcc5a0aba90424f9170ba4176aab11b0694c4 Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Tue, 7 Oct 2025 13:42:45 -0300 Subject: [PATCH] feat: CSV exports, PDF improvements, play internal/external with hour split, roles cleanup, admin companies with 'Cliente avulso', ticket list spacing/alignment fixes, status translations and mappings --- Captura de tela 2025-10-07 133523.png | Bin 0 -> 128256 bytes PROXIMOS_PASSOS.md | 14 +- convex/migrations.ts | 9 +- convex/queues.ts | 6 +- convex/rbac.ts | 9 +- convex/reports.ts | 40 ++- convex/schema.ts | 4 + convex/seed.ts | 16 +- convex/tickets.ts | 65 +++-- convex/users.ts | 14 + middleware.ts | 20 +- prisma/schema.prisma | 15 +- scripts/import-convex-to-prisma.mjs | 12 +- scripts/seed-auth.mjs | 10 +- src/app/admin/companies/page.tsx | 26 ++ src/app/api/admin/companies/[id]/route.ts | 27 ++ src/app/api/admin/companies/route.ts | 47 ++++ .../api/admin/users/assign-company/route.ts | 37 +++ src/app/api/reports/backlog.csv/route.ts | 113 ++++++++ src/app/api/reports/csat.csv/route.ts | 99 +++++++ src/app/api/reports/sla.csv/route.ts | 101 +++++++ .../reports/tickets-by-channel.csv/route.ts | 102 +++++++ src/app/api/tickets/[id]/export/pdf/route.ts | 252 ++++++++++++++++-- src/app/reports/csat/page.tsx | 7 + src/app/reports/sla/page.tsx | 7 + src/components/admin/admin-users-manager.tsx | 78 +++++- .../companies/admin-companies-manager.tsx | 213 +++++++++++++++ src/components/app-sidebar.tsx | 6 +- src/components/chart-area-interactive.tsx | 41 +-- src/components/portal/portal-shell.tsx | 8 +- src/components/portal/portal-ticket-card.tsx | 2 - .../portal/portal-ticket-detail.tsx | 1 - src/components/reports/backlog-report.tsx | 28 +- src/components/tickets/status-badge.tsx | 1 - src/components/tickets/status-select.tsx | 3 +- .../tickets/ticket-summary-header.tsx | 104 +++++--- src/components/tickets/tickets-filters.tsx | 1 - src/components/tickets/tickets-table.tsx | 53 ++-- src/components/tickets/tickets-view.tsx | 2 +- src/lib/auth-client.tsx | 4 +- src/lib/authz.ts | 7 +- src/lib/mappers/ticket.ts | 7 +- src/lib/mocks/tickets.ts | 17 +- src/lib/schemas/ticket.ts | 24 +- types/pdfkit-standalone.d.ts | 5 + 45 files changed, 1401 insertions(+), 256 deletions(-) create mode 100644 Captura de tela 2025-10-07 133523.png create mode 100644 src/app/admin/companies/page.tsx create mode 100644 src/app/api/admin/companies/[id]/route.ts create mode 100644 src/app/api/admin/companies/route.ts create mode 100644 src/app/api/admin/users/assign-company/route.ts create mode 100644 src/app/api/reports/backlog.csv/route.ts create mode 100644 src/app/api/reports/csat.csv/route.ts create mode 100644 src/app/api/reports/sla.csv/route.ts create mode 100644 src/app/api/reports/tickets-by-channel.csv/route.ts create mode 100644 src/components/admin/companies/admin-companies-manager.tsx create mode 100644 types/pdfkit-standalone.d.ts diff --git a/Captura de tela 2025-10-07 133523.png b/Captura de tela 2025-10-07 133523.png new file mode 100644 index 0000000000000000000000000000000000000000..813dbe5b4a0a0fd288a5eef7b7f0090bf2e5a203 GIT binary patch literal 128256 zcmZ^~1z1#T_dZO6QliqSC`wD`5F#N+OEZ9U4n4|H(n@zTfOHL=BT~{abce){14uXi z$Me1?zV~;&>l!Zhz}|b-tY@utuY2A5`K+cQPkfi=E(Qh$@oR-w8WbW1%?) z#;?5BucY68FhXT`rYj>aTU$K$8&f{37qn%wHa0{F(my!s1FI#AsO}<)S=4k+)3h4`Ot(aUtYN>ep5!51t(J z(M6^j5qK~yWPinMBs23k9OI*qnuDr?zDw`n&&$CvD;X(CAhzDN4)-~92GiDjq$WDp z`j=pp-tjcNIlW~~KGzL3yENvCHZ>5Ex~(Y};TeG%)i#u^G2QXF<0N4GY>{K?qy|Tl ztDZgDq@-Xk@inO=SJK=#_nY$@Ytw3|f2FwPL%2SCt{*79ptV$;)xpFxg%W*Np2T=D ztu;5!c4pVD-gCFA>_dRKFu-JLG|O*x`8kakE!viYDg9}2|nCt0Cg6-E2^_x5u00oD( zOQ_voO@{YOowG0&z~J6RX?m->0rO^;)Y@ap#vWNNmzXfa{~jKeyp3G{{vvA&cgqfO zkot(W@vw6S7H8B7lje?w(GtX_RaBPb<%pKlxRoq;(QlAIrbVaS8vBRZ@=a^(o~r!< zyRHBIk+s@c>^;sPN1coNrB?0pFYp>?U|=rZ{#{gR z|IV2Kc3t9)`Ym798qws~_w3{2BUOdQY~X;)%N^>W`X+wg?#9QQ4GSL=+hF>hkLWfo zg*+Rpfx8A$2$y-h-hlT)jPN1MIjOkm&38_v1|*VmhcrCfdTH__gc4EDd9ijMlc>fjb9ydtf!sN@+o7mKbGPQ(2~9}yL)VFExAiG z?lwKw6VPV{Vw49cgT^(Hk&s#L78+}wyt{v;Rl7i7f1C7PeB=nv52p8rYsts_-Qh9! zAef9n>vt;pup=4Jz6+|Lb)v!PA0!F#Lg`;~NrQ^qW0*ng(;8n3oiQrb-LE)$8ntKCyPbn64#GE?Roq z9S1A;oN?^dSO$Fu=Sqq&lIkcf_)H#BmEFXTY3-Ou*;5JoL3$zA)J*dHgbH;%c@X0C z5&zg8Jl>1RAT?(m4b85>gX=e$3JHHKxY1}*`*ajOl4Wz`6D||Wu>k0iw5fri3iR4u zgB+(^pf9$ISSP^(yH-E@^xk_k6SPS(0W8e zi~Narm0@)yy9%$G7X~*7Wu@&HJ)5tw98#%)LRpmUusX@U1FQRKDAAU}?74}uq9qQb zz)ld!57x){a9fYE1%WI|VF_$3@&c(eeTG^HYM1ThotnQ3;S`Z}cO&^=8%Wl7kpyfG z=Z6QAG0UNIstrT{Sb$~7NcV55F`U$A0C&?)B7QIdWc(+bvwC;w$saB9<5l3P@TVvV zSx;bF0km)gBb8+Hw?z^tH^{iMXzF9^zz%nKwCIF&vRat@gq38UM=H0J0S&tIvKKN? zm$l##0-LFMA8AOjE?@a4Vo0#*PSf(-#0z@T#9Px^JA1~X1DiyHDBy0xIMLj!7IJ-6 zQ;T3!Gmidb+*}I;Fw>m_j!m4YEWsvVhn-vEIdksdPquf5{5W%fC5Bt|`P<`4Cedjl z8UrULqpn2Be%fD{oK1X7!Q|JJWS^=TSqrvpiYXe`Kaj3f#=*zIl`&#-T33E~j60U? zcfJP=^WA-uMhZ5k4Rf&4=_vXdN)*@jb2qi$&Vf-3-t=b4f2n+JV(s$a{X0y)GXL}; zJ}@|8^*fz#<=ZUQd+*|D|6J_Sje5L+_M#7Qz^g+O8g(;np{G=WjN&VU6+on$^QY5j8qGy`Y=FcZL`qrz(>!*5(tt1reNa!j7vE;_N90MgK(~j^8UII!Rul-%`<+usWLxY6S;eWG78ggwUU?c9(6`DOtMRqKv2&*)BBo|geh&+BHs-u+962SN4;pnLF6bRt*HjXPf)Wd z6*!|#+ZfASQBo|PwPCG;Krh+J@Tu&B<nNP;$ zw=c)HA5jUav-_TFNJAor`^ppDu21n3Z6jWr955unzMbd#nkdiK=+u8 zBXKty)KPpdmy)C2L2c|E<4XqXB&=ED9=FDuQ#ZK&mGA=D#m#+dvcHxE=3+=fawyd_ zkz<-L_wF!#^PG_XL{q{A+ox8XvXoN6X?RQx=`+!MZ_VjNUm2MAgnTWXNQV2z5JBut zXWgv3%UiqTHO!4qu^@Zn64;0|C_aTw?P8M}sqZbCAQWv|NjuDEj}EOHOX6BRdP~Y} zUDC-hWF3U1Q5hn|90-lb5zR`ZBk_v1w z#^U_D70?P?*{o}N;Kq*+PiS3*$IZ8ae)!G4R=u%HpsLro2X9J~Zc|rv6)a@%V**gJ zs0K{%m^`FCsi?GjHBy}vGKwv$>guJ3)b}Whd@Y+VqDa3+0-=e7=r<|*xyh?kFyP#^ z--uC1*>ajDhow=H^ z>iF&|c>Zb|+Bk-Qu2Y`=_+8sWCO|*!`s5+aRZx*Q1wJGiC=U?OOE5qXOy1t#^i73M z-WLQ%EYZ;?QQ*V0RC_HSs8z@7OpPJ+r3P}X=3zyQ)JPmf`w}bK7h{Tr$C92RKk8?- z;^?uRn}D8f@+AVmGNzk?*Tr}%FMENu@M-dCb%f9NG*Fi#&CI*_oXHH90K0%u4}(YKc~TE}hyr{KX2GU6PxJpuFTh*NR1VLqzKDAoSRWE5U~lsW>kPzALb z=N9eS36KRDFbJf9uUREC)=t3=wBx2zxh4kCYt;5R_ltX0^MG99K56wC)xW z6n|DRoMpDhKQSCn!V9Zf&8kLnOylP^i`s`Fv!o^SqCt@n;?a9*iy!AdbZks6^)Yc^6@-s+4{^aga1? zv3gtK##^J^wB`k;zEzT3-MNWeD-p?T3S2FOMQq82Z~p|l>+K9zRjMLCbdHX&Y zDU_N~lBHVG+-$9IpZ_t(I{AbNJd{Hco+{kXLRF9MjDR`|&?0e5fSK=TRLc*cZe^FS zKX50o%1#0{cw#Qj_&iVh#J}`rW%d@^Z*I`Wu& zN;L6N0_dKN6-nPrN|gs^i|6C?uR9>6kvy65bSM0c>Z%E1+cquZnbMeggNm)NU=Zbr zgreL(`e_}0E`0n(P0{UDXY1~!%NGtYX~%}ko-Q@`Cu(4F{FoWgG{g@l7K2-s48t4K=LuX({ECXBGKrI*<>PLle74{I>1T5QI0O-(?ff5xd#&)S?sKGKW z1;#;@V$x1(xck5nm^^t@cMy~ek+Fp z-OqTvryn(%lx*dZ22uz;<8Dt z-K*-I&3o>{|6n^QbkAsf427ed+Oiy_J@gtpRs56|UyGcE= z*g|>g_YIop)H2c{4_j03T3$!`2|JLY4p#X!R3^sWa=MhY;2ci>fk{0XyTv!H!%J9O znD7hSm6w~;xcP&$C_Kl-Nf_E}a%w*?cQVk`qiomAN+)T{;OAAs*HlBvTcU1wwp1$6 zEbFCAJ*}5_WvyL-m6?u+1Sf7~C9OjJ+pNQ}KPiwFjBe@_$>WGH0x7<*U*x&B@$w2` zcrX2!cE*Mkvlg3!su~jDWHcl@;7n^vRSa zeCcib`Kr9{1{`V6OagW|aclYb4UjYcDPZ2Q(4cyHIl`pYFtkHd7hyeBx)XBGk%pH3 z2N)c&Yv!(P7H!}jabg|Mnm4l6Ca6sETpk|!NFEM`_;HgU)m!w=)RsIaV5uF&@(SHe zB0sI#)e+Bi=DOwftaaQ!+3qqtA>Go|DYd<6XVZzavunbfBVG?NGBTh8kMF-g8ek+{ z8Y_4>H5u>7P&7;JT6_1X?2X>{XCIErw^MszZ^TpL_|E~|MKt8nL4hLYiJbuhZR3hADSazpa^U_ z+(q|?UAuT&xRI)3jMY%2SJozO74Ck1wSh<@ZYbb&$jaK1AB6>bITK{Xt0DK$#Ol(xLbxViZvSkR)}m zegi!T?&vmOboZCNso6^`R4}-w#GtmP*1n0EV;c5YQwi3GeMPqEDd@6VnFX4%HX_5{ zVdZu8i0CA#vYP>#p~E+42d`hbTV3IwtZ0@}8D{z$p&m{{V`rW1|woYCE~t2OCG0jhC62&j8A(+=8rH*0BA7VS;-!5b2Cg_G#K)FcCtu_1X;uxl5(ShYG!Tk0xPVC2fFJ^$`Vdc&bT^%}`V(4JxB|Y5_ z%o=69Eowzg4EQW}9wBa~{I#{Ws*B8C!Ri;IW2yfof-U>39AEW&h*h|9$zQ=HR7?*P73?5m!a6qL`6@Trm)^0=yZCA zBpI1gaO*rySd&Ng49@mwcY7*>2_R5lTqfz-WQ2L@BIC@7taU9lKuQgJ%)d2~Z*Gmm z(@Kk5_-Lfi__z+b5rCR?Bc;Q32*u6YbNADOC3SxVAk(UWs|VEg3aO6Kj9491&)7A4 zC(laz?a5nNu||Pm7QcVwPL;H?z1{+@XllTM;*X1|*Lx;9zDB33E+J0#sTzZG{Zp`P z@yQK0dBEUIR^l@SL{RUTiFbuisIm_xEnMy)z|%ttG1->AX%op^qRU=B!Bf=3FJBZe zFHIVcCDx05YJWFZ%*FqNNzG)A-o1!Kb1wA7wq6}=u9b}%vLMyu{DJiY|J_UCtNYQk zl?;Tz;96J1)XOyWiP61F!GM!DE&)(&`>_KPXE*d<&g?4Lq7N*9o>C%-UF|#~Zra~6 zgkb}EU^&oK)N`RJ&8%NG5dA^it&|oKQ7(FH%V3Er^jQiqTAGKk&s7yJ343be5H(;iS#^M zxPOo);9UvE}#S#_U;1Nwk#`+7pSO<1`@45c#{L#?A_t8evI_n0=lIb0XyToZq#nIki3A$*Zgat@{D@&>L zWO9wgpw+^kV$C+`QNTDeww~VX{<0>y?nEuQENFlO4gJLQjKFw{Rz6xdz~fCm9xubK zv#b4gYLy_M>%YrJ)o&)pp6%}0WGEt0wk4X_AtlRNzTjN)uH35io7Ck+J4pxzbg2y! z*1t>t1Cx%1-(*VTKwt34qwDc|n>%gf%kpCa{Q;%dna z3T8zCZ}PurG8?UqN6$KzvLc}Ge8&&BsZgSL15PzNq}0}$LUx75@WWK~T^ejlmLf=o zCfkw`Y)hmkA<{MuBpgLsEPGS*M7NyD@Am$euO^ZS>8FFbMyt+*is}(}<(dhrX(ORN z(p7dkB|v7(YJCCxH9;`3SaJNbbFb2#<8W<&ntY8l?Rdpv@~1!LroWXZpQ1G!2BU~A zMTwGcT%_hN=BD^QmI18*Ny;An&>V*~7JHQKmhPltZIY)dW4&hZSVc3GAT9eE|l)$9*=> zTs=pADffmBn| zLMx1>*xSj~o^2K|=BKpukYFvUeO;tM%gnIZ)ZXmWOr~I0?@h zzScqX1#_TmLd9aenEmu|k95%FOB!(+bf@EjNdYnMlY0I|zDAc@@vr%lzuG;+Y`QfQ zNVH&H@Tlw0wR2^XpV#6dO@^Loq!v2P_kdYOOl_}W`gbj+nt&z1j=gPOTDUYM@ne`Y zNU?sq*oAp=HM>*VYhrLFimmI&r z0au!!(a6*C|FSOA|FW(}!~B9S7LqbEfAX%|KX{j)=<&|~5HE7%2WOYk60ifC!W1F+ zYud>GIhB_LdNE8F7&QY#1;e4}`u@#qVP2xmPY{r{Xe#LPa&BU~=lRrS+(Bwif5yqn ze&Wu`_$f5``k3;Yd=*={3`bF3c)1XlX$xZRG7ej1oXLm&KXRpsA2JhSw64Y2fd$eP`^Zu{-lXS%w zaUq)g(26X8PHF~oaJT*cP_j{MYWlvM+d6bqRevL+Llb7p@DrOkv{bv{R52^?Wi!a> zljx>jHIxktzdJ9u?JK4v*C8#nnWNA@2^kjd%D|jEHF}2EXk21!=AQ_AUUEkT!lr^m zr%(fDA^a9M%Xvwky_&bGSv&UO1Q%k$ED!m7Io4>a4PJllBo)TW1^NS6j z&{LzL-bfd8dhvSik2^|3W+^*iIjg+6GH*32?i9{U3uNmA>LD6{nv%SI>h+d?(Xn#O z|I#rMOupu>^cTYVoLhyFpd_VoV_>o9WrAW&k{v|wP%6g%c-+TiQ`PVI2U9HS{?cR?PLAQ*cq@$Orn)RXJUk9xx7<$bl6mF!U-o~JIF{_l?3rszAs z3BLw#+g>9^GqL8z*C24X6aHxJZ7%&jsGfyy6Iao+#wG@yj==Y6xsRAchRRCf>gIIr zpW$;kd3boNT^#EbRCjv?aRUEE* z3R7)lYUt$z%|}i1b&O~|7%nsb+aYUhP-yrIuI{yup8m`8??&=&zWOZt1K${$iAtv%vbu0;Y}<7vqo($oLv|s-tiHsU zlt%>Qs{|C@fuW~k{8@5wCCs33voG}6ocS0^u&5&ui7MBlPZ6JU72Z7IGF*H8>hBrC zpBj)PwS#Ewk)DXTCGsBG@*}p4m|~QD7^{{D=bu)XCMXD&+*?6 z$|B=x^CNc%3{#@+x*yFB5wxqJlCL!1SaXq>lYg5VvSXU_wDs?PpJkX&-ubj9sL%|UA+2c`}w{KsWn1{_dw1C4rh$)`7(O8d6dfy4R9+9hD zLn32R5|mXK=I=2oLBmb|R%-q$*(~4f-UU0eaXv_*4gLOV*#++0IKXVs>O)K+-xxYa zV}5Y4%-y;Ar04&BDJ^LuXlBd9s19Vvk>1SOh1dDf_1eYJ@d#TX=EIgI!7yW>OQ-an zj@4aP+5*D<-hiFe)t?uKZcqPDumrNxWpYRT{`ZD-qEOOT@uw6k;Kqa)w%|6-Sp~|Z zxx6uG~Ui{ZS z9X%AZDNFm^yGIv4*Vzb{`rY3BjaC1>*l=(SdC{0{P3HAY+)KikDS4$mFWkhrL`KK( zvWH?khMTKiv`Mxzi12;BP+DOR!8}7f*Z=XRbc3R^=lm;qxgRH%tNoe*oVt@HBXSjm zm`SaU9WeK|IZ36+cI0-Je2cs_V4*n@(O)@#%3YG;bFX#k_mvKpN50Wu)(lTK908Phv<8W zt3#ThmHtQ1xMO=1N~9X>dwZGUgiUv62a;qgRi0GxXty!St7wzN@PLCJ{3o=yxLtaB zL!TU~etrM&Zt%RFvB;XarIbBgl9pW%B=KROSM(9WqDS`fBUh91(TtDEIe#VcM=k=W z_=OT6h$!S1e5k@n&38SYN(ko!n%Oy)x4~!q!#*-qR*IT|Mj%x%uWZZzvu4*=*`rG`|;J21x73oFSL-d^ zSDB8?0{A~)X*AhL;z?@9eTdR}{d@HSmHZ*zLQ5e6zt3;zcLKKb?=7~rLwgsf8hHZ( z1(~UD6!c1T8%BGtKL@RLA6%Tib-^%2Yvd_OTN)(|&}ZMhZG6rm-y`jq&plR)J0Rk6 zUCv5z8`Sf8G?m-8I|$8~?~Aj6-?)p;PNm&b5Y4CS&Tbx7WtK#y_X`)_-McY}zXP*_ ze=WQE=?0(sG@+S+z`WQpIHNs2ie}y2e(HWAEa%gpZNvVUbzwy|7+fS*>abwqqg^Lv zc{nC!yUVRFMnAe6j9z~(gZ9ItQlT(K$y~afJ`WmGTGHj4y$P(tkIK7xi?c0nHrNg& ze-TisNVu2RW|Nh9%Upg$4UPHPVO`W0X~Sp$9=;}KwT!y3r`dJo2j0BRafb_$iO?TeFBl!#I<5V>wg6&TLq+^=xW}f zD`2DaQk6|UL+QNWqD)upeaZIzy@)gl= z2T(-vcK`I-;gxaO9f;4+Y}fas?uyxkz;DcpR`bb^#FKwM;4^0Exa6?2?3ovjoWhb$ zY>V#}zDZ}+qb3nw%aTI4id$LX~N*AUar=v)f2$vK)W;GOv6wo)gm zzOnSSw=;rf)#%9}G~`qal}IYAPSZHVAwsHA9+}P$#v9a8jg>4cdtAco6Qg`m8iTvQ-?%57 zzP%ssS?ZdI^b!BIh!oUFBC%SbX=cDeXfi{;tn}uEds#gNk4=ZXF9*hf9-#da=uFha z9Z5eH!s}fwZU3@gELNIyp=;zq#>4DeTT0r#2ovK?yD%xRD3=p?^V;sSLs`x4rnIt@ z-PsFhflVRfA|{HT#JY3)?X4F2NwS;PLfQjSrlM}>3R%dXlQcaHCUQ+xyqyaH&c!^{^D zwkw?OMM)StIF*ZaOOhGnVZWYu*S$?1j9+Ff zl8RONxn5n+$flaB+^4mH;r)YN>d1U^sa}I3Co|`j)&9}r#>#M7%LnF!vI`Xa0KUx6 zO7@G%cV=3^m0oR+Olc)mf91|8eBvJBJh^;PGv;6p<6j)V^BX>Y%HPv@+aehj)$-BC z?zGfGbKPOwGnXY#waolP%Y~V{5OtA?AnhLcnL_!xI9kffOK%$h_*7u6~g$Om%DUrkb@Q0CDV#E2$_wHwE z$dS`=<^X_a`m9{PK_c7h(6WxBjY>VjRHP?Kk9^CEAsHPu`AC;QVZ|hP!uk2illg-u z9d;))$AG?~%gx1|w z&r2+Fqu2={+1_bVdZqoj!V00>&PGJ@ctjf9(`40WD)$7WT6PoXvTSqfqY>Lfo zY$`X}#Gig$FkhZ+cI=p0RbM)hRN{JPiD;(gXV|)Y0Q2@^&k+aIFw+eMZVMbQ_h1~< zFUft~$0@?CeEPfhZI_L0%ci%}YcaY&sTF+YBuLXKf5KPEqE$S;1`8ybEQ_nxE@v6l z@L`FgdE3&FtRD;kLPm>>{15ro(9wYgs^#g1M1`x*9o^Y(MfV}I7eu3@L(yO_%6=g_ zKgD|WkwO{iy;{Cr1u+@@S!xn)<6e@LLJih)>Y>PUTP@aIvF{_Ref$1U?9xl8VCCxC zjqm0Xx;{OnP6C=mBBYC{gO?+!BX&J$Z!%kA#(Q351SX-9@0@V38Xlm2&HhMA86?Rm z7zQSZzVC=655;yy6er|rHKdK|sYvVgH?pCQqMUfl)3t^cik5r_n;sVG9tZ-h=f2?{ zVIomWgYUB3dIPa)wx}QPLqcgpZmtlkuIFp@%}j1m+UZ&R@ z2XvQ)PYo3#E6xRw^9#Of8oYZSVnNDfnsD5>7E){GxU1tYs^vp&o(`99#Rxh`smv15K5zI7E|mmd>4uNf314~&!joN1k-HQw0%CP2@y zkAQN@Lhwu8n=RhxstEcn1>htXg^HqOWnDD+J%f720DR(XN6l&RTY$C!6dF3^5eNc6 ziZdxL4MW>;vj!6G&LuAvPL{TuS?x3HmRy=d3ia0y|1J!ydXyLy=&qPb?U9_ zla3`Sbko83vn}~qktx-T+GY00n_uVHO%oVj`ZPv{`X8P)gRw7t#KX2a4C16n?$poQ z`b?8b&%zeyPU7?2dI`nwsZPj4H8;q1MJk^yq|)REH1_qrp6R+gk(3EaeF8Xq%>1lb zZtPrU0Qm3=R`$Kfl($1vOXsIZjF`TMY>zUZ2C+l$4ifvM$QOblCMWIRbWCSXA6>9m zT&7-vRlGu6wWc>1m)lu8WO6KdZdw3Edwzv!?CP_^Cov9J!K>KCas|uG)VJ%xUCojK zXTK(N@0dTVQ7#c58auPB`mp`DWA5m}Ueb=ULN% z6Nf!Zd_++=inNFYp>xfVTL7U`KMX z!-aWs^M=p4fV>&4TK67j{b-p-ld`jR`8b#VMK+Nv!tjD5hw|4eTXaD$XY97)^L<=* za5GM5mHhP&s_rX1`WW*)k>$F^-B zbm*OXv)R~$=t??2d60Db>cHUsVGDPy>HB>u81dNAD?AaMWm=z0{immzO;U8kjGlp` zMPbR`=J%BQuomr>E(tQWJ1foB)9*RHu^kJiH$}Ab5T#r^sIRd@0HIC@OqI(^iZ>fc zHQ{#a*JK`+%wvn5alPy$F)1n1+t3Jb^@XDcu1jeM=qlRquZf@e zgkG8gi<1U^p*z|31O02>$rFC@i9#L8NWc>8QJI_>ji_+xEpB2g_Rp2RvU6}ykjad#fuTNlr#+^+?ZCbhYpjn@xbph1RBFrZzUV?|&ycNzC zCdA3-!4XA!b3^k{LeYB5plt(eagr+scDOdzizym(Nuq-7Uhipo!;^2rV)143^u@~dOn>TS& z@3@jX>~vzcZKN_%gTJS`7j3+@xOpGasu`i;ZJ_3*%FBJWEmyt~TrFPB0)*Pg+l0oA z-@Goi;<7Mr0UcjDb(_P|t&{Rk+*1UVt zw_g@UKAVD(Lkx<)7MnkKtjIl1`__{N960j%V+)N_W0tDcZ*;l5_9T<9o~!_sxL*eI z&TCEm4yho`;M_;$2Wl6qUJ_NL-VjpLmG$STB#;Qfyp9I$+BV4VfI2$*u10~s_`SL7 z`9dU@^6IqE`#J}wT$8ienE4ZHd^SriWOey2mJn4wHSPD!auv18F*_oV4qm$izVIe~ zMY=IWwGk|*GJhcKZSc?vF)n?4DKB&xWvIALuR2<#ilzN5!+6Vm^6W2EtfW3=jbDmHT;Z39P?T>6dYL$%g`Vq%;1TB;D6E+y?@_N-c>i~L#rrrUy;R{b_i}T# zT0>9sX@bnq4AG|u%>vd7vz}gY85MKqbO!uipEjhIw`GSUlOA1Md^}wKGI#vAo|_;k z)vG*Ahe+oPa8*?E<}vdlQBk~ld0>d_=a%QbBl%Q(PYe_P@o2!GBg;U^WKEltSF%l? z!b3H%IrpTbWYZJ}Dm0XSAdo!n|1+KY-2i8X*KIaeVTCnGged{jWq;+YIW=iW| z;lE>8>3g^4H3GV=FJzL9@3fvz2*B(zQa$A*W$PQ+r?ou92B2hdZ39NfZU?-4X8 z{NwSuzear2xnClUMkMuzWmU&onE&Ow;NZdqbl}kQ=g`$(R73XSOL^ND^t2SeeBp~6@X0@R()_79+|v!|{E8Qr z2#YX5-wyqW0+Wyb&8P|>TFOpHu9FMvFN^e2$NA^xfXL1izfy4f7;f9$xyU=EjaB_`HLbQ`;r=X0a z+Ydhf(Kh|%%-;uw0fsu)c#j`nzmk<-J^0`RUpSxe% zUjR7cJ`sG1{M@$c%-;6U*QD+RhQ+I}N5?B?{WO1X5BPI|39<(7TIvO$wWC!RR%wc- zz$9k7e>_h6=V^u0Wo;{?GOYTovKO$@g@0YPnh`dir;{tAzB@U(;U$&NZT@ZD2*Nd- z@#Sit+hFu@VCcfRm? zTA>mq_M%DlA3Graw4IJajn-Z~ zTK_f1i0o>zA}vR-xYo(I(B7kHc|*_gXKMWwJ0@}l0iEE$FtoJy-ei~J-*Va-ElAu6 zcT$(HIOplRZr~lVDk-V|>qghF&)d@ClwjOZhQSBV7gAKh&dz`f^p8N0G6(~uHR)I=UhE-LrpF=~LL`P+EyV1hg zU16(h4)4LvaRD*~fra^0K;ogy`ftpemoLq(zX`@)08tR?ZncACFS*#e}sq z;P<^+h1WixN%-~Baw7H*%R^UxTmCp+Dy(}Ity=DsKgN~SxNb1yy8b(Eml2Qo*yWw4 zU&-I2eL#L{P<|^enX*VXVv&%zW%=;z{@|&-}%sx>IEv2<9ai zxs16$Em70S&(VAAeQ>24M0*DM+B`+mOfzp()>h)q`G#a(!M0YTdV&ZtMan+lG@d0g zuP(B^>gs&Bcp^HbbXnAudY@QpFdZ!Z>^{k1h@?ib}2envenT6+} z#?@VIn-`5nqpAhc_IE-%^@8wfZe~*COq=1)9TynGGL9X_J~*n-MBtz08S%JI5~7j( zxtwEZk0f4wo|p8t%srR}mS18r721 zxP1#_AweyAuA*^=&Ia3)HvQqoWwBnuf|&wI_vd->!KF0^H!ge*abCEEUU(pWu@0k#K$c9a$(6zDqPSF2ki z9?XB8^msUS=P5vd*b#Oq#vCy|W~9WDV932y^h`luUFQG_R$M|{mQT90_F4*i@Qx~6 z_~4d&Q+>4XC`|3e=vk@B1eD*diWzs%*!sFI=&3QM<;0>=r2{wX4rTT2u)t)I%D(AD zF)-I4a-t+Moc|_0K1QxMT^wDN{C`3Lz56YhYo!aUqZ>%n{B}z>=@|7EDa%9}wL5%0_s`E; zJtGUbTUbDyilalo^OIS)Ez=-mXa$3O^XKIgmN&`Y;oh`rsA(*y-AIVmmUhI;^?c~S zLSxWwH6+$L0f=#WH5M3X7Dnx6tV1D+Qq2)Mt@a+{3KpnR>z>d`SK7n@r5%py%~Q+^)GUt`7FhhHwUhrL1tQa@PpJez7St{78Go!oR=bNtn_F z0_n0kd7qPSugP#v>7Mn>si^yon|Uw|F7U)fVEbEDdn(~a>^Oam92E!Ghcoa@YSr<4 z7iA^LC52qQQm)Hjmw3M12Rk=tCuWYT(2}@~J^fgsi&n|VPd}LWiIlhP+hXPYpl3vK zI3MF*N3iM$#<<*|W{3LQp`YW~(|R-so1-Pd3JlzlMcROrRVT6+@8XuyK#aQV&;2km zv@jGKjUANsrP+Vve<=C6G9ws@FM9H$QZVOve9~PQ!D#ZBjz`$q)E?$!Vr{WJK#w2LCs3N|leY2CU?;maqDT9``izKk?gndT-J+ePpv zMmS)V{^(Sn3cw?^`n;{9d?T1bwh&i% zC8ZC?yCO^^0R749TxgYTqw>m@Z^huLv93F0qU!TO6xjUQ(yod4 z+wMSGL&na|6w`RPvkpgE_rZCXEBCx(LS$-h=rGxE&q?H zuZ)T#*t#VUAOuTrCpZKM?j8v4F2NaGgS)%Cy9IX!*WfS&_rcxWnK!xLci($|X04v7 zUfomGeX7nrd+$Qs+~&|(taZY$vE}Ve=n_a%@*@jbfDK3I7h&{szt#ev`j{}7jwt(4 zE`xDV2NK-wf@7R~oxd>lrxQ+7L!LAk{hN@sal9ywdk}ODJ;qS_BVJ9 zRg*pLW=j{hyvwmq0Z*jXfc_<{5;>h8=kU)-d9urb;Ed)7pi9=2Gn!o~kd84_bcK_} ztq)@qRlhH>p}Bsu@@?-a22xdU-*|e$-5T0^?)9Tp>ltR4w=2{TqWir{X$)6}6xFMLw%wx3 zDbg~^lbSAfd9Qp~`O^6}fMI#a+W_$g7vr~w+ouIP&0G4vH@Qwsb~sf0b$)@N%9`_{ z_+|-R9q5n+%P-ptW=c&CNXq-s#jb^eQE%Yi$)Np7s>_#g-K>jK9@jpOzWq z41a_pF)qCTgQj8Xg}Kl-*_f%QM%MK2BKFZco3JnG1u`u8wDQg`+jqfFNn9 zn{(xNi+w3QOOh}fHB$VVmJMqf+B6ajJC^%m?A0@bcL$lqgCH2iVGL*9<5gul68E(#lIV{&Z)AM=FF1=;8s@1 z5;}iRw*+zTX49#VM|+?a$p~2WkOk>t1H=@#3q4eIEQu{Vt* zQM`IK6CW;m@jZKlsr(fm=~W566ySzHV9 z+2(If5cY3*kPiWhFh&q@Wz3C}R0goAqZd<|KAN^q zT6nwOemVU+9naf;V|TdT0cB=qd;hXf>f29C)`nkUVNSS5k~s7~jL5^o>=Ts~_b1eb zTd$XPo^&xxT2Y20!^juTzqY8z@>S!!7Gsw=AG8=`T{_ZdHrBXGE(cb-DC7FF1_mELD8wlnGpumg81|Q}d9Cu0phW>* zGzRzoV9t73;3B%(8unMnp&WkH8M-kr^niczZOCP!j@{nopg+_{nn%aCBK*Q)2o!oJ z9jB@8&IxkeN()11Jo$uL)}UDcFTlxY8iQ-;d@lVstA#>b7kR*$xcIil0M716LmjaR zmzOhj`t7G^X@g)VWBBZk!`g9!h&)zmHqB~d>1isk|ERhSkQ25@&4i5 zSN!U{jGiY&0avKg_#0;E-QH!FXxhWba^4H|D_2d2P-=Fm+}4-+*lN|Qd$$zMT-%IQ4|7QkZ^*SR>Goneq4{bh%R)uNl6bP!_wD*2ZWsLO&qbBp1utsi zEe~*0INshkub!Lm+CFQ23CZ-DtwYFJ24kbxl|Q@EvA(?i{it5@BQeiZE}JLr^SZ>0 z>Fg(MEG`0zzF`GK)EMH{g0X^7|Z-(&aq3`MBJzCA_d|oNMMpNP-yygJN zZ(K&n=8PP&w0JC6YSpb5f^?@`UX+jz?h)-=&zyyhgzTy4^ho;F9x9ZH@WWy#Db*FEeHNTH3AH@{7O6A$- z2l9$Xqt!--Y|2&?c$(2=nVC?`fp#Tyxcz1Oa1CzdIh%_Xyji!LX!7kg_{Vhy&kHG2 zD>tk~Or(;!GJGLND^Mup!_?}+lh2pznl3PjXdN(ZK2%fLE+?E@)5CNSl4jO*H}$6~ihOK$y4x_DBAvyXl}IxH*lHbJ=fzF9}u z=leZ)xtjp?6&odM=|#35E{z$UJ?O@|^enNl!;T}w;?W0;2je{h;L_1n9|!G z0W4X2;^A6*=-Z<`?kBlEVmb4vI)X!eCWEVEICt9vc4hxriCWYIZ|#XpAniL6V#D7H~$w+YBbJ0jz+9t*heuH_It1-%mxZ>S#b zbn>_nu@z(v=_Ms3ZdkJRX{T7DJ|(;C8ai-VXMACVn`SGoE zKKWY9i%)w8{TZs#f8B;D+ZL3%fbPy1m?5hYNF5CLv+*N{TV?mt4C;2vef3IGSTef3 zd1MVJg@x?pm$69w7Kt#5nP-^PESn>iJxF^u)?*Jg8QIQWFUoKcHiToMuQ;@5^Q*d1 zJ}u}n%G+fNt^{NA45mB%;YHO1-;aCgWee4Dx{_tLdZc92n1fP~dYU&o9^(6dP@%LV zQoja@RLI9YTu`RiEcYFydzWRj=Z+}sG=RpyN;hvs1QgwxBe;dU3ByS0sAqlVw^DT0 z?VgGFbxndy(dmEM(KS>{ZU{R9X6uR5YgMiIxGzLB#I52lYZoUUSlZ3mOffJAZ;I64 z7x^p)P90_0R_L9MW*JYhdl*v@HZ}{yjXOe?VAVWXH|LtAdm7~x{((Y5<*?uZl%u3^ z|3!ojc1L1D&MFg=NH=8-RFmKRI(Ux@q6Em#bSh}ng8UIo^OYvj>j+qUapNk)bI!_r zOK10mZokislp{Gd(#zQBmqmfxWA^{nA9kx4zRTrA10o@%|HK{mU5!? z450GH7U(PA$D+O`87^0q^m5}S^{q=oik@dR4*)z3!_F2uLzK<#cF6i?<0{P1^7T$| z27$*F3}4dP1M=6*;*X$Hd9e=csoO?iSBII`vEHYk$bGx}#2x?yHdM%yzSk%#8(Vr8AOjoVY`COe%T57dFg#UU+E8Z;-0X;O^N4tLSw7KO%UuariPYjlBL4YFS)J%Pwdb$rY z(8V$}+t?%Vq>T7-tn3>4YMO9nH!RDpxz1-v8NmFcWsWia3H(TAwu3>N7pg`xI2va2&?r{C4Q zU-AuH&XsNcQ2jkX*syNKkmbn&g?Qba|Lt|E_v3U`6S-7%`|adPR}M$0e3Hxc9;&0D z=SwxZme*bFyBD$O6~Q6@MaC7qnA?p6@z8DJy!C?{Yox38>qa%}FJZLS}sYIlv zQhEdp^54B2kAtDyucIbZCV&+dg&rUFJ1oYM@wNn8pS!5KOO=cZyh#nkN_7cdCrgRo zmy*hbyZx1kUcu^B?~`R@%2jI>Tf(nRg%fgdPlrY>`_+IEA|`h4zMbPJ796$$@@PKi zB2nV)dQ=>S`_G$mhjWt?=-mT^3XMgEg`sYnb2O6tjRDS)NJ1_EGJ6S$TWDVZQQ2@~Iz{vg9>(RVJs-PDfs#+;!$=QrEML zKMdQ9zqSfHET=i0 z6q`CQP9%9+kTJ@Y1nqxG8IQ!ht1LjWLsMW?V8i}+TC|I$OEJz*;DBwhQbs|%JWIW~ zFc5XXlBISf?a1sP)oAmsHjQeXRxhll(eQL-YWUD7F{7^r##gTB`*$P^TT5X`>obp= zISz3EsBbC>qJwdZ`7T1KN(qAzqV8{AV8KHcOzKh%kdB3B@JV@I?Jm+AX0O`b;1L+A z-^=AlbG9&&MLrdj+F&JEw2A%hWB-zVHNNoKW0vTJ4A?QVI!bKKtwOHZrCX)S?&7xPCarYB>tN>MsaWl97|+jTl1}3VkaqRjU>fkrF*J>{Qj|0OGuqH#vWX)v+Zde+!Q(CAv|+ zQx;6aX2nbWYmDKje^x%L|NW!mU=0k0JGDEsOC*cD3jJmAbBf-J2(Em0dPHdK(BW~# zt(Xr=sUlDmL{p#066d9IGtFA2`AWs|D~nak?aFNQN#k+;+W&DJTruaUakQosol*lb zGNEuxM^=uVe|`2k(#}Jw>8z1OP1mfUQ3;_({s4NrdY>hje}G#dxg|%Hj_@#*%!CyY#^<6NeG&NT1d_#nfU_ax^+qs7Gne^2V$jZ75jBhfH$Id$%G=YKS!Nev?*R8%<#BaB}`ACdbuq)!1=ew57l)O1X zK^R580+F}EReTli=pW9*bQOKRjJenuE{!{M%(ga1qyG{*=EOG@UTlnk6P_#QJeFNi z4!{Wa1HNYrjWXIH95q|qAVcx&WQq=W(Z^|g)e`!-PMQptsAonNw^&BJDc7Uo|831m z^hl!;qMFWG2_SUF$w-i{r9O_S_GgXp^5$kjB>@e~&sByHeE`bW_(&&ph(O}qkOvoq zRY$%iDR2*-U9-X=7eI20#QEgZGgu!DGAX^A1x9$d1t|YOfe{8cpaGemn}4%GK87 zs1hfkvvvH6%`DsKey(%&2#hz%m1^Dv^R(AA)yv6fMGaPSM)Qb!Dy)fak@YcSXK;JK zT$R`o+H0H?6zGaLh?@U;kXAfKw92VG?@M@%W{Z@D$ka;56;=+`T6 zo3*~FtnhQdnrq+t*^)DXrZP_j?~rO(Zh6Z#L4tBGo9&LY@W#W1L{7PhV-Oa-Encu% zK0(UusB92`iz|9fUv6sv6b(Je=4-0W_(U!R?Zh z`{^^D=8TSSGl7U_WpZnLm~;z{{v46%FGnKBq^=8`jR5whHq;`=3IxjX_D>$|bSLYm zivHTJ1grkZmMWN~Jq91U&!{v@O}%GyxV1^8Uw>CDyIQH-=Tyte!*^I@eNfULux0N` zFX%!QwXr13%9?Wh`h2QeBYTY()oe$Tc8PBD+*J0g%39`kubTFHy6??-OQg{GG|18R zQ9btV8JOa`UYdFh38{qH_;ctaDY->@x=qHBK2%Pv+$wKOLdcymdA}IT4Xyia{hoR8 ziAVrt*!lWpuh$PE0wJB=2FtZyq?^rF16&WmwI~t`lP2TcZBuXHC#P51RqW3*TGut= zt1~Z#2q?LugERJ?YS#tUbBGKkN8QVgG%JsLfxa`PDimi@1fvAbmMojo&~%^TD{G$v zdt`C~m5fro2^};(*P)I*CTzZmoLqg?AT_WIh zSp6k=SAF?%on{4#%@0B<&wY)ZNfh}b5_-V>W$({od;|en{bHLJbZdl;{BxgtxZf!H zU7qu%PhSML-p$|I{-Jl>0g5Z4T>gNm=f+CQ9vcE1<613zmwnHYQh&Xo1<+zEv!br!(j)u8MYO&gJE1>GVW6yJ>zn0|ziOp^ z8-u^4?UIkqxQ+;1C4<@6ZsUe7aRz-gGghhAF7ihpidd(8A$^xRvHQfs4oBW6P$U>ddTa|59#e0`hEYSh`64jmuCz)~!TH-K!f` za^@;=AJD??{E6`9FR1KB#r}=kQ+AhM!st3h6^X^*BdE^N%kHhS2@T?Ltb`$&^f=jP zi<)APo`d+2@$VK@-5RC1OL?BcN|F3_k*o+?4BGDIAB9XQ#OZDVk~E>qs?w;c{35t!L{q+M-&uWvG;0*F?smreYm75kv3lts)36x!A+y(eem+9)A1m)gbu-w| z#K~z%N1H_1&rY9l8+=45O*})r6rT%TxveG`hBOfjKh(q|F6J}!Ljj8<@TUG^f3fC_ zgY{x#8WPiOkzAEgk*#*mgTK^EE})zfGc>{dw~~gqS8e3Mpuoezu!YxU8^(vmgp6_q zKiB+Qg94b9rc#(#6Ew#Lb?*m&3dGy24@#yuko@s{0zY&}K%7TFtTzo5vR>r=mrVzS z(rF8Ku%K%>cpf$KT zKaq-$735G?6~%oYZR}tQE6&=76^KL1Xrra_V1~@lJ{4azg6`WZOQgD;;mVP#)Yl`x z{xh4LJhMa$$oQoo@$ZSQdX3&_qpRL_`m-%7;L_C8aoZa%?Plrg8_*f~I;RX}XrJZP z3P6kpjS$CU6uhI4hh-*o+k{HN5>82>tM>ce-O^B(fs02k-z)PJB2bRh_D(vxd=5u- zL;kh^p>~(|lL3JsTRLQyZ7hXLv~K(MT~{K*vk7^jIzdT83(Cl~+r{Hs4@ZzZvC*B< z#-MV?FMVv14xQ)O-Q47N=jUE;f#|l3LylDbug||9?>>4c=!jR*mF;oJR>y9;aG9Jg z{&;>|zkcMZkX%j^-S{vs?)%~sf;u#5&ksOX_|~kIG%4mlQl>#PT(sVzMrpU&pY=%Z z_Jk;!f?RKv|BL5oP;|aow=n}EkZZRpeU7ej!(r){whTBVfFtaBi}4{;S*-KKjP>yf zU~omLVMp8EyDKkcwF&JSq=$bCa+~_zQaOCP`}x*8!%>lvR_^(yLEMj5;`A>1mt*J0 zoN--_a5S|7o?0&)M^li|<`X7mBVOMsN1-`ZC~MQpVouV1m4Ra9@@yxNsr}fg(Scec z53)$v-1RpQ{F<;N4c|7&us97n6)k?bL?|UzLpr9yXgPBdURN0_XSDM|Hd}An+;8j4 zsdY~y`?ehaO|JL&7GsSp+{54X8^+<=B}1DAPj8mL6nh6(xJYhJo+VIA){DD{W>vVw z*@Lkmoa%aV>Sf|b2Si)w?eUK&yK2c=U8%C#S4Tgr8J(JaZtt6{`nZ<~Y`RI*)wNbq zh!^Se41~LoVwM{g+S%`Z5U#}VVo5+jayEj(r0V?$5BON&buFe+p~s;d%V?@tN+O#B zOrv58xmH{-Q4TK^C3Hr`@0At9BsnBP1rJna)df)Lg`LfU?E5%tUAr+RK$6=%CDvz3mcLOcBH`J>o_D0@P3vgBWiEgY-L6z zZ@aqWnmJg9@@8(OKh2M>mR)NVRL8!n^(@B7qxJQKvKrglH700?5BQC#RGyrR0npV< zP~ou8T_~~Gb;8^6w|NMj=r)O{o9mu*vmNmdiZ8D-1HZ>w-p9B3P__GO*L*WQBNVWh zDM!NIIwjtr0eSRSAU65dIv}^h$wM&557Yms1eVs#5rSMW%hZ^(@m)G*K0zJF1Bzj8 zEmu%(<%;^U-=nJCRX<5jP5mnKHurV;o_F4Ct(F@Rj$P(`t{Y;3e_cp$d;HGYFfxC= zs9Jmc-IumIT{Jr_^XF!Cy8xe;#ttBP_^y=-fz8vdGs$!!L#F{N>_aS{*U*I8cb97P?v*p~*H{z! zntMFqeiAtIMPr7@O0J92uAMU5a<9eI(WHKHR`rCA2TC;(k0i+0J|GS?5%F*Bkfb0F zLQyrDd#`%QH!y+qHEaL*+qSq4@wyjzEvJ-L;vdmSZ7vwWgzgo@q=NH?COj1m9FS=( zC4A0-HmE~fMi!e|P%A>3gjA+`ZK$x~3U9U=YXJ3%i-cBsL}keQcJJe8=RI3xq+Upb%rzFd=_*2LUwXyS;yyKj5(y(k?K zQQz1cR*VmkkVoN4=u+T`PH$xW~1wlrr zZif}GM5|Rc#zOiXt6!J{Bi;1_@8g+|RKp6*SLtG23t}Q}vW?bGf%l~02*Z0f$~&qGp)=S@YQ>DsH9C4#dAX&c#p$!q zv>N_M?Nly&R9?rw;Q1jD2vBNTVGukk$*sH^Q>mPwOzF%hsvMiHLG09*H?05?5GQCb z*T?5)1%!K4@nTFM4Wbjue4TP?(br<;!@H%k+^5xcJ?e$aMqo9b)1I%9HodCE990!* zio(TxEvwLE8R+FTK4HiCwie8lA2c1?Jh9cO_AIs59stFz%#ua;(##{)U~1DKT}^($ z$0}d)i6L&>B+aOs`TjGSS93-Qd}p29;YsYi3l^kF4EEN$|L%p)6j5n?&98 zmsf3dK*cWYhviJy$7M#5EgRu8SkrOEP&>4fez$gbfdEOv7Zn?P7GoARJ-abuOl3cW zmu-JBKzRppV`kj%@gGl*vlb_B9Kmb3XgjS^XcQ9+Ccy%{WYLq)l$Wae&HNmu?~WI0 z0Gnx0roo$>bIH(v3hc(1R8$Vo?>6+yW{tFr^{aKIAmzO?z>?Op?>AzV?+b6rP!7>R zUkwhMv4IW(8_IGeHlcGoeGt;-WRgIZvG81ln$y<2e0U90pkAu$#4v+e#+ z3^c)YFuOhS7ao%!l|wJmMfq&YwV;_53{_#geR3xLHWW)j=6*vszZ0A9`YR9!4 z2J525gA?2j>hv*P(+TVA)+5NicBacdk+!EJcr#%*?;>I9*iRNCqPTQ^*=y3X>Xj=@ zqE!v!&E%k4{qlE_nYyQv(LN*?_k0!cA)OB@wBsM{7<7KHm-U1;Ekw+hUf4)9Jbiq< zrIywLn-37GAur;>Z+@hjM6brgC;JcbcPX*6H7OXt6h);S5Ees2MwVkfbdOv>hofAb zzt=#G8)hCs6?A8{7uhT{f73^KW(53fo2?f)n@(i_W?KRy5E526{)(&6zdNMXFoAgK@@#E- zYd-NRHDR&f&W=dfJy45i2V?xjI)2FLWfl%#h&uzypFo?LO z>M80|Xv_pyGlJpAK6u-sPg-XV8q%SEp0XKbA?Rie3u%sgs08diuZ(QqMTWTSTuR(3 zKps;UewzT>5RjjGZ~$dEsr0ojkSqAAEyZgsJeI zrIaWDgriqBn}VH`yE$=&AEW~9_y*7;}b17;si z3Z10d5%ia4Kd{XcZRCuEE8q;gsj4X$XWD5BZ$0bbjuE|oV{oGvm;--)Z|q*VM!Q?} z=ux=H+(3cUN&|JNqqxZ_>lPr@Ev7j4Vva~z&XhXTNm~UXQm7w_1Vbmh%aLO#N-^Ql zqCs||y4)+Ks+n~$?j+d;@qV7aJ{cIj3qBeIeMiLt$%z(%E){G^ch9 zb`J}TbIWHksO&U$a%_psEPpr}3w5X_IX#A}4R0y~{V!Arg3qMnOSk8R?siAHUtier zO^hVp{-16TgNs{18m9K-xY&!`7~vHW1?nPa9OdJQWfx80J=L3nSqi#LeX!@9nd?1l z*dK|G=sj0)rC*p12FF&GHddewGxjt*IWu@1dRT zdv@*kq;ypwip$&T^n}VZ*V8iQ%sZbI&%ime$cy^8+^2SQ+rvTlAR4>^SF}iDh!?BN zg!mUALVm41m|VG+4C39)Yi-9}wXFt3^{dT&E6qC5hl<^>S3&~OZcV37rMBtlgaAX50M6J!Y#(YD0XGG{KeZ~qI`40Y^$SLb~UNm__@%r=d_qqT*r@rukF={ZK5i*>Y4Y9<<2sa<`%HKu)BkFuj7Yi51S_w+K;W886TIlG!Gkv>#8{_wYvp>Mp?x;X9vi}JSMG-cPheU+%&UVu$Xi(ian5we+==g; zlPOX#J?6^?4f^gthm5@OxA`(k$SfycD~$U#3iAd zwi4admFJ9G(l5l@-S!dpwgm-1OU^wUFwaCZMqny1v`cET`JeGyJ2)2o-@$A7Y5U@y zzkd2vs~&42gJT~W3!q_vH`etGr3V=f;bJQ4Fb~04FoPv~$}aZfd!z50lh+lZZ#7hn z8Ydq?`wgb=y`_^|!}ZZ2<9VgIji%^6=U8gB?a=vK{F^p`K4V0|ez5UtCe zRmkL9^nqvF)!IXzTD?H)0TJ{4dlM>E?EpXVEG7uC!I$4Uy! zMJ=9BthrXbg57(~68#>WIp=KjEeiPLLc=yDGx%8f{X_lFhhb=iJxJzpcj>laaS#+9^X&yOor|2ksry?8Bcq1p-b z(>;l9z+USA+AajiNCpi>bLr?_laWfNFaSO@%l>0FSC0vjbc-%2m2#!ODLM5=!_Gn|U(o zi-eEG0#uBZEEJf}q z3LGEL5Ldmu2WukDgz4?18T&D`cWqjdEU=qCGiVD_eQ{}>EkjTK1DiGSUxEFnkK(F9 zp(k0ZUJDjew9udgeW1QW-oc6JmO{xyum3n?|3I#6K9~36_2%zz>7gR*C7xEXA9$gX zKVQY%RyUBDeq%>$OT@zbZGae5{N6~a8C%CRc<$2AGh)nGH<*A)vKz+)Z0@9D;y+Eq zzIdES2xfApdFGrDuDS?T%1;;7kq`UxKfY8mIX3rI0bEe7ejVs@T7 z>!mfyrDHH`f67dyVRN@Y;97GaynHVGYj>jki&lP#Z&W$!xOF$i5-O%F8aG?4t+@m{ zaXFtw7|mq9rq`-b<6s6=pmF4F-9KMe6l%X5eZWB#ijaN&`OVe!zg7!BgQ0S7qXP2b z8=P^ZsY-Wbu*b6v$@wb2A3&c8aI;eAW@zB6hqssu&34npTk%G2_C66G_Nx7rTCcGu z=ePZ7SQ(l~N+pv@m-Pi1X4hg}EWNSftn;2%cKuc($E%o0A!%f%VP~rGfG;E8imfbb zHWjj3$}pgJ7T?w3V#Z+RTC+0%k)?Q)HGZ0hLTR*Q7<^lU;e_tXxBi^zFp6(%J4KMgZ(!lQZeDo?;G*?)2X1)hhI}j1g{OGHb)$J zddj44kHYDGmY}Ct&5KkJidzxOoYh>>gyW=&*|C?pMZYf8R>vxKezsyEEj0I3c%VvW zN#5b>e0wyZRGv{Mf@%j@=&j?P(#sUCdyAA@`GVz;pV^t|PyzPSZ+xiF!nawverBk$ zY?E~o_V~NhBzG<`xB~Fs!T5md_fK3~0(JGq?)CO}q_&s|A{Hv?a6QK|85tYfR(*1h z7!coXwa!}dMS&{!%~HyW8W3+$DgSJOCAIcMwyOh6d7D=z+wp7$%XiI zt%nl9-0xoGxk$M5lUElS3a(ruL>7KGjUPEyagrT`8?-YeuYW>x?6#w)hwxqoSh^@u zau}^<8h$CwxCrStM((UF{ZZ{qXK7+JhvZ`2saJg$2d?mRZS#fLgd(nhMI+rNOx?kf zlDn-xBo;kI@xh0hk!}0;%+??#Sw8q!_NoE;`=l?_wegX(`X z$ewsvWybT(S5Xaf(PvW7c9(Vm2jDIOCE+7il?V27x{@0^VM@aEi$#PZuhmdtBA;t+ z-pS5zE{W#A$5(d;r4Ux^Vrf%tA{&XA64YyV2Zk3%6FzCUG;92BQp3%>HwH;2`AQev zOF*1@rGOoR6Vw=D8k(VDD!l*a2XohQ*&;C7#<0i`YW|@m$d&qk?Y7^Gv7&H~t>38Sr` z=j^ttED2M~2x*E}JxXY$5lcC>zZOUJHM%QK~b%85(UnoaqnNQ-KBWYIiTZEcGHN0j%si-NV(2MO>p@w7kzUziF9|Ls-$nH&8_*7( zpbx)W68M~9d&DN)zaF`7E@Eh#n)Mrk-B^1dbQJOby~+0>U@>tV`ku!Nl;inVu|9o+ z_51e4*Vk|If7Yb?VmPcpuwm~NzM4ge%6RQfHD2DMZgo1GBMPdN#FohU7g7vZ@G1f? z!9n=rabLzbggNm`72+K>f%SdZVO(PyYf$k#SgT8zWq-LWQWfo06qZ;{Bdc?9uLmzS!Ah7D zp~2}&jGGgcR1I607JqaoeAh8ZwGmpy-?k!AJb3%~I$#QIO8-xF(jV%6jTm^^z|z6J z^FQJjU#5=_nzj4C$yYja4Ts1|u1<-FFJTb%?Iz~6b6ytwN1l-}A+KK$^)*J*Z~M%HE_2!Djp2twc&zs;^h(!ZbC4<{f4^8>)p_&+? zUwp&`-s9;F$#b|uY`I^4-u=N2@AH(Px;m?N83|x`+d3Tyx74jYzAfXherkP}jc`}w ziGF%bjt;Ji22@PK%$X~me{eLbq+&$;C4ah=ILU=DE3?oBt>$?5QZt%nw5E*IOql6w z0;za5;All}GvX`XpgRt;StYpJyeW)oGxe#BQx6I)$6sbv9rn#dIiuoio8uU_{lC4U z`bbBee-v=L3T9TG^r63p%8q`Wd2uda|FLky#vsP_Upa5DmuUJ~41FI~eMqO2KC4d^ zQP)@e*g7w`EJz<&Up={Zq-``<&%`0 z&1iLtWCUS7jyO*s&tN&$#^8J2By~6=2ekt?!VNhyiLQ8Zs8>u&wItRdDq+0F>D_nq z`cLaoQE6}RU3#RtoeAEq3&k;X>cmp79vn+RTT}}%<<23=HVNtN|bSSEYO(Lv*EJM6!x*_b>g z4aDp7R4KifznG#_@XqaJ9Q_^OlER^WR^n<Cz{YciJDA!M)+QCx=JF~ZE(SwzL@=pj-X7szK8N&28 zkm@|~D(K~Nr~8XOG7mzWca(E(#}K_Y-}L`#KrKS*XPzHDj{a#$L4KcAw7gaKn4U#1 zDo8ofpewdyXYH3Ka~#Hvf@m+gMJ>knK=mr&`4Z;zurWKcBmHw;INdZSUE3mB<^?&W}sujs8`2P(3a0F+HdWhq^@gGMdY$m2c7aHO? z`<*=hhpe}Zifh@rhLHdvSQ6Yr2u|a!2?Pl4uEC+P#$AF1cN%whcXxMpC%C(PoqONs zekUhm@PqE%WAECls#eXV6LJL|_Y6U*-xe30aF2w#!<$?E2LOz8Y(@_`mNH)YOgcRU zU%2~{XdTNsA*FLOkxrTiV@hYjTyh0xJ5sgD9dMvLv=f3>E4#hYVER^= zG;;2?E{NFcJ+6rBp^*B!5(YMDSFd;zMaQ-A*yuUd0BBe~5+U@QzY5h_8?Ov&xHidl zuPcuae4Dtpc(0?)JNea=NEU98DpOB7T3b2AL#}!E{$DW~y<3SEzuRK`Tp@d<^vv4H zGEt_}9&hl;8BCCrDLy70<`-ZYgt`s*tZm$GDB-Gj!Nf*% zxjq=Kl23Q0$-uRg%D0T-|8@R?v|@oAkIxM_r=9&($Rh^MNElXUyLbH@sl)k~7i$oS z>b`k2Je(eZ=**v1cc9$?^)L^%<0Z3?8qYA&QSvyHq>O95+V1Nz3%J+rHvoFvS^9L2 z?7&P~+Lt3DCF0?aIrgdzJ#7>8tp;;7nFP{z5#P~cWt~qy!u#V=QK_C764`e8mCjB) zB=WjGA30Pd7`<-5fbo@;3KWrj&*91ev5|7DlD;;qpu?`9+uom$E1gKMUr4uPuFC#w z&EyH&LeF~dQ;2fpoUELV3L)r-R4>Nu>h^>5glY+GYPBV=>>_v+TF}@yu=}Lg5WGH2 zXTWjuL}&kkCzOfZ%UIX%3$F!75|>Eq9>ktvEZQlIcGgiS9qwohcQEyButAy}RCG)e zOnCYh1)mjCt`dbi^N2HVNfxRiLxEIz98d{$@|~PNyLg+-<}so~rKC9= zB;KtrvL?ZEH24$lu{v&B!W5Qkm60d$=;flu!8XBTkaCY_W5U zCv^8cmt>*>2)nB7fn6;|%|A-8H2lSmXfAc9xih$VR`#ZaJX-q2FW}`VcCzg*v+;KQ zms4E{OKlA8n{u+cqnZf~ge+H1zX0)28@K9&C8tdgR#@tQlHIS8C`c~Ke@$vSX-H?T zcf{C&5E7p$X51vs++eBx;bCStt>4Yotlg4scC2>Hu=0(VAlY=3SooS{zP6=|5SwBB zY+I~sTe>SbF{BqPUOva4o7ywg=#5zGJom7ST7atlrPxyMmrQ%KH(Q9fEEQAG)*b(Z z`}Dh?`F%b{gJ(Khsw)X+gxf3go$4aXJhz`USNa?xFk|AVC0r{oVt63Rgx(I?Z;HvyHpYilql;uhV>D@W zwi4z0LU@()59FvDG#k>!l2Uu$lb#M> zT+eu^@EkgcZJR?st%uI=EM-zGH&$h?6u;Mh_Xhl)3`Gzh-7;Nct=Ikf@W;Lf@NHZH z>e@)-L0itr9PYPzFF8a%EFHjW#X> z$8{;;kxwO_tK498Y)n5C&hlIl}JvID+nakqXLMxp_laC4qY)iu4dAlKtt!y zQe))wZefH%r(dRk; zF0P%jFFw$rnbRxKh^Jsw%10F z5_i`&b&HgS)IW~n0*-@b6WPRxF`Cvn9eamhVxrr{>!dyMK2vEMc2DSDIjOyaQ_GSxP*FtCLIaM0ABTO*_AC(vOCHAU|_l zUEOPTWVn*&OIHrVWJzI(7pbyy8?iJx=l5rL(Wg4TSH~Ox7x$z1^BHbPOFDXNWPZvh zS!Z&f6K_@T9P!Rgv&HvozMyr$BjplEKX*|hKYq>6$oQKB4AOAWtr?3oa-+923wVoU zv(Mmpa3J$k3ONX{!>i+m`D1ctH0^;E(P>|T@E+N247BhW{m@#Mx-8desloNB)kqY~ z_FJ!ooA`cYx0?Jws-o~&dmZ$$9AxBz;Nb`0(;as8O8P#zq|X;$>7`JZ#{gjulJ>XV z$=7mo)RSYfL_X}1U%qE{JHQ2w2pFtxKz?@*_W7rG4pE58Z+jY4BiT^aD%4WVK zCivEfJ%zY3nc4TL&AGewZd+vGx%%)MV&g2vo5fs=9XsK-J~VHzAw3)^Gc`(r*8OxD zAIbP)Tf2I#-O6z`N`pusU>JhZH>#+hAk=6Zpz$up4;_gCi;zgfyEnMCqar@+Q8;KA zVL{Iq-n3BTLV~YUWw2suU#UXrZUkyOp2B#3+t{Qx5BKe$G|D3DgINnD;nRWy2j2Re zIcrYsIBP1D%yv|NgDY4Jf72|C-Ldz9@#cBI9!h-Ce9?J6#Kq)AJb)f5Kq0hmdNzBZOE{9kQxHQHm9-ZQq&fqFQ?_ zNQ%|tDLB;42Gl>D@Vx9FG@XVaFpbfL@u&Ef-U1m%g~M|QQvK!4MRYLnZZ)o*cu42=X?;4QUV~ze2S_l8s%C@e z!RHI%tI-(nQN>51Ft+*fb;4>50r}TN6g!1$TQs7yTy~l8z-w@DYHaXk)G)>GxB(#dr>qwH;lo_g`=HU%kghzsfD@{|~8LM`}9?i-kKo)dhL^uRJCL z;B|k@<4qK=g2k-g`OG06NGO2YEjZft>SnKcNFxgYSkda@jT+X{Zffop9K4ytg#co5 zm?IO>_7{3V6_e<0un!`?cm(zhmL?2q5WHjG0U58~JgzN|Ua=CR6^+7J>F%-<(9cJ6 zixU~xo%4jQ$OWmRTdb7^I6YR~zw$75r2rw!R7BxT(GVl_I+UH)wvN>~o2;?h7jblH zCsb#Nfj-zUuhKq4TcDYj$`C})elnVGd<4o_n0~1oAW>2gTkPK8BxW){hj3B@gu5pw zZcTfnN(wc-HVAa*v*rn!&gcdZa=LlCDn!LOmDBOEb=OV<`-iw~Nvj7i{j1Dfxt=_> zRx~10v71G#|8o<8ZZURrBilZ_S|_HU4FgNkc9Jx3rNu|fBAU@k($tI|JJXCs2>ry1 z$lax{g!;jHOHb{)%ox^ZEGd9BWX^TT)%w%IW-%}M_-U@GUrz3W~CAm;K*^y9y zvk(s!WK6G?glodjVFXP`C=m~&+iox32CW}eq?vaS>Wru8vH;cMGbksOil1HIOy){f zmRiD^s2g33zgT~h;OJ9Er(gh`VKD_NFhvKrUaJEx>}stx3fY}3&q>6p3Pkf^W{S^l z%K-8kWCbhwI5Y|NKc`bEm^q#JfY+%c1!iRLOD`xJ#A`sewdh%d+->wFZxo(@!3zkx z3>TaFeA*XL^(&HY!LHZJ%a18SL-Aa`FLWy%{Z*7=%9MKH*b4li6SYqcgAp@uP@tdId3-loVxIvQ1J5=lIq212z+;2th%%GX`xkJ5$QJOnlYbJEU z{cod=B)j5+PF*v?jMPeWf=Lg^+=kM6dn1Ab;=G+_u&rydql;g5!E&{7HFfLcqv^y8 zdOX)LHpYkTzK2wXP1inL{J)I@IFgRdoNukEZD+G|y+4vN z))2o1>_7)PDd^@nMjJcD?6^h4nNX>kB;>4$G0P9SkFCoRMeisE#s}u;tYGKm8~agY zd>nK;ISHHhosSWptZ?Rc2`*<&jZY3zwO_e-ZW_nHZPPit|12nKG?@Vd&jJsd&;e5g z2WNZ{W6?oiR%}(2(RrVO>4GKjnxsqn1g0_e#PizQ1h{`4gmkFp{%V#;dU6WRjMz*v z=!f}NR|bDyad5>(uN#J4a(nO>zB!nz?#k~^oUXw~(0TK5T;SH7%z5qi^{!MAi zbJkqb{>RHtR)zSVJELQU9@m&->9obR_T9$5Goiz?bjdzX)fqu}Nykmw2YMFfe9LPt zTccRTc7pPe${UG{+Z)(su$A9l_!CQCnTxSbKR{YnCyi|%8ZHMC2h5`nUK6%%PSvAH zJ99>4qgHX4)HOH(fc6?~`F0cuzcy}7uA~oFn!O1@HI8)}Ib)TN+X38)0iVTZ_&Qc^ z!?lZvvPHs_rp84sh8H?N5e)a%O5^WaC8X}UiU)dVt4*^F+d@8=^_==xU$A}OG*i;L z$#pp33a{`d(>5o2u66q~efSyNrnc4NF5b0|LF7ygw1y*A71X^J!Z1nwlLSMwL)s!5l+XZr{~?z)#XSd_b_ zHEf4AS}3%*kNsva_)uDQC=A8hP@NfJ^YMURyJOmKl+sR#&Qj+bKNU2PiO$Q--g$RE zN~=~zC^;y_Vq4yXXi=VOgIcMx^ob3tDo3Q2ZHk7c??$_14Q*7as=yprqlM}euW<)J zO}Jc(@ZVqCQw_<0y}*5GHw+|l_oINtr+ILkpN`biSwCM8Z8{q^F@1HFF7Ao#=e>Hm zC&m3^VOIGECH4%HU56YU-&jnFG)wpO7SqStIB+w;=tH}Cg3Mws zY~kWk?fz<5G;fgV>4g+3VRgDJ*q)`9dk{@r3Dn+*Uu>%WWt}lLII#m2EOmLf1T)&< zP5ab(G*^A%St`i~m+w*bV#=EGYwCw{wQJs9TvwQUPsQy`!mBIS$erq#rc+xz_@?xiz?{`(5TEU9R_0NL@AL)6&hG7kG!sMF;-G(o- zI!`v{1G^U;bs0;WYf*BUt|Q_HwCD%bw6Q0d+&}KDL7OGmW}C|+xQD3?C0uCn%r79l zITz)v4I3mPeEsG$-F{h(12fv%?QUan<3QYLcilT%Ba^-~xeI@ger++EJ&e74y>@$$ zBRoP(_RL6HXcmFQ=dDA>f>=;;F)J64ricZ;%I_m(pGcIpXUzvR^DiyPn5b|nn3y_+ zaNIxus(p>Z`206dq1Plnx60=kM<49MyIG_#S=zB2T1PIAXTwsKADDFJ7*b3$yfGsjp4%nKqMGSS~L+Y89D_ zZc24i&0VTq*vK^CNkckIVyFw<6V0&`_3hN zAHrMqLf~mWb*rOkVQ<+E5}e*Ui^EnjWN#n7x!As{RJCeGKao80PX$|RBsAt)Ww!al zYoY36mMTMC@etNE*f7I|`HweBvThre08w1k`2l zW5@SF<^CD^aV5ETOi63NX|e>kBME`W&3K}L3~koZGqLcfcpg8dNh`LwCFh{eLPvvE2i9Of|a!mRwYQyQj!3-322QoXe7jsx#*Cf~OLOIXE9-XYbPT=>94J2r*# zsC9dImt+*kN3){hF)>0Ii1rakyUfx9S`M1?x_s9QYIdVqeUhwFnp;@ZQ#XVKu^a5} zWL8h;nio$cP>(#$P+p-);?l;B$1ZYiIi$|ui5R&Q0eZo`3q&HlOgnW^+X2?~EC(ZP z(B6uI43Zj9=XaZrtfsYQk|1QbQ;U@!)CIG(rKlkZ1r^do`{PaVu!xAcUi{xU;7WwR zbImSb5QjkQNKBCrzeVfyS9fx;(f~4zB*kS0RcNJxW2-{~i)A(kG(8{OG}P*S0r!+` zIq^xTRlj(%JN-9EGZwpFb;%W*BsITLEzW020xNHTOBtL5ykGAO49-P4YyuVlu=&Le zw=<{&Bb8pq3OS*fk}=LKB|t&lM~MHm>Id9Qt>318WQ4)I-1p5DPv>blQI4u0LLhdf zjuS1o{}VU*+WXySPx4~5!j(m@UNr=a)&XgbVFPUZiqM46Jvw^|FPdCY4)vwr{o{g* z412<+eu~y`rq#@P5AVopHdIIU>vr-poJI%BwFmm$(@w;`0b0FGC{yv&Vw`64kD zs0}eF$5(Dv)2TbZj*lMewvbxxuHNe#q%ZjBZd;t(O(s#Abz}mjTlpoOoE04K@IAg9 z>pv?2){Gy^BSGENprO&!ov+^&zsV&li!hhl^>1C)SCnEIq#DKCHeUk8)oUt2wbl}s zE;kV}tu&fVG_@hs_-xr?j~{c~s(z%RChy?conG5h?%vdF92nFeAbHXdO)H?7&Su+_ z)N0^kW@~F}s8#hmEPR*JXdb>e9pHSXG!rO3oC=`)fRk7*ov>L+C)T-MgbQ(y`@o4TzhazW8~A>?AY|l=gJCpv2@f5=(V4AXOkD0<_>!nma8k| zg*nLa{Hfu+Jl~eC;Fz_DhkmkS*<=)bkQ!n9xQf#*Zt2k;dpo(+z@EJ=Xh*A#t60{| zZkf@WlXP)Bvg2R}0w8{h&+lSvbwcN57iHltrIYP0g&j7Hv7c@InBAItcVZyF;9$M` zYsGELCV-GzYO+XAnG8og-rS5QX>=zz-8_VUu6oVGCxl=~T|88Fh9v9#?BMzAjeQKr z4FqbN#t3gnz9^e=+X%d8c)mOEe*53)gV;fY3=o?60%nz9ep9FrVQ=9~zz7h+VpAu0 zJaz3e2&Rh<8Gd;}qr+}JZ<|JW^F|TnlD)pf*{M)mgH{zPd|;0`zpl;+%I+*5sRVF; z){zGm^NXNA6KI7}G!zZ!DPHIpi7^u`Pm1gh%}eb!eb=Ywp*+hhggSLR7p9!+I)shH zzYCYDM>}tYbJ||Z_eqOkwf258cf8TX3Mis%dhGbXwPdX^roE{adY3n$xzVC`!3bVqV`TDH@vK@aN6LR7lK z>h2?WXk=MFNfgvwjTbX_Jh61#(u`hGwa=P#cH%aY;XCQ{@x|z4EjXeXWX8?EZL2>3 zCT4b@66bkPP)~@)wJp+dAvW`*_ps_dbYMq+nwE%u+mSp}`gq*f2)6H0b{)#!^k!Ey zSy8sr!W}I(Qdh7o5|giA6+f{j%y*-V)gC?4o85+5s9}WXqizhbD0Xu0Q*Xjss<1wAaT%yZ_0h@ zBzt}ZFnS14jmRY}XS~ut9_ZUZhR}HZJU1}Pn9WTOcr&Ij%DIby4sbq{aVY|Er2X)b+h*txP>k7Ts`^7a$|?pHLKTlVLrGQ zyGzv2;MLzrUPd=wM<|s4Y1NY&71fi*d=bIQX_Ki2r;=T8$F&UKjjFRpc^FnJRBSI7qwWU>pD4x+ zJQK4O{xC1$8E*!}%QD|x=1yu%YLl+#qTTI$?tgDQRZb1&fOT4MeZ4)$~F zJGND9WU9YVd4S(}paH>Yo3oXmugP>nM?SAbf*f`!O4bmrxS~z5`Dxip&kWFIn6l{FMbYlWD2bW$w zsMJnxz&n?}shK4UzaFN4SN&*IvJJbHx!U}b{kJhMcWnEdUBor%nac%^y^ud+%oRr9 zV&@-j=w1vNeDxI2-NdSo9I-2wpr}RWK@YLc1MdCj%P$%7!w@d=sKV9P*>-GLbU$U1 z+T(hleW2MUk5>v+prXLhxW1~0JWxlB-y=)zk8yTWy=JzRsi~IRO&1OMyui+~N)b@V zXH;mSMQ!6&*A*H@&ft=`w=}%f6-hPpTwgT%e@eyYEhZzDfo#~gX*3?d&>TZ8L>LB; z;R?>oVc@znsQkVl-Gi?b;?HdN4FX8cq;h4 zwO#!;i|@~j`!~d8{c{%c?H*yyUn9>yj15=L(dn2fue;WB)z0qu8grc)KLJQ34qoKcJ>31o#}7s zG^73SoLGPtm+k&ythjcV{%ITMA-!$(#pxG?c{bm7ke6~?d);A!H!);gqbs@hAgBI( zyKt-3Nl`XkU$2U-#s=s)U7<1mw#?>l61BRXh(w6P-{d3;Tj-1&haueDNzCN@%>y2d zKw_@8zsk*xC5$T{O92&JV#!#p$+YT+do%0b&%>N$-hU}Y&)5dSpY*5Nofc2so@^U6 zdskbqoP52|9gIJb+AgR?z9C_w<+yC?uH3X-+pz5B;$o&2yjE=wzNJ=}?`cm@waHh+ zp|4_3VTNwTU8@M$6TH8eDG(pK<q;^z=_ zj%z=DS!7?ys9Yo*Y5mOmQVcQYy9bA~ziSwJ2#<34JcJ`6pUSc}qQpEj+h)u6LcN#L z%uvL?I?)hXG!}%kRWWv1x7j@T;vwt-3Q#I-`uZ?2ag}C0JNRa{*0neV*XN*y3YqAZ zYgL=_=>8nFu`pIco0(&-T0Z zv%v>hHh`ymyU7+i+qRwxIsxD3p=+fwtypRfqPshtOuW?AO+n$uDNi(`0$7{y)EF_P zbXYp6Y}zcQbLR2gxmZpQ1F(nF(Lqx+yNqgUlEkG@OHpzX9BDx!ffFD6)NHmRwd@lO(4Kx0SW6cVsq7 zNH3r;fZ~RwG?P|YDF`(1RmxYpip4_2cNPxpd z7JOZ8-q+d`RBAZ9JCw;BFK4y1@J4y>am*4aGGwzc4Qn=AYo0`Cf%{ATwHwR z(m5P zrds&{OCdI~6@Z<^^hmqdUnfd7Wv8-!t#eZKFE*P{t3nn#uE|D@l@92GoM}IW)D^<%RZD+5*<^fc;aiG{eo6)@>BopuyZ)+Q z{C;B*%XxS=tlGj9;32fFNBcnF75*CHQm|&*L|p+7r<}=26_8IHO`is((!SDm-Mzrx z-D_;@OVziNBGU8i5vOI$dpwE{$rU(P)&_-s3EYBC$ ze_LWO?efmSSA!m6um9HqY84)rwLf&oIARWy8=ObH@lVN7E+nw;PI^_$8ag6H);Y|3 z*t`;))~n#_KT2WUo%;@@PQtl*_qEG5Rg*1Q)TW;O=2Q)u`98<`ujjYz&FT>s2W(Pc zmi0&9L7uTS5~z|-xs#N?Oz`Sxj=a@oca}u6ud3&8=va=0PX&oMxMp)CUo57$szU@# zpunOt?CJ8Sf_2>7>(4Sq3sYQ!h?DqxmXWfZ_{3M&c{7uro|vkf?#$t}mx$eO9$ zdVR;MOA7QV@e$_msw0}gA?5=bCuX6?+)~9OurY3H8i{OA-10OQ^k+h3K@T)kWNJge zvb=*D%E{>Pp!3QvFo6Ht!T-@FU-KjBLBrc$@Du>J+j~bs?b%2g^0lOi&EpTC{jnJM zg2rQPsV6DGD*R{k1q5|_f4g{b<-(20Z_TF&X9KN6z8k)&6zdY^uK1LtcsK5eMu`bI z22obmOx632&{iaSnFfcai_4x;mFLbVZlOfNf@` zp!&InZ4N_|Y~B^&#h?lgMJ*(fWi|P&XRz;K#||5tS~znGa{xG2Wf=ZZ9jbH1EB>>h-&WJz?XrNJQARl?*iRdw$6v z1fIO@7Z&ANC(<$;I1}}32vz1 z{87zLWN_;lYq%VTsh<_4SEPsLS;#C^tKYHYmywt>=jKVHS^&x}VaL$NTw8gDTXA)O zpImiHTnAj#N3Q!C#X}dki060e-YvM6C6Rk!{Zq-SuyqPLgJ$-z-Ha=cw+hhn8BHXz>2-9Jt=_h{ewk|di$AiU4CSP}TBVwB2geFrt z+uJ>3apDY4ODH(|?9Eo(w|ax_AB@YWTx0R6#neU(hg3kBZFIeG_0bU|VPvXWba0Yr z$hg~>xdnxhZDV`@UBSz1*`JkP^}qdHI;eU=v$)G_)D6uP2vx!Pv}s?$>Vd4Mn$Vgg zoGFyaXi#Dt7^TAEsZ^+6wiG!Z8J}!4(-`;vFQDK51y@K_90zT887zGM!gpNk$H;U0 zc=tbl)U)Fe7=)zkT$;ER;D8B_MhZ!Z6jy@uGx*~NQ3s!!3heZU>PG5dm32u~ny#2s1ikFs8d^A_ z*`_^#>bI9s<{Gte)%%;9IJ4cb6(jGjhGz+yEWGW$vrX*{t*sNDv6Q-Ke>}#|`8rGI zuI&j-G-Bkhr6?pW{V^)#ajHc_PW!7LYZu`Kh_7%Ls+j@sP8uAi>p#2nzD_yTjmvdghTo(_Esz?}flUMYV%J^$?ZUbz=S8Q_k zYx44!Txx-mA>jW`)Bhf+>Uj|QaCP;}abvOK@r*CHmIGE1!C106L)u2C6NNT&uFDa^ z0JX`!ePkLwf{ve~VU#H6ll4x#zC!|Q=`F#8m_0~gF99ErtiB(qz@x;CLJuNhGL{6D zNCDNVjU}h2RA$`q2GW#C>t&4RRsNV*%ESivASMioM#@q}TsoyF^45!zZ5W@l)7S4x zJ-YjMzPmCTv1k&xME9m14wBZZt2AC?(6viXtU1$0a9d6k^-i*|Ch0uTBlH(NP@)vT z_);Z3Il+mun{A%ff8@b;UN?pd@9-)my?xF=y1GhofA0Nw<{7K5 zQt3vX9)l*{JZ!jw6Lw*2=|`>|43p_*$Fc;b=R-eE+NWV(KVR-!=R0zDn$vXEWu6Up zl|F|=T~5t4LFSXl2}O2vwzH>5;yZ(|JL^AJ0rP)j3OWYnAxec`GG+HGj9KyY*n$Fj z4r8{DOi3^vJalcyt31v{D@$sL2na?h`lxKJWZ2=EMotfQB$K|uaEvY}KV0nZmS;ij zX67lU9z2-ASA?kR*9|`}$h0ua( zFjiPtr!!xIfeP>&FGPH-vBMYrypn>8jK>*rl{-P=LX4z!G+J0V0&8`3`}>wdx1*uq z*6zeKe2!58Q7chJd&B)GnsEMPwQ6P8p)Fv*U0RHT_*7|?$ zl%F0+6Q*TxJuy=jj8eTgKg%_KzJ_+v7X_1YK9p8A%vl z>~vJFyJ&MBakVYBiDmT|;${kV+lSAS`M8^-ZHg3L}2<-DKh` z{wo-ofxX7T@%DFlqZhuqLwDhBzLQ5w`Kh?Rtm0wO?vp6Nt#cBrT zE0`}v2rE(Ca_;kR)U8`RiL7{ROSaHtA_pae6a%bcnJMJ^hG1XXLOHU!E8LagQAa|p zej)X=az!2G&|)C<_tsQyf{zYW9&k6clRBaOQG*F*2#lo`QA72QA0sqzLlx{i>wNWa znO*Ry3h-Dk3-J2TCD%Iqwh!x)I)+!-%XK=P96$Y}I0WD${Zq!b6ja6V;wG$WVhtEk z5M_({0>)<0v$8q&xQh%Z^d7VMF+aR6M4nDHT)50#&t=EGl)!dwZU{^q zzMm)*a`M&0U`@sQ_E4~^%2>iL?;+-8n0a@*eV_F72ebi17`PRw&HF#g@l=&(Cst{ij-Zm>|TVP;Y_{OC2 z*KtLVD(M6BuNcm*dY<9nGs0la0K`9DC3hkZSQae@0W;HS;|WGh#!QtL$cB18ltNne zL^ap7#A1Eo;v}mhDE^D7(*i{C>=$Jq`>tzyCK<6PaQiTUdI8rqh z(&saaUzKGQ@aoB^#WP}vc{rq$Vx!;jka2?T)B@4fK8=0+LwmkXN3Bv(RmDB1h%+jU z`$!M}xo}*E;e)3C9|L|XAfu59SBXcfLwPr<(AYhtWcn|>AwbEJpbjjHgP~CiF*w57 zhA=Puv76rt;MgcL;4t7!;nMb)bZ-tJ{Plx8nhIkDT-t8==pih5fgoXsF8>Q~zlG6T z#$ z()+6Q|71U0s(;TTI_p^wi~a|!c8HoEED^U(YIC1BDh=tJJkEN3yEwW_{T)~=! zNT!Wd?5!RyM3^6PSWjDD@+yH10qPS|%0FoTX%C)R%0i&m7$tw=v<0K&7bdl@s-cx` z+i*({JfiaQ8Q*$@-qQik%w+Jnql3Xw2-4PMG-%wl@GMssN)?)N(iIP{Zl@+}b~Nym zh@h)1UbUX^k95=K$<-nP@D*~a9-|%$dpT|InN;uW>!vM*<>`&|8hJ{d>^ckP9(KwmsgCDO zjiG_g5mL{FJO6r>Y_c#m1sHOJ7LR>V`&7!VLPPl7=e|tgAbSPQT3`K}kylf23MT<2 zTvv(rn|pE#SpRb${+2-<@$#5adNq8qlz&W2wVZc$G%{95564Q}=|@~Z1~T2;{K{6? z+~%ik92*sagAL%D3Jfj7lN`cNSX}b8p%e>S&1TD>NCMA83=55b&0kI~rc)sD-62%A zmnQI_J!{^9Y+p4g_ngD{<8rjn^S~&#bfkectOFWN3E^DVOy{hZ0NtV5XsCSr-1J#i zi5q#Yy~U;q+Z<>8rm2zdObPYzbaFXqR8*_khD;#9?!a*)IYPw1 zgf?+h-lK4FnohY#2|njFmg$wfa-UC{YX(b{>Tp$`c6D~5TRON@*_Uu|9^|R@`XjjF zZ*0pZn^prOUDhetPdjhC_Ki!E$wg=5+6=49Tk61?9vtK6`Mr6{&0MSpiWCP!?@Ejl zW$4}bALzKoAN%cD@>IPS_@?_FpB$J9rZT^19;mOpK<8qF5;y*k)FWPEqO-aWgdP5| zINp}Y70PDyw8Y4i^mq!Y?d~k(% zCVN)?BF$kmej@VhvY$L;oLRcQu`yZ}k@0$AKotKCMx|-uwRSGlIVfLTWsVw(z|@%% z!=rslcWL&-`mfHYcX!GhI%=iW+;!oK-(>m1V?BpYSzvpN=5@9RS>|dBhs8iCJ|ECfl>3S;3f)^K^#=NSV$bq!^iKgR^ zQgEx0yImPbFT{>Soaj^*OQPQ*H%P+lpLq#b!YJB!Q@3i$j z%+2WpE}XD8l(1D^r4)s*fnTJQ&ufYfb?px|ecLv1-Z^PJ9Kr0>*mFR$9w>QlO(qR7NR9l|>+R9LBNeF*UX)90m@fLpiS7UX zfbx^cDA$Yyww4Gb6(zg<%P#F_j!Grzs|n*;mbdV z-?b`~->$2b*zE}&^4^dte>fw74q}+@6-;#$uf3-TCEA+HpG@Kc1>5Xh^UUSIq9j$V zX<*MD$`hb6v1mQNa^y-Mu4~&Hlxa+-2>smC9hEd%D!hrn1C?DP8feao zr-H!%D%lhpnHN0dc24q%>b)gh^xp9TGF^Gzc!A+7|8jqkCd5}SY0_gc3@d1 zE=t(hYq|IYoGNM>i$#I$5;#~CogYr%M$W;dmuspKmx{nv({qQ!Sz|zkUBKHf4=9MO zqB?k@vC6&p1Gn5h{737kRZmM8qs?b6@ z{TZfJ+s~QM8!^xDPoulbqyImC7d$gNuA(ZX9tkW#QSf&hW;F>&z=;OpS366M^oXex zk+$IjAS6<=o?1-3k_HD}=>1a?0*{UVJAq~faEz%&bYvTlXaJo;{{|ViqmAjK9Rq6p z#^Y)xs-203FJHD@Hi&s-2)eeY`%mmP|OvILLk*);Bq`WZtTJ5NPsB?T<&l zCSkCDmosh}yhqam>hWN~lLTP}`9-q*+h6PhNws0bK^}Nv!~s@Oe#hL-KI7NJN5T6G zM(GU6GWscsF{wyU0ru?|A`k!aKMBN{E*ENH7?olel~H=(Tygw$51dMsvb^>ms_;Q3 zNjV%Be~IC34j!Q3CE6-5vOS|@{6{+!9@)~zzYrT~NCRIG$cM@WZj!~kANbc(Fqli* zb?@i191}p{pa11+`U*0~x=0Ozq!z^k$jqZbe~Ck8+PGb(z@=2yv@(xbBY0AhHwdCI z|GC+H-gbP`^zP0 ztZD}0Ec|X@ko`&lxxh1!F;!?-2P))CzumtV(P)E}V%UtDw^XhmFdNXX<+b4%^j=O@ zfG)@6-@ZPQSJ7(T-8u|P7L0koTH`=qEyO7QmrxW^yDQ*>%Ef}5j$$J{gZ^5NGE53n zu-((jkR>$4q>**N@CQf{c+DDwjAi#D2-HAzA(RQp_WDzPJ_3r~aD&KL$UP-p(O-YZ zCjVUS>Awwt^Drm)AgXF1#J<0rjISW>yFAmf=wSMIDG+zaU!xPtV^RrZRu-eKf=Zjl z$gTS8{a`GLA%U0q3}kqg0i7W$=)Xn)opXpOj}jYMKH$T|r16)Hl3p;bJ7mCU_#27g zFl!*g3xeAI-Ty0<9%3A~??=$|tMRdl_fb)TdBCP3A;QSnk zijql2LE)8Wj8agHl6P?kW(}Lt+=1E9y&#qYd23pDQWMO%^+i8DWmekjt-lvGSujf2^ZkcHsi1O`%4(tf0G4PR=4AviR% zvL5Rn-scXFPZlv$qJx9;ox|GFO9cCy%R;LOzFYWs=j=$2VZw@GQnh7L++SHuHo5>0 z1kw-`{IV*mtu~{BM+^A+wYNeM&Gi1?@VTP&2+en}urfb0vIxVZ2{4!PE{X z7Y!e^{ zx;RfP@FxCD2Xm78TP$ouO8fWM6I+jBE=8?++*8Z;ZvSIWwtC1XB^TlA4$nX6B?jIJ#_ z*~@+rT7zFbU}r6$@!To7i80vzs>x)ofEgCSHpfoS(iGmOVyD7jup;>-hx_??S=F}P zS`4n_|0C)xqvB|ytzj%kAXpNd0fM``yF+kycXziSgS$g;3vPqEySux)%Qw%xcfH@g z?wMY_s;jzB9oc)I3e$~DiC-Aj`)&L6GurARZhn3(eoC%Zb>|*6^yfHUO3eBsac0U< zG|5i-$0EOy$BoAYRWi6;v*&Zhh;M-Kjk-0NWPN^V-8AWWbEY*V>vpqLnV?h_IuRh!sHWGSt@p=wTzBq^-YlFSpz<;gfXv3-scLQr=; zlDx8)?rSnrPE$~KeAQB=R|>zME!BtCvD>wq?-%JDzTC6eW;x~zOYOegpBAh?Qol^b zrRYgGN|AACS!Lqe(svn3me22;l8Hf{3JQ1qbVw|5n zlovM(7>JcPRR=WT+y)IyAHMyq^E_e=#_-uRvhXf4*=;?Yfz>_16*}|?r7d2oS#uI! zYP7*5uhNi16I!z=nzSmGuQcGMMiVxTX3SLt5lRoA3L$YGtJ^$bhuU<$l}?_n*%;L0 zqDew^Af8!I5zxL^Mo6yKlc{JqGyF4c{oXdb5+w@yRA2?dH4ZnJ;0 zlO7nUFm;6R?K-D^zV;^s!giMV(WMG)aL7*h`ddxo3P*8`+HimFl$w{eRdeRrDC0_g)sN835~{1x4c31j!rN*#%4jz zk0CG)l61D6I|NN-JHl*>utERcu%k|g)xakXBus^iY{w2e_e?93KA{a zn(iFs_UgJlwn)}C)pbt`4&KjI@V^(FbxIm2pe@=K7-hr-Sedr$8$0AUj4MaaH?#t* zA=k8+4Q|X$!?L*|L}4`**6!UdYlCA%8ht*`V{)%N;6>E^`! z;IB07nG{#ZENl*6GKE7ako50VnYoGpXXK-?E)4}!6qW&cL}F`ilIxWG)P-XF4<(}j zchyfjve|`!6qY%1R8H!|j)3zcT&9E}qonq3<%I_taZ5585*ygv^%}r% zSy;JBgPkXaqJONN%xIr3SO&YrXeMn{>21V;7Q$S+n=XX2J^ABJ_pcUXZ~Ckws{P+N z`68=Lu1V}N#Wn@}im)$pR4sEptj zB-4)GCMJDXx;d41?haq;Oe4W;PeAZL&~D*6v(!Vxjj-fH#NTEy@J|0F5}R%8BKY+Z;LvAq0E*~~|d~1wH%sW<}@dje{ZI#iX7*WuMS9#Y-gSnFX7CCUUynG20 zv-c*>_sA`H+NbQ;)JW7OJ{)f;HbCzP!BQ{W_lGEBiYCB4^_%X}yNM4QnbSNL4akId z&PO#nJlF1uC9m@6>XgYJQpbCk7h`w`!zWz%i`HF+um$_C_$MJ0D6en1Zn!A%>~i^D zr{Q?bb#PRDKKndC&)3~c{yyrtJPIp+MpaG`C!sO(Hk4*gU*Hwgt8t_C^#BPTq z+1Txwbv#2|ZimdD3D0B5HZimP_mP*(+4vU8PGO(%KYZAbRjh03+F$)-R+cq)_3fmk zR`|~*u6dnzP03s}G&l}M_@0aH$HoWfsDZfbmp8NY(f8)7HMIELjr*mTWXzpa)IR`3 zn)BylQ9f2_+y=jRh4_pwW_+|2c`H0`2IDJc8VT-#)bWI!AquE7SpiLc>?j5+DsZXllPSRwe-G@gs^BL}0FI`)ziN7WyLdvL) zw@FE?9JD>t9bQY#fav@89Dj@$ZQsjcH-nZu@fmCIPR8W>p=lU(3*OmfBKR`OKHZ@6 zsy5pRfu*uNXqhN)-mf`}n2qQ@kVj0>u`dS+JYu$ll$7DAzey)WIyn;;vU*aRsNp&Z zQQ|vu28YB~8E;EG>3FSlk427Z-77J&$vh_{G~vccHr(wfJnTr~Mh_To(&9w$W`zam zVr9;1C4N1U;bNl6nxDD`kB19=`V*X`iK`d?xqF}BXy@6tj*+f%Kw1>MXi`Xg2s4=& zo)@9h8b6lF?SS(pknEe2Fv^9Y2qSg0B#Gm8X1NLuzM6P?%n%c4LkwVz*I@A>{HG;L z-GNeMZACMpiQ9CY5a&#QCg@GJ_e0e+=PQJwwZ(NelGuy79BQoiT_ok%ea_g)eYr;a zi<$3789C)GdF}6YoedvRXo_X(B2C%>p5%B899qqBopE!8U)kR~H&D2Y?wq`_fbxNS zT!Gb`(*t_yiPqt2rtw=${-srAK>*LY`@vcwx71aJUl`Bc5plU+tO37 z+mZJ$?>+Df#;QfK$e!i;2WK0T;E$v1@yGtD?9(>K&e)%Qq(>MA*Z1Ujt(&?Uezrm{ zw!+hsBX}FjEwpZC_g5V;$L#fl536bFjawHlSAN1f~k6uZd46ja_ThYWXDVdjF-fO(pM6er}882M{om7M&vm-t6n zS`+Mjd~kV;`)mdxba5H=o~ldV(qWmLPv}NYJXVy*!O3`Rv?!%Ce!LX!Bq^ z!|!j#3r#&IC%W-7oa5NISVn?VjAbK5W*LP_np4!h(gbam#32H;hQkJ|Uy4O_ldMjGIO|KoCtwUV5)n(mF)bCTaydxtbR+BfPt?v4DMWN~EZYL&kHQ%ia=tR#cdaiFQ2eY7VsaV7^(o)%q1P8au|+tQ7wO(pCtd+?^on5a^=!*kyyGr8~vBcqen zi$5VlDDgf%h{aIw2aYtTIc*o%)d6aT$=Ab!IjDTEK1DQP2T~R&_$38+y2Iq(#ikGU z5xi?j4w4^CXFK_2B2HQrdr%0jr%?t$=N3d33vX1tgLZ}}#q6n3eBbKdzf`2J}eIB7`F_BS!wkQ1?bPOmz zi`f`i7=PtNIhwRFDc2396eso2z?zLdIpTGr>aIKqfnhuhMW}aHih+*qLq~jqh5Jbl z?R`8a3TJKdSu;|9{mXb*2=-~s^BLWZDmnb)Nl}#?ack^7`^bd zUC9VTbK9S|kBtGk-M4GIqo_~+o0bbH!Vi+I-4JnX_DRa6k%cHJ2C@QiN&gAAJ&wTN zypZ-}Qj%%j9KmSgGXJ#UeK}!Jwz>xXmFee72|~p|zX4k_Zy)W#+}d4bRtq(ccWh51 zZT^dEs_!aCW$s#wD8EdOJ%o6;xer^3?_91=J#~Dr+&!3W^}x)J8D=uKbPh&`xA=I(N$jTu5A zxNJiFu0{s2ehKd5U{#%ux2DV%?j84K_OW#~IqG8Tjm|2&pCr*iywZIY*veP&gvWrN z1KO0$Z?NcOdl>MuX7QY58We$yy(;xk=$eV{A8IK4D1s+~(O&6|7RkMZ7vs_AD}8a2 zUh1^M)Z;y@#PLe@=oM$QTNJI#+UzZJd%c}QGSd49G1Fin3{xPWJ*;>oW;tLO-8`2s zUtGu!X05C-+ow{c#S;EZ>E+B!9Ec+*Vzyjy9(c^?dnBiee|QC8v}5!=$&QG!+N`=~ zpKykUK~FXRgTrJOWhjznKfq_Ml-v3B!q56A>{}?v^&Doib2dtWnl_N4lRi&v^+QIl zB^0>lXle$N(8AyUWHaDuk%2wi!9Ux{i}a14oB3Rv&zmt!fx0S#{*eE_gv?!qcIKH4 zE%~}j^aGpm!QQ4|3dtt`oN(AX*pqTI_|&gVI|d)dCpn=oF)K#b-K0qS!|~nQ5SlPE zV}1rGy*gNjXHL8_o7EXOT4gG--TLvDG08QDPj&dPeJ8$!qcP4>Zayd>rrVE^|qNBbLqIMWIhm~2C zc|YzZGs|e?p^?~_ z7m^QW;I2yx<5Qt;{gAr&QkXo4_CnP*$RrZL1;lzC#O5i$+%V(&WgY%vstNEiyE71Y za%gKsGPTFFi=HIty1gAOn>-lL{nQ$_vQf^jJU5vHY0i}u;KfDdo_IBIf(#Uj}Ne4JQbIfZLJ&*AR!w z7g%jG?0&B&A)}4=sv6T@MHmnU$s8q5aiqa4cHB5o{)yLk7GvO{PDp3=3;S6{a-}qF zjiTp8Bz6lt)cMyiAkaXL9W%ZugmL;#qUoUO=1D0G7oNVZy}1LpC&Vyxcv~K^xhs+bcsCzI9N3qE`RkO} z!2h`qUvxg+&+h#5Rc18>Pj;z5g7nJS6hJ&6o^ZzOAJW-}*5vooC_}IZ6FSXefxWQ* zT2)?=n=>I!`aSj52}$eVTNfD5Jcwd^Ah~;2+vw%#CbXEILu$nrCh$0egyG8?AOOcN z)R8V|hvJ_c?A;I>LjME_5{bH$?!(4}DyCS)Vy4GR*)#9iV6rv(;>a#!Dtb*+R8ITknJBJDCv&w4 zGxr>d2d4A(J8x^)2HN=9L~Nr0uS~S*Vx!Jk44$Bl{%s?#0G$VbH}z`3dcpSf&+=!+ zFkH%#D9f#K364c4@K_JdB8J zd6dJ`3O_4=*Vh2(jO9$kD82VTc6`1$%hIY>`>l1@)2UKAi=Y3PYHz@q-q+%437;*14iNn3 zz|{DFU^yz>f2+G=?v1h3Yqv>#$@soY;Y(c~5V?z|;)8aJU=579rd$k7r`V*T)1yAE zdYC>ZjlVIWHde(X0Arf%?*9Zmps%GL*g3``Q z^xR6ZgJ>B3!iL!rsV{IIPv&|uUvDc=EC`J?Rg_tn0VODwv6#M8jke`~8|@Z_aDZ zz`#KFi}9*7&Na+cWfM|s=oiNK&kjagVbsZXY$5JqEF8CL`BPFTylVYwH?X}8->M+-E@Iu@%or3`TKV#KQO(eBVZZtDuXZHFevLwmg{3<&4P~C7kH+AvUnjP3VVHjvnJGC z{gvtldBg?|sbRNIj|crdMzK#3tfNUzlI1Z7MNt~Gx5iW~gK3So)wUzgj|4Aj;9sJv znMFM(2s|y%{>_0o zyf*HCsGdBD>z`9hFK9<+CdR%c1QS>08v{HJ)JRPnD)kvfus96~4W);Ep|U6`BLA9) zIkwA#%HzcOo$F<1-WtN`S1&fV37wDSA;h6ZJ0S+V;XocL6ff_UC2r46n$&dt(EV`Y zT(iU9fEY{w0PysHLQcmR-fS@vb-yP)LLQi+25sr?zBTU(g^Qjbp~)!lJXOJ;oC*xj zT;U4c3gx-FLMo82rjM0A6TF>DT)t+@$XkfVE*j;ywWU1uikzaCGwqUf*7IRf(he+Cu2UJiwDRr@`eCKb4c9%by#0m=o&$H=c7@ z)UL{W4UX;#8A{y-qW1*j|3Ae3t9;#>nwj>ZQPhutN2sql!6`88v zmvLuH-7+gi|7g6%0`SIE`Vw}>P>7H9(}!&V0-m@8?rQswuJz9j2aG;{GdS_7%>)sv zs;0uz20zi)ss>Vkz)Voe`eJGEU3Tq52q+wg6gtOmxg$Y5Gaz~$-srmLRSy|YGbFTC z2s+2}bW1c^0-~<3U1|OYQ2{M1w))zpDsEC8{HG-w^}IUSRx&He_cZB@;n$z9*}$&; z0;?`jIXp?|mD&6{2fA{&ZdNlqi*K-a#a3UY zFOtu@Fvh!mTuZl(`DXpgzwYACu)JT;*vlTul*Oaf@(1JlrIlG8wbI2>-{f7+$H;FF zH0g@ee`b9HU-IK;Qo-w|nz892xZEa`qBo4Iw$o8Ksupcx`k=#+9DRTEWDMuYw;a&j zD}LR-D6P(06#ncoES2P7s+FQD@0wfO`9iOYYaIm{%WZ1ni*Ij>CoxH`wVPjtQQ;lh zDtqEGn9POZmV21+t)vo%`lA9r(rzjCPZMusF$cEZ+M-O(;)23(6O8DEYi$3VTtajE zP0k`93*HxtgCoI>9rtqMNc-yQO5jt8q()%$z*m3;ZFcwX2aMsw09qYaW@)chk|PRJ zz*0M@Tfm){ktD=hdh+v9NeIr|6_g_{d^TGsXI+xP0Jl;aqZ5tbPf;S7)jBK9;Rh_z z;LcH;F00=JRBCTsDQNUf!Fv>jok}g=5HEgKXzyB4bf;&U_5v3jCdRO3e9T~-U{!37 zlQzL-lGUoL#o*>f3?H}p-!hEeBQD-2YkHJ4-**0$Ir=c+?m|c7))*r)ChQa_>jZj= z%3siqPuxGlq6Jqwl8kGG<7~WZfi`HjSlP#VzD#r&ht!@18EU0TxIq~Twxin#Qi@tj zTo20IT9N}*_U;k!`KY}Rk1XhJW+6^Qj$A-MpIeI01mB~;C1FR+V=yM}$W(ejcivMM zhCSM8>`uTXjVUaRF+C)5uP*%H)Z4GQ0T$?C2u2%Vu(&bilX}X?TJm8@v7mD1lCfGX z6x`pzNMws2rk|&}!8$g{5?kBQ(eXk}pDWiA=k4D6XFu5}^kq}NOM_Bk9OHoeM$_$6 zg|||jA*@u3Vr$|0J-deL_wzy+>3=M;7&f!$<|uYgPIgq8x)WXYRu$Jj3+Vp=Aj3!- z>-n5{L+jqIDC6k{7|hfex_LdBi5wdoZ#@?cRdHL{p|);RfW;7wQ7&X>W3_1$dbQ2F zI{GbHHh_Qhs=4I8J}|p44ZZwb1ef5EmQKV=hp91{04xeecBZClO^&Ca2E;=;9Y$Cz*PX~rgje}-bX;2#0e+W z%u%vY=qu{?J1tt22^gA0v+8FLGb~^m;nA!DPXu9(*G1g5tP=_<%&(}ZJ6o<#8y_4i zJ{oDUNNEhZj(MGJoWvsYX~iar51G z$D7=}Zn%?hHr#J%eOIvh{{|KEPDA1Rg9U3WE`uI(R|{XY!Jq@=<@oU%yY1L4NmJG1 z%>+B3m(@T!X9T5PwBgzoa{Z0s7oIhpQs}8_M5RcZqBg!vhrhjPH_p8CLL;V!>Rd~p%yd(H_bSp*!YS+q*cW)o@?^Bopc~*-nyH>+v->f9~+k3xUGJ_?WihgdV_WmW08 zIUsbW1)cT)j>R_Rjkcc531hc;M|`d4GNZQ)pzVp$!--ANeVEdMZJ4o_T8K7ei&V7p z3oFh&2J}FOJ52uARmyBf&;a@SzoZCbnLi{eZ8>_qusT24aRnrpB|e-<3^x0nK7@GU zc2}pX|FZjxmQNcN-Mrp)Qpfae_a03Ue;Id0}V~o7eIq$+! z7R1_Lp@v3;iezfK=0~Y%#Ml+u6NfkwL`bZmisb-&Qd^ASLgSrTj@OFL02U+a*%?~H zp%34Dz8aG|j9K$CU-_2d%8s_1q;zk4Pu?Fy%CQ zw<n7@|1n3fO<>CVYVIQBm3(sm0D;SVXsZE8xhlxZ((Sy|YBI(b`jt;}5p59hmFy(imr&T-mZycuHjS>4*8!g3Dw6w+OX*ed7#8|48w<0|2=>fCEVNItZL#s~eAGb(S z>#I#Lry~k@x#gK7Ih%#ja&%_nR2*<0c>J(33be29_M}@Yo!x#dhf=9an~I@P`tsgmzhZDk>g(69p9j{-JWO^4vB{bvT*hXxuEZfS&%}8%U}VLQ zA3sz({&Z)VF9jT)^cJc(r2&$<3Z)S$GFY=8CLNc%*3ep# zR!yuHS{eusnyn~C5s#(S$1i~!LjrY%af9@>raOS+mi39nEv=5DA8Q-u)~jye+9%U> zZoJJH{RP;ooF0^E2?{xkxtEAMIE^}%^=m3o$FH4_9-ob&9VJfx83_7L)YYeYrl4O< z(ahi6FpL@78zs4QpC?aWh^&{|VE88Lj{)HA{v71iir(F^+wD` z@aiG>RQ=o6METFlNsmkfrp|9oLM%Fb3!)>qLf3p@T~*W>|FGJ?m7zuF7V9aP{~UIV zxKWD1DX)kM1*lq3f4*!5;!RTaf*@`Yu(hRYISfLe>44i79U4&yW@(q&Tbii1olmR2 ztlW8=p~85ef0S#MGI>1qTh`6%kS|_c%X#)Zz-_zkae{eHx&6^CENjt&jXrK`Y#QrG zDNIRnxelSX=5G81zNE;~{r#I5pX!36kHresM5dC<`4?mDRv|Hfz%NECzG z!zjj}qlF?t&NLETmMf5p2STyb$4BM<{+FkjQ-C;`BK zaq64YW_TG<8=OBqxGuQFP#;-RBWyPi}~*6ZCs?N zPSI3Udp0bbFTRh|-ycHqL#;wls?~U@|AhBiLaaInaJJlm+{^1kl|Qp{b^e+q^>l(I zho2*in)^u?p-#w78n9%;$%4fezzMD&xw%3U0lKDHMRfe1j^>X7wy6!(KMw2zyqqk4}zz!x!4gb7sG^zrz^{>R~PJ*#0z(kc&wgD zHYnV1Hi)~mzZ%gcej(eX0)H*9mKAW>E^1VMT7iO4uzDm3j(*U0L+c9{F){xhYsnld zfugn7%BewQn7b%W*Do#;wm6faxHMYp$Zl&^P-^n_PmibCo%Nw%ZkP)3W{Oz==oSd^ zR6cL?QFa^Cck+Dq)iYk)SvLtI39X4!1K^U=8BB&*+t6Lm?{^T|T?sNsYhbp_M$5Ck zHLY&3%8BiR!HhXTEb3aB{)C?d{3TnJM|8TliqPE$$TYHn<+xw}n^*gON@Ul?Y+OrH3sH+PsGeU%@toUj&t<=6`ta^BP z8TUF!ge-cwr_DK8yGCz~cr!fQn&M-;XmFJ|jP%el+!9kfWpwqU$gXu*xSj0Qx4PV9 zcV@ZC)f#@!+HreN4JdMeW5!k5@2d##*Bt{nXXYgrOXFfPoYSfVXeWGxR>vn-q0*@9c; z7>f7eM>yuL`qKfDVSKzqdX@9?#ZMezMMWf|){yGI%{oX*tI0GZ=Dz5XlG`Ki1@Se( z<;l=n+~!0MnBO^nVh)zpNs{2}4nfR*GtEH--y#g5{k|i1n07V6`Sd}ZZm|A?h_8(8 z=%}3}yr6&t-K^|&6G9s*t%WtNpswgq!g#d4Ye$!5tm%HirU3m_nPX=!wI@l$$RNJw zhhcJ6FOSIIorQ2)8*~k{j%aER2#*4hNu#4F1nV}~xnr?Owm}UpyL<<(0=$o4gDhB7 zhY>@K{m82xoplcF}O=`k+{i0^C#5ON$`LNg!{(o|spf^;&vQB5Lx1NhSK zxBFQdL*iD-{c0Qafo#*V%$qPe!Z9;ClRXCat4~4!fh_4E% zD+F5R?EApZy;MekylatD`NSA6a<19Ix=^a>Uo`!gkT~iq3u2bK=&JjdYwJvHRirTBT2WGn^4s4|fpN(0V z#oNlyf~3BB`f|_-+$~K+BIyawEgCp`DwM7&p2^i8Yhfu(_T{$vVKc_1st z1A*vw8axU8o8o9W7F-x08o+c)8u+Bam(ig6{OB#1|8H4t5vowChd#{aJ4;T0tV`y4 zZG;N7TFr+5AS%ZrCn}EEz51*Vmbs;P;Kq0GBvYLsF}Km?xM5Gw)49>wg&T+2Z~z`K zwmw#HVPmoS>@vZ()_X>&wVrT!J6;Vp{oWCAToq+7lMIJ|Z~Fn(ZZ_Dw8m}JMvw4pF z>*L9Ia9THa{>CT0VG~p7jvXbr%J9e5W_9MgUsT(gC6J!~_%-L>&)@3CU7A`Ci^!O2 z`(hXD(kYNv2T#{v!`YXYM1`8*oTNeEW6k-Dv7kx&CF%I~U5|vGonW<1kx9~aw!`T9 zEUL;nx|?4~YK+HK&O222=L5mVW8L^Dah2lwNn#P}rQK_?FrpXbdQeh-4Zvys zy!5pece)&1yBgnFbk~Xqeet=bvv6-WF((F|_NBKtk?Qnku|0->;;O;HhUU#9*F!*MzL z#5K^_j9hRbiMPmpMEZtEV^1|e<8wI|PF65Gr*kG`Uz8ntAV$q{Pnq@TT1KY{N(hgd zB-wjgGUf1Q49@)3IiC6pt&@|{pO(uxc6JWiJnf_nAB`(E+wkArkGfx%WhwR>O&FUt znA2uS!@~pz_S;=Tx{Z{(S*i7e4&(+O7ZL7`3#En;?K8ypU&W%a`6yBmFbpKqC zJrd60Bkeu997U3Tmj&Cc`XUbLVUomaP12D(8Fq@lX@1dx+IN(kOG$19B{H}Q0pOTr zI^Cks_xj4@XDsWTDM&PtADl^o^+jnUSygq+-*B4} zQk_H^$ge=W->2yL6O8UL=B%X^x|wUAtTmRu$rjboOB}||TK;ov?u!$=hx&U)mUK_g z?-!Y?*9$i_G2GCmR!Fq$z zIoG8wR>nKmWL}onQ;4H z*HFb-M08em>)0h#A0j>Xq-!agFae8_%I;AER5&l?TkPkJR?BN57GYL+dZZWAW|^(pF6;hl%; z_g7gKjBCX=W~8P5_gwZlci8ZRNj*j^oC6tc%aN=cORwh&OX<nja8Il-=@M6b1r!7t*q7O47`~2}gEF!D_Vd2bB1z;qiMnEQ3_C^p>m0f=}3jm+ohUOA7OMl2$Ig{|H$=7Co z&0!guCr>caxlVuqQ4a5MxNj+L^hE6E$u9&6e;FO0PpFs_JyaatPk#^=T}_OS1Yq*4 zjo7LfWXSLir|nl{SgemO4QTr?z4SOH>k$FFv<2y2SnMgyJbv0LaXKy0MXCX?&5AU1=Te2bW-uYm*5$ek5OepV-tES?nsJEfxlZ2Q?#>FLpMO)gy6`3-IsdZJ z>~%nsW3ENW^bc-}tsUBp_AlT|a00BlSF=H(eD}(D)EG5o|LyhQR}5ZEx%4Dl+bo>l zSEGO#$MurWea#kj_I7!$i6}y9>h2&*#@L!fMv5O6h+Iy8isCWM_EdFTKq9CbTA91X*!u-z*d$q)=RR6hYR8!41He|$FiE}KF z25Ymb?Yt^c3Kr*L@2w-jgXS&mU1jWLfe^81S1-*56HMZwF>laBYVz3a6pKn9dpR=d z&}fMSI|gGFF??)@waS0~4*9Ea>LkD8VsAx*TP_xV|D-^!sK35FY@e=jIEise9>1u? z<>KN0;RGB1*61tV>?+K?BnBW@zNn z6TjuLBA5%CSe9t`)j4(aC}#ovW)uog_62WkIL+Gr@|K8KoHEP9T~0@Os7<$dGt6-G z4W^>?BL5I10_*gf&K-x0qBj)iKipqy^?8cie3e5` z^Bn6nX_Xtw2q_$!f@ng_&&uBp|A(9hQ8=g1IB%vfqbgOD(Sn{po%X-X%bo0+1xpGF zs#-i9N>tnf9~7|YL|5$ zI8b;EM%-J3MkIj_s`utN6&KrYENaM5(P|vbei;1T3_yZ|OQJR9${`8g%u z`kO*kh_0VZRhaAyZf~fR{wgzoM&Dc-H> zDzaip-(|S^aH?xwtYFf?GLqjmu0vtp)x$Pe4b!j4srgMKrn0>8i zd+(8!!Q=e;4Tn$tD#WDjtNZQ)#Ff{N!H?fjbTV5j(neesue47^X~`j>0Y{1^UZUR^aytR3`f>(ph{9eWgHFAqfz6F6;Csk?ar zFL+N35w+j*tDqpi6_ZeGqaW#tGBhKn<~G$j-ai0E|w%r3R! zAgi`=bl7lXCh*u>ZHb)FqgD7^>$vuNOf_pk-|Q|pdK1i@Eg}*a*_iCNYXb+D$GCXyqQ0RI2Rg!_h(Z* zG$tnmqwZAJG|o8YN3g0q$^{H)w-1Hdl2H=rZ4DI-2a*c4p zw;q8rpznM#(B!e7fd}xtF#s@}nJxFwo^>h&<&xv>YP-kHeLI+LB6mJq;5*s)G=E?8azEe3y06Bw z0z2L2%hl=46C>=>dMk0h3Xg$6bYW$Y1~RYL|BoN9T7Tl(izLMctO?8(%U2-77BFI< z*w^|$V)>44b6Ro~r2P}n_J^72VL-QkhV;)-$xq8;4ba|(OfM_*y5o!<-F}Kjg%^25 zz4k1YR;E{n@MIHUaNM2#_V+C;uJv;31 z4FAppk1!~~X|VQw73OQ?tvdUWcyZWofMX!FbZE69AB$FdF!I0;%>DK@=>Zc0v)QTN zavzXKp4Y}>>HsPJzCW=|98O%5&B|JtIiInmyBP|X zas0MK^el?$#g%>;ajSrX1dvZj&8`pZ=L?54d1EhZA!6;-_U6v*F&s}4D1EWWgyCWI z?-NA9N8c*3{Po>Q4Nni?;C{K6xP0&i{<&`qYiwdLnW?lr>V{0b7yG6iC8y)@MAZ;yi=MyDT?x1=aTdSNxZ@5eH})7Qy-h{-j`1N`7X+UP6fMH%#i}qG z@3>2{#@+!MLc@A6@vq}{4?KZeuZ!+BIxLmsAO#FG>6!F&}54a-*pYkYHG7_^dK z4uKVbN1V-K(2l<5evgb|R?dFHjt7`T?_4AR{5NrtEC7W4-=YKKU(x?ZyuP5t1ykG4 ztcQjsnlhaiO^gqB_05!yGuS@K*cMd5x}|Yew~NNeDt{Uha3`A3swFhoqbCCcHFzAp zyym$%n%~o~1(OFV(c?7Mm|L3wof`Pjz+L#7;A^o4*C)6(r9o_s;G4XYPyRO*lLIRe zRCFpsezo`iI}c#P{j2GB*;4Dqz&&kJ7Gv>M3374tz;{2E>~dt3EeCgUFGmRLZYpq# zs(8wf13$NB*Q~J~dQ1$I&3|vfd@f_@%7N7K?K-uQA&8wu4KNxX(BM!&lBhTFj_Lzi zMkgd`PJcSR1wlzx&9aH28|(jnDLBEz`iO{#p$U*%ck)@LH~84z<>mFn;%yyF^dGAK z%3q(kXf<}bzM`BBvqTR?5_#WP%Bh@8#(Pu1lt=@U8$~{wLwEYm;1AC)ihX1ETalL5o)JV?E9h1qU zWD?a%PG03dcE+=oX}4Q6-WeOzPAjUq@=8^{|G=T>clGJcXbNZac&>leLEDCfCdG_0 z>iX#mVjHP>E4Rc2E9QiQ>G&^$1me4!N0o3I6=kkO!t57d--+H9crceAa}}mBwf<)i zTFaiwl)Q9lYA0zqc;%z(v?_9MAH~RgsY6FnDF;%A@IN0p z)d+G%oP3jK|Ic1O3fbyHE-KZ5)gAgbE-u!RhbD&qo2`>dm8sq}(AO{`1Hs6Hr=?(9swhf*UQI zU>h9FC;nYrr7p3zLc1gIA0uURgIA{RByygt>}-8IjkbLxY`d1di3bcdQ_JZ`?vOAoa zsB{OkNM9H1@S^ZTHKvnih6R)Q+)e3s^SJgT${?|tvrGq1F(^j&zeyA&jc%aRZB`BVSvD$iA zeXL+fWp@Gx`x`VvM$dwkdLylB%I!|J_UllCJ{7#EI|)Q^X5*iNTLjD0-t7;{+f(p3X=k(ZI751v~SRhi&%*wU>T8lhL{?(BGO`dcBAu;N#xJK%`?clFDx04~%8xDIZBh)KdJ zH9)Mxk3%tLe7~AEi0s6kEXg`!Ue z@w*>LT#ZRu&c0h;L8#g{t&Xlex;RH_9Y82P++yx4`;n`ySOkyd{pB{@PDN$(b~}v(sO6x;V)1EmxaAw;%yHT)eZ|d&k>td$xE&)b+!YCq?U|9Brv7 zctqRbnK!{ii9CUhKx+R`{7_wj!49kYFuHfPM_j|$8C^dn^k88ZfJd{3X>wI@gOv1vTowVr;3F ziUiAFPY8+!k7Z@hsFkSCbUDQV{OPv3nMaP05(%UDVksYEO2l%J3BDJf8=&wXzp64L zX@>ut=go1n^)Kq5!k*2P*B*7n(>3T`83i*!+Zwv^7pBlWC=!0g`T(>2q$Q0#3LtrB zv_HV@{IIS`giyx_2nHhne)sjsj>B1s)wJ?lW^GNOx{1NpLLv+MONpe>r%;7FEV8$O zF8(}n&m6c%s48U*Hy|MHEQD2biXZ11)a6L1{^|fpu2PVyDn5fd<4>pUSMPa1M|1-^ zHMYX1KswFJSNir~m%Ook4|%wo%gs^Cc)Z04%ue%6j|x%ZF(OV09++K@cKrDv%+6?q zNs08x3ln(DrIDD~ardHFfq=C**pS&aCR-X4uZF%#Eo(3SZON{|wI&qP(}gah7?Q~S zId#v|FU2Lv_S#&}s$Ef{yctHL}E`!O=pA7{g0#tEiRZ&*}{ zF$yMDmYn~u)9x)6G%=>DDZ zRN<*%Ute(z@4KH?a%ym9grApIZH4jRfj1*-_jmJLUb$6lTr5nbT#q3XJ-C#5;K0;v zU#)4vhz~v46KRciD+m}aPCu8`^!_lI1Usp$;sB}==sR<^wLQQ_CR6z^G1u4l4lS(R zoqsuw(dp%{!%6r<13}|%UQ#CaeacN0n&lzC2GP*ElM?-T{7-^3B~w068if`G%WRK3|;q9>53>m zIM2?(`s(c#8MSXj#0e%8!Z-k{l`H_9QAaV#5%#1y_r2DE`M~X)k&sqjV#%StErd;5 z+j_PDtxUb$A&m6q2!(MUmYDg-xy5eo+mV*d=_}FL<|<~>WW4ZU*|l-jP)R|#$SPq3 z7kt?Y!Od{zKPc+Q$~I*d0FkcA-UatG54} zzb3A|NkG0t@kOJiJG8b+6fUtvoH(!Ga{Rq(PdX<*OqJ=ITg0EL%B6O?f%;|BM?v-& z+k-sGH;!%yywkQ%eJn8y>-7N-_~%2aJL0kwrXGIK&K7^sQVLrPU+%bAfHG)(8tTw- zdw?vTDEsK_#Nb4Ijs3OpZb?t~iQ{eY;}7xS=Y>PNo4I)MD^)~Kr!GqaPyO$WG4!@7 zo7O7Ko3l!bO}Jbh?JD~G4x82h-~j$`dF^0zJG0q5h4RAr_R zWsD)ZfR`tBlMXidPPih9{{5Jml)Fp!LSO7x1~t0$-;8scq+L<+3iV`Xch6?^l+QKJ zE5xxnwz%Z7j>S{mn~5_WWSd3u_63}!(PaFnpU-YRKuP$aZIX5SNp2FaI9a|=C}}K9 zEWhn%=t4s>@x%dRBFO^u)E^^+@zH`?werOYg&=RNeQ&L}L*(#Do0I43UHC29bbcu! z4hYin67IHKm4Z#tO>0j-elm+kd2MPTdi7*+ItI@BI)@zXXRe8aV5X!L&nD-D+7?$@ zs2P5zOj8uxY`j{ft1@+I1qGxS`mCi2x8%1wtSOW7nq@>7KKF?;!@gdSNWxE9BV~nW z1y_}5mX5d1y>sqZyWk&2L(ACZTieAa@ZthsAYQ`2Gat=zM^Pfbht&9Bo%!1QYsVU< zlS!D<-3mvx$rSN;x#<{-Tw&3#u~H-M5ep8&5w?A4Y~EL|33$xn^`zJLsvUre-yNcU z`t`Tn9NxoeRrU*QzGMM*A*rlp>}TpHFDgl~`x_4jHmw&NI_)wIuQuDE``aW^gBhBH z-K)b%!=wt3d#J&x%#TH^cRCDyKjKtIFa@BWwI5AFITJN)>r2Wo$&d7L`guYrO4%BjxUtJnz(L!>%t~d8*XqrcKm}dni=+w3rLJtF>uY zA&l{*GYXLHjg|Bb_%v_@LfrM)8O^h_j}P$bajpfu?p9aRB+)?;zU zd(RdYxHyPMntahCipW58g)f=khR)0g8p~BRdaB63b4ChUzkD?BZ}104z-xXNpg@skX8fpCot9YwC!$`WIIFTk!tZY^F)Dz_Y$R>SQc63qD-AJmak`rx%H>GpO6=Fh4~Vp`FigWv`oX>fhkWs0z zjPt#Ydad5^`0~D7ti9A%{0}%+Vk5^7TkF{4k8B!dlR>l;eS- z8-<964j3-^58vTZ3ZC$dC9sC0E2F^^eoXYVhq!%yossRFIyeuygJDL_^UwLdrzGz&YZK_#$TM9Ej&a|v{fsb9RwNG-aGVwV`I)+Xr*!(h5t`pB z`OmoQA3K=+FS3pY);SHn)8Em&+(;h;y^V}%6*-FSInu06TVjkUl;Ov0)0rDM5qs8k zF|OPQF}$~4a=x?SD$Cbj&wt%>*6^N(>MFfM9@l`%xRD-Q70*f1sAo9^LCknXuGzkg zxj3TV^D8(>5@9Q$?FouHzSMkt5bVA?_fe~sVU=-iBd*@L3xl$)li+GN@cE@H__|r{ zGiePtDhTy?iCMGJ@SB83W2lyf!a)NF{b|u^%)2)2!<|qP9Y)S#Vl9ziQ`nd66+FHY zimZC=O&!#+N6t|0-Gf&@*RqQzvd7EH1UkHXIVW&@4Bk zGR-?Yl+U?kf6sVcd$3Y7g&tfR>Hs7YyMG?Xs;sj0u%1{J4>x)zQ3%TBIMsO~sA00T zOS)nIjC$FXAz~$%IA`N%_mpY(atZ-~U~Hx_{T6&1_Bql#e zA)E81?_$(OF6mTZ&0~A*(F!*aWZLiP%M0O~Yr$W>f(h0187Ko>I)hl_vyU#NY9Txw z*S$Kv*#hZm-H;&b!2q0B3w(BH7Cd-&{k2}b2yO+pEKZ}U_r(5KWC-4NnA%cu7!v#meUT_LaKQ=sv#2T-KX4u|M7o5K0vRGppD&NSn2e=U!C;hd(#wl+2W~O zRq9bVhP}^Kl#cOsE~gVMa$!m6?z{~pjTMkQ6P`@@;?@?Ux(dj=Tua&Szxa!E?)!OL z)RN_REQo)O`kLg}c}5Pl#5W$=;kq?_U1Do!JbADkgcgUTyX1oyXZIx;hf@YzB0!ka z^qq>+-ghxFEn7D6kCZ2?9UFM&q(9A4>F!Lfcw1IgAbWOeuE2R^YhI?Mj1p|{VD4gM z06wD=(4Wpqz6xRBnKg}AG-*r^<&XFEm~A7XAyLpV`S*K(m+H+D*bMAiRgJP_pv2A|C6t;zA#T zy#Z!^!JxQ93j^arF5+d>!F+!Sro z{m+ayXSilcV?qMAg(ycFD#@-J#dL|MGuWg3-o^F8ao*4;367{E*c|=xs9>Ry~#-uLEfr*|1zG+^#;y6BwoHqR6k6n-%7_*io#+aZRD z54dj8Ss`moclMc_uNYQ*w*6?#7^_)}Xe-MaLDN`ZmWe$kfy+z2DkobE=du}8bfsi) zA0^h~2x|BkSUkv~XJ8|hnNZuCf*sEZk}GeHIYm`*)o$p$(nYg4Cp%uox8uAhR=`p) zHUSRp<(m}kJLG-#<&3e0h~YaIIC``zYrFX>tugge);)TCw7 z%p4E(PA~?#jDaJIFZPrZGY{%>%Gq3&@F8TIGm-0$A(J=`d!oH^tUb!Y>N%Kz$G?1J zxz389nCnT$iQ=ONrApmzy#0iP$m|DpG*(uf_UlV-Ny{VvvcM}KcXvEcXIAw zCk;n^&lkFs+DJx~s#!ay5oN*acc7E1f4D(C?)DzI zmwE{m3=BefWQ>*3m>az<7rIZf^vm*c(z9x@GyV-p{kEa{o=Z!2$To`$r2!#40}sG; z9d2|1$H=CJRZR;wC58%PcWl`IbHZCC#Yhotyk)uzeeLlr{%yq|I5RdlZx@nFXM)4E zu|W1EtTwsI+z0RNVSknJlGbo+_(p&Q{rbX~dz9d$=FjEn=4WnOb-Q48nH1>B2RP8p zhMuv|n^-K6u?O^+NyI|I)Kn4XQSMN?wAiup#PLZ+H5ocCe4i**TWDEbUEt}VIBH{g z`>SOC`E=n0Z_=GW90R1`0yBqiJU5og)jB|@_Iv998;(b(B*{3(_x(k+SEF!ml0#z< zF=I9jh==DcCbu<9Hl^A5N-i?6!oqfrb)%_f`{83=t?AzlW@4GlKjKO z9&X`7%O_@^K&^*Xv)#CYTn-74G$E@cG8R<9(4;N5S|~dayrp`r7;N7-=KytNFY}X| zV*FW9<-Ym`Iv&=>ySKF!siMsimKwZkIk0fq-WO!`hQ6n%i1l^Gn_FpeDF5_}PDiYP zQ7^$1(Bjjp=E_h)cDl-^4XpV1-!Y2hyzatzBX^oQ4%X4l}++DjtGri`(f6 zN9M{bj+fo+NMMjb@Ndq00mrR3BIl0LYTGwNdQ z_N+f5Tpla@s@v}6O#9(ie3!hz;7xTu;5%}d=J4qb`NfMDLO+jKJrLx~09Vx~LaCP3 zzQZZeED-M5&85e_iMn4@wXmI-_s?v9e;7EZ!kcdTFQpHMI?=jq7Rap5HhKU-I!ols zk%M&G6rS1Blact{HB-|=K~0>3wPb$?WaG$$M?8)y^X|^k_4W-M9F7CuS|Y<+B=BWV z$R*~pH*XBzka45`V%Gu(tPDRUcbBec3Y_E`O?`a0JtDp5J}VbAwwjwXcwn)d4e6>t zzNxZ@G3k&)NasPz;*M=8-hDC6>F@9Vh)^k&JDN~^qgJEad|wBsh%I4~zIwqF{EK$m z5rf{qy{Gx;E7`IG1N^5QhoT{eB8;Z-h=*WjwWo{?-K*pA1W`M*Y;Gd^;r&k88&})G zu+reUi$LP}PJzTm9XvSJaSZdFyQ?r}5{UnMu@>}Q@SCX?26au}>?Ej<64I#lW^#Ui zd6@ggcp7e+NB-Ea0yETjh(W48t%AO_#Xz__njq^VVZ>>x%JUK}w#fhbW4%3jY8G+j z^}1e+b*9A@7eb<;HMQzMPrj07E?Y77*psCqz{=wyV7Ih#aB|JN0d32C$)}{>g`I6I z>SbmxnVMc-4_7I5-^AS|>8Exy_|_a!eX7H4_tqeghZXZKT;6@=#zT|sfNlC#zDb>C z3Ezin=!}S9+gj1$YuDiGVkaK9C?0fn`KBbUbw?36obLsrk+!+Qnd*~=mRu*uD+8d% z4v!aR9_5jz;7(J&|Bkz}dFmhU%m+~1vWA7gTT*3N0dTn7j$q0>bt(Vnzm9ux+NXI%n~-}X)jORZKyGJXTv9u_(2rcnRY5UryCHht#<#OI}MA)-LYZuaV91ytFS$5yDkQD?{oHM=$AJB z+Cb9qUwUiOU#eV5tnF`@y*(7tG&;vR5Y!{MBeO(Pm z&QjXf7!w%nXqQey^mx^|X`mpj{b#GYhlX$YSrq;@k5mH=e}a)L~Bns%^7W z8*g^FTd;ssFE!x%w3Wp{rvL(MmB0>r%$%tR_NImhRB~ILiIFLcY02AzDHGLW)(lu; zpDKQ=u@-}}^U2PyMv^okBFRoI+6KYMZh2Z));3S%uUgedODvHK#Hvq4*pVVhgphmA z?#rTiX(FuY7tVOK+DtLE8>Mw++zZtXy|yx+fx$DlrK)Pf^>BQgss-Bl`j^(JqY8rh2!-^o z^=NSTmHl=;N~`cRmTK1dc0V;;+q@-kii)HOkR@It21IM#91P^ET1^3TD;foPlA>N$ z*232t@Vayd-A}J7HfD0jVCLdjf1ON61d~7?GYu*Rw=a@mJ_xvkK6yfNH1e%fT}q|O z0{BZjH(;oyU@KxXcI1+?&`WoeW38firoQf&8DEx(Xw#(5Wann>pQRiXEA_Jj)iOxJ zj5wnge)K(=-v{IO^oO^)r$*zCRvO~-=g@ql7f*+i2P>vT{4jK_CclE5iLhRMU?4cI zG#fEw1~gdYQHq#M>r-azVhI41`7?wwvl|T~G{(74LVq0)VwwxY;`9zDezt#9I_4SA zX}7|E_G4!cLRB)Ii;6B+(L$x$Bs+?P4r9LvvH2igOM5=|LDs2Q-(;%m@GNqN;Q9N0 z#OJ!ua!Pc9y=+fNdY3 zJ@~qo(eZsLW&KrIoHswdq%HZ~EQ9%D+{{r#b;b+1O6^0EIu?0O^YxWnTy!~O85j>2 z^U3a~{8Q&(0g5B{`hN6MJiny)RH1;R&+*Kra~0*;aYv_yKQxtcgmS0n44cdJa-T#j zOD6s<%F~^;g{_?E7E~_=Q=*Ozftx}Ux);FEy(n@fjL&&KsCz#eZE=vMrpp{FB26=& zs#gffNo&Q41XJm4;9E;?HCK9`le3oAO0MPp-aD*4em2+tMzZ0*6efKFoPS%42MUa%ciiU7BaK%FRi>Y zyu@V%YGYFBl~cO%{0u|u_kIc{|4moMD5{J%X^@~`g}lcmfGoXljW0duQbu>g_CwDB zarwXcO6ov;)wCS^3aUA!RaHgMkz6}}jfmXCa2$lV`hbEn`o@F(D2}0dv zuD0bM6lsVvY(6(AU<&NpB7Go7Lgie5stzx7gcwE^`{_{pn}48wYq#$#LUlXCF}azfBwFGR-@=2S5FrnC{3x7m%VlRb`Sq+?f#9>@pwU6Es3V9bQl*0kRjEzFhHpv~c&mC;n zS*E6SIenA;0X!{)D`*0A3J2w;PD?L(0-m^Vt!Mptk*zv)~e ztdac|iel0YVMlF5{nEB;g7ePRXrrnKCsSs@{>tpSTzfR2Oi`(jOy@BA)L-e+*0hjm zRMhQXIPUhkf7B#Wkg`$9U|!`95hL>mfwDf$;=d~viCH?;e^hJOx!Q?{uZxN4bT*w% z(Kx9%GnNa$u??VapbVrsy-oRrl8*Erqb!&#MoV=t_@pt{g?*4wHAQ2@zMPVZn2jRVYZfYYKhPgCViJ@m0Utt(G4g>L2_|Al~8{A|ji!tjR`vp}RvP%1q%E`D9c*1gHSZ~Yi^uoW8yG|XXU}Ys3Iza1TLh)>NsVt7O?ykW0#Lx|3rBj<-E!%? zG=CkzYa3FR%OT`T;|S`$_0mBQG>{zt<1^Cm5`dPzJas;NTXIQ}(O{|egyOe-#pZ}3 zXbt&Q+4$1&^3}fD$Wmklk^X4R89iRJH;R3OmCT&LcwEyX=o*L3G&H;TmdgVC{+Er= zv2=f39AAaxx6KH-m%2lx5fOXN zvx=*i=*iW6yLRAka&`QGCq+?~1yse-q&wJLxJx`hlt&^10lAmWU~hgBj-38VdDde;VXjMtd~) zdFneG&un}UYeawZBQ5CK&gAj)pf|2(#%CSODg}w#FB+SU7Ox-|BplvQ5)}?ghr@dk zkpVgL`>mJX+Pxz#1a05CESomqj~PhaIs00|1h7KB_@6a}yl`8vvH!F4MV1)>spg<+ zB$+{WZETbv1npj*(8d16;jx?Xo8WQtk9p1#D#!FDHj0cOe(cRjVt6NT45O+)o$Dt|cj3PEvTpK!?rXh1Q~NG^E>OSsXy5RX@t0hKU~x0$t?EMW zzP!K^$Eo-1s5bgaJK)7Ssb_VUZE= z%_Y9H?HqR5(T!1CwKrQ!@bO2??;qq2i&Ka!espq*h?^)?&^!H z<=~cPB^~(L6PxsMZCc^z7wZSI=vZpMceLQ@v#$)^-M*lT3~Xu4ZRdPvV<6L8ud2*= zO6CHBYg-PuR<|0*9cCK4ZD0EE!@Ymy3`a-MEB(xRED%Jv$oA>;aqdxbhV;RQ z`|S?MU~0wNz>>_@!h3044{R2F#E^DsN53&Fj&I1Y&I&nS=RRdUv~HR;cl_BEp?B&D zKk}%*bs491eX=~p>GAvB84K1|IMFB^pcWAtl1mcN&E=R?{AbnP(<=KthVrr{ zMyk+ySTX^9`%so4#-F)BLcqk1%9qM~p;eJhSnAg(2^=U#Hnvr@^+%HqH%3{WCG2ZuCv@a1b0p}tL2pEnXk7+7YB>BSUpX(d0=ZRQS7ZN{od~iC(MdMAHk6^bbUV?7j zCN45)dx-=_NgK=S9s&#WIzBjMZ7ZYRHr~GFc>bI$fk7djQR~lxS7^xiH>8LTjtF@T zw#1aNn{27$-}1d>Y>ObYnyrViXsw<-h?fGOB`AIBds=mmTW?m$(j<4ZG4+n%6T77# zz0uH!eJJ9O+pvXze67bRjOYQaI{qqJ8-kU@z5%-AFzXnII>ssJ1HidJX3W|AbzXHH;P~r>U@ieolW9l-zQnf zeHQkg76F--BgrVaMLZgqv{t+QmXuY4xGCLnfGi@7P4wM&d>cdPml|IZY;|pDO)Y?i zYwRrp>|C6QeaE7lIiaF5ZJwZy2BF>HP8BNywrJyiiiDnl&CrV>`$fs9Q94^z@Y-$ ztO{>RbL37Mh#abY@xz>AqwpPHMU}&2Ddcn|hB+#obtH&~v(A0Y7AT`|oWl;jI+5<= zY?(Y=G3&*P(~I_7Oq{sbWzBWhknnT=B$sc2HTb%FjomaEkCiKWv=~ySS8^M6Td7kA zTQXkl?V{k-xth4z4q`Do7N3T>l@jyjiSz~jtTIr5$}NcGodIu-Q*;UR;an?=O+P~g z0S^4TlQj8XgwO_Gdj$*oPg41@PzwP+oz79;(+X-n%HgAwUi#CiN_P#f&POEHq;ZSO z!wYVVFE`~IhuK>9Cj_s4Kfkl5NKDZ{vnRn5a>g4jL(Q!p2*HH}`F1GutC9!A}Qs76zC)NKU}&U|Jh9UWQ@=g!T?5dc=!%S8S~R(s*TlAcmmJNMd5FT$NX)0-%?e) z8Xau&RL0*s-|G~yZE4pll(71A#kE0~T>e#`{-3*uRG|hv8I{L)FB(ElIqXbkJLOs5 zZWVVgz6m138UY%qbt`#MeB`1<4)e}* z2KEa*N%ir+xY|A+R|`pyYaF+-LY8d5p+7NJmT6QS4DhYHcUx2nP2Hh3*sS#=Kdf__ zFj4rTJ0}&dH!5alvNp=03=6IqGnZ?S#FEu=uNuCxZam#+T~LU>kI+JHzhNKIN**AL zfK-{?c_H!M8<{OuCgVeH-|%@RN-cHf#8{9FhBcm%v}c{p+L^A*w}{a_1q;$?vXJa@9 z(N5#Op+s5xOo^MgC|%e(&dS&m_BioegR`}9N#(m;wIgV@BkI|&ZaDRCM1!ar95tp7 za@w*61L29Zw^e;|-5SSa`5)b-UH`v5BTr#oGH1NN07#_&q>#)cUr>{~Y@zV(U`v3X zJFez+j>8je-Ib|So?A681ic#FXO*wpKc}hS@Shrv)Okb8xq(FWVPdE-;T zG|Jc6TP{{46S;`=}4Lsw^Z;1-!TKjIf~zq7k-6vM$Vc=7RCy z$`M2#Ju9lf4;>>5$)8n@HTGHaB=JA73e^{v7@6F(h^Xlj(Ji7!vTGN)mktr@mTGs+ zZ%W%{OR(82hXzOKO!(}5Se)T7n47$6SMg-rg5e~#n#{XW=`}S|x4ijEP&%&pS~#!L2^}kd(3{w%iUaTM%hl zd(O@;-yRNozwrQY(XrP|TE~Za)Ykc%VOuuO#<1nOsiA2j3C}N>Uw)jP&76U11s`qG z*0UU%KfB5{xf+gK#$Fy=Bbk^-Zy_d{Av@RW`@x^Cah|W>qkFLRw@SltvPD@=Y?^6% zES>G9vN>?L$ls@egEO0a?=31cEXDY7Z&=9!zS7_QJWfou+;fVeos(-+ERY8KDQ!ct zQO3`%0+DZ4m?X4Tra;Pn`W+{M^#pYNjMylM(m_&g5dpt9Lbrzg>ChU7{Na(pHlxn$ z1<}jCJ;{*LeHIXo9u2%J60>Vc@Rcw>K34%1JnloJ&aw8q76!Z!mcCW`V9|tLYR#}s zMa8RmFsB=X?PJRLcVGLT_Uqo8>BtveKEG^cP)}19E(uxujq=^&t`ZzGEj%AqPPS~l zLRRfBx5}B&$MI`9573Mcm@KX%4+p;o@Z-0yMKudxT(nNm5X_{4fJ4i{O?q_ zN9C0!;z8PsQ|04_9vam))go6cJ_gR<-bTXaID>S36QukVD8p%_Z?d}tBlw$)trea@ z0fB-es}1#Oq({WlQGMZQ3TFu-n`b?I+R79j|D#?%W2=9ANFPU}ReM*;63o3)edoqCaliX>vQ(yX86}Mt3Gb0ag`hL45}rH!5ug>+`NYQb&;PT{5oOFd^RyP?7g4Jy1;P(5yht{LY@k6clli1NIcU(X+1KU%1pv ziAdb6vA5U1ERXKIySjZLal>bvtk3JFt8DHf=9d=Mw=}((Wd0nVB+~%!=)%E6L`0;x zIKNl?k~T62Z%SiiPM8XxJ`VwsJwraSwHxcrowgR&l^=k4#ZOZ0AFzXg&+Vp4Bhx=5 zO1?n%r{5QHY~{p*A}S$cl+*WJGqsPU7Vw1gRGU4uPqseN;i8@;m#!Oz46WMKu>8^7 z&(!X}&u2>%6F(d+b?O%}4`kY-yK0#Wcu^?u?;T9LbPQ#&}`A1tJQdSoObV`g}u+ z4@V0mDhDZN4;YV_GdIvCpkRmR{R&6W7AyT-H}s|p=M1+Dcw``g)*sis@cYex$8!3FcM_JYD2304w@2g||HBzBYX7(1M6c)&XrvG#BpdYb7ow8`4el2~0(~~S6 zqztU|pk#v_(fm=#{L$h!29g17LA~&bG}zR(*z*gEh8EUu#ZUiSW&I+$3Fv>Hx12D;>B#IQ_ zJH%viEjjjF z+@Fo<3<YzL+(Fo=K6CLQAHmv?Okq7UB zV$%8#+rk1B+Ck%VELzwoRU`jw@#ic{^rXBrI_T%5?%g2F2cCbl`g9lUuF?dg1+-hm zpDO?H7&JQTDr|}>>Ts*ZJ&^R@hf?S!bs`EdnCp*lf;xO7%mV*fGJgNo8~=k3@YD1o zn1_LT3+x%|QU?5eFp4HgD*;DXoCpykAbkM6?4QFGOMHPAFj^8mU5;q>W!@?6KkkA0 zucawKq-zDAA&6Z&;U6O-r;0LSf6iTS1p1FAJmU`%2@}cghNBL5(!|bDC9tkbv1s@w zAT?rDyN7aEzX#9IkCv3@#{UmfrYKSiS(X4`(X4XHVV-J^>fT`kwM!9J_`e5O@0w6? zATPJ)mgV{2)D}=pqNJz@;~%9Q7Ns1}o{4_##=-UVNRcK&`Ks?>;^W2O;8a|4v2gxO z-lzulYAocPB1{7Z4}dULH#YVFwV1p*gu`}#jb(rA*1yJ_-=n3TU~X{}>t*Ev)HVokij$B(wom^56*87C7R?PjGd&h`(QN zbZT?UP*R*KQ5UZh^WcbYdV%|6{cXt)VKoP2-XJ8xAe=&Zzv?3?AU8O!vQ|95_N|6i zfG!b`8`Cv70ZSzb8a@FMKg3uc+S~-n`vrL5(*;@HP@t}4@*0*QauSy83kk1CZ?U^j zmF0$b>!Xs9a0A=Yf94!>`Hb-^BW76&^T@Wp0LD%GxU59colHKO5*uJ^Gr-e)A5|eV zkg1El&`gj)bmRbF^$X&fj#QGO5=jJvnK-+j*i@IRkIs9C3*oe+HO^oE-gWXm1!kJz z0+`cgfyH)CM{MBmtf~!UIp+!H&N?Q|LF z)fyKuvi_4-wDt+jFbRc9aeN*Ut~;)3LsAsQ&!Q^O1fTrebMt;e{iJgDshzm2Gz@u? zCN3?GG$xX9#{FrlD{}p$0TX6vWPH2{QqU%2bmX^uwH{n-|wP`qCIQqJawV7W&7n`>s`KFq7uyy$AJ!A+ojyo$~>(0mL6yOE_lIqfLjillUXt&0O8Ry<0A> z)P|Fkb=PEn@(MC;*Z>FgSvMXfQID;l_KOGh57CxHg}i>menVmD^-@;KlOecqs- zt{3x8{RQJbt=&5mWS{-;hXM|hY(@B*zs(pj+o~CstmLWUrD0=|{mdJs;HSvcE#GM7 z$xN8mjgUuL)SXmO{=j@}|0P{}`J5~Ih)+b<(IhEX!OjcCQPSOcYjOn~L6yPNzUNac zPeri;1%3Fv0MBm4IbKk5+OM7N78^Z8-swG&wbW*p9^dabQim%;7~Y0oITxQ_g`o}O zzyi}mN=hxirm-+?svS|2?tP*(4R@*H!a`i!S4vI=-{!^y14SWeDhJA*k zo`M3AW1RMb%_3gpwc!g^(lRs$JlrF=91w{L71{_VvtW6fnQ1xzkW<(@a|bZ1ttgWsn|%pzQ)>_*QIUnzVY>w$tIOYeu6kAX9{n zkObLNtDq?POe{fI-^;2wTI0lp$E6<`>KBnFyXb=XHh+6W^5*7p!&(%Z&DhDuyNIOf z2vKOc=6V`(#vDVw+h5J{Gh{J}E~z-%T)**P+HOmdRP=MBZ>PhitRF}6B30&F*838@ zHRMkJH_$fi?8WgL=8KQag3VbUHJBSP*CvY@r$2@AD2AqRHFc6 zhi3L-$IAX{hrmiF-D5={%YtCs!AzNsUnEP`!ZQi&h8ai~qakH0SdL*^6Ti$?MG!hN zKMJG`-P}*%Mc>Z+tXV+>=Gx{<#|*7MqNpXSYmea1x(M@@ygr@U-DiQIKgPXD= z>+u-;+WR}MK}PCz2`eNTIT|~jW)>g8Wfx9GOXcRX56C+HUq@5`Gh@~N4Byzn@`wIWlQcP&-!7lTkMra zpHI6lAFZ`2nQ`3t4#Uu{kje2lA)(vSFa<;yW;^$ZL{hK}wnjsr&qkK5d|fHbXFU29 zQU>jMTxSwQb4Q4#VYB7oQ<~|z?=?BcOjifeCrH)MHn4yIr5@UQkU8)KOjgYOyTttr zfd=^(uU-iKcs{(Xz9m_0WQl#^E9cl?10u?JMrOE-^olNdEryoRgYiwyeLt;xQN0!r zRX{1+HhvJjeZiuwyt(_^!@kilS(9CpYqH&HvTaVb zZQHhO+qUhR?8&aZ=6T=mJC42ge|M`|tK;sz&g;C+pC+trIRo_wN7QL}rlbe~#dywr zJxL*Bw_44S=Q5+%e+jz666WG6k#5O9W?kE7IJA%W+B8QNjwd*@+UbayxY83cOlH@q zqGx-lgm~ip{Csi+75`F^(cThLli{ZjM64#o_KwYhv?FW|R^J$V+R7*(KB|mD%CFFohn~1f@ga|2xar#Z2Elq{>HX*oJn{iyr5j zKY~`Q$Q&oZCR?dArU~BP6lA79h8s^W5V^46A!J`()00E5MprJgaJVCJ61LLU@Bjd6EPq?lU! zr*!Q#sQz=L89n0VDHi74;|Q{Os2Cj>DlV}4UIWdYJX|G+oBADZGIk7?!&Msauv%q+ ztA^)>>w7}0%V;q~zsXdcaSvya;@uzX4SYXGYEk%eXre1wpwo_Z&W?X*#v!I5<73!c zkVHvO&(q9#1ypD&tRo%}guWw>$<-z$BSYp?$T5lU_930xh@m(zMcn=*D71tpIw1ob zWie}0XVC)Pfe+fT;_rM&^yR-#eHb)59 z%&m9WR=^|>7mUpJ5qgig9|lnPr>%s@S{=&sb&8Z_IB0oE;n{r|B)x|tVZnIB{o98Q zZnT~F_pOj1l!D;Q_xfx_Q2uh>Zo(O|(rBXqhLrKOx>g=QoLE5$C$XeNdPXB5$H=_NBXxGO)9GsX;l zbNe?}rQ-Kd_c=rx+I~ZhVXvmTU$S~El)MFKz z4&enc*U$BL#)c-h63B!#Bg+Vx-Gj_!elBVtDBx^TwWii1%G3sGYfT=3ll3f$OsSJ2T^*Y&Q<%K8l$VZC`x;t6NthC+hPK{wlF-68<>LEmj$QQZ+tK0;qcV# zEXKfFTsF`?@3gA4oKx2+mMQtC`#U-OdALuHyl4F%4tD9`)>^%vieC+3aN=(-hH9$s z$2d%e*1EqJ;$h&gu4H?jic#w6=l)`Q_(^}gq*MA@aK9V?6xST*7}TtZ-%_NEmN#Y9 zIw=EdVzy~EqrJPzq_%ARG60_ayH}r{_Gqncp*5Ja#vd>V-7N)b02j26btj|0Z$y^i z4Lrpoy6!5761YZf#`S9Uj$?QkfWJ4 zL;AYt7=c8`;L^h&^}Bbx!>QTe)myypDIm^l^9TR3crjvTf8}-lXr&ISz6&F@D@%b3 zu0O5%jnKtgoE0@7@9Ui~tSgWF^+1Ij{sqvy<(pZ!!{Gv4(pyo_lrlrbvEbi(l6w7e zYF(dlN^#n5kGl~f37FD{dQz>mpygFg)j2t?CMb8LF!l#HwueYcKRFz1a#k=6LP0w< z*r>wM+d5-TU{oK6;2YfR!tQlQGs%nXNh}z>W8h0W z-rHE>`~gc5gkCdqpCV2(UN8_3k0N)E?isW1TDS|TBio7?6GeH}n_^YI})}Pcr4K1618{TC?M{SIB%3GFgh?S1)&hdOi7V^{4SAZh;u_{7~QL!+OP|IZN<%EbazL(SffZ>DDuAD z>d?4+zX5BFm#Q}^h^xZvdOHh9Voa$rLdMq%k3r(=5?t%56x`!1|Efd+UrsYjOmatz z+MgtGOpN(_MyQe8YzIB6xolXv$*>zdNb$?3db>ZK4B^}PR9Q7wdi}2sxxMtAJ9=w1Is#A-3;6ZJQijVd{QW*iAF5Bmsg6>w5t5#1BmS zyYwl04Pd6poPq zZA@(trst0n2ru#ouDAx#m^+W3uTJYP%;sy}benz|B`bGhwJP@-HywJq*0!Dl@r?w-$Gb z%eg7X?V(D(^sDxS&A2M}y+Pp9o$otGbmp8~!wQSaLk*1Y*IV9&)@)pQVI8d@cnV%d zgY5!JcjgV~-cD3OgaM9)>J|OCE3m^36Um5q6=5r{`Sf)Cg)uO|<__gjUqAloW_6zX zrD}l!{lc1<+Y%5?K&^k@5X5p*vH@Z<|71<%IP5Co-5QVfR}u z68m#U;3&a*$u(zY@f_fyrNEO(0TPVyRd&&I?Bv@5&`K>MLK%WL70=-;b%R`(4SWu4Bq(H2-&iQ z2Dp%jXTp~ga9BM@_2MBsGLr~DY}ee(UGPSGqnkK;SYp{zl9__+mkEr0+=IFazsYY5 z*Wcs}sCf)E%0y)6o3;U^A2O=he#s>v^?c^MeFVE7d3(`sL0cd`7=+Ai9sn=6eR8~C zOBUD;9U3osMq~lI<3=Cs-7dU$3W@}*7N^(MvhDX2{6e(wyD%?n1^c@8S7DDErk<_`@R3-j;kz)QXDnM=SxV zo_EU7^w!|J+1iBGE4}Fbi0&H1z(|d?4IV;mJVWxs)4VJw2T}e>6K_*ko%D~Z3D8Mqp?QZVS-svAyWRQp zx(T1%kSPPZ7gusksS(=$>A}F;W=}3prI2_>vuBQK|DN+so`SsB<_H!)wE0vBpFn$NBFr0OKr^=5J zwRK#lKG-gkh!H2OwH=UG@!<+7%1}ksLPc@8JO-nE+1(Z30^`A)bb%sy&P5mcZ~^1C zQOO@M5#?}V@DDA}#NPN$NAn!}P`vVK!+sdBKbOhnns{m@l?-m1AWi7iAM`E~x#bjI z07b%qQ6!0!lao)o)wd6PM}L0s37h9HEtiYs z%&wmevRC!}3ss_o*Rs@dR{f^rlk51X!hSyW)r|Yi4=u+sLqlGFb=n);lesHt&yB7y zo1C@Es1U9$5r$3z+dXy}&?E)fde=|Y$@DMu2of7b7!IfHd&sim8yl-u!MPXC^hGf1 zLOaw}=RI~Nzva)VGSd_7ksfpe*p)6I!;^a#8k7C3h4Fl0OKY+vpzG)0GdmuHFGLSN(lH0A-Qqm*0|H*~E zgL~A!3L}dg;W&_R;?wQJlUW1YZU?ud6-*~h($SG+ZsbUx%S3Fm9&xeyS~Ta0kNABB zul%DTZA^MigsxSI$xUMn0`mv^!24NrK+<`lvE^jVYAXvI>E9}lZ0gHk>-`%%E+Z!J zSUd)=%pLItOeRzrFA;p+KRQrY$|;%*5N%i2 zYYboFDnD+yXp6m!PIgklbZ1CLC$bkE1>eGHUO!#XQ4uZ|x>Lm#PUM!;hZ)Qf4Cjsw zPR@6dikp7eOH;1sW)G<>KS!KT*shm;8jqc0oPVm2x1oY}H za1+2DBG(Z|p4Z6iSBMAS6nl!yjFT@ZRa^dG$!A1Wr6(@!0DNu*sNIX}Drxqw8eg`8 zjuPK4$W6QCh}n8gH2dIuBeYnE7YO5vO2N8%oCe^l(vA3ru&jP^nErApZlUv-a zlSd$c;dKIWV1KZQuJoX>P*wEV^Bv18lV|5}QwE&Qf0TTRibOv}U534Kwp zyPni4Q!jV`TklLEF}5 zVCoHd?L+p3^Sg#j^>HmaEm>~2r}L-1d;8w_6Vr5Rmf+}@h{IHBha0k=bm!lzo=|Ov z^wXIMjE}k`Z^S5IM@t{M%iccMp-fxKo;*7EO&QpDU{e31v1ePIiq{PGpDPTh9hsYdd; zXQvgM;=4D4A{DaRBPSIRjsxwSuOb=liORX$4iM?3mmj9G1kuSe2+8lsR$jurkvQgK zQMfia4wp@R{gh(U0k0<0C>sNd6vlMdE=#d58ilEK}nxuq1-^aZb5or9+PYwG8+b%>WZ zyGv3~)XR*YM3lxtgM-6asuG+^lC%5*&VtuSb~=4yk;(CKseZi@PpP0(scz2e(|f9K zUUc(w2(!1&#+6JPHKoG#FAM{KaDR-^SLdUtYzw9@^WC#)4-r!agA1eXY%$Rki zd7q3=0OnL~8=B7|VWP+p2S?+%8>~Uvw@_OCsur-nY!+0}m;>8pkW?LF~6hBf@t_O#1)_^?Q{y5&Ab^@1wVo>DZlv4AsH1FS_jwHc zSgGFcZ-h)5E4;krVAXf18#Znu0R>1v;?F4VWG$nY-io@sipBaR6u4(c4CnDgGd-e1 zuTdg`5dKIRty_?HO6{E*DX{>Gdb**sGqJrEQS5XP#*P1LaF_vOQb!U^-B4o#c*Lbe zOmBWHI8j_qOP<2TpNI0(gObhuVC`r)subH_kJUki1sI>I*QyctjBxZ!QTYr(T#mAz0@+R-3 zMTzM`*c$eDtGD+v%R`>C6W}P#xK6JXwO8L$IpQ}t(y7Qf*X#aR6hPoPU(eC>EI{_- zOo`Aq*+D*)^>As@@l%Rk#}dXikc4a!^n%qLlcGmgcJ;QlU-a)CMwY$7Y8Pyvg@&t&Fsl_8#l=;+@$Py zb~MNwBd72*g$u;Gheou^a4^)lqA+ay!)Tfa>9(^NQ$3~#S2#|Mjqc+Mh z7|6!g2#YnecsMeeiyZZ7v<6cuH+ImVB3uLuf{mt$(^vH~8^nca@+`efiXQs&hP4Yw z!CT2wOl!FWYg5MAw}3y|3@BIT0Uhl!s3*(lFj=OPR%Vy|U`#`2aOE*NHkVu5NKT)+ zJ?)1O%HbY)hsePJqs}T-sp48HR~{)5N8QHZ2z`O$I}G`FdvV+Yn^>9xP6L3X9K&P2 zKZ-nCP)^Q>(-6fAykWOTTCu@g8w{+w1^MO6^DAlB7ueaIbY(m;HbI!zP(ede} zT~7(=Una|m%C0$X9>upTlIvxy;WAL5gCKI`@&j>%;<(F(!;9I{jRrf&c z`h`H*KA@G*mBiXVKob$tG;Z$>KlvpD828MuQ|C%|4#61NWzho`*2N;7nO&xw_|CA8qW>2~<-bh6; zT9R423Ef}ldcfkPHH7v>CCKp zR285RN<5`NGTcpIDSh^NHrnz6CeMx6Dul?26WZP7D#8F;?vB)+u9}b{IFq}E3Mk^@ zbCX`6vpPiT9#4gOuh(S^{mC{!C?+}ZOPHxZnd%AI@ZbbaSi|Y-nBi&HHNAyUc68d% zyo)osEyqg#f=LWkN?oIFOX;Swu6`ypv8XM-V*?iy=h15aF$4&}YR4R}n;+F0+t83u{8pTFf z#rBnxFcqI8sj@CzKhIRh2GOHpW!hl*9HM9kRmIkG;RNw{de%~8!`s?y#@OIIVt1*5 zw{ax9o3sLnj$TeLd^nwDY63o|JNQ2Fb0puElMjp&*a){pzZA;%#U8@>^nM~pGLbh3P{<}Hd`peg2S#7fGxJ^1kLr6`r`UC0 z*|;rg1%z^k@P)=I!qVJ#$j-9>-QUonKOGI`uCNo$uJ9~I9Qtov$4|=mgPj65H?@>( zoC}=xSj#oO*{llvy&DURx#YjYNP7 z)6%;qD&`6Ytx#|Mb(Rf*+`3ofdMJgU+TCJ|wFY{)f|w(07v*rz9n@7q`C=Nlh&EW* zGRhm2Y)8V``$;mN5^%)Oa;q}kJ^WBP@_(8fd)@#Jaj*JrUVp&Rv3Sr$&yEG_E29ph zT60DvP*OCx)+NC{N8!cf{}60pn~|S57uIOG7YJgn(geLQzRS*c97{#mGYX9!vHfkq zwTfu85ceQOFdt=I#luijYJvB~QX=(9Ap61=iWo55a4SVeB}x(snm)zZTOHJQf3-K` zKzG#Vt3iJfQ8ocGB&R!F%1I#G5+#81nDmA2Ov)?dXiVltl zHm9r;bc+6`2H0CsVHq<1D;57P z)0gxu3TWPfGDd`CvN4olT2|hd>}6yO^yEx1Ae+c8pcZf>+%X5Z8W>Jc1@WV_s&n4r z8b_bg)%;Oktj=&Ksi9E&z`Lwb%E$NR0c0<3jD93dtt#F##2?4CF(7h za-)%=EnOgU!CT)i{iFECs3v`r=Na7(eB}G{sl4^_*=S0`y26@02$4`7PzFi)#emeP z1}S`nSjwXo3!zE78P-uUW1*a7O~Vz-uOL7(H&;hWN{;;D#4sgw1#mKB6$J3QInuy{ z%>_!QOa9v_Bp(z9S8%Y!6%TX_&F0de5sE@=;Mk zqoTyN0vTNY8a{4HRZqUGcw9mR@nrZ)*`L@E?QUUsms5nkJ4!8NC%>6*>O zbWeLgYaB{S1Nv5dwY!c{DZraQt+3DGI%BoaUf|Un1}Xt}Co<8#Cenm;BR#A=`oOhz=+^ zG9mMfsYucbT6zV)%)vw)!atD;Yb{@<9iXW!qsOE1qnTSc@a9g z*WZKVZk3y@8Ca|=xYacQ_4aCnM^>9tS6S59J5w=;HLnczvRu2rdOQ4Hd;1v(x!~tZ zBp%l!>RgI2ZgN$?`8$G=D&VPN4Mb7&lRr%HzF>*G5Z-FddA3Hb$c?|yl~|Qrd5U~u zGPi46DYqO%0#AR;5~q$y`+ks|GB}fUxLE6LgLpB_6!|cKl5IlU8cj_0=j!YkWaT|Z zu+owvF=Y#$^O*BO^(NOQ4Qh1> zOt$P}w@rEq1NXZ>U_z0X{Im767doujk2dG$%~(lBctC1dOl&0F#ZGOQGS-*yNithp>LFAkVbcV584Q^uawQJiRlh)Zi88lvhUbgj5UGS65b?RQhJhmWvt9Lno&XT z=oh@VdE8Z4&1T@38(YYJ-@=r~q-*Mfy8VBG9Tr{qQa&&lYg)K>WoGR9IIi7eWUMyk zo2`(J4B3l=4u6nuddwKekB|!NFhxG~IYCAMig z=Lqtbq4G=EwaCB{sIM+6*z!SA#%BtrvmlE@d`@NQ*1=2SQVjAl*jo0tHJlg#b$m3B zIgCMvyX7HwBSg2s)Ucr++Wj?5Zn%eVoQk!T1;O~HgQ0pu>d&@XO-$@a$e*WqsXxFb z|3J3y)t8|6P_F%L=AdzJyi4@!L28Tq?e*rM8X!E&J1Iq`w`J?k^4YOsjJlH z*lw|kaFRsPd$BV`0Q^sE^TthD_I>iS7xc0}D&rjF_RQAt7Mew?j6^As@TG>$Hf@HR zlYy{gRjQCLz{}~taAnHNSwadRL;A*+{xkd->8EM7A63Me- z(Mds}!F9d9chi98)ELM5MSKnF*8j?@6DGx)(VUFt(T>=Y0yi%@-2(McEt25KOmrVFvsR70{`i z!Hc!twS5n92hTW@^rxYo0HkTM8Ih-1v)bckX{T~kDudw=V2sv)wzN#ixMtjMMNhfH z&Hx%9+4HmMGz^$&9#i+uQ$OF(yM}+94|_{CF-^=mL*SC4uQCd}!p+>uk`A9wA^yLK z3gvZRzz_vvzXI;*=wz#*(FtPKjzF((xAh8R<1Tb{GX;NuglR5rh=1-161YIrnAx;XxnjXBW#CI(y~CcY6JO$F5F zoSm9LPVwmrovRxD3T4WDVmx@R5w7SzHgQb+JQyty8D|7eU5g;4rLe(iiuP zohqqn371jVTK-Jnlw1M%>xkf0{Y&9-Z<)Z8w(=%O$px#6&9UU`47AOX*6nSf9$_h$u9mA8sz*FoiGt}#2 zn983p>q<=TVgB)6+r|g_jkUR|Nh?Oeo%27KSnRX2Wu{h8X>qn>P$dY6fwng_q-xHW z*B6_WoAxlo@176*ed*OVXSgDYnqR#p#5b(#S5cOV4f5a!aZzk@O(fz_kn)`pS{wc+ z-Wn_rDpSg*){}@eU#b9&%}lghQ>r|1c>Uur>{K;}eo{+iG+sXvg-F0Ls*j_@=xt?5 z-JzB5!w0t5cmDG8ucyV?D*tkvmsZmSY_DP`j};mH3fPHM%j`AG**ltPu1Jk|V@7=p zW5VZv?;hlObj^?|JEn^0R^3ZUMJE*Xg6 zW4T5f9Csjimfac3*Ew|C`+&aFLD((eJ!QFjthf9hyNAO`l`?)!x%Z(j^QH3rg+iFQ zxz}BBbs!kLkY`YW!u-1t0%Da?dO|Hc9{y@!$oy!0Q9b|*ZFbGxZfyP$*$J%lj%%A0 zB&6Au9je-fDguea6K5Akoea~nErKz_$YHdx-#5Z2 zIO4ki&zNNrZ0V_-zTCl;5fX-Bp9-AJ($&iym+=yoKe-k8cKu%7Y_5#r9i`GCia;Xa z2j1@^;9QvtpKG5*FIXijFEs)Ys9+id1Xf}C>53-YdjQ+_IU&)J_*kqd_XHfe!va?3 z{QNK(vEQn*335*ZIY#2*q-o2_|B3aU;np_Ra;NC|X8nm)<1>Ne++<+M^IrYp<>Py0 zS{<4@!u)OHvQoGh!y2R~QSr8?UQm9RFCYsj%8{t7te3gm=i>K-&VgdtFYr~l;DEG^ zXaERnI@4#rLZ6E^>W>xHUF>)V;+-k(b56(@VKO+gxpA%?811<2{*d$&FtevG0N#Pu zi%Uz&Qq8i#+)-`NBq;d*q!5{BTN}qt-tciDnWe@Kz6L(xg@^DH0m%;k9990*70Y2A zfiD1oleE&Y# z;zUPZZjdHd0r3}xWs@oXmlKeSS3(k6dOWky{dn<4G+dvQ*pQ)q^TL!|1&|rH0Y)Hw z(wGg4NW+e>SN~o+o`C)$%4XR=mS0!zU&$e3-y;39iyhd_VmK^ zhy%u*Ubt_Gs5x9eGZ6ONW$Oskx)jo21DOZD0y!`L*DrNYaKE&kgk4tCGAsGS;aE&Y zCSRlJtWvWhSQe9R7r5~~1E*v_6bs$B_TP&@ zES#I0O&xvi7sIh7C2l7?YnwHL`*`mE_d)3Q3OJ7|NUtdc~Gru7`SLuo{5UF!h5@u z_!}wLdv_Xw2Yq6my5@peDI2@ar14k@yI$+rNZ21QgsrSM!yWT>0(>vQp$j*Lpa&H7 zZsTZ=hf8@z5y_=8Gr#YTO&%2e%l0{MK8|TN3XV?6AM8G?IQ!s8=5|H4$enBlqPV0x z%QRtH>(i(dpW-wt$Y?c7IruzSh)Rs~8Hor)VJF~|`$;hezR+ROf)ok=ugYyuuu$oz zt#jkCO!Y&=_hJ9X1oG|~q_?L4J@;DR{rvs6w*?DGT@c(g0$j_cj=rIiYed?%aQ`Q% zlH1Mvxz-QHf-J~6eJtd zV%rb_46_$vyN(#zD`IrCR7u;|2zcQsW!CFBpDhhKboYtTGJ?3coN(8ZgC_cXiOgAf z@qVa;6dwN{roqPf)1zCKL+XHhcca@HqWSB-?j1*54-W4BS~qRlBPuz{>^f2pfkE2h zLW8aHRNRtfB~M@(`1_63kz{XSSR+qY82E4?S_oU`$Cbr%(<6oLX2v4GE10=ep~uOK z=WnjJ1>j6|`Foo;iFH6R%4#3xjxned{ziPm1|pi#6p%LzH=HBEmBrXF^ta};%V3yI zutT7P*#ZN&@c`FrsD07gQG&f@ln0q;;&{jPgM^F>F((x^aJ5b*gSybtOaJ->T86y_ z%mLOj@jz0Hz%{yH@@o6j`UQ%|Za~kFm z14Be$^G`7nKX3bC3ErML*#Z9Z5X(@WLrF;cfxEojBk{$=4ERXFtRIh!c5knOQvv8H zc)qNSIDivN5@MZyH~7NHc- z$babx3Jq~orOE*PrvB%4@K`!GvvF{)#wBS>sYo`Rxj-nED_2-MxtZ11KdjLhb(!9A z)Bz2#mY0LI2zV+EuML4tq_(BcF|hRq3ZR;r85Fm?MBxSoN?nnU11r9vy2vPJjAsr#HDkG~W8triyJJ@0n7wYa58 zE-GsKM_O=`zsoIDN@BOp9}eU*_V;%iHGEUg1_5@E#qmidZCX6llR!@^CM`z{Y`T+s zWDG$Nk^f^F78)|$lurTy0bzKohSZ!Ga)E~c{GwIGvC-09pM#Stw#czbi)LItc^;iY z6l!AVAD{=69g0QEdYPWy&HB^8KJgutweM^kKU59ieQm|`DT*@PYVRBT^h(b(y6$7? z&0woSeK9s7HN%NHML#`4H}+jc|DB>Tg@8RvsFotKiW;94=va_K-UZJXp4-YXG>6()i zm3%2}Pio1``u_0ue?@I1hv>n0pmM?@8YC%U@F+^fp*6m2x38K*83;}T$kV!_)wrrP zqtm~X6j|od7w9Zu_IaAxGmjM#qth}^H^|-&jW9mTAm+D?@kANZ+3W&z$rilND0S@W z{o>7hAsL@^EYKEuXN1M2i>GX}vW z9C`++Os*Ag7vl<_2m-M z`O7nv^lkXLi(r!f3kPj`J`QIjn^w580sV4wzm%)S(}0at>S=c0zIpL zZh>4`h!C(J_jM3jdqCSD6XAgPwU@Vd0QB#Cl4+pZRyG64489 zNH@U!bFzF69_z*EANQcUs(^Mm85!a-@N+i?IvR zsmR@t@;XXK)W7a#9P*zjtd)BA{hJHGvjK_WqYAk)g1ymX2)WG#_YR=eU*qJ?RIh2E z^a4}q!5(}1#(d*tKnl*ieDprgKWrnuRFgnB6t733<}#4t{Y4&691#9*K*N z6frGV0--JMJH+B4ydz3z3G{j|SY9Gqfs11|>&5$;4dw=c`|dG`aZ>!6P9++OrrpJG;6R z4Fm>H!voPO3JuYUlRx~t$8}g)(tgqV5szd1R8steCp#}VA%4<Spnk-+U_850 z3;zWIqBPbmo=}NAX?C?uZm22W2$)RE(+pf8oahyC%ICrt_?B{py5LudR6wn-ZSeR< z>;5hBqs*84XwC|o(%q#{Mu|eJ;~A6cB3&aMOFB@IVx2~bq{`9xDryx$!VK0zsV8J@S;@pvGH8jc~SHWLJ^R!WQsU$EAE$wNQA z-rGp0v*=aK6x|N1^k6O?^UKThK}-9L?^rV6B1g@8Gj64wrI(S>pT0YYU2u4z?3Z?@ z=f-@wFnD&24z@>w*}ah4xZH8RqJLGEww_4D4S*y_An$8KSho=(mxozH0;p+uiExh9 zTEf&NbcuJNH69AP=s$`yz+JUp2na?v`?ZMa+4(cr(%0YOLbEXypX!Rma`yX(Bz5ZD z3(-Aeml9ph#@LzDSAl*&@M0x2%*IiJt18nKU-z?$wDCwjdgdhLM){1~%oeiflRHc0 zel%??&_@3q-6T&&5NCOEH-wCN%~rezP?ln_+QygprED%XQ1TO}*-5=;3+3s5jUMGy zs?qVwj;F;@+!2*|eeg**<$QJ%dPR4QN! z2f+q|NP@zOQlM4O1^!H9k7D!;E#>wg1l!Z{fMB{ zU8?B74V$(iK#(T;O1!>3${m)+9ce&6N5m-FaiZlJdw(kLmz}mvcM( zM#X)ReOuh-B8?~hd#75c6DlTO_56s%W-CR$X6fMIDdgTpKUb&3EG=sz!-V*BVF_~#U=LY(eUL{zy9YF4m?}@Q ztvCqHs@fb^MZqnKFTY9}ZE{Ef%C;7)gq7_tH~%$|MB70?OYJYu;+&Q863zT7z_*9H zhS7IbEbOm!q2W8tvD?>R%Na5>*T|v1EnU8vAym>N&9cobBZW4mtCv`lx>h-7w7dY1 z+5JlhOJ0ZU&9m;tn=xD!{-2Sv9-GJ86mq?!q)GSDPqMs9v-*0MDjcO?$fC5{1 zbsBP@^A@a8+p;OuuV3VLAuWH0`qL)gXAF(6x{Q}EbUw=RDWV@-izV3B)?)3b(VtAt z<sL=yC@+8~`YaL(`|hE&klt@At)&RAH}Jtu;14Oe$i zoC}nOKN_`kRDiigW@ah`!;BAart8j6FSL?`etTYT{535Q4@1z-nbLJy|FW(^p>pMX zfj$br|4o_7T3Wg?H88goGrRhzUs0J_p{kx4_zgSzM$s~w4KxD*LdzT5n!x!y6e?I* z`?B9a2Z}gJ2nDokO8lRDM8G8UKuS{JC`fI8=q_s4-cxS~14LmahoZ?Hp*(|@)y|zA zx4z?cZCZIUgC#DP{oTxcrHe7?2A3t+P1o}1jW=hG)dn}OqgvA$1@L+X+_xQ>DX+v=5?w;mCIh)QiMzmoFyBF!WAl_ErmH?Hc zRXG)r(YqYGq*c8fusg>5+{1ZyVud2srU{9CN-o>bFsD=*>Gm1!WVBNvn({j9>0fL~ z1J45*>ysIq_v@SVhF&+%F$2-B>xQz`UX?gzT?pj+CG3eI;dtl2db1%HV)d4kvLost*@`%WPJv96;pJogZ|UushErsQ4Cwu$lS)!+Bfo}*^4b_z zj(>g=#;rgu_Py@c5Ua+Op^4&H0|6`bI@nq!52nd-w!1!|d@$U=S_z`9z*=nZQl7?s zFvsUBGes0;)ECd-+}F}Thu`VCh)ZjNVp#gjQei%j8PL#5dq^>vY4*gfTs+;*`H)O8nO&VFG^8QXzVehxb_l9@5wF?bB(@v~5IE~w<* z$$SVt4bUhPO>2*%r^7bF%SoxS$rq*rV5gJrkH`9^?a}{xkBZC=sr?4_TQ%-h)PL~T zLdIR&MNg<(mt<>IkJDBVob9WXrC5?7$- zVREC5C8Rj8T>a6QNbfDyw@lC&z0G3FBQA6PFNoxGbL9Q&Q>fU3I#~AXm(~9XH2Hs} zk%zvzgPCODEC+NnE5Gyc%KTC1)dvgv=rJKR-avi&7eRX^oUSbg4yBPwd`Aw{cuK@kU=C5|PzXOO} z$!15R>}XD7EI?W?-c#c7)|q}3Y_anwLb7?X2@4tH2{GPqt%Iw1cx1H@O%ON;Ub^6o zgKpB?-Fb6*KgYTj;r=fmTgefg3J}t8s%rB7+7sN;q>*RFc(K|UFhBwSw(ka+r&fo>D?U|n7~D9yBlv`RKx#22Yl&IW}|Bu9WU5lRuO{mwdmJ3 zql=-O-t;=*nlInJ&gI$@nZCvylGO%Uf7CiR*GAI;3(%T9D_4)gEJO;PW>By9B|PX2 z8KHLEPu?B!zeWkMqTV=+9&B|aw^%LO@27XpxYbkm152Oe$7O|H?C3oyMz|y#-U8BF z&fBhw=l!EHPN84k*dB8}R+}X&p1@=Ip)<-QFyWl&`%dS6wE{k2 zQT5FiI)(YSF~>KNn;rUnwL^X|YQl#0ng2HPWZ;a){5zV(M z3Xxk=^WyUIUr;y~*uNijW%w5eeyn%>$J1ph`H!JVTsp$^)%5>E*Lz1b`855*6akSY zB2Ai#D2N6SX$C}4KtSn8Clu)=AiX3s0qKGwO}aGc(gP&)pmgaaRH-4f5LyU%bKg(- zeZ8Oao|BV*a%Hcb*(slyo!v1k_sGsxdE?I}-Mr#l7bd7l+Uygmq6$d9!o|h)`L1s7 zmgm{REw8%>3E}H!o1MSJhIMW{oZg(C(l7f^KF(D@YUk%v<>B5n@uTFKx@{q~ij&Ki z+Ud8pv)a{GC357$-^G8{mlqF(HnAW7&`c<Q2kfu)lnZZ4@)z+J9JS#%M*( zVkF+t>~@;QXc;&+p8WscKwND2UH7Lar#OevNv3yILaxd|@lq>9v&YflPWg$~u(ES< zVR&UT?%Lrsb8{=<4-e}ZP%mt*er%$o@Z%C}e}7BhdfLq!*L0E;?mySpR(*}fTfZJM z4!}U}lV9Jva4&Go_S#)H8oF^@(Q0V7aHM{IG2G{dmG4)67|g%~WdPxg?%4*d1#*iy z3LEdVFvJLFci@Oc%q+jiyaAmFa8K4Ruk5?1_%be+nZ(ZS5an)M>?@l-a(ZdYY&|PJ zxg;z<6^wu#HA`C~s@TXp0qtHv2(pKtxZE#Pe=SfB@N5_Y$+IWv1oW-1Opk`Mftdx? zzusObgsQ|Vb_&^99GwW+OWRUFiD9RWTirtTvS7en#qm=#(kv=to$ozE-xrI0v&p3r z2%v4<_I2AIemkp<45mdVV&m0Jq-ikLF1}-eeYeL&b7EO{fdII1+}CIK=nVwHOlN*0 ziuTj`a_pzOc^=a)h9_^S)`^S$kK<{cq?kvFm^ahw4mOc_+|#e}BKq8HX}=sUD4hCRq||5;{CmT7oE5V30S%X`2Cc#a&bOBDr8 z>N+J)34kkIznIi7p9D#>>h?Ug7!2XZgf58?E$ibPI>vO}dQ@4e%QxlSW;1k$ zhZ(qi&rDy
    ~sjaYi17q+IS)rn;ORSz2AT*2gr_tFrSKaaWG@$b>@1l7!u)PLT^ z*WChl0G{==<-pnnlYgC=J-E20mQGP{XM_%*mH_Z;0gmOkzL>vFSDRI1ul>EfO7`Km zLf5vK2V`2}Z<*MPsxrh{H62d&UM;S&l~Dy}mR36wJo~>I#YA-4=3!Uaa9Lkm9{O4m zE^)h0z03jA?!fC<>`cAn08b%|YurB6fTkm>$7QA;nn7&w3t!{u;6Y-8(yoka(5**- z$9ZXn5G2Z;xu_rh5`Qvv+IWyBvvH2QhTBPk3pC>GNXNki7Pnv%GS2<%awJ6A=TL9_4Cu4bXPRE8q_x%qOz;%xG8 zdg+79+{})l)1W5a6o?~Wja!_P--NHN1Eh)UmT$ba9`tJRRBG^<%?QTV*W}^d{;y%1 z*1@^>fv&GBwpJY$7C$rTs!#b-mYP8_lpgUJVd8L4IrzoMKqKZ2QMj)q@YQmoxQkiV zesbl?IYDw!YN)g!Q}1cuT>ySTzAYKM=7?aoIEpW-8k@Z@#l;?EaGdRDu)MtTWTaN_ zIIHg1dZ|)n=^PyMBe0G<6Z`}n(!)qL74T%O|7&P2N7yEE;3uexBTyD_0lTlevdV6K zG{Z(Oqlt7o6KFjD#6NQI;frkA7hTHe*!V2_$?}8PqaC0_H zijH=JU58RBmPVyeroQ=6_u-dhkrEb#enEZFDb@bFX{j>2ZBC^lzooAD=F`4LOc2w7 z+MLr-`UsXv26^YZH@W}`x5VwN{O#z>#VY&T1!Gaj%~Vllmj3;J{cAEsE;8P za;IvW1n}hy3BRacrFykAe0uvw#n$>@&BlIu<9EGWzPBbfaiCX&TAc|>oUwhCD$&1|FwB&KH@KtD@vk&^f+?!a3uJZrgeKN67nkg3{An#2lu?{(l zIT_itTmDhmzdyLYk8ZHT-FN0NQn0JBZ;%SsHjEp0`j%<=@J;2V8}V5W;aBMFPkLYF zoKjU=u+`>U_H#6Bgwk>#NJUZQ=GWf<-#)r=RN6hmo-us5=yEdR<&#g9xgomyf+DyT zY4zEDmX_r%8RQq-4rA=hD^FL`1LUFU?_!o54(Wks&~t|0x?A{2i!SF)m!XN&7_UM3 zuH261zoGYkeSK_*fgE3%0~$UuPX8{#JbIhm_~lc6F1IcySCyj|ld-vJ#4VMjvr&1ZOHQ|^`F9sy}PWOOCtvXl&luGq&{k5(Ihz1{_zOR<__vS?hy1al_-=w zjHf&mnSxk=JcHJgrM;xJ(qwr)g|U7Xx|bTBuD*u|I?$bS#|os%EncCjuLo0kyLLEM zqMnE?eQQdE`}j(dId@OETqshIS&RwBZc|39nZ_|Bm_?mtXC^r98i#%v2ooi|Puok$ zik_Z404`l}wne%W0s({q9S-Mkq0i$p7OOGxS2 zj96!Sonf392PdSMPt@y4q4h&!%nm!*RD^z|b7!iUHF<3NAIeCJPHGXFWEDU9o)5BX zrk(;7=My%mrw0_K?gnmf8~VH3k+Fy@2`4ko?>&c%TCQy%h~pE8l^wcCL*X=u_xj<2b9Cksrw%Y*W-x>m zQ?bYgiMyT;+BEt?`PX=#+APMn6+R$cUma$s)j2qB=rp^AiiRxemG6=t7F7o%Tsnh5 zN>BVB|5u{q-}`R8S&4;b0>@{DI~Ll+vE}Gj>xy(jZx8zO#r~|~tL+J|9&@i1t(@Yk z8}+{>WW?y&RrH|bjbaDR^smjD!)ZZs$1#Q{K)aX;eD$6|#yDQoe`2}bgqJ!#BW8sr z#^Zp)Pj+o^E*KB?Gh81Db3wqV8W4|o?7rr`p}Mg)*qsVmh|e&e0BcW|V5Lv#`)RQp zkGMc*zqy^^DJDQ6S$(sDo)1%x^ z9HiqAC!!k{$t(j7(^6Xxv)#?D`ELy2Z}4)=eW(m-588eClvGOE?8Uooc@gL2em;qa z*Bx+8rUbM#T)00mhKuvX#l_iwJsz^Z;dQGbi!UfV5R;g)xP|S>*#uR#iA+Y`4`u>= zC|ytJ0K>}(W}Z@o{ow1n&YaI)X@6N#StAG^Ed(uw?>&C*_h^tf$GvDEwN!8TH9*bp z5#WR*;s@vYsX-Od#29&0>1eq z2VYnxME%$94@n*WP$~ogjd>esnc$6-a?kHBPSp$|f9j%x2Q-%>xU!#_xcL)k*_Zn6 zrHhn+k)I}mm|W~kLu&3(x(q$+dL3ild9iL9;(;Tw&tELJHf|z79r0ODUP-TUeqY(R(3{tHHXBaky{_#ef(U%pvwOSiHt-BX;Wb-d3Z+jtE=G@EbA8T)IN^OgiFJ+EFW<^3?4d>zkUJ6iYibqZdMTl<4` zE9mE2PhMyKdG$FU!nL7cREq2~jlKikN8kvR?+8H*;aUE7y?bdwypGMovR%@`=tN}c zRN?|C6V4|)?TLHUsZabV7}HLfx#jTogk90kZwO41@pz8-`Jj)FcKmr0WKBB~<5;SW z3QOHbx0X0&N2BK*_>iO{a%HXTlXEaykBg&Sw~1}Yeh)t}JQ8@rZF?+Nl2~Dru!wMq z?*d80D$laWcQJ@;$9TxgGi9Ij!tGm$Nc-wzbctoH@e98lqgsX;CR{+> z()3Hn^)I`cZSjLn&(4mIeKB1{1Ht-E<=l(1CcAAS0ZKbUX{7H;OB0l#?{u@*nOP3JZ%j% zCD{NG`#6Fx2oRqsP2btW9a}^6XM<(=a|@${d|k|h3wb<6$#T3#86O8OJ9En_X9oQe zTdB4^y-(IwcrUxliJ@p^`+1Mm)Q5fPBk>~Z^JwJ$wl|r5ccBY|G_EX6%)3i8R_@Pr zZXlGw__I(|IRj?p2&rep%Uo1Y{B$_n2I0_tqAQ}c$|=T+gsxqW1w*L^2xL{%nhRbc zyyhc}f5ud_PcQVk=G(32hw_F*e+H`1{lx~I-X4aj$M5blR`{rXy1Sb&KRIdnNtVgX z_sl?D?A*F&*l2pU!(yWw_Vv}9mkXVux`|0?rVre^nrT)APx^qsQK?*=_9 zIbmZ(5ePU1mQmc8{Y~rd=zW%ZCE~>42nU+)RGehbP^p3L)rCmTy{hPr3=^W#+BXCFI0NHRb zT?GLE4?M5>dL{^1$c`r2xVJYxQaD^W)nNnF{;%QrH=GocbWt`bnFvzpovJS0O`#>5 zcp8c{;d1-6Z$(9sQFV}Pye;1yuo7({F4;g{H?Mmp#$mER$3f&Wuv*O_+%gZb+H{07 z9M=wZ$atX1ofT*KLT+lsqG&c4td-c)!3Y|zcxjq@8Ez^hUU;<@%bx}I1EJF9tystN zleCJq3S$WU^G?f^{Vn2LOfrVde|B&CA(PcAZ-wH?Dt&U%=4Ml8_d0c?o6M$SF<~k=n(L;|H^0J{^(EYpdgyleP z8w0s5E%`j^jQOkT`~@is~A+`V-q{YWNTOo z{UL|mM^@W&F>c)w3*A1IFbej8Ed?k1omanCjj(g0hFFy)5#`#zAhkIS)~2w6&)E1c zIvqTJHJMd>`IT8^X{w}hxmpQ5Oh=YE(0E+(TKc@08lpJp%0^cDrqvc?#b?J6pDxE* z`Sj@K1Wpkn*|e+b{A643G1)WX{W5=-8;mkqspm_~rueWFw++%oEJA$OHpv*D+OqjW zu~)Okn+=Wm{H_iJ40UN+AFeA+E=Qy=b*GRnXzZb>hMx`VH=Y#@Y)}MWc+28@Yc|p! z3)OroG=|hZJ(4rU)!2>9Gm)7Y`VFm2d3L#L)%}n1Cr;b02nv$a z-{wZQf^1lLn{3IA<@e3{<+&Zjmf07O9czHGF9E~iwrqfwJ?B?Ec@Ux_Jpi#`n9MG@ zl<361xIgflFBqcHV?*@3(nDbnS*AWeNqm#Two#H6F^YK1huopG!16a9%^;c6C$c>` zFIH=-pRM1R3mAQE2y0@igP65->AEc~WX7t__ZdD`!i_9M-vghE@dok~KW7)+_cT51 zW_d&hWHjO%|9`%jnt?Y~rzut^X+tUbMNoL^42lAavy6JOsQ4c?FwU%XgGX-#artRN zT*BfOXl$tuBzFt1@ri@bx`C-wh;xHGjEpzBREn(dX>fm}Q4sO>8PBoRN_|S^-#i@t zWQ^h4M|;w@#WQboT^F1J~d=J!kwW4$=4SRBL8k7qW{xG?}K()^e19= zM99M*WY2_}1+@bfe@YXE$pfx#FWsgi8>4qiq|)Ew*U36&+Ce>7<9VQ($7Zx08sqDu zo^)q!2KTSx3fO#jWawP1uvhAprX9Uwlu#c^Z`gWXv*Dr92mLjsxtdU((^D} zkr}n&Tf^XDGE=pa1+s8Y`IrTIuY^vXn9zoh+iZHp*`L$s+y2EzKIcD9nDI5%y3Xz$ z&ymuHkK*Vae*4(E-jB3N0Y&sxG*LAWQS=rAD~2;yELKVl;YT)Q3l*B%PMxJps935= zKwD@A+tRq?(l?C$1DFFjn`v{~ENfVOLF4HRRb%BU!vnYjxhqtevrb|m3SDUyjNWxr zHfe0r`KLhln>sSxO;vcL1pNM!JYVxauvfrKmXqa{}&8zRn&R3Ywo^l zK^~kmk6QMwFAjUZYk#X*QwoMBYuSIqCO7xq07z!Rv(r+{3~F>`p75%(Ms?Edg=-R7 z*nG&Qeq2t0p3ybJH(!%s450}mvf>R!4)<=)or?q|pn1(gSGjz0^EZKwi@&+5?N2i{ zpiBOyXS>2L=Bon%-FXj}um4P4aX-J+_B)a-mw^n%w!+TjWAU4WtOv8kB9u}fH$aWK zv|tK*&;?x}HWY!(L>KTQVL*6rPYj#I2+X^-(&RlAJLE>n({Jo1#uX|n3xO;#6O?UF z9-K{_p3J?jq~`r6#qV3}qFTbJRP%f)FTu>+mPpb6pMkrrqboD&`^Q8$cdGx5wQcsV zV}X3odRxIYb9W%rx4U7#GaD3!kg)}A&rC7IO7kv|Vsp2?AoH+308tVSUAtp3Ap8-{ z5s;0Qll5H}*>ClLxi17gR7UJR)XN7=Tq zB1SwoP1RqB#k)dSpRYr?8i=zE`(h6h7Sw}h$Ue8})rC$? zR|JE+jON;34PySZCJ-6fKH0{zl&8f#G_}7K?pX^!CatF|?yF?%_l$Y$f7;ECaoe;o zQx|_CO3Rm}txfAyBT9^}T&^u)&6$hy8t_^Ej&OOg;=tFv4o!#5{v0dmS@CbFXXEhS zpwB?oKA%&!_;LEGJMx(F$rnWQh7$%4)0RJe+u!z1c6=EOi8PKZ1Yoa{Hkvq>(ue5?Dn~{)yDl%zV3U2 zXWSQ?i1a&BzuvJ>B6``;lC1vt@TF#pmLh{P`HXZL8o&P9Vwh zc_hU~TgDpX;f6BOn~?PhkBCwas^$I!XrU&$<15|zh+Kftml0M|S~=DMk*_y*+>q0O z!DbM$Bi`s%vm{M|gEXGQ!mn?@@W=I3_vN3(DgLvNleGdVtJ80%jcfAIC=g-&yk9ZN zWkH3JoWxyQoJ#aN1I-~D%5x38sb0T- z`bVn9URnDpO6P0E%;weK_S2))hAk(bl80=oXt1sJ?8=1P{^tf!9U)3X;+2V7ubhCc zed#80Lm%Q$Rbw54ixb;Y4}Ovy8AyL`lDrRm#;pN9XZ-S}&~hRzyWL^*J^x>(br;gb z$W|94zzj6Z(a~*12xzN|k;ITP-<1(?u}yEW8P_NiDDZ2e#%(~t&T_YKck#SO`71H= z?lxrbcrPx&Ky>r#X{yOWESgI`Iw1?n3bX6K5({A@HiK8n_R((ZHtYHOU~m`QV@+DD zcb$$nczJ0!?qUG!aFc2v7FB*$7A3%5FG0^;Roufea8YH zJ#m;j(UBX3odrlcyGf_{D}B@ah*u2=3y(q)ZiJof0)9H_nd;a}c(-s#_aTTa2ha@qw`)L$lbwWbMB zCF$gWhAH-Y{!ifZek1(LrGKFTK=Q{s0^eS0Utu#nr-YI!rvDoCaxf`+I=uU35B0F* zLZkZe>VIG(#mk3i!xo>a)NwY#4>oMJn(iiP(f%G{qP^C@Rwk`zlx1#`{pgCNnrZMkD!M&2{S;dzxkCh)w$%RRnz(yjkD8G7g9my@h%KvLAD#-i{ zR-aG#Tr6)#lXJ@W0lb`+M^`>dJmEW&(qHuLikmOg@(?6w|CK8;1(YhW9uHV*|9-q0 z&z6K_dqCJ#sV)qqk;Rfam2 zXgm`CV((KyQg9#Iwe9?Ki}u>*%9ev)!p~LSlOa_4JWx}+{W8+_FL|~icEVIwRPyff z#4hDW=Ce5>)%(QNtDRRJYh6p*i>vcR8!zR!P-Xdg(>Vr!x;=*Au{a)nG513gL)_st zS;V`ClHN?6??;~Jl?I}~wTo$XDVE*Bk z#n!KCE;dcpFO?6L+oOKRv>x-t!QdFfW@p+jEw$Is3sX_8M?5jPwl(StY-PUwv{|60 z8|6Iwi4{RtL|kXN@ChZtgNT36iC;|)(TN=*FapsAZ%q3&(M!13{m(=1=H({hg^`WI5>9&eOxO>ys0f3sL;Tb^c;IL5rNu z#k1_6dT^F2*qD*p!662+Gs(wG7s!-+>q>VHsfyS*gx_T*xn`(#_9|WCiKSofE#c_n4!5;nc!f{CXU)mZD`J%$t@mv0lsKfG|*r}!~2~YRvjmhiO z*Fz#lTJl5PS9)@PRMQTXO@}^oG1EVov@xEHEvP)S>}nL_5aXD-SYxZjkOeb7mJgKA za-dSy{QBPAzFQpO*x|1&`8Q)IZ|_>Dj$Rj z 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 Hw&DK;|2N!3 literal 0 HcmV?d00001 diff --git a/PROXIMOS_PASSOS.md b/PROXIMOS_PASSOS.md index 54bc7c5..a7f1653 100644 --- a/PROXIMOS_PASSOS.md +++ b/PROXIMOS_PASSOS.md @@ -7,8 +7,8 @@ Lista priorizada de evoluções propostas para o Sistema de Chamados. Confira `a - [x] Criar perfil **Gestor da Empresa (cliente)** com permissões específicas - [x] Ver todos os chamados da sua empresa - [x] Acessar relatórios e dashboards resumidos - - [ ] Exportar relatórios em PDF ou CSV -- [x] Manter perfis: Administrador, Técnico, Gestor da Empresa, Usuário Final + - [x] Exportar relatórios em PDF ou CSV +- [x] Manter perfis: Administrador, Gestor, Agente e Colaborador --- @@ -34,19 +34,19 @@ Lista priorizada de evoluções propostas para o Sistema de Chamados. Confira `a - [ ] Criar **relatório de horas por cliente (CSV/Dashboard)** - [ ] Separar por atendimento interno e externo - [ ] Filtrar por período (dia, semana, mês) -- [ ] Permitir exportar relatórios completos (CSV ou PDF) +- [x] Permitir exportar relatórios completos (CSV ou PDF) --- # ⏱️ Controle de tempo e contratos -- [ ] Adicionar botão **Play interno** (atendimento remoto) -- [ ] Adicionar botão **Play externo** (atendimento presencial) -- [ ] Separar contagem de horas por tipo (interno/externo) +- [x] Adicionar botão **Play interno** (atendimento remoto) +- [x] Adicionar botão **Play externo** (atendimento presencial) +- [x] Separar contagem de horas por tipo (interno/externo) - [ ] Exibir e somar **horas gastas por cliente** (com base no tipo) - [ ] Incluir no cadastro: - [ ] Horas contratadas por mês - - [ ] Tipo de cliente: mensalista ou avulso + - [x] Tipo de cliente: mensalista ou avulso - [ ] Enviar alerta automático por e-mail quando atingir limite de horas --- diff --git a/convex/migrations.ts b/convex/migrations.ts index cb7274a..5a27e1e 100644 --- a/convex/migrations.ts +++ b/convex/migrations.ts @@ -6,7 +6,7 @@ import type { MutationCtx, QueryCtx } from "./_generated/server" const SECRET = process.env.CONVEX_SYNC_SECRET ?? "dev-sync-secret" -const VALID_ROLES = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR", "CUSTOMER"]) +const VALID_ROLES = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]) const INTERNAL_STAFF_ROLES = new Set(["ADMIN", "AGENT", "COLLABORATOR"]) function normalizeEmail(value: string) { @@ -30,6 +30,7 @@ type ImportedQueue = { type ImportedCompany = { slug: string name: string + isAvulso?: boolean | null cnpj?: string | null domain?: string | null phone?: string | null @@ -43,6 +44,8 @@ function normalizeRole(role: string | null | undefined) { if (!role) return "AGENT" const normalized = role.toUpperCase() if (VALID_ROLES.has(normalized)) return normalized + // map legacy CUSTOMER to MANAGER + if (normalized === "CUSTOMER") return "MANAGER" return "AGENT" } @@ -182,6 +185,7 @@ async function ensureCompany( tenantId, name: data.name, slug, + isAvulso: data.isAvulso ?? undefined, cnpj: data.cnpj ?? undefined, domain: data.domain ?? undefined, phone: data.phone ?? undefined, @@ -195,6 +199,7 @@ async function ensureCompany( if (existing) { const needsPatch = existing.name !== payload.name || + (existing as any).isAvulso !== (payload.isAvulso ?? (existing as any).isAvulso) || existing.cnpj !== (payload.cnpj ?? undefined) || existing.domain !== (payload.domain ?? undefined) || existing.phone !== (payload.phone ?? undefined) || @@ -203,6 +208,7 @@ async function ensureCompany( if (needsPatch) { await ctx.db.patch(existing._id, { name: payload.name, + isAvulso: payload.isAvulso, cnpj: payload.cnpj, domain: payload.domain, phone: payload.phone, @@ -344,6 +350,7 @@ export const exportTenantSnapshot = query({ companies: companies.map((company) => ({ slug: company.slug, name: company.name, + isAvulso: (company as any).isAvulso ?? false, cnpj: company.cnpj ?? null, domain: company.domain ?? null, phone: company.phone ?? null, diff --git a/convex/queues.ts b/convex/queues.ts index 9b6c2f9..4705d3c 100644 --- a/convex/queues.ts +++ b/convex/queues.ts @@ -5,7 +5,7 @@ import type { Id } from "./_generated/dataModel"; import { requireAdmin, requireStaff } from "./rbac"; -type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED" | "CLOSED"; +type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED"; const STATUS_NORMALIZE_MAP: Record = { NEW: "PENDING", @@ -15,7 +15,7 @@ const STATUS_NORMALIZE_MAP: Record = { ON_HOLD: "PAUSED", PAUSED: "PAUSED", RESOLVED: "RESOLVED", - CLOSED: "CLOSED", + CLOSED: "RESOLVED", }; function normalizeStatus(status: string | null | undefined): TicketStatusNormalized { @@ -123,7 +123,7 @@ export const summary = query({ }).length; const open = pending.filter((t) => { const status = normalizeStatus(t.status); - return status !== "RESOLVED" && status !== "CLOSED"; + return status !== "RESOLVED"; }).length; const breached = 0; return { id: qItem._id, name: renameQueueString(qItem.name), pending: open, waiting, breached }; diff --git a/convex/rbac.ts b/convex/rbac.ts index f729a6f..89fb981 100644 --- a/convex/rbac.ts +++ b/convex/rbac.ts @@ -4,7 +4,6 @@ import type { Id } from "./_generated/dataModel" import type { MutationCtx, QueryCtx } from "./_generated/server" const STAFF_ROLES = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]) -const CUSTOMER_ROLE = "CUSTOMER" const MANAGER_ROLE = "MANAGER" type Ctx = QueryCtx | MutationCtx @@ -45,13 +44,7 @@ export async function requireAdmin(ctx: Ctx, userId: Id<"users">, tenantId?: str return result } -export async function requireCustomer(ctx: Ctx, userId: Id<"users">, tenantId?: string) { - const result = await requireUser(ctx, userId, tenantId) - if (result.role !== CUSTOMER_ROLE) { - throw new ConvexError("Acesso restrito ao portal do cliente") - } - return result -} +// removed customer role; use requireCompanyManager or requireStaff as appropriate export async function requireCompanyManager(ctx: Ctx, userId: Id<"users">, tenantId?: string) { const result = await requireUser(ctx, userId, tenantId) diff --git a/convex/reports.ts b/convex/reports.ts index 4d25bd9..924830e 100644 --- a/convex/reports.ts +++ b/convex/reports.ts @@ -5,7 +5,7 @@ import type { Doc, Id } from "./_generated/dataModel"; import { requireStaff } from "./rbac"; -type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED" | "CLOSED"; +type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED"; const STATUS_NORMALIZE_MAP: Record = { NEW: "PENDING", @@ -15,7 +15,7 @@ const STATUS_NORMALIZE_MAP: Record = { ON_HOLD: "PAUSED", PAUSED: "PAUSED", RESOLVED: "RESOLVED", - CLOSED: "CLOSED", + CLOSED: "RESOLVED", }; function normalizeStatus(status: string | null | undefined): TicketStatusNormalized { @@ -128,7 +128,7 @@ function formatDateKey(timestamp: number) { } export const slaOverview = query({ - args: { tenantId: v.string(), viewerId: v.id("users") }, + args: { tenantId: v.string(), viewerId: v.id("users"), range: v.optional(v.string()) }, handler: async (ctx, { tenantId, viewerId }) => { const viewer = await requireStaff(ctx, viewerId, tenantId); const tickets = await fetchScopedTickets(ctx, tenantId, viewer); @@ -138,7 +138,7 @@ export const slaOverview = query({ const openTickets = tickets.filter((ticket) => OPEN_STATUSES.has(normalizeStatus(ticket.status))); const resolvedTickets = tickets.filter((ticket) => { const status = normalizeStatus(ticket.status); - return status === "RESOLVED" || status === "CLOSED"; + return status === "RESOLVED"; }); const overdueTickets = openTickets.filter((ticket) => ticket.dueAt && ticket.dueAt < now); @@ -179,11 +179,17 @@ export const slaOverview = query({ }); export const csatOverview = query({ - args: { tenantId: v.string(), viewerId: v.id("users") }, - handler: async (ctx, { tenantId, viewerId }) => { + args: { tenantId: v.string(), viewerId: v.id("users"), range: v.optional(v.string()) }, + handler: async (ctx, { tenantId, viewerId, range }) => { const viewer = await requireStaff(ctx, viewerId, tenantId); const tickets = await fetchScopedTickets(ctx, tenantId, viewer); - const surveys = await collectCsatSurveys(ctx, tickets); + const surveysAll = await collectCsatSurveys(ctx, tickets); + const days = range === "7d" ? 7 : range === "30d" ? 30 : 90; + const end = new Date(); + end.setUTCHours(0, 0, 0, 0); + const endMs = end.getTime() + ONE_DAY_MS; + const startMs = endMs - days * ONE_DAY_MS; + const surveys = surveysAll.filter((s) => s.receivedAt >= startMs && s.receivedAt < endMs); const averageScore = average(surveys.map((item) => item.score)); const distribution = [1, 2, 3, 4, 5].map((score) => ({ @@ -205,28 +211,37 @@ export const csatOverview = query({ score: item.score, receivedAt: item.receivedAt, })), + rangeDays: days, }; }, }); export const backlogOverview = query({ - args: { tenantId: v.string(), viewerId: v.id("users") }, - handler: async (ctx, { tenantId, viewerId }) => { + args: { tenantId: v.string(), viewerId: v.id("users"), range: v.optional(v.string()) }, + handler: async (ctx, { tenantId, viewerId, range }) => { const viewer = await requireStaff(ctx, viewerId, tenantId); const tickets = await fetchScopedTickets(ctx, tenantId, viewer); - const statusCounts = tickets.reduce>((acc, ticket) => { + // Optional range filter (createdAt) for reporting purposes + const days = range === "7d" ? 7 : range === "30d" ? 30 : 90; + const end = new Date(); + end.setUTCHours(0, 0, 0, 0); + const endMs = end.getTime() + ONE_DAY_MS; + const startMs = endMs - days * ONE_DAY_MS; + const inRange = tickets.filter((t) => t.createdAt >= startMs && t.createdAt < endMs); + + const statusCounts = inRange.reduce>((acc, ticket) => { const status = normalizeStatus(ticket.status); acc[status] = (acc[status] ?? 0) + 1; return acc; }, {} as Record); - const priorityCounts = tickets.reduce>((acc, ticket) => { + const priorityCounts = inRange.reduce>((acc, ticket) => { acc[ticket.priority] = (acc[ticket.priority] ?? 0) + 1; return acc; }, {}); - const openTickets = tickets.filter((ticket) => OPEN_STATUSES.has(normalizeStatus(ticket.status))); + const openTickets = inRange.filter((ticket) => OPEN_STATUSES.has(normalizeStatus(ticket.status))); const queueMap = new Map(); for (const ticket of openTickets) { @@ -245,6 +260,7 @@ export const backlogOverview = query({ } return { + rangeDays: days, statusCounts, priorityCounts, queueCounts: Array.from(queueMap.entries()).map(([id, data]) => ({ diff --git a/convex/schema.ts b/convex/schema.ts index f7b9b43..03adc56 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -20,6 +20,7 @@ export default defineSchema({ tenantId: v.string(), name: v.string(), slug: v.string(), + isAvulso: v.optional(v.boolean()), cnpj: v.optional(v.string()), domain: v.optional(v.string()), phone: v.optional(v.string()), @@ -91,6 +92,8 @@ export default defineSchema({ ) ), totalWorkedMs: v.optional(v.number()), + internalWorkedMs: v.optional(v.number()), + externalWorkedMs: v.optional(v.number()), activeSessionId: v.optional(v.id("ticketWorkSessions")), }) .index("by_tenant_status", ["tenantId", "status"]) @@ -141,6 +144,7 @@ export default defineSchema({ ticketWorkSessions: defineTable({ ticketId: v.id("tickets"), agentId: v.id("users"), + workType: v.optional(v.string()), // INTERNAL | EXTERNAL startedAt: v.number(), stoppedAt: v.optional(v.number()), durationMs: v.optional(v.number()), diff --git a/convex/seed.ts b/convex/seed.ts index f29b202..9ae1b39 100644 --- a/convex/seed.ts +++ b/convex/seed.ts @@ -71,7 +71,7 @@ export const seedDemo = mutation({ function defaultAvatar(name: string, email: string, role: string) { const normalizedRole = role.toUpperCase(); - if (normalizedRole === "CUSTOMER" || normalizedRole === "MANAGER") { + if (normalizedRole === "MANAGER") { return `https://i.pravatar.cc/150?u=${encodeURIComponent(email)}`; } const first = name.split(" ")[0] ?? email; @@ -130,7 +130,7 @@ export const seedDemo = mutation({ avatarUrl?: string; }): Promise> { const normalizedEmail = params.email.trim().toLowerCase(); - const normalizedRole = (params.role ?? "CUSTOMER").toUpperCase(); + const normalizedRole = (params.role ?? "MANAGER").toUpperCase(); const desiredAvatar = params.avatarUrl ?? defaultAvatar(params.name, normalizedEmail, normalizedRole); const existing = await ctx.db .query("users") @@ -139,7 +139,7 @@ export const seedDemo = mutation({ if (existing) { const updates: Record = {}; if (existing.name !== params.name) updates.name = params.name; - if ((existing.role ?? "CUSTOMER") !== normalizedRole) updates.role = normalizedRole; + if ((existing.role ?? "MANAGER") !== normalizedRole) updates.role = normalizedRole; if ((existing.avatarUrl ?? undefined) !== desiredAvatar) updates.avatarUrl = desiredAvatar; if ((existing.companyId ?? undefined) !== (params.companyId ?? undefined)) updates.companyId = params.companyId ?? undefined; if (Object.keys(updates).length > 0) { @@ -217,13 +217,13 @@ export const seedDemo = mutation({ const joaoAtlasId = await ensureUser({ name: "João Pedro Ramos", email: "joao.ramos@atlasengenharia.com.br", - role: "CUSTOMER", + role: "MANAGER", companyId: atlasCompanyId, }); await ensureUser({ name: "Aline Rezende", email: "aline.rezende@atlasengenharia.com.br", - role: "CUSTOMER", + role: "MANAGER", companyId: atlasCompanyId, }); @@ -237,19 +237,19 @@ export const seedDemo = mutation({ const ricardoOmniId = await ensureUser({ name: "Ricardo Matos", email: "ricardo.matos@omnisaude.com.br", - role: "CUSTOMER", + role: "MANAGER", companyId: omniCompanyId, }); await ensureUser({ name: "Luciana Prado", email: "luciana.prado@omnisaude.com.br", - role: "CUSTOMER", + role: "MANAGER", companyId: omniCompanyId, }); const clienteDemoId = await ensureUser({ name: "Cliente Demo", email: "cliente.demo@sistema.dev", - role: "CUSTOMER", + role: "MANAGER", companyId: omniCompanyId, }); diff --git a/convex/tickets.ts b/convex/tickets.ts index 12e64b1..42ae8f8 100644 --- a/convex/tickets.ts +++ b/convex/tickets.ts @@ -3,7 +3,7 @@ import type { MutationCtx, QueryCtx } from "./_generated/server"; import { ConvexError, v } from "convex/values"; import { Id, type Doc } from "./_generated/dataModel"; -import { requireCustomer, requireStaff, requireUser } from "./rbac"; +import { requireStaff, requireUser } from "./rbac"; const STAFF_ROLES = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]); const INTERNAL_STAFF_ROLES = new Set(["ADMIN", "AGENT", "COLLABORATOR"]); @@ -13,14 +13,13 @@ const PAUSE_REASON_LABELS: Record = { IN_PROCEDURE: "Em procedimento", }; -type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED" | "CLOSED"; +type TicketStatusNormalized = "PENDING" | "AWAITING_ATTENDANCE" | "PAUSED" | "RESOLVED"; const STATUS_LABELS: Record = { PENDING: "Pendente", AWAITING_ATTENDANCE: "Aguardando atendimento", PAUSED: "Pausado", RESOLVED: "Resolvido", - CLOSED: "Fechado", }; const LEGACY_STATUS_MAP: Record = { @@ -31,7 +30,7 @@ const LEGACY_STATUS_MAP: Record = { ON_HOLD: "PAUSED", PAUSED: "PAUSED", RESOLVED: "RESOLVED", - CLOSED: "CLOSED", + CLOSED: "RESOLVED", }; function normalizeStatus(status: string | null | undefined): TicketStatusNormalized { @@ -277,9 +276,7 @@ export const list = query({ } let filtered = base; - if (role === "CUSTOMER") { - filtered = filtered.filter((t) => t.requesterId === args.viewerId); - } else if (role === "MANAGER") { + if (role === "MANAGER") { if (!user.companyId) { throw new ConvexError("Gestor não possui empresa vinculada") } @@ -310,6 +307,7 @@ export const list = query({ const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null; const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; + const company = t.companyId ? ((await ctx.db.get(t.companyId)) as Doc<"companies"> | null) : null; const queueName = normalizeQueueName(queue); const activeSession = t.activeSessionId ? await ctx.db.get(t.activeSessionId) : null; let categorySummary: { id: Id<"ticketCategories">; name: string } | null = null; @@ -342,6 +340,7 @@ export const list = query({ priority: t.priority, channel: t.channel, queue: queueName, + company: company ? { id: company._id, name: company.name, isAvulso: (company as any).isAvulso ?? false } : null, requester: requester && { id: requester._id, name: requester.name, @@ -371,11 +370,14 @@ export const list = query({ subcategory: subcategorySummary, workSummary: { totalWorkedMs: t.totalWorkedMs ?? 0, + internalWorkedMs: (t as any).internalWorkedMs ?? 0, + externalWorkedMs: (t as any).externalWorkedMs ?? 0, activeSession: activeSession ? { id: activeSession._id, agentId: activeSession.agentId, startedAt: activeSession.startedAt, + workType: (activeSession as any).workType ?? "INTERNAL", } : null, }, @@ -393,9 +395,7 @@ export const getById = query({ const { user, role } = await requireUser(ctx, viewerId, tenantId) const t = await ctx.db.get(id); if (!t || t.tenantId !== tenantId) return null; - if (role === "CUSTOMER" && t.requesterId !== viewerId) { - throw new ConvexError("Acesso restrito ao solicitante") - } + // no customer role; managers are constrained to company via ensureManagerTicketAccess let requester: Doc<"users"> | null = null if (role === "MANAGER") { requester = (await ensureManagerTicketAccess(ctx, user, t)) ?? null @@ -405,6 +405,7 @@ export const getById = query({ } const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; + const company = t.companyId ? ((await ctx.db.get(t.companyId)) as Doc<"companies"> | null) : null; const queueName = normalizeQueueName(queue); const category = t.categoryId ? await ctx.db.get(t.categoryId) : null; const subcategory = t.subcategoryId ? await ctx.db.get(t.subcategoryId) : null; @@ -463,6 +464,7 @@ export const getById = query({ priority: t.priority, channel: t.channel, queue: queueName, + company: company ? { id: company._id, name: company.name, isAvulso: (company as any).isAvulso ?? false } : null, requester: requester && { id: requester._id, name: requester.name, @@ -503,11 +505,14 @@ export const getById = query({ : null, workSummary: { totalWorkedMs: t.totalWorkedMs ?? 0, + internalWorkedMs: (t as any).internalWorkedMs ?? 0, + externalWorkedMs: (t as any).externalWorkedMs ?? 0, activeSession: activeSession ? { id: activeSession._id, agentId: activeSession.agentId, startedAt: activeSession.startedAt, + workType: (activeSession as any).workType ?? "INTERNAL", } : null, }, @@ -557,9 +562,7 @@ export const create = mutation({ }, handler: async (ctx, args) => { const { user: actorUser, role } = await requireUser(ctx, args.actorId, args.tenantId) - if (role === "CUSTOMER" && args.requesterId !== args.actorId) { - throw new ConvexError("Clientes só podem abrir chamados para si mesmos") - } + // no customer role; managers validated below if (args.assigneeId && (!role || !INTERNAL_STAFF_ROLES.has(role))) { throw new ConvexError("Somente a equipe interna pode definir o responsável") @@ -706,12 +709,9 @@ export const addComment = mutation({ } if (ticketDoc.requesterId === args.authorId) { - if (normalizedRole === "CUSTOMER") { - await requireCustomer(ctx, args.authorId, ticketDoc.tenantId) - if (args.visibility !== "PUBLIC") { - throw new ConvexError("Clientes só podem registrar comentários públicos") - } - } else if (STAFF_ROLES.has(normalizedRole)) { + // requester commenting: managers restricted to PUBLIC (handled above); + // internal staff require staff permission + if (STAFF_ROLES.has(normalizedRole)) { await requireTicketStaff(ctx, args.authorId, ticketDoc) } else { throw new ConvexError("Autor não possui permissão para comentar") @@ -768,9 +768,7 @@ export const updateComment = mutation({ } const normalizedRole = (actor.role ?? "AGENT").toUpperCase() if (ticketDoc.requesterId === actorId) { - if (normalizedRole === "CUSTOMER") { - await requireCustomer(ctx, actorId, ticketDoc.tenantId) - } else if (STAFF_ROLES.has(normalizedRole)) { + if (STAFF_ROLES.has(normalizedRole)) { await requireTicketStaff(ctx, actorId, ticketDoc) } else { throw new ConvexError("Autor não possui permissão para editar") @@ -828,9 +826,7 @@ export const removeCommentAttachment = mutation({ const normalizedRole = (actor.role ?? "AGENT").toUpperCase() if (ticketDoc.requesterId === actorId) { - if (normalizedRole === "CUSTOMER") { - await requireCustomer(ctx, actorId, ticketDoc.tenantId) - } else if (STAFF_ROLES.has(normalizedRole)) { + if (STAFF_ROLES.has(normalizedRole)) { await requireTicketStaff(ctx, actorId, ticketDoc) } else { throw new ConvexError("Autor não possui permissão para alterar anexos") @@ -1051,11 +1047,14 @@ export const workSummary = query({ return { ticketId, totalWorkedMs: ticket.totalWorkedMs ?? 0, + internalWorkedMs: (ticket as any).internalWorkedMs ?? 0, + externalWorkedMs: (ticket as any).externalWorkedMs ?? 0, activeSession: activeSession ? { id: activeSession._id, agentId: activeSession.agentId, startedAt: activeSession.startedAt, + workType: (activeSession as any).workType ?? "INTERNAL", } : null, } @@ -1083,8 +1082,8 @@ export const updatePriority = mutation({ }); export const startWork = mutation({ - args: { ticketId: v.id("tickets"), actorId: v.id("users") }, - handler: async (ctx, { ticketId, actorId }) => { + args: { ticketId: v.id("tickets"), actorId: v.id("users"), workType: v.optional(v.string()) }, + handler: async (ctx, { ticketId, actorId, workType }) => { const ticket = await ctx.db.get(ticketId) if (!ticket) { throw new ConvexError("Ticket não encontrado") @@ -1098,6 +1097,7 @@ export const startWork = mutation({ const sessionId = await ctx.db.insert("ticketWorkSessions", { ticketId, agentId: actorId, + workType: (workType ?? "INTERNAL").toUpperCase(), startedAt: now, }) @@ -1111,7 +1111,7 @@ export const startWork = mutation({ await ctx.db.insert("ticketEvents", { ticketId, type: "WORK_STARTED", - payload: { actorId, actorName: actor?.name, actorAvatar: actor?.avatarUrl, sessionId }, + payload: { actorId, actorName: actor?.name, actorAvatar: actor?.avatarUrl, sessionId, workType: (workType ?? "INTERNAL").toUpperCase() }, createdAt: now, }) @@ -1156,10 +1156,16 @@ export const pauseWork = mutation({ pauseNote: note ?? "", }) + const sessionType = ((session as any).workType ?? "INTERNAL").toUpperCase() + const deltaInternal = sessionType === "INTERNAL" ? durationMs : 0 + const deltaExternal = sessionType === "EXTERNAL" ? durationMs : 0 + await ctx.db.patch(ticketId, { working: false, activeSessionId: undefined, totalWorkedMs: (ticket.totalWorkedMs ?? 0) + durationMs, + internalWorkedMs: ((ticket as any).internalWorkedMs ?? 0) + deltaInternal, + externalWorkedMs: ((ticket as any).externalWorkedMs ?? 0) + deltaExternal, updatedAt: now, }) @@ -1173,6 +1179,7 @@ export const pauseWork = mutation({ actorAvatar: actor?.avatarUrl, sessionId: session._id, sessionDurationMs: durationMs, + workType: sessionType, pauseReason: reason, pauseReasonLabel: PAUSE_REASON_LABELS[reason], pauseNote: note ?? "", @@ -1256,7 +1263,7 @@ export const playNext = mutation({ } candidates = candidates.filter( - (t) => t.status !== "RESOLVED" && t.status !== "CLOSED" && !t.assigneeId + (t) => t.status !== "RESOLVED" && !t.assigneeId ); if (candidates.length === 0) return null; diff --git a/convex/users.ts b/convex/users.ts index 7dadbd8..59eb77c 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -110,3 +110,17 @@ export const deleteUser = mutation({ }, }); +export const assignCompany = mutation({ + args: { tenantId: v.string(), email: v.string(), companyId: v.id("companies"), actorId: v.id("users") }, + handler: async (ctx, { tenantId, email, companyId, actorId }) => { + await requireAdmin(ctx, actorId, tenantId) + const user = await ctx.db + .query("users") + .withIndex("by_tenant_email", (q) => q.eq("tenantId", tenantId).eq("email", email)) + .first() + if (!user) throw new ConvexError("Usuário não encontrado no Convex") + await ctx.db.patch(user._id, { companyId }) + const updated = await ctx.db.get(user._id) + return updated + }, +}) diff --git a/middleware.ts b/middleware.ts index f6e3708..0d6de97 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from "next/server" import { getCookieCache } from "better-auth/cookies" const PUBLIC_PATHS = [/^\/login$/, /^\/api\/auth/, /^\/_next\//, /^\/favicon/] -const CUSTOMER_ALLOWED_PATHS = [/^\/portal(?:$|\/)/, /^\/api\/auth/, /^\/_next\//, /^\/favicon/] const ADMIN_ONLY_PATHS = [/^\/admin(?:$|\/)/] const PORTAL_HOME = "/portal" const APP_HOME = "/dashboard" @@ -24,21 +23,9 @@ export async function middleware(request: NextRequest) { const role = (session.user as { role?: string })?.role?.toLowerCase() ?? "agent" - if (role === "customer") { - const canAccess = CUSTOMER_ALLOWED_PATHS.some((pattern) => pattern.test(pathname)) - if (!canAccess) { - const redirectUrl = new URL(PORTAL_HOME, request.url) - redirectUrl.searchParams.set("callbackUrl", pathname + search) - return NextResponse.redirect(redirectUrl) - } - } else { - if (pathname.startsWith(PORTAL_HOME)) { - return NextResponse.redirect(new URL(APP_HOME, request.url)) - } - const isAdmin = role === "admin" - if (!isAdmin && ADMIN_ONLY_PATHS.some((pattern) => pattern.test(pathname))) { - return NextResponse.redirect(new URL(APP_HOME, request.url)) - } + const isAdmin = role === "admin" + if (!isAdmin && ADMIN_ONLY_PATHS.some((pattern) => pattern.test(pathname))) { + return NextResponse.redirect(new URL(APP_HOME, request.url)) } return NextResponse.next() @@ -48,4 +35,3 @@ export const config = { runtime: "nodejs", matcher: ["/(.*)"], } - diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d3210e3..e129fb0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -7,20 +7,18 @@ datasource db { url = env("DATABASE_URL") } -enum UserRole { - ADMIN - MANAGER - AGENT - COLLABORATOR - CUSTOMER -} +enum UserRole { + ADMIN + MANAGER + AGENT + COLLABORATOR +} enum TicketStatus { PENDING AWAITING_ATTENDANCE PAUSED RESOLVED - CLOSED } enum TicketPriority { @@ -74,6 +72,7 @@ model Company { tenantId String name String slug String + isAvulso Boolean @default(false) cnpj String? domain String? phone String? diff --git a/scripts/import-convex-to-prisma.mjs b/scripts/import-convex-to-prisma.mjs index 1dff888..78f8245 100644 --- a/scripts/import-convex-to-prisma.mjs +++ b/scripts/import-convex-to-prisma.mjs @@ -26,7 +26,7 @@ if (!secret) { process.exit(1) } -const allowedRoles = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR", "CUSTOMER"]) +const allowedRoles = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]) const client = new ConvexHttpClient(convexUrl) @@ -61,7 +61,7 @@ const STATUS_MAP = { ON_HOLD: "PAUSED", PAUSED: "PAUSED", RESOLVED: "RESOLVED", - CLOSED: "CLOSED", + CLOSED: "RESOLVED", } function normalizeStatus(status) { @@ -84,6 +84,7 @@ async function upsertCompanies(snapshotCompanies) { }, update: { name: company.name, + isAvulso: Boolean(company.isAvulso ?? false), cnpj: company.cnpj ?? null, domain: company.domain ?? null, phone: company.phone ?? null, @@ -94,6 +95,7 @@ async function upsertCompanies(snapshotCompanies) { tenantId, name: company.name, slug, + isAvulso: Boolean(company.isAvulso ?? false), cnpj: company.cnpj ?? null, domain: company.domain ?? null, phone: company.phone ?? null, @@ -117,8 +119,8 @@ async function upsertUsers(snapshotUsers, companyMap) { const normalizedEmail = normalizeEmail(user.email) if (!normalizedEmail) continue - const normalizedRole = (user.role ?? "CUSTOMER").toUpperCase() - const role = allowedRoles.has(normalizedRole) ? normalizedRole : "CUSTOMER" + const normalizedRole = (user.role ?? "MANAGER").toUpperCase() + const role = allowedRoles.has(normalizedRole) ? normalizedRole : "MANAGER" const companyId = user.companySlug ? companyMap.get(user.companySlug) ?? null : null const record = await prisma.user.upsert({ @@ -195,7 +197,7 @@ async function upsertUsers(snapshotUsers, companyMap) { await prisma.user.update({ where: { id: staff.id }, data: { - role: "CUSTOMER", + role: "MANAGER", }, }) } diff --git a/scripts/seed-auth.mjs b/scripts/seed-auth.mjs index 92a0f72..1001aa9 100644 --- a/scripts/seed-auth.mjs +++ b/scripts/seed-auth.mjs @@ -29,7 +29,7 @@ const defaultUsers = singleUserFromEnv ?? [ email: "cliente.demo@sistema.dev", password: "cliente123", name: "Cliente Demo", - role: "customer", + role: "manager", tenantId, }, { @@ -50,28 +50,28 @@ const defaultUsers = singleUserFromEnv ?? [ email: "joao.ramos@atlasengenharia.com.br", password: "cliente123", name: "João Pedro Ramos", - role: "customer", + role: "manager", tenantId, }, { email: "aline.rezende@atlasengenharia.com.br", password: "cliente123", name: "Aline Rezende", - role: "customer", + role: "manager", tenantId, }, { email: "ricardo.matos@omnisaude.com.br", password: "cliente123", name: "Ricardo Matos", - role: "customer", + role: "manager", tenantId, }, { email: "luciana.prado@omnisaude.com.br", password: "cliente123", name: "Luciana Prado", - role: "customer", + role: "manager", tenantId, }, { diff --git a/src/app/admin/companies/page.tsx b/src/app/admin/companies/page.tsx new file mode 100644 index 0000000..450c06f --- /dev/null +++ b/src/app/admin/companies/page.tsx @@ -0,0 +1,26 @@ +import { AppShell } from "@/components/app-shell" +import { SiteHeader } from "@/components/site-header" +import { prisma } from "@/lib/prisma" +import { AdminCompaniesManager } from "@/components/admin/companies/admin-companies-manager" + +export const runtime = "nodejs" +export const dynamic = "force-dynamic" + +export default async function AdminCompaniesPage() { + const companiesRaw = await prisma.company.findMany({ orderBy: { name: "asc" } }) + const companies = companiesRaw.map((c: any) => ({ ...c, isAvulso: Boolean(c.isAvulso ?? false) })) + return ( + + } + > +
    + +
    +
    + ) +} diff --git a/src/app/api/admin/companies/[id]/route.ts b/src/app/api/admin/companies/[id]/route.ts new file mode 100644 index 0000000..73fb94b --- /dev/null +++ b/src/app/api/admin/companies/[id]/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from "next/server" + +import { prisma } from "@/lib/prisma" +import { assertAdminSession } from "@/lib/auth-server" + +export const runtime = "nodejs" + +export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) { + const session = await assertAdminSession() + if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + const { id } = await params + const body = await request.json() + + const updates: Record = {} + for (const key of ["name", "slug", "cnpj", "domain", "phone", "description", "address"]) { + if (key in body) updates[key] = body[key] ?? null + } + if ("isAvulso" in body) updates.isAvulso = Boolean(body.isAvulso) + + try { + const company = await prisma.company.update({ where: { id }, data: updates as any }) + return NextResponse.json({ company }) + } catch (error) { + console.error("Failed to update company", error) + return NextResponse.json({ error: "Falha ao atualizar empresa" }, { status: 500 }) + } +} diff --git a/src/app/api/admin/companies/route.ts b/src/app/api/admin/companies/route.ts new file mode 100644 index 0000000..beeef63 --- /dev/null +++ b/src/app/api/admin/companies/route.ts @@ -0,0 +1,47 @@ +import { NextResponse } from "next/server" + +import { prisma } from "@/lib/prisma" +import { assertAdminSession } from "@/lib/auth-server" + +export const runtime = "nodejs" + +export async function GET() { + const session = await assertAdminSession() + if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + + const companies = await prisma.company.findMany({ + orderBy: { name: "asc" }, + }) + return NextResponse.json({ companies }) +} + +export async function POST(request: Request) { + const session = await assertAdminSession() + if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + + const body = await request.json() + const { name, slug, isAvulso, cnpj, domain, phone, description, address } = body ?? {} + if (!name || !slug) { + return NextResponse.json({ error: "Nome e slug são obrigatórios" }, { status: 400 }) + } + + try { + const company = await prisma.company.create({ + data: ({ + tenantId: session.user.tenantId ?? "tenant-atlas", + name: String(name), + slug: String(slug), + isAvulso: Boolean(isAvulso ?? false), + cnpj: cnpj ? String(cnpj) : null, + domain: domain ? String(domain) : null, + phone: phone ? String(phone) : null, + description: description ? String(description) : null, + address: address ? String(address) : null, + } as any), + }) + return NextResponse.json({ company }) + } catch (error) { + console.error("Failed to create company", error) + return NextResponse.json({ error: "Falha ao criar empresa" }, { status: 500 }) + } +} diff --git a/src/app/api/admin/users/assign-company/route.ts b/src/app/api/admin/users/assign-company/route.ts new file mode 100644 index 0000000..a5761b9 --- /dev/null +++ b/src/app/api/admin/users/assign-company/route.ts @@ -0,0 +1,37 @@ +import { NextResponse } from "next/server" +import { ConvexHttpClient } from "convex/browser" + +import { assertAdminSession } from "@/lib/auth-server" +import { api } from "@/convex/_generated/api" + +export const runtime = "nodejs" + +export async function POST(request: Request) { + const session = await assertAdminSession() + if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + + const body = await request.json().catch(() => null) as { email?: string; companyId?: string } + const email = body?.email?.trim().toLowerCase() + const companyId = body?.companyId + if (!email || !companyId) { + return NextResponse.json({ error: "Informe e-mail e empresa" }, { status: 400 }) + } + + const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) + const client = new ConvexHttpClient(convexUrl) + + try { + await client.mutation(api.users.assignCompany, { + tenantId: session.user.tenantId ?? "tenant-atlas", + email, + companyId: companyId as any, + actorId: (session.user as any).convexUserId ?? (session.user.id as any), + }) + return NextResponse.json({ ok: true }) + } catch (error) { + console.error("Failed to assign company", error) + return NextResponse.json({ error: "Falha ao vincular usuário" }, { status: 500 }) + } +} + diff --git a/src/app/api/reports/backlog.csv/route.ts b/src/app/api/reports/backlog.csv/route.ts new file mode 100644 index 0000000..addc2db --- /dev/null +++ b/src/app/api/reports/backlog.csv/route.ts @@ -0,0 +1,113 @@ +import { NextResponse } from "next/server" +import { ConvexHttpClient } from "convex/browser" + +import { api } from "@/convex/_generated/api" +import type { Id } from "@/convex/_generated/dataModel" +import { env } from "@/lib/env" +import { assertAuthenticatedSession } from "@/lib/auth-server" +import { DEFAULT_TENANT_ID } from "@/lib/constants" + +export const runtime = "nodejs" + +function csvEscape(value: unknown): string { + const s = value == null ? "" : String(value) + if (/[",\n]/.test(s)) { + return '"' + s.replace(/"/g, '""') + '"' + } + return s +} + +function rowsToCsv(rows: Array>): string { + return rows.map((row) => row.map(csvEscape).join(",")).join("\n") + "\n" +} + +export async function GET(request: Request) { + const session = await assertAuthenticatedSession() + if (!session) { + return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + } + + const convexUrl = env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) { + return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) + } + + const client = new ConvexHttpClient(convexUrl) + const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID + + let viewerId: string | null = null + try { + const ensuredUser = await client.mutation(api.users.ensureUser, { + tenantId, + name: session.user.name ?? session.user.email, + email: session.user.email, + avatarUrl: session.user.avatarUrl ?? undefined, + role: session.user.role.toUpperCase(), + }) + viewerId = ensuredUser?._id ?? null + } catch (error) { + console.error("Failed to synchronize user with Convex for backlog CSV", error) + return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 }) + } + + if (!viewerId) { + return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 }) + } + + try { + const { searchParams } = new URL(request.url) + const range = searchParams.get("range") ?? undefined + const report = await client.query(api.reports.backlogOverview, { + tenantId, + viewerId: viewerId as unknown as Id<"users">, + range, + }) + + const rows: Array> = [] + rows.push(["Relatório", "Backlog"]) + rows.push(["Período", report.rangeDays ? `Últimos ${report.rangeDays} dias` : "—"]) + rows.push([]) + rows.push(["Seção", "Chave", "Valor"]) // header + + // Status + const STATUS_PT: Record = { + PENDING: "Pendentes", + AWAITING_ATTENDANCE: "Aguardando atendimento", + PAUSED: "Pausados", + RESOLVED: "Resolvidos", + } + for (const [status, total] of Object.entries(report.statusCounts)) { + rows.push(["Status", STATUS_PT[status] ?? status, total]) + } + + // Prioridade + const PRIORITY_PT: Record = { + LOW: "Baixa", + MEDIUM: "Média", + HIGH: "Alta", + URGENT: "Crítica", + } + for (const [priority, total] of Object.entries(report.priorityCounts)) { + rows.push(["Prioridade", PRIORITY_PT[priority] ?? priority, total]) + } + + // Filas + for (const q of report.queueCounts) { + rows.push(["Fila", q.name || q.id, q.total]) + } + + rows.push(["Abertos", "Total", report.totalOpen]) + + const csv = rowsToCsv(rows) + return new NextResponse(csv, { + headers: { + "Content-Type": "text/csv; charset=UTF-8", + "Content-Disposition": `attachment; filename="backlog-${tenantId}-${report.rangeDays ?? 'all'}d.csv"`, + "Cache-Control": "no-store", + }, + }) + } catch (error) { + console.error("Failed to generate backlog CSV", error) + return NextResponse.json({ error: "Falha ao gerar CSV do backlog" }, { status: 500 }) + } +} diff --git a/src/app/api/reports/csat.csv/route.ts b/src/app/api/reports/csat.csv/route.ts new file mode 100644 index 0000000..a57e350 --- /dev/null +++ b/src/app/api/reports/csat.csv/route.ts @@ -0,0 +1,99 @@ +import { NextResponse } from "next/server" +import { ConvexHttpClient } from "convex/browser" + +import { api } from "@/convex/_generated/api" +import type { Id } from "@/convex/_generated/dataModel" +import { env } from "@/lib/env" +import { assertAuthenticatedSession } from "@/lib/auth-server" +import { DEFAULT_TENANT_ID } from "@/lib/constants" + +export const runtime = "nodejs" + +function csvEscape(value: unknown): string { + const s = value == null ? "" : String(value) + if (/[",\n]/.test(s)) { + return '"' + s.replace(/"/g, '""') + '"' + } + return s +} + +function rowsToCsv(rows: Array>): string { + return rows.map((row) => row.map(csvEscape).join(",")).join("\n") + "\n" +} + +export async function GET(request: Request) { + const session = await assertAuthenticatedSession() + if (!session) { + return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + } + + const convexUrl = env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) { + return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) + } + + const { searchParams } = new URL(request.url) + const range = searchParams.get("range") ?? undefined + + const client = new ConvexHttpClient(convexUrl) + const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID + + let viewerId: string | null = null + try { + const ensuredUser = await client.mutation(api.users.ensureUser, { + tenantId, + name: session.user.name ?? session.user.email, + email: session.user.email, + avatarUrl: session.user.avatarUrl ?? undefined, + role: session.user.role.toUpperCase(), + }) + viewerId = ensuredUser?._id ?? null + } catch (error) { + console.error("Failed to synchronize user with Convex for CSAT CSV", error) + return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 }) + } + + if (!viewerId) { + return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 }) + } + + try { + const report = await client.query(api.reports.csatOverview, { + tenantId, + viewerId: viewerId as unknown as Id<"users">, + range, + }) + + const rows: Array> = [] + rows.push(["Relatório", "CSAT"]) + rows.push(["Período", report.rangeDays ? `Últimos ${report.rangeDays} dias` : (range ?? '90d')]) + rows.push([]) + rows.push(["Métrica", "Valor"]) // header + rows.push(["CSAT médio", report.averageScore ?? "—"]) + rows.push(["Total de respostas", report.totalSurveys ?? 0]) + rows.push([]) + rows.push(["Distribuição", "Total"]) + for (const entry of report.distribution ?? []) { + rows.push([`Nota ${entry.score}`, entry.total]) + } + rows.push([]) + rows.push(["Recentes", "Nota", "Recebido em"]) + for (const item of report.recent ?? []) { + const date = new Date(item.receivedAt).toISOString() + rows.push([`#${item.reference}`, item.score, date]) + } + + const csv = rowsToCsv(rows) + return new NextResponse(csv, { + headers: { + "Content-Type": "text/csv; charset=UTF-8", + "Content-Disposition": `attachment; filename="csat-${tenantId}-${report.rangeDays ?? '90'}d.csv"`, + "Cache-Control": "no-store", + }, + }) + } catch (error) { + console.error("Failed to generate CSAT CSV", error) + return NextResponse.json({ error: "Falha ao gerar CSV de CSAT" }, { status: 500 }) + } +} + diff --git a/src/app/api/reports/sla.csv/route.ts b/src/app/api/reports/sla.csv/route.ts new file mode 100644 index 0000000..94187cb --- /dev/null +++ b/src/app/api/reports/sla.csv/route.ts @@ -0,0 +1,101 @@ +import { NextResponse } from "next/server" +import { ConvexHttpClient } from "convex/browser" + +import { api } from "@/convex/_generated/api" +import type { Id } from "@/convex/_generated/dataModel" +import { env } from "@/lib/env" +import { assertAuthenticatedSession } from "@/lib/auth-server" +import { DEFAULT_TENANT_ID } from "@/lib/constants" + +export const runtime = "nodejs" + +function csvEscape(value: unknown): string { + const s = value == null ? "" : String(value) + if (/[",\n]/.test(s)) { + return '"' + s.replace(/"/g, '""') + '"' + } + return s +} + +function rowsToCsv(rows: Array>): string { + return rows.map((row) => row.map(csvEscape).join(",")).join("\n") + "\n" +} + +export async function GET(request: Request) { + const session = await assertAuthenticatedSession() + if (!session) { + return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + } + + const convexUrl = env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) { + return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) + } + + const { searchParams } = new URL(request.url) + const range = searchParams.get("range") ?? undefined + + const client = new ConvexHttpClient(convexUrl) + const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID + + let viewerId: string | null = null + try { + const ensuredUser = await client.mutation(api.users.ensureUser, { + tenantId, + name: session.user.name ?? session.user.email, + email: session.user.email, + avatarUrl: session.user.avatarUrl ?? undefined, + role: session.user.role.toUpperCase(), + }) + viewerId = ensuredUser?._id ?? null + } catch (error) { + console.error("Failed to synchronize user with Convex for SLA CSV", error) + return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 }) + } + + if (!viewerId) { + return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 }) + } + + try { + const report = await client.query(api.reports.slaOverview, { + tenantId, + viewerId: viewerId as unknown as Id<"users">, + range, + }) + + const rows: Array> = [] + rows.push(["Relatório", "SLA e produtividade"]) + rows.push(["Período", range ?? "—"]) + rows.push([]) + + rows.push(["Métrica", "Valor"]) // header + rows.push(["Tickets totais", report.totals.total]) + rows.push(["Tickets abertos", report.totals.open]) + rows.push(["Tickets resolvidos", report.totals.resolved]) + rows.push(["Atrasados (SLA)", report.totals.overdue]) + rows.push([]) + rows.push(["Tempo médio de 1ª resposta (min)", report.response.averageFirstResponseMinutes ?? "—"]) + rows.push(["Respostas registradas", report.response.responsesRegistered ?? 0]) + rows.push(["Tempo médio de resolução (min)", report.resolution.averageResolutionMinutes ?? "—"]) + rows.push(["Tickets resolvidos (amostra)", report.resolution.resolvedCount ?? 0]) + rows.push([]) + rows.push(["Fila", "Abertos"]) + for (const q of report.queueBreakdown ?? []) { + rows.push([q.name || q.id, q.open]) + } + + const csv = rowsToCsv(rows) + return new NextResponse(csv, { + headers: { + "Content-Type": "text/csv; charset=UTF-8", + "Content-Disposition": `attachment; filename="sla-${tenantId}.csv"`, + "Cache-Control": "no-store", + }, + }) + } catch (error) { + console.error("Failed to generate SLA CSV", error) + return NextResponse.json({ error: "Falha ao gerar CSV de SLA" }, { status: 500 }) + } +} + diff --git a/src/app/api/reports/tickets-by-channel.csv/route.ts b/src/app/api/reports/tickets-by-channel.csv/route.ts new file mode 100644 index 0000000..de7d958 --- /dev/null +++ b/src/app/api/reports/tickets-by-channel.csv/route.ts @@ -0,0 +1,102 @@ +import { NextResponse } from "next/server" +import { ConvexHttpClient } from "convex/browser" + +import { api } from "@/convex/_generated/api" +import type { Id } from "@/convex/_generated/dataModel" +import { env } from "@/lib/env" +import { assertAuthenticatedSession } from "@/lib/auth-server" +import { DEFAULT_TENANT_ID } from "@/lib/constants" + +export const runtime = "nodejs" + +function csvEscape(value: unknown): string { + const s = value == null ? "" : String(value) + if (/[",\n]/.test(s)) { + return '"' + s.replace(/"/g, '""') + '"' + } + return s +} + +function rowsToCsv(rows: Array>): string { + return rows.map((row) => row.map(csvEscape).join(",")).join("\n") + "\n" +} + +export async function GET(request: Request) { + const session = await assertAuthenticatedSession() + if (!session) { + return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) + } + + const convexUrl = env.NEXT_PUBLIC_CONVEX_URL + if (!convexUrl) { + return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) + } + + const { searchParams } = new URL(request.url) + const range = searchParams.get("range") ?? undefined // "7d" | "30d" | undefined(=90d) + + const client = new ConvexHttpClient(convexUrl) + const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID + + let viewerId: string | null = null + try { + const ensuredUser = await client.mutation(api.users.ensureUser, { + tenantId, + name: session.user.name ?? session.user.email, + email: session.user.email, + avatarUrl: session.user.avatarUrl ?? undefined, + role: session.user.role.toUpperCase(), + }) + viewerId = ensuredUser?._id ?? null + } catch (error) { + console.error("Failed to synchronize user with Convex for channel CSV", error) + return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 }) + } + + if (!viewerId) { + return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 }) + } + + try { + const report = await client.query(api.reports.ticketsByChannel, { + tenantId, + viewerId: viewerId as unknown as Id<"users">, + range, + }) + + const channels = report.channels + const CHANNEL_PT: Record = { + EMAIL: "E-mail", + PHONE: "Telefone", + CHAT: "Chat", + WHATSAPP: "WhatsApp", + API: "API", + MANUAL: "Manual", + WEB: "Portal", + PORTAL: "Portal", + } + const header = ["Data", ...channels.map((ch) => CHANNEL_PT[ch] ?? ch)] + const rows: Array> = [] + rows.push(["Relatório", "Tickets por canal"]) + rows.push(["Período", report.rangeDays ? `Últimos ${report.rangeDays} dias` : (range ?? '90d')]) + rows.push([]) + rows.push(header) + + for (const point of report.points) { + const values = channels.map((ch) => point.values[ch] ?? 0) + rows.push([point.date, ...values]) + } + + const csv = rowsToCsv(rows) + return new NextResponse(csv, { + headers: { + "Content-Type": "text/csv; charset=UTF-8", + "Content-Disposition": `attachment; filename="tickets-by-channel-${tenantId}-${range ?? '90d'}.csv"`, + "Cache-Control": "no-store", + }, + }) + } catch (error) { + console.error("Failed to generate tickets-by-channel CSV", error) + return NextResponse.json({ error: "Falha ao gerar CSV de tickets por canal" }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/[id]/export/pdf/route.ts b/src/app/api/tickets/[id]/export/pdf/route.ts index d953b7d..d48eb9f 100644 --- a/src/app/api/tickets/[id]/export/pdf/route.ts +++ b/src/app/api/tickets/[id]/export/pdf/route.ts @@ -1,5 +1,9 @@ import { NextResponse } from "next/server" -import PDFDocument from "pdfkit" +// Use the standalone build to avoid AFM filesystem lookups +// and ensure compatibility in serverless/traced environments. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore – no ambient types for this path; declared in types/ +import PDFDocument from "pdfkit/js/pdfkit.standalone.js" import { format } from "date-fns" import { ptBR } from "date-fns/locale" import { ConvexHttpClient } from "convex/browser" @@ -7,16 +11,44 @@ import { ConvexHttpClient } from "convex/browser" import { api } from "@/convex/_generated/api" import type { Id } from "@/convex/_generated/dataModel" import { env } from "@/lib/env" -import { assertStaffSession } from "@/lib/auth-server" +import { assertAuthenticatedSession } from "@/lib/auth-server" import { mapTicketWithDetailsFromServer } from "@/lib/mappers/ticket" import { DEFAULT_TENANT_ID } from "@/lib/constants" +// Force Node.js runtime for pdfkit compatibility +export const runtime = "nodejs" + const statusLabel: Record = { PENDING: "Pendente", AWAITING_ATTENDANCE: "Aguardando atendimento", PAUSED: "Pausado", RESOLVED: "Resolvido", - CLOSED: "Fechado", +} + +const statusColors: Record = { + PENDING: "#64748B", // slate-500 + AWAITING_ATTENDANCE: "#0EA5E9", // sky-500 + PAUSED: "#F59E0B", // amber-500 + RESOLVED: "#10B981", // emerald-500 +} + +const priorityLabel: Record = { + LOW: "Baixa", + MEDIUM: "Média", + HIGH: "Alta", + URGENT: "Urgente", + CRITICAL: "Crítica", +} + +const channelLabel: Record = { + EMAIL: "E-mail", + PHONE: "Telefone", + CHAT: "Chat", + PORTAL: "Portal", + WEB: "Portal", + API: "API", + SOCIAL: "Redes sociais", + OTHER: "Outro", } const timelineLabel: Record = { @@ -31,6 +63,12 @@ const timelineLabel: Record = { WORK_STARTED: "Atendimento iniciado", WORK_PAUSED: "Atendimento pausado", CATEGORY_CHANGED: "Categoria alterada", + MANAGER_NOTIFIED: "Gestor notificado", + SUBJECT_CHANGED: "Assunto atualizado", + SUMMARY_CHANGED: "Resumo atualizado", + VISIT_SCHEDULED: "Visita agendada", + CSAT_RECEIVED: "CSAT recebido", + CSAT_RATED: "CSAT avaliado", } function formatDateTime(date: Date | null | undefined) { @@ -57,9 +95,104 @@ function decodeHtmlEntities(input: string) { .replace(/ /g, " ") } +function stringifyPayload(payload: unknown): string | null { + if (!payload) return null + if (typeof payload === "object") { + if (Array.isArray(payload)) { + if (payload.length === 0) return null + } else if (payload) { + if (Object.keys(payload as Record).length === 0) return null + } + } + if (typeof payload === "string" && payload.trim() === "") return null + try { + return JSON.stringify(payload, null, 2) + } catch { + return String(payload) + } +} + +function formatDurationMs(ms: number | null | undefined) { + if (!ms || ms <= 0) return null + const totalSeconds = Math.floor(ms / 1000) + const hours = Math.floor(totalSeconds / 3600) + const minutes = Math.floor((totalSeconds % 3600) / 60) + const seconds = totalSeconds % 60 + if (hours > 0) return `${hours}h ${String(minutes).padStart(2, "0")}m` + if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s` + return `${seconds}s` +} + +function buildTimelineMessage(type: string, payload: any): string | null { + if (!payload || typeof payload !== "object") payload = {} + const to = payload.toLabel ?? payload.to + const assignee = payload.assigneeName ?? payload.assigneeId + const queue = payload.queueName ?? payload.queueId + const requester = payload.requesterName + const author = payload.authorName ?? payload.authorId + const actor = payload.actorName ?? payload.actorId + const attachmentName = payload.attachmentName + const subjectTo = payload.to + const pauseReason = payload.pauseReasonLabel ?? payload.pauseReason + const pauseNote = payload.pauseNote + const sessionDuration = formatDurationMs(payload.sessionDurationMs) + const categoryName = payload.categoryName + const subcategoryName = payload.subcategoryName + + switch (type) { + case "STATUS_CHANGED": + return to ? `Status alterado para ${to}` : "Status alterado" + case "ASSIGNEE_CHANGED": + return assignee ? `Responsável alterado para ${assignee}` : "Responsável alterado" + case "QUEUE_CHANGED": + return queue ? `Fila alterada para ${queue}` : "Fila alterada" + case "PRIORITY_CHANGED": + return to ? `Prioridade alterada para ${to}` : "Prioridade alterada" + case "CREATED": + return requester ? `Criado por ${requester}` : "Criado" + case "COMMENT_ADDED": + return author ? `Comentário adicionado por ${author}` : "Comentário adicionado" + case "COMMENT_EDITED": { + const who = actor ?? author + return who ? `Comentário editado por ${who}` : "Comentário editado" + } + case "SUBJECT_CHANGED": + return subjectTo ? `Assunto alterado para "${subjectTo}"` : "Assunto alterado" + case "SUMMARY_CHANGED": + return "Resumo atualizado" + case "ATTACHMENT_REMOVED": + return attachmentName ? `Anexo removido: ${attachmentName}` : "Anexo removido" + case "WORK_PAUSED": { + const parts: string[] = [] + if (pauseReason) parts.push(`Motivo: ${pauseReason}`) + if (sessionDuration) parts.push(`Tempo registrado: ${sessionDuration}`) + if (pauseNote) parts.push(`Observação: ${pauseNote}`) + return parts.length > 0 ? parts.join(" • ") : "Atendimento pausado" + } + case "WORK_STARTED": + return "Atendimento iniciado" + case "CATEGORY_CHANGED": { + if (categoryName || subcategoryName) { + return `Categoria alterada para ${categoryName ?? ""}${subcategoryName ? ` • ${subcategoryName}` : ""}`.trim() + } + return "Categoria removida" + } + case "MANAGER_NOTIFIED": + return "Gestor notificado" + case "VISIT_SCHEDULED": + return "Visita agendada" + case "CSAT_RECEIVED": + return "CSAT recebido" + case "CSAT_RATED": + return "CSAT avaliado" + default: + return null + } +} + export async function GET(_request: Request, context: { params: Promise<{ id: string }> }) { const { id: ticketId } = await context.params - const session = await assertStaffSession() + const session = await assertAuthenticatedSession() if (!session) { return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) } @@ -111,10 +244,10 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st } const ticket = mapTicketWithDetailsFromServer(ticketRaw) - const doc = new PDFDocument({ size: "A4", margin: 48 }) + const doc = new PDFDocument({ size: "A4", margin: 56 }) const chunks: Buffer[] = [] - doc.on("data", (chunk) => { + doc.on("data", (chunk: any) => { chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk) }) @@ -123,24 +256,58 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st doc.on("error", reject) }) + // Título doc.font("Helvetica-Bold").fontSize(18).text(`Ticket #${ticket.reference} — ${ticket.subject}`) - doc.moveDown(0.5) + doc.moveDown(0.25) + // Linha abaixo do título doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() + + // Badge de status + doc.moveDown(0.5) + const statusText = statusLabel[ticket.status] ?? ticket.status + const badgeColor = statusColors[ticket.status] ?? "#475569" + const badgeFontSize = 10 + const badgePaddingX = 6 + const badgePaddingY = 3 + const badgeX = doc.page.margins.left + const badgeY = doc.y + doc.save() + doc.font("Helvetica-Bold").fontSize(badgeFontSize) + const badgeTextWidth = doc.widthOfString(statusText) + const badgeHeight = badgeFontSize + badgePaddingY * 2 + const badgeWidth = badgeTextWidth + badgePaddingX * 2 + ;(doc as any).roundedRect?.(badgeX, badgeY, badgeWidth, badgeHeight, 4) ?? doc.rect(badgeX, badgeY, badgeWidth, badgeHeight) + doc.fill(badgeColor) + doc.fillColor("#FFFFFF").text(statusText, badgeX + badgePaddingX, badgeY + badgePaddingY) + doc.restore() + doc.y = badgeY + badgeHeight + 8 + + // Metadados básicos + doc + .fillColor("#0F172A") .font("Helvetica") .fontSize(11) - .text(`Status: ${statusLabel[ticket.status] ?? ticket.status}`) + .text(`Prioridade: ${priorityLabel[ticket.priority] ?? ticket.priority}`, { lineGap: 2 }) .moveDown(0.15) - .text(`Prioridade: ${ticket.priority}`) + .text(`Canal: ${channelLabel[ticket.channel] ?? ticket.channel ?? "—"}`, { lineGap: 2 }) .moveDown(0.15) - .text(`Canal: ${ticket.channel}`) - .moveDown(0.15) - .text(`Fila: ${ticket.queue ?? "—"}`) + .text(`Fila: ${ticket.queue ?? "—"}`, { lineGap: 2 }) doc.moveDown(0.75) doc .font("Helvetica-Bold") .fontSize(12) .text("Solicitante") + doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() + doc.moveDown(0.3) doc .font("Helvetica") .fontSize(11) @@ -148,6 +315,12 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st doc.moveDown(0.5) doc.font("Helvetica-Bold").fontSize(12).text("Responsável") + doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() + doc.moveDown(0.3) doc .font("Helvetica") .fontSize(11) @@ -155,6 +328,12 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st doc.moveDown(0.75) doc.font("Helvetica-Bold").fontSize(12).text("Datas") + doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() + doc.moveDown(0.3) doc .font("Helvetica") .fontSize(11) @@ -167,25 +346,35 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st if (ticket.summary) { doc.moveDown(0.75) doc.font("Helvetica-Bold").fontSize(12).text("Resumo") + doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() doc .font("Helvetica") .fontSize(11) - .text(ticket.summary, { align: "justify" }) + .text(ticket.summary, { align: "justify", lineGap: 2 }) } if (ticket.description) { doc.moveDown(0.75) doc.font("Helvetica-Bold").fontSize(12).text("Descrição") + doc + .strokeColor("#E2E8F0") + .moveTo(doc.page.margins.left, doc.y) + .lineTo(doc.page.width - doc.page.margins.right, doc.y) + .stroke() doc .font("Helvetica") .fontSize(11) - .text(htmlToPlainText(ticket.description), { align: "justify" }) + .text(htmlToPlainText(ticket.description), { align: "justify", lineGap: 2 }) } if (ticket.comments.length > 0) { doc.addPage() doc.font("Helvetica-Bold").fontSize(14).text("Comentários") - doc.moveDown(0.5) + doc.moveDown(0.6) const commentsSorted = [...ticket.comments].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) commentsSorted.forEach((comment, index) => { const visibility = @@ -193,15 +382,14 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st doc .font("Helvetica-Bold") .fontSize(11) - .text( - `${comment.author.name} • ${visibility} • ${formatDateTime(comment.createdAt)}` - ) + .text(`${comment.author.name} • ${visibility} • ${formatDateTime(comment.createdAt)}`) + doc.moveDown(0.15) const body = htmlToPlainText(comment.body) if (body) { doc .font("Helvetica") .fontSize(11) - .text(body, { align: "justify" }) + .text(body, { align: "justify", lineGap: 2, indent: 6 }) } if (comment.attachments.length > 0) { doc.moveDown(0.25) @@ -210,17 +398,17 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st doc .font("Helvetica") .fontSize(10) - .text(`• ${attachment.name ?? attachment.id}`, { indent: 12 }) + .text(`• ${attachment.name ?? attachment.id}`, { indent: 16, lineGap: 1 }) }) } if (index < commentsSorted.length - 1) { - doc.moveDown(0.75) + doc.moveDown(1) doc .strokeColor("#E2E8F0") .moveTo(doc.x, doc.y) .lineTo(doc.page.width - doc.page.margins.right, doc.y) .stroke() - doc.moveDown(0.75) + doc.moveDown(0.9) } }) } @@ -228,7 +416,7 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st if (ticket.timeline.length > 0) { doc.addPage() doc.font("Helvetica-Bold").fontSize(14).text("Linha do tempo") - doc.moveDown(0.5) + doc.moveDown(0.6) const timelineSorted = [...ticket.timeline].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) timelineSorted.forEach((event) => { const label = timelineLabel[event.type] ?? event.type @@ -236,14 +424,24 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st .font("Helvetica-Bold") .fontSize(11) .text(`${label} • ${formatDateTime(event.createdAt)}`) - if (event.payload) { - const payloadText = JSON.stringify(event.payload, null, 2) + doc.moveDown(0.15) + + const friendly = buildTimelineMessage(event.type, event.payload) + if (friendly) { doc .font("Helvetica") .fontSize(10) - .text(payloadText, { indent: 12 }) + .text(friendly, { indent: 16, lineGap: 1 }) + } else { + const payloadText = stringifyPayload(event.payload) + if (payloadText) { + doc + .font("Helvetica") + .fontSize(10) + .text(payloadText, { indent: 16, lineGap: 1 }) + } } - doc.moveDown(0.5) + doc.moveDown(0.7) }) } diff --git a/src/app/reports/csat/page.tsx b/src/app/reports/csat/page.tsx index 83d0715..c24934b 100644 --- a/src/app/reports/csat/page.tsx +++ b/src/app/reports/csat/page.tsx @@ -11,6 +11,13 @@ export default function ReportsCsatPage() { + + Exportar CSV + + + } /> } > diff --git a/src/app/reports/sla/page.tsx b/src/app/reports/sla/page.tsx index 824f935..91cec01 100644 --- a/src/app/reports/sla/page.tsx +++ b/src/app/reports/sla/page.tsx @@ -11,6 +11,13 @@ export default function ReportsSlaPage() { + + Exportar CSV + + + } /> } > diff --git a/src/components/admin/admin-users-manager.tsx b/src/components/admin/admin-users-manager.tsx index fe4a2ba..2e03d45 100644 --- a/src/components/admin/admin-users-manager.tsx +++ b/src/components/admin/admin-users-manager.tsx @@ -95,9 +95,26 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d const [lastInviteLink, setLastInviteLink] = useState(null) const [revokingId, setRevokingId] = useState(null) const [isPending, startTransition] = useTransition() + const [companies, setCompanies] = useState>([]) + const [linkEmail, setLinkEmail] = useState("") + const [linkCompanyId, setLinkCompanyId] = useState("") const normalizedRoles = useMemo(() => roleOptions ?? ROLE_OPTIONS, [roleOptions]) + // load companies for association + useMemo(() => { + void (async () => { + try { + const r = await fetch("/api/admin/companies", { credentials: "include" }) + const j = await r.json() + const items = (j.companies ?? []).map((c: any) => ({ id: c.id as string, name: c.name as string })) + setCompanies(items) + } catch { + // noop + } + })() + }, []) + async function handleInviteSubmit(event: React.FormEvent) { event.preventDefault() if (!email || !email.includes("@")) { @@ -238,9 +255,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d {normalizedRoles.map((item) => ( - {item === "customer" - ? "Cliente" - : item === "admin" + {item === "admin" ? "Administrador" : item === "manager" ? "Gestor" @@ -294,6 +309,63 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d + + + Vincular usuário a empresa + Associe um colaborador à sua empresa (usado para escopo de gestores e relatórios). + + +
    { + e.preventDefault() + if (!linkEmail || !linkCompanyId) { + toast.error("Informe e-mail e empresa") + return + } + startTransition(async () => { + toast.loading("Vinculando...", { id: "assign-company" }) + try { + const r = await fetch("/api/admin/users/assign-company", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: linkEmail, companyId: linkCompanyId }), + credentials: "include", + }) + if (!r.ok) throw new Error("failed") + toast.success("Usuário vinculado à empresa!", { id: "assign-company" }) + } catch { + toast.error("Não foi possível vincular", { id: "assign-company" }) + } + }) + }} + > +
    + + setLinkEmail(e.target.value)} placeholder="colaborador@empresa.com" /> +
    +
    + + +
    +
    + +
    +
    +
    +
    + Convites emitidos diff --git a/src/components/admin/companies/admin-companies-manager.tsx b/src/components/admin/companies/admin-companies-manager.tsx new file mode 100644 index 0000000..bcd077c --- /dev/null +++ b/src/components/admin/companies/admin-companies-manager.tsx @@ -0,0 +1,213 @@ +"use client" + +import { useMemo, useState, useTransition } from "react" +import { toast } from "sonner" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Checkbox } from "@/components/ui/checkbox" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" + +type Company = { + id: string + tenantId: string + name: string + slug: string + isAvulso: boolean + cnpj: string | null + domain: string | null + phone: string | null + description: string | null + address: string | null +} + +export function AdminCompaniesManager({ initialCompanies }: { initialCompanies: Company[] }) { + const [companies, setCompanies] = useState(initialCompanies) + const [isPending, startTransition] = useTransition() + const [form, setForm] = useState>({}) + const [editingId, setEditingId] = useState(null) + + const resetForm = () => setForm({}) + + async function refresh() { + const r = await fetch("/api/admin/companies", { credentials: "include" }) + const json = (await r.json()) as { companies: Company[] } + setCompanies(json.companies) + } + + function handleEdit(c: Company) { + setEditingId(c.id) + setForm({ ...c }) + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + const payload = { + name: form.name?.trim(), + slug: form.slug?.trim(), + isAvulso: Boolean(form.isAvulso ?? false), + cnpj: form.cnpj?.trim() || null, + domain: form.domain?.trim() || null, + phone: form.phone?.trim() || null, + description: form.description?.trim() || null, + address: form.address?.trim() || null, + } + if (!payload.name || !payload.slug) { + toast.error("Informe nome e slug válidos") + return + } + startTransition(async () => { + toast.loading(editingId ? "Atualizando empresa..." : "Criando empresa...", { id: "companies" }) + try { + if (editingId) { + const r = await fetch(`/api/admin/companies/${editingId}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + credentials: "include", + }) + if (!r.ok) throw new Error("update_failed") + } else { + const r = await fetch(`/api/admin/companies`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + credentials: "include", + }) + if (!r.ok) throw new Error("create_failed") + } + await refresh() + resetForm() + setEditingId(null) + toast.success(editingId ? "Empresa atualizada" : "Empresa criada", { id: "companies" }) + } catch { + toast.error("Não foi possível salvar", { id: "companies" }) + } + }) + } + + async function toggleAvulso(c: Company) { + startTransition(async () => { + try { + const r = await fetch(`/api/admin/companies/${c.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ isAvulso: !c.isAvulso }), + credentials: "include", + }) + if (!r.ok) throw new Error("toggle_failed") + await refresh() + } catch { + toast.error("Não foi possível atualizar o cliente avulso") + } + }) + } + + return ( +
    + + + Nova empresa + Cadastre um cliente/empresa e defina se é avulso. + + +
    +
    + + setForm((p) => ({ ...p, name: e.target.value }))} /> +
    +
    + + setForm((p) => ({ ...p, slug: e.target.value }))} /> +
    +
    + + setForm((p) => ({ ...p, cnpj: e.target.value }))} /> +
    +
    + + setForm((p) => ({ ...p, domain: e.target.value }))} /> +
    +
    + + setForm((p) => ({ ...p, phone: e.target.value }))} /> +
    +
    + + setForm((p) => ({ ...p, address: e.target.value }))} /> +
    +
    + setForm((p) => ({ ...p, isAvulso: Boolean(v) }))} + id="is-avulso" + /> + +
    +
    + + {editingId ? ( + + ) : null} +
    +
    +
    +
    + + + + Empresas cadastradas + Gerencie empresas e o status de cliente avulso. + + + + + + Nome + Slug + Avulso + Domínio + Telefone + CNPJ + Ações + + + + {companies.map((c) => ( + + {c.name} + {c.slug} + + + + {c.domain ?? "—"} + {c.phone ?? "—"} + {c.cnpj ?? "—"} + + + + + ))} + +
    +
    +
    +
    + ) +} + diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index b878c8d..b604159 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -40,7 +40,7 @@ import { useAuth } from "@/lib/auth-client" import type { LucideIcon } from "lucide-react" -type NavRoleRequirement = "staff" | "admin" | "customer" +type NavRoleRequirement = "staff" | "admin" type NavigationItem = { title: string @@ -91,6 +91,7 @@ const navigation: { versions: string[]; navMain: NavigationGroup[] } = { }, { title: "Canais & roteamento", url: "/admin/channels", icon: Waypoints, requiredRole: "admin" }, { title: "Times & papéis", url: "/admin/teams", icon: Users, requiredRole: "admin" }, + { title: "Empresas & clientes", url: "/admin/companies", icon: Users, requiredRole: "admin" }, { title: "Campos personalizados", url: "/admin/fields", icon: Layers3, requiredRole: "admin" }, { title: "SLAs", url: "/admin/slas", icon: Timer, requiredRole: "admin" }, ], @@ -105,7 +106,7 @@ const navigation: { versions: string[]; navMain: NavigationGroup[] } = { export function AppSidebar({ ...props }: React.ComponentProps) { const pathname = usePathname() - const { session, isLoading, isAdmin, isStaff, isCustomer } = useAuth() + const { session, isLoading, isAdmin, isStaff } = useAuth() const [isHydrated, setIsHydrated] = React.useState(false) React.useEffect(() => { @@ -128,7 +129,6 @@ export function AppSidebar({ ...props }: React.ComponentProps) { if (!requiredRole) return true if (requiredRole === "admin") return isAdmin if (requiredRole === "staff") return isStaff - if (requiredRole === "customer") return isCustomer return false } diff --git a/src/components/chart-area-interactive.tsx b/src/components/chart-area-interactive.tsx index 5a12f10..f85b51e 100644 --- a/src/components/chart-area-interactive.tsx +++ b/src/components/chart-area-interactive.tsx @@ -9,14 +9,15 @@ import type { Id } from "@/convex/_generated/dataModel" import { useAuth } from "@/lib/auth-client" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { useIsMobile } from "@/hooks/use-mobile" -import { - Card, - CardAction, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" +import { + Card, + CardAction, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Button } from "@/components/ui/button" import { ChartConfig, ChartContainer, @@ -111,19 +112,27 @@ export function ChartAreaInteractive() { return ( - Entrada de tickets por canal - + Entrada de tickets por canal + Distribuição dos canais nos últimos {timeRange.replace("d", " dias")} Período: {timeRange} - - + + 90 dias diff --git a/src/components/portal/portal-shell.tsx b/src/components/portal/portal-shell.tsx index 252de57..320460f 100644 --- a/src/components/portal/portal-shell.tsx +++ b/src/components/portal/portal-shell.tsx @@ -23,7 +23,7 @@ const navItems = [ export function PortalShell({ children }: PortalShellProps) { const pathname = usePathname() const router = useRouter() - const { session, isCustomer } = useAuth() + const { session } = useAuth() const [isSigningOut, setIsSigningOut] = useState(false) const initials = useMemo(() => { @@ -107,11 +107,7 @@ export function PortalShell({ children }: PortalShellProps) {
    - {!isCustomer ? ( -
    - Este portal é voltado a clientes. Algumas ações podem não estar disponíveis para o seu perfil. -
    - ) : null} + {null} {children}