Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -155,12 +159,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',
Expand Down
6 changes: 6 additions & 0 deletions src/app/core/constants/nav-items.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export const NAV_ITEMS: NavItem[] = [
icon: 'my-projects',
useExactMatch: true,
},
{
path: '/my-profile',
label: 'navigation.profile',
icon: 'profile',
useExactMatch: true,
},
{
path: '/meetings',
label: 'navigation.meetings',
Expand Down
2 changes: 0 additions & 2 deletions src/app/core/constants/ngxs-states.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { UserState } from '@core/store/user';
import { InstitutionsState } from '@osf/features/institutions/store';
import { MyProjectsState } from '@osf/features/my-projects/store';
import { AnalyticsState } from '@osf/features/project/analytics/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';
Expand All @@ -15,7 +14,6 @@ export const STATES = [
TokensState,
AddonsState,
UserState,
SearchState,
MyProjectsState,
InstitutionsState,
ProfileSettingsState,
Expand Down
9 changes: 0 additions & 9 deletions src/app/core/services/user/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<div class="search-container" [class]="!isMyProfilePage() ? 'mt-6 xl:mt-8' : 'mt-5 xl:mt-6'">
<img
ngSrc="assets/icons/colored/question-mark.svg"
alt="better-research"
tabindex="0"
role="button"
height="36"
width="36"
(click)="currentStep = 1"
(keydown.enter)="currentStep = 1"
class="cursor-pointer"
/>
<osf-search-input
[searchValue]="searchValue()"
(searchValueChange)="searchValue.set($event || '')"
placeholder="Enter search term(s) here"
/>
</div>

<div class="flex-column flex flex-1 w-full resources-tab-panel">
<p-tabs [value]="selectedTab" (valueChange)="onTabChange(+$event)" class="flex-1">
@if (!isMobile()) {
<p-tablist>
<p-tab [value]="ResourceTab.All">All</p-tab>
<p-tab [value]="ResourceTab.Projects">Projects</p-tab>
<p-tab [value]="ResourceTab.Registrations">Registrations</p-tab>
<p-tab [value]="ResourceTab.Preprints">Preprints</p-tab>
<p-tab [value]="ResourceTab.Files">Files</p-tab>
</p-tablist>
}

<p-tabpanels class="p-3 sm:p-5 flex-1">
<p-tabpanel [value]="ResourceTab.All" class="flex flex-column gap-4 flex-1"> </p-tabpanel>
<p-tabpanel [value]="ResourceTab.Projects" class="flex flex-column gap-5"> </p-tabpanel>
<p-tabpanel [value]="ResourceTab.Registrations" class="flex flex-column gap-5"></p-tabpanel>
<p-tabpanel [value]="ResourceTab.Preprints" class="flex flex-column gap-5"></p-tabpanel>
<p-tabpanel [value]="ResourceTab.Files" class="flex flex-column gap-5"> </p-tabpanel>
</p-tabpanels>
</p-tabs>

<div class="resources">
<osf-my-profile-resources></osf-my-profile-resources>

@if (currentStep === 1) {
<div class="stepper first-stepper">
<h3>Improved OSF Search</h3>
<p>
Enter any term in the search box and filter by specific object types. More information is available on our
help guides.
</p>
<div class="flex align-items-center mt-4">
<p>1 of 3</p>
<div class="flex column-gap-3 ml-auto wide-button">
<p-button (click)="currentStep = 0" label="Skip" severity="secondary" />
<p-button (click)="currentStep = currentStep + 1">Next</p-button>
</div>
</div>
</div>
}

@if (currentStep === 2) {
<div class="stepper second-stepper">
<h3>Refine Your Search</h3>
<p>
Narrow the source, discipline, and more. For example, find content supported by a specific funder or view only
datasets.
</p>
<div class="flex align-items-center mt-4">
<p>2 of 3</p>
<div class="flex column-gap-3 ml-auto wide-button">
<p-button (click)="currentStep = 0" label="Skip" severity="secondary" />
<p-button (click)="currentStep = currentStep + 1">Next</p-button>
</div>
</div>
</div>
}

@if (currentStep === 3) {
<div class="stepper third-stepper">
<h3>Add Metadata</h3>
<p>
Remember to add metadata and resources to your own work on OSF to make it more discoverable! Learn more in our
help guides.
</p>
<div class="flex align-items-center mt-4">
<p>3 of 3</p>
<div class="flex column-gap-3 ml-auto wide-button">
<p-button (click)="currentStep = currentStep + 1">Done</p-button>
</div>
</div>
</div>
}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<MyProfileSearchComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyProfileSearchComponent],
}).compileComponents();

fixture = TestBed.createComponent(MyProfileSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Store } from '@ngxs/store';

import { Button } from 'primeng/button';
import { Tab, TabList, TabPanel, TabPanels, Tabs } from 'primeng/tabs';

import { debounceTime, skip } 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 { UserSelectors } from '@core/store/user/user.selectors';
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 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);
readonly currentUser = this.#store.select(UserSelectors.getCurrentUser);

protected selectedTab: ResourceTab = ResourceTab.All;
protected readonly ResourceTab = ResourceTab;
protected currentStep = 0;
private skipInitializationEffects = 0;

constructor() {
this.currentUser.subscribe((user) => {
if (user?.id) {
this.#store.dispatch(GetAllOptions);
this.#store.dispatch(GetResources);
}
});

effect(() => {
this.dateCreatedFilter();
this.funderFilter();
this.subjectFilter();
this.licenseFilter();
this.resourceTypeFilter();
this.institutionFilter();
this.providerFilter();
this.partOfCollectionFilter();
this.searchStoreValue();
this.resourcesTabStoreValue();
this.sortByStoreValue();
if (this.skipInitializationEffects > 0) {
this.#store.dispatch(GetResources);
}
this.skipInitializationEffects += 1;
});

// put search value in store and update resources, filters
toObservable(this.searchValue)
.pipe(skip(1), 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);
}
}
Loading