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) {
+
+ @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