diff --git a/projects/admin/src/app/app-routing.module.ts b/projects/admin/src/app/app-routing.module.ts index 41657ed72..83abb182f 100644 --- a/projects/admin/src/app/app-routing.module.ts +++ b/projects/admin/src/app/app-routing.module.ts @@ -24,6 +24,10 @@ import { PERMISSIONS } from '@rero/shared'; import { PermissionGuard } from './guard/permission.guard'; const routes: Routes = [ + { + path: 'migrations', + loadChildren: () => import('./migration/migration.module').then(m => m.MigrationModule) + }, { path: '', component: FrontpageComponent diff --git a/projects/admin/src/app/menu/menu-definition/menu-app.ts b/projects/admin/src/app/menu/menu-definition/menu-app.ts index ddab837ee..4f717c19c 100644 --- a/projects/admin/src/app/menu/menu-definition/menu-app.ts +++ b/projects/admin/src/app/menu/menu-definition/menu-app.ts @@ -422,6 +422,15 @@ export const MENU_APP: IMenuParent[] = [ permissions: [PERMISSIONS.PERM_MANAGEMENT] } }, + { + name: 'Migrations', + router_link: ['/', 'migrations', 'records', 'migrations'], + attributes: { id: 'permissions-menu' }, + extras: { iconClass: 'fa fa-cloud-upload' }, + access: { + permissions: [PERMISSIONS.MIG_ACCESS] + } + }, ] } ] diff --git a/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.html b/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.html new file mode 100644 index 000000000..a65dcc2f9 --- /dev/null +++ b/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.html @@ -0,0 +1,29 @@ + + +@if (record) { +
+ {{ record.id }} +
+ @if (record?.metadata?.json?.title[0].mainTitle[0].value) { + {{ record?.metadata?.json?.title[0].mainTitle[0].value }} + } +} diff --git a/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.ts b/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.ts new file mode 100644 index 000000000..41bc04ce3 --- /dev/null +++ b/projects/admin/src/app/migration/brief-view/migration-data/migration-data.component.ts @@ -0,0 +1,35 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { Component, Input } from '@angular/core'; +import { ResultItem } from '@rero/ng-core'; + +@Component({ + selector: 'admin-migration-data', + templateUrl: './migration-data.component.html' +}) +export class MigrationDataComponent implements ResultItem { + + @Input() record: any; + + /** Type of record */ + @Input() type: string; + + /** Detail Url */ + @Input() detailUrl: { link: string, external: boolean }; + +} diff --git a/projects/admin/src/app/migration/brief-view/migration/migration.component.html b/projects/admin/src/app/migration/brief-view/migration/migration.component.html new file mode 100644 index 000000000..bbac3bd58 --- /dev/null +++ b/projects/admin/src/app/migration/brief-view/migration/migration.component.html @@ -0,0 +1,34 @@ + + +@if (record) { + Check Conversion +
+ {{ record.metadata.name }} +
+ @if(record.metadata?.description) { +

+ {{ record.metadata.description }} +

+ } +} diff --git a/projects/admin/src/app/migration/brief-view/migration/migration.component.ts b/projects/admin/src/app/migration/brief-view/migration/migration.component.ts new file mode 100644 index 000000000..3921cc1b2 --- /dev/null +++ b/projects/admin/src/app/migration/brief-view/migration/migration.component.ts @@ -0,0 +1,34 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { Component, Input } from '@angular/core'; +import { ResultItem } from '@rero/ng-core'; + +@Component({ + selector: 'admin-migration', + templateUrl: './migration.component.html' +}) +export class MigrationComponent implements ResultItem { + + @Input() record: any; + + /** Type of record */ + @Input() type: string; + + /** Detail Url */ + @Input() detailUrl: { link: string, external: boolean }; +} diff --git a/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.html b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.html new file mode 100644 index 000000000..9e9aa576c --- /dev/null +++ b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.html @@ -0,0 +1,31 @@ + +@if (record) { +
+ {{record.id}} +
+ @if (messages) { + + } +
+
+
+
+ } diff --git a/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.scss b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.scss new file mode 100644 index 000000000..2216d07eb --- /dev/null +++ b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.scss @@ -0,0 +1,32 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +::ng-deep { + .markdown td, + .markdown th { + padding: 0 10px; + } +} +.data { + .col { + max-height: 600px; + overflow: auto; + } + .json { + border-left: 1px solid gray; + } +} diff --git a/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.ts b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.ts new file mode 100644 index 000000000..906143f02 --- /dev/null +++ b/projects/admin/src/app/migration/detailed-view/migration-data/migration-data.component.ts @@ -0,0 +1,69 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { HttpClient } from '@angular/common/http'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { of, Subscription, switchMap, tap } from 'rxjs'; + +@Component({ + selector: 'admin-migration-data', + templateUrl: './migration-data.component.html', + styleUrl: './migration-data.component.scss', +}) +export class MigrationDataComponent implements OnInit, OnDestroy { + /** Observable resolving record data */ + record: any; + protected route = inject(ActivatedRoute); + protected http = inject(HttpClient); + private subscriptions = new Subscription(); + messages = []; + ngOnInit(): void { + this.subscriptions.add( + this.route.paramMap + .pipe( + switchMap(() => { + const docType = this.route.snapshot.params.type; + const id = this.route.snapshot.params.pid; + const migrationId = this.route.snapshot.queryParams.migration; + if (docType == null || id == null || migrationId == null) { + return of(null); + } + return this.http.get(`/api/${docType}/${id}?migration=${migrationId}`); + }), + tap((record: any) => { + if (record?.conversion_logs) { + ['info', 'warning', 'error'].map((field) => { + const log = record.conversion_logs[field]; + if (log) { + this.messages.push({ + severity: field == 'warning' ? 'warn' : field, + detail: log, + }); + } + }); + } + }) + ) + .subscribe((res) => (this.record = res)) + ); + } + /** OnDestroy hook */ + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } +} diff --git a/projects/admin/src/app/migration/migration-routing.module.ts b/projects/admin/src/app/migration/migration-routing.module.ts new file mode 100644 index 000000000..01cb0fe4a --- /dev/null +++ b/projects/admin/src/app/migration/migration-routing.module.ts @@ -0,0 +1,103 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { RouterModule, Routes, UrlSegment } from '@angular/router'; +import { RecordSearchPageComponent } from '@rero/ng-core'; +import { of } from 'rxjs'; +import { MigrationDataComponent } from './brief-view/migration-data/migration-data.component'; +import { MigrationComponent } from './brief-view/migration/migration.component'; + +import { MigrationDataComponent as MigrationDataDetailedComponent } from './detailed-view/migration-data/migration-data.component'; +const routes: Routes = [ + { + matcher: (url) => { + if (url.length == 2 && url[0].path === 'records' && url[1].path === 'migrations') { + return { + consumed: url, + posParams: { type: new UrlSegment(url[1].path, {}) }, + }; + } + return null; + }, + data: { + types: [ + { + key: 'migrations', + label: 'Migrations', + component: MigrationComponent, + canDelete: (record: any) => of(false), + canAdd: (record: any) => of(false), + canUpdate: (record: any) => of(false), + aggregationsExpand: ['status'], + aggregationsOrder: ['status'], + aggregationsBucketSize: 10, + }, + ], + }, + children: [ + { + path: '', + component: RecordSearchPageComponent, + }, + ], + }, + { + matcher: (url) => { + if (url[0].path === 'records' && url[1].path === 'migration_data') { + const segments = [new UrlSegment(url[0].path, {}), new UrlSegment(url[1].path, {})]; + return { + consumed: segments, + posParams: { type: new UrlSegment(url[1].path, {}) }, + }; + } + return null; + }, + data: { + types: [ + { + key: 'migration_data', + label: 'Migrations Data', + component: MigrationDataComponent, + canDelete: (record: any) => of(false), + canAdd: (record: any) => of(false), + canUpdate: (record: any) => of(false), + aggregationsExpand: ['migration', 'conversion_status'], + aggregationsOrder: ['migration', 'conversion_status'], + aggregationsBucketSize: 10, + }, + ], + // detailUrl: 'migrations/records/migration_data/detail/:pid', + }, + children: [ + { + path: '', + component: RecordSearchPageComponent, + }, + { + path: 'detail/:pid', + component: MigrationDataDetailedComponent, + }, + ], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class MigrationRoutingModule {} diff --git a/projects/admin/src/app/migration/migration.module.ts b/projects/admin/src/app/migration/migration.module.ts new file mode 100644 index 000000000..a2d3922b2 --- /dev/null +++ b/projects/admin/src/app/migration/migration.module.ts @@ -0,0 +1,49 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { RecordModule } from '@rero/ng-core'; +import { SharedModule } from '@rero/shared'; +import { BadgeModule } from 'primeng/badge'; +import { ButtonModule } from 'primeng/button'; +import { CardModule } from 'primeng/card'; +import { DividerModule } from 'primeng/divider'; +import { MessagesModule } from 'primeng/messages'; +import { MigrationDataComponent } from './brief-view/migration-data/migration-data.component'; +import { MigrationComponent } from './brief-view/migration/migration.component'; +import { MigrationDataComponent as MigrationDataDetailedComponent } from './detailed-view/migration-data/migration-data.component'; +import { MigrationRoutingModule } from './migration-routing.module'; +import { HighlightJsonPipe } from './pipes/highlight-json.pipe'; + + +@NgModule({ + declarations: [MigrationComponent, MigrationDataComponent, MigrationDataDetailedComponent, HighlightJsonPipe], + imports: [ + CommonModule, + MigrationRoutingModule, + BadgeModule, + ButtonModule, + SharedModule, + RecordModule, + DividerModule, + CardModule, + MessagesModule + ], +}) +export class MigrationModule {} diff --git a/projects/admin/src/app/migration/pipes/highlight-json.pipe.ts b/projects/admin/src/app/migration/pipes/highlight-json.pipe.ts new file mode 100644 index 000000000..dae27d82d --- /dev/null +++ b/projects/admin/src/app/migration/pipes/highlight-json.pipe.ts @@ -0,0 +1,73 @@ +/* + * RERO ILS UI + * Copyright (C) 2024 RERO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +/** + * Highlight a JSON structure. + */ +@Pipe({ + name: 'highlightJson' +}) +export class HighlightJsonPipe implements PipeTransform { + /** + * Constructor. + * + * @param sanitizer DOM Sanitizer. + */ + constructor(public sanitizer: DomSanitizer) {} + + /** + * Highlight a JSON structure. + * + * @param value Json structure. + * @return Highlighted string. + */ + transform(value: string): any { + console.log('json', value); + let json = value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/(?:\r\n|\r|\n)/g, '
') + .replace(/( )/g, ' '); + + json = json.replace( + /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, + (match: any) => { + let cls = 'text-gray-600'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'text-cyan-600'; + } else { + cls = 'text-orange-600'; + } + } else if (/true|false/.test(match)) { + cls = 'text-blue-600'; + } else if (/null/.test(match)) { + cls = 'text-blue-600'; + } + return `${match}`; + } + ); + const html = this.sanitizer.bypassSecurityTrustHtml(json); + console.log(html) + + return html; + } +} diff --git a/projects/shared/src/lib/util/permissions.ts b/projects/shared/src/lib/util/permissions.ts index ab6274d28..95dd2053a 100644 --- a/projects/shared/src/lib/util/permissions.ts +++ b/projects/shared/src/lib/util/permissions.ts @@ -129,6 +129,9 @@ export const PERMISSIONS = { 'VNDR_ACCESS': 'vndr-access', 'VNDR_CREATE': 'vndr-create', 'VNDR_SEARCH': 'vndr-search', + // Migrations + 'MIG_ACCESS': 'mig-access', + 'MIG_SEARCH': 'mig-search' }; // User with permission service