From 500aebdf16b99a0676359074e9e35833698aaef7 Mon Sep 17 00:00:00 2001 From: Kyrylo Petrov Date: Fri, 16 May 2025 17:19:58 +0300 Subject: [PATCH 1/2] fix(my-profile): minor fixes --- src/app/core/services/user/user.entity.ts | 9 -- .../my-profile/my-profile.component.html | 87 +++++++++++++++--- .../my-profile/my-profile.component.ts | 4 + .../affiliated-institutions.component.ts | 4 +- src/assets/icons/socials/academia.svg | 6 ++ src/assets/icons/socials/baidu.png | Bin 0 -> 24641 bytes src/assets/icons/socials/github2.svg | 19 ++++ src/assets/icons/socials/impactstory.png | Bin 0 -> 22627 bytes src/assets/icons/socials/linkedin2.svg | 10 ++ src/assets/icons/socials/researchGate.svg | 1 + src/assets/icons/socials/researcherID.png | Bin 0 -> 94217 bytes src/assets/icons/socials/scholar.svg | 1 + src/assets/icons/socials/ssrn.svg | 44 +++++++++ src/assets/icons/socials/twitter.svg | 17 ++++ 14 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 src/assets/icons/socials/academia.svg create mode 100644 src/assets/icons/socials/baidu.png create mode 100644 src/assets/icons/socials/github2.svg create mode 100644 src/assets/icons/socials/impactstory.png create mode 100644 src/assets/icons/socials/linkedin2.svg create mode 100644 src/assets/icons/socials/researchGate.svg create mode 100644 src/assets/icons/socials/researcherID.png create mode 100644 src/assets/icons/socials/scholar.svg create mode 100644 src/assets/icons/socials/ssrn.svg create mode 100644 src/assets/icons/socials/twitter.svg diff --git a/src/app/core/services/user/user.entity.ts b/src/app/core/services/user/user.entity.ts index e90d83eb5..701e1a52f 100644 --- a/src/app/core/services/user/user.entity.ts +++ b/src/app/core/services/user/user.entity.ts @@ -16,15 +16,6 @@ export interface User { dateRegistered: Date; link?: string; iri?: string; - socials?: { - orcid?: string; - github?: string; - scholar?: string; - twitter?: string; - linkedIn?: string; - impactStory?: string; - researcherId?: string; - }; defaultRegionId: string; allowIndexing: boolean | undefined; } diff --git a/src/app/features/my-profile/my-profile.component.html b/src/app/features/my-profile/my-profile.component.html index 5afdbd793..d949d67ea 100644 --- a/src/app/features/my-profile/my-profile.component.html +++ b/src/app/features/my-profile/my-profile.component.html @@ -17,12 +17,14 @@

Member since: {{ currentUser()?.dateRegistered | date: '
- + @if (currentUser()?.social?.orcid) { + + }
cos-shield @@ -46,6 +48,59 @@

+
+ @if (currentUser()?.social?.github) { + github + } + @if (currentUser()?.social?.scholar) { + scholar + } + @if (currentUser()?.social?.twitter) { + twitter + } + @if (currentUser()?.social?.linkedIn) { + linkedin + } + @if (currentUser()?.social?.impactStory) { + impactstory + } + @if (currentUser()?.social?.baiduScholar) { + baidu + } + @if (currentUser()?.social?.researchGate) { + researchGate + } + @if (currentUser()?.social?.researcherId) { + researchId + } + @if (currentUser()?.social?.ssrn) { + ssrn + } + @if (currentUser()?.social?.academiaProfileID) { + ssrn + } +
+ @if (isMobile()) {
@@ -65,8 +120,12 @@

Employment

{{ employment.institution }}

- - + {{ createDate(employment.startYear, employment.startMonth) | date: 'MMM yyyy' }} - + @if (employment.ongoing) { + ongoing + } @else { + {{ createDate(employment.endYear!, employment.endMonth!) | date: 'MMM yyyy' }} + }

@@ -87,12 +146,12 @@

Education

{{ education.institution }}

- - - - - - + {{ createDate(education.startYear, education.startMonth) | date: 'MMM yyyy' }} - + @if (education.ongoing) { + ongoing + } @else { + {{ createDate(education.endYear!, education.endMonth!) | date: 'MMM yyyy' }} + }

diff --git a/src/app/features/my-profile/my-profile.component.ts b/src/app/features/my-profile/my-profile.component.ts index f20273769..70b197eba 100644 --- a/src/app/features/my-profile/my-profile.component.ts +++ b/src/app/features/my-profile/my-profile.component.ts @@ -56,4 +56,8 @@ export class MyProfileComponent implements OnDestroy { this.#store.dispatch(ResetSearchState); this.#store.dispatch(new SetIsMyProfile(false)); } + + protected createDate(year: number | string, month: number): Date { + return new Date(+year, month - 1); + } } diff --git a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts index 211e0160b..a3de613e2 100644 --- a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts +++ b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts @@ -2,8 +2,6 @@ import { Store } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; -import { Button } from 'primeng/button'; - import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { UserSelectors } from '@core/store/user/user.selectors'; @@ -12,7 +10,7 @@ import { AccountSettingsSelectors } from '@osf/features/settings/account-setting @Component({ selector: 'osf-affiliated-institutions', - imports: [Button, TranslatePipe], + imports: [TranslatePipe], templateUrl: './affiliated-institutions.component.html', styleUrl: './affiliated-institutions.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/assets/icons/socials/academia.svg b/src/assets/icons/socials/academia.svg new file mode 100644 index 000000000..2d9744546 --- /dev/null +++ b/src/assets/icons/socials/academia.svg @@ -0,0 +1,6 @@ + + + +academia + + \ No newline at end of file diff --git a/src/assets/icons/socials/baidu.png b/src/assets/icons/socials/baidu.png new file mode 100644 index 0000000000000000000000000000000000000000..ee4fb1209565517ce5790902c3ec33e194bfb803 GIT binary patch literal 24641 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 literal 0 HcmV?d00001 diff --git a/src/assets/icons/socials/github2.svg b/src/assets/icons/socials/github2.svg new file mode 100644 index 000000000..84e4d2f94 --- /dev/null +++ b/src/assets/icons/socials/github2.svg @@ -0,0 +1,19 @@ + + + + + github [#142] + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/socials/impactstory.png b/src/assets/icons/socials/impactstory.png new file mode 100644 index 0000000000000000000000000000000000000000..26b1ae756fe910c5d8ce1b1b1ef8aaf3f025d293 GIT binary patch literal 22627 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 + + + + + + + + + diff --git a/src/assets/icons/socials/researchGate.svg b/src/assets/icons/socials/researchGate.svg new file mode 100644 index 000000000..122d8b666 --- /dev/null +++ b/src/assets/icons/socials/researchGate.svg @@ -0,0 +1 @@ +ResearchGate icon \ No newline at end of file diff --git a/src/assets/icons/socials/researcherID.png b/src/assets/icons/socials/researcherID.png new file mode 100644 index 0000000000000000000000000000000000000000..150bc4c9982befe3e7a43c526e1e06cf65e641c7 GIT binary patch literal 94217 zcmY(pW0WRMurAuxOxw0?Oxw0?+kV^Awr$(Sw5Dy_w)OUzZ{NMnz4u41%0Na$KC4z_ zR8&SN%1gk*;lP1_fWS*hiYkMEfKvS%0x(ejI8TfEJpUe`F3J+ZAhpwYXa5KY3n4in z5RitrAD>2$|7ci8Ni7!;5ER${2I#gqq36E^zpYd?T{Y!oc}yJa7>rCEjLjH4?HvDU zgMjb~csd%H*qFHz8Jk&H+4E7}boEgZS()-tYOu>O$vKLcSz1YYJDaI^%d47r+n8{h zQVQ_H!|{6Z{Ig(Z=4wRbX=iKi!sE$D@;`=o{>}emGm`N0I-8pFD2s~!uf{)$kHpf| z)scsh(Zj=o!Go2-!P$b5nVXxNk%@(og@yi~2EB`yy{nNYy}b+R{~8cAb1`wYa&)zF zuqXP@ppmhIn=2oQo12v>kGYXKyRoS$JH4?Ht0_G*v#Aliktr)Py(ufJDJMHK7mGQw zG0FeBceOJAfBoCL{4Wgu0Ac)3!pO|P^k2yT&3P1@t<3&K@t;e6X5Rmi|NnTrjQ<7l z{|Su$|Ka@G>%ZCmMdm-4{x7;__WvOD_=g=`a{3Pt5Fm<_sF13s{^gg?Cc~)H4zz!j z&#~9`%O+wiG?8Ec1Ql4ms9t%OnG~(oJx#==<%;S)S7bJqdZT;ewrSF8#1||X<=Rl| za_rS=1m3T%R8tuiG965v1bS%_>O>OqJE(XfPjJD_n-idZu0`W)UyE>(b%(rCwosOf@9``()_i|U8-NIPsZ~?pYHy8 z8l7M5cwZv*jvIH(^IPf6(%-zR?W(h-M<3#=vOA5Svg7WK?@yUT{fIbk?lb9~u0B=< zN<9W7eXEYTQjG;6$+Z25iINu?J|CimBi~s}8b9XkFJ*kE3Y-7L-`MjfgBsAWjQn|V zZ@vqd_gk>Cj8Czb;b~y?Uhldu26APioi`<+Bo;7v&X3HS-8x?}*RIej8*j8$3~9 zj+=@1weG_!*!8g z(eKpdkMl{he(+ic0CpTQ5DC2aXPUk<1IZSSTeNm0e0-N#Ub0a-`DZdmRJSH>RAwH<<4IA21 zmbb+BC>HHgmE*vI&gU0ng9yORkgZcmB}+szA{D7tJ^Ku`bGF1pkwoFlmwpOSL5byy z-be=+AT!_XC^ONTgPNxYh_0p19JWmYKKe+JK!MY;BrHlPJ7H9;j91*~ra<50JiWj- z7Z#tilbEQ5U(el;Bf=K!-pB_Q;};8lQ7*_2X)x(iGfmxZhNe!k(X?K6Q0m4$fRhKX zAgtsDSwK>H05MTmU_~IDVURH!GBX@SzVZAgA1Ms`Bu+g8Q~B6B8HkLfp?q5=rl&0|pK2J^aO!t>(m539!*J@2O9F@9UE{afpA%t7tmA|k+_q3y!I^d)FN2UL4N zK$)a%(rL`im4gPr7ev)uz-K0Qq_p`M|7r4cq?71 z(f-*L+Vyo=^U^1k+i{`YaeGJ!vD<-*@x&@{e{;S0RtDV5lD(7=d%Gq{rXbgpx2z1O zB8Tk{@&XU2geZW8k&Xe?b_8?VM?(@b4@dwhoi-Cv( zNvkL#Dgpw_+qm^jkBFeHRQixXlBFd2S!9JR>mdy4Sc`zx!14|TW7!$$=Fl{{>AXNH z26b7tht$XK!~}Yw$tLCEXv3s{l>6wZTx#WV(*8F${1&^qB1&j_WK4h<1ans;m1MT!!khiCdm7nZ>J9o^%`ijCvlD0Zc*)5{MC12^?o^% zdlQw|NMdmMINp77-BpzBz_|Hc?X=S3{PAPs>2a76qqQs9jI(>GUO<4Tu1fh-Z1x;Q zNe=4r&mvpw`ZQh%t-NKY1}zpjvYWB%{E!f}brN4DoNh{xmZV6Afll(-4c>s*Z315f z>&_YW*LS?Wcp;;H6boZz^yLLaOsGGqENW)<$`xUj&XMfCwmOTgq_nCd>L8y z37u3(2|eH2hzBohE!Av_>FJGG+Orwcyi33Fi)y7lTWHpz@cd(Xd$hEWLH*sG!?#I@ zOygmlGU;JaMdae!;F;~`MS;UH+@snB$|GP`t(yPg2Cm0r(&exz-?&~8p*zFi8-3Conw$(1Ti@%Cl9t2fr2som{l^yr#?y_t7~&K z%sLnrcy{qCyOH?SexW}}zzVEtiZUJ=ERl4|K;FZP*Kp3N9(u$DiKR_#YxGOegeVg{ zNaPGEEED3fs+x4n%YhvWXb5ph>`|;*q{ka=_fLU5{shZdK3@`q!gX!ms#ini)D!sJ z^V!RW2EXH-lO41x76krwv}kZkFq6kih!HK{<~Rt)PAL|nRm8Mhzxpv7pcNdboRG4- z45%)Bi7tCt#AFfi(Xd;BaLrL4;4-1?+h+TYbWq3tbo8B+lh^1bePi#s&!GJz_w!Pg z(aoP|elghKHTh=Tzblb!(~ooWn*!8xVr=24p=L^qyV=f7LP5Pnz~(PN)@gx4C_paS z7(?C0$a2uK2y1yNLeUo@l|<7?HbG8AdW>J5=|Wm;ayFk% z13xie<=ru*xMHP3a&;WcH8Dds$hlOC0$XBdf3mW)2!41nPzFp?H*_c^9wHNOSVpXc zxInq$y-nN>p9!qG@uiVqdebKfZ3b5%?rQHTl4HEvNsnF70-^~bYm!?EFZ@NredsmU zl8`XOH~*IB7*~WQV`)ObZrL5C#!H9kc}_L1K_FbbAM%EzU?dY+@5qk`Qq2WP1A6*W z3>ckSx!irDxIhaJ(} z7%T?y{SQI<(^)XQ$f%Wb(#lFmq2Mr2J!>Htad`B;YGD4l((jt=XROWFrH@1qW+OsB6q;XL^$+P&-I>x;F^x65P0FpIXM)z?rfSw4T0x$}l1s(Lpa zfQTlGlrx(y&ad!m>iPWI?p*7I9lEoVH?Utz*3lSBtJC5Xk$ z>=FbA_Y*~YK9-3^h)D)ao7ceRP76&qg_ypLxFMqZ6!j~!ZzF@HIPvU?xPt5L2e~db z$aAHe&tgYBLAGLW;Bh{R;OUHlnJK5HbO2~rku}@0mvJFPQUj^QB)NV&_(6Iphhy&e zCx5yo)2{CMMpENED>{piJ=h|39jm?W1Lk|z2rzrwL00911QiA&0=Q|Q&wE4Ni)~pG z(^N02^G-D91?4a2JyIw8>?re<|AMIt%_T7`c@PpGp&hJ-i{kxDR>1{=_ZET zvi$wokoU&upZpK3e`#tjw{4oo^0W1`9z0E76xE=gZT_kE;^7hHsY7S`eVKF2Vaqh? z{m*h@>pL^+oBz_lWM(=$rGIoO0vIgWH9XVP*x!69CVBn<8H4nw=?Y=Za#WPoY}0E~ zW6XCy@zO9?raLhtb={xK(Ju;a88V748ws&t_8NVKs|ja~cmRfUep@zu``A_lZ)z>? zocu-V9eqP1u&|7fgeO_WOtM5j5S*7L0*sTI4!+^SF&p2a$wiIH5*oml?0&XPa0!w5 z9tX-35iaLCiT{k+vZ_*3jSSB6Ni$OqLX-`uQmJ?DkpwsOUFZ5T6*(sPD`)i67^p+C zazXe@m30(<#DMUUS|=a)h^n}Hmn0~Ab7#$xYWL1BP#YNGilZH`PYd9sf-KR~m)$k~ z3%BRJOlE27=btj7yfoRhD52hmil6=`b1e2*lQAj##H$LJ6X$3M;YexWiTG!yYW zU}yMp-o@fE%b_a`YA??G`Ig#R8E2cgJGsZVs_N%eNFeh5*&H*2{06Io3sMu)kJ)HQaUR4BwElw0LhqM9<_aYznBUIIp~ znXzuvJY4Kb9t4@xDVp*Iw!37}lrDvL=OigxHls6Ng=&D6HY!A_Huie`d29*)ovYZQ zHaKVf`H2+MZy{EcD+m6mJRB(@4n2jZSQtHG>hNbXHw`^S6@74o3Wea>{O56^GNy8B zTUnaJu{{&8Q}rTy`SS~s7S!B2U_rXZ&DMf$y}Q`LcOFWzV&`FT28h}BXUhZApR9WZ z(KFNgdbh+<58}GD?oHwPvy~Oo9bjc)!=+mp7i-rt;u;gKM5fvRLKcNhVh0~FZAv!l zDRM7@B>?8U?_+v$xVN`c@#W;>=xU;5OeuTZ^psTft}3}7uu!}sBG5;AA3yU8 zK)7Zr1GI55eIwiP$N8>vBxRfIjWp=_CoDUl?rbU1+egXSQf|)s`c~(wdkP%CIl5T1Apnc>ZWfyJMCRFC9 zrJ`h@c75XlrUs~QtZjrsuB8~5A`ez&^+`sMGkW7*cXj%?^Szd z!Up=}QaGv$W4imgTvh&$}0#NNB zegJnc=|27ok!*Le&y;M}LV@%^i^C=-!%RaVW2VMr%c30@M>iw{>jjVvM{o2$-0W0M zyN01$g5G=q@r^^t#_rX|Wz$>Vt$E))`PIM*)q)LM5Wg1 zk?AV%9$e4dXV2j!X89swK7W5RIVCOR?RZG+fy;UID(%yajdLQ7@C2xJe zs>0jF$fT-UsANP{)gnE~mn;UzUeid#aA?{OirQDOCBtb;wYxhh!YL=aA>qVSL(c1s zO;p23!ow!jCKbmNI9R07!I|TPg1m5}zkoV3yU@svz@M`DVAg{0fcz-(6 z+Qt?}gg$I7AOs>_IjMXu>^VN2pO^wus7-WSUBaq`YgV#AQ&bA4UIG(+jlJQRRv{(+PYPZgo{a7e9-0IWGn0~v+dc9Em@$etbc1mO@qRqDvHh4L6DxWH zNv(aT0_6IpwhogDWW^nwu{F?7zqn5#OBg-L{1`o9mj>DZllGQ~7$w2jH*!jK(w7BS zbSM%{MQs6j;tL<$GCYE7v@%?%B)^mt#K(_zAJlfMHgvs-HaRH`b0fzLdBw7a`!nh&%h!r-rBMiW{ro)rHdqO*%VH8~bV{#cD4K$_zaB;l)Kv zzc5`!bfyNh6bdVAdq&44u_cXPY-(g6V~c5*5gAd;`U7WLc4v6jvP#=O+vFmW8Wga? zZJ@#erKJX@(jY29RZin?Hxf&w-S%AZ<_Z)QQHu8=C1Ir&-ndGyhHlRQs4F+Zg>(fg z;VH_3Q0s7D^DKy9nci%lS#xIC@4}<(uWo^VuCI$ ziP#bfk?@fjr-X_tq{4l+1#wo(UPWR^nc&3(3-O}7ewT0s??%(o>VWy zj^vqG`knYOkOy(#;^Mid86)livraj*GE1?x0kz;jqW3k1Jj8v#^o>Qh}K*lE7&)v3n zqn};}2C^2}5;a1ADd`5n!pqi#hvPa=kv%Rc3hBDBo{%w)URhXGi$S1jgzAd0$cj2c zJZr<0OHbFw&7MN7&Q^~^4P^~P;ntmeY=vJKnC}x?(*1m{j1?md56#?WMj=jAyk=1_7(bH?RMgCc+>B3 zo9OX2AlF#_QPvCh%UvxkQTlRbMK-v((tZ8-ivclIi9~?A9Q(;-PJ&?7Jqj8lRnDL$Ca~ zC{3?kY)qsUq76Ztle|9cOvC)I3tGt)ERk!d>|qoh$c0V!vvWFnAZ9n|%cxnwnk6kB z3@clZJxx4Zff>5E`j@%ro2pHZ) z9pBF-#Aep~e}&30-Lkh{00jm)d`6*{E2?&wjrEoDE!Iaxsx&SR8mC()NM0>1a7zph z-1peA!r*O}5AeJx1XWQ%iD;fEruCVj(+jKt*gfC^)u#0{S${@0>$S1RG>Hdki~L?{ z->#kG2fgBZnp|_}KK{&L{D200A^u(Qzu?5xHE~?@io}2(uxDISaG`MCsLs1Fo9h`f zvf$atdTxpqSp>51dTnE-4UpIMwntMd2oFX*_|~$4s`xwT=k+-O#(;~TH6MAhyFO0q z2Ft)hkCPew^9ZjoSXckvzH623ua*0k5*x1$>O6Vv`89SzBqWd1DeRrn@o8(ZHT{Nl zmhHFMH&%hS)}1$pv%FYdH!`!Vtd`M#|=pI-R=TMX=cnl$>MER+dox;NN& z8J5YtQdh}G)>}|$7uH)AGT!Wdt<8CI&vCC04;l-!8OJr9buGiK?`CUS4Cj>>J{Wq# zw_@9Zlfsgo%TWTT6OKrT@JXmY`5hEF3qNIyE&?V0ZH{L*d)V7Y_r(TrMr#nR?v=E=g&jI*qm{F(BKe_V;zMzO_k?t|ZG z)IxXY!;1{%K&N_`a39^E^jWx}I|ge|ki#1TlHnqf<7q}{CVbU^ zai|MH43w%q$U8Ztk1Tv zM%wiE$X+i`8iT;io2+HC%rUl28|U}yWYqZ!cdVb8+zO-9m2)h%VDG8E|GCU=-omiV z4xqyd;PnA8;F)r{8vXs5^YT>}u2xYftyOfbu=Mi7`Ye8xZt>Vp4q_fPt(r&=LEP>( z|Nc82!5Z@9i&oy3Us_cH2ornA&=n+z-fS8Uhe}rJv@Zft8z%=2bMP0n{_4ILh)^r#_a+wG| z?fH-xaC608u&PCZ`Z@ zvboJ^%tTjNO&d3jpJ3Hh5tL?uIH55aCY1si5gMld9*Qy4Y0JW152Azv6}hQ}@m#XT z7jOdj_a37ybX+?lYFZ8=#;yH20pYed zx}NbTTfNfYG}u)C*tjW)#q2w{kM+vB1g~SBp_^(r{E_+G<1xdoL}Oft zcBYvuz2c_n8Pp+;sc(QxvUY(_i!st4M0({r*L!|c8jPU$139%9xPb139Ko(DFDg#w zF{lTm|K!cANYTa}>8#0AdjB#r5P}ODF<$3H^oaDiYmw1y^Bet31Yba$`r9ClD>35! zC6{#EbMC2IqG;%Cn{+#Z5&53fPPw<&tD8bxh}tJNuA3Tvm{V>C=_qz*cPHiK1F^wq zriozQ!y&^bcaHnCm&>8`;FbTyd{gBbui4-cA?8)@ZgQ2;X=`HpgTUe8tKX-Dx}-9# zA3l^u?T6)=Z#@HCqR@@C*k2+vP*LnHA~LEUD|mJKy1e~o`QWNg7*B522dAlm!_|AF zz!=eVwd~>{*>Pd(%Tq6MY9TBB!qE@Z@)2ZDf5zOY_i6IPx0^ehgYeeT-Xdf^GhZ>R z^~^f%W*90?e7oq-qrf8^4o@VGm{-kSc_lxV02e!yQPmf!YquFPE=tv0(IjA=YkjD$zSf8KV;6+rB}PLQfP^9<*RJSxbw@J%D3-?j_0{BbtN z+5T6Kk)pzxgR7^ynXve?O|`nEG?}IRRXG*HF9HS`wwHf4CIL{Nz`)Dage{o>?hwp# z3iDoC>w43Db9PoUew2);y#2YWlz=7X-fxT-*skys zml3Ba(x2T%=%|~k7fQk^aDN%1ED?~`KtgCjVEgORV~NEj3fO(T;WBWMDe1MjUYDOe z4Mr{Em}`|2>rP+!nkp;mK5F(wbf1p@Lixh-OroLVClz(TUpzSYyg+|n?|irX?)?~4 z$o5;9UhbdPwi?V_M>k;F+2rRAoxO9Xd!_lo`f#diyr}&>nI!On2^)R>x|IYBkrbRK61In1rh}Y6N6_$?y51<%5Q;p}@$RVWE(e7AA| z)bpT_Df>G}tiwP@O`Pym_5DHU0{#rI4EvN@!wh9k%T^)!EY~N9MkjPPZuM7}Vs3rU zjg5MrYr7lZY~0uD2Ek|W3%}R(l#Y&#yE|W{gq~~lN|#ce;)Ur=Ti0j@qBHW>`3&!u z!nHqozP@-S_@cx1vm4-WwZP$RMavFRcXeHSgXT|Fu|$WGtQI184Lref`57a<>(gq& z!b#l+se#Ye%U8+_oZ_UQHKX8FtwPho?u^SPDgd#wiD)$6)==*xwy1E?6$6s+YJorH zNb>F_Q~1o>zIQ;9sRAN#1#giGR2!6F6|UH*hDdFbK}|Rm9tphrr&>HjIm7lrua|y{ zr-6T`rtGCEVB}}Bcn5ud(bW35TuARm)NJ#Q*+=?^4C*j0sS}siNECa8RPK|c0*T?F@F8WeBk!fZ5ZSHTfr*W5;PXJeZ zgzpV`o*Th{U6Ht4U}etLh&-x#mySASPcsMC8-&+~MpxxYaN2?^b#GEE`mqmX5`<*`0R6#3)z8y_-u}Xe2iF6deGsy1WQDo( zRIA!$^t3`%-(pVKTJb;dvb>Gp_EaU4XDRJ=$Po_*k3ztpSa&jb|tGCXJ#O z6w8cr_JOH#%?xv$LRAc62yVg4cK!P>%lN|Ykjsol4X|5K^NjIFO5?{)rUy*6cqR0R z1yS1%besh@@jQy>ncC3ST5(F=EpQZ(e;M>TBokcFxD^r}T%B`k^^24L5fbz#LJUON zkiX#H@r-8_k07Zm1gF7H`g6hk6310ka|>_T3KfXoAx?{upQ%`Qg|P`l!xR*MLsJco zQeQxS`hGE=zaO|(Da_UJ3-x&i9G&yO=OEUv0ztNxq2pcpz)t|s)4jO4ALyRf7JFmg zgjaigal9Y)yV4?ozv>Os-@*%pA+Uz1N>CFe#Iuqd>L3}|JFN`CTtHCYd`nyVsQ%~R zFF;~i$&>iqWNDeO4|TYTXp5!mZDn4ra1#=7D@~?}EmT=IyrPN_M6Jpr8^SIQ?y8Fk z10Pp^Qpk&t|Rz=?2aY_mjYEPUVTG)JTnFvvvXv!5ZT{QI?3x^QOt${v{@TgE9FG1Cn=d3b-UKWqF z)idM!>e^*q>3YDDwr10mSab@mNX)woZA9lSWP0pbziY=R|HgbU@5fz367(ImNwdGU*^jU^qqM?bziq+{$IPtIIw(Aafy3z}>A`?zcJn|&p*;tF^ z?=}#v|6#*k%61h!K~5Jq2B_>v=!ce)fS*{S1wp2F<_2L`7+VDEoT%^JrO}L8>u|I| zg8ePym=*Fymopaxipv9>CjA1;Yq6SxKdgmYgueIs8!j5dfLq#nS`D;^9dP12-A=Lf zbh{H0=qYKcmeJLK`#!_#<_yf8>xC2 z-uL|2@<)3{!)J0)oNQg-nj|qlUo{Vwkc)|cY)ThoVw}jKT?k|ar9UIz0x?(lHou!> z;&u$Xyf72Z(gNmkJH%d1n_5hIgmeWO$avEtXl|h_+a=!Ca=k59YT2};9zP5Zz$jrl z@kY8!;W+tcWbwfEB0tctAUN}!f*iez*y7SNMbt5SmdfsRlF;Fk{Ri|2ZfTLAwKu z1ErML@o=BHQ3;0E-OXkTZIX%GAj2HevQi-H77S{duNqU~BxV{@?@TR?^9P68Mp{c{ zVwd0qGOyv^6)4z>dZwTbKpY?`4%euE%ERKt#Y7s?=P$2HU#E z3q7jJp<+5BnfbD%%9$Y{O<7#QWuVQMb3p_2{6UtzeIam^+c3KWS$9$n(UyJ0XKFGs z=q6NMA4({mN~EldnckpifMUru)8B)=W-?u2=;bzf)w&X*N5MKrASpm4@i_-;t1v}| z3gu^$>|#l>DV@`jI*b&H(tP5;;FBRl05=m`d{{DQMWpHs;RV%*MWdu>eCEO@0uN-K z{R_S!PH~mkoeu-tFTq$4a{f~;++og=PF&@j4~djjKF-$0%$6M(!E_CK+e7TXqTkWK zk@6D}XUJktACM0l=|{T>kfz71pv)(s7{Q`dMN%$>`P>ZHF>gjA0jbGsS@4KawN1)m zQC-zTP|Mr$Ch#I#DlT>R${|KrBzOrEde_d5w+lj$<=E`unl?fXQt1rjkab+hznNFa zoy3-071bWZl&_V2)~I3sz-~G26zi7LvKsGzb4X<~OUFG^{GiA?a$%C%H&!)jx+EE3 za`Lh z02;n|waj54CE^h+{<{dx_MHz786e~2bkH2opRm5dIkC_8Hw3@x9?*Zd$R<|h0o&en>JcZ$U|6N*<3QJ$(U zN9zC!SkGfDNk(UZg3bb85-rznyR(iT+&Yfg_bmD?&U%&Wi zy1%^;q$pPoZ;B*}TMcd5bz1LE+R;&z2XE6h)(nc)3~LA3ieXB%_qE$qU$(OZ zM%*3LH)Z2-XDN^<3yD4siZNN~Su2JJ<+hcxT~=R>9Tp_Li%4^En{C8xJnUv< z!XfhKdt5*l?H|h`Z+3#klbpz#0KTF!qO%02U~zi$F^G69 zy+W4pji|P$umTu!n9sIOaiRPOWi}b2xgluDMyB;;)Gdns>z(@W9S4nMsGDASpXqH6|T z2~Pa2WhCwJsBmjhVQphRf`Z)^XKAe&4mdMMa)8DSUWy6UZM_VXt78-FnJ2t-Zf$cd zCc2n|!FtrL1Q+J2!3%>1+-NA=X#n;pfC4YwVSTTxZTCCdF24XrC-!@FpMNgiF8Vy-0pA)NZz((c zLmvVy17!xQrZxv;N2|=P4MI&p?4d0rok8YU`1AA$oub8Y4z+tXEjT55W9$x~*V9aM z9Iz~}Vp1z5Ulkz`Ni-{-^tOr+J*Ga=oGlI!MWs4Iod|TBazq#$arR&+;`Z;FKeuOe z(f{ZPp{5vr6?TLukoMa;-rHP&R3je=$gC@r-RTT`@osLHou9KpmB8pdIpCi$K1ACI zjNR_*_d@gNRYW~u{@T(l0q#cN-dv`(ZH_TvT^ujWtox!ZI&w90ajMx>mS}xwLeISX zs+y{6e>=_DdD=2)37PsjG9WoB&k|rHn)za|<=Yii=9xh@Qq@bq(W1}g9FiL(#y$!^ zn(3kHIn|DRWsLxRT5BPhR#Y~(#;})lX!ilT=53#XLKJWYzwx}0`&_Ie+#lWX3i-r9DZ`e z)#}um74qVXffN-yEQ9-S^mXUm+>qH;H4Ju+=h;ZNc}23tQA(PMq(!1M9}}9{eF30v z&Bb;1L%ldK0AKN?zwL1MZPXGGD>|zOPO>s~pKL?VY(AbgS=h5a8P%RK9mTYGhPP#8 zj0j%)D-tA9k=6Y9)k@&`vC(m@fO*|#LAXa4?W%~(=3~Bm=QW4V;wSoiUN&^~bGOJM zVSig(Zc4YU{SR6E_8blbRZ?90Wd>W5u`1($zT7L8I zbwW~gtSuAXwxQc0)!!rG*{jm+N>s5T-`JEBy+`fpu4;_sMO#f#xflrsR z*N>Z?Sc#dPzt{UT)w#y;VD6d5zvd9QRZiw}eX=DC(4pO-Yk0glS*26$Ih_dWFE^#+ z?pnh+P;ZLHhzM3DyJ>9^WZ6A?J3QJZH{s5~&P(gCsD5vrB@&E7IeNa+pABj8smhyh zXd&M;#kbRwu~*f@Nz5K0YjZ!dtjUwIh%{)=HbV%cc}?K|3~to^C6taFrI>^T&h)dB1nil4VEVbM^YdIs zVe<*P_kxzQRyT+yJx3zfm(zCZW=I_~fmF26nNEC#Z)SS9Ko8R$KP5F{qG{H&PnuZhjUH8J-o}NsyfGFe$$S| zGH!`ztA__#2L+0zqnqy%=s2HPl)93_K791^3raT1iJF5D|CVI z>OoHE7#_HD`ToN5CbiuJ&8qu7wPAFy%w_*OhV##eUHu)DfPwio=lj>uh0&W?y)QVO zKH?YsY!5+d+lR}<%HW6`(F*HhTagCf2j0j1L_qoJ%N?HOI{(Atn#b1nYQ_rh=i?b? zGex5|VtB}4|8d%gfHE1aRKq zy-mLV$X2W_Nm6d$(dlrTF!Tg4cZD(`vfV*p6z!oCVMF_29z1fNI3hkbp%Ro(bmdAf zv8ToN>A|E2k6r|-y=n#C=@cEu=Sctk+9UCHf~cCirPZjPUCh8!%^(C<@=)PVWz_FJ z!r}6f8Oq6W%p4|de-*0SLi7dIsadZRDpz54XMcoS5q{m zL?t6@yshAFLSX|6swnnn2#bT~dsVOUtOo!QH^ zmENZ~Dn*OZE^%LdkCQVyUnfAbNOL6ByK#y^Qn60MLk$Q*WC}GR@$KU|4rXQ*8#%t) ziwRVWLBg`b12e)sS;+%YWInYK`>6n%Zj6;v5*u4bs8k7Le?k95UvF_@)K zP4*Ys;*!DY!SSx%p#&q z&pLjyCrK|5sDb)+Ok4hlz|uNceCehaH;jDFeflQQnWG9ldbsNnX@XHw&uLnN14^)0LHoJXmCJum&2LL~M#VsFo zD9dfn@&1HQM)mj;!8tJ2MMb$I%Kdh9FrelTR@Dbr`=#9u%-UVA>-y=KS;nn*NY{cx za`Eq`%QWAhi%W$w6ufr(B4Vl~um-Uk{qO*4f{WGM(n&UPtGdmxSssuAfV>s^U6pT*jD;F^Z zg=B(#ND!?YkF+TBga}K7OQJ`6X94fi7g?BTc|)UI08Fz9Dx&b|cdJ6uCNQy*+q}bOoQf$wVzlxv1B0%(C@0CnHB%FI;1ppHST3~XbawOW)Yh3#{^lpG)x8A_afo-%W z5-rPmyz<4s|5U=t;dp+^!*1-9iiB<^=AOqZ#QQdb(&)TQs*hlX)|E6as_lh`=owRs zA+VTPqUN_)(vlf1miC$%0;F}r#KhQBs(r-gx8$?UieAxAwD+8Ps;gvW6LNI(~1H8 z?PMs4u4Bp=V6ic^1%IEby2~{lw^{AkijV(cs(MAU!O^*yx1?%FgZTeQ`v%I{zIIB&~r z|M*FWN8?|&f64M_1eBYa_V-H4PgOjmjW>wT8n)PWs@6)a#Wxda%}ow~s?8#42B(aJ zlf0anqc3WDr2*=dPOjdi^Paf`V-g@>%%-7nyLb^wjvf@WROai_uRH~q7%|=JQb%Zz zGn+-Exw>Xd_XKB{IcJPgQ>ZM-IcbN=4Hi_~Fuqvw!r7Djop)ZvZ$7-i`p{KaEg&0i z1P|TC01dm9TRsF$BXsfi__{Z7|Hb0o(NbbPT&Qsrqg3|-uM&ouS{Z1VtywIM<%Ta5 zZVa@3L9xQ|dX`7HnV)!L5SSHYAuMmL{+WpxYRx73I6Pv3Pa0(b zIW}5&=@%)Ke+g&tUh%$9(SCG-Q$}WY>B;l>5(@7D_lODukcDoTD%ce!%l*&d{gMVr z(&psglN)3DTjq?G=kr1Xti}gi&Ka4C#6|II)?j1Bqt7jQ$9>9E?K;?4&}0)jS8Xd2 zyI^$r4+i(B{qp5XX8?o;%cT;2MiJ14MD1d@S>;m zLTeGywqIhlMXejW|Kgqe(gP>C@0p|Q>_BSD_4MdU^)t`-*GcCnOdMaFm(SCBlvpcK=|tSa8w z&Opu}F*g!NoR7oT?E0QM#Pz(C_!RG$)EHoS2>N6E`bQ7(-bXiCZ63kOVMGc#3~1Y> z_B};5Jl0aEWv+fYvwD}y^EdfY=xEvzXD)e2Y%h8ztx?0?Ap}2cOVNKUp zYE_1|%L--P^~idQ^NZ_w=M&fSTc19}*47aiI*{E6;XDuiHwTNTxA$lCITNsZ15FPk zkf+Bs8qc^(LZ{~uJMXi<(PMksK3_S$Kd)Wt;uky3Dk*@OnfU;rgZHhOF*IM4oVF)V z!6!kdBDq@*;Dgmc=l@r{g7YqaC6Y*t=>Eh1@eu-Nz`UkYG|gtp7!#!s!E|3v^4T=s z;DAscMk@E)Bf|u6jg&qo?in+A#r+r!>f(%O!DQKr8t?0cN1u2jzi{scFIY!e2-YoQ znv$e> zJ65d@0)o@nbKyGQN^7026~*hNA(aY=(lao(5c#70 z%zdg{OpK2w2ZWgvD(*&s7$WCsJ5LgE1tuo8khI2roBbZ8V`kT8dH4VRf&kgXP9ow< zxd16e&6GihSYYknWU&{XS1Bd^pskSnNB=0V^7MiOnqDsFzEpapdPxW?j2W>lXCi`c zW0W=r4Tdi@R1z~EIh&YbqU&a2Wj?KT1<8pv1FbpX=Ph%x7{N2oBg4p0*(w1@eslbs zkx3+NPZ6TSr_!L?g!Kk*zi)?!UN{W>hFjIph-%>8-uRpj%?6JrF3_~O{mBMuaTTx= z=yc^1CXlxcDx(-`k5+CA^(-?1;^#|Uz9(F|-Ccph1Q z3M6fWp5K>lx~(Qvq00iBIY`a_2EFV#EPUu}2A-d*{C#OET~IMubk#BTCGW!#$^U z6KaRzGi+&tms-c7yV6rTSMSUQY`tO_2E>di714pU^%x6|73I`&hf~WfUUO*RxNdW# zZ*-lo=qih@XG4ryU?G(n4(JkLXmW#yQiVm=Bj%u{Vn|;or9-VlY==kX8b0#)2ItQ1 zFzB{#O$D*LaTzBeydAPVLPVXnqm0}bM_kL4ZD?7Jfu5l|eJ}Og{j3#M1Nb^~zc09b z&dmr>=A2%V`0~hd!^Snz#vTC5Jn`&U#eFn!IVmQ9#=#I(YXf4Tw&ne#-U#*U4!2YN zo93Fw$eH0w4{$oD00k*Q5jYcW2F?&ZvbyzWj5I{c*@^;|!$W~)ITF8|BoR5$JJnYk zIPpybXZCYm$dtcFT`+zpkwo(wPUJY;D3dixuB<=bAtLUB~%T-+78Z^_@5JAAR>V{Fy&^6@UC2 zD*v}TcX-pW7dW8K_NJu|g?GUs&6Mk3Zk@ z!uFEBU-U#$_9aKLE0VtH$ zf&9Sn*W6kA8*isIV`#n!q%~hQk)*2O8ztt3+t!8;7pWKfRgX4czsnJNe2xU(Yw*d5Ukn<62Jk!e`EGac~sX{!`fnI*x9=z}H-Rnp&q)9COFIy?od z$I#W|*zgprpGJqLVR!}`o`v>2Hk`)v9IEFrJ&)KH*m3X9iVPS_<^f#{)*b)cpI%{$X{3KQb@AUvo#h>g3(Mrgt1*$CSP;Y ziMzi0yT1JRl>roh(1tUVXWoPCe8NGaTZA6(OG7L=eD8Dq?D#vUPUcJ&F@8os6PejA zA`D7dazStAS3kPR?>_nhr9Xz0Lr!4DMRp0JsG7PRE_MttYrZJ9LXk!lq3swy@a@;~ z|M=ryL3iTyl*MsGJjy|#P^8i?Hd!9IieuNE;HzHS@!HMGM;|%E^E)obnE9?Csy?0V zUEsUf5wV6;jG-A?d|Sg>*Hn5I>n*u7eN4@wv)N}8p zUU(1n?7Jvu-$Ob79?H3QA?M#kIsco;xpyHK-i=)NZRFzbBIkbF^PGJra`9cr>EA?N z_;q;USK--r!t=iY&;JhW4sha)lL~)Hq?*wV4p6Aln(33zoq5Nnp15%2(@#G8br-kR ztXuQ7l;%>D15-vVjln}(sI%@0BC$Xm;R9PAKiB|upjA9@^}$4Z!E20|Qe)YL#9AX& zN~zQcu;yyENU4+}VTgKJeo)R7zQ!-q3~*!Q9v_o5cloT_w7_*AFpbhu*ear&c-2I( z$*}!`Gh2swvi;kz`XKCn5O&@N7vBr#e+$mO3(ou|oPHaec?X<-7o3Z0XWjv)-wtQr0cYP1 zr~f%T_lxktJ@CxWz*G0YBmWQ{{V90-$KkP`hNu4tJoZoE;h$sY(O-w1N2AtBll!qD ze@`^)cBY1bc^I5R*LT!fV`1==EN1Y`47G>_1tCfL{rzt|sOg{r)YN{$XoOBuMLjSb zrsE8>xftPesm&;2#mpR`KA-Ne*eTZm$k+=`37Y#E%JW5iiP0XyE-X3YN z+m7+nxr>z2`^K7LG;_~k7wZEM#Q^n~RO$xkTA(mcY@oBo9oIFkd+qBY&i4K`NoLPF z3$TO@_@+BfauqAupww8Htsw!55Jve>H`RdROKSiT^C$=f27%|d7OY!HGzF02GqDyE z66V(&>}PcgX~_-60aBw;dp019pAiF^F%1M({hPF#p3@jG%)QNheP9=TaR_R}B9}hm zJ4K8q7O}%i=l5*sG0f{G)FaRxhI$orCtRf~1-iyY*@E@fto8Tz!%L*eSrgER?KSsv z!ZnF-_OfWR5#BANED@jJ@Z~WFmH)?d&?_i{MC`FJ?5RZv*~AQYJ`+ubPr@`Sh@q;3 zq*`#fDT$^89Zv>aA!;Q#(&sewj4?_u#aLWOOk<_vd|s!qjEh;n_ts#<|wfMVx8dNU-E7;b9<+znL5siEgH}Im z>FXn931!nGI@H6kJnVs}bSJ7~VKbc3$|aJwchI#I6H^-oUliQZ`!vd^zEIwy8$@4< zxcZldba0xM{BeNOK?SIhstnvgZ4~vhFMO13L8wxF(WI*a&Vx#mSR?1#5?@h@jzmL( z%1e8j%ttSOp`SZ&v)WMm>bB7{m5my3Lz zhQnd96rUYyMqgLl`TDC|f*70E;baewjOtYxZDF#!j()jhvx^Z~KrI#Pyah^n8D1BmwC)a`tSk+%*0$Q z6$u+(xN}I(^3EhM%lGHrcrz8$$1|6x7Zqyb<`xA%Nfxt=1e<2F?M+}5Rltf1$cSBxaS`- zT^p|#&^1*mYg=N)Hya?fNzaW-En-z$tY&X*z`_tJ5^8U$9P1i4Tyrd>1lR&T`lc&AkIte)eE(Ne6-Ll{3749_d_SjsT=QDXNCXhyTa2!aZ z{SRrO2i`y8d5zZf8z=PioJwmIlB8b54;wfAfcL# z(!`v2gK+jR$c#Xyi(?rhgQ-hCoi<7pH$4kuc$XHV<@~$mr0oW5UqIbTVdJoH^|7Pw zpfg{1YpGf#Fy;BgY`ZH(DOejkrg(UGTi84-MrU7$?CuZLkc=V}2xf>?I$2Sr1prRl zPmLcgGbdFNaH5T}&Gt(w9 z8-rH@e2Va+RBXP1gBq}=$gpRied(05r1*@fh%{A`T1?g3KEtb6NR_2bgO-rh&-U}= zO`z1S`|Phj0O^1Ol#dHOO5+~N#evCe-tZm)(zSsgjOeDWbT0Q?v5M!(%wihXnQZR+ z<6<%s<0Qd{3+bFLEN4!}5iA(Z^08nn38^8B-V1i=mpw&u#Pf-PC=}FU8H?9tRJ-Cm zt_oJF^z@|%*6xd=p$ccn>;xz;C79D4RJV7HZH2+a*ENo6S@>ebau<_Hr68O)T{v_l z*{rdwyDaN2swKK4X7WZmU$l>9Tg@=VKYDgAeW!^2iTilDQ_42adoz`g_;wdf#_Z7v zQ{K#H5*ZQ{Q_71-4kD98JXarefVK?`lOR6QP5zyyslG;9D8$;j87SI}u9i@(oxY}T z4A+gI!7}s5H1C=;l3(h@2Pz#@fQlY&2x7JP?zs}JYf*8D>248cI2*jfYJz3;*vru3_DxwsL-Th5;3of+rJPQ1EE>i zIt1`IonRD1B?Ousjw}b3y`df{Z|v#OW;bK91cOv(6RQDsU{OMuGvHhjDCJ^KQ`MDiRWxSSPgd@WUq;Lfl* zVCrriwYoaH*UmH<$KBV=>BiI;h5;Q3^x&q03Q%K;wsyOV0Yk1Q^0v)Lo9;F1faQ_1 zMZ9AqnE2&=st`1c?6q4kF;nu?{_b|sn~1TL_mg2}jFKNoNJ-|?-mA58^w65Cj%`qy z#hOAz5NfR$9wo6gUw0)rVus3P>U*O}h4s*Q@KaBCxRdm=N=8etAOW;G($RwnI~TX9 z8yl=x(xk8&)-glqfGOa!LgwHFzEIf=3d$f#Q>d&t(ydvPhS34Jx_;VVGC{-fGdq6* zi?NAAMIzcBX42H2&dBeI>*T-?--<*#q)*sByA|=C5ybP{>+hhHeVBBaN0dFD4oF!c z)R~8L);ZJsc@Nn;)P2f*7r)>R(#bkOT4 z%vGrv9WwZ7CIC)YgmsgH-DcO#hBLNyYflPDosQGo?d zKk+2*{`fO&?HWCes=i51HTQLr{Lj3}3BM*aAJtoYw@AmKe#P}i6zUF$AvnXhL>$sW zG@bW(HgNv&|Dv&Hf1};T%+V+kfMOQynoZZ8*a#wW@9WgduQZL}en;czl!W4=p)lHlJm9du zD?Qjne2Z!e31}o*$x$6V5XjpO2c0(!FsM^p{I9|57@%WZAa;zi7EQcCbMVL#&5^a-`l>@W4TlPiN zIR}tzPbU@!UkeT&1%@m$eiC_8O_XkLUUAwQCChZk)ap)}gZN~_fJZu51B1b;!LWk1 z1-1=sC-x(pzxT7H9QY>bPesMdq(lI_Y=#m*Rcq{Z7y|_13zcptPKP7l?Iq{~m@d1i zzGP_x{Uj>G#Fue6KM7LYV3?w<)B+mT$Z!vMnfT;PK};8i`jyOS+0!{Fr^FP0NjtzE zKhYrc@dY`OCxgX^Z#GU*-*6I6big`nP^tDxp_wp9Wl%+}pcIA(w;^=_(ji*dUMZ&y ze&vJb`160`SNZ9m{v|&7zy~>h?lf(+!d3(8t!#Fu2$2rb;CGBo4r4dv9)0gC#%vT<5`0+CT0k8@|C zsVx(H_OMn0mN7|45TFxnyGMXdS}Jj##FSEOl$k|fAZuV3I0te8$_iu!We3Wx|1N8& zLpY-wEW^-K(o=njbw}K@{IjNfcMPoNHa;^$J*Kz^)fs0CC)!Jwj$oQ`X^UBGoEj=$ z?vZ%V(*XylI;u?|NLN4%Eix{Ybf5@Nh7^uX)-&dZB5T3&MyhE13Z00Em zdU{9llHUyAl#sYylxfBs&g~wMt{AK@a%%GlUU!qx2ElCbd?lvwqXKT6o>adtT6yR_?Q;8NCHisVXQ`igdN3OFN%nlkZcsT)LZ#)EZwG#&Y#&*atrSUZ`VCuQM$d zX+~?r1^{tAXic?EZULfNq%)EI;{4^Le4x1(&uCj<9IX87Ofd~q#lB|wp~av!IU|Kl8c-8cJA$9rD7 zKgGQfgvZZv>MAZh4b4M4TPz}03$-}{*GZ^|UX)Nf-#al{?Ds@E&;TXA(#};TGZ++1 zJP>enKrQZwXzBzv8VbR8vBw9*97QfadH$liz#6pzlnAhvvn6d*J6=@!OzUvK3JTLU z0aHGe(pU0XJS5WSWKCDj@cV9hl2iQ)?Ak^!HlstJ+Zr4c!D!7QZc~6s@c*!eu6C#t zn)Iy7Ay(Z{q&ot#$*vvdqV?>odbZcFtrcw^yL&w-tpO%co_YBq=x8(Z?b3Z3U8vS* zWng1{k=sr_%bTxy+SMchitoOnVRVTjoB_Z`$5zLR?DyQtTH2Xf7~z*XN2*M19J_f5#vces6G6qlL7#rP3pav+|7s`zYN zF>~5a_YUyyNDHXoK2Q$$UWQ)}by*d4_Bzn%Km(M48;@%7tuZ6$y-BV=GtwD$)V2)7 zOmfE)H`w~-=a-j~Se*FNcf=RY&WM~CY=cKg!()<1yiX*ynGWe`%p6BbB%_2t7+B24 zopx=#%hz1@1mEZgXQ5J|5-+Z$QNR1is0FC6qxbC(ommn-w9~f?iWVDkj3v6e~jM1Y_+XUOK@TSd?A<)f=DZj#JOl^oV<- zo2#TG&_rygkrP7-$o9980$i%dcJlTf?`6A7JTRu2DsK7QrBc|dvTj3j+tIPR;I%(Q zf773&9(fCkjhiTkZlWBzj z%FKRSO4vDrJzjU`@U&>`n*?$|%NXRI$zsvH;f}BBJHYIJ`GHFZ8lb>Cso+!C5{*xY zv~C}2pgyBlLPapsust;^Dkf52_QGv{dvSZvNZ=!71S5Fa$vA#12qqXM{eqGoJ&8+Y z0ZyqRj!YEH=C+p${Gq#^=FP9U$gVAE;*uodwko#+xq3(GC|xvd+z9-f)(0yXhQ< z%NY-1*QNR*9_^s!n!Gs|;v0rWJNeR$DWybZWc$qSk6j%NPJ&W2sK0D_pD?*v5Hoe9 zxQjt8uyH4x{8n1Mnxc#wnpW{y)e`#)mb4p6ylX5s*WqQwLggHfOnHC&fC*^hPbt1j zw$oBbow1z7m#;Kh1aQoUQmQHuu1D}4q<;@^|G}=JfaQ`7(+npL2s&JE!M6#(T# z>5J&n6L3xkmz{D%F0p7WePSjWbh1nPNPtYvk7Rvwhm8O*AJsw{7%P^NnwQ~7p|f3! z)_lzkpW@qJ_ZX)*gRMPMrf)&zsukqIT#(vPy56NT1S#DFE~Q{nX%B(JN(-{&CfICHK%_wqn2u|sY&R3wN@~*xeACR9QlP>t8WVy zk!Qsm_@#JApM#zbG(hD>ix!M*MaE4uvr}9}a!JC(!9vIFru;uEVI+AONyQn@7p_nG zy)(%KRK#+Pvcf1)$w!c` z<6`Hbb5Wz|5WAyzu(H;SX6{7j=D7l;N2EioVp5UXW7M!AIWwsVrFIduz-Lh{7|jr* zI^+7%qUyw6JC|SuYB4$+SgqGkFY?W=`3&EA-KXgnPt#UCU9S$10n|3m?8^l7!H_Od zMtexMekN~MGa_F|B@%$TPa2DiV_*5TU{EKVv9J7E2W)~Kf$k`)_*9@*)c=I4YznbY~W97wHbYcw6Ad18e{e6x=&LUp%upLGq~ZY}J7KaC~EGYBvmNcb(fQpK>e?e?y*+Pa%`H^ zu_PJ`RdihY7j!pLKro0)2|hEK_skNyoM;WuHlWsgrVJ=rsn+qDqtEizJ0IlE#Z#;< zu6zNh2KAXyI^QW_7!-}=FheNITezV>421hW(Bidj;){PO-G*LkDU4p*If5BeZNGi#d4DT)@WuUKraWN=+98w zk_pq}e3V3SZj@4^ZPVrwWStD1oiV2DoV-5EC;7K}L`>QABaHQu63YHZ_oO)hT?&-) znCHD@L3`2+ynV;*cuK@2XEk5umGqP=C?qBsLJ$q7O{2L8e4x|82PlbnP&0^e9~_sc z6bB9ipYt<`0b0ysLYXo6H2dYJiBLtt`#YWw90=ME<7Cz?{#v*lIQaxqg&ifOF3&UL zp}00434$1A6~pZwZ6SQ^Yaii<-t+)>E}v(mhiJ8PdsM8G@2WdfN)0=dLG4`fX=;?( zxlKxqLZ#N8(p5L2ic3Tgvw)+0#~H2Z6lPY$m!~vaBc-{NDJ#02Enav0QU2X8e~vfb zyp7o=s8BZW-QNPf|xOt0fq^!WFZ$?G?-``$FaK8YeP5>R;bl?Fh8{8!c3zfscXtcM0d(}=h4behG&n|TJ<*0b(mKI&?s$Y# zJjr?hL|8}(yOqz>WbAe+E@foclAOkOwIP;FAe7pB+jlq%Z}f3%(-k5d#Q}G@0>nnq>1J; zK_F6uf|^@4eMTnb*CV!li z6@>Vfcqsk7oXCV~N~r_g@n`t=zWT%bdtd$ut{t9dt1Vf@tPuC68XXIcZ%CVqDOkhIEkV@Eq7+OE zi~c$O%{M>B_kYE6+{lxxFRp3LLA**qnt=|njIhRxUO~+Ph*+ZH^>E)9a)X8v%qrCf zgz)Do;!=|hJ5<(GQx@eS%bh3r^38|&b8kAscYfJp*s&c-Un!kY%EChi6Vj7;c|?1q zgpd-BgN`>yVv9HSyp6Jb9KffPQfo>}<0DZ<_HEveXA{i!qz1Z6YlDA3+1E^yAr=5= zBbw|zUX=FzDr3CR)WVQ_W?1>bE$fK@BpU+ z4Nz`Q^=+qMTCr^jP6p^CrDUMC<{LKp4aIlr8Ar^|mFD?o;3qb!10_bPBuX8tSMTMHFHO+9Y!|T!o7nQgD0Is770%sjs=CmN2;ccNmI@x+hiL=IH7LzV*~o z-2L@W^BuRoz-prSGjU$~?e?Ueis#GsPxn%k#4GZH{H0+=`M$gJ5n6C+2`PB!P2xY$4@<>>D6>e@cA+B;DzxDvm&qzw_Sw-C5_U-Y-gV%R<`3 zKrriynUo?MT}MZeuGUy^-ONT)LjqMy&c=Ufa>_O>UG=xTs^Wb<$<^*Ef~XU&cyGBc zT@COU2SlOPj#>&G;xpvpTKnavARaRecYbt9EL;MDN#k|n6|=C1Byk0?iliE0NX&`( zZwJ(Q&Em3Ov+5e*WNX&#*jqmes{w%=aWh245zm z>jAR?RSg@St9Qg+3GO8xHi{9@8`$7RaB(}5Sfj`;3we%P+5`Oiw>-#?zU4Guf6Iys zCw7shz6`kZ;cGQEr+-|mJIsM)VtQ|*l%BF(=D?oybG0Qw-d`|pYac-J8xr7L#dK9N zFGS@e8$GW@XOuNnpNI8|LDQb)fZ=LssHU^SgY?ahxil5;8`J!JUrH$`zEMOBg|6$w zw;79y>}ex^m4^V1YvQdcGa*z6W8(o&2Opp!(yv(tKO<)9g0Vs}ks4uEaFFnXJ4QU1qocn|;Ow?Dz{ho8gF?Xa^htlAJhT7s=(qfKoz9ca3C zZ!v2q!=A0ATnaQm8FbB{E4p%yr9I14KFKLzU~G7D)JJLZ{$UT>5oY&*io8+w(gUq>6ug0@;)=Eq-x5qzr>mwM*yE%;G??ubs2p{F|z+ zh)X;I4BOQ8r@+qA1aJozh*?gra@6rshsN$Q7806WN}_tH6lDaBnENrASSzch9x^2* z%!mUvF3l1TU|6J%ku)Vu3x>xeA3yxyrh^VphWOF~EZhsiW|9LF;nSho#%WXEj>=*n zOOM|x0jQA=T!*(%KWHvV!6+st_)OA3K$2NFI0nRwjN|#{Gf#nqdx>231~&8O)Fm)+ z<}RX?u22OI_loHba^w;IpLe{Czw!q^#`k{Nr?__g8Q8i=8`kWuw^_BpC8gwm2plC%F z3rg3IU|`{kGjf6@K*agjF1d(0;3xZb&-Z&f<`jA+AABaj@pfdAAVx-?n0Epy2{xF4 zhUG>ZZr9|(r{VN(VQaY1z=ndDP^=*`_>ueH<%)xdh2`rhT?)m9*|uNt{;6fAy2de{ z8A0D6@(9J$V~mxE^#rc66F~#LO^F%N7~gz1VdtOyuRWmYpaT?Q-c>?KMoENz7m1xt zQc`hEE*41Py3vf5FC#_n7-}qb3@$lZ#+$Q)Cj;#i7dk;IL*ROjY-(qoQFKp+g;8}K zpBX7MzdIS)nF4XJ!UQvAql2z%TDRw~& zT9^#(mTf1g3kS{27=906jRThF8V3dqfbyAzxc?mOiGP7Seh>2eN7xu`MGU2(Zf}j` zZSKY8+QrlyM0|iJJkBE=PrmfPru|E+4^%2}OR4?8bMm!suH7{^)5bNI;Wca1ESvge zKevW4d;sjiW-HwFnxkL;2mUW#zFF$V{#U$2%8}L3oku9oyc=oHBJ{ERBOpYanB3l> zaqiELvVa(78TKNXCZb}-yF{YA;Gqfh7;QnhiIXFCGQZ~>&D`2p*ub(@nk^{(HgCM? zEZ=j}S>AO0nwt(?;CMSry>NzpXN$$^0-Jh{&GqwatX|;I`UQ>;&vJ^>++3dHmhvcX z+I*bvx#0=^&D)>nkKOe&-+lY@Tzg`d)y6qgcj!uw^oz;egOG;IKqCCCdTo8T9S0_7 zm{1{S69!`|Nl20LeU|PPW!eeGRqO=&Fg60N_-B2>DRC-k>?6MsHl<5S*u@W7QHCez zcAkL?pQLSl61nghZ1+*@;-|6g2dS$^VfQnzdIVWNimg6_?tX@{bw8Z{2<&_c&V337 zLl>_>I*EGbI`%yc5TNYe)|$8Hdev5^FYLbK;it~r`o!}W-+1ogiuGWUd*gwKYI9mh zcY|1WXy&`VOv0lfMSkbv$KU?Tyy>ll`|mlBU^RB#K};Qdn=B6h>g#{sAMF=!_$CGw zcGqk7!5L!f?dG1%ZDlchht{}nHhS^$)? z^TNBR_x~tm>%&x*SY7yHSpu!8!N6mbe#PSRBjKA=0pW@Ga!zcJpzWxDz@T(u-d+vCguYCP^Uh^8ETvJ&sw$SB8YG;(? z0$Fyh(xSed5)DZYa0^uzFl`}+c%Rjfeq23^PYPR}OGJ4+5hv8R$edABGo|V~m62(G zf6pY<@m3N~pru62uhEM?1`Rr}O=vx=8?rtJ`Yfy-h2e2nJq6oO!uDfu@o~8D7+ic3 zR!_ou8+!5f(Q|a`=V0|rsHm=jjq3ZXK7Qs>x=ZK|G-l0w4{cuvpS$K5b3hM?GLnc# zGtf4;O%Q`JZMb@n(}4yk-&;c_2T4d1kXbM;)*@cFxf)l=}tUkbrd^*f8U4Y0LAWFkjVg=(%w~;y*^Wj^ znhsES3&t3h*QW7c1b^?m=;IV8a$}4Dt1&=f3>G30;UK2(HG=M0iEX?(-{`b1plo>H zx^lw*5{r)O!uK$24I6y;H7C&VI~|LoPKnWhsHPTVU8L(fiU^CoN5p|Hea!^ti7^S@ z#<2!ZoT^?8Socb#92rLt7=Nc9lB-cJYy@NA^h0Z=D8mneE6`%3*-eu-wL(Si-;go3zrF0`hxaym*VcrU(3ajxh+#6r~{UXz#kPPUO#?1C+blrLAWqY;R{1 zCy;Su3}bmdTEwiiX4QOA?B%Bfff-rHi0zRsrcU;3Qvfz1))Nr6gBAoflf%q6QFN+v zBWuV?K}5v?L-3gDc~=V5ik1$mJ(LBMB}JBqEveFb*cEm4Cxyn~dFQk04t!oecZSTR zD=xVWlrltcwl$_hc+Gp63Rvl(E`1B8h!`ojR3XxVRYY8R$cY38lTgW`ZaQ0IC@F<{ z15IEQp6sJam+ii1!10L!e}{jMtCa}=nKv0URcjd~x9E&no~i5!pk_do2T2E^24-Uu zV!fp=R0OWendp2y{_1Ja^LOUAT6}9ABXSnhR4k%Hv5vmvC0>#eR;Ez_Fvp>w zKeLGQl~TD6e%?MVRhmt%1|sg@oX?g$hN^>^4mv=I1Rd2-?J{_s5~@iJKbPVlkv*J}Y zV$$BUpT}p~uk48NXG+kEA@6PZg|*~B(8w&FG%k+AtVNVpSXkZ4r5n9;Xg`t>%V+w3mj zd?}n6mOS~wQQq_MBmDdauI3+n_$Gep{x|T>$4~L##jDt<$Jv!7JA*Q;V%zi;h*hL4 zF&4B|V~2Rc*y;>HDf*ixN79vqvP!pOy?7YDFcO65^D(^M^d7R z?|^B5klA1nzg?;g0EvEe2A}6kI~1Nr61|;9_6~kgfA8d}BZcVy0N@e0oMs)QrV_>! zK;%Gf?&s93C`L2i!6m<;#zv^%Dqq+45$V7|%$Q=56Rt4_JsosU@>{9>zjM`V-(2d| zH=*Y7vBi8-0L%f)^=qGMC4Fs7KU|8O-CQ@hv=dyN(_rKyLQWBS;+n=F6 z_g>h20--|ZJCdX~I52_vvf+_!FD#Y%MvtDIC*A z*LSSDinRfe4wFd@8zRoTS#!XzJ*tC`T-rg|V249oyjVGN_81Sma5KO2*tNX#;cNN8 z<45`U^Cx)t%n=?vx5=|xJ=^P!u2wb|3%a^skV-4+>;E#Ql?}n4;@e`4#_?oskB<%+ zSKa#}B?fP|P!+@YZz-8d$`HAJGP^X??-COQF+1!1ld5;55pV)TB&4blRN|TW{pj&2 zUx24c%;cE^RCf$L`IXqA+hTK>Fiwo+%~F7+nW3$*v%B7T{@nIEpFF+&#wVUXf9vz- zF0#A!AUftoZ%G@hPs#*uLa+)WHX+uK&RkH-AN` zSG|TILdKf8gIX^CP;euulpt7WI((=TIlF9w+uz_pB`Sr!@Jk>=`+Xq$kWI8@KeY5=rfz#|I{Hq`s`sIJ5zbSDMK4r_LUKxn_kT7*rdz>s-1iba_IFz4xz^3t|AUA@h-ef2N`91 zXV`i6+{Jf3@!YvLKKA_iTc5wM&CYI{v_r3_rUJ6h?S5&9Gpr3j%W4$&5>|~xUe(jN^?U;8-+(ZaBvk> ziWyj8-7VN@3(lOoiu)gWJ^%Fmr})RebCh>JaWxOFujkz2D(E}b(zB*8w2q+(Z78f8 zZ1f%H%MqTqc#Mb69OAK^4HjL`p+id!cMAr}xDkV}aEVUbxN1^SHW};)+nnIJ^C$Vh zlehC558uY`UO3KU>m_Hz*g@E$=X|&1TwQXmJI3=I<>_{mhb|uC-WQJXp(i%D_pwbL zey-=@j`!U{g^k4mxw}a&Nh`O?d;mE5uCdoGp1lTnX}i^VYYK;lqAa z#N#(b-%XmFjB4Qs4&K3&1tdW#v11%;642&(;+l)uGj))VpiZDCzZMSP=C>>)!zeGM z#Y|>|B&KZbtar|w+kNNb&t16l@fXhD^6Z%ltQ(>lt|`R>fQ^IZ46O5-w@mQ?zLWzm zL1oJw*i@p=O~m6`M}JGW??>N(8pEKzIG*>%vL24O& zcW{+0iPQ(SagxXRp-@cd%wRZ=igKYB&eC}NsYCqwhpyvqyzOTG`fpvuKY9ElAGfPG z)1N>W3raDXbzn+o1Dy_Z)>xJ`hdZU$75&yTm>Q36-^_13`UZaHfiL6zPrrd@wy$Cs ze4+h%U064x;cg37dX$c}RCX3iY`1X#)2H~g2aoW9bMWj+*li87o}qQDT4fjtCQF)B zcIy$&$x&WdoaEWX^*qRJd}#L${?WsC@S`8Ni~sJo-oW4bz)k$Z1IKvi`Ht1PQngYz zglGr517#R!12ipAI;8YaI-dXuRlY?$^HDY0*SBW46KSB3h8O=HwJds1&HL7SGhGv0 zH5!3pKke9v921sAKhMke>ycr+)J91-bZ%6L`-n)_6^d^^ zC0DlXROS_5@275KB1R`kz)8ZGfz(I=0L8=XB0g(pCLUX=xgSBX#_p=I-EXoz9OltS zZ{i>S=AHcLZ@ixW?cUe%o9AE4Bi)T$SX|9^JPIB?w0vS50czjQ!pi~C!LxSnq8H`6xFZsw*hq?FS zNuFIVSq;$WF}iUZETqz`W4-o}oU2wCb_=_^1!chnImG(V37(Q$cwpys{Q8r(@sl6D zg}?pY8~OPUp5VSmdd_bxK#n5)Q3k76TcgdRcQ)%#0-(Yhi}2}5`$`}NBwz_hs@D>- zl{v%u?->XLprjp#EG=f$^4bI-0YFjq-W2x&8223JYArh8^P4%8W!*hFT}>sLxrFWC z7W5u_Ca!H@7(6VqxXMOrL-^r&P)9eCyE8NKCf|`e-ER+eI?w=Bapz_Sgd`YWw&5at z*a(D}rSlQZ?NRQF&=nBLwQZN1W<)7QkudJ!lAC0>sR)}pMsFl&SEgAUBPAv;uV_WA zW7UN77~A~`o_hK={_$_WiNE?Q*YV>Yy@mH}-Nu9c4V*7W*w&7fE?8Ml!#6%~d=vLm za;WxbndVGWq(h}5wa_)C+umliyUlMsbCeH0ah&INu4dhOnhYSuU_#RcW<5=gveOnk z_58KGj`fJvD0er7(p?CxyNgnhiA8>^2Zvv9=A)$qAlr zH}k!svo9=lFmOzY+{T2Z%Xbc=JY@gHLA4oS7+p2Te}r322NqKd0(U0bu*mUR&8T zD=DSWNu`?5cNK{Ur4q|o4FBeo4AWy=VhPuv|-pGr*iMW1yOJ{v3J48Y3Z!q#=X z_ak@kW501d_k6H$@9Gq1`jZUww60MGw-lFfYVLdfI9pqlzHYEsEHEv|Ac$?Ub+Pg3hdTbneJA<#CvM>*pW@n)#JR--@r$D!Dl-R0q}Q~dLXuH|FTAE6&Q`kjib*Q^Jj?HJEJdyIEK_*&le#3`P$tJt*; zhU$)<=IUVIEqxHEZWElZk|)~SEY;XTrBX_Vu9Y$fZFivU?8Xi>n>;z($VXRq@UsuS zk-vN2Yx(C79p}tWL3ESW6j^VfZOvMJ&7XyJ_ml&`FXZ#rujBpU@ zy2c^llNOKf$-74yn@dWhn4A*ZzI&NQ0FA8U4W}vpJ$q}6&H*zND!KJJV{9uT%=KP& zmg16Tjh$bJG9;G4*p)HGmn0o{fFg*+5VZJ$;S7Z4w_{e!VtrT8O)(Q4lmRo7SXy$~ zDf`VA`l24TC8v&^88SH`3WgL7BW6LWFUB=97=)EA=r)&}ef~KA^nCghu5VwhL|M_#}7*EEHVtucgE>A7|H@Gnn2J_^ErZ z=V$J_mQOw-EOsiTEMxnN7Bg-h_QWt+8v<|wbix;IOmRqabRP4%Z86&?YK7b>SygODSbIWe+($^t1JltN*RTF0v2pluxH;}71zKm6Uh`009;|8_#hK zI|wW7S%)#$B5cXfoH$)x^I5|hDu@)Lb``0i-Zn)vmxB->6<-nR&)S5+da4$7*-2I8;sW6SiR1u2|Cbrcj<-|W!T|j+2CUrZ{?RieG5PLiJSQF3!ALA4I2uy z9Yj~*hvp8Rjs+2yKEkl`-{~bl6esL4drDO!xOo!sb_8FVgf=rBWW8Rq)?6hKPl`KY>bN;V8rc1Q`bR~R z?R(R~Oa~pHV(hS(K6O1@@YywB6j?P?F2VhdOcfO|#igGVg|9R@v8u=SF`p$YVJHW| zY`|=V5$mhEe(HIv>}o~y7$5w|jr`>AT+gpPc#21cQ>@AY?Ro||9J5p&4Zx^SEA-AV z){?>8PpP@{7vSp?yVxCg{B|ZH)zN!8W^78u=uunpba?~sf8hq+{h4EY=%FKg__=F& zKyT+Uxsh|_Fj_WTQYkfTdd~b(o0 zBQO!b1OZ+%un2>@*E1$WBL4&}kt~QMfhn&gF_=%dK&Z-l9^Y41B=gFQYwFBEq?oFT zFZJ@l(f7cMX9B3kzQ}t~KL|~y#h(_SqDjxT)YEHj4a;5e(uw(8P~u+EkAHR zXL3c!aF6CxEpi%L)ECl5IF%y3i_GQ=UUjWpw1%+3d+xi2pZM(!-hJjZJlh?m)-_i8 z@YnQ6haxqC?UmS!G>NTHI)P$PGFpO5l%C&5Rwv-#sPqwws0cm)3sx#kDwK|GIl-qd zUdL}edV>33cnu#ueIt)tJi+tB2J4}KcD}U7jZ8=>)Y?-^brmwak}W{66kit^O@7b^V#ece3{3!tH+{{j zI0a}}2HVLFP+a8z#;XAv+LOlT7f>MxIhL_Q4g#1oj*gw<`^tGE_ZNQ!;Fvp*6p92* zLU)A-gQ585)HfxHza$545Dk1SHZM&AO;K6+6t`0kP>!~<1)*5;h;hJWEESfNejh0* z7j9Bf1yT|>ci4*(@ftD4E~c@|I`7HdxB_5-hNPKXj{vZOIv7ssfLYh$eV;ss|K|s` z@w-Pa$20jNO6pKk4_AQ+&F4vTESgN~825czU=kJKBIFk6k=|mhNVL@|YGtSjAzP4h zhD-d0D4DJqu02FD1_iFl8GL$v7v6VxJ3hI%6_42tPSD1b*C8(dD(2{z-&`slDi`sqk6gqre(p9rcy$-^ znqWv{&@dC$k-H#hhqx6B^5$vf!soI4igXp<#c$&uJR&xP5dYe^G~ijiSUc$^QVUud zug5W5ei3xmeqL?SUxc%!2prX7zu+~b>0QzCj@Nj8USj!tTtgnY!XS!jf2<_`!?vZs z+;+RDV&Wc_3-wVc3o!qXxTBg$u}T{?=laWN!UT1~$ThkT5S*B`KqLLDvT%SGKoTs^ z;A^}1;3sdv|Mb4|_?^YQxYF-2LSMS@erA>g1|3r*O2SN*nyGUl4U(|hA}Kz=#Gs-C zgi*>O``&z8m}{wtqFl&U4JLqlhMV9(Iy~N;$J5xxVb|l>96khb8+Azt9fzX~ehO&L+(u7xDrtBGUWNdN8_+v#UCOn@}sHR>WDMY-s3Gn>*FX(-=N=oRkR*rAB z&C}dYJ3s+}{-l2{g0>(U%PU|$I?|(aLD1j<4d%a1*o$i65Xn`IQTM~iW?!(nIaY-i zhjdds`sg|Q%tvp7eXkUB-`6q5KI^etQ>H!hP5kcc@OMr5tE=a^f+=@9}S=>z>5O5M3Bf+BqpT#}fwMUR@ zH*I<*f1Uz8j36TjwHqOTsD16=_2RMsRT#`FBVukSM(Zi>xw0eZY>QP&rW zJ5Y3jgJ%Z(tKXa8Lr<^a+1V6X4v~>rKd#0OrVdMq;VMK@FmVR^YF*y!K##r_d|^0?4?KMy zpSZGzgX0OTb_53zF8r7u&VPVP6ip?k(`bjQUPK`wCM|(3{sk~_BWijrUvf|laN?IG zECgV!e7gk7Kp$D9=9>;^UyNi1wA(ra;;QO7QBRqa8&f>pXuVq+1(tD$s%gB9Di!4; zgL#VEX$L65s)Z)>E(DG+ZHvGZs0hbRfXLeK|I5WQ0yzYw7BY$fZb`avVNj9HzKaSN3m zW8EE^6`HB=mPT00-+8;1U@-d8=2fDwg90Yxev!EFm{}n+`+`=S_&Odrxr|Rfw}mGT zI?UZd{O0ozqS>FNs1?u>{!ML8N3jd!h}`M@-v9;(httvLjo{DYd2Ji-v4SKc#I;+= z_&vX*j)>O47i#e|vi$5$k)Cu6tmJS7;I!R z@!n5dz=J23aRS3&Q{?asqCSUKKPr=>Qf_74JhW_@VE77mIwLl!P~ViOh@SNoYiU5% z3bg@An~xOGsUVvW&~nfqO@^`4hJ&XY)4T+|H_o)y%G4y`^yu#FqX5-77b&%6!i=Ko z6}`_f>4ZVtsp+0#GokcJy;)m8O*aL=m5bUzlyp^kkE3F)K$QuyO!2gw!$+Rkz(dzJ zP={&6AQl#;@BoIO2|iSFx(qr>QGzSLm($z;r1hcAL(m(zq8k`DaBJ&(1f=%cXk8Al za*QQVkkd1fN4O&=Z$61NTdPQ_@{%s zd*jlU;_DI&;tZNDCZ9G@h@W z8w0^E#>E4ZROl~v+S_RdC~2xsx5#C)GUy%)ScMib^YE&6>MtxFP{2j?9VKOnB59ItQiwSm3Fp@Zue*@(mb=#R*1NaxhFjKg|8B-D z>yDi~AWH?3135Ss1yXR70_sc@&7ukVdkKxA=Ctyq)L~G;kpaEW@zih*pWENUQ-^C9 z1_x>^HmAObt9OKw80A=~4VYr%TLmn);8KFP8oW>^*u*G)yX2%sq68Y|#j_9uKw*2E3{!p1QcHU1mdj{rH-}XK67;&pSw22 zjmbI`baeE@vOiYY*XEoki6E3jBIuTjik|7rN}vG?u^Af;9S(O< z8Y5KbHek)iEF3nMfJ^<9w6)P*aUY|9&Z%yv9-x|(+?Z>TV3$MorfQ|_Ms#qbn}O4X zt-8~4^yM@rax*w!bD*9yIu4(wAf%jdSoiReXLs&J<^E-^I7z zdk(KSy8-VeFy$CAUz8(v85T-i(IDtj=vCZAIVVy`q&RV*cs&w4j9gupSv@2tT;9p} zrhCuhwfCOK9k=e`&fCu76)!uFSKfOG-}I`>_>-?Zk2k(-8+V@H#+mgF)2!jMM$S~+ z;a+)N%}CDb#tgP5CsfTaM2$gHFi<(KC?xum%fMZZG7xY|?&`p2OzU%5umA?xiibfa-gg;qxc>s)boVCiy|jf(n;q6Bf&xbsWfe*ZVb6gY zoC+5kM$QSy@RS)Qqa7C7;n}i=r}sPD*mo=$m*qZw6IN<1Q$f;%~GE+ambg-cuL zCmlNN(4`Jt-(hWSioM-++?EeHb9bajg=|hs zVbc{6Ckd7&9?p#k6@rBT3t7XJp~v;34nqw$0T0f43}hzP3g`Q%=8id~$P<1M?YZL` zqnI>e3y&Xx$N8yoFe?`cU_soi0hf(gUEAg)SOUffY`!GDv8|w>LSYicveDcin))L) z>BLYVk9f_-J-M`;X8;5BI)CI$B1OzWPNDe@SjHy@}wxGH<#pM1bh|OK0$BzTsuK)o(%r+Xze2Huk3=s<` zP^W(w-079OQ>Y_oR8pWOTe>ds9wxYMyUBZ_-)P87S? zmH^U%yZLXWdjJqtw-Mk%Dz%~3B-Eufbr83!ff zr~*T9Z>o;uh6DvF;G&_}q>O@6yMbBXg$Fmmg^MYC zh(t%-n#m;Kc@CXa?ux6?dFeIQ;6^9krAEp2@$J87UB@INaxHex6+qh9JsY+rt ztT{RcSV=G`JeOdjAQ#faN%t11*kERbr3niTL8HCMU3s&Zf;U>F@qnr?`V49m%-v1I z)jdL;qFMNAQB>!KKAwnB_6IJb>8QSpCGk?XKLTB3OCpf!#E6i)we2nv`ku&3PNFcT zHn2h+%$NJ9ZX_qB0pR^sEITmAP#wpMLNUYX@JQu-EZ7u2N#U>3iPN(@l+dxqqLYG) z8wqz`+Qe<=H?h`bBx(7U$X$%pOyXQ=PM4aHl48eW3#6;K^Wp}+`JVH5)ot6@SnrW@g62rY5{pBy6i*PJ zH8`uBJ8Z12;lkcFUVeE4x9s&;OM+eqEFuY&%3E?{O3u|iyxIYRlL=}u%uR7vS)-3L z6UT(7I`vLL58)+G{oD#@gn}CZF3ow7To|pMP=Q5`M1w_0PJ3_A67O-ZhKTe%3$PCm zCa|z?H=#^piQ==!@*9%0YuDv0pi^rSm3Dm)9T+UE0RZh9alN1%%!sZaV@1DHv$K1#qR*5UH~xRc9F$Xz10=>}E6MA3JGd(W=nTVA$@Z@7IO zXZE%+nM|Q6LlXmbTj%Ij8^F9WQcZE)<&5d9!?`mv+<9Sw?O8%bh1n2^)wr(!2(Bca zwfdN5Md7prR$wvby9Q_3^kpKEmS+SZUXf$PF|}CCoV4fBh)4RBaL*V|5BG@n=g5P4 z)8Z{({JSn;N9a6EXWs_mF`un(JQ5gtBk_?rzB*M`RY@X2+0HJt<2{~Ff zauH_KbdBF44T}kC6uN{(G0ns@!|p-{x^egCqN4>evIvCfTeV^%SKNAT9cOpe&}W6J zMOqLpvKAK7o}`gAu`1jGGy$o{G|%vgdv3)Kf8zz*e`yn065Jhw#Itw==1N3gC@uOq z337OncM9xI4fmbx@s@ke;f;50;>_7C^pgy6CYXzxj~9{gg6;byMB2?D2eIHDbR-kaFg@IE@+OlK5jC)R)}^5pvxYQ+yk&b(mKHiU>-J zU527oz@T*OtFqjdzvLD~>y_CZ0qUXJf@#NyVo?PrCK$|NHjq$_x)d&eo0G>i(F0EZ zAG%8v?5+uRx2Bl%O}|8^$oA;G#_}}+KoBqhT8<;r<;L13UU%P}_@Ou7ir2qv4clEo zwmGU)cvyJ4MBlNLl%*jmz`8OO8#CY)XL`Klo(p)>y_a$8E$5J@Q!*({cj7TlLPhry zu-d{OM=C~5AfPC^z20Met%KDCx!&MTiN}#4GNz4so92s^fRs@+Vd08W8)q9q)`Ezc zT+u@{D_rQK%n_V5$5mH=jpwy}fxz0DH;{_sni8Jhg;G9^hY>Lv80~YA$}f#H2_GHpw9_CbKi{%ivQwxH zYO56(_`pR?I3iHA$s*^_{sQRUV9w3KB+Ar}_iC(ayAC%T4I{${guqHt0FQJk5n~uZ z)go=K4C)nT1s;n1diF|O{-Cqi7+J*99PktAk@vMS28d&YCLF}KRJ(4aSZ z++r0WPiK2EtM+Tt1VLO8x)Hk^Ep{X%6`Ig<=Aw3?Vor5Hz1 zi7$zbMPw<`wauozv+GPh(?nGf0x};&09imWw4oqvkEHpKwm6|+wUfvK04QKKRuZ-VhZn~| zwMK>F%{wHC8xkwW059aMk}w#*wYKOQ5fO-j=$2GRvBe5z-2^#xa9M;?U-OU-qKf0) zV}Y%TRIyN&eMBgJ=|V7@OmXKOx8U2q?&bK-S6{?i?mdGyylfk|I7BUwX+e4(jsL_Bk)kj3s%NsrI+J@z_L$VV<Kj;qZb9bH$iU7%P*=F;bhdGYM6zstsPRaH+SMH{|& zg$8|iODrhMu^^2GXWHh9#M7*JFIMmIlD98s)xM;yVS_o7o*+_lpe^)KsajAfb9x&D zk|7wXz~~3mbcipikv>N~3;%A&!ks=$aeyz7#gJmRZJiv~ZKIhLi;)A7qui0jv6dB+ zERfm(#u|7mmRVa@Ryr1a7~h_EBeaz#W5$K)WDS=tU%=~M{W84u>tBwyz4kVI=lvJ) z-4EP~?|!y)hU10oTr9Z0!0CE%H z!lW}Bi$16^+9H5~+the5ZyQAbIzdWe zx3K^s`q|UrF+bEeJ&x{#Z%=higG-BwWh%?v7GxC{ZIa z-5ryZ(5D24z^h|`!Bk1VH!rI%tvQKNMVL}WBk5W=y^V`735zp06aVwv*aaOV8ZL<_ zvlK6hqM|C8PJ48d33EtgCW1pO=23(j7yGI*TI28FAiaIhQ^xZo0}&c@W_yO0-+B(O zy8Bjq)BShh&9Au&Z+PH#-2aNpxb^mPICK6S*4Nh2=bqUM67vczJ;h_^U$NG$if1?9 zJpT4&ieUlZwW16K<{4cMU1H1;h1Rgd0pXfKKoJSCOX-8}-T|WxBtT@96T$MU<+V9K z?VS#ut9qrFD-eJnMJ47&0g!C2^}8jGC(v%-|NtCRDkH-~x&FY>m~1047TH5OK(+BmB?Gl-rz6}xsg2}%sS zh@oJ>!hoD-SYKP`0K-nmX$7Mzj0#UNH_LVdWaf<%M3cj|pOv8Vu;elEzIddnrf@MUIV zv?AYSt*Dt?0b?vkR6z47g3L5D^+74k@ zE(&3&h)58;P+wAG-B_#e`K2~*MCeNkUekfay#Nv~U`RrTJf+ybT$pZfU@UIQYyu3C zFaQ-9!%#7}!%G3oqo^g|lHjrU9rRl=BP1LE4%Bcv;62D;j@=vY?Fw4r_ER+^w*_g4u;%~?qmrYu zQSZj{+ptoQ2o{!BMgxEk#weV|cG>}oG!7t1qGM*FZbgI&a!n-8X3=9R3x|k^CXp`Z zSR;*Eztlz&bA(vez8k4N`sOUjqzKE=crcHXSnLFjt&7ZC$}V) zS$EJoKGK2n<$#3`6#+{|dlSL4iF_--)Bs}y60QKcQGhO%!42_{r5H(&ZN@QO|Euok&RCi$r#+TaK76wwS z#;Q^3amF4bDiQ4eUm+N-WU{$du#OWOWO092CZFzFQCB0kDJiX=c8U#1T2x(J+2uF`pi zqBxKF(p#(&uplUBIjDYYp^z1(sm-ED5e(dpQpB$1EaCvyX5c1ZIiT9XE#Yu*92Uj? ziQ(Y*7>CCT93IcHe{hK7#R66h!%$I&iaHcn;d>bh>M)=#7O=8FDGSuHKrI6-US0-Z z7~sPKJ}h911?n(|mj$F2pcF2yT7Xi4S|PRY*fNL5HM|T!DUeF1MS@5(Rv2s;U_(JI z1L|Uc4GUN)sEY;4e2#KF$H~C~j`nZByr7eUS_dqQVk7m49K$ilmT1NABvKPwWpYyk zfDE%pzz166+TvX5a~cO$LRSSEJS43+6T}*A9Ht0N6xG$;#9?r)a4FSL=7zFx_v%0b zat8VtYPW%-$rcW$x8cV0Ww@&M!2H#|z}KBTRbGGX5r5kgPuUwj`RLQzzw^mYKlT2{ zpMC7NuU~!qx31oJ_I-y32ftf<@d>T<^HPVWZ76fE6;=(4Qf(-PVX!9zGDt0lmFfb4 z!5r!kcW;V)kKpxUpE^`uqepj!$sy_ypHZ zPOyJ+f`j=S`zL%I9G_r+KHz9E;P7OQ!}%Ns^EnRY3*4A5aQ)~6*N={I-i2mSMSH;$M@r*XTNdrdyjwXGr#ljw|?^d4}JHq z{nlgO^$WlD^q=^b|K`e{_%|Ose(U=`eDJ!D{?_AXKmXx}yK7%~GA*8Z=nE%LJ$e1` z@hc}MSFar&9iI$0jup=o$EQv3Th;K}aC}H1A1W0O8Pdk0l$T4LZ`L9(B#1)HDE5*Y zPodkSI7<%E!VQ5OqiBi^^2=KlFJ(K`9ZbO6B~9OR+v~m`>koV@2Fo}p%;}9*%EauKUja_}~01f1zLBKL7k%{64J$mVm{8`ouqnKK%C} zM<3(&BGfme2~Y)QiYKq%j`uusKYsRi*Kj>;;c%W%z|^~38Hvfs8Yl$|o>kyrgn6HU z+qXNs=F%oE?o6?}KE=jtf_~DY?=o^0^iyW9bfE>u$`Iluml-Q~0A1gQ$y^z}(YTUO zNIRI-xYFs{-B2Vr#C8wKFc|7kFt-J4 zKH#}4SMbTtJ&q4OI^a{+fa4)Y+$4S307e8@^p#EENipjxG72_Np2c@wejGn^&u4MV z_T!jz15%$M$ka7?4fhX)iGE7q^hvhEg=O{l%-P+AqOLnS@kz#_cBtrZVw-X@ z-Va4T`WzXvMMTkP21@Qs62hoZLj|U%wGK#RPQk#3aVFlWw;BBvbq6uY6KVlP!>Oz z%zk6*ZzYcdgv#BqmPn9QUOxOBxw~GJma~8 z+wqRDR37Ih5E2@Zp=`tVJH=glL3eG1rFwdr%wdWEfS6` zbV2OQsI#+(eXCZfz|V?FXXvD0=lEHC_brd%2Ve0pF7G~qiB@QrkR*?ey@C+`Mm-Ja ziU7b&T%NXpw#|Gj7 zUpxek3a|EepMLj${h$6ZKz*4lA-!F4_nzBc|NYo_&9|b|jN=9C#+ieO*9>D2Bu)4g z;*P-&zx==GxAwji4vGsMmVlwao_aU*bAJ~&{uqBtx&)}= zSh(W)!EJcY6Zhk%KeC4Fx{brdgih?L)O|%BAO}@w7CF`dl0u3S=gAAK3=m=UUFn!K zE(4M5&Lj0p#3s50c+ql+D7@+6b={gMft*D|nOQSXbnp<-B+@vyd#1!xF%;(= zU8+bZIJ3Bh@4M}B{J<+dgG)PKKqm`G>X1@DhQ_G&^gY)aVvYhbz#wiWP*l(EF7AGk zlMJSq4?F(&l}qWf&z+OcUD?DVSAl02ljm-f^-o{J?4cv=&WhAmiVYXUkzEqZ$+x*D zAc_?TDAFs$O@LxmRF^bbYef}vjk%Qsu+)dSdlkuwsfw*lr#k5}`V92L0%ahu?=J=% zSHO@QNVGP(h&$^omQXUp1ci?vjXVZ}JO1v||NLDm0M*(b1Jsw)64Lid?%sXd>wW<1 z54;sa%{W;UNaTVtHJacHGZ-`r)?%>$lKotA{q>!blganI^30F_cklY)eq;C2^KbD2 zZH0pxD(chkM*7^}0gfIFfC`Ut?SWz_X4pTt4gd1#2k?_0*~As!z`lYb_G1!afmm@wW1iOSj#zXG~SR_KriCH}vG|Ms30H2oI z+oP5sfxrMPmC~a@0_0{|Rt0l%e*s@qY&*S_HTGQ<8uD`{Ygg;Qz!amXTdLECNfVMJ zc-Ntb@R=(!6EusIgGQ;8V(XX0YB^ZI1vocc#}D82IsCxOKZCvP$B}h_>Vz2&gz17s z`ml-(E;&b9V?zSmZGc(zniCdKKiprp&z;V! zH#%oJz%;8(z0=aS3g}T@9mO0eXTVi*HX6@T733F*#U2}pB>K6a?Y=T9$PxWX;+Q5u zl9s1j!Ckd+bxS~kF|rF#f^22l5b8{r*Sjfk)f9CQl$tP<4#g%IYz;$Q!`!A=lo=Lv z4RhPTNtt0`8<^KM9LziHA7>oRCper>a5zkHW0+#Utl^-}a9G!IWE(iLbsUv7ocJc@ z*u>m7ae_^pU>zq|$1&D1w>8Ym8s>E!#nw@L6N7JH@O3PF9RoHo*gA@DV1ad<*bEC> z!=lcxSjgO#<}84kpSEu+4qIahjkNzkQ%VsnP5q4NPr z7L3zH$12XPth|zQHMpCyex5;`HVi{S!hx>KN!i8cAH662%tv0=|HI$9SAX&|8;}0_ zQ^UXb!eRNP=MEQkb*^u^ew@Ge@F>0F_*is)>^__nvtnjt;V?_^P9#kP(xae5K|&2N z&zajx1k?~@&En?5{U;pE4C3a>y+*`c1g_3l$R5V)`a&nZuN{sJa4=X}Ad*x?G!uIS zs2Ut{u+knI6m^ebr8J)dz;Jv;@Q!z$N=x1;2dE&<(5p00Ztje+sedj2eLb!0u3W3i!4wibCL|m1Oi8|4v9QH0-j44;#{lhC{`gD6x9e} zaiA3Lc7&<~H$f?eloB!$5-}R8qf2HCKm_o{VswyG5oL%BIiL=bI=CdLbODw0#D*#p z)=7f8KuoA)mdI_Tnf_X6CE%k9uqYA)$zx|HS3+}|7pNIhq&KF%E&Pwnp+?ro6sT6= zR>&PTvB(ill}m8q;*K@sg5&^^KyJSo8FM5%fvAI}9hhUu5QzYpf89N)C+%HD45j$o z7Py|=_UqIhy?z^h?gRIwzxUg>+0Q+3*C&7PaOd6EkL-62ZE|ms>7S`OeXV&~lj@cg zHL1IbNKUFrlPa>PE4ja+z^kFT2h>tfONBXo+-l+XV>CgynmAYhyM5;Y!U-a&x(H)U z-Kooy=Cr#A{a$s$!oqiL^mz(84oERu%aN_jGO5#Wn3IdQwvNAhn zj`%heWqzf^0-T6^NAA?AY08k4P^%-QjNJ7E6j5kYcS%w0#VVi@rJf|A`BA5+_CkwQ zf=Y%WA*T-FimV-Sq%js(h%*@+`^sxk$;@iWq5*173!ysilr#ci9q6=!N=Ai(`5#%; zg^(H<3sR;M*EyeK7)?Mp_l5v=VIZ-G{-kA*6a&_}1!n0Gn&yy5J{K4Yh@d*$q=l>` zSI>Tg9tG=m?b^%bmp}UI{P%w4w&Q>F*>msx;K42bfiGz&o+QXg>9HUFS$y`k z$tcA)<5rQvn%Z0o&_PTU|P=puH+W*}*sfVsQXIs<^|L2d+&n>nekeLM94g+P4j zI$}DlIp7hrh*EJdw_1ju#KIvVM2_+o)yR!d^jZ===a0lAaAc|2))RC%foSMs;45h` zfLiGWLBk`rO{?4~0T$IX6M{$z-yRnrLR~!GMG~D-C9*a+7lecTYJifIfF_cWa=!$x z#P^)L@pnz#cx{mkm>>txBxT8Q(GH>=RC6RdY1Lc^G%Dn}s5MIjB<)aLVJ<9K3WbBB zueN)E;<7skT88i&fbJscb#D*11hSftX=97G)vVa z6^>#RLoum^j%}9RXA@C9#C@kdhK{5(3YgJ4T`ChH8p8QM0>}}e zP$Yf2u8iOp_h@8VgL_4Y%c*Sw{M%h*a+<*Ch1#hHC}}&UvWz!c38vU>y%?p6h#`~E zVbp3;=rP4zWVuJYsOAzuOVR`0)AH0rJlb@TP_Us#*juaUg%&D_977FU8ENluV@QUF z6{pk^tUY7(8msT1)rxf)NJI3bZW}ON7TWFzr)jzIiM$56yr9n!2s~o=8o>*MV6LIT z7llPdX7Z^aG6qPtU7Y~le5-gKAmLxs6tX;q`nctDG)9!>aLy)w3+v&?F*k5x-xR#2 z!m%}V>}(BCJqo9+jEdw)%5cFl*f10yG&`h|XRhC!{^jr7hkx*a*=K(BA^Rr}9Z#oo z*MG0Lyv3wncZZj`)lvqaqDf2~<^^5~yjHkF%nasX(hv!$sLf2wp@$Aah%$Wo zpxdQD9stsCgy)c9YMEoHQ)@+9C59D21Kv&+ASH^MQdxvpc^HAKd6&asDh9|g0Q8$z zIQZ{)=PFp83g9~B07ZKUoC{ZTA~O>~rjim;;)J0(N>sYwT!lyxWZsm8UvhI2jw535 zB!WUmu`N6mYemul8Hbqki#WauPJ)D#lzkva#kzJHfzF`@fUksStahQX@e-*tJblkco<@!3WHuH#7^daN+Wn^ip1d})U$=|%;UGQ zaHx_)Lkpq}@eRSbh11vA2R=gs?a0TJ2u_SyBsg(Q93qv@mR8_)jGew|f0*X5wX3-d=h@BLrXztR&ws;OPJ2pEBf|2Fe zwRz1?MOplU>shkyecY6P89 zydyyb)C(OGbBG5iXUScHD)mw?E+;wSt`zaO7a^470ZvF!f;+){z(zjA)-0peZWQkz z36iLYrWZFCTjOHfNX+g`Zk9r4(ZGTXh}^W75eyo!k|=A0&Y%%rBCxj|SrZ*a-RPK! za29Qg!9#E2;W6HF` z`5;cNLV+Vi!2!V$a=C%Ei6P~ z73oz)0x9@2^d@mlcKQLjwuaiwxnf9-h=i3)B*03?cjk$r0y-4n%F3}p`vD5<6TrxN z#5jm7iF{nl!tfOb&-V}+xsXC>+|G-0EXp*EztcFsOk?X#PNHQ^@Ro|0*Wf)kYUDWD z#z5|&#_te;xp1BmEt}8=fb`Fz(wuR4f^a$&quYrMkVVDX>;T*H9Jcd5L^_}JYp$AH zbWM(BCUvsuk36&0{fke$X8w~8zWmodv^e+J;PPjx&0kYWqI7LgH|`B&P*l7+rP=Vp z576S1S&4C4CbOew*-~6vk{Rbg8khu-5O|_y-2R4D6Z1jpeY<-|u+v{uf#yH{a-M&`vc#0o;|%Y|Irz(7*^5 zqe_H|C`g!WA&JhGUxSwsM?!SXDyj;&D9>g(L``IXU|N2)C_W!FnEG$QYcC zg6@&pM8YB!+>ELst0HHLL)xArwqFsT&tR-uV?MGFjH6VQ$8RacB1pmoFR`dv{_t4r zo(Kv84j(x&icti{_$&kl5swmB&fMF|Ygdm28fCS(!SlC}3L)x9nc%C8=jP&eqY5wb zI9V*jTIqnRaHtsh6gQX;P$>{pn9osdjx;3fOxJLxyN0g6;VSCh2?n3xz}H-L$3FFi zbNS~#e*4q!`NYP*`uKjoI}G*PV90{5n5X2TS+tXsdjJ|vuI>4lEdos^G|~ml^KE0TN$IaU2y(52jge7OIVwB zSes6;HtDcA?XWSMU^eNoHtDd|DK=*vw$`UupY&LtbXcEcOghD^Pnh)yYtxL`G-EQ! z=sU$E0h2zX&+$16rWu&@34L-TH3l+w9de?heF|lERtW$Wq%PBPm@;z8$YIr+XhN4V zy5J^9PjmsYI1(xbJHf&W76AIL$Ikg47pB*7cCzo={*3oB_%Kht=x2_+T|fA_ySjh+ z(c6FbUp#v2zj|occzsunUae5?L`}?+i_*P9jUp|>J^-PhW!07pGnP6d6(Vi$mjLJ+ zPLUk$PNl>6UE_uu4T1)Mmg7C8969QF40UqUAv$Z;JRc1FAqILj)*p zOvM6$l_~{b*bC?OMYiAwBt*Iqpkl~cB7Zn40PQ-Q?XKXC{#k6*>zEdn8xll1m?RMT z7o!WyOEo@UP8lkImjOu~eb0)&8W|7zE~8K3h}tEjtmwLqz|m(+17s#SVb&!~I>B_( zVK(J4(=K7HQ>;%j`cBYwg6TA2I_uC+Gp5rHeV+(Q6aKwE>##P>n9ee$6UA&6pJyGW zGscBYCmEACcRCdUTc?;#6S_{(bwVJUurNaI6kQf1b)@9zIs)3Xa~{(xrhUS!Q*>D| z?F8!+#dbH~Tz(cW@2}#X&12lR_Y_{a_Icl&9QZ*_m}7>yuLE|m{N9tV>woH#7eD%& zS1-N)SvmKEBDf`WlPZ#>R#S({q+f$*h6_0-8#5z?`4TpVWR zW;E!9@y@)i@HoNdn9&h7XqqHM_k;K{M1Jb}MvH9dvq*;_GRDRhR~bb>1UJkJ#_z$E zHSS4T@mq%c?cDa{rE>uA^-o+*5{ruhz-h*7I*Mwk3v9 z&Ud5ksLUTj_w=s=!&5v$I__Q(NF)b1W(sB%u7awp*IFJyE{|`y$Vut zxC%=yWjg1%Cge$vF6XdNW=NNjaz{mbSh$+?E5&(|g0NFU*Ac{WPUyQF&VqujQ{+A) zCEl~2u)NhIX>d=9*(71oD`uTy+6!jAV7*tYO%m3p37cyZY|bXwoORe<>#?~$!R9Pu zW2)GmCG5;Hwx$W&(}b;A!uDFm)~v%u@7SEOV)8~WJbo=amkBefPis|aQYG%B0@n)0ZTm^^>58hM5I~B8Ys9YNg@J> zh-*@3g2ZG{skHJzRcMMNIN&DbI4A;oz=jCmNffF{$aytziBncUNh5o#G=(|{U>e-s z2!PT5_=7!f|8w&_XKn`vm*l-qd~jcI=UWy3<2w!SeJjs-?^}Vl{;J@;?_Hky61T4j zDt_rLeQ%%hyD!}T-Przyw_{-m$HM?sM_m{ys2py=u2w9RgpR?7clToN-90JOx81${ z&cF8G{Q0!CbLj<{sW$TD)u^bnk|0TOeEoe``@O#!=~g0>0VxyCqEr#E4B%wIlh0JV z?}>Bx_~9iyJ>S9AqaJ0xASp%EI~2%DaRM41#2^t#xDzz17{AL5M?~Z7xoDajfPuTQ zhDW`|Zi`5Tj4*)%jvS#fg0Sd~yE=F3Ryn1KGptv~%FS?4>EAdFFo;a~NbD1sDy6y; z2tt9}{HORxp{%dWu;3D@g3VAhp^9Q~LvM!dJV)Oxu$@mZ!&R)w5zekX<`=pf$XnuT z8*^FT-Wfjeg;(`I_uz$xf9tWmUwqWJ{#V(C)JYmLq+867LzkB6Gzv!Zn{mv@#vt;D zDIh?t1IGXW+`LpnfRc;BHLHjuF?Zyy2S!eZV!t3JMYO{F7-kiUnTx}!PdxnBn5KsM zE}>zS0i=vNrA|piij|1gCRc9(oO~8iwKnUlld4s8X(bHKiriLMeLXmN_dosg&;Qr| z?2do`9n**J!~*Ynm*E}nSV~v#dKV}AYtWv*R!>z+NN<;v-*fR*--)wt_&yvj6vw4N z)L2_l7>QrQ^`M#DYbg+vclVI@ZY>s*Z+rRXyZ-ur`y**<`{D{1u{FT*wmUj=P+Otj z&dK%nVeP@c3LHGh6{t)-BHBT6M)3+WMX8R#3a0x{WB;JyXxPFti)}n~at;Uc8Dvpl zMNq1vKwy#mv$}%mTNSm^>)H@{v{q-&1vNrV*x%)DC;>i%XevngeEh!YKWa5}Idc`k zxtqsvqt`Y7Nr{;$mEkOnN9+<~95plS#mtd2iw_8>OmB*HhSy-Uwu%650}N&4dTUso zybh3hf|Lrx1{8O=7)o8BQ^PtI*h&ZJCKVgqF}Btcw%4ldZVSrR%w^X3vrpchf9j+6 zeEL@&-~Qm$(tV$lvXRhLO_ENIPmmy(Or~Mk42xyBdAJI=OoJvunOq|meB*Ki%|TeD zxk>;)%ED)nDgU zUuU(x3_i^0R5{{|K}D3hR)K|K!4XD=2Kb?XLf~TCM8ksHICu+ge#BnzMk483ANyxN z_TI1m(f>Yw@W20G5AkaR5sx6%+Disjryii7={=WT^F7%6hPUHrA()p6748z%XqK+k z7!Kf$S}Kw_Ozl0%%dhUtPbP1^Yx7-y_uq%v zNB!Eh67NI^tVBbwSsY9H22W8Cy2aODrC(m z_K0&I*KjqURY_1S2k`KFjUttRLDiUh-Gtm|gtZiNy7UW>+>s$bWnhkIMG|A?OQIQ) z4_NCIeR8Z#kC1YPYJqn^&Yj8n8rE;w@Td20PyggME?xVD&ustvQ`5^oG_40SR4)SL zGG9Pdy-PDO7^*P?X|*!ydAK^4++V2$Dhx8BhTEN<5)tN;)Lql(RrFtLmw!cb|Nf!x zp4wiUNI~75%JfVY+mn>eXim2{bW2sH4$*49aNx+@u1dv?0rgrf{*2A*iPfRP=iTDC z+=()Oz{>nj*kbV}b6<0qXI1lB6=bTU)~H$>oKpoIGbI$u1Sp2c7|4Dnp<9Q?uHlWA zsP8)IlHLEv&%En%c*{F7-utewDg{U_0qP|KC;U+WC>MRt<=1=;L@krp>Jjz#j-ucpe7@ob3NL);{*v zf&C8?pmfFq1u(R99$_`9EF4fu3Ckj@e@F@|UgT(k2Ip4{SpfIKQbo#`JrYlaj2a3C z1e4_t@ky9nKu7D2E|a8Dx@y`9CaDDDw~B{3x+O3Rhj@Y(K`epX4?_mITSJ(c6W~rQ zLN|(bOo$4|iNy>If;5VX3DrYXWXvn;1ezk{46g=Fj17bGT`Yr2FQs(|dBxx27c9Ny!1H z@PE>lGC86GYS1qt#qpvHW);tr#p2^K4F9^!Pd>0%4A)K$k1hrX-uw$qLqsp5bjnr z66Zzoerp*b-AF6|9EBok=+H<&O%)RQ8mGm^osNuj{0?Wo+RV!pp?+JjF1Du&r$0|& zXAD9NafLf6LN7$!3-w%~&uiBmbt@6<+B#|oFY#d2BEC@KI}Pv-Wd~9$QpS@d>b5kl z8M+WRM{)qtLDbMofh%JcMFrK8n2&hCJe%B=Cta6Pit9$UhVbBmC_O+Ou`GzZgv8N9kwbFpeDi$$%A#pj0k z;@=)09KG-4#=*7Y`N5rwqvP)`i}KAXo|9NrbA@{vYSm(9qAubpDZ-$*X)J@2jZ-n| zp-4k?;3Ri!5Y4}|-PP}V@TcFkAV~daE2|xJ1-z6%b*cdhNZ-??{4Z{K;O&^b?mIC^ zkNIMN!=dIV0~O*e)MKax&g3Adb@6VPy?6KUWcpqA?!EJ`|DS#&t?ymD37lfS#{Vhe z(P7U^f{9~(_yMea{Qm-6|7`#rkUP{$M>?d0>TTgu|6}GL-GC*If&*y_0TFYDP%JNd zv(zCldqL|Vd$#&UJR^uXxcHjVf^QK$KVCa_$hca2@b=X0DA0>1es6jV{z~S8@8kVe z$1t9+9iuJR6&N>`QX&|M02U!15s zm|VIWZi+fAVl8m-5C9ll+EQ3BWrcmZPx{bg81l<@*52}+-~Jly*EUwk;)~k2R2zo? zgO#U{pM5`Y{5Yli7C0=*IJ`(9GV*aj8UPOqb28Vn1|ev0K-wHVsRxLK5ywTqZFm&7 za|Gc8IL2YMP#W%<1eG|J+xi&GHQwu{&ygiU+P&yl+3vmUTZH8()8^9p)y|^St`_^$d>HN)!Hz;sxDaf)o|Q5iK&AOA zVbUR#KYNI_jg|mFA+O(j>i>}laDFZAN|gjHh3 z`|&=!XS*M-)A}dzUHff$O#+W5VTeuwQgUW|P?pe28AzQV^$O_(s^Q8o6`bgXzyFa7 z^1;V99(-fEmL{QW{(N%%ENysvtP2&WahrMxQG}~){Kiw(xiKN|+uWfAo?cK{4 zzUjg(=YQhd<-NbSvAwm`^^-;4O-s_eoB%Pn5(|kAy9!-5 zG(b!uWlEP4a#BfIa?WCD(%0F}?zzo#=l{dqTWJ)x*HKx;jFqg zmNf{VvOE$*QQ#1DUtnN3gEGDBAJlG!8?1;p3FaxLF9IAuLZ?VV!qdnK=@5z$A$qa9 z%lIupBS_(S0S+=osMBM;9k1HXYkn`W0OKV5X~&}d-oA_HTfW-$ z+jq; zvv=eCZ5RK_-lcQ@`TFkGj&{=lE|N8ONm7=U1r!1|S{MWoHy3N2i=HMfO8x5e|^_k>XN6V zL+mH}E~VG~T0;46HV=6BYB z3EZYQC;_0HSvCVRayWud5rRntYcQt7_|mM5&&&x)vy`;fm#qsI?z?#V?f?6|%a=>q zIP*74*I%`MGV?wSNs=pTlo5bp&2V!KAFv2MZuG`X3xJyo(*WVDbodDXz`NeXA7A2j z+5w7-BRph1C`4GvA|^VV&V;3_YnaZ(xL}JFRD}zZGk*~nwIxK7SuV2icX#Gog+x3a zn}9&W#TyGaxCx#YAQ4=i8Lz9-bON9?&4YxSCS*y{Hvv{tVk#O}wiG5~^IhtEgmyZW z0IdMFRWRf^R2Vl-hl`HKk;|iar&Je(z68t*E__e2k_IR4HRcVEV{f|u_>Aqj2}A-! zUzodH;`sy#n65*7VjsJ5D?a($?9u(n#*XN8$J{C~coOQxOe#DS;)p(TVcM9!#;kZ! zwUoroWzc@|e_h|+`WIWbT=;kQ?zsG)ZJs$h&C}UYPTeG>lv2u)x-25@npAY0U_JvC z?lgq7O{OWM<1qFmbdit%r4g`K<|LHD>MSwVtZxd6gk4gZ_Puslb5_k=mwjV*XL9NC z<-d6D)(bn`*4F=h@N`vDo}{cbf!A$O35>Xi%~LtLt&#rX@Aio9fXJV{^@rZ|mhr%s zxSeW%iad@0=%>T&u%a|iTX$w^j1;^T22JiBsYWyb3Kt386;=RgwaFj&{Rjh%;(=g= zfu&Apxxh89L=J?DvjHkb>rUf{5T&PRttSQZXeHOzq$6|CPAp-k3_i|*sINk+o;Mu= zeX|IU641u;*z?96t{%hZYq#U?yk~o^aXxJM+wv*_)-g<6xGhsCcW#ErjQm^h5V1Ln z(g+dhg&_&4o63;R;L}%MW54-@JL*Gr;Xx$a2T4fk@LGlL8v+|RFDd2lzXAYb#o$_8 z#JppCV@fFphG90FLI>U`X94bFMT`J099iE zxlUr41s4z|*;u%3a2eOs$1Z1;-q}eM$=X8t;ubU!Eskr~5NA&$l;k5<6pCU5N7M2w z#owgt)@Pd8NBRUOU*dZw1qPQ9w~t+pQm$BXo@mZ2m-06$vh=P2!SkUT!!XKk5=C#-<^T2XeW9b0j=Q z+Hdf<_K0d6oA$er_S6AMBDo`grldJ#UF1Id?wK>+yLa*I?fu%?PeHmPMb4^PM4E1{ z-D8<40dYqM0aU`u=@zOJ&fA~6nf_|GQw>llR%^D>Fk=(ZOL_XxM) z%-L9Oo=9%WoOiIglB6%L6^Zwx5ql7JwMv}}TKT?_AL1RG<7ia;fUo4MUZK=cG->ZE z-!&qRZ{rxgFTpZiRA7&R#gbp4T@1#ysg!rSEIyIs(W+^Zvp+#@*X9h8|VP zaghTCs@s1ZJzkQ*OVCa=K(#3raM4i|Vj}w%htw7Y^(J&dD5avR0Ev~`gqr~{PQwed z)nOkGVVDJQvu0Sv<70uwNyMX2>I-!3o{;|Su6MI8je^FtrBvb!QU#`c#WuF;-) z)NQm5FM*?7qoo{193NE5$7!6+$jQX-h<=9nG2Sm;yhSX~29&XQ+q10pWfbqMdD*W* zIshz7zDt%B}t;%sdjxzvNoAv_x!m(b>`x^ z<9>bi6Gg;KkTlGg4%R-C=#u~pv+^ct@}_bVm*_Ca+b;jmkKG#&^^&#I4p1Zx2gEC+ zRw@iZU5mC*3EFyNF|!Ohz&Iz3GQ_d$Zo;9l1syII3>yWG_HCsvln3d`DuEfTUP7~Y z2vR`+$}5FMDW;y5tHxEdE(k?CAjCFm)FES<*cI@J3W#!&(=5qxidVejJr%0BDvyp1;b0k=tlvMawD`)&wX9I1A?v7V4*{{1VDx*9jip zzbwD=$gDgko7dFkmhjO}Bxg;hDQa3jZ`oSvYJC7YGdi%LO~SlX!3*;|9Wi9IhpmNt@WQEqNBS>5FShY=9i& z)eWP?oJQD6Ot~~Vp);&^IcrW@x=Fvav$y+S?(FOw=Slw)NU6A(Ba9KEOP{c@+D>G? zK#1Ut-jXU^Hr!Aa{=bY~uRhRMwVif=;`B(9kHd)GbWZM&96QTER@)`&Jb{6$1TTk0 z)c*6^_$}8{d~Tn7jEjRCU@Z^0M3Dm!m_m0#qKadiyFplIU||Xuf)0YKkN__gjM*C7 z!XGVx(KCV}k8_K>RgveLYlFKT`%NHoH<%UPZv>eD1xgHYpUN_7x2rdN7$7Rr{9a09U4Nf;Gj!d2pd(gYxG8YX zxNw`FDa)CU~Z0Z;8u@rkE9T-WZY4t|SPH#3v!j?{HA*VNH2z~`)-j^RwGO!eV4PPoace13+j|Grp;?#1};bpkU ziMQk!NjplwYyTQJG>&!T$^(#^r4K=)Ttm?OizY{i)Xv zxchaNBu2w=WX+(+IC5~&d2SR0$uWKsKu+nk=l=9Nzk^Ts(zH_zP;3GTJ&p2DW=9lG z2tbcb3&H{DsqD4F+#OW(jY%uxpqrY5*S#62K&D-#?On8djf;_)3W90?V^kjhB1eH> z$$7D`oHX$#L_%JROP|?5D?HMHH13KoV1K-`^y@oU8*wZ+N*LRO<$%q9u0_+N)joeogh1(#gieKaOSQCCh2IV<|DIkJe0C^U5s_LH^W-8*~!&u{K- z@8(H=1Wnlmrck(vxG+3~>=+w;*9aIXJp8?tt8a@s%=b;bFrF8pn}+B(TK1x^&I3qU z0WQ2O(TR6%<4Mw^K(Io;0=R{$0K^eZg4`vMBwb2sTU%Ri-adcsqU8M3=2l$@gsQEWn?GSI<@Y3}KKZsTnWiCGl2xQHDEW@Rj01#e)oavv`MhEZ%oT!WU!0kC0tJ}f|`KtNF9_fgiV0TU`m zE$$~MfDbF6R05;|yjnlvbGw(o$LHaDdk%7U99oV?12YlxlBa%D*ZuR)Y~Zou$y0r< z7n>Bs-9NOuPp%Xb!gv&7vaDP5SO9H+Hw*xW2o6U!Kega+*p?DT#{G6IDj% zQZkX_l#kdq>Y{)YIklQTs&NI38w~~uu?L7eS zZ{GV1HkO{{e~@hQhi|7EpxUq*92lJ=n~6S#(m@{)6^J?qGR*2`6$OB3atX!xh&3bF zJg=>M&5tY8xh|HaVM)fwpI+ZU8*`T_b*&OqaS}}ibd)VYt;=Gg(eYU2nK!RB|M_FC4mFK z1I1mlik$NM`pNWUYj5}Mc{Vf23c`{CDTyjQ$f|`Ua-8odOTJ<~6Z($QrD5a^DQ+zr zpA+D@jmugE0E%KwJ+Qn7q=8XTp72%SC^4Qqd?}Jih={Fu0A^A}-}T6o-ZwY5&+qN- zyv=p;D_+HTOxrXA;DmtB8^pE7GVX07a0uk`^hCe0-R=LcIVfr*f-!K9h;c>ML|-4` z5>iU^8!45!t^o)VG6q?#hUU4!3?ga{X6TI1YANa)LJ1h7TF$8A$u@GiwP zkx;-!|2;$;S`-OD082GdzKrFeGL9Gf&=x>@5ugF52o@kfZfdFL@S0_@0G_-t#s0B8 z;*bREL

w!i*YlBcKBUNb{s;?kTIBuSuUhPo}@IvA6ewYg^mffK&&vD#u;i;g#jT z0E1>0urP$vKFB=~@W{_=b8YzxRTM-dgOCrsp8k5oX zn-#~^7=x?F_r|&(;;6#!s~1#M6fdYgfOT(}Ue+SJ;@|}fD{)Q!Z!lCh6fY=V z$Jg>#@NaL|X^Sj?HFPCrLCEos3RwcPg%y!LVzIFZvp1-)4qZZmrQR3AntuM-DXx~u z$E)id28E{V1?FbVe5smv539@wnm8}>VF7dZoHJnZtFw)bGdt(czrJ6aLUQUgWeC|q zAWBa`0id%tp*dm(Fi03g&|DE#II2cFqe!=GBe1n&g7cCP zf?Ir|gJbHjaCmKi#tq3EkOcRdQWEJVm~L)P*S9ynO|t*8Sb18L3OW4X7(UvPW_bx>bj@eY3k0Lr~_+`eQrgA}+c)uwYHVVTg8lgi|R8q^q@=IW)} zRet~GLQzJd$c4lNl176_nx>3mlsAr}iE9QH7abJ35a>c6i-a@?(%^hwD10HnLLiF@ zUl>pTDGm$<9UKxbUpRbG;R}b&0b2mRpo&N;kC&mq7X?1>T0T^MUjZ!+EzaKt8(*jn zsTC*{S{+gxS^=%#c~v1*AXTB3__+wAFbYUbkPl zl=5+#f?EUJMu2JO@BFR_txWpwC!hS`+o=aA50VX#r9!2ri~7sKgteY(YZYD+|gl=#Tp>ie6W|vYRL!WGFt}lWVFA7ma4g(mDY1^6C%E(&s-n1{ zNXEhxgC`Uy1{V}$6rZ4YkLn#t%_x>oyobpIh6!9|aG9W@hoQqz6Bb2LYr;@7ighSe zF%(6$IKQT`T1_c{TL-H>P%~gG8PBal;7BmA=AS!#xXca39A@ff35zQJ#PxMNxxe+? zk@e>XQvmKAfJtx@0_G?eVM^a5Ku+X_+`yE{q7TltH!iJjZ@)UzB|#M8A~bcC*zgFB zT>K3jn!i$8I&$gW(vE2WGe!jjf$^Q6L!U!(2IKP(fW>|+fy(37ql!Se z0}@^cpo$1QkJor#zDw$I+T7g5+S=CJM7qy-HA4jiQHrWPm0ivS{#Dvq=7W(6*ZPE>diLnxfQ zG1MhMXnaAa`v|2h5SAOQw4SAmgSw8BMUUeX$H8+IPd^7dbv5JJqYlsPE1tUMc;c$z zxvRj{=PI6ldLPd`djnVZ3$E-dp1$sQW*@kExQ=J9uj85LGM;%(@a#3mbJq;dKD)qk z&&=`km4c_At9bUR;MuDko_<#G?9~p>T}ycOO2uS=dLC^cP-)Se#W)^gu~;6{iB2%2ZDp6gsc0&{=VSgFyZh(adf0OJV-b>%s84) zaD0+cOTv87<6zEGN2ooHj|9U2*uV-j=A0XHAT%PxqIFit=`b&f$M)B8<+y*Q)L}zR z0fV=NnFKbgxNG<6`*omNC3bJ z&;+v=i#0f~IA|n^;HIzwXWnRZImw~)-%_S&lB3Ece zJ)x*VQbyjG+18n@Z_Y{F;J@lE^$VDNmZTk{VpOLHt?3kXLl+DHZt{lq7*RVfp5`mt zzA6{}%C-EaKBZqhbI+U8-W&f6=GI|RD7q~QNlHS-w_tlqF{^G0)L-c8@GraL<1^p& zz{UUF-}&*sFx}og2l6{Bxa?sy3*{qVlxZs2+hPCL(SP!912;YZAOpFN3Mzu;`T%n{ z28cwEssZz1iZa>2Lyzs?>1%>Vu1;}qEGR>P$O)1QT#iv4FjXX^JXI>F~zSqa&jc4&sXXaCY@n2acp&tS$~4_+cQk^0M9goi3lB1)nQhh zm6OFG8*=@4hX40R?k+$3@a2y_i|MPx)+a9Rngm`L16K{w5(HK&5G5DE#p4hYn`~_S z%ANPU?2a=RF5DwsKPruLN+|S^iz%f3%dG;cu&D0RI4U}%bD7jiAfr#zvW>to@-)jo z-B+w*!Cf^F8?hw-W=t689^-^D=UDtP_Ko!3be+5*FBS6zhI)xarDrn?L&4KeJc`FY z@$p~0v48!y6Y~EdsU`!UW1)`dkNXlVW0{O#2LV)n>xqB*V{c>)y?3(G$Z`5KqXZu~mBPyHPNlq8lucLkbTrsgnSEdXV#9_P2iwc%d; z!e?HAPdwJ)Dt2)FWPk#gmx5Xs5GzPZu`YXsFBX6R3C)^B!+<-WVlZJwC;F+gypt7% zu?T?b445{nEc}!bN;T9IOwZ@5mE8wttKM!w=l~ z&@Vi(@tKm>-sZLjQ#eFnr7$J3iMSJF$U##zpOZk~FcaTCd*-vZ-*?aM_U`T(Ntymb zG}i%{6=uvWbeh7-FX8b{k92Zk3VLjNh-CngMaaSh;4H9{y+Vx)uWBR}`39JN*Ea2~c})_|rH+kNGgr zOS%N-6h16wOt)g@=Hgnt{BkGd=O>HBx$k)8-hc78|BpX1-FZO{s*%MC2Zi9E)KPBy zMgY`@f`dwp=?!3$AUXDn0Hv@K;Is9u_(z|54LLS!uo*bf+Pxfg*yGvL8NgaY1q0Rm8fzXve5^E-?nw^Zz_O1M(+*SJF5x}i(}pBZ&b zpo8nt{=Pr-$xp~L4?pw|47jK%eV;X5kFnBp19DMj5E0PrLX>V6d}N2_{JO_~`o})J zeE6?CNS$^aRMzo`A0Ok;qFRMB)FT0aCs!9q25)Tn3k~7#i_)YI9-t8( zA_|rrp_teFl-LLiP@w8Mo|~`Zi37(qa~#YUn0vypC(JEju}C;R>2WykaAncs$}qvT zI>EJ)aa1!7tj7&>xLPN;UM9FP^f)Lz4s3#hnz3Iqu6c)R*5jIYxLP|L+7ySFV&A9O zw+`1lVc#Y=Y}P#SN_C8fLgwdt9wkT=OY^vk9(Sk1IC84WD8k6YSRs z_G^b5C1byIxKVo?pvS&-xM3Zx*Muu|f~%#+_0r+Wk>c<~u)o;Gvx}GGvj=zK@%?r9 z;Iuh}(dvZUXjZ6ZNi3rY#=FEYlZz263*_wHMUL>U>(NbS z3)MVh)JbrL+Ja=mrzn8DftIL1$&8JQ5Gj=BT)&ZPE5Iqh%2|CgoE;4aKQuveMCEV1 zf75pbi8cCeTzwM@$JW+9z1IQIQCWB}#r7pfCo3G&_*<|>qJrG_GMTMmI-R{NLEhI~ zF2qBUh(K-C0DGbLItO8yKOGQwl{=$zFR-eVftK=^;H#F zJ4jNIY?d^1L~RU5G-_lw-fHFZ%6GBjMJBjLxi{@@Wbs!n=N(Me@W?ZU8%GKIIzjbJ zMg5|pEC$rYG0J?vFi)sMLM?()608Vd0vUibD6kOtpfJlY%WzBZqL8Y60 zs8wNuz-s~)3Mqo@87OoSutDHMf>++xOM-X?iUKu5EkmtiW#yV-g92_oMLPfhZ|zA$ zK~zPcRRL32Rk&r;L0~n(YDTdh3rm=Lhgz6=cQJ5bU#V+&}!JB$g%u z5ilGk9ga%Ey!Zh;t-*7w3yME3bly<15{!#;n#o5e#+16_KP6p?hp@f1c3t~t~*+q9$rD3*SR!c73gFF>GM6+@hA+BC1;P9ZVW-*LDwxL zXVgE%GJx2*p!n8aXc7IP+Gz(Ui{a<)6VpbWKq$sn4i=&AW)MU58lI4)G0rPTuD165 zO;-ZK{5n!Kn%21;-u_tm4M8z(SojPN->A4c9K#k4FBu-0(G$4XW%T0aMFl|_#uaR6 zPtWUmln-wNb9Svpm;#GPGSHP>6%>t{f&f7sOs65ChqExjGktIaX%eTz z@}nnwaIX+#PgMMSIHJ0smI5CXPu=M8_>D~*ONYT6=EE1w9bDn2zF2fvEO1h&^dM*K z5_&&wP#%ilBm#iJvf73ofcCle-6W@!m5VdPBE)8b#-#*JIXMo;(wlqyZJdK>F&vFc zB4ZxElTdlC1>guvj`Jh0IUXN#JoX{JCn+^RS#FAs;5LB(AI_=BN?1|d#)u)T2&cJ5 zU>4O~G_jgV3)2t>lB)m;GZ)cN?8W5swKA^K{=0d6Nr;iZ8|@|W9=a%RL>{LL0PV1_ zZ9I3hja`k7fA+eXG}1wtHRdX#uTk8!33h<}F{yeFo+ zS(sp6GD=OjF;_hP+ycX59f}<16Ic;*Ss_rXD3Wo49#E> zLT^MNG_kPJZaj2|VT~SRJQ@wh4!VB z9gv5Y;2aW)#A%~=*ly5rt}(L1#6rXy+xU22srE+!pqTDAyog!EfD02*Nw!v6WXwX$ zOF9OdKwJ!wH>zFmrWV@;uq5J}TINd-`h_46;SP`Rkr~f70fr9qVT%2^V6iY{YPaF$ z%vFUHQRyMx)Rxyeh<>zA5zs9BV7xfg!pEf=t#56CRuEi9&_Yuoqm+U=RM=qf;*nTO z0T);WhM|mpFU1UAg%;6b@Je7%D95=0S#=CU0U)SG6TG{?E8~3|>#KFZm4l2MN5DZ% zSUCGc1vwX(RVw$ZcNm6@YAK~OsW~NdP@4KjzayI7+hS$VZKR!1W=)ZtC3T$`ctTt3 zE#=8ll;h??4{Xk%k;&xLMvP}WHdZa4Gahi{SezPw#s`u6WGzPmYa=sqpa6{+#G55Y zN|ZDPBnPdJ*BrT_b`F8Ev0>x2R`O&u_-o%w5_G9Y-*r&c^Kg5F;~KvUML7@g)|>!G zY>QEfJ_vB0pGGd~`HSZd-A+9~1$RsfQf&#&nv%8YFUSQ55QJ|qcgjx@x-G^DyNPqU z>GSGWtK-}zxc#|07o1p5K1CZyg1KN`1w(a|p#X)1Z4P+l4pge4)DljMiaLq}vFp(r zo&;?{w~@*Y08|tBb#PO{WXLcf09oRBEW8y-_LUaK;*pGieq81)$shl&VKGc86FjP~ zum{m65on^=>fs#ASiL4tlM|`tKn4!xifbo835QvZutiz)#Bdd4RL?NWnLCn@<4bhv z45c^(jZ>x`3J5i=Lpbe{s0|gR8t8u3(AtiD$ z5;=Orn6gL|T5Cm7g=9oP!3#Bl&*~l8Un1cfBF(j6HDH+R`|`9zQ@uMD)iEyvvd$rh zLYD|-qk^T_0SL$$Vuf{ARfOFq77Q0D@919(6c6HF2QA43?lfE=ENhhnSl&q8(SXRxRA!d#fBHxN9eZrF9sv7x!jlH znODXpw#E=U$r*vT#P36=7$-$rZn6^nYG;5$LU#%!S8rhu5|Ac%F5jIO7@v*cL4e_$ zlOy*qHo2;}2n4?7>eqSu=Fb&<@o-Iv@%Eq}mb>ATID7+dd1qQe{a3!7c7UoJIns)Q zieVFaGcmmhcRJ^O3_v1sNE14s?~)6ai~09$U-bKu;Fs?)VWV8$C`_rFP>Otjz{HR- zpqDf%+cgNv>cHs@J%J0uD{7_kIOmK~3Wi|_eHC}!T3Mxp?-UE0GTh?5#`k6+Hkn1r z+6G=E(pn9*R+Q49GWs3okBc~TW(^=}<^3V_#c>cU2p+tSgXf!5Pv$Tj4;eN$iewTQ zDPoZ$GHi~w9k!3Ti=2oS^^iZO!2r~CC-&4 z-LZvkFj^CB+nhq+nlscffm>i8Im2eDqm_`?bSyU_-XZvS!RFqrSQ~G&I!IrkcFF;Y znkKIW8H~bLW2~1ufAvUdE5YrgWi=GBVSrUrX}iJmul_|}JpUUZw?I*l zG-@p;K`lo}J-xw^8ex$G5YS1Giv_MF+Vv$t>GOXIyQbLFfGA~fPvr=XgPA! zQcxo&5*I~~RUAVupy_`II9!Xo7Z2vsljEYEsBsS-iuL90PN&b15M#k%bW%jEIB`Xp z10pT^=gKew5EQ7Gx&T>l&Y1|6OS*~g;|RwywDD}y7Kc`6l*Bc;r1q#wLTjxN!p)7= zE;NAhFx4ZJ<4f`h6F5e_g?K@(6{;D<72*Yof{Fq&$Gp&LRVq{Z)>=^AQOvMdEMO5| z22l*Ppjsg@2CLq3EETI!w39iJiqXPXqSEeA3iV#G?kF-*_r)v%tRGjaVJHJ?VeHm; zY=k+~$|^Fv54p2iSya6m@k+?CRk~-2mD_kYo+Lm@$vy2DQSfB{vV4S#lljRPv9o?cD6(9`w~j+s2&p zTwT$@5D=HwqiDzLsnHtO_A5GU2AK##e?)L{xA5g^9Arb70D~6F+qk9(5N=I`n;Op< zpA-mDycr(^1S*~cKtx#N)Q%n4a8Op2rq^3}CIl$4xC5_+X0TxLv#)A9^#Ij&CYrF7 z3Dm+9nQ?WD6{9D4=ve?{fu?SP-rZ4+84Z9Jgd&VnvD(@;+rnB_KPQ&iZd)H)Do0#sPKir~Qf;%)I0SZd{qt`EVz)reD!#l?6qE^u;p1TynV z&d(AY898T6XO6?ef>a8Us038g z$k9TZm2-t6%|^>&Ozll{+#cJ4(-d!duhtj-19*Ogb5C%)>cCb@NH_MeIc|dbGy-9O zVR+&L0u%2hR{F6cFSLyQknOYsltF}>M=U6DXE3cJRIrh3D=GIJtAH5baW0WTG_={vkc#h9qJ3@3K1)NP>0eGTzs#1?wuyCD+L@i?^z zBZ<%Ae2=_;aXq>^cmaHHphU8}p+L=np#r5KyCJ(HsbkumU`>zFO&uNPs3!$(9V9%S zTg4LRg#nK$u;<|-BA_KOlDn<|7XN52FmQw= zT_(0T8GEwYtFKf${Qw2=sP(!eNRk%>U^KshB1oyI2IQo0aY>1@tW6$#9vE>n+jjW^ zEtG^n5BM|yDdxaq<{EVYBVi;}T%I1|_O<7*HhdbkFw9HEv1FWxU=GC~iIkv9R-Otp zdI*TXG)K%|LUqMpf&~Oc1XbvQz{D#wc{k4i)@M;vFnL{d?v}L`MFdSZpojQq!HX4D%fgG5&fo(9 ziJ&7RD3K#T(1TNu99LIvj+h=Q@0DdoUW8uc2pydWPIHkx9>W?{*7;Y)2sT~+IlR=5t{ga46ls;AruAz8?HdFd;lnq@O#ZS zH>Y4!-o?{IYwVk##dL5kZnL`bEI;N&gRJTL*&H)`f`YVv5&4CVp@*)cRj+S z93#&U(9I9f&-c-tT*GYGM|W}^-C`e;;Q)Dl1Czx*^6_=#<7?=Suk!b!Ye+{|(VbjF zKDvs@(G|>2u3~!hG&bhXV*TV9Oy^HxZT>8}`P1l6p2WuSW0;&=$2ol(cj=>e#l`_% zedjUu&LyONi2341qYl6n1v6Zm--?evw<8Z-tG|bw);j2A>Vg6=+(z-W#hh_5Exp-Q zvv2HfV>+E3q?C6??m}X*H*g#CjdRg){z(8W9~8NY#^&&?xl^bnipA|n{zxrAk&3)m4P@H$@(e*O@WYcMnLqPP zDTfFDs=B?+fb&#JAsiFf9I>uiU)p9Yk*i{yh(z|4;tv zfBD1f+q-)~N~^nPgm%OPQ}$s#CCQDrR#?W(Wfl5bA?=UPX z`lClspUZgvwF`Lm#srTn_VDD<77pfNMXMF1QUvX~#PZVNDA$GTZ)QlMpl_YSHKCZH z7P1DVR3ueenPIF^SE&Os7dRBPrAvmx!XgSQ)aL+HWJg3k!s4lLBo!nzCZhuYFX$4` zN5;l3RZO~yi5gOukWxmq3fKaDUSKw<*qZ@o)=sdI2kf1nV6r#GU;`kEB!D^fdN#mM z>Lwn#@c@46(|5@)eAfQ;Nm|>Vihe&$3TXru3*9VrT_G@8q;*b;Zf(OZy!@`k`OB9c zU7u~-O^%CUD-s)sgU`8SE=U;w;86^-ebe+;0*(7~-WhU3z|a(n7gUmar8hT$sQD$4 zMhPNqgN(a}b#=T~yf=^V6?h8Vp<WAX;@`y_fyT?VUIN0IJMzGFTLeWbfU8VkKM;1l7HYcq&pp(o6k&n`Lq5+g^3{Fa6E` z>d$X%ZtjNkzXYfeWJZ9B`?r;(_zyLr8@e> zbJQDo0v3V=wox1yJQDC*MY1`dg5rs~Cm)!6P7)+y+r%v@j{s`SfN7CDjOPBPcxs@_ z9h<5Gih_JjdZ{bI6d%@`(TE@hXo5Li2V4`q#>FEZ&Z(CJn7GajrY%~ohd9t>$3zUt zxrvAitV;}!G49cH6y&b%uJ8h!3=@BJ|5fKj~Kw5TsJL7;k5t)W)t}WqQ>j;f=gg)eQYSvv|?`X zp;XMv;Ez3XMfZR2_daabum99M%YR#{!6BZK3TqaE^8&y_6t1;EG(W ztq%c8?OLngW8MrK+}s39k!~r@g=hteZOyVr4yuWH%wjR>)3^|#E6Z`=Zvkd5Vv7ap zF#p{u{>lcZA`+e)alV9G4+kMEpYZ^i;8p;|=i{5NT089k6|_h^PpYgBmX?J(MWYTsbbW`M9Vu0$T+wwv*YOt83A6`NPrbZ!0!--W2~?nSparC5y*H1# zEW7H%etVz$-uHb~)$^n-geEjV%S@J;oPdN0HU>t>8cyv(5aW`6>UTh2_kGyye{ zKKw|opHX0KqSO=!tSkWQmud=u(t}*Hz2~`bSg=bK}lUJaR`@$czG9yXQir7lS@JujAcWG(%u0PiDme>O+LA zgig@b{U4@|O%nU-Hjo$7ntTjAKqMqpg0hx*>Z7+ z!^1t)Hoi;1vQm=-Ge5ZjDCwo$IVxmZ&`OtU!|Hu@&dbq7v}CHSk9~q~n)tiEQ;(ZT zce0#0N!?=_0|AS;_6{sECUhSprjstSe`bJZf6aS;1$sWkD;hvpjfinvf=7!dQTa52 zuL8R#y~%&nRarT^oFJ<{n&p?4zP4YoErfVaz&(>gZbS&gSU^2uESHowA2J=Xecd2RQLv%(%>HKEWVWBmMC35Ii{e zQMU1ZntL!Z+|@8-R*I?0aJD#QvNh$cH#VR$8tmbmynR9Zw$qIcP!&`S&Ok=6qrb^j z4xQqj`e`kk)&iC}I=m?h^tDgAJ{gek1|TPB{%Z+BopZ{nxSB0Fs{+a4;w-vXg z3MLLlpQO3iqO&B~q2SfWdg_#P&P3C5s;0P2EKa&B3C@$-VD?2G$_Ye0{;qq4Pk__= zl=n__Vy3rIF00%tCQdOwseX5ae5QKLSzhOiuC+_wAuSq>a$mUGKn6GmYq)DWc>B%( zTDi*{=Z#s#1RY>g1WK+1(~t&{UR`y$L_1u(VzGY^<2at3#1)z>oxLrr{HP@K=@sww<^qgb4$X5$kD?v_@oQ~(k8KRGiEZ*_1TMl%nk)7Nn&$ol`YzVExplFIwQ_&& z3J&)UZf}e6u(c62T12a;>uW!`2AfRw3D7zTFdajan@#N2@l?2X@FtaJ*ZNOGqA-~p zP6G1^?6ds@o`l`2KuRMcz?k?XUpe?ftdT&qgSF;X?z$PsMB_M);FbNKN2{NSV#i3* zOwJxkxuJQP!>YD&qn4-`^7Vs(hTquPT3*VcozENH=l~TYTd_%!&w@eabdCff7kui$R#>|B zJwsrGW#Z^8vdxxyw5Gi6S0%M$W}E2uUdxyeEi-qLnV@fFnS=CMgjH-|&+b35$GdEC zztli6GIbzQaznd1B!aog!@P4U0bn*1#A1QH-3xcsI{u4R12Hy>sti5?`2Lntbj!H7 zRn~yqMiOB9+|$LWmq|+`kYZcr>0%E^PIbhD7nA6()yGWnXUw_WoA02Mk$zgSl zKOIfGdRq4b*cyotBWUIftF2*J9*!65M!|CyZsrd<@EIbfb*NbbtieSxlD%f*@ZFzETzfR5|$ysm4{1G%sThSRl3 zkv2KQ$@?d277o?1k_*cyu8Rn|HFz6w+x8VZd24NV?hMB)e9MfxT}|iESRq3@m@-yb zOPE(&18xBJ_bz?G-sLNgwy`}O42xh;*$&iawycn>T7y#jCwDf*=;>K?tlwV?wPz+k zPAk1zjf9?W4Y-q2?xm;HobHPR#I7)(IIiTJ%v6`q`nW@x6EumXb};4Omk0<&G(liB zG>#ZA@BY(z>C%@s8}4bz#YZoEhvb%!9BCJOGtG`PsRAyv0E_=1U=#=S{E2xtIzWwp z)ksvTJwzed`)<(yOctZ!aLo(2!Hb1egWKF_EmzSRT|KG^1h8DZI@_=flw*KxO-gG` zBVw$GvHzQ&Wkp+#h_NA-4O}+Fvg+>| z;MmZX4KX&LWjj?t+YE7Y(G);DAN>tRwlRr)>K6%DB7QRq!YUiRE*Jmt@Z3|V%jH*$#@e4#TO~3^ zQ0pU~TK`WcU-Lo!eI7uu$Is)nzXH&3=FG%cJ!^seHqea@P_j{ncDs_wMh9L*dy+X04|xUe#xf`3s`@!tDl@>P;HS(6&*Tuh!X0nBYxK-3EWMadfm7zdcm zVc3FWj=~v+7%)V^jDx;&hJo7{VjHuVW7bN3977B!F~<;F7}|iL4k&Ggq3M{{9h7z) zLmbCYcTmP14DA@^Z5uPrFvJXn+i>i_{TR%*0G|QF04sC&umdk!@M0J|FxV1wSAi#+F&UQ?PzbD2Oe9e<{!J<73?POQ#PaCiE3Z7g z`&c_XdJm!k;D~6{@8l-oWsnr#RR<8qqxG&-KH}u_oHUB7xs<>mWqb!Fg(|I!N)Yb{ zBq%Z4VHW}O3?`70Ua>!;lF zln}c9L~0*ZVnNNROG%O#9AHD(04mu!TDnoFZA+6LR0x47>H&%ODc%9F zkp2UpOvf9%mBCoMX9prYAI43ra5b95}i28x`&1UQDTI}0>&d4mBT6; z(NM79fUs={hzj;`3xDR38GiEN+jrkJ-t|+#@sAkY*yNGUG_98u@mrR~l2R8ar65Sm zh8esRY@N9E;rm|t$v?Pr`-#)T&h{K$Vy4OpC~#PD)TVhR$S(Ku^q5R`mr@d*)PAJ%i`~k{dFjv9fXI0|T_E z!Kjtv7{kHd;lF+2J@5Y2OCS8eH-=-;g7dr!%-v-D*0cyo9|Bf400T%OSW#T}wPE;- zb8maIIH-xlQ~q|-jSf&-#pl0p=WD+3)9?JeuSShIjz;M*m`rH2)EBs+jt#9vbMwMt z?ZWY{J|-pc#LyCS&pc2mDnK!Lgks~sRN|&yL%Iqb zL!T~|`XMc~wP&L4-*?M?CbXlwqL5Y#0i{U4?&Kf!a|evT2wMfb!r^K1f241bLGi z2{V6u4jkjEp_+(D-PH8nG_GBQ%U|S@r+^|Yb;z_cT}|lni=tc(7-g7|K`a*wAIIvo zR_vZV|J@&Yvlp`LRF#_rB|E@4oN8lL>3a^NWL(>FbUl9GmYoGM0YbJ2Yey)!~KIimE zX)V%k*7`m*vJQUYbozHtIpq7M`E$SQr~mQ)_e1BF!xs$0@N%Fa zJkXX6K1ie^C@$2+#kH1sqDp}~8D?fEJaPLSM|a(K?|(PH<(B_qer)H<=kx6mLn(u~ zMbb+KT2Xp1HgfS23RH)Z4+>NS8ghf^fRn6p9}ng`%ePq%820xM{_a!n`t=7coPF|3030%~)fhDQB4bI4 zjs2B)_CL8mVn8@Rt772kf9w2@y!p@R2hTqTb))N`LV0SsABHBtU?yd3=#?8}4mUT1 z8O81g5LQUUvNjtguIYNTHqO@}b4P*9unswfx|jS6%9!Xgn9oprfScm)yv*Pki&x4F zUgmJ0tMro3U^avK>X`c+Zd-7h9H^6#c$`tUph->FKjn+;wgKW+?JAo9>&(N?EmAh5QS!{pl~6kPG4HX*z~O2#uiph=z<$!UTh84NO7)K(WQ zVyKJdTP{6)w(MQl{XOQTMqrC>;a-f$5F}4MYu|uK>2lrVl<82)9R{qm%Wn2}005^> z`STrzhnoSQ95x!2JCf86Ifir^vb=P8UAvi7=t}aK#ZwJ@$!9os=YmSVAMF!~V`oYt&(uiV#VhzQqNz&WT zGEKz*cTRqro>m=qZMy59k`9h?+RWcxbv{3nJ4AQv)JM6Y)V{6}=#}(Zp1^E6mSbR! z!A(KM{eJmA6&|d7>?-1-R5R&>1+_1c%-dSdYRltJ&bM-0ePCcyCYR z*dh?HkW!CzJQ@!U_rLJy^4=Sd_V(VqI66Q?Y)7plmd&hJM-I|c$0S!VV0{@s%2OzxJQmlj|`d#L~?L1*GNJ0;g-SX-HW$~JLL(Q5-5(8fwHg>KFk!KkCM z*%QadT7T~1?(So|ANbIJ9m|D5E?G=(0VfPCGp+&|Hq%ob2Xt8}l2)R@2++#+pMBe3 zQ3iu425+5<`t7G19iRZ9$$Xk}vWay^vgC}=gm8yhzyNcV2CuDQs4V~ww#j|?qaDv{ zugkyFnq%o>WZ4ho-+cnEe48>A?DV@nNv}RW&AR;B?_u8ap7*wUA9|NuMMKR2^!k6= zarLqEhmx}$Qbo_zjjr>_>#w~AYKz)-v(5f}{j+L^y&b(7EkMs}rq$QS+A^*3Ab<8U z*!I>ozVI_&`HFk*@UtA3sUg=&(>v%3-%*AcVB!J*Il~$R&M^0gHapzk~uaUhoh2`{WziFUh`nL7=2jsgmJr~#A6xQWA zQe&E6<((eTwVZ%|w>>mK@+>?5;-qYW1x)}g!Nu;+jy4e?5PfYPm zDpslbYklykow;}sKg{~EJgQ#Pw7XINW6r77M<_5Yz-t%V+DF%TZa977TB^Z)1sYQi zI*>S!VID+Rdas{f6F2gF@7MZA$ND|jez)Gv^ilijci#KD&nZxR_UGBh*W1(i{3Nh; zpzY4D#=*?C9b3oosh@P$%Rcp$FFibLaeidO;6OSOnNOhSjvbJU3knPAgzSw>ael z?2e*L|CBCzT<@cn`$E-UeQ%z2ORcD!umYRO_XS-fxO&kGkorBiI(`+{Jp0q{cQxSV zXKH`75U1a){dS$MZNK09S^d>|SdBvmQLO))vQ;zCjJKUQhA({GOYgXKXZC1`#k8}- ziD7%H3IlmZtBzkpq&V1m(6b@Zvf667d|?-tFP#6whkKW~JUsk+QAY&1)oNX~MI|Zc z73s*u0fCg;fIH}hMse>d)3iQ9dX9;M0y1td0cve*I#<2or1iazx@WeB^_Bqx*&-Sl zj0j>ux{b^Bfh&7g{{Hz7yziS1o_XrlrEx(kGxm%HQ?R{!wP;j%Ge|UsWxvubro?~} z28)Uz_73Tz({g zx^{WxojWFUfa&aTuXVo;K+^(WEohxvE&M6iId`k+_gJ+Pxsy&x*=%fTd+oeL0bQ`( z#&dom+sNNfpWeoFZvDP%Bh-(tk6NFqdHpng*W7#cENj2H9ypnUC`xxcH~c5BzU}ir z>;4n(8QZb&^v8)t)UhcZFvTv<+)kPg5a*chA|hBvvXM?kT-<%8TzcmGUpm~|!{Tu9 z*Qs>R+l*FY*&J2xDN;O^wYclPq^DE&^>6eC5~P706uMZp&Ompd&fj!U(tGI5n7F3M z%~$$K5-O(_Q4JB+qVCkOKDxMa_>XrVf8tvX&OLR14ITtAXRFja>77vJ7Nn9somAxa zl&OWoARUw&vcTv6@x+h3`DbzFH07YV>f!TEH#$He5QpNB09eUvBQiw?Rphc~35eFf zUfC69J{x>6k4j*yA7(IDtK_N$tjV1m(KQnXIeGf7_pFcLQl0ZM{Czjf3E?@Z;Gz@;ndgu9P1~p>6zJDpVt0=?bo&8&IQ_kwmu@5I6(8kS{d+? zJMVn$gJ1Be_4b{5KsGJdOkq>#PCMp^+a*l5F|#``Z^*!{lqA9(Z|FF*C6moefntQ;eyv^8@pzT(?- zXU%a#rY&-lZ(P&3g`2`p8O?*=0~qn(TeT8TowgM7e$!L!qoo@jpbWf3Gev<@FBUC9 z$PDnV1SNtMKG4KYn*+v?9<|Naj15-<&9q2z&0MNNglYE90k0%Z# zMKuJ&jAYb$@%+b;WqXF+@=sn#ultGjU zUb?5|tS4y6_X!|~)*pYGd+EcrVstbzW=bW72pYL;m33^bE-Gu|uw2xCb@}PtAARQW z$G_un_nCW*p6^4S4I< zDMaDp1Wvda0LtN)^sP_*w0yaIfs_h_uxQXmlrdXGjcCn{Yvrkt_2^g0oe3sF2KGJ_3tXXf1i1jGgBqGNZqgo0v~U%YvXW{xNxfh!lE;WKBS_?>&toc|LCdslwA z9W8#A5p%N5YShElYNIfMY)TeqkPgRkVEW=;kG}&jWtu>WRb2Z!6%o_;2%iYT5KPTx zALvG)T8x#$qD48_Km4c9oPF{ao__E9{_NtJr|vqcyy5(wLZc0Srhk6mYJ0p zxVAp~&YunS_WHxGZm64H_1o#XJeOmW)AE|%uTgLO!%VZ)JvMgSby*j(^x-? z^=Dn@DyM(1Z7(g4W(XQ#1#LFm`?;TT@>f3XzHJl|h*4hE09q*5RL%}i%v3J;)Ug=A zlGe5WVhjd}F_xvZ8v7S7)!nC_`pk=G&wlIW%NO5qc(DIHwT@3kv||x18XBt-e!&=M zO{K2nmDguTij|)IFx4H5eAJ+<&oD#nVM<)REtml$8I1<{#F0eQuyHxs(c*AeE*8Ij zw72&?yH7r~eEPA+{`}shbAGrSk6Md)Yn6Id%9|}kM=8SvmPCJDUF=jaFx+8L;o}mH zLKOeQPyXcTL!3EHJaoD}-^uB&M>jG+X$Z-SlJ~VEhN)fvGL4PECeWhhPA)5if$k(7 z@EX9u(P8Fp$_djnr8;MkPP$VQ+Ff#&>425^1WaH0#lW->r)_ZUV19Yl>(!LL>QCfD zO;djTrJd5SY5BjSjCE6@7~X*359^9z^0}XK-<4aAZRscL8w$lOn7dN+Qk)dJKobezxmiS`wQ*Ec zmobV(E6e@EcIDiKyDy&K{novUmtTJ6;>GVdJUn_=t#uYbZ>`l}BTOMsj4Xta>>4v@ zxGT&jP|jg8rD%elHpe6_d)+x!k4;#S5~J1T%T~Fl?c%|*{*&DcPygQ^eDvL4df@}_ z`-Y<{&me-M`LInuO7+~wnMEwj*SNYIa_h!~d?lXQKb51<(Y*Z6=ic^Le)Xk)@O!uM z#?y)*|2SCfuB9Hjej`#&UH`P5+wT7Z$6o$BK+I5Epu3w8+8r806D5JcV1V0EnE&E@ z2;9=f-}!~Fx%c*0ecJsn%Hrde`iNCa(txJpo6hxs)~i3==}t;?c|cpJny)}l*F_Hy z&ev+$p0WGwr0F_;mn$mop>Q2p(=zFJw5)2Aedr&`2W(>3yI)ww4ayRfN#v&cMqb-6 z@{c_$t4(`awoI*lPSO#}^a_5%SeBH$yev zamf1KTK9K;{R2n@)%5NPQ5P!(vuB9a@;F$jRcKpvkN8cBl{aq|0hp*6o5)R zn70-f$I5YBF2_ZC$KIvOKfL?o6L&oG_y@mfdGX@O)(#Qy3fj;m2PEIV?jr_EZnV#_ z^ywnBnG%4RKs^-V@}Vs|_LcX(>=*pl*2Osg&rVmII&CU(ex?uf$AxZqfHKVP*}3JO zuRs2Z&p@*|+F0QvtQ176lch~FW=bR$1ZHM@ytImXxcKZ(d+Es&Z+PI9h=B-q!on<* zZ+me?YNw)-Dxd_ExW<6?p4dC+0+pRe#jlFy?MugNUl#cdQlB zMj#qm*6ypdPQE;~j?>?{gi&ilbP=O9jCB-O$c7d=7tv5#Lu12O8(P0U0d$Ot8gkpH zp=Xzcbh3-C(#&Sq2Yt=t4q41#2D(onA^L4MVe48ej*&#|*QbYkm=VRTnKR6lt1o@O zDnYoL4{l*pKJfxSXv@dISiJY#h1b02nToM3<-8?B2$C{hl34F@kRw0g6`Uw zNFZFfl8s^RUc8Z9E|zVvxVJ7AuZx4luf@3hXHnyPt?ibmyp2>kWJ)~+36X2=2FZO# za#a%7`f;gp^b4IQfH8=u&DvP8JY0NmZ~x$5UfI3y_6tuwad72BANWIi&zyT>ERGBo z%Lq5Q+`}RoNkvh>dR3QnFimI@Sa+SD^ijcFX?@oZM6i7I*&q4#-+0Mqe_nay0aQHn zCJ3CKU$61&QTFPFBm=-1W^cItlRx7>-2KL{#{xT8E>$`xLRkx$yB0yTTBiwNuxO5d zRocNbzTE%MzU1U9Zv9hV|5^Ctm)s3219rA&*gAF$tu0wwLs-^X^Q_}zG&%ZJM$-h8 zQ;gW-bZj?-F_??9^_orA+c)K+spD`-fIWDqr!Lc}I#L&e{_7n1V-a+!iN zeX$h6r4$kX+h|@2R~$mdp-nGXz-utHOi<)5;B_N>7{E|2JY))5H$Xw7N(86pZ!k9k zP=##9nsaO**v#ZXlYy<-gHRs=w4N$NA{^1G%7(Sfc~nFB%8@=zG+-ITQESV?gCpC! za{1rvK7IIO|L#3ce$Trua(3~Mz#tBaS;G`1{o`^xTX_!tuo)*2eVpXrv~mlLQlJD=|7u3~Y>=3Y@+Z!|?Jxhp0}q~>Kl0YoBQUuY zbaCV3N;f<}0mtsY^W{u!DQX0cx9$RzFU|yV21616uRv4QVqq!9; zvai$t-3&IsU`oa|Y2X{i7HFBX2_P&LrMVt7G4g!PB9crI(4ATRMEaO|)zpwlxMv(7 z1r*lfxFD~4@430VxH{<@#9_!!$E26(k@6shY)4=xF{!wrwn}hH<|g0_S+ER2C^IFk zDP@&%#77Gi$!iE8em7m{R@LLzd>|3q`v8YBGoG5g*d~m=(YS5b^>bmYpgSJZDE3o2Nm7g4nT%bD? zvK^>SaF`7X-3eJHOfW%fB`Hal60A7bXm2P=2MtvNXc zd|)ZgXa|hgZH&vT_33ep3sKuSj?44)=-?t3N84>&ysR#c?q3`o{jOM!ueBIGh}K|1 zGj*!RB*P3T*1Cy9^P;rFU4#Q<*(Q@qr)G9{W<`p3wT-9Fz5Uc*#e;8=WcAhu*Jj?w zpL@a$4p8Ws?13n9=ZaFA|b^D=WcGol45Sj+}D zYfQC-FUD{)nzLCAnz{Rs-hIW3HEm-9*$Uto9B2%e)4Qe`0GJn(v$Dx+SPFjt4ob$I zCXBgpbsk`rb|6VNqpN6a20TO*STnk8=I-K!8X3_zD-Hu2!-&CITMNSJ3>pz-Rsb3; zNGbgf~Yt7KFpwfP#vKer*e<6EPrKL>Ptvg<)fHGe#*kZ(-$d z5nPzf4gp{RAflXH#PahR{P#8Ti)%DS=1FQnl)+QJ3>w^!;yaU*L~?n^{N*r1Kt(yE z?$+w}H2gKB3PC$#D=ae`iqq^$naw`vWq8MItNd)(Y-c`{?XWUh8Js}#;nmH-SE0qd0FOme?Lqj?mBwqOx+- zV$Qc|9-z^U7Bnuy+F+y?)kGj8fEl%(3rj%+BN`ENx7NUBhVWo(uvWZ8s2-7uHgzG< z27)0qtsV_QWXX_$<}DCpL~vfTxuwuDG6;hkN+|*b(ws&bMmH~m5^A*u3pX6mOLO>w z;E|gbM0j&ld7;J@fO!zdjhGvxHeklYQF&TNkj99vdzV(tXrW@AmUxsLR62qpA29Xp zz1+LI(jUve%#bp-LWyLx`_ZSLUVa@OI^FQ#TU`#BpLlS>4GvJq{_&2Ne8!i( z2W%A#l0%k&mW_&Huce|gxkAvr$XZ9R6o)1>0Rt@+F!D)}^f7bcDhL!08M0tXB+GQ} zV`Gp1lJZ>&eWt$*NON;z>No%xL*?z}?nq5rH*XtAluSNM00CM+Wu^veCZ|b&LCc8v z(Un=EESt*a~VixYnz=n~Vx3rMa<6w9EG^ZbRdzEl`F7hO!LkO^O?dsD#{u>4S-b z4Hy|#${>{wXzl_pz*n+62&Y?1T^|WpUMIoKRP%t;oD&e*TPt&9PL%GsX2R@=74BrUuJYzOA*NIyZ~2!duTQ@!(s^0SGPk(h8h9ePTiC zrU0nh?)~&vo&54|`4@*r98zl1Au7DP^1Gu_mJO#0sL0|K zsW!~&8M&Cu05?r2FWO353!q5z0zkW-CMiHE(7WZNxJystmYZixdLdI60j6~)t%a8L z-3rp{w<}W+(-We%<&Y0v7x-4_Rx6-z9CDsJ7-s5xk<$nIi+EI9(bK8EVM zbR2M376}7}RQ}R3?QZI;3{sF37a%Y)Qa*?VWvq)<({idYa159$2a|Lqt-??+FUDX4 z!@~zRC|~xpWjI~IB||~2N)^?8i6SKG3o8_c(y5C(f@O`3j`djpkSg$L!mHJQCU9vm zLnGUDIcaYoqY?r5u`+*(5#L4gs?Z0M zxx1GHO8r59%`oH(k_6%%A}?8fNU4 zC?Ftrh1TQ;6{BhbvI1?C_+3eitzwKN#&H3*3XB!)s3H~(_2^J87*SEjF{|~ASVXl5 zGXo1-b2K=cS!*=XjJYmV#YXR0yiwjiZ@Ud=kWv2g9O;fvO+TsyO}b6Bl)qe^zcx4C$b{D-9Az ztq2fXif{rb=sfhCmI)Ezz6uXB%T&e%mF-8*@5WcJ!0GE+E}Z0VZw$H-0jf{CY1AV| zJG5thG~oZxlna|^9FZXchTQ#)Qlw-8{hmulEUuJ>v)LTQ2cQF2Ca+iS@G|*`7{v)t zn;#5NYTHsKj(|;tDvK#6xI`DF$B?t793>qFry=bynKD-kqqxlQF5w`*XX;*4a(+vW z%FP5!Q#8D`Te)*(#n;yFpVydx%{mW|ML!D##FjdjzL3xe4`doWo50ZI?v?GX%J^A= z)?l9LeU>W zYXWAS1IgznH3!TKTmsxiHkEgxeN%&WQrf8h!573{oR7J7U{b|nzW1g zl^rw(t8x@ueG)*m$*$4{N#UirCiYE=eA!k6RtEL2bAHkoK-o*vD%O?4IjJThSf=?icro?L?5$VJ^4G?3xKY~+P@4lN7L`2o` zjZEwo$;J3#o)-pra($ zsJu-A>0!#5q_G&x;4N#InropanvzIxx0*lQ2G*i;!Ede?goTjvA8ZPDAxCdKojVtSS+X84mio}^}h5z zL!c{gO!3?drKlF7CD%ttj7KWTxZXKtMi=R&3{PR5!QE1W}>Xoayigq4D?k0Iov!u@i>3YNl z3j|;WlL`_rRdNBk^+Vzrf>{(;oD+TzUqw>i(S*Ngo{VXoTdl5~OtdJg>Dk zi6q^>1)!9np69xlX6c|u-AilTQtzTMhzzmu9{Pb^y2@a#(3{os=6K}JY!G`Vz!T@u z`=?Yl`3%X0m{YG!oz8Jp{|(Zmq%wNq2t4(6(HdZxMbhM?+TUM4Ch&()Fp-IUZ>7nqIsV=-CNZ$*HM@oU9Y5rDy@uQuIu6&(4BR3s@5?g~?Sr zIoXjD6RAt+;MA#~lRsitrmI5&wft?ipLX(G47yqLf|scSHj{E#$JW~E52|&iqLFz{ zAfrw*q*Lqn=iRQ4dS$QE|u)ld&-}CCZbFIRc}XatclCi=GRpS{q=PaQa}68#Zgav zM}OTbV9rrpJ$O3W3WK$=_BpQm$RAUcS?n)A`L^%;#=T$r*{AW%-#5dn-W&Lbrvq<# zhbdFR7q2GQ4MsOIK=tB?+5KG12LCarb{Od!CVD5 zqfNVyC5O@hqQ^sZ8NQ@wrv<1`8svhETu_=o(#?=EWoz+jQAL8FBgH6_KFcgEWu`|H z2qtJSRTIg*bTrUnSeujmT<*Yq0Ry=}x<030YyCdihE!*1O)Q|xYUxr1_uEeW=xvbF zmG%G7`H2qh1l$zz60zb6OzUV`(51{$Ea|e4#60i$y~~Pw(1K0w2O8Ve_{bFk1_52S zfSQYY>l=Ydmo|y>`5OpBCJwTLx$NY*i zO9pc1F(!cZG3(&J&Rwa^)v<|Nn%=F(gp@l5#L1C{pC*Q{dg^W8^`ig{-+Xe03wH$0 z-)ngAt$Lsv7@Tke0hC7g*~eDrUjKT0)@#0${7_v2=eD6W`^-*vN{3D7b&L2dU5m0cmG z1wKFyq>1$~M zIx}%unhxT!4C=eAmz~32qpnA_oX0&j(x92qIc)&&)LjDq`xE}aAKiP~pE`#JPtD)( zpT66lyl_Y0frp8E&MS4wLtlLLeQrQX9{m$e&yC{e9autQ6@_o!6{33S9b7z%OnNG7XC@0?*8%j&3NzSXfCHAv&SpbVaY_yV z9F{)OQ}iBEe59p^u#2-YOO_%ZH^q&xt^`ibM6D+DBng$E{}M3sV(b1bnU$aeRdg#B z^gy~@7G!kg)v{c$?w&;_4eqKt$Xv9{1t>;TE(c6kkMOJlnxLn#P0*`y>j;Xo%oUY- zp1zQT1Ze<6bbSh?=uf0^*PbUSr*Px zT+)DrT=a^8@P7&bul};Xy@j`)9^ZNY6FvAKY0|dc&~()r_yp4R`muh@@WwaV8(#NE z3;^Ew{oh<4c<`;W%e-`=#^M$Z_9_E^kZrj`@^<-tK>jb$Qj4Ie6-dzwngrCYMbCu$ zHi#2zBs9ILD5}sxG8hUAK`CJy0ycEugAD8hqRETBC1(M1(98@%qeilR&25c>*n|K6vM}Qta*3a@7}^tU>wz^+nFk zjUkpOMMsuw%-JjG@`i|*Pty4vK$-utc5 z(^w(rieCBjW#ZEMjG%QLzEDh-x)ha@#fT>H&fQ4O!*6>0E#LVMa$e|Ws~h)YH$2@a zA|BRO*tLJ_-^U)qlQ?N7PoBh+uX-=e1O8ImUhdY5fqLviGpqj|1WUvY}b3}jd?OQS_lacv;YeV3zVb6&FCyN7~MT%mq}#Px^_&GwG0kTy%XwNQWlwuuSIwO=>HdN+q9FR>ySc zGn1&6sHuLaoqkV3T%_f1y2v3Ba`owYCUF6}&S5C&FV#zh(`hN9Ao_aV; zK;J>-YVJZ8C1+uMq4#$xpw4EJq_Z)qQgtvz{p=4D^vDFGjvpoUKjZ`wNG=LyYXu6@ zcQYdu`ZQL%dZ#*0ZJZsTx{AOOy!srG0%HwT^4|T(nq?DkTg-=lfAA|`_^+~ss~&4= z;wGRQ1)%aZeAr|9{^YCp+7sJ&Z2$F!;|Fmb;G*2RJp7DDm-7$Zb-v7(??Z4Gw09fq zm=T|p!c8kh0ZA$1X)B79rKrh1iGOCfR|9aLAPgBhrMyf8Bc|9mK>43RN5x#6y>nu=#C_;E(OQ_N>8*1~ef_Msjcl_& z=yXoHnyVKnLDCE>OPggJ;G`=`FYHxA;*uuaTq?UYcKrd9>oO_8qXsNPe6k^l?z+SD z+s?H~JQnCiA0$}nzorKQk&6C&P)~Ki>b&$#9jG-I610mp0V51$hFbA`2ao*BBRPmt zvQ1BO^U#e9P`Ku}0gvFZSLCt%^M;qs96LvbcV4kq-gY^T4)6Rxo8z6v_DeATWze1q z@E(|L5vbAHD91y1@d6)U1{dxaQ&AN{AxS=|_cf3-DvL>`ya<=U&>RkT>wCRL9SFn86tHcy2~MD|n+6Qz!RElqXuIGGRp+SZwXGp$9Gy^h{ z){?bKr{pkLFw8x@n%oTV{H4%5l!?f^|5kUiegwME0x$~W^w84^%BLm_>A+|Lj2_^u zHnYW)pr-p!IlB~!N2X;$nnLw8ML&aeAQ6a(`x9t@t;GVs0430YVzvePQo5`2WEZn@ zd^;%UrCkM1vYZK|Xi{R+q`$HvBTZLgi|f>H%pHx5dhKrg-B|+DN^hrsg;V`yV|7nJ zK6(IPg*MEM#-G~%)t~vLYydYSIAN{xpJ;lneoU`K|2zmh_#klZ9>cxoR|EgR!+iC> zyK{D>Er;>mc{#SI!%^icQT!O{c#FesNAVLdKRzH%1h8W)GcsmmY|~;pNGoO@U@^Lz zdqJRtsy-WF#T{Wju(etWfEt+svo+bFi%FRRGh-MTG&66}90n8{%-kFiuE>VaY|HYv z+C<%qa3Tx=7+gsZECdIcea%eT8-sU%)9Drlqf|Zv%u;jgkQa0WjI9|8s0C5-mtnyQ zV=m;3VIKIXsgJ$z8iSR3y@1fE4d7s>{WnL4*&b46^2xK$g5Uje6CrPwy83;{0a z%;(Z>AuAm~!hm%pc*ld4YMVxQ832kC5^e%8h5V?UAe%8rG-=f>2s8uGz^#Eax?w0q z&`XlaZlENDuGm88wyS+Y#-UQ?n7hGzjzhHn@`-=)?LV9=UN7N+hdEWDICa|ajug)yI%0hgw4y96CPz*A{P6a&RfT+e-9apy|upHgIMfaKd-iS z!R3wtKrFZHwt8d$a0$RIJ98bc3j+XF{e3NQTj#GOJK7@a!fe>OY9iXORR4x8MqQX+ zbNOo(;3wmgAD@*GtSxU zI2c>>Iy885`Uu1hVTIN%S|HrYn@jO8gZyioQ4zqjOb(NZa&#p>6`}%kE>`Smc!ZVt ztYCK3+Q`vZy_A`|ZxL?BWpZWRPs@-IOIT(&O;m%SVv7y)foNtU;e=tHBaz%(8a_P4 zUrixXg7%ruVFbuAfiiohBeU60nmYW?ru zGIjOohCVTrCfQS`m?o%RVPdziS(qZ#j z?{7*K-p0mHdg;sgxnJ@{#do$?#|G|?^=P?_oh`FA8=6%h#svpMofY3csN~pM#k_bK z(0pvHvB1$-4u9=e-aY@&dw%Vyo$cYT1nf0%`|Ap|Dm14>G`3V|G9w?9yDQVOl!6(% zh=<>C_J_XX;cGVjn~pxrLjQ!($I_qY-Wg7v`Y^ldbI`Su|D&SyMf73^EN@)bUF=}H_P>|^eQJM&rGdD{s-aq-YEUD@Y}<8#0Y#%Ks<+_JNUxf_-( zxHwwimww?_?TPpN+V-$L`!hh?W@i6AThP2T7z-E?q|1LkooCIm)*(@bLCX6w$7tpC zPyOIu>{h&+c0Nw}l^cY(_IWOP?rYARNhN0ge3W$Nv=YakIg{>`AI%2|OdD`|{)y?# z@L|cG^-dmmfOy@>seF>V-C7wHgfYSm<|U1o0ePFc%W9~GNI4FWVWe5%FwDYT&Qgbq zMLfNGflqwsoIQDV505={!JfIiD6^eg%h6Em(Mw1E-g6i11G~HS!DlYo;~zTj?|%0O z?ff&Bv8*FnZOv*d=>yo>E7;-zIn%owJgBlRGHvR=wC7F{3IRF1HUn6u#LG7|-Dm*S z={GyL>i~D1&$XcPkvzf@XaJ!s0#l*p&8XkZ`r+S|A43p|^STv|Avr?AfquR)C zV7gHOs)I`Ze!X+o`)p`K*QYfj$?WK8hGyg7W?sxGv%j?#77>|@u66$ckj?0m3x^yc z9f)Q&4BK{C7q%Ej8+-r^R*Rtyz6>^iFrAk*lDbVDZQIYw8Z*sa(0qT0R0ig~37AU!? zGpD)0*eoIn+=en}VJD|V2%jR5nN~T~ozv2dwWAYeRP%<)WhTIJl z7~$p^P;V3`?B5dI=m7QMxTp8MYHMKt(qhA6acgKb+guv)Zc#%AVW1z|(D4OYyH!w1WVB^%v* zM~<@v9%fb?HaHADkGs6%gMgJGLtvDm+Gz0RTh~1OO-VO8Ky|vFru#Rmr`phqmC_wy zo5xx)Z*UXnO#D^PVV_Fq=#^CEdC# z7;Is!HbZSTquJnY3gI;PFc^cbfEO$SI3^KKGrbq%$Vj1#!Bo5ynmpzc-2ek;ZCG6U z_%|cnEC6*q@$TBcH?*PWAmbaJJXy-t!J@UgP_(r+I%Wof0>CmkAIPlCqFRW}12GWh zfdXC2ATV>5VwjZ_wW=Fo7-oab-K{m6c|j=!(P&ha8!JBaZkdtho>}A#VOF+c;@VaP zF;p!^5@*I2iUMFLjM4#&^I@Zd`UKDhls5ERqQeVM*dymuoe+}d=%q-X(*yF#Ds$%C zh`|6r^$5KMN+n7SSZ(HJ@IqLGZTnzB!peX;R-4b}*eQnP(E<^`e72Qd=(50-(#xD> z=rLi6xw8hexdBkl3I}|Ek|8!lu`5NXc5X!qf>0eo(#d+$t?ld53*sR*w4slMUcGZ? zaUTZdAI-c>YYvYQN^HnX)zVWsn>jqFIK2qa3(DXaLjC~_s)MM4IFe@y#m;n|_?E@$qXZmTW`VG}5A`46Ol*c5@+Y4ed z-vHExUR1Pm$4TxFyT!qhscYO!i=Jkv88(uo=B*`QNv49b>6oFBXy&Lifz*5pBV9#V zJ#gFHv2)uo3_io+sGd@4_A~Tt32;!vN@*m%)4tjQdL*9aRL4F#_$Vkz+Lghcw(xD9N6HQRS>6 zi;OB)s(?$%b-6wSiS`jg6mLG-0Mv$FR5aeVYmdDjm1bij5Dc!0jmlZm;VAh*z*32B zUSu_EB&!2v&N3t1fog&6J8s2GU->e;^yMdU+nw8}%Y7UzE`z1w%6J8B7P#x4+j#fA zx1ij5jF@e6f7wuJ${HLathW6|+(9U3y&OSX0;O`*GyS!QUV=+fo@(p?=0*?Oh? z(9K8>+zWTJEDN10AL2wNuW)G5BNTPpJlHH4)>J~n3ywT+c-Y{WV?N(ui-wB_N4T(m zgx!NBb}wAQ{_Zo_yL=g!&Yr{G#a-;~U%~mkU0%5G6rOqFgLwS0U*~VU`<;C1{qMD- zqoZgX-`lL-646v;Cwk(Bta8&u(AFk!lH;bhL5Hk(3mGDBdU|2;gB#k=bI_~5>~C+4 z_w8ES+xc07UjT+u<|>hjl-FJ7lSN>emNE07(GheM16l*_x#yGcvRA(vERM_jd)U2v z3GMIzJ}xjUmmosvPt3DM#fS)FT^xW%Ba0cr0`7x1_Zq+tM5|v)+AES8(NU zANyCX;NZeVES`A=%Zrz=xO4$=xC9Y)#IT0Js;c&$6_Z7C0J$w!uE zdZa!_ImlJa)4ae3)v#;ngJb~cjwr5#aU@BTe-fKT1lY)Eq&(Dv(r+s)*+8YqWDsVB z5&MnxUlRBXodWMHV|$VM_(U2>J^{L5>YTZRo{H=7aKQcJ3pU z&q#AUA;9WExq+U4X~HxG=9*LSbPh(9V^lVnfil44v|#A|O_TEy+?3iEdv5+0Nc?_r zbjnx|EfgBl739GT(CK}56%NhzGu;Y{ZEb>uH?*Ofje6Zf80nN3cvmz}oRjcJ`l${C z(Q`troX;7csvD;v&@3yqPBrzE-jW6?uL@vcfoibGBEtb#BO~qr?Q@Iyma4OoX1zFT z0^=l%_A;xoGEOpG_M3({uyF%W8`{v#M(yZwSK=JS6gon<7dbxmG`}gvnHfq^XbL(8 ziNOIAoQSX>yMwooD+q*Bp?yv$SVlKYViw#3QO&4=?*L#a2TBp1=(r;3p2WdLhQ0_C z^*4y{_QDYJHUPDu7Zt_rCj=_NCQ;5QWw>%c34k)ACJfO6a*I+bU`6otn7xUyNe)W) z5SS5gn6y|lqgiWVi~@$MEdfpPWo`%yMue0}$PgAY{oGXC)vYRIp&m8@}G|`XBl|DR4`!s?*G7W~B{4ZRkZsesPbG>SpQY zlh?SXivb&e%>N{ykxpuI*z8glCx!;fLvChSi?{;}Gng2_Bqmx1l9;0Abho^hd@lu7 zx>nAad2WqWqkNv!0sSq!1<3F#2OEIe(1va}JQfZEWDl8X53Sg=+%jcTq;4oNre_;K z=fo1MTFXM964A`eO-X-~*wIM5lzSI5VNA>7#K{@J)(|xi%slEH5Ov=+zw3n&EW81z z4ZWyncJ@vJw1!y9QYTp{LW*%w;s!ARk5D1?L9rG@jFlTw=f+adQa-2ToU%FLQ2sT! zI2fR!!GQE)`HQsPb*o^g_8tHMUl;k*0V&K0z})I%XYbr_P#fCN%}NBCJd-l%TPnuQ z9bQu3Gl4+<$W1wyN~m+OE5D5bzn zWuMGc?H4_kuUqP}$fq1BB{B*D4M7K30Ln?AgHM;_GRulstu>I6m?_^*p`di~>Yso% z)38+B|HQ@*YC{{k*=T;tE+GMH0SI6)`4!38G1BKq+>o+Y!VE@7YbxYRbI^)%w)g}z zG{xr0sOIS!?hfhDdz4>Bs9@!H_ud zh_|5)-Lw>T(Dg$q<+KV0Hdi4}aVsfRLE;}_Xn?q(ko7NGz|0u(S2D`Y00?j}XOI+* zm>Cn5vJFN+#a+aDF>lz?DyLHH$xWrmn0%I$@g~R24tx$EXfFuv2^)ag(2I>8x#J`O zpaF-bst58|%Fq_EIxClfuE-XbC5O%~-U%S&W;yvC5DYdFWMf!sQVLD32ry+?A;bw; zN!8MtSm_e`dQ};?^it9<86q<0lj%6qgWMQ!5FRmJ{nR%vZ2)RRFD`o56U3?0RtynP zq#Pj28QGbe3?5EtI^PwHaA%tJ;$ zrBXpw2xd?7g6@)_W9Wg*J+K){grI*_raij>I1bjsdTwUg0Mv$FRP?~TaGW_!FMcRT zP1TkIhK@sE>WHvr;gkMb`YNe}q%#UK-3hGG5zJ~-1{>V{&|%;7)MvNXc*KLCeU&-> zro7YPaBq|nsl#L5ulM$91-KQ!oG>;NDGgy6ed*sg{_=M{QRcVovhJMo`@Z_ry-)nan}34FZ%pO%H?v|NQCi6qxk2X`>gix%in?d_HwVyc6Ry5ImCTi#};RIv3uW_ z{*B{z&H0bu_~XJJ+Xk%3=cASbiCAgB48`;@$k}uT98M2$lhX#EHuNH) z6z^Vl((uUZ%`Kj=^ceS&%YXtO6ob!ZD6=8cq@)C9>3%SYd&zBC(Iw`8_56>0=hr;F zJDz{(!?$idb}!`X&8=b3moL1GScV&++0}-NL2)m zOoIXfaJUcRV7wTB$6@%rPyhIL{DYT&!|5#mc#6x_HX;wc;l(zv4hu-kAB|nh&wJg&LMF4_V&<2XMl$u0v>vZz(WAu zng5<~yyG2y_8))i!Qq$pWBa>?lK@0mT~3 zRdlqtL_T*vw*I^~Gi?BBLoXhB5<(+v+i%;_F)Lg1l?q&T|zvpV+BL&Sq$=Xew1 z(a+l*aq5&m^~2xscEtGkj`$5nLvi^&H7G8J02+ZBfw2-T3Je1n16YZM9r$qK{Kipp zLmRp|NZeBw@9up)0pP-0zxi?u_~+G$W+;t;#W-S&ii$vGphm+;qLLU%G$WexUpJ4h zIBT!?ivSG|zNO%y(+#Ii8yao-QE z-Q%XF7la~hLmPSyGMqYPS4a38U-}3Cr=7BeZ$Z>!t;MG{!z}27OFcgG`PP2a<$Juv zgQF52_f|sZ-kQr+Yl-(pp_`>Xg7~ogREk!CSvhLB9{a zrM&8v-}tmS>MdLxY{k+3XISii8r=^T%eJ&}xnnlx7TbGkEPw1hKmEfWysE7YZD>O` zG)a^Ekx=6eZ40e(u`OwIK1~+WlCmxBcPTy52MN?+#$N&iR|3UQiFSp$$C;+4b~5YsaruEcffy z&-H7c5BgZv<9gpdT>o!+Qr?CQZRmwheL-K#Wn2pieZgOQeErz9=dMQ|%{9+O*Mi|X zD6JoR!2wgJ4M1(^#X{Fx+?}oko{t1B*ZckRNgWuk2axOi{CM~4<3b3U#vE%$M~YkoUu gz1L!A^-pY07*qoM6N<$f?I56asU7T literal 0 HcmV?d00001 diff --git a/src/assets/icons/socials/scholar.svg b/src/assets/icons/socials/scholar.svg new file mode 100644 index 000000000..775383090 --- /dev/null +++ b/src/assets/icons/socials/scholar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/socials/ssrn.svg b/src/assets/icons/socials/ssrn.svg new file mode 100644 index 000000000..c3ad32a35 --- /dev/null +++ b/src/assets/icons/socials/ssrn.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + diff --git a/src/assets/icons/socials/twitter.svg b/src/assets/icons/socials/twitter.svg new file mode 100644 index 000000000..50f120774 --- /dev/null +++ b/src/assets/icons/socials/twitter.svg @@ -0,0 +1,17 @@ + + + + + Twitter-color + Created with Sketch. + + + + + + + + + + + \ No newline at end of file From 4b6506284b1ec2efc3411d317efb118c0f6ebad8 Mon Sep 17 00:00:00 2001 From: Kyrylo Petrov Date: Sun, 18 May 2025 19:39:36 +0300 Subject: [PATCH 2/2] fix(my-profile): separate stores --- src/app/app.routes.ts | 14 +- src/app/core/constants/nav-items.constant.ts | 6 + .../core/constants/ngxs-states.constant.ts | 2 - .../my-profile-search.component.html | 94 +++++++ .../my-profile-search.component.scss | 71 ++++++ .../my-profile-search.component.spec.ts | 22 ++ .../my-profile-search.component.ts | 113 +++++++++ .../my-profile-filter-chips.component.html | 119 +++++++++ .../my-profile-filter-chips.component.scss} | 0 .../my-profile-filter-chips.component.spec.ts | 22 ++ .../my-profile-filter-chips.component.ts | 68 +++++ .../my-profile-resource-card.component.html} | 0 .../my-profile-resource-card.component.scss | 1 + ...my-profile-resource-card.component.spec.ts | 22 ++ .../my-profile-resource-card.component.ts | 60 +++++ .../my-profile-resource-card.service.ts | 27 ++ .../components/filters/index.ts | 8 + ...rofile-date-created-filter.component.html} | 0 ...rofile-date-created-filter.component.scss} | 0 ...file-date-created-filter.component.spec.ts | 22 ++ ...y-profile-date-created-filter.component.ts | 52 ++++ .../my-profile-funder-filter.component.html} | 0 .../my-profile-funder-filter.component.scss} | 0 ...my-profile-funder-filter.component.spec.ts | 22 ++ .../my-profile-funder-filter.component.ts | 74 ++++++ ...profile-institution-filter.component.html} | 0 ...profile-institution-filter.component.scss} | 0 ...ofile-institution-filter.component.spec.ts | 22 ++ ...my-profile-institution-filter.component.ts | 74 ++++++ .../my-profile-license-filter.component.html} | 0 .../my-profile-license-filter.component.scss} | 0 ...y-profile-license-filter.component.spec.ts | 22 ++ .../my-profile-license-filter.component.ts | 72 ++++++ ...-part-of-collection-filter.component.html} | 0 ...-part-of-collection-filter.component.scss} | 0 ...art-of-collection-filter.component.spec.ts | 22 ++ ...ile-part-of-collection-filter.component.ts | 63 +++++ ...my-profile-provider-filter.component.html} | 0 ...my-profile-provider-filter.component.scss} | 0 ...-profile-provider-filter.component.spec.ts | 22 ++ .../my-profile-provider-filter.component.ts | 72 ++++++ ...ofile-resource-type-filter.component.html} | 0 ...ofile-resource-type-filter.component.scss} | 0 ...ile-resource-type-filter.component.spec.ts | 22 ++ ...-profile-resource-type-filter.component.ts | 74 ++++++ .../my-profile-subject-filter.component.html} | 0 .../my-profile-subject-filter.component.scss} | 0 ...y-profile-subject-filter.component.spec.ts | 22 ++ .../my-profile-subject-filter.component.ts | 72 ++++++ ...rofile-resource-filters-options.actions.ts | 41 +++ ...-profile-resource-filters-options.model.ts | 21 ++ ...file-resource-filters-options.selectors.ts | 68 +++++ ...-profile-resource-filters-options.state.ts | 138 ++++++++++ ...my-profile-resource-filters.component.html | 77 ++++++ ...y-profile-resource-filters.component.scss} | 0 ...profile-resource-filters.component.spec.ts | 22 ++ .../my-profile-resource-filters.component.ts | 106 ++++++++ .../my-profile-resource-filters.service.ts | 82 ++++++ .../my-profile-resource-filters.actions.ts | 68 +++++ .../my-profile-resource-filters.model.ts | 17 ++ .../my-profile-resource-filters.selectors.ts | 61 +++++ .../my-profile-resource-filters.state.ts | 142 +++++++++++ .../my-profile-resources.component.html | 149 +++++++++++ .../my-profile-resources.component.scss} | 0 .../my-profile-resources.component.spec.ts | 22 ++ .../my-profile-resources.component.ts | 161 ++++++++++++ .../my-profile/my-profile.component.html | 2 +- .../my-profile/my-profile.component.ts | 32 +-- src/app/features/my-profile/store/index.ts | 4 + .../my-profile/store/my-profile.actions.ts | 39 +++ .../my-profile/store/my-profile.model.ts | 14 ++ .../my-profile/store/my-profile.selectors.ts | 53 ++++ .../my-profile/store/my-profile.state.ts | 90 +++++++ src/app/features/my-profile/utils/data.ts | 13 + .../filter-chips/filter-chips.component.html | 0 .../filter-chips/filter-chips.component.scss | 13 + .../filter-chips.component.spec.ts | 0 .../filter-chips/filter-chips.component.ts | 7 +- .../resource-card.component.html | 181 +++++++++++++ .../resource-card.component.scss | 5 + .../resource-card.component.spec.ts | 2 +- .../resource-card/resource-card.component.ts | 8 +- .../services}/resource-card.service.ts | 8 +- .../creators/creators-filter.component.html | 0 .../creators/creators-filter.component.scss} | 0 .../creators/creators-filter.component.ts | 9 +- .../date-created-filter.component.html | 13 + .../date-created-filter.component.scss | 0 .../date-created-filter.component.spec.ts | 0 .../date-created-filter.component.ts | 9 +- .../funder/funder-filter.component.html | 17 ++ .../funder/funder-filter.component.scss | 0 .../funder/funder-filter.component.spec.ts | 0 .../filters/funder/funder-filter.component.ts | 9 +- .../institution-filter.component.html | 17 ++ .../institution-filter.component.scss | 0 .../institution-filter.component.spec.ts | 0 .../institution-filter.component.ts | 9 +- .../license-filter.component.html | 17 ++ .../license-filter.component.scss | 0 .../license-filter.component.spec.ts | 0 .../license-filter.component.ts | 9 +- .../part-of-collection-filter.component.html | 16 ++ .../part-of-collection-filter.component.scss | 0 ...art-of-collection-filter.component.spec.ts | 0 .../part-of-collection-filter.component.ts | 9 +- .../provider-filter.component.html | 17 ++ .../provider-filter.component.scss | 0 .../provider-filter.component.spec.ts | 0 .../provider-filter.component.ts | 9 +- .../resource-type-filter.component.html | 17 ++ .../resource-type-filter.component.scss | 0 .../resource-type-filter.component.spec.ts | 0 .../resource-type-filter.component.ts | 9 +- .../store/resource-filters-options.actions.ts | 0 .../store/resource-filters-options.model.ts | 21 ++ .../resource-filters-options.selectors.ts | 22 +- .../store/resource-filters-options.state.ts | 6 +- .../subject/subject-filter.component.html | 17 ++ .../subject/subject-filter.component.scss | 0 .../subject/subject-filter.component.spec.ts | 0 .../subject/subject-filter.component.ts | 9 +- .../resource-filters.component.html | 0 .../resource-filters.component.scss | 15 ++ .../resource-filters.component.spec.ts | 2 +- .../resource-filters.component.ts | 20 +- .../services/resource-filters.service.ts | 83 ++++++ .../resource-filters/store/index.ts | 0 .../store/resource-filters.actions.ts | 0 .../store/resource-filters.model.ts | 0 .../store/resource-filters.selectors.ts | 4 +- .../store/resource-filters.state.ts | 30 +-- .../resources-wrapper.component.html | 0 .../resources-wrapper.component.scss | 0 .../resources-wrapper.component.spec.ts | 0 .../resources-wrapper.component.ts | 23 +- .../resources/resources.component.html | 4 +- .../resources/resources.component.scss | 67 +++++ .../resources/resources.component.spec.ts | 0 .../resources/resources.component.ts | 13 +- .../features/search/mappers/search.mapper.ts | 4 +- .../search/models/resources-data.entity.ts | 2 +- .../features/search/search.component.spec.ts | 4 +- src/app/features/search/search.component.ts | 12 +- .../features/search/store/search.actions.ts | 2 +- src/app/features/search/store/search.model.ts | 4 +- .../features/search/store/search.selectors.ts | 4 +- src/app/features/search/store/search.state.ts | 9 +- src/app/features/search/utils/data.ts | 2 +- .../store/resource-filters-options.model.ts | 21 -- .../mappers/creators/creators.mappers.ts | 9 - .../funder-index-value-search.entity.ts | 4 - .../models/index-value-search.entity.ts | 4 - .../institution-index-value-search.entity.ts | 4 - .../license-index-value-search.entity.ts | 4 - ...of-collection-index-value-search.entity.ts | 4 - .../provider-index-value-search.entity.ts | 4 - ...resource-type-index-value-search.entity.ts | 4 - .../resource-filters.service.ts | 232 ----------------- .../filters}/creator/creator-item.entity.ts | 0 .../filters}/creator/creator.entity.ts | 0 .../dateCreated/date-created.entity.ts | 0 .../dateCreated/date-index-card.entity.ts | 0 .../filters}/filter-type.enum.ts | 0 .../filters}/funder/funder-filter.entity.ts | 0 .../funder/funder-index-card-filter.entity.ts | 0 .../funder-index-value-search.entity.ts | 4 + .../filters}/index-card-filter.entity.ts | 0 .../filters/index-value-search.entity.ts | 4 + .../institution/institution-filter.entity.ts | 0 .../institution-index-card-filter.entity.ts | 0 .../institution-index-value-search.entity.ts | 4 + .../filters}/license/license-filter.entity.ts | 0 .../license-index-card-filter.entity.ts | 0 .../license-index-value-search.entity.ts | 4 + .../part-of-collection-filter.entity.ts | 0 ...-of-collection-index-card-filter.entity.ts | 0 ...of-collection-index-value-search.entity.ts | 4 + .../provider/provider-filter.entity.ts | 0 .../provider-index-card-filter.entity.ts | 0 .../provider-index-value-search.entity.ts | 4 + .../resource-type-index-card-filter.entity.ts | 0 ...resource-type-index-value-search.entity.ts | 4 + .../resource-type/resource-type.entity.ts | 0 .../filters}/search-result-count.entity.ts | 0 .../subject/subject-filter-response.entity.ts | 0 .../filters}/subject/subject-filter.entity.ts | 0 .../resource-card}/resource-tab.enum.ts | 0 .../resource-card}/resource-type.enum.ts | 0 .../resource-card}/resource.entity.ts | 2 +- .../user-counts-response.entity.ts | 0 .../user-related-data-counts.entity.ts | 0 .../filters/creators/creators.mappers.ts | 9 + .../dateCreated/date-created.mapper.ts | 6 +- .../mappers/filters}/funder/funder.mapper.ts | 6 +- .../institution/institution.mapper.ts | 6 +- .../filters}/license/license.mapper.ts | 6 +- .../part-of-collection.mapper.ts | 6 +- .../filters}/provider/provider.mapper.ts | 6 +- .../resource-type/resource-type.mapper.ts | 6 +- .../filters}/subject/subject.mapper.ts | 6 +- .../resource-card}/user-counts.mapper.ts | 4 +- .../services/filters-options.service.ts | 238 ++++++++++++++++++ .../services}/search.service.ts | 0 .../styles/filter-chips/filter-chips.scss | 0 .../resource-card/resource-card.scss} | 2 +- .../utils/add-filters-params.helper.ts | 20 +- .../filter-labels.model.ts} | 2 +- .../utils}/get-resource-types.helper.ts | 2 +- .../resource-filters-defaults.model.ts} | 22 +- src/assets/i18n/en.json | 4 +- 211 files changed, 3777 insertions(+), 483 deletions(-) create mode 100644 src/app/features/my-profile/components/my-profile-search/my-profile-search.component.html create mode 100644 src/app/features/my-profile/components/my-profile-search/my-profile-search.component.scss create mode 100644 src/app/features/my-profile/components/my-profile-search/my-profile-search.component.spec.ts create mode 100644 src/app/features/my-profile/components/my-profile-search/my-profile-search.component.ts create mode 100644 src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.html rename src/app/{shared/components/resources/filter-chips/filter-chips.component.scss => features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.ts rename src/app/{shared/components/resources/resource-card/resource-card.component.html => features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.html} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.scss create mode 100644 src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-card/services/my-profile-resource-card.service.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/index.ts rename src/app/{shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/creators/creators-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/funder/funder-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/funder/funder-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/license-filter/license-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/license-filter/license-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.ts rename src/app/{shared/components/resources/resource-filters/filters/subject/subject-filter.component.html => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.html} (100%) rename src/app/{shared/components/resources/resource-filters/filters/subject/subject-filter.component.scss => features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.state.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.html rename src/app/{shared/components/resources/resource-filters/resource-filters.component.scss => features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/services/my-profile-resource-filters.service.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.model.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors.ts create mode 100644 src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state.ts create mode 100644 src/app/features/my-profile/components/resources/my-profile-resources.component.html rename src/app/{shared/components/resources/resources.component.scss => features/my-profile/components/resources/my-profile-resources.component.scss} (100%) create mode 100644 src/app/features/my-profile/components/resources/my-profile-resources.component.spec.ts create mode 100644 src/app/features/my-profile/components/resources/my-profile-resources.component.ts create mode 100644 src/app/features/my-profile/store/index.ts create mode 100644 src/app/features/my-profile/store/my-profile.actions.ts create mode 100644 src/app/features/my-profile/store/my-profile.model.ts create mode 100644 src/app/features/my-profile/store/my-profile.selectors.ts create mode 100644 src/app/features/my-profile/store/my-profile.state.ts create mode 100644 src/app/features/my-profile/utils/data.ts rename src/app/{shared/components/resources => features/search/components/resources/components}/filter-chips/filter-chips.component.html (100%) create mode 100644 src/app/features/search/components/resources/components/filter-chips/filter-chips.component.scss rename src/app/{shared/components/resources => features/search/components/resources/components}/filter-chips/filter-chips.component.spec.ts (100%) rename src/app/{shared/components/resources => features/search/components/resources/components}/filter-chips/filter-chips.component.ts (86%) create mode 100644 src/app/features/search/components/resources/components/resource-card/resource-card.component.html create mode 100644 src/app/features/search/components/resources/components/resource-card/resource-card.component.scss rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-card/resource-card.component.spec.ts (92%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-card/resource-card.component.ts (83%) rename src/app/{shared/components/resources/resource-card => features/search/components/resources/components/resource-card/services}/resource-card.service.ts (61%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/creators/creators-filter.component.html (100%) rename src/app/{shared/components/resources/resources-wrapper/resources-wrapper.component.scss => features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.scss} (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/creators/creators-filter.component.ts (85%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/date-created/date-created-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/date-created/date-created-filter.component.ts (77%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/funder/funder-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/funder/funder-filter.component.ts (82%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.html rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/institution-filter/institution-filter.component.scss (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/institution-filter/institution-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/institution-filter/institution-filter.component.ts (83%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/license-filter/license-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/license-filter/license-filter.component.ts (82%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/part-of-collection-filter/part-of-collection-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/part-of-collection-filter/part-of-collection-filter.component.ts (81%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/provider-filter/provider-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/provider-filter/provider-filter.component.ts (82%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/resource-type-filter/resource-type-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/resource-type-filter/resource-type-filter.component.ts (83%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/store/resource-filters-options.actions.ts (100%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model.ts rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/store/resource-filters-options.selectors.ts (55%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/store/resource-filters-options.state.ts (89%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.html create mode 100644 src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.scss rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/subject/subject-filter.component.spec.ts (100%) rename src/app/{shared/components/resources/resource-filters => features/search/components/resources/components/resource-filters/components}/filters/subject/subject-filter.component.ts (82%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/resource-filters.component.html (100%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/resource-filters.component.scss rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/resource-filters.component.spec.ts (91%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/resource-filters.component.ts (64%) create mode 100644 src/app/features/search/components/resources/components/resource-filters/services/resource-filters.service.ts rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/store/index.ts (100%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/store/resource-filters.actions.ts (100%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/store/resource-filters.model.ts (100%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/store/resource-filters.selectors.ts (86%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resource-filters/store/resource-filters.state.ts (73%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resources-wrapper/resources-wrapper.component.html (100%) create mode 100644 src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.scss rename src/app/{shared/components/resources => features/search/components/resources/components}/resources-wrapper/resources-wrapper.component.spec.ts (100%) rename src/app/{shared/components/resources => features/search/components/resources/components}/resources-wrapper/resources-wrapper.component.ts (91%) rename src/app/{shared => features/search}/components/resources/resources.component.html (97%) create mode 100644 src/app/features/search/components/resources/resources.component.scss rename src/app/{shared => features/search}/components/resources/resources.component.spec.ts (100%) rename src/app/{shared => features/search}/components/resources/resources.component.ts (87%) delete mode 100644 src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.model.ts delete mode 100644 src/app/shared/components/resources/resource-filters/mappers/creators/creators.mappers.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/license/license-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity.ts delete mode 100644 src/app/shared/components/resources/resource-filters/resource-filters.service.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/creator/creator-item.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/creator/creator.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/dateCreated/date-created.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/dateCreated/date-index-card.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/filter-type.enum.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/funder/funder-filter.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/funder/funder-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/funder/funder-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/institution/institution-filter.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/institution/institution-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/institution/institution-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/license/license-filter.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/license/license-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/license/license-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/part-of-collection/part-of-collection-filter.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/part-of-collection/part-of-collection-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/provider/provider-filter.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/provider/provider-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/provider/provider-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/resource-type/resource-type-index-card-filter.entity.ts (100%) create mode 100644 src/app/shared/entities/filters/resource-type/resource-type-index-value-search.entity.ts rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/resource-type/resource-type.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/search-result-count.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/subject/subject-filter-response.entity.ts (100%) rename src/app/shared/{components/resources/resource-filters/models => entities/filters}/subject/subject-filter.entity.ts (100%) rename src/app/{features/search/models => shared/entities/resource-card}/resource-tab.enum.ts (100%) rename src/app/{features/search/models => shared/entities/resource-card}/resource-type.enum.ts (100%) rename src/app/{features/search/models => shared/entities/resource-card}/resource.entity.ts (90%) rename src/app/shared/{components/resources/resource-card/models => entities/resource-card}/user-counts-response.entity.ts (100%) rename src/app/shared/{components/resources/resource-card/models => entities/resource-card}/user-related-data-counts.entity.ts (100%) create mode 100644 src/app/shared/helpers/mappers/filters/creators/creators.mappers.ts rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/dateCreated/date-created.mapper.ts (61%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/funder/funder.mapper.ts (62%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/institution/institution.mapper.ts (61%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/license/license.mapper.ts (61%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/part-of-collection/part-of-collection.mapper.ts (59%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/provider/provider.mapper.ts (61%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/resource-type/resource-type.mapper.ts (61%) rename src/app/shared/{components/resources/resource-filters/mappers => helpers/mappers/filters}/subject/subject.mapper.ts (64%) rename src/app/shared/{components/resources/resource-card/mappers => helpers/mappers/resource-card}/user-counts.mapper.ts (67%) create mode 100644 src/app/shared/services/filters-options.service.ts rename src/app/{features/search => shared/services}/search.service.ts (100%) create mode 100644 src/app/shared/styles/filter-chips/filter-chips.scss rename src/app/shared/{components/resources/resource-card/resource-card.component.scss => styles/resource-card/resource-card.scss} (96%) rename src/app/shared/{components/resources/resource-filters => }/utils/add-filters-params.helper.ts (66%) rename src/app/shared/{components/resources/resource-filters/models/filter-labels.ts => utils/filter-labels.model.ts} (87%) rename src/app/{features/search/utils/helpers => shared/utils}/get-resource-types.helper.ts (86%) rename src/app/shared/{components/resources/resource-filters/utils/data.ts => utils/resource-filters-defaults.model.ts} (52%) diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index c7b266930..be6db078b 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -2,8 +2,12 @@ import { provideStates } from '@ngxs/store'; import { Routes } from '@angular/router'; -import { ResourceFiltersOptionsState } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.state'; -import { ResourceFiltersState } from '@shared/components/resources/resource-filters/store/resource-filters.state'; +import { MyProfileResourceFiltersOptionsState } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.state'; +import { MyProfileResourceFiltersState } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state'; +import { MyProfileState } from '@osf/features/my-profile/store'; +import { ResourceFiltersOptionsState } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.state'; +import { ResourceFiltersState } from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.state'; +import { SearchState } from '@osf/features/search/store'; export const routes: Routes = [ { @@ -112,12 +116,14 @@ export const routes: Routes = [ { path: 'search', loadComponent: () => import('./features/search/search.component').then((mod) => mod.SearchComponent), - providers: [provideStates([ResourceFiltersState, ResourceFiltersOptionsState])], + providers: [provideStates([ResourceFiltersState, ResourceFiltersOptionsState, SearchState])], }, { path: 'my-profile', loadComponent: () => import('./features/my-profile/my-profile.component').then((mod) => mod.MyProfileComponent), - providers: [provideStates([ResourceFiltersState, ResourceFiltersOptionsState])], + providers: [ + provideStates([MyProfileResourceFiltersState, MyProfileResourceFiltersOptionsState, MyProfileState]), + ], }, { path: 'confirm/:userId/:emailId', diff --git a/src/app/core/constants/nav-items.constant.ts b/src/app/core/constants/nav-items.constant.ts index 661a1e514..d773d6cb3 100644 --- a/src/app/core/constants/nav-items.constant.ts +++ b/src/app/core/constants/nav-items.constant.ts @@ -27,6 +27,12 @@ export const NAV_ITEMS: NavItem[] = [ icon: 'my-projects', useExactMatch: true, }, + { + path: '/my-profile', + label: 'navigation.profile', + icon: 'profile', + useExactMatch: true, + }, { path: '/settings', label: 'navigation.settings', diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts index 93ad98ce6..84177462f 100644 --- a/src/app/core/constants/ngxs-states.constant.ts +++ b/src/app/core/constants/ngxs-states.constant.ts @@ -2,7 +2,6 @@ import { AuthState } from '@core/store/auth'; import { UserState } from '@core/store/user'; import { InstitutionsState } from '@osf/features/institutions/store'; import { MyProjectsState } from '@osf/features/my-projects/store'; -import { SearchState } from '@osf/features/search/store'; import { AccountSettingsState } from '@osf/features/settings/account-settings/store/account-settings.state'; import { AddonsState } from '@osf/features/settings/addons/store'; import { DeveloperAppsState } from '@osf/features/settings/developer-apps/store'; @@ -14,7 +13,6 @@ export const STATES = [ TokensState, AddonsState, UserState, - SearchState, MyProjectsState, InstitutionsState, ProfileSettingsState, diff --git a/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.html b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.html new file mode 100644 index 000000000..4d18ad32b --- /dev/null +++ b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.html @@ -0,0 +1,94 @@ +

+ better-research + +
+ +
+ + @if (!isMobile()) { + + All + Projects + Registrations + Preprints + Files + + } + + + + + + + + + + +
+ + + @if (currentStep === 1) { +
+

Improved OSF Search

+

+ Enter any term in the search box and filter by specific object types. More information is available on our + help guides. +

+
+

1 of 3

+
+ + Next +
+
+
+ } + + @if (currentStep === 2) { +
+

Refine Your Search

+

+ Narrow the source, discipline, and more. For example, find content supported by a specific funder or view only + datasets. +

+
+

2 of 3

+
+ + Next +
+
+
+ } + + @if (currentStep === 3) { +
+

Add Metadata

+

+ Remember to add metadata and resources to your own work on OSF to make it more discoverable! Learn more in our + help guides. +

+
+

3 of 3

+
+ Done +
+
+
+ } +
+
diff --git a/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.scss b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.scss new file mode 100644 index 000000000..ddd15a54e --- /dev/null +++ b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.scss @@ -0,0 +1,71 @@ +@use "assets/styles/variables" as var; + +:host { + .search-container { + margin: 3.4rem 1.7rem 0.4rem 1.7rem; + position: relative; + + img { + position: absolute; + right: 0.3rem; + top: 0.3rem; + z-index: 1; + } + } + + .resources { + position: relative; + background: white; + padding: 2rem; + } + + .stepper { + position: absolute; + padding: 1.7rem; + width: 32rem; + display: flex; + flex-direction: column; + row-gap: 1.7rem; + background: white; + border: 1px solid var.$grey-2; + border-radius: 12px; + h3 { + font-size: 1.3rem; + } + } + + .first-stepper { + top: 2rem; + left: 1.7rem; + } + + .second-stepper { + top: calc(2rem + 42px); + left: calc(1.5rem + 30%); + } + + .third-stepper { + top: calc(5rem + 42px); + left: calc(0.4rem + 30%); + } + + @media (max-width: 1000px) { + .search-container { + margin: 3.4rem 2.5rem 0.4rem 2.5rem; + } + + .resources { + padding: 2.5rem; + } + } + + @media (max-width: 600px) { + .search-container { + margin: 2.5rem 1.1rem 2.5rem 1.1rem; + } + + .resources { + padding: 1.1rem; + } + } +} diff --git a/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.spec.ts b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.spec.ts new file mode 100644 index 000000000..31aa477e4 --- /dev/null +++ b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileSearchComponent } from './my-profile-search.component'; + +describe('MyProfileSearchComponent', () => { + let component: MyProfileSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileSearchComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.ts b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.ts new file mode 100644 index 000000000..15b008f35 --- /dev/null +++ b/src/app/features/my-profile/components/my-profile-search/my-profile-search.component.ts @@ -0,0 +1,113 @@ +import { Store } from '@ngxs/store'; + +import { Button } from 'primeng/button'; +import { Tab, TabList, TabPanel, TabPanels, Tabs } from 'primeng/tabs'; + +import { debounceTime } from 'rxjs'; + +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, effect, inject, signal, untracked } from '@angular/core'; +import { toObservable, toSignal } from '@angular/core/rxjs-interop'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { MyProfileResourcesComponent } from '@osf/features/my-profile/components/resources/my-profile-resources.component'; +import { GetResources, SetResourceTab, SetSearchText } from '@osf/features/my-profile/store/my-profile.actions'; +import { MyProfileSelectors } from '@osf/features/my-profile/store/my-profile.selectors'; +import { SearchInputComponent } from '@shared/components/search-input/search-input.component'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; +import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; + +@Component({ + selector: 'osf-my-profile-search', + imports: [ + Button, + NgOptimizedImage, + SearchInputComponent, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + MyProfileResourcesComponent, + ], + templateUrl: './my-profile-search.component.html', + styleUrl: './my-profile-search.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileSearchComponent { + readonly #store = inject(Store); + + protected searchValue = signal(''); + protected readonly isMobile = toSignal(inject(IS_XSMALL)); + + protected readonly creatorsFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getCreator); + protected readonly dateCreatedFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getDateCreated); + protected readonly funderFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getFunder); + protected readonly subjectFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getSubject); + protected readonly licenseFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getLicense); + protected readonly resourceTypeFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getResourceType); + protected readonly institutionFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getInstitution); + protected readonly providerFilter = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getProvider); + protected readonly partOfCollectionFilter = this.#store.selectSignal( + MyProfileResourceFiltersSelectors.getPartOfCollection + ); + protected searchStoreValue = this.#store.selectSignal(MyProfileSelectors.getSearchText); + protected resourcesTabStoreValue = this.#store.selectSignal(MyProfileSelectors.getResourceTab); + protected sortByStoreValue = this.#store.selectSignal(MyProfileSelectors.getSortBy); + readonly isMyProfilePage = this.#store.selectSignal(MyProfileSelectors.getIsMyProfile); + + protected selectedTab: ResourceTab = ResourceTab.All; + protected readonly ResourceTab = ResourceTab; + protected currentStep = 0; + + constructor() { + effect(() => { + this.creatorsFilter(); + this.dateCreatedFilter(); + this.funderFilter(); + this.subjectFilter(); + this.licenseFilter(); + this.resourceTypeFilter(); + this.institutionFilter(); + this.providerFilter(); + this.partOfCollectionFilter(); + this.searchStoreValue(); + this.resourcesTabStoreValue(); + this.sortByStoreValue(); + console.log('get resources triggered'); + this.#store.dispatch(GetResources); + }); + + // put search value in store and update resources, filters + toObservable(this.searchValue) + .pipe(debounceTime(500)) + .subscribe((searchText) => { + this.#store.dispatch(new SetSearchText(searchText)); + this.#store.dispatch(GetAllOptions); + }); + + // sync search with query parameters if search is empty and parameters are not + effect(() => { + const storeValue = this.searchStoreValue(); + const currentInput = untracked(() => this.searchValue()); + + if (storeValue && currentInput !== storeValue) { + this.searchValue.set(storeValue); + } + }); + + // sync resource tabs with store + effect(() => { + if (this.selectedTab !== this.resourcesTabStoreValue()) { + this.selectedTab = this.resourcesTabStoreValue(); + } + }); + } + + onTabChange(index: ResourceTab): void { + this.#store.dispatch(new SetResourceTab(index)); + this.selectedTab = index; + this.#store.dispatch(GetAllOptions); + } +} diff --git a/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.html b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.html new file mode 100644 index 000000000..bfdf735af --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.html @@ -0,0 +1,119 @@ +@if (filters().dateCreated.value) { + @let dateCreated = filters().dateCreated.filterName + ': ' + filters().dateCreated.label; + + + + + +} + +@if (filters().funder.value) { + @let funder = filters().funder.filterName + ': ' + filters().funder.label; + + + + + +} + +@if (filters().subject.value) { + @let subject = filters().subject.filterName + ': ' + filters().subject.label; + + + + + +} + +@if (filters().license.value) { + @let license = filters().license.filterName + ': ' + filters().license.label; + + + + + +} + +@if (filters().resourceType.value) { + @let resourceType = filters().resourceType.filterName + ': ' + filters().resourceType.label; + + + + + +} + +@if (filters().institution.value) { + @let institution = filters().institution.filterName + ': ' + filters().institution.label; + + + + + +} + +@if (filters().provider.value) { + @let provider = filters().provider.filterName + ': ' + filters().provider.label; + + + + + +} + +@if (filters().partOfCollection.value) { + @let partOfCollection = filters().partOfCollection.filterName + ': ' + filters().partOfCollection.label; + + + + + +} diff --git a/src/app/shared/components/resources/filter-chips/filter-chips.component.scss b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.scss similarity index 100% rename from src/app/shared/components/resources/filter-chips/filter-chips.component.scss rename to src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.scss diff --git a/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.spec.ts b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.spec.ts new file mode 100644 index 000000000..86201f510 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileFilterChipsComponent } from './my-profile-filter-chips.component'; + +describe('FilterChipsComponent', () => { + let component: MyProfileFilterChipsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileFilterChipsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileFilterChipsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.ts b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.ts new file mode 100644 index 000000000..84fa7f603 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component.ts @@ -0,0 +1,68 @@ +import { Store } from '@ngxs/store'; + +import { PrimeTemplate } from 'primeng/api'; +import { Chip } from 'primeng/chip'; + +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { + SetDateCreated, + SetFunder, + SetInstitution, + SetLicense, + SetPartOfCollection, + SetProvider, + SetResourceType, + SetSubject, +} from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { MyProfileSelectors } from '@osf/features/my-profile/store/my-profile.selectors'; +import { FilterType } from '@shared/entities/filters/filter-type.enum'; + +@Component({ + selector: 'osf-my-profile-filter-chips', + imports: [Chip, PrimeTemplate], + templateUrl: './my-profile-filter-chips.component.html', + styleUrl: './my-profile-filter-chips.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileFilterChipsComponent { + readonly #store = inject(Store); + + protected filters = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getAllFilters); + + readonly isMyProfilePage = this.#store.selectSignal(MyProfileSelectors.getIsMyProfile); + + clearFilter(filter: FilterType) { + switch (filter) { + case FilterType.DateCreated: + this.#store.dispatch(new SetDateCreated('')); + break; + case FilterType.Funder: + this.#store.dispatch(new SetFunder('', '')); + break; + case FilterType.Subject: + this.#store.dispatch(new SetSubject('', '')); + break; + case FilterType.License: + this.#store.dispatch(new SetLicense('', '')); + break; + case FilterType.ResourceType: + this.#store.dispatch(new SetResourceType('', '')); + break; + case FilterType.Institution: + this.#store.dispatch(new SetInstitution('', '')); + break; + case FilterType.Provider: + this.#store.dispatch(new SetProvider('', '')); + break; + case FilterType.PartOfCollection: + this.#store.dispatch(new SetPartOfCollection('', '')); + break; + } + this.#store.dispatch(GetAllOptions); + } + + protected readonly FilterType = FilterType; +} diff --git a/src/app/shared/components/resources/resource-card/resource-card.component.html b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.html similarity index 100% rename from src/app/shared/components/resources/resource-card/resource-card.component.html rename to src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.html diff --git a/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.scss b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.scss new file mode 100644 index 000000000..f5e99a7c1 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.scss @@ -0,0 +1 @@ +@use "/app/shared/styles/resource-card/resource-card.scss" as resource-card; diff --git a/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.spec.ts new file mode 100644 index 000000000..58d77776e --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileResourceCardComponent } from './my-profile-resource-card.component'; + +describe('MyProfileResourceCardComponent', () => { + let component: MyProfileResourceCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileResourceCardComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileResourceCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.ts b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.ts new file mode 100644 index 000000000..687f7428a --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component.ts @@ -0,0 +1,60 @@ +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; +import { Skeleton } from 'primeng/skeleton'; + +import { finalize } from 'rxjs'; + +import { DatePipe, NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; + +import { ResourceCardService } from '@osf/features/search/components/resources/components/resource-card/services/resource-card.service'; +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceType } from '@shared/entities/resource-card/resource-type.enum'; +import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; + +@Component({ + selector: 'osf-my-profile-resource-card', + imports: [Accordion, AccordionContent, AccordionHeader, AccordionPanel, DatePipe, NgOptimizedImage, Skeleton], + templateUrl: './my-profile-resource-card.component.html', + styleUrl: './my-profile-resource-card.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileResourceCardComponent { + item = model(undefined); + readonly #resourceCardService = inject(ResourceCardService); + loading = false; + dataIsLoaded = false; + isSmall = toSignal(inject(IS_XSMALL)); + + protected readonly ResourceType = ResourceType; + + onOpen() { + if (this.item() && !this.dataIsLoaded) { + const userIri = this.item()?.id.split('/').pop(); + if (userIri) { + this.loading = true; + this.#resourceCardService + .getUserRelatedCounts(userIri) + .pipe( + finalize(() => { + this.loading = false; + this.dataIsLoaded = true; + }) + ) + .subscribe((res) => { + this.item.update( + (current) => + ({ + ...current, + publicProjects: res.projects, + publicPreprints: res.preprints, + publicRegistrations: res.registrations, + education: res.education, + employment: res.employment, + }) as Resource + ); + }); + } + } + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-card/services/my-profile-resource-card.service.ts b/src/app/features/my-profile/components/resources/components/resource-card/services/my-profile-resource-card.service.ts new file mode 100644 index 000000000..ea786529b --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-card/services/my-profile-resource-card.service.ts @@ -0,0 +1,27 @@ +import { map, Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { JsonApiService } from '@core/services/json-api/json-api.service'; +import { UserCountsResponse } from '@shared/entities/resource-card/user-counts-response.entity'; +import { UserRelatedDataCounts } from '@shared/entities/resource-card/user-related-data-counts.entity'; +import { MapUserCounts } from '@shared/helpers/mappers/resource-card/user-counts.mapper'; + +import { environment } from '../../../../../../../../environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class ResourceCardService { + #jsonApiService = inject(JsonApiService); + + getUserRelatedCounts(userIri: string): Observable { + const params: Record = { + related_counts: 'nodes,registrations,preprints', + }; + + return this.#jsonApiService + .get(`${environment.apiUrl}/users/${userIri}`, params) + .pipe(map((response) => MapUserCounts(response))); + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/index.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/index.ts new file mode 100644 index 000000000..91d233e60 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/index.ts @@ -0,0 +1,8 @@ +export * from './my-profile-date-created-filter/my-profile-date-created-filter.component'; +export * from './my-profile-funder-filter/my-profile-funder-filter.component'; +export * from './my-profile-institution-filter/my-profile-institution-filter.component'; +export * from './my-profile-license-filter/my-profile-license-filter.component'; +export * from './my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component'; +export * from './my-profile-provider-filter/my-profile-provider-filter.component'; +export * from './my-profile-resource-type-filter/my-profile-resource-type-filter.component'; +export * from './my-profile-subject-filter/my-profile-subject-filter.component'; diff --git a/src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.spec.ts new file mode 100644 index 000000000..410fca7e8 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileDateCreatedFilterComponent } from './my-profile-date-created-filter.component'; + +describe('MyProfileDateCreatedFilterComponent', () => { + let component: MyProfileDateCreatedFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileDateCreatedFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileDateCreatedFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.ts new file mode 100644 index 000000000..2d9c0ee31 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-date-created-filter/my-profile-date-created-filter.component.ts @@ -0,0 +1,52 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetDateCreated } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-date-created-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-date-created-filter.component.html', + styleUrl: './my-profile-date-created-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileDateCreatedFilterComponent { + readonly #store = inject(Store); + + protected availableDates = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getDatesCreated); + protected dateCreatedState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getDateCreated); + protected inputDate = signal(null); + protected datesOptions = computed(() => { + return this.availableDates().map((date) => ({ + label: date.value + ' (' + date.count + ')', + value: date.value, + })); + }); + + constructor() { + effect(() => { + const storeValue = this.dateCreatedState().label; + const currentInput = untracked(() => this.inputDate()); + + if (!storeValue && currentInput !== null) { + this.inputDate.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputDate.set(storeValue); + } + }); + } + + setDateCreated(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId) { + this.#store.dispatch(new SetDateCreated(event.value)); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.spec.ts new file mode 100644 index 000000000..ee95da25e --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileFunderFilterComponent } from './my-profile-funder-filter.component'; + +describe('MyProfileFunderFilterComponent', () => { + let component: MyProfileFunderFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileFunderFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileFunderFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.ts new file mode 100644 index 000000000..40b253436 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-funder-filter/my-profile-funder-filter.component.ts @@ -0,0 +1,74 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetFunder } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-funder-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-funder-filter.component.html', + styleUrl: './my-profile-funder-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileFunderFilterComponent { + readonly #store = inject(Store); + + protected funderState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getFunder); + protected availableFunders = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getFunders); + protected inputText = signal(null); + protected fundersOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableFunders() + .filter((funder) => funder.label.toLowerCase().includes(search)) + .map((funder) => ({ + labelCount: funder.label + ' (' + funder.count + ')', + label: funder.label, + id: funder.id, + })); + } + + const res = this.availableFunders().map((funder) => ({ + labelCount: funder.label + ' (' + funder.count + ')', + label: funder.label, + id: funder.id, + })); + + return res; + }); + + constructor() { + effect(() => { + const storeValue = this.funderState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + loading = signal(false); + + setFunders(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const funder = this.fundersOptions()?.find((funder) => funder.label.includes(event.value)); + if (funder) { + this.#store.dispatch(new SetFunder(funder.label, funder.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetFunder('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.spec.ts new file mode 100644 index 000000000..c50ffcc68 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileInstitutionFilterComponent } from './my-profile-institution-filter.component'; + +describe('MyProfileInstitutionFilterComponent', () => { + let component: MyProfileInstitutionFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileInstitutionFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileInstitutionFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.ts new file mode 100644 index 000000000..a11c70d70 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-institution-filter/my-profile-institution-filter.component.ts @@ -0,0 +1,74 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetInstitution } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-institution-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-institution-filter.component.html', + styleUrl: './my-profile-institution-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileInstitutionFilterComponent { + readonly #store = inject(Store); + + protected institutionState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getInstitution); + protected availableInstitutions = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getInstitutions); + protected inputText = signal(null); + protected institutionsOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableInstitutions() + .filter((institution) => institution.label.toLowerCase().includes(search)) + .map((institution) => ({ + labelCount: institution.label + ' (' + institution.count + ')', + label: institution.label, + id: institution.id, + })); + } + + const res = this.availableInstitutions().map((institution) => ({ + labelCount: institution.label + ' (' + institution.count + ')', + label: institution.label, + id: institution.id, + })); + + return res; + }); + + constructor() { + effect(() => { + const storeValue = this.institutionState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + loading = signal(false); + + setInstitutions(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const institution = this.institutionsOptions()?.find((institution) => institution.label.includes(event.value)); + if (institution) { + this.#store.dispatch(new SetInstitution(institution.label, institution.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetInstitution('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.spec.ts new file mode 100644 index 000000000..9424d6b4a --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileLicenseFilterComponent } from './my-profile-license-filter.component'; + +describe('MyProfileLicenseFilterComponent', () => { + let component: MyProfileLicenseFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileLicenseFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileLicenseFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.ts new file mode 100644 index 000000000..c2009e775 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-license-filter/my-profile-license-filter.component.ts @@ -0,0 +1,72 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetLicense } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-license-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-license-filter.component.html', + styleUrl: './my-profile-license-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileLicenseFilterComponent { + readonly #store = inject(Store); + + protected availableLicenses = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getLicenses); + protected licenseState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getLicense); + protected inputText = signal(null); + protected licensesOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableLicenses() + .filter((license) => license.label.toLowerCase().includes(search)) + .map((license) => ({ + labelCount: license.label + ' (' + license.count + ')', + label: license.label, + id: license.id, + })); + } + + return this.availableLicenses().map((license) => ({ + labelCount: license.label + ' (' + license.count + ')', + label: license.label, + id: license.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.licenseState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setLicenses(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const license = this.licensesOptions().find((license) => license.label.includes(event.value)); + if (license) { + this.#store.dispatch(new SetLicense(license.label, license.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetLicense('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.spec.ts new file mode 100644 index 000000000..6ab3d0fe8 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfilePartOfCollectionFilterComponent } from './my-profile-part-of-collection-filter.component'; + +describe('MyProfilePartOfCollectionFilterComponent', () => { + let component: MyProfilePartOfCollectionFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfilePartOfCollectionFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfilePartOfCollectionFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.ts new file mode 100644 index 000000000..7bab21c6d --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-part-of-collection-filter/my-profile-part-of-collection-filter.component.ts @@ -0,0 +1,63 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetPartOfCollection } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-part-of-collection-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-part-of-collection-filter.component.html', + styleUrl: './my-profile-part-of-collection-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfilePartOfCollectionFilterComponent { + readonly #store = inject(Store); + + protected availablePartOfCollections = this.#store.selectSignal( + MyProfileResourceFiltersOptionsSelectors.getPartOfCollection + ); + protected partOfCollectionState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getPartOfCollection); + protected inputText = signal(null); + protected partOfCollectionsOptions = computed(() => { + return this.availablePartOfCollections().map((partOfCollection) => ({ + labelCount: partOfCollection.label + ' (' + partOfCollection.count + ')', + label: partOfCollection.label, + id: partOfCollection.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.partOfCollectionState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setPartOfCollections(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const part = this.partOfCollectionsOptions().find((p) => p.label.includes(event.value)); + if (part) { + this.#store.dispatch(new SetPartOfCollection(part.label, part.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetPartOfCollection('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.spec.ts new file mode 100644 index 000000000..897e75514 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileProviderFilterComponent } from './my-profile-provider-filter.component'; + +describe('MyProfileProviderFilterComponent', () => { + let component: MyProfileProviderFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileProviderFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileProviderFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.ts new file mode 100644 index 000000000..23ba707cb --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-provider-filter/my-profile-provider-filter.component.ts @@ -0,0 +1,72 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetProvider } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-provider-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-provider-filter.component.html', + styleUrl: './my-profile-provider-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileProviderFilterComponent { + readonly #store = inject(Store); + + protected availableProviders = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getProviders); + protected providerState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getProvider); + protected inputText = signal(null); + protected providersOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableProviders() + .filter((provider) => provider.label.toLowerCase().includes(search)) + .map((provider) => ({ + labelCount: provider.label + ' (' + provider.count + ')', + label: provider.label, + id: provider.id, + })); + } + + return this.availableProviders().map((provider) => ({ + labelCount: provider.label + ' (' + provider.count + ')', + label: provider.label, + id: provider.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.providerState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setProviders(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const provider = this.providersOptions().find((p) => p.label.includes(event.value)); + if (provider) { + this.#store.dispatch(new SetProvider(provider.label, provider.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetProvider('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.spec.ts new file mode 100644 index 000000000..a716ca8a3 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileResourceTypeFilterComponent } from './my-profile-resource-type-filter.component'; + +describe('MyProfileResourceTypeFilterComponent', () => { + let component: MyProfileResourceTypeFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileResourceTypeFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileResourceTypeFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.ts new file mode 100644 index 000000000..fb4414672 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-resource-type-filter/my-profile-resource-type-filter.component.ts @@ -0,0 +1,74 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { SetResourceType } from '@osf/features/search/components/resources/components/resource-filters/store'; + +@Component({ + selector: 'osf-my-profile-resource-type-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-resource-type-filter.component.html', + styleUrl: './my-profile-resource-type-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileResourceTypeFilterComponent { + readonly #store = inject(Store); + + protected availableResourceTypes = this.#store.selectSignal( + MyProfileResourceFiltersOptionsSelectors.getResourceTypes + ); + protected resourceTypeState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getResourceType); + protected inputText = signal(null); + protected resourceTypesOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableResourceTypes() + .filter((resourceType) => resourceType.label.toLowerCase().includes(search)) + .map((resourceType) => ({ + labelCount: resourceType.label + ' (' + resourceType.count + ')', + label: resourceType.label, + id: resourceType.id, + })); + } + + return this.availableResourceTypes().map((resourceType) => ({ + labelCount: resourceType.label + ' (' + resourceType.count + ')', + label: resourceType.label, + id: resourceType.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.resourceTypeState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setResourceTypes(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const resourceType = this.resourceTypesOptions().find((p) => p.label.includes(event.value)); + if (resourceType) { + this.#store.dispatch(new SetResourceType(resourceType.label, resourceType.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetResourceType('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.html rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.html diff --git a/src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.spec.ts new file mode 100644 index 000000000..c2df28347 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileSubjectFilterComponent } from './my-profile-subject-filter.component'; + +describe('MyProfileSubjectFilterComponent', () => { + let component: MyProfileSubjectFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileSubjectFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileSubjectFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.ts new file mode 100644 index 000000000..4902d3c51 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/my-profile-subject-filter/my-profile-subject-filter.component.ts @@ -0,0 +1,72 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GetAllOptions } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { SetSubject } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; + +@Component({ + selector: 'osf-my-profile-subject-filter', + imports: [Select, FormsModule], + templateUrl: './my-profile-subject-filter.component.html', + styleUrl: './my-profile-subject-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileSubjectFilterComponent { + readonly #store = inject(Store); + + protected availableSubjects = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getSubjects); + protected subjectState = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getSubject); + protected inputText = signal(null); + protected subjectsOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableSubjects() + .filter((subject) => subject.label.toLowerCase().includes(search)) + .map((subject) => ({ + labelCount: subject.label + ' (' + subject.count + ')', + label: subject.label, + id: subject.id, + })); + } + + return this.availableSubjects().map((subject) => ({ + labelCount: subject.label + ' (' + subject.count + ')', + label: subject.label, + id: subject.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.subjectState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setSubject(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const subject = this.subjectsOptions().find((p) => p.label.includes(event.value)); + if (subject) { + this.#store.dispatch(new SetSubject(subject.label, subject.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetSubject('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions.ts new file mode 100644 index 000000000..f420a151d --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions.ts @@ -0,0 +1,41 @@ +export class GetCreatorsOptions { + static readonly type = '[My Profile Resource Filters Options] Get Creators'; + + constructor(public searchName: string) {} +} + +export class GetDatesCreatedOptions { + static readonly type = '[My Profile Resource Filters Options] Get Dates Created'; +} + +export class GetFundersOptions { + static readonly type = '[My Profile Resource Filters Options] Get Funders'; +} + +export class GetSubjectsOptions { + static readonly type = '[My Profile Resource Filters Options] Get Subjects'; +} + +export class GetLicensesOptions { + static readonly type = '[My Profile Resource Filters Options] Get Licenses'; +} + +export class GetResourceTypesOptions { + static readonly type = '[My Profile Resource Filters Options] Get Resource Types'; +} + +export class GetInstitutionsOptions { + static readonly type = '[My Profile Resource Filters Options] Get Institutions'; +} + +export class GetProvidersOptions { + static readonly type = '[My Profile Resource Filters Options] Get Providers'; +} + +export class GetPartOfCollectionOptions { + static readonly type = '[My Profile Resource Filters Options] Get Part Of Collection Options'; +} + +export class GetAllOptions { + static readonly type = '[My Profile Resource Filters Options] Get All Options'; +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model.ts new file mode 100644 index 000000000..29c093eab --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model.ts @@ -0,0 +1,21 @@ +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { InstitutionFilter } from '@shared/entities/filters/institution/institution-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; + +export interface MyProfileResourceFiltersOptionsStateModel { + creators: Creator[]; + datesCreated: DateCreated[]; + funders: FunderFilter[]; + subjects: SubjectFilter[]; + licenses: LicenseFilter[]; + resourceTypes: ResourceTypeFilter[]; + institutions: InstitutionFilter[]; + providers: ProviderFilter[]; + partOfCollection: PartOfCollectionFilter[]; +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors.ts new file mode 100644 index 000000000..ad4e0e5a8 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors.ts @@ -0,0 +1,68 @@ +import { Selector } from '@ngxs/store'; + +import { MyProfileResourceFiltersOptionsStateModel } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model'; +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { InstitutionFilter } from '@shared/entities/filters/institution/institution-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; + +import { MyProfileResourceFiltersOptionsState } from './my-profile-resource-filters-options.state'; + +export class MyProfileResourceFiltersOptionsSelectors { + @Selector([MyProfileResourceFiltersOptionsState]) + static getCreators(state: MyProfileResourceFiltersOptionsStateModel): Creator[] { + return state.creators; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getDatesCreated(state: MyProfileResourceFiltersOptionsStateModel): DateCreated[] { + return state.datesCreated; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getFunders(state: MyProfileResourceFiltersOptionsStateModel): FunderFilter[] { + return state.funders; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getSubjects(state: MyProfileResourceFiltersOptionsStateModel): SubjectFilter[] { + return state.subjects; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getLicenses(state: MyProfileResourceFiltersOptionsStateModel): LicenseFilter[] { + return state.licenses; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getResourceTypes(state: MyProfileResourceFiltersOptionsStateModel): ResourceTypeFilter[] { + return state.resourceTypes; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getInstitutions(state: MyProfileResourceFiltersOptionsStateModel): InstitutionFilter[] { + return state.institutions; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getProviders(state: MyProfileResourceFiltersOptionsStateModel): ProviderFilter[] { + return state.providers; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getPartOfCollection(state: MyProfileResourceFiltersOptionsStateModel): PartOfCollectionFilter[] { + return state.partOfCollection; + } + + @Selector([MyProfileResourceFiltersOptionsState]) + static getAllOptions(state: MyProfileResourceFiltersOptionsStateModel): MyProfileResourceFiltersOptionsStateModel { + return { + ...state, + }; + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.state.ts b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.state.ts new file mode 100644 index 000000000..88962ec25 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.state.ts @@ -0,0 +1,138 @@ +import { Action, State, StateContext, Store } from '@ngxs/store'; + +import { tap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { + GetAllOptions, + GetCreatorsOptions, + GetDatesCreatedOptions, + GetFundersOptions, + GetInstitutionsOptions, + GetLicensesOptions, + GetPartOfCollectionOptions, + GetProvidersOptions, + GetResourceTypesOptions, + GetSubjectsOptions, +} from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.actions'; +import { MyProfileResourceFiltersOptionsStateModel } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.model'; +import { MyProfileFiltersOptionsService } from '@osf/features/my-profile/components/resources/components/resource-filters/services/my-profile-resource-filters.service'; +import { ResourceFiltersOptionsStateModel } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model'; + +@State({ + name: 'myProfileResourceFiltersOptions', + defaults: { + creators: [], + datesCreated: [], + funders: [], + subjects: [], + licenses: [], + resourceTypes: [], + institutions: [], + providers: [], + partOfCollection: [], + }, +}) +@Injectable() +export class MyProfileResourceFiltersOptionsState { + readonly #store = inject(Store); + readonly #filtersOptionsService = inject(MyProfileFiltersOptionsService); + + @Action(GetCreatorsOptions) + getCreators(ctx: StateContext, action: GetCreatorsOptions) { + if (!action.searchName) { + ctx.patchState({ creators: [] }); + return []; + } + + return this.#filtersOptionsService.getCreators(action.searchName).pipe( + tap((creators) => { + ctx.patchState({ creators: creators }); + }) + ); + } + + @Action(GetDatesCreatedOptions) + getDatesCreated(ctx: StateContext) { + return this.#filtersOptionsService.getDates().pipe( + tap((datesCreated) => { + ctx.patchState({ datesCreated: datesCreated }); + }) + ); + } + + @Action(GetFundersOptions) + getFunders(ctx: StateContext) { + return this.#filtersOptionsService.getFunders().pipe( + tap((funders) => { + ctx.patchState({ funders: funders }); + }) + ); + } + + @Action(GetSubjectsOptions) + getSubjects(ctx: StateContext) { + return this.#filtersOptionsService.getSubjects().pipe( + tap((subjects) => { + ctx.patchState({ subjects: subjects }); + }) + ); + } + + @Action(GetLicensesOptions) + getLicenses(ctx: StateContext) { + return this.#filtersOptionsService.getLicenses().pipe( + tap((licenses) => { + ctx.patchState({ licenses: licenses }); + }) + ); + } + + @Action(GetResourceTypesOptions) + getResourceTypes(ctx: StateContext) { + return this.#filtersOptionsService.getResourceTypes().pipe( + tap((resourceTypes) => { + ctx.patchState({ resourceTypes: resourceTypes }); + }) + ); + } + + @Action(GetInstitutionsOptions) + getInstitutions(ctx: StateContext) { + return this.#filtersOptionsService.getInstitutions().pipe( + tap((institutions) => { + ctx.patchState({ institutions: institutions }); + }) + ); + } + + @Action(GetProvidersOptions) + getProviders(ctx: StateContext) { + return this.#filtersOptionsService.getProviders().pipe( + tap((providers) => { + ctx.patchState({ providers: providers }); + }) + ); + } + @Action(GetPartOfCollectionOptions) + getPartOfCollection(ctx: StateContext) { + return this.#filtersOptionsService.getPartOtCollections().pipe( + tap((partOfCollection) => { + ctx.patchState({ partOfCollection: partOfCollection }); + }) + ); + } + + @Action(GetAllOptions) + getAllOptions() { + this.#store.dispatch(GetDatesCreatedOptions); + this.#store.dispatch(GetFundersOptions); + this.#store.dispatch(GetSubjectsOptions); + this.#store.dispatch(GetLicensesOptions); + this.#store.dispatch(GetResourceTypesOptions); + this.#store.dispatch(GetInstitutionsOptions); + this.#store.dispatch(GetProvidersOptions); + this.#store.dispatch(GetPartOfCollectionOptions); + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.html b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.html new file mode 100644 index 000000000..05c15b5f1 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.html @@ -0,0 +1,77 @@ +@if (anyOptionsCount()) { +
+ + @if (datesOptionsCount() > 0) { + + Date Created + + + + + } + + @if (funderOptionsCount() > 0) { + + Funder + + + + + } + + @if (subjectOptionsCount() > 0) { + + Subject + + + + + } + + @if (licenseOptionsCount() > 0) { + + License + + + + + } + + @if (resourceTypeOptionsCount() > 0) { + + Resource Type + + + + + } + + @if (institutionOptionsCount() > 0) { + + Institution + + + + + } + + @if (providerOptionsCount() > 0) { + + Provider + + + + + } + + @if (partOfCollectionOptionsCount() > 0) { + + Part of Collection + + + + + } + +
+} diff --git a/src/app/shared/components/resources/resource-filters/resource-filters.component.scss b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/resource-filters.component.scss rename to src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.scss diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.spec.ts b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.spec.ts new file mode 100644 index 000000000..f6396d507 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileResourceFiltersComponent } from './my-profile-resource-filters.component'; + +describe('MyProfileResourceFiltersComponent', () => { + let component: MyProfileResourceFiltersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileResourceFiltersComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileResourceFiltersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.ts b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.ts new file mode 100644 index 000000000..0e9597a23 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component.ts @@ -0,0 +1,106 @@ +import { Store } from '@ngxs/store'; + +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; + +import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; + +import { + MyProfileDateCreatedFilterComponent, + MyProfileFunderFilterComponent, + MyProfileInstitutionFilterComponent, + MyProfileLicenseFilterComponent, + MyProfilePartOfCollectionFilterComponent, + MyProfileProviderFilterComponent, + MyProfileResourceTypeFilterComponent, + MyProfileSubjectFilterComponent, +} from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { MyProfileSelectors } from '@osf/features/my-profile/store/my-profile.selectors'; + +@Component({ + selector: 'osf-my-profile-resource-filters', + imports: [ + Accordion, + AccordionContent, + AccordionHeader, + AccordionPanel, + MyProfileDateCreatedFilterComponent, + MyProfileFunderFilterComponent, + MyProfileSubjectFilterComponent, + MyProfileLicenseFilterComponent, + MyProfileResourceTypeFilterComponent, + MyProfileInstitutionFilterComponent, + MyProfileProviderFilterComponent, + MyProfilePartOfCollectionFilterComponent, + ], + templateUrl: './my-profile-resource-filters.component.html', + styleUrl: './my-profile-resource-filters.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileResourceFiltersComponent { + readonly #store = inject(Store); + + readonly datesOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getDatesCreated)() + .reduce((accumulator, date) => accumulator + date.count, 0); + }); + + readonly funderOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getFunders)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly subjectOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getSubjects)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly licenseOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getLicenses)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly resourceTypeOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getResourceTypes)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly institutionOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getInstitutions)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly providerOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getProviders)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly partOfCollectionOptionsCount = computed(() => { + return this.#store + .selectSignal(MyProfileResourceFiltersOptionsSelectors.getPartOfCollection)() + .reduce((acc, item) => acc + item.count, 0); + }); + + readonly isMyProfilePage = this.#store.selectSignal(MyProfileSelectors.getIsMyProfile); + + readonly anyOptionsCount = computed(() => { + return ( + this.datesOptionsCount() > 0 || + this.funderOptionsCount() > 0 || + this.subjectOptionsCount() > 0 || + this.licenseOptionsCount() > 0 || + this.resourceTypeOptionsCount() > 0 || + this.institutionOptionsCount() > 0 || + this.providerOptionsCount() > 0 || + this.partOfCollectionOptionsCount() > 0 || + !this.isMyProfilePage() + ); + }); +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/services/my-profile-resource-filters.service.ts b/src/app/features/my-profile/components/resources/components/resource-filters/services/my-profile-resource-filters.service.ts new file mode 100644 index 000000000..0754da54c --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/services/my-profile-resource-filters.service.ts @@ -0,0 +1,82 @@ +import { Store } from '@ngxs/store'; + +import { Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { MyProfileSelectors } from '@osf/features/my-profile/store/my-profile.selectors'; +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; +import { FiltersOptionsService } from '@shared/services/filters-options.service'; +import { addFiltersParams } from '@shared/utils/add-filters-params.helper'; +import { getResourceTypes } from '@shared/utils/get-resource-types.helper'; + +@Injectable({ + providedIn: 'root', +}) +export class MyProfileFiltersOptionsService { + #store = inject(Store); + #filtersOptions = inject(FiltersOptionsService); + + #getFilterParams(): Record { + return addFiltersParams(this.#store.selectSignal(MyProfileResourceFiltersSelectors.getAllFilters)()); + } + + #getParams(): Record { + const params: Record = {}; + const resourceTab = this.#store.selectSnapshot(MyProfileSelectors.getResourceTab); + const resourceTypes = getResourceTypes(resourceTab); + const searchText = this.#store.selectSnapshot(MyProfileSelectors.getSearchText); + const sort = this.#store.selectSnapshot(MyProfileSelectors.getSortBy); + + params['cardSearchFilter[resourceType]'] = resourceTypes; + params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; + params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; + params['page[size]'] = '10'; + params['sort'] = sort; + return params; + } + + getCreators(valueSearchText: string): Observable { + return this.#filtersOptions.getCreators(valueSearchText, this.#getParams(), this.#getFilterParams()); + } + + getDates(): Observable { + return this.#filtersOptions.getDates(this.#getParams(), this.#getFilterParams()); + } + + getFunders(): Observable { + return this.#filtersOptions.getFunders(this.#getParams(), this.#getFilterParams()); + } + + getSubjects(): Observable { + return this.#filtersOptions.getSubjects(this.#getParams(), this.#getFilterParams()); + } + + getLicenses(): Observable { + return this.#filtersOptions.getLicenses(this.#getParams(), this.#getFilterParams()); + } + + getResourceTypes(): Observable { + return this.#filtersOptions.getResourceTypes(this.#getParams(), this.#getFilterParams()); + } + + getInstitutions(): Observable { + return this.#filtersOptions.getInstitutions(this.#getParams(), this.#getFilterParams()); + } + + getProviders(): Observable { + return this.#filtersOptions.getProviders(this.#getParams(), this.#getFilterParams()); + } + + getPartOtCollections(): Observable { + return this.#filtersOptions.getPartOtCollections(this.#getParams(), this.#getFilterParams()); + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions.ts b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions.ts new file mode 100644 index 000000000..9ff219206 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions.ts @@ -0,0 +1,68 @@ +export class SetCreator { + static readonly type = '[ My Profile Resource Filters] Set Creator'; + constructor( + public name: string, + public id: string + ) {} +} + +export class SetDateCreated { + static readonly type = '[ My Profile Resource Filters] Set DateCreated'; + constructor(public date: string) {} +} + +export class SetFunder { + static readonly type = '[ My Profile Resource Filters] Set Funder'; + constructor( + public funder: string, + public id: string + ) {} +} + +export class SetSubject { + static readonly type = '[ My Profile Resource Filters] Set Subject'; + constructor( + public subject: string, + public id: string + ) {} +} + +export class SetLicense { + static readonly type = '[ My Profile Resource Filters] Set License'; + constructor( + public license: string, + public id: string + ) {} +} + +export class SetResourceType { + static readonly type = '[ My Profile Resource Filters] Set Resource Type'; + constructor( + public resourceType: string, + public id: string + ) {} +} + +export class SetInstitution { + static readonly type = '[ My Profile Resource Filters] Set Institution'; + constructor( + public institution: string, + public id: string + ) {} +} + +export class SetProvider { + static readonly type = '[ My Profile Resource Filters] Set Provider'; + constructor( + public provider: string, + public id: string + ) {} +} + +export class SetPartOfCollection { + static readonly type = '[ My Profile Resource Filters] Set PartOfCollection'; + constructor( + public partOfCollection: string, + public id: string + ) {} +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.model.ts b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.model.ts new file mode 100644 index 000000000..e1b20d42a --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.model.ts @@ -0,0 +1,17 @@ +export interface MyProfileResourceFiltersStateModel { + creator: ResourceFilterLabel; + dateCreated: ResourceFilterLabel; + funder: ResourceFilterLabel; + subject: ResourceFilterLabel; + license: ResourceFilterLabel; + resourceType: ResourceFilterLabel; + institution: ResourceFilterLabel; + provider: ResourceFilterLabel; + partOfCollection: ResourceFilterLabel; +} + +export interface ResourceFilterLabel { + filterName: string; + label?: string; + value?: string; +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors.ts b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors.ts new file mode 100644 index 000000000..08e6625a3 --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors.ts @@ -0,0 +1,61 @@ +import { Selector } from '@ngxs/store'; + +import { MyProfileResourceFiltersState } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state'; +import { + ResourceFilterLabel, + ResourceFiltersStateModel, +} from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.model'; + +export class MyProfileResourceFiltersSelectors { + @Selector([MyProfileResourceFiltersState]) + static getAllFilters(state: ResourceFiltersStateModel): ResourceFiltersStateModel { + return { + ...state, + }; + } + + @Selector([MyProfileResourceFiltersState]) + static getCreator(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.creator; + } + + @Selector([MyProfileResourceFiltersState]) + static getDateCreated(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.dateCreated; + } + + @Selector([MyProfileResourceFiltersState]) + static getFunder(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.funder; + } + + @Selector([MyProfileResourceFiltersState]) + static getSubject(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.subject; + } + + @Selector([MyProfileResourceFiltersState]) + static getLicense(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.license; + } + + @Selector([MyProfileResourceFiltersState]) + static getResourceType(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.resourceType; + } + + @Selector([MyProfileResourceFiltersState]) + static getInstitution(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.institution; + } + + @Selector([MyProfileResourceFiltersState]) + static getProvider(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.provider; + } + + @Selector([MyProfileResourceFiltersState]) + static getPartOfCollection(state: ResourceFiltersStateModel): ResourceFilterLabel { + return state.partOfCollection; + } +} diff --git a/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state.ts b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state.ts new file mode 100644 index 000000000..b4993f04b --- /dev/null +++ b/src/app/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.state.ts @@ -0,0 +1,142 @@ +import { Action, NgxsOnInit, State, StateContext, Store } from '@ngxs/store'; + +import { inject, Injectable } from '@angular/core'; + +import { UserSelectors } from '@core/store/user/user.selectors'; +import { + SetCreator, + SetDateCreated, + SetFunder, + SetInstitution, + SetLicense, + SetPartOfCollection, + SetProvider, + SetResourceType, + SetSubject, +} from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.actions'; +import { MyProfileResourceFiltersStateModel } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.model'; +import { FilterLabelsModel } from '@shared/utils/filter-labels.model'; +import { resourceFiltersDefaultsModel } from '@shared/utils/resource-filters-defaults.model'; + +// Store for user selected filters values +@State({ + name: 'myProfileResourceFilters', + defaults: resourceFiltersDefaultsModel, +}) +@Injectable() +export class MyProfileResourceFiltersState implements NgxsOnInit { + #store = inject(Store); + #currentUser = this.#store.select(UserSelectors.getCurrentUser); + + ngxsOnInit(ctx: StateContext) { + this.#currentUser.subscribe((user) => { + if (user) { + ctx.patchState({ + creator: { + filterName: FilterLabelsModel.creator, + label: undefined, + value: user.iri, + }, + }); + } + }); + } + @Action(SetCreator) + setCreator(ctx: StateContext, action: SetCreator) { + ctx.patchState({ + creator: { + filterName: FilterLabelsModel.creator, + label: action.name, + value: action.id, + }, + }); + } + + @Action(SetDateCreated) + setDateCreated(ctx: StateContext, action: SetDateCreated) { + ctx.patchState({ + dateCreated: { + filterName: FilterLabelsModel.dateCreated, + label: action.date, + value: action.date, + }, + }); + } + + @Action(SetFunder) + setFunder(ctx: StateContext, action: SetFunder) { + ctx.patchState({ + funder: { + filterName: FilterLabelsModel.funder, + label: action.funder, + value: action.id, + }, + }); + } + + @Action(SetSubject) + setSubject(ctx: StateContext, action: SetSubject) { + ctx.patchState({ + subject: { + filterName: FilterLabelsModel.subject, + label: action.subject, + value: action.id, + }, + }); + } + + @Action(SetLicense) + setLicense(ctx: StateContext, action: SetLicense) { + ctx.patchState({ + license: { + filterName: FilterLabelsModel.license, + label: action.license, + value: action.id, + }, + }); + } + + @Action(SetResourceType) + setResourceType(ctx: StateContext, action: SetResourceType) { + ctx.patchState({ + resourceType: { + filterName: FilterLabelsModel.resourceType, + label: action.resourceType, + value: action.id, + }, + }); + } + + @Action(SetInstitution) + setInstitution(ctx: StateContext, action: SetInstitution) { + ctx.patchState({ + institution: { + filterName: FilterLabelsModel.institution, + label: action.institution, + value: action.id, + }, + }); + } + + @Action(SetProvider) + setProvider(ctx: StateContext, action: SetProvider) { + ctx.patchState({ + provider: { + filterName: FilterLabelsModel.provider, + label: action.provider, + value: action.id, + }, + }); + } + + @Action(SetPartOfCollection) + setPartOfCollection(ctx: StateContext, action: SetPartOfCollection) { + ctx.patchState({ + partOfCollection: { + filterName: FilterLabelsModel.partOfCollection, + label: action.partOfCollection, + value: action.id, + }, + }); + } +} diff --git a/src/app/features/my-profile/components/resources/my-profile-resources.component.html b/src/app/features/my-profile/components/resources/my-profile-resources.component.html new file mode 100644 index 000000000..2d705ec17 --- /dev/null +++ b/src/app/features/my-profile/components/resources/my-profile-resources.component.html @@ -0,0 +1,149 @@ +
+
+ @if (isMobile()) { + + } + @if (searchCount() > 10000) { +

10 000+ results

+ } @else if (searchCount() > 0) { +

{{ searchCount() }} results

+ } @else { +

0 results

+ } +
+ +
+ @if (isWeb()) { +

Sort by:

+ + } @else { + @if (isAnyFilterOptions()) { + filter by + } + sort by + } +
+
+ +@if (isFiltersOpen()) { +
+ +
+} @else if (isSortingOpen()) { +
+ @for (option of sortTabOptions; track option.value) { +
+ {{ option.label }} +
+ } +
+} @else { + @if (isAnyFilterSelected()) { +
+ +
+ } + +
+ @if (isWeb() && isAnyFilterOptions()) { + + } + + + +
+ @if (items.length > 0) { + @for (item of items; track item.id) { + + } + +
+ @if (first() && prev()) { + + + + } + + + + + + + + +
+ } +
+
+
+
+} diff --git a/src/app/shared/components/resources/resources.component.scss b/src/app/features/my-profile/components/resources/my-profile-resources.component.scss similarity index 100% rename from src/app/shared/components/resources/resources.component.scss rename to src/app/features/my-profile/components/resources/my-profile-resources.component.scss diff --git a/src/app/features/my-profile/components/resources/my-profile-resources.component.spec.ts b/src/app/features/my-profile/components/resources/my-profile-resources.component.spec.ts new file mode 100644 index 000000000..424043086 --- /dev/null +++ b/src/app/features/my-profile/components/resources/my-profile-resources.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileResourcesComponent } from './my-profile-resources.component'; + +describe('MyProfileResourcesComponent', () => { + let component: MyProfileResourcesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyProfileResourcesComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyProfileResourcesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/my-profile/components/resources/my-profile-resources.component.ts b/src/app/features/my-profile/components/resources/my-profile-resources.component.ts new file mode 100644 index 000000000..1afd6d6ac --- /dev/null +++ b/src/app/features/my-profile/components/resources/my-profile-resources.component.ts @@ -0,0 +1,161 @@ +import { Store } from '@ngxs/store'; + +import { DataView } from 'primeng/dataview'; +import { Select } from 'primeng/select'; + +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; + +import { MyProfileFilterChipsComponent } from '@osf/features/my-profile/components/resources/components/filter-chips/my-profile-filter-chips.component'; +import { MyProfileResourceCardComponent } from '@osf/features/my-profile/components/resources/components/resource-card/my-profile-resource-card.component'; +import { MyProfileResourceFiltersOptionsSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/components/filters/store/my-profile-resource-filters-options.selectors'; +import { MyProfileResourceFiltersComponent } from '@osf/features/my-profile/components/resources/components/resource-filters/my-profile-resource-filters.component'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { GetResourcesByLink, SetResourceTab, SetSortBy } from '@osf/features/my-profile/store/my-profile.actions'; +import { MyProfileSelectors } from '@osf/features/my-profile/store/my-profile.selectors'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; +import { IS_WEB, IS_XSMALL } from '@shared/utils/breakpoints.tokens'; + +@Component({ + selector: 'osf-my-profile-resources', + imports: [ + DataView, + MyProfileFilterChipsComponent, + NgOptimizedImage, + MyProfileResourceCardComponent, + MyProfileResourceFiltersComponent, + Select, + FormsModule, + ], + templateUrl: './my-profile-resources.component.html', + styleUrl: './my-profile-resources.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyProfileResourcesComponent { + readonly #store = inject(Store); + + selectedTabStore = this.#store.selectSignal(MyProfileSelectors.getResourceTab); + searchCount = this.#store.selectSignal(MyProfileSelectors.getResourcesCount); + resources = this.#store.selectSignal(MyProfileSelectors.getResources); + sortBy = this.#store.selectSignal(MyProfileSelectors.getSortBy); + first = this.#store.selectSignal(MyProfileSelectors.getFirst); + next = this.#store.selectSignal(MyProfileSelectors.getNext); + prev = this.#store.selectSignal(MyProfileSelectors.getPrevious); + + isWeb = toSignal(inject(IS_WEB)); + + isFiltersOpen = signal(false); + isSortingOpen = signal(false); + + protected filters = this.#store.selectSignal(MyProfileResourceFiltersSelectors.getAllFilters); + protected filtersOptions = this.#store.selectSignal(MyProfileResourceFiltersOptionsSelectors.getAllOptions); + protected isAnyFilterSelected = computed(() => { + return ( + this.filters().creator.value || + this.filters().dateCreated.value || + this.filters().funder.value || + this.filters().subject.value || + this.filters().license.value || + this.filters().resourceType.value || + this.filters().institution.value || + this.filters().provider.value || + this.filters().partOfCollection.value + ); + }); + protected isAnyFilterOptions = computed(() => { + return ( + this.filtersOptions().datesCreated.length > 0 || + this.filtersOptions().creators.length > 0 || + this.filtersOptions().funders.length > 0 || + this.filtersOptions().subjects.length > 0 || + this.filtersOptions().licenses.length > 0 || + this.filtersOptions().resourceTypes.length > 0 || + this.filtersOptions().institutions.length > 0 || + this.filtersOptions().providers.length > 0 || + this.filtersOptions().partOfCollection.length > 0 + ); + }); + + protected readonly isMobile = toSignal(inject(IS_XSMALL)); + + protected selectedSort = signal(''); + protected readonly sortTabOptions = [ + { label: 'Relevance', value: '-relevance' }, + { label: 'Date created (newest)', value: '-dateCreated' }, + { label: 'Date created (oldest)', value: 'dateCreated' }, + { label: 'Date modified (newest)', value: '-dateModified' }, + { label: 'Date modified (oldest)', value: 'dateModified' }, + ]; + + protected selectedTab = signal(ResourceTab.All); + protected readonly tabsOptions = [ + { label: 'All', value: ResourceTab.All }, + { label: 'Projects', value: ResourceTab.Projects }, + { label: 'Registrations', value: ResourceTab.Registrations }, + { label: 'Preprints', value: ResourceTab.Preprints }, + { label: 'Files', value: ResourceTab.Files }, + { label: 'Users', value: ResourceTab.Users }, + ]; + + constructor() { + // if new value for sorting in store, update value in dropdown + effect(() => { + const storeValue = this.sortBy(); + const currentInput = untracked(() => this.selectedSort()); + + if (storeValue && currentInput !== storeValue) { + this.selectedSort.set(storeValue); + } + }); + + // if the sorting was changed, set new value to store + effect(() => { + const chosenValue = this.selectedSort(); + const storeValue = untracked(() => this.sortBy()); + + if (chosenValue !== storeValue) { + this.#store.dispatch(new SetSortBy(chosenValue)); + } + }); + + effect(() => { + const storeValue = this.selectedTabStore(); + const currentInput = untracked(() => this.selectedTab()); + + if (storeValue && currentInput !== storeValue) { + this.selectedTab.set(storeValue); + } + }); + + effect(() => { + const chosenValue = this.selectedTab(); + const storeValue = untracked(() => this.selectedTabStore()); + + if (chosenValue !== storeValue) { + this.#store.dispatch(new SetResourceTab(chosenValue)); + } + }); + } + + // pagination + switchPage(link: string) { + this.#store.dispatch(new GetResourcesByLink(link)); + } + + openFilters() { + this.isFiltersOpen.set(!this.isFiltersOpen()); + this.isSortingOpen.set(false); + } + + openSorting() { + this.isSortingOpen.set(!this.isSortingOpen()); + this.isFiltersOpen.set(false); + } + + selectSort(value: string) { + this.selectedSort.set(value); + this.openSorting(); + } +} diff --git a/src/app/features/my-profile/my-profile.component.html b/src/app/features/my-profile/my-profile.component.html index d949d67ea..1ae168688 100644 --- a/src/app/features/my-profile/my-profile.component.html +++ b/src/app/features/my-profile/my-profile.component.html @@ -165,4 +165,4 @@

Education

- + diff --git a/src/app/features/my-profile/my-profile.component.ts b/src/app/features/my-profile/my-profile.component.ts index 70b197eba..a8b541d79 100644 --- a/src/app/features/my-profile/my-profile.component.ts +++ b/src/app/features/my-profile/my-profile.component.ts @@ -4,26 +4,30 @@ import { AccordionModule } from 'primeng/accordion'; import { Button } from 'primeng/button'; import { DatePipe, NgOptimizedImage } from '@angular/common'; -import { ChangeDetectionStrategy, Component, effect, inject, OnDestroy, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, OnDestroy, signal } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { UserSelectors } from '@osf/core/store/user/user.selectors'; +import { MyProfileSearchComponent } from '@osf/features/my-profile/components/my-profile-search/my-profile-search.component'; +import { ResetFiltersState } from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.actions'; import { ResetSearchState, SetIsMyProfile } from '@osf/features/search/store'; -import { - ResetFiltersState, - SetCreator, -} from '@osf/shared/components/resources/resource-filters/store/resource-filters.actions'; import { IS_XSMALL } from '@osf/shared/utils/breakpoints.tokens'; - -import { ResourceTab } from '../search/models/resource-tab.enum'; -import { SearchComponent } from '../search/search.component'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; @Component({ selector: 'osf-my-profile', standalone: true, - imports: [Button, DatePipe, NgOptimizedImage, AccordionModule, FormsModule, ReactiveFormsModule, SearchComponent], + imports: [ + Button, + DatePipe, + NgOptimizedImage, + AccordionModule, + FormsModule, + ReactiveFormsModule, + MyProfileSearchComponent, + ], templateUrl: './my-profile.component.html', styleUrl: './my-profile.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -44,11 +48,11 @@ export class MyProfileComponent implements OnDestroy { } constructor() { - this.#store.dispatch(new SetIsMyProfile(true)); - - effect(() => { - this.#store.dispatch(new SetCreator(this.currentUser()?.fullName ?? '', this.currentUser()?.iri ?? '')); - }); + // this.#store.dispatch(new SetIsMyProfile(true)); + // + // effect(() => { + // this.#store.dispatch(new SetCreator(this.currentUser()?.fullName ?? '', this.currentUser()?.iri ?? '')); + // }); } ngOnDestroy(): void { diff --git a/src/app/features/my-profile/store/index.ts b/src/app/features/my-profile/store/index.ts new file mode 100644 index 000000000..98e372ac9 --- /dev/null +++ b/src/app/features/my-profile/store/index.ts @@ -0,0 +1,4 @@ +export * from './my-profile.actions'; +export * from './my-profile.model'; +export * from './my-profile.selectors'; +export * from './my-profile.state'; diff --git a/src/app/features/my-profile/store/my-profile.actions.ts b/src/app/features/my-profile/store/my-profile.actions.ts new file mode 100644 index 000000000..56a9b7df9 --- /dev/null +++ b/src/app/features/my-profile/store/my-profile.actions.ts @@ -0,0 +1,39 @@ +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; + +export class GetResources { + static readonly type = '[My Profile] Get Resources'; +} + +export class GetResourcesByLink { + static readonly type = '[My Profile] Get Resources By Link'; + + constructor(public link: string) {} +} + +export class GetResourcesCount { + static readonly type = '[My Profile] Get Resources Count'; +} + +export class SetSearchText { + static readonly type = '[My Profile] Set Search Text'; + + constructor(public searchText: string) {} +} + +export class SetSortBy { + static readonly type = '[My Profile] Set SortBy'; + + constructor(public sortBy: string) {} +} + +export class SetResourceTab { + static readonly type = '[My Profile] Set Resource Tab'; + + constructor(public resourceTab: ResourceTab) {} +} + +export class SetIsMyProfile { + static readonly type = '[My Profile] Set IsMyProfile'; + + constructor(public isMyProfile: boolean) {} +} diff --git a/src/app/features/my-profile/store/my-profile.model.ts b/src/app/features/my-profile/store/my-profile.model.ts new file mode 100644 index 000000000..038414d0f --- /dev/null +++ b/src/app/features/my-profile/store/my-profile.model.ts @@ -0,0 +1,14 @@ +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; + +export interface MyProfileStateModel { + resources: Resource[]; + resourcesCount: number; + searchText: string; + sortBy: string; + resourceTab: ResourceTab; + first: string; + next: string; + previous: string; + isMyProfile: boolean; +} diff --git a/src/app/features/my-profile/store/my-profile.selectors.ts b/src/app/features/my-profile/store/my-profile.selectors.ts new file mode 100644 index 000000000..ec37c5fdf --- /dev/null +++ b/src/app/features/my-profile/store/my-profile.selectors.ts @@ -0,0 +1,53 @@ +import { Selector } from '@ngxs/store'; + +import { MyProfileStateModel } from '@osf/features/my-profile/store/my-profile.model'; +import { MyProfileState } from '@osf/features/my-profile/store/my-profile.state'; +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; + +export class MyProfileSelectors { + @Selector([MyProfileState]) + static getResources(state: MyProfileStateModel): Resource[] { + return state.resources; + } + + @Selector([MyProfileState]) + static getResourcesCount(state: MyProfileStateModel): number { + return state.resourcesCount; + } + + @Selector([MyProfileState]) + static getSearchText(state: MyProfileStateModel): string { + return state.searchText; + } + + @Selector([MyProfileState]) + static getSortBy(state: MyProfileStateModel): string { + return state.sortBy; + } + + @Selector([MyProfileState]) + static getResourceTab(state: MyProfileStateModel): ResourceTab { + return state.resourceTab; + } + + @Selector([MyProfileState]) + static getFirst(state: MyProfileStateModel): string { + return state.first; + } + + @Selector([MyProfileState]) + static getNext(state: MyProfileStateModel): string { + return state.next; + } + + @Selector([MyProfileState]) + static getPrevious(state: MyProfileStateModel): string { + return state.previous; + } + + @Selector([MyProfileState]) + static getIsMyProfile(state: MyProfileStateModel): boolean { + return state.isMyProfile; + } +} diff --git a/src/app/features/my-profile/store/my-profile.state.ts b/src/app/features/my-profile/store/my-profile.state.ts new file mode 100644 index 000000000..9c3d9dd3f --- /dev/null +++ b/src/app/features/my-profile/store/my-profile.state.ts @@ -0,0 +1,90 @@ +import { Action, State, StateContext, Store } from '@ngxs/store'; + +import { tap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { UserSelectors } from '@core/store/user/user.selectors'; +import { MyProfileResourceFiltersSelectors } from '@osf/features/my-profile/components/resources/components/resource-filters/store/my-profile-resource-filters.selectors'; +import { + GetResources, + GetResourcesByLink, + MyProfileSelectors, + MyProfileStateModel, + SetIsMyProfile, + SetResourceTab, + SetSearchText, + SetSortBy, +} from '@osf/features/my-profile/store'; +import { searchStateDefaults } from '@osf/features/search/utils/data'; +import { SearchService } from '@shared/services/search.service'; +import { addFiltersParams } from '@shared/utils/add-filters-params.helper'; +import { getResourceTypes } from '@shared/utils/get-resource-types.helper'; + +@Injectable() +@State({ + name: 'myProfile', + defaults: searchStateDefaults, +}) +export class MyProfileState { + searchService = inject(SearchService); + store = inject(Store); + currentUser = this.store.selectSignal(UserSelectors.getCurrentUser); + + @Action(GetResources) + getResources(ctx: StateContext) { + const filters = this.store.selectSnapshot(MyProfileResourceFiltersSelectors.getAllFilters); + const filtersParams = addFiltersParams(filters); + const searchText = this.store.selectSnapshot(MyProfileSelectors.getSearchText); + const sortBy = this.store.selectSnapshot(MyProfileSelectors.getSortBy); + const resourceTab = this.store.selectSnapshot(MyProfileSelectors.getResourceTab); + const resourceTypes = getResourceTypes(resourceTab); + const iri = this.currentUser()?.iri; + if (iri) { + filtersParams['cardSearchFilter[creator][]'] = iri; + } + + return this.searchService.getResources(filtersParams, searchText, sortBy, resourceTypes).pipe( + tap((response) => { + ctx.patchState({ resources: response.resources }); + ctx.patchState({ resourcesCount: response.count }); + ctx.patchState({ first: response.first }); + ctx.patchState({ next: response.next }); + ctx.patchState({ previous: response.previous }); + }) + ); + } + + @Action(GetResourcesByLink) + getResourcesByLink(ctx: StateContext, action: GetResourcesByLink) { + return this.searchService.getResourcesByLink(action.link).pipe( + tap((response) => { + ctx.patchState({ resources: response.resources }); + ctx.patchState({ resourcesCount: response.count }); + ctx.patchState({ first: response.first }); + ctx.patchState({ next: response.next }); + ctx.patchState({ previous: response.previous }); + }) + ); + } + + @Action(SetSearchText) + setSearchText(ctx: StateContext, action: SetSearchText) { + ctx.patchState({ searchText: action.searchText }); + } + + @Action(SetSortBy) + setSortBy(ctx: StateContext, action: SetSortBy) { + ctx.patchState({ sortBy: action.sortBy }); + } + + @Action(SetResourceTab) + setResourceTab(ctx: StateContext, action: SetResourceTab) { + ctx.patchState({ resourceTab: action.resourceTab }); + } + + @Action(SetIsMyProfile) + setIsMyProfile(ctx: StateContext, action: SetIsMyProfile) { + ctx.patchState({ isMyProfile: action.isMyProfile }); + } +} diff --git a/src/app/features/my-profile/utils/data.ts b/src/app/features/my-profile/utils/data.ts new file mode 100644 index 000000000..b76ff86d5 --- /dev/null +++ b/src/app/features/my-profile/utils/data.ts @@ -0,0 +1,13 @@ +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; + +export const myProfileStateDefaults = { + resources: [], + resourcesCount: 0, + searchText: '', + sortBy: '-relevance', + resourceTab: ResourceTab.All, + first: '', + next: '', + previous: '', + isMyProfile: false, +}; diff --git a/src/app/shared/components/resources/filter-chips/filter-chips.component.html b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.html similarity index 100% rename from src/app/shared/components/resources/filter-chips/filter-chips.component.html rename to src/app/features/search/components/resources/components/filter-chips/filter-chips.component.html diff --git a/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.scss b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.scss new file mode 100644 index 000000000..97920b501 --- /dev/null +++ b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.scss @@ -0,0 +1,13 @@ +:host { + display: flex; + flex-direction: column; + gap: 0.4rem; + + @media (max-width: 1200px) { + flex-direction: row; + } + + @media (max-width: 600px) { + flex-direction: column; + } +} diff --git a/src/app/shared/components/resources/filter-chips/filter-chips.component.spec.ts b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/filter-chips/filter-chips.component.spec.ts rename to src/app/features/search/components/resources/components/filter-chips/filter-chips.component.spec.ts diff --git a/src/app/shared/components/resources/filter-chips/filter-chips.component.ts b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.ts similarity index 86% rename from src/app/shared/components/resources/filter-chips/filter-chips.component.ts rename to src/app/features/search/components/resources/components/filter-chips/filter-chips.component.ts index 30a8dfd42..3d1c0144c 100644 --- a/src/app/shared/components/resources/filter-chips/filter-chips.component.ts +++ b/src/app/features/search/components/resources/components/filter-chips/filter-chips.component.ts @@ -5,9 +5,10 @@ import { Chip } from 'primeng/chip'; import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; import { SearchSelectors } from '@osf/features/search/store'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { FilterType } from '@shared/components/resources/resource-filters/models/filter-type.enum'; +import { FilterType } from '@shared/entities/filters/filter-type.enum'; + import { ResourceFiltersSelectors, SetCreator, @@ -19,7 +20,7 @@ import { SetProvider, SetResourceType, SetSubject, -} from '@shared/components/resources/resource-filters/store'; +} from 'src/app/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-filter-chips', diff --git a/src/app/features/search/components/resources/components/resource-card/resource-card.component.html b/src/app/features/search/components/resources/components/resource-card/resource-card.component.html new file mode 100644 index 000000000..92174acce --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-card/resource-card.component.html @@ -0,0 +1,181 @@ +
+ + + +
+ @if (item()?.resourceType && item()?.resourceType === ResourceType.Agent) { +

User

+ } @else if (item()?.resourceType) { +

{{ ResourceType[item()?.resourceType!] }}

+ } + +
+ @if (item()?.resourceType === ResourceType.File && item()?.fileName) { + {{ item()?.fileName }} + } @else if (item()?.title && item()?.title) { + {{ item()?.title }} + } + @if (item()?.orcid) { + + orcid + + } +
+ + @if (item()?.creators?.length) { +
+ @for (creator of item()?.creators!.slice(0, 4); track creator.id; let i = $index) { + {{ creator.name }} + @if (i < (item()?.creators)!.length - 1 && i < 3) { + , + } + } + @if ((item()?.creators)!.length > 4) { +

 and {{ (item()?.creators)!.length - 4 }} more

+ } +
+ } + + @if (item()?.from?.id && item()?.from?.name) { + + } + + @if (item()?.dateCreated && item()?.dateModified) { +

+ @if (!isSmall()) { + Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }} | Date modified: + {{ item()?.dateModified | date: 'MMMM d, y' }} + } @else { +

+

Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }}

+

+ Date modified: + {{ item()?.dateModified | date: 'MMMM d, y' }} +

+
+ } +

+ } + + @if (item()?.resourceType === ResourceType.Registration) { + + } +
+
+ +
+ @if (item()?.description) { +

Description: {{ item()?.description }}

+ } + + @if (item()?.provider?.id) { + +

Registration provider: 

+ {{ item()?.provider?.name }} +
+ } + + @if (item()?.license?.id) { + +

License: 

+ {{ item()?.license?.name }} +
+ } + + @if (item()?.registrationTemplate) { +

Registration Template: {{ item()?.registrationTemplate }}

+ } + + @if (item()?.provider?.id) { + +

Provider: 

+ {{ item()?.provider?.name }} +
+ } + + @if (item()?.conflictOfInterestResponse && item()?.conflictOfInterestResponse === 'no-conflict-of-interest') { +

Conflict of Interest response: Author asserted no Conflict of Interest

+ } + + @if (item()?.resourceType !== ResourceType.Agent && item()?.id) { + +

URL: 

+ {{ item()?.id }} +
+ } + + @if (item()?.doi) { + +

DOI: 

+ {{ item()?.doi }} +
+ } + + @if (item()?.resourceType === ResourceType.Agent) { + @if (loading) { + + + + } @else { +

Public projects: {{ item()?.publicProjects ?? 0 }}

+

Public registrations: {{ item()?.publicRegistrations ?? 0 }}

+

Public preprints: {{ item()?.publicPreprints ?? 0 }}

+ } + } + + @if (item()?.employment) { +

Employment: {{ item()?.employment }}

+ } + + @if (item()?.education) { +

Education: {{ item()?.education }}

+ } +
+
+
+
+
diff --git a/src/app/features/search/components/resources/components/resource-card/resource-card.component.scss b/src/app/features/search/components/resources/components/resource-card/resource-card.component.scss new file mode 100644 index 000000000..5859dee06 --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-card/resource-card.component.scss @@ -0,0 +1,5 @@ +@use "/app/shared/styles/resource-card/resource-card.scss" as resource-card; + +//:host { +// @extend .resource; +//} diff --git a/src/app/shared/components/resources/resource-card/resource-card.component.spec.ts b/src/app/features/search/components/resources/components/resource-card/resource-card.component.spec.ts similarity index 92% rename from src/app/shared/components/resources/resource-card/resource-card.component.spec.ts rename to src/app/features/search/components/resources/components/resource-card/resource-card.component.spec.ts index 9490eaf44..fbe7f04b7 100644 --- a/src/app/shared/components/resources/resource-card/resource-card.component.spec.ts +++ b/src/app/features/search/components/resources/components/resource-card/resource-card.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ResourceCardComponent } from './resource-card.component'; -describe('ResourceCardComponent', () => { +describe('MyProfileResourceCardComponent', () => { let component: ResourceCardComponent; let fixture: ComponentFixture; diff --git a/src/app/shared/components/resources/resource-card/resource-card.component.ts b/src/app/features/search/components/resources/components/resource-card/resource-card.component.ts similarity index 83% rename from src/app/shared/components/resources/resource-card/resource-card.component.ts rename to src/app/features/search/components/resources/components/resource-card/resource-card.component.ts index 009c158f6..f52184319 100644 --- a/src/app/shared/components/resources/resource-card/resource-card.component.ts +++ b/src/app/features/search/components/resources/components/resource-card/resource-card.component.ts @@ -7,10 +7,10 @@ import { DatePipe, NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { Resource } from '@osf/features/search/models/resource.entity'; -import { ResourceType } from '@osf/features/search/models/resource-type.enum'; -import { IS_XSMALL } from '@osf/shared/utils/breakpoints.tokens'; -import { ResourceCardService } from '@shared/components/resources/resource-card/resource-card.service'; +import { ResourceCardService } from '@osf/features/search/components/resources/components/resource-card/services/resource-card.service'; +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceType } from '@shared/entities/resource-card/resource-type.enum'; +import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; @Component({ selector: 'osf-resource-card', diff --git a/src/app/shared/components/resources/resource-card/resource-card.service.ts b/src/app/features/search/components/resources/components/resource-card/services/resource-card.service.ts similarity index 61% rename from src/app/shared/components/resources/resource-card/resource-card.service.ts rename to src/app/features/search/components/resources/components/resource-card/services/resource-card.service.ts index 02c5ca5d0..ea786529b 100644 --- a/src/app/shared/components/resources/resource-card/resource-card.service.ts +++ b/src/app/features/search/components/resources/components/resource-card/services/resource-card.service.ts @@ -3,11 +3,11 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { JsonApiService } from '@core/services/json-api/json-api.service'; -import { MapUserCounts } from '@shared/components/resources/resource-card/mappers/user-counts.mapper'; -import { UserCountsResponse } from '@shared/components/resources/resource-card/models/user-counts-response.entity'; -import { UserRelatedDataCounts } from '@shared/components/resources/resource-card/models/user-related-data-counts.entity'; +import { UserCountsResponse } from '@shared/entities/resource-card/user-counts-response.entity'; +import { UserRelatedDataCounts } from '@shared/entities/resource-card/user-related-data-counts.entity'; +import { MapUserCounts } from '@shared/helpers/mappers/resource-card/user-counts.mapper'; -import { environment } from '../../../../../environments/environment'; +import { environment } from '../../../../../../../../environments/environment'; @Injectable({ providedIn: 'root', diff --git a/src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.html rename to src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.html diff --git a/src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.scss rename to src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.scss diff --git a/src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.ts similarity index 85% rename from src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.ts index b9305ecef..701b5307d 100644 --- a/src/app/shared/components/resources/resource-filters/filters/creators/creators-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component.ts @@ -20,9 +20,12 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { GetAllOptions, GetCreatorsOptions, -} from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetCreator } from '@shared/components/resources/resource-filters/store'; +} from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetCreator, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-creators-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.html new file mode 100644 index 000000000..b6188ece4 --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.html @@ -0,0 +1,13 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.ts similarity index 77% rename from src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.ts index 3b0b094c0..47216d48c 100644 --- a/src/app/shared/components/resources/resource-filters/filters/date-created/date-created-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetDateCreated } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetDateCreated, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-date-created-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.html new file mode 100644 index 000000000..f2bba5e1f --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.ts similarity index 82% rename from src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.ts index 74645e052..7cbaf8f4f 100644 --- a/src/app/shared/components/resources/resource-filters/filters/funder/funder-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetFunder } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetFunder, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-funder-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.html new file mode 100644 index 000000000..0f59afc86 --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.scss similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.scss rename to src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.scss diff --git a/src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.ts similarity index 83% rename from src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.ts index 028067bf6..2611d1a58 100644 --- a/src/app/shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetInstitution } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetInstitution, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-institution-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.html new file mode 100644 index 000000000..ce0d34b4d --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.ts similarity index 82% rename from src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.ts index a6eeb7d0c..002dc831e 100644 --- a/src/app/shared/components/resources/resource-filters/filters/license-filter/license-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetLicense } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetLicense, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-license-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.html new file mode 100644 index 000000000..cd1ea9e8c --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.html @@ -0,0 +1,16 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.ts similarity index 81% rename from src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.ts index 1af25a598..0ac785fb7 100644 --- a/src/app/shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetPartOfCollection } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetPartOfCollection, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-part-of-collection-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.html new file mode 100644 index 000000000..3d299cdde --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.ts similarity index 82% rename from src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.ts index 81d2c15df..3278b0790 100644 --- a/src/app/shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetProvider } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetProvider, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-provider-filter', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.html new file mode 100644 index 000000000..d001451ad --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.ts similarity index 83% rename from src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.ts index 2c914a26e..cb9595569 100644 --- a/src/app/shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetResourceType } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetResourceType, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-resource-type-filter', diff --git a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.actions.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.actions.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions.ts diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model.ts new file mode 100644 index 000000000..eca6adc2d --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model.ts @@ -0,0 +1,21 @@ +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { InstitutionFilter } from '@shared/entities/filters/institution/institution-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; + +export interface ResourceFiltersOptionsStateModel { + creators: Creator[]; + datesCreated: DateCreated[]; + funders: FunderFilter[]; + subjects: SubjectFilter[]; + licenses: LicenseFilter[]; + resourceTypes: ResourceTypeFilter[]; + institutions: InstitutionFilter[]; + providers: ProviderFilter[]; + partOfCollection: PartOfCollectionFilter[]; +} diff --git a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors.ts similarity index 55% rename from src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors.ts index 3165f0140..bf83d1f01 100644 --- a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors.ts @@ -1,16 +1,16 @@ import { Selector } from '@ngxs/store'; -import { ResourceFiltersOptionsStateModel } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.model'; -import { ResourceFiltersOptionsState } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.state'; -import { Creator } from '@shared/components/resources/resource-filters/models/creator/creator.entity'; -import { DateCreated } from '@shared/components/resources/resource-filters/models/dateCreated/date-created.entity'; -import { FunderFilter } from '@shared/components/resources/resource-filters/models/funder/funder-filter.entity'; -import { InstitutionFilter } from '@shared/components/resources/resource-filters/models/institution/institution-filter.entity'; -import { LicenseFilter } from '@shared/components/resources/resource-filters/models/license/license-filter.entity'; -import { PartOfCollectionFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity'; -import { ProviderFilter } from '@shared/components/resources/resource-filters/models/provider/provider-filter.entity'; -import { ResourceTypeFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type.entity'; -import { SubjectFilter } from '@shared/components/resources/resource-filters/models/subject/subject-filter.entity'; +import { ResourceFiltersOptionsStateModel } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model'; +import { ResourceFiltersOptionsState } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.state'; +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { InstitutionFilter } from '@shared/entities/filters/institution/institution-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; export class ResourceFiltersOptionsSelectors { @Selector([ResourceFiltersOptionsState]) diff --git a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.state.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.state.ts similarity index 89% rename from src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.state.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.state.ts index 2329c5709..2a4967d6d 100644 --- a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.state.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.state.ts @@ -15,9 +15,9 @@ import { GetProvidersOptions, GetResourceTypesOptions, GetSubjectsOptions, -} from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsStateModel } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.model'; -import { ResourceFiltersService } from '@shared/components/resources/resource-filters/resource-filters.service'; +} from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsStateModel } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.model'; +import { ResourceFiltersService } from '@osf/features/search/components/resources/components/resource-filters/services/resource-filters.service'; @State({ name: 'resourceFiltersOptions', diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.html b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.html new file mode 100644 index 000000000..a33f1f7af --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.scss b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.spec.ts diff --git a/src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.ts b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.ts similarity index 82% rename from src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.ts rename to src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.ts index 1ecb03f2a..6214fb9a2 100644 --- a/src/app/shared/components/resources/resource-filters/filters/subject/subject-filter.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component.ts @@ -5,9 +5,12 @@ import { Select, SelectChangeEvent } from 'primeng/select'; import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { ResourceFiltersSelectors, SetSubject } from '@shared/components/resources/resource-filters/store'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { + ResourceFiltersSelectors, + SetSubject, +} from '@osf/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-subject-filter', diff --git a/src/app/shared/components/resources/resource-filters/resource-filters.component.html b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.html similarity index 100% rename from src/app/shared/components/resources/resource-filters/resource-filters.component.html rename to src/app/features/search/components/resources/components/resource-filters/resource-filters.component.html diff --git a/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.scss b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.scss new file mode 100644 index 000000000..1ab98caa2 --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.scss @@ -0,0 +1,15 @@ +@use "../../../../../../../assets/styles/variables" as var; + +:host { + width: 30%; + + .filters { + border: 1px solid var.$grey-2; + border-radius: 12px; + padding: 0 1.7rem 0 1.7rem; + display: flex; + flex-direction: column; + row-gap: 0.8rem; + height: fit-content; + } +} diff --git a/src/app/shared/components/resources/resource-filters/resource-filters.component.spec.ts b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.spec.ts similarity index 91% rename from src/app/shared/components/resources/resource-filters/resource-filters.component.spec.ts rename to src/app/features/search/components/resources/components/resource-filters/resource-filters.component.spec.ts index a6959486d..d692eef9a 100644 --- a/src/app/shared/components/resources/resource-filters/resource-filters.component.spec.ts +++ b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ResourceFiltersComponent } from './resource-filters.component'; -describe('ResourceFiltersComponent', () => { +describe('MyProfileResourceFiltersComponent', () => { let component: ResourceFiltersComponent; let fixture: ComponentFixture; diff --git a/src/app/shared/components/resources/resource-filters/resource-filters.component.ts b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.ts similarity index 64% rename from src/app/shared/components/resources/resource-filters/resource-filters.component.ts rename to src/app/features/search/components/resources/components/resource-filters/resource-filters.component.ts index ed56a3991..c27fc65fb 100644 --- a/src/app/shared/components/resources/resource-filters/resource-filters.component.ts +++ b/src/app/features/search/components/resources/components/resource-filters/resource-filters.component.ts @@ -6,17 +6,17 @@ import { AutoCompleteModule } from 'primeng/autocomplete'; import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; +import { CreatorsFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/creators/creators-filter.component'; +import { DateCreatedFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/date-created/date-created-filter.component'; +import { FunderFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/funder/funder-filter.component'; +import { InstitutionFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/institution-filter/institution-filter.component'; +import { LicenseFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/license-filter/license-filter.component'; +import { PartOfCollectionFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/part-of-collection-filter/part-of-collection-filter.component'; +import { ProviderFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/provider-filter/provider-filter.component'; +import { ResourceTypeFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/resource-type-filter/resource-type-filter.component'; +import { ResourceFiltersOptionsSelectors } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.selectors'; +import { SubjectFilterComponent } from '@osf/features/search/components/resources/components/resource-filters/components/filters/subject/subject-filter.component'; import { SearchSelectors } from '@osf/features/search/store'; -import { CreatorsFilterComponent } from '@shared/components/resources/resource-filters/filters/creators/creators-filter.component'; -import { DateCreatedFilterComponent } from '@shared/components/resources/resource-filters/filters/date-created/date-created-filter.component'; -import { FunderFilterComponent } from '@shared/components/resources/resource-filters/filters/funder/funder-filter.component'; -import { InstitutionFilterComponent } from '@shared/components/resources/resource-filters/filters/institution-filter/institution-filter.component'; -import { LicenseFilterComponent } from '@shared/components/resources/resource-filters/filters/license-filter/license-filter.component'; -import { PartOfCollectionFilterComponent } from '@shared/components/resources/resource-filters/filters/part-of-collection-filter/part-of-collection-filter.component'; -import { ProviderFilterComponent } from '@shared/components/resources/resource-filters/filters/provider-filter/provider-filter.component'; -import { ResourceTypeFilterComponent } from '@shared/components/resources/resource-filters/filters/resource-type-filter/resource-type-filter.component'; -import { ResourceFiltersOptionsSelectors } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.selectors'; -import { SubjectFilterComponent } from '@shared/components/resources/resource-filters/filters/subject/subject-filter.component'; @Component({ selector: 'osf-resource-filters', diff --git a/src/app/features/search/components/resources/components/resource-filters/services/resource-filters.service.ts b/src/app/features/search/components/resources/components/resource-filters/services/resource-filters.service.ts new file mode 100644 index 000000000..46d68b119 --- /dev/null +++ b/src/app/features/search/components/resources/components/resource-filters/services/resource-filters.service.ts @@ -0,0 +1,83 @@ +import { Store } from '@ngxs/store'; + +import { Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { SearchSelectors } from '@osf/features/search/store/search.selectors'; +import { FiltersOptionsService } from '@osf/shared/services/filters-options.service'; +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; +import { addFiltersParams } from '@shared/utils/add-filters-params.helper'; +import { getResourceTypes } from '@shared/utils/get-resource-types.helper'; + +import { ResourceFiltersSelectors } from '../store/resource-filters.selectors'; + +@Injectable({ + providedIn: 'root', +}) +export class ResourceFiltersService { + #store = inject(Store); + #filtersOptions = inject(FiltersOptionsService); + + #getFilterParams(): Record { + return addFiltersParams(this.#store.selectSignal(ResourceFiltersSelectors.getAllFilters)()); + } + + #getParams(): Record { + const params: Record = {}; + const resourceTab = this.#store.selectSnapshot(SearchSelectors.getResourceTab); + const resourceTypes = getResourceTypes(resourceTab); + const searchText = this.#store.selectSnapshot(SearchSelectors.getSearchText); + const sort = this.#store.selectSnapshot(SearchSelectors.getSortBy); + + params['cardSearchFilter[resourceType]'] = resourceTypes; + params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; + params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; + params['page[size]'] = '10'; + params['sort'] = sort; + return params; + } + + getCreators(valueSearchText: string): Observable { + return this.#filtersOptions.getCreators(valueSearchText, this.#getParams(), this.#getFilterParams()); + } + + getDates(): Observable { + return this.#filtersOptions.getDates(this.#getParams(), this.#getFilterParams()); + } + + getFunders(): Observable { + return this.#filtersOptions.getFunders(this.#getParams(), this.#getFilterParams()); + } + + getSubjects(): Observable { + return this.#filtersOptions.getSubjects(this.#getParams(), this.#getFilterParams()); + } + + getLicenses(): Observable { + return this.#filtersOptions.getLicenses(this.#getParams(), this.#getFilterParams()); + } + + getResourceTypes(): Observable { + return this.#filtersOptions.getResourceTypes(this.#getParams(), this.#getFilterParams()); + } + + getInstitutions(): Observable { + return this.#filtersOptions.getInstitutions(this.#getParams(), this.#getFilterParams()); + } + + getProviders(): Observable { + return this.#filtersOptions.getProviders(this.#getParams(), this.#getFilterParams()); + } + + getPartOtCollections(): Observable { + return this.#filtersOptions.getPartOtCollections(this.#getParams(), this.#getFilterParams()); + } +} diff --git a/src/app/shared/components/resources/resource-filters/store/index.ts b/src/app/features/search/components/resources/components/resource-filters/store/index.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/store/index.ts rename to src/app/features/search/components/resources/components/resource-filters/store/index.ts diff --git a/src/app/shared/components/resources/resource-filters/store/resource-filters.actions.ts b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.actions.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/store/resource-filters.actions.ts rename to src/app/features/search/components/resources/components/resource-filters/store/resource-filters.actions.ts diff --git a/src/app/shared/components/resources/resource-filters/store/resource-filters.model.ts b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.model.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/store/resource-filters.model.ts rename to src/app/features/search/components/resources/components/resource-filters/store/resource-filters.model.ts diff --git a/src/app/shared/components/resources/resource-filters/store/resource-filters.selectors.ts b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.selectors.ts similarity index 86% rename from src/app/shared/components/resources/resource-filters/store/resource-filters.selectors.ts rename to src/app/features/search/components/resources/components/resource-filters/store/resource-filters.selectors.ts index 69d1e5213..605c41531 100644 --- a/src/app/shared/components/resources/resource-filters/store/resource-filters.selectors.ts +++ b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.selectors.ts @@ -3,8 +3,8 @@ import { Selector } from '@ngxs/store'; import { ResourceFilterLabel, ResourceFiltersStateModel, -} from '@shared/components/resources/resource-filters/store/resource-filters.model'; -import { ResourceFiltersState } from '@shared/components/resources/resource-filters/store/resource-filters.state'; +} from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.model'; +import { ResourceFiltersState } from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.state'; export class ResourceFiltersSelectors { @Selector([ResourceFiltersState]) diff --git a/src/app/shared/components/resources/resource-filters/store/resource-filters.state.ts b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.state.ts similarity index 73% rename from src/app/shared/components/resources/resource-filters/store/resource-filters.state.ts rename to src/app/features/search/components/resources/components/resource-filters/store/resource-filters.state.ts index b81694382..1d7375171 100644 --- a/src/app/shared/components/resources/resource-filters/store/resource-filters.state.ts +++ b/src/app/features/search/components/resources/components/resource-filters/store/resource-filters.state.ts @@ -2,7 +2,6 @@ import { Action, State, StateContext } from '@ngxs/store'; import { Injectable } from '@angular/core'; -import { FilterLabels } from '@shared/components/resources/resource-filters/models/filter-labels'; import { ResetFiltersState, SetCreator, @@ -14,14 +13,15 @@ import { SetProvider, SetResourceType, SetSubject, -} from '@shared/components/resources/resource-filters/store/resource-filters.actions'; -import { ResourceFiltersStateModel } from '@shared/components/resources/resource-filters/store/resource-filters.model'; -import { resourceFiltersDefaults } from '@shared/components/resources/resource-filters/utils/data'; +} from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.actions'; +import { ResourceFiltersStateModel } from '@osf/features/search/components/resources/components/resource-filters/store/resource-filters.model'; +import { FilterLabelsModel } from '@shared/utils/filter-labels.model'; +import { resourceFiltersDefaultsModel } from '@shared/utils/resource-filters-defaults.model'; // Store for user selected filters values @State({ name: 'resourceFilters', - defaults: resourceFiltersDefaults, + defaults: resourceFiltersDefaultsModel, }) @Injectable() export class ResourceFiltersState { @@ -29,7 +29,7 @@ export class ResourceFiltersState { setCreator(ctx: StateContext, action: SetCreator) { ctx.patchState({ creator: { - filterName: FilterLabels.creator, + filterName: FilterLabelsModel.creator, label: action.name, value: action.id, }, @@ -40,7 +40,7 @@ export class ResourceFiltersState { setDateCreated(ctx: StateContext, action: SetDateCreated) { ctx.patchState({ dateCreated: { - filterName: FilterLabels.dateCreated, + filterName: FilterLabelsModel.dateCreated, label: action.date, value: action.date, }, @@ -51,7 +51,7 @@ export class ResourceFiltersState { setFunder(ctx: StateContext, action: SetFunder) { ctx.patchState({ funder: { - filterName: FilterLabels.funder, + filterName: FilterLabelsModel.funder, label: action.funder, value: action.id, }, @@ -62,7 +62,7 @@ export class ResourceFiltersState { setSubject(ctx: StateContext, action: SetSubject) { ctx.patchState({ subject: { - filterName: FilterLabels.subject, + filterName: FilterLabelsModel.subject, label: action.subject, value: action.id, }, @@ -73,7 +73,7 @@ export class ResourceFiltersState { setLicense(ctx: StateContext, action: SetLicense) { ctx.patchState({ license: { - filterName: FilterLabels.license, + filterName: FilterLabelsModel.license, label: action.license, value: action.id, }, @@ -84,7 +84,7 @@ export class ResourceFiltersState { setResourceType(ctx: StateContext, action: SetResourceType) { ctx.patchState({ resourceType: { - filterName: FilterLabels.resourceType, + filterName: FilterLabelsModel.resourceType, label: action.resourceType, value: action.id, }, @@ -95,7 +95,7 @@ export class ResourceFiltersState { setInstitution(ctx: StateContext, action: SetInstitution) { ctx.patchState({ institution: { - filterName: FilterLabels.institution, + filterName: FilterLabelsModel.institution, label: action.institution, value: action.id, }, @@ -106,7 +106,7 @@ export class ResourceFiltersState { setProvider(ctx: StateContext, action: SetProvider) { ctx.patchState({ provider: { - filterName: FilterLabels.provider, + filterName: FilterLabelsModel.provider, label: action.provider, value: action.id, }, @@ -117,7 +117,7 @@ export class ResourceFiltersState { setPartOfCollection(ctx: StateContext, action: SetPartOfCollection) { ctx.patchState({ partOfCollection: { - filterName: FilterLabels.partOfCollection, + filterName: FilterLabelsModel.partOfCollection, label: action.partOfCollection, value: action.id, }, @@ -126,6 +126,6 @@ export class ResourceFiltersState { @Action(ResetFiltersState) resetState(ctx: StateContext) { - ctx.patchState(resourceFiltersDefaults); + ctx.patchState(resourceFiltersDefaultsModel); } } diff --git a/src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.html b/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.html similarity index 100% rename from src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.html rename to src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.html diff --git a/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.scss b/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.spec.ts b/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.spec.ts similarity index 100% rename from src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.spec.ts rename to src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.spec.ts diff --git a/src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.ts b/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.ts similarity index 91% rename from src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.ts rename to src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.ts index 26175b31b..46632c8b5 100644 --- a/src/app/shared/components/resources/resources-wrapper/resources-wrapper.component.ts +++ b/src/app/features/search/components/resources/components/resources-wrapper/resources-wrapper.component.ts @@ -5,10 +5,12 @@ import { take } from 'rxjs'; import { ChangeDetectionStrategy, Component, effect, inject, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourcesComponent } from '@osf/features/search/components/resources/resources.component'; import { SearchSelectors, SetResourceTab, SetSearchText, SetSortBy } from '@osf/features/search/store'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { FilterLabels } from '@shared/components/resources/resource-filters/models/filter-labels'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; +import { FilterLabelsModel } from '@shared/utils/filter-labels.model'; + import { ResourceFilterLabel, ResourceFiltersSelectors, @@ -21,8 +23,7 @@ import { SetProvider, SetResourceType, SetSubject, -} from '@shared/components/resources/resource-filters/store'; -import { ResourcesComponent } from '@shared/components/resources/resources.component'; +} from 'src/app/features/search/components/resources/components/resource-filters/store'; @Component({ selector: 'osf-resources-wrapper', @@ -75,14 +76,14 @@ export class ResourcesWrapperComponent implements OnInit { const search = params.get('search'); const resourceTab = params.get('resourceTab'); - const creator = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.creator); + const creator = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.creator); const dateCreated = filters.find((p: ResourceFilterLabel) => p.filterName === 'DateCreated'); - const funder = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.funder); - const subject = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.subject); - const license = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.license); + const funder = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.funder); + const subject = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.subject); + const license = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.license); const resourceType = filters.find((p: ResourceFilterLabel) => p.filterName === 'ResourceType'); - const institution = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.institution); - const provider = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabels.provider); + const institution = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.institution); + const provider = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.provider); const partOfCollection = filters.find((p: ResourceFilterLabel) => p.filterName === 'PartOfCollection'); if (creator) { diff --git a/src/app/shared/components/resources/resources.component.html b/src/app/features/search/components/resources/resources.component.html similarity index 97% rename from src/app/shared/components/resources/resources.component.html rename to src/app/features/search/components/resources/resources.component.html index 283248fa8..77eac6339 100644 --- a/src/app/shared/components/resources/resources.component.html +++ b/src/app/features/search/components/resources/resources.component.html @@ -24,7 +24,7 @@

Sort by:

} @else { @if (isAnyFilterOptions()) { filter bySort by:

/> } sort by { let component: SearchComponent; diff --git a/src/app/features/search/search.component.ts b/src/app/features/search/search.component.ts index 4360d2e3c..5fb9488bc 100644 --- a/src/app/features/search/search.component.ts +++ b/src/app/features/search/search.component.ts @@ -15,7 +15,8 @@ import { ChangeDetectionStrategy, Component, effect, inject, OnDestroy, signal, import { toObservable, toSignal } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { GetAllOptions } from '@osf/features/search/components/resources/components/resource-filters/components/filters/store/resource-filters-options.actions'; +import { ResourcesWrapperComponent } from '@osf/features/search/components/resources/components/resources-wrapper/resources-wrapper.component'; import { GetResources, ResetSearchState, @@ -23,12 +24,15 @@ import { SetResourceTab, SetSearchText, } from '@osf/features/search/store'; -import { GetAllOptions } from '@shared/components/resources/resource-filters/filters/store/resource-filters-options.actions'; -import { ResetFiltersState, ResourceFiltersSelectors } from '@shared/components/resources/resource-filters/store'; -import { ResourcesWrapperComponent } from '@shared/components/resources/resources-wrapper/resources-wrapper.component'; import { SearchInputComponent } from '@shared/components/search-input/search-input.component'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; import { IS_XSMALL } from '@shared/utils/breakpoints.tokens'; +import { + ResetFiltersState, + ResourceFiltersSelectors, +} from 'src/app/features/search/components/resources/components/resource-filters/store'; + @Component({ selector: 'osf-search', imports: [ diff --git a/src/app/features/search/store/search.actions.ts b/src/app/features/search/store/search.actions.ts index f04dc736a..578c49838 100644 --- a/src/app/features/search/store/search.actions.ts +++ b/src/app/features/search/store/search.actions.ts @@ -1,4 +1,4 @@ -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; export class GetResources { static readonly type = '[Search] Get Resources'; diff --git a/src/app/features/search/store/search.model.ts b/src/app/features/search/store/search.model.ts index 7155f2c63..5b37f54ca 100644 --- a/src/app/features/search/store/search.model.ts +++ b/src/app/features/search/store/search.model.ts @@ -1,5 +1,5 @@ -import { Resource } from '@osf/features/search/models/resource.entity'; -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; export interface SearchStateModel { resources: Resource[]; diff --git a/src/app/features/search/store/search.selectors.ts b/src/app/features/search/store/search.selectors.ts index f87cfa12f..16b2085a6 100644 --- a/src/app/features/search/store/search.selectors.ts +++ b/src/app/features/search/store/search.selectors.ts @@ -1,9 +1,9 @@ import { Selector } from '@ngxs/store'; -import { Resource } from '@osf/features/search/models/resource.entity'; -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; import { SearchStateModel } from '@osf/features/search/store/search.model'; import { SearchState } from '@osf/features/search/store/search.state'; +import { Resource } from '@shared/entities/resource-card/resource.entity'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; export class SearchSelectors { @Selector([SearchState]) diff --git a/src/app/features/search/store/search.state.ts b/src/app/features/search/store/search.state.ts index c1663c094..6e468ee40 100644 --- a/src/app/features/search/store/search.state.ts +++ b/src/app/features/search/store/search.state.ts @@ -4,7 +4,6 @@ import { tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { SearchService } from '@osf/features/search/search.service'; import { GetResources, GetResourcesByLink, @@ -17,9 +16,11 @@ import { import { SearchStateModel } from '@osf/features/search/store/search.model'; import { SearchSelectors } from '@osf/features/search/store/search.selectors'; import { searchStateDefaults } from '@osf/features/search/utils/data'; -import { getResourceTypes } from '@osf/features/search/utils/helpers/get-resource-types.helper'; -import { ResourceFiltersSelectors } from '@shared/components/resources/resource-filters/store'; -import { addFiltersParams } from '@shared/components/resources/resource-filters/utils/add-filters-params.helper'; +import { SearchService } from '@shared/services/search.service'; +import { addFiltersParams } from '@shared/utils/add-filters-params.helper'; +import { getResourceTypes } from '@shared/utils/get-resource-types.helper'; + +import { ResourceFiltersSelectors } from 'src/app/features/search/components/resources/components/resource-filters/store'; @Injectable() @State({ diff --git a/src/app/features/search/utils/data.ts b/src/app/features/search/utils/data.ts index aaade4442..f030bdeb9 100644 --- a/src/app/features/search/utils/data.ts +++ b/src/app/features/search/utils/data.ts @@ -1,4 +1,4 @@ -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; export const searchStateDefaults = { resources: [], diff --git a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.model.ts b/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.model.ts deleted file mode 100644 index 428e66b94..000000000 --- a/src/app/shared/components/resources/resource-filters/filters/store/resource-filters-options.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Creator } from '@shared/components/resources/resource-filters/models/creator/creator.entity'; -import { DateCreated } from '@shared/components/resources/resource-filters/models/dateCreated/date-created.entity'; -import { FunderFilter } from '@shared/components/resources/resource-filters/models/funder/funder-filter.entity'; -import { InstitutionFilter } from '@shared/components/resources/resource-filters/models/institution/institution-filter.entity'; -import { LicenseFilter } from '@shared/components/resources/resource-filters/models/license/license-filter.entity'; -import { PartOfCollectionFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity'; -import { ProviderFilter } from '@shared/components/resources/resource-filters/models/provider/provider-filter.entity'; -import { ResourceTypeFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type.entity'; -import { SubjectFilter } from '@shared/components/resources/resource-filters/models/subject/subject-filter.entity'; - -export interface ResourceFiltersOptionsStateModel { - creators: Creator[]; - datesCreated: DateCreated[]; - funders: FunderFilter[]; - subjects: SubjectFilter[]; - licenses: LicenseFilter[]; - resourceTypes: ResourceTypeFilter[]; - institutions: InstitutionFilter[]; - providers: ProviderFilter[]; - partOfCollection: PartOfCollectionFilter[]; -} diff --git a/src/app/shared/components/resources/resource-filters/mappers/creators/creators.mappers.ts b/src/app/shared/components/resources/resource-filters/mappers/creators/creators.mappers.ts deleted file mode 100644 index 2615cc553..000000000 --- a/src/app/shared/components/resources/resource-filters/mappers/creators/creators.mappers.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Creator } from '@shared/components/resources/resource-filters/models/creator/creator.entity'; -import { CreatorItem } from '@shared/components/resources/resource-filters/models/creator/creator-item.entity'; - -export function MapCreators(rawItem: CreatorItem): Creator { - return { - id: rawItem?.['@id'], - name: rawItem?.name?.[0]?.['@value'], - }; -} diff --git a/src/app/shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity.ts deleted file mode 100644 index db9e4d4f3..000000000 --- a/src/app/shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { FunderIndexCardFilter } from '@shared/components/resources/resource-filters/models/funder/funder-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type FunderIndexValueSearch = SearchResultCount | FunderIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/index-value-search.entity.ts deleted file mode 100644 index c09f5bbd3..000000000 --- a/src/app/shared/components/resources/resource-filters/models/index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { IndexCardFilter } from '@shared/components/resources/resource-filters/models/index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type IndexValueSearch = SearchResultCount | IndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity.ts deleted file mode 100644 index 75f4649f8..000000000 --- a/src/app/shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { InstitutionIndexCardFilter } from '@shared/components/resources/resource-filters/models/institution/institution-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type InstitutionIndexValueSearch = SearchResultCount | InstitutionIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/license/license-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/license/license-index-value-search.entity.ts deleted file mode 100644 index 2f2275125..000000000 --- a/src/app/shared/components/resources/resource-filters/models/license/license-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { LicenseIndexCardFilter } from '@shared/components/resources/resource-filters/models/license/license-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type LicenseIndexValueSearch = SearchResultCount | LicenseIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity.ts deleted file mode 100644 index 18add9ae1..000000000 --- a/src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartOfCollectionIndexCardFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type PartOfCollectionIndexValueSearch = SearchResultCount | PartOfCollectionIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity.ts deleted file mode 100644 index 0a20d358d..000000000 --- a/src/app/shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ProviderIndexCardFilter } from '@shared/components/resources/resource-filters/models/provider/provider-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type ProviderIndexValueSearch = SearchResultCount | ProviderIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity.ts b/src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity.ts deleted file mode 100644 index bc3dc6d4f..000000000 --- a/src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ResourceTypeIndexCardFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type-index-card-filter.entity'; -import { SearchResultCount } from '@shared/components/resources/resource-filters/models/search-result-count.entity'; - -export type ResourceTypeIndexValueSearch = SearchResultCount | ResourceTypeIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/resource-filters.service.ts b/src/app/shared/components/resources/resource-filters/resource-filters.service.ts deleted file mode 100644 index 1582b78ac..000000000 --- a/src/app/shared/components/resources/resource-filters/resource-filters.service.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { Store } from '@ngxs/store'; - -import { map, Observable } from 'rxjs'; - -import { inject, Injectable } from '@angular/core'; - -import { ApiData, JsonApiResponse } from '@core/services/json-api/json-api.entity'; -import { JsonApiService } from '@core/services/json-api/json-api.service'; -import { SearchSelectors } from '@osf/features/search/store'; -import { getResourceTypes } from '@osf/features/search/utils/helpers/get-resource-types.helper'; -import { MapCreators } from '@shared/components/resources/resource-filters/mappers/creators/creators.mappers'; -import { MapDateCreated } from '@shared/components/resources/resource-filters/mappers/dateCreated/date-created.mapper'; -import { MapFunders } from '@shared/components/resources/resource-filters/mappers/funder/funder.mapper'; -import { MapInstitutions } from '@shared/components/resources/resource-filters/mappers/institution/institution.mapper'; -import { MapLicenses } from '@shared/components/resources/resource-filters/mappers/license/license.mapper'; -import { MapPartOfCollections } from '@shared/components/resources/resource-filters/mappers/part-of-collection/part-of-collection.mapper'; -import { MapProviders } from '@shared/components/resources/resource-filters/mappers/provider/provider.mapper'; -import { MapResourceType } from '@shared/components/resources/resource-filters/mappers/resource-type/resource-type.mapper'; -import { MapSubject } from '@shared/components/resources/resource-filters/mappers/subject/subject.mapper'; -import { Creator } from '@shared/components/resources/resource-filters/models/creator/creator.entity'; -import { CreatorItem } from '@shared/components/resources/resource-filters/models/creator/creator-item.entity'; -import { DateCreated } from '@shared/components/resources/resource-filters/models/dateCreated/date-created.entity'; -import { FunderFilter } from '@shared/components/resources/resource-filters/models/funder/funder-filter.entity'; -import { FunderIndexValueSearch } from '@shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity'; -import { IndexValueSearch } from '@shared/components/resources/resource-filters/models/index-value-search.entity'; -import { InstitutionIndexValueSearch } from '@shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity'; -import { LicenseFilter } from '@shared/components/resources/resource-filters/models/license/license-filter.entity'; -import { LicenseIndexValueSearch } from '@shared/components/resources/resource-filters/models/license/license-index-value-search.entity'; -import { PartOfCollectionFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity'; -import { PartOfCollectionIndexValueSearch } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity'; -import { ProviderFilter } from '@shared/components/resources/resource-filters/models/provider/provider-filter.entity'; -import { ProviderIndexValueSearch } from '@shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity'; -import { ResourceTypeFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type.entity'; -import { ResourceTypeIndexValueSearch } from '@shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity'; -import { SubjectFilter } from '@shared/components/resources/resource-filters/models/subject/subject-filter.entity'; -import { ResourceFiltersSelectors } from '@shared/components/resources/resource-filters/store'; -import { addFiltersParams } from '@shared/components/resources/resource-filters/utils/add-filters-params.helper'; - -import { environment } from '../../../../../environments/environment'; - -@Injectable({ - providedIn: 'root', -}) -export class ResourceFiltersService { - #jsonApiService = inject(JsonApiService); - #store = inject(Store); - - #getFilterParams(): Record { - return addFiltersParams(this.#store.selectSignal(ResourceFiltersSelectors.getAllFilters)()); - } - - #getParams(): Record { - const params: Record = {}; - const resourceTab = this.#store.selectSnapshot(SearchSelectors.getResourceTab); - const resourceTypes = getResourceTypes(resourceTab); - const searchText = this.#store.selectSnapshot(SearchSelectors.getSearchText); - const sort = this.#store.selectSnapshot(SearchSelectors.getSortBy); - - params['cardSearchFilter[resourceType]'] = resourceTypes; - params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; - params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; - params['page[size]'] = '10'; - params['sort'] = sort; - return params; - } - - getCreators(valueSearchText: string): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'creator', - valueSearchText, - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse[]> - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe( - map((response) => { - const included = (response?.included ?? []) as ApiData<{ resourceMetadata: CreatorItem }, null, null>[]; - return included - .filter((item) => item.type === 'index-card') - .map((item) => MapCreators(item.attributes.resourceMetadata)); - }) - ); - } - - getDates(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'dateCreated', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get>(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapDateCreated(response?.included ?? []))); - } - - getFunders(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'funder', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapFunders(response?.included ?? []))); - } - - getSubjects(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'subject', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get>(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapSubject(response?.included ?? []))); - } - - getLicenses(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'rights', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapLicenses(response?.included ?? []))); - } - - getResourceTypes(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'resourceNature', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapResourceType(response?.included ?? []))); - } - - getInstitutions(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'affiliation', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapInstitutions(response?.included ?? []))); - } - - getProviders(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'publisher', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapProviders(response?.included ?? []))); - } - - getPartOtCollections(): Observable { - const dynamicParams = { - valueSearchPropertyPath: 'isPartOfCollection', - }; - - const fullParams = { - ...this.#getParams(), - ...this.#getFilterParams(), - ...dynamicParams, - }; - - return this.#jsonApiService - .get< - JsonApiResponse - >(`${environment.shareDomainUrl}/index-value-search`, fullParams) - .pipe(map((response) => MapPartOfCollections(response?.included ?? []))); - } -} diff --git a/src/app/shared/components/resources/resource-filters/models/creator/creator-item.entity.ts b/src/app/shared/entities/filters/creator/creator-item.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/creator/creator-item.entity.ts rename to src/app/shared/entities/filters/creator/creator-item.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/creator/creator.entity.ts b/src/app/shared/entities/filters/creator/creator.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/creator/creator.entity.ts rename to src/app/shared/entities/filters/creator/creator.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/dateCreated/date-created.entity.ts b/src/app/shared/entities/filters/dateCreated/date-created.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/dateCreated/date-created.entity.ts rename to src/app/shared/entities/filters/dateCreated/date-created.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/dateCreated/date-index-card.entity.ts b/src/app/shared/entities/filters/dateCreated/date-index-card.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/dateCreated/date-index-card.entity.ts rename to src/app/shared/entities/filters/dateCreated/date-index-card.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/filter-type.enum.ts b/src/app/shared/entities/filters/filter-type.enum.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/filter-type.enum.ts rename to src/app/shared/entities/filters/filter-type.enum.ts diff --git a/src/app/shared/components/resources/resource-filters/models/funder/funder-filter.entity.ts b/src/app/shared/entities/filters/funder/funder-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/funder/funder-filter.entity.ts rename to src/app/shared/entities/filters/funder/funder-filter.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/funder/funder-index-card-filter.entity.ts b/src/app/shared/entities/filters/funder/funder-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/funder/funder-index-card-filter.entity.ts rename to src/app/shared/entities/filters/funder/funder-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/funder/funder-index-value-search.entity.ts b/src/app/shared/entities/filters/funder/funder-index-value-search.entity.ts new file mode 100644 index 000000000..02abf1118 --- /dev/null +++ b/src/app/shared/entities/filters/funder/funder-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { FunderIndexCardFilter } from '@shared/entities/filters/funder/funder-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type FunderIndexValueSearch = SearchResultCount | FunderIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/index-card-filter.entity.ts b/src/app/shared/entities/filters/index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/index-card-filter.entity.ts rename to src/app/shared/entities/filters/index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/index-value-search.entity.ts b/src/app/shared/entities/filters/index-value-search.entity.ts new file mode 100644 index 000000000..d397bff51 --- /dev/null +++ b/src/app/shared/entities/filters/index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { IndexCardFilter } from '@shared/entities/filters/index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type IndexValueSearch = SearchResultCount | IndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/institution/institution-filter.entity.ts b/src/app/shared/entities/filters/institution/institution-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/institution/institution-filter.entity.ts rename to src/app/shared/entities/filters/institution/institution-filter.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/institution/institution-index-card-filter.entity.ts b/src/app/shared/entities/filters/institution/institution-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/institution/institution-index-card-filter.entity.ts rename to src/app/shared/entities/filters/institution/institution-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/institution/institution-index-value-search.entity.ts b/src/app/shared/entities/filters/institution/institution-index-value-search.entity.ts new file mode 100644 index 000000000..131adf260 --- /dev/null +++ b/src/app/shared/entities/filters/institution/institution-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { InstitutionIndexCardFilter } from '@shared/entities/filters/institution/institution-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type InstitutionIndexValueSearch = SearchResultCount | InstitutionIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/license/license-filter.entity.ts b/src/app/shared/entities/filters/license/license-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/license/license-filter.entity.ts rename to src/app/shared/entities/filters/license/license-filter.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/license/license-index-card-filter.entity.ts b/src/app/shared/entities/filters/license/license-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/license/license-index-card-filter.entity.ts rename to src/app/shared/entities/filters/license/license-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/license/license-index-value-search.entity.ts b/src/app/shared/entities/filters/license/license-index-value-search.entity.ts new file mode 100644 index 000000000..344b5ed80 --- /dev/null +++ b/src/app/shared/entities/filters/license/license-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { LicenseIndexCardFilter } from '@shared/entities/filters/license/license-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type LicenseIndexValueSearch = SearchResultCount | LicenseIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity.ts b/src/app/shared/entities/filters/part-of-collection/part-of-collection-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity.ts rename to src/app/shared/entities/filters/part-of-collection/part-of-collection-filter.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-card-filter.entity.ts b/src/app/shared/entities/filters/part-of-collection/part-of-collection-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-card-filter.entity.ts rename to src/app/shared/entities/filters/part-of-collection/part-of-collection-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity.ts b/src/app/shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity.ts new file mode 100644 index 000000000..41ba6fdb8 --- /dev/null +++ b/src/app/shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { PartOfCollectionIndexCardFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type PartOfCollectionIndexValueSearch = SearchResultCount | PartOfCollectionIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/provider/provider-filter.entity.ts b/src/app/shared/entities/filters/provider/provider-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/provider/provider-filter.entity.ts rename to src/app/shared/entities/filters/provider/provider-filter.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/provider/provider-index-card-filter.entity.ts b/src/app/shared/entities/filters/provider/provider-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/provider/provider-index-card-filter.entity.ts rename to src/app/shared/entities/filters/provider/provider-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/provider/provider-index-value-search.entity.ts b/src/app/shared/entities/filters/provider/provider-index-value-search.entity.ts new file mode 100644 index 000000000..aaf894550 --- /dev/null +++ b/src/app/shared/entities/filters/provider/provider-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { ProviderIndexCardFilter } from '@shared/entities/filters/provider/provider-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type ProviderIndexValueSearch = SearchResultCount | ProviderIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-card-filter.entity.ts b/src/app/shared/entities/filters/resource-type/resource-type-index-card-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/resource-type/resource-type-index-card-filter.entity.ts rename to src/app/shared/entities/filters/resource-type/resource-type-index-card-filter.entity.ts diff --git a/src/app/shared/entities/filters/resource-type/resource-type-index-value-search.entity.ts b/src/app/shared/entities/filters/resource-type/resource-type-index-value-search.entity.ts new file mode 100644 index 000000000..b05419104 --- /dev/null +++ b/src/app/shared/entities/filters/resource-type/resource-type-index-value-search.entity.ts @@ -0,0 +1,4 @@ +import { ResourceTypeIndexCardFilter } from '@shared/entities/filters/resource-type/resource-type-index-card-filter.entity'; +import { SearchResultCount } from '@shared/entities/filters/search-result-count.entity'; + +export type ResourceTypeIndexValueSearch = SearchResultCount | ResourceTypeIndexCardFilter; diff --git a/src/app/shared/components/resources/resource-filters/models/resource-type/resource-type.entity.ts b/src/app/shared/entities/filters/resource-type/resource-type.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/resource-type/resource-type.entity.ts rename to src/app/shared/entities/filters/resource-type/resource-type.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/search-result-count.entity.ts b/src/app/shared/entities/filters/search-result-count.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/search-result-count.entity.ts rename to src/app/shared/entities/filters/search-result-count.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/subject/subject-filter-response.entity.ts b/src/app/shared/entities/filters/subject/subject-filter-response.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/subject/subject-filter-response.entity.ts rename to src/app/shared/entities/filters/subject/subject-filter-response.entity.ts diff --git a/src/app/shared/components/resources/resource-filters/models/subject/subject-filter.entity.ts b/src/app/shared/entities/filters/subject/subject-filter.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-filters/models/subject/subject-filter.entity.ts rename to src/app/shared/entities/filters/subject/subject-filter.entity.ts diff --git a/src/app/features/search/models/resource-tab.enum.ts b/src/app/shared/entities/resource-card/resource-tab.enum.ts similarity index 100% rename from src/app/features/search/models/resource-tab.enum.ts rename to src/app/shared/entities/resource-card/resource-tab.enum.ts diff --git a/src/app/features/search/models/resource-type.enum.ts b/src/app/shared/entities/resource-card/resource-type.enum.ts similarity index 100% rename from src/app/features/search/models/resource-type.enum.ts rename to src/app/shared/entities/resource-card/resource-type.enum.ts diff --git a/src/app/features/search/models/resource.entity.ts b/src/app/shared/entities/resource-card/resource.entity.ts similarity index 90% rename from src/app/features/search/models/resource.entity.ts rename to src/app/shared/entities/resource-card/resource.entity.ts index 7329bd52d..0c513791b 100644 --- a/src/app/features/search/models/resource.entity.ts +++ b/src/app/shared/entities/resource-card/resource.entity.ts @@ -1,5 +1,5 @@ import { LinkItem } from '@osf/features/search/models/link-item.entity'; -import { ResourceType } from '@osf/features/search/models/resource-type.enum'; +import { ResourceType } from '@shared/entities/resource-card/resource-type.enum'; export interface Resource { id: string; diff --git a/src/app/shared/components/resources/resource-card/models/user-counts-response.entity.ts b/src/app/shared/entities/resource-card/user-counts-response.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-card/models/user-counts-response.entity.ts rename to src/app/shared/entities/resource-card/user-counts-response.entity.ts diff --git a/src/app/shared/components/resources/resource-card/models/user-related-data-counts.entity.ts b/src/app/shared/entities/resource-card/user-related-data-counts.entity.ts similarity index 100% rename from src/app/shared/components/resources/resource-card/models/user-related-data-counts.entity.ts rename to src/app/shared/entities/resource-card/user-related-data-counts.entity.ts diff --git a/src/app/shared/helpers/mappers/filters/creators/creators.mappers.ts b/src/app/shared/helpers/mappers/filters/creators/creators.mappers.ts new file mode 100644 index 000000000..20e779abc --- /dev/null +++ b/src/app/shared/helpers/mappers/filters/creators/creators.mappers.ts @@ -0,0 +1,9 @@ +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { CreatorItem } from '@shared/entities/filters/creator/creator-item.entity'; + +export function MapCreators(rawItem: CreatorItem): Creator { + return { + id: rawItem?.['@id'], + name: rawItem?.name?.[0]?.['@value'], + }; +} diff --git a/src/app/shared/components/resources/resource-filters/mappers/dateCreated/date-created.mapper.ts b/src/app/shared/helpers/mappers/filters/dateCreated/date-created.mapper.ts similarity index 61% rename from src/app/shared/components/resources/resource-filters/mappers/dateCreated/date-created.mapper.ts rename to src/app/shared/helpers/mappers/filters/dateCreated/date-created.mapper.ts index 736d1769d..96050b7ce 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/dateCreated/date-created.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/dateCreated/date-created.mapper.ts @@ -1,6 +1,6 @@ -import { DateCreated } from '@shared/components/resources/resource-filters/models/dateCreated/date-created.entity'; -import { IndexCardFilter } from '@shared/components/resources/resource-filters/models/index-card-filter.entity'; -import { IndexValueSearch } from '@shared/components/resources/resource-filters/models/index-value-search.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { IndexCardFilter } from '@shared/entities/filters/index-card-filter.entity'; +import { IndexValueSearch } from '@shared/entities/filters/index-value-search.entity'; export function MapDateCreated(items: IndexValueSearch[]): DateCreated[] { const datesCreated: DateCreated[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/funder/funder.mapper.ts b/src/app/shared/helpers/mappers/filters/funder/funder.mapper.ts similarity index 62% rename from src/app/shared/components/resources/resource-filters/mappers/funder/funder.mapper.ts rename to src/app/shared/helpers/mappers/filters/funder/funder.mapper.ts index 65062ed2b..a260e3c9e 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/funder/funder.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/funder/funder.mapper.ts @@ -1,6 +1,6 @@ -import { FunderFilter } from '@shared/components/resources/resource-filters/models/funder/funder-filter.entity'; -import { FunderIndexCardFilter } from '@shared/components/resources/resource-filters/models/funder/funder-index-card-filter.entity'; -import { FunderIndexValueSearch } from '@shared/components/resources/resource-filters/models/funder/funder-index-value-search.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { FunderIndexCardFilter } from '@shared/entities/filters/funder/funder-index-card-filter.entity'; +import { FunderIndexValueSearch } from '@shared/entities/filters/funder/funder-index-value-search.entity'; export function MapFunders(items: FunderIndexValueSearch[]): FunderFilter[] { const funders: FunderFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/institution/institution.mapper.ts b/src/app/shared/helpers/mappers/filters/institution/institution.mapper.ts similarity index 61% rename from src/app/shared/components/resources/resource-filters/mappers/institution/institution.mapper.ts rename to src/app/shared/helpers/mappers/filters/institution/institution.mapper.ts index e9c34df34..aa53aa3e6 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/institution/institution.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/institution/institution.mapper.ts @@ -1,6 +1,6 @@ -import { InstitutionFilter } from '@shared/components/resources/resource-filters/models/institution/institution-filter.entity'; -import { InstitutionIndexCardFilter } from '@shared/components/resources/resource-filters/models/institution/institution-index-card-filter.entity'; -import { InstitutionIndexValueSearch } from '@shared/components/resources/resource-filters/models/institution/institution-index-value-search.entity'; +import { InstitutionFilter } from '@shared/entities/filters/institution/institution-filter.entity'; +import { InstitutionIndexCardFilter } from '@shared/entities/filters/institution/institution-index-card-filter.entity'; +import { InstitutionIndexValueSearch } from '@shared/entities/filters/institution/institution-index-value-search.entity'; export function MapInstitutions(items: InstitutionIndexValueSearch[]): InstitutionFilter[] { const institutions: InstitutionFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/license/license.mapper.ts b/src/app/shared/helpers/mappers/filters/license/license.mapper.ts similarity index 61% rename from src/app/shared/components/resources/resource-filters/mappers/license/license.mapper.ts rename to src/app/shared/helpers/mappers/filters/license/license.mapper.ts index 287e8e2b0..8841c5a17 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/license/license.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/license/license.mapper.ts @@ -1,6 +1,6 @@ -import { LicenseFilter } from '@shared/components/resources/resource-filters/models/license/license-filter.entity'; -import { LicenseIndexCardFilter } from '@shared/components/resources/resource-filters/models/license/license-index-card-filter.entity'; -import { LicenseIndexValueSearch } from '@shared/components/resources/resource-filters/models/license/license-index-value-search.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { LicenseIndexCardFilter } from '@shared/entities/filters/license/license-index-card-filter.entity'; +import { LicenseIndexValueSearch } from '@shared/entities/filters/license/license-index-value-search.entity'; export function MapLicenses(items: LicenseIndexValueSearch[]): LicenseFilter[] { const licenses: LicenseFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/part-of-collection/part-of-collection.mapper.ts b/src/app/shared/helpers/mappers/filters/part-of-collection/part-of-collection.mapper.ts similarity index 59% rename from src/app/shared/components/resources/resource-filters/mappers/part-of-collection/part-of-collection.mapper.ts rename to src/app/shared/helpers/mappers/filters/part-of-collection/part-of-collection.mapper.ts index 397d166fe..27d2337ea 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/part-of-collection/part-of-collection.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/part-of-collection/part-of-collection.mapper.ts @@ -1,6 +1,6 @@ -import { PartOfCollectionFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-filter.entity'; -import { PartOfCollectionIndexCardFilter } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-card-filter.entity'; -import { PartOfCollectionIndexValueSearch } from '@shared/components/resources/resource-filters/models/part-of-collection/part-of-collection-index-value-search.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { PartOfCollectionIndexCardFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-index-card-filter.entity'; +import { PartOfCollectionIndexValueSearch } from '@shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity'; export function MapPartOfCollections(items: PartOfCollectionIndexValueSearch[]): PartOfCollectionFilter[] { const partOfCollections: PartOfCollectionFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/provider/provider.mapper.ts b/src/app/shared/helpers/mappers/filters/provider/provider.mapper.ts similarity index 61% rename from src/app/shared/components/resources/resource-filters/mappers/provider/provider.mapper.ts rename to src/app/shared/helpers/mappers/filters/provider/provider.mapper.ts index 46eca9156..de268c0be 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/provider/provider.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/provider/provider.mapper.ts @@ -1,6 +1,6 @@ -import { ProviderFilter } from '@shared/components/resources/resource-filters/models/provider/provider-filter.entity'; -import { ProviderIndexCardFilter } from '@shared/components/resources/resource-filters/models/provider/provider-index-card-filter.entity'; -import { ProviderIndexValueSearch } from '@shared/components/resources/resource-filters/models/provider/provider-index-value-search.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ProviderIndexCardFilter } from '@shared/entities/filters/provider/provider-index-card-filter.entity'; +import { ProviderIndexValueSearch } from '@shared/entities/filters/provider/provider-index-value-search.entity'; export function MapProviders(items: ProviderIndexValueSearch[]): ProviderFilter[] { const providers: ProviderFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/resource-type/resource-type.mapper.ts b/src/app/shared/helpers/mappers/filters/resource-type/resource-type.mapper.ts similarity index 61% rename from src/app/shared/components/resources/resource-filters/mappers/resource-type/resource-type.mapper.ts rename to src/app/shared/helpers/mappers/filters/resource-type/resource-type.mapper.ts index 9bd0c0e14..18a3a55a4 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/resource-type/resource-type.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/resource-type/resource-type.mapper.ts @@ -1,6 +1,6 @@ -import { ResourceTypeFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type.entity'; -import { ResourceTypeIndexCardFilter } from '@shared/components/resources/resource-filters/models/resource-type/resource-type-index-card-filter.entity'; -import { ResourceTypeIndexValueSearch } from '@shared/components/resources/resource-filters/models/resource-type/resource-type-index-value-search.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { ResourceTypeIndexCardFilter } from '@shared/entities/filters/resource-type/resource-type-index-card-filter.entity'; +import { ResourceTypeIndexValueSearch } from '@shared/entities/filters/resource-type/resource-type-index-value-search.entity'; export function MapResourceType(items: ResourceTypeIndexValueSearch[]): ResourceTypeFilter[] { const resourceTypes: ResourceTypeFilter[] = []; diff --git a/src/app/shared/components/resources/resource-filters/mappers/subject/subject.mapper.ts b/src/app/shared/helpers/mappers/filters/subject/subject.mapper.ts similarity index 64% rename from src/app/shared/components/resources/resource-filters/mappers/subject/subject.mapper.ts rename to src/app/shared/helpers/mappers/filters/subject/subject.mapper.ts index 77336572e..30319e1c1 100644 --- a/src/app/shared/components/resources/resource-filters/mappers/subject/subject.mapper.ts +++ b/src/app/shared/helpers/mappers/filters/subject/subject.mapper.ts @@ -1,6 +1,6 @@ -import { IndexCardFilter } from '@shared/components/resources/resource-filters/models/index-card-filter.entity'; -import { IndexValueSearch } from '@shared/components/resources/resource-filters/models/index-value-search.entity'; -import { SubjectFilter } from '@shared/components/resources/resource-filters/models/subject/subject-filter.entity'; +import { IndexCardFilter } from '@shared/entities/filters/index-card-filter.entity'; +import { IndexValueSearch } from '@shared/entities/filters/index-value-search.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; export function MapSubject(items: IndexValueSearch[]): SubjectFilter[] { const subjects: SubjectFilter[] = []; diff --git a/src/app/shared/components/resources/resource-card/mappers/user-counts.mapper.ts b/src/app/shared/helpers/mappers/resource-card/user-counts.mapper.ts similarity index 67% rename from src/app/shared/components/resources/resource-card/mappers/user-counts.mapper.ts rename to src/app/shared/helpers/mappers/resource-card/user-counts.mapper.ts index 0505be35a..198e311bc 100644 --- a/src/app/shared/components/resources/resource-card/mappers/user-counts.mapper.ts +++ b/src/app/shared/helpers/mappers/resource-card/user-counts.mapper.ts @@ -1,5 +1,5 @@ -import { UserCountsResponse } from '@shared/components/resources/resource-card/models/user-counts-response.entity'; -import { UserRelatedDataCounts } from '@shared/components/resources/resource-card/models/user-related-data-counts.entity'; +import { UserCountsResponse } from '@shared/entities/resource-card/user-counts-response.entity'; +import { UserRelatedDataCounts } from '@shared/entities/resource-card/user-related-data-counts.entity'; export function MapUserCounts(response: UserCountsResponse): UserRelatedDataCounts { return { diff --git a/src/app/shared/services/filters-options.service.ts b/src/app/shared/services/filters-options.service.ts new file mode 100644 index 000000000..0c4c6bc9c --- /dev/null +++ b/src/app/shared/services/filters-options.service.ts @@ -0,0 +1,238 @@ +import { map, Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { ApiData, JsonApiResponse } from '@core/services/json-api/json-api.entity'; +import { JsonApiService } from '@core/services/json-api/json-api.service'; +import { Creator } from '@shared/entities/filters/creator/creator.entity'; +import { CreatorItem } from '@shared/entities/filters/creator/creator-item.entity'; +import { DateCreated } from '@shared/entities/filters/dateCreated/date-created.entity'; +import { FunderFilter } from '@shared/entities/filters/funder/funder-filter.entity'; +import { FunderIndexValueSearch } from '@shared/entities/filters/funder/funder-index-value-search.entity'; +import { IndexValueSearch } from '@shared/entities/filters/index-value-search.entity'; +import { InstitutionIndexValueSearch } from '@shared/entities/filters/institution/institution-index-value-search.entity'; +import { LicenseFilter } from '@shared/entities/filters/license/license-filter.entity'; +import { LicenseIndexValueSearch } from '@shared/entities/filters/license/license-index-value-search.entity'; +import { PartOfCollectionFilter } from '@shared/entities/filters/part-of-collection/part-of-collection-filter.entity'; +import { PartOfCollectionIndexValueSearch } from '@shared/entities/filters/part-of-collection/part-of-collection-index-value-search.entity'; +import { ProviderFilter } from '@shared/entities/filters/provider/provider-filter.entity'; +import { ProviderIndexValueSearch } from '@shared/entities/filters/provider/provider-index-value-search.entity'; +import { ResourceTypeFilter } from '@shared/entities/filters/resource-type/resource-type.entity'; +import { ResourceTypeIndexValueSearch } from '@shared/entities/filters/resource-type/resource-type-index-value-search.entity'; +import { SubjectFilter } from '@shared/entities/filters/subject/subject-filter.entity'; +import { MapCreators } from '@shared/helpers/mappers/filters/creators/creators.mappers'; +import { MapDateCreated } from '@shared/helpers/mappers/filters/dateCreated/date-created.mapper'; +import { MapFunders } from '@shared/helpers/mappers/filters/funder/funder.mapper'; +import { MapInstitutions } from '@shared/helpers/mappers/filters/institution/institution.mapper'; +import { MapLicenses } from '@shared/helpers/mappers/filters/license/license.mapper'; +import { MapPartOfCollections } from '@shared/helpers/mappers/filters/part-of-collection/part-of-collection.mapper'; +import { MapProviders } from '@shared/helpers/mappers/filters/provider/provider.mapper'; +import { MapResourceType } from '@shared/helpers/mappers/filters/resource-type/resource-type.mapper'; +import { MapSubject } from '@shared/helpers/mappers/filters/subject/subject.mapper'; + +import { environment } from '../../../environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class FiltersOptionsService { + #jsonApiService = inject(JsonApiService); + + // #getFilterParams(): Record { + // return addFiltersParams(this.#store.selectSignal(ResourceFiltersSelectors.getAllFilters)()); + // } + // + // #getParams(): Record { + // const params: Record = {}; + // const resourceTab = this.#store.selectSnapshot(SearchSelectors.getResourceTab); + // const resourceTypes = getResourceTypes(resourceTab); + // const searchText = this.#store.selectSnapshot(SearchSelectors.getSearchText); + // const sort = this.#store.selectSnapshot(SearchSelectors.getSortBy); + // + // params['cardSearchFilter[resourceType]'] = resourceTypes; + // params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; + // params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; + // params['page[size]'] = '10'; + // params['sort'] = sort; + // return params; + // } + + getCreators( + valueSearchText: string, + params: Record, + filterParams: Record + ): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'creator', + valueSearchText, + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse[]> + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe( + map((response) => { + const included = (response?.included ?? []) as ApiData<{ resourceMetadata: CreatorItem }, null, null>[]; + return included + .filter((item) => item.type === 'index-card') + .map((item) => MapCreators(item.attributes.resourceMetadata)); + }) + ); + } + + getDates(params: Record, filterParams: Record): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'dateCreated', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get>(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapDateCreated(response?.included ?? []))); + } + + getFunders(params: Record, filterParams: Record): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'funder', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapFunders(response?.included ?? []))); + } + + getSubjects(params: Record, filterParams: Record): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'subject', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get>(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapSubject(response?.included ?? []))); + } + + getLicenses(params: Record, filterParams: Record): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'rights', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapLicenses(response?.included ?? []))); + } + + getResourceTypes( + params: Record, + filterParams: Record + ): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'resourceNature', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapResourceType(response?.included ?? []))); + } + + getInstitutions( + params: Record, + filterParams: Record + ): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'affiliation', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapInstitutions(response?.included ?? []))); + } + + getProviders(params: Record, filterParams: Record): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'publisher', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapProviders(response?.included ?? []))); + } + + getPartOtCollections( + params: Record, + filterParams: Record + ): Observable { + const dynamicParams = { + valueSearchPropertyPath: 'isPartOfCollection', + }; + + const fullParams = { + ...params, + ...filterParams, + ...dynamicParams, + }; + + return this.#jsonApiService + .get< + JsonApiResponse + >(`${environment.shareDomainUrl}/index-value-search`, fullParams) + .pipe(map((response) => MapPartOfCollections(response?.included ?? []))); + } +} diff --git a/src/app/features/search/search.service.ts b/src/app/shared/services/search.service.ts similarity index 100% rename from src/app/features/search/search.service.ts rename to src/app/shared/services/search.service.ts diff --git a/src/app/shared/styles/filter-chips/filter-chips.scss b/src/app/shared/styles/filter-chips/filter-chips.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/resources/resource-card/resource-card.component.scss b/src/app/shared/styles/resource-card/resource-card.scss similarity index 96% rename from src/app/shared/components/resources/resource-card/resource-card.component.scss rename to src/app/shared/styles/resource-card/resource-card.scss index 6f9d51825..e0a92b726 100644 --- a/src/app/shared/components/resources/resource-card/resource-card.component.scss +++ b/src/app/shared/styles/resource-card/resource-card.scss @@ -1,4 +1,4 @@ -@use "../../../../../assets/styles/variables" as var; +@use "assets/styles/variables" as var; .resource { display: flex; diff --git a/src/app/shared/components/resources/resource-filters/utils/add-filters-params.helper.ts b/src/app/shared/utils/add-filters-params.helper.ts similarity index 66% rename from src/app/shared/components/resources/resource-filters/utils/add-filters-params.helper.ts rename to src/app/shared/utils/add-filters-params.helper.ts index 65d9d4b21..5068d86e6 100644 --- a/src/app/shared/components/resources/resource-filters/utils/add-filters-params.helper.ts +++ b/src/app/shared/utils/add-filters-params.helper.ts @@ -1,33 +1,33 @@ -import { ResourceFiltersStateModel } from '@shared/components/resources/resource-filters/store'; +import { ResourceFiltersStateModel } from '@osf/features/search/components/resources/components/resource-filters/store'; export function addFiltersParams(filters: ResourceFiltersStateModel): Record { const params: Record = {}; - if (filters.creator.value) { + if (filters.creator?.value) { params['cardSearchFilter[creator][]'] = filters.creator.value; } - if (filters.dateCreated.value) { + if (filters.dateCreated?.value) { params['cardSearchFilter[dateCreated][]'] = filters.dateCreated.value; } - if (filters.subject.value) { + if (filters.subject?.value) { params['cardSearchFilter[subject][]'] = filters.subject.value; } - if (filters.funder.value) { + if (filters.funder?.value) { params['cardSearchFilter[funder][]'] = filters.funder.value; } - if (filters.license.value) { + if (filters.license?.value) { params['cardSearchFilter[rights][]'] = filters.license.value; } - if (filters.resourceType.value) { + if (filters.resourceType?.value) { params['cardSearchFilter[resourceNature][]'] = filters.resourceType.value; } - if (filters.institution.value) { + if (filters.institution?.value) { params['cardSearchFilter[affiliation][]'] = filters.institution.value; } - if (filters.provider.value) { + if (filters.provider?.value) { params['cardSearchFilter[publisher][]'] = filters.provider.value; } - if (filters.partOfCollection.value) { + if (filters.partOfCollection?.value) { params['cardSearchFilter[isPartOfCollection][]'] = filters.partOfCollection.value; } diff --git a/src/app/shared/components/resources/resource-filters/models/filter-labels.ts b/src/app/shared/utils/filter-labels.model.ts similarity index 87% rename from src/app/shared/components/resources/resource-filters/models/filter-labels.ts rename to src/app/shared/utils/filter-labels.model.ts index 2b620967c..a5f03f7d7 100644 --- a/src/app/shared/components/resources/resource-filters/models/filter-labels.ts +++ b/src/app/shared/utils/filter-labels.model.ts @@ -1,4 +1,4 @@ -export const FilterLabels = { +export const FilterLabelsModel = { creator: 'Creator', dateCreated: 'Date Created', funder: 'Funder', diff --git a/src/app/features/search/utils/helpers/get-resource-types.helper.ts b/src/app/shared/utils/get-resource-types.helper.ts similarity index 86% rename from src/app/features/search/utils/helpers/get-resource-types.helper.ts rename to src/app/shared/utils/get-resource-types.helper.ts index 32e048eb0..3434c36dd 100644 --- a/src/app/features/search/utils/helpers/get-resource-types.helper.ts +++ b/src/app/shared/utils/get-resource-types.helper.ts @@ -1,4 +1,4 @@ -import { ResourceTab } from '@osf/features/search/models/resource-tab.enum'; +import { ResourceTab } from '@shared/entities/resource-card/resource-tab.enum'; export function getResourceTypes(resourceTab: ResourceTab): string { switch (resourceTab) { diff --git a/src/app/shared/components/resources/resource-filters/utils/data.ts b/src/app/shared/utils/resource-filters-defaults.model.ts similarity index 52% rename from src/app/shared/components/resources/resource-filters/utils/data.ts rename to src/app/shared/utils/resource-filters-defaults.model.ts index 4f4d3e0c7..e21262559 100644 --- a/src/app/shared/components/resources/resource-filters/utils/data.ts +++ b/src/app/shared/utils/resource-filters-defaults.model.ts @@ -1,48 +1,48 @@ -import { FilterLabels } from '@shared/components/resources/resource-filters/models/filter-labels'; +import { FilterLabelsModel } from '@shared/utils/filter-labels.model'; -export const resourceFiltersDefaults = { +export const resourceFiltersDefaultsModel = { creator: { - filterName: FilterLabels.creator, + filterName: FilterLabelsModel.creator, label: undefined, value: undefined, }, dateCreated: { - filterName: FilterLabels.dateCreated, + filterName: FilterLabelsModel.dateCreated, label: undefined, value: undefined, }, funder: { - filterName: FilterLabels.funder, + filterName: FilterLabelsModel.funder, label: undefined, value: undefined, }, subject: { - filterName: FilterLabels.subject, + filterName: FilterLabelsModel.subject, label: undefined, value: undefined, }, license: { - filterName: FilterLabels.license, + filterName: FilterLabelsModel.license, label: undefined, value: undefined, }, resourceType: { - filterName: FilterLabels.resourceType, + filterName: FilterLabelsModel.resourceType, label: undefined, value: undefined, }, institution: { - filterName: FilterLabels.institution, + filterName: FilterLabelsModel.institution, label: undefined, value: undefined, }, provider: { - filterName: FilterLabels.provider, + filterName: FilterLabelsModel.provider, label: undefined, value: undefined, }, partOfCollection: { - filterName: FilterLabels.partOfCollection, + filterName: FilterLabelsModel.partOfCollection, label: undefined, value: undefined, }, diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index ba7f1fbf3..7fbec08a4 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1,6 +1,6 @@ { "navigation": { - "myProfile": "My profile", + "profile": "Profile", "settings": "Settings", "logOut": "Log out", "signIn": "Sign in", @@ -643,4 +643,4 @@ }, "copyright": "Copyright © 2011-2025" } -} \ No newline at end of file +}