Skip to content

Commit

Permalink
SF-3020 Add progress and error reporting to draft generation page (#2836
Browse files Browse the repository at this point in the history
)
  • Loading branch information
RaymondLuong3 authored Nov 14, 2024
1 parent 0ddfd50 commit 505fac2
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ <h2 mat-dialog-title>{{ t("select_alternate_project") }}</h2>
@if (!isAppOnline) {
<mat-error class="offline-message">{{ t("connect_to_the_internet") }}</mat-error>
}
<div class="unlisted-project-message">
<a
[appRouterLink]="['projects']"
(click)="close()"
[innerHtml]="i18n.translateAndInsertTags('draft_apply_dialog.looking_for_unlisted_project')"
></a>
</div>
@if (targetProject$ | async; as project) {
<div class="target-project-content">
@if (targetChapters$ | async; as chapters) {
Expand All @@ -44,6 +37,13 @@ <h2 mat-dialog-title>{{ t("select_alternate_project") }}</h2>
}}</mat-error>
}
</form>
<div class="unlisted-project-message">
<a
[appRouterLink]="['projects']"
(click)="close()"
[innerHtml]="i18n.translateAndInsertTags('draft_apply_dialog.looking_for_unlisted_project')"
></a>
</div>
</div>
<div mat-dialog-actions>
<button mat-button class="cancel-button" [mat-dialog-close]="false">{{ t("cancel") }}</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
padding: 8px 0;
}

.unlisted-project-message {
margin-top: 1em;
}

a {
text-decoration: none;
color: initial;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<ng-container *transloco="let t; read: 'draft_apply_progress-dialog'">
<mat-dialog-content>
<div class="progress-content">
@if (draftApplyProgress?.completed !== true) {
<div class="progress-container">
<mat-progress-bar mode="determinate" color="primary" [value]="progress"></mat-progress-bar>
<p>{{ t("add_draft_to_book", { bookName }) }}</p>
</div>
} @else {
<div class="result-container">
@if (failedToApplyChapters != null && failedToApplyChapters.length > 0) {
<div class="error-result">
<h2 class="failed-message">
<mat-icon>warning</mat-icon>
<span>{{ t("some_chapters_not_applied", { bookName }) }}</span>
</h2>
<p>{{ t("go_to_chapter") }}</p>
<span>{{ t("failed_to_apply_chapters") }}</span>
<span class="failed-chapters">{{ failedToApplyChapters }}</span>
</div>
} @else {
<h2>
<span>{{ t("successfully_applied_all_chapters", { bookName }) }}</span>
</h2>
}
</div>
}
</div>
</mat-dialog-content>
<mat-dialog-actions>
@if (draftApplyProgress?.completed === true) {
<button mat-flat-button (click)="close()">{{ t("close") }}</button>
}
</mat-dialog-actions>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.progress-content {
min-width: 240px;
max-width: 500px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

.progress-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;

mat-progress-bar {
margin-top: 1em;
}
}

.error-result {
display: flex;
flex-direction: column;

.mat-icon {
flex-shrink: 0;
}

.failed-chapters {
text-indent: 1em;
font-weight: bold;
font-size: 2em;
margin-top: 0.5em;
line-height: 1em;
}
}

.failed-message {
display: flex;
column-gap: 8px;
align-items: center;
}
}

.mat-mdc-dialog-actions {
justify-content: flex-end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { CommonModule } from '@angular/common';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { BehaviorSubject } from 'rxjs';
import { anything, mock, when } from 'ts-mockito';
import { I18nService } from 'xforge-common/i18n.service';
import { configureTestingModule, TestTranslocoModule } from 'xforge-common/test-utils';
import { UICommonModule } from 'xforge-common/ui-common.module';
import { DraftApplyProgress, DraftApplyProgressDialogComponent } from './draft-apply-progress-dialog.component';

const mockI18nService = mock(I18nService);
const mockMatDialogRef = mock(MatDialogRef);

describe('DraftApplyProgressDialogComponent', () => {
let env: TestEnvironment;
let progress$: BehaviorSubject<DraftApplyProgress> = new BehaviorSubject<DraftApplyProgress>({
bookNum: 1,
completed: false,
chapters: [1, 2, 3],
chaptersApplied: []
});

configureTestingModule(() => ({
imports: [DraftApplyProgressDialogComponent, UICommonModule, CommonModule, TestTranslocoModule],
providers: [
{ provide: I18nService, useMock: mockI18nService },
{ provide: MatDialogRef, useMock: mockMatDialogRef },
{ provide: MAT_DIALOG_DATA, useValue: { draftApplyProgress$: progress$ } }
]
}));

beforeEach(async () => {
env = new TestEnvironment();
});

it('shows progress', () => {
progress$.next({ bookNum: 1, chapters: [1, 2], chaptersApplied: [1], completed: false });
env.fixture.detectChanges();
expect(env.progressContainer).not.toBeNull();
expect(env.resultContainer).toBeNull();
expect(env.component.progress).toBe(50);
});

it('shows apply draft completed', () => {
progress$.next({ bookNum: 1, chapters: [1, 2], chaptersApplied: [1, 2], completed: true });
env.fixture.detectChanges();
expect(env.progressContainer).toBeNull();
expect(env.resultContainer).not.toBeNull();
expect(env.component.failedToApplyChapters).toBeUndefined();
expect(env.resultContainer.textContent).toContain('Successfully applied all chapters');
});

it('shows chapters that failed to be applied', () => {
progress$.next({ bookNum: 1, chapters: [1, 2], chaptersApplied: [1], completed: true });
env.fixture.detectChanges();
expect(env.progressContainer).toBeNull();
expect(env.resultContainer).not.toBeNull();
expect(env.component.failedToApplyChapters).toEqual('2');
expect(env.resultContainer.textContent).toContain('warning');
});
});

class TestEnvironment {
component: DraftApplyProgressDialogComponent;
fixture: ComponentFixture<DraftApplyProgressDialogComponent>;

constructor() {
this.fixture = TestBed.createComponent(DraftApplyProgressDialogComponent);
this.component = this.fixture.componentInstance;
this.fixture.detectChanges();
when(mockI18nService.enumerateList(anything())).thenCall((items: string[]) => items.join(', '));
}

get progressContainer(): HTMLElement {
return this.fixture.nativeElement.querySelector('.progress-container');
}

get resultContainer(): HTMLElement {
return this.fixture.nativeElement.querySelector('.result-container');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { CommonModule } from '@angular/common';
import { Component, DestroyRef, Inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { TranslocoModule } from '@ngneat/transloco';
import { Observable } from 'rxjs';
import { I18nService } from 'xforge-common/i18n.service';
import { UICommonModule } from 'xforge-common/ui-common.module';

export interface DraftApplyProgress {
bookNum: number;
chapters: number[];
chaptersApplied: number[];
completed: boolean;
}

@Component({
selector: 'app-draft-apply-progress',
standalone: true,
imports: [CommonModule, UICommonModule, TranslocoModule],
templateUrl: './draft-apply-progress-dialog.component.html',
styleUrl: './draft-apply-progress-dialog.component.scss'
})
export class DraftApplyProgressDialogComponent {
draftApplyProgress?: DraftApplyProgress;

constructor(
@Inject(MatDialogRef) private readonly dialogRef: MatDialogRef<DraftApplyProgressDialogComponent>,
@Inject(MAT_DIALOG_DATA) data: { draftApplyProgress$: Observable<DraftApplyProgress | undefined> },
private readonly i18n: I18nService,
destroyRef: DestroyRef
) {
data.draftApplyProgress$
.pipe(takeUntilDestroyed(destroyRef))
.subscribe(progress => (this.draftApplyProgress = progress));
}

get progress(): number | undefined {
if (this.draftApplyProgress == null) return undefined;
return (this.draftApplyProgress.chaptersApplied.length / this.draftApplyProgress.chapters.length) * 100;
}

get bookName(): string {
if (this.draftApplyProgress == null) return '';
return this.i18n.localizeBook(this.draftApplyProgress.bookNum);
}

get failedToApplyChapters(): string | undefined {
if (this.draftApplyProgress == null || !this.draftApplyProgress.completed) return undefined;
const chapters: string[] = this.draftApplyProgress.chapters
.filter(c => !this.draftApplyProgress.chaptersApplied.includes(c))
.map(c => c.toString());
return chapters.length > 0 ? this.i18n.enumerateList(chapters) : undefined;
}

close(): void {
this.dialogRef.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ <h3>{{ t("draft_active_header") }}</h3>
}
@if (isDraftComplete(draftJob) || hasAnyCompletedBuild) {
<section class="draft-complete">
<mat-card>
<mat-card class="preview-card">
<mat-card-header>
<mat-card-title>
{{ isDraftComplete(draftJob) ? t("draft_is_ready") : t("preview_last_draft_header") }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ mat-divider.mat-divider-inset {
.draft-complete {
max-width: 500px;
margin-inline-start: 1px;
display: flex;
flex-direction: column;
row-gap: 16px;

.preview-card {
flex-shrink: 0;
}

app-draft-apply-progress {
width: 100%;
}

h2 {
font-weight: 500;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { ServalProjectComponent } from '../../serval-administration/serval-proje
import { SharedModule } from '../../shared/shared.module';
import { WorkingAnimatedIndicatorComponent } from '../../shared/working-animated-indicator/working-animated-indicator.component';
import { NllbLanguageService } from '../nllb-language.service';
import { DraftApplyProgressDialogComponent } from './draft-apply-progress-dialog/draft-apply-progress-dialog.component';
import { BuildConfig, DraftZipProgress, activeBuildStates } from './draft-generation';
import {
DraftGenerationStepsComponent,
Expand Down Expand Up @@ -62,7 +63,8 @@ import { SupportedBackTranslationLanguagesDialogComponent } from './supported-ba
DraftInformationComponent,
SupportedBackTranslationLanguagesDialogComponent,
ServalProjectComponent,
DraftPreviewBooksComponent
DraftPreviewBooksComponent,
DraftApplyProgressDialogComponent
]
})
export class DraftGenerationComponent extends DataLoadingComponent implements OnInit {
Expand Down
Loading

0 comments on commit 505fac2

Please sign in to comment.