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

Import Feature flag modal implemented #1673

Merged
merged 9 commits into from
Jul 2, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -462,44 +462,56 @@ export class ExperimentRepository extends Repository<Experiment> {

public async findOneExperiment(id: string): Promise<Experiment> {
const experiment = await this.createBaseQueryBuilder()
.addOrderBy('conditions.order', 'ASC')
.addOrderBy('partitions.order', 'ASC')
.addOrderBy('factors.order', 'ASC')
.addOrderBy('levels.order', 'ASC')
.where({ id })
.getOne();
.addOrderBy('conditions.order', 'ASC')
.addOrderBy('partitions.order', 'ASC')
.addOrderBy('factors.order', 'ASC')
.addOrderBy('levels.order', 'ASC')
.where({ id })
.getOne();
return experiment;
}

public async getExperimentCSVDataExport(experimentId: string): Promise<ExperimentCSVData[]> {
// Get the experiment details
const experimentQuery = await this.createBaseQueryBuilder()
.select([
'experiment.id as "experimentId"',
'experiment.name as "experimentName"',
'experiment.context as "context"',
'experiment.assignmentUnit as "assignmentUnit"',
'experiment.group as "group"',
'experiment.consistencyRule as "consistencyRule"',
'experiment.type as "designType"',
'experiment.assignmentAlgorithm as "algorithmType"',
'experiment.stratificationFactorStratificationFactorName as "stratification"',
'experiment.postExperimentRule as "postRule"',
'experimentRevertCondition.conditionCode as "revertTo"',
'"enrollingStateTimeLog"."timeLog" as "enrollmentStartDate"',
'"enrollmentCompleteStateTimeLog"."timeLog" as "enrollmentCompleteDate"',
'"conditionPayloadMain"."payloadValue" as "payload"',
'"decisionPointData"."excludeIfReached" as "excludeIfReached"',
'"decisionPointData"."id" as "expDecisionPointId"',
'experimentCondition.id as "expConditionId"',
'experimentCondition.conditionCode as "conditionName"',
'experiment.id as "experimentId"',
'experiment.name as "experimentName"',
'experiment.context as "context"',
'experiment.assignmentUnit as "assignmentUnit"',
'experiment.group as "group"',
'experiment.consistencyRule as "consistencyRule"',
'experiment.type as "designType"',
'experiment.assignmentAlgorithm as "algorithmType"',
'experiment.stratificationFactorStratificationFactorName as "stratification"',
'experiment.postExperimentRule as "postRule"',
'experimentRevertCondition.conditionCode as "revertTo"',
'"enrollingStateTimeLog"."timeLog" as "enrollmentStartDate"',
'"enrollmentCompleteStateTimeLog"."timeLog" as "enrollmentCompleteDate"',
'"conditionPayloadMain"."payloadValue" as "payload"',
'"decisionPointData"."excludeIfReached" as "excludeIfReached"',
'"decisionPointData"."id" as "expDecisionPointId"',
'experimentCondition.id as "expConditionId"',
'experimentCondition.conditionCode as "conditionName"',
])
.leftJoin(ExperimentCondition, 'experimentCondition', 'experimentCondition.experimentId = experiment.id')
.leftJoin(ExperimentCondition, 'experimentRevertCondition', 'experimentRevertCondition.id = experiment.revertTo')
.leftJoin(DecisionPoint, 'decisionPointData', 'decisionPointData.experimentId = experiment.id')
.leftJoin(ConditionPayload, 'conditionPayloadMain', 'conditionPayloadMain.parentConditionId = experimentCondition.id AND conditionPayloadMain.decisionPointId = decisionPointData.id')
.leftJoin(StateTimeLog, 'enrollingStateTimeLog', 'enrollingStateTimeLog.experimentId = experiment.id AND enrollingStateTimeLog.toState = \'enrolling\'')
.leftJoin(StateTimeLog, 'enrollmentCompleteStateTimeLog', 'enrollmentCompleteStateTimeLog.experimentId = experiment.id AND enrollmentCompleteStateTimeLog.toState = \'enrollmentComplete\'')
.leftJoin(
ConditionPayload,
'conditionPayloadMain',
'conditionPayloadMain.parentConditionId = experimentCondition.id AND conditionPayloadMain.decisionPointId = decisionPointData.id'
)
.leftJoin(
StateTimeLog,
'enrollingStateTimeLog',
"enrollingStateTimeLog.experimentId = experiment.id AND enrollingStateTimeLog.toState = 'enrolling'"
)
.leftJoin(
StateTimeLog,
'enrollmentCompleteStateTimeLog',
"enrollmentCompleteStateTimeLog.experimentId = experiment.id AND enrollmentCompleteStateTimeLog.toState = 'enrollmentComplete'"
)
.groupBy('experiment.id')
.addGroupBy('experimentCondition.id')
.addGroupBy('experimentRevertCondition.conditionCode')
Expand All @@ -509,7 +521,7 @@ export class ExperimentRepository extends Repository<Experiment> {
.addGroupBy('enrollmentCompleteStateTimeLog.timeLog')
.where('experiment.id = :experimentId', { experimentId })
.getRawMany();

return experimentQuery;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<div class="modal-container">
<app-common-dialog
[title]="data.title"
[cancelBtnLabel]="data.cancelBtnLabel"
[primaryActionBtnLabel]="data.primaryActionBtnLabel"
[primaryActionBtnColor]="data.primaryActionBtnColor"
[primaryActionBtnDisabled]="isImportActionBtnDisabled | async"
>
<div class="drag-drop-container">
<div
class="input-container"
(dragover)="onDragOver($event)"
(dragleave)="onDragLeave($event)"
(drop)="onDrop($event)"
[ngClass]="{ 'drag-over': (isDragOver | async) }"
>
<div class="input-container-header">
<mat-icon>close</mat-icon>
</div>
<div class="input-container-content" *ngIf="fileName | async as fileName; else uploadPrompt">
<mat-icon>insert_drive_file</mat-icon>
<p>{{ fileName }}</p>
</div>
<ng-template #uploadPrompt>
<mat-icon>file_upload</mat-icon>
<p>Drag & drop or</p>
<button mat-flat-button color="primary" (click)="fileInput.click()">
<span>Choose JSON</span>
</button>
<input type="file" #fileInput (change)="onFileSelected($event)" style="display: none" />
</ng-template>
</div>
<p>
{{ 'feature-flags.import-feature-flag.message.text' | translate }}
<a href="">Learn More</a>
</p>
</div>
</app-common-dialog>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.drag-drop-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: grey;

mat-icon {
height: 70px;
width: 70px;
font-size: 70px;
color: grey;
}
}

.input-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px dashed grey;
border-radius: 4px;
height: 206px;
width: 592px;
position: relative;

button {
font-size: 14px;
}
}

.input-container-header {
position: absolute;
top: 5px;
right: 5px;

mat-icon {
height: 24px;
width: 24px;
font-size: 24px;
}
}

.input-container-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

p {
color: grey;
font-size: 12px;
}

a {
text-decoration: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ChangeDetectionStrategy, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
import { CommonModalComponent } from '../../../../../shared-standalone-component-lib/components';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { CommonModalConfig } from '../../../../../shared-standalone-component-lib/components/common-modal/common-modal-config';
import { FeatureFlagsService } from '../../../../../core/feature-flags/feature-flags.service';
import { BehaviorSubject } from 'rxjs';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../../../../shared/shared.module';

@Component({
selector: 'app-import-feature-flag-modal',
standalone: true,
imports: [CommonModalComponent, CommonModule, SharedModule],
templateUrl: './import-feature-flag-modal.component.html',
styleUrls: ['./import-feature-flag-modal.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ImportFeatureFlagModalComponent {
@ViewChild('fileInput') fileInput: ElementRef<HTMLInputElement>;

isImportActionBtnDisabled = new BehaviorSubject<boolean>(true);
isDragOver = new BehaviorSubject<boolean>(false);
fileName = new BehaviorSubject<string | null>(null);

constructor(
@Inject(MAT_DIALOG_DATA)
public data: CommonModalConfig,
public dialog: MatDialog,
public dialogRef: MatDialogRef<ImportFeatureFlagModalComponent>
) {}

KD1712 marked this conversation as resolved.
Show resolved Hide resolved
onDragOver(event: DragEvent) {
event.preventDefault();
event.stopPropagation();
this.isDragOver.next(true);
}

onDragLeave(event: DragEvent) {
event.preventDefault();
event.stopPropagation();
this.isDragOver.next(false);
}

onDrop(event: DragEvent) {
event.preventDefault();
event.stopPropagation();
this.isDragOver.next(false);

const files = event.dataTransfer?.files;
if (files && files.length > 0) {
this.processFile(files[0]);
}
}

onFileSelected(event: Event) {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length > 0) {
this.processFile(input.files[0]);
}
}

processFile(file: File) {
if (file.type === 'application/json') {
this.fileName.next(file.name);
this.isImportActionBtnDisabled.next(false);
this.handleFileInput(file);
} else {
alert('Please upload a valid JSON file.');
this.fileName.next(null);
this.isImportActionBtnDisabled.next(true);
}
}

handleFileInput(file: File) {
const reader = new FileReader();
reader.onload = (e: any) => {
const jsonContent = e.target.result;
console.log(JSON.parse(jsonContent));
};
reader.readAsText(file);
}

closeModal() {
this.dialogRef.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
[showPrimaryButton]="true"
[primaryButtonText]="'feature-flags.add-feature-flag.text' | translate"
[menuButtonItems]="menuButtonItems"
[showMenuButton]="true"
[isSectionCardExpanded]="isSectionCardExpanded"
(primaryButtonClick)="onAddFeatureFlagButtonClick()"
(menuButtonItemClick)="onMenuButtonItemClick($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export class FeatureFlagRootSectionCardComponent {
}

onMenuButtonItemClick(menuButtonItemName: string) {
console.log('onMenuButtonItemClick:', menuButtonItemName);
if (menuButtonItemName === 'Import Feature Flag') {
this.dialogService.openImportFeatureFlagModal();
} else if (menuButtonItemName === 'Export All Feature Flags') {
console.log('onMenuButtonItemClick:', menuButtonItemName);
}
}

onSectionCardExpandChange(isSectionCardExpanded: boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { MatConfirmDialogComponent } from '../components/mat-confirm-dialog/mat-
import { AddFeatureFlagModalComponent } from '../../features/dashboard/feature-flags/modals/add-feature-flag-modal/add-feature-flag-modal.component';
import { CommonModalConfig } from '../../shared-standalone-component-lib/components/common-modal/common-modal-config';
import { DeleteFeatureFlagModalComponent } from '../../features/dashboard/feature-flags/modals/delete-feature-flag-modal/delete-feature-flag-modal.component';

import { ImportFeatureFlagModalComponent } from '../../features/dashboard/feature-flags/modals/import-feature-flag-modal/import-feature-flag-modal.component';
import { UpdateFlagStatusConfirmationModalComponent } from '../../features/dashboard/feature-flags/modals/update-flag-status-confirmation-modal/update-flag-status-confirmation-modal.component';
import { EditFeatureFlagModalComponent } from '../../features/dashboard/feature-flags/modals/edit-feature-flag-modal/edit-feature-flag-modal.component';

Expand Down Expand Up @@ -101,4 +103,21 @@ export class DialogService {
};
return this.dialog.open(DeleteFeatureFlagModalComponent, config);
}

openImportFeatureFlagModal() {
const commonModalConfig: CommonModalConfig = {
title: 'Import Feature Flag',
primaryActionBtnLabel: 'Import',
primaryActionBtnColor: 'primary',
cancelBtnLabel: 'Cancel',
};
const config: MatDialogConfig = {
data: commonModalConfig,
width: '670px',
height: '450px',
autoFocus: 'input',
disableClose: true,
};
return this.dialog.open(ImportFeatureFlagModalComponent, config);
}
}
1 change: 1 addition & 0 deletions frontend/projects/upgrade/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@
"feature-flags.enable.text": "Enable",
"feature-flags.add-feature-flag.text": "Add Feature Flag",
"feature-flags.import-feature-flag.text": "Import Feature Flag",
"feature-flags.import-feature-flag.message.text": "The Feature Flag JSON file should include the required properties for it to be imported",
"feature-flags.export-all-feature-flags.text": "Export All Feature Flags",
"segments.title.text": "Segments",
"segments.subtitle.text": "Define new segments to include or exclude from any experiment",
Expand Down
Loading