UX Overhaul Part 1 (#3047)
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
This commit is contained in:
parent
5934d516f3
commit
ff79710ac6
324 changed files with 11589 additions and 4598 deletions
|
|
@ -0,0 +1,179 @@
|
|||
<ng-container *transloco="let t; read: 'import-cbl-modal'">
|
||||
|
||||
<div class="row g-0" style="min-width: 135px;">
|
||||
<app-step-tracker [steps]="steps" [currentStep]="currentStepIndex"></app-step-tracker>
|
||||
</div>
|
||||
|
||||
<!-- This is going to need to have a fixed height with a scrollbar-->
|
||||
<div>
|
||||
@switch (currentStepIndex) {
|
||||
@case (Step.Import) {
|
||||
<div class="row g-0">
|
||||
<p>{{t('import-description')}}</p>
|
||||
<form [formGroup]="uploadForm" enctype="multipart/form-data">
|
||||
<file-upload formControlName="files"></file-upload>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
@case (Step.Validate) {
|
||||
<p>{{t('validate-description')}}</p>
|
||||
<div class="row g-0">
|
||||
<div ngbAccordion #accordion="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
@if (fileToProcess.validateSummary; as summary) {
|
||||
<div ngbAccordionItem>
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="validationList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@case (Step.DryRun) {
|
||||
<div class="row g-0">
|
||||
<p>{{t('dry-run-description')}}</p>
|
||||
|
||||
<div ngbAccordion #a="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
@if (fileToProcess.dryRunSummary; as summary) {
|
||||
<div ngbAccordionItem>
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@case (Step.Finalize) {
|
||||
<div class="row g-0">
|
||||
<div ngbAccordion #a="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
@if (fileToProcess.finalizeSummary; as summary) {
|
||||
<div ngbAccordionItem>
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<ng-template #validationList let-summary="summary">
|
||||
@if (summary.results.length > 0) {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-triangle-exclamation" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-warning')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="list-group list-group-numbered list-group-flush" >
|
||||
@for(result of summary.results; track result) {
|
||||
<li class="list-group-item no-hover"
|
||||
[innerHTML]="result | cblConflictReason | safeHtml">
|
||||
</li>
|
||||
}
|
||||
|
||||
|
||||
</ol>
|
||||
}
|
||||
@else {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-no-issue-description')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #resultsList let-summary="summary">
|
||||
<ul class="list-group list-group-flush">
|
||||
@for(result of summary.results; track result.order) {
|
||||
<li class="list-group-item no-hover"
|
||||
innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li>
|
||||
}
|
||||
</ul>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #heading let-filename="filename" let-summary="summary">
|
||||
@switch (summary.success) {
|
||||
@case (CblImportResult.Success) {
|
||||
<span class="badge heading-badge bg-primary me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Fail) {
|
||||
<span class="badge heading-badge bg-danger me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Partial) {
|
||||
<span class="badge heading-badge bg-warning me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
}
|
||||
<span>{{filename}}
|
||||
@if(summary.cblName) {
|
||||
<span>: ({{summary.cblName}})</span>
|
||||
}
|
||||
</span>
|
||||
</ng-template>
|
||||
|
||||
<div class="modal-footer mt-3">
|
||||
<form [formGroup]="cblSettingsForm" class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="settings-comicvine-mode" role="switch" formControlName="comicVineMatching" class="form-check-input"
|
||||
aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="settings-comicvine-mode">{{t('comicvine-parsing-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Spacer -->
|
||||
<div class="col" aria-hidden="true"></div>
|
||||
<div class="col-auto ms-1">
|
||||
<a class="btn btn-icon" [href]="WikiLink.ReadingListCBL" target="_blank" rel="noopener noreferrer">Help</a>
|
||||
</div>
|
||||
<div class="col-auto ms-1">
|
||||
<button type="button" class="btn btn-primary" (click)="prevStep()" [disabled]="!canMoveToPrevStep()">{{t('prev')}}</button>
|
||||
</div>
|
||||
<div class="col-auto ms-1">
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="!canMoveToNextStep()">{{t(NextButtonLabel)}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</ng-container>
|
||||
|
|
@ -43,3 +43,4 @@ file-upload {
|
|||
::ng-deep .reading-list-fail--item {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
|
|
@ -1,23 +1,29 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, inject, ViewChild} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FileUploadModule, FileUploadValidators} from '@iplab/ngx-file-upload';
|
||||
import {
|
||||
NgbAccordionModule, NgbAccordionToggle,
|
||||
NgbActiveModal
|
||||
} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { CblImportResult } from 'src/app/_models/reading-list/cbl/cbl-import-result.enum';
|
||||
import { CblImportSummary } from 'src/app/_models/reading-list/cbl/cbl-import-summary';
|
||||
import { ReadingListService } from 'src/app/_services/reading-list.service';
|
||||
import {StepTrackerComponent, TimelineStep} from '../../_components/step-tracker/step-tracker.component';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
|
||||
import {CblConflictReasonPipe} from "../../../_pipes/cbl-conflict-reason.pipe";
|
||||
import {CblImportResultPipe} from "../../../_pipes/cbl-import-result.pipe";
|
||||
import {FileUploadComponent, FileUploadValidators} from "@iplab/ngx-file-upload";
|
||||
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||
import {NgForOf, NgIf, NgTemplateOutlet} from "@angular/common";
|
||||
import {
|
||||
NgbAccordionBody,
|
||||
NgbAccordionButton,
|
||||
NgbAccordionCollapse,
|
||||
NgbAccordionDirective,
|
||||
NgbAccordionHeader,
|
||||
NgbAccordionItem,
|
||||
NgbActiveModal
|
||||
} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
|
||||
import {StepTrackerComponent, TimelineStep} from "../step-tracker/step-tracker.component";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {WikiLink} from "../../../_models/wiki";
|
||||
import {ReadingListService} from "../../../_services/reading-list.service";
|
||||
import {UtilityService} from "../../../shared/_services/utility.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {forkJoin} from "rxjs";
|
||||
import {CblImportSummary} from "../../../_models/reading-list/cbl/cbl-import-summary";
|
||||
import { WikiLink } from 'src/app/_models/wiki';
|
||||
import { CblImportResult } from 'src/app/_models/reading-list/cbl/cbl-import-result.enum';
|
||||
|
||||
|
||||
interface FileStep {
|
||||
fileName: string;
|
||||
|
|
@ -34,18 +40,35 @@ enum Step {
|
|||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-import-cbl-modal',
|
||||
selector: 'app-import-cbl',
|
||||
standalone: true,
|
||||
imports: [CommonModule,
|
||||
FileUploadModule,
|
||||
NgbAccordionModule,
|
||||
imports: [
|
||||
CblConflictReasonPipe,
|
||||
CblImportResultPipe,
|
||||
FileUploadComponent,
|
||||
FormsModule,
|
||||
NgbAccordionBody,
|
||||
NgbAccordionButton,
|
||||
NgbAccordionCollapse,
|
||||
NgbAccordionDirective,
|
||||
NgbAccordionHeader,
|
||||
NgbAccordionItem,
|
||||
ReactiveFormsModule,
|
||||
SafeHtmlPipe,
|
||||
CblConflictReasonPipe, ReactiveFormsModule, StepTrackerComponent, CblImportResultPipe, NgbAccordionToggle, TranslocoDirective],
|
||||
templateUrl: './import-cbl-modal.component.html',
|
||||
styleUrls: ['./import-cbl-modal.component.scss'],
|
||||
StepTrackerComponent,
|
||||
TranslocoDirective,
|
||||
NgTemplateOutlet
|
||||
],
|
||||
templateUrl: './import-cbl.component.html',
|
||||
styleUrl: './import-cbl.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ImportCblModalComponent {
|
||||
export class ImportCblComponent {
|
||||
private readonly readingListService = inject(ReadingListService);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
protected readonly utilityService = inject(UtilityService);
|
||||
|
||||
|
||||
protected readonly CblImportResult = CblImportResult;
|
||||
protected readonly Step = Step;
|
||||
|
|
@ -90,13 +113,7 @@ export class ImportCblModalComponent {
|
|||
}
|
||||
}
|
||||
|
||||
constructor(private ngModal: NgbActiveModal, private readingListService: ReadingListService,
|
||||
public utilityService: UtilityService, private readonly cdRef: ChangeDetectorRef,
|
||||
private toastr: ToastrService) {}
|
||||
|
||||
close() {
|
||||
this.ngModal.close();
|
||||
}
|
||||
|
||||
nextStep() {
|
||||
if (this.currentStepIndex === Step.Import && !this.isFileSelected()) return;
|
||||
|
|
@ -113,11 +130,12 @@ export class ImportCblModalComponent {
|
|||
const pages = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const formData = new FormData();
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', 'true');
|
||||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.validateCbl(formData));
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', 'true');
|
||||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.validateCbl(formData));
|
||||
}
|
||||
|
||||
forkJoin(pages).subscribe(results => {
|
||||
this.filesToProcess = [];
|
||||
results.forEach(cblImport => {
|
||||
|
|
@ -210,7 +228,7 @@ export class ImportCblModalComponent {
|
|||
pages.push(this.readingListService.importCbl(formData));
|
||||
}
|
||||
forkJoin(pages).subscribe(results => {
|
||||
results.forEach(cblImport => {
|
||||
results.forEach(cblImport => {
|
||||
const index = this.filesToProcess.findIndex(p => p.fileName === cblImport.fileName);
|
||||
this.filesToProcess[index].dryRunSummary = cblImport;
|
||||
});
|
||||
|
|
@ -234,6 +252,7 @@ export class ImportCblModalComponent {
|
|||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.importCbl(formData));
|
||||
}
|
||||
|
||||
forkJoin(pages).subscribe(results => {
|
||||
results.forEach(cblImport => {
|
||||
const index = this.filesToProcess.findIndex(p => p.fileName === cblImport.fileName);
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
<ng-container *transloco="let t; read: 'reading-list-detail'">
|
||||
<app-side-nav-companion-bar [hasExtras]="readingList !== undefined" [extraDrawer]="extrasDrawer">
|
||||
<h2 title>
|
||||
<app-card-actionables (actionHandler)="performAction($event)" [actions]="actions" [attr.aria-labelledby]="readingList?.title" *ngIf="actions.length > 0"></app-card-actionables>
|
||||
<h4 title>
|
||||
{{readingList?.title}}
|
||||
@if (readingList?.promoted) {
|
||||
<span class="ms-1">(<i class="fa fa-angle-double-up" aria-hidden="true"></i>)</span>
|
||||
}
|
||||
</h2>
|
||||
<h6 subtitle class="subtitle-with-actionables">{{t('item-count', {num: items.length | number})}}</h6>
|
||||
<app-card-actionables (actionHandler)="performAction($event)" [actions]="actions" [attr.aria-labelledby]="readingList?.title" *ngIf="actions.length > 0"></app-card-actionables>
|
||||
</h4>
|
||||
<h5 subtitle class="subtitle-with-actionables">{{t('item-count', {num: items.length | number})}}</h5>
|
||||
|
||||
<ng-template #extrasDrawer let-offcanvas>
|
||||
@if (readingList) {
|
||||
<div style="margin-top: 56px">
|
||||
<div>
|
||||
<div class="offcanvas-header">
|
||||
<h4 class="offcanvas-title" id="offcanvas-basic-title">{{t('page-settings-title')}}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="offcanvas.dismiss()"></button>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<ng-container *transloco="let t; read: 'reading-lists'">
|
||||
<app-side-nav-companion-bar>
|
||||
<h2 title>
|
||||
<app-card-actionables [actions]="globalActions" (actionHandler)="performGlobalAction($event)"></app-card-actionables>
|
||||
<h4 title>
|
||||
<span>{{t('title')}}</span>
|
||||
</h2>
|
||||
<app-card-actionables [actions]="globalActions" (actionHandler)="performGlobalAction($event)"></app-card-actionables>
|
||||
</h4>
|
||||
@if (pagination) {
|
||||
<h6 subtitle class="subtitle-with-actionables">{{t('item-count', {num: pagination.totalItems | number})}}</h6>
|
||||
<h5 subtitle class="subtitle-with-actionables">{{t('item-count', {num: pagination.totalItems | number})}}</h5>
|
||||
}
|
||||
|
||||
</app-side-nav-companion-bar>
|
||||
|
|
|
|||
|
|
@ -12,21 +12,17 @@ import { ActionService } from 'src/app/_services/action.service';
|
|||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { JumpbarService } from 'src/app/_services/jumpbar.service';
|
||||
import { ReadingListService } from 'src/app/_services/reading-list.service';
|
||||
import { ImportCblModalComponent } from '../../_modals/import-cbl-modal/import-cbl-modal.component';
|
||||
import { CardItemComponent } from '../../../cards/card-item/card-item.component';
|
||||
import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/card-detail-layout.component';
|
||||
import { NgIf, DecimalPipe } from '@angular/common';
|
||||
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
|
||||
import {Title} from "@angular/platform-browser";
|
||||
import {WikiLink} from "../../../_models/wiki";
|
||||
import {BulkSelectionService} from "../../../cards/bulk-selection.service";
|
||||
import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component";
|
||||
import {KEY_CODES} from "../../../shared/_services/utility.service";
|
||||
import {UserCollection} from "../../../_models/collection-tag";
|
||||
import {User} from "../../../_models/user";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
@Component({
|
||||
selector: 'app-reading-lists',
|
||||
|
|
@ -50,8 +46,8 @@ export class ReadingListsComponent implements OnInit {
|
|||
hasPromote: boolean = false;
|
||||
jumpbarKeys: Array<JumpKey> = [];
|
||||
actions: {[key: number]: Array<ActionItem<ReadingList>>} = {};
|
||||
globalActions: Array<ActionItem<any>> = [{action: Action.Import, title: 'import-cbl', children: [], requiresAdmin: true, callback: this.importCbl.bind(this)}];
|
||||
trackByIdentity = (index: number, item: ReadingList) => `${item.id}_${item.title}`;
|
||||
globalActions: Array<ActionItem<any>> = [];
|
||||
trackByIdentity = (index: number, item: ReadingList) => `${item.id}_${item.title}_${item.promoted}`;
|
||||
|
||||
@HostListener('document:keydown.shift', ['$event'])
|
||||
handleKeypress(event: KeyboardEvent) {
|
||||
|
|
@ -88,6 +84,7 @@ export class ReadingListsComponent implements OnInit {
|
|||
getActions(readingList: ReadingList) {
|
||||
const d = this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this))
|
||||
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin || this.hasPromote));
|
||||
|
||||
return this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this))
|
||||
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin || this.hasPromote));
|
||||
}
|
||||
|
|
@ -104,12 +101,6 @@ export class ReadingListsComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
importCbl() {
|
||||
const ref = this.ngbModal.open(ImportCblModalComponent, {size: 'xl', fullscreen: 'md'});
|
||||
ref.closed.subscribe(result => this.loadPage());
|
||||
ref.dismissed.subscribe(_ => this.loadPage());
|
||||
}
|
||||
|
||||
handleReadingListActionCallback(action: ActionItem<ReadingList>, readingList: ReadingList) {
|
||||
switch(action.action) {
|
||||
case Action.Delete:
|
||||
|
|
@ -125,6 +116,22 @@ export class ReadingListsComponent implements OnInit {
|
|||
this.cdRef.markForCheck();
|
||||
});
|
||||
break;
|
||||
case Action.Promote:
|
||||
this.actionService.promoteMultipleReadingLists([readingList], true, (res) => {
|
||||
// Reload information around list
|
||||
readingList.promoted = true;
|
||||
this.loadPage();
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
break;
|
||||
case Action.UnPromote:
|
||||
this.actionService.promoteMultipleReadingLists([readingList], false, (res) => {
|
||||
// Reload information around list
|
||||
readingList.promoted = false;
|
||||
this.loadPage();
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
<ng-container *transloco="let t; read: 'import-cbl-modal'">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{t('title')}}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
|
||||
</div>
|
||||
<div class="modal-body scrollable-modal">
|
||||
<div class="row g-0" style="min-width: 135px;">
|
||||
<app-step-tracker [steps]="steps" [currentStep]="currentStepIndex"></app-step-tracker>
|
||||
</div>
|
||||
|
||||
<!-- This is going to need to have a fixed height with a scrollbar-->
|
||||
<div>
|
||||
<div class="row g-0" *ngIf="currentStepIndex === Step.Import">
|
||||
<p>{{t('import-description')}}</p>
|
||||
<form [formGroup]="uploadForm" enctype="multipart/form-data">
|
||||
<file-upload formControlName="files"></file-upload>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="currentStepIndex === Step.Validate">
|
||||
<p>{{t('validate-description')}}</p>
|
||||
<div class="row g-0">
|
||||
<div ngbAccordion #accordion="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
<div ngbAccordionItem *ngIf="fileToProcess.validateSummary as summary">
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="validationList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentStepIndex === Step.DryRun">
|
||||
<div class="row g-0">
|
||||
<p>{{t('dry-run-description')}}</p>
|
||||
|
||||
<div ngbAccordion #a="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
<div ngbAccordionItem *ngIf="fileToProcess.dryRunSummary as summary">
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentStepIndex === Step.Finalize">
|
||||
<div class="row g-0">
|
||||
<div ngbAccordion #a="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
<div ngbAccordionItem *ngIf="fileToProcess.finalizeSummary as summary">
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #validationList let-summary="summary">
|
||||
@if (summary.results.length > 0) {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-triangle-exclamation" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-warning')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="list-group list-group-numbered list-group-flush" >
|
||||
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
|
||||
[innerHTML]="result | cblConflictReason | safeHtml">
|
||||
</li>
|
||||
</ol>
|
||||
}
|
||||
@else {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-no-issue-description')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #resultsList let-summary="summary">
|
||||
<ul class="list-group list-group-flush">
|
||||
@for(result of summary.results; track result.order) {
|
||||
<li class="list-group-item no-hover"
|
||||
innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li>
|
||||
}
|
||||
</ul>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #heading let-filename="filename" let-summary="summary">
|
||||
@switch (summary.success) {
|
||||
@case (CblImportResult.Success) {
|
||||
<span class="badge heading-badge bg-primary me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Fail) {
|
||||
<span class="badge heading-badge bg-danger me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Partial) {
|
||||
<span class="badge heading-badge bg-warning me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
}
|
||||
<span>{{filename}}<span *ngIf="summary.cblName">: ({{summary.cblName}})</span></span>
|
||||
</ng-template>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<form [formGroup]="cblSettingsForm" class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="settings-comicvine-mode" role="switch" formControlName="comicVineMatching" class="form-check-input"
|
||||
aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="settings-comicvine-mode">{{t('comicvine-parsing-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Spacer -->
|
||||
<div class="col" aria-hidden="true"></div>
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-icon" [href]="WikiLink.ReadingListCBL" target="_blank" rel="noopener noreferrer">Help</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">{{t('close')}}</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" (click)="prevStep()" [disabled]="!canMoveToPrevStep()">{{t('prev')}}</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="!canMoveToNextStep()">{{t(NextButtonLabel)}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</ng-container>
|
||||
Loading…
Add table
Add a link
Reference in a new issue