From b34c369a71cf818df6c5b331d450ecd5849a11a2 Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 14 Oct 2025 12:06:24 +0300 Subject: [PATCH 1/2] fix(bugs): fixed ui bugs --- .../core/components/layout/layout.component.html | 2 +- .../home/pages/dashboard/dashboard.component.html | 9 --------- .../home/pages/dashboard/dashboard.component.scss | 4 ---- .../overview-wiki/overview-wiki.component.html | 13 +++++++++++-- .../overview-wiki/overview-wiki.component.ts | 14 +++++++++++--- .../registry-resources.component.html | 6 +++++- .../token-add-edit-form.component.ts | 6 ++---- .../settings/tokens/mappers/token.mapper.ts | 1 + .../settings/tokens/models/token-json-api.model.ts | 1 + .../features/settings/tokens/models/token.model.ts | 1 + src/assets/i18n/en.json | 10 +++------- 11 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/app/core/components/layout/layout.component.html b/src/app/core/components/layout/layout.component.html index a6b12163d..c67d66397 100644 --- a/src/app/core/components/layout/layout.component.html +++ b/src/app/core/components/layout/layout.component.html @@ -9,10 +9,10 @@ } @else { - @if (isMedium()) { } + } diff --git a/src/app/features/home/pages/dashboard/dashboard.component.html b/src/app/features/home/pages/dashboard/dashboard.component.html index 1cebfca76..d2e420a6d 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.html +++ b/src/app/features/home/pages/dashboard/dashboard.component.html @@ -58,15 +58,6 @@

{{ 'home.loggedIn.latestResearch.title' | translate }}

severity="success" /> - -
-
-

{{ 'home.loggedIn.hosting.title' | translate }}

-

{{ 'home.loggedIn.hosting.subtitle' | translate }}

-
- - -
} @else { -

{{ 'project.overview.wiki.title' | translate }}

+
+

{{ 'project.overview.wiki.title' | translate }}

+ +
+ @if (isWikiLoading()) { } @else { @@ -14,7 +23,7 @@

{{ 'project.overview.wiki.title' | translate }}

} @else { -
+
} } diff --git a/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts b/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts index 186c87cd3..979f1992f 100644 --- a/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts +++ b/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts @@ -2,25 +2,33 @@ import { select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; +import { Button } from 'primeng/button'; import { Skeleton } from 'primeng/skeleton'; -import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core'; +import { Router } from '@angular/router'; import { MarkdownComponent, TruncatedTextComponent } from '@osf/shared/components'; import { WikiSelectors } from '@osf/shared/stores'; @Component({ selector: 'osf-overview-wiki', - imports: [Skeleton, TranslatePipe, TruncatedTextComponent, MarkdownComponent], + imports: [Skeleton, TranslatePipe, TruncatedTextComponent, MarkdownComponent, Button], templateUrl: './overview-wiki.component.html', styleUrl: './overview-wiki.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class OverviewWikiComponent { + private readonly router = inject(Router); + isWikiLoading = select(WikiSelectors.getHomeWikiLoading); wikiContent = select(WikiSelectors.getHomeWikiContent); resourceId = input(''); - wikiLink = () => ['/', this.resourceId(), 'wiki']; + wikiLink = computed(() => ['/', this.resourceId(), 'wiki']); + + navigateToWiki() { + this.router.navigate(this.wikiLink()); + } } diff --git a/src/app/features/registry/pages/registry-resources/registry-resources.component.html b/src/app/features/registry/pages/registry-resources/registry-resources.component.html index d03ab0cab..68ffcaef7 100644 --- a/src/app/features/registry/pages/registry-resources/registry-resources.component.html +++ b/src/app/features/registry/pages/registry-resources/registry-resources.component.html @@ -11,7 +11,11 @@ } @else {

- {{ 'resources.description' | translate }} + @if (addButtonVisible()) { + {{ 'resources.linkDoi' | translate }} + } + + {{ 'resources.description' | translate }} {{ 'common.labels.learnMore' | translate }} diff --git a/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.ts b/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.ts index 83d098b35..2abd3b83e 100644 --- a/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.ts +++ b/src/app/features/settings/tokens/components/token-add-edit-form/token-add-edit-form.component.ts @@ -64,9 +64,7 @@ export class TokenAddEditFormComponent implements OnInit { }); constructor() { - effect(() => { - return this.isLoading() ? this.tokenForm.disable() : this.tokenForm.enable(); - }); + effect(() => (this.isLoading() ? this.tokenForm.disable() : this.tokenForm.enable())); } ngOnInit(): void { @@ -97,7 +95,7 @@ export class TokenAddEditFormComponent implements OnInit { const tokens = this.store.selectSignal(TokensSelectors.getTokens); const newToken = tokens()[0]; this.dialogRef.close(); - this.showTokenCreatedDialog(newToken.name, newToken.id); + this.showTokenCreatedDialog(newToken.name, newToken.tokenId); }, }); } else { diff --git a/src/app/features/settings/tokens/mappers/token.mapper.ts b/src/app/features/settings/tokens/mappers/token.mapper.ts index 92451a3cf..00a23d80c 100644 --- a/src/app/features/settings/tokens/mappers/token.mapper.ts +++ b/src/app/features/settings/tokens/mappers/token.mapper.ts @@ -16,6 +16,7 @@ export class TokenMapper { static fromGetResponse(response: TokenGetResponseJsonApi): TokenModel { return { id: response.id, + tokenId: response.attributes.token_id, name: response.attributes.name, scopes: response.embeds.scopes.data.map((item) => item.id), }; diff --git a/src/app/features/settings/tokens/models/token-json-api.model.ts b/src/app/features/settings/tokens/models/token-json-api.model.ts index 66bc0ac31..c2875fe9b 100644 --- a/src/app/features/settings/tokens/models/token-json-api.model.ts +++ b/src/app/features/settings/tokens/models/token-json-api.model.ts @@ -16,6 +16,7 @@ export interface TokenGetResponseJsonApi { interface TokenAttributesJsonApi { name: string; + token_id: string; } interface TokenEmbedsJsonApi { diff --git a/src/app/features/settings/tokens/models/token.model.ts b/src/app/features/settings/tokens/models/token.model.ts index 735b29bcd..2174e0e66 100644 --- a/src/app/features/settings/tokens/models/token.model.ts +++ b/src/app/features/settings/tokens/models/token.model.ts @@ -1,5 +1,6 @@ export interface TokenModel { id: string; + tokenId: string; name: string; scopes: string[]; } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 07489ea56..e29e85ef0 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -326,11 +326,6 @@ "title": "Browse the latest research", "subtitle": "Check out the latest preprints hosted on OSF covering a variety of research areas", "button": "View Preprints" - }, - "hosting": { - "title": "Hosting a conference or meeting?", - "subtitle": "Use the OSF for Meetings service to provide a central location for conference submissions", - "button": "View Meetings" } }, "loggedOut": { @@ -672,7 +667,7 @@ }, "wiki": { "title": "Wiki", - "noWikiMessage": "Add important information, links, or images here to describe your project." + "noWikiMessage": "Click the “Edit” to add important information, links, or images here to describe your project." }, "files": { "title": "Files", @@ -2889,7 +2884,8 @@ }, "resources": { "title": "Resources", - "description": "Link a DOI from a repository to your registration by clicking “Add resource” button. Contributors affirmed to adhere to the criteria for each badge.", + "linkDoi": "Link a DOI from a repository to your registration by clicking “Add resource” button.", + "description": " Contributors affirmed to adhere to the criteria for each badge.", "add": "Add Resource", "edit": "Edit Resource", "delete": "Delete Resource", From 41b7685816aa7ca0ec9d0273e853d6862a10e12a Mon Sep 17 00:00:00 2001 From: nsemets Date: Thu, 16 Oct 2025 17:35:04 +0300 Subject: [PATCH 2/2] fix(contributors): fixed pagination for contributors --- .../contributors/contributors.component.ts | 4 ++ .../contributors-dialog.component.html | 5 +- .../contributors-dialog.component.ts | 30 ++++++---- .../features/metadata/metadata.component.ts | 31 ++++------ .../metadata/store/metadata.selectors.ts | 12 ++++ .../preprints-contributors.component.html | 23 ++++---- .../preprints-contributors.component.ts | 36 +++++------- .../registries-contributors.component.html | 3 +- .../registries-contributors.component.ts | 42 ++++++-------- .../contributors-table.component.html | 2 +- .../contributors-table.component.ts | 23 +------- .../stores/contributors/contributors.model.ts | 17 ++++-- .../contributors/contributors.selectors.ts | 12 +++- .../stores/contributors/contributors.state.ts | 57 ++++++++++++------- 14 files changed, 151 insertions(+), 146 deletions(-) diff --git a/src/app/features/contributors/contributors.component.ts b/src/app/features/contributors/contributors.component.ts index 46ae814a1..25754e14a 100644 --- a/src/app/features/contributors/contributors.component.ts +++ b/src/app/features/contributors/contributors.component.ts @@ -123,11 +123,15 @@ export class ContributorsComponent implements OnInit { readonly hasAdminAccess = select(CurrentResourceSelectors.hasResourceAdminAccess); readonly resourceAccessRequestEnabled = select(CurrentResourceSelectors.resourceAccessRequestEnabled); readonly currentUser = select(UserSelectors.getCurrentUser); + page = select(ContributorsSelectors.getContributorsPageNumber); + pageSize = select(ContributorsSelectors.getContributorsPageSize); readonly tableParams = computed(() => ({ ...DEFAULT_TABLE_PARAMS, totalRecords: this.contributorsTotalCount(), paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows, + firstRowIndex: (this.page() - 1) * this.pageSize(), + rows: this.pageSize(), })); canCreateViewLink = computed(() => !!this.resourceDetails() && !!this.resourceId()); diff --git a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.html b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.html index 911229205..b6ee30cba 100644 --- a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.html +++ b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.html @@ -1,6 +1,6 @@

- @if (isCurrentUserAdminContributor()) { + @if (hasAdminAccess()) { @if (hasChanges) { diff --git a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts index 93ef2e95d..dd5693767 100644 --- a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts +++ b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts @@ -4,6 +4,7 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { TablePageEvent } from 'primeng/table'; import { filter } from 'rxjs'; @@ -29,7 +30,7 @@ import { ContributorsTableComponent, } from '@osf/shared/components/contributors'; import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants'; -import { AddContributorType, ContributorPermission, ResourceType } from '@osf/shared/enums'; +import { AddContributorType, ResourceType } from '@osf/shared/enums'; import { findChangedItems } from '@osf/shared/helpers'; import { ContributorDialogAddModel, ContributorModel, TableParameters } from '@osf/shared/models'; import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services'; @@ -39,11 +40,14 @@ import { BulkUpdateContributors, ContributorsSelectors, DeleteContributor, + GetAllContributors, UpdateBibliographyFilter, UpdateContributorsSearchValue, UpdatePermissionFilter, } from '@osf/shared/stores'; +import { MetadataSelectors } from '../../store'; + @Component({ selector: 'osf-contributors-dialog', imports: [Button, SearchInputComponent, TranslatePipe, FormsModule, ContributorsTableComponent], @@ -64,7 +68,10 @@ export class ContributorsDialogComponent implements OnInit { isLoading = select(ContributorsSelectors.isContributorsLoading); initialContributors = select(ContributorsSelectors.getContributors); contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount); + hasAdminAccess = select(MetadataSelectors.hasAdminAccess); contributors = signal([]); + page = select(ContributorsSelectors.getContributorsPageNumber); + pageSize = select(ContributorsSelectors.getContributorsPageSize); currentUser = select(UserSelectors.getCurrentUser); @@ -72,9 +79,12 @@ export class ContributorsDialogComponent implements OnInit { ...DEFAULT_TABLE_PARAMS, totalRecords: this.contributorsTotalCount(), paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows, + firstRowIndex: (this.page() - 1) * this.pageSize(), + rows: this.pageSize(), })); actions = createDispatchMap({ + getContributors: GetAllContributors, updateSearchValue: UpdateContributorsSearchValue, updatePermissionFilter: UpdatePermissionFilter, updateBibliographyFilter: UpdateBibliographyFilter, @@ -87,17 +97,6 @@ export class ContributorsDialogComponent implements OnInit { private readonly resourceType: ResourceType; private readonly resourceId: string; - isCurrentUserAdminContributor = computed(() => { - const currentUserId = this.currentUser()?.id; - const initialContributors = this.initialContributors(); - if (!currentUserId) return false; - - return initialContributors.some( - (contributor: ContributorModel) => - contributor.userId === currentUserId && contributor.permission === ContributorPermission.Admin - ); - }); - get searchPlaceholder() { return this.resourceType === ResourceType.Project ? 'project.contributors.searchProjectPlaceholder' @@ -207,6 +206,13 @@ export class ContributorsDialogComponent implements OnInit { }); } + pageChanged(event: TablePageEvent) { + const page = Math.floor(event.first / event.rows) + 1; + const pageSize = event.rows; + + this.actions.getContributors(this.resourceId, this.resourceType, page, pageSize); + } + cancel() { this.contributors.set(structuredClone(this.initialContributors())); } diff --git a/src/app/features/metadata/metadata.component.ts b/src/app/features/metadata/metadata.component.ts index 2c0c5d769..2ac2bc5a2 100644 --- a/src/app/features/metadata/metadata.component.ts +++ b/src/app/features/metadata/metadata.component.ts @@ -19,7 +19,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; import { MetadataTabsComponent, SubHeaderComponent } from '@osf/shared/components'; -import { MetadataResourceEnum, ResourceType, UserPermissions } from '@osf/shared/enums'; +import { MetadataResourceEnum, ResourceType } from '@osf/shared/enums'; import { MetadataTabsModel, SubjectModel } from '@osf/shared/models'; import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services'; import { @@ -136,6 +136,9 @@ export class MetadataComponent implements OnInit { areInstitutionsLoading = select(InstitutionsSelectors.areResourceInstitutionsLoading); areResourceInstitutionsSubmitting = select(InstitutionsSelectors.areResourceInstitutionsSubmitting); + hasWriteAccess = select(MetadataSelectors.hasWriteAccess); + hasAdminAccess = select(MetadataSelectors.hasAdminAccess); + provider = this.environment.defaultProvider; private readonly resourceNameMap = new Map([ @@ -166,37 +169,23 @@ export class MetadataComponent implements OnInit { updateContributorsSearchValue: UpdateContributorsSearchValue, }); - isLoading = computed(() => { - return ( + isLoading = computed( + () => this.isMetadataLoading() || this.isContributorsLoading() || this.areInstitutionsLoading() || this.isSubmitting() || this.areResourceInstitutionsSubmitting() - ); - }); + ); - hideEditDoi = computed(() => { - return ( + hideEditDoi = computed( + () => this.resourceType() === ResourceType.Project && (!!this.metadata()?.identifiers?.length || !this.metadata()?.public) - ); - }); + ); isRegistrationType = computed(() => this.resourceType() === ResourceType.Registration); - hasWriteAccess = computed(() => { - const metadata = this.metadata(); - if (!metadata) return false; - return metadata.currentUserPermissions.includes(UserPermissions.Write); - }); - - hasAdminAccess = computed(() => { - const metadata = this.metadata(); - if (!metadata) return false; - return metadata.currentUserPermissions.includes(UserPermissions.Admin); - }); - constructor() { effect(() => { const records = this.cedarRecords(); diff --git a/src/app/features/metadata/store/metadata.selectors.ts b/src/app/features/metadata/store/metadata.selectors.ts index a65a967ae..9693a56fc 100644 --- a/src/app/features/metadata/store/metadata.selectors.ts +++ b/src/app/features/metadata/store/metadata.selectors.ts @@ -1,5 +1,7 @@ import { Selector } from '@ngxs/store'; +import { UserPermissions } from '@osf/shared/enums'; + import { MetadataStateModel } from './metadata.model'; import { MetadataState } from './metadata.state'; @@ -63,4 +65,14 @@ export class MetadataSelectors { static getCedarRecordsLoading(state: MetadataStateModel) { return state.cedarRecords.isLoading; } + + @Selector([MetadataState]) + static hasWriteAccess(state: MetadataStateModel): boolean { + return state.metadata.data?.currentUserPermissions?.includes(UserPermissions.Write) || false; + } + + @Selector([MetadataState]) + static hasAdminAccess(state: MetadataStateModel): boolean { + return state.metadata.data?.currentUserPermissions?.includes(UserPermissions.Admin) || false; + } } diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html index f8038831c..87e081260 100644 --- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html +++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html @@ -11,26 +11,23 @@

{{ 'project.overview.metadata.contributors' | translate }}

class="w-full" [(contributors)]="contributors" [tableParams]="tableParams()" - [currentUserId]="currentUser()?.id" - [hasAdminAccess]="isCurrentUserAdminContributor()" [isLoading]="isContributorsLoading()" (remove)="removeContributor($event)" + (pageChanged)="pageChanged($event)" />
@if (hasChanges) {
- - + +
} - @if (isCurrentUserAdminContributor()) { - - } +
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts index c94ee8457..b2c08a55f 100644 --- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts +++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts @@ -5,7 +5,7 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; import { Message } from 'primeng/message'; -import { TableModule } from 'primeng/table'; +import { TableModule, TablePageEvent } from 'primeng/table'; import { filter } from 'rxjs'; @@ -24,14 +24,13 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; -import { UserSelectors } from '@core/store/user'; import { AddContributorDialogComponent, AddUnregisteredContributorDialogComponent, ContributorsTableComponent, } from '@osf/shared/components/contributors'; import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants'; -import { AddContributorType, ContributorPermission, ResourceType } from '@osf/shared/enums'; +import { AddContributorType, ResourceType } from '@osf/shared/enums'; import { findChangedItems } from '@osf/shared/helpers'; import { ContributorDialogAddModel, ContributorModel, TableParameters } from '@osf/shared/models'; import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services'; @@ -64,25 +63,17 @@ export class PreprintsContributorsComponent implements OnInit { contributors = signal([]); contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount); isContributorsLoading = select(ContributorsSelectors.isContributorsLoading); - currentUser = select(UserSelectors.getCurrentUser); + page = select(ContributorsSelectors.getContributorsPageNumber); + pageSize = select(ContributorsSelectors.getContributorsPageSize); readonly tableParams = computed(() => ({ ...DEFAULT_TABLE_PARAMS, totalRecords: this.contributorsTotalCount(), paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows, + firstRowIndex: (this.page() - 1) * this.pageSize(), + rows: this.pageSize(), })); - isCurrentUserAdminContributor = computed(() => { - const currentUserId = this.currentUser()?.id; - const initialContributors = this.initialContributors(); - if (!currentUserId) return false; - - return initialContributors.some( - (contributor: ContributorModel) => - contributor.userId === currentUserId && contributor.permission === ContributorPermission.Admin - ); - }); - actions = createDispatchMap({ getContributors: GetAllContributors, deleteContributor: DeleteContributor, @@ -171,8 +162,6 @@ export class PreprintsContributorsComponent implements OnInit { } removeContributor(contributor: ContributorModel) { - const isDeletingSelf = contributor.userId === this.currentUser()?.id; - this.customConfirmationService.confirmDelete({ headerKey: 'project.contributors.removeDialog.title', messageKey: 'project.contributors.removeDialog.message', @@ -180,20 +169,23 @@ export class PreprintsContributorsComponent implements OnInit { acceptLabelKey: 'common.buttons.remove', onConfirm: () => { this.actions - .deleteContributor(this.preprintId(), ResourceType.Preprint, contributor.userId, isDeletingSelf) + .deleteContributor(this.preprintId(), ResourceType.Preprint, contributor.userId) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.toastService.showSuccess('project.contributors.removeDialog.successMessage', { name: contributor.fullName, }); - - if (isDeletingSelf) { - this.router.navigate(['/']); - } }, }); }, }); } + + pageChanged(event: TablePageEvent) { + const page = Math.floor(event.first / event.rows) + 1; + const pageSize = event.rows; + + this.actions.getContributors(this.preprintId(), ResourceType.Preprint, page, pageSize); + } } diff --git a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html index b920f2b04..7726dae69 100644 --- a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html +++ b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html @@ -6,9 +6,8 @@

{{ 'project.overview.metadata.contributors' | translate }}

[(contributors)]="contributors" [tableParams]="tableParams()" [isLoading]="isContributorsLoading()" - [currentUserId]="currentUser()?.id" - [hasAdminAccess]="isCurrentUserAdminContributor()" (remove)="removeContributor($event)" + (pageChanged)="pageChanged($event)" />
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts index 5974ed64e..bf07f0974 100644 --- a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts +++ b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts @@ -4,7 +4,7 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; -import { TableModule } from 'primeng/table'; +import { TableModule, TablePageEvent } from 'primeng/table'; import { filter, map, of } from 'rxjs'; @@ -21,16 +21,15 @@ import { } from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { FormControl, FormsModule } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; -import { UserSelectors } from '@core/store/user'; import { AddContributorDialogComponent, AddUnregisteredContributorDialogComponent, ContributorsTableComponent, } from '@osf/shared/components/contributors'; import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants'; -import { AddContributorType, ContributorPermission, ResourceType } from '@osf/shared/enums'; +import { AddContributorType, ResourceType } from '@osf/shared/enums'; import { findChangedItems } from '@osf/shared/helpers'; import { ContributorDialogAddModel, ContributorModel, TableParameters } from '@osf/shared/models'; import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services'; @@ -59,32 +58,22 @@ export class RegistriesContributorsComponent implements OnInit { readonly customConfirmationService = inject(CustomConfirmationService); private readonly route = inject(ActivatedRoute); - private readonly router = inject(Router); private readonly draftId = toSignal(this.route.params.pipe(map((params) => params['id'])) ?? of(undefined)); - currentUser = select(UserSelectors.getCurrentUser); - - isCurrentUserAdminContributor = computed(() => { - const currentUserId = this.currentUser()?.id; - const initialContributors = this.initialContributors(); - if (!currentUserId) return false; - - return initialContributors.some( - (contributor: ContributorModel) => - contributor.userId === currentUserId && contributor.permission === ContributorPermission.Admin - ); - }); - initialContributors = select(ContributorsSelectors.getContributors); contributors = signal([]); - readonly isContributorsLoading = select(ContributorsSelectors.isContributorsLoading); + isContributorsLoading = select(ContributorsSelectors.isContributorsLoading); contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount); + page = select(ContributorsSelectors.getContributorsPageNumber); + pageSize = select(ContributorsSelectors.getContributorsPageSize); readonly tableParams = computed(() => ({ ...DEFAULT_TABLE_PARAMS, totalRecords: this.contributorsTotalCount(), paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows, + firstRowIndex: (this.page() - 1) * this.pageSize(), + rows: this.pageSize(), })); actions = createDispatchMap({ @@ -183,8 +172,6 @@ export class RegistriesContributorsComponent implements OnInit { } removeContributor(contributor: ContributorModel) { - const isDeletingSelf = contributor.userId === this.currentUser()?.id; - this.customConfirmationService.confirmDelete({ headerKey: 'project.contributors.removeDialog.title', messageKey: 'project.contributors.removeDialog.message', @@ -192,20 +179,23 @@ export class RegistriesContributorsComponent implements OnInit { acceptLabelKey: 'common.buttons.remove', onConfirm: () => { this.actions - .deleteContributor(this.draftId(), ResourceType.DraftRegistration, contributor.userId, isDeletingSelf) + .deleteContributor(this.draftId(), ResourceType.DraftRegistration, contributor.userId) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.toastService.showSuccess('project.contributors.removeDialog.successMessage', { name: contributor.fullName, }); - - if (isDeletingSelf) { - this.router.navigate(['/']); - } }, }); }, }); } + + pageChanged(event: TablePageEvent) { + const page = Math.floor(event.first / event.rows) + 1; + const pageSize = event.rows; + + this.actions.getContributors(this.draftId(), ResourceType.DraftRegistration, page, pageSize); + } } diff --git a/src/app/shared/components/contributors/contributors-table/contributors-table.component.html b/src/app/shared/components/contributors/contributors-table/contributors-table.component.html index 7d8bf2789..99e579a57 100644 --- a/src/app/shared/components/contributors/contributors-table/contributors-table.component.html +++ b/src/app/shared/components/contributors/contributors-table/contributors-table.component.html @@ -162,7 +162,7 @@ } - @if (canRemoveContributor().get(contributor.userId)) { + @if (hasAdminAccess() || contributor.userId === currentUserId()) { this.contributors().some((contributor) => contributor.deactivated)); - canRemoveContributor = computed(() => { - const contributors = this.contributors(); - const currentUserId = this.currentUserId(); - const isAdmin = this.hasAdminAccess(); - const adminCount = contributors.filter((c) => c.permission === ContributorPermission.Admin).length; - - const result = new Map(); - - for (const c of contributors) { - const isOwnAccount = currentUserId === c.userId; - const isLastAdmin = c.permission === ContributorPermission.Admin && adminCount <= 1; - - const canRemove = (isAdmin || isOwnAccount) && !isLastAdmin; - - result.set(c.userId, canRemove); - } - - return result; - }); - removeContributor(contributor: ContributorModel) { this.remove.emit(contributor); } @@ -106,7 +86,8 @@ export class ContributorsTableComponent { } onRowReorder() { - const reorderedContributors = this.contributors().map((item, i) => ({ ...item, index: i })); + const firstIndex = this.tableParams().firstRowIndex; + const reorderedContributors = this.contributors().map((item, i) => ({ ...item, index: i + firstIndex })); this.contributors.set(reorderedContributors); } diff --git a/src/app/shared/stores/contributors/contributors.model.ts b/src/app/shared/stores/contributors/contributors.model.ts index 9584f746f..5f7c92b61 100644 --- a/src/app/shared/stores/contributors/contributors.model.ts +++ b/src/app/shared/stores/contributors/contributors.model.ts @@ -1,12 +1,17 @@ +import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants'; import { ContributorAddModel, ContributorModel, RequestAccessModel } from '@osf/shared/models'; import { AsyncStateModel, AsyncStateWithTotalCount } from '@osf/shared/models/store'; +export interface ContributorsListModel extends AsyncStateWithTotalCount { + searchValue: string | null; + permissionFilter: string | null; + bibliographyFilter: boolean | null; + page: number; + pageSize: number; +} + export interface ContributorsStateModel { - contributorsList: AsyncStateWithTotalCount & { - searchValue: string | null; - permissionFilter: string | null; - bibliographyFilter: boolean | null; - }; + contributorsList: ContributorsListModel; requestAccessList: AsyncStateModel; users: AsyncStateWithTotalCount; } @@ -20,6 +25,8 @@ export const CONTRIBUTORS_STATE_DEFAULTS: ContributorsStateModel = { permissionFilter: null, bibliographyFilter: null, totalCount: 0, + page: 1, + pageSize: DEFAULT_TABLE_PARAMS.rows, }, requestAccessList: { data: [], diff --git a/src/app/shared/stores/contributors/contributors.selectors.ts b/src/app/shared/stores/contributors/contributors.selectors.ts index b15305a32..f3816a2de 100644 --- a/src/app/shared/stores/contributors/contributors.selectors.ts +++ b/src/app/shared/stores/contributors/contributors.selectors.ts @@ -42,9 +42,19 @@ export class ContributorsSelectors { return state?.contributorsList?.isLoading || false; } + @Selector([ContributorsState]) + static getContributorsPageNumber(state: ContributorsStateModel) { + return state.contributorsList.page; + } + + @Selector([ContributorsState]) + static getContributorsPageSize(state: ContributorsStateModel) { + return state.contributorsList.pageSize; + } + @Selector([ContributorsState]) static getContributorsTotalCount(state: ContributorsStateModel) { - return state?.contributorsList?.totalCount || 0; + return state.contributorsList.totalCount; } @Selector([ContributorsState]) diff --git a/src/app/shared/stores/contributors/contributors.state.ts b/src/app/shared/stores/contributors/contributors.state.ts index 1731cf44d..be0259ff5 100644 --- a/src/app/shared/stores/contributors/contributors.state.ts +++ b/src/app/shared/stores/contributors/contributors.state.ts @@ -42,25 +42,34 @@ export class ContributorsState { return; } + const page = action.page ?? state.contributorsList.page; + const pageSize = action.pageSize ?? state.contributorsList.pageSize; + ctx.patchState({ - contributorsList: { ...state.contributorsList, data: [], isLoading: true, error: null }, + contributorsList: { + ...state.contributorsList, + data: [], + isLoading: true, + error: null, + }, }); - return this.contributorsService - .getAllContributors(action.resourceType, action.resourceId, action.page, action.pageSize) - .pipe( - tap((res) => { - ctx.patchState({ - contributorsList: { - ...state.contributorsList, - data: res.data, - isLoading: false, - totalCount: res.totalCount, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'contributorsList', error)) - ); + return this.contributorsService.getAllContributors(action.resourceType, action.resourceId, page, pageSize).pipe( + tap((res) => { + ctx.patchState({ + contributorsList: { + ...state.contributorsList, + data: res.data, + isLoading: false, + totalCount: res.totalCount, + page, + pageSize, + error: null, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'contributorsList', error)) + ); } @Action(GetRequestAccessContributors) @@ -141,7 +150,9 @@ export class ContributorsState { return this.contributorsService.addContributor(action.resourceType, action.resourceId, action.contributor).pipe( tap(() => { - ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType)); + ctx.dispatch( + new GetAllContributors(action.resourceId, action.resourceType, 1, state.contributorsList.pageSize) + ); }), catchError((error) => handleSectionError(ctx, 'contributorsList', error)) ); @@ -163,7 +174,9 @@ export class ContributorsState { .bulkUpdateContributors(action.resourceType, action.resourceId, action.contributors) .pipe( tap(() => { - ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType)); + ctx.dispatch( + new GetAllContributors(action.resourceId, action.resourceType, 1, state.contributorsList.pageSize) + ); }), catchError((error) => handleSectionError(ctx, 'contributorsList', error)) ); @@ -185,7 +198,9 @@ export class ContributorsState { .bulkAddContributors(action.resourceType, action.resourceId, action.contributors, action.childNodeIds) .pipe( tap(() => { - ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType)); + ctx.dispatch( + new GetAllContributors(action.resourceId, action.resourceType, 1, state.contributorsList.pageSize) + ); }), catchError((error) => handleSectionError(ctx, 'contributorsList', error)) ); @@ -208,7 +223,9 @@ export class ContributorsState { .pipe( tap(() => { if (!action.skipRefresh) { - ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType)); + ctx.dispatch( + new GetAllContributors(action.resourceId, action.resourceType, 1, state.contributorsList.pageSize) + ); } }), catchError((error) => handleSectionError(ctx, 'contributorsList', error))