Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Exclude List Row Actions #1826

Merged
merged 3 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,19 @@ export class FeatureFlagsDataService {
const url = `${this.environment.api.addFlagInclusionList}/${segmentId}`;
return this.http.delete(url);
}

addExclusionList(list: AddPrivateSegmentListRequest): Observable<FeatureFlagSegmentListDetails> {
const url = this.environment.api.addFlagExclusionList;
return this.http.post<FeatureFlagSegmentListDetails>(url, list);
}

updateExclusionList(list: EditPrivateSegmentListRequest): Observable<FeatureFlagSegmentListDetails> {
const url = `${this.environment.api.addFlagExclusionList}/${list.list.id}`;
return this.http.put<FeatureFlagSegmentListDetails>(url, list);
}

deleteExclusionList(segmentId: string) {
const url = `${this.environment.api.addFlagExclusionList}/${segmentId}`;
return this.http.delete(url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,16 @@ export class FeatureFlagsService {
deleteFeatureFlagInclusionPrivateSegmentList(segmentId: string) {
this.store$.dispatch(FeatureFlagsActions.actionDeleteFeatureFlagInclusionList({ segmentId }));
}

addFeatureFlagExclusionPrivateSegmentList(list: AddPrivateSegmentListRequest) {
this.store$.dispatch(FeatureFlagsActions.actionAddFeatureFlagExclusionList({ list }));
}

updateFeatureFlagExclusionPrivateSegmentList(list: EditPrivateSegmentListRequest) {
this.store$.dispatch(FeatureFlagsActions.actionUpdateFeatureFlagExclusionList({ list }));
}

deleteFeatureFlagExclusionPrivateSegmentList(segmentId: string) {
this.store$.dispatch(FeatureFlagsActions.actionDeleteFeatureFlagExclusionList({ segmentId }));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export const actionUpdateFilterModeSuccess = createAction(
);

export const actionUpdateFilterModeFailure = createAction('[Feature Flags] Update Filter Mode Failure');

export const actionAddFeatureFlagInclusionList = createAction(
'[Feature Flags] Add Feature Flag Inclusion List',
props<{ list: AddPrivateSegmentListRequest }>()
Expand Down Expand Up @@ -180,3 +181,48 @@ export const actionDeleteFeatureFlagInclusionListFailure = createAction(
'[Feature Flags] Delete Feature Flag Inclusion List Failure',
props<{ error: any }>()
);

export const actionAddFeatureFlagExclusionList = createAction(
'[Feature Flags] Add Feature Flag Exclusion List',
props<{ list: AddPrivateSegmentListRequest }>()
);

export const actionAddFeatureFlagExclusionListSuccess = createAction(
'[Feature Flags] Add Feature Flag Exclusion List Success',
props<{ listResponse: FeatureFlagSegmentListDetails }>()
);

export const actionAddFeatureFlagExclusionListFailure = createAction(
'[Feature Flags] Add Feature Flag Exclusion List Failure',
props<{ error: any }>()
);

export const actionUpdateFeatureFlagExclusionList = createAction(
'[Feature Flags] Update Feature Flag Exclusion List',
props<{ list: EditPrivateSegmentListRequest }>()
);

export const actionUpdateFeatureFlagExclusionListSuccess = createAction(
'[Feature Flags] Update Feature Flag Exclusion List Success',
props<{ listResponse: FeatureFlagSegmentListDetails }>()
);

export const actionUpdateFeatureFlagExclusionListFailure = createAction(
'[Feature Flags] Update Feature Flag Exclusion List Failure',
props<{ error: any }>()
);

export const actionDeleteFeatureFlagExclusionList = createAction(
'[Feature Flags] Delete Feature Flag Exclusion List',
props<{ segmentId: string }>()
);

export const actionDeleteFeatureFlagExclusionListSuccess = createAction(
'[Feature Flags] Delete Feature Flag Exclusion List Success',
props<{ segmentId: string }>()
);

export const actionDeleteFeatureFlagExclusionListFailure = createAction(
'[Feature Flags] Delete Feature Flag Exclusion List Failure',
props<{ error: any }>()
);
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,43 @@ export class FeatureFlagsEffects {
)
);

addFeatureFlagExclusionList$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionAddFeatureFlagExclusionList),
switchMap((action) => {
return this.featureFlagsDataService.addExclusionList(action.list).pipe(
map((listResponse) => FeatureFlagsActions.actionAddFeatureFlagExclusionListSuccess({ listResponse })),
catchError((error) => of(FeatureFlagsActions.actionAddFeatureFlagExclusionListFailure({ error })))
);
})
)
);

updateFeatureFlagExclusionList$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionUpdateFeatureFlagExclusionList),
switchMap((action) => {
return this.featureFlagsDataService.updateExclusionList(action.list).pipe(
map((listResponse) => FeatureFlagsActions.actionUpdateFeatureFlagExclusionListSuccess({ listResponse })),
catchError((error) => of(FeatureFlagsActions.actionUpdateFeatureFlagExclusionListFailure({ error })))
);
})
)
);

deleteFeatureFlagExclusionList$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionDeleteFeatureFlagExclusionList),
map((action) => action.segmentId),
switchMap((segmentId) => {
return this.featureFlagsDataService.deleteExclusionList(segmentId).pipe(
map(() => FeatureFlagsActions.actionDeleteFeatureFlagExclusionListSuccess({ segmentId })),
catchError((error) => of(FeatureFlagsActions.actionDeleteFeatureFlagExclusionListFailure({ error })))
);
})
)
);

fetchFeatureFlagsOnSearchString$ = createEffect(
() =>
this.actions$.pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,82 @@ const reducer = createReducer(
on(FeatureFlagsActions.actionDeleteFeatureFlagInclusionListFailure, (state) => ({
...state,
isLoadingUpsertPrivateSegmentList: false,
})),

// Feature Flag Exclusion List Add Actions
on(FeatureFlagsActions.actionAddFeatureFlagExclusionList, (state) => ({
...state,
isLoadingUpsertPrivateSegmentList: true,
})),
on(FeatureFlagsActions.actionAddFeatureFlagExclusionListSuccess, (state, { listResponse }) => {
const { featureFlag } = listResponse;
const existingFlag = state.entities[featureFlag?.id];

return adapter.updateOne(
{
id: featureFlag?.id,
changes: { featureFlagSegmentExclusion: [listResponse, ...existingFlag.featureFlagSegmentExclusion] },
},
{ ...state }
);
}),
on(FeatureFlagsActions.actionAddFeatureFlagExclusionListFailure, (state) => {
return { ...state, isLoadingUpsertPrivateSegmentList: false };
}),

// Feature Flag Exclusion List Update Actions
on(FeatureFlagsActions.actionUpdateFeatureFlagExclusionListSuccess, (state, { listResponse }) => {
const { featureFlag } = listResponse;
const existingFlag = state.entities[featureFlag?.id];

if (existingFlag) {
const updatedExclusions = existingFlag.featureFlagSegmentExclusion.map((exclusion) =>
exclusion.segment.id === listResponse.segment.id ? listResponse : exclusion
);

return adapter.updateOne(
{
id: featureFlag.id,
changes: { featureFlagSegmentExclusion: updatedExclusions },
},
{ ...state, isLoadingUpsertPrivateSegmentList: false }
);
}

return state;
}),

// Feature Flag Exclusion List Delete Actions
on(FeatureFlagsActions.actionDeleteFeatureFlagExclusionList, (state) => ({
...state,
isLoadingUpsertPrivateSegmentList: true,
})),
on(FeatureFlagsActions.actionDeleteFeatureFlagExclusionListSuccess, (state, { segmentId }) => {
const updatedState = { ...state, isLoadingUpsertPrivateSegmentList: false };
const flagId = Object.keys(state.entities).find((id) =>
state.entities[id].featureFlagSegmentExclusion.some((exclusion) => exclusion.segment.id === segmentId)
);

if (flagId) {
const flag = state.entities[flagId];
const updatedExclusions = flag.featureFlagSegmentExclusion.filter(
(exclusion) => exclusion.segment.id !== segmentId
);

return adapter.updateOne(
{
id: flagId,
changes: { featureFlagSegmentExclusion: updatedExclusions },
},
updatedState
);
}

return updatedState;
}),
on(FeatureFlagsActions.actionDeleteFeatureFlagExclusionListFailure, (state) => ({
...state,
isLoadingUpsertPrivateSegmentList: false,
}))
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,17 @@ export const selectFeatureFlagInclusions = createSelector(

export const selectFeatureFlagExclusions = createSelector(
selectSelectedFeatureFlag,
(featureFlag: FeatureFlag): ParticipantListTableRow[] => []
(featureFlag: FeatureFlag): ParticipantListTableRow[] => {
if (!featureFlag || !featureFlag.featureFlagSegmentExclusion) {
return [];
}
return [...featureFlag.featureFlagSegmentExclusion]
.sort((a, b) => new Date(a.segment.createdAt).getTime() - new Date(b.segment.createdAt).getTime())
.map((exclusion) => {
return {
segment: exclusion.segment,
listType: exclusion.listType,
};
});
}
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<app-common-section-card>
<app-common-section-card *ngIf="selectedFlag$ | async as flag">
<!-- header-left -->
<app-common-section-card-title-header
header-left
Expand All @@ -15,12 +15,16 @@
[showMenuButton]="true"
[menuButtonItems]="menuButtonItems"
[isSectionCardExpanded]="isSectionCardExpanded"
(primaryButtonClick)="addExcludeListClicked()"
(primaryButtonClick)="onAddExcludeListClick(flag.context[0], flag.id)"
(menuButtonItemClick)="onMenuButtonItemClick($event)"
(sectionCardExpandChange)="onSectionCardExpandChange($event)"
>
</app-common-section-card-action-buttons>

<!-- content -->
<app-feature-flag-exclusions-table content *ngIf="isSectionCardExpanded"></app-feature-flag-exclusions-table>
<app-feature-flag-exclusions-table
content
*ngIf="isSectionCardExpanded"
(rowAction)="onRowAction($event, flag.id)"
></app-feature-flag-exclusions-table>
</app-common-section-card>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@ import {
} from '../../../../../../../shared-standalone-component-lib/components';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { IMenuButtonItem } from 'upgrade_types';
import { IMenuButtonItem, SEGMENT_TYPE } from 'upgrade_types';
import { FeatureFlagExclusionsTableComponent } from './feature-flag-exclusions-table/feature-flag-exclusions-table.component';
import { FeatureFlagsService } from '../../../../../../../core/feature-flags/feature-flags.service';
import { DialogService } from '../../../../../../../shared/services/common-dialog.service';
import {
PARTICIPANT_LIST_ROW_ACTION,
ParticipantListRowActionEvent,
ParticipantListTableRow,
} from '../../../../../../../core/feature-flags/store/feature-flags.model';
import {
EditPrivateSegmentListDetails,
EditPrivateSegmentListRequest,
Segment,
} from '../../../../../../../core/segments/store/segments.model';

@Component({
selector: 'app-feature-flag-exclusions-section-card',
Expand All @@ -28,24 +39,69 @@ import { FeatureFlagsService } from '../../../../../../../core/feature-flags/fea
export class FeatureFlagExclusionsSectionCardComponent {
@Input() isSectionCardExpanded;
tableRowCount$ = this.featureFlagService.selectFeatureFlagExclusionsLength$;
selectedFlag$ = this.featureFlagService.selectedFeatureFlag$;

constructor(private featureFlagService: FeatureFlagsService) {}

constructor(private featureFlagService: FeatureFlagsService, private dialogService: DialogService) {}
menuButtonItems: IMenuButtonItem[] = [
{ name: 'Edit', disabled: false },
{ name: 'Delete', disabled: false },
// { name: 'Import Exclude List', disabled: false },
// { name: 'Export All Exclude Lists', disabled: false },
];

addExcludeListClicked() {
console.log('add Exclude List Clicked');
onAddExcludeListClick(appContext: string, flagId: string) {
this.dialogService.openAddExcludeListModal(appContext, flagId);
}

onMenuButtonItemClick(event) {
console.log('Menu button Clicked');
console.log(event);
console.log('Menu Button Item Clicked:', event);
}

onSectionCardExpandChange(isSectionCardExpanded: boolean) {
this.isSectionCardExpanded = isSectionCardExpanded;
}

// Participant list row action events
onRowAction(event: ParticipantListRowActionEvent, flagId: string): void {
switch (event.action) {
case PARTICIPANT_LIST_ROW_ACTION.EDIT:
this.onEditExcludeList(event.rowData, flagId);
break;
case PARTICIPANT_LIST_ROW_ACTION.DELETE:
this.onDeleteExcludeList(event.rowData.segment);
break;
}
}

onEditExcludeList(rowData: ParticipantListTableRow, flagId: string): void {
this.dialogService.openEditExcludeListModal(rowData, rowData.segment.context, flagId);
}

createEditPrivateSegmentListDetails(segment: Segment): EditPrivateSegmentListDetails {
const editPrivateSegmentListDetails: EditPrivateSegmentListDetails = {
id: segment.id,
name: segment.name,
description: segment.description,
context: segment.context,
type: SEGMENT_TYPE.PRIVATE,
userIds: segment.individualForSegment.map((individual) => individual.userId),
groups: segment.groupForSegment,
subSegmentIds: segment.subSegments.map((subSegment) => subSegment.id),
};

return editPrivateSegmentListDetails;
}

sendUpdateFeatureFlagExclusionRequest(request: EditPrivateSegmentListRequest): void {
this.featureFlagService.updateFeatureFlagExclusionPrivateSegmentList(request);
}

onDeleteExcludeList(segment: Segment): void {
this.dialogService
.openDeleteExcludeListModal(segment.name)
.afterClosed()
.subscribe((confirmClicked) => {
if (confirmClicked) {
this.featureFlagService.deleteFeatureFlagExclusionPrivateSegmentList(segment.id);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<div class="table-container">
<app-common-details-participant-list-table
[tableType]="tableType"
[dataSource]="dataSource$ | async"
[isLoading]="isLoading$ | async"
noDataRowText="feature-flags.details.exclusions.card.no-data-row.text"
(rowAction)="onRowAction($event)"
></app-common-details-participant-list-table>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
import { FeatureFlagsService } from '../../../../../../../../core/feature-flags/feature-flags.service';
import { FEATURE_FLAG_PARTICIPANT_LIST_KEY } from '../../../../../../../../core/feature-flags/store/feature-flags.model';
import {
FEATURE_FLAG_PARTICIPANT_LIST_KEY,
ParticipantListRowActionEvent,
} from '../../../../../../../../core/feature-flags/store/feature-flags.model';
import { CommonDetailsParticipantListTableComponent } from '../../../../../../../../shared-standalone-component-lib/components/common-details-participant-list-table/common-details-participant-list-table.component';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
Expand All @@ -17,6 +20,11 @@ export class FeatureFlagExclusionsTableComponent {
tableType = FEATURE_FLAG_PARTICIPANT_LIST_KEY.EXCLUDE;
dataSource$ = this.featureFlagService.selectFeatureFlagExclusions$;
isLoading$ = this.featureFlagService.isLoadingSelectedFeatureFlag$;
@Output() rowAction = new EventEmitter<ParticipantListRowActionEvent>();

constructor(private featureFlagService: FeatureFlagsService) {}

onRowAction(event: ParticipantListRowActionEvent): void {
this.rowAction.emit(event);
}
}
Loading
Loading