mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
59334: edit metadata finetuning
This commit is contained in:
@@ -7,13 +7,16 @@
|
||||
<li *ngFor="let page of pages" class="nav-item">
|
||||
<a class="nav-link"
|
||||
[ngClass]="{'active' : page === currentPage}"
|
||||
[routerLink]="'/items/' + (itemRD$ | async)?.payload.uuid + '/edit/' + page">
|
||||
[routerLink]="['./' + page]">
|
||||
{{'item.edit.tabs.' + page + '.head' | translate}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-pane active">
|
||||
<router-outlet></router-outlet>
|
||||
<div class="mb-4">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<a [routerLink]="getItemPage((itemRD$ | async)?.payload)" class="btn btn-outline-secondary">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,5 @@
|
||||
@import '../../../styles/variables.scss';
|
||||
|
||||
.btn {
|
||||
min-width: $edit-item-button-min-width;
|
||||
}
|
@@ -6,6 +6,7 @@ import { Item } from '../../core/shared/item.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { getItemPageRoute } from '../item-page-routing.module';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-edit-item-page',
|
||||
@@ -48,4 +49,8 @@ export class EditItemPageComponent implements OnInit {
|
||||
.filter((path: string) => isNotEmpty(path)); // ignore reroutes
|
||||
this.itemRD$ = this.route.data.pipe(map((data) => data.item));
|
||||
}
|
||||
|
||||
getItemPage(item: Item): string {
|
||||
return getItemPageRoute(item.id)
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import { ItemPublicComponent } from './item-public/item-public.component';
|
||||
import { ItemDeleteComponent } from './item-delete/item-delete.component';
|
||||
import { ItemMetadataComponent } from './item-metadata/item-metadata.component';
|
||||
import { EditInPlaceFieldComponent } from './item-metadata/edit-in-place-field/edit-in-place-field.component';
|
||||
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
|
||||
|
||||
/**
|
||||
* Module that contains all components related to the Edit Item page administrator functionality
|
||||
@@ -36,6 +37,7 @@ import { EditInPlaceFieldComponent } from './item-metadata/edit-in-place-field/e
|
||||
ItemDeleteComponent,
|
||||
ItemStatusComponent,
|
||||
ItemMetadataComponent,
|
||||
ItemBitstreamsComponent,
|
||||
EditInPlaceFieldComponent
|
||||
]
|
||||
})
|
||||
|
@@ -9,6 +9,7 @@ import { ItemPublicComponent } from './item-public/item-public.component';
|
||||
import { ItemDeleteComponent } from './item-delete/item-delete.component';
|
||||
import { ItemStatusComponent } from './item-status/item-status.component';
|
||||
import { ItemMetadataComponent } from './item-metadata/item-metadata.component';
|
||||
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
|
||||
|
||||
const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
|
||||
const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
|
||||
@@ -39,8 +40,7 @@ const ITEM_EDIT_DELETE_PATH = 'delete';
|
||||
},
|
||||
{
|
||||
path: 'bitstreams',
|
||||
/* TODO - change when bitstreams page exists */
|
||||
component: ItemStatusComponent
|
||||
component: ItemBitstreamsComponent
|
||||
},
|
||||
{
|
||||
path: 'metadata',
|
||||
@@ -49,12 +49,12 @@ const ITEM_EDIT_DELETE_PATH = 'delete';
|
||||
{
|
||||
path: 'view',
|
||||
/* TODO - change when view page exists */
|
||||
component: ItemStatusComponent
|
||||
component: ItemBitstreamsComponent
|
||||
},
|
||||
{
|
||||
path: 'curate',
|
||||
/* TODO - change when curate page exists */
|
||||
component: ItemStatusComponent
|
||||
component: ItemBitstreamsComponent
|
||||
},
|
||||
]
|
||||
},
|
||||
|
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
|
||||
</div>
|
@@ -0,0 +1 @@
|
||||
@import '../../../../styles/variables.scss';
|
@@ -0,0 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-bitstreams',
|
||||
styleUrls: ['./item-bitstreams.component.scss'],
|
||||
templateUrl: './item-bitstreams.component.html',
|
||||
})
|
||||
/**
|
||||
* Component for displaying an item's bitstreams edit page
|
||||
*/
|
||||
export class ItemBitstreamsComponent {
|
||||
/* TODO implement */
|
||||
}
|
@@ -1,11 +1,6 @@
|
||||
<script src="edit-in-place-field.component.ts"></script>
|
||||
<div [ngClass]="{
|
||||
'table-warning': fieldUpdate.changeType === 0,
|
||||
'table-danger': fieldUpdate.changeType === 2,
|
||||
'table-success': fieldUpdate.changeType === 1
|
||||
}" class="d-flex">
|
||||
<!--{{metadata?.uuid}}-->
|
||||
<td class="col-3">
|
||||
<!--{{metadata?.uuid}}-->
|
||||
<td>
|
||||
<div class="metadata-field">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.key?.split('.').join('.​')}}</span>
|
||||
</div>
|
||||
@@ -25,39 +20,47 @@
|
||||
</div>
|
||||
<small class="text-danger"
|
||||
*ngIf="!(valid | async)">{{"item.edit.metadata.metadatafield.invalid" | translate}}</small>
|
||||
</td>
|
||||
<td class="col-6">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.value}}</span>
|
||||
</div>
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
</div>
|
||||
</td>
|
||||
<td class="w-100">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.value}}</span>
|
||||
</div>
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
<textarea class="form-control" type="textarea" [(ngModel)]="metadata.value" [dsDebounce]
|
||||
(onDebounce)="update()"></textarea>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-1 text-center">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.language}}</span>
|
||||
</div>
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
<input class="form-control" type="text" [(ngModel)]="metadata.language" [dsDebounce]
|
||||
(onDebounce)="update()"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-2 text-center">
|
||||
<div class="btn-group">
|
||||
<button [disabled]="!(canSetEditable() | async)" *ngIf="!(editable | async)" (click)="setEditable(true)" class="btn btn-primary btn-sm" title="{{'item.edit.metadata.edit.buttons.edit' | translate}}">
|
||||
<i class="fas fa-edit fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canSetUneditable() | async)" *ngIf="(editable | async)" (click)="setEditable(false)" class="btn btn-success btn-sm" title="{{'item.edit.metadata.edit.buttons.unedit' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canRemove() | async)" (click)="remove()" class="btn btn-danger btn-sm" title="{{'item.edit.metadata.edit.buttons.remove' | translate}}">
|
||||
<i class="fas fa-trash-alt fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canUndo() | async)" (click)="removeChangesFromField()" class="btn btn-warning btn-sm" title="{{'item.edit.metadata.edit.buttons.undo' | translate}}">
|
||||
<i class="fas fa-undo-alt fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.language}}</span>
|
||||
</div>
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
<input class="form-control" type="text" [(ngModel)]="metadata.language" [dsDebounce]
|
||||
(onDebounce)="update()"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group">
|
||||
<button [disabled]="!(canSetEditable() | async)" *ngIf="!(editable | async)"
|
||||
(click)="setEditable(true)" class="btn btn-outline-primary btn-sm"
|
||||
title="{{'item.edit.metadata.edit.buttons.edit' | translate}}">
|
||||
<i class="fas fa-edit fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canSetUneditable() | async)" *ngIf="(editable | async)"
|
||||
(click)="setEditable(false)" class="btn btn-outline-success btn-sm"
|
||||
title="{{'item.edit.metadata.edit.buttons.unedit' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canRemove() | async)" (click)="remove()"
|
||||
class="btn btn-outline-danger btn-sm"
|
||||
title="{{'item.edit.metadata.edit.buttons.remove' | translate}}">
|
||||
<i class="fas fa-trash-alt fa-fw"></i>
|
||||
</button>
|
||||
<button [disabled]="!(canUndo() | async)" (click)="removeChangesFromField()"
|
||||
class="btn btn-outline-warning btn-sm"
|
||||
title="{{'item.edit.metadata.edit.buttons.undo' | translate}}">
|
||||
<i class="fas fa-undo-alt fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
@@ -1 +1,10 @@
|
||||
@import '../../../../../styles/variables.scss';
|
||||
.btn[disabled] {
|
||||
color: $gray-600;
|
||||
border-color: $gray-600;
|
||||
z-index: 0; // prevent border colors jumping on hover
|
||||
}
|
||||
|
||||
.metadata-field {
|
||||
width: $edit-item-metadata-field-width;
|
||||
}
|
@@ -400,66 +400,64 @@ describe('EditInPlaceFieldComponent', () => {
|
||||
});
|
||||
|
||||
describe('canRemove', () => {
|
||||
describe('when editable is currently true', () => {
|
||||
describe('when the fieldUpdate\'s changeType is currently not REMOVE or ADD', () => {
|
||||
beforeEach(() => {
|
||||
comp.editable = observableOf(true);
|
||||
fixture.detectChanges();
|
||||
comp.fieldUpdate.changeType = FieldChangeType.UPDATE;
|
||||
});
|
||||
it('canRemove should return an observable emitting true', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canRemove()).toBe(expected, { a: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the fieldUpdate\'s changeType is currently ADD', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = FieldChangeType.ADD;
|
||||
});
|
||||
it('canRemove should return an observable emitting false', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canRemove()).toBe(expected, { a: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when editable is currently false', () => {
|
||||
beforeEach(() => {
|
||||
comp.editable = observableOf(false);
|
||||
});
|
||||
|
||||
describe('when the fieldUpdate\'s changeType is currently not REMOVE or ADD', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = FieldChangeType.UPDATE;
|
||||
});
|
||||
it('canRemove should return an observable emitting true', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canRemove()).toBe(expected, { a: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the fieldUpdate\'s changeType is currently ADD', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = FieldChangeType.ADD;
|
||||
});
|
||||
it('canRemove should return an observable emitting false', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canRemove()).toBe(expected, { a: false });
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe('canUndo', () => {
|
||||
|
||||
describe('when the fieldUpdate\'s changeType is currently ADD, UPDATE or REMOVE', () => {
|
||||
describe('when editable is currently true', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = FieldChangeType.ADD;
|
||||
comp.editable = observableOf(true);
|
||||
comp.fieldUpdate.changeType = undefined;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('canUndo should return an observable emitting true', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canUndo()).toBe(expected, { a: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the fieldUpdate\'s changeType is currently undefined', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = undefined;
|
||||
describe('when editable is currently false', () => {
|
||||
describe('when the fieldUpdate\'s changeType is currently ADD, UPDATE or REMOVE', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = FieldChangeType.ADD;
|
||||
});
|
||||
|
||||
it('canUndo should return an observable emitting true', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canUndo()).toBe(expected, { a: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('canUndo should return an observable emitting false', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canUndo()).toBe(expected, { a: false });
|
||||
describe('when the fieldUpdate\'s changeType is currently undefined', () => {
|
||||
beforeEach(() => {
|
||||
comp.fieldUpdate.changeType = undefined;
|
||||
});
|
||||
|
||||
it('canUndo should return an observable emitting false', () => {
|
||||
const expected = '(a|)';
|
||||
scheduler.expectObservable(comp.canUndo()).toBe(expected, { a: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@@ -14,7 +14,8 @@ import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-edit-in-place-field',
|
||||
// tslint:disable-next-line:component-selector
|
||||
selector: '[ds-edit-in-place-field]',
|
||||
styleUrls: ['./edit-in-place-field.component.scss'],
|
||||
templateUrl: './edit-in-place-field.component.html',
|
||||
})
|
||||
@@ -131,7 +132,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
||||
(fields: MetadataField[]) => this.metadataFieldSuggestions.next(
|
||||
fields.map((field: MetadataField) => {
|
||||
return {
|
||||
displayValue: field.toString(),
|
||||
displayValue: field.toString().split('.').join('.​'),
|
||||
value: field.toString()
|
||||
}
|
||||
})
|
||||
|
@@ -1,56 +1,63 @@
|
||||
<div class="container">
|
||||
<div class="item-metadata">
|
||||
<div class="button-row d-flex justify-content-between">
|
||||
<button class="btn btn-success my-2"
|
||||
(click)="add()"><i
|
||||
class="fas fa-plus"></i> {{"item.edit.metadata.add-button" | translate}}
|
||||
<div class="item-metadata">
|
||||
<div class="button-row top d-flex">
|
||||
<button class="mr-auto btn btn-success"
|
||||
(click)="add()"><i
|
||||
class="fas fa-plus"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.add-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.discard-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<table class="table table-responsive table-striped table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{{'item.edit.metadata.headers.field' | translate}}</th>
|
||||
<th>{{'item.edit.metadata.headers.value' | translate}}</th>
|
||||
<th class="text-center">{{'item.edit.metadata.headers.language' | translate}}</th>
|
||||
<th class="text-center">{{'item.edit.metadata.headers.edit' | translate}}</th>
|
||||
</tr>
|
||||
<tr ds-edit-in-place-field
|
||||
*ngFor="let updateValue of ((updates$ | async)| dsObjectValues); trackBy: trackUpdate"
|
||||
[fieldUpdate]="updateValue || {}"
|
||||
[url]="url"
|
||||
[ngClass]="{
|
||||
'table-warning': updateValue.changeType === 0,
|
||||
'table-danger': updateValue.changeType === 2,
|
||||
'table-success': updateValue.changeType === 1
|
||||
}">
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="button-row bottom">
|
||||
<div class="my-2 float-right">
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i> {{"item.edit.metadata.discard-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i> {{"item.edit.metadata.reinstate-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i> {{"item.edit.metadata.save-button" | translate}}
|
||||
</button>
|
||||
<div class="my-2 spaced-btn-group">
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i> {{"item.edit.metadata.discard-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i> {{"item.edit.metadata.reinstate-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i> {{"item.edit.metadata.save-button" | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-responsive table-striped table-bordered">
|
||||
<tbody>
|
||||
<tr class="d-flex">
|
||||
<th class="col-3">{{'item.edit.metadata.headers.field' | translate}}</th>
|
||||
<th class="col-6">{{'item.edit.metadata.headers.value' | translate}}</th>
|
||||
<th class="col-1 text-center">{{'item.edit.metadata.headers.language' | translate}}</th>
|
||||
<th class="col-2 text-center">{{'item.edit.metadata.headers.edit' | translate}}</th>
|
||||
</tr>
|
||||
<tr *ngFor="let updateValue of ((updates$ | async)| dsObjectValues); trackBy: trackUpdate">
|
||||
<ds-edit-in-place-field [fieldUpdate]="updateValue || {}"
|
||||
[url]="url"></ds-edit-in-place-field>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="button-row">
|
||||
<div class="my-2 float-right spaced-btn-group">
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i> {{"item.edit.metadata.discard-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i> {{"item.edit.metadata.reinstate-button" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i> {{"item.edit.metadata.save-button" | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,13 +1,22 @@
|
||||
@import '../../../../styles/variables.scss';
|
||||
|
||||
.button-row {
|
||||
.spaced-btn-group > .btn {
|
||||
.btn {
|
||||
margin-right: 0.5 * $spacer;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: map-get($grid-breakpoints, sm)) {
|
||||
min-width: $edit-item-button-min-width;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
min-width: $button-min-width;
|
||||
|
||||
&.top .btn {
|
||||
margin-top: $spacer/2;
|
||||
margin-bottom: $spacer/2;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -57,6 +57,9 @@ export class ItemMetadataComponent implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up and initialize all fields
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.route.parent.data.pipe(map((data) => data.item))
|
||||
.pipe(
|
||||
@@ -116,8 +119,10 @@ export class ItemMetadataComponent implements OnInit {
|
||||
this.objectUpdatesService.initialize(this.url, this.item.metadata, this.item.lastModified);
|
||||
}
|
||||
|
||||
/* Prevent unnecessary rerendering so fields don't lose focus **/
|
||||
protected trackUpdate(index, update: FieldUpdate) {
|
||||
/**
|
||||
* Prevent unnecessary rerendering so fields don't lose focus
|
||||
*/
|
||||
trackUpdate(index, update: FieldUpdate) {
|
||||
return update && update.field ? update.field.uuid : undefined;
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
{{'item.edit.tabs.status.labels.itemPage' | translate}}:
|
||||
</div>
|
||||
<div class="col-9 float-left status-data" id="status-itemPage">
|
||||
<a href="{{getItemPage()}}">{{getItemPage()}}</a>
|
||||
<a href="{{getItemPage((itemRD$ | async)?.payload)}}">{{getItemPage((itemRD$ | async)?.payload)}}</a>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let operation of operations" class="w-100 pt-3">
|
||||
|
@@ -6,6 +6,7 @@ import { ItemOperation } from '../item-operation/itemOperation.model';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { getItemEditPath, getItemPageRoute } from '../../item-page-routing.module';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-status',
|
||||
@@ -68,16 +69,16 @@ export class ItemStatusComponent implements OnInit {
|
||||
*/
|
||||
this.operations = [];
|
||||
if (item.isWithdrawn) {
|
||||
this.operations.push(new ItemOperation('reinstate', this.getCurrentUrl() + '/reinstate'));
|
||||
this.operations.push(new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate'));
|
||||
} else {
|
||||
this.operations.push(new ItemOperation('withdraw', this.getCurrentUrl() + '/withdraw'));
|
||||
this.operations.push(new ItemOperation('withdraw', this.getCurrentUrl(item) + '/withdraw'));
|
||||
}
|
||||
if (item.isDiscoverable) {
|
||||
this.operations.push(new ItemOperation('private', this.getCurrentUrl() + '/private'));
|
||||
this.operations.push(new ItemOperation('private', this.getCurrentUrl(item) + '/private'));
|
||||
} else {
|
||||
this.operations.push(new ItemOperation('public', this.getCurrentUrl() + '/public'));
|
||||
this.operations.push(new ItemOperation('public', this.getCurrentUrl(item) + '/public'));
|
||||
}
|
||||
this.operations.push(new ItemOperation('delete', this.getCurrentUrl() + '/delete'));
|
||||
this.operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete'));
|
||||
});
|
||||
|
||||
}
|
||||
@@ -86,20 +87,16 @@ export class ItemStatusComponent implements OnInit {
|
||||
* Get the url to the simple item page
|
||||
* @returns {string} url
|
||||
*/
|
||||
getItemPage(): string {
|
||||
return this.router.url.substr(0, this.router.url.lastIndexOf('/'));
|
||||
getItemPage(item: Item): string {
|
||||
return getItemPageRoute(item.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current url without query params
|
||||
* @returns {string} url
|
||||
*/
|
||||
getCurrentUrl(): string {
|
||||
if (this.router.url.indexOf('?') > -1) {
|
||||
return this.router.url.substr(0, this.router.url.indexOf('?'));
|
||||
} else {
|
||||
return this.router.url;
|
||||
}
|
||||
getCurrentUrl(item: Item): string {
|
||||
return getItemEditPath(item.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -68,6 +68,7 @@ import { MenuService } from '../shared/menu/menu.service';
|
||||
import { NormalizedObjectBuildService } from './cache/builders/normalized-object-build.service';
|
||||
import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service';
|
||||
import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
|
||||
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service';
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
@@ -133,6 +134,7 @@ const PROVIDERS = [
|
||||
UUIDService,
|
||||
DSpaceObjectDataService,
|
||||
DSOChangeAnalyzer,
|
||||
DefaultChangeAnalyzer,
|
||||
CSSVariableService,
|
||||
MenuService,
|
||||
ObjectUpdatesService,
|
||||
|
29
src/app/core/data/default-change-analyzer.service.ts
Normal file
29
src/app/core/data/default-change-analyzer.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Operation } from 'fast-json-patch/lib/core';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { ChangeAnalyzer } from './change-analyzer';
|
||||
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
|
||||
/**
|
||||
* A class to determine what differs between two
|
||||
* CacheableObjects
|
||||
*/
|
||||
@Injectable()
|
||||
export class DefaultChangeAnalyzer<T extends CacheableObject> implements ChangeAnalyzer<T> {
|
||||
|
||||
/**
|
||||
* Compare the metadata of two CacheableObject and return the differences as
|
||||
* a JsonPatch Operation Array
|
||||
*
|
||||
* @param {NormalizedObject} object1
|
||||
* The first object to compare
|
||||
* @param {NormalizedObject} object2
|
||||
* The second object to compare
|
||||
*/
|
||||
diff(object1: T | NormalizedObject<T>, object2: T | NormalizedObject<T>): Operation[] {
|
||||
return compare(object1, object2);
|
||||
}
|
||||
}
|
@@ -11,10 +11,10 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { FindAllOptions } from './request.models';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { MetadataSchema } from '../metadata/metadataschema.model';
|
||||
import { ChangeAnalyzer } from './change-analyzer';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||
|
||||
@Injectable()
|
||||
export class MetadataSchemaDataService extends DataService<MetadataSchema> {
|
||||
@@ -27,7 +27,7 @@ export class MetadataSchemaDataService extends DataService<MetadataSchema> {
|
||||
private bs: BrowseService,
|
||||
protected halService: HALEndpointService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected comparator: ChangeAnalyzer<MetadataSchema>,
|
||||
protected comparator: DefaultChangeAnalyzer<MetadataSchema>,
|
||||
protected dataBuildService: NormalizedObjectBuildService,
|
||||
protected http: HttpClient,
|
||||
protected notificationsService: NotificationsService) {
|
||||
|
@@ -228,6 +228,7 @@ describe('MetadataService', () => {
|
||||
const mockPublisher = (mockItem: Item): Item => {
|
||||
const publishedMockItem = Object.assign(new Item(), mockItem) as Item;
|
||||
publishedMockItem.metadata.push({
|
||||
uuid: 'b3826cf5-5f07-44cf-88d8-2da968354d18',
|
||||
key: 'dc.publisher',
|
||||
language: 'en_US',
|
||||
value: 'Mock Publisher'
|
||||
|
@@ -19,7 +19,6 @@ $gray-700: lighten($gray-base, 46.6%) !default; // #777
|
||||
$gray-600: lighten($gray-base, 73.3%) !default; // #bbb
|
||||
$gray-100: lighten($gray-base, 93.5%) !default; // #eee
|
||||
|
||||
|
||||
/* Reassign color vars to semantic color scheme */
|
||||
$blue: #2B4E72 !default;
|
||||
$green: #94BA65 !default;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
$content-spacing: $spacer * 1.5;
|
||||
|
||||
$button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem($input-btn-border-width*2);
|
||||
$button-min-width: 100px;
|
||||
|
||||
$card-height-percentage:98%;
|
||||
$card-thumbnail-height:240px;
|
||||
@@ -24,3 +23,6 @@ $admin-sidebar-header-bg: darken($dark, 7%);
|
||||
|
||||
$dark-scrollbar-background: $admin-sidebar-active-bg;
|
||||
$dark-scrollbar-foreground: #47495d;
|
||||
|
||||
$edit-item-button-min-width: 100px;
|
||||
$edit-item-metadata-field-width: 190px;
|
Reference in New Issue
Block a user