diff --git a/src/app/core/constants/nav-items.constant.ts b/src/app/core/constants/nav-items.constant.ts index 4a2cd9db7..c6b8ab56a 100644 --- a/src/app/core/constants/nav-items.constant.ts +++ b/src/app/core/constants/nav-items.constant.ts @@ -181,7 +181,7 @@ export const MENU_ITEMS: MenuItem[] = [ }, { id: 'my-resources', - label: 'navigation.myResources', + label: 'navigation.myOsf', icon: 'custom-icon-projects', routerLinkActiveOptions: { exact: true }, visible: false, diff --git a/src/app/features/admin-institutions/components/admin-table/admin-table.component.html b/src/app/features/admin-institutions/components/admin-table/admin-table.component.html index b3a97c3b8..30d4a2fc6 100644 --- a/src/app/features/admin-institutions/components/admin-table/admin-table.component.html +++ b/src/app/features/admin-institutions/components/admin-table/admin-table.component.html @@ -50,7 +50,7 @@ outlined class="grey-border-color download-button" severity="info" - (click)="downloadMenu.toggle($event)" + (onClick)="downloadMenu.toggle($event)" /> @if (reportsLink()) { @@ -69,7 +69,7 @@ - - @for (col of columns; track col.field) { - -
- @if (col.isLink && isLink(rowData[col.field])) { - + @if (isLoading()) { + + + + + + } @else { + + @for (col of columns; track col.field) { + +
+ @if (col.isLink && isLink(rowData[col.field])) { + + @if (col.dateFormat) { + {{ getCellValue(rowData[col.field]) | date: col.dateFormat }} + } @else { + {{ getCellValue(rowData[col.field]) }} + } + + } @else { @if (col.dateFormat) { {{ getCellValue(rowData[col.field]) | date: col.dateFormat }} } @else { {{ getCellValue(rowData[col.field]) }} } - - } @else { - @if (col.dateFormat) { - {{ getCellValue(rowData[col.field]) | date: col.dateFormat }} - } @else { - {{ getCellValue(rowData[col.field]) }} } - } - @if (col.showIcon) { - - } -
- - } - + @if (col.showIcon) { + + } +
+ + } + + }
@@ -154,7 +162,7 @@ severity="contrast" text [disabled]="!prevLink()" - (click)="switchPage(prevLink())" + (onClick)="switchPage(prevLink())" > @@ -163,7 +171,7 @@ severity="contrast" text [disabled]="!nextLink()" - (click)="switchPage(nextLink())" + (onClick)="switchPage(nextLink())" > diff --git a/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts b/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts index 6dc0a535f..1830cf04c 100644 --- a/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts +++ b/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts @@ -5,6 +5,7 @@ import { Button, ButtonDirective } from 'primeng/button'; import { Menu } from 'primeng/menu'; import { MultiSelect } from 'primeng/multiselect'; import { PaginatorState } from 'primeng/paginator'; +import { Skeleton } from 'primeng/skeleton'; import { TableModule } from 'primeng/table'; import { Tooltip } from 'primeng/tooltip'; @@ -37,6 +38,7 @@ import { DownloadType } from '../../enums'; TranslatePipe, Button, Menu, + Skeleton, StopPropagationDirective, DatePipe, ], @@ -52,6 +54,7 @@ export class AdminTableComponent { tableColumns = input.required(); tableData = input.required(); + isLoading = input(false); enablePagination = input(false); totalCount = input(0); currentPage = input(1); @@ -60,6 +63,7 @@ export class AdminTableComponent { sortField = input(''); sortOrder = input(1); + reportsLink = input(''); isNextPreviousPagination = input(false); @@ -79,8 +83,7 @@ export class AdminTableComponent { linkPageChanged = output(); downloadClicked = output(); - reportsLink = input(''); - + skeletonData: TableCellData[] = Array.from({ length: 10 }, () => ({}) as TableCellData); selectedColumns = signal([]); downloadMenuItems = DOWNLOAD_OPTIONS; diff --git a/src/app/features/admin-institutions/helpers/index.ts b/src/app/features/admin-institutions/helpers/index.ts index 45eb44ccd..32049f042 100644 --- a/src/app/features/admin-institutions/helpers/index.ts +++ b/src/app/features/admin-institutions/helpers/index.ts @@ -1,2 +1,3 @@ +export * from './camel-to-snake.helper'; export * from './download-url.helper'; export * from './extract-path-after-domain.helper'; diff --git a/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.html b/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.html index c914d18fc..af42fcf0c 100644 --- a/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.html +++ b/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.html @@ -1,22 +1,15 @@ -@if (isLoading()) { -
- + +
+

{{ totalCount() }} {{ 'adminInstitutions.preprints.totalPreprints' | translate | lowercase }}

-} @else { - -
-

- {{ totalCount() }} {{ 'adminInstitutions.preprints.totalPreprints' | translate | lowercase }} -

-
-
-} +
diff --git a/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.ts b/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.ts index 8ae90eebd..1f5d3a5c8 100644 --- a/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.ts +++ b/src/app/features/admin-institutions/pages/institutions-preprints/institutions-preprints.component.ts @@ -6,7 +6,6 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { LoadingSpinnerComponent } from '@osf/shared/components'; import { TABLE_PARAMS } from '@osf/shared/constants'; import { SortOrder } from '@osf/shared/enums'; import { Institution, QueryParams } from '@osf/shared/models'; @@ -22,7 +21,7 @@ import { FetchPreprints, InstitutionsAdminSelectors } from '../../store'; @Component({ selector: 'osf-institutions-preprints', - imports: [CommonModule, AdminTableComponent, TranslatePipe, LoadingSpinnerComponent], + imports: [CommonModule, AdminTableComponent, TranslatePipe], templateUrl: './institutions-preprints.component.html', styleUrl: './institutions-preprints.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -77,7 +76,7 @@ export class InstitutionsPreprintsComponent implements OnInit { const sortField = this.sortField(); const sortOrder = this.sortOrder(); - const sortParam = sortOrder === -1 ? `-${sortField}` : sortField; + const sortParam = sortOrder === SortOrder.Desc ? `-${sortField}` : sortField; const institution = this.institution() as Institution; const institutionIris = institution.iris || []; diff --git a/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.html b/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.html index d51a97e2d..0a197c067 100644 --- a/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.html +++ b/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.html @@ -1,27 +1,22 @@ -@if (isLoading()) { -
- + +
+

{{ totalCount() }} {{ 'adminInstitutions.projects.totalProjects' | translate }}

-} @else { - -
-

{{ totalCount() }} {{ 'adminInstitutions.projects.totalProjects' | translate }}

-
-
-} +
diff --git a/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.ts b/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.ts index f6ef5e56f..d5a1c7437 100644 --- a/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.ts +++ b/src/app/features/admin-institutions/pages/institutions-projects/institutions-projects.component.ts @@ -11,7 +11,6 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; import { UserSelectors } from '@osf/core/store/user'; -import { LoadingSpinnerComponent } from '@osf/shared/components'; import { TABLE_PARAMS } from '@osf/shared/constants'; import { SortOrder } from '@osf/shared/enums'; import { Institution, QueryParams } from '@osf/shared/models'; @@ -29,7 +28,7 @@ import { FetchProjects, InstitutionsAdminSelectors, RequestProjectAccess, SendUs @Component({ selector: 'osf-institutions-projects', - imports: [AdminTableComponent, TranslatePipe, LoadingSpinnerComponent], + imports: [AdminTableComponent, TranslatePipe], templateUrl: './institutions-projects.component.html', styleUrl: './institutions-projects.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -95,7 +94,7 @@ export class InstitutionsProjectsComponent implements OnInit { const sortField = this.sortField(); const sortOrder = this.sortOrder(); - const sortParam = sortOrder === -1 ? `-${sortField}` : sortField; + const sortParam = sortOrder === SortOrder.Desc ? `-${sortField}` : sortField; const institution = this.institution() as Institution; const institutionIris = institution.iris || []; diff --git a/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.html b/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.html index a4c009522..b62de5613 100644 --- a/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.html +++ b/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.html @@ -1,22 +1,17 @@ -@if (isLoading()) { -
- + +
+

+ {{ totalCount() }} {{ 'adminInstitutions.registrations.totalRegistrations' | translate | lowercase }} +

-} @else { - -
-

- {{ totalCount() }} {{ 'adminInstitutions.registrations.totalRegistrations' | translate | lowercase }} -

-
-
-} +
diff --git a/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.ts b/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.ts index cfdcbbb3b..d8763889d 100644 --- a/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.ts +++ b/src/app/features/admin-institutions/pages/institutions-registrations/institutions-registrations.component.ts @@ -6,7 +6,6 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { LoadingSpinnerComponent } from '@osf/shared/components'; import { TABLE_PARAMS } from '@osf/shared/constants'; import { SortOrder } from '@osf/shared/enums'; import { Institution, QueryParams } from '@osf/shared/models'; @@ -22,7 +21,7 @@ import { FetchRegistrations, InstitutionsAdminSelectors } from '../../store'; @Component({ selector: 'osf-institutions-registrations', - imports: [CommonModule, AdminTableComponent, TranslatePipe, LoadingSpinnerComponent], + imports: [CommonModule, AdminTableComponent, TranslatePipe], templateUrl: './institutions-registrations.component.html', styleUrl: './institutions-registrations.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -75,7 +74,7 @@ export class InstitutionsRegistrationsComponent implements OnInit { const sortField = this.sortField(); const sortOrder = this.sortOrder(); - const sortParam = sortOrder === -1 ? `-${sortField}` : sortField; + const sortParam = sortOrder === SortOrder.Desc ? `-${sortField}` : sortField; const institution = this.institution() as Institution; const institutionIris = institution.iris || []; diff --git a/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.html b/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.html index b38e96b27..59bd55443 100644 --- a/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.html +++ b/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.html @@ -1,51 +1,46 @@ -@if (isLoading()) { -
- + +
+

{{ amountText() }}

-} @else { - -
-

{{ amountText() }}

-
-
-
- - - -
+
+
+ + + +
-
- -
+
+
- -} +
+ diff --git a/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.ts b/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.ts index 1a8180f06..829a8fdd9 100644 --- a/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.ts +++ b/src/app/features/admin-institutions/pages/institutions-users/institutions-users.component.ts @@ -23,7 +23,7 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { UserSelectors } from '@osf/core/store/user'; -import { LoadingSpinnerComponent, SelectComponent } from '@osf/shared/components'; +import { SelectComponent } from '@osf/shared/components'; import { TABLE_PARAMS } from '@osf/shared/constants'; import { SortOrder } from '@osf/shared/enums'; import { Primitive } from '@osf/shared/helpers'; @@ -35,14 +35,14 @@ import { AdminTableComponent } from '../../components'; import { departmentOptions, userTableColumns } from '../../constants'; import { SendEmailDialogComponent } from '../../dialogs'; import { DownloadType } from '../../enums'; -import { camelToSnakeCase } from '../../helpers/camel-to-snake.helper'; +import { camelToSnakeCase } from '../../helpers'; import { mapUserToTableCellData } from '../../mappers'; import { InstitutionUser, SendEmailDialogData, TableCellData, TableCellLink, TableIconClickEvent } from '../../models'; import { FetchInstitutionUsers, InstitutionsAdminSelectors, SendUserMessage } from '../../store'; @Component({ selector: 'osf-institutions-users', - imports: [AdminTableComponent, FormsModule, SelectComponent, CheckboxModule, TranslatePipe, LoadingSpinnerComponent], + imports: [AdminTableComponent, FormsModule, SelectComponent, CheckboxModule, TranslatePipe], templateUrl: './institutions-users.component.html', styleUrl: './institutions-users.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/features/home/components/confirm-email/confirm-email.component.html b/src/app/features/home/components/confirm-email/confirm-email.component.html index 00bb2a80c..8c6acabfd 100644 --- a/src/app/features/home/components/confirm-email/confirm-email.component.html +++ b/src/app/features/home/components/confirm-email/confirm-email.component.html @@ -11,14 +11,14 @@

severity="info" class="w-full" styleClass="w-full" - (click)="closeDialog()" + (onClick)="closeDialog()" [label]="'common.buttons.cancel' | translate" >

diff --git a/src/app/features/home/components/confirm-email/confirm-email.component.ts b/src/app/features/home/components/confirm-email/confirm-email.component.ts index 7d781d2db..366a156b0 100644 --- a/src/app/features/home/components/confirm-email/confirm-email.component.ts +++ b/src/app/features/home/components/confirm-email/confirm-email.component.ts @@ -11,7 +11,7 @@ import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountSettingsService } from '@osf/features/settings/account-settings/services'; -import { LoadingSpinnerComponent } from '@shared/components'; +import { LoadingSpinnerComponent } from '@osf/shared/components'; @Component({ selector: 'osf-confirm-email', @@ -31,7 +31,7 @@ export class ConfirmEmailComponent { verifyingEmail = signal(false); closeDialog() { - this.router.navigate(['/home']); + this.router.navigate(['/']); this.dialogRef.close(); } diff --git a/src/app/features/home/pages/dashboard/dashboard.component.html b/src/app/features/home/pages/dashboard/dashboard.component.html index 46a55cd5e..738974891 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.html +++ b/src/app/features/home/pages/dashboard/dashboard.component.html @@ -1,4 +1,4 @@ -
+
(''); + readonly activeProject = signal(null); + readonly sortColumn = signal(undefined); + readonly sortOrder = signal(SortOrder.Asc); + readonly tableParams = signal({ ...MY_PROJECTS_TABLE_PARAMS }); - protected readonly searchControl = new FormControl(''); - protected readonly activeProject = signal(null); - protected readonly sortColumn = signal(undefined); - protected readonly sortOrder = signal(SortOrder.Asc); - protected readonly tableParams = signal({ - ...MY_PROJECTS_TABLE_PARAMS, - }); + readonly projects = select(MyResourcesSelectors.getProjects); + readonly totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); + readonly areProjectsLoading = select(MyResourcesSelectors.getProjectsLoading); - protected readonly projects = select(MyResourcesSelectors.getProjects); - protected readonly totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); + readonly actions = createDispatchMap({ getMyProjects: GetMyProjects, clearMyResources: ClearMyResources }); - protected readonly filteredProjects = computed(() => { + readonly filteredProjects = computed(() => { const search = this.searchControl.value?.toLowerCase() ?? ''; return this.projects().filter((project) => project.title.toLowerCase().includes(search)); }); @@ -125,7 +122,7 @@ export class DashboardComponent implements OnInit { if (sortField) { this.sortColumn.set(sortField); - this.sortOrder.set(sortOrder || SortOrder.Asc); + this.sortOrder.set(+sortOrder || SortOrder.Asc); } if (search) { @@ -154,25 +151,14 @@ export class DashboardComponent implements OnInit { setupCleanup(): void { this.destroyRef.onDestroy(() => { - this.store.dispatch(new ClearMyResources()); + this.actions.clearMyResources(); }); } fetchProjects(): void { - this.isLoading.set(true); const filters = this.createFilters(); const page = Math.floor(this.tableParams().firstRowIndex / this.tableParams().rows) + 1; - this.store - .dispatch(new GetMyProjects(page, this.tableParams().rows, filters)) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe({ - complete: () => { - this.isLoading.set(false); - }, - error: () => { - this.isLoading.set(false); - }, - }); + this.actions.getMyProjects(page, this.tableParams().rows, filters); } createFilters(): MyResourcesSearchFilters { @@ -201,7 +187,7 @@ export class DashboardComponent implements OnInit { }); } - protected onPageChange(event: TablePageEvent): void { + onPageChange(event: TablePageEvent): void { this.tableParams.update((current) => ({ ...current, rows: event.rows, @@ -211,34 +197,29 @@ export class DashboardComponent implements OnInit { this.updateQueryParams(); } - protected onSort(event: SortEvent): void { + onSort(event: SortEvent): void { if (event.field) { this.sortColumn.set(event.field); - this.sortOrder.set(event.order === -1 ? SortOrder.Desc : SortOrder.Asc); + this.sortOrder.set(event.order as SortOrder); this.updateQueryParams(); } } - protected navigateToProject(project: MyResourcesItem): void { + navigateToProject(project: MyResourcesItem): void { this.activeProject.set(project); this.router.navigate([project.id]); } - protected createProject(): void { + createProject(): void { const dialogWidth = this.isMedium() ? '850px' : '95vw'; - this.isSubmitting.set(true); - this.dialogService - .open(CreateProjectDialogComponent, { - width: dialogWidth, - focusOnShow: false, - header: this.translateService.instant('myProjects.header.createProject'), - closeOnEscape: true, - modal: true, - closable: true, - }) - .onClose.subscribe(() => { - this.isSubmitting.set(false); - }); + this.dialogService.open(CreateProjectDialogComponent, { + width: dialogWidth, + focusOnShow: false, + header: this.translateService.instant('myProjects.header.createProject'), + closeOnEscape: true, + modal: true, + closable: true, + }); } } diff --git a/src/app/features/meetings/pages/meeting-details/meeting-details.component.html b/src/app/features/meetings/pages/meeting-details/meeting-details.component.html index c3f24e5da..8a1994f8d 100644 --- a/src/app/features/meetings/pages/meeting-details/meeting-details.component.html +++ b/src/app/features/meetings/pages/meeting-details/meeting-details.component.html @@ -27,7 +27,7 @@ (onPage)="onPageChange($event)" (onSort)="onSort($event)" [sortField]="sortColumn()" - [sortOrder]="sortOrder() === 0 ? 1 : -1" + [sortOrder]="sortOrder()" [customSort]="true" [resetPageOnSort]="false" > diff --git a/src/app/features/meetings/pages/meeting-details/meeting-details.component.ts b/src/app/features/meetings/pages/meeting-details/meeting-details.component.ts index ba43de55d..9229a7e4a 100644 --- a/src/app/features/meetings/pages/meeting-details/meeting-details.component.ts +++ b/src/app/features/meetings/pages/meeting-details/meeting-details.component.ts @@ -138,7 +138,7 @@ export class MeetingDetailsComponent { if (event.field) { this.updateQueryParams({ sortColumn: event.field, - sortOrder: event.order === -1 ? SortOrder.Desc : SortOrder.Asc, + sortOrder: event.order as SortOrder, }); } } diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html index 87b658163..8a8c42613 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html @@ -33,7 +33,7 @@ (onPage)="onPageChange($event)" (onSort)="onSort($event)" [sortField]="sortColumn()" - [sortOrder]="sortOrder() === 0 ? 1 : -1" + [sortOrder]="sortOrder()" [customSort]="true" [resetPageOnSort]="false" > diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts index eb69a5af2..9e1c24ba7 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts @@ -100,7 +100,7 @@ export class MeetingsLandingComponent { if (event.field) { this.updateQueryParams({ sortColumn: event.field, - sortOrder: event.order === -1 ? SortOrder.Desc : SortOrder.Asc, + sortOrder: event.order as SortOrder, }); } } diff --git a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts index 2f5c8506d..0e967d8f4 100644 --- a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts +++ b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts @@ -23,7 +23,7 @@ import { CreateProject, GetMyProjects, MyResourcesSelectors } from '@osf/shared/ changeDetection: ChangeDetectionStrategy.OnPush, }) export class CreateProjectDialogComponent { - protected readonly dialogRef = inject(DynamicDialogRef); + readonly dialogRef = inject(DynamicDialogRef); private actions = createDispatchMap({ getMyProjects: GetMyProjects, diff --git a/src/app/features/my-projects/my-projects.component.ts b/src/app/features/my-projects/my-projects.component.ts index a5b517912..8c09683b5 100644 --- a/src/app/features/my-projects/my-projects.component.ts +++ b/src/app/features/my-projects/my-projects.component.ts @@ -69,37 +69,34 @@ export class MyProjectsComponent implements OnInit { readonly route = inject(ActivatedRoute); readonly translateService = inject(TranslateService); - protected readonly isLoading = signal(false); - protected readonly isTablet = toSignal(inject(IS_MEDIUM)); - protected readonly tabOptions = MY_PROJECTS_TABS; - protected readonly tabOption = MyProjectsTab; - - protected readonly searchControl = new FormControl(''); - - protected readonly queryParams = toSignal(this.route.queryParams); - protected readonly currentPage = signal(1); - protected readonly currentPageSize = signal(MY_PROJECTS_TABLE_PARAMS.rows); - protected readonly selectedTab = signal(MyProjectsTab.Projects); - protected readonly activeProject = signal(null); - protected readonly sortColumn = signal(undefined); - protected readonly sortOrder = signal(SortOrder.Asc); - protected readonly tableParams = signal({ - ...MY_PROJECTS_TABLE_PARAMS, - firstRowIndex: 0, - }); - - protected readonly projects = select(MyResourcesSelectors.getProjects); - protected readonly registrations = select(MyResourcesSelectors.getRegistrations); - protected readonly preprints = select(MyResourcesSelectors.getPreprints); - protected readonly bookmarks = select(MyResourcesSelectors.getBookmarks); - protected readonly totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); - protected readonly totalRegistrationsCount = select(MyResourcesSelectors.getTotalRegistrations); - protected readonly totalPreprintsCount = select(MyResourcesSelectors.getTotalPreprints); - protected readonly totalBookmarksCount = select(MyResourcesSelectors.getTotalBookmarks); - - protected readonly bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); - - protected readonly actions = createDispatchMap({ + readonly isLoading = signal(false); + readonly isTablet = toSignal(inject(IS_MEDIUM)); + readonly tabOptions = MY_PROJECTS_TABS; + readonly tabOption = MyProjectsTab; + + readonly searchControl = new FormControl(''); + + readonly queryParams = toSignal(this.route.queryParams); + readonly currentPage = signal(1); + readonly currentPageSize = signal(MY_PROJECTS_TABLE_PARAMS.rows); + readonly selectedTab = signal(MyProjectsTab.Projects); + readonly activeProject = signal(null); + readonly sortColumn = signal(undefined); + readonly sortOrder = signal(SortOrder.Asc); + readonly tableParams = signal({ ...MY_PROJECTS_TABLE_PARAMS, firstRowIndex: 0 }); + + readonly projects = select(MyResourcesSelectors.getProjects); + readonly registrations = select(MyResourcesSelectors.getRegistrations); + readonly preprints = select(MyResourcesSelectors.getPreprints); + readonly bookmarks = select(MyResourcesSelectors.getBookmarks); + readonly totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); + readonly totalRegistrationsCount = select(MyResourcesSelectors.getTotalRegistrations); + readonly totalPreprintsCount = select(MyResourcesSelectors.getTotalPreprints); + readonly totalBookmarksCount = select(MyResourcesSelectors.getTotalBookmarks); + + readonly bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); + + readonly actions = createDispatchMap({ getBookmarksCollectionId: GetBookmarksCollectionId, clearMyProjects: ClearMyResources, getMyProjects: GetMyProjects, @@ -128,9 +125,7 @@ export class MyProjectsComponent implements OnInit { setupSearchSubscription(): void { this.searchControl.valueChanges .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)) - .subscribe((searchValue) => { - this.handleSearch(searchValue ?? ''); - }); + .subscribe((searchValue) => this.handleSearch(searchValue ?? '')); } setupTotalRecordsEffect(): void { @@ -242,7 +237,8 @@ export class MyProjectsComponent implements OnInit { createFilters(params: QueryParams): MyResourcesSearchFilters { return { searchValue: params.search || '', - searchFields: ['title', 'tags', 'description'], + searchFields: + this.selectedTab() === MyProjectsTab.Preprints ? ['title', 'tags'] : ['title', 'tags', 'description'], sortColumn: params.sortColumn, sortOrder: params.sortOrder, }; @@ -292,7 +288,7 @@ export class MyProjectsComponent implements OnInit { }); } - protected onPageChange(event: TablePageEvent): void { + onPageChange(event: TablePageEvent): void { const page = Math.floor(event.first / event.rows) + 1; const currentParams = this.queryParams() || {}; @@ -304,16 +300,16 @@ export class MyProjectsComponent implements OnInit { }); } - protected onSort(event: SortEvent): void { + onSort(event: SortEvent): void { if (event.field) { this.updateQueryParams({ sortColumn: event.field, - sortOrder: event.order === -1 ? SortOrder.Desc : SortOrder.Asc, + sortOrder: event.order as SortOrder, }); } } - protected onTabChange(tabIndex: number): void { + onTabChange(tabIndex: number): void { this.actions.clearMyProjects(); this.selectedTab.set(tabIndex); const currentParams = this.queryParams() || {}; @@ -327,7 +323,7 @@ export class MyProjectsComponent implements OnInit { }); } - protected createProject(): void { + createProject(): void { const dialogWidth = this.isTablet() ? '850px' : '95vw'; this.dialogService.open(CreateProjectDialogComponent, { @@ -340,12 +336,12 @@ export class MyProjectsComponent implements OnInit { }); } - protected navigateToProject(project: MyResourcesItem): void { + navigateToProject(project: MyResourcesItem): void { this.activeProject.set(project); this.router.navigate([project.id]); } - protected navigateToRegistry(registry: MyResourcesItem): void { + navigateToRegistry(registry: MyResourcesItem): void { this.activeProject.set(registry); this.router.navigate([registry.id]); } diff --git a/src/app/features/preprints/pages/my-preprints/my-preprints.component.html b/src/app/features/preprints/pages/my-preprints/my-preprints.component.html index e914d66ba..cef30ce96 100644 --- a/src/app/features/preprints/pages/my-preprints/my-preprints.component.html +++ b/src/app/features/preprints/pages/my-preprints/my-preprints.component.html @@ -32,7 +32,7 @@ (onPage)="onPageChange($event)" (onSort)="onSort($event)" [sortField]="sortColumn()" - [sortOrder]="sortOrder() === 0 ? 1 : -1" + [sortOrder]="sortOrder()" [customSort]="true" [resetPageOnSort]="false" > diff --git a/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts b/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts index c7671d651..6e12c9e42 100644 --- a/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts +++ b/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts @@ -23,13 +23,14 @@ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { FormControl } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { PreprintShortInfo } from '@osf/features/preprints/models'; -import { FetchMyPreprints, PreprintSelectors } from '@osf/features/preprints/store/preprint'; +import { ListInfoShortenerComponent, SearchInputComponent, SubHeaderComponent } from '@osf/shared/components'; +import { TABLE_PARAMS } from '@osf/shared/constants'; +import { SortOrder } from '@osf/shared/enums'; import { parseQueryFilterParams } from '@osf/shared/helpers'; -import { ListInfoShortenerComponent, SearchInputComponent, SubHeaderComponent } from '@shared/components'; -import { TABLE_PARAMS } from '@shared/constants'; -import { SortOrder } from '@shared/enums'; -import { QueryParams, SearchFilters, TableParameters } from '@shared/models'; +import { QueryParams, SearchFilters, TableParameters } from '@osf/shared/models'; + +import { PreprintShortInfo } from '../../models'; +import { FetchMyPreprints, PreprintSelectors } from '../../store/preprint'; @Component({ selector: 'osf-my-preprints', @@ -52,9 +53,7 @@ export class MyPreprintsComponent { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); private readonly destroyRef = inject(DestroyRef); - private actions = createDispatchMap({ - fetchMyPreprints: FetchMyPreprints, - }); + private readonly actions = createDispatchMap({ fetchMyPreprints: FetchMyPreprints }); searchControl = new FormControl(''); @@ -63,10 +62,7 @@ export class MyPreprintsComponent { sortOrder = signal(SortOrder.Asc); currentPage = signal(1); currentPageSize = signal(TABLE_PARAMS.rows); - tableParams = signal({ - ...TABLE_PARAMS, - firstRowIndex: 0, - }); + tableParams = signal({ ...TABLE_PARAMS, firstRowIndex: 0 }); preprints = select(PreprintSelectors.getMyPreprints); preprintsTotalCount = select(PreprintSelectors.getMyPreprintsTotalCount); @@ -96,7 +92,7 @@ export class MyPreprintsComponent { if (event.field) { this.updateQueryParams({ sortColumn: event.field, - sortOrder: event.order === -1 ? SortOrder.Desc : SortOrder.Asc, + sortOrder: event.order as SortOrder.Asc, }); } } diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html index e61ebec0e..557a7f29f 100644 --- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html +++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html @@ -36,62 +36,38 @@ > - @if (isCurrentTableLoading()) { - - - - - {{ 'project.overview.dialog.linkProject.table.title' | translate }} - - {{ 'project.overview.dialog.linkProject.table.created' | translate }} - - {{ 'project.overview.dialog.linkProject.table.modified' | translate }} - - - {{ 'project.overview.dialog.linkProject.table.contributors' | translate }} - - - - - - - - - - - - } @else { - - - - - {{ 'project.overview.dialog.linkProject.table.title' | translate }} - - {{ 'project.overview.dialog.linkProject.table.created' | translate }} - - {{ 'project.overview.dialog.linkProject.table.modified' | translate }} - - - {{ 'project.overview.dialog.linkProject.table.contributors' | translate }} - - - - + + + + + {{ 'project.overview.dialog.linkProject.table.title' | translate }} + + {{ 'project.overview.dialog.linkProject.table.created' | translate }} + + {{ 'project.overview.dialog.linkProject.table.modified' | translate }} + + + {{ 'project.overview.dialog.linkProject.table.contributors' | translate }} + + + + + @if (item.id) {

@@ -113,15 +89,21 @@ } - - - - - {{ 'common.search.noResultsFound' | translate }} + } @else { + + + + - - - } + } + + + + + {{ 'common.search.noResultsFound' | translate }} + + + (ResourceSearchMode.User); - protected resourceType = signal(ResourceType.Project); - protected searchControl = new FormControl(''); - - protected currentProject = select(ProjectOverviewSelectors.getProject); - protected myProjects = select(MyResourcesSelectors.getProjects); - protected isMyProjectsLoading = select(MyResourcesSelectors.getProjectsLoading); - protected myRegistrations = select(MyResourcesSelectors.getRegistrations); - protected isMyRegistrationsLoading = select(MyResourcesSelectors.getRegistrationsLoading); - protected totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); - protected totalRegistrationsCount = select(MyResourcesSelectors.getTotalRegistrations); - protected isNodeLinksSubmitting = select(NodeLinksSelectors.getNodeLinksSubmitting); - protected linkedResources = select(NodeLinksSelectors.getLinkedResources); - - protected currentTableItems = computed(() => { - return this.resourceType() === ResourceType.Project ? this.myProjects() : this.myRegistrations(); - }); - - protected isCurrentTableLoading = computed(() => { - return this.resourceType() === ResourceType.Project ? this.isMyProjectsLoading() : this.isMyRegistrationsLoading(); - }); - - protected currentTotalCount = computed(() => { - return this.resourceType() === ResourceType.Project ? this.totalProjectsCount() : this.totalRegistrationsCount(); - }); - - protected isItemLinked = computed(() => { + readonly dialogRef = inject(DynamicDialogRef); + readonly ResourceSearchMode = ResourceSearchMode; + readonly ResourceType = ResourceType; + + currentPage = signal(1); + searchMode = signal(ResourceSearchMode.User); + resourceType = signal(ResourceType.Project); + searchControl = new FormControl(''); + + skeletonData: MyResourcesItem[] = Array.from({ length: 10 }, () => ({}) as MyResourcesItem); + + currentProject = select(ProjectOverviewSelectors.getProject); + myProjects = select(MyResourcesSelectors.getProjects); + isMyProjectsLoading = select(MyResourcesSelectors.getProjectsLoading); + myRegistrations = select(MyResourcesSelectors.getRegistrations); + isMyRegistrationsLoading = select(MyResourcesSelectors.getRegistrationsLoading); + totalProjectsCount = select(MyResourcesSelectors.getTotalProjects); + totalRegistrationsCount = select(MyResourcesSelectors.getTotalRegistrations); + isNodeLinksSubmitting = select(NodeLinksSelectors.getNodeLinksSubmitting); + linkedResources = select(NodeLinksSelectors.getLinkedResources); + + currentTableItems = computed(() => + this.resourceType() === ResourceType.Project ? this.myProjects() : this.myRegistrations() + ); + + isCurrentTableLoading = computed(() => + this.resourceType() === ResourceType.Project ? this.isMyProjectsLoading() : this.isMyRegistrationsLoading() + ); + + currentTotalCount = computed(() => + this.resourceType() === ResourceType.Project ? this.totalProjectsCount() : this.totalRegistrationsCount() + ); + + isItemLinked = computed(() => { const linkedResources = this.linkedResources(); const linkedTargetIds = new Set(linkedResources.map((resource) => resource.id)); return (itemId: string) => linkedTargetIds.has(itemId); }); - protected actions = createDispatchMap({ + actions = createDispatchMap({ getProjects: GetMyProjects, getRegistrations: GetMyRegistrations, createNodeLink: CreateNodeLink, diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts index cbb76ab8f..11a667ff2 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts @@ -58,11 +58,12 @@ export class OverviewToolbarComponent { private translateService = inject(TranslateService); private toastService = inject(ToastService); private socialShareService = inject(SocialShareService); - protected destroyRef = inject(DestroyRef); private readonly router = inject(Router); private readonly route = inject(ActivatedRoute); - protected isPublic = signal(false); - protected isBookmarked = signal(false); + + destroyRef = inject(DestroyRef); + isPublic = signal(false); + isBookmarked = signal(false); isCollectionsRoute = input(false); isAdmin = input.required(); @@ -71,16 +72,16 @@ export class OverviewToolbarComponent { projectDescription = input(''); showViewOnlyLinks = input(true); - protected isBookmarksLoading = select(MyResourcesSelectors.getBookmarksLoading); - protected isBookmarksSubmitting = select(BookmarksSelectors.getBookmarksCollectionIdSubmitting); - protected bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); - protected bookmarkedProjects = select(MyResourcesSelectors.getBookmarks); - protected socialsActionItems = computed(() => { + isBookmarksLoading = select(MyResourcesSelectors.getBookmarksLoading); + isBookmarksSubmitting = select(BookmarksSelectors.getBookmarksCollectionIdSubmitting); + bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId); + bookmarkedProjects = select(MyResourcesSelectors.getBookmarks); + socialsActionItems = computed(() => { const shareableContent = this.createShareableContent(); return shareableContent ? this.buildSocialActionItems(shareableContent) : []; }); - protected readonly forkActionItems = [ + readonly forkActionItems = [ { label: 'project.overview.actions.forkProject', command: () => this.handleForkResource(), @@ -96,7 +97,7 @@ export class OverviewToolbarComponent { }, }, ]; - protected readonly ResourceType = ResourceType; + readonly ResourceType = ResourceType; get isRegistration(): boolean { return this.currentResource()?.resourceType === ResourceType.Registration; @@ -134,7 +135,7 @@ export class OverviewToolbarComponent { }); } - protected handleToggleProjectPublicity(): void { + handleToggleProjectPublicity(): void { const resource = this.currentResource(); if (!resource) return; @@ -162,7 +163,7 @@ export class OverviewToolbarComponent { }); } - protected toggleBookmark(): void { + toggleBookmark(): void { const resource = this.currentResource(); const bookmarksId = this.bookmarksCollectionId(); diff --git a/src/app/features/project/settings/mappers/settings.mapper.ts b/src/app/features/project/settings/mappers/settings.mapper.ts index 7ffca3f16..9ebc60308 100644 --- a/src/app/features/project/settings/mappers/settings.mapper.ts +++ b/src/app/features/project/settings/mappers/settings.mapper.ts @@ -36,10 +36,12 @@ export class SettingsMapper { description: data.attributes.description, isPublic: data.attributes.public, region: { - id: data.embeds.region.data.id, - name: data.embeds.region.data.attributes.name, + id: data.embeds?.region.data.id, + name: data.embeds?.region.data.attributes.name, }, - affiliatedInstitutions: InstitutionsMapper.fromInstitutionsResponse(data.embeds.affiliated_institutions), + affiliatedInstitutions: data.embeds + ? InstitutionsMapper.fromInstitutionsResponse(data.embeds.affiliated_institutions) + : [], lastFetched: Date.now(), }; } diff --git a/src/app/features/project/settings/services/settings.service.ts b/src/app/features/project/settings/services/settings.service.ts index 5a5cb8981..85f121369 100644 --- a/src/app/features/project/settings/services/settings.service.ts +++ b/src/app/features/project/settings/services/settings.service.ts @@ -14,6 +14,7 @@ import { JsonApiService } from '@shared/services'; import { SettingsMapper } from '../mappers'; import { + NodeDataJsonApi, NodeDetailsModel, NodeResponseJsonApi, ProjectSettingsData, @@ -74,8 +75,8 @@ export class SettingsService { updateProjectById(model: UpdateNodeRequestModel): Observable { return this.jsonApiService - .patch(`${this.baseUrl}/nodes/${model?.data?.id}/`, model) - .pipe(map((response) => SettingsMapper.fromNodeResponse(response.data))); + .patch(`${this.baseUrl}/nodes/${model?.data?.id}/`, model) + .pipe(map((response) => SettingsMapper.fromNodeResponse(response))); } deleteProject(projectId: string): Observable { diff --git a/src/app/features/project/settings/store/settings.state.ts b/src/app/features/project/settings/store/settings.state.ts index 4e3698001..1debaaad3 100644 --- a/src/app/features/project/settings/store/settings.state.ts +++ b/src/app/features/project/settings/store/settings.state.ts @@ -99,8 +99,11 @@ export class SettingsState { tap((updatedProject) => { ctx.patchState({ projectDetails: { - ...ctx.getState().projectDetails, - data: updatedProject, + data: { + ...ctx.getState().projectDetails.data, + title: updatedProject.title, + description: updatedProject.description, + }, isLoading: false, error: null, }, diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.html b/src/app/features/registries/pages/my-registrations/my-registrations.component.html index 999717b41..fc1b1733f 100644 --- a/src/app/features/registries/pages/my-registrations/my-registrations.component.html +++ b/src/app/features/registries/pages/my-registrations/my-registrations.component.html @@ -1,4 +1,4 @@ -

+
@if (isDraftRegistrationsLoading()) { @for (item of skeletons; track $index) { - + } } @else { @if (draftRegistrationsTotalCount() === 0) { @@ -58,7 +58,7 @@ @if (isSubmittedRegistrationsLoading()) { @for (item of skeletons; track $index) { - + } } @else { @if (submittedRegistrationsTotalCount() === 0) { @@ -91,8 +91,7 @@
diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.ts b/src/app/features/registries/pages/my-registrations/my-registrations.component.ts index df5120703..8b0dda23d 100644 --- a/src/app/features/registries/pages/my-registrations/my-registrations.component.ts +++ b/src/app/features/registries/pages/my-registrations/my-registrations.component.ts @@ -15,13 +15,17 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { UserSelectors } from '@osf/core/store/user'; -import { CustomPaginatorComponent, SelectComponent, SubHeaderComponent } from '@osf/shared/components'; -import { RegistrationCardComponent } from '@osf/shared/components/registration-card/registration-card.component'; +import { UserSelectors } from '@core/store/user'; +import { + CustomPaginatorComponent, + RegistrationCardComponent, + SelectComponent, + SubHeaderComponent, +} from '@osf/shared/components'; import { IS_XSMALL } from '@osf/shared/helpers'; import { CustomConfirmationService, ToastService } from '@osf/shared/services'; -import { REGISTRATIONS_TABS } from '../../constants/registrations-tabs'; +import { REGISTRATIONS_TABS } from '../../constants'; import { RegistrationTab } from '../../enums'; import { CreateSchemaResponse, @@ -60,19 +64,19 @@ export class MyRegistrationsComponent { private readonly customConfirmationService = inject(CustomConfirmationService); private readonly toastService = inject(ToastService); - protected readonly isMobile = toSignal(inject(IS_XSMALL)); - protected readonly tabOptions = REGISTRATIONS_TABS; + readonly isMobile = toSignal(inject(IS_XSMALL)); + readonly tabOptions = REGISTRATIONS_TABS; private currentUser = select(UserSelectors.getCurrentUser); - protected draftRegistrations = select(RegistriesSelectors.getDraftRegistrations); - protected draftRegistrationsTotalCount = select(RegistriesSelectors.getDraftRegistrationsTotalCount); - protected isDraftRegistrationsLoading = select(RegistriesSelectors.isDraftRegistrationsLoading); - protected submittedRegistrations = select(RegistriesSelectors.getSubmittedRegistrations); - protected submittedRegistrationsTotalCount = select(RegistriesSelectors.getSubmittedRegistrationsTotalCount); - protected isSubmittedRegistrationsLoading = select(RegistriesSelectors.isSubmittedRegistrationsLoading); - protected schemaResponse = select(RegistriesSelectors.getSchemaResponse); - - protected actions = createDispatchMap({ + draftRegistrations = select(RegistriesSelectors.getDraftRegistrations); + draftRegistrationsTotalCount = select(RegistriesSelectors.getDraftRegistrationsTotalCount); + isDraftRegistrationsLoading = select(RegistriesSelectors.isDraftRegistrationsLoading); + submittedRegistrations = select(RegistriesSelectors.getSubmittedRegistrations); + submittedRegistrationsTotalCount = select(RegistriesSelectors.getSubmittedRegistrationsTotalCount); + isSubmittedRegistrationsLoading = select(RegistriesSelectors.isSubmittedRegistrationsLoading); + schemaResponse = select(RegistriesSelectors.getSchemaResponse); + + actions = createDispatchMap({ getDraftRegistrations: FetchDraftRegistrations, getSubmittedRegistrations: FetchSubmittedRegistrations, deleteDraft: DeleteDraft, @@ -80,7 +84,7 @@ export class MyRegistrationsComponent { createSchemaResponse: CreateSchemaResponse, }); - protected readonly RegistrationTab = RegistrationTab; + readonly RegistrationTab = RegistrationTab; readonly provider = environment.defaultProvider; @@ -108,6 +112,7 @@ export class MyRegistrationsComponent { this.submittedFirst = 0; this.actions.getSubmittedRegistrations(this.currentUser()?.id); } + this.router.navigate([], { relativeTo: this.route, queryParams: { tab: tab === RegistrationTab.Drafts ? 'drafts' : 'submitted' }, @@ -148,11 +153,7 @@ export class MyRegistrationsComponent { onUpdateRegistration(id: string): void { this.actions .createSchemaResponse(id) - .pipe( - tap(() => { - this.navigateToJustificationPage(); - }) - ) + .pipe(tap(() => this.navigateToJustificationPage())) .subscribe(); } diff --git a/src/app/features/search/components/filters/institution-filter/institution-filter.component.spec.ts b/src/app/features/search/components/filters/institution-filter/institution-filter.component.spec.ts index 30f9aabda..96581d199 100644 --- a/src/app/features/search/components/filters/institution-filter/institution-filter.component.spec.ts +++ b/src/app/features/search/components/filters/institution-filter/institution-filter.component.spec.ts @@ -1,6 +1,7 @@ import { Store } from '@ngxs/store'; -import { MockProvider } from 'ng-mocks'; +import { TranslatePipe } from '@ngx-translate/core'; +import { MockPipe, MockProvider } from 'ng-mocks'; import { SelectChangeEvent } from 'primeng/select'; @@ -41,7 +42,7 @@ describe('InstitutionFilterComponent', () => { }); await TestBed.configureTestingModule({ - imports: [InstitutionFilterComponent], + imports: [InstitutionFilterComponent, MockPipe(TranslatePipe)], providers: [MockProvider(Store, store)], }).compileComponents(); diff --git a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html index eb77e35d6..b4652f129 100644 --- a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html +++ b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html @@ -48,7 +48,7 @@ class="w-12rem btn-full-width" [label]="'settings.developerApps.form.buttons.cancel' | translate" severity="info" - (click)="dialogRef.close()" + (onClick)="dialogRef.close()" /> { - return this.isLoading() ? this.appForm.disable() : this.appForm.enable(); - }); + effect(() => (this.isLoading() ? this.appForm.disable() : this.appForm.enable())); } ngOnInit(): void { @@ -91,21 +89,21 @@ export class DeveloperAppAddEditFormComponent implements OnInit { return; } - if (!this.isEditMode()) { - this.actions.createDeveloperApp({ ...this.appForm.value } as DeveloperAppCreateUpdate).subscribe({ - next: () => this.toastService.showSuccess('settings.developerApps.form.createSuccess'), - complete: () => this.dialogRef.close(), - }); - } else { + if (this.isEditMode()) { this.actions .updateDeveloperApp(this.initialValues()!.clientId, { ...this.appForm.value, id: this.initialValues()!.id, } as DeveloperAppCreateUpdate) .subscribe({ - next: () => this.toastService.showSuccess('settings.developerApps.form.createSuccess'), + next: () => this.toastService.showSuccess('settings.developerApps.form.updateSuccess'), complete: () => this.router.navigate(['settings/developer-apps']), }); + } else { + this.actions.createDeveloperApp({ ...this.appForm.value } as DeveloperAppCreateUpdate).subscribe({ + next: () => this.toastService.showSuccess('settings.developerApps.form.createSuccess'), + complete: () => this.dialogRef.close(), + }); } } } diff --git a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.html b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.html index c74d99e72..a65cd6d94 100644 --- a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.html +++ b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.html @@ -1,4 +1,4 @@ -
+
- @if (developerApp()) { -
-

{{ developerApp()?.name }}

+ @if (isLoading()) { + + } @else { + @if (developerApp()) { +
+

{{ developerApp()?.name }}

- -
+ +
-
- -
-

- {{ 'settings.developerApps.details.clientId.title' | translate }} -

+
+ +
+

+ {{ 'settings.developerApps.details.clientId.title' | translate }} +

-

- {{ 'settings.developerApps.details.clientId.description' | translate }} -

+

+ {{ 'settings.developerApps.details.clientId.description' | translate }} +

-
- - - - - - -
-
-
- - -
-

- {{ 'settings.developerApps.details.clientSecret.title' | translate }} -

- -

- {{ 'settings.developerApps.details.clientSecret.description' | translate }} -

- -
-
+
- + - +
- -
+ + + +
+

+ {{ 'settings.developerApps.details.clientSecret.title' | translate }} +

-
- +

+ {{ 'settings.developerApps.details.clientSecret.description' | translate }} +

+ +
+
+ + + + + + +
+ + +
+ +
+ +
-
-
+ - -
-

{{ 'settings.developerApps.form.editTitle' | translate }}

- -
-
-
+ +
+

{{ 'settings.developerApps.form.editTitle' | translate }}

+ +
+
+
+ } }
diff --git a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.scss b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.scss index 507eb8f27..4c8ed8c3a 100644 --- a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.scss +++ b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.scss @@ -1,3 +1,7 @@ +:host { + flex: 1; +} + .p-iconfield { --p-icon-size: 1.5rem; } diff --git a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.ts b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.ts index ef106ce8a..5b39b234a 100644 --- a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.ts +++ b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.ts @@ -1,4 +1,4 @@ -import { createDispatchMap, Store } from '@ngxs/store'; +import { createDispatchMap, select, Store } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; @@ -15,9 +15,8 @@ import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@a import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { CopyButtonComponent, IconComponent } from '@osf/shared/components'; -import { IS_XSMALL } from '@osf/shared/helpers'; -import { CustomConfirmationService } from '@osf/shared/services'; +import { CopyButtonComponent, IconComponent, LoadingSpinnerComponent } from '@osf/shared/components'; +import { CustomConfirmationService, ToastService } from '@osf/shared/services'; import { DeveloperAppAddEditFormComponent } from '../../components'; import { DeleteDeveloperApp, DeveloperAppsSelectors, GetDeveloperAppDetails, ResetClientSecret } from '../../store'; @@ -35,6 +34,7 @@ import { DeleteDeveloperApp, DeveloperAppsSelectors, GetDeveloperAppDetails, Res TranslatePipe, CopyButtonComponent, IconComponent, + LoadingSpinnerComponent, ], templateUrl: './developer-app-details.component.html', styleUrl: './developer-app-details.component.scss', @@ -46,17 +46,17 @@ export class DeveloperAppDetailsComponent { private readonly activatedRoute = inject(ActivatedRoute); private readonly router = inject(Router); private readonly store = inject(Store); + private readonly toastService = inject(ToastService); private readonly actions = createDispatchMap({ getDeveloperAppDetails: GetDeveloperAppDetails, deleteDeveloperApp: DeleteDeveloperApp, resetClientSecret: ResetClientSecret, }); - protected readonly isXSmall = toSignal(inject(IS_XSMALL)); - - protected readonly isClientSecretVisible = signal(false); - protected readonly clientSecret = computed(() => this.developerApp()?.clientSecret ?? ''); - protected readonly hiddenClientSecret = computed(() => '*'.repeat(this.clientSecret().length)); + readonly isClientSecretVisible = signal(false); + readonly clientSecret = computed(() => this.developerApp()?.clientSecret ?? ''); + readonly hiddenClientSecret = computed(() => '*'.repeat(this.clientSecret().length)); + readonly isLoading = select(DeveloperAppsSelectors.isLoading); readonly clientId = toSignal( this.activatedRoute.params.pipe( @@ -86,10 +86,9 @@ export class DeveloperAppDetailsComponent { headerParams: { name: this.developerApp()?.name }, messageKey: 'settings.developerApps.confirmation.delete.message', onConfirm: () => { - this.actions.deleteDeveloperApp(this.clientId()).subscribe({ - complete: () => { - this.router.navigate(['settings/developer-apps']); - }, + this.actions.deleteDeveloperApp(this.clientId()).subscribe(() => { + this.router.navigate(['settings/developer-apps']); + this.toastService.showSuccess('settings.developerApps.confirmation.delete.success'); }); }, }); @@ -101,7 +100,10 @@ export class DeveloperAppDetailsComponent { headerParams: { name: this.developerApp()?.name }, messageKey: 'settings.developerApps.confirmation.resetSecret.message', acceptLabelKey: 'settings.developerApps.details.clientSecret.reset', - onConfirm: () => this.actions.resetClientSecret(this.clientId()), + onConfirm: () => + this.actions + .resetClientSecret(this.clientId()) + .subscribe(() => this.toastService.showSuccess('settings.developerApps.confirmation.resetSecret.success')), }); } } diff --git a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts index 7389509eb..11fa6a47c 100644 --- a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts +++ b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts @@ -9,7 +9,7 @@ import { Skeleton } from 'primeng/skeleton'; import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { RouterLink } from '@angular/router'; -import { CustomConfirmationService } from '@osf/shared/services'; +import { CustomConfirmationService, ToastService } from '@osf/shared/services'; import { DeveloperApp } from '../../models'; import { DeleteDeveloperApp, DeveloperAppsSelectors, GetDeveloperApps } from '../../store'; @@ -27,8 +27,9 @@ export class DeveloperAppsListComponent implements OnInit { deleteDeveloperApp: DeleteDeveloperApp, }); private readonly customConfirmationService = inject(CustomConfirmationService); + private readonly toastService = inject(ToastService); - protected readonly isLoading = select(DeveloperAppsSelectors.isLoading); + readonly isLoading = select(DeveloperAppsSelectors.isLoading); readonly developerApplications = select(DeveloperAppsSelectors.getDeveloperApps); ngOnInit(): void { @@ -42,7 +43,10 @@ export class DeveloperAppsListComponent implements OnInit { headerKey: 'settings.developerApps.confirmation.delete.title', headerParams: { name: developerApp.name }, messageKey: 'settings.developerApps.confirmation.delete.message', - onConfirm: () => this.actions.deleteDeveloperApp(developerApp.clientId), + onConfirm: () => + this.actions + .deleteDeveloperApp(developerApp.clientId) + .subscribe(() => this.toastService.showSuccess('settings.developerApps.confirmation.delete.success')), }); } } diff --git a/src/app/features/settings/developer-apps/services/developer-apps.service.ts b/src/app/features/settings/developer-apps/services/developer-apps.service.ts index 23a02ec70..200e3a8fa 100644 --- a/src/app/features/settings/developer-apps/services/developer-apps.service.ts +++ b/src/app/features/settings/developer-apps/services/developer-apps.service.ts @@ -14,8 +14,8 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class DeveloperApplicationsService { - jsonApiService = inject(JsonApiService); - baseUrl = `${environment.apiUrl}/applications/`; + private readonly jsonApiService = inject(JsonApiService); + private readonly baseUrl = `${environment.apiUrl}/applications/`; getApplications(): Observable { return this.jsonApiService @@ -25,7 +25,7 @@ export class DeveloperApplicationsService { getApplicationDetails(clientId: string): Observable { return this.jsonApiService - .get>(`${this.baseUrl}/${clientId}/`) + .get>(`${this.baseUrl}${clientId}/`) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response.data))); } @@ -41,7 +41,7 @@ export class DeveloperApplicationsService { const request = DeveloperAppMapper.toUpdateRequest(developerAppUpdate); return this.jsonApiService - .patch(`${this.baseUrl}/${clientId}/`, request) + .patch(`${this.baseUrl}${clientId}/`, request) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response))); } @@ -49,11 +49,11 @@ export class DeveloperApplicationsService { const request = DeveloperAppMapper.toResetSecretRequest(clientId); return this.jsonApiService - .patch(`${this.baseUrl}/${clientId}/`, request) + .patch(`${this.baseUrl}${clientId}/`, request) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response))); } deleteApplication(clientId: string): Observable { - return this.jsonApiService.delete(`${this.baseUrl}/${clientId}/`); + return this.jsonApiService.delete(`${this.baseUrl}${clientId}/`); } } diff --git a/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts b/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts index 1e8fa57b7..f4c70b6d7 100644 --- a/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts +++ b/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts @@ -3,3 +3,9 @@ import { AsyncStateModel } from '@osf/shared/models'; import { DeveloperApp } from '../models'; export type DeveloperAppsStateModel = AsyncStateModel; + +export const DEVELOPER_APPS_STATE_DEFAULTS: DeveloperAppsStateModel = { + data: [], + isLoading: false, + error: null, +}; diff --git a/src/app/features/settings/developer-apps/store/developer-apps.state.ts b/src/app/features/settings/developer-apps/store/developer-apps.state.ts index 52193733e..c83afbe13 100644 --- a/src/app/features/settings/developer-apps/store/developer-apps.state.ts +++ b/src/app/features/settings/developer-apps/store/developer-apps.state.ts @@ -16,15 +16,11 @@ import { ResetClientSecret, UpdateDeveloperApp, } from './developer-apps.actions'; -import { DeveloperAppsStateModel } from './developer-apps.state-model'; +import { DEVELOPER_APPS_STATE_DEFAULTS, DeveloperAppsStateModel } from './developer-apps.state-model'; @State({ name: 'developerApps', - defaults: { - data: [], - isLoading: false, - error: null, - }, + defaults: DEVELOPER_APPS_STATE_DEFAULTS, }) @Injectable() export class DeveloperAppsState { @@ -52,6 +48,8 @@ export class DeveloperAppsState { return of(developerAppFromState); } + ctx.patchState({ ...state.data, isLoading: true, error: null }); + return this.developerAppsService.getApplicationDetails(action.clientId).pipe( tap((fetchedApp) => { ctx.setState( diff --git a/src/app/features/settings/notifications/notifications.component.html b/src/app/features/settings/notifications/notifications.component.html index 0da3bff0e..cc51848d8 100644 --- a/src/app/features/settings/notifications/notifications.component.html +++ b/src/app/features/settings/notifications/notifications.component.html @@ -103,7 +103,7 @@

{{ 'settings.notifications.notificationPreferences.title' | translate }}

- @for (_ of [1, 2, 3, 4, 5]; track $index) { + @for (_ of [1, 2, 3]; track $index) { } diff --git a/src/app/features/settings/notifications/notifications.component.ts b/src/app/features/settings/notifications/notifications.component.ts index aa0f58c16..d69669bcd 100644 --- a/src/app/features/settings/notifications/notifications.component.ts +++ b/src/app/features/settings/notifications/notifications.component.ts @@ -68,12 +68,13 @@ export class NotificationsComponent implements OnInit { [EmailPreferencesFormControls.SubscribeOsfHelpEmail]: this.fb.control(false, { nonNullable: true }), }); - protected readonly SUBSCRIPTION_EVENTS = SUBSCRIPTION_EVENTS; - protected subscriptionFrequencyOptions = Object.entries(SubscriptionFrequency).map(([key, value]) => ({ + readonly SUBSCRIPTION_EVENTS = SUBSCRIPTION_EVENTS; + subscriptionFrequencyOptions = Object.entries(SubscriptionFrequency).map(([key, value]) => ({ label: key, value, })); - protected notificationSubscriptionsForm = this.fb.group( + + notificationSubscriptionsForm = this.fb.group( SUBSCRIPTION_EVENTS.reduce( (control, { event }) => { control[event] = this.fb.control(SubscriptionFrequency.Never, { nonNullable: true }); @@ -127,11 +128,9 @@ export class NotificationsComponent implements OnInit { const id = `${user.id}_${event}`; this.loaderService.show(); - this.actions.updateNotificationSubscription({ id, frequency }).subscribe({ - complete: () => { - this.loaderService.hide(); - this.toastService.showSuccess('settings.notifications.notificationPreferences.successUpdate'); - }, + this.actions.updateNotificationSubscription({ id, frequency }).subscribe(() => { + this.loaderService.hide(); + this.toastService.showSuccess('settings.notifications.notificationPreferences.successUpdate'); }); } @@ -144,9 +143,11 @@ export class NotificationsComponent implements OnInit { private updateNotificationSubscriptionsForm() { const patch: Record = {}; + for (const sub of this.notificationSubscriptions()) { patch[sub.event] = sub.frequency; } + this.notificationSubscriptionsForm.patchValue(patch); } } diff --git a/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.html b/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.html index 58e74fb85..df184c214 100644 --- a/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.html +++ b/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.html @@ -49,7 +49,7 @@ class="w-12rem btn-full-width" [label]="'settings.tokens.form.buttons.cancel' | translate" severity="info" - (click)="dialogRef.close()" + (onClick)="dialogRef.close()" /> {{ token()?.name }} class="btn-full-width" [label]="'settings.tokens.details.deleteButton' | translate" severity="danger" - (click)="deleteToken()" + (onClick)="deleteToken()" />
diff --git a/src/app/features/settings/tokens/store/tokens.selectors.ts b/src/app/features/settings/tokens/store/tokens.selectors.ts index 5faf7c033..899161344 100644 --- a/src/app/features/settings/tokens/store/tokens.selectors.ts +++ b/src/app/features/settings/tokens/store/tokens.selectors.ts @@ -11,11 +11,6 @@ export class TokensSelectors { return state.scopes.data; } - @Selector([TokensState]) - static isScopesLoading(state: TokensStateModel) { - return state.scopes.isLoading; - } - @Selector([TokensState]) static getTokens(state: TokensStateModel): TokenModel[] { return state.tokens.data; diff --git a/src/app/features/settings/tokens/tokens.component.spec.ts b/src/app/features/settings/tokens/tokens.component.spec.ts new file mode 100644 index 000000000..de8bf16cc --- /dev/null +++ b/src/app/features/settings/tokens/tokens.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TokensComponent } from './tokens.component'; + +import { OSFTestingModule } from '@testing/osf.testing.module'; + +describe.skip('TokensComponent', () => { + let component: TokensComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TokensComponent, OSFTestingModule], + }).compileComponents(); + + fixture = TestBed.createComponent(TokensComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/my-projects-table/my-projects-table.component.html b/src/app/shared/components/my-projects-table/my-projects-table.component.html index 141f1fda4..9e0685657 100644 --- a/src/app/shared/components/my-projects-table/my-projects-table.component.html +++ b/src/app/shared/components/my-projects-table/my-projects-table.component.html @@ -1,65 +1,43 @@
- @if (isLoading()) { - - - - - {{ 'myProjects.table.columns.title' | translate }} - - - {{ 'myProjects.table.columns.contributors' | translate }} - - {{ 'myProjects.table.columns.modified' | translate }} - - - - - - - - - - - - - } @else { - - - - - {{ 'myProjects.table.columns.title' | translate }} - - - {{ 'myProjects.table.columns.contributors' | translate }} - - {{ 'myProjects.table.columns.modified' | translate }} - - - - - + + + + + + {{ 'myProjects.table.columns.title' | translate }} + + + {{ 'myProjects.table.columns.contributors' | translate }} + + {{ 'myProjects.table.columns.modified' | translate }} + + + + + + @if (item?.id) {

@@ -74,13 +52,19 @@ {{ item.dateModified | date: 'MMM d, y, h:mm a' }} - - - - - {{ 'common.search.noResultsFound' | translate }} + } @else { + + + + - - - } + } + + + + + {{ 'common.search.noResultsFound' | translate }} + + +

diff --git a/src/app/shared/components/my-projects-table/my-projects-table.component.ts b/src/app/shared/components/my-projects-table/my-projects-table.component.ts index a748d2238..f553444df 100644 --- a/src/app/shared/components/my-projects-table/my-projects-table.component.ts +++ b/src/app/shared/components/my-projects-table/my-projects-table.component.ts @@ -34,6 +34,8 @@ export class MyProjectsTableComponent { sort = output(); itemClick = output(); + skeletonData: MyResourcesItem[] = Array.from({ length: 10 }, () => ({}) as MyResourcesItem); + protected onPageChange(event: TablePageEvent): void { this.pageChange.emit(event); } diff --git a/src/app/shared/enums/sort-order.enum.ts b/src/app/shared/enums/sort-order.enum.ts index 7b5414cff..bc8b3edf3 100644 --- a/src/app/shared/enums/sort-order.enum.ts +++ b/src/app/shared/enums/sort-order.enum.ts @@ -1,4 +1,4 @@ export enum SortOrder { - Asc = 0, - Desc, + Asc = 1, + Desc = -1, } diff --git a/src/app/shared/mappers/registration/registration.mapper.ts b/src/app/shared/mappers/registration/registration.mapper.ts index d9ca3570f..6ef5830ce 100644 --- a/src/app/shared/mappers/registration/registration.mapper.ts +++ b/src/app/shared/mappers/registration/registration.mapper.ts @@ -80,7 +80,7 @@ export class RegistrationMapper { title: registration.attributes.title, description: registration.attributes.description || '', status: MapRegistryStatus(registration.attributes), - dateCreated: registration.attributes.datetime_initiated, + dateCreated: registration.attributes.date_created, dateModified: registration.attributes.date_modified, registrationTemplate: registration.embeds?.registration_schema?.data?.attributes?.name || '', registry: registration.embeds?.provider?.data?.attributes?.name || '', diff --git a/src/app/shared/models/registration/registration-json-api.model.ts b/src/app/shared/models/registration/registration-json-api.model.ts index 873388f81..370c1e0aa 100644 --- a/src/app/shared/models/registration/registration-json-api.model.ts +++ b/src/app/shared/models/registration/registration-json-api.model.ts @@ -44,7 +44,7 @@ export interface DraftRegistrationAttributesJsonApi { export interface RegistrationAttributesJsonApi { access_requests_enabled: boolean; - datetime_initiated: string; + date_created: string; date_modified: string; description: string; embargoed: boolean; diff --git a/src/app/shared/models/table-parameters.model.ts b/src/app/shared/models/table-parameters.model.ts index 5b8ea2259..97617946d 100644 --- a/src/app/shared/models/table-parameters.model.ts +++ b/src/app/shared/models/table-parameters.model.ts @@ -1,4 +1,4 @@ -import { SortOrder } from '@osf/shared/enums/sort-order.enum'; +import { SortOrder } from '../enums'; export interface TableParameters { rows: number; diff --git a/src/app/shared/services/bookmarks.service.ts b/src/app/shared/services/bookmarks.service.ts index 6216fa276..e4f03586e 100644 --- a/src/app/shared/services/bookmarks.service.ts +++ b/src/app/shared/services/bookmarks.service.ts @@ -5,6 +5,8 @@ import { inject, Injectable } from '@angular/core'; import { SparseCollectionsResponseJsonApi } from '@shared/models'; import { JsonApiService } from '@shared/services'; +import { ResourceType } from '../enums'; + import { environment } from 'src/environments/environment'; @Injectable({ @@ -12,6 +14,15 @@ import { environment } from 'src/environments/environment'; }) export class BookmarksService { private jsonApiService = inject(JsonApiService); + private readonly urlMap = new Map([ + [ResourceType.Project, 'linked_nodes'], + [ResourceType.Registration, 'linked_registrations'], + ]); + + private readonly resourceMap = new Map([ + [ResourceType.Project, 'nodes'], + [ResourceType.Registration, 'registrations'], + ]); getBookmarksCollectionId(): Observable { const params: Record = { @@ -28,58 +39,16 @@ export class BookmarksService { ); } - addProjectToBookmarks(bookmarksId: string, projectId: string): Observable { - const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/linked_nodes/`; - const payload = { - data: [ - { - type: 'nodes', - id: projectId, - }, - ], - }; - - return this.jsonApiService.post(url, payload); - } - - removeProjectFromBookmarks(bookmarksId: string, projectId: string): Observable { - const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/linked_nodes/`; - const payload = { - data: [ - { - type: 'nodes', - id: projectId, - }, - ], - }; - - return this.jsonApiService.delete(url, payload); - } - - addRegistrationToBookmarks(bookmarksId: string, registryId: string): Observable { - const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/linked_registrations/`; - const payload = { - data: [ - { - type: 'registrations', - id: registryId, - }, - ], - }; + addResourceToBookmarks(bookmarksId: string, resourceId: string, resourceType: ResourceType): Observable { + const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/${this.urlMap.get(resourceType)}/`; + const payload = { data: [{ type: this.resourceMap.get(resourceType), id: resourceId }] }; return this.jsonApiService.post(url, payload); } - removeRegistrationFromBookmarks(bookmarksId: string, registryId: string): Observable { - const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/linked_registrations/`; - const payload = { - data: [ - { - type: 'registrations', - id: registryId, - }, - ], - }; + removeResourceFromBookmarks(bookmarksId: string, resourceId: string, resourceType: ResourceType): Observable { + const url = `${environment.apiUrl}/collections/${bookmarksId}/relationships/${this.urlMap.get(resourceType)}/`; + const payload = { data: [{ type: this.resourceMap.get(resourceType), id: resourceId }] }; return this.jsonApiService.delete(url, payload); } diff --git a/src/app/shared/services/my-resources.service.ts b/src/app/shared/services/my-resources.service.ts index d7cb7b931..a8bd35b3f 100644 --- a/src/app/shared/services/my-resources.service.ts +++ b/src/app/shared/services/my-resources.service.ts @@ -23,7 +23,6 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class MyResourcesService { - private apiUrl = environment.apiUrl; private sortFieldMap: Record = { title: 'title', dateModified: 'date_modified', diff --git a/src/app/shared/stores/bookmarks/bookmarks.model.ts b/src/app/shared/stores/bookmarks/bookmarks.model.ts index fb832bb89..dca9eb9b6 100644 --- a/src/app/shared/stores/bookmarks/bookmarks.model.ts +++ b/src/app/shared/stores/bookmarks/bookmarks.model.ts @@ -3,3 +3,12 @@ import { AsyncStateModel } from '@shared/models/store'; export interface BookmarksStateModel { bookmarksId: AsyncStateModel; } + +export const BOOKMARKS_DEFAULTS: BookmarksStateModel = { + bookmarksId: { + data: '', + isLoading: false, + isSubmitting: false, + error: null, + }, +}; diff --git a/src/app/shared/stores/bookmarks/bookmarks.selectors.ts b/src/app/shared/stores/bookmarks/bookmarks.selectors.ts index d040c90f1..bb3781f10 100644 --- a/src/app/shared/stores/bookmarks/bookmarks.selectors.ts +++ b/src/app/shared/stores/bookmarks/bookmarks.selectors.ts @@ -18,9 +18,4 @@ export class BookmarksSelectors { static getBookmarksCollectionIdSubmitting(state: BookmarksStateModel) { return state.bookmarksId.isSubmitting; } - - @Selector([BookmarksState]) - static getBookmarksError(state: BookmarksStateModel) { - return state.bookmarksId.error; - } } diff --git a/src/app/shared/stores/bookmarks/bookmarks.state.ts b/src/app/shared/stores/bookmarks/bookmarks.state.ts index 25020a87d..90cb96a30 100644 --- a/src/app/shared/stores/bookmarks/bookmarks.state.ts +++ b/src/app/shared/stores/bookmarks/bookmarks.state.ts @@ -1,24 +1,14 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { catchError, EMPTY, tap } from 'rxjs'; +import { catchError, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { ResourceType } from '@shared/enums'; import { handleSectionError } from '@shared/helpers'; import { BookmarksService } from '@shared/services'; import { AddResourceToBookmarks, GetBookmarksCollectionId, RemoveResourceFromBookmarks } from './bookmarks.actions'; -import { BookmarksStateModel } from './bookmarks.model'; - -const BOOKMARKS_DEFAULTS: BookmarksStateModel = { - bookmarksId: { - data: '', - isLoading: false, - isSubmitting: false, - error: null, - }, -}; +import { BOOKMARKS_DEFAULTS, BookmarksStateModel } from './bookmarks.model'; @State({ name: 'bookmarks', @@ -63,34 +53,19 @@ export class BookmarksState { }, }); - switch (action.resourceType) { - case ResourceType.Project: - return this.bookmarksService.addProjectToBookmarks(action.bookmarksId, action.resourceId).pipe( - tap(() => { - ctx.patchState({ - bookmarksId: { - ...state.bookmarksId, - isSubmitting: false, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) - ); - case ResourceType.Registration: - return this.bookmarksService.addRegistrationToBookmarks(action.bookmarksId, action.resourceId).pipe( - tap(() => { - ctx.patchState({ - bookmarksId: { - ...state.bookmarksId, - isSubmitting: false, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) - ); - default: - return EMPTY; - } + return this.bookmarksService + .addResourceToBookmarks(action.bookmarksId, action.resourceId, action.resourceType) + .pipe( + tap(() => { + ctx.patchState({ + bookmarksId: { + ...state.bookmarksId, + isSubmitting: false, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) + ); } @Action(RemoveResourceFromBookmarks) @@ -103,33 +78,18 @@ export class BookmarksState { }, }); - switch (action.resourceType) { - case ResourceType.Project: - return this.bookmarksService.removeProjectFromBookmarks(action.bookmarksId, action.resourceId).pipe( - tap(() => { - ctx.patchState({ - bookmarksId: { - ...state.bookmarksId, - isSubmitting: false, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) - ); - case ResourceType.Registration: - return this.bookmarksService.removeRegistrationFromBookmarks(action.bookmarksId, action.resourceId).pipe( - tap(() => { - ctx.patchState({ - bookmarksId: { - ...state.bookmarksId, - isSubmitting: false, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) - ); - default: - return EMPTY; - } + return this.bookmarksService + .removeResourceFromBookmarks(action.bookmarksId, action.resourceId, action.resourceType) + .pipe( + tap(() => { + ctx.patchState({ + bookmarksId: { + ...state.bookmarksId, + isSubmitting: false, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'bookmarksId', error)) + ); } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 19c9a7055..53d9215c2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -145,7 +145,7 @@ "meetings": "Meetings", "myProjects": "My Projects", "projects": "Projects", - "myResources": "My Resources", + "myOsf": "My OSF", "registries": "Registries", "overview": "Overview", "discover": "Discover", @@ -650,8 +650,8 @@ "success": "Project has been duplicated successfully" }, "bookmark": { - "add": "Project has been added to bookmarks", - "remove": "Project has been removed from bookmarks" + "add": "Successfully added to bookmarks", + "remove": "Successfully removed from bookmarks" } }, "linkProject": { @@ -972,7 +972,7 @@ "selectFilesFoldersDescription": "Click on a row (outside of the file or folder name) to show further actions in the top toolbar. Click on more rows to select multiple files. To select a range of files, click a file to begin selection and then shift+click another file to select the range.", "openViewFiles": "Open/View Files", "openViewFilesDescription": "Click a file name to go to view the file in the OSF. Opens file in a new tab.", - "upload": "Upload.", + "upload": "Upload", "uploadDescription": "There are two ways to upload files. Open the storage provider or folder where you intend to upload files; you can then drag files from your desktop into files list area OR click “Upload Files”, and select your files. Note: File names with special characters may cause unexpected behavior with certain addons.", "createFolder": "Create a folder", "createFolderDescription": "To create a folder to help organize your files, click “Create a Folder”. Note: You can also organize your project content by creating Components.", @@ -1349,11 +1349,13 @@ "confirmation": { "delete": { "title": "Delete App {{name}}?", - "message": "Are you sure you want to delete this developer app? All users' access tokens will be revoked. This cannot be reversed." + "message": "Are you sure you want to delete this developer app? All users' access tokens will be revoked. This cannot be reversed.", + "success": "Delete App successfully deleted." }, "resetSecret": { "title": "Reset Client Secret?", - "message": "Resetting the client secret will render your application unusable until it is updated with the new client secret, and all users must reauthorize access. Previously issued access tokens will no longer work.

Are you sure you want to reset the client secret? This cannot be reversed." + "message": "Resetting the client secret will render your application unusable until it is updated with the new client secret, and all users must reauthorize access. Previously issued access tokens will no longer work.

Are you sure you want to reset the client secret? This cannot be reversed.", + "success": "Client secret successfully reset." } } },