Skip to content

Commit c8459ab

Browse files
committed
Added Back capability to the import dialog
Before, follow-on steps in the dialog were "cancel" buttons, which would just close the dialog. This wasn't great UX. Now, all the major states of the dialog should allow the user to get back to the initial state. I had to split out the filter state into two states, since the Back for the Paratext notes (the new step) has to go to the Tag screen, not the initial.
1 parent 5794303 commit c8459ab

File tree

2 files changed

+83
-68
lines changed

2 files changed

+83
-68
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/checking/import-questions-dialog/import-questions-dialog.component.html

Lines changed: 74 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -180,62 +180,12 @@ <h2 mat-dialog-title class="dialog-icon-title">
180180
</div>
181181
}
182182

183-
@case ("filter") {
184-
<form [formGroup]="filterForm" autocomplete="off">
185-
<div class="filter-references">
186-
<mat-form-field appearance="outline">
187-
<mat-label>{{ t("reference_from") }}</mat-label>
188-
<input matInput type="text" formControlName="from" />
189-
<button mat-icon-button matSuffix #fromRef id="from-btn" (click)="openScriptureChooser(fromControl)">
190-
<mat-icon>expand_more</mat-icon>
191-
</button>
192-
@if (fromControl.invalid) {
193-
<mat-error>{{ t("must_be_valid_reference") }}</mat-error>
194-
}
195-
</mat-form-field>
196-
<mat-form-field appearance="outline">
197-
<mat-label>{{ t("reference_to") }}</mat-label>
198-
<input matInput type="text" formControlName="to" />
199-
<button mat-icon-button matSuffix (click)="openScriptureChooser(toControl)">
200-
<mat-icon>expand_more</mat-icon>
201-
</button>
202-
@if (toControl.invalid) {
203-
<mat-error>{{ t("must_be_valid_reference") }}</mat-error>
204-
}
205-
</mat-form-field>
206-
</div>
207-
<div colspan="2" class="filter-text">
208-
<div>
209-
<mat-form-field appearance="fill" subscriptSizing="dynamic">
210-
<input matInput type="text" formControlName="filter" [placeholder]="t('filter_questions')" />
211-
</mat-form-field>
212-
<button mat-button type="button" (click)="clearFilters()">{{ t("show_all") }}</button>
213-
</div>
214-
</div>
215-
<table mat-table [dataSource]="questionsForDisplay" class="question-list">
216-
<ng-container matColumnDef="verse">
217-
<th mat-header-cell *matHeaderCellDef>
218-
<mat-checkbox #selectAllCheckbox (change)="selectAllChanged($event.checked)">{{
219-
t("select_all")
220-
}}</mat-checkbox>
221-
</th>
222-
<td mat-cell *matCellDef="let element">
223-
<mat-checkbox
224-
[(ngModel)]="element.checked"
225-
[ngModelOptions]="{ standalone: true }"
226-
(ngModelChange)="checkboxChanged(element)"
227-
>{{ referenceForDisplay(element.question) }}</mat-checkbox
228-
>
229-
</td>
230-
</ng-container>
231-
<ng-container matColumnDef="question">
232-
<th mat-header-cell *matHeaderCellDef>{{ t("question") }}</th>
233-
<td mat-cell *matCellDef="let element">{{ element.question.text }}</td>
234-
</ng-container>
235-
<tr mat-header-row *matHeaderRowDef="['verse', 'question']"></tr>
236-
<tr mat-row *matRowDef="let row; columns: ['verse', 'question']"></tr>
237-
</table>
238-
</form>
183+
@case ("filter_questions") {
184+
<ng-container *ngTemplateOutlet="filterImportTemplate"></ng-container>
185+
}
186+
187+
@case ("filter_notes") {
188+
<ng-container *ngTemplateOutlet="filterImportTemplate"></ng-container>
239189
}
240190

241191
@case ("no_questions") {
@@ -262,7 +212,7 @@ <h2 mat-dialog-title class="dialog-icon-title">
262212
}
263213
</div>
264214

265-
@if (status === "filter") {
215+
@if (status === "filter_questions" || status === "filter_notes") {
266216
<div class="dialog-content-footer">
267217
@if (importClicked && selectedCount < 1) {
268218
<mat-error>
@@ -294,10 +244,67 @@ <h2 mat-dialog-title class="dialog-icon-title">
294244
</div>
295245
}
296246
</mat-dialog-content>
247+
<ng-template #filterImportTemplate>
248+
<form [formGroup]="filterForm" autocomplete="off">
249+
<div class="filter-references">
250+
<mat-form-field appearance="outline">
251+
<mat-label>{{ t("reference_from") }}</mat-label>
252+
<input matInput type="text" formControlName="from" />
253+
<button mat-icon-button matSuffix #fromRef id="from-btn" (click)="openScriptureChooser(fromControl)">
254+
<mat-icon>expand_more</mat-icon>
255+
</button>
256+
@if (fromControl.invalid) {
257+
<mat-error>{{ t("must_be_valid_reference") }}</mat-error>
258+
}
259+
</mat-form-field>
260+
<mat-form-field appearance="outline">
261+
<mat-label>{{ t("reference_to") }}</mat-label>
262+
<input matInput type="text" formControlName="to" />
263+
<button mat-icon-button matSuffix (click)="openScriptureChooser(toControl)">
264+
<mat-icon>expand_more</mat-icon>
265+
</button>
266+
@if (toControl.invalid) {
267+
<mat-error>{{ t("must_be_valid_reference") }}</mat-error>
268+
}
269+
</mat-form-field>
270+
</div>
271+
<div colspan="2" class="filter-text">
272+
<div>
273+
<mat-form-field appearance="fill" subscriptSizing="dynamic">
274+
<input matInput type="text" formControlName="filter" [placeholder]="t('filter_questions')" />
275+
</mat-form-field>
276+
<button mat-button type="button" (click)="clearFilters()">{{ t("show_all") }}</button>
277+
</div>
278+
</div>
279+
<table mat-table [dataSource]="questionsForDisplay" class="question-list">
280+
<ng-container matColumnDef="verse">
281+
<th mat-header-cell *matHeaderCellDef>
282+
<mat-checkbox #selectAllCheckbox (change)="selectAllChanged($event.checked)">{{
283+
t("select_all")
284+
}}</mat-checkbox>
285+
</th>
286+
<td mat-cell *matCellDef="let element">
287+
<mat-checkbox
288+
[(ngModel)]="element.checked"
289+
[ngModelOptions]="{ standalone: true }"
290+
(ngModelChange)="checkboxChanged(element)"
291+
>{{ referenceForDisplay(element.question) }}</mat-checkbox
292+
>
293+
</td>
294+
</ng-container>
295+
<ng-container matColumnDef="question">
296+
<th mat-header-cell *matHeaderCellDef>{{ t("question") }}</th>
297+
<td mat-cell *matCellDef="let element">{{ element.question.text }}</td>
298+
</ng-container>
299+
<tr mat-header-row *matHeaderRowDef="['verse', 'question']"></tr>
300+
<tr mat-row *matRowDef="let row; columns: ['verse', 'question']"></tr>
301+
</table>
302+
</form>
303+
</ng-template>
297304
@if (questionSource != null && importCanceled === false) {
298305
<mat-dialog-actions align="end">
299306
@if (status === "paratext_tag_selection") {
300-
<button mat-button (click)="cancelParatextTagSelection()">
307+
<button mat-button (click)="reset()">
301308
{{ t("back") }}
302309
</button>
303310
<button
@@ -309,9 +316,14 @@ <h2 mat-dialog-title class="dialog-icon-title">
309316
{{ t("import_from_paratext_next") }}
310317
</button>
311318
}
312-
@if (status === "filter" || status === "file_import_errors") {
313-
<button mat-button mat-dialog-close>
314-
{{ t("cancel") }}
319+
@if (status === "filter_questions" || status === "file_import_errors") {
320+
<button mat-button (click)="reset()">
321+
{{ t("back") }}
322+
</button>
323+
}
324+
@if (status === "filter_notes") {
325+
<button mat-button (click)="importFromParatext()">
326+
{{ t("back") }}
315327
</button>
316328
}
317329
@if (status === "no_questions" || status === "update_transcelerator" || status === "missing_header_row") {
@@ -324,7 +336,7 @@ <h2 mat-dialog-title class="dialog-icon-title">
324336
{{ t("continue_import") }}
325337
</button>
326338
}
327-
@if (status === "filter") {
339+
@if (status === "filter_questions" || status === "filter_notes") {
328340
<button id="import-button" mat-flat-button color="primary" (click)="importQuestions()">
329341
<span class="hide-lt-sm">{{ t("import_count_questions", { count: selectedCount }) }}</span>
330342
<span class="hide-gt-sm">{{ t("import") }}</span>

src/SIL.XForge.Scripture/ClientApp/src/app/checking/import-questions-dialog/import-questions-dialog.component.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CdkScrollable } from '@angular/cdk/scrolling';
2-
import { AsyncPipe } from '@angular/common';
2+
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
33
import { Component, DestroyRef, ElementRef, Inject, NgZone, OnDestroy, ViewChild } from '@angular/core';
44
import { AbstractControl, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
55
import { MatAnchor, MatButton, MatIconButton } from '@angular/material/button';
@@ -52,8 +52,8 @@ import { environment } from '../../../environments/environment';
5252
import { ParatextProject } from '../../core/models/paratext-project';
5353
import { QuestionDoc } from '../../core/models/question-doc';
5454
import { TextsByBookId } from '../../core/models/texts-by-book-id';
55-
import { ParatextNote, ParatextNoteTag, ParatextService } from '../../core/paratext.service';
5655
import { TransceleratorQuestion } from '../../core/models/transcelerator-question';
56+
import { ParatextNote, ParatextNoteTag, ParatextService } from '../../core/paratext.service';
5757
import { SFProjectService } from '../../core/sf-project.service';
5858
import {
5959
ScriptureChooserDialogComponent,
@@ -91,7 +91,8 @@ type DialogErrorState = 'update_transcelerator' | 'file_import_errors' | 'missin
9191
type DialogStatus =
9292
| 'initial'
9393
| 'no_questions'
94-
| 'filter'
94+
| 'filter_questions'
95+
| 'filter_notes'
9596
| 'loading'
9697
| 'progress'
9798
| 'paratext_tag_selection'
@@ -137,6 +138,7 @@ type DialogStatus =
137138
MatProgressBar,
138139
MatDialogActions,
139140
AsyncPipe,
141+
NgTemplateOutlet,
140142
MatSelect,
141143
MatOption
142144
]
@@ -260,7 +262,8 @@ export class ImportQuestionsDialogComponent implements OnDestroy {
260262
return 'file_import_errors';
261263
}
262264
if (this.questionSource != null) {
263-
return this.questionList.length === 0 ? 'no_questions' : 'filter';
265+
if (this.questionList.length === 0) return 'no_questions';
266+
return this.selectedParatextTagId !== null ? 'filter_notes' : 'filter_questions';
264267
} else {
265268
return 'initial';
266269
}
@@ -316,7 +319,7 @@ export class ImportQuestionsDialogComponent implements OnDestroy {
316319
}
317320

318321
dialogScroll(): void {
319-
if (this.status === 'file_import_errors' || this.status === 'filter') {
322+
if (this.status === 'file_import_errors' || this.status === 'filter_notes' || this.status === 'filter_questions') {
320323
const element = this.dialogContentBody.nativeElement;
321324
// add more list items if the user has scrolled to within 1000 pixels of the bottom of the list
322325
if (element.scrollHeight <= element.scrollTop + element.clientHeight + 1000) {
@@ -533,7 +536,7 @@ export class ImportQuestionsDialogComponent implements OnDestroy {
533536
}
534537
}
535538

536-
cancelParatextTagSelection(): void {
539+
reset(): void {
537540
this.showParatextTagSelector = false;
538541
this.questionSource = null;
539542
this.loadingParatextTags = false;

0 commit comments

Comments
 (0)