mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 19:43:04 +00:00
97075: Edit metadata redesign pt2
This commit is contained in:
@@ -349,7 +349,7 @@ export const metadataFieldsToString = () =>
|
|||||||
map((schema: MetadataSchema) => ({ field, schema }))
|
map((schema: MetadataSchema) => ({ field, schema }))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return observableCombineLatest(fieldSchemaArray);
|
return isNotEmpty(fieldSchemaArray) ? observableCombineLatest(fieldSchemaArray) : [[]];
|
||||||
}),
|
}),
|
||||||
map((fieldSchemaArray: { field: MetadataField, schema: MetadataSchema }[]): string[] => {
|
map((fieldSchemaArray: { field: MetadataField, schema: MetadataSchema }[]): string[] => {
|
||||||
return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString());
|
return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString());
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
import { MetadataMap, MetadataValue } from '../../core/shared/metadata.models';
|
import { MetadataMap, MetadataValue } from '../../core/shared/metadata.models';
|
||||||
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { Operation } from 'fast-json-patch';
|
||||||
|
import { MetadataPatchReplaceOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-replace-operation.model';
|
||||||
|
import { MetadataPatchRemoveOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-remove-operation.model';
|
||||||
|
import { MetadataPatchAddOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-add-operation.model';
|
||||||
|
import { MetadataPatchOperation } from '../../core/data/object-updates/patch-operation-service/operations/metadata/metadata-patch-operation.model';
|
||||||
|
|
||||||
export enum DsoEditMetadataChangeType {
|
export enum DsoEditMetadataChangeType {
|
||||||
UPDATE = 1,
|
UPDATE = 1,
|
||||||
@@ -55,10 +60,14 @@ export class DsoEditMetadataValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reinstate(): void {
|
reinstate(): void {
|
||||||
this.newValue = this.reinstatableValue;
|
if (hasValue(this.reinstatableValue)) {
|
||||||
this.reinstatableValue = undefined;
|
this.newValue = this.reinstatableValue;
|
||||||
this.change = this.reinstatableChange;
|
this.reinstatableValue = undefined;
|
||||||
this.reinstatableChange = undefined;
|
}
|
||||||
|
if (hasValue(this.reinstatableChange)) {
|
||||||
|
this.change = this.reinstatableChange;
|
||||||
|
this.reinstatableChange = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isReinstatable(): boolean {
|
isReinstatable(): boolean {
|
||||||
@@ -67,16 +76,23 @@ export class DsoEditMetadataValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DsoEditMetadataForm {
|
export class DsoEditMetadataForm {
|
||||||
|
originalFieldKeys: string[];
|
||||||
fieldKeys: string[];
|
fieldKeys: string[];
|
||||||
fields: {
|
fields: {
|
||||||
[mdField: string]: DsoEditMetadataValue[],
|
[mdField: string]: DsoEditMetadataValue[],
|
||||||
};
|
};
|
||||||
|
reinstatableNewValues: {
|
||||||
|
[mdField: string]: DsoEditMetadataValue[],
|
||||||
|
};
|
||||||
newValue: DsoEditMetadataValue;
|
newValue: DsoEditMetadataValue;
|
||||||
|
|
||||||
constructor(metadata: MetadataMap) {
|
constructor(metadata: MetadataMap) {
|
||||||
|
this.originalFieldKeys = [];
|
||||||
this.fieldKeys = [];
|
this.fieldKeys = [];
|
||||||
this.fields = {};
|
this.fields = {};
|
||||||
|
this.reinstatableNewValues = {};
|
||||||
Object.entries(metadata).forEach(([mdField, values]: [string, MetadataValue[]]) => {
|
Object.entries(metadata).forEach(([mdField, values]: [string, MetadataValue[]]) => {
|
||||||
|
this.originalFieldKeys.push(mdField);
|
||||||
this.fieldKeys.push(mdField);
|
this.fieldKeys.push(mdField);
|
||||||
this.fields[mdField] = values.map((value) => new DsoEditMetadataValue(value));
|
this.fields[mdField] = values.map((value) => new DsoEditMetadataValue(value));
|
||||||
});
|
});
|
||||||
@@ -89,12 +105,17 @@ export class DsoEditMetadataForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMetadataField(mdField: string) {
|
setMetadataField(mdField: string) {
|
||||||
|
this.newValue.editing = false;
|
||||||
|
this.addValueToField(this.newValue, mdField);
|
||||||
|
this.newValue = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private addValueToField(value: DsoEditMetadataValue, mdField: string) {
|
||||||
if (isEmpty(this.fields[mdField])) {
|
if (isEmpty(this.fields[mdField])) {
|
||||||
this.fieldKeys.push(mdField);
|
this.fieldKeys.push(mdField);
|
||||||
this.fields[mdField] = [];
|
this.fields[mdField] = [];
|
||||||
}
|
}
|
||||||
this.fields[mdField].push(this.newValue);
|
this.fields[mdField].push(value);
|
||||||
this.newValue = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(mdField: string, index: number) {
|
remove(mdField: string, index: number) {
|
||||||
@@ -112,11 +133,31 @@ export class DsoEditMetadataForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
discard(): void {
|
discard(): void {
|
||||||
Object.values(this.fields).forEach((values) => {
|
Object.entries(this.fields).forEach(([field, values]) => {
|
||||||
values.forEach((value) => {
|
let removeFromIndex = -1;
|
||||||
value.discard();
|
values.forEach((value, index) => {
|
||||||
|
if (value.change === DsoEditMetadataChangeType.ADD) {
|
||||||
|
if (isEmpty(this.reinstatableNewValues[field])) {
|
||||||
|
this.reinstatableNewValues[field] = [];
|
||||||
|
}
|
||||||
|
this.reinstatableNewValues[field].push(value);
|
||||||
|
if (removeFromIndex === -1) {
|
||||||
|
removeFromIndex = index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value.discardAndMarkReinstatable();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
if (removeFromIndex > -1) {
|
||||||
|
this.fields[field].splice(removeFromIndex, this.fields[field].length - removeFromIndex);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
this.fieldKeys.forEach((field) => {
|
||||||
|
if (this.originalFieldKeys.indexOf(field) < 0) {
|
||||||
|
delete this.fields[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.fieldKeys = [...this.originalFieldKeys];
|
||||||
}
|
}
|
||||||
|
|
||||||
reinstate(): void {
|
reinstate(): void {
|
||||||
@@ -125,9 +166,48 @@ export class DsoEditMetadataForm {
|
|||||||
value.reinstate();
|
value.reinstate();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Object.entries(this.reinstatableNewValues).forEach(([field, values]) => {
|
||||||
|
values.forEach((value) => {
|
||||||
|
this.addValueToField(value, field);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.reinstatableNewValues = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
isReinstatable(): boolean {
|
isReinstatable(): boolean {
|
||||||
return Object.values(this.fields).some((values) => values.some((value) => value.isReinstatable()));
|
return isNotEmpty(this.reinstatableNewValues) ||
|
||||||
|
Object.values(this.fields)
|
||||||
|
.some((values) => values
|
||||||
|
.some((value) => value.isReinstatable()));
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperations(): Operation[] {
|
||||||
|
const operations: Operation[] = [];
|
||||||
|
Object.entries(this.fields).forEach(([field, values]) => {
|
||||||
|
values.forEach((value, place) => {
|
||||||
|
if (value.hasChanges()) {
|
||||||
|
let operation: MetadataPatchOperation;
|
||||||
|
if (value.change === DsoEditMetadataChangeType.UPDATE) {
|
||||||
|
operation = new MetadataPatchReplaceOperation(field, place, {
|
||||||
|
value: value.newValue.value,
|
||||||
|
language: value.newValue.language,
|
||||||
|
});
|
||||||
|
} else if (value.change === DsoEditMetadataChangeType.REMOVE) {
|
||||||
|
operation = new MetadataPatchRemoveOperation(field, place);
|
||||||
|
} else if (value.change === DsoEditMetadataChangeType.ADD) {
|
||||||
|
operation = new MetadataPatchAddOperation(field, {
|
||||||
|
value: value.newValue.value,
|
||||||
|
language: value.newValue.language,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('Illegal metadata change state detected for', value);
|
||||||
|
}
|
||||||
|
if (hasValue(operation)) {
|
||||||
|
operations.push(operation.toOperation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return operations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
<div class="item-metadata" *ngIf="form">
|
<div class="item-metadata" *ngIf="form">
|
||||||
<div class="button-row top d-flex my-2 space-children-mr">
|
<div class="button-row top d-flex my-2 space-children-mr">
|
||||||
<button class="mr-auto btn btn-success" [disabled]="form.newValue"
|
<button class="mr-auto btn btn-success" [disabled]="form.newValue || (saving$ | async)"
|
||||||
(click)="add()"><i class="fas fa-plus"></i>
|
(click)="add()"><i class="fas fa-plus"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.add-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.add-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-warning" *ngIf="isReinstatable"
|
<button class="btn btn-warning ml-2" *ngIf="isReinstatable" [disabled]="(saving$ | async)"
|
||||||
(click)="reinstate()"><i class="fas fa-undo-alt"></i>
|
(click)="reinstate()"><i class="fas fa-undo-alt"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" [disabled]="!hasChanges"
|
<button class="btn btn-primary ml-2" [disabled]="!hasChanges || (saving$ | async)"
|
||||||
(click)="submit()"><i class="fas fa-save"></i>
|
(click)="submit()"><i class="fas fa-save"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.save-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.save-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" *ngIf="!isReinstatable"
|
<button class="btn btn-danger ml-2" *ngIf="!isReinstatable"
|
||||||
[disabled]="!hasChanges"
|
[disabled]="!hasChanges || (saving$ | async)"
|
||||||
(click)="discard()"><i class="fas fa-times"></i>
|
(click)="discard()"><i class="fas fa-times"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.discard-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.discard-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="flex-grow-1 ds-drop-list">
|
<div class="flex-grow-1 ds-drop-list">
|
||||||
<div class="d-flex flex-row ds-value-row ds-success">
|
<div class="d-flex flex-row ds-value-row ds-success">
|
||||||
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
|
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
|
||||||
<input class="form-control" type="text" [(ngModel)]="form.newValue.newValue.value" />
|
<textarea class="form-control" rows="2" [(ngModel)]="form.newValue.newValue.value"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="ds-flex-cell ds-lang-cell">
|
<div class="ds-flex-cell ds-lang-cell">
|
||||||
<input class="form-control" type="text" [(ngModel)]="form.newValue.newValue.language" />
|
<input class="form-control" type="text" [(ngModel)]="form.newValue.newValue.language" />
|
||||||
@@ -44,16 +44,16 @@
|
|||||||
<div class="text-center ds-flex-cell ds-edit-cell">
|
<div class="text-center ds-flex-cell ds-edit-cell">
|
||||||
<div class="btn-group edit-field">
|
<div class="btn-group edit-field">
|
||||||
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="Confirm changes"
|
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="Confirm changes"
|
||||||
[disabled]="!newMdField" (click)="form.setMetadataField(newMdField)">
|
[disabled]="!newMdField || (saving$ | async)" (click)="form.setMetadataField(newMdField); onValueSaved()">
|
||||||
<i class="fas fa-check fa-fw"></i>
|
<i class="fas fa-check fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove" [disabled]="true">
|
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove" [disabled]="true">
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes" (click)="form.newValue = undefined">
|
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes" [disabled]="(saving$ | async)" (click)="form.newValue = undefined">
|
||||||
<i class="fas fa-undo-alt fa-fw"></i>
|
<i class="fas fa-undo-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" ngbTooltip="Drag to reorder" [disabled]="true">
|
<button class="btn btn-outline-secondary ds-drag-handle btn-sm disabled" ngbTooltip="Drag to reorder" [disabled]="true">
|
||||||
<i class="fas fa-grip-vertical fa-fw"></i>
|
<i class="fas fa-grip-vertical fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,15 +63,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-row ds-field-row" *ngFor="let mdField of form.fieldKeys">
|
<div class="d-flex flex-row ds-field-row" *ngFor="let mdField of form.fieldKeys">
|
||||||
<div class="lbl-cell">
|
<div class="lbl-cell">
|
||||||
<span>{{ mdField }}</span>
|
<span class="dont-break-out preserve-line-breaks">{{ mdField }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow-1 ds-drop-list">
|
<div class="flex-grow-1 ds-drop-list">
|
||||||
<div class="d-flex flex-row ds-value-row"
|
<div class="d-flex flex-row ds-value-row"
|
||||||
[ngClass]="{ 'ds-warning': mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD }"
|
[ngClass]="{ 'ds-warning': mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': form.fields[mdField].length === 1 }"
|
||||||
*ngFor="let mdValue of form.fields[mdField]; let idx = index">
|
*ngFor="let mdValue of form.fields[mdField]; let idx = index">
|
||||||
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
|
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
|
||||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.value }}</div>
|
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.value }}</div>
|
||||||
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.value" />
|
<textarea class="form-control" rows="2" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.value" ></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="ds-flex-cell ds-lang-cell">
|
<div class="ds-flex-cell ds-lang-cell">
|
||||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
|
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
|
||||||
@@ -80,23 +80,24 @@
|
|||||||
<div class="text-center ds-flex-cell ds-edit-cell">
|
<div class="text-center ds-flex-cell ds-edit-cell">
|
||||||
<div class="btn-group edit-field">
|
<div class="btn-group edit-field">
|
||||||
<button class="btn btn-outline-primary btn-sm ng-star-inserted" ngbTooltip="Edit" *ngIf="!mdValue.editing"
|
<button class="btn btn-outline-primary btn-sm ng-star-inserted" ngbTooltip="Edit" *ngIf="!mdValue.editing"
|
||||||
[disabled]="mdValue.change && mdValue.change !== DsoEditMetadataChangeTypeEnum.UPDATE" (click)="mdValue.editing = true">
|
[disabled]="mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE || (saving$ | async)" (click)="mdValue.editing = true">
|
||||||
<i class="fas fa-edit fa-fw"></i>
|
<i class="fas fa-edit fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="Confirm changes" *ngIf="mdValue.editing"
|
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="Confirm changes" *ngIf="mdValue.editing"
|
||||||
(click)="mdValue.confirmChanges()">
|
[disabled]="(saving$ | async)" (click)="mdValue.confirmChanges(); onValueSaved()">
|
||||||
<i class="fas fa-check fa-fw"></i>
|
<i class="fas fa-check fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove"
|
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove"
|
||||||
[disabled]="mdValue.change || mdValue.editing" (click)="mdValue.change = DsoEditMetadataChangeTypeEnum.REMOVE">
|
[disabled]="mdValue.change || mdValue.editing || (saving$ | async)" (click)="mdValue.change = DsoEditMetadataChangeTypeEnum.REMOVE; onValueSaved()">
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes"
|
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes"
|
||||||
[disabled]="!mdValue.change && !mdValue.editing" (click)="mdValue.change === DsoEditMetadataChangeTypeEnum.ADD ? form.remove(mdField, idx) : mdValue.discard()">
|
[disabled]="(!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="mdValue.change === DsoEditMetadataChangeTypeEnum.ADD ? form.remove(mdField, idx) : mdValue.discard(); onValueSaved()">
|
||||||
<i class="fas fa-undo-alt fa-fw"></i>
|
<i class="fas fa-undo-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<!-- TODO: Enable drag -->
|
<!-- TODO: Enable drag -->
|
||||||
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" ngbTooltip="Drag to reorder" [disabled]="true">
|
<button class="btn btn-outline-secondary ds-drag-handle btn-sm"
|
||||||
|
[ngClass]="{'disabled': form.fields[mdField].length === 1 || (saving$ | async)}" ngbTooltip="Drag to reorder" [disabled]="form.fields[mdField].length === 1 || (saving$ | async)">
|
||||||
<i class="fas fa-grip-vertical fa-fw"></i>
|
<i class="fas fa-grip-vertical fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,14 +111,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="button-row bottom">
|
<div class="button-row bottom">
|
||||||
<div class="mt-2 float-right space-children-mr ml-gap">
|
<div class="mt-2 float-right space-children-mr ml-gap">
|
||||||
<button class="btn btn-warning" *ngIf="isReinstatable"
|
<button class="btn btn-warning ml-2" *ngIf="isReinstatable" [disabled]="(saving$ | async)"
|
||||||
(click)="reinstate()"><i class="fas fa-undo-alt"></i> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}
|
(click)="reinstate()"><i class="fas fa-undo-alt"></i> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" [disabled]="!hasChanges"
|
<button class="btn btn-primary ml-2" [disabled]="!hasChanges || (saving$ | async)"
|
||||||
(click)="submit()"><i class="fas fa-save"></i> {{ dsoType + '.edit.metadata.save-button' | translate }}
|
(click)="submit()"><i class="fas fa-save"></i> {{ dsoType + '.edit.metadata.save-button' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" *ngIf="!isReinstatable"
|
<button class="btn btn-danger ml-2" *ngIf="!isReinstatable"
|
||||||
[disabled]="!hasChanges"
|
[disabled]="!hasChanges || (saving$ | async)"
|
||||||
(click)="discard()"><i class="fas fa-times"></i> {{ dsoType + '.edit.metadata.discard-button' | translate }}
|
(click)="discard()"><i class="fas fa-times"></i> {{ dsoType + '.edit.metadata.discard-button' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
.lbl-cell {
|
.lbl-cell {
|
||||||
min-width: 210px;
|
min-width: 210px;
|
||||||
|
max-width: 210px;
|
||||||
background-color: var(--bs-gray-100);
|
background-color: var(--bs-gray-100);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
@@ -16,7 +17,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ds-lang-cell {
|
.ds-lang-cell {
|
||||||
min-width: 72px;
|
min-width: 90px;
|
||||||
|
max-width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ds-edit-cell {
|
.ds-edit-cell {
|
||||||
@@ -68,6 +70,6 @@
|
|||||||
background-color: var(--bs-gray-100);
|
background-color: var(--bs-gray-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ds-drag-handle {
|
.ds-drag-handle:not(.disabled) {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Injector, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { AlertType } from '../../shared/alert/aletr-type';
|
import { AlertType } from '../../shared/alert/aletr-type';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { DsoEditMetadataChangeType, DsoEditMetadataForm } from './dso-edit-metadata-form';
|
import { DsoEditMetadataChangeType, DsoEditMetadataForm } from './dso-edit-metadata-form';
|
||||||
@@ -12,7 +12,17 @@ import { RegistryService } from '../../core/registry/registry.service';
|
|||||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { getFirstSucceededRemoteData, metadataFieldsToString } from '../../core/shared/operators';
|
import {
|
||||||
|
getFirstCompletedRemoteData,
|
||||||
|
getFirstSucceededRemoteData,
|
||||||
|
metadataFieldsToString
|
||||||
|
} from '../../core/shared/operators';
|
||||||
|
import { UpdateDataService } from '../../core/data/update-data.service';
|
||||||
|
import { getDataServiceFor } from '../../core/cache/builders/build-decorators';
|
||||||
|
import { ResourceType } from '../../core/shared/resource-type';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { DataService } from '../../core/data/data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dso-edit-metadata',
|
selector: 'ds-dso-edit-metadata',
|
||||||
@@ -21,6 +31,7 @@ import { getFirstSucceededRemoteData, metadataFieldsToString } from '../../core/
|
|||||||
})
|
})
|
||||||
export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
||||||
@Input() dso: DSpaceObject;
|
@Input() dso: DSpaceObject;
|
||||||
|
updateDataService: UpdateDataService<DSpaceObject>;
|
||||||
dsoType: string;
|
dsoType: string;
|
||||||
|
|
||||||
form: DsoEditMetadataForm;
|
form: DsoEditMetadataForm;
|
||||||
@@ -29,6 +40,7 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
|||||||
isReinstatable: boolean;
|
isReinstatable: boolean;
|
||||||
hasChanges: boolean;
|
hasChanges: boolean;
|
||||||
isEmpty: boolean;
|
isEmpty: boolean;
|
||||||
|
saving$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The AlertType enumeration for access in the component's template
|
* The AlertType enumeration for access in the component's template
|
||||||
@@ -44,7 +56,10 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
dsoUpdateSubscription: Subscription;
|
dsoUpdateSubscription: Subscription;
|
||||||
|
|
||||||
constructor(protected route: ActivatedRoute) {
|
constructor(protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translateService: TranslateService,
|
||||||
|
protected parentInjector: Injector) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -54,27 +69,55 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
|||||||
map((data: any) => data.dso)
|
map((data: any) => data.dso)
|
||||||
).subscribe((rd: RemoteData<DSpaceObject>) => {
|
).subscribe((rd: RemoteData<DSpaceObject>) => {
|
||||||
this.dso = rd.payload;
|
this.dso = rd.payload;
|
||||||
|
this.initDataService();
|
||||||
this.initForm();
|
this.initForm();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.initDataService();
|
||||||
this.initForm();
|
this.initForm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initForm(): void {
|
initDataService(): void {
|
||||||
this.dsoType = typeof this.dso.type === 'string' ? this.dso.type as any : this.dso.type.value;
|
let type: ResourceType;
|
||||||
this.form = new DsoEditMetadataForm(this.dso.metadata);
|
if (typeof this.dso.type === 'string') {
|
||||||
this.onDebounce();
|
type = new ResourceType(this.dso.type);
|
||||||
|
} else {
|
||||||
|
type = this.dso.type;
|
||||||
|
}
|
||||||
|
const provider = getDataServiceFor(type);
|
||||||
|
this.updateDataService = Injector.create({
|
||||||
|
providers: [],
|
||||||
|
parent: this.parentInjector
|
||||||
|
}).get(provider);
|
||||||
|
this.dsoType = type.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDebounce(): void {
|
initForm(): void {
|
||||||
|
this.form = new DsoEditMetadataForm(this.dso.metadata);
|
||||||
|
this.onValueSaved();
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueSaved(): void {
|
||||||
this.hasChanges = this.form.hasChanges();
|
this.hasChanges = this.form.hasChanges();
|
||||||
this.isReinstatable = this.form.isReinstatable();
|
this.isReinstatable = this.form.isReinstatable();
|
||||||
this.isEmpty = Object.keys(this.form.fields).length === 0;
|
this.isEmpty = Object.keys(this.form.fields).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
submit(): void {
|
submit(): void {
|
||||||
|
this.saving$.next(true);
|
||||||
|
this.updateDataService.patch(this.dso, this.form.getOperations()).pipe(
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
).subscribe((rd: RemoteData<DSpaceObject>) => {
|
||||||
|
this.saving$.next(false);
|
||||||
|
if (rd.hasFailed) {
|
||||||
|
this.notificationsService.error('error', rd.errorMessage);
|
||||||
|
} else {
|
||||||
|
this.notificationsService.success('saved', 'saved');
|
||||||
|
this.dso = rd.payload;
|
||||||
|
this.initForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
add(): void {
|
add(): void {
|
||||||
@@ -84,10 +127,12 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
discard(): void {
|
discard(): void {
|
||||||
this.form.discard();
|
this.form.discard();
|
||||||
|
this.onValueSaved();
|
||||||
}
|
}
|
||||||
|
|
||||||
reinstate(): void {
|
reinstate(): void {
|
||||||
this.form.reinstate();
|
this.form.reinstate();
|
||||||
|
this.onValueSaved();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { startWith, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
import { switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import {
|
import {
|
||||||
getAllSucceededRemoteData,
|
getAllSucceededRemoteData,
|
||||||
getFirstSucceededRemoteData,
|
|
||||||
metadataFieldsToString
|
metadataFieldsToString
|
||||||
} from '../../../core/shared/operators';
|
} from '../../../core/shared/operators';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
@@ -26,7 +25,8 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy {
|
|||||||
public input: FormControl = new FormControl();
|
public input: FormControl = new FormControl();
|
||||||
|
|
||||||
query$: BehaviorSubject<string> = new BehaviorSubject<string>(null);
|
query$: BehaviorSubject<string> = new BehaviorSubject<string>(null);
|
||||||
debounceTime = 500;
|
debounceTime = 300;
|
||||||
|
selectedValueLoading = false;
|
||||||
|
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy {
|
|||||||
this.input.valueChanges.pipe(
|
this.input.valueChanges.pipe(
|
||||||
debounceTime(this.debounceTime),
|
debounceTime(this.debounceTime),
|
||||||
).subscribe((valueChange) => {
|
).subscribe((valueChange) => {
|
||||||
this.query$.next(valueChange);
|
if (!this.selectedValueLoading) {
|
||||||
|
this.query$.next(valueChange);
|
||||||
|
}
|
||||||
|
this.selectedValueLoading = false;
|
||||||
this.mdField = valueChange;
|
this.mdField = valueChange;
|
||||||
this.mdFieldChange.emit(this.mdField);
|
this.mdFieldChange.emit(this.mdField);
|
||||||
}),
|
}),
|
||||||
@@ -59,7 +62,8 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select(mdFieldOption: string) {
|
select(mdFieldOption: string) {
|
||||||
this.mdField = mdFieldOption;
|
this.selectedValueLoading = true;
|
||||||
|
this.input.setValue(mdFieldOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
Reference in New Issue
Block a user