diff --git a/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.html b/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.html index 77233b7c6..b85ab5ff8 100644 --- a/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.html +++ b/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.html @@ -8,6 +8,7 @@ [hasPMOAccess]="canEdit()" [loading]="loading()" [totalRecords]="votes().length" + [editQueryParams]="editVoteQueryParams()" (viewResults)="viewVoteResults($event)" (viewVote)="viewVoteResults($event)"> @if (canEdit()) { @@ -17,8 +18,7 @@ icon="fa-light fa-check-to-slot" severity="info" size="small" - [routerLink]="['/votes', 'create']" - [queryParams]="createVoteQueryParams()" + (click)="onCreateVote()" data-testid="committee-votes-create-btn"> } @@ -30,8 +30,7 @@ icon="fa-light fa-check-to-slot" severity="info" size="small" - [routerLink]="['/votes', 'create']" - [queryParams]="createVoteQueryParams()" + (click)="onCreateVote()" data-testid="committee-votes-empty-create-btn"> } diff --git a/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.ts b/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.ts index fa205dfb8..3bb0173b2 100644 --- a/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.ts +++ b/apps/lfx-one/src/app/modules/committees/components/committee-votes/committee-votes.component.ts @@ -3,12 +3,15 @@ import { ChangeDetectionStrategy, Component, computed, inject, input, model, signal, Signal } from '@angular/core'; import { toObservable, toSignal } from '@angular/core/rxjs-interop'; +import { Router } from '@angular/router'; import { ButtonComponent } from '@components/button/button.component'; import { CardComponent } from '@components/card/card.component'; import { Committee, Vote } from '@lfx-one/shared/interfaces'; import { buildCommitteeCreateQueryParams } from '@lfx-one/shared/utils'; import { VotesTableComponent } from '@app/modules/votes/components/votes-table/votes-table.component'; import { VoteResultsDrawerComponent } from '@app/modules/votes/components/vote-results-drawer/vote-results-drawer.component'; +import { CommitteeService } from '@services/committee.service'; +import { LensService } from '@services/lens.service'; import { VoteService } from '@services/vote.service'; import { MessageService } from 'primeng/api'; import { catchError, filter, finalize, of, switchMap } from 'rxjs'; @@ -21,8 +24,11 @@ import { catchError, filter, finalize, of, switchMap } from 'rxjs'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class CommitteeVotesComponent { + private readonly committeeService = inject(CommitteeService); + private readonly lensService = inject(LensService); private readonly voteService = inject(VoteService); private readonly messageService = inject(MessageService); + private readonly router = inject(Router); // Inputs public committee = input.required(); @@ -37,6 +43,31 @@ export class CommitteeVotesComponent { // Data public votes: Signal = this.initVotes(); public createVoteQueryParams: Signal> = this.initCreateVoteQueryParams(); + public editVoteQueryParams: Signal> = this.createVoteQueryParams; + + /** Checks committee write permission fresh before navigating to the create-vote route. + * Redirects to project overview with _notice=votes if permission has been revoked + * since the page loaded — consistent with the writerGuard denial flow. */ + protected onCreateVote(): void { + const committee = this.committee(); + const overviewPath = this.lensService.activeLens() === 'foundation' ? '/foundation/overview' : '/project/overview'; + const denyParams: Record = { _notice: 'votes' }; + if (committee.project_slug) denyParams['project'] = committee.project_slug; + const deny = () => void this.router.navigate([overviewPath], { queryParams: denyParams }); + + this.committeeService + .fetchCommittee(committee.uid) + .subscribe({ + next: (fresh) => { + if (fresh?.writer !== true) { + deny(); + return; + } + void this.router.navigate(['/votes', 'create'], { queryParams: this.createVoteQueryParams() }); + }, + error: () => deny(), + }); + } /** Opens the vote results drawer for the selected vote. */ public viewVoteResults(voteUid: string): void { diff --git a/apps/lfx-one/src/app/modules/votes/components/votes-table/votes-table.component.html b/apps/lfx-one/src/app/modules/votes/components/votes-table/votes-table.component.html index f0f0b0247..e8c3604db 100644 --- a/apps/lfx-one/src/app/modules/votes/components/votes-table/votes-table.component.html +++ b/apps/lfx-one/src/app/modules/votes/components/votes-table/votes-table.component.html @@ -156,9 +156,16 @@
@if (hasPMOAccess()) { @if (vote.status === PollStatus.DISABLED) { - - - + + (false); // Draft tab is only meaningful in management contexts (project/committee lens); hide it in the Me lens. public readonly showDraftTab = input(true); + public readonly editQueryParams = input>({}); // === Outputs === public readonly viewVote = output();