Skip to content

Commit

Permalink
feat(admin): add section for managing build jobs (PPT-1758)
Browse files Browse the repository at this point in the history
  • Loading branch information
MrYuion committed Dec 31, 2024
1 parent 1ac396e commit 6823628
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 6 deletions.
5 changes: 5 additions & 0 deletions apps/backoffice/src/app/admin/admin.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ export class PlaceComponent extends AsyncHandler {
name: i18n('ADMIN.TAB_UPLOADS_LIBRARY'),
icon: { value: 'photo_album' },
},
{
id: 'build-jobs',
name: i18n('ADMIN.TAB_BUILD_JOBS'),
icon: { value: 'laps' },
},
// {
// id: 'mailing-list',
// name: 'Email Templates',
Expand Down
2 changes: 2 additions & 0 deletions apps/backoffice/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ResourceImportsComponent } from './resource-imports.component';
import { EmailTemplatesComponent } from './mailing-lists/email-templates.component';
import { EmailTemplateFormComponent } from './mailing-lists/email-template-form.component';
import { SharedContentModule } from '../ui/ui.module';
import { PlaceBuildListComponent } from './build-list.component';

@NgModule({
declarations: [
Expand Down Expand Up @@ -60,6 +61,7 @@ import { SharedContentModule } from '../ui/ui.module';
ResourceImportsComponent,
EmailTemplatesComponent,
EmailTemplateFormComponent,
PlaceBuildListComponent,
],
imports: [
CommonModule,
Expand Down
2 changes: 2 additions & 0 deletions apps/backoffice/src/app/admin/admin.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { UploadLibraryComponent } from './upload-library.component';
import { ResourceImportsComponent } from './resource-imports.component';
import { EmailTemplatesComponent } from './mailing-lists/email-templates.component';
import { EmailTemplateFormComponent } from './mailing-lists/email-template-form.component';
import { PlaceBuildListComponent } from './build-list.component';

export const ROUTES: Routes = [
{
Expand Down Expand Up @@ -49,6 +50,7 @@ export const ROUTES: Routes = [
{ path: '**', redirectTo: '' },
],
},
{ path: 'build-jobs', component: PlaceBuildListComponent },
{ path: 'extend/:id', component: ExtensionOutletComponent },
{ path: '**', redirectTo: 'about' },
],
Expand Down
198 changes: 198 additions & 0 deletions apps/backoffice/src/app/admin/build-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { del, get, PlaceEdge } from '@placeos/ts-client';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import {
catchError,
debounceTime,
map,
shareReplay,
startWith,
switchMap,
} from 'rxjs/operators';
import { openConfirmModal } from '../common/general';
import { notifyError, notifySuccess } from '../common/notifications';
import { toQueryString } from '../common/api';
import { i18n } from '../common/translate';

interface BuildJob {
state: 'pending' | 'running' | 'cancelled' | 'error' | 'done';
id: string;
message: string;
driver: string;
repo: string;
branch: string;
commit: string;
timestamp: string;
}

function queryBuildJobs(q: any = {}): Observable<BuildJob> {
const query = toQueryString(q);
return get(`/api/build/v1/monitor${query ? '?' + query : ''}`) as any;
}

function cancelBuildJob(id, q = {}) {
const query = toQueryString(q);
return del(`/api/build/v1/cancel/${id}${query ? '?' + query : ''}`);
}

@Component({
selector: '[admin-build-list]',
template: `
<div class="flex flex-col h-full w-full">
<div class="flex items-center justify-between space-x-2 my-4">
<div class="text-2xl">
{{ 'ADMIN.BUILD_LIST_HEADER' | translate }}
</div>
</div>
<div class="flex-1 w-full h-1/2 overflow-auto">
<mat-progress-bar
mode="indeterminate"
class="w-full"
[class.opacity-0]="!loading"
></mat-progress-bar>
<simple-table
class="min-w-[64rem] block text-sm"
[data]="jobs"
[columns]="[
{
key: 'repo',
name: 'REPOS.SINGULAR' | translate,
content: repo_template,
},
{
key: 'driver',
name: 'DRIVERS.SINGULAR' | translate,
content: driver_template,
},
{
key: 'message',
name: 'COMMON.FIELD_DESCRIPTION' | translate,
size: '32rem',
content: description_template
},
{
key: 'actions',
name: ' ',
content: actions_template,
size: '3.5rem',
sortable: false
}
]"
[sortable]="true"
[empty_message]="'ADMIN.BUILD_LIST_EMPTY' | translate"
></simple-table>
</div>
</div>
<ng-template #repo_template let-row="row">
<div class="flex flex-col px-4 py-2">
<div>{{ row.repo }}</div>
<div class="text-xs opacity-30">{{ row.branch }}</div>
</div>
</ng-template>
<ng-template #driver_template let-row="row">
<div class="flex flex-col px-4 py-2">
<div>{{ row.driver }}</div>
<div class="text-xs opacity-30">{{ row.commit }}</div>
</div>
</ng-template>
<ng-template #description_template let-data="data">
<div class="px-4 py-2 select-text overflow-hidden w-full text-xs">
{{ data }}
<span class="opacity-30" *ngIf="!data">
{{ 'COMMON.DESCRIPTION_EMPTY' | translate }}
</span>
</div>
</ng-template>
<ng-template #actions_template let-row="row">
<div class="flex items-center space-x-2 p-2">
<button
icon
matRipple
class="text-error"
[matTooltip]="'ADMIN.BUILD_LIST_REMOVE' | translate"
(click)="remove(row)"
>
<app-icon>delete</app-icon>
</button>
</div>
</ng-template>
`,
styles: [
`
:host {
height: 100%;
width: 100%;
min-height: 10rem;
}
`,
],
})
export class PlaceBuildListComponent {
public loading = '';

private _change = new BehaviorSubject<number>(0);
private _hide = new BehaviorSubject<string>('');
public last_change = new BehaviorSubject<PlaceEdge>(null);

public get item() {
return this.last_change.getValue();
}

private _job_list: Observable<PlaceEdge[]> = this._change.pipe(
debounceTime(300),
switchMap((_) => {
this.loading = 'Loading Edges...';
return queryBuildJobs();
}),
catchError((_) => of({})),
map((details?: { data: PlaceEdge[] }) => {
this.loading = '';
return (details?.data || []).sort((a, b) =>
a.id?.localeCompare(b.id)
);
}),
startWith([]),
shareReplay(1)
);

public readonly jobs = combineLatest([this._job_list, this._hide]).pipe(
debounceTime(500),
map(([list, hide]) => {
if (!hide) return list;
const edges = list.filter((_) => _.id !== hide);
return edges.sort((a, b) => a.id?.localeCompare(b.id));
})
);

public readonly remove = async (i: BuildJob) => {
const details = await openConfirmModal(
{
title: i18n('ADMIN.BUILD_LIST_REMOVE'),
content: i18n('ADMIN.BUILD_LIST_REMOVE_MSG', {
driver: i.driver,
repo: i.repo,
}),
icon: { type: 'icon', content: 'delete' },
},
this._dialog
);
if (!details) return;
details.loading(i18n('ADMIN.BUILD_LIST_REMOVE_LOADING'));
const err = await cancelBuildJob(i.id)
.toPromise()
.catch((_) => _);
details.close();
if (err)
return notifyError(
i18n('ADMIN.BUILD_LIST_REMOVE_ERROR', {
error: err.statusText || err.message || err,
})
);
this.last_change.next(null);
notifySuccess(i18n('ADMIN.BUILD_LIST_REMOVE_SUCCESS'));
this._hide.next(i.id);
};

constructor(private _dialog: MatDialog) {}
}
19 changes: 18 additions & 1 deletion apps/backoffice/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ import { AuthorisedAdminGuard } from './ui/guards/authorised-admin.guard';
import './mocks';
import { LocaleService } from './common/locale.service';

import localeFr from '@angular/common/locales/fr';
import localeJa from '@angular/common/locales/ja';
import localeAr from '@angular/common/locales/ar';
import localeZh from '@angular/common/locales/zh';
import localeEs from '@angular/common/locales/es';
import localeIt from '@angular/common/locales/it';
import { registerLocaleData } from '@angular/common';

@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
Expand Down Expand Up @@ -49,4 +57,13 @@ import { LocaleService } from './common/locale.service';
},
],
})
export class AppModule {}
export class AppModule {
constructor() {
registerLocaleData(localeFr);
registerLocaleData(localeAr);
registerLocaleData(localeJa);
registerLocaleData(localeZh);
registerLocaleData(localeEs);
registerLocaleData(localeIt);
}
}
11 changes: 10 additions & 1 deletion apps/backoffice/src/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@
"TAB_CUSTOM_SCHEMAS": "Custom Schemas",
"TAB_UPLOAD_STORAGE": "Data Stores",
"TAB_UPLOADS_LIBRARY": "Uploads Library",
"TAB_BUILD_JOBS": "Build Jobs",
"APPLICATION_DETAILS": "Application Details",
"VIEW_CHANGELOG": "View Changelog",
"BUILD": "Build",
Expand Down Expand Up @@ -964,6 +965,14 @@
"UPLOADS_LIB_REMOVE_LOADING": "Removing upload...",
"UPLOADS_LIB_FIELD_TYPE": "File Type",
"UPLOADS_LIB_FIELD_SIZE": "Size",
"UPLOADS_LIB_LIST_EMPTY": "No uploads for the selected domain"
"UPLOADS_LIB_LIST_EMPTY": "No uploads for the selected domain",

"BUILD_LIST_HEADER": "Build Service Jobs",
"BUILD_LIST_EMPTY": "No build jobs available to monitor",
"BUILD_LIST_REMOVE": "Cancel Build Job",
"BUILD_LIST_REMOVE_MSG": "Are you sure you wish to cancel the build job for {{ driver }} from {{ repo }}?",
"BUILD_LIST_REMOVE_LOADING": "Cancelling build job...",
"BUILD_LIST_REMOVE_ERROR": "Failed to cancel build job. Error: {{ error }}",
"BUILD_LIST_REMOVE_SUCCESS": "Successfully cancelled build job."
}
}
9 changes: 6 additions & 3 deletions apps/backoffice/src/assets/locale/jp.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@
"TRIGGER_ACTIVE": "トリガー有効",
"TRIGGER_ENABLED": "有効",
"TRIGGER_EXECUTE_ENABLED": "実行有効",
"TRIGGER_IMPORTANT": "重要"
"TRIGGER_IMPORTANT": "重要",
"MISCONFIGURED": "ルームシステムに必要なすべてのゾーンが含まれていません"
},
"MODULES": {
"SINGULAR": "モジュール",
Expand Down Expand Up @@ -325,7 +326,8 @@
"CUSTOM_NAME": "カスタム名",
"STATE": "モジュール状態",
"STATE_UPDATE": "状態を更新",
"STATE_LOADING": "モジュール状態を読み込み中..."
"STATE_LOADING": "モジュール状態を読み込み中...",
"ERROR": "モジュールでランタイムエラーが発生しました"
},
"ZONES": {
"SINGULAR": "ゾーン",
Expand Down Expand Up @@ -373,7 +375,8 @@
"TRIGGERS_EMPTY": "選択されたゾーンにはトリガーがありません",
"PARENT_ZONE": "親ゾーン",
"NAME_REQUIRED": "一意のゾーン名が必要です",
"DISPLAY_NAME": "表示名"
"DISPLAY_NAME": "表示名",
"MISCONFIGURED": "ゾーン内のタグには親ゾーンが必要です"
},
"DRIVERS": {
"SINGULAR": "ドライバー",
Expand Down
2 changes: 1 addition & 1 deletion config/proxy.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const context = [
'/backoffice',
'/stylesheets',
];
const ws_context = ['/api'];
const ws_context = ['/api', '/control'];

function add(endpoint, extras = {}) {
PROXY_CONFIG[`${endpoint}/**`] = {
Expand Down

0 comments on commit 6823628

Please sign in to comment.