Skip to content

Commit

Permalink
feat(admin-ui): Display background jobs in UI
Browse files Browse the repository at this point in the history
Relates to #111
  • Loading branch information
michaelbromley committed Jun 4, 2019
1 parent a83945a commit 59d8312
Show file tree
Hide file tree
Showing 14 changed files with 339 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { EMPTY, Observable } from 'rxjs';
import { delay, filter, map, switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';
import { delay, map, switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';

import { BaseListComponent } from '../../../common/base-list.component';
import { SearchInput, SearchProducts } from '../../../common/generated-types';
import { JobState, SearchInput, SearchProducts } from '../../../common/generated-types';
import { _ } from '../../../core/providers/i18n/mark-for-extraction';
import { JobQueueService } from '../../../core/providers/job-queue/job-queue.service';
import { NotificationService } from '../../../core/providers/notification/notification.service';
import { DataService } from '../../../data/providers/data.service';
import { ModalService } from '../../../shared/providers/modal/modal.service';
Expand All @@ -28,6 +29,7 @@ export class ProductListComponent
private dataService: DataService,
private modalService: ModalService,
private notificationService: NotificationService,
private jobQueueService: JobQueueService,
router: Router,
route: ActivatedRoute,
) {
Expand Down Expand Up @@ -91,16 +93,19 @@ export class ProductListComponent

rebuildSearchIndex() {
this.dataService.product.reindex().subscribe(({ reindex }) => {
if (reindex.success) {
const time = new Intl.NumberFormat().format(reindex.timeTaken);
this.notificationService.success(_('catalog.reindex-successful'), {
count: reindex.indexedItemCount,
time,
});
this.refresh();
} else {
this.notificationService.error(_('catalog.reindex-error'));
}
this.notificationService.info(_('catalog.reindexing'));
this.jobQueueService.addJob(reindex.id, job => {
if (job.state === JobState.COMPLETED) {
const time = new Intl.NumberFormat().format(job.duration || 0);
this.notificationService.success(_('catalog.reindex-successful'), {
count: job.result.indexedItemCount,
time,
});
this.refresh();
} else {
this.notificationService.error(_('catalog.reindex-error'));
}
});
});
}

Expand Down
91 changes: 79 additions & 12 deletions admin-ui/src/app/common/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,29 @@ export type ImportInfo = {
imported: Scalars['Int'],
};

export type JobInfo = {
id: Scalars['String'],
name: Scalars['String'],
state: JobState,
progress: Scalars['Float'],
result?: Maybe<Scalars['JSON']>,
started?: Maybe<Scalars['DateTime']>,
ended?: Maybe<Scalars['DateTime']>,
duration?: Maybe<Scalars['Int']>,
};

export type JobListInput = {
state?: Maybe<JobState>,
ids?: Maybe<Array<Scalars['String']>>,
};

export enum JobState {
PENDING = 'PENDING',
RUNNING = 'RUNNING',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED'
}


/** ISO 639-1 language code */
export enum LanguageCode {
Expand Down Expand Up @@ -1472,7 +1495,7 @@ export type Mutation = {
createProductOptionGroup: ProductOptionGroup,
/** Update an existing ProductOptionGroup */
updateProductOptionGroup: ProductOptionGroup,
reindex: SearchReindexResponse,
reindex: JobInfo,
/** Create a new Product */
createProduct: Product,
/** Update an existing Product */
Expand Down Expand Up @@ -2316,6 +2339,8 @@ export type Query = {
globalSettings: GlobalSettings,
order?: Maybe<Order>,
orders: OrderList,
job?: Maybe<JobInfo>,
jobs: Array<JobInfo>,
paymentMethods: PaymentMethodList,
paymentMethod?: Maybe<PaymentMethod>,
productOptionGroups: Array<ProductOptionGroup>,
Expand Down Expand Up @@ -2430,6 +2455,16 @@ export type QueryOrdersArgs = {
};


export type QueryJobArgs = {
jobId: Scalars['String']
};


export type QueryJobsArgs = {
input?: Maybe<JobListInput>
};


export type QueryPaymentMethodsArgs = {
options?: Maybe<PaymentMethodListOptions>
};
Expand Down Expand Up @@ -3412,11 +3447,6 @@ export type SearchProductsQueryVariables = {

export type SearchProductsQuery = ({ __typename?: 'Query' } & { search: ({ __typename?: 'SearchResponse' } & Pick<SearchResponse, 'totalItems'> & { items: Array<({ __typename?: 'SearchResult' } & Pick<SearchResult, 'enabled' | 'productId' | 'productName' | 'productPreview' | 'productVariantId' | 'productVariantName' | 'productVariantPreview' | 'sku'>)>, facetValues: Array<({ __typename?: 'FacetValueResult' } & Pick<FacetValueResult, 'count'> & { facetValue: ({ __typename?: 'FacetValue' } & Pick<FacetValue, 'id' | 'name'> & { facet: ({ __typename?: 'Facet' } & Pick<Facet, 'id' | 'name'>) }) })> }) });

export type ReindexMutationVariables = {};


export type ReindexMutation = ({ __typename?: 'Mutation' } & { reindex: ({ __typename?: 'SearchReindexResponse' } & Pick<SearchReindexResponse, 'indexedItemCount' | 'success' | 'timeTaken'>) });

export type ConfigurableOperationFragment = ({ __typename?: 'ConfigurableOperation' } & Pick<ConfigurableOperation, 'code' | 'description'> & { args: Array<({ __typename?: 'ConfigArg' } & Pick<ConfigArg, 'name' | 'type' | 'value'>)> });

export type PromotionFragment = ({ __typename?: 'Promotion' } & Pick<Promotion, 'id' | 'createdAt' | 'updatedAt' | 'name' | 'enabled'> & { conditions: Array<({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment)>, actions: Array<({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment)> });
Expand Down Expand Up @@ -3673,6 +3703,27 @@ export type GetServerConfigQueryVariables = {};

export type GetServerConfigQuery = ({ __typename?: 'Query' } & { globalSettings: ({ __typename?: 'GlobalSettings' } & { serverConfig: ({ __typename?: 'ServerConfig' } & Pick<ServerConfig, 'customFields'>) }) });

export type JobInfoFragment = ({ __typename?: 'JobInfo' } & Pick<JobInfo, 'id' | 'name' | 'state' | 'progress' | 'duration' | 'result'>);

export type GetJobInfoQueryVariables = {
id: Scalars['String']
};


export type GetJobInfoQuery = ({ __typename?: 'Query' } & { job: Maybe<({ __typename?: 'JobInfo' } & JobInfoFragment)> });

export type GetAllJobsQueryVariables = {
input?: Maybe<JobListInput>
};


export type GetAllJobsQuery = ({ __typename?: 'Query' } & { jobs: Array<({ __typename?: 'JobInfo' } & JobInfoFragment)> });

export type ReindexMutationVariables = {};


export type ReindexMutation = ({ __typename?: 'Mutation' } & { reindex: ({ __typename?: 'JobInfo' } & JobInfoFragment) });

export type ShippingMethodFragment = ({ __typename?: 'ShippingMethod' } & Pick<ShippingMethod, 'id' | 'createdAt' | 'updatedAt' | 'code' | 'description'> & { checker: ({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment), calculator: ({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment) });

export type GetShippingMethodListQueryVariables = {
Expand Down Expand Up @@ -4179,12 +4230,6 @@ export namespace SearchProducts {
export type Facet = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>)['facetValue']['facet'];
}

export namespace Reindex {
export type Variables = ReindexMutationVariables;
export type Mutation = ReindexMutation;
export type Reindex = ReindexMutation['reindex'];
}

export namespace ConfigurableOperation {
export type Fragment = ConfigurableOperationFragment;
export type Args = (NonNullable<ConfigurableOperationFragment['args'][0]>);
Expand Down Expand Up @@ -4457,6 +4502,28 @@ export namespace GetServerConfig {
export type ServerConfig = GetServerConfigQuery['globalSettings']['serverConfig'];
}

export namespace JobInfo {
export type Fragment = JobInfoFragment;
}

export namespace GetJobInfo {
export type Variables = GetJobInfoQueryVariables;
export type Query = GetJobInfoQuery;
export type Job = JobInfoFragment;
}

export namespace GetAllJobs {
export type Variables = GetAllJobsQueryVariables;
export type Query = GetAllJobsQuery;
export type Jobs = JobInfoFragment;
}

export namespace Reindex {
export type Variables = ReindexMutationVariables;
export type Mutation = ReindexMutation;
export type Reindex = JobInfoFragment;
}

export namespace ShippingMethod {
export type Fragment = ShippingMethodFragment;
export type Checker = ConfigurableOperationFragment;
Expand Down
20 changes: 20 additions & 0 deletions admin-ui/src/app/core/components/job-list/job-list.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<vdr-dropdown *ngIf="(activeJobs$ | async) as jobs">
<button
type="button"
class="btn btn-link btn-sm job-button"
[class.hidden]="jobs.length === 0"
vdrDropdownTrigger
>
<clr-icon shape="hourglass" class="has-badge"></clr-icon>
{{ 'common.jobs-in-progress' | translate: { count: jobs.length } }}
</button>
<vdr-dropdown-menu vdrPosition="top-right">
<div *ngFor="let job of (activeJobs$ | async); trackBy: trackById" class="job-row">
{{ getJobName(job) | translate }}
<div class="progress labeled">
<progress max="100" [value]="job.progress" data-displayval="0%"></progress>
<span>{{ job.progress }}%</span>
</div>
</div>
</vdr-dropdown-menu>
</vdr-dropdown>
25 changes: 25 additions & 0 deletions admin-ui/src/app/core/components/job-list/job-list.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@import "variables";

:host {
position: fixed;
bottom: 12px;
left: 12px;
z-index: 5;
}

.job-button {
background-color: $color-grey-200;
box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.2);
&.hidden {
display: none;
}
}

.job-row {
padding: 0 60px 0 12px;
width: 90vw;
max-width: 360px;
@media screen and (min-width: $breakpoint-small){
max-width: 400px;
}
}
35 changes: 35 additions & 0 deletions admin-ui/src/app/core/components/job-list/job-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { JobInfoFragment } from '../../../common/generated-types';
import { _ } from '../../providers/i18n/mark-for-extraction';
import { JobQueueService } from '../../providers/job-queue/job-queue.service';

@Component({
selector: 'vdr-job-list',
templateUrl: './job-list.component.html',
styleUrls: ['./job-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class JobListComponent implements OnInit {
activeJobs$: Observable<JobInfoFragment[]>;

constructor(private jobQueueService: JobQueueService) {}

ngOnInit() {
this.activeJobs$ = this.jobQueueService.activeJobs$;
}

getJobName(job: JobInfoFragment): string {
switch (job.name) {
case 'reindex':
return _('job.reindex');
default:
return job.name;
}
}

trackById(index: number, item: JobInfoFragment) {
return item.id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
</a>
</li>
<li>
í
<a
class="nav-link"
[routerLink]="['/settings', 'global-settings']"
Expand All @@ -151,5 +152,8 @@
</li>
</ul>
</section>
<section class="nav-group">
<vdr-job-list></vdr-job-list>
</section>
</section>
</nav>
4 changes: 4 additions & 0 deletions admin-ui/src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module';

import { AppShellComponent } from './components/app-shell/app-shell.component';
import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
import { JobListComponent } from './components/job-list/job-list.component';
import { MainNavComponent } from './components/main-nav/main-nav.component';
import { NotificationComponent } from './components/notification/notification.component';
import { OverlayHostComponent } from './components/overlay-host/overlay-host.component';
Expand All @@ -14,6 +15,7 @@ import { UserMenuComponent } from './components/user-menu/user-menu.component';
import { AuthService } from './providers/auth/auth.service';
import { AuthGuard } from './providers/guard/auth.guard';
import { I18nService } from './providers/i18n/i18n.service';
import { JobQueueService } from './providers/job-queue/job-queue.service';
import { LocalStorageService } from './providers/local-storage/local-storage.service';
import { NotificationService } from './providers/notification/notification.service';
import { OverlayHostService } from './providers/overlay-host/overlay-host.service';
Expand All @@ -28,6 +30,7 @@ import { OverlayHostService } from './providers/overlay-host/overlay-host.servic
I18nService,
OverlayHostService,
NotificationService,
JobQueueService,
],
declarations: [
AppShellComponent,
Expand All @@ -37,6 +40,7 @@ import { OverlayHostService } from './providers/overlay-host/overlay-host.servic
OverlayHostComponent,
NotificationComponent,
UiLanguageSwitcherComponent,
JobListComponent,
],
entryComponents: [NotificationComponent],
})
Expand Down
Loading

0 comments on commit 59d8312

Please sign in to comment.