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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[surveys]="surveys()"
[hasPMOAccess]="canEdit()"
[loading]="loading()"
[editQueryParams]="editSurveyQueryParams()"
(viewResults)="viewSurveyResults($event)"
(rowClick)="viewSurveyResults($event)">
@if (canEdit()) {
Expand All @@ -16,8 +17,7 @@
icon="fa-light fa-chart-simple"
severity="info"
size="small"
[routerLink]="['/surveys', 'create']"
[queryParams]="createSurveyQueryParams()"
(click)="onCreateSurvey()"
data-testid="committee-surveys-create-btn"></lfx-button>
}
</lfx-surveys-table>
Expand All @@ -29,8 +29,7 @@
icon="fa-light fa-chart-simple"
severity="info"
size="small"
[routerLink]="['/surveys', 'create']"
[queryParams]="createSurveyQueryParams()"
(click)="onCreateSurvey()"
data-testid="committee-surveys-empty-create-btn"></lfx-button>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

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, Survey } from '@lfx-one/shared/interfaces';
import { buildCommitteeCreateQueryParams } from '@lfx-one/shared/utils';
import { SurveysTableComponent } from '@app/modules/surveys/components/surveys-table/surveys-table.component';
import { SurveyResultsDrawerComponent } from '@app/modules/surveys/components/survey-results-drawer/survey-results-drawer.component';
import { CommitteeService } from '@services/committee.service';
import { LensService } from '@services/lens.service';
import { SurveyService } from '@services/survey.service';
import { MessageService } from 'primeng/api';
import { catchError, filter, finalize, of, switchMap, tap } from 'rxjs';
import { catchError, filter, finalize, of, switchMap, take, tap } from 'rxjs';

@Component({
selector: 'lfx-committee-surveys',
Expand All @@ -21,8 +24,11 @@ import { catchError, filter, finalize, of, switchMap, tap } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CommitteeSurveysComponent {
private readonly committeeService = inject(CommitteeService);
private readonly lensService = inject(LensService);
private readonly surveyService = inject(SurveyService);
private readonly messageService = inject(MessageService);
private readonly router = inject(Router);

// Inputs
public committee = input.required<Committee>();
Expand All @@ -37,6 +43,32 @@ export class CommitteeSurveysComponent {
// Data
public surveys: Signal<Survey[]> = this.initSurveys();
public createSurveyQueryParams: Signal<Record<string, string>> = this.initCreateSurveyQueryParams();
public editSurveyQueryParams: Signal<Record<string, string>> = this.createSurveyQueryParams;

/** Checks committee write permission fresh before navigating to the create-survey route.
* Redirects to the lens-appropriate overview with _notice=surveys if permission has been
* revoked since the page loaded — consistent with the writerGuard denial flow. */
public onCreateSurvey(): void {
const committee = this.committee();
const overviewPath = this.lensService.activeLens() === 'foundation' ? '/foundation/overview' : '/project/overview';
const denyParams: Record<string, string> = { _notice: 'surveys' };
if (committee.project_slug) denyParams['project'] = committee.project_slug;
const deny = () => void this.router.navigate([overviewPath], { queryParams: denyParams });

this.committeeService
.fetchCommittee(committee.uid)
.pipe(take(1))
.subscribe({
next: (fresh) => {
if (fresh?.writer !== true) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] Client gates on fresh?.writer !== true (committee writer), but the server-side writerGuard for writeFeature: 'surveys' only accepts project.writer — the committee-writer code path in the guard is gated to 'meetings' only.

Consequence: a committee Manager who is not a project writer will pass this client check, then be denied by writerGuard and shown the access-denied toast — a confusing double-redirect UX loop that makes it look like the permission check is broken.

Two options:

  1. Align the client check to project.writer — gate here on fresh?.project?.writer === true (or equivalent field on the committee response) to match the server rule.
  2. Update writerGuard to accept committee writers for surveys — if the intent is that committee writers should be able to create surveys, extend the guard accordingly.

Worth confirming which is the intended design with the team before the fix (ref LFXV2-2252).

deny();
return;
}
void this.router.navigate(['/surveys', 'create'], { queryParams: this.createSurveyQueryParams() });
},
error: () => deny(),
});
}

public viewSurveyResults(survey: Survey): void {
this.selectedSurveyId.set(survey.uid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@
<td>
<div class="flex items-center justify-end gap-1">
@if (hasPMOAccess() && survey.displayStatus === SurveyStatus.DRAFT) {
<lfx-button label="Edit" severity="primary" size="small" [text]="true" styleClass="" [attr.data-testid]="'surveys-edit-' + rowId">
</lfx-button>
<a [routerLink]="['/surveys', survey.uid, 'edit']" [queryParams]="editQueryParams()" (click)="$event.stopPropagation()">
Comment thread
MRashad26 marked this conversation as resolved.
<lfx-button label="Edit" severity="primary" size="small" [text]="true" [attr.data-testid]="'surveys-edit-' + rowId"> </lfx-button>
</a>
Comment thread
MRashad26 marked this conversation as resolved.
<lfx-button
label="Delete"
severity="danger"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DatePipe } from '@angular/common';
import { Component, computed, inject, input, output, signal, Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { AbstractControl, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { ButtonComponent } from '@components/button/button.component';
import { CardTabsBarComponent } from '@components/card-tabs-bar/card-tabs-bar.component';
import { CardComponent } from '@components/card/card.component';
Expand Down Expand Up @@ -35,6 +36,7 @@ import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs';
ButtonComponent,
DatePipe,
ReactiveFormsModule,
RouterLink,
InputTextComponent,
SelectComponent,
SurveyStatusLabelPipe,
Expand Down Expand Up @@ -70,6 +72,7 @@ export class SurveysTableComponent {
public readonly showFoundationFilter = input<boolean>(false);
public readonly showProjectFilter = input<boolean>(false);
public readonly isMeLens = input<boolean>(false);
public readonly editQueryParams = input<Record<string, string>>({});

// === Outputs ===
public readonly viewResults = output<Survey>();
Expand Down
Loading