From 274b6f0bc44da54137b46580cc5c6acf7f4ea8cc Mon Sep 17 00:00:00 2001 From: "Matt J." Date: Thu, 3 Oct 2024 17:05:19 +0200 Subject: [PATCH] feat(UI & API): Manage views in the frontend (#414) ## This PR allows user to create/duplicate/edit & delete views. ### Backend All project endpoints are now below the same prefix. - `GET /api/project/items` lists all items of a project. - `POST /api/project/views/share/{key}` returns a static shareable HTML file named `{view name}-{yyyy-MM-dd-HH-mm}.html`. - `PUT /api/project/views/{key}` save a view in the project. - `DELETE /api/project/views/{key}` delete a view from the project. ### Frontend - A new `EditableList` component as been implemented to manage views. - A lot of renaming to get rid of the concept of "report". - Plug everything to the new layout. - Rework view card layout and add an action dropdown to it. - Rework dropdowns using floating UI to make them fixed. https://github.com/user-attachments/assets/3f94e483-ecea-4d5a-8433-50f9150fc6e6 Co-authored with @augustebaum. Fixes #336, #407, #377, #332 --------- Co-authored-by: Auguste Baum Co-authored-by: Auguste Baum <52001167+augustebaum@users.noreply.github.com> --- frontend/package-lock.json | 63 +++++++ frontend/package.json | 1 + frontend/src/ShareApp.vue | 9 +- frontend/src/assets/fonts/icomoon.eot | Bin 5756 -> 5576 bytes frontend/src/assets/fonts/icomoon.svg | 51 +++-- frontend/src/assets/fonts/icomoon.ttf | Bin 5592 -> 5412 bytes frontend/src/assets/fonts/icomoon.woff | Bin 5668 -> 5488 bytes frontend/src/assets/styles/_icons.css | 58 +++--- frontend/src/assets/styles/main.css | 1 + frontend/src/components/DataFrameWidget.vue | 7 +- frontend/src/components/DropdownButton.vue | 76 ++++++-- .../src/components/DropdownButtonItem.vue | 14 +- frontend/src/components/EditableList.vue | 61 ++++++ frontend/src/components/EditableListItem.vue | 143 ++++++++++++++ ...ReportCanvas.vue => ProjectViewCanvas.vue} | 72 +++----- .../{ReportCard.vue => ProjectViewCard.vue} | 65 ++----- frontend/src/components/SectionHeader.vue | 9 + frontend/src/components/SimpleButton.vue | 9 +- frontend/src/main.ts | 1 - frontend/src/models.ts | 26 ++- frontend/src/router.ts | 8 +- frontend/src/services/api.ts | 28 ++- frontend/src/share.ts | 9 +- frontend/src/stores/{report.ts => project.ts} | 164 +++++++++-------- frontend/src/views/ComponentsView.vue | 167 +++++++++++++++++ ...{ReportBuilderView.vue => ProjectView.vue} | 174 +++++++++++++++--- frontend/tests/services/api.spec.ts | 63 ++----- frontend/tests/stores/project.spec.ts | 74 ++++++++ frontend/tests/stores/reports.spec.ts | 129 ------------- ...uilderView.spec.ts => ProjectView.spec.ts} | 18 +- src/skore/ui/app.py | 4 +- src/skore/ui/{report.py => project_routes.py} | 56 ++++-- src/skore/ui/templates/share.html.jinja | 2 +- src/skore/view/view.py | 26 +-- tests/integration/ui/test_ui.py | 38 ++-- tests/unit/test_project.py | 7 +- tests/unit/view/test_view_repository.py | 9 +- 37 files changed, 1066 insertions(+), 576 deletions(-) create mode 100644 frontend/src/components/EditableList.vue create mode 100644 frontend/src/components/EditableListItem.vue rename frontend/src/components/{ReportCanvas.vue => ProjectViewCanvas.vue} (70%) rename frontend/src/components/{ReportCard.vue => ProjectViewCard.vue} (53%) rename frontend/src/stores/{report.ts => project.ts} (53%) rename frontend/src/views/{ReportBuilderView.vue => ProjectView.vue} (52%) create mode 100644 frontend/tests/stores/project.spec.ts delete mode 100644 frontend/tests/stores/reports.spec.ts rename frontend/tests/views/{ReportBuilderView.spec.ts => ProjectView.spec.ts} (72%) rename src/skore/ui/{report.py => project_routes.py} (74%) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e683fa99..1c54bd58 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "skore-frontend", "version": "0.0.0", "dependencies": { + "@floating-ui/vue": "^1.1.5", "@vscode/markdown-it-katex": "^1.1.0", "date-fns": "^3.6.0", "markdown-it": "^14.1.0", @@ -1260,6 +1261,68 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "license": "MIT" + }, + "node_modules/@floating-ui/vue": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.5.tgz", + "integrity": "sha512-ynL1p5Z+woPVSwgMGqeDrx6HrJfGIDzFyESFkyqJKilGW1+h/8yVY29Khn0LaU6wHBRwZ13ntG6reiHWK6jyzw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "@floating-ui/utils": "^0.2.8", + "vue-demi": ">=0.13.0" + } + }, + "node_modules/@floating-ui/vue/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", diff --git a/frontend/package.json b/frontend/package.json index aefe63ef..eb6410ea 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "format": "prettier --write src/" }, "dependencies": { + "@floating-ui/vue": "^1.1.5", "@vscode/markdown-it-katex": "^1.1.0", "date-fns": "^3.6.0", "markdown-it": "^14.1.0", diff --git a/frontend/src/ShareApp.vue b/frontend/src/ShareApp.vue index 86d9ef78..5e80c6de 100644 --- a/frontend/src/ShareApp.vue +++ b/frontend/src/ShareApp.vue @@ -1,16 +1,19 @@ diff --git a/frontend/src/assets/fonts/icomoon.eot b/frontend/src/assets/fonts/icomoon.eot index 65804798f4028a72804d5e20469bdb48c93b8b40..948bc6ff6573aad6839f79a5cbec1b35a5f50ccb 100644 GIT binary patch delta 2900 zcmaJ@4Qv}%9e?lceD>YhXJ78l=bX>y&%{n$yNMltq)D9E*L>93I+BzMEmg=zLyNR# zrJHC9RMtRRHL0|XuCP`r6RHrDs!~b|5<;C62^ggaX)DrHXqhMwfnZD<<6{MU<2^fR z+D+T(?!Djt^S$?f|M$Q5zpXctwi_}4v1MC<%1)iHFAp9OQUHMK=$n~8ID1IhbL*w$ z8(Qt;;hj?ez$E}YcMmMg?&}B^-T|OxH`2==Kn#10{4JtSAUby7;Im7gK>*QLP;=<1 zr{`yT_vGIMKnx*z`@z|zL!c9U0nvLA9a@|{xNxq@{1?$*LSsCC=;>#k1tgLHnZj~F zlIA=dgo^nsIELD{;b&_QfcIC4r^r=6Ba^IM%r`Xk0e$Snm>{7YYE85H7$-m=)yGS!)sVpYZ4pGW)H*%1JZy8 zG6;Y+uoZNJ9#8@mFbu}P6X26z3e127un3Nz2}NjG@@B)Ce6G7aD%((lIGBYv(L$5V z+M6_~!$Jwh?QLR#tl7nIE-bf+aZ(J+;czw=m2pF6@`bqEro(K`F3O0J#Z5fpRSIbs zmF*=Yh|qTBHC~cT-Y#0OZ*EXvi-Z#)w|4GbG83X>9LBZ@r^t_o23xyHmF)^GpMhXG|wnoU? z&>p+YSE*KB^7g_;G?NfT()>4-sr?MC)X$3|Uthzc-@)(>FTuN$)?1UXA{9mJk2agg ze}#5YP?MyI`adLo0c$Pr648hhvilmg#b4)y_0|M3laQ6u{$*aYVXLAv(O4ihdnENJ z3IsuidD#}4PC_{=gmY+b`{BFr6V?@YMwhLVvaZW7>M}gs0MA=jUX*o|#Vc_9_*pp+ zkgc!Efd+uj$N|GzIUbPD0)S2iO73N}a4*;bs^D|x7Wy%mGneRi?U$_G&az=|>ey)8 zaL77CF_bsV{uJ($b#_w}D5&tNQ+4M~?AmoA=T@B?HlxQeh)9f#NT6-7k4NNa6PO$sd< zhvl5sA|zGBXc0CTLQ7J`hnZ-sciLRC57qvvCzCo{qR8~%V7i=UTbN|f8X|07x7%BF zd%f-|Mms&S?Ab|1lY0GXFzFE4!8B4XQzT65Nh{pYlRX=HUbhDo!i~62qqwRd2Mly( z8>dvjiIs-AZgd3AK1W~e?Qkj;{#*N2uUyS^^#ps>RLDxf%N@~Jx5w@FbjP9{i4oPW zyWP21^c!0vv~3rsy1I6pk0fn0SxkrO%b|4JLQgT1smij;1*uTl`l833i^dX(STyJM z1XcgYI{a~apK}q#@Nt?R8k~0awezl44$pSI#F;b1{#voCYp0B!on2kUhq1NdSNHb! z4-fbE?^XQ|V>r%B%|yqky@7~TS*~_<6_5`!vj#2}8rXVwsMJ5@S5^O1e`#oV6AxdM z_|Ssn!cNc;S&(=aM(fktkAmq3Y4a`{uIVrcvnYn>?MDSkgNhQBvjnJC%5xfe=E{}o zLpTjdNVQRrfDrb;((o{;YJu5GxjI&^%m%au9;-I6b=4i<+uQko+pV%kk8-W(mG4lL5f| z9h+V(Wby;>Bg1=(AvvecypU|nVARGt?7Z#5$E^J>r!y?+{LWU6;{x2MjfQsml?Owg zHanfw+H0kJVO;U~l<`8oRLbWkFrLWgOV-x|+FYeFHdZOmYg#J)U6x^0=F+w!m9a`? zP79Du#W#-hN`?HmPrd)Jqht=P)&cB{Q3doGT_yW8P`Z!(Wq1 z6zj_kc5|>zm7L53xn};!lWsw` zt7xP4)fM2SEM&RQA*EzJCG^-x&e; zL-_uWHhc7|eA#?O42mxUbaQ;<(aiV6LybxSSRer%-AoU_5Ig~I5}zk-l6~ZL%1Mn< o-=^NP?X~@d9;8pwx9xrQTlW7rB95wK5jn#yHL!H;y0i-a4-ltSFaQ7m delta 3176 zcmaJ@4Qw0b8UDUI-<|EVeLmZFzQp!9XD4=&#*XWsIF6hDI8M_v86`ARE3Hk_C|O6^ z&~;l13>OVn!B|*1q-Il!GRBz1U_XsCZK!C`eiSI%1XYDPsp~3!24d5M&^A>9U-#ZQ zA)%psx_j^a-rw(@?|r|!m$!Y~MBOhUbma_{HRUe7a8l(xo-?Bpd;%eK0q%nnGvl+~ zy_bG?=4F5B#V7VZj}V?ih(9$wIexf79E~C5x&d}A(;#3TA+ICk)_rTIXHLxBvkDw? zj{>>>?qd_ef0z)L4$-} z>v}|z>ICk>)#@{N7tjyzml;ssTp-8DGa)jjTq zvBBfRoN$B_1n!8yZE(cZtuZR`w70j$sKh%jaHUTLF~T8tWqITZVi1SCD2Sq{8MUB1 z>Ouvy6YWNKpuK26I*2CG4Eh=%6QTu=n95MemP|Sl7APPfN--b=Q8;_c+%4m52aZzi zELmdQN=6at+)>g?DuSYuSSk{^VzJ}nfY2*(D7(rt(`;BEo99mtOq)QN@x zQh-jWz^MR~0B;@}(``6JtB;$KrC)hCBBw8-yvlhz+`BsaF6Y7j8`J(ihVS&`1?@6L z364h@JB6D(o-dZKc(||Vra*{)>x}L)uM@bt$Fyr>cvR56?93yaKvQ^|kQc`0*Dax% z*9sSwYBC`i)vJ9}&6mPVidQnQ(e3zcJfWS#&jm&8q*zlUE(Jw=+F<6jb4y~7xC+{d z`6XQh7D0mni(pYZHy;$2AcM&O<0p_oM2%<{IxL@W=EJYr{m#}W_w0GH)#Bw zKlH_2F4-nkS4%e8<%;{Z2oNHS_dTvUnL_DmO*~$MA0thPzP>~uVf9$zHCl{NyiTWg z*y;5;hc`H~5L3!eC^3th?MrO$+ulc-aEB7pXv0qAonpm9bnACE@+llWeS%>cE}&wzyuik~Uk7^?Pw6 z#t>Ax*+KpEbKdz+wxAV!ei3I`wuT*|Xw1;B0|eNp^n(Bad^g=nQWSU(9aci6NQO_h zq*jUJdg+$ubxNG|I$@W9!v~$-b!TF+;*3)jXE+KgzW6)u^PsJ|NmA9`T&_p^mMl;8 zLw)uare!(Nuxznd{g&nA1N|d~{wY}|V_SVHv9v!{EGqu}4nsv=|cYq zV6pbWSS8iJ*}hqNsxdEn1bIOS1cU{m$#}?uN6t4clrOI_hLCL7uxrd~cgunxPwJY< z4URkwZl8qMY+pWRh&LxQf_obm@S$6x=<(DK><>np0#&^O$p)q){6-{>a}D@%vZ%dN z&r5E;*KC!XS`))E^qcB};~Nc460eO;DT>8%ARLx#ip^$|SoVM}SdyHQCP^>Pd&?JjN#cnXgTEK|>P8apl(d)NslLK3l-`SVh5~_5XRNweXbvffr?XKT zmyP;-(JW_+W}i+dAy8-+UriQqpF!4p^)cA;Rx}G+9)@3zNQiM@{qo}g_G3ZD-pbX6 z^SBK|3&8=JZAnMdk!U6ojpJw}%7hs8y0gj^ZN$+~BHW6%gE)uNiHyqf2TKE(#02TE znC-O5VtU?WHkoXU#ZHj~#w10%SjIt7W)rTjt<8x7Tkr=adU|#Z_4G^x{02YdlrSbt zBnEqu<{fYH&?%LJ1{uY!RQfD{jXsOCV`wu08qW~qlu$)d#{aU~?N-fWaX2iSd7{g1 z4eG+6)&2v!Ro1P5pyl84?YMMvh1d&OXaFe1r-U$%?+N|>9pY;F{SeWrS1I(nK2E-H zDR+R|@n6Kkrya&y(&;N3PxuX9g(wJ_^_3d~{K%D?E5;`a1mx;^q>3775F)8#?iqE~ zZ5{=EWmu61eo3Cfqj&{t#B+p3&XOzS$EJON?M2gj6ipqWZqUQ@BK;3$khyMlncK{V d%#WIXW?nHe1ku;^KVKMx`b)vR9QOPe{~y{!lK=n! diff --git a/frontend/src/assets/fonts/icomoon.svg b/frontend/src/assets/fonts/icomoon.svg index c75f27a7..df16e710 100644 --- a/frontend/src/assets/fonts/icomoon.svg +++ b/frontend/src/assets/fonts/icomoon.svg @@ -7,31 +7,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/fonts/icomoon.ttf b/frontend/src/assets/fonts/icomoon.ttf index 3f16b15e9cce59983f636e3d649aacf59fff8a25..60f856194aa6cc46c906505537bda0bc701686d7 100644 GIT binary patch delta 2929 zcmaJ@4QvzF9e?lceD>YhXJ35hbL_Jd+ez$T>?C&l5t5kLM?P>UED1$Z!?GlhhDks( zs0M6f9;*bZMETgU6|_=KXdet!3Ik25CMspxmxeTHWmUxno78Pc)7Dk%x=rgQAN#*^ zAkeCP(!KZlf4%?v|KEH6d$*I{T316DAtazLBN}yYo9fEAo7fa^ZooIYYv0_Gy5rW% z2w@HA?Td2f=)=IiyJul;cV|oCU4)vpgFL?n81@?dJD^Vj z-L_}nvxlDs0Q6Ppmi9igt8cD zc))Co$otSCi$tWL5Nb#3Q7`I4B~(EpXdG=rPoilwix$v+bO;s{Wfj?%jb!q<-j0}J zgAO=~ai8+l=rAsnaNOSREzovvBo|TIy>Z$bQ6iCSE~b!<&g2Vmr9F(ZIlET@MwWE( zj8843aZIt7KoDi^>g%E`i_{G1&1J7hM$xO^w0fP;H0>8%Zl-LWZP8owdA!5CQo|Pj zm(2veUB9R6R8ZH;fjFg`i&Q+IKdwiitQ!hW9`~-1w`SA{q zxkQ?7c1f%mXYg-r^*`(SM*JI_*@55Jq1AuV7G(5|!8V=2*_O-D-cClO!l=F_lGH~F z5<)gB!R@f4E@M5@EXuxqY~?PAA#cVMld1h2A`OaOuUNlFyx$@4jws{1Q|3EUxFQ$5 z=AUdfulOA6Vz4I5ll6bf;sViH5M|2B6xe-@*pjb%(tKwU%w(`~+P@-tZN#c5Pg(&I zv&Rk^!=M(_X*g`HGfAvurARKD$q(Z9@F&cx_*_^qPbuNB@?u!Q2P|;hy!xUNhBLd2 zCr+GKLLtR`K?zv^A5=oRxqKp|oJWYU0zV6Z`_MX6MV~c}+qU4G@rEs4`z3F;^L)gY zIy%-q(rTV#IK~&@e~Nc2VSY^%Dg^K~XTY61xpnKwoIBurU^8|!1?s&0300EP!G<`P zmL&CqG&Rh7Sk}V}GVk@V><=(a@v<;JeT|d(HHpeFzjuhN+T5(NqT|4-zN&gb7qsr( z3nZl`O(1eXYm$-yU^Gb&7*bO*K&~!j#XDoX!4B8{8crs|_%K7KhlbMSG~dJ}Tg+j~ z=5xD!RkzRQt`fA_qbQ!ubSxRJUu#J^y!=oal*x>m^K zH{y@X>Mf2IoMGjgmw31c=E+u5HzbbSVLtDzyt zY}77-M3Ie)LSOCmQob;u`u*xeAzv!x^OFQm=JO@<>mhBvQW+nwly_-bD*ip5;{)84 zu0xgaN@ZRP(N5Jr0eYoEe!{O3Tp;#<%{O7+=gGd$SIXo1p{^@jfaAC;iBw}oSkmf0 zV3{_abRMmJKT(7luAP=OO+HPzt${F#iPQCWS3yH&uIVL;<}pIe63OfuqUVc=GlX}B z@W|Jo62rx>u zcna}gFkbKo@xrC9Xaof2FK%W=v2Jw_&iqN(@*;v)AC9GQ+=zJmeAbug#~U!5JVW=S zo8So*i}uEfxCa#aaCdKy@tkLNdq26#6fN>_;uxNJh307ZZ#qPVrZHw1*2_CUz|j~= z=LSV_4u`Z|6&;R>zDo-M)+^(ZABusEsMD-w$t_(c8ER2-nI|eZ`uFrh&rl{{a{I+)EYWWo`29N N<4213dRG26{y)-bP<;RZ delta 3100 zcmaJ@4{RG(8GrBo+CJOovwi1FY@dC05+`ZnxOSYxaq~aUrKy|HHfvBTXwx(#bp4Zc z1=0a?CkE991lFt6fZ8dHQKT_oN+dK;ks<+#=m2$7nTk}6YJb?YA&{nlDh=QG?wl^! zvhL~bd*A!s_x-;2zWcs=@9D;yTS^EcggA5-ktj8~w>iywnG{H_!h2})_{3~r-%CG2 z2!~)iHa+p+Ec6~2o4sxN*qJZ<=KO(YVLXSBaCT;D;z)xu)`XDv4$QaCfPj6Byp530 z#H^V)etPbKRp5|s49M4xot(@~6b=t7Ah-^Kp5qg9vnYcA%nwdb^u)yRsUHm6--j^( z;BU>IeDE|Z6q@+_6VGe7`aIr=tMwxOA`8+FR>&jz9d^2uZ)w>jLEF{RlHbT1mqMY5 z-k#y%p5BQNv~?aI;l-o8DDp=|ew`z3ZN{kJ8Lw}~sNgM${O3LbV!Olpx1MNb)(LQtl9YRy+IQkL?RsQJ2dTknpRVrvlsY-UDNhlZP!7b__fHpO>{T-n0Je!d&zXV^dX1$gAc08 z%^6M_zekt$XuL(t zi{tZmH6d@;(iE0TQAk1c`XUwme4Nb)YBrN@?ZmI+N#inpCL$SUq-a!Hj7a#L#mpO* z7o`Ys3uY(h7flgpL@Wk0BARh|J|ZoG2g(Y@Pr-wP8qrR4N>}J2(TVl@=|t%&XR~o! zOm2R(m>5=#M`$}G$GGcwPKr9#ry?Ce{DLdwX}_>%&xLkR$aUAI_-HezbGFaOUT-Q~ znGUDCUisr=;s7TwjMu?Q4!_JYD+Eb6ByM#7RfnH*co|0EtQm@W$Hp^qFeo#;ZSJmA z_Jc0i&pq^RFzNLwPPw{Tb}C+PGPp^A5Mk~2lzxmUlx{_n$teB~Nu>q`QiYVmZ%;;z zCPEE(Jb@8UAmABU=g2~nT0XBf*?rtVYTLlJ0g}R9YLmfOdXnIgDi)GQkRYsUaXkfb ztwveYkBXHV70kR!;%p0)qn={7mlC&QsZ{L!`lJ*XY02i*?$EZF;lsagtgCJJKx?n9 zYuvIk6jVJzTV3syWDQI2aaQx0p{Hw_Sz03xq+;b0u~gz{XFl695|G?(?2Dz0sWr>m zIzWkrLOZuq0RLRy?K*Ae>Af_&V_?$N?W^a5PHipEz6z@iqNIM^q3RL4t(5J;oj4Aq z5D3J@41q=qh3T-Ogr^II(G7eW69iRH0R?s3J3LJ0H|ebD8;hQZyP?4y@pyvV*|W|d ze^E1|Rmt;;{|Ji1&pRDZ^egt*?hSr6`aHUQJJH?!DUMz#vT*S$7odNg zDAR+GkAsC7MM*U*+wG2!eL4N`;AmlRT2aWR%{CP*;|~>$iv8fj>1BJ!VYe?gq!eYQ zFgOZZUpp#RNj>NMdg;l=yy6#?1u+~J7l^G^r-*(f-?&h|vBp?JvSD4XF>f4GL{XVC zXQtLU@*J3b2(eMWe9{tcbY_7E8W-^4kD-`&deQYrVr#gne<3~&$%02Veha-`%P*wgYU+-K!?EtB>SnPyrly{1 zOE~$qL@=0W)TM{R7xFwa9*Tk3}vUDrEC=CWbH1RBrqmv#>=s8 znzm86zNRK8iCiHRp6u(}S?ueZ42LYf=#eodC=!D{$q4RO1<3izKLc55t5zx|jID~P z>%>qnB9xFN$f>c4q>TURaJd|Y-|lwXH}XV}%Mme!5r^xCE{9@j0YS?_p%V{(utMwu zFAS7uM#zY90Y4D?-5bQM^4l?@-Aq99x()5j&tJ+N@a)V=CyU$4%;5L{ld0l!AH=S&HE`dLKj5ea>@4-{C~VAf?xmu diff --git a/frontend/src/assets/fonts/icomoon.woff b/frontend/src/assets/fonts/icomoon.woff index cd3b752a1b93cd444586096a631006473d824660..c73706aa87c68164074132c5685d411c58f43ec7 100644 GIT binary patch delta 2986 zcmaJ@du$WQ8K2o*ukE$H_GWjzj=gsLO6*|lBzF7=Nu1bA9ylCa63QP0r6iDsN|2*) zD$oj9w9?U&I3QZC2h#OQQ6Cf`>kFxBk4h(6Xr*fp^>sbn_1db};%&cK z2LgAx8|j<*eUJHO=bLZ7ov*KdXPtC->*y%L2%3BVIm|Jx3tmwVdzs~wR8Q~TKX(8j zycbm42r1idy|g%Yh{&!3Z(AkL;=ZGw0@-2B;?tl6S$nFCVe$fGNg!VnP8|=#BKEN9Y^=~h{wSVpi7>JjE51Z7D zKDRWte*t7ag~gvCbiQW)?}4WdJqw ztSotRk!&H~+Yys3(1C=AKoTo9*_^e>(;-|ePV5jhgc)$s8F^8=Y4#f7Y1+rT z+)Tw-ZqwSddA!}YjL!qC7)gA)aj&jvR6x@z{sg5Ni&Vm|J*#OaG%Xq{*em>w{&{;L z*1vG2MayLg&zbqTDIVDN7&jX!DH5dO8r=)mu5&>FAC1B^Blh-(ba zU4r&jDk2oew9OkLDQ&DMAY?H^-VSH%LMn=&A+!a;x)#%j8R33xMmU9m)4$4O>py|r z2Y6BB8~4Z;-68M}FX6k>#@o}lDwRazO^Ze3zsR~6tV+^U;~x^gK&%#ci87-H^PVSj z33X2yZ%@HA31&I1U*JUxnN^ae%uSPNkEJ8Spf=R0Z?lAFQ&`RkkvyFDAbt-&X}pZj zhGgTE916)Vgk*fs1kV^RzaWPovsdusiF0x=C>zhoK@;FZa!@l?P6Xw12r*_bmLV8k zv<}tKXY^Z^%{Z@z*+l*4oYl&45pVj~c>8GBILp`>Z-o01-Xn*&HC3?a$5)+xcmCv- zEhqDCzw^PI@ndN)=d4dCf{+O`)q#v4C?Djh5zfQ19?l_gqL*dAhiQtF91}BF?Gm@9 zQ5fcTHl9^lTNReK9bC0n6cKE}>fSwnN@!6XMDI{rgp?lyEy4qV(30|#3r?B)oz=tE zk@{alsZ8_s}n zg`sgKlrkcwo$PsF=XHBzvQ9Iuvk+H5$|DW#v3YX^$=i=JINuA`Qh(DrP=6W1Yz{e<0*`=i{-jtdFvmEe^k{XXA^}l!c{B znQ&t{oM~U^D`m4aS$4TF6V4c)_qg-1crqD}<=vh(e_(7C{;*@fxdbtMf@MdBXPpBb zysOnL`>SP7w^57rQcuq&8QP|vp3H_cyFXUI1}*u12co=krW4-9U>WnqJWXU*AxLriEYYox!rQ{hRJ40lInq0EfST@DY#nwzpO7aZe z+Vmo>`4d(ByRPGjkm}dQvg_D#{Pp%OyrC2SDm!M}>l74G(Cl_aFw!>8%D$oRY577kq-^gQ`51r3!+g-{+TN)W3X!$Je+?N;OsRc?4hM zDBcKVo0M+FxDKQKocrAPo%B#lRbxYGf49^fiFREqCLEq(A`nOvJ&r{2VplW*3gc%t zvtw8@y9cR%5{|t@-g`Kf!3ll8$H(Qo*?znML-y&XJ((7GZ^fd$u@de9gFf8d+oS)& zvuj&FxzQBObC7im$6TT9H2gkoJVVnMGYl(oHc;4U3>9>PEIUVn>h7ursiN&xgMhW_ zgy4gkU?Ju-E4Y3y_^$m?7{u__L#XlMOzxBJfqxa)~MdVs#pIGJ(go6Ns0J1u`#b gmi0fjsI6vOf|>9ptmyzxR8-_vicGd*8eJcjL27;(cR7LkJ@nat&h5S-SCrxTTbwJUVd#Av^=f9+Uig zU;WYa#Ay?|3Tyi+@zY1neg&|X5E9Or^xG%*zc4d3aTp=rAn3H2)FAI^o&gZB2Vh-e zlI>CQ*38i}bATI64Iw z7~*f6^n(${M<AGA-Ga;>d9Wf(hKTXP#_ z<5Dm<(bqdN(%Ux?gt0E;!<;nBNfI|JaqALsbF+_%KjW>r?K?d?@JKBkMp}lB7I*6vwQS^04S(K4PIh~D>b{<~@?+xnc&r z@`;4^K@6(O!x}Cc|A#LBad>zDW>^u`1sLTY$AdH-j1S}9D=Cy>eV!^QZB zW}K%TloDnCis$5rbA2n^8Ne^QgM7zRyLUg;!3W)IHpPcqK%KRJLGgN%p~`kB>Gdji zu8IAuz%X7XD?3HS#w-&g;gs32eOH|#>+~{=z*;*L^-nv;D1m^&aQ3-1R1rZJ?C0+P zAdv8SRhLp-t+-ULHxbwbAVgUAJ*EGe$(L?M5{U?YoTQS2gUNi-DLN7nqnXhBJntXn z{eFIQT_W?%TKP$>+2LaclUoP34w4k^)|w5*(vt;Vu2{&tAVXSL^Lh&MT8%Pj02M18 zDwuVZz?oL)J^caOwo?37G?|QkRG*Ojqpg{o))U+sHGKG`#=6=L9!5uPUE`J=!GOjK z?RB-!C2DN+ZdWyz9)7l_#YPv%{mE$gSTq@*?aF0ZNBy$LgMHDYF|}%0TL)eu!QhT9 z6^DPW?{S}TaP&R2ZTsM)yT@101zd&II{PM^Hh>a(kkv{m4&yW&GSiK_a17eP?~h4o z0*&SK(;-z2P3QAt8{`xw2&$d}4(hmXWQ5FZ(pl9v7WuHJp}`a8`2c(FoGZXxESMRq z)OzI%VFJZ;l@05x)3Iwqn2jN4w{0VOdOj!4o5m~n;x_PHdFH0)AnJnWpdYb^^z+V0 z>5;pg5!bL`7t4;1v#cv@PM=!g<+~hd?7N?-ziPeV908Pq?~|^v&82iV?!kA>>+4R^ z<%&4JpRjxsq3-P-`lp|B=|8!`POy0~!Ln?GEz%6OF|R-l;Cl|AyMvGe9x|Y%WJwU2 zm=-P7WrWtY^a^pxEadXM$%%`89-b0N_#p3JGbS!ojB&bR3@^rtE&k4Xf-DOb+hke^ zOpM;mw&rh@zWL0!s~LMvzsio3{?eE2ytfL*y`9;<{%m${m7-Mb&1U-9$MWbRr^k8bq5p+5nOASd?oyiZ4!PQs4 z3@fDmi0fOWryFysD5(olC?qWqP1dAJqMB=5C|_R{ERbwi*K5ofM^s5tr_7zHb&0$H zW}mp&s9!#A!5f2F&i#!G_|P3*On>@<`+U49R5dW1YOr<3UaL#sYy-ZYDj09q3yMz| zusaprXtJ?3<_*2qb6-P~A{cw7HO=8T5Q`};&E;|_EPKEN78PDmctI(oh&DmdZ+VWz zNYXC|{_-_JQ3RsRlJCa@rjjD?it#&`^^@Gf@Vm`D(NHMb(_GymwM4b#GwpE~*B%c9 z;_aL(-u_Hdi^7I+?d4Pf4_a)kHy?-c=s@O=WDNc-^j*B-^mpPM?!?d@`e{DljW^uYMV zvc%?tp~?RK9mW3s$xzUei@bs{L6I2rNk;IzAwa=bUKq%#S*_A8A#AlvT^ELKk)VGp zAge_yNEyHHbi18~=*y;YE+o_sbz|hjP(1k}nUMBWJ6b3Gy7Sd8o zz$c@>f19{jelJRNnAHbuYmT!&do_E2+xB0?!uJ}+o63b7>oYX=ihj)-_N}bxTS_Ji z>$klp^oY-1B;iNe8crm_uf)@M4_?Lw@jPLWC&*>;Hnk7Zca3_NX6RY^LuQn@#QeiH dV!LJc+B@xs?2p-hZeO toValue(props.data), () => toValue(props.columns)], () => {